xref: /illumos-gate/usr/src/cmd/ls/ls.c (revision 4e5b757fbcf21077677360be274461dcd9064106)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
30 /*	  All Rights Reserved	*/
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 
34 /*
35  * List files or directories
36  */
37 
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/mkdev.h>
41 #include <sys/stat.h>
42 #include <sys/acl.h>
43 
44 #include <wchar.h>
45 #include <stdio.h>
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <string.h>
49 #include <locale.h>
50 #include <curses.h>
51 #include <termios.h>
52 #include <stdlib.h>
53 #include <widec.h>
54 #include <locale.h>
55 #include <wctype.h>
56 #include <pwd.h>
57 #include <grp.h>
58 #include <limits.h>
59 #include <fcntl.h>
60 #include <unistd.h>
61 #include <libgen.h>
62 #include <errno.h>
63 #include <aclutils.h>
64 
65 #ifndef STANDALONE
66 #define	TERMINFO
67 #endif
68 
69 /*
70  * -DNOTERMINFO can be defined on the cc command line to prevent
71  * the use of terminfo.  This should be done on systems not having
72  * the terminfo feature(pre 6.0 sytems ?).
73  * As a result, columnar listings assume 80 columns for output,
74  * unless told otherwise via the COLUMNS environment variable.
75  */
76 #ifdef NOTERMINFO
77 #undef TERMINFO
78 #endif
79 
80 #include <term.h>
81 
82 #define	BFSIZE	16
83 /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */
84 #define	ISARG	0100000
85 
86 /*
87  * this flag has been added to manipulate the display of S instead of 'l' when
88  * the file is not a regular file and when group execution bit is off
89  */
90 #define	LS_NOTREG	010000
91 
92 
93 /*
94  * Date and time formats
95  *
96  * b --- abbreviated month name
97  * e --- day number
98  * Y --- year in the form ccyy
99  * H --- hour(24-hour version)
100  * M --- minute
101  * F --- yyyy-mm-dd
102  * T --- hh:mm:ss
103  * z --- time zone as hours displacement from UTC
104  * note that %F and %z are from the ISO C99 standard and are
105  * not present in older C libraries
106  */
107 #define	FORMAT1	 " %b %e  %Y "
108 #define	FORMAT2  " %b %e %H:%M "
109 #define	FORMAT3  " %b %e %T %Y "
110 #define	FORMAT4  " %%F %%T.%.09ld %%z "
111 
112 #undef BUFSIZ
113 #define	BUFSIZ 4096
114 #define	NUMBER_WIDTH 40
115 #define	FMTSIZE 50
116 
117 struct ditem {
118 	dev_t	dev;			/* directory items device number */
119 	ino_t	ino;			/* directory items inode number */
120 	struct ditem *parent;		/* dir items ptr to its parent's info */
121 };
122 
123 struct	lbuf	{
124 	union	{
125 		char	lname[MAXNAMLEN]; /* used for filename in a directory */
126 		char	*namep;		/* for name in ls-command; */
127 	} ln;
128 	char	ltype;		/* filetype */
129 	ino_t	lnum;		/* inode number of file */
130 	mode_t	lflags; 	/* 0777 bits used as r,w,x permissions */
131 	nlink_t	lnl;		/* number of links to file */
132 	uid_t	luid;
133 	gid_t	lgid;
134 	off_t	lsize;		/* filesize or major/minor dev numbers */
135 	blkcnt_t	lblocks;	/* number of file blocks */
136 	timestruc_t	lmtime;
137 	char	*flinkto;	/* symbolic link contents */
138 	char 	acl;		/* indicate there are additional acl entries */
139 	int	cycle;		/* cycle detected flag */
140 	struct ditem *ancinfo;	/* maintains ancestor info */
141 	acl_t *aclp;		/* ACL if present */
142 };
143 
144 struct dchain {
145 	char *dc_name;		/* path name */
146 	int cycle_detected;	/* cycle detected visiting this directory */
147 	struct ditem *myancinfo;	/* this directory's ancestry info */
148 	struct dchain *dc_next;	/* next directory in the chain */
149 };
150 
151 /*
152  * A numbuf_t is used when converting a number to a string representation
153  */
154 typedef char numbuf_t[NUMBER_WIDTH];
155 
156 static struct dchain *dfirst;	/* start of the dir chain */
157 static struct dchain *cdfirst;	/* start of the current dir chain */
158 static struct dchain *dtemp;	/* temporary - used for linking */
159 static char *curdir;		/* the current directory */
160 
161 static int	first = 1;	/* true if first line is not yet printed */
162 static int	nfiles = 0;	/* number of flist entries in current use */
163 static int	nargs = 0;	/* number of flist entries used for arguments */
164 static int	maxfils = 0;	/* number of flist/lbuf entries allocated */
165 static int	maxn = 0;	/* number of flist entries with lbufs asigned */
166 static int	quantn = 64;	/* allocation growth quantum */
167 
168 static struct lbuf	*nxtlbf;	/* ptr to next lbuf to be assigned */
169 static struct lbuf	**flist;	/* ptr to list of lbuf pointers */
170 static struct lbuf	*gstat(char *, int, struct ditem *);
171 static char		*getname(uid_t);
172 static char		*getgroup(gid_t);
173 static char		*makename(char *, char *);
174 static void		pentry(struct lbuf *);
175 static void		column(void);
176 static void		pmode(mode_t aflag);
177 static void		selection(int *);
178 static void		new_line(void);
179 static void		rddir(char *, struct ditem *);
180 static int		strcol(unsigned char *);
181 static void		pem(struct lbuf **, struct lbuf **, int);
182 static void		pdirectory(char *, int, int, int, struct ditem *);
183 static struct cachenode *findincache(struct cachenode **, long);
184 static void		csi_pprintf(unsigned char *);
185 static void		pprintf(char *, char *);
186 static int		compar(struct lbuf **pp1, struct lbuf **pp2);
187 static char 		*number_to_scaled_string(numbuf_t buf,
188 			    unsigned long long number,
189 			    long scale);
190 static void		record_ancestry(char *, struct stat *, struct lbuf *,
191 			    int, struct ditem *);
192 
193 static int		aflg;
194 static int		atflg;
195 static int		bflg;
196 static int		cflg;
197 static int		dflg;
198 static int		eflg;
199 static int		fflg;
200 static int		gflg;
201 static int		hflg;
202 static int		iflg;
203 static int		lflg;
204 static int		mflg;
205 static int		nflg;
206 static int		oflg;
207 static int		pflg;
208 static int		qflg;
209 static int		rflg = 1; /* init to 1 for special use in compar */
210 static int		sflg;
211 static int		tflg;
212 static int		uflg;
213 static int		xflg;
214 static int		Aflg;
215 static int		Cflg;
216 static int		Eflg;
217 static int		Fflg;
218 static int		Hflg;
219 static int		Lflg;
220 static int		Rflg;
221 static int		Sflg;
222 static int		vflg;
223 static int		Vflg;
224 static long		hscale;
225 static mode_t		flags;
226 static int		err = 0;	/* Contains return code */
227 
228 static uid_t		lastuid	= (uid_t)-1;
229 static gid_t		lastgid = (gid_t)-1;
230 static char		*lastuname = NULL;
231 static char		*lastgname = NULL;
232 
233 /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg are on */
234 static int		statreq;
235 
236 static char		*dotp = ".";
237 
238 static u_longlong_t 	tblocks; /* number of blocks of files in a directory */
239 static time_t		year, now;
240 
241 static int		num_cols = 80;
242 static int		colwidth;
243 static int		filewidth;
244 static int		fixedwidth;
245 static int		nomocore;
246 static int		curcol;
247 
248 static struct	winsize	win;
249 
250 static char	time_buf[50];	/* array to hold day and time */
251 
252 #define	NOTWORKINGDIR(d, l)	(((l) < 2) || \
253 				    (strcmp((d) + (l) - 2, "/.") != 0))
254 
255 #define	NOTPARENTDIR(d, l)	(((l) < 3) || \
256 				    (strcmp((d) + (l) - 3, "/..") != 0))
257 
258 int
259 main(int argc, char *argv[])
260 {
261 	int		c;
262 	int		i;
263 	int		width;
264 	int		amino = 0;
265 	int		opterr = 0;
266 	struct lbuf	*ep;
267 	struct lbuf	lb;
268 	struct ditem	*myinfo;
269 
270 	(void) setlocale(LC_ALL, "");
271 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
272 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
273 #endif
274 	(void) textdomain(TEXT_DOMAIN);
275 #ifdef STANDALONE
276 	if (argv[0][0] == '\0')
277 		argc = getargv("ls", &argv, 0);
278 #endif
279 
280 	lb.lmtime.tv_sec = time(NULL);
281 	lb.lmtime.tv_nsec = 0;
282 	year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */
283 	now = lb.lmtime.tv_sec + 60;
284 	if (isatty(1)) {
285 		Cflg = 1;
286 		mflg = 0;
287 	}
288 
289 	while ((c = getopt(argc, argv,
290 	    "aAbcCdeEfFghHilLmnopqrRsStux1@vV")) != EOF)
291 		switch (c) {
292 		case 'a':
293 			aflg++;
294 			continue;
295 		case 'A':
296 			Aflg++;
297 			continue;
298 		case 'b':
299 			bflg = 1;
300 			qflg = 0;
301 			continue;
302 		case 'c':
303 			uflg = 0;
304 			cflg++;
305 			continue;
306 		case 'C':
307 			Cflg = 1;
308 			mflg = 0;
309 #ifdef XPG4
310 			lflg = 0;
311 #endif
312 			continue;
313 		case 'd':
314 			dflg++;
315 			continue;
316 		case 'e':
317 			eflg++;
318 			lflg++;
319 			statreq++;
320 			Eflg = 0;
321 			continue;
322 		case 'E':
323 			Eflg++;
324 			lflg++;
325 			statreq++;
326 			eflg = 0;
327 			continue;
328 		case 'f':
329 			fflg++;
330 			continue;
331 		case 'F':
332 			Fflg++;
333 			statreq++;
334 			continue;
335 		case 'g':
336 			gflg++;
337 			lflg++;
338 			statreq++;
339 			continue;
340 		case 'h':
341 			hflg++;
342 			hscale = 1024;
343 			continue;
344 		case 'H':
345 			Hflg++;
346 			/* -H and -L are mutually exclusive */
347 			Lflg = 0;
348 			continue;
349 		case 'i':
350 			iflg++;
351 			continue;
352 		case 'l':
353 			lflg++;
354 			statreq++;
355 			Cflg = 0;
356 			xflg = 0;
357 			mflg = 0;
358 			atflg = 0;
359 			continue;
360 		case 'L':
361 			Lflg++;
362 			/* -H and -L are mutually exclusive */
363 			Hflg = 0;
364 			continue;
365 		case 'm':
366 			Cflg = 0;
367 			mflg = 1;
368 #ifdef XPG4
369 			lflg = 0;
370 #endif
371 			continue;
372 		case 'n':
373 			nflg++;
374 			lflg++;
375 			statreq++;
376 			Cflg = 0;
377 			xflg = 0;
378 			mflg = 0;
379 			atflg = 0;
380 			continue;
381 		case 'o':
382 			oflg++;
383 			lflg++;
384 			statreq++;
385 			continue;
386 		case 'p':
387 			pflg++;
388 			statreq++;
389 			continue;
390 		case 'q':
391 			qflg = 1;
392 			bflg = 0;
393 			continue;
394 		case 'r':
395 			rflg = -1;
396 			continue;
397 		case 'R':
398 			Rflg++;
399 			statreq++;
400 			continue;
401 		case 's':
402 			sflg++;
403 			statreq++;
404 			continue;
405 		case 'S':
406 			tflg = 0;
407 			Sflg++;
408 			statreq++;
409 			continue;
410 		case 't':
411 			Sflg = 0;
412 			tflg++;
413 			statreq++;
414 			continue;
415 		case 'u':
416 			cflg = 0;
417 			uflg++;
418 			continue;
419 		case 'V':
420 			Vflg++;
421 			/*FALLTHROUGH*/
422 		case 'v':
423 			vflg++;
424 #if !defined(XPG4)
425 			if (lflg)
426 				continue;
427 #endif
428 			lflg++;
429 			statreq++;
430 			Cflg = 0;
431 			xflg = 0;
432 			mflg = 0;
433 			continue;
434 		case 'x':
435 			xflg = 1;
436 			Cflg = 1;
437 			mflg = 0;
438 #ifdef XPG4
439 			lflg = 0;
440 #endif
441 			continue;
442 		case '1':
443 			Cflg = 0;
444 			continue;
445 		case '@':
446 #if !defined(XPG4)
447 			/*
448 			 * -l has precedence over -@
449 			 */
450 			if (lflg)
451 				continue;
452 #endif
453 			atflg++;
454 			lflg++;
455 			statreq++;
456 			Cflg = 0;
457 			xflg = 0;
458 			mflg = 0;
459 			continue;
460 		case '?':
461 			opterr++;
462 			continue;
463 		}
464 	if (opterr) {
465 		(void) fprintf(stderr, gettext(
466 		    "usage: ls -aAbcCdeEfFghHilLmnopqrRsStuxvV1@ [files]\n"));
467 		exit(2);
468 	}
469 
470 	if (fflg) {
471 		aflg++;
472 		lflg = 0;
473 		sflg = 0;
474 		tflg = 0;
475 		Sflg = 0;
476 		statreq = 0;
477 	}
478 
479 	fixedwidth = 2;
480 	if (pflg || Fflg)
481 		fixedwidth++;
482 	if (iflg)
483 		fixedwidth += 11;
484 	if (sflg)
485 		fixedwidth += 5;
486 
487 	if (lflg) {
488 		if (!gflg && !oflg)
489 			gflg = oflg = 1;
490 		else
491 		if (gflg && oflg)
492 			gflg = oflg = 0;
493 		Cflg = mflg = 0;
494 	}
495 
496 	if (Cflg || mflg) {
497 		char *clptr;
498 		if ((clptr = getenv("COLUMNS")) != NULL)
499 			num_cols = atoi(clptr);
500 #ifdef TERMINFO
501 		else {
502 			if (ioctl(1, TIOCGWINSZ, &win) != -1)
503 				num_cols = (win.ws_col == 0 ? 80 : win.ws_col);
504 		}
505 #endif
506 		if (num_cols < 20 || num_cols > 1000)
507 			/* assume it is an error */
508 			num_cols = 80;
509 	}
510 
511 	/* allocate space for flist and the associated	*/
512 	/* data structures (lbufs)			*/
513 	maxfils = quantn;
514 	if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) ||
515 	    ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) {
516 		perror("ls");
517 		exit(2);
518 	}
519 	if ((amino = (argc-optind)) == 0) {
520 					/*
521 					 * case when no names are given
522 					 * in ls-command and current
523 					 * directory is to be used
524 					 */
525 		argv[optind] = dotp;
526 	}
527 
528 	for (i = 0; i < (amino ? amino : 1); i++) {
529 
530 		/*
531 		 * If we are recursing, we need to make sure we don't
532 		 * get into an endless loop.  To keep track of the inodes
533 		 * (actually, just the directories) visited, we
534 		 * maintain a directory ancestry list for a file
535 		 * hierarchy.  As we go deeper into the hierarchy,
536 		 * a parent directory passes its directory list
537 		 * info (device id, inode number, and a pointer to
538 		 * its parent) to each of its children.  As we
539 		 * process a child that is a directory, we save
540 		 * its own personal directory list info.  We then
541 		 * check to see if the child has already been
542 		 * processed by comparing its device id and inode
543 		 * number from its own personal directory list info
544 		 * to that of each of its ancestors.  If there is a
545 		 * match, then we know we've detected a cycle.
546 		 */
547 		if (Rflg) {
548 			/*
549 			 * This is the first parent in this lineage
550 			 * (first in a directory hierarchy), so
551 			 * this parent's parent doesn't exist.  We
552 			 * only initialize myinfo when we are
553 			 * recursing, otherwise it's not used.
554 			 */
555 			if ((myinfo = (struct ditem *)malloc(
556 			    sizeof (struct ditem))) == NULL) {
557 				perror("ls");
558 				exit(2);
559 			} else {
560 				myinfo->dev = 0;
561 				myinfo->ino = 0;
562 				myinfo->parent = NULL;
563 			}
564 		}
565 
566 		if (Cflg || mflg) {
567 			width = strcol((unsigned char *)argv[optind]);
568 			if (width > filewidth)
569 				filewidth = width;
570 		}
571 		if ((ep = gstat((*argv[optind] ? argv[optind] : dotp),
572 		    1, myinfo)) == NULL) {
573 			if (nomocore)
574 				exit(2);
575 			err = 2;
576 			optind++;
577 			continue;
578 		}
579 		ep->ln.namep = (*argv[optind] ? argv[optind] : dotp);
580 		ep->lflags |= ISARG;
581 		optind++;
582 		nargs++;	/* count good arguments stored in flist */
583 	}
584 	colwidth = fixedwidth + filewidth;
585 	qsort(flist, (unsigned)nargs, sizeof (struct lbuf *),
586 	    (int (*)(const void *, const void *))compar);
587 	for (i = 0; i < nargs; i++) {
588 		if (flist[i]->ltype == 'd' && dflg == 0 || fflg)
589 			break;
590 	}
591 	pem(&flist[0], &flist[i], 0);
592 	for (; i < nargs; i++) {
593 		pdirectory(flist[i]->ln.namep, Rflg ||
594 		    (amino > 1), nargs, 0, flist[i]->ancinfo);
595 		if (nomocore)
596 			exit(2);
597 		/* -R: print subdirectories found */
598 		while (dfirst || cdfirst) {
599 			/* Place direct subdirs on front in right order */
600 			while (cdfirst) {
601 				/* reverse cdfirst onto front of dfirst */
602 				dtemp = cdfirst;
603 				cdfirst = cdfirst -> dc_next;
604 				dtemp -> dc_next = dfirst;
605 				dfirst = dtemp;
606 			}
607 			/* take off first dir on dfirst & print it */
608 			dtemp = dfirst;
609 			dfirst = dfirst->dc_next;
610 			pdirectory(dtemp->dc_name, 1, nargs,
611 			    dtemp->cycle_detected, dtemp->myancinfo);
612 			if (nomocore)
613 				exit(2);
614 			free(dtemp->dc_name);
615 			free(dtemp);
616 		}
617 	}
618 	return (err);
619 }
620 
621 /*
622  * pdirectory: print the directory name, labelling it if title is
623  * nonzero, using lp as the place to start reading in the dir.
624  */
625 static void
626 pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo)
627 {
628 	struct dchain *dp;
629 	struct lbuf *ap;
630 	char *pname;
631 	int j;
632 
633 	filewidth = 0;
634 	curdir = name;
635 	if (title) {
636 		if (!first)
637 			(void) putc('\n', stdout);
638 		pprintf(name, ":");
639 		new_line();
640 	}
641 	/*
642 	 * If there was a cycle detected, then notify and don't report
643 	 * further.
644 	 */
645 	if (cdetect) {
646 		if (lflg || sflg) {
647 			curcol += printf(gettext("total %d"), 0);
648 			new_line();
649 		}
650 		(void) fprintf(stderr, gettext(
651 		    "ls: cycle detected for %s\n"), name);
652 		return;
653 	}
654 
655 	nfiles = lp;
656 	rddir(name, myinfo);
657 	if (nomocore)
658 		return;
659 	if (fflg == 0)
660 		qsort(&flist[lp], (unsigned)(nfiles - lp),
661 		    sizeof (struct lbuf *),
662 		    (int (*)(const void *, const void *))compar);
663 	if (Rflg) {
664 		for (j = nfiles - 1; j >= lp; j--) {
665 			ap = flist[j];
666 			if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") &&
667 			    strcmp(ap->ln.lname, "..")) {
668 				dp = malloc(sizeof (struct dchain));
669 				if (dp == NULL) {
670 					perror("ls");
671 					exit(2);
672 				}
673 				pname = makename(curdir, ap->ln.lname);
674 				if ((dp->dc_name = strdup(pname)) == NULL) {
675 					perror("ls");
676 					exit(2);
677 				}
678 				dp->cycle_detected = ap->cycle;
679 				dp->myancinfo = ap->ancinfo;
680 				dp->dc_next = dfirst;
681 				dfirst = dp;
682 			}
683 		}
684 	}
685 	if (lflg || sflg) {
686 		curcol += printf(gettext("total %llu"), tblocks);
687 		new_line();
688 	}
689 	pem(&flist[lp], &flist[nfiles], lflg||sflg);
690 }
691 
692 /*
693  * pem: print 'em. Print a list of files (e.g. a directory) bounded
694  * by slp and lp.
695  */
696 static void
697 pem(struct lbuf **slp, struct lbuf **lp, int tot_flag)
698 {
699 	long row, nrows, i;
700 	int col, ncols;
701 	struct lbuf **ep;
702 
703 	if (Cflg || mflg) {
704 		if (colwidth > num_cols) {
705 			ncols = 1;
706 		} else {
707 			ncols = num_cols / colwidth;
708 		}
709 	}
710 
711 	if (ncols == 1 || mflg || xflg || !Cflg) {
712 		for (ep = slp; ep < lp; ep++)
713 			pentry(*ep);
714 		new_line();
715 		return;
716 	}
717 	/* otherwise print -C columns */
718 	if (tot_flag) {
719 		slp--;
720 		row = 1;
721 	}
722 	else
723 		row = 0;
724 
725 	nrows = (lp - slp - 1) / ncols + 1;
726 	for (i = 0; i < nrows; i++, row++) {
727 		for (col = 0; col < ncols; col++) {
728 			ep = slp + (nrows * col) + row;
729 			if (ep < lp)
730 				pentry(*ep);
731 		}
732 		new_line();
733 	}
734 }
735 
736 /*
737  * print one output entry;
738  * if uid/gid is not found in the appropriate
739  * file(passwd/group), then print uid/gid instead of
740  * user/group name;
741  */
742 static void
743 pentry(struct lbuf *ap)
744 {
745 	struct lbuf *p;
746 	numbuf_t hbuf;
747 	char buf[BUFSIZ];
748 	char fmt_buf[FMTSIZE];
749 	char *dmark = "";	/* Used if -p or -F option active */
750 	char *cp;
751 
752 	p = ap;
753 	column();
754 	if (iflg)
755 		if (mflg && !lflg)
756 			curcol += printf("%llu ", (long long)p->lnum);
757 		else
758 			curcol += printf("%10llu ", (long long)p->lnum);
759 	if (sflg)
760 		curcol += printf((mflg && !lflg) ? "%lld " :
761 			(p->lblocks < 10000) ? "%4lld " : "%lld ",
762 			(p->ltype != 'b' && p->ltype != 'c') ?
763 				p->lblocks : 0LL);
764 	if (lflg) {
765 		(void) putchar(p->ltype);
766 		curcol++;
767 		pmode(p->lflags);
768 
769 		/* ACL: additional access mode flag */
770 		(void) putchar(p->acl);
771 		curcol++;
772 
773 		curcol += printf("%3lu ", (ulong_t)p->lnl);
774 		if (oflg)
775 			if (!nflg) {
776 				cp = getname(p->luid);
777 				curcol += printf("%-8s ", cp);
778 			} else
779 				curcol += printf("%-8lu ", (ulong_t)p->luid);
780 		if (gflg)
781 			if (!nflg) {
782 				cp = getgroup(p->lgid);
783 				curcol += printf("%-8s ", cp);
784 			} else
785 				curcol += printf("%-8lu ", (ulong_t)p->lgid);
786 		if (p->ltype == 'b' || p->ltype == 'c') {
787 			curcol += printf("%3u, %2u",
788 			    (uint_t)major((dev_t)p->lsize),
789 			    (uint_t)minor((dev_t)p->lsize));
790 		} else if (hflg && (p->lsize >= hscale)) {
791 			curcol += printf("%7s",
792 			    number_to_scaled_string(hbuf, p->lsize, hscale));
793 		} else {
794 			curcol += printf((p->lsize < (off_t)10000000) ?
795 			    "%7lld" : "%lld", p->lsize);
796 		}
797 		if (eflg) {
798 			(void) strftime(time_buf, sizeof (time_buf),
799 			    dcgettext(NULL, FORMAT3, LC_TIME),
800 			    localtime(&p->lmtime.tv_sec));
801 		} else if (Eflg) {
802 			/* fill in nanoseconds first */
803 			(void) snprintf(fmt_buf, sizeof (fmt_buf),
804 			    FORMAT4, p->lmtime.tv_nsec);
805 			(void) strftime(time_buf, sizeof (time_buf),
806 			    fmt_buf, localtime(&p->lmtime.tv_sec));
807 		} else {
808 			if ((p->lmtime.tv_sec < year) ||
809 			    (p->lmtime.tv_sec > now)) {
810 				(void) strftime(time_buf, sizeof (time_buf),
811 				    dcgettext(NULL, FORMAT1, LC_TIME),
812 				    localtime(&p->lmtime.tv_sec));
813 			} else {
814 				(void) strftime(time_buf, sizeof (time_buf),
815 				    dcgettext(NULL, FORMAT2, LC_TIME),
816 				    localtime(&p->lmtime.tv_sec));
817 			}
818 		}
819 		curcol += printf("%s", time_buf);
820 	}
821 
822 	/*
823 	 * prevent both "->" and trailing marks
824 	 * from appearing
825 	 */
826 
827 	if (pflg && p->ltype == 'd')
828 		dmark = "/";
829 
830 	if (Fflg && !(lflg && p->flinkto)) {
831 		if (p->ltype == 'd')
832 			dmark = "/";
833 		else if (p->ltype == 'D')
834 			dmark = ">";
835 		else if (p->ltype == 'p')
836 			dmark = "|";
837 		else if (p->ltype == 'l')
838 			dmark = "@";
839 		else if (p->ltype == 's')
840 			dmark = "=";
841 		else if (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH))
842 			dmark = "*";
843 		else
844 			dmark = "";
845 	}
846 
847 	if (lflg && p->flinkto) {
848 		(void) strncpy(buf, " -> ", 4);
849 		(void) strcpy(buf + 4, p->flinkto);
850 		dmark = buf;
851 	}
852 
853 	if (p->lflags & ISARG) {
854 		if (qflg || bflg)
855 			pprintf(p->ln.namep, dmark);
856 		else {
857 			(void) printf("%s%s", p->ln.namep, dmark);
858 			curcol += strcol((unsigned char *)p->ln.namep);
859 			curcol += strcol((unsigned char *)dmark);
860 		}
861 	} else {
862 		if (qflg || bflg)
863 			pprintf(p->ln.lname, dmark);
864 		else {
865 			(void) printf("%s%s", p->ln.lname, dmark);
866 			curcol += strcol((unsigned char *)p->ln.lname);
867 			curcol += strcol((unsigned char *)dmark);
868 		}
869 	}
870 
871 	if (vflg) {
872 		new_line();
873 		if (p->aclp) {
874 			acl_printacl(p->aclp, num_cols, Vflg);
875 		}
876 	}
877 }
878 
879 /* print various r,w,x permissions */
880 static void
881 pmode(mode_t aflag)
882 {
883 	/* these arrays are declared static to allow initializations */
884 	static int	m0[] = { 1, S_IRUSR, 'r', '-' };
885 	static int	m1[] = { 1, S_IWUSR, 'w', '-' };
886 	static int	m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR,
887 	    'x', S_ISUID, 'S', '-' };
888 	static int	m3[] = { 1, S_IRGRP, 'r', '-' };
889 	static int	m4[] = { 1, S_IWGRP, 'w', '-' };
890 	static int	m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP,
891 				'x', S_ISGID|LS_NOTREG, 'S',
892 #ifdef XPG4
893 		S_ISGID, 'L', '-'};
894 #else
895 		S_ISGID, 'l', '-'};
896 #endif
897 	static int	m6[] = { 1, S_IROTH, 'r', '-' };
898 	static int	m7[] = { 1, S_IWOTH, 'w', '-' };
899 	static int	m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH,
900 	    'x', S_ISVTX, 'T', '-'};
901 
902 	static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8};
903 
904 	int **mp;
905 
906 	flags = aflag;
907 	for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++)
908 		selection(*mp);
909 }
910 
911 static void
912 selection(int *pairp)
913 {
914 	int n;
915 
916 	n = *pairp++;
917 	while (n-->0) {
918 		if ((flags & *pairp) == *pairp) {
919 			pairp++;
920 			break;
921 		} else {
922 			pairp += 2;
923 		}
924 	}
925 	(void) putchar(*pairp);
926 	curcol++;
927 }
928 
929 /*
930  * column: get to the beginning of the next column.
931  */
932 static void
933 column(void)
934 {
935 	if (curcol == 0)
936 		return;
937 	if (mflg) {
938 		(void) putc(',', stdout);
939 		curcol++;
940 		if (curcol + colwidth + 2 > num_cols) {
941 			(void) putc('\n', stdout);
942 			curcol = 0;
943 			return;
944 		}
945 		(void) putc(' ', stdout);
946 		curcol++;
947 		return;
948 	}
949 	if (Cflg == 0) {
950 		(void) putc('\n', stdout);
951 		curcol = 0;
952 		return;
953 	}
954 	if ((curcol / colwidth + 2) * colwidth > num_cols) {
955 		(void) putc('\n', stdout);
956 		curcol = 0;
957 		return;
958 	}
959 	do {
960 		(void) putc(' ', stdout);
961 		curcol++;
962 	} while (curcol % colwidth);
963 }
964 
965 static void
966 new_line(void)
967 {
968 	if (curcol) {
969 		first = 0;
970 		(void) putc('\n', stdout);
971 		curcol = 0;
972 	}
973 }
974 
975 /*
976  * read each filename in directory dir and store its
977  * status in flist[nfiles]
978  * use makename() to form pathname dir/filename;
979  */
980 static void
981 rddir(char *dir, struct ditem *myinfo)
982 {
983 	struct dirent *dentry;
984 	DIR *dirf;
985 	int j;
986 	struct lbuf *ep;
987 	int width;
988 
989 	if ((dirf = opendir(dir)) == NULL) {
990 		(void) fflush(stdout);
991 		perror(dir);
992 		err = 2;
993 		return;
994 	} else {
995 		tblocks = 0;
996 		for (;;) {
997 			errno = 0;
998 			if ((dentry = readdir(dirf)) == NULL)
999 				break;
1000 			if (aflg == 0 && dentry->d_name[0] == '.' &&
1001 			    (Aflg == 0 ||
1002 			    dentry->d_name[1] == '\0' ||
1003 			    dentry->d_name[1] == '.' &&
1004 			    dentry->d_name[2] == '\0'))
1005 				/*
1006 				 * check for directory items '.', '..',
1007 				 *  and items without valid inode-number;
1008 				 */
1009 				continue;
1010 
1011 			if (Cflg || mflg) {
1012 				width = strcol((unsigned char *)dentry->d_name);
1013 				if (width > filewidth)
1014 					filewidth = width;
1015 			}
1016 			ep = gstat(makename(dir, dentry->d_name), 0, myinfo);
1017 			if (ep == NULL) {
1018 				if (nomocore)
1019 					return;
1020 				continue;
1021 			} else {
1022 				ep->lnum = dentry->d_ino;
1023 				for (j = 0; dentry->d_name[j] != '\0'; j++)
1024 					ep->ln.lname[j] = dentry->d_name[j];
1025 				ep->ln.lname[j] = '\0';
1026 			}
1027 		}
1028 		if (errno) {
1029 			int sav_errno = errno;
1030 
1031 			(void) fprintf(stderr,
1032 			    gettext("ls: error reading directory %s: %s\n"),
1033 			    dir, strerror(sav_errno));
1034 		}
1035 		(void) closedir(dirf);
1036 		colwidth = fixedwidth + filewidth;
1037 	}
1038 }
1039 
1040 /*
1041  * Attaching a link to an inode's ancestors.  Search
1042  * through the ancestors to check for cycles (an inode which
1043  * we have already tracked in this inodes ancestry).  If a cycle
1044  * is detected, set the exit code and record the fact so that
1045  * it is reported at the right time when printing the directory.
1046  * In addition, set the exit code.  Note:  If the -a flag was
1047  * specified, we don't want to check for cycles for directories
1048  * ending in '/.' or '/..' unless they were specified on the
1049  * command line.
1050  */
1051 static void
1052 record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep,
1053     int argfl, struct ditem *myparent)
1054 {
1055 	size_t		file_len;
1056 	struct ditem	*myinfo;
1057 	struct ditem	*tptr;
1058 
1059 	file_len = strlen(file);
1060 	if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) &&
1061 	    NOTPARENTDIR(file, file_len))) {
1062 		/*
1063 		 * Add this inode's ancestry
1064 		 * info and insert it into the
1065 		 * ancestry list by pointing
1066 		 * back to its parent.  We save
1067 		 * it (in rep) with the other info
1068 		 * we're gathering for this inode.
1069 		 */
1070 		if ((myinfo = malloc(
1071 		    sizeof (struct ditem))) == NULL) {
1072 			perror("ls");
1073 			exit(2);
1074 		}
1075 		myinfo->dev = pstatb->st_dev;
1076 		myinfo->ino = pstatb->st_ino;
1077 		myinfo->parent = myparent;
1078 		rep->ancinfo = myinfo;
1079 
1080 		/*
1081 		 * If this node has the same device id and
1082 		 * inode number of one of its ancestors,
1083 		 * then we've detected a cycle.
1084 		 */
1085 		if (myparent != NULL) {
1086 			for (tptr = myparent; tptr->parent != NULL;
1087 			    tptr = tptr->parent) {
1088 				if ((tptr->dev == pstatb->st_dev) &&
1089 				    (tptr->ino == pstatb->st_ino)) {
1090 					/*
1091 					 * Cycle detected for this
1092 					 * directory.  Record the fact
1093 					 * it is a cycle so we don't
1094 					 * try to process this
1095 					 * directory as we are
1096 					 * walking through the
1097 					 * list of directories.
1098 					 */
1099 					rep->cycle = 1;
1100 					err = 2;
1101 					break;
1102 				}
1103 			}
1104 		}
1105 	}
1106 }
1107 
1108 /*
1109  * get status of file and recomputes tblocks;
1110  * argfl = 1 if file is a name in ls-command and = 0
1111  * for filename in a directory whose name is an
1112  * argument in the command;
1113  * stores a pointer in flist[nfiles] and
1114  * returns that pointer;
1115  * returns NULL if failed;
1116  */
1117 static struct lbuf *
1118 gstat(char *file, int argfl, struct ditem *myparent)
1119 {
1120 	struct stat statb, statb1;
1121 	struct lbuf *rep;
1122 	char buf[BUFSIZ];
1123 	ssize_t cc;
1124 	int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat;
1125 	int aclcnt;
1126 	int error;
1127 	aclent_t *tp;
1128 	o_mode_t groupperm, mask;
1129 	int grouppermfound, maskfound;
1130 
1131 	if (nomocore)
1132 		return (NULL);
1133 
1134 	if (nfiles >= maxfils) {
1135 		/*
1136 		 * all flist/lbuf pair assigned files, time to get some
1137 		 * more space
1138 		 */
1139 		maxfils += quantn;
1140 		if (((flist = realloc(flist,
1141 		    maxfils * sizeof (struct lbuf *))) == NULL) ||
1142 		    ((nxtlbf = malloc(quantn *
1143 		    sizeof (struct lbuf))) == NULL)) {
1144 			perror("ls");
1145 			nomocore = 1;
1146 			return (NULL);
1147 		}
1148 	}
1149 
1150 	/*
1151 	 * nfiles is reset to nargs for each directory
1152 	 * that is given as an argument maxn is checked
1153 	 * to prevent the assignment of an lbuf to a flist entry
1154 	 * that already has one assigned.
1155 	 */
1156 	if (nfiles >= maxn) {
1157 		rep = nxtlbf++;
1158 		flist[nfiles++] = rep;
1159 		maxn = nfiles;
1160 	} else {
1161 		rep = flist[nfiles++];
1162 	}
1163 	rep->lflags = (mode_t)0;
1164 	rep->flinkto = NULL;
1165 	rep->cycle = 0;
1166 	if (argfl || statreq) {
1167 		int doacl;
1168 
1169 		if (lflg)
1170 			doacl = 1;
1171 		else
1172 			doacl = 0;
1173 		if ((*statf)(file, &statb) < 0) {
1174 			if (argfl || errno != ENOENT ||
1175 			    (Lflg && lstat(file, &statb) == 0)) {
1176 				/*
1177 				 * Avoid race between readdir and lstat.
1178 				 * Print error message in case of dangling link.
1179 				 */
1180 				perror(file);
1181 			}
1182 			nfiles--;
1183 			return (NULL);
1184 		}
1185 
1186 		/*
1187 		 * If -H was specified, and the file linked to was
1188 		 * not a directory, then we need to get the info
1189 		 * for the symlink itself.
1190 		 */
1191 		if ((Hflg) && (argfl) &&
1192 		    ((statb.st_mode & S_IFMT) != S_IFDIR)) {
1193 			if (lstat(file, &statb) < 0) {
1194 				perror(file);
1195 			}
1196 		}
1197 
1198 		rep->lnum = statb.st_ino;
1199 		rep->lsize = statb.st_size;
1200 		rep->lblocks = statb.st_blocks;
1201 		switch (statb.st_mode & S_IFMT) {
1202 		case S_IFDIR:
1203 			rep->ltype = 'd';
1204 			if (Rflg) {
1205 				record_ancestry(file, &statb, rep,
1206 				    argfl, myparent);
1207 			}
1208 			break;
1209 		case S_IFBLK:
1210 			rep->ltype = 'b';
1211 			rep->lsize = (off_t)statb.st_rdev;
1212 			break;
1213 		case S_IFCHR:
1214 			rep->ltype = 'c';
1215 			rep->lsize = (off_t)statb.st_rdev;
1216 			break;
1217 		case S_IFIFO:
1218 			rep->ltype = 'p';
1219 			break;
1220 		case S_IFSOCK:
1221 			rep->ltype = 's';
1222 			rep->lsize = 0;
1223 			break;
1224 		case S_IFLNK:
1225 			/* symbolic links may not have ACLs, so elide acl() */
1226 			if ((Lflg == 0) || (Hflg == 0) ||
1227 			    ((Hflg) && (!argfl))) {
1228 				doacl = 0;
1229 			}
1230 			rep->ltype = 'l';
1231 			if (lflg) {
1232 				cc = readlink(file, buf, BUFSIZ);
1233 				if (cc >= 0) {
1234 
1235 					/*
1236 					 * follow the symbolic link
1237 					 * to generate the appropriate
1238 					 * Fflg marker for the object
1239 					 * eg, /bin -> /sym/bin/
1240 					 */
1241 					if ((Fflg || pflg) &&
1242 					    (stat(file, &statb1) >= 0)) {
1243 						switch (statb1.st_mode &
1244 						    S_IFMT) {
1245 						case S_IFDIR:
1246 							buf[cc++] = '/';
1247 							break;
1248 						case S_IFSOCK:
1249 							buf[cc++] = '=';
1250 							break;
1251 						case S_IFDOOR:
1252 							buf[cc++] = '>';
1253 							break;
1254 						case S_IFIFO:
1255 							buf[cc++] = '|';
1256 							break;
1257 						default:
1258 							if ((statb1.st_mode &
1259 							    ~S_IFMT) &
1260 							    (S_IXUSR|S_IXGRP|
1261 							    S_IXOTH))
1262 								buf[cc++] = '*';
1263 							break;
1264 						}
1265 					}
1266 					buf[cc] = '\0';
1267 					rep->flinkto = strdup(buf);
1268 				}
1269 				break;
1270 			}
1271 
1272 			/*
1273 			 * ls /sym behaves differently from ls /sym/
1274 			 * when /sym is a symbolic link. This is fixed
1275 			 * when explicit arguments are specified.
1276 			 */
1277 
1278 #ifdef XPG6
1279 			/* Do not follow a symlink when -F is specified */
1280 			if ((!argfl) || (argfl && Fflg) ||
1281 			    (stat(file, &statb1) < 0))
1282 #else
1283 			/* Follow a symlink when -F is specified */
1284 			if (!argfl || stat(file, &statb1) < 0)
1285 #endif /* XPG6 */
1286 				break;
1287 			if ((statb1.st_mode & S_IFMT) == S_IFDIR) {
1288 				statb = statb1;
1289 				rep->ltype = 'd';
1290 				rep->lsize = statb1.st_size;
1291 				if (Rflg) {
1292 					record_ancestry(file, &statb, rep,
1293 					    argfl, myparent);
1294 				}
1295 			}
1296 			break;
1297 		case S_IFDOOR:
1298 			rep->ltype = 'D';
1299 			break;
1300 		case S_IFREG:
1301 			rep->ltype = '-';
1302 			break;
1303 		case S_IFPORT:
1304 			rep->ltype = 'P';
1305 			break;
1306 		default:
1307 			rep->ltype = '?';
1308 			break;
1309 		}
1310 		rep->lflags = statb.st_mode & ~S_IFMT;
1311 
1312 		if (!S_ISREG(statb.st_mode))
1313 			rep->lflags |= LS_NOTREG;
1314 
1315 		/* ACL: check acl entries count */
1316 		if (doacl) {
1317 
1318 			error = acl_get(file, 0, &rep->aclp);
1319 			if (error) {
1320 				(void) fprintf(stderr,
1321 				    gettext("ls: can't read ACL on %s: %s\n"),
1322 				    file, acl_strerror(error));
1323 				return (NULL);
1324 			}
1325 
1326 			rep->acl = ' ';
1327 
1328 			if (rep->aclp &&
1329 			    ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) {
1330 				rep->acl = '+';
1331 				/*
1332 				 * Special handling for ufs aka aclent_t ACL's
1333 				 */
1334 				if (rep->aclp &&
1335 				    acl_type(rep->aclp) == ACLENT_T) {
1336 					/*
1337 					 * For files with non-trivial acls, the
1338 					 * effective group permissions are the
1339 					 * intersection of the GROUP_OBJ value
1340 					 * and the CLASS_OBJ (acl mask) value.
1341 					 * Determine both the GROUP_OBJ and
1342 					 * CLASS_OBJ for this file and insert
1343 					 * the logical AND of those two values
1344 					 * in the group permissions field
1345 					 * of the lflags value for this file.
1346 					 */
1347 
1348 					/*
1349 					 * Until found in acl list, assume
1350 					 * maximum permissions for both group
1351 					 * a nd mask.  (Just in case the acl
1352 					 * lacks either value for some reason.)
1353 					 */
1354 					groupperm = 07;
1355 					mask = 07;
1356 					grouppermfound = 0;
1357 					maskfound = 0;
1358 					aclcnt = acl_cnt(rep->aclp);
1359 					for (tp =
1360 					    (aclent_t *)acl_data(rep->aclp);
1361 					    aclcnt--; tp++) {
1362 						if (tp->a_type == GROUP_OBJ) {
1363 							groupperm = tp->a_perm;
1364 							grouppermfound = 1;
1365 							continue;
1366 						}
1367 						if (tp->a_type == CLASS_OBJ) {
1368 							mask = tp->a_perm;
1369 							maskfound = 1;
1370 						}
1371 						if (grouppermfound && maskfound)
1372 							break;
1373 					}
1374 
1375 
1376 					/* reset all the group bits */
1377 					rep->lflags &= ~S_IRWXG;
1378 
1379 					/*
1380 					 * Now set them to the logical AND of
1381 					 * the GROUP_OBJ permissions and the
1382 					 * acl mask.
1383 					 */
1384 
1385 					rep->lflags |= (groupperm & mask) << 3;
1386 
1387 				}
1388 			}
1389 
1390 			if (!vflg && !Vflg && rep->aclp) {
1391 				acl_free(rep->aclp);
1392 				rep->aclp = NULL;
1393 			}
1394 
1395 			if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1)
1396 				rep->acl = '@';
1397 		} else
1398 			rep->acl = ' ';
1399 
1400 		/* mask ISARG and other file-type bits */
1401 
1402 		rep->luid = statb.st_uid;
1403 		rep->lgid = statb.st_gid;
1404 		rep->lnl = statb.st_nlink;
1405 		if (uflg)
1406 			rep->lmtime = statb.st_atim;
1407 		else if (cflg)
1408 			rep->lmtime = statb.st_ctim;
1409 		else
1410 			rep->lmtime = statb.st_mtim;
1411 
1412 		if (rep->ltype != 'b' && rep->ltype != 'c')
1413 			tblocks += rep->lblocks;
1414 	}
1415 	return (rep);
1416 }
1417 
1418 /*
1419  * returns pathname of the form dir/file;
1420  * dir and file are null-terminated strings.
1421  */
1422 static char *
1423 makename(char *dir, char *file)
1424 {
1425 	/*
1426 	 * PATH_MAX is the maximum length of a path name.
1427 	 * MAXNAMLEN is the maximum length of any path name component.
1428 	 * Allocate space for both, plus the '/' in the middle
1429 	 * and the null character at the end.
1430 	 * dfile is static as this is returned by makename().
1431 	 */
1432 	static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1];
1433 	char *dp, *fp;
1434 
1435 	dp = dfile;
1436 	fp = dir;
1437 	while (*fp)
1438 		*dp++ = *fp++;
1439 	if (dp > dfile && *(dp - 1) != '/')
1440 		*dp++ = '/';
1441 	fp = file;
1442 	while (*fp)
1443 		*dp++ = *fp++;
1444 	*dp = '\0';
1445 	return (dfile);
1446 }
1447 
1448 
1449 #include <pwd.h>
1450 #include <grp.h>
1451 #include <utmpx.h>
1452 
1453 struct	utmpx utmp;
1454 
1455 #define	NMAX	(sizeof (utmp.ut_name))
1456 #define	SCPYN(a, b)	(void) strncpy(a, b, NMAX)
1457 
1458 
1459 struct cachenode {		/* this struct must be zeroed before using */
1460 	struct cachenode *lesschild;	/* subtree whose entries < val */
1461 	struct cachenode *grtrchild;	/* subtree whose entries > val */
1462 	long val;			/* the uid or gid of this entry */
1463 	int initted;			/* name has been filled in */
1464 	char name[NMAX+1];		/* the string that val maps to */
1465 };
1466 static struct cachenode *names, *groups;
1467 
1468 static struct cachenode *
1469 findincache(struct cachenode **head, long val)
1470 {
1471 	struct cachenode **parent = head;
1472 	struct cachenode *c = *parent;
1473 
1474 	while (c != NULL) {
1475 		if (val == c->val) {
1476 			/* found it */
1477 			return (c);
1478 		} else if (val < c->val) {
1479 			parent = &c->lesschild;
1480 			c = c->lesschild;
1481 		} else {
1482 			parent = &c->grtrchild;
1483 			c = c->grtrchild;
1484 		}
1485 	}
1486 
1487 	/* not in the cache, make a new entry for it */
1488 	c = calloc(1, sizeof (struct cachenode));
1489 	if (c == NULL) {
1490 		perror("ls");
1491 		exit(2);
1492 	}
1493 	*parent = c;
1494 	c->val = val;
1495 	return (c);
1496 }
1497 
1498 /*
1499  * get name from cache, or passwd file for a given uid;
1500  * lastuid is set to uid.
1501  */
1502 static char *
1503 getname(uid_t uid)
1504 {
1505 	struct passwd *pwent;
1506 	struct cachenode *c;
1507 
1508 	if ((uid == lastuid) && lastuname)
1509 		return (lastuname);
1510 
1511 	c = findincache(&names, uid);
1512 	if (c->initted == 0) {
1513 		if ((pwent = getpwuid(uid)) != NULL) {
1514 			SCPYN(&c->name[0], pwent->pw_name);
1515 		} else {
1516 			(void) sprintf(&c->name[0], "%-8u", (int)uid);
1517 		}
1518 		c->initted = 1;
1519 	}
1520 	lastuid = uid;
1521 	lastuname = &c->name[0];
1522 	return (lastuname);
1523 }
1524 
1525 /*
1526  * get name from cache, or group file for a given gid;
1527  * lastgid is set to gid.
1528  */
1529 static char *
1530 getgroup(gid_t gid)
1531 {
1532 	struct group *grent;
1533 	struct cachenode *c;
1534 
1535 	if ((gid == lastgid) && lastgname)
1536 		return (lastgname);
1537 
1538 	c = findincache(&groups, gid);
1539 	if (c->initted == 0) {
1540 		if ((grent = getgrgid(gid)) != NULL) {
1541 			SCPYN(&c->name[0], grent->gr_name);
1542 		} else {
1543 			(void) sprintf(&c->name[0], "%-8u", (int)gid);
1544 		}
1545 		c->initted = 1;
1546 	}
1547 	lastgid = gid;
1548 	lastgname = &c->name[0];
1549 	return (lastgname);
1550 }
1551 
1552 /* return >0 if item pointed by pp2 should appear first */
1553 static int
1554 compar(struct lbuf **pp1, struct lbuf **pp2)
1555 {
1556 	struct lbuf *p1, *p2;
1557 
1558 	p1 = *pp1;
1559 	p2 = *pp2;
1560 	if (dflg == 0) {
1561 /*
1562  * compare two names in ls-command one of which is file
1563  * and the other is a directory;
1564  * this portion is not used for comparing files within
1565  * a directory name of ls-command;
1566  */
1567 		if (p1->lflags&ISARG && p1->ltype == 'd') {
1568 			if (!(p2->lflags&ISARG && p2->ltype == 'd'))
1569 				return (1);
1570 		} else {
1571 			if (p2->lflags&ISARG && p2->ltype == 'd')
1572 				return (-1);
1573 		}
1574 	}
1575 	if (tflg) {
1576 		if (p2->lmtime.tv_sec > p1->lmtime.tv_sec)
1577 			return (rflg);
1578 		else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec)
1579 			return (-rflg);
1580 		/* times are equal to the sec, check nsec */
1581 		if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec)
1582 			return (rflg);
1583 		else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec)
1584 			return (-rflg);
1585 		/* if times are equal, fall through and sort by name */
1586 	} else if (Sflg) {
1587 		/*
1588 		 * The size stored in lsize can be either the
1589 		 * size or the major minor number (in the case of
1590 		 * block and character special devices).  If it's
1591 		 * a major minor number, then the size is considered
1592 		 * to be zero and we want to fall through and sort
1593 		 * by name.  In addition, if the size of p2 is equal
1594 		 * to the size of p1 we want to fall through and
1595 		 * sort by name.
1596 		 */
1597 		off_t	p1size = (p1->ltype == 'b') ||
1598 			    (p1->ltype == 'c') ? 0 : p1->lsize;
1599 		off_t	p2size = (p2->ltype == 'b') ||
1600 			    (p2->ltype == 'c') ? 0 : p2->lsize;
1601 		if (p2size > p1size) {
1602 			return (rflg);
1603 		} else if (p2size < p1size) {
1604 			return (-rflg);
1605 		}
1606 		/* Sizes are equal, fall through and sort by name. */
1607 	}
1608 	return (rflg * strcoll(
1609 	    p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname,
1610 	    p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname));
1611 }
1612 
1613 static void
1614 pprintf(char *s1, char *s2)
1615 {
1616 	csi_pprintf((unsigned char *)s1);
1617 	csi_pprintf((unsigned char *)s2);
1618 }
1619 
1620 static void
1621 csi_pprintf(unsigned char *s)
1622 {
1623 	unsigned char *cp;
1624 	char	c;
1625 	int	i;
1626 	int	c_len;
1627 	int	p_col;
1628 	wchar_t	pcode;
1629 
1630 	if (!qflg && !bflg) {
1631 		for (cp = s; *cp != '\0'; cp++) {
1632 			(void) putchar(*cp);
1633 			curcol++;
1634 		}
1635 		return;
1636 	}
1637 
1638 	for (cp = s; *cp; ) {
1639 		if (isascii(c = *cp)) {
1640 			if (!isprint(c)) {
1641 				if (qflg) {
1642 					c = '?';
1643 				} else {
1644 					curcol += 3;
1645 					(void) putc('\\', stdout);
1646 					c = '0' + ((*cp >> 6) & 07);
1647 					(void) putc(c, stdout);
1648 					c = '0' + ((*cp >> 3) & 07);
1649 					(void) putc(c, stdout);
1650 					c = '0' + (*cp & 07);
1651 				}
1652 			}
1653 			curcol++;
1654 			cp++;
1655 			(void) putc(c, stdout);
1656 			continue;
1657 		}
1658 
1659 		if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) {
1660 			c_len = 1;
1661 			goto not_print;
1662 		}
1663 
1664 		if ((p_col = wcwidth(pcode)) > 0) {
1665 			(void) putwchar(pcode);
1666 			cp += c_len;
1667 			curcol += p_col;
1668 			continue;
1669 		}
1670 
1671 not_print:
1672 		for (i = 0; i < c_len; i++) {
1673 			if (qflg) {
1674 				c = '?';
1675 			} else {
1676 				curcol += 3;
1677 				(void) putc('\\', stdout);
1678 				c = '0' + ((*cp >> 6) & 07);
1679 				(void) putc(c, stdout);
1680 				c = '0' + ((*cp >> 3) & 07);
1681 				(void) putc(c, stdout);
1682 				c = '0' + (*cp & 07);
1683 			}
1684 			curcol++;
1685 			(void) putc(c, stdout);
1686 			cp++;
1687 		}
1688 	}
1689 }
1690 
1691 static int
1692 strcol(unsigned char *s1)
1693 {
1694 	int	w;
1695 	int	w_col;
1696 	int	len;
1697 	wchar_t	wc;
1698 
1699 	w = 0;
1700 	while (*s1) {
1701 		if (isascii(*s1)) {
1702 			w++;
1703 			s1++;
1704 			continue;
1705 		}
1706 
1707 		if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
1708 			w++;
1709 			s1++;
1710 			continue;
1711 		}
1712 
1713 		if ((w_col = wcwidth(wc)) < 0)
1714 			w_col = len;
1715 		s1 += len;
1716 		w += w_col;
1717 	}
1718 	return (w);
1719 }
1720 
1721 /*
1722  * Convert an unsigned long long to a string representation and place the
1723  * result in the caller-supplied buffer.
1724  *
1725  * The number provided is a size in bytes.  The number is first
1726  * converted to an integral multiple of 'scale' bytes.  This new
1727  * number is then scaled down until it is small enough to be in a good
1728  * human readable format, i.e.  in the range 0 thru scale-1.  If the
1729  * number used to derive the final number is not a multiple of scale, and
1730  * the final number has only a single significant digit, we compute
1731  * tenths of units to provide a second significant digit.
1732  *
1733  * The value "(unsigned long long)-1" is a special case and is always
1734  * converted to "-1".
1735  *
1736  * A pointer to the caller-supplied buffer is returned.
1737  */
1738 static char *
1739 number_to_scaled_string(
1740 			numbuf_t buf,		/* put the result here */
1741 			unsigned long long number, /* convert this number */
1742 			long scale)
1743 {
1744 	unsigned long long save;
1745 	/* Measurement: kilo, mega, giga, tera, peta, exa */
1746 	char *uom = "KMGTPE";
1747 
1748 	if ((long long)number == (long long)-1) {
1749 		(void) strlcpy(buf, "-1", sizeof (numbuf_t));
1750 		return (buf);
1751 	}
1752 
1753 	save = number;
1754 	number = number / scale;
1755 
1756 	/*
1757 	 * Now we have number as a count of scale units.
1758 	 * If no further scaling is necessary, we round up as appropriate.
1759 	 *
1760 	 * The largest value number could have had entering the routine is
1761 	 * 16 Exabytes, so running off the end of the uom array should
1762 	 * never happen.  We check for that, though, as a guard against
1763 	 * a breakdown elsewhere in the algorithm.
1764 	 */
1765 	if (number < (unsigned long long)scale) {
1766 		if ((save % scale) >= (unsigned long long)(scale / 2)) {
1767 			if (++number == (unsigned long long)scale) {
1768 				uom++;
1769 				number = 1;
1770 			}
1771 		}
1772 	} else {
1773 		while ((number >= (unsigned long long)scale) && (*uom != 'E')) {
1774 			uom++; /* next unit of measurement */
1775 			save = number;
1776 			/*
1777 			 * If we're over half way to the next unit of
1778 			 * 'scale' bytes (which means we should round
1779 			 * up), then adding half of 'scale' prior to
1780 			 * the division will push us into that next
1781 			 * unit of scale when we perform the division
1782 			 */
1783 			number = (number + (scale / 2)) / scale;
1784 		}
1785 	}
1786 
1787 	/* check if we should output a decimal place after the point */
1788 	if ((save / scale) < 10) {
1789 		/* snprintf() will round for us */
1790 		float fnum = (float)save / scale;
1791 		(void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c",
1792 		    fnum, *uom);
1793 	} else {
1794 		(void) snprintf(buf, sizeof (numbuf_t), "%4llu%c",
1795 		    number, *uom);
1796 	}
1797 	return (buf);
1798 }
1799