diff options
Diffstat (limited to 'src/cmd/ls.C')
-rw-r--r-- | src/cmd/ls.C | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/src/cmd/ls.C b/src/cmd/ls.C new file mode 100644 index 00000000..de2bee94 --- /dev/null +++ b/src/cmd/ls.C @@ -0,0 +1,305 @@ +#include <u.h> +#include <libc.h> +#include <bio.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 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){ + 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 = (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; +} + |