ls.c - 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
       ---
       ls.c (5449B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 
            5 #define dirbuf p9dirbuf        /* avoid conflict on sun */
            6 
            7 typedef struct NDir NDir;
            8 struct NDir
            9 {
           10         Dir *d;
           11         char        *prefix;
           12 };
           13 
           14 int        errs = 0;
           15 int        dflag;
           16 int        lflag;
           17 int        mflag;
           18 int        nflag;
           19 int        pflag;
           20 int        qflag;
           21 int        Qflag;
           22 int        rflag;
           23 int        sflag;
           24 int        tflag;
           25 int        uflag;
           26 int        Fflag;
           27 int        ndirbuf;
           28 int        ndir;
           29 NDir*        dirbuf;
           30 int        ls(char*, int);
           31 int        compar(NDir*, NDir*);
           32 char*        asciitime(long);
           33 char*        darwx(long);
           34 void        rwx(long, char*);
           35 void        growto(long);
           36 void        dowidths(Dir*);
           37 void        format(Dir*, char*);
           38 void        output(void);
           39 ulong        clk;
           40 int        swidth;                        /* max width of -s size */
           41 int        qwidth;                        /* max width of -q version */
           42 int        vwidth;                        /* max width of dev */
           43 int        uwidth;                        /* max width of userid */
           44 int        mwidth;                        /* max width of muid */
           45 int        glwidth;                /* max width of groupid and length */
           46 Biobuf        bin;
           47 
           48 void
           49 main(int argc, char *argv[])
           50 {
           51         int i;
           52 
           53         Binit(&bin, 1, OWRITE);
           54         ARGBEGIN{
           55         case 'F':        Fflag++; break;
           56         case 'd':        dflag++; break;
           57         case 'l':        lflag++; break;
           58         case 'm':        mflag++; break;
           59         case 'n':        nflag++; break;
           60         case 'p':        pflag++; break;
           61         case 'q':        qflag++; break;
           62         case 'Q':        Qflag++; break;
           63         case 'r':        rflag++; break;
           64         case 's':        sflag++; break;
           65         case 't':        tflag++; break;
           66         case 'u':        uflag++; break;
           67         default:        fprint(2, "usage: ls [-dlmnpqrstuFQ] [file ...]\n");
           68                         exits("usage");
           69         }ARGEND
           70 
           71         doquote = needsrcquote;
           72         quotefmtinstall();
           73         fmtinstall('M', dirmodefmt);
           74 
           75         if(lflag)
           76                 clk = time(0);
           77         if(argc == 0)
           78                 errs = ls(".", 0);
           79         else for(i=0; i<argc; i++)
           80                 errs |= ls(argv[i], 1);
           81         output();
           82         exits(errs? "errors" : 0);
           83 }
           84 
           85 int
           86 ls(char *s, int multi)
           87 {
           88         int fd;
           89         long i, n;
           90         char *p;
           91         Dir *db;
           92 
           93         for(;;) {
           94                 p = utfrrune(s, '/');
           95                 if(p == 0 || p[1] != 0 || p == s)
           96                         break;
           97                 *p = 0;
           98         }
           99         db = dirstat(s);
          100         if(db == nil){
          101     error:
          102                 fprint(2, "ls: %s: %r\n", s);
          103                 return 1;
          104         }
          105         if(db->qid.type&QTDIR && dflag==0){
          106                 free(db);
          107                 db = nil;
          108                 output();
          109                 fd = open(s, OREAD);
          110                 if(fd == -1)
          111                         goto error;
          112                 n = dirreadall(fd, &db);
          113                 if(n < 0)
          114                         goto error;
          115                 growto(ndir+n);
          116                 for(i=0; i<n; i++){
          117                         dirbuf[ndir+i].d = db+i;
          118                         dirbuf[ndir+i].prefix = multi? s : 0;
          119                 }
          120                 ndir += n;
          121                 close(fd);
          122                 output();
          123         }else{
          124                 growto(ndir+1);
          125                 dirbuf[ndir].d = db;
          126                 dirbuf[ndir].prefix = 0;
          127                 p = utfrrune(s, '/');
          128                 if(p){
          129                         dirbuf[ndir].prefix = s;
          130                         *p = 0;
          131                         /* restore original name; don't use result of stat */
          132                         dirbuf[ndir].d->name = strdup(p+1);
          133                 }
          134                 ndir++;
          135         }
          136         return 0;
          137 }
          138 
          139 void
          140 output(void)
          141 {
          142         int i;
          143         char buf[4096];
          144         char *s;
          145 
          146         if(!nflag)
          147                 qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(const void*, const void*))compar);
          148         for(i=0; i<ndir; i++)
          149                 dowidths(dirbuf[i].d);
          150         for(i=0; i<ndir; i++) {
          151                 if(!pflag && (s = dirbuf[i].prefix)) {
          152                         if(strcmp(s, "/") ==0)        /* / is a special case */
          153                                 s = "";
          154                         sprint(buf, "%s/%s", s, dirbuf[i].d->name);
          155                         format(dirbuf[i].d, buf);
          156                 } else
          157                         format(dirbuf[i].d, dirbuf[i].d->name);
          158         }
          159         ndir = 0;
          160         Bflush(&bin);
          161 }
          162 
          163 void
          164 dowidths(Dir *db)
          165 {
          166         char buf[256];
          167         int n;
          168 
          169         if(sflag) {
          170                 n = sprint(buf, "%llud", (db->length+1023)/1024);
          171                 if(n > swidth)
          172                         swidth = n;
          173         }
          174         if(qflag) {
          175                 n = sprint(buf, "%lud", db->qid.vers);
          176                 if(n > qwidth)
          177                         qwidth = n;
          178         }
          179         if(mflag) {
          180                 n = snprint(buf, sizeof buf, "[%s]", db->muid);
          181                 if(n > mwidth)
          182                         mwidth = n;
          183         }
          184         if(lflag) {
          185                 n = sprint(buf, "%ud", db->dev);
          186                 if(n > vwidth)
          187                         vwidth = n;
          188                 n = strlen(db->uid);
          189                 if(n > uwidth)
          190                         uwidth = n;
          191                 n = sprint(buf, "%llud", db->length);
          192                 n += strlen(db->gid);
          193                 if(n > glwidth)
          194                         glwidth = n;
          195         }
          196 }
          197 
          198 char*
          199 fileflag(Dir *db)
          200 {
          201         if(Fflag == 0)
          202                 return "";
          203         if(QTDIR & db->qid.type)
          204                 return "/";
          205         if(0111 & db->mode)
          206                 return "*";
          207         return "";
          208 }
          209 
          210 void
          211 format(Dir *db, char *name)
          212 {
          213         int i;
          214 
          215         if(sflag)
          216                 Bprint(&bin, "%*llud ",
          217                         swidth, (db->length+1023)/1024);
          218         if(mflag){
          219                 Bprint(&bin, "[%s] ", db->muid);
          220                 for(i=2+strlen(db->muid); i<mwidth; i++)
          221                         Bprint(&bin, " ");
          222         }
          223         if(qflag)
          224                 Bprint(&bin, "(%.16llux %*lud %.2ux) ",
          225                         db->qid.path,
          226                         qwidth, db->qid.vers,
          227                         db->qid.type);
          228         if(lflag)
          229                 Bprint(&bin,
          230                         "%M %C %*ud %*s %s %*llud %s ",
          231                         db->mode, db->type,
          232                         vwidth, db->dev,
          233                         -uwidth, db->uid,
          234                         db->gid,
          235                         (int)(glwidth-strlen(db->gid)), db->length,
          236                         asciitime(uflag? db->atime : db->mtime));
          237         Bprint(&bin,
          238                 Qflag? "%s%s\n" : "%q%s\n",
          239                 name, fileflag(db));
          240 }
          241 
          242 void
          243 growto(long n)
          244 {
          245         if(n <= ndirbuf)
          246                 return;
          247         ndirbuf = n;
          248         dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir));
          249         if(dirbuf == 0){
          250                 fprint(2, "ls: malloc fail\n");
          251                 exits("malloc fail");
          252         }                
          253 }
          254 
          255 int
          256 compar(NDir *a, NDir *b)
          257 {
          258         long i;
          259         Dir *ad, *bd;
          260 
          261         ad = a->d;
          262         bd = b->d;
          263 
          264         if(tflag){
          265                 if(uflag)
          266                         i = bd->atime-ad->atime;
          267                 else
          268                         i = bd->mtime-ad->mtime;
          269         }else{
          270                 if(a->prefix && b->prefix){
          271                         i = strcmp(a->prefix, b->prefix);
          272                         if(i == 0)
          273                                 i = strcmp(ad->name, bd->name);
          274                 }else if(a->prefix){
          275                         i = strcmp(a->prefix, bd->name);
          276                         if(i == 0)
          277                                 i = 1;        /* a is longer than b */
          278                 }else if(b->prefix){
          279                         i = strcmp(ad->name, b->prefix);
          280                         if(i == 0)
          281                                 i = -1;        /* b is longer than a */
          282                 }else
          283                         i = strcmp(ad->name, bd->name);
          284         }
          285         if(i == 0)
          286                 i = (ad<bd? -1 : 1);
          287         if(rflag)
          288                 i = -i;
          289         return i;
          290 }
          291 
          292 char*
          293 asciitime(long l)
          294 {
          295         static char buf[32];
          296         char *t;
          297 
          298         t = ctime(l);
          299         /* 6 months in the past or a day in the future */
          300         if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
          301                 memmove(buf, t+4, 7);                /* month and day */
          302                 memmove(buf+7, t+23, 5);                /* year */
          303         }else
          304                 memmove(buf, t+4, 12);                /* skip day of week */
          305         buf[12] = 0;
          306         return buf;
          307 }
          308