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