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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM