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