1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <auth.h> 5 #include <fcall.h> 6 7 typedef struct NDir NDir; 8 struct NDir 9 { 10 Dir; 11 char *prefix; 12 }; 13 14 int errs = 0; 15 int dflag; 16 int lflag; 17 int nflag; 18 int pflag; 19 int qflag; 20 int rflag; 21 int sflag; 22 int tflag; 23 int uflag; 24 int Fflag; 25 int ndirbuf; 26 int ndir; 27 NDir* dirbuf; 28 int ls(char*, int); 29 int compar(NDir*, NDir*); 30 char* asciitime(long); 31 char* darwx(long); 32 void rwx(long, char*); 33 void growto(long); 34 void dowidths(Dir*); 35 void format(Dir*, char*); 36 void output(void); 37 ulong clk; 38 int swidth; 39 int qwidth; 40 int vwidth; 41 int uwidth; 42 int glwidth; 43 Biobuf bin; 44 45 #define HUNK 50 46 47 void 48 main(int argc, char *argv[]) 49 { 50 int i, fd; 51 char buf[64]; 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 'n': nflag++; break; 59 case 'p': pflag++; break; 60 case 'q': qflag++; break; 61 case 'r': rflag++; break; 62 case 's': sflag++; break; 63 case 't': tflag++; break; 64 case 'u': uflag++; break; 65 default: fprint(2, "usage: ls [-dlnpqrstuF] [file ...]\n"); 66 exits("usage"); 67 }ARGEND 68 69 fmtinstall('M', dirmodeconv); 70 71 if(lflag){ 72 fd = open("/dev/time", OREAD); 73 if(fd<0 || read(fd, buf, sizeof buf-1)<=0) 74 fprint(2, "ls: can't open /dev/time\n"); 75 close(fd); 76 clk = strtoul(buf, 0, 0); 77 } 78 if(argc == 0) 79 errs = ls(".", 0); 80 else for(i=0; i<argc; i++) 81 errs |= ls(argv[i], 1); 82 output(); 83 exits(errs? "errors" : 0); 84 } 85 86 int 87 ls(char *s, int multi) 88 { 89 int fd; 90 long i, n; 91 char *p; 92 Dir db[HUNK]; 93 94 for(;;) { 95 p = utfrrune(s, '/'); 96 if(p == 0 || p[1] != 0 || p == s) 97 break; 98 *p = 0; 99 } 100 if(dirstat(s, db)==-1){ 101 error: 102 fprint(2, "ls: %s: %r\n", s); 103 return 1; 104 } 105 if(db[0].qid.path&CHDIR && dflag==0){ 106 output(); 107 fd = open(s, OREAD); 108 if(fd == -1) 109 goto error; 110 while((n=dirread(fd, db, sizeof db)) > 0){ 111 n /= sizeof(Dir); 112 growto(ndir+n); 113 for(i=0; i<n; i++){ 114 memmove(dirbuf+ndir+i, db+i, sizeof(Dir)); 115 dirbuf[ndir+i].prefix = multi? s : 0; 116 } 117 ndir += n; 118 } 119 close(fd); 120 output(); 121 }else{ 122 growto(ndir+1); 123 memmove(dirbuf+ndir, db, sizeof(Dir)); 124 dirbuf[ndir].prefix = 0; 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 swidth = 0; /* max width of -s size */ 145 qwidth = 0; /* max width of -q version */ 146 vwidth = 0; /* max width of dev */ 147 uwidth = 0; /* max width of userid */ 148 glwidth = 0; /* max width of groupid and length */ 149 for(i=0; i<ndir; i++) 150 dowidths(&dirbuf[i]); 151 for(i=0; i<ndir; i++) { 152 if(!pflag && (s = dirbuf[i].prefix)) { 153 if(strcmp(s, "/") ==0) /* / is a special case */ 154 s = ""; 155 sprint(buf, "%s/%s", s, dirbuf[i].name); 156 format(&dirbuf[i], buf); 157 } else 158 format(&dirbuf[i], dirbuf[i].name); 159 } 160 ndir = 0; 161 Bflush(&bin); 162 } 163 164 void 165 dowidths(Dir *db) 166 { 167 char buf[100]; 168 int n; 169 170 if(sflag) { 171 sprint(buf, "%ld", (db->length+1023)/1024); 172 n = strlen(buf); 173 if(n > swidth) 174 swidth = n; 175 } 176 if(qflag) { 177 sprint(buf, "%ld", db->qid.vers); 178 n = strlen(buf); 179 if(n > qwidth) 180 qwidth = n; 181 } 182 if(lflag) { 183 sprint(buf, "%d", db->dev); 184 n = strlen(buf); 185 if(n > vwidth) 186 vwidth = n; 187 n = strlen(db->uid); 188 if(n > uwidth) 189 uwidth = n; 190 sprint(buf, "%ld", db->length); 191 n = strlen(buf) + strlen(db->gid); 192 if(n > glwidth) 193 glwidth = n; 194 } 195 } 196 197 char* 198 fileflag(Dir *db) 199 { 200 if(Fflag == 0) 201 return ""; 202 if(CHDIR & db->qid.path) 203 return "/"; 204 if(0111 & db->mode) 205 return "*"; 206 return ""; 207 } 208 209 void 210 format(Dir *db, char *name) 211 { 212 if(sflag) 213 Bprint(&bin, "%*ld ", 214 swidth, (db->length+1023)/1024); 215 if(qflag) 216 Bprint(&bin, "%.8lux %*lud ", 217 db->qid.path, 218 qwidth, db->qid.vers); 219 if(lflag) 220 Bprint(&bin, "%M %C %*d %*s %s %*ld %s %s\n", 221 db->mode, db->type, 222 vwidth, db->dev, 223 -uwidth, db->uid, 224 db->gid, 225 glwidth-strlen(db->gid), db->length, 226 asciitime(uflag? db->atime : db->mtime), name); 227 else 228 Bprint(&bin, "%s%s\n", name, fileflag(db)); 229 } 230 231 void 232 growto(long n) 233 { 234 if(n <= ndirbuf) 235 return; 236 ndirbuf = n; 237 dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir)); 238 if(dirbuf == 0){ 239 fprint(2, "ls: malloc fail\n"); 240 exits("malloc fail"); 241 } 242 } 243 244 int 245 compar(NDir *a, NDir *b) 246 { 247 long i; 248 249 if(tflag){ 250 if(uflag) 251 i = b->atime-a->atime; 252 else 253 i = b->mtime-a->mtime; 254 }else{ 255 if(a->prefix && b->prefix){ 256 i = strcmp(a->prefix, b->prefix); 257 if(i == 0) 258 i = strcmp(a->name, b->name); 259 }else if(a->prefix){ 260 i = strcmp(a->prefix, b->name); 261 if(i == 0) 262 i = 1; /* a is longer than b */ 263 }else if(b->prefix){ 264 i = strcmp(a->name, b->prefix); 265 if(i == 0) 266 i = -1; /* b is longer than a */ 267 }else 268 i = strcmp(a->name, b->name); 269 } 270 if(i == 0) 271 i = (a<b? -1 : 1); 272 if(rflag) 273 i = -i; 274 return i; 275 } 276 277 char* 278 asciitime(long l) 279 { 280 static char buf[32]; 281 char *t; 282 283 t = ctime(l); 284 /* 6 months in the past or a day in the future */ 285 if(l<clk-180L*24*60*60 || clk+24L*60*60<l){ 286 memmove(buf, t+4, 7); /* month and day */ 287 memmove(buf+7, t+23, 5); /* year */ 288 }else 289 memmove(buf, t+4, 12); /* skip day of week */ 290 buf[12] = 0; 291 return buf; 292 } 293