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