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