| #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, |
| "%M %C %*ud %*s %s %*llud %s ", |
| 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)); |
| 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; |
| } |
| |