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