BSD 2 development
[unix-history] / upgrade / src / ls.c
CommitLineData
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
43struct {
44 int fdes;
45 int nleft;
46 char *nextc;
47 char buff[512];
48} inf, obuf;
49
50struct 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
64struct 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
77struct lbufx {
78 char *namep;
79};
80
81int aflg, dflg, lflg, sflg, tflg, uflg, iflg, fflg, nflg, Aflg;
82int cflg, qflg, across;
83int nopad;
84int rflg 1;
85char *year;
86int flags;
87int uidfil -1;
88int lastuid -1;
89char tbuf[16];
90int tblocks;
91int statreq;
92extern end;
93struct lbuf *lastp &end;
94struct lbuf *rlastp &end;
95char *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
117int colwidth 16;
118int outcol;
119
120main(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
298pem(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
330putchar(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
350newline()
351{
352 if (outcol)
353 putc('\n', &obuf);
354 outcol = 0;
355}
356
357column()
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
398pentry(ap)
399struct 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
461nblock(size0, size)
462int 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
474int m0[] { 3, DIR, 'd', BLK, 'b', CHR, 'c', '-'};
475int m1[] { 1, ROWN, 'r', '-' };
476int m2[] { 1, WOWN, 'w', '-' };
477int m3[] { 2, SUID, 's', XOWN, 'x', '-' };
478int m1a[] { 1, RGRP, 'r', '-' };
479int m1b[] { 1, WGRP, 'w', '-' };
480int m1c[] { 2, SGID, 's', XGRP, 'x', '-' };
481int m4[] { 1, ROTH, 'r', '-' };
482int m5[] { 1, WOTH, 'w', '-' };
483int m6[] { 2, STXT, 't', XOTH, 'x', '-' };
484
485int *m[] { m0, m1, m2, m3, m1a, m1b, m1c, m4, m5, m6};
486
487pmode(ptr)
488char *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
503select(pairp)
504int *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
515makename(dir, file)
516char *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
534readdir(dir)
535char *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
572gstat(file, argfl)
573char *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
633compar(ap1, ap2)
634struct 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
678strcpy(to, from)
679 char *to, *from;
680{
681
682 while (*to++ = *from++)
683 continue;
684}
685
686strcat(to, from)
687 char *to, *from;
688{
689
690 while (*to)
691 to++;
692 strcpy(to, from);
693}