本周的项目任务是实现ls命令,经过三天的努力。。。实现了ls -a -l -R -i功能
我是按照书上的框架做的,因为没有用链表。。所以-R并不能完全跑完根目录。。。日后有时间再写一个链表版的吧。。
下面是代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/limits.h>
#include <dirent.h>
#include <grp.h>
#include <pwd.h>
#include <errno.h>
#include <math.h>
#include <fcntl.h>
#define NORMAL 0
#define BLUE 1
#define GREEN 2
#define YELLOW 3
#define WHITE 4
#define PARAM_NONE 0 // 无参数
#define PARAM_A 1 // -a:显示所有文件
#define PARAM_L 2 // -l:一行只显示一个文件的详细信息
#define PARAM_R 4 // -R:连同子目录内容一起列出来
#define PARAM_I 8 // -i:显示inode
#define MAXROWLEN 80 // 一行显示的最多字符数
int g_leave_len = MAXROWLEN; //一行剩下的长度,用于输出对齐
int g_maxlen; //存放某目录下最长文件名的长度
void display_dir(int flag_param,char *path);
void mycolor(char*name,int color)
{
if(color==BLUE){
printf("\033[;34m %-s\033[0m" "",name);
}
else if(color==GREEN){
printf("\033[;32m %-s\033[0m" "",name);
}
else if(color==NORMAL){
printf(" %-s",name);
}
}
/*错误处理函数,打印出错误所在行数和错误信息*/
void error(const char *p,int line)
{
fprintf(stderr,"line:%d ",line);
perror(p);
exit(1);
}
//获取文件属性并打印
void getprintugrrwx(struct stat a,char *name,int color)
{
char time[32];
struct passwd *psd; //从该结构体中获取文件所有者的用户名
struct group *grp; //从该结构体中获取文件所有者的所属组的组名
char b[11]={"----------"};
//获取文件类型
if(S_ISLNK(a.st_mode))
b[0]='l';
if(S_ISREG(a.st_mode))
b[0]='-';
if(S_ISDIR(a.st_mode))
b[0]='d';
if(S_ISCHR(a.st_mode))
b[0]='c';
if(S_ISBLK(a.st_mode))
b[0]='b';
if(S_ISFIFO(a.st_mode))
b[0]='f';
if(S_ISSOCK(a.st_mode))
b[0]='s';
//获取文件所有者权限
if(a.st_mode & S_IRUSR)
b[1]='r';
if(a.st_mode & S_IWUSR)
b[2]='w';
if(a.st_mode & S_IXUSR)
b[3]='x';
//获取文件所有组权限
if(a.st_mode & S_IRGRP)
b[4]='r';
if(a.st_mode & S_IWGRP)
b[5]='w';
if(a.st_mode & S_IXGRP)
b[6]='x';
//获取其他人权限
if(a.st_mode & S_IROTH)
b[7]='r';
if(a.st_mode & S_IWOTH)
b[8]='w';
if(a.st_mode & S_IXOTH)
b[9]='x';
//printf("\t");
//获取用户名,组名
psd=getpwuid(a.st_uid);
grp=getgrgid(a.st_gid);
strcpy(time,ctime(&a.st_mtime));
time[strlen(time)-1]='\0'; //去掉换行符
//打印所有
printf("%s%4ld%-8s%-8s%-6ld%-s",b,a.st_nlink,psd->pw_name,grp->gr_name,a.st_size,time);
mycolor(name,color);
printf("\n");
}
//没有用-l时,打印一个文件名并对齐
void printwithoutl(char *name,int color)
{
int i,len;
//如果这一行不够打印一个文件名则换行
if(g_leave_len < g_maxlen){
printf("\n");
g_leave_len = MAXROWLEN;
}
len = strlen(name);
len = g_maxlen - len;
mycolor(name,color);
for(i=0;i<len;i++){
printf(" ");
}
printf(" ");
//下面的2指示空两格
g_leave_len -= (g_maxlen + 2);
}
void inode(struct stat buf,char *name,int color)
{
int i,len;
if(g_leave_len < g_maxlen+8){
printf("\n");
g_leave_len=MAXROWLEN;
}
printf("%ld ", buf.st_ino);
len = strlen(name);
len = g_maxlen - len;
mycolor(name,color);
for(i=0;i<len;i++)
printf(" ");
printf(" ");
g_leave_len -= (g_maxlen+2+8);
}
//根据命令行参数和完整路径名显示目标文件
void display(int flag,char *pathname)
{
int i,j;
struct stat buf;
char name[NAME_MAX + 1];
int color = NORMAL;
//从路径中解析出文件名
for(i=0,j=0;i<strlen(pathname);i++){
if(pathname[i]=='/'){
j=0;
continue;
}
name[j++]=pathname[i];
}
name[j]='\0';
//用lstat方便解析连接文件
if(lstat(pathname,&buf)==-1){
if(errno==13){
printf("权限有问题\n");
return ;
}
error("stat",__LINE__);
}
if(S_ISDIR(buf.st_mode)){
color = GREEN;
}
if((buf.st_mode & S_IXUSR) && color != GREEN){
color = BLUE;
}
switch(flag)
{
case PARAM_NONE: //没有任何参数
case PARAM_R:
if(name[0]!='.'){
printwithoutl(name,color);
}
break;
case PARAM_A: //-a
case PARAM_A+PARAM_R:
printwithoutl(name,color);
break;
case PARAM_L:
case PARAM_R+PARAM_L:
if(name[0]!='.'){ //-l
getprintugrrwx(buf,name,color);
}
break;
case PARAM_A+PARAM_L:
case PARAM_A+PARAM_L+PARAM_R:
getprintugrrwx(buf,name,color);
break;
case PARAM_I:
if(name[0]!='-'){
inode(buf,name,color);
}
break;
case PARAM_A+PARAM_I:
inode(buf,name,color);
break;
case PARAM_L+PARAM_I:
if(name[0] != '.'){
printf("%ld",buf.st_ino);
getprintugrrwx(buf,name,color);
}
break;
case PARAM_A+PARAM_I+PARAM_L:
printf("%ld",buf.st_ino);
getprintugrrwx(buf,name,color);
default:
break;
}
}
void display_dir(int flag_param,char *path)
{
DIR *dir;
struct dirent *ptr;
int count = 0;
int i,j,len = strlen(path);
char /*file[256][PATH_MAX+1],*/temp[PATH_MAX+1];
//获取该目录下文件总数和最长的文件名
dir = opendir(path);
if(dir == NULL){
if(errno==13){
printf("没有权限");
return ;
}
error("opendir",__LINE__);
}
while((ptr = readdir(dir)) != NULL){
if(g_maxlen < strlen(ptr->d_name))
g_maxlen = strlen(ptr->d_name);
count++;
}
closedir(dir);
char (*file)[PATH_MAX+1]=(char(*)[PATH_MAX+1])malloc(sizeof(char)*count*(PATH_MAX+1));
// if(count>256)
// error("太多了,再打就崩了\n",__LINE__);
//获取该目录下所有的文件名
dir = opendir(path);
for(i = 0;i < count; i++){
ptr = readdir(dir);
if( ptr == NULL){
error("readdir",__LINE__);
}
strncpy(file[i],path,len);
file[i][len] = '\0';
strcat(file[i],ptr->d_name);
file[i][len+strlen(ptr->d_name)] = '\0';
}
//使用冒泡法对文件名进行排序,排序后文件名按照字母顺序存储于file
for(i = 0; i < count-1; i++)
for(j = 0; j < count-1-i; j++){
if( strcmp(file[j],file[j+1]) > 0){
strcpy(temp,file[j+1]);
temp[strlen(file[j+1])] = '\0';
strcpy(file[j+1],file[j]);
file[j+1][strlen(file[j])] = '\0';
strcpy(file[j],temp);
file[j][strlen(temp)] = '\0';
}
}
if((flag_param & PARAM_R)!=0)
printf("%s:\n",path);
for(i = 0; i < count; i++)
display(flag_param,file[i]);
closedir(dir);
if((flag_param & PARAM_L)==0)
printf("\n");
if((flag_param & PARAM_R)!=0){
struct stat buf ;
char c[30];
char a[PATH_MAX];
int b;
for(i=0;i<count;i++){
strcpy(a,file[i]);
strcpy(c,file[i]+len);
b=strlen(file[i]);
if(c[0]=='.')
continue;
//如果目录文件或目录不存在,报错并退出程序
if( lstat(a,&buf)== -1 ){
if(errno==13)
{
printf("没有权限\n");
return ;
}
error("stat",__LINE__);
}
if( S_ISDIR(buf.st_mode) ){
a[b]='/';
a[b+1]='\0';
display_dir(flag_param,a);
}
}
}
free(file);
}
int main(int argc , char ** argv)
{
int i,j,k,num;
char path[PATH_MAX];
char param[32]; //保存命令行参数,目录文件名和目录名不在此列
int flag_param = PARAM_NONE ;
struct stat buf ;
/*命令行参数解析,分析各个选项*/
j = 0;
num = 0;
for(i = 1; i < argc; i++){
if(argv[i][0] == '-'){
for(k=1;k < strlen(argv[i]); k++,j++)
param[j]=argv[i][k]; //将所有的参数都保存到数组中
num++;//'-'的个数
}
}
/*所支持的参数*/
for(i=0; i<j; i++)
{
if(param[i] == 'a'){
flag_param |= PARAM_A;
continue;
}else if(param[i] == 'l'){
flag_param |= PARAM_L;
continue;
}else if(param[i]=='R'){
flag_param |= PARAM_R;
continue;
}else if(param[i]=='i'){
flag_param |= PARAM_I;
continue;
}else{
printf("你输入的这个参数,并没有写嘻嘻嘻...\n");
exit(1);
}
}
param[j] = '\0';
//如果没有输入文件名或目录,就显示当前目录
if((num+1) == argc){
strcpy(path,"./");
path[2]='\0';
display_dir(flag_param,path);
return 0;
}
for(i=1;i<argc;i++)
{
//如果不是目录文件名或者目录,解析下一个命令参数
if(argv[i][0] =='-'){
continue;
}
else{
strcpy(path,argv[i]);
}
//如果目录文件或目录不存在,报错并退出程序
if( stat(path,&buf)== -1 ){
error("stat",__LINE__);
}
if( S_ISDIR(buf.st_mode) ) //argv[i]是一个目录
//如果最后一个字符不是'/',就加上'/'
{
if(path[strlen(argv[i])-1]!='/'){
path[strlen(argv[i])]='/';
path[strlen(argv[i])+1]='\0';
}
else{
path[ strlen(argv[i]) ] = '\0';
}
display_dir(flag_param,path);
}
else{
display(flag_param,path);
}
}
return 0;
}