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