xref: /original-bsd/bin/ls/ls.c (revision 957a0273)
1 #ifndef lint
2 static	char *sccsid = "@(#)ls.c	4.6 82/05/07";
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;
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",
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->ftype == 'l')
457 			*dp++ = '@';
458 		else if (fp->fflags & 0111)
459 			*dp++ = '*';
460 	}
461 	if (lflg && fp->flinkto) {
462 		(void) strcpy(dp, " -> "); dp += 4;
463 		for (cp = fp->flinkto; *cp; cp++)
464 			if (qflg && (*cp < ' ' || *cp >= 0177))
465 				*dp++ = '?';
466 			else
467 				*dp++ = *cp;
468 	}
469 	*dp++ = 0;
470 	return (fmtres);
471 }
472 
473 char *
474 fmtinum(p)
475 	register struct afile *p;
476 {
477 	static char inumbuf[8];
478 
479 	(void) sprintf(inumbuf, "%5d ", p->fnum);
480 	return (inumbuf);
481 }
482 
483 char *
484 fmtsize(p)
485 	register struct afile *p;
486 {
487 	static char sizebuf[32];
488 
489 	switch (p->ftype) {
490 
491 	case 'b':
492 	case 'c':
493 		(void) sprintf(sizebuf, "%4ld ", 0);
494 		break;
495 
496 	default:
497 		(void) sprintf(sizebuf, "%4ld ", kbytes(p->fsize));
498 		break;
499 	}
500 	return (sizebuf);
501 }
502 
503 char *
504 fmtlstuff(p)
505 	register struct afile *p;
506 {
507 	static char lstuffbuf[256];
508 	char gname[32], uname[32], fsize[32], ftime[32];
509 	register char *lp = lstuffbuf;
510 
511 	/* type mode uname gname fsize ftime */
512 /* get uname */
513 	{ char *cp = getname(p->fuid);
514 	  if (cp)
515 		(void) sprintf(uname, "%-9.9s", cp);
516 	  else
517 		(void) sprintf(uname, "%-9d", p->fuid);
518 	}
519 /* get gname */
520 	if (gflg) {
521 	  char *cp = getgroup(p->fgid);
522 	  if (cp)
523 		(void) sprintf(gname, "%-9.9s", cp);
524 	  else
525 		(void) sprintf(gname, "%-9d", p->fgid);
526 	}
527 /* get fsize */
528 	if (p->ftype == 'b' || p->ftype == 'c')
529 		(void) sprintf(fsize, "%3d,%4d",
530 		    major(p->fsize), minor(p->fsize));
531 	else
532 		(void) sprintf(fsize, "%8ld", p->fsize);
533 /* get ftime */
534 	{ char *cp = ctime(&p->fmtime);
535 	  if ((p->fmtime < sixmonthsago) || (p->fmtime > now))
536 		(void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20);
537 	  else
538 		(void) sprintf(ftime, " %-12.12s ", cp+4);
539 	}
540 /* splat */
541 	*lp++ = p->ftype;
542 	lp = fmtmode(lp, p->fflags);
543 	(void) sprintf(lp, "%3d %s%s%s%s",
544 	    p->fnl, uname, gflg ? gname : "", fsize, ftime);
545 	return (lstuffbuf);
546 }
547 
548 int	m1[] = { 1, S_IREAD>>0, 'r', '-' };
549 int	m2[] = { 1, S_IWRITE>>0, 'w', '-' };
550 int	m3[] = { 2, S_ISUID, 's', S_IEXEC>>0, 'x', '-' };
551 int	m4[] = { 1, S_IREAD>>3, 'r', '-' };
552 int	m5[] = { 1, S_IWRITE>>3, 'w', '-' };
553 int	m6[] = { 2, S_ISGID, 's', S_IEXEC>>3, 'x', '-' };
554 int	m7[] = { 1, S_IREAD>>6, 'r', '-' };
555 int	m8[] = { 1, S_IWRITE>>6, 'w', '-' };
556 int	m9[] = { 2, S_ISVTX, 't', S_IEXEC>>6, 'x', '-' };
557 
558 int	*m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
559 
560 char *
561 fmtmode(lp, flags)
562 	char *lp;
563 	int flags;
564 {
565 	int **mp;
566 
567 	for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])]; ) {
568 		register int *pairp = *mp++;
569 		register int n = *pairp++;
570 
571 		while (--n >= 0 && (flags&*pairp++) == 0)
572 			pairp++;
573 		*lp++ = *pairp;
574 	}
575 	return (lp);
576 }
577 
578 /* rest should be done with nameserver or database */
579 
580 #include <pwd.h>
581 #include <grp.h>
582 #include <utmp.h>
583 
584 struct	utmp utmp;
585 
586 #define NUID	2048
587 #define NGID	300
588 #define	NMAX	(sizeof (utmp.ut_name))
589 
590 char	names[NUID][NMAX+1];
591 char	groups[NGID][NMAX+1];
592 
593 char *
594 getname(uid)
595 {
596 	register struct passwd *pw;
597 	static init;
598 	struct passwd *getpwent();
599 
600 	if (uid >= 0 && uid < NUID && names[uid][0])
601 		return (&names[uid][0]);
602 	if (init == 2)
603 		return (0);
604 	if (init == 0)
605 		setpwent(), init = 1;
606 	while (pw = getpwent()) {
607 		if (pw->pw_uid < 0 || pw->pw_uid >= NUID)
608 			continue;
609 		if (names[pw->pw_uid][0])
610 			continue;
611 		strncpy(names[pw->pw_uid], pw->pw_name, NMAX);
612 		if (pw->pw_uid == uid) {
613 			return (&names[uid][0]);
614 		}
615 	}
616 	init = 2;
617 	endpwent();
618 	return (0);
619 }
620 
621 char *
622 getgroup(gid)
623 {
624 	register struct group *gr;
625 	static init;
626 	struct group *getgrent();
627 
628 	if (gid >= 0 && gid < NGID && groups[gid][0])
629 		return (&groups[gid][0]);
630 	if (init == 2)
631 		return (0);
632 	if (init == 0)
633 		setgrent(), init = 1;
634 	while (gr = getgrent()) {
635 		if (gr->gr_gid < 0 || gr->gr_gid >= NGID)
636 			continue;
637 		if (groups[gr->gr_gid][0])
638 			continue;
639 		strncpy(groups[gr->gr_gid], gr->gr_name, NMAX);
640 		if (gr->gr_gid == gid) {
641 			return (&groups[gid][0]);
642 		}
643 	}
644 	init = 2;
645 	endgrent();
646 	return (0);
647 }
648