Unix ls命令的实现
2012-11-21 13:13 wid 阅读(7018) 评论(6) 编辑 收藏 举报说明一下: 这篇博文是我的一个好友借用我的账号发的, 我想让他注册个博客但是他觉得平时写博也不多, 所以就发到我博客里来了。
---------------
这是ls命令的实现,写的比较仓促,所以代码不是非常精简,望见谅。程序实现的参数有1ACFLHRacdfgilnoqrstu,大部分的参数和标准ls的功能一样,大家可参阅联机帮助来获取帮助。
程序的-n选项和标准ls命令不同,它会关闭-g,-o选项,程序所实现的分栏功能并不高明,没有标准ls命令要好。程序所使用的fts系列的函数大家可参阅联机帮助,man fts_open便可获得。另外应博主的要求写了些必要的注释,不过由于程序的水准并不高,实际上注释所起的用处不大。另外-U选项有一句是废话,相应的注释指出了。如果各位有谁对代码有兴趣而又看不大懂的可以找本人。最后程序借鉴了v6的实现。代码如下,大家可参阅。
/* ls -[1ACFLHRacdfgilnoqrstu] [file ...] */ /* 作者: 莫尘。 源码参考了Unix v6的实现 */ #include <pwd.h> #include <grp.h> #include <fts.h> #include <err.h> #include <time.h> #include <errno.h> #include <ctype.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <sys/ioctl.h> #define IS_NOPRINT(b) ((b)->fts_number == 1) /* 1 表示为not print */ struct buf{ int num; int total; int bcfile; int maxlen; int usrlen; int grplen; int linklen; int sizelen; int inodelen; int blocklen; FTSENT *list; /* FTS, FTSENT结构和fts函数具体参考/usr/include/fts.h,联机帮助会有更多的资料以供参考(man fts_open可以获得联机帮助的信息 */ }b; typedef struct name{ union{ int uid; char *usr; }usr; union{ int gid; char *grp; }grp; char date[0]; /* 为了节省空间而做出的策略,date不占用任何空间 */ }NAME; int col; int rflg = 1; int putout = 0; int seeusr = 1, seegrp = 1; int seeuid, seegid, singleflg, fts_options; int Aflg, Cflg, Fflg, Lflg, Rflg, Sflg, Uflg; int cflg, dflg, fflg, iflg, lflg, oflg, pflg, qflg, sflg, tflg, uflg; static char *com_name; void printone(void); void printcol(void); void printlong(void); void q_copy(char *a, char *b); void do_ls(int argc, char *argv[]); void do_print(FTSENT *fp, FTSENT *list); int cmparg(const FTSENT **a, const FTSENT **b); int cmpname(const FTSENT *a, const FTSENT *b); int cmptime(const FTSENT *a, const FTSENT *b); int cmpsize(const FTSENT *a, const FTSENT *b); static void (*printfcn)(void); static void printtime(time_t t); static void printlink(FTSENT *p); static void modetostr(mode_t mode, char *buf); static int printtype(mode_t mode); static int printname(FTSENT *b, int flg); static int (*sortfcn)(const FTSENT *a, const FTSENT *b); int main(int argc, char *argv[]) { char *p; register int c; struct winsize wbuf; static char dot[] = ".", *dotav[] = { dot, (char *)NULL }; com_name = *argv; if(isatty(STDOUT_FILENO)){ /* 得出终端的宽度,默认宽度为80,如果获取失败,将激活-1选项 */ if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &wbuf) == -1 || wbuf.ws_col == 0){ if(p = getenv("COLUMNS")) col = atoi(p); else col = 80; } else col = wbuf.ws_col; Cflg = qflg = 1; } else singleflg = 1; sortfcn = cmpname; printfcn = printcol; fts_options = FTS_PHYSICAL; while(--argc > 0 && (*++argv)[0] == '-'){ while(c = *++argv[0]) switch(c){ case '1': Cflg = 0; singleflg = 1; printfcn = printone; break; case 'A': Aflg = 1; break; case 'C': lflg = 0; Cflg = 1; printfcn = printcol; break; case 'F': pflg = 0; Fflg = 1; break; case 'H': fts_options |= FTS_COMFOLLOW; break; case 'L': fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL; break; case 'R': Rflg = 1; dflg = 0; break; case 'S': Sflg = 1; sortfcn = cmpsize; break; case 'U': Uflg = 1; sortfcn = (int(*)(const FTSENT **, const FTSENT **))NULL; /* 这一步实则不必要,编写的理由是个人的感情因素 */ break; case 'a': Aflg = 1; fts_options |= FTS_SEEDOT; break; case 'c': /* -u,-c,-t和标准ls命令的表现方式一样,详见联机帮助,可ls --help或man ls */ uflg = 0; cflg = 1; if(lflg == 0) sortfcn = cmptime; break; case 'd': dflg = 1; Rflg = 0; break; case 'f': /* -f和标准ls一样,联机帮助有其说明,注意此选项的输出是没有颜色的 */ fts_options |= FTS_SEEDOT; fflg = Aflg = Uflg = 1; sortfcn = (int(*)(const FTSENT **, const FTSENT **))NULL; lflg = sflg = tflg = 0; break; case 'g': lflg = 1; seeusr = 0; printfcn = printlong; break; case 'i': iflg = 1; break; case 'l': Cflg = 0; lflg = 1; if(tflg == 0) sortfcn = cmpname; printfcn = printlong; break; case 'n': lflg = 1; seeuid = seegid = 1; break; case 'o': lflg = 1; seegrp = 0; printfcn = printlong; break; case 'p': /* -p和-F的表现和标准ls命令一样,具体参阅联机帮助 */ Fflg = 0; pflg = 1; break; case 'q': qflg = 1; break; case 'r': rflg = -1; break; case 's': sflg = 1; break; case 't': tflg = 1; sortfcn = cmptime; break; case 'u': cflg = 0; uflg = 1; if(lflg == 0) sortfcn = cmptime; break; default: fprintf(stderr, "Usage: %s -[1ACFLHRTacdfgilnoqrstu] [file ...]\n", com_name); exit(1); } } if(lflg) /* -l命令会导致-1选项的失效 */ printfcn = printlong; if(argc) do_ls(argc, argv); else do_ls(1, dotav); return 0; } void do_ls(int argc, char *argv[]) { FTS *ftsp; register FTSENT *p, *tmp; ftsp = fts_open(argv, fts_options, Uflg ? NULL : cmparg); /* 把所有文件读入一棵树中, 详见联机帮助 */ if(ftsp == (FTS *)NULL){ fprintf(stderr, "%s: fts_open error\n", com_name); exit(1); } do_print(NULL, fts_children(ftsp, 0)); if(dflg) return; while(p = fts_read(ftsp)){ switch(p->fts_info){ case FTS_DC: warnx("%s: directory causes a cycle", p->fts_name); /* err, warn, warnx等函数具体可查看err.h头文件和man warn来了解其功能 */ break; case FTS_ERR: case FTS_DNR: errno = p->fts_errno; /* fts函数不会设置errno,函数具体功能参阅联机帮助 */ warn("%s", p->fts_name); break; case FTS_D: if(p->fts_level != 0 && p->fts_name == '.' && Aflg == 0) break; if(putout) printf("\n%s:\n", p->fts_path); else if(argc > 1){ printf("%s:\n", p->fts_path); putout = 1; } tmp = fts_children(ftsp, 0); do_print(p, tmp); if(Rflg == 0 && tmp) fts_set(ftsp, p, FTS_SKIP); break; default: break; } } fts_close(ftsp); /* 分配的内存会自动释放 */ } void do_print(FTSENT *fp, FTSENT *list) { NAME *np; char buf[20]; struct group *gp; struct passwd *pw; register FTSENT *p; register struct stat *q; int num, total, needstat; int n, m, in, im, sn, sm; int un, um, gn, gm, ln, lm, bn, bm, bcfile; if(list == NULL) return; num = total = bcfile = 0; n = m = in = im = sn = sm = 0; un = um = gn = gm = ln = lm = bn = bm = 0; needstat = iflg | sflg | lflg; for(p = list; p; p = p->fts_link){ if(p->fts_info == FTS_ERR || p->fts_info == FTS_NS){ errno = p->fts_errno; warn("%s", p->fts_name); p->fts_number = 1; continue; } if(fp == NULL){ /* 若为命令行参数 */ if(p->fts_info == FTS_D && dflg == 0){ p->fts_number = 1; continue; } } else{ if(p->fts_name[0] == '.' && Aflg == 0){ p->fts_number = 1; continue; } } if(qflg) /* 以?代替不可打印的字符 */ q_copy(p->fts_name, p->fts_name); if(m < (n = p->fts_namelen)) m = n; if(needstat){ q = p->fts_statp; if(iflg && im < (in = q->st_ino)) im = in; if(sflg && bm < (bn = q->st_blocks)) bm = bn; if(sm < (sn = q->st_size)) sm = sn; if(lm < (ln = q->st_nlink)) lm = ln; total += q->st_blocks; if(lflg){ if(seeuid){ /* 与标准ls不同,本程序,-n选项会使-g -o选项失效 */ if(um < (un = q->st_uid)) um = un; } else if(seeusr){ pw = getpwuid(q->st_uid); if(pw && um < (un = strlen(pw->pw_name))) um = un; } else um = 0; if(seegid){ if(gm < (gn = q->st_gid)) gm = gn; } else if(seegrp){ gp = getgrgid(q->st_gid); if(gp && gm < (gn = strlen(gp->gr_name))) gm = gn; } else gm = 0; if(seeuid && seegid){ np = malloc(sizeof(NAME)); np->usr.uid = un; np->grp.gid = gn; } else{ if((np = malloc(sizeof(NAME) + un + gn + !(!un) + !(!gn))) == NULL) /* !(!un) + !(!gn)用来判断是否有设置seeusr和seegrp */ err(1, NULL); if(seeusr){ np->usr.usr = &np->date[0]; strcpy(np->usr.usr, pw->pw_name); } if(seegrp){ np->grp.grp = &np->date[un + !(!un)]; strcpy(np->grp.grp, gp->gr_name); } } if(S_ISCHR(q->st_mode) || S_ISBLK(q->st_mode)) bcfile = 1; p->fts_pointer = np; } } ++num; } if(num == 0) return; b.list = list; b.num = num; b.maxlen = m; if(needstat){ b.total = total; b.bcfile = bcfile; snprintf(buf, sizeof(buf), "%u", lm); b.linklen = strlen(buf); snprintf(buf, sizeof(buf), "%u", sm); b.sizelen = strlen(buf); snprintf(buf, sizeof(buf), "%u", im); b.inodelen = strlen(buf); snprintf(buf, sizeof(buf), "%u", bm); b.blocklen = strlen(buf); if(seeuid && seegid){ snprintf(buf, sizeof(buf), "%u", um); b.usrlen = strlen(buf); snprintf(buf, sizeof(buf), "%u", gm); b.grplen = strlen(buf); } else{ b.usrlen = um; b.grplen = gm; } } printfcn(); putout = 1; if(lflg) for(p = list; p; p = p->fts_link) free(p->fts_pointer); } int cmparg(const FTSENT **a, const FTSENT **b) { FTSENT *ap, *bp; ap = *(FTSENT **)a; if(ap->fts_info == FTS_ERR) return 0; bp = *(FTSENT **)b; if(bp->fts_info == FTS_ERR) return 0; if(ap->fts_info == FTS_NS || bp->fts_info == FTS_NS) return cmpname(ap, bp); if(ap->fts_info == bp->fts_info) sortfcn(ap, bp); if(ap->fts_level == 0){ /* 命令行参数的目录默认大于文件, 详见标准ls命令的说明文档 */ if(ap->fts_info == FTS_D){ if(bp->fts_info != FTS_D) return 1; } else if(bp->fts_info == FTS_D) return -1; } return sortfcn(ap, bp); } int cmpname(const FTSENT *a, const FTSENT *b) { return rflg * strcmp(a->fts_name, b->fts_name); } int cmpsize(const FTSENT *a, const FTSENT *b) { return rflg * (b->fts_statp->st_size - a->fts_statp->st_size); } int cmptime(const FTSENT *a, const FTSENT *b) { if(cflg) return rflg * (b->fts_statp->st_ctime - a->fts_statp->st_ctime); else if(uflg) return rflg * (b->fts_statp->st_atime - a->fts_statp->st_atime); else return rflg * (b->fts_statp->st_mtime - a->fts_statp->st_mtime); } void q_copy(char *a, char *b) { register char *p, *q; for(p = a, q = b; *p; p++, q++){ if(isprint(*p)) *q = *p; else *q = '?'; } } void printone(void) { register FTSENT *p; for(p = b.list; p; p = p->fts_link){ if(IS_NOPRINT(p)) continue; printname(p, 1); putchar('\n'); } } void printcol(void) { static int lastnum = -1; static FTSENT **array; register FTSENT *p; int i, j, k, tm, cn, chn, num, len, nrow, ncol, diff, left, tmcol; if(b.num > lastnum){ lastnum = b.num; if((array = realloc(array, sizeof(FTSENT *) * b.num)) == NULL){ err(1, NULL); printone(); } } num = 0; for(p = b.list; p; p = p->fts_link) if(p->fts_number != 1) array[num++] = p; i = b.maxlen; if(iflg) i += b.inodelen + 1; if(sflg) i += b.blocklen + 1; if(pflg || Fflg) i += 1; len = (i + 8) & ~7; /* 规整到下一个8的倍数 */ if(col < 2 * len){ printone(); return; } ncol = col / len; nrow = num / ncol; if(num % ncol) nrow++; tmcol = num / nrow; left = num - (tmcol * nrow); diff = left / nrow; diff += 1; /* 每行需另外输出的的个数 */ if(sflg) printf("total = %u\n", b.total); tm = 0; for(i = 0; i < nrow; ++i){ k = len; /* 输出应该停止处 */ for(j = chn = 0; j < ncol; ++j){ if(tm >= num) break; if(j >= tmcol){ /* 用来调整使每行的个数尽可能一样 */ if(j < tmcol + diff && left) --left; else break; } chn += printname(array[tm++], 1); while((cn = (chn + 8 & ~7)) <= k){ putchar('\t'); chn = cn; } k += len; } putchar('\n'); } } void printlong(void) { NAME *np; int mode; char buf[10]; register FTSENT *p; register struct stat *sp; if(b.list->fts_level != 0) printf("total %u\n", b.total); for(p = b.list; p; p = p->fts_link){ if(IS_NOPRINT(p)) continue; sp = p->fts_statp; mode = sp->st_mode; if(iflg) printf("%*lu ", b.inodelen, sp->st_ino); if(sflg) printf("%*lu ", b.blocklen, sp->st_blocks); modetostr(mode, buf); np = p->fts_pointer; printf("%s ", buf); printf("%*u ", b.linklen, sp->st_nlink); if(seeuid) printf("%*u ", b.usrlen, np->usr.uid); else if(seeusr) printf("%*s ", b.usrlen, np->usr.usr); if(seegid) printf("%*u ", b.grplen, np->grp.gid); else if(seegrp) printf("%*s ", b.grplen, np->grp.grp); if(S_ISCHR(mode) || S_ISBLK(mode)) printf("%3d,%3d ", major(sp->st_rdev), minor(sp->st_rdev)); else if(b.bcfile) printf("%*s%*lu ", 8 - b.sizelen, "", b.sizelen, sp->st_size); else printf("%*lu ", b.sizelen, sp->st_size); if(cflg) printtime(sp->st_ctime); else if(uflg) printtime(sp->st_atime); else printtime(sp->st_mtime); printname(p, 0); if(Fflg || pflg) printtype(mode); if(S_ISLNK(mode)) printlink(p); putchar('\n'); } } static void printlink(FTSENT *p) { int lnklen; char name[256], path[256]; if(p->fts_level == 0) snprintf(name, sizeof(name), "%s", p->fts_name); else snprintf(name, sizeof(name), "%s/%s", p->fts_parent->fts_accpath, p->fts_name); if((lnklen = readlink(name, path, sizeof(path) - 1)) == -1){ /* readlink详见联机帮助 */ fprintf(stderr, "\n%s: %s: %s\n", com_name, name, strerror(errno)); return; } path[lnklen] = '\0'; printf(" -> %s", path); } static void printtime(time_t t) { char *s; time_t year, tm; time(&tm); year = tm - 60 * 30 * 24 * 60 * 60; s = ctime(&t); if(t < year) printf("%.7s %.4s ", s + 4, s + 20); else printf("%.12s ", s + 4); } static void modetostr(mode_t mode, char *buf) { strcpy(buf, "----------"); if(S_ISDIR(mode)) buf[0] = 'd'; if(S_ISCHR(mode)) buf[0] = 'c'; if(S_ISBLK(mode)) buf[0] = 'b'; if(S_ISLNK(mode)) buf[0] = 'l'; if(S_ISSOCK(mode)) buf[0] = 's'; if(S_ISFIFO(mode)) buf[0] = 'p'; if(mode & S_IRUSR) buf[1] = 'r'; if(mode & S_IWUSR) buf[2] = 'w'; if(mode & S_IXUSR) buf[3] = 'x'; if(mode & S_ISUID) buf[3] = 's'; if(mode & S_IRGRP) buf[4] = 'r'; if(mode & S_IWGRP) buf[5] = 'w'; if(mode & S_IXGRP) buf[6] = 'x'; if(mode & S_ISGID) buf[6] = 'x'; if(mode & S_IROTH) buf[7] = 'r'; if(mode & S_IWOTH) buf[8] = 'w'; if(mode & S_IXOTH) buf[9] = 'x'; if(mode & S_ISVTX) buf[9] = 't'; } static int printname(FTSENT *p, int flg) { int np, mode; struct stat *sp; np = 0; sp = p->fts_statp; if(flg){ if(iflg) np += printf("%*lu ", b.inodelen, sp->st_ino); if(sflg) np += printf("%*lu ", b.blocklen, sp->st_blocks); } mode = sp->st_mode; if(fflg) np += printf("%s", p->fts_name); else{ /* 注意printf的返回值 */ if(mode & S_ISUID){ printf("\033[37;41m"); np += printf("%s", p->fts_name); printf("\033[0m"); } else if(mode & S_ISGID){ printf("\033[30;43m"); np += printf("%s", p->fts_name); printf("\033[0m"); } else if(mode & S_ISVTX){ printf("\033[37;44m"); np += printf("%s", p->fts_name); printf("\033[0m"); } else{ switch(mode & S_IFMT){ case S_IFDIR: printf("\033[01;34m"); np += printf("%s", p->fts_name); printf("\033[0m"); break; case S_IFIFO: printf("\033[33m"); np += printf("%s", p->fts_name); printf("\033[0m"); break; case S_IFSOCK: printf("\033[01;35m"); np += printf("%s", p->fts_name); printf("\033[0m"); break; case S_IFLNK: printf("\033[01;36m"); np += printf("%s", p->fts_name); printf("\033[0m"); break; case S_IFCHR: case S_IFBLK: printf("\033[01;33m"); np += printf("%s", p->fts_name); printf("\033[0m"); break; default: if(mode & S_IXUSR || mode & S_IXGRP || mode & S_IXOTH){ printf("\033[01;32m"); np += printf("%s", p->fts_name); printf("\033[0m"); } else np += printf("%s", p->fts_name); break; } } } if(Fflg || pflg) np += printtype(mode); return np; } static int printtype(mode_t mode) { switch(mode & S_IFMT){ case S_IFDIR: putchar('/'); return 1; case S_IFLNK: if(Fflg == 0) break; putchar('@'); return 1; case S_IFSOCK: if(Fflg == 0) break; putchar('='); return 1; default: if(Fflg == 0) break; if(mode & (S_IXUSR | S_IXGRP | S_IXOTH)){ putchar('*'); return 1; } break; } return 0; }