date and time created 87/02/05 15:19:02 by slatteng
[unix-history] / usr / src / bin / ls / ls.c
CommitLineData
bcf1365c
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
9"@(#) Copyright (c) 1980 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
547b0031 13#ifndef lint
cc83ec2c 14static char sccsid[] = "@(#)ls.c 5.6 (Berkeley) %G%";
bcf1365c 15#endif not lint
547b0031
BJ
16
17/*
18 * ls
19 *
905d30b9
SL
20 * 4.2bsd version for symbolic links, variable length
21 * directory entries, block size in the inode, etc.
547b0031 22 */
547b0031
BJ
23#include <sys/param.h>
24#include <sys/stat.h>
e957bf05 25#include <sys/dir.h>
547b0031
BJ
26#include <stdio.h>
27#include <sgtty.h>
28
905d30b9 29#define kbytes(size) (((size) + 1023) / 1024)
547b0031
BJ
30
31struct afile {
32 char ftype; /* file type, e.g. 'd', 'c', 'f' */
33 ino_t fnum; /* inode number of file */
34 short fflags; /* mode&~S_IFMT, perhaps ISARG */
35 short fnl; /* number of links */
36 short fuid; /* owner id */
37 short fgid; /* group id */
38 long fsize; /* file size */
905d30b9 39 long fblks; /* number of blocks used */
547b0031
BJ
40 time_t fmtime; /* time (modify or access or create) */
41 char *fname; /* file name */
42 char *flinkto; /* symbolic link value */
43};
44
45#define ISARG 0x8000 /* extra ``mode'' */
46
47struct subdirs {
48 char *sd_name;
49 struct subdirs *sd_next;
50} *subdirs;
51
9fcd824c 52int aflg, dflg, gflg, lflg, sflg, tflg, uflg, iflg, fflg, cflg, rflg = 1;
1c52c7b1 53int qflg, Aflg, Cflg, Fflg, Lflg, Rflg, Sflg;
547b0031
BJ
54
55int usetabs;
56
57time_t now, sixmonthsago;
58
59char *dotp = ".";
60
accfb8ba 61struct winsize win;
fdae50a9
JB
62int twidth;
63
547b0031
BJ
64struct afile *gstat();
65int fcmp();
66char *cat(), *savestr();
67char *fmtentry();
68char *getname(), *getgroup();
69
70char *ctime();
71char *malloc(), *calloc(), *realloc();
72char *sprintf(), *strcpy(), *strcat();
73
74main(argc, argv)
75 int argc;
76 char *argv[];
77{
78 int i;
79 struct afile *fp0, *fplast;
80 register struct afile *fp;
81 struct sgttyb sgbuf;
82
83 argc--, argv++;
84 if (getuid() == 0)
85 Aflg++;
86 (void) time(&now); sixmonthsago = now - 6L*30L*24L*60L*60L; now += 60;
fdae50a9 87 twidth = 80;
547b0031 88 if (isatty(1)) {
547b0031
BJ
89 qflg = Cflg = 1;
90 (void) gtty(1, &sgbuf);
fdae50a9
JB
91 if (ioctl(1, TIOCGWINSZ, &win) != -1)
92 twidth = (win.ws_col == 0 ? 80 : win.ws_col);
7008e61f 93 if ((sgbuf.sg_flags & XTABS) != XTABS)
547b0031
BJ
94 usetabs = 1;
95 } else
96 usetabs = 1;
97 while (argc > 0 && **argv == '-') {
98 (*argv)++;
99 while (**argv) switch (*(*argv)++) {
100
101 case 'C':
102 Cflg = 1; break;
103 case 'q':
104 qflg = 1; break;
105 case '1':
106 Cflg = 0; break;
107 case 'a':
108 aflg++; break;
109 case 'A':
110 Aflg++; break;
111 case 'c':
112 cflg++; break;
1c52c7b1
JB
113 case 'S':
114 Sflg++; /* fall into... */
547b0031
BJ
115 case 's':
116 sflg++; break;
117 case 'd':
118 dflg++; break;
119 case 'g':
120 gflg++; break;
121 case 'l':
122 lflg++; break;
123 case 'r':
124 rflg = -1; break;
125 case 't':
126 tflg++; break;
127 case 'u':
128 uflg++; break;
129 case 'i':
130 iflg++; break;
9fcd824c
SL
131 case 'f':
132 fflg++; break;
791c0121
BJ
133 case 'L':
134 Lflg++; break;
547b0031
BJ
135 case 'F':
136 Fflg++; break;
137 case 'R':
138 Rflg++; break;
139 }
140 argc--, argv++;
141 }
9fcd824c 142 if (fflg) {
1c52c7b1 143 aflg++; Sflg = 0; tflg = 0; /* -f: only turn off sort flags */
9fcd824c 144 }
547b0031
BJ
145 if (lflg)
146 Cflg = 0;
147 if (argc == 0) {
148 argc++;
149 argv = &dotp;
150 }
151 fp = (struct afile *)calloc(argc, sizeof (struct afile));
152 if (fp == 0) {
153 fprintf(stderr, "ls: out of memory\n");
154 exit(1);
155 }
156 fp0 = fp;
157 for (i = 0; i < argc; i++) {
2955376b
BJ
158 if (gstat(fp, *argv, 1, (int *)0)) {
159 fp->fname = *argv;
160 fp->fflags |= ISARG;
161 fp++;
162 }
547b0031
BJ
163 argv++;
164 }
165 fplast = fp;
166 qsort(fp0, fplast - fp0, sizeof (struct afile), fcmp);
167 if (dflg) {
168 formatf(fp0, fplast);
169 exit(0);
170 }
9fcd824c
SL
171 if (fflg)
172 fp = fp0;
173 else {
174 for (fp = fp0; fp < fplast && fp->ftype != 'd'; fp++)
175 continue;
176 formatf(fp0, fp);
177 }
547b0031
BJ
178 if (fp < fplast) {
179 if (fp > fp0)
180 printf("\n");
181 for (;;) {
182 formatd(fp->fname, argc > 1);
183 while (subdirs) {
184 struct subdirs *t;
185
186 t = subdirs; subdirs = t->sd_next;
187 printf("\n");
188 formatd(t->sd_name, 1);
189 cfree(t->sd_name);
190 cfree((char *)t);
191 }
192 if (++fp == fplast)
193 break;
194 printf("\n");
195 }
196 }
197 exit(0);
198}
199
200formatd(name, title)
201 char *name;
202 int title;
203{
204 register struct afile *fp;
205 register struct subdirs *dp;
206 struct afile *dfp0, *dfplast;
207 int nkb;
208
209 nkb = getdir(name, &dfp0, &dfplast);
210 if (dfp0 == 0)
211 return;
9fcd824c
SL
212 if (fflg == 0)
213 qsort(dfp0, dfplast - dfp0, sizeof (struct afile), fcmp);
547b0031
BJ
214 if (title)
215 printf("%s:\n", name);
216 if (lflg || sflg)
217 printf("total %ld\n", nkb);
218 formatf(dfp0, dfplast);
219 if (Rflg)
212af679 220 for (fp = dfplast - 1; fp >= dfp0; fp--) {
547b0031
BJ
221 if (fp->ftype != 'd' ||
222 !strcmp(fp->fname, ".") ||
223 !strcmp(fp->fname, ".."))
224 continue;
225 dp = (struct subdirs *)malloc(sizeof (struct subdirs));
226 dp->sd_name = savestr(cat(name, fp->fname));
227 dp->sd_next = subdirs; subdirs = dp;
228 }
229 for (fp = dfp0; fp < dfplast; fp++) {
230 if ((fp->fflags&ISARG) == 0 && fp->fname)
231 cfree(fp->fname);
232 if (fp->flinkto)
233 cfree(fp->flinkto);
234 }
235 cfree((char *)dfp0);
236}
237
238getdir(dir, pfp0, pfplast)
239 char *dir;
240 struct afile **pfp0, **pfplast;
241{
242 register struct afile *fp;
243 DIR *dirp;
244 register struct direct *dp;
905d30b9 245 int nb, nent = 20;
547b0031
BJ
246
247 dirp = opendir(dir);
248 if (dirp == NULL) {
249 *pfp0 = *pfplast = NULL;
250 printf("%s unreadable\n", dir); /* not stderr! */
251 return (0);
252 }
253 fp = *pfp0 = (struct afile *)calloc(nent, sizeof (struct afile));
254 *pfplast = *pfp0 + nent;
905d30b9 255 nb = 0;
547b0031
BJ
256 while (dp = readdir(dirp)) {
257 if (dp->d_ino == 0)
258 continue;
259 if (aflg == 0 && dp->d_name[0]=='.' &&
260 (Aflg == 0 || dp->d_name[1]==0 ||
261 dp->d_name[1]=='.' && dp->d_name[2]==0))
262 continue;
905d30b9 263 if (gstat(fp, cat(dir, dp->d_name), Fflg+Rflg, &nb) == 0)
547b0031
BJ
264 continue;
265 fp->fnum = dp->d_ino;
266 fp->fname = savestr(dp->d_name);
267 fp++;
268 if (fp == *pfplast) {
269 *pfp0 = (struct afile *)realloc((char *)*pfp0,
270 2 * nent * sizeof (struct afile));
271 if (*pfp0 == 0) {
272 fprintf(stderr, "ls: out of memory\n");
273 exit(1);
274 }
275 fp = *pfp0 + nent;
276 *pfplast = fp + nent;
277 nent *= 2;
278 }
279 }
280 closedir(dirp);
281 *pfplast = fp;
905d30b9 282 return (kbytes(dbtob(nb)));
547b0031
BJ
283}
284
285int stat(), lstat();
286
287struct afile *
905d30b9 288gstat(fp, file, statarg, pnb)
547b0031
BJ
289 register struct afile *fp;
290 char *file;
905d30b9 291 int statarg, *pnb;
547b0031 292{
9fcd824c 293 int (*statf)() = Lflg ? stat : lstat;
547b0031
BJ
294 char buf[BUFSIZ]; int cc;
295 static struct afile azerofile;
296
297 *fp = azerofile;
298 fp->fflags = 0;
299 fp->fnum = 0;
300 fp->ftype = '-';
301 if (statarg || sflg || lflg || tflg) {
cc1b4140 302 struct stat stb, stb1;
547b0031
BJ
303
304 if ((*statf)(file, &stb) < 0) {
1d28a39b 305 if (statf == lstat || lstat(file, &stb) < 0) {
5af7550c
SL
306 fprintf(stderr, "%s not found\n", file);
307 return (0);
308 }
547b0031 309 }
905d30b9 310 fp->fblks = stb.st_blocks;
2955376b 311 fp->fsize = stb.st_size;
547b0031
BJ
312 switch (stb.st_mode & S_IFMT) {
313
314 case S_IFDIR:
315 fp->ftype = 'd'; break;
316 case S_IFBLK:
317 fp->ftype = 'b'; fp->fsize = stb.st_rdev; break;
318 case S_IFCHR:
319 fp->ftype = 'c'; fp->fsize = stb.st_rdev; break;
b1198826
SL
320 case S_IFSOCK:
321 fp->ftype = 's'; fp->fsize = 0; break;
547b0031
BJ
322 case S_IFLNK:
323 fp->ftype = 'l';
324 if (lflg) {
325 cc = readlink(file, buf, BUFSIZ);
326 if (cc >= 0) {
327 buf[cc] = 0;
328 fp->flinkto = savestr(buf);
329 }
cc1b4140
KM
330 break;
331 }
332 if (stat(file, &stb1) < 0)
333 break;
334 if ((stb1.st_mode & S_IFMT) == S_IFDIR) {
335 stb = stb1;
336 fp->ftype = 'd';
337 fp->fsize = stb.st_size;
905d30b9 338 fp->fblks = stb.st_blocks;
547b0031
BJ
339 }
340 break;
341 }
342 fp->fnum = stb.st_ino;
343 fp->fflags = stb.st_mode & ~S_IFMT;
344 fp->fnl = stb.st_nlink;
345 fp->fuid = stb.st_uid;
346 fp->fgid = stb.st_gid;
547b0031
BJ
347 if (uflg)
348 fp->fmtime = stb.st_atime;
349 else if (cflg)
350 fp->fmtime = stb.st_ctime;
351 else
352 fp->fmtime = stb.st_mtime;
905d30b9
SL
353 if (pnb)
354 *pnb += stb.st_blocks;
547b0031
BJ
355 }
356 return (fp);
357}
358
359formatf(fp0, fplast)
360 struct afile *fp0, *fplast;
361{
362 register struct afile *fp;
363 int width = 0, w, nentry = fplast - fp0;
364 int i, j, columns, lines;
365 char *cp;
366
367 if (fp0 == fplast)
368 return;
369 if (lflg || Cflg == 0)
370 columns = 1;
371 else {
372 for (fp = fp0; fp < fplast; fp++) {
373 int len = strlen(fmtentry(fp));
374
375 if (len > width)
376 width = len;
377 }
378 if (usetabs)
379 width = (width + 8) &~ 7;
380 else
381 width += 2;
fdae50a9 382 columns = twidth / width;
547b0031
BJ
383 if (columns == 0)
384 columns = 1;
385 }
386 lines = (nentry + columns - 1) / columns;
387 for (i = 0; i < lines; i++) {
388 for (j = 0; j < columns; j++) {
389 fp = fp0 + j * lines + i;
390 cp = fmtentry(fp);
391 printf("%s", cp);
392 if (fp + lines >= fplast) {
393 printf("\n");
394 break;
395 }
396 w = strlen(cp);
397 while (w < width)
398 if (usetabs) {
399 w = (w + 8) &~ 7;
400 putchar('\t');
401 } else {
402 w++;
403 putchar(' ');
404 }
405 }
406 }
407}
408
409fcmp(f1, f2)
410 register struct afile *f1, *f2;
411{
412
9fcd824c 413 if (dflg == 0 && fflg == 0) {
547b0031
BJ
414 if ((f1->fflags&ISARG) && f1->ftype == 'd') {
415 if ((f2->fflags&ISARG) == 0 || f2->ftype != 'd')
416 return (1);
417 } else {
418 if ((f2->fflags&ISARG) && f2->ftype == 'd')
419 return (-1);
420 }
421 }
422 if (tflg) {
423 if (f2->fmtime == f1->fmtime)
424 return (0);
425 if (f2->fmtime > f1->fmtime)
426 return (rflg);
427 return (-rflg);
428 }
1c52c7b1
JB
429 if (Sflg) {
430 if (f2->fsize == f1->fsize)
431 return (0);
432 if (f2->fsize > f1->fsize)
433 return (rflg);
434 return (-rflg);
435 }
547b0031
BJ
436 return (rflg * strcmp(f1->fname, f2->fname));
437}
438
439char *
440cat(dir, file)
441 char *dir, *file;
442{
443 static char dfile[BUFSIZ];
444
445 if (strlen(dir)+1+strlen(file)+1 > BUFSIZ) {
446 fprintf(stderr, "ls: filename too long\n");
447 exit(1);
448 }
449 if (!strcmp(dir, "") || !strcmp(dir, ".")) {
450 (void) strcpy(dfile, file);
451 return (dfile);
452 }
453 (void) strcpy(dfile, dir);
454 if (dir[strlen(dir) - 1] != '/' && *file != '/')
455 (void) strcat(dfile, "/");
456 (void) strcat(dfile, file);
457 return (dfile);
458}
459
460char *
461savestr(str)
462 char *str;
463{
464 char *cp = malloc(strlen(str) + 1);
465
466 if (cp == NULL) {
467 fprintf(stderr, "ls: out of memory\n");
468 exit(1);
469 }
470 (void) strcpy(cp, str);
471 return (cp);
472}
473
474char *fmtinum(), *fmtsize(), *fmtlstuff(), *fmtmode();
475
476char *
477fmtentry(fp)
478 register struct afile *fp;
479{
480 static char fmtres[BUFSIZ];
481 register char *cp, *dp;
482
9cdf1877 483 (void) sprintf(fmtres, "%s%s%s",
547b0031
BJ
484 iflg ? fmtinum(fp) : "",
485 sflg ? fmtsize(fp) : "",
486 lflg ? fmtlstuff(fp) : "");
487 dp = &fmtres[strlen(fmtres)];
488 for (cp = fp->fname; *cp; cp++)
489 if (qflg && (*cp < ' ' || *cp >= 0177))
490 *dp++ = '?';
491 else
492 *dp++ = *cp;
493 if (Fflg) {
494 if (fp->ftype == 'd')
495 *dp++ = '/';
404df744
BJ
496 else if (fp->ftype == 'l')
497 *dp++ = '@';
b1198826
SL
498 else if (fp->ftype == 's')
499 *dp++ = '=';
547b0031
BJ
500 else if (fp->fflags & 0111)
501 *dp++ = '*';
502 }
503 if (lflg && fp->flinkto) {
504 (void) strcpy(dp, " -> "); dp += 4;
505 for (cp = fp->flinkto; *cp; cp++)
506 if (qflg && (*cp < ' ' || *cp >= 0177))
507 *dp++ = '?';
508 else
509 *dp++ = *cp;
510 }
511 *dp++ = 0;
512 return (fmtres);
513}
514
515char *
516fmtinum(p)
517 register struct afile *p;
518{
519 static char inumbuf[8];
520
3f43f0b2 521 (void) sprintf(inumbuf, "%6d ", p->fnum);
547b0031
BJ
522 return (inumbuf);
523}
524
525char *
526fmtsize(p)
527 register struct afile *p;
528{
529 static char sizebuf[32];
530
905d30b9 531 (void) sprintf(sizebuf, "%4ld ", kbytes(dbtob(p->fblks)));
547b0031
BJ
532 return (sizebuf);
533}
534
535char *
536fmtlstuff(p)
537 register struct afile *p;
538{
539 static char lstuffbuf[256];
540 char gname[32], uname[32], fsize[32], ftime[32];
541 register char *lp = lstuffbuf;
542
543 /* type mode uname gname fsize ftime */
544/* get uname */
545 { char *cp = getname(p->fuid);
546 if (cp)
547 (void) sprintf(uname, "%-9.9s", cp);
548 else
549 (void) sprintf(uname, "%-9d", p->fuid);
550 }
551/* get gname */
552 if (gflg) {
553 char *cp = getgroup(p->fgid);
554 if (cp)
555 (void) sprintf(gname, "%-9.9s", cp);
556 else
557 (void) sprintf(gname, "%-9d", p->fgid);
558 }
559/* get fsize */
560 if (p->ftype == 'b' || p->ftype == 'c')
561 (void) sprintf(fsize, "%3d,%4d",
562 major(p->fsize), minor(p->fsize));
b1198826
SL
563 else if (p->ftype == 's')
564 (void) sprintf(fsize, "%8ld", 0);
547b0031
BJ
565 else
566 (void) sprintf(fsize, "%8ld", p->fsize);
567/* get ftime */
568 { char *cp = ctime(&p->fmtime);
569 if ((p->fmtime < sixmonthsago) || (p->fmtime > now))
570 (void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20);
571 else
572 (void) sprintf(ftime, " %-12.12s ", cp+4);
573 }
574/* splat */
575 *lp++ = p->ftype;
576 lp = fmtmode(lp, p->fflags);
577 (void) sprintf(lp, "%3d %s%s%s%s",
578 p->fnl, uname, gflg ? gname : "", fsize, ftime);
579 return (lstuffbuf);
580}
581
582int m1[] = { 1, S_IREAD>>0, 'r', '-' };
583int m2[] = { 1, S_IWRITE>>0, 'w', '-' };
584int m3[] = { 2, S_ISUID, 's', S_IEXEC>>0, 'x', '-' };
585int m4[] = { 1, S_IREAD>>3, 'r', '-' };
586int m5[] = { 1, S_IWRITE>>3, 'w', '-' };
587int m6[] = { 2, S_ISGID, 's', S_IEXEC>>3, 'x', '-' };
588int m7[] = { 1, S_IREAD>>6, 'r', '-' };
589int m8[] = { 1, S_IWRITE>>6, 'w', '-' };
590int m9[] = { 2, S_ISVTX, 't', S_IEXEC>>6, 'x', '-' };
591
592int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
593
594char *
595fmtmode(lp, flags)
596 char *lp;
597 int flags;
598{
599 int **mp;
600
601 for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])]; ) {
602 register int *pairp = *mp++;
603 register int n = *pairp++;
604
605 while (--n >= 0 && (flags&*pairp++) == 0)
606 pairp++;
607 *lp++ = *pairp;
608 }
609 return (lp);
610}
611
612/* rest should be done with nameserver or database */
613
614#include <pwd.h>
615#include <grp.h>
616#include <utmp.h>
617
618struct utmp utmp;
93b9cc72
SL
619#define NMAX (sizeof (utmp.ut_name))
620#define SCPYN(a, b) strncpy(a, b, NMAX)
547b0031 621
accfb8ba
JB
622#define NUID 64 /* power of 2 */
623#define UIDMASK 0x3f
547b0031 624#define NGID 300
547b0031 625
accfb8ba
JB
626struct ncache {
627 int uid;
628 char name[NMAX+1];
629} nc[NUID];
94d5b261
KM
630char outrangename[NMAX+1];
631int outrangeuid = -1;
adce3e94
KM
632char groups[NGID][NMAX+1];
633char outrangegroup[NMAX+1];
634int outrangegid = -1;
547b0031
BJ
635
636char *
637getname(uid)
638{
639 register struct passwd *pw;
547b0031 640 struct passwd *getpwent();
accfb8ba
JB
641 extern int _pw_stayopen;
642 register int cp;
643
644 _pw_stayopen = 1;
645 cp = uid & UIDMASK;
646 if (uid >= 0 && nc[cp].uid == uid && nc[cp].name[0])
647 return (nc[cp].name);
648 pw = getpwuid(uid);
649 if (!pw)
547b0031 650 return (0);
accfb8ba
JB
651 nc[cp].uid = uid;
652 SCPYN(nc[cp].name, pw->pw_name);
653 return (nc[cp].name);
547b0031
BJ
654}
655
656char *
657getgroup(gid)
658{
659 register struct group *gr;
660 static init;
661 struct group *getgrent();
662
663 if (gid >= 0 && gid < NGID && groups[gid][0])
664 return (&groups[gid][0]);
adce3e94
KM
665 if (gid >= 0 && gid == outrangegid)
666 return (outrangegroup);
ed1aa7ae 667rescan:
adce3e94
KM
668 if (init == 2) {
669 if (gid < NGID)
670 return (0);
671 setgrent();
672 while (gr = getgrent()) {
673 if (gr->gr_gid != gid)
674 continue;
675 outrangegid = gr->gr_gid;
93b9cc72 676 SCPYN(outrangegroup, gr->gr_name);
adce3e94
KM
677 endgrent();
678 return (outrangegroup);
679 }
680 endgrent();
547b0031 681 return (0);
adce3e94 682 }
547b0031
BJ
683 if (init == 0)
684 setgrent(), init = 1;
685 while (gr = getgrent()) {
adce3e94
KM
686 if (gr->gr_gid < 0 || gr->gr_gid >= NGID) {
687 if (gr->gr_gid == gid) {
688 outrangegid = gr->gr_gid;
93b9cc72 689 SCPYN(outrangegroup, gr->gr_name);
adce3e94
KM
690 return (outrangegroup);
691 }
547b0031 692 continue;
adce3e94 693 }
547b0031
BJ
694 if (groups[gr->gr_gid][0])
695 continue;
93b9cc72 696 SCPYN(groups[gr->gr_gid], gr->gr_name);
ed1aa7ae 697 if (gr->gr_gid == gid)
547b0031 698 return (&groups[gid][0]);
547b0031
BJ
699 }
700 init = 2;
ed1aa7ae 701 goto rescan;
547b0031 702}