URI: 
       added ls - 9base - revived minimalist port of Plan 9 userland to Unix
  HTML git clone git://git.suckless.org/9base
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 6ccdc8cffd953f6dae2692e687d19ac6e58a7e2b
   DIR parent b7abc7dd08640a98b0af92b05bb0a57e614f3160
  HTML Author: Anselm R. Garbe <garbeam@wmii.de>
       Date:   Tue, 31 Jan 2006 20:37:59 +0200
       
       added ls
       
       Diffstat:
         M Makefile                            |       4 ++--
         M config.mk                           |       2 +-
         A ls/Makefile                         |       7 +++++++
         A ls/ls.1                             |     172 ++++++++++++++++++++++++++++++
         A ls/ls.c                             |     309 +++++++++++++++++++++++++++++++
       
       5 files changed, 491 insertions(+), 3 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       @@ -3,8 +3,8 @@
        
        include config.mk
        
       -SUBDIRS  = lib9 yacc awk basename bc dc cat cleanname date echo grep mk \
       -                   rc read sed seq sleep sort tee test touch tr uniq
       +SUBDIRS  = lib9 yacc awk basename bc dc cat cleanname date echo grep ls \
       +                   mk rc read sed seq sleep sort tee test touch tr uniq
        
        all:
                @echo 9base build options:
   DIR diff --git a/config.mk b/config.mk
       @@ -4,7 +4,7 @@
        PREFIX      = /usr/local/9
        MANPREFIX   = ${PREFIX}/share/man
        
       -VERSION     = 2
       +VERSION     = 20060129
        
        # Linux/BSD
        CFLAGS      = -Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -c -I. -DPREFIX="\"${PREFIX}\""
   DIR diff --git a/ls/Makefile b/ls/Makefile
       @@ -0,0 +1,7 @@
       +# ls - ls unix port from plan9
       +#
       +# Depends on ../lib9
       +
       +TARG      = ls
       +
       +include ../std.mk
   DIR diff --git a/ls/ls.1 b/ls/ls.1
       @@ -0,0 +1,172 @@
       +.TH LS 1
       +.SH NAME
       +ls, lc \- list contents of directory
       +.SH SYNOPSIS
       +.B ls
       +[
       +.B -dlmnpqrstuFQ
       +]
       +.I name ...
       +.PP
       +.B lc
       +[
       +.B -dlmnpqrstuFQ
       +]
       +.I name ...
       +.SH DESCRIPTION
       +For each directory argument,
       +.I ls
       +lists the contents of the directory;
       +for each file argument,
       +.I ls
       +repeats its name and any other information requested.
       +When no argument is given, the current directory is listed.
       +By default, the output is sorted alphabetically by name.
       +.PP
       +.I Lc
       +is the same as
       +.IR ls ,
       +but sets the
       +.B -p
       +option and pipes the output through
       +.IR mc (1).
       +.PP
       +There are a number of options:
       +.TP
       +.B  -d
       +If argument is a directory, list it, not
       +its contents.
       +.TP
       +.B  -l
       +List in long format, giving mode (see below), file system type
       +(e.g., for devices, the
       +.B #
       +code letter that names it; see
       +.IR intro (3)),
       +the instance or subdevice number, owner, group,
       +size in bytes, and time of last modification
       +for each file.
       +.TP
       +.B -m
       +List the name of the user who most recently modified the file.
       +.TP
       +.B  -n
       +Don't sort the listing.
       +.TP
       +.B  -p
       +Print only the final path element of each file name.
       +.TP
       +.B  -q
       +List the
       +.I qid
       +(see
       +.IR stat (3))
       +of each file; the printed fields are in the order
       +path, version, and type.
       +.TP
       +.B  -r
       +Reverse the order of sort.
       +.TP
       +.B  -s
       +Give size in Kbytes for each entry.
       +.TP
       +.B  -t
       +Sort by time modified (latest first) instead of
       +by name.
       +.TP
       +.B  -u
       +Under
       +.B -t
       +sort by time of last access;
       +under
       +.B -l
       +print time of last access.
       +.TP
       +.B  -F
       +Add the character
       +.B /
       +after all directory names
       +and the character
       +.B *
       +after all executable files.
       +.TP
       +.B -L
       +Print the character
       +.B t
       +before each file if it has the temporary flag set, and
       +.B -
       +otherwise.
       +.TP
       +.B -Q
       +By default, printed file names are quoted if they contain characters special to
       +.IR rc (1).
       +The
       +.B -Q
       +flag disables this behavior.
       +.PP
       +The mode printed under the
       +.B -l
       +option contains 11 characters,
       +interpreted
       +as follows:
       +the first character is
       +.TP
       +.B d
       +if the entry is a directory;
       +.TP
       +.B a
       +if the entry is an append-only file;
       +.TP
       +.B D
       +if the entry is a Unix device;
       +.TP
       +.B L
       +if the entry is a symbolic link;
       +.TP
       +.B P
       +if the entry is a named pipe;
       +.TP
       +.B S
       +if the entry is a socket;
       +.TP
       +.B  -
       +if the entry is a plain file.
       +.PD
       +.PP
       +The next letter is
       +.B l
       +if the file is exclusive access (one writer or reader at a time).
       +.PP
       +The last 9 characters are interpreted
       +as three sets of three bits each.
       +The first set refers to owner permissions;
       +the next to permissions to others in the same user-group;
       +and the last to all others.
       +Within each set the three characters indicate
       +permission respectively to read, to write, or to
       +execute the file as a program.
       +For a directory, `execute' permission is interpreted
       +to mean permission to search the directory
       +for a specified file.
       +The permissions are indicated as follows:
       +.TP 3
       +.B  r
       +if the file is readable;
       +.PD 0
       +.TP 3
       +.B  w
       +if the file is writable;
       +.TP 3
       +.B  x
       +if the file is executable;
       +.TP 3
       +.B  -
       +if none of the above permissions is granted.
       +.PD
       +.SH SOURCE
       +.B \*9/src/cmd/ls.c
       +.br
       +.B \*9/bin/lc
       +.SH SEE ALSO
       +.IR stat (3),
       +.IR mc (1)
   DIR diff --git a/ls/ls.c b/ls/ls.c
       @@ -0,0 +1,309 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +
       +#define dirbuf p9dirbuf        /* avoid conflict on sun */
       +
       +typedef struct NDir NDir;
       +struct NDir
       +{
       +        Dir *d;
       +        char        *prefix;
       +};
       +
       +int        errs = 0;
       +int        dflag;
       +int        lflag;
       +int        mflag;
       +int        nflag;
       +int        pflag;
       +int        qflag;
       +int        Qflag;
       +int        rflag;
       +int        sflag;
       +int        tflag;
       +int        uflag;
       +int        Fflag;
       +int        ndirbuf;
       +int        ndir;
       +NDir*        dirbuf;
       +int        ls(char*, int);
       +int        compar(NDir*, NDir*);
       +char*        asciitime(long);
       +char*        darwx(long);
       +void        rwx(long, char*);
       +void        growto(long);
       +void        dowidths(Dir*);
       +void        format(Dir*, char*);
       +void        output(void);
       +ulong        clk;
       +int        swidth;                        /* max width of -s size */
       +int        qwidth;                        /* max width of -q version */
       +int        vwidth;                        /* max width of dev */
       +int        uwidth;                        /* max width of userid */
       +int        mwidth;                        /* max width of muid */
       +int        glwidth;                /* max width of groupid and length */
       +Biobuf        bin;
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        int i;
       +
       +        Binit(&bin, 1, OWRITE);
       +        ARGBEGIN{
       +        case 'F':        Fflag++; break;
       +        case 'd':        dflag++; break;
       +        case 'l':        lflag++; break;
       +        case 'm':        mflag++; break;
       +        case 'n':        nflag++; break;
       +        case 'p':        pflag++; break;
       +        case 'q':        qflag++; break;
       +        case 'Q':        Qflag++; break;
       +        case 'r':        rflag++; break;
       +        case 's':        sflag++; break;
       +        case 't':        tflag++; break;
       +        case 'u':        uflag++; break;
       +        default:        fprint(2, "usage: ls [-dlmnpqrstuFQ] [file ...]\n");
       +                        exits("usage");
       +        }ARGEND
       +
       +        doquote = needsrcquote;
       +        quotefmtinstall();
       +        fmtinstall('M', dirmodefmt);
       +
       +        if(lflag)
       +                clk = time(0);
       +        if(argc == 0)
       +                errs = ls(".", 0);
       +        else for(i=0; i<argc; i++)
       +                errs |= ls(argv[i], 1);
       +        output();
       +        exits(errs? "errors" : 0);
       +}
       +
       +int
       +ls(char *s, int multi)
       +{
       +        int fd;
       +        long i, n;
       +        char *p;
       +        Dir *db;
       +
       +        for(;;) {
       +                p = utfrrune(s, '/');
       +                if(p == 0 || p[1] != 0 || p == s)
       +                        break;
       +                *p = 0;
       +        }
       +        db = dirstat(s);
       +        if(db == nil){
       +    error:
       +                fprint(2, "ls: %s: %r\n", s);
       +                return 1;
       +        }
       +        if(db->qid.type&QTDIR && dflag==0){
       +                free(db);
       +                db = nil;
       +                output();
       +                fd = open(s, OREAD);
       +                if(fd == -1)
       +                        goto error;
       +                n = dirreadall(fd, &db);
       +                if(n < 0)
       +                        goto error;
       +                growto(ndir+n);
       +                for(i=0; i<n; i++){
       +                        dirbuf[ndir+i].d = db+i;
       +                        dirbuf[ndir+i].prefix = multi? s : 0;
       +                }
       +                ndir += n;
       +                close(fd);
       +                output();
       +        }else{
       +                growto(ndir+1);
       +                dirbuf[ndir].d = db;
       +                dirbuf[ndir].prefix = 0;
       +                p = utfrrune(s, '/');
       +                if(p){
       +                        dirbuf[ndir].prefix = s;
       +                        *p = 0;
       +                        /* restore original name; don't use result of stat */
       +                        dirbuf[ndir].d->name = strdup(p+1);
       +                }
       +                ndir++;
       +        }
       +        return 0;
       +}
       +
       +void
       +output(void)
       +{
       +        int i;
       +        char buf[4096];
       +        char *s;
       +
       +        if(!nflag)
       +                qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(const void*, const void*))compar);
       +        for(i=0; i<ndir; i++)
       +                dowidths(dirbuf[i].d);
       +        for(i=0; i<ndir; i++) {
       +                if(!pflag && (s = dirbuf[i].prefix)) {
       +                        if(strcmp(s, "/") ==0)        /* / is a special case */
       +                                s = "";
       +                        sprint(buf, "%s/%s", s, dirbuf[i].d->name);
       +                        format(dirbuf[i].d, buf);
       +                } else
       +                        format(dirbuf[i].d, dirbuf[i].d->name);
       +        }
       +        ndir = 0;
       +        Bflush(&bin);
       +}
       +
       +void
       +dowidths(Dir *db)
       +{
       +        char buf[256];
       +        int n;
       +
       +        if(sflag) {
       +                n = sprint(buf, "%llud", (db->length+1023)/1024);
       +                if(n > swidth)
       +                        swidth = n;
       +        }
       +        if(qflag) {
       +                n = sprint(buf, "%lud", db->qid.vers);
       +                if(n > qwidth)
       +                        qwidth = n;
       +        }
       +        if(mflag) {
       +                n = snprint(buf, sizeof buf, "[%s]", db->muid);
       +                if(n > mwidth)
       +                        mwidth = n;
       +        }
       +        if(lflag) {
       +                n = sprint(buf, "%ud", db->dev);
       +                if(n > vwidth)
       +                        vwidth = n;
       +                n = strlen(db->uid);
       +                if(n > uwidth)
       +                        uwidth = n;
       +                n = sprint(buf, "%llud", db->length);
       +                n += strlen(db->gid);
       +                if(n > glwidth)
       +                        glwidth = n;
       +        }
       +}
       +
       +char*
       +fileflag(Dir *db)
       +{
       +        if(Fflag == 0)
       +                return "";
       +        if(QTDIR & db->qid.type)
       +                return "/";
       +        if(0111 & db->mode)
       +                return "*";
       +        return "";
       +}
       +
       +void
       +format(Dir *db, char *name)
       +{
       +        int i;
       +
       +        if(sflag)
       +                Bprint(&bin, "%*llud ",
       +                        swidth, (db->length+1023)/1024);
       +        if(mflag){
       +                Bprint(&bin, "[%s] ", db->muid);
       +                for(i=2+strlen(db->muid); i<mwidth; i++)
       +                        Bprint(&bin, " ");
       +        }
       +        if(qflag)
       +                Bprint(&bin, "(%.16llux %*lud %.2ux) ",
       +                        db->qid.path,
       +                        qwidth, db->qid.vers,
       +                        db->qid.type);
       +        if(lflag)
       +                Bprint(&bin,
       +                        Qflag? "%M %C %*ud %*s %s %*llud %s %s\n" : "%M %C %*ud %*s %s %*llud %s %q\n",
       +                        db->mode, db->type,
       +                        vwidth, db->dev,
       +                        -uwidth, db->uid,
       +                        db->gid,
       +                        (int)(glwidth-strlen(db->gid)), db->length,
       +                        asciitime(uflag? db->atime : db->mtime), name);
       +        else
       +                Bprint(&bin,
       +                        Qflag? "%s%s\n" : "%q%s\n",
       +                        name, fileflag(db));
       +}
       +
       +void
       +growto(long n)
       +{
       +        if(n <= ndirbuf)
       +                return;
       +        ndirbuf = n;
       +        dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir));
       +        if(dirbuf == 0){
       +                fprint(2, "ls: malloc fail\n");
       +                exits("malloc fail");
       +        }                
       +}
       +
       +int
       +compar(NDir *a, NDir *b)
       +{
       +        long i;
       +        Dir *ad, *bd;
       +
       +        ad = a->d;
       +        bd = b->d;
       +
       +        if(tflag){
       +                if(uflag)
       +                        i = bd->atime-ad->atime;
       +                else
       +                        i = bd->mtime-ad->mtime;
       +        }else{
       +                if(a->prefix && b->prefix){
       +                        i = strcmp(a->prefix, b->prefix);
       +                        if(i == 0)
       +                                i = strcmp(ad->name, bd->name);
       +                }else if(a->prefix){
       +                        i = strcmp(a->prefix, bd->name);
       +                        if(i == 0)
       +                                i = 1;        /* a is longer than b */
       +                }else if(b->prefix){
       +                        i = strcmp(ad->name, b->prefix);
       +                        if(i == 0)
       +                                i = -1;        /* b is longer than a */
       +                }else
       +                        i = strcmp(ad->name, bd->name);
       +        }
       +        if(i == 0)
       +                i = (ad<bd? -1 : 1);
       +        if(rflag)
       +                i = -i;
       +        return i;
       +}
       +
       +char*
       +asciitime(long l)
       +{
       +        static char buf[32];
       +        char *t;
       +
       +        t = ctime(l);
       +        /* 6 months in the past or a day in the future */
       +        if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
       +                memmove(buf, t+4, 7);                /* month and day */
       +                memmove(buf+7, t+23, 5);                /* year */
       +        }else
       +                memmove(buf, t+4, 12);                /* skip day of week */
       +        buf[12] = 0;
       +        return buf;
       +}
       +