Commit | Line | Data |
---|---|---|
8d1274f2 BJ |
1 | # |
2 | ||
3 | /* | |
4 | * ls - list file or directory | |
5 | * | |
6 | * Modified by Bill Joy UCB May/August 1977 | |
7 | * | |
8 | * This version of ls is designed for graphic terminals and to | |
9 | * list directories with lots of files in them compactly. | |
10 | * It supports three variants for listings: | |
11 | * | |
12 | * 1) Columnar output. | |
13 | * 2) Stream output. | |
14 | * 3) Old one per line format. | |
15 | * | |
16 | * Columnar output is the default. | |
17 | * If, however, the standard output is not a teletype, the default | |
18 | * is one-per-line. | |
19 | * | |
20 | * With columnar output, the items are sorted down the columns. | |
21 | * We use columns only for a directory we are interpreting. | |
22 | * Thus, in particular, we do not use columns for | |
23 | * | |
24 | * ls /usr/bin/p* | |
25 | * | |
26 | * This version of ls also prints non-printing characters as '?' if | |
27 | * the standard output is a teletype. | |
28 | * | |
29 | * Flags relating to these and other new features are: | |
30 | * | |
31 | * -m force stream output. | |
32 | * | |
33 | * -1 force one entry per line, e.g. to a teletype | |
34 | * | |
35 | * -q force non-printings to be '?'s, e.g. to a file | |
36 | * | |
37 | * -c force columnar output, e.g. into a file | |
38 | * | |
39 | * -n like -l, but user/group id's in decimal rather than | |
40 | * looking in /etc/passwd to save time | |
41 | */ | |
42 | ||
43 | struct { | |
44 | int fdes; | |
45 | int nleft; | |
46 | char *nextc; | |
47 | char buff[512]; | |
48 | } inf, obuf; | |
49 | ||
50 | struct ibuf { | |
51 | int idev; | |
52 | int inum; | |
53 | int iflags; | |
54 | char inl; | |
55 | char iuid; | |
56 | char igid; | |
57 | char isize0; | |
58 | int isize; | |
59 | int iaddr[8]; | |
60 | char *iatime[2]; | |
61 | char *imtime[2]; | |
62 | }; | |
63 | ||
64 | struct lbuf { | |
65 | char lname[15]; | |
66 | int lnum; | |
67 | int lflags; | |
68 | char lnl; | |
69 | char luid; | |
70 | char lgid; | |
71 | char lsize0; | |
72 | int lsize; | |
73 | char *lmtime[2]; | |
74 | unsigned int lquot[2]; | |
75 | }; | |
76 | ||
77 | struct lbufx { | |
78 | char *namep; | |
79 | }; | |
80 | ||
81 | int aflg, dflg, lflg, sflg, tflg, uflg, iflg, fflg, nflg, Aflg; | |
82 | int cflg, qflg, across; | |
83 | int nopad; | |
84 | int rflg 1; | |
85 | char *year; | |
86 | int flags; | |
87 | int uidfil -1; | |
88 | int lastuid -1; | |
89 | char tbuf[16]; | |
90 | int tblocks; | |
91 | int statreq; | |
92 | extern end; | |
93 | struct lbuf *lastp &end; | |
94 | struct lbuf *rlastp &end; | |
95 | char *dotp "."; | |
96 | ||
97 | #define IFMT 060000 | |
98 | #define DIR 0100000 | |
99 | #define CHR 020000 | |
100 | #define BLK 040000 | |
101 | #define ISARG 01000 | |
102 | #define LARGE 010000 | |
103 | #define STXT 010000 | |
104 | #define SUID 04000 | |
105 | #define SGID 02000 | |
106 | #define ROWN 0400 | |
107 | #define WOWN 0200 | |
108 | #define XOWN 0100 | |
109 | #define RGRP 040 | |
110 | #define WGRP 020 | |
111 | #define XGRP 010 | |
112 | #define ROTH 04 | |
113 | #define WOTH 02 | |
114 | #define XOTH 01 | |
115 | #define RSTXT 01000 | |
116 | ||
117 | int colwidth 16; | |
118 | int outcol; | |
119 | ||
120 | main(argc, argv) | |
121 | int argc; | |
122 | char **argv; | |
123 | { | |
124 | char *cp; | |
125 | int i, j; | |
126 | register struct lbuf *ep; | |
127 | register struct lbuf *slastp; | |
128 | struct lbuf lb; | |
129 | int t; | |
130 | int compar(); | |
131 | ||
132 | obuf.fdes = 1; | |
133 | qflg = gtty(1, &obuf.buff) == 0; | |
134 | /* | |
135 | * If the standard output is not a teletype, | |
136 | * then we default to one-per-line format | |
137 | * otherwise decide between stream and | |
138 | * columnar based on our name. | |
139 | */ | |
140 | if (qflg) { | |
141 | cflg = 1; | |
142 | for (cp = argv[0]; cp[0] && cp[1]; cp++) | |
143 | continue; | |
144 | /* | |
145 | * Name ends in l => stream | |
146 | */ | |
147 | if (cp[0] == 'l') | |
148 | nopad = 1, cflg = 0; | |
149 | /* | |
150 | * ... if doesn't end in l or s ==> columns sorted across | |
151 | */ | |
152 | else if (cp[0] != 's') | |
153 | across = 1; | |
154 | } | |
155 | time(lb.lmtime); | |
156 | year = lb.lmtime[0] - 245; /* 6 months ago */ | |
157 | while (--argc > 0 && *argv[1] == '-') { | |
158 | argv++; | |
159 | while (*++*argv) switch (**argv) { | |
160 | /* | |
161 | * c - force columnar output | |
162 | */ | |
163 | case 'c': | |
164 | cflg = 1; | |
165 | nopad = 0; | |
166 | continue; | |
167 | /* | |
168 | * m - force stream output | |
169 | */ | |
170 | case 'm': | |
171 | cflg = 0; | |
172 | nopad = 1; | |
173 | continue; | |
174 | /* | |
175 | * x - force sort across | |
176 | */ | |
177 | case 'x': | |
178 | across = 1; | |
179 | nopad = 0; | |
180 | cflg = 1; | |
181 | continue; | |
182 | /* | |
183 | * q - force ?'s in output | |
184 | */ | |
185 | case 'q': | |
186 | qflg = 1; | |
187 | continue; | |
188 | /* | |
189 | * 1 - force 1/line in output | |
190 | */ | |
191 | case '1': | |
192 | cflg = 0; | |
193 | nopad = 0; | |
194 | continue; | |
195 | /* STANDARD FLAGS */ | |
196 | case 'a': | |
197 | aflg++; | |
198 | continue; | |
199 | case 'A': | |
200 | Aflg++; | |
201 | continue; | |
202 | ||
203 | case 's': | |
204 | colwidth = 24; | |
205 | sflg++; | |
206 | statreq++; | |
207 | continue; | |
208 | ||
209 | case 'd': | |
210 | dflg++; | |
211 | continue; | |
212 | ||
213 | /* | |
214 | * n - don't look in password file | |
215 | */ | |
216 | case 'n': | |
217 | nflg++; | |
218 | case 'l': | |
219 | lflg++; | |
220 | statreq++; | |
221 | continue; | |
222 | ||
223 | case 'r': | |
224 | rflg = -1; | |
225 | continue; | |
226 | ||
227 | case 't': | |
228 | tflg++; | |
229 | statreq++; | |
230 | continue; | |
231 | ||
232 | case 'u': | |
233 | uflg++; | |
234 | continue; | |
235 | ||
236 | case 'i': | |
237 | colwidth = 24; | |
238 | iflg++; | |
239 | continue; | |
240 | ||
241 | case 'f': | |
242 | fflg++; | |
243 | continue; | |
244 | ||
245 | default: | |
246 | continue; | |
247 | } | |
248 | } | |
249 | if (fflg) { | |
250 | aflg++; | |
251 | lflg = 0; | |
252 | sflg = 0; | |
253 | tflg = 0; | |
254 | statreq = 0; | |
255 | } | |
256 | if(lflg) { | |
257 | cflg = 0; | |
258 | t = "/etc/passwd"; | |
259 | nopad = 0; | |
260 | colwidth = 70; | |
261 | uidfil = open(t, 0); | |
262 | } | |
263 | if (argc==0) { | |
264 | argc++; | |
265 | argv = &dotp - 1; | |
266 | } | |
267 | for (i=0; i < argc; i++) { | |
268 | if ((ep = gstat(*++argv, 1))==0) | |
269 | continue; | |
270 | ep->namep = *argv; | |
271 | ep->lflags =| ISARG; | |
272 | } | |
273 | qsort(&end, lastp - &end, sizeof *lastp, compar); | |
274 | slastp = lastp; | |
275 | for (ep = &end; ep<slastp; ep++) { | |
276 | if (ep->lflags&DIR && dflg==0 || fflg) { | |
277 | if (argc>1) { | |
278 | printf("\n%s:\n", ep->namep); | |
279 | } | |
280 | lastp = slastp; | |
281 | readdir(ep->namep); | |
282 | if (fflg==0) | |
283 | qsort(slastp,lastp - slastp,sizeof *lastp,compar); | |
284 | if (statreq) { | |
285 | printf("total %s", locv(0, tblocks)); | |
286 | } | |
287 | pem(slastp, lastp); | |
288 | newline(); | |
289 | } else | |
290 | pentry(ep); | |
291 | } | |
292 | if (outcol) | |
293 | putc('\n', &obuf); | |
294 | fflush(&obuf); | |
295 | exit(0); | |
296 | } | |
297 | ||
298 | pem(slp, lp) | |
299 | register struct lbuf *slp, *lp; | |
300 | { | |
301 | int ncols, nrows, row, col; | |
302 | register struct lbuf *ep; | |
303 | ||
304 | ncols = 80 / colwidth; | |
305 | if (ncols == 1 || cflg == 0) { | |
306 | for (ep = slp; ep < lp; ep++) | |
307 | pentry(ep); | |
308 | return; | |
309 | } | |
310 | if (across) { | |
311 | for (ep = slp; ep < lp; ep++) | |
312 | pentry(ep); | |
313 | return; | |
314 | } | |
315 | if (statreq) | |
316 | slp--; | |
317 | nrows = (lp - slp - 1) / ncols + 1; | |
318 | for (row = 0; row < nrows; row++) { | |
319 | col = row == 0 && statreq; | |
320 | for (; col < ncols; col++) { | |
321 | ep = slp + (nrows * col) + row; | |
322 | if (ep < lp) | |
323 | pentry(ep); | |
324 | } | |
325 | if (outcol) | |
326 | printf("\n"); | |
327 | } | |
328 | } | |
329 | ||
330 | putchar(c) | |
331 | char c; | |
332 | { | |
333 | ||
334 | switch (c) { | |
335 | case '\t': | |
336 | outcol = (outcol + 8) &~ 7; | |
337 | break; | |
338 | case '\n': | |
339 | outcol = 0; | |
340 | break; | |
341 | default: | |
342 | if (qflg && (c < ' ' || c >= 0177)) | |
343 | c = '?'; | |
344 | outcol++; | |
345 | break; | |
346 | } | |
347 | putc(c, &obuf); | |
348 | } | |
349 | ||
350 | newline() | |
351 | { | |
352 | if (outcol) | |
353 | putc('\n', &obuf); | |
354 | outcol = 0; | |
355 | } | |
356 | ||
357 | column() | |
358 | { | |
359 | register int i, j; | |
360 | ||
361 | if (outcol == 0) | |
362 | return; | |
363 | if (nopad) { | |
364 | putc(',', &obuf); | |
365 | outcol++; | |
366 | if (outcol + colwidth + 2 > 80) { | |
367 | putc('\n', &obuf); | |
368 | outcol = 0; | |
369 | return; | |
370 | } | |
371 | putc(' ', &obuf); | |
372 | outcol++; | |
373 | return; | |
374 | } | |
375 | if (cflg == 0) { | |
376 | putc('\n', &obuf); | |
377 | return; | |
378 | } | |
379 | if ((outcol / colwidth + 2) * colwidth > 80) { | |
380 | putc('\n', &obuf); | |
381 | outcol = 0; | |
382 | return; | |
383 | } | |
384 | do { | |
385 | i = colwidth - outcol % colwidth; | |
386 | j = (outcol + 8) &~ 7; | |
387 | if (j - outcol > i) | |
388 | break; | |
389 | putc('\t', &obuf); | |
390 | outcol = j; | |
391 | } while (outcol % colwidth); | |
392 | while (outcol % colwidth) { | |
393 | outcol++; | |
394 | putc(' ', &obuf); | |
395 | } | |
396 | } | |
397 | ||
398 | pentry(ap) | |
399 | struct lbuf *ap; | |
400 | { | |
401 | struct { char dminor, dmajor;}; | |
402 | register t; | |
403 | register struct lbuf *p; | |
404 | register char *cp; | |
405 | ||
406 | p = ap; | |
407 | if (p->lnum == -1) | |
408 | return; | |
409 | column(); | |
410 | if (iflg) | |
411 | if (nopad && !lflg) | |
412 | printf("%d ", p->lnum); | |
413 | else | |
414 | printf("%4d ", p->lnum); | |
415 | if (lflg) { | |
416 | pmode(p); | |
417 | printf("%3d ", p->lnl&0377); | |
418 | t = /* p->lgid<<8 | */ (p->luid&0377); | |
419 | if (nflg == 0 && getpw(t, tbuf)==0) { | |
420 | char *cp = tbuf; | |
421 | while (*cp && *cp != ':') cp++; *cp = 0; | |
422 | printf("%-8.8s", tbuf); | |
423 | } else | |
424 | printf("%8d", t); | |
425 | if (p->lflags & (BLK|CHR)) { | |
426 | if (p->lflags&CHR && p->lsize == -1) | |
427 | if (p->lquot[0] < 10000 && p->lquot[1] < 10000) | |
428 | printf("%4d/%4d", | |
429 | p->lquot[0], p->lquot[1]); | |
430 | else | |
431 | printf("%u/%u", | |
432 | p->lquot[0], p->lquot[1]); | |
433 | else | |
434 | printf("%4d,%4d", p->lsize.dmajor&0477, | |
435 | p->lsize.dminor&0377); | |
436 | } else | |
437 | printf("%9s", locv(p->lsize0&0377, p->lsize)); | |
438 | ||
439 | } | |
440 | if (sflg) { | |
441 | t = nblock(p->lsize0&0377, p->lsize); | |
442 | if (nopad && !lflg) | |
443 | printf("%s ", locv(0, t)); | |
444 | else | |
445 | printf("%4s ", locv(0, t)); | |
446 | } | |
447 | if (lflg) { | |
448 | if (!sflg) | |
449 | putchar(' '); | |
450 | cp = ctime(p->lmtime); | |
451 | if(p->lmtime[0] < year) | |
452 | printf("%-7.7s %-4.4s ", cp+4, cp+20); else | |
453 | printf("%-12.12s ", cp+4); | |
454 | } | |
455 | if (p->lflags&ISARG) | |
456 | printf("%s", p->namep); | |
457 | else | |
458 | printf("%.14s", p->lname); | |
459 | } | |
460 | ||
461 | nblock(size0, size) | |
462 | int size0, size; | |
463 | { | |
464 | register int n; | |
465 | ||
466 | n = ldiv(size0&0377, size, 512); | |
467 | if (size&0777) | |
468 | n++; | |
469 | if (n>8) | |
470 | n =+ (n+255)/256; | |
471 | return(n); | |
472 | } | |
473 | ||
474 | int m0[] { 3, DIR, 'd', BLK, 'b', CHR, 'c', '-'}; | |
475 | int m1[] { 1, ROWN, 'r', '-' }; | |
476 | int m2[] { 1, WOWN, 'w', '-' }; | |
477 | int m3[] { 2, SUID, 's', XOWN, 'x', '-' }; | |
478 | int m1a[] { 1, RGRP, 'r', '-' }; | |
479 | int m1b[] { 1, WGRP, 'w', '-' }; | |
480 | int m1c[] { 2, SGID, 's', XGRP, 'x', '-' }; | |
481 | int m4[] { 1, ROTH, 'r', '-' }; | |
482 | int m5[] { 1, WOTH, 'w', '-' }; | |
483 | int m6[] { 2, STXT, 't', XOTH, 'x', '-' }; | |
484 | ||
485 | int *m[] { m0, m1, m2, m3, m1a, m1b, m1c, m4, m5, m6}; | |
486 | ||
487 | pmode(ptr) | |
488 | char *ptr; | |
489 | { | |
490 | register int **mp; | |
491 | register struct lbuf *p; | |
492 | ||
493 | p = ptr; | |
494 | flags = p->lflags; | |
495 | if (flags&CHR && p->lsize == -1) | |
496 | putchar('q'); | |
497 | else | |
498 | select(m[0]); | |
499 | for (mp = &m[1]; mp < &m[10];) | |
500 | select(*mp++); | |
501 | } | |
502 | ||
503 | select(pairp) | |
504 | int *pairp; | |
505 | { | |
506 | register int n, *ap; | |
507 | ||
508 | ap = pairp; | |
509 | n = *ap++; | |
510 | while (--n>=0 && (flags&*ap++)==0) | |
511 | ap++; | |
512 | putchar(*ap); | |
513 | } | |
514 | ||
515 | makename(dir, file) | |
516 | char *dir, *file; | |
517 | { | |
518 | static char dfile[100]; | |
519 | register char *dp, *fp; | |
520 | register int i; | |
521 | ||
522 | dp = dfile; | |
523 | fp = dir; | |
524 | while (*fp) | |
525 | *dp++ = *fp++; | |
526 | *dp++ = '/'; | |
527 | fp = file; | |
528 | for (i=0; i<14; i++) | |
529 | *dp++ = *fp++; | |
530 | *dp = 0; | |
531 | return(dfile); | |
532 | } | |
533 | ||
534 | readdir(dir) | |
535 | char *dir; | |
536 | { | |
537 | static struct { | |
538 | int dinode; | |
539 | char dname[14]; | |
540 | } dentry; | |
541 | register char *p; | |
542 | register int j; | |
543 | register struct lbuf *ep; | |
544 | ||
545 | if (fopen(dir, &inf) < 0) { | |
546 | newline(); | |
547 | printf("%s unreadable\n", dir); | |
548 | return; | |
549 | } | |
550 | tblocks = 0; | |
551 | for(;;) { | |
552 | p = &dentry; | |
553 | for (j=0; j<16; j++) | |
554 | *p++ = getc(&inf); | |
555 | if (dentry.dinode==0 | |
556 | || aflg==0 && dentry.dname[0]=='.' && ( | |
557 | !Aflg || | |
558 | dentry.dname[1] == 0 || (dentry.dname[1] == '.' | |
559 | && dentry.dname[2] == 0))) | |
560 | continue; | |
561 | if (dentry.dinode == -1) | |
562 | break; | |
563 | ep = gstat(makename(dir, dentry.dname), 0); | |
564 | if (ep->lnum != -1) | |
565 | ep->lnum = dentry.dinode; | |
566 | for (j=0; j<14; j++) | |
567 | ep->lname[j] = dentry.dname[j]; | |
568 | } | |
569 | close(inf.fdes); | |
570 | } | |
571 | ||
572 | gstat(file, argfl) | |
573 | char *file; | |
574 | { | |
575 | struct ibuf statb; | |
576 | register struct lbuf *rep; | |
577 | ||
578 | if (lastp+1 >= rlastp) { | |
579 | sbrk(512); | |
580 | rlastp.idev =+ 512; | |
581 | } | |
582 | rep = lastp; | |
583 | lastp++; | |
584 | rep->lflags = 0; | |
585 | rep->lnum = 0; | |
586 | if (argfl || statreq) { | |
587 | if (stat(file, &statb)<0) { | |
588 | newline(); | |
589 | printf("%s not found\n", file); | |
590 | statb.inum = -1; | |
591 | statb.isize0 = 0; | |
592 | statb.isize = 0; | |
593 | statb.iflags = 0; | |
594 | if (argfl) { | |
595 | lastp--; | |
596 | return(0); | |
597 | } | |
598 | } | |
599 | rep->lnum = statb.inum; | |
600 | statb.iflags =& ~DIR; | |
601 | if ((statb.iflags&IFMT) == 060000) { | |
602 | statb.iflags =& ~020000; | |
603 | } else if ((statb.iflags&IFMT)==040000) { | |
604 | statb.iflags =& ~IFMT; | |
605 | statb.iflags =| DIR; | |
606 | } | |
607 | statb.iflags =& ~ LARGE; | |
608 | if (statb.iflags & RSTXT) | |
609 | statb.iflags =| STXT; | |
610 | statb.iflags =& ~ RSTXT; | |
611 | rep->lflags = statb.iflags; | |
612 | rep->luid = statb.iuid; | |
613 | rep->lgid = statb.igid; | |
614 | rep->lnl = statb.inl; | |
615 | rep->lsize0 = statb.isize0; | |
616 | rep->lsize = statb.isize; | |
617 | if (rep->lflags & (BLK|CHR) && lflg) { | |
618 | rep->lsize = statb.iaddr[0]; | |
619 | rep->lquot[0] = statb.iaddr[1]; | |
620 | rep->lquot[1] = statb.iaddr[2]; | |
621 | } | |
622 | rep->lmtime[0] = statb.imtime[0]; | |
623 | rep->lmtime[1] = statb.imtime[1]; | |
624 | if(uflg) { | |
625 | rep->lmtime[0] = statb.iatime[0]; | |
626 | rep->lmtime[1] = statb.iatime[1]; | |
627 | } | |
628 | tblocks =+ nblock(statb.isize0, statb.isize); | |
629 | } | |
630 | return(rep); | |
631 | } | |
632 | ||
633 | compar(ap1, ap2) | |
634 | struct lbuf *ap1, *ap2; | |
635 | { | |
636 | register struct lbuf *p1, *p2; | |
637 | register int i; | |
638 | int j; | |
639 | struct { char *charp;}; | |
640 | ||
641 | p1 = ap1; | |
642 | p2 = ap2; | |
643 | if (dflg==0) { | |
644 | if ((p1->lflags&(DIR|ISARG)) == (DIR|ISARG)) { | |
645 | if ((p2->lflags&(DIR|ISARG)) != (DIR|ISARG)) | |
646 | return(1); | |
647 | } else { | |
648 | if ((p2->lflags&(DIR|ISARG)) == (DIR|ISARG)) | |
649 | return(-1); | |
650 | } | |
651 | } | |
652 | if (tflg) { | |
653 | i = 0; | |
654 | if (p2->lmtime[0] > p1->lmtime[0]) | |
655 | i++; | |
656 | else if (p2->lmtime[0] < p1->lmtime[0]) | |
657 | i--; | |
658 | else if (p2->lmtime[1] > p1->lmtime[1]) | |
659 | i++; | |
660 | else if (p2->lmtime[1] < p1->lmtime[1]) | |
661 | i--; | |
662 | return(i*rflg); | |
663 | } | |
664 | if (p1->lflags&ISARG) | |
665 | p1 = p1->namep; | |
666 | else | |
667 | p1 = p1->lname; | |
668 | if (p2->lflags&ISARG) | |
669 | p2 = p2->namep; | |
670 | else | |
671 | p2 = p2->lname; | |
672 | for (;;) | |
673 | if ((j = *p1.charp++ - *p2.charp++) || p1.charp[-1]==0) | |
674 | return(rflg*j); | |
675 | return(0); | |
676 | } | |
677 | ||
678 | strcpy(to, from) | |
679 | char *to, *from; | |
680 | { | |
681 | ||
682 | while (*to++ = *from++) | |
683 | continue; | |
684 | } | |
685 | ||
686 | strcat(to, from) | |
687 | char *to, *from; | |
688 | { | |
689 | ||
690 | while (*to) | |
691 | to++; | |
692 | strcpy(to, from); | |
693 | } |