xref: /original-bsd/bin/ls/ls.c (revision 549425d7)
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
8 char copyright[] =
9 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)ls.c	5.11 (Berkeley) 03/06/89";
15 #endif not lint
16 
17 /*
18  * ls
19  *
20  * 4.2bsd version for symbolic links, variable length
21  * directory entries, block size in the inode, etc.
22  */
23 #include <sys/param.h>
24 #include <sys/stat.h>
25 #include <sys/dir.h>
26 #include <stdio.h>
27 #include <sgtty.h>
28 
29 #define	kbytes(size)	(((size) + 1023) / 1024)
30 
31 struct 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 	uid_t	fuid;		/* owner id */
37 	gid_t	fgid;		/* group id */
38 	off_t	fsize;		/* file size */
39 	long	fblks;		/* number of blocks used */
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 
47 struct subdirs {
48 	char	*sd_name;
49 	struct	subdirs *sd_next;
50 } *subdirs;
51 
52 int	aflg, dflg, gflg, lflg, sflg, tflg, uflg, iflg, fflg, cflg, rflg = 1;
53 int	qflg, Aflg, Cflg, Fflg, Lflg, Rflg;
54 
55 int	usetabs;
56 
57 time_t	now, sixmonthsago;
58 
59 char	*dotp = ".";
60 
61 struct	winsize win;
62 int	twidth;
63 
64 struct	afile *gstat();
65 int	fcmp();
66 char	*cat(), *savestr();
67 char	*fmtentry();
68 char	*getname(), *getgroup();
69 
70 char	*ctime();
71 char	*malloc(), *calloc(), *realloc();
72 char	*strcpy(), *strcat();
73 
74 main(argc, argv)
75 	int argc;
76 	char *argv[];
77 {
78 	extern int optind;
79 	struct afile *fp0, *fplast;
80 	register struct afile *fp;
81 	struct sgttyb sgbuf;
82 	int ch, i;
83 	time_t time();
84 
85 	Aflg = !getuid();
86 	(void) time(&now); sixmonthsago = now - 6L*30L*24L*60L*60L; now += 60;
87 	twidth = 80;
88 	if (isatty(1)) {
89 		qflg = Cflg = 1;
90 		(void) gtty(1, &sgbuf);
91 		if (ioctl(1, TIOCGWINSZ, &win) != -1)
92 			twidth = (win.ws_col == 0 ? 80 : win.ws_col);
93 		if ((sgbuf.sg_flags & XTABS) != XTABS)
94 			usetabs = 1;
95 	} else
96 		usetabs = 1;
97 	while ((ch = getopt(argc, argv, "1ACLFRacdfgilqrstu")) != EOF)
98 		switch((char)ch) {
99 		case '1':
100 			Cflg = 0; break;
101 		case 'A':
102 			Aflg++; break;
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;
113 		case 'c':
114 			cflg++; break;
115 		case 'd':
116 			dflg++; break;
117 		case 'f':
118 			fflg++; break;
119 		case 'g':
120 			gflg++; break;
121 		case 'i':
122 			iflg++; break;
123 		case 'l':
124 			lflg++; break;
125 		case 'q':
126 			qflg = 1; break;
127 		case 'r':
128 			rflg = -1; break;
129 		case 's':
130 			sflg++; break;
131 		case 't':
132 			tflg++; break;
133 		case 'u':
134 			uflg++; break;
135 		case '?':
136 		default:
137 			fputs("usage: ls [ -1ACLFRacdfgilqrstu ] [ file ]\n", stderr);
138 			exit(1);
139 	}
140 	if (fflg) {
141 		aflg++; lflg = 0; sflg = 0; tflg = 0;
142 	}
143 	if (lflg)
144 		Cflg = 0;
145 	argc -= optind;
146 	argv += optind;
147 	if (argc == 0) {
148 		argc++;
149 		argv = &dotp;
150 	}
151 	fp = (struct afile *)calloc((u_int)argc, sizeof (struct afile));
152 	if (fp == 0) {
153 		fputs("ls: out of memory\n", stderr);
154 		exit(1);
155 	}
156 	fp0 = fp;
157 	setpassent(1);
158 	setgroupent(1);
159 	for (i = 0; i < argc; i++) {
160 		if (gstat(fp, *argv, 1, (int *)0)) {
161 			fp->fname = *argv;
162 			fp->fflags |= ISARG;
163 			fp++;
164 		}
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 	}
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 	}
180 	if (fp < fplast) {
181 		if (fp > fp0)
182 			putchar('\n');
183 		for (;;) {
184 			formatd(fp->fname, argc > 1);
185 			while (subdirs) {
186 				struct subdirs *t;
187 
188 				t = subdirs; subdirs = t->sd_next;
189 				putchar('\n');
190 				formatd(t->sd_name, 1);
191 				cfree(t->sd_name);
192 				cfree((char *)t);
193 			}
194 			if (++fp == fplast)
195 				break;
196 			putchar('\n');
197 		}
198 	}
199 	exit(0);
200 }
201 
202 formatd(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;
214 	if (fflg == 0)
215 		qsort(dfp0, dfplast - dfp0, sizeof (struct afile), fcmp);
216 	if (title)
217 		printf("%s:\n", name);
218 	if (lflg || sflg)
219 		printf("total %d\n", nkb);
220 	formatf(dfp0, dfplast);
221 	if (Rflg)
222 		for (fp = dfplast - 1; fp >= dfp0; fp--) {
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 
240 getdir(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;
247 	int nb, nent = 20;
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;
257 	nb = 0;
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;
265 		if (gstat(fp, cat(dir, dp->d_name), Fflg+Rflg, &nb) == 0)
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) {
274 				fputs("ls: out of memory\n", stderr);
275 				exit(1);
276 			}
277 			fp = *pfp0 + nent;
278 			*pfplast = fp + nent;
279 			nent *= 2;
280 		}
281 	}
282 	closedir(dirp);
283 	*pfplast = fp;
284 	return (kbytes(dbtob(nb)));
285 }
286 
287 int	stat(), lstat();
288 
289 struct afile *
290 gstat(fp, file, statarg, pnb)
291 	register struct afile *fp;
292 	char *file;
293 	int statarg, *pnb;
294 {
295 	int (*statf)() = Lflg ? stat : lstat;
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) {
304 		struct stat stb, stb1;
305 
306 		if ((*statf)(file, &stb) < 0) {
307 			if (statf == lstat || lstat(file, &stb) < 0) {
308 				fprintf(stderr, "%s not found\n", file);
309 				return (0);
310 			}
311 		}
312 		fp->fblks = stb.st_blocks;
313 		fp->fsize = stb.st_size;
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;
322 		case S_IFSOCK:
323 			fp->ftype = 's'; fp->fsize = 0; break;
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 				}
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;
340 				fp->fblks = stb.st_blocks;
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;
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;
355 		if (pnb)
356 			*pnb += stb.st_blocks;
357 	}
358 	return (fp);
359 }
360 
361 formatf(fp0, fplast)
362 	struct afile *fp0, *fplast;
363 {
364 	register struct afile *fp;
365 	register int i, j, w;
366 	int width = 0, nentry = fplast - fp0;
367 	int columns, lines;
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;
385 		columns = twidth / width;
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);
394 			fputs(cp, stdout);
395 			if (fp + lines >= fplast) {
396 				putchar('\n');
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 
412 fcmp(f1, f2)
413 	register struct afile *f1, *f2;
414 {
415 
416 	if (dflg == 0 && fflg == 0) {
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 	}
432 	return (rflg * strcmp(f1->fname, f2->fname));
433 }
434 
435 char *
436 cat(dir, file)
437 	char *dir, *file;
438 {
439 	static char dfile[BUFSIZ];
440 	register int dlen;
441 
442 	if ((dlen = strlen(dir))+1+strlen(file)+1 > BUFSIZ) {
443 		fputs("ls: filename too long\n", stderr);
444 		exit(1);
445 	}
446 	if (!dir[0] || dir[0] == '.' && !dir[1])
447 		return(strcpy(dfile, file));
448 	(void) strcpy(dfile, dir);
449 	if (dir[dlen - 1] != '/' && *file != '/')
450 		dfile[dlen++] = '/';
451 	(void) strcpy(dfile + dlen, file);
452 	return (dfile);
453 }
454 
455 char *
456 savestr(str)
457 	char *str;
458 {
459 	char *cp = malloc(strlen(str) + 1);
460 
461 	if (cp == NULL) {
462 		fputs("ls: out of memory\n", stderr);
463 		exit(1);
464 	}
465 	return(strcpy(cp, str));
466 }
467 
468 char	*fmtinum(), *fmtsize(), *fmtlstuff(), *fmtmode();
469 
470 char *
471 fmtentry(fp)
472 	register struct afile *fp;
473 {
474 	static char fmtres[BUFSIZ];
475 	register char *cp, *dp;
476 
477 	(void) sprintf(fmtres, "%s%s%s",
478 	    iflg ? fmtinum(fp) : "",
479 	    sflg ? fmtsize(fp) : "",
480 	    lflg ? fmtlstuff(fp) : "");
481 	dp = &fmtres[strlen(fmtres)];
482 	for (cp = fp->fname; *cp; cp++)
483 		if (qflg && (*cp < ' ' || *cp >= 0177))
484 			*dp++ = '?';
485 		else
486 			*dp++ = *cp;
487 	if (Fflg) {
488 		if (fp->ftype == 'd')
489 			*dp++ = '/';
490 		else if (fp->ftype == 'l')
491 			*dp++ = '@';
492 		else if (fp->ftype == 's')
493 			*dp++ = '=';
494 		else if (fp->fflags & 0111)
495 			*dp++ = '*';
496 	}
497 	if (lflg && fp->flinkto) {
498 		(void) strcpy(dp, " -> "); dp += 4;
499 		for (cp = fp->flinkto; *cp; cp++)
500 			if (qflg && (*cp < ' ' || *cp >= 0177))
501 				*dp++ = '?';
502 			else
503 				*dp++ = *cp;
504 	}
505 	*dp++ = 0;
506 	return (fmtres);
507 }
508 
509 char *
510 fmtinum(p)
511 	register struct afile *p;
512 {
513 	static char inumbuf[8];
514 
515 	(void) sprintf(inumbuf, "%6ld ", p->fnum);
516 	return (inumbuf);
517 }
518 
519 char *
520 fmtsize(p)
521 	register struct afile *p;
522 {
523 	static char sizebuf[32];
524 
525 	(void) sprintf(sizebuf, "%4ld ", kbytes(dbtob(p->fblks)));
526 	return (sizebuf);
527 }
528 
529 char *
530 fmtlstuff(p)
531 	register struct afile *p;
532 {
533 	static char lstuffbuf[256];
534 	char gname[32], uname[32], fsize[32], ftime[32];
535 	register char *lp = lstuffbuf;
536 
537 	/* type mode uname gname fsize ftime */
538 /* get uname */
539 	{ char *cp = getname(p->fuid);
540 	  if (cp)
541 		(void) sprintf(uname, "%-9.9s", cp);
542 	  else
543 		(void) sprintf(uname, "%-9u", p->fuid);
544 	}
545 /* get gname */
546 	if (gflg) {
547 	  char *cp = getgroup(p->fgid);
548 	  if (cp)
549 		(void) sprintf(gname, "%-9.9s", cp);
550 	  else
551 		(void) sprintf(gname, "%-9u", p->fgid);
552 	}
553 /* get fsize */
554 	if (p->ftype == 'b' || p->ftype == 'c')
555 		(void) sprintf(fsize, "%3d,%4d",
556 		    major(p->fsize), minor(p->fsize));
557 	else if (p->ftype == 's')
558 		(void) sprintf(fsize, "%8ld", 0L);
559 	else
560 		(void) sprintf(fsize, "%8ld", p->fsize);
561 /* get ftime */
562 	{ char *cp = ctime(&p->fmtime);
563 	  if ((p->fmtime < sixmonthsago) || (p->fmtime > now))
564 		(void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20);
565 	  else
566 		(void) sprintf(ftime, " %-12.12s ", cp+4);
567 	}
568 /* splat */
569 	*lp++ = p->ftype;
570 	lp = fmtmode(lp, p->fflags);
571 	(void) sprintf(lp, "%3d %s%s%s%s",
572 	    p->fnl, uname, gflg ? gname : "", fsize, ftime);
573 	return (lstuffbuf);
574 }
575 
576 int	m1[] = { 1, S_IREAD>>0, 'r', '-' };
577 int	m2[] = { 1, S_IWRITE>>0, 'w', '-' };
578 int	m3[] = { 3, S_ISUID|(S_IEXEC>>0), 's', S_ISUID, 'S', S_IEXEC>>0, 'x', '-' };
579 int	m4[] = { 1, S_IREAD>>3, 'r', '-' };
580 int	m5[] = { 1, S_IWRITE>>3, 'w', '-' };
581 int	m6[] = { 3, S_ISGID|(S_IEXEC>>3), 's', S_ISGID, 'S', S_IEXEC>>3, 'x', '-' };
582 int	m7[] = { 1, S_IREAD>>6, 'r', '-' };
583 int	m8[] = { 1, S_IWRITE>>6, 'w', '-' };
584 int	m9[] = { 3, S_ISVTX|(S_IEXEC>>6), 't', S_ISVTX, 'T', S_IEXEC>>6, 'x', '-' };
585 
586 int	*m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
587 
588 char *
589 fmtmode(lp, flags)
590 	char *lp;
591 	register int flags;
592 {
593 	int **mp;
594 
595 	for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])]; ) {
596 		register int *pairp = *mp++;
597 		register int n = *pairp++;
598 
599 		while (--n >= 0 && (flags&*pairp) != *pairp)
600 			pairp += 2;
601 		*lp++ = pairp[n>=0];
602 	}
603 	return (lp);
604 }
605 
606 /* rest should be done with nameserver or database */
607 
608 #include <pwd.h>
609 #include <grp.h>
610 #include <utmp.h>
611 
612 struct	utmp utmp;
613 #define	NMAX	(sizeof (utmp.ut_name))
614 #define SCPYN(a, b)	strncpy(a, b, NMAX)
615 
616 #define NCACHE	64		/* power of 2 */
617 #define CAMASK	NCACHE - 1
618 
619 char *
620 getname(uid)
621 	uid_t uid;
622 {
623 	static struct ncache {
624 		uid_t	uid;
625 		char	name[NMAX+1];
626 	} c_uid[NCACHE];
627 	register struct passwd *pw;
628 	register struct ncache *cp;
629 
630 	cp = c_uid + (uid & CAMASK);
631 	if (cp->uid == uid && *cp->name)
632 		return(cp->name);
633 	if (!(pw = getpwuid(uid)))
634 		return((char *)0);
635 	cp->uid = uid;
636 	SCPYN(cp->name, pw->pw_name);
637 	return(cp->name);
638 }
639 
640 char *
641 getgroup(gid)
642 	gid_t gid;
643 {
644 	static struct ncache {
645 		gid_t	gid;
646 		char	name[NMAX+1];
647 	} c_gid[NCACHE];
648 	register struct group *gr;
649 	register struct ncache *cp;
650 
651 	cp = c_gid + (gid & CAMASK);
652 	if (cp->gid == gid && *cp->name)
653 		return(cp->name);
654 	if (!(gr = getgrgid(gid)))
655 		return((char *)0);
656 	cp->gid = gid;
657 	SCPYN(cp->name, gr->gr_name);
658 	return(cp->name);
659 }
660