Home | History | Annotate | Download | only in ls
      1 /* Portions Copyright 2006 Stephen P. Potter */
      2 
      3 /*
      4  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
      5  * Use is subject to license terms.
      6  */
      7 
      8 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
      9 /*	  All Rights Reserved	*/
     10 
     11 /*
     12  * Copyright (c) 1980 Regents of the University of California.
     13  * All rights reserved.  The Berkeley software License Agreement
     14  * specifies the terms and conditions for redistribution.
     15  */
     16 
     17 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     18 
     19 /*
     20  * ls
     21  *
     22  * 4.2bsd version for symbolic links, variable length
     23  * directory entries, block size in the inode, etc.
     24  */
     25 
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <unistd.h>
     29 #include <string.h>
     30 #include <stddef.h>
     31 #include <dirent.h>
     32 #include <ctype.h>
     33 #include <time.h>
     34 #include <limits.h>
     35 #include <locale.h>
     36 #include <errno.h>
     37 #include <sys/types.h>
     38 #include <sys/param.h>
     39 #include <sys/stat.h>
     40 #include <sys/termios.h>
     41 #include <sys/mkdev.h>
     42 #include <sys/acl.h>
     43 
     44 #define	dbtokb(nb)	((nb) / (1024 / DEV_BSIZE))
     45 
     46 struct afile {
     47 	char	ftype;		/* file type, e.g. 'd', 'c', 'f' */
     48 	ino_t	fnum;		/* inode number of file */
     49 	short	fflags;		/* mode&~S_IFMT, perhaps ISARG */
     50 	nlink_t	fnl;		/* number of links */
     51 	uid_t	fuid;		/* owner id */
     52 	gid_t	fgid;		/* group id */
     53 	off_t	fsize;		/* file size */
     54 	blkcnt_t	fblks;		/* number of blocks used */
     55 	time_t	fmtime;		/* time (modify or access or create) */
     56 	char	*fname;		/* file name */
     57 	char	*flinkto;	/* symbolic link value */
     58 	char	acl;		/* acl access flag */
     59 };
     60 
     61 #define	ISARG	0x8000		/* extra ``mode'' */
     62 
     63 static struct subdirs {
     64 	char	*sd_name;
     65 	struct	subdirs *sd_next;
     66 } *subdirs;
     67 
     68 static	int	aflg, dflg, gflg, lflg, sflg, tflg, uflg, iflg, fflg, cflg;
     69 static	int	rflg = 1;
     70 static	int	qflg, Aflg, Cflg, Fflg, Lflg, Rflg;
     71 
     72 static	int	usetabs;
     73 
     74 static	time_t	now, sixmonthsago, onehourfromnow;
     75 
     76 static	char	*dotp = ".";
     77 
     78 static	struct	winsize win;
     79 static	int	twidth;
     80 
     81 static	struct	afile *gstat(struct afile *, char *, int, off_t *);
     82 static	int	fcmp(const void *, const void *);
     83 static	char	*cat(char *, char *);
     84 static	char	*savestr(char *);
     85 static	char	*fmtentry(struct afile *);
     86 static	char	*getname(), *getgroup();
     87 static	void	formatd(char *, int);
     88 static	void	formatf(struct afile *, struct afile *);
     89 static	off_t	getdir(char *, struct afile **, struct afile **);
     90 
     91 int
     92 main(int argc, char **argv)
     93 {
     94 	int i;
     95 	struct afile *fp0, *fplast;
     96 	register struct afile *fp;
     97 	struct termios trbuf;
     98 
     99 	argc--, argv++;
    100 	if (getuid() == 0)
    101 		Aflg++;
    102 	(void) time(&now);
    103 	sixmonthsago = now - 6L*30L*24L*60L*60L;
    104 	onehourfromnow = now + 60L*60L;
    105 	now += 60;
    106 	twidth = 80;
    107 	if (isatty(1)) {
    108 		qflg = Cflg = 1;
    109 		(void) ioctl(1, TCGETS, &trbuf);
    110 		if (ioctl(1, TIOCGWINSZ, &win) != -1)
    111 			twidth = (win.ws_col == 0 ? 80 : win.ws_col);
    112 		if ((trbuf.c_oflag & TABDLY) != TAB3)
    113 			usetabs = 1;
    114 	} else
    115 		usetabs = 1;
    116 
    117 	(void) setlocale(LC_ALL, "");		/* set local environment */
    118 
    119 	while (argc > 0 && **argv == '-') {
    120 		(*argv)++;
    121 		while (**argv) {
    122 			switch (*(*argv)++) {
    123 			case 'C':
    124 				Cflg = 1; break;
    125 			case 'q':
    126 				qflg = 1; break;
    127 			case '1':
    128 				Cflg = 0; break;
    129 			case 'a':
    130 				aflg++; break;
    131 			case 'A':
    132 				Aflg++; break;
    133 			case 'c':
    134 				cflg++; break;
    135 			case 's':
    136 				sflg++; break;
    137 			case 'd':
    138 				dflg++; break;
    139 			case 'g':
    140 				gflg++; break;
    141 			case 'l':
    142 				lflg++; break;
    143 			case 'r':
    144 				rflg = -1; break;
    145 			case 't':
    146 				tflg++; break;
    147 			case 'u':
    148 				uflg++; break;
    149 			case 'i':
    150 				iflg++; break;
    151 			case 'f':
    152 				fflg++; break;
    153 			case 'L':
    154 				Lflg++; break;
    155 			case 'F':
    156 				Fflg++; break;
    157 			case 'R':
    158 				Rflg++; break;
    159 			}
    160 		}
    161 		argc--, argv++;
    162 	}
    163 	if (fflg) {
    164 		aflg++; lflg = 0; sflg = 0; tflg = 0;
    165 	}
    166 	if (lflg)
    167 		Cflg = 0;
    168 	if (argc == 0) {
    169 		argc++;
    170 		argv = &dotp;
    171 	}
    172 	fp = (struct afile *)calloc(argc, sizeof (struct afile));
    173 	if (fp == 0) {
    174 		(void) fprintf(stderr, "ls: out of memory\n");
    175 		exit(1);
    176 	}
    177 	fp0 = fp;
    178 	for (i = 0; i < argc; i++) {
    179 		if (gstat(fp, *argv, 1, (off_t *)0)) {
    180 			fp->fname = *argv;
    181 			fp->fflags |= ISARG;
    182 			fp++;
    183 		}
    184 		argv++;
    185 	}
    186 	fplast = fp;
    187 	qsort(fp0, fplast - fp0, sizeof (struct afile), fcmp);
    188 	if (dflg) {
    189 		formatf(fp0, fplast);
    190 		exit(0);
    191 	}
    192 	if (fflg)
    193 		fp = fp0;
    194 	else {
    195 		for (fp = fp0; fp < fplast && fp->ftype != 'd'; fp++)
    196 			continue;
    197 		formatf(fp0, fp);
    198 	}
    199 	if (fp < fplast) {
    200 		if (fp > fp0)
    201 			(void) printf("\n");
    202 		for (;;) {
    203 			formatd(fp->fname, argc > 1);
    204 			while (subdirs) {
    205 				struct subdirs *t;
    206 
    207 				t = subdirs; subdirs = t->sd_next;
    208 				(void) printf("\n");
    209 				formatd(t->sd_name, 1);
    210 				free(t->sd_name);
    211 				free(t);
    212 			}
    213 			if (++fp == fplast)
    214 				break;
    215 			(void) printf("\n");
    216 		}
    217 	}
    218 	return (0);
    219 }
    220 
    221 static void
    222 formatd(char *name, int title)
    223 {
    224 	register struct afile *fp;
    225 	register struct subdirs *dp;
    226 	struct afile *dfp0, *dfplast;
    227 	off_t nkb;
    228 
    229 	nkb = getdir(name, &dfp0, &dfplast);
    230 	if (dfp0 == 0)
    231 		return;
    232 	if (fflg == 0)
    233 		qsort(dfp0, dfplast - dfp0, sizeof (struct afile), fcmp);
    234 	if (title)
    235 		(void) printf("%s:\n", name);
    236 	if (lflg || sflg)
    237 		(void) printf("total %lld\n", nkb);
    238 	formatf(dfp0, dfplast);
    239 	if (Rflg)
    240 		for (fp = dfplast - 1; fp >= dfp0; fp--) {
    241 			if (fp->ftype != 'd' ||
    242 			    strcmp(fp->fname, ".") == 0 ||
    243 			    strcmp(fp->fname, "..") == 0)
    244 				continue;
    245 			dp = (struct subdirs *)malloc(sizeof (struct subdirs));
    246 			dp->sd_name = savestr(cat(name, fp->fname));
    247 			dp->sd_next = subdirs; subdirs = dp;
    248 		}
    249 	for (fp = dfp0; fp < dfplast; fp++) {
    250 		if ((fp->fflags&ISARG) == 0 && fp->fname)
    251 			free(fp->fname);
    252 		if (fp->flinkto)
    253 			free(fp->flinkto);
    254 	}
    255 	free(dfp0);
    256 }
    257 
    258 static off_t
    259 getdir(char *dir, struct afile **pfp0, struct afile **pfplast)
    260 {
    261 	register struct afile *fp;
    262 	DIR *dirp;
    263 	register struct dirent *dp;
    264 	off_t nb;
    265 	size_t nent = 20;
    266 
    267 	/*
    268 	 * This code (opendir, readdir, and the "for" loop) is arranged in
    269 	 * this strange manner to handle the case where UNIX lets root open
    270 	 * any directory for reading, but NFS does not let root read the
    271 	 * openned directory.
    272 	 */
    273 	*pfp0 = *pfplast = NULL;
    274 	if ((dirp = opendir(dir)) == NULL) {
    275 		(void) printf("%s unreadable\n", dir);	/* not stderr! */
    276 		return (0);
    277 	}
    278 	errno = 0;
    279 	if (((dp = readdir(dirp)) == NULL) && (errno != 0)) {
    280 		/* root reading across NFS can get to this error case */
    281 		(void) printf("%s unreadable\n", dir);	/* not stderr! */
    282 		(void) closedir(dirp);
    283 		return (0);
    284 	}
    285 	fp = *pfp0 = (struct afile *)calloc(nent, sizeof (struct afile));
    286 	*pfplast = *pfp0 + nent;
    287 	for (nb = 0; dp != NULL; dp = readdir(dirp)) {
    288 		if (dp->d_ino == 0)
    289 			continue;
    290 		if (aflg == 0 && dp->d_name[0] == '.' &&
    291 		    (Aflg == 0 || dp->d_name[1] == 0 ||
    292 			dp->d_name[1] == '.' && dp->d_name[2] == 0))
    293 			continue;
    294 		if (gstat(fp, cat(dir, dp->d_name), Fflg+Rflg, &nb) == 0)
    295 			continue;
    296 		fp->fnum = dp->d_ino;
    297 		fp->fname = savestr(dp->d_name);
    298 		fp++;
    299 		if (fp == *pfplast) {
    300 			*pfp0 = (struct afile *)realloc((char *)*pfp0,
    301 			    2 * nent * sizeof (struct afile));
    302 			if (*pfp0 == 0) {
    303 				(void) fprintf(stderr, "ls: out of memory\n");
    304 				exit(1);
    305 			}
    306 			fp = *pfp0 + nent;
    307 			*pfplast = fp + nent;
    308 			nent *= 2;
    309 		}
    310 	}
    311 	(void) closedir(dirp);
    312 	*pfplast = fp;
    313 	return (dbtokb(nb));
    314 }
    315 
    316 
    317 static struct afile *
    318 gstat(struct afile *fp, char *file, int statarg, off_t *pnb)
    319 {
    320 	static struct afile azerofile;
    321 	int (*statf)() = Lflg ? stat : lstat;
    322 	int cc;
    323 	char buf[PATH_MAX];
    324 	int aclcnt;
    325 	aclent_t *aclp;
    326 	aclent_t *tp;
    327 	o_mode_t groupperm, mask;
    328 	int grouppermfound, maskfound;
    329 
    330 	*fp = azerofile;
    331 	fp->fflags = 0;
    332 	fp->fnum = 0;
    333 	fp->ftype = '-';
    334 	if (statarg || sflg || lflg || tflg) {
    335 		struct stat stb, stb1;
    336 
    337 		if ((*statf)(file, &stb) < 0) {
    338 			if (statf == lstat || lstat(file, &stb) < 0) {
    339 				if (errno == ENOENT)
    340 					(void) fprintf(stderr,
    341 						"%s not found\n", file);
    342 				else {
    343 					(void) fprintf(stderr, "ls: ");
    344 					perror(file);
    345 				}
    346 				return (0);
    347 			}
    348 		}
    349 		fp->fblks = stb.st_blocks;
    350 		fp->fsize = stb.st_size;
    351 		switch (stb.st_mode & S_IFMT) {
    352 		case S_IFDIR:
    353 			fp->ftype = 'd'; break;
    354 		case S_IFDOOR:
    355 			fp->ftype = 'D'; break;
    356 		case S_IFBLK:
    357 			fp->ftype = 'b'; fp->fsize = (off_t)stb.st_rdev; break;
    358 		case S_IFCHR:
    359 			fp->ftype = 'c'; fp->fsize = (off_t)stb.st_rdev; break;
    360 		case S_IFSOCK:
    361 			fp->ftype = 's'; fp->fsize = 0LL; break;
    362 		case S_IFIFO:
    363 			fp->ftype = 'p'; fp->fsize = 0LL; break;
    364 		case S_IFLNK:
    365 			fp->ftype = 'l';
    366 			if (lflg) {
    367 				cc = readlink(file, buf, BUFSIZ);
    368 				if (cc >= 0) {
    369 					/*
    370 					 * here we follow the symbolic
    371 					 * link to generate the proper
    372 					 * Fflg marker for the object,
    373 					 * eg, /bin -> /pub/bin/
    374 					 */
    375 					buf[cc] = 0;
    376 					if (Fflg && !stat(file, &stb1))
    377 						switch (stb1.st_mode & S_IFMT) {
    378 						case S_IFDIR:
    379 							buf[cc++] = '/';
    380 							break;
    381 						case S_IFDOOR:
    382 							buf[cc++] = '>';
    383 							break;
    384 						case S_IFIFO:
    385 							buf[cc++] = '|';
    386 							break;
    387 						case S_IFSOCK:
    388 							buf[cc++] = '=';
    389 							break;
    390 						default:
    391 						if ((stb1.st_mode & ~S_IFMT)
    392 						    & 0111)
    393 							buf[cc++] = '*';
    394 							break;
    395 						}
    396 					buf[cc] = 0;
    397 					fp->flinkto = savestr(buf);
    398 				}
    399 				break;
    400 			}
    401 			/*
    402 			 *  this is a hack from UCB to avoid having
    403 			 *  ls /bin behave differently from ls /bin/
    404 			 *  when /bin is a symbolic link.  We hack the
    405 			 *  hack to have that happen, but only for
    406 			 *  explicit arguments, by inspecting pnb.
    407 			 */
    408 			if (pnb != (off_t *)0 || stat(file, &stb1) < 0)
    409 				break;
    410 			if ((stb1.st_mode & S_IFMT) == S_IFDIR) {
    411 				stb = stb1;
    412 				fp->ftype = 'd';
    413 				fp->fsize = stb.st_size;
    414 				fp->fblks = stb.st_blocks;
    415 			}
    416 			break;
    417 		}
    418 		fp->fnum = stb.st_ino;
    419 		fp->fflags = stb.st_mode & ~S_IFMT;
    420 		fp->fnl = stb.st_nlink;
    421 		fp->fuid = stb.st_uid;
    422 		fp->fgid = stb.st_gid;
    423 
    424 		/* ACL: check acl entries count */
    425 		if ((aclcnt = acl(file, GETACLCNT, 0, NULL)) >
    426 		    MIN_ACL_ENTRIES) {
    427 
    428 			/* this file has a non-trivial acl */
    429 
    430 			fp->acl = '+';
    431 
    432 			/*
    433 			 * For files with non-trivial acls, the
    434 			 * effective group permissions are the
    435 			 * intersection of the GROUP_OBJ value and
    436 			 * the CLASS_OBJ (acl mask) value. Determine
    437 			 * both the GROUP_OBJ and CLASS_OBJ for this
    438 			 * file and insert the logical AND of those
    439 			 * two values in the group permissions field
    440 			 * of the lflags value for this file.
    441 			 */
    442 
    443 			if ((aclp = (aclent_t *)malloc(
    444 			    (sizeof (aclent_t)) * aclcnt)) == NULL) {
    445 				perror("ls");
    446 				exit(2);
    447 			}
    448 
    449 			if (acl(file, GETACL, aclcnt, aclp) < 0) {
    450 				free(aclp);
    451 				(void) fprintf(stderr, "ls: ");
    452 				perror(file);
    453 				return (0);
    454 			}
    455 
    456 			/*
    457 			 * Until found in acl list, assume maximum
    458 			 * permissions for both group and mask.  (Just
    459 			 * in case the acl lacks either value for
    460 			 * some reason.)
    461 			 */
    462 			groupperm = 07;
    463 			mask = 07;
    464 			grouppermfound = 0;
    465 			maskfound = 0;
    466 			for (tp = aclp; aclcnt--; tp++) {
    467 				if (tp->a_type == GROUP_OBJ) {
    468 					groupperm = tp->a_perm;
    469 					grouppermfound = 1;
    470 					continue;
    471 				}
    472 				if (tp->a_type == CLASS_OBJ) {
    473 					mask = tp->a_perm;
    474 					maskfound = 1;
    475 				}
    476 				if (grouppermfound && maskfound)
    477 					break;
    478 			}
    479 
    480 			free(aclp);
    481 
    482 			/* reset all the group bits */
    483 			fp->fflags &= ~S_IRWXG;
    484 
    485 			/*
    486 			 * Now set them to the logical AND of the
    487 			 * GROUP_OBJ permissions and the acl mask.
    488 			 */
    489 
    490 			fp->fflags |= (groupperm & mask) << 3;
    491 		} else
    492 			fp->acl = ' ';
    493 
    494 		if (uflg)
    495 			fp->fmtime = stb.st_atime;
    496 		else if (cflg)
    497 			fp->fmtime = stb.st_ctime;
    498 		else
    499 			fp->fmtime = stb.st_mtime;
    500 		if (pnb)
    501 			*pnb += stb.st_blocks;
    502 	}
    503 	return (fp);
    504 }
    505 
    506 static void
    507 formatf(struct afile *fp0, struct afile *fplast)
    508 {
    509 	register struct afile *fp;
    510 	int width = 0, w, nentry = fplast - fp0;
    511 	int i, j, columns, lines;
    512 	char *cp;
    513 
    514 	if (fp0 == fplast)
    515 		return;
    516 	if (lflg || Cflg == 0)
    517 		columns = 1;
    518 	else {
    519 		for (fp = fp0; fp < fplast; fp++) {
    520 			int len = strlen(fmtentry(fp));
    521 
    522 			if (len > width)
    523 				width = len;
    524 		}
    525 		if (usetabs)
    526 			width = (width + 8) &~ 7;
    527 		else
    528 			width += 2;
    529 		columns = twidth / width;
    530 		if (columns == 0)
    531 			columns = 1;
    532 	}
    533 	lines = (nentry + columns - 1) / columns;
    534 	for (i = 0; i < lines; i++) {
    535 		for (j = 0; j < columns; j++) {
    536 			fp = fp0 + j * lines + i;
    537 			cp = fmtentry(fp);
    538 			(void) printf("%s", cp);
    539 			if (fp + lines >= fplast) {
    540 				(void) printf("\n");
    541 				break;
    542 			}
    543 			w = strlen(cp);
    544 			while (w < width)
    545 				if (usetabs) {
    546 					w = (w + 8) &~ 7;
    547 					(void) putchar('\t');
    548 				} else {
    549 					w++;
    550 					(void) putchar(' ');
    551 				}
    552 		}
    553 	}
    554 }
    555 
    556 static int
    557 fcmp(const void *arg1, const void *arg2)
    558 {
    559 	const struct afile *f1 = arg1;
    560 	const struct afile *f2 = arg2;
    561 
    562 	if (dflg == 0 && fflg == 0) {
    563 		if ((f1->fflags&ISARG) && f1->ftype == 'd') {
    564 			if ((f2->fflags&ISARG) == 0 || f2->ftype != 'd')
    565 				return (1);
    566 		} else {
    567 			if ((f2->fflags&ISARG) && f2->ftype == 'd')
    568 				return (-1);
    569 		}
    570 	}
    571 	if (tflg) {
    572 		if (f2->fmtime == f1->fmtime)
    573 			return (0);
    574 		if (f2->fmtime > f1->fmtime)
    575 			return (rflg);
    576 		return (-rflg);
    577 	}
    578 	return (rflg * strcmp(f1->fname, f2->fname));
    579 }
    580 
    581 static char *
    582 cat(char *dir, char *file)
    583 {
    584 	static char dfile[BUFSIZ];
    585 
    586 	if (strlen(dir)+1+strlen(file)+1 > BUFSIZ) {
    587 		(void) fprintf(stderr, "ls: filename too long\n");
    588 		exit(1);
    589 	}
    590 	if (strcmp(dir, "") == 0 || strcmp(dir, ".") == 0) {
    591 		(void) strcpy(dfile, file);
    592 		return (dfile);
    593 	}
    594 	(void) strcpy(dfile, dir);
    595 	if (dir[strlen(dir) - 1] != '/' && *file != '/')
    596 		(void) strcat(dfile, "/");
    597 	(void) strcat(dfile, file);
    598 	return (dfile);
    599 }
    600 
    601 static char *
    602 savestr(char *str)
    603 {
    604 	char *cp = malloc(strlen(str) + 1);
    605 
    606 	if (cp == NULL) {
    607 		(void) fprintf(stderr, "ls: out of memory\n");
    608 		exit(1);
    609 	}
    610 	(void) strcpy(cp, str);
    611 	return (cp);
    612 }
    613 
    614 static	char	*fmtinum(struct afile *);
    615 static	char	*fmtsize(struct afile *);
    616 static	char	*fmtlstuff(struct afile *);
    617 static	char	*fmtmode(char *, int);
    618 
    619 static char *
    620 fmtentry(struct afile *fp)
    621 {
    622 	static char fmtres[BUFSIZ];
    623 	register char *cp, *dp;
    624 
    625 	(void) sprintf(fmtres, "%s%s%s",
    626 	    iflg ? fmtinum(fp) : "",
    627 	    sflg ? fmtsize(fp) : "",
    628 	    lflg ? fmtlstuff(fp) : "");
    629 	dp = &fmtres[strlen(fmtres)];
    630 	for (cp = fp->fname; *cp; cp++)
    631 		if (qflg && !isprint((unsigned char)*cp))
    632 			*dp++ = '?';
    633 		else
    634 			*dp++ = *cp;
    635 	/* avoid both "->" and trailing marks */
    636 	if (Fflg && ! (lflg && fp->flinkto)) {
    637 		if (fp->ftype == 'd')
    638 			*dp++ = '/';
    639 		else if (fp->ftype == 'D')
    640 			*dp++ = '>';
    641 		else if (fp->ftype == 'p')
    642 			*dp++ = '|';
    643 		else if (fp->ftype == 'l')
    644 			*dp++ = '@';
    645 		else if (fp->ftype == 's')
    646 			*dp++ = '=';
    647 		else if (fp->fflags & 0111)
    648 			*dp++ = '*';
    649 	}
    650 	if (lflg && fp->flinkto) {
    651 		(void) strcpy(dp, " -> "); dp += 4;
    652 		for (cp = fp->flinkto; *cp; cp++)
    653 			if (qflg && !isprint((unsigned char) *cp))
    654 				*dp++ = '?';
    655 			else
    656 				*dp++ = *cp;
    657 	}
    658 	*dp++ = 0;
    659 	return (fmtres);
    660 }
    661 
    662 static char *
    663 fmtinum(struct afile *p)
    664 {
    665 	static char inumbuf[12];
    666 
    667 	(void) sprintf(inumbuf, "%10llu ", p->fnum);
    668 	return (inumbuf);
    669 }
    670 
    671 static char *
    672 fmtsize(struct afile *p)
    673 {
    674 	static char sizebuf[32];
    675 
    676 	(void) sprintf(sizebuf, (off_t)dbtokb(p->fblks) < 10000 ? "%4lld " : \
    677 		"%lld ", (off_t)dbtokb(p->fblks));
    678 	return (sizebuf);
    679 }
    680 
    681 static char *
    682 fmtlstuff(struct afile *p)
    683 {
    684 	static char lstuffbuf[256];
    685 	char gname[32], uname[32], fsize[32], ftime[32];
    686 	register char *lp = lstuffbuf;
    687 
    688 	/* type mode uname gname fsize ftime */
    689 /* get uname */
    690 	{
    691 		char *cp = getname(p->fuid);
    692 		(void) sprintf(uname, "%-8s ", cp);
    693 	}
    694 /* get gname */
    695 	if (gflg) {
    696 		char *cp = getgroup(p->fgid);
    697 		(void) sprintf(gname, "%-8s ", cp);
    698 	}
    699 /* get fsize */
    700 	if (p->ftype == 'b' || p->ftype == 'c')
    701 		(void) sprintf(fsize, "%3ld,%4ld",
    702 		    major(p->fsize), minor(p->fsize));
    703 	else if (p->ftype == 's')
    704 		(void) sprintf(fsize, "%8d", 0);
    705 	else
    706 		(void) sprintf(fsize, p->fsize < 100000000 ? "%8lld" : \
    707 				"%lld", p->fsize);
    708 /* get ftime */
    709 	{
    710 		char *cp = ctime(&p->fmtime);
    711 		if ((p->fmtime < sixmonthsago) || (p->fmtime > onehourfromnow))
    712 			(void) sprintf(ftime, " %-7.7s %-4.4s ", cp+4, cp+20);
    713 		else
    714 			(void) sprintf(ftime, " %-12.12s ", cp+4);
    715 	}
    716 /* splat */
    717 	*lp++ = p->ftype;
    718 	lp = fmtmode(lp, p->fflags);
    719 	(void) sprintf(lp, "%c%3ld %s%s%s%s",
    720 	    p->acl, p->fnl, uname, gflg ? gname : "", fsize, ftime);
    721 	return (lstuffbuf);
    722 }
    723 
    724 static	int	m1[] =
    725 	{ 1, S_IREAD>>0, 'r', '-' };
    726 static	int	m2[] =
    727 	{ 1, S_IWRITE>>0, 'w', '-' };
    728 static	int	m3[] =
    729 	{ 3, S_ISUID|(S_IEXEC>>0), 's', S_IEXEC>>0, 'x', S_ISUID, 'S', '-' };
    730 static	int	m4[] =
    731 	{ 1, S_IREAD>>3, 'r', '-' };
    732 static	int	m5[] =
    733 	{ 1, S_IWRITE>>3, 'w', '-' };
    734 static	int	m6[] =
    735 	{ 3, S_ISGID|(S_IEXEC>>3), 's', S_IEXEC>>3, 'x', S_ISGID, 'S', '-' };
    736 static	int	m7[] =
    737 	{ 1, S_IREAD>>6, 'r', '-' };
    738 static	int	m8[] =
    739 	{ 1, S_IWRITE>>6, 'w', '-' };
    740 static	int	m9[] =
    741 	{ 3, S_ISVTX|(S_IEXEC>>6), 't', S_ISVTX, 'T', S_IEXEC>>6, 'x', '-' };
    742 
    743 static	int	*m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
    744 
    745 static char *
    746 fmtmode(char *lp, int flags)
    747 {
    748 	int **mp;
    749 
    750 	for (mp = &m[0]; mp < &m[sizeof (m)/sizeof (m[0])]; ) {
    751 		register int *pairp = *mp++;
    752 		register int n = *pairp++;
    753 
    754 		while (n-- > 0) {
    755 			if ((flags&*pairp) == *pairp) {
    756 				pairp++;
    757 				break;
    758 			} else
    759 				pairp += 2;
    760 		}
    761 		*lp++ = *pairp;
    762 	}
    763 	return (lp);
    764 }
    765 
    766 /* rest should be done with nameserver or database */
    767 
    768 #include <pwd.h>
    769 #include <grp.h>
    770 #include <utmpx.h>
    771 
    772 #define	NMAX	(sizeof (((struct utmpx *)0)->ut_name))
    773 #define	SCPYN(a, b)	strncpy(a, b, NMAX)
    774 
    775 
    776 static struct cachenode {	/* this struct must be zeroed before using */
    777 	struct cachenode *lesschild;	/* subtree whose entries < val */
    778 	struct cachenode *grtrchild;	/* subtree whose entries > val */
    779 	int val;			/* the uid or gid of this entry */
    780 	int initted;			/* name has been filled in */
    781 	char name[NMAX+1];		/* the string that val maps to */
    782 } *names, *groups;
    783 
    784 static struct cachenode *
    785 findincache(struct cachenode **head, id_t val)
    786 {
    787 	register struct cachenode **parent = head;
    788 	register struct cachenode *c = *parent;
    789 
    790 	while (c != NULL) {
    791 		if (val == c->val) {
    792 			/* found it */
    793 			return (c);
    794 		} else if (val < c->val) {
    795 			parent = &c->lesschild;
    796 			c = c->lesschild;
    797 		} else {
    798 			parent = &c->grtrchild;
    799 			c = c->grtrchild;
    800 		}
    801 	}
    802 
    803 	/* not in the cache, make a new entry for it */
    804 	*parent = c = (struct cachenode *)calloc(1, sizeof (struct cachenode));
    805 	c->val = val;
    806 	return (c);
    807 }
    808 
    809 static char *
    810 getname(uid_t uid)
    811 {
    812 	struct cachenode *c;
    813 	struct passwd *pw;
    814 
    815 	c = findincache(&names, uid);
    816 	if (c->initted == 0) {
    817 		if ((pw = getpwuid(uid)) != NULL) {
    818 			(void) SCPYN(&c->name[0], pw->pw_name);
    819 		} else {
    820 			(void) sprintf(&c->name[0], "%-8lu ", uid);
    821 		}
    822 		c->initted = 1;
    823 	}
    824 	return (&c->name[0]);
    825 }
    826 
    827 static char *
    828 getgroup(gid_t gid)
    829 {
    830 	struct cachenode *c;
    831 	struct group *gr;
    832 
    833 	c = findincache(&groups, gid);
    834 	if (c->initted == 0) {
    835 		if ((gr = getgrgid(gid)) != NULL) {
    836 			(void) SCPYN(&c->name[0], gr->gr_name);
    837 		} else {
    838 			(void) sprintf(&c->name[0], "%-8lu ", gid);
    839 		}
    840 		c->initted = 1;
    841 	}
    842 	return (&c->name[0]);
    843 }
    844