remove pw_stayopen
[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
658acf73 14static char sccsid[] = "@(#)ls.c 5.10 (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 */
ec0c22c1
KB
36 uid_t fuid; /* owner id */
37 gid_t fgid; /* group id */
38 off_t 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();
6521d648 72char *strcpy(), *strcat();
547b0031
BJ
73
74main(argc, argv)
75 int argc;
76 char *argv[];
77{
ec0c22c1 78 extern int optind;
547b0031
BJ
79 struct afile *fp0, *fplast;
80 register struct afile *fp;
81 struct sgttyb sgbuf;
ec0c22c1
KB
82 int ch, i;
83 time_t time();
547b0031 84
ec0c22c1 85 Aflg = !getuid();
547b0031 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;
ec0c22c1
KB
97 while ((ch = getopt(argc, argv, "1ACLFRacdfgilqrstu")) != EOF)
98 switch((char)ch) {
547b0031
BJ
99 case '1':
100 Cflg = 0; break;
547b0031
BJ
101 case 'A':
102 Aflg++; break;
ec0c22c1
KB
103 case 'C':
104 Cflg = 1; break;
105 case 'L':
106 Lflg++; break;
107 case 'F':
108 Fflg++; break;
109 case 'R':
110 Rflg++; break;
111 case 'a':
112 aflg++; break;
547b0031
BJ
113 case 'c':
114 cflg++; break;
1c52c7b1
JB
115 case 'S':
116 Sflg++; /* fall into... */
547b0031
BJ
117 case 'd':
118 dflg++; break;
ec0c22c1
KB
119 case 'f':
120 fflg++; break;
547b0031
BJ
121 case 'g':
122 gflg++; break;
ec0c22c1
KB
123 case 'i':
124 iflg++; break;
547b0031
BJ
125 case 'l':
126 lflg++; break;
ec0c22c1
KB
127 case 'q':
128 qflg = 1; break;
547b0031
BJ
129 case 'r':
130 rflg = -1; break;
ec0c22c1
KB
131 case 's':
132 sflg++; break;
547b0031
BJ
133 case 't':
134 tflg++; break;
135 case 'u':
136 uflg++; break;
ec0c22c1
KB
137 case '?':
138 default:
139 fputs("usage: ls [ -1ACLFRacdfgilqrstu ] [ file ]\n", stderr);
140 exit(1);
547b0031 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;
ec0c22c1
KB
147 argc -= optind;
148 argv += optind;
547b0031
BJ
149 if (argc == 0) {
150 argc++;
151 argv = &dotp;
152 }
ec0c22c1 153 fp = (struct afile *)calloc((u_int)argc, sizeof (struct afile));
547b0031 154 if (fp == 0) {
ec0c22c1 155 fputs("ls: out of memory\n", stderr);
547b0031
BJ
156 exit(1);
157 }
158 fp0 = fp;
159 for (i = 0; i < argc; i++) {
2955376b
BJ
160 if (gstat(fp, *argv, 1, (int *)0)) {
161 fp->fname = *argv;
162 fp->fflags |= ISARG;
163 fp++;
164 }
547b0031
BJ
165 argv++;
166 }
167 fplast = fp;
168 qsort(fp0, fplast - fp0, sizeof (struct afile), fcmp);
169 if (dflg) {
170 formatf(fp0, fplast);
171 exit(0);
172 }
9fcd824c
SL
173 if (fflg)
174 fp = fp0;
175 else {
176 for (fp = fp0; fp < fplast && fp->ftype != 'd'; fp++)
177 continue;
178 formatf(fp0, fp);
179 }
547b0031
BJ
180 if (fp < fplast) {
181 if (fp > fp0)
ec0c22c1 182 putchar('\n');
547b0031
BJ
183 for (;;) {
184 formatd(fp->fname, argc > 1);
185 while (subdirs) {
186 struct subdirs *t;
187
188 t = subdirs; subdirs = t->sd_next;
ec0c22c1 189 putchar('\n');
547b0031
BJ
190 formatd(t->sd_name, 1);
191 cfree(t->sd_name);
192 cfree((char *)t);
193 }
194 if (++fp == fplast)
195 break;
ec0c22c1 196 putchar('\n');
547b0031
BJ
197 }
198 }
199 exit(0);
200}
201
202formatd(name, title)
203 char *name;
204 int title;
205{
206 register struct afile *fp;
207 register struct subdirs *dp;
208 struct afile *dfp0, *dfplast;
209 int nkb;
210
211 nkb = getdir(name, &dfp0, &dfplast);
212 if (dfp0 == 0)
213 return;
9fcd824c
SL
214 if (fflg == 0)
215 qsort(dfp0, dfplast - dfp0, sizeof (struct afile), fcmp);
547b0031
BJ
216 if (title)
217 printf("%s:\n", name);
218 if (lflg || sflg)
ec0c22c1 219 printf("total %d\n", nkb);
547b0031
BJ
220 formatf(dfp0, dfplast);
221 if (Rflg)
212af679 222 for (fp = dfplast - 1; fp >= dfp0; fp--) {
547b0031
BJ
223 if (fp->ftype != 'd' ||
224 !strcmp(fp->fname, ".") ||
225 !strcmp(fp->fname, ".."))
226 continue;
227 dp = (struct subdirs *)malloc(sizeof (struct subdirs));
228 dp->sd_name = savestr(cat(name, fp->fname));
229 dp->sd_next = subdirs; subdirs = dp;
230 }
231 for (fp = dfp0; fp < dfplast; fp++) {
232 if ((fp->fflags&ISARG) == 0 && fp->fname)
233 cfree(fp->fname);
234 if (fp->flinkto)
235 cfree(fp->flinkto);
236 }
237 cfree((char *)dfp0);
238}
239
240getdir(dir, pfp0, pfplast)
241 char *dir;
242 struct afile **pfp0, **pfplast;
243{
244 register struct afile *fp;
245 DIR *dirp;
246 register struct direct *dp;
905d30b9 247 int nb, nent = 20;
547b0031
BJ
248
249 dirp = opendir(dir);
250 if (dirp == NULL) {
251 *pfp0 = *pfplast = NULL;
252 printf("%s unreadable\n", dir); /* not stderr! */
253 return (0);
254 }
255 fp = *pfp0 = (struct afile *)calloc(nent, sizeof (struct afile));
256 *pfplast = *pfp0 + nent;
905d30b9 257 nb = 0;
547b0031
BJ
258 while (dp = readdir(dirp)) {
259 if (dp->d_ino == 0)
260 continue;
261 if (aflg == 0 && dp->d_name[0]=='.' &&
262 (Aflg == 0 || dp->d_name[1]==0 ||
263 dp->d_name[1]=='.' && dp->d_name[2]==0))
264 continue;
905d30b9 265 if (gstat(fp, cat(dir, dp->d_name), Fflg+Rflg, &nb) == 0)
547b0031
BJ
266 continue;
267 fp->fnum = dp->d_ino;
268 fp->fname = savestr(dp->d_name);
269 fp++;
270 if (fp == *pfplast) {
271 *pfp0 = (struct afile *)realloc((char *)*pfp0,
272 2 * nent * sizeof (struct afile));
273 if (*pfp0 == 0) {
ec0c22c1 274 fputs("ls: out of memory\n", stderr);
547b0031
BJ
275 exit(1);
276 }
277 fp = *pfp0 + nent;
278 *pfplast = fp + nent;
279 nent *= 2;
280 }
281 }
282 closedir(dirp);
283 *pfplast = fp;
905d30b9 284 return (kbytes(dbtob(nb)));
547b0031
BJ
285}
286
287int stat(), lstat();
288
289struct afile *
905d30b9 290gstat(fp, file, statarg, pnb)
547b0031
BJ
291 register struct afile *fp;
292 char *file;
905d30b9 293 int statarg, *pnb;
547b0031 294{
9fcd824c 295 int (*statf)() = Lflg ? stat : lstat;
547b0031
BJ
296 char buf[BUFSIZ]; int cc;
297 static struct afile azerofile;
298
299 *fp = azerofile;
300 fp->fflags = 0;
301 fp->fnum = 0;
302 fp->ftype = '-';
303 if (statarg || sflg || lflg || tflg) {
cc1b4140 304 struct stat stb, stb1;
547b0031
BJ
305
306 if ((*statf)(file, &stb) < 0) {
1d28a39b 307 if (statf == lstat || lstat(file, &stb) < 0) {
5af7550c
SL
308 fprintf(stderr, "%s not found\n", file);
309 return (0);
310 }
547b0031 311 }
905d30b9 312 fp->fblks = stb.st_blocks;
2955376b 313 fp->fsize = stb.st_size;
547b0031
BJ
314 switch (stb.st_mode & S_IFMT) {
315
316 case S_IFDIR:
317 fp->ftype = 'd'; break;
318 case S_IFBLK:
319 fp->ftype = 'b'; fp->fsize = stb.st_rdev; break;
320 case S_IFCHR:
321 fp->ftype = 'c'; fp->fsize = stb.st_rdev; break;
b1198826
SL
322 case S_IFSOCK:
323 fp->ftype = 's'; fp->fsize = 0; break;
547b0031
BJ
324 case S_IFLNK:
325 fp->ftype = 'l';
326 if (lflg) {
327 cc = readlink(file, buf, BUFSIZ);
328 if (cc >= 0) {
329 buf[cc] = 0;
330 fp->flinkto = savestr(buf);
331 }
cc1b4140
KM
332 break;
333 }
334 if (stat(file, &stb1) < 0)
335 break;
336 if ((stb1.st_mode & S_IFMT) == S_IFDIR) {
337 stb = stb1;
338 fp->ftype = 'd';
339 fp->fsize = stb.st_size;
905d30b9 340 fp->fblks = stb.st_blocks;
547b0031
BJ
341 }
342 break;
343 }
344 fp->fnum = stb.st_ino;
345 fp->fflags = stb.st_mode & ~S_IFMT;
346 fp->fnl = stb.st_nlink;
347 fp->fuid = stb.st_uid;
348 fp->fgid = stb.st_gid;
547b0031
BJ
349 if (uflg)
350 fp->fmtime = stb.st_atime;
351 else if (cflg)
352 fp->fmtime = stb.st_ctime;
353 else
354 fp->fmtime = stb.st_mtime;
905d30b9
SL
355 if (pnb)
356 *pnb += stb.st_blocks;
547b0031
BJ
357 }
358 return (fp);
359}
360
361formatf(fp0, fplast)
362 struct afile *fp0, *fplast;
363{
364 register struct afile *fp;
ec0c22c1
KB
365 register int i, j, w;
366 int width = 0, nentry = fplast - fp0;
367 int columns, lines;
547b0031
BJ
368 char *cp;
369
370 if (fp0 == fplast)
371 return;
372 if (lflg || Cflg == 0)
373 columns = 1;
374 else {
375 for (fp = fp0; fp < fplast; fp++) {
376 int len = strlen(fmtentry(fp));
377
378 if (len > width)
379 width = len;
380 }
381 if (usetabs)
382 width = (width + 8) &~ 7;
383 else
384 width += 2;
fdae50a9 385 columns = twidth / width;
547b0031
BJ
386 if (columns == 0)
387 columns = 1;
388 }
389 lines = (nentry + columns - 1) / columns;
390 for (i = 0; i < lines; i++) {
391 for (j = 0; j < columns; j++) {
392 fp = fp0 + j * lines + i;
393 cp = fmtentry(fp);
ec0c22c1 394 fputs(cp, stdout);
547b0031 395 if (fp + lines >= fplast) {
ec0c22c1 396 putchar('\n');
547b0031
BJ
397 break;
398 }
399 w = strlen(cp);
400 while (w < width)
401 if (usetabs) {
402 w = (w + 8) &~ 7;
403 putchar('\t');
404 } else {
405 w++;
406 putchar(' ');
407 }
408 }
409 }
410}
411
412fcmp(f1, f2)
413 register struct afile *f1, *f2;
414{
415
9fcd824c 416 if (dflg == 0 && fflg == 0) {
547b0031
BJ
417 if ((f1->fflags&ISARG) && f1->ftype == 'd') {
418 if ((f2->fflags&ISARG) == 0 || f2->ftype != 'd')
419 return (1);
420 } else {
421 if ((f2->fflags&ISARG) && f2->ftype == 'd')
422 return (-1);
423 }
424 }
425 if (tflg) {
426 if (f2->fmtime == f1->fmtime)
427 return (0);
428 if (f2->fmtime > f1->fmtime)
429 return (rflg);
430 return (-rflg);
431 }
1c52c7b1
JB
432 if (Sflg) {
433 if (f2->fsize == f1->fsize)
434 return (0);
435 if (f2->fsize > f1->fsize)
436 return (rflg);
437 return (-rflg);
438 }
547b0031
BJ
439 return (rflg * strcmp(f1->fname, f2->fname));
440}
441
442char *
443cat(dir, file)
444 char *dir, *file;
445{
446 static char dfile[BUFSIZ];
ec0c22c1 447 register int dlen;
547b0031 448
ec0c22c1
KB
449 if ((dlen = strlen(dir))+1+strlen(file)+1 > BUFSIZ) {
450 fputs("ls: filename too long\n", stderr);
547b0031
BJ
451 exit(1);
452 }
ec0c22c1
KB
453 if (!dir[0] || dir[0] == '.' && !dir[1])
454 return(strcpy(dfile, file));
547b0031 455 (void) strcpy(dfile, dir);
ec0c22c1
KB
456 if (dir[dlen - 1] != '/' && *file != '/')
457 dfile[dlen++] = '/';
458 (void) strcpy(dfile + dlen, file);
547b0031
BJ
459 return (dfile);
460}
461
462char *
463savestr(str)
464 char *str;
465{
466 char *cp = malloc(strlen(str) + 1);
467
468 if (cp == NULL) {
ec0c22c1 469 fputs("ls: out of memory\n", stderr);
547b0031
BJ
470 exit(1);
471 }
ec0c22c1 472 return(strcpy(cp, str));
547b0031
BJ
473}
474
475char *fmtinum(), *fmtsize(), *fmtlstuff(), *fmtmode();
476
477char *
478fmtentry(fp)
479 register struct afile *fp;
480{
481 static char fmtres[BUFSIZ];
482 register char *cp, *dp;
483
9cdf1877 484 (void) sprintf(fmtres, "%s%s%s",
547b0031
BJ
485 iflg ? fmtinum(fp) : "",
486 sflg ? fmtsize(fp) : "",
487 lflg ? fmtlstuff(fp) : "");
488 dp = &fmtres[strlen(fmtres)];
489 for (cp = fp->fname; *cp; cp++)
490 if (qflg && (*cp < ' ' || *cp >= 0177))
491 *dp++ = '?';
492 else
493 *dp++ = *cp;
494 if (Fflg) {
495 if (fp->ftype == 'd')
496 *dp++ = '/';
404df744
BJ
497 else if (fp->ftype == 'l')
498 *dp++ = '@';
b1198826
SL
499 else if (fp->ftype == 's')
500 *dp++ = '=';
547b0031
BJ
501 else if (fp->fflags & 0111)
502 *dp++ = '*';
503 }
504 if (lflg && fp->flinkto) {
505 (void) strcpy(dp, " -> "); dp += 4;
506 for (cp = fp->flinkto; *cp; cp++)
507 if (qflg && (*cp < ' ' || *cp >= 0177))
508 *dp++ = '?';
509 else
510 *dp++ = *cp;
511 }
512 *dp++ = 0;
513 return (fmtres);
514}
515
516char *
517fmtinum(p)
518 register struct afile *p;
519{
520 static char inumbuf[8];
521
ec0c22c1 522 (void) sprintf(inumbuf, "%6ld ", p->fnum);
547b0031
BJ
523 return (inumbuf);
524}
525
526char *
527fmtsize(p)
528 register struct afile *p;
529{
530 static char sizebuf[32];
531
905d30b9 532 (void) sprintf(sizebuf, "%4ld ", kbytes(dbtob(p->fblks)));
547b0031
BJ
533 return (sizebuf);
534}
535
536char *
537fmtlstuff(p)
538 register struct afile *p;
539{
540 static char lstuffbuf[256];
541 char gname[32], uname[32], fsize[32], ftime[32];
542 register char *lp = lstuffbuf;
543
544 /* type mode uname gname fsize ftime */
545/* get uname */
546 { char *cp = getname(p->fuid);
547 if (cp)
548 (void) sprintf(uname, "%-9.9s", cp);
549 else
ec0c22c1 550 (void) sprintf(uname, "%-9u", p->fuid);
547b0031
BJ
551 }
552/* get gname */
553 if (gflg) {
554 char *cp = getgroup(p->fgid);
555 if (cp)
556 (void) sprintf(gname, "%-9.9s", cp);
557 else
ec0c22c1 558 (void) sprintf(gname, "%-9u", p->fgid);
547b0031
BJ
559 }
560/* get fsize */
561 if (p->ftype == 'b' || p->ftype == 'c')
562 (void) sprintf(fsize, "%3d,%4d",
563 major(p->fsize), minor(p->fsize));
b1198826 564 else if (p->ftype == 's')
ec0c22c1 565 (void) sprintf(fsize, "%8ld", 0L);
547b0031
BJ
566 else
567 (void) sprintf(fsize, "%8ld", p->fsize);
568/* get ftime */
569 { char *cp = ctime(&p->fmtime);
570 if ((p->fmtime < sixmonthsago) || (p->fmtime > now))
571 (void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20);
572 else
573 (void) sprintf(ftime, " %-12.12s ", cp+4);
574 }
575/* splat */
576 *lp++ = p->ftype;
577 lp = fmtmode(lp, p->fflags);
578 (void) sprintf(lp, "%3d %s%s%s%s",
579 p->fnl, uname, gflg ? gname : "", fsize, ftime);
580 return (lstuffbuf);
581}
582
583int m1[] = { 1, S_IREAD>>0, 'r', '-' };
584int m2[] = { 1, S_IWRITE>>0, 'w', '-' };
55e55e97 585int m3[] = { 3, S_ISUID|(S_IEXEC>>0), 's', S_ISUID, 'S', S_IEXEC>>0, 'x', '-' };
547b0031
BJ
586int m4[] = { 1, S_IREAD>>3, 'r', '-' };
587int m5[] = { 1, S_IWRITE>>3, 'w', '-' };
55e55e97 588int m6[] = { 3, S_ISGID|(S_IEXEC>>3), 's', S_ISGID, 'S', S_IEXEC>>3, 'x', '-' };
547b0031
BJ
589int m7[] = { 1, S_IREAD>>6, 'r', '-' };
590int m8[] = { 1, S_IWRITE>>6, 'w', '-' };
55e55e97 591int m9[] = { 3, S_ISVTX|(S_IEXEC>>6), 't', S_ISVTX, 'T', S_IEXEC>>6, 'x', '-' };
547b0031
BJ
592
593int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
594
595char *
596fmtmode(lp, flags)
597 char *lp;
ec0c22c1 598 register int flags;
547b0031
BJ
599{
600 int **mp;
601
602 for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])]; ) {
603 register int *pairp = *mp++;
604 register int n = *pairp++;
605
55e55e97
KB
606 while (--n >= 0 && (flags&*pairp) != *pairp)
607 pairp += 2;
608 *lp++ = pairp[n>=0];
547b0031
BJ
609 }
610 return (lp);
611}
612
613/* rest should be done with nameserver or database */
614
615#include <pwd.h>
616#include <grp.h>
617#include <utmp.h>
618
619struct utmp utmp;
93b9cc72
SL
620#define NMAX (sizeof (utmp.ut_name))
621#define SCPYN(a, b) strncpy(a, b, NMAX)
547b0031 622
ec0c22c1
KB
623#define NCACHE 64 /* power of 2 */
624#define CAMASK NCACHE - 1
547b0031
BJ
625
626char *
627getname(uid)
ec0c22c1 628 uid_t uid;
547b0031 629{
ec0c22c1
KB
630 static struct ncache {
631 uid_t uid;
632 char name[NMAX+1];
633 } c_uid[NCACHE];
634 register struct passwd *pw;
635 register struct ncache *cp;
accfb8ba 636
ec0c22c1
KB
637 cp = c_uid + (uid & CAMASK);
638 if (cp->uid == uid && *cp->name)
639 return(cp->name);
640 if (!(pw = getpwuid(uid)))
641 return((char *)0);
642 cp->uid = uid;
643 SCPYN(cp->name, pw->pw_name);
644 return(cp->name);
547b0031
BJ
645}
646
647char *
648getgroup(gid)
ec0c22c1 649 gid_t gid;
547b0031 650{
ec0c22c1
KB
651 static struct ncache {
652 gid_t gid;
653 char name[NMAX+1];
654 } c_gid[NCACHE];
547b0031 655 register struct group *gr;
ec0c22c1
KB
656 register struct ncache *cp;
657
658 cp = c_gid + (gid & CAMASK);
659 if (cp->gid == gid && *cp->name)
660 return(cp->name);
661 if (!(gr = getgrgid(gid)))
662 return((char *)0);
663 cp->gid = gid;
664 SCPYN(cp->name, gr->gr_name);
665 return(cp->name);
547b0031 666}