BSD 4_1_snap release
[unix-history] / usr / src / cmd / ls / ucbls.c
CommitLineData
31cef89c
BJ
1#define UCB /* Controls output format for -F */
2/* #define UCB_PWHASH /* If have hashed password file */
3
1217e91a
BJ
4/*
5 * ls - list file or directory
6 *
7 * Modified by Bill Joy UCB May/August 1977
31cef89c
BJ
8 * Modified by Dave Presotto BTL Feb/80
9 * Modified by Bill Joy and Mark Horton Summer 1980
1217e91a 10 *
31cef89c 11 * this version of ls is designed for graphic terminals and to
1217e91a
BJ
12 * list directories with lots of files in them compactly.
13 * It supports three variants for listings:
14 *
15 * 1) Columnar output.
16 * 2) Stream output.
17 * 3) Old one per line format.
18 *
19 * Columnar output is the default.
20 * If, however, the standard output is not a teletype, the default
21 * is one-per-line.
22 *
23 * With columnar output, the items are sorted down the columns.
24 * We use columns only for a directory we are interpreting.
25 * Thus, in particular, we do not use columns for
26 *
27 * ls /usr/bin/p*
28 *
29 * This version of ls also prints non-printing characters as '?' if
30 * the standard output is a teletype.
31 *
32 * Flags relating to these and other new features are:
33 *
34 * -m force stream output.
35 *
36 * -1 force one entry per line, e.g. to a teletype
37 *
38 * -q force non-printings to be '?'s, e.g. to a file
39 *
31cef89c 40 * -C force columnar output, e.g. into a file
1217e91a
BJ
41 *
42 * -n like -l, but user/group id's in decimal rather than
43 * looking in /etc/passwd to save time
31cef89c
BJ
44 *
45 * -F turns on the "flagging" of executables and directories
46 *
47 * -R causes ls to recurse through the branches of the subtree
48 * ala find
1217e91a
BJ
49 */
50
51#include <sys/param.h>
52#include <sys/stat.h>
53#include <sys/dir.h>
54#include <stdio.h>
55#include <ctype.h>
31cef89c
BJ
56#include <pwd.h>
57#include <grp.h>
58#include <utmp.h>
1217e91a 59
31cef89c
BJ
60struct utmp utmp;
61#define NMAX (sizeof utmp.ut_name)
1217e91a 62
31cef89c
BJ
63#define MAXFILEWIDTH 14
64#define NFILES 1024
1217e91a
BJ
65FILE *pwdf, *dirf;
66
67struct lbuf {
68 union {
69 char lname[15];
70 char *namep;
71 } ln;
72 char ltype;
31cef89c 73 ino_t lnum;
1217e91a
BJ
74 short lflags;
75 short lnl;
76 short luid;
77 short lgid;
78 long lsize;
79 long lmtime;
80};
81
31cef89c
BJ
82struct dchain {
83 char *dc_name; /* the path name */
84 struct dchain *dc_next; /* the next directory on the chain */
85};
86
87struct dchain *dfirst; /* the start of the directory chain */
88struct dchain *cdfirst; /* the start of the current directory chain */
89struct dchain *dtemp; /* temporary used when linking */
90char *curdir; /* the current directory */
91
92int aflg, bflg, dflg, lflg, sflg, tflg, uflg, iflg, fflg, gflg, cflg;
93int Aflg, nflg, qflg, Fflg, Rflg, across, Cflg;
1217e91a 94int nopad;
31cef89c 95int tabflg;
1217e91a
BJ
96int rflg = 1;
97long year;
98int flags;
1217e91a
BJ
99long tblocks;
100int statreq;
31cef89c 101int xtraent; /* for those switches which print out a total */
1217e91a
BJ
102struct lbuf *flist[NFILES];
103struct lbuf **lastp = flist;
104struct lbuf **firstp = flist;
105char *dotp = ".";
106
107char *makename();
108struct lbuf *gstat();
109char *ctime();
110long nblock();
31cef89c 111char *getname();
1217e91a 112
31cef89c
BJ
113#define ISARG 0100000
114int colwidth;
115int filewidth;
116int fixedwidth;
1217e91a
BJ
117int outcol;
118
119char obuf[BUFSIZ];
120
121main(argc, argv)
31cef89c 122int argc;
1217e91a
BJ
123char *argv[];
124{
31cef89c
BJ
125#include <sgtty.h>
126
127 int i, width;
128 register struct lbuf *ep;
1217e91a
BJ
129 register struct lbuf **slastp;
130 struct lbuf **epp;
131 struct lbuf lb;
132 char *t;
133 char *cp;
134 int compar();
31cef89c 135 struct sgttyb sgbuf;
1217e91a 136
31cef89c
BJ
137 Fflg = 0;
138 tabflg = 0;
1217e91a
BJ
139 Aflg = getuid() == 0;
140 setbuf(stdout, obuf);
31cef89c 141 lb.lmtime = time((long *) 0);
1217e91a 142 year = lb.lmtime - 6L*30L*24L*60L*60L; /* 6 months ago */
31cef89c
BJ
143 qflg = gtty(1, &sgbuf) == 0;
144
145 /* guarantee at least on column width */
146 fixedwidth = 2;
147
1217e91a
BJ
148 /*
149 * If the standard output is not a teletype,
150 * then we default to one-per-line format
151 * otherwise decide between stream and
152 * columnar based on our name.
153 */
154 if (qflg) {
31cef89c
BJ
155 Cflg = 1;
156 if ((sgbuf.sg_flags & XTABS) == 0)
157 tabflg++;
1217e91a
BJ
158 for (cp = argv[0]; cp[0] && cp[1]; cp++)
159 continue;
160 /*
31cef89c
BJ
161 * Certain kinds of links (l, ll, lr, lf, lx) cause some
162 * various options to be turned on.
1217e91a 163 */
31cef89c
BJ
164 switch (cp[0]) {
165 case 'l':
166 if (cp[-1] == 'l') {
167 /* ll => -l */
168 lflg = 1;
169 statreq++;
170 xtraent++;
171 } else {
172 /* l => -m */
173 nopad = 1;
174 Cflg = 0;
175 }
176 break;
177 case 'x': /* lx => -x */
1217e91a 178 across = 1;
31cef89c
BJ
179 break;
180 case 'f': /* lf => -F */
181 Fflg = 1;
182 break;
183 case 'r': /* lr => -R */
184 Rflg = 1;
185 break;
186 }
187 } else {
188 tabflg++;
1217e91a 189 }
31cef89c
BJ
190
191 while (--argc > 0 && *argv[1] == '-') {
1217e91a
BJ
192 argv++;
193 while (*++*argv) switch (**argv) {
194 /*
31cef89c 195 * C - force columnar output
1217e91a 196 */
31cef89c
BJ
197 case 'C':
198 Cflg = 1;
1217e91a
BJ
199 nopad = 0;
200 continue;
201 /*
202 * m - force stream output
203 */
204 case 'm':
31cef89c 205 Cflg = 0;
1217e91a
BJ
206 nopad = 1;
207 continue;
208 /*
209 * x - force sort across
210 */
211 case 'x':
212 across = 1;
213 nopad = 0;
31cef89c 214 Cflg = 1;
1217e91a
BJ
215 continue;
216 /*
217 * q - force ?'s in output
218 */
219 case 'q':
220 qflg = 1;
31cef89c
BJ
221 bflg = 0;
222 continue;
223 /*
224 * b - force octal value in output
225 */
226 case 'b':
227 bflg = 1;
228 qflg = 0;
1217e91a
BJ
229 continue;
230 /*
231 * 1 - force 1/line in output
232 */
233 case '1':
31cef89c 234 Cflg = 0;
1217e91a
BJ
235 nopad = 0;
236 continue;
237 /* STANDARD FLAGS */
238 case 'a':
239 aflg++;
240 continue;
241
242 case 'A':
243 Aflg = !Aflg;
244 continue;
245
31cef89c
BJ
246 case 'c':
247 cflg++;
248 continue;
249
1217e91a 250 case 's':
31cef89c 251 fixedwidth += 5;
1217e91a
BJ
252 sflg++;
253 statreq++;
31cef89c 254 xtraent++;
1217e91a
BJ
255 continue;
256
257 case 'd':
258 dflg++;
259 continue;
260
261 /*
262 * n - don't look in password file
263 */
264 case 'n':
265 nflg++;
266 case 'l':
267 lflg++;
268 statreq++;
31cef89c 269 xtraent++;
1217e91a
BJ
270 continue;
271
272 case 'r':
273 rflg = -1;
274 continue;
275
276 case 't':
277 tflg++;
278 statreq++;
279 continue;
280
281 case 'u':
282 uflg++;
283 continue;
284
285 case 'i':
31cef89c 286 fixedwidth += 6;
1217e91a
BJ
287 iflg++;
288 continue;
289
290 case 'f':
291 fflg++;
292 continue;
293
294 case 'g':
295 gflg++;
296 continue;
297
31cef89c
BJ
298 case 'F':
299 Fflg++;
300 continue;
301
302 case 'R':
303 Rflg++;
1217e91a 304 continue;
31cef89c
BJ
305
306 default:
307 fprintf (stderr, "usage: ls [-1ACFRabcdfgilmnqrstux] [files]\n");
308 exit(1);
1217e91a 309 }
1217e91a 310 }
31cef89c
BJ
311 if (Fflg)
312#ifdef UCB
313 fixedwidth++;
314#else
315 fixedwidth += 2;
316#endif
1217e91a
BJ
317 if (fflg) {
318 aflg++;
319 lflg = 0;
320 sflg = 0;
321 tflg = 0;
322 statreq = 0;
31cef89c 323 xtraent = 0;
1217e91a
BJ
324 }
325 if(lflg) {
31cef89c 326 Cflg = 0;
1217e91a
BJ
327 t = "/etc/passwd";
328 if (gflg)
329 t = "/etc/group";
330 nopad = 0;
31cef89c 331 fixedwidth = 70;
1217e91a
BJ
332 pwdf = fopen(t, "r");
333 }
334 if (argc==0) {
335 argc++;
336 argv = &dotp - 1;
337 }
338 for (i=0; i < argc; i++) {
31cef89c
BJ
339 argv++;
340 if (Cflg) {
341 width = strlen (*argv);
342 if (width > filewidth)
343 filewidth = width;
344 }
345 if ((ep = gstat(*argv, 1))==NULL)
1217e91a
BJ
346 continue;
347 ep->ln.namep = *argv;
348 ep->lflags |= ISARG;
349 }
31cef89c
BJ
350 if (!Cflg)
351 filewidth = MAXFILEWIDTH;
352 else
353 colwidth = fixedwidth + filewidth;
1217e91a
BJ
354 qsort(firstp, lastp - firstp, sizeof *lastp, compar);
355 slastp = lastp;
31cef89c 356 /* For each argument user typed */
1217e91a
BJ
357 for (epp=firstp; epp<slastp; epp++) {
358 ep = *epp;
31cef89c
BJ
359 if (ep->ltype=='d' && dflg==0 || fflg)
360 pdirectory(ep->ln.namep, (argc>1), slastp);
361 else
1217e91a 362 pentry(ep);
31cef89c
BJ
363
364 /* -R: print subdirectories found */
365 while (dfirst || cdfirst) {
366 /* Place direct subdirs on front in right order */
367 while (cdfirst) {
368 /* reverse cdfirst onto front of dfirst */
369 dtemp = cdfirst;
370 cdfirst = cdfirst -> dc_next;
371 dtemp -> dc_next = dfirst;
372 dfirst = dtemp;
373 }
374 /* take off first dir on dfirst & print it */
375 dtemp = dfirst;
376 dfirst = dfirst->dc_next;
377 pdirectory (dtemp->dc_name, 1, firstp);
378 cfree (dtemp->dc_name);
379 cfree (dtemp);
380 }
1217e91a
BJ
381 }
382 if (outcol)
383 putc('\n', stdout);
384 fflush(stdout);
385}
386
31cef89c
BJ
387/*
388 * pdirectory: print the directory name, labelling it if title is
389 * nonzero, using lp as the place to start reading in the dir.
390 */
391pdirectory (name, title, lp)
392char *name;
393int title;
394struct lbuf **lp;
395{
396 register struct dchain *dp;
397 register struct lbuf *ap;
398 register char *pname;
399 struct lbuf **app;
400
401 filewidth = 0;
402 curdir = name;
403 if (title)
404 printf("\n%s:\n", name);
405 lastp = lp;
406 readdir(name);
407 if (!Cflg)
408 filewidth = MAXFILEWIDTH;
409 colwidth = fixedwidth + filewidth;
410#ifdef notdef
411 /* Taken out because it appears this is done below in pem. */
412 if (tabflg) {
413 if (colwidth <= 8)
414 colwidth = 8;
415 else
416 if (colwidth <= 16)
417 colwidth = 16;
418 }
419#endif
420 if (fflg==0)
421 qsort(lp,lastp - lp,sizeof *lastp,compar);
422 if (Rflg) for (app=lastp-1; app>=lp; app--) {
423 ap = *app;
424 if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") &&
425 strcmp(ap->ln.lname, "..")) {
426 dp = (struct dchain *) calloc(1, sizeof(struct dchain));
427 pname = makename (curdir, ap->ln.lname);
428 dp->dc_name = (char *) calloc(1, strlen(pname)+1);
429 strcpy(dp->dc_name, pname);
430 dp -> dc_next = dfirst;
431 dfirst = dp;
432 }
433 }
434 if (lflg || sflg)
435 printf("total %D", tblocks);
436 pem(lp, lastp);
437 newline();
438}
439
440/*
441 * pem: print 'em. Print a list of files (e.g. a directory) bounded
442 * by slp and lp.
443 */
1217e91a
BJ
444pem(slp, lp)
445 register struct lbuf **slp, **lp;
446{
447 int ncols, nrows, row, col;
448 register struct lbuf **ep;
449
31cef89c
BJ
450 if (tabflg) {
451 if (colwidth <= 9)
452 colwidth = 8;
453 else
454 if (colwidth <= 17)
455 colwidth = 16;
456 }
1217e91a 457 ncols = 80 / colwidth;
31cef89c 458 if (ncols == 1 || Cflg == 0) {
1217e91a
BJ
459 for (ep = slp; ep < lp; ep++)
460 pentry(*ep);
461 return;
462 }
463 if (across) {
464 for (ep = slp; ep < lp; ep++)
465 pentry(*ep);
466 return;
467 }
31cef89c 468 if (xtraent)
1217e91a
BJ
469 slp--;
470 nrows = (lp - slp - 1) / ncols + 1;
471 for (row = 0; row < nrows; row++) {
31cef89c 472 col = row == 0 && xtraent;
1217e91a
BJ
473 for (; col < ncols; col++) {
474 ep = slp + (nrows * col) + row;
475 if (ep < lp)
476 pentry(*ep);
477 }
478 if (outcol)
479 printf("\n");
480 }
481}
482
31cef89c
BJ
483/*
484 * pputchar: like putchar but knows how to handle control chars.
485 * CAUTION: if you make ctrl chars print in ^x notation, or any
486 * other notation which is wider than one character, the column
487 * nature of things (such as files with 14 letter names) will be
488 * messed up. Weigh this carefully!
489 */
1217e91a
BJ
490pputchar(c)
491 char c;
492{
31cef89c 493 char cc;
1217e91a
BJ
494
495 switch (c) {
496 case '\t':
497 outcol = (outcol + 8) &~ 7;
498 break;
499 case '\n':
500 outcol = 0;
501 break;
502 default:
31cef89c
BJ
503 if (c < ' ' || c >= 0177) {
504 if (qflg)
505 c = '?';
506 else if (bflg) {
507 outcol += 3;
508 putc ('\\', stdout);
509 cc = '0' + (c>>6 & 07);
510 putc (cc, stdout);
511 cc = '0' + (c>>3 & 07);
512 putc (cc, stdout);
513 c = '0' + (c & 07);
514 }
515 }
1217e91a
BJ
516 outcol++;
517 break;
518 }
519 putc(c, stdout);
520}
521
522newline()
523{
524 if (outcol)
525 putc('\n', stdout);
526 outcol = 0;
527}
528
31cef89c
BJ
529/*
530 * column: get to the beginning of the next column.
531 */
1217e91a
BJ
532column()
533{
534
535 if (outcol == 0)
536 return;
537 if (nopad) {
538 putc(',', stdout);
539 outcol++;
540 if (outcol + colwidth + 2 > 80) {
541 putc('\n', stdout);
542 outcol = 0;
543 return;
544 }
545 putc(' ', stdout);
546 outcol++;
547 return;
548 }
31cef89c 549 if (Cflg == 0) {
1217e91a
BJ
550 putc('\n', stdout);
551 return;
552 }
553 if ((outcol / colwidth + 2) * colwidth > 80) {
554 putc('\n', stdout);
555 outcol = 0;
556 return;
557 }
31cef89c
BJ
558 if (tabflg && (colwidth <= 16)) {
559 if (colwidth > 8)
560 if ((outcol % 16) < 8) {
561 outcol += 8 - (outcol % 8);
562 putc ('\t', stdout);
563 }
564 outcol += 8 - (outcol % 8);
565 putc ('\t', stdout);
566 return;
567 }
1217e91a
BJ
568 do {
569 outcol++;
570 putc(' ', stdout);
571 } while (outcol % colwidth);
572}
573
574
31cef89c
BJ
575/*
576 * nblock: the number of 512 byte blocks a size byte file takes up.
577 * (Note: the number stays 512 no matter what BUFSIZ or the filesystem uses.)
578 */
1217e91a
BJ
579long
580nblock(size)
581long size;
582{
583 return((size+511)>>9);
584}
585
31cef89c
BJ
586/*
587 * This code handles the rwx- business.
588 * You figure it out.
589 */
1217e91a
BJ
590int m1[] = { 1, S_IREAD>>0, 'r', '-' };
591int m2[] = { 1, S_IWRITE>>0, 'w', '-' };
592int m3[] = { 2, S_ISUID, 's', S_IEXEC>>0, 'x', '-' };
593int m4[] = { 1, S_IREAD>>3, 'r', '-' };
594int m5[] = { 1, S_IWRITE>>3, 'w', '-' };
595int m6[] = { 2, S_ISGID, 's', S_IEXEC>>3, 'x', '-' };
596int m7[] = { 1, S_IREAD>>6, 'r', '-' };
597int m8[] = { 1, S_IWRITE>>6, 'w', '-' };
598int m9[] = { 2, S_ISVTX, 't', S_IEXEC>>6, 'x', '-' };
599
600int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
601
602pmode(aflag)
603{
604 register int **mp;
605
606 flags = aflag;
607 for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])];)
608 select(*mp++);
609}
610
611select(pairp)
612register int *pairp;
613{
614 register int n;
615
616 n = *pairp++;
617 while (--n>=0 && (flags&*pairp++)==0)
618 pairp++;
619 pputchar(*pairp);
620}
621
31cef89c
BJ
622/*
623 * returns cat(dir, "/", file), unless dir ends in /, when it doesn't //
624 */
1217e91a
BJ
625char *
626makename(dir, file)
627char *dir, *file;
628{
629 static char dfile[100];
630 register char *dp, *fp;
631 register int i;
632
633 dp = dfile;
634 fp = dir;
635 while (*fp)
636 *dp++ = *fp++;
31cef89c 637 if (*(dp-1) != '/')
1217e91a
BJ
638 *dp++ = '/';
639 fp = file;
640 for (i=0; i<DIRSIZ; i++)
641 *dp++ = *fp++;
642 *dp = 0;
643 return(dfile);
644}
645
31cef89c
BJ
646/*
647 * readdir: read in the directory whose name is dir,
648 * starting at lastp.
649 */
1217e91a
BJ
650readdir(dir)
651char *dir;
652{
653 static struct direct dentry;
31cef89c 654 register int j, width;
1217e91a
BJ
655 register struct lbuf *ep;
656
657 if ((dirf = fopen(dir, "r")) == NULL) {
658 printf("%s unreadable\n", dir);
659 return;
660 }
661 tblocks = 0;
662 for(;;) {
663 if (fread(&dentry, sizeof(dentry), 1, dirf) != 1)
664 break;
665 if (dentry.d_ino==0 ||
666 aflg==0 && dentry.d_name[0]=='.' && (
667 !Aflg ||
668 dentry.d_name[1]=='\0'
669 || dentry.d_name[1]=='.' && dentry.d_name[2]=='\0'))
670 continue;
31cef89c
BJ
671 if (Cflg) {
672 width = strlen (dentry.d_name);
673 if (width > filewidth)
674 filewidth = width;
675 }
676 ep = gstat(makename(dir, dentry.d_name), Fflg || Rflg);
1217e91a
BJ
677 if (ep==NULL)
678 continue;
679 if (ep->lnum != -1)
680 ep->lnum = dentry.d_ino;
681 for (j=0; j<DIRSIZ; j++)
682 ep->ln.lname[j] = dentry.d_name[j];
683 }
684 fclose(dirf);
685}
686
31cef89c
BJ
687/*
688 * stat the given file and return an lbuf containing it.
689 * argfl is nonzero if a stat is required because the file is
690 * an argument, rather than having been found in a directory.
691 */
1217e91a
BJ
692struct lbuf *
693gstat(file, argfl)
694char *file;
695{
696 struct stat statb;
697 register struct lbuf *rep;
698 static int nomocore;
699
700 if (nomocore)
701 return(NULL);
702 rep = (struct lbuf *)malloc(sizeof(struct lbuf));
703 if (rep==NULL) {
704 fprintf(stderr, "ls: out of memory\n");
705 nomocore = 1;
706 return(NULL);
707 }
708 if (lastp >= &flist[NFILES]) {
709 static int msg;
710 lastp--;
711 if (msg==0) {
712 fprintf(stderr, "ls: too many files\n");
713 msg++;
714 }
715 }
716 *lastp++ = rep;
717 rep->lflags = 0;
718 rep->lnum = 0;
719 rep->ltype = '-';
720 if (argfl || statreq) {
721 if (stat(file, &statb)<0) {
722 printf("%s not found\n", file);
723 statb.st_ino = -1;
724 statb.st_size = 0;
725 statb.st_mode = 0;
726 if (argfl) {
727 lastp--;
728 return(0);
729 }
730 }
731 rep->lnum = statb.st_ino;
732 rep->lsize = statb.st_size;
733 switch(statb.st_mode&S_IFMT) {
734
735 case S_IFDIR:
736 rep->ltype = 'd';
737 break;
738
739 case S_IFBLK:
740 rep->ltype = 'b';
741 rep->lsize = statb.st_rdev;
742 break;
743
744 case S_IFCHR:
745 rep->ltype = 'c';
746 rep->lsize = statb.st_rdev;
747 break;
31cef89c
BJ
748
749 case S_IFMPB:
750 rep->ltype = 'M';
751 rep->lsize = statb.st_rdev;
752 break;
753
754 case S_IFMPC:
755 rep->ltype = 'm';
756 rep->lsize = statb.st_rdev;
757 break;
1217e91a
BJ
758 }
759 rep->lflags = statb.st_mode & ~S_IFMT;
760 rep->luid = statb.st_uid;
761 rep->lgid = statb.st_gid;
762 rep->lnl = statb.st_nlink;
763 if(uflg)
764 rep->lmtime = statb.st_atime;
765 else if (cflg)
766 rep->lmtime = statb.st_ctime;
767 else
768 rep->lmtime = statb.st_mtime;
769 tblocks += nblock(statb.st_size);
770 }
771 return(rep);
772}
773
31cef89c
BJ
774/*
775 * decide whether to print pp1 before or after pp2, based on their
776 * names, various times, and the r flag.
777 */
1217e91a
BJ
778compar(pp1, pp2)
779struct lbuf **pp1, **pp2;
780{
781 register struct lbuf *p1, *p2;
782
783 p1 = *pp1;
784 p2 = *pp2;
785 if (dflg==0) {
786 if (p1->lflags&ISARG && p1->ltype=='d') {
787 if (!(p2->lflags&ISARG && p2->ltype=='d'))
788 return(1);
789 } else {
790 if (p2->lflags&ISARG && p2->ltype=='d')
791 return(-1);
792 }
793 }
794 if (tflg) {
795 if(p2->lmtime == p1->lmtime)
796 return(0);
797 if(p2->lmtime > p1->lmtime)
798 return(rflg);
799 return(-rflg);
800 }
801 return(rflg * strcmp(p1->lflags&ISARG? p1->ln.namep: p1->ln.lname,
802 p2->lflags&ISARG? p2->ln.namep: p2->ln.lname));
803}
31cef89c
BJ
804
805/*
806 * print the entry pointed at by ap
807 */
1217e91a
BJ
808pentry(ap)
809struct lbuf *ap;
810{
811 struct { char dminor, dmajor;};
1217e91a
BJ
812 register struct lbuf *p;
813 register char *cp;
31cef89c
BJ
814 char fname[100];
815 char *pname;
816 struct passwd *getpwuid();
817 struct passwd *pwptr;
818 struct group *getgrgid();
819 struct group *grptr;
1217e91a 820
31cef89c 821 fname[0] = 0;
1217e91a
BJ
822 p = ap;
823 if (p->lnum == -1)
824 return;
825 column();
826 if (iflg)
827 if (nopad && !lflg)
828 printf("%d ", p->lnum);
829 else
830 printf("%5d ", p->lnum);
831 if (sflg)
4b9ccde7
C
832 switch (p->ltype) {
833
834 case 'b':
835 case 'c':
836 case 'm':
837 case 'M':
838 if (nopad && !lflg)
839 printf("%D ", 0);
840 else
841 printf("%4D ", 0);
842 break;
843
844 default:
845 if (nopad && !lflg)
846 printf("%D ", nblock(p->lsize));
847 else
848 printf("%4D ", nblock(p->lsize));
849 break;
850 }
1217e91a
BJ
851 if (lflg) {
852 pputchar(p->ltype);
853 pmode(p->lflags);
854 printf("%2d ", p->lnl);
31cef89c
BJ
855 if(gflg) {
856 grptr = getgrgid(p->lgid);
857 if (nflg == 0 && grptr != 0)
858 printf("%-8.8s", grptr->gr_name);
859 else
860 printf("%-8d", p->lgid);
861 } else {
862#ifndef UCB_PWHASH
863 char *name;
864 if (nflg == 0 && (name = getname(p->luid))) {
865 printf("%-8.8s", name);
866 }
867#else
868 pwptr = getpwuid(p->luid);
869 if (nflg == 0 && pwptr != 0)
870 printf("%-8.8s", pwptr->pw_name);
871#endif
872 else
873 printf("%-8d", p->luid);
874 }
875 switch (p->ltype) {
876
877 case 'b':
878 case 'c':
879 case 'm':
880 case 'M':
881 printf("%3d,%3d",
882 major((int)p->lsize), minor((int)p->lsize));
883 break;
884 default:
1217e91a 885 printf("%7ld", p->lsize);
31cef89c 886 }
1217e91a
BJ
887 cp = ctime(&p->lmtime);
888 if(p->lmtime < year)
889 printf(" %-7.7s %-4.4s ", cp+4, cp+20); else
890 printf(" %-12.12s ", cp+4);
891 }
31cef89c
BJ
892#ifndef UCB
893 if (Fflg) {
894 if (p->ltype == 'd')
895 strcat (fname, "[");
896 else if (p->lflags & 0111)
897 strcat (fname, "*");
898 else if (!nopad)
899 strcat (fname, " ");
900 }
901#endif
902 if (p->lflags & ISARG)
903 strncat (fname, p->ln.namep, 98);
1217e91a 904 else
31cef89c
BJ
905 strncat (fname, p->ln.lname, 14);
906#ifndef UCB
907 if (Fflg) {
908 if (p->ltype == 'd')
909 strcat (fname, "]");
910 else if (!nopad)
911 strcat (fname, " ");
912 }
913#else
914 if (Fflg) {
915 if (p->ltype == 'd')
916 strcat (fname, "/");
917 else if (p->lflags & 0111)
918 strcat (fname, "*");
919 else if (!nopad)
920 strcat (fname, " ");
921 }
922#endif
923 printf ("%s", fname);
924 free(ap);
1217e91a 925}
31cef89c 926
1217e91a 927/* char printf_id[] = "@(#) printf.c:2.2 6/5/79";*/
31cef89c 928
1217e91a 929#include "varargs.h"
31cef89c
BJ
930
931/*
932 * This version of printf is compatible with the Version 7 C
1217e91a
BJ
933 * printf. The differences are only minor except that this
934 * printf assumes it is to print through pputchar. Version 7
935 * printf is more general (and is much larger) and includes
936 * provisions for floating point.
937 */
1217e91a 938
31cef89c
BJ
939#define MAXOCT 11 /* Maximum octal digits in a long */
940#define MAXINT 32767 /* largest normal length positive integer */
1217e91a 941#define BIG 1000000000 /* largest power of 10 less than an unsigned long */
31cef89c 942#define MAXDIGS 10 /* number of digits in BIG */
1217e91a
BJ
943
944static int width, sign, fill;
945
946char *b_dconv();
947
948printf(va_alist)
949 va_dcl
950{
951 va_list ap;
952 register char *fmt;
953 char fcode;
954 int prec;
955 int length,mask1,nbits,n;
956 long int mask2, num;
957 register char *bptr;
958 char *ptr;
959 char buf[134];
960
961 va_start(ap);
962 fmt = va_arg(ap,char *);
963 for (;;) {
964 /* process format string first */
965 while ((fcode = *fmt++)!='%') {
966 /* ordinary (non-%) character */
967 if (fcode=='\0')
968 return;
969 pputchar(fcode);
970 }
971 /* length modifier: -1 for h, 1 for l, 0 for none */
972 length = 0;
973 /* check for a leading - sign */
974 sign = 0;
975 if (*fmt == '-') {
976 sign++;
977 fmt++;
978 }
979 /* a '0' may follow the - sign */
980 /* this is the requested fill character */
981 fill = 1;
982 if (*fmt == '0') {
983 fill--;
984 fmt++;
985 }
986
987 /* Now comes a digit string which may be a '*' */
988 if (*fmt == '*') {
989 width = va_arg(ap, int);
990 if (width < 0) {
991 width = -width;
992 sign = !sign;
993 }
994 fmt++;
995 }
996 else {
997 width = 0;
998 while (*fmt>='0' && *fmt<='9')
999 width = width * 10 + (*fmt++ - '0');
1000 }
1001
1002 /* maybe a decimal point followed by more digits (or '*') */
1003 if (*fmt=='.') {
1004 if (*++fmt == '*') {
1005 prec = va_arg(ap, int);
1006 fmt++;
1007 }
1008 else {
1009 prec = 0;
1010 while (*fmt>='0' && *fmt<='9')
1011 prec = prec * 10 + (*fmt++ - '0');
1012 }
1013 }
1014 else
1015 prec = -1;
1016
1017 /*
1018 * At this point, "sign" is nonzero if there was
1019 * a sign, "fill" is 0 if there was a leading
1020 * zero and 1 otherwise, "width" and "prec"
1021 * contain numbers corresponding to the digit
1022 * strings before and after the decimal point,
1023 * respectively, and "fmt" addresses the next
1024 * character after the whole mess. If there was
1025 * no decimal point, "prec" will be -1.
1026 */
1027 switch (*fmt) {
1028 case 'L':
1029 case 'l':
1030 length = 2;
1031 /* no break!! */
1032 case 'h':
1033 case 'H':
1034 length--;
1035 fmt++;
1036 break;
1037 }
1038
1039 /*
1040 * At exit from the following switch, we will
1041 * emit the characters starting at "bptr" and
1042 * ending at "ptr"-1, unless fcode is '\0'.
1043 */
1044 switch (fcode = *fmt++) {
1045 /* process characters and strings first */
1046 case 'c':
1047 buf[0] = va_arg(ap, int);
1048 ptr = bptr = &buf[0];
1049 if (buf[0] != '\0')
1050 ptr++;
1051 break;
1052 case 's':
1053 bptr = va_arg(ap,char *);
1054 if (bptr==0)
1055 bptr = "(null pointer)";
1056 if (prec < 0)
1057 prec = MAXINT;
1058 for (n=0; *bptr++ && n < prec; n++) ;
1059 ptr = --bptr;
1060 bptr -= n;
1061 break;
1062 case 'O':
1063 length = 1;
1064 fcode = 'o';
1065 /* no break */
1066 case 'o':
1067 case 'X':
1068 case 'x':
1069 if (length > 0)
1070 num = va_arg(ap,long);
1071 else
1072 num = (unsigned)va_arg(ap,int);
1073 if (fcode=='o') {
1074 mask1 = 0x7;
1075 mask2 = 0x1fffffffL;
1076 nbits = 3;
1077 }
1078 else {
1079 mask1 = 0xf;
1080 mask2 = 0x0fffffffL;
1081 nbits = 4;
1082 }
1083 n = (num!=0);
1084 bptr = buf + MAXOCT + 3;
1085 /* shift and mask for speed */
1086 do
1087 if (((int) num & mask1) < 10)
31cef89c 1088 *--bptr = ((int) num & mask1) + 060;
1217e91a
BJ
1089 else
1090 *--bptr = ((int) num & mask1) + 0127;
1091 while (num = (num >> nbits) & mask2);
1092
1093 if (fcode=='o') {
1094 if (n)
1095 *--bptr = '0';
1096 }
1097 else
1098 if (!sign && fill <= 0) {
1099 pputchar('0');
1100 pputchar(fcode);
1101 width -= 2;
1102 }
1103 else {
1104 *--bptr = fcode;
1105 *--bptr = '0';
1106 }
1107 ptr = buf + MAXOCT + 3;
1108 break;
1109 case 'D':
1110 case 'U':
1111 case 'I':
1112 length = 1;
31cef89c
BJ
1113
1114 fcode = fcode + 'a' - 'A';
1217e91a
BJ
1115 /* no break */
1116 case 'd':
1117 case 'i':
1118 case 'u':
1119 if (length > 0)
1120 num = va_arg(ap,long);
1121 else {
1122 n = va_arg(ap,int);
1123 if (fcode=='u')
1124 num = (unsigned) n;
1125 else
1126 num = (long) n;
1127 }
1128 if (n = (fcode != 'u' && num < 0))
1129 num = -num;
1130 /* now convert to digits */
1131 bptr = b_dconv(num, buf);
1132 if (n)
1133 *--bptr = '-';
1134 if (fill == 0)
1135 fill = -1;
1136 ptr = buf + MAXDIGS + 1;
1137 break;
1138 default:
1139 /* not a control character,
1140 * print it.
1141 */
1142 ptr = bptr = &fcode;
1143 ptr++;
1144 break;
1145 }
1146 if (fcode != '\0')
1147 b_emit(bptr,ptr);
1148 }
1149 va_end(ap);
1150}
1151
1152/* b_dconv converts the unsigned long integer "value" to
1153 * printable decimal and places it in "buffer", right-justified.
1154 * The value returned is the address of the first non-zero character,
1155 * or the address of the last character if all are zero.
1156 * The result is NOT null terminated, and is MAXDIGS characters long,
1157 * starting at buffer[1] (to allow for insertion of a sign).
1158 *
1159 * This program assumes it is running on 2's complement machine
1160 * with reasonable overflow treatment.
1161 */
1162char *
1163b_dconv(value, buffer)
1164 long value;
1165 char *buffer;
1166{
1167 register char *bp;
1168 register int svalue;
1169 int n;
1170 long lval;
1171
1172 bp = buffer;
1173
1174 /* zero is a special case */
1175 if (value == 0) {
1176 bp += MAXDIGS;
1177 *bp = '0';
1178 return(bp);
1179 }
1180
1181 /* develop the leading digit of the value in "n" */
1182 n = 0;
1183 while (value < 0) {
1184 value -= BIG; /* will eventually underflow */
1185 n++;
1186 }
1187 while ((lval = value - BIG) >= 0) {
1188 value = lval;
1189 n++;
1190 }
1191
1192 /* stash it in buffer[1] to allow for a sign */
1193 bp[1] = n + '0';
1194 /*
1195 * Now develop the rest of the digits. Since speed counts here,
1196 * we do it in two loops. The first gets "value" down until it
1197 * is no larger than MAXINT. The second one uses integer divides
1198 * rather than long divides to speed it up.
1199 */
1200 bp += MAXDIGS + 1;
1201 while (value > MAXINT) {
1202 *--bp = (int)(value % 10) + '0';
1203 value /= 10;
1204 }
1205
1206 /* cannot lose precision */
1207 svalue = value;
1208 while (svalue > 0) {
1209 *--bp = (svalue % 10) + '0';
1210 svalue /= 10;
1211 }
1212
1213 /* fill in intermediate zeroes if needed */
1214 if (buffer[1] != '0') {
1215 while (bp > buffer + 2)
1216 *--bp = '0';
1217 --bp;
1218 }
1219 return(bp);
1220}
1221
1222/*
1223 * This program sends string "s" to pputchar. The character after
1224 * the end of "s" is given by "send". This allows the size of the
1225 * field to be computed; it is stored in "alen". "width" contains the
1226 * user specified length. If width<alen, the width will be taken to
1227 * be alen. "sign" is zero if the string is to be right-justified
1228 * in the field, nonzero if it is to be left-justified. "fill" is
1229 * 0 if the string is to be padded with '0', positive if it is to be
1230 * padded with ' ', and negative if an initial '-' should appear before
1231 * any padding in right-justification (to avoid printing "-3" as
1232 * "000-3" where "-0003" was intended).
1233 */
1234b_emit(s, send)
1235 register char *s;
1236 char *send;
1237{
1238 char cfill;
1239 register int alen;
1240 int npad;
1241
1242 alen = send - s;
1243 if (alen > width)
1244 width = alen;
1245 cfill = fill>0? ' ': '0';
1246
1247 /* we may want to print a leading '-' before anything */
1248 if (*s == '-' && fill < 0) {
1249 pputchar(*s++);
1250 alen--;
1251 width--;
1252 }
1253 npad = width - alen;
1254
1255 /* emit any leading pad characters */
1256 if (!sign)
1257 while (--npad >= 0)
1258 pputchar(cfill);
1259
1260 /* emit the string itself */
1261 while (--alen >= 0)
1262 pputchar(*s++);
1263
1264 /* emit trailing pad characters */
1265 if (sign)
1266 while (--npad >= 0)
1267 pputchar(cfill);
1268}
31cef89c
BJ
1269
1270#ifndef UCB_PWHASH
1271#define NUID 2048
1272
1273char names[NUID][NMAX+1];
1274
1275char *
1276getname(uid)
1277{
1278 register struct passwd *pw;
1279 static init;
1280 struct passwd *getpwent();
1281
1282 if (uid >= 0 && uid < NUID && names[uid][0])
1283 return (&names[uid][0]);
1284 if (init == 2)
1285 return (0);
1286 if (init == 0)
1287 setpwent(), init = 1;
1288 while (pw = getpwent()) {
1289 if (pw->pw_uid < 0 || pw->pw_uid >= NUID)
1290 continue;
1291 if (names[pw->pw_uid][0])
1292 continue;
1293 strncpy(names[pw->pw_uid], pw->pw_name, NMAX);
1294 if (pw->pw_uid == uid)
1295 return (&names[uid][0]);
1296 }
1297 init = 2;
1298 endpwent();
1299 return (0);
1300}
1301#endif