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