ref: f0ac2d01765f938a4f3656efef440cf2c1bca69d
dir: /sys/src/cmd/ls.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <fcall.h> 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 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); char* xcleanname(char*); 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 lwidth; /* max width of length */ int gwidth; /* max width of groupid */ 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 'T': Tflag++; break; case 'u': uflag++; break; default: fprint(2, "usage: ls [-dlmnpqrstuFQT] [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; 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); output(); fd = open(s, OREAD); if(fd == -1) goto error; n = dirreadall(fd, &db); if(n < 0) goto error; xcleanname(s); 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; xcleanname(s); p = utfrrune(s, '/'); if(p){ dirbuf[ndir].prefix = s; *p = 0; } ndir++; } return 0; } void output(void) { int i; char buf[4096]; char *s; if(!nflag) qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(void*, 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, "[%q]", db->muid); if(n > mwidth) mwidth = n; } if(lflag) { n = sprint(buf, "%ud", db->dev); if(n > vwidth) vwidth = n; n = sprint(buf, "%q", db->uid); if(n > uwidth) uwidth = n; n = sprint(buf, "%q", db->gid); if(n > gwidth) gwidth = n; n = sprint(buf, "%llud", db->length); if(n > lwidth) lwidth = 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, "[%q] ", 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(Tflag) Bprint(&bin, "%c ", (db->mode&DMTMP)? 't': '-'); if(lflag) Bprint(&bin, "%M %C %*ud %*q %*q %*llud %s ", db->mode, db->type, vwidth, db->dev, -uwidth, db->uid, -gwidth, db->gid, lwidth, db->length, asciitime(uflag? db->atime: db->mtime)); 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 = (a<b? -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; } /* * Compress slashes, remove trailing slash. Don't worry about . and .. */ char* xcleanname(char *name) { char *r, *w; for(r=w=name; *r; r++){ if(*r=='/' && r>name && *(r-1)=='/') continue; if(w != r) *w = *r; w++; } *w = 0; while(w-1>name && *(w-1)=='/') *--w = 0; return name; }