xref: /original-bsd/bin/ls/ls.c (revision f0fd5f8a)
1 #ifndef lint
2 static	char *sccsid = "@(#)ls.c	4.8 82/12/03";
3 #endif
4 
5 /*
6  * ls
7  *
8  * 4.2bsd version for symbolic links and variable length directory entries.
9  */
10 
11 #include <sys/param.h>
12 #include <sys/stat.h>
13 #include <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 	time_t	fmtime;		/* time (modify or access or create) */
28 	char	*fname;		/* file name */
29 	char	*flinkto;	/* symbolic link value */
30 };
31 
32 #define ISARG	0x8000		/* extra ``mode'' */
33 
34 struct subdirs {
35 	char	*sd_name;
36 	struct	subdirs *sd_next;
37 } *subdirs;
38 
39 int	aflg, dflg, gflg, lflg, sflg, tflg, uflg, iflg, fflg, cflg, rflg = 1;
40 int	qflg, Aflg, Cflg, Fflg, Lflg, Rflg;
41 
42 int	usetabs;
43 
44 time_t	now, sixmonthsago;
45 
46 char	*dotp = ".";
47 
48 struct	afile *gstat();
49 int	fcmp();
50 char	*cat(), *savestr();
51 char	*fmtentry();
52 char	*getname(), *getgroup();
53 
54 char	*ctime();
55 char	*malloc(), *calloc(), *realloc();
56 char	*sprintf(), *strcpy(), *strcat();
57 
58 main(argc, argv)
59 	int argc;
60 	char *argv[];
61 {
62 	int i;
63 	struct afile *fp0, *fplast;
64 	register struct afile *fp;
65 	struct sgttyb sgbuf;
66 
67 	argc--, argv++;
68 	if (getuid() == 0)
69 		Aflg++;
70 	(void) time(&now); sixmonthsago = now - 6L*30L*24L*60L*60L; now += 60;
71 	if (isatty(1)) {
72 
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 nkb, 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 	nkb = 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, &nkb) == 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 (nkb);
263 }
264 
265 int	stat(), lstat();
266 
267 struct afile *
268 gstat(fp, file, statarg, pnkb)
269 	register struct afile *fp;
270 	char *file;
271 	int statarg, *pnkb;
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 			fprintf(stderr, "%s not found\n", file);
286 			return (0);
287 		}
288 		fp->fsize = stb.st_size;
289 		switch (stb.st_mode & S_IFMT) {
290 
291 		case S_IFDIR:
292 			fp->ftype = 'd'; break;
293 		case S_IFBLK:
294 			fp->ftype = 'b'; fp->fsize = stb.st_rdev; break;
295 		case S_IFCHR:
296 			fp->ftype = 'c'; fp->fsize = stb.st_rdev; break;
297 		case S_IFSOCK:
298 			fp->ftype = 's'; fp->fsize = 0; break;
299 		case S_IFLNK:
300 			fp->ftype = 'l';
301 			if (lflg) {
302 				cc = readlink(file, buf, BUFSIZ);
303 				if (cc >= 0) {
304 					buf[cc] = 0;
305 					fp->flinkto = savestr(buf);
306 				}
307 				break;
308 			}
309 			if (stat(file, &stb1) < 0)
310 				break;
311 			if ((stb1.st_mode & S_IFMT) == S_IFDIR) {
312 				stb = stb1;
313 				fp->ftype = 'd';
314 				fp->fsize = stb.st_size;
315 			}
316 			break;
317 		}
318 		fp->fnum = stb.st_ino;
319 		fp->fflags = stb.st_mode & ~S_IFMT;
320 		fp->fnl = stb.st_nlink;
321 		fp->fuid = stb.st_uid;
322 		fp->fgid = stb.st_gid;
323 		if (uflg)
324 			fp->fmtime = stb.st_atime;
325 		else if (cflg)
326 			fp->fmtime = stb.st_ctime;
327 		else
328 			fp->fmtime = stb.st_mtime;
329 		if (pnkb)
330 			if (fp->ftype != 'b' && fp->ftype != 'c' &&
331 			    fp->ftype != 's')
332 				*pnkb += kbytes(fp->fsize);
333 	}
334 	return (fp);
335 }
336 
337 formatf(fp0, fplast)
338 	struct afile *fp0, *fplast;
339 {
340 	register struct afile *fp;
341 	int width = 0, w, nentry = fplast - fp0;
342 	int i, j, columns, lines;
343 	char *cp;
344 
345 	if (fp0 == fplast)
346 		return;
347 	if (lflg || Cflg == 0)
348 		columns = 1;
349 	else {
350 		for (fp = fp0; fp < fplast; fp++) {
351 			int len = strlen(fmtentry(fp));
352 
353 			if (len > width)
354 				width = len;
355 		}
356 		if (usetabs)
357 			width = (width + 8) &~ 7;
358 		else
359 			width += 2;
360 		columns = 80 / width;
361 		if (columns == 0)
362 			columns = 1;
363 	}
364 	lines = (nentry + columns - 1) / columns;
365 	for (i = 0; i < lines; i++) {
366 		for (j = 0; j < columns; j++) {
367 			fp = fp0 + j * lines + i;
368 			cp = fmtentry(fp);
369 			printf("%s", cp);
370 			if (fp + lines >= fplast) {
371 				printf("\n");
372 				break;
373 			}
374 			w = strlen(cp);
375 			while (w < width)
376 				if (usetabs) {
377 					w = (w + 8) &~ 7;
378 					putchar('\t');
379 				} else {
380 					w++;
381 					putchar(' ');
382 				}
383 		}
384 	}
385 }
386 
387 fcmp(f1, f2)
388 	register struct afile *f1, *f2;
389 {
390 
391 	if (dflg == 0 && fflg == 0) {
392 		if ((f1->fflags&ISARG) && f1->ftype == 'd') {
393 			if ((f2->fflags&ISARG) == 0 || f2->ftype != 'd')
394 				return (1);
395 		} else {
396 			if ((f2->fflags&ISARG) && f2->ftype == 'd')
397 				return (-1);
398 		}
399 	}
400 	if (tflg) {
401 		if (f2->fmtime == f1->fmtime)
402 			return (0);
403 		if (f2->fmtime > f1->fmtime)
404 			return (rflg);
405 		return (-rflg);
406 	}
407 	return (rflg * strcmp(f1->fname, f2->fname));
408 }
409 
410 char *
411 cat(dir, file)
412 	char *dir, *file;
413 {
414 	static char dfile[BUFSIZ];
415 
416 	if (strlen(dir)+1+strlen(file)+1 > BUFSIZ) {
417 		fprintf(stderr, "ls: filename too long\n");
418 		exit(1);
419 	}
420 	if (!strcmp(dir, "") || !strcmp(dir, ".")) {
421 		(void) strcpy(dfile, file);
422 		return (dfile);
423 	}
424 	(void) strcpy(dfile, dir);
425 	if (dir[strlen(dir) - 1] != '/' && *file != '/')
426 		(void) strcat(dfile, "/");
427 	(void) strcat(dfile, file);
428 	return (dfile);
429 }
430 
431 char *
432 savestr(str)
433 	char *str;
434 {
435 	char *cp = malloc(strlen(str) + 1);
436 
437 	if (cp == NULL) {
438 		fprintf(stderr, "ls: out of memory\n");
439 		exit(1);
440 	}
441 	(void) strcpy(cp, str);
442 	return (cp);
443 }
444 
445 char	*fmtinum(), *fmtsize(), *fmtlstuff(), *fmtmode();
446 
447 char *
448 fmtentry(fp)
449 	register struct afile *fp;
450 {
451 	static char fmtres[BUFSIZ];
452 	register char *cp, *dp;
453 
454 	(void) sprintf(fmtres, "%s%s%s",
455 	    iflg ? fmtinum(fp) : "",
456 	    sflg ? fmtsize(fp) : "",
457 	    lflg ? fmtlstuff(fp) : "");
458 	dp = &fmtres[strlen(fmtres)];
459 	for (cp = fp->fname; *cp; cp++)
460 		if (qflg && (*cp < ' ' || *cp >= 0177))
461 			*dp++ = '?';
462 		else
463 			*dp++ = *cp;
464 	if (Fflg) {
465 		if (fp->ftype == 'd')
466 			*dp++ = '/';
467 		else if (fp->ftype == 'l')
468 			*dp++ = '@';
469 		else if (fp->ftype == 's')
470 			*dp++ = '=';
471 		else if (fp->fflags & 0111)
472 			*dp++ = '*';
473 	}
474 	if (lflg && fp->flinkto) {
475 		(void) strcpy(dp, " -> "); dp += 4;
476 		for (cp = fp->flinkto; *cp; cp++)
477 			if (qflg && (*cp < ' ' || *cp >= 0177))
478 				*dp++ = '?';
479 			else
480 				*dp++ = *cp;
481 	}
482 	*dp++ = 0;
483 	return (fmtres);
484 }
485 
486 char *
487 fmtinum(p)
488 	register struct afile *p;
489 {
490 	static char inumbuf[8];
491 
492 	(void) sprintf(inumbuf, "%5d ", p->fnum);
493 	return (inumbuf);
494 }
495 
496 char *
497 fmtsize(p)
498 	register struct afile *p;
499 {
500 	static char sizebuf[32];
501 
502 	switch (p->ftype) {
503 
504 	case 'b':
505 	case 'c':
506 	case 's':
507 		(void) sprintf(sizebuf, "%4ld ", 0);
508 		break;
509 
510 	default:
511 		(void) sprintf(sizebuf, "%4ld ", kbytes(p->fsize));
512 		break;
513 	}
514 	return (sizebuf);
515 }
516 
517 char *
518 fmtlstuff(p)
519 	register struct afile *p;
520 {
521 	static char lstuffbuf[256];
522 	char gname[32], uname[32], fsize[32], ftime[32];
523 	register char *lp = lstuffbuf;
524 
525 	/* type mode uname gname fsize ftime */
526 /* get uname */
527 	{ char *cp = getname(p->fuid);
528 	  if (cp)
529 		(void) sprintf(uname, "%-9.9s", cp);
530 	  else
531 		(void) sprintf(uname, "%-9d", p->fuid);
532 	}
533 /* get gname */
534 	if (gflg) {
535 	  char *cp = getgroup(p->fgid);
536 	  if (cp)
537 		(void) sprintf(gname, "%-9.9s", cp);
538 	  else
539 		(void) sprintf(gname, "%-9d", p->fgid);
540 	}
541 /* get fsize */
542 	if (p->ftype == 'b' || p->ftype == 'c')
543 		(void) sprintf(fsize, "%3d,%4d",
544 		    major(p->fsize), minor(p->fsize));
545 	else if (p->ftype == 's')
546 		(void) sprintf(fsize, "%8ld", 0);
547 	else
548 		(void) sprintf(fsize, "%8ld", p->fsize);
549 /* get ftime */
550 	{ char *cp = ctime(&p->fmtime);
551 	  if ((p->fmtime < sixmonthsago) || (p->fmtime > now))
552 		(void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20);
553 	  else
554 		(void) sprintf(ftime, " %-12.12s ", cp+4);
555 	}
556 /* splat */
557 	*lp++ = p->ftype;
558 	lp = fmtmode(lp, p->fflags);
559 	(void) sprintf(lp, "%3d %s%s%s%s",
560 	    p->fnl, uname, gflg ? gname : "", fsize, ftime);
561 	return (lstuffbuf);
562 }
563 
564 int	m1[] = { 1, S_IREAD>>0, 'r', '-' };
565 int	m2[] = { 1, S_IWRITE>>0, 'w', '-' };
566 int	m3[] = { 2, S_ISUID, 's', S_IEXEC>>0, 'x', '-' };
567 int	m4[] = { 1, S_IREAD>>3, 'r', '-' };
568 int	m5[] = { 1, S_IWRITE>>3, 'w', '-' };
569 int	m6[] = { 2, S_ISGID, 's', S_IEXEC>>3, 'x', '-' };
570 int	m7[] = { 1, S_IREAD>>6, 'r', '-' };
571 int	m8[] = { 1, S_IWRITE>>6, 'w', '-' };
572 int	m9[] = { 2, S_ISVTX, 't', S_IEXEC>>6, 'x', '-' };
573 
574 int	*m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
575 
576 char *
577 fmtmode(lp, flags)
578 	char *lp;
579 	int flags;
580 {
581 	int **mp;
582 
583 	for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])]; ) {
584 		register int *pairp = *mp++;
585 		register int n = *pairp++;
586 
587 		while (--n >= 0 && (flags&*pairp++) == 0)
588 			pairp++;
589 		*lp++ = *pairp;
590 	}
591 	return (lp);
592 }
593 
594 /* rest should be done with nameserver or database */
595 
596 #include <pwd.h>
597 #include <grp.h>
598 #include <utmp.h>
599 
600 struct	utmp utmp;
601 
602 #define NUID	2048
603 #define NGID	300
604 #define	NMAX	(sizeof (utmp.ut_name))
605 
606 char	names[NUID][NMAX+1];
607 char	groups[NGID][NMAX+1];
608 
609 char *
610 getname(uid)
611 {
612 	register struct passwd *pw;
613 	static init;
614 	struct passwd *getpwent();
615 
616 	if (uid >= 0 && uid < NUID && names[uid][0])
617 		return (&names[uid][0]);
618 	if (init == 2)
619 		return (0);
620 	if (init == 0)
621 		setpwent(), init = 1;
622 	while (pw = getpwent()) {
623 		if (pw->pw_uid < 0 || pw->pw_uid >= NUID)
624 			continue;
625 		if (names[pw->pw_uid][0])
626 			continue;
627 		strncpy(names[pw->pw_uid], pw->pw_name, NMAX);
628 		if (pw->pw_uid == uid) {
629 			return (&names[uid][0]);
630 		}
631 	}
632 	init = 2;
633 	endpwent();
634 	return (0);
635 }
636 
637 char *
638 getgroup(gid)
639 {
640 	register struct group *gr;
641 	static init;
642 	struct group *getgrent();
643 
644 	if (gid >= 0 && gid < NGID && groups[gid][0])
645 		return (&groups[gid][0]);
646 	if (init == 2)
647 		return (0);
648 	if (init == 0)
649 		setgrent(), init = 1;
650 	while (gr = getgrent()) {
651 		if (gr->gr_gid < 0 || gr->gr_gid >= NGID)
652 			continue;
653 		if (groups[gr->gr_gid][0])
654 			continue;
655 		strncpy(groups[gr->gr_gid], gr->gr_name, NMAX);
656 		if (gr->gr_gid == gid) {
657 			return (&groups[gid][0]);
658 		}
659 	}
660 	init = 2;
661 	endgrent();
662 	return (0);
663 }
664