Unix ls命令的实现

说明一下: 这篇博文是我的一个好友借用我的账号发的, 我想让他注册个博客但是他觉得平时写博也不多, 所以就发到我博客里来了。

---------------

 

这是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;

}

 

你可能感兴趣的:(unix)