xref: /original-bsd/bin/ls/ls.c (revision 29f0621e)
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.9 (Berkeley) 10/22/87";
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 	for (i = 0; i < argc; i++) {
158 		if (gstat(fp, *argv, 1, (int *)0)) {
159 			fp->fname = *argv;
160 			fp->fflags |= ISARG;
161 			fp++;
162 		}
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 	}
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 	}
178 	if (fp < fplast) {
179 		if (fp > fp0)
180 			putchar('\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 				putchar('\n');
188 				formatd(t->sd_name, 1);
189 				cfree(t->sd_name);
190 				cfree((char *)t);
191 			}
192 			if (++fp == fplast)
193 				break;
194 			putchar('\n');
195 		}
196 	}
197 	exit(0);
198 }
199 
200 formatd(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;
212 	if (fflg == 0)
213 		qsort(dfp0, dfplast - dfp0, sizeof (struct afile), fcmp);
214 	if (title)
215 		printf("%s:\n", name);
216 	if (lflg || sflg)
217 		printf("total %d\n", nkb);
218 	formatf(dfp0, dfplast);
219 	if (Rflg)
220 		for (fp = dfplast - 1; fp >= dfp0; fp--) {
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 
238 getdir(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;
245 	int nb, nent = 20;
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;
255 	nb = 0;
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;
263 		if (gstat(fp, cat(dir, dp->d_name), Fflg+Rflg, &nb) == 0)
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 				fputs("ls: out of memory\n", stderr);
273 				exit(1);
274 			}
275 			fp = *pfp0 + nent;
276 			*pfplast = fp + nent;
277 			nent *= 2;
278 		}
279 	}
280 	closedir(dirp);
281 	*pfplast = fp;
282 	return (kbytes(dbtob(nb)));
283 }
284 
285 int	stat(), lstat();
286 
287 struct afile *
288 gstat(fp, file, statarg, pnb)
289 	register struct afile *fp;
290 	char *file;
291 	int statarg, *pnb;
292 {
293 	int (*statf)() = Lflg ? stat : lstat;
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) {
302 		struct stat stb, stb1;
303 
304 		if ((*statf)(file, &stb) < 0) {
305 			if (statf == lstat || lstat(file, &stb) < 0) {
306 				fprintf(stderr, "%s not found\n", file);
307 				return (0);
308 			}
309 		}
310 		fp->fblks = stb.st_blocks;
311 		fp->fsize = stb.st_size;
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;
320 		case S_IFSOCK:
321 			fp->ftype = 's'; fp->fsize = 0; break;
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 				}
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;
338 				fp->fblks = stb.st_blocks;
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;
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;
353 		if (pnb)
354 			*pnb += stb.st_blocks;
355 	}
356 	return (fp);
357 }
358 
359 formatf(fp0, fplast)
360 	struct afile *fp0, *fplast;
361 {
362 	register struct afile *fp;
363 	register int i, j, w;
364 	int width = 0, nentry = fplast - fp0;
365 	int columns, lines;
366 	char *cp;
367 
368 	if (fp0 == fplast)
369 		return;
370 	if (lflg || Cflg == 0)
371 		columns = 1;
372 	else {
373 		for (fp = fp0; fp < fplast; fp++) {
374 			int len = strlen(fmtentry(fp));
375 
376 			if (len > width)
377 				width = len;
378 		}
379 		if (usetabs)
380 			width = (width + 8) &~ 7;
381 		else
382 			width += 2;
383 		columns = twidth / width;
384 		if (columns == 0)
385 			columns = 1;
386 	}
387 	lines = (nentry + columns - 1) / columns;
388 	for (i = 0; i < lines; i++) {
389 		for (j = 0; j < columns; j++) {
390 			fp = fp0 + j * lines + i;
391 			cp = fmtentry(fp);
392 			fputs(cp, stdout);
393 			if (fp + lines >= fplast) {
394 				putchar('\n');
395 				break;
396 			}
397 			w = strlen(cp);
398 			while (w < width)
399 				if (usetabs) {
400 					w = (w + 8) &~ 7;
401 					putchar('\t');
402 				} else {
403 					w++;
404 					putchar(' ');
405 				}
406 		}
407 	}
408 }
409 
410 fcmp(f1, f2)
411 	register struct afile *f1, *f2;
412 {
413 
414 	if (dflg == 0 && fflg == 0) {
415 		if ((f1->fflags&ISARG) && f1->ftype == 'd') {
416 			if ((f2->fflags&ISARG) == 0 || f2->ftype != 'd')
417 				return (1);
418 		} else {
419 			if ((f2->fflags&ISARG) && f2->ftype == 'd')
420 				return (-1);
421 		}
422 	}
423 	if (tflg) {
424 		if (f2->fmtime == f1->fmtime)
425 			return (0);
426 		if (f2->fmtime > f1->fmtime)
427 			return (rflg);
428 		return (-rflg);
429 	}
430 	return (rflg * strcmp(f1->fname, f2->fname));
431 }
432 
433 char *
434 cat(dir, file)
435 	char *dir, *file;
436 {
437 	static char dfile[BUFSIZ];
438 	register int dlen;
439 
440 	if ((dlen = strlen(dir))+1+strlen(file)+1 > BUFSIZ) {
441 		fputs("ls: filename too long\n", stderr);
442 		exit(1);
443 	}
444 	if (!dir[0] || dir[0] == '.' && !dir[1])
445 		return(strcpy(dfile, file));
446 	(void) strcpy(dfile, dir);
447 	if (dir[dlen - 1] != '/' && *file != '/')
448 		dfile[dlen++] = '/';
449 	(void) strcpy(dfile + dlen, file);
450 	return (dfile);
451 }
452 
453 char *
454 savestr(str)
455 	char *str;
456 {
457 	char *cp = malloc(strlen(str) + 1);
458 
459 	if (cp == NULL) {
460 		fputs("ls: out of memory\n", stderr);
461 		exit(1);
462 	}
463 	return(strcpy(cp, str));
464 }
465 
466 char	*fmtinum(), *fmtsize(), *fmtlstuff(), *fmtmode();
467 
468 char *
469 fmtentry(fp)
470 	register struct afile *fp;
471 {
472 	static char fmtres[BUFSIZ];
473 	register char *cp, *dp;
474 
475 	(void) sprintf(fmtres, "%s%s%s",
476 	    iflg ? fmtinum(fp) : "",
477 	    sflg ? fmtsize(fp) : "",
478 	    lflg ? fmtlstuff(fp) : "");
479 	dp = &fmtres[strlen(fmtres)];
480 	for (cp = fp->fname; *cp; cp++)
481 		if (qflg && (*cp < ' ' || *cp >= 0177))
482 			*dp++ = '?';
483 		else
484 			*dp++ = *cp;
485 	if (Fflg) {
486 		if (fp->ftype == 'd')
487 			*dp++ = '/';
488 		else if (fp->ftype == 'l')
489 			*dp++ = '@';
490 		else if (fp->ftype == 's')
491 			*dp++ = '=';
492 		else if (fp->fflags & 0111)
493 			*dp++ = '*';
494 	}
495 	if (lflg && fp->flinkto) {
496 		(void) strcpy(dp, " -> "); dp += 4;
497 		for (cp = fp->flinkto; *cp; cp++)
498 			if (qflg && (*cp < ' ' || *cp >= 0177))
499 				*dp++ = '?';
500 			else
501 				*dp++ = *cp;
502 	}
503 	*dp++ = 0;
504 	return (fmtres);
505 }
506 
507 char *
508 fmtinum(p)
509 	register struct afile *p;
510 {
511 	static char inumbuf[8];
512 
513 	(void) sprintf(inumbuf, "%6ld ", p->fnum);
514 	return (inumbuf);
515 }
516 
517 char *
518 fmtsize(p)
519 	register struct afile *p;
520 {
521 	static char sizebuf[32];
522 
523 	(void) sprintf(sizebuf, "%4ld ", kbytes(dbtob(p->fblks)));
524 	return (sizebuf);
525 }
526 
527 char *
528 fmtlstuff(p)
529 	register struct afile *p;
530 {
531 	static char lstuffbuf[256];
532 	char gname[32], uname[32], fsize[32], ftime[32];
533 	register char *lp = lstuffbuf;
534 
535 	/* type mode uname gname fsize ftime */
536 /* get uname */
537 	{ char *cp = getname(p->fuid);
538 	  if (cp)
539 		(void) sprintf(uname, "%-9.9s", cp);
540 	  else
541 		(void) sprintf(uname, "%-9u", p->fuid);
542 	}
543 /* get gname */
544 	if (gflg) {
545 	  char *cp = getgroup(p->fgid);
546 	  if (cp)
547 		(void) sprintf(gname, "%-9.9s", cp);
548 	  else
549 		(void) sprintf(gname, "%-9u", p->fgid);
550 	}
551 /* get fsize */
552 	if (p->ftype == 'b' || p->ftype == 'c')
553 		(void) sprintf(fsize, "%3d,%4d",
554 		    major(p->fsize), minor(p->fsize));
555 	else if (p->ftype == 's')
556 		(void) sprintf(fsize, "%8ld", 0L);
557 	else
558 		(void) sprintf(fsize, "%8ld", p->fsize);
559 /* get ftime */
560 	{ char *cp = ctime(&p->fmtime);
561 	  if ((p->fmtime < sixmonthsago) || (p->fmtime > now))
562 		(void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20);
563 	  else
564 		(void) sprintf(ftime, " %-12.12s ", cp+4);
565 	}
566 /* splat */
567 	*lp++ = p->ftype;
568 	lp = fmtmode(lp, p->fflags);
569 	(void) sprintf(lp, "%3d %s%s%s%s",
570 	    p->fnl, uname, gflg ? gname : "", fsize, ftime);
571 	return (lstuffbuf);
572 }
573 
574 int	m1[] = { 1, S_IREAD>>0, 'r', '-' };
575 int	m2[] = { 1, S_IWRITE>>0, 'w', '-' };
576 int	m3[] = { 3, S_ISUID|(S_IEXEC>>0), 's', S_ISUID, 'S', S_IEXEC>>0, 'x', '-' };
577 int	m4[] = { 1, S_IREAD>>3, 'r', '-' };
578 int	m5[] = { 1, S_IWRITE>>3, 'w', '-' };
579 int	m6[] = { 3, S_ISGID|(S_IEXEC>>3), 's', S_ISGID, 'S', S_IEXEC>>3, 'x', '-' };
580 int	m7[] = { 1, S_IREAD>>6, 'r', '-' };
581 int	m8[] = { 1, S_IWRITE>>6, 'w', '-' };
582 int	m9[] = { 3, S_ISVTX|(S_IEXEC>>6), 't', S_ISVTX, 'T', S_IEXEC>>6, 'x', '-' };
583 
584 int	*m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
585 
586 char *
587 fmtmode(lp, flags)
588 	char *lp;
589 	register int flags;
590 {
591 	int **mp;
592 
593 	for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])]; ) {
594 		register int *pairp = *mp++;
595 		register int n = *pairp++;
596 
597 		while (--n >= 0 && (flags&*pairp) != *pairp)
598 			pairp += 2;
599 		*lp++ = pairp[n>=0];
600 	}
601 	return (lp);
602 }
603 
604 /* rest should be done with nameserver or database */
605 
606 #include <pwd.h>
607 #include <grp.h>
608 #include <utmp.h>
609 
610 struct	utmp utmp;
611 #define	NMAX	(sizeof (utmp.ut_name))
612 #define SCPYN(a, b)	strncpy(a, b, NMAX)
613 
614 #define NCACHE	64		/* power of 2 */
615 #define CAMASK	NCACHE - 1
616 
617 char *
618 getname(uid)
619 	uid_t uid;
620 {
621 	extern int _pw_stayopen;
622 	static struct ncache {
623 		uid_t	uid;
624 		char	name[NMAX+1];
625 	} c_uid[NCACHE];
626 	register struct passwd *pw;
627 	register struct ncache *cp;
628 
629 	_pw_stayopen = 1;
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