GNU/Linux C 开发实战—myls 需求 对不同类型或不同权限的的文件,输出不同颜色的文字 实现ls的以下7个参数任意组合-a 不隐藏任何以 .
开始的项目 -i 显示每个文件的索引编号(inode 号) -l 使用较长格式列出信息 -s 以块数形式显示每个文件分配的尺寸 -t 按时间排序,最新的最前 -r 逆序排列 -R 递归显示子目录 必要的头文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <grp.h> #include <locale.h> #include <pwd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <time.h> #include <unistd.h>
开发过程 获取并解析用户输入 分别声明7个_Bool
类型的全局变量存储解析到的各个参数的使用情况
1 2 3 4 5 6 7 _Bool Options_a;_Bool Options_i; _Bool Options_l;_Bool Options_r; _Bool Options_R;_Bool Options_s; _Bool Options_t;
通过判断argv
中的指针指向的字符串的首字符是不是-
来判断这个字符串是参数还是路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 _Bool p = 0 ; char *path;for (int i = 1 ; i < argc; i++){ if (*argv[i] == '-' ) { for (unsigned int n = 1 ; n < strlen (argv[i]); n++) switch (argv[i][n]) { case 'a' : Options_a = 1 ; break ; case 'i' : Options_i = 1 ; break ; case 'l' : Options_l = 1 ; break ; case 'r' : Options_r = 1 ; break ; case 'R' : Options_R = 1 ; break ; case 's' : Options_s = 1 ; break ; case 't' : Options_t = 1 ; break ; default : printf ("%s error:Unknow options: %s\n" , __FILE__, argv[i]); exit (EXIT_FAILURE); break ; } } else { p = 1 ; path = argv[i]; } }
info
在ls中,如果用户输入了路径,那么应该输出用户输入的路径下的文件,否则路径的缺省值应该为当前目录
1 2 3 4 5 6 if (!p) { path = getcwd(NULL , 0 ); if (path == NULL ) myerror("getcwd" , " " , __LINE__); }
上面的代码调用了笔者为了简化错误处理流程写的myerror()函数
,该函数定义如下
1 2 3 4 5 6 void myerror (const char *string1, const char *string2, int line) { printf ("\033[31mline:%d:file:%s\n%s:%s\033[0m\n" , line, string2, string1, strerror(errno)); exit (EXIT_FAILURE); }
笔者相信细致的读者一定会觉得!p
的设计时不必要的,因为可以通过预先执行path=NULL;
,然后在解析完成后判断if (path==NULL)
区分是否已经读取到路径,从而删去p
变量,但这样的做法是有缺陷的.
当用户输入路径时,path
指向某一个argv中的某一个指针指向的字符串.不需要执行free(path)
. 当用户不输入路径时,path
指向由getcwd
函数自动分配内存存储的当前路径.需要执行free(path)
. 为了区分是否需要执行free
,防止产生内存泄漏,笔者设置p
变量来完成对是否需要free
的检测.
递归打开目录 在需求中的7个参数中,-R
的实现无疑是最为困难的. 笔者通过设计一个以存储目标文件夹路径的字符串
为参数的函数,并通过递归调用该函数实现 -R
参数.
首先,笔者定义了几个宏:
1 2 3 #define StackPush(x) FileStack[++FileStackTop] = (x) #define StackTop FileStack[FileStackTop] #define StackPop free(FileStack[FileStackTop--])
下面是OpenADirectory
的大致流程:
warning TIP 笔者为了方便各位读者理解该函数运行的流程,在下面的代码中笔者省略了很多细节. 请读者们此时更多的关注该函数的「整体流程与思想」,而不是细枝末节.请不要担心、不要着急,后文中笔者将逐一说明被笔者省略的内容.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 typedef struct { struct stat FileStat ; struct dirent File_di ; } FileInfo; void OpenADirectory (const char *path) { char *oldpath = getcwd(NULL , 0 ); if (oldpath == NULL ) myerror("getcwd" , " " , __LINE__); DIR *CurrentDir = opendir(path); if (chdir(path) == -1 ) FileInfo **FileStack = (FileInfo **)malloc (sizeof (FileInfo *) * FileNumberMax); if (FileStack == NULL ) myerror("malloc" , " " , __LINE__); int FileStackTop = -1 ; struct dirent *CurrentFile ; while ((CurrentFile = readdir(CurrentDir)) != NULL ) { FileInfo *temp = (FileInfo *)malloc (sizeof (FileInfo)); if (temp == NULL ) myerror("malloc" , "" , __LINE__); temp->File_di = *CurrentFile; if (lstat(CurrentFile->d_name, &(temp->FileStat)) == -1 ) { printf ("\033[31mError:Line:%d: can't get stat of %s,%s\033[0m\n" , __LINE__, CurrentFile->d_name, strerror(errno)); free (temp); continue ; } if (FileStackTop < FileNumberMax) StackPush(temp); else myerror("\033[31mToo much File\033[0m\n" , " " , __LINE__); } if (errno) printf ("\033[31mline:%d:error:%s\033[0m\n" , __LINE__, strerror(errno));
该函数在运行的开始,首先保存当前的工作目录
的路径,然后打开作为参数的路径中指定的文件夹.
OpenADirectory()
新建了一个名叫FileStack
的指针,该指针指向指向FileInfo
类型的指针,换而言之,FileStack
是一个二级指针.由于使用malloc()
为其分配了sizeof(FileInfo *) * FileNumberMax
字节的空间,即FileNumberMax
个FileInfo *
类型所占的空间,那么此时,FileStack
就相当与一个「内含FileNumberMax
个指针元素的数组」.在此,笔者将该数组作为存储path
指定的文件夹内每个文件对应的FIleInfo
的堆栈
.
danger ERROR 可能会有读者在想,FileStack
不就是个指针数组嘛.直接使用FileInfo (*FileStack)[FileNumberMax];
便可以自动分配一个指针数组,何必使用malloc()
呢? 这不是笔者在使用二级指针故作高深,而是确有必要.FileInfo (*FileStack)[FileNumberMax];
语句定义的是自动变量,占用栈区
空间,而栈区
空间通常较小,在多层递归中容易出现栈溢出
的错误.而malloc()
分配的空间在堆区
上,堆区
远大于栈区
,这样才能保证程序的正常运行. 还有人可能会问,那能否这样调用malloc
呢?
FileInfo *array=malloc(sizeof(FileInfo) * FileNumberMax);
这样的做法,由FileInfo *
类型的指针数组改为FileInfo
数组,这样确实也不占用栈区
空间,也避免了二级指针带来的理解困难,但却存在着更为严重的内存浪费问题.在绝大多数文件夹中,文件数量远远少于FileNumberMax
,在相同的文件夹,如果使用指针数组的方案,浪费的空间仅为多个指针所占据的空间,而使用FileInfo
数组的方案却浪费了多个FileInfo
的空间,要知道FileInfo
所占的空间远大于FileInfo *
.所以使用FileInfo
的方案也不合理.
假设打开文件夹成功,则将程序的工作目录
切换至已打开的文件夹(也就是参数中指定的文件夹),这是因为笔者需要调用lstat
函数获取文件夹下每个文件的属性.lstat
以文件路径为参数.切换目录后,笔者便可以以文件名作为相对路径直接调用lstat
函数.如不切换目录则会找不到文件,当然也可以采取字符串拼接的做法,但这样做需要对文件夹下每个文件都执行一次字符串拼接,效率较低,而且字符串的长度不定,分配空间也易出现浪费或溢出.笔者直接切换目录避免了这些麻烦,也提升了效率.
在此后笔者使用循环遍历文件夹中的每个文件,获取每个文件的属性,并将每个文件对应的struct stat
和struct dirent
一同存储在的struct FileInfo
. 这样做的好处有很多,完成了这步后,输出文件信息所必要的所以内容已被集中在了一个struct FileInfo
结构体中,为后面对详细信息的输出和文件信息的排序排序给予了极大的便利.
1 qsort(FileStack, FileStackTop + 1 , sizeof (FileInfo *), cmp);
之后笔者使用qsort
函数对FileStack
进行排序处理,作为函数指针传递的cmp
函数要如何写,请容笔者在后文交代. 这这里,需要注意的是,真正被排序的是FileInfo *
,而每个FileInfo
元素都还存储在原来的位置.排序FileInfo *
,代替FileInfo
是一个十分有用的小技巧,能提升排序的效率.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 for (int i = FileStackTop; i >= 0 ; i--) { } while (FileStackTop >= 0 ) { if (Options_R && S_ISDIR(StackTop->FileStat.st_mode) && strcmp (StackTop->File_di.d_name, "." ) != 0 && strcmp (StackTop->File_di.d_name, ".." ) != 0 ) OpenADirectory(StackTop->File_di.d_name); StackPop; } if (chdir(oldpath) == -1 ) myerror("chdir" , path, __LINE__); free (oldpath); closedir(CurrentDir); free (FileStack); }
如上,笔者使用for循环
从堆的顶部遍历每个元素,并输出其中的所需的信息,这样便做到了排序输出.
其后,笔者再次从堆顶逐一访问每个元素,在启用了-R
参数时,检测堆栈
顶部的元素是否为文件夹,如果堆栈顶部为文件夹,且不是.
和..
则把堆栈顶部的元素对应的文件夹的路径作为参数递归调用OpenADirectory()
.完成后对先free堆栈顶的元素所指向的FileInfo
分配的空间并对堆栈执行pop操作. 最终释放堆栈空间及其他内存分配.
secondary SECONDARY
获取文件属性的函数还有stat
,为什么要选择lstat
而不是stat
呢?
原因很简单lstat
函数获取符号链接(Symbolic link)
本身的属性,而stat
获取被链接的文件的属性.
顺带一提,得益于FileStack
已经被qsort
函数完成了排序,所以接下来通过递归调用打开子文件夹也是有序的.这使得myls
程序运行期间所有文件的输出顺序是正确的.
至此,笔者终于完整的描述了OpenADirectory()
的运行的流程.
打开目录过程中的细节 首先需要关注的是错误处理.其中readdir()
函数的错误处理需要特别的关注.
tip
TIP
readir()
在读到目录结尾和出错时返回NULL
.仅在出错时设置errno
.
If the end of the directory stream is reached, NULL is returned and errno is not changed. If an error occurs, NULL is returned and errno is set appropriately. To distinguish end of stream from an error, set errno to zero before calling readdir() and then check the value of errno if NULL is returned.
readdir()
的返回值NULL
具有双重含义,只能使用检测errno
的值是否为0
来判断readdir()
是否执行正常. 在检测前需保证errno==0
.
调用opendir
时,易因权限不足等原因致使opendir
无法正常执行.在发生错误时,笔者并未选择直接退出程序,而是选择报错并跳过打开失败的文件夹. 记得要释放getcwd
中为了存储当前工作目录路径的字符串分配的内存空间,清除errno
的值.
1 2 3 4 5 6 7 8 DIR *CurrentDir = opendir(path); if (CurrentDir == NULL ){ printf ("\033[31mLine:%d:readfailed:%s/%s\t %s\033[0m\n" , __LINE__, oldpath, path, strerror(errno)); errno = 0 ; free (oldpath); return ; }
切换目录过程中,也可能因权限不足而导致切换失败,例如:用户缺少文件夹的x
权限时,便无法进入相应的文件夹.因此,这一步的错误检查同样必不可少. 同样不能忘记释放内存空间、清除errno
的值,额外的还需要关闭已打开的文件夹.
1 2 3 4 5 6 7 8 9 if (chdir(path) == -1 ){ printf ("\033[31mLine:%d:chdir:%s\t %s\033[0m\n" , __LINE__, path, strerror(errno)); errno = 0 ; free (oldpath); closedir(CurrentDir); return ; }
当然不必笔者多提的就是malloc()
的错误处理,相信各位读者一定知道该怎么做,笔者便不再赘述.
在OpenADirectory
的结尾,笔者将工作目录切换回去,方便递归中打开后续文件夹.
实现文件详细信息输出 格式化输出文件大小 这部分十分容易实现,只需要从相应的struct stat
中访问st_size
成员,并将其作为参数传递给相应的格式化输出函数即可.
1 2 3 4 5 6 7 8 9 10 11 void FormatBytes (off_t size) { char *array [] = {"B" , "KB" , "MB" , "GB" , "TB" , "PB" }; int n = 0 ; while (size >= 1024 ) { n++; size /= 1024 ; } printf ("%ld%s\t" , size, array [n]); }
格式化输出修改时间 1 2 3 4 5 6 7 void FormatTime (time_t mtime) { char string [20 ]; struct tm *timeinfo = gmtime(&mtime); strftime(string , 17 , "%b %e %R" , timeinfo); printf ("%s\t" , string ); }
文件的修改时间被存储在struct stat
的st_mtim.tv_sec
成员中.有必要多说一句的是,为了输出本地时间(UTC +8),还需要设置本地化的时间,笔者将这部分需求在main
函数中实现.
1 2 3 if (setlocale(LC_TIME, "" ) == NULL ) myerror("setlocale" , " " , __LINE__);
格式化输出文件所属的用户和用户组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void FormateUserAndGroup (uid_t userid, gid_t groupid) { struct passwd *owner = getpwuid(userid); if (owner == NULL ) { printf ("%s\n" , getcwd(NULL , 0 )); printf ("uid:%u\n" , userid); } struct group *group = getgrgid(groupid); if (group == NULL ) myerror("getgruid" , " " , __LINE__); printf ("%s\t%s\t" , owner->pw_name, group->gr_name); }
函数以struct stat
中的st_uid
成员和st_gid
成员为实际参数,分别通过uid
和gid
调用getpwuid()
函数和getgrgid()
函数,获取相关结构体,并输出其中的用户名和用户组名称.
tip
TIP
getpwuid()
函数 由 pwd.h
提供getgrgid()
函数 由 grp.h
提供格式化输出文件权限 文件权限的格式化输出最为简单.只是机械的判断并输出即可.
考录到存在SUID
、 SGID
、SBIT
这些特殊权限的存在,笔者并未尝试使用位移运算符来复用部分代码,使得这部分代码显得很长.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 void prauthority (mode_t mode) { if (S_ISREG(mode)) putchar ('-' ); else if (S_ISDIR(mode)) putchar ('d' ); else if (S_ISLNK(mode)) putchar ('l' ); else if (S_ISFIFO(mode)) putchar ('f' ); else if (S_ISBLK(mode)) putchar ('b' ); else if (S_ISCHR(mode)) putchar ('c' ); else if (S_ISSOCK(mode)) putchar ('s' ); if (S_IRUSR & mode) putchar ('r' ); else putchar ('-' ); if (S_IWUSR & mode) putchar ('w' ); else putchar ('-' ); if (S_IXUSR & mode) { if (S_ISUID & mode) putchar ('s' ); else putchar ('x' ); } else putchar ('-' ); if (S_IRGRP & mode) putchar ('r' ); else putchar ('-' ); if (S_IWGRP & mode) putchar ('w' ); else putchar ('-' ); if (S_IXGRP & mode) { if (S_ISGID & mode) putchar ('s' ); else putchar ('x' ); } else putchar ('-' ); if (S_IROTH & mode) putchar ('r' ); else putchar ('-' ); if (S_IWOTH & mode) putchar ('w' ); else putchar ('-' ); if (S_IXOTH & mode) { if (S_ISVTX & mode) putchar ('t' ); else putchar ('x' ); } else putchar ('-' ); putchar ('\t' ); }
格式化输出文件的i-node
编号和以块为单位文件的大小 直接从struct stat
中读取相关信息并输出即可.
1 2 3 4 if (Options_i) printf ("%-10lu\t" , FileStack[i]->FileStat.st_ino); if (Options_s) printf ("%-8ld\t" , FileStack[i]->FileStat.st_blksize);
根据文件的类型和权限输出不同颜色的文件名 根据struct dirent
中的char d_name[256]
输出即可.无非是根据不同类型输出不同的颜色而已.
1 2 3 4 5 6 7 8 9 10 11 if (S_ISREG(FileStack[i]->FileStat.st_mode) && ((S_IXUSR & FileStack[i]->FileStat.st_mode) || (S_IXGRP & FileStack[i]->FileStat.st_mode) || (S_IXOTH & FileStack[i]->FileStat.st_mode))) printf ("\033[32m%s\033[0m\n" , FileStack[i]->File_di.d_name); else if (S_ISREG(FileStack[i]->FileStat.st_mode)) printf ("%s\n" , FileStack[i]->File_di.d_name); else if (S_ISDIR(FileStack[i]->FileStat.st_mode)) printf ("\033[34m%s\033[0m\n" , FileStack[i]->File_di.d_name); else if (S_ISLNK(FileStack[i]->FileStat.st_mode)) printf ("\033[31m%s\033[0m\n" , FileStack[i]->File_di.d_name);
实现排序输出 在用OpenADirectory()
中笔者调用了qsort()
.其中,qsort()
将cmp
进行隐式类型转换
为函数指针
,完成了对FileStack
这个指针数组的排序.
1 qsort(FileStack, FileStackTop + 1 , sizeof (FileInfo *), cmp);
在此,笔者来实现cmp()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int cmp (const void *a, const void *b) { const FileInfo *A = *(FileInfo **)a; const FileInfo *B = *(FileInfo **)b; int i; if (Options_t) { time_t t = B->FileStat.st_mtim.tv_sec - A->FileStat.st_mtim.tv_sec; if (t > 0 ) i = -1 ; else if (t == 0 ) i = 0 ; else i = 1 ; } else i = strcmp (B->File_di.d_name, A->File_di.d_name); if (Options_r) i = -i; return i; }
其中,根据用户是否输入了参数-r
决定是否进行逆序排列,根据用户是否输入了参数-t
决定排序的方式.
至此,myls
终于完成了,完整的代码见本文末的附录.
反思 动态分配 FileStack
在上面的实现中,笔者粗暴的使用了一个FileNumbertMax
作为FileStack
中指针的数量,但这并非最优解.
大多数文件夹中,文件数量远远小于 FileNumbertMax
意味着浪费了很多空间.
更合理的做法是,为FileStack
设置一个大于「大多数文件夹中存放文件数量」的初始值,在遇到FileStack
满后,使用realloc()
扩充FileStack
的空间即可.
当然,这不可避免的是在一定程度上减缓myls
的运行速度,这个运行速度与消耗空间的平衡需要读者自行考量.
获取文件属性 warning
WARNING
该部分内容含较多的笔者的未验证个人观点,不保证正确.欢迎读者们指出错误.
OpenADirectory()
中,使用readdir()
读取目录的记录项,获取的struct dirent
中包含文件名与i-node
编号.
然后,使用lstat()
根据文件路径(笔者使用文件名作为相对路径)读取文件的属性.
在使用i-node
的文件系统中,文件的属性存储在i-node
中,lstat()
可能的 读取文件属性的方式为:
打开并遍历文件所在目录 读取目录的记录项,直到找到指定的文件所对应的记录项 从文件所对应的记录项中得到文件的i-node
编号 根据文件的i-node
编号找到对应的i-node
,完成读取文件的属性 读者们一定能发现根据获取的struct dirent
已经可以读取到i-node
编号了,但使用lstat()
函数却还要重复上面的1-3步.
笔者未能想到如何更好的读取文件的属性,欢迎对此有了解的读者告诉笔者.
点击三角形展开附录
附录--完整源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <grp.h> #include <locale.h> #include <pwd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <time.h> #include <unistd.h> #define StackPush(x) FileStack[++FileStackTop] = (x) #define StackTop FileStack[FileStackTop] #define StackPop free(FileStack[FileStackTop--]) #define FileNumberMax 40960 _Bool Options_a;_Bool Options_i; _Bool Options_l;_Bool Options_r; _Bool Options_R;_Bool Options_s; _Bool Options_t; typedef struct { struct stat FileStat ; struct dirent File_di ; } FileInfo; void prauthority (mode_t mode) ;void myerror (const char *string , const char *filename, int line) ;void OpenADirectory (const char *path) ;int cmp (const void *a, const void *b) ;void FormateUserAndGroup (uid_t userid, gid_t groupid) ;void FormatTime (time_t mtime) ;void FormatBytes (off_t size) ;int main (int argc, char **argv) { if (setlocale(LC_TIME, "" ) == NULL ) myerror("setlocale" , " " , __LINE__); signal(SIGTTIN, SIG_IGN); _Bool p = 0 ; char *path; for (int i = 1 ; i < argc; i++) { if (*argv[i] == '-' ) { for (unsigned int n = 1 ; n < strlen (argv[i]); n++) switch (argv[i][n]) { case 'a' : Options_a = 1 ; break ; case 'i' : Options_i = 1 ; break ; case 'l' : Options_l = 1 ; break ; case 'r' : Options_r = 1 ; break ; case 'R' : Options_R = 1 ; break ; case 's' : Options_s = 1 ; break ; case 't' : Options_t = 1 ; break ; default : printf ("%s error:Unknow options: %s\n" , __FILE__, argv[i]); exit (EXIT_FAILURE); break ; } } else { p = 1 ; path = argv[i]; } } if (!p) { path = getcwd(NULL , 0 ); if (path == NULL ) myerror("getcwd" , " " , __LINE__); } OpenADirectory(path); if (!p) free (path); } int cmp (const void *a, const void *b) { const FileInfo *A = *(FileInfo **)a; const FileInfo *B = *(FileInfo **)b; int i; if (Options_t) { time_t t = B->FileStat.st_mtim.tv_sec - A->FileStat.st_mtim.tv_sec; if (t > 0 ) i = -1 ; else if (t == 0 ) i = 0 ; else i = 1 ; } else i = strcmp (B->File_di.d_name, A->File_di.d_name); if (Options_r) i = -i; return i; } void OpenADirectory (const char *path) { char *oldpath = getcwd(NULL , 0 ); if (oldpath == NULL ) myerror("getcwd" , " " , __LINE__); DIR *CurrentDir = opendir(path); if (CurrentDir == NULL ) { printf ("\033[31mLine:%d:readfailed:%s/%s\t %s\033[0m\n" , __LINE__, oldpath, path, strerror(errno)); errno = 0 ; free (oldpath); return ; } if (chdir(path) == -1 ) { printf ("\033[31mLine:%d:chdir:%s\t %s\033[0m\n" , __LINE__, path, strerror(errno)); errno = 0 ; free (oldpath); closedir(CurrentDir); return ; } FileInfo **FileStack = (FileInfo **)malloc (sizeof (FileInfo *) * FileNumberMax); if (FileStack == NULL ) myerror("malloc" , " " , __LINE__); int FileStackTop = -1 ; if (Options_R) { char *NewPath = getcwd(NULL , 0 ); if (NewPath == NULL ) myerror("getcwd" , " " , __LINE__); printf ("%s:\n" , NewPath); free (NewPath); } struct dirent *CurrentFile ; while ((CurrentFile = readdir(CurrentDir)) != NULL ) { FileInfo *temp = (FileInfo *)malloc (sizeof (FileInfo)); if (temp == NULL ) myerror("malloc" , "" , __LINE__); temp->File_di = *CurrentFile; if (lstat(CurrentFile->d_name, &(temp->FileStat)) == -1 ) { printf ("\033[31mError:Line:%d: can't get stat of %s,%s\033[0m\n" , __LINE__, CurrentFile->d_name, strerror(errno)); free (temp); continue ; } if (FileStackTop < FileNumberMax) StackPush(temp); else myerror("\033[31mToo much File\033[0m\n" , " " , __LINE__); } if (errno) printf ("\033[31mline:%d:error:%s\033[0m\n" , __LINE__, strerror(errno)); qsort(FileStack, FileStackTop + 1 , sizeof (FileInfo *), cmp); for (int i = FileStackTop; i >= 0 ; i--) { if (Options_a == 0 && *FileStack[i]->File_di.d_name == '.' ) continue ; if (Options_l) { if (Options_i) printf ("%-10lu\t" , FileStack[i]->FileStat.st_ino); if (Options_s) printf ("%-8ld\t" , FileStack[i]->FileStat.st_blksize); prauthority(FileStack[i]->FileStat.st_mode); FormateUserAndGroup(FileStack[i]->FileStat.st_uid, FileStack[i]->FileStat.st_gid); FormatBytes(FileStack[i]->FileStat.st_size); FormatTime(FileStack[i]->FileStat.st_mtim.tv_sec); } if (S_ISREG(FileStack[i]->FileStat.st_mode) && ((S_IXUSR & FileStack[i]->FileStat.st_mode) || (S_IXGRP & FileStack[i]->FileStat.st_mode) || (S_IXOTH & FileStack[i]->FileStat.st_mode))) printf ("\033[32m%s\033[0m\n" , FileStack[i]->File_di.d_name); else if (S_ISREG(FileStack[i]->FileStat.st_mode)) printf ("%s\n" , FileStack[i]->File_di.d_name); else if (S_ISDIR(FileStack[i]->FileStat.st_mode)) printf ("\033[34m%s\033[0m\n" , FileStack[i]->File_di.d_name); else if (S_ISLNK(FileStack[i]->FileStat.st_mode)) printf ("\033[31m%s\033[0m\n" , FileStack[i]->File_di.d_name); } while (FileStackTop >= 0 ) { if (Options_R && S_ISDIR(StackTop->FileStat.st_mode) && strcmp (StackTop->File_di.d_name, "." ) != 0 && strcmp (StackTop->File_di.d_name, ".." ) != 0 ) OpenADirectory(StackTop->File_di.d_name); StackPop; } if (chdir(oldpath) == -1 ) myerror("chdir" , path, __LINE__); free (oldpath); closedir(CurrentDir); free (FileStack); } void myerror (const char *string1, const char *string2, int line) { printf ("\033[31mline:%d:file:%s\n%s:%s\033[0m\n" , line, string2, string1, strerror(errno)); exit (EXIT_FAILURE); } void FormatBytes (off_t size) { char *array [] = {"B" , "KB" , "MB" , "GB" , "TB" , "PB" }; int n = 0 ; while (size >= 1024 ) { n++; size /= 1024 ; } printf ("%ld%s\t" , size, array [n]); } void FormatTime (time_t mtime) { char string [20 ]; struct tm *timeinfo = gmtime(&mtime); strftime(string , 17 , "%b %e %R" , timeinfo); printf ("%s\t" , string ); } void FormateUserAndGroup (uid_t userid, gid_t groupid) { struct passwd *owner = getpwuid(userid); if (owner == NULL ) { printf ("%s\n" , getcwd(NULL , 0 )); printf ("uid:%u\n" , userid); } struct group *group = getgrgid(groupid); if (group == NULL ) myerror("getgruid" , " " , __LINE__); printf ("%s\t%s\t" , owner->pw_name, group->gr_name); } void prauthority (mode_t mode) { if (S_ISREG(mode)) putchar ('-' ); else if (S_ISDIR(mode)) putchar ('d' ); else if (S_ISLNK(mode)) putchar ('l' ); else if (S_ISFIFO(mode)) putchar ('f' ); else if (S_ISBLK(mode)) putchar ('b' ); else if (S_ISCHR(mode)) putchar ('c' ); else if (S_ISSOCK(mode)) putchar ('s' ); if (S_IRUSR & mode) putchar ('r' ); else putchar ('-' ); if (S_IWUSR & mode) putchar ('w' ); else putchar ('-' ); if (S_IXUSR & mode) { if (S_ISUID & mode) putchar ('s' ); else putchar ('x' ); } else putchar ('-' ); if (S_IRGRP & mode) putchar ('r' ); else putchar ('-' ); if (S_IWGRP & mode) putchar ('w' ); else putchar ('-' ); if (S_IXGRP & mode) { if (S_ISGID & mode) putchar ('s' ); else putchar ('x' ); } else putchar ('-' ); if (S_IROTH & mode) putchar ('r' ); else putchar ('-' ); if (S_IWOTH & mode) putchar ('w' ); else putchar ('-' ); if (S_IXOTH & mode) { if (S_ISVTX & mode) putchar ('t' ); else putchar ('x' ); } else putchar ('-' ); putchar ('\t' ); }
参考资料 1 . 童永清.Linux C 编程实战[M].第1版.北京:人民邮电出版社 ↩ 2 . W.RichardStevens.Stephen.UNIX环境高级编程[M].第3版.戚正伟,译.北京:人民邮电出版社 ↩ 3 . Linux Programmer’s Manual ↩ 4 . General Commands Manual ↩ 5 . 鸟哥.鸟哥的Linux私房菜[M].第四版.北京:人民邮电出版社 ↩