xref: /illumos-gate/usr/src/cmd/ls/ls.c (revision 828d47c166ce67972b1f1929669b9af5be769423)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright 2009 Jason King.  All rights reserved.
29  * Use is subject to license terms.
30  */
31 
32 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
33 /*	  All Rights Reserved  	*/
34 
35 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
36 /*	  All Rights Reserved	*/
37 
38 /*
39  * List files or directories
40  */
41 
42 #include <sys/param.h>
43 #include <sys/types.h>
44 #include <sys/mkdev.h>
45 #include <sys/stat.h>
46 #include <sys/acl.h>
47 
48 #include <wchar.h>
49 #include <stdio.h>
50 #include <ctype.h>
51 #include <dirent.h>
52 #include <string.h>
53 #include <locale.h>
54 #include <curses.h>
55 #include <term.h>
56 #include <termios.h>
57 #include <stdlib.h>
58 #include <widec.h>
59 #include <locale.h>
60 #include <wctype.h>
61 #include <pwd.h>
62 #include <grp.h>
63 #include <limits.h>
64 #include <fcntl.h>
65 #include <unistd.h>
66 #include <libgen.h>
67 #include <errno.h>
68 #include <aclutils.h>
69 #include <libnvpair.h>
70 #include <libcmdutils.h>
71 #include <attr.h>
72 #include <getopt.h>
73 #include <inttypes.h>
74 
75 #ifndef STANDALONE
76 #define	TERMINFO
77 #endif
78 
79 /*
80  * -DNOTERMINFO can be defined on the cc command line to prevent
81  * the use of terminfo.  This should be done on systems not having
82  * the terminfo feature(pre 6.0 systems ?).
83  * As a result, columnar listings assume 80 columns for output,
84  * unless told otherwise via the COLUMNS environment variable.
85  */
86 #ifdef NOTERMINFO
87 #undef TERMINFO
88 #endif
89 
90 #include <term.h>
91 
92 #define	BFSIZE	16
93 /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */
94 #define	ISARG	0100000
95 
96 /*
97  * this flag has been added to manipulate the display of S instead of 'l' when
98  * the file is not a regular file and when group execution bit is off
99  */
100 #define	LS_NOTREG	010000
101 
102 
103 /*
104  * Date and time formats
105  *
106  * b --- abbreviated month name
107  * e --- day number
108  * Y --- year in the form ccyy
109  * H --- hour(24-hour version)
110  * M --- minute
111  * F --- yyyy-mm-dd
112  * T --- hh:mm:ss
113  * z --- time zone as hours displacement from UTC
114  * note that %F and %z are from the ISO C99 standard and are
115  * not present in older C libraries
116  */
117 #define	FORMAT_OLD	" %b %e  %Y "
118 #define	FORMAT_NEW	" %b %e %H:%M "
119 #define	FORMAT_LONG	" %b %e %T %Y "
120 #define	FORMAT_ISO_FULL	" %%F %%T.%.09ld %%z "
121 #define	FORMAT_ISO_LONG	" %F %R "
122 #define	FORMAT_ISO_NEW	" %m-%d %H:%M "
123 #define	FORMAT_ISO_OLD	" %F "
124 
125 #undef BUFSIZ
126 #define	BUFSIZ 4096
127 #define	NUMBER_WIDTH 40
128 #define	FMTSIZE 50
129 
130 struct ditem {
131 	dev_t	dev;			/* directory items device number */
132 	ino_t	ino;			/* directory items inode number */
133 	struct ditem *parent;		/* dir items ptr to its parent's info */
134 };
135 /* Holds boolean extended system attributes */
136 struct attrb {
137 	char		*name;
138 };
139 /* Holds timestamp extended system attributes */
140 struct attrtm {
141 	char		*name;
142 	uint64_t	stm;
143 	uint64_t	nstm;
144 };
145 
146 struct	lbuf	{
147 	union	{
148 		char	lname[MAXNAMLEN]; /* used for filename in a directory */
149 		char	*namep;		/* for name in ls-command; */
150 	} ln;
151 	char	ltype;		/* filetype */
152 	ino_t	lnum;		/* inode number of file */
153 	mode_t	lflags; 	/* 0777 bits used as r,w,x permissions */
154 	nlink_t	lnl;		/* number of links to file */
155 	uid_t	luid;
156 	gid_t	lgid;
157 	off_t	lsize;		/* filesize or major/minor dev numbers */
158 	blkcnt_t	lblocks;	/* number of file blocks */
159 	timestruc_t	lmtime;
160 	timestruc_t	lat;
161 	timestruc_t	lct;
162 	timestruc_t	lmt;
163 	char	*flinkto;	/* symbolic link contents */
164 	char 	acl;		/* indicate there are additional acl entries */
165 	int	cycle;		/* cycle detected flag */
166 	struct ditem *ancinfo;	/* maintains ancestor info */
167 	acl_t *aclp;		/* ACL if present */
168 	struct attrb *exttr;	/* boolean extended system attributes */
169 	struct attrtm *extm;	/* timestamp extended system attributes */
170 };
171 
172 struct dchain {
173 	char *dc_name;		/* path name */
174 	int cycle_detected;	/* cycle detected visiting this directory */
175 	struct ditem *myancinfo;	/* this directory's ancestry info */
176 	struct dchain *dc_next;	/* next directory in the chain */
177 };
178 
179 #define	LSA_NONE	(0)
180 #define	LSA_BOLD	(1L << 0)
181 #define	LSA_UNDERSCORE	(1L << 1)
182 #define	LSA_BLINK	(1L << 2)
183 #define	LSA_REVERSE	(1L << 3)
184 #define	LSA_CONCEALED	(1L << 4)
185 
186 /* these should be ordered most general to most specific */
187 typedef enum LS_CFTYPE {
188 	LS_NORMAL,
189 	LS_FILE,
190 	LS_EXEC,
191 	LS_DIR,
192 	LS_LINK,
193 	LS_FIFO,
194 	LS_SOCK,
195 	LS_DOOR,
196 	LS_BLK,
197 	LS_CHR,
198 	LS_PORT,
199 	LS_STICKY,
200 	LS_ORPHAN,
201 	LS_SETGID,
202 	LS_SETUID,
203 	LS_OTHER_WRITABLE,
204 	LS_STICKY_OTHER_WRITABLE,
205 	LS_PAT
206 } ls_cftype_t;
207 
208 typedef struct ls_color {
209 	char		*sfx;
210 	ls_cftype_t	ftype;
211 	int		attr;
212 	int		fg;
213 	int		bg;
214 } ls_color_t;
215 
216 /*
217  * A numbuf_t is used when converting a number to a string representation
218  */
219 typedef char numbuf_t[NUMBER_WIDTH];
220 
221 static struct dchain *dfirst;	/* start of the dir chain */
222 static struct dchain *cdfirst;	/* start of the current dir chain */
223 static struct dchain *dtemp;	/* temporary - used for linking */
224 static char *curdir;		/* the current directory */
225 
226 static int	first = 1;	/* true if first line is not yet printed */
227 static int	nfiles = 0;	/* number of flist entries in current use */
228 static int	nargs = 0;	/* number of flist entries used for arguments */
229 static int	maxfils = 0;	/* number of flist/lbuf entries allocated */
230 static int	maxn = 0;	/* number of flist entries with lbufs asigned */
231 static int	quantn = 64;	/* allocation growth quantum */
232 
233 static struct lbuf	*nxtlbf;	/* ptr to next lbuf to be assigned */
234 static struct lbuf	**flist;	/* ptr to list of lbuf pointers */
235 static struct lbuf	*gstat(char *, int, struct ditem *);
236 static char		*getname(uid_t);
237 static char		*getgroup(gid_t);
238 static char		*makename(char *, char *);
239 static void		pentry(struct lbuf *);
240 static void		column(void);
241 static void		pmode(mode_t aflag);
242 static void		selection(int *);
243 static void		new_line(void);
244 static void		rddir(char *, struct ditem *);
245 static int		strcol(unsigned char *);
246 static void		pem(struct lbuf **, struct lbuf **, int);
247 static void		pdirectory(char *, int, int, int, struct ditem *);
248 static struct cachenode *findincache(struct cachenode **, long);
249 static void		csi_pprintf(unsigned char *);
250 static void		pprintf(char *, char *);
251 static int		compar(struct lbuf **pp1, struct lbuf **pp2);
252 static char 		*number_to_scaled_string(numbuf_t buf,
253 			    unsigned long long number,
254 			    long scale);
255 static void		record_ancestry(char *, struct stat *, struct lbuf *,
256 			    int, struct ditem *);
257 static void		ls_color_init(void);
258 static void		ls_start_color(struct lbuf *);
259 static void		ls_end_color(void);
260 
261 static int		aflg;
262 static int		atflg;
263 static int		bflg;
264 static int		cflg;
265 static int		dflg;
266 static int		eflg;
267 static int		fflg;
268 static int		gflg;
269 static int		hflg;
270 static int		iflg;
271 static int		lflg;
272 static int		mflg;
273 static int		nflg;
274 static int		oflg;
275 static int		pflg;
276 static int		qflg;
277 static int		rflg = 1; /* init to 1 for special use in compar */
278 static int		sflg;
279 static int		tflg;
280 static int		uflg;
281 static int		Uflg;
282 static int		wflg;
283 static int		xflg;
284 static int		Aflg;
285 static int		Bflg;
286 static int		Cflg;
287 static int		Eflg;
288 static int		Fflg;
289 static int		Hflg;
290 static int		Lflg;
291 static int		Rflg;
292 static int		Sflg;
293 static int		vflg;
294 static int		Vflg;
295 static int		saflg;		/* boolean extended system attr. */
296 static int		sacnt;		/* number of extended system attr. */
297 static int		copt;
298 static int		vopt;
299 static int		tmflg;		/* create time ext. system attr. */
300 static int		ctm;
301 static int		atm;
302 static int		mtm;
303 static int		crtm;
304 static int		alltm;
305 static long		hscale;
306 static mode_t		flags;
307 static int		err = 0;	/* Contains return code */
308 static int		colorflg;
309 static int		file_typeflg;
310 
311 static uid_t		lastuid	= (uid_t)-1;
312 static gid_t		lastgid = (gid_t)-1;
313 static char		*lastuname = NULL;
314 static char		*lastgname = NULL;
315 
316 /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg, colorflg are on */
317 static int		statreq;
318 
319 static uint64_t		block_size = 1;
320 static char		*dotp = ".";
321 
322 static u_longlong_t 	tblocks; /* number of blocks of files in a directory */
323 static time_t		year, now;
324 
325 static int		num_cols = 80;
326 static int		colwidth;
327 static int		filewidth;
328 static int		fixedwidth;
329 static int		nomocore;
330 static int		curcol;
331 
332 static struct	winsize	win;
333 
334 /* if time_fmt_new is left NULL, time_fmt_old is used for all times */
335 static const char	*time_fmt_old = FORMAT_OLD;	/* non-recent files */
336 static const char	*time_fmt_new = FORMAT_NEW;	/* recent files */
337 static int		time_custom;	/* != 0 if a custom format */
338 static char	time_buf[FMTSIZE];	/* array to hold day and time */
339 
340 static int		lsc_debug;
341 static ls_color_t	*lsc_match;
342 static ls_color_t	*lsc_colors;
343 static size_t		lsc_ncolors;
344 static char		*lsc_bold;
345 static char		*lsc_underline;
346 static char		*lsc_blink;
347 static char		*lsc_reverse;
348 static char		*lsc_concealed;
349 static char		*lsc_none;
350 static char		*lsc_setfg;
351 static char		*lsc_setbg;
352 
353 #define	NOTWORKINGDIR(d, l)	(((l) < 2) || \
354 				    (strcmp((d) + (l) - 2, "/.") != 0))
355 
356 #define	NOTPARENTDIR(d, l)	(((l) < 3) || \
357 				    (strcmp((d) + (l) - 3, "/..") != 0))
358 /* Extended system attributes support */
359 static int get_sysxattr(char *, struct lbuf *);
360 static void set_sysattrb_display(char *, boolean_t, struct lbuf *);
361 static void set_sysattrtm_display(char *, struct lbuf *);
362 static void format_time(time_t, time_t);
363 static void print_time(struct lbuf *);
364 static void format_attrtime(struct lbuf *);
365 static void *xmalloc(size_t, struct lbuf *);
366 static void free_sysattr(struct lbuf *);
367 static nvpair_t *pair;
368 static nvlist_t	*response;
369 static int acl_err;
370 
371 const struct option long_options[] = {
372 	{ "all", no_argument, NULL, 'a' },
373 	{ "almost-all", no_argument, NULL, 'A' },
374 	{ "escape", no_argument, NULL, 'b' },
375 	{ "classify", no_argument, NULL, 'F' },
376 	{ "human-readable", no_argument, NULL, 'h' },
377 	{ "dereference", no_argument, NULL, 'L' },
378 	{ "dereference-command-line", no_argument, NULL, 'H' },
379 	{ "ignore-backups", no_argument, NULL, 'B' },
380 	{ "inode", no_argument, NULL, 'i' },
381 	{ "numeric-uid-gid", no_argument, NULL, 'n' },
382 	{ "no-group", no_argument, NULL, 'o' },
383 	{ "hide-control-chars", no_argument, NULL, 'q' },
384 	{ "reverse", no_argument, NULL, 'r' },
385 	{ "recursive", no_argument, NULL, 'R' },
386 	{ "size", no_argument, NULL, 's' },
387 	{ "width", required_argument, NULL, 'w' },
388 
389 	/* no short options for these */
390 	{ "block-size", required_argument, NULL, 0 },
391 	{ "full-time", no_argument, NULL, 0 },
392 	{ "si", no_argument, NULL, 0 },
393 	{ "color", optional_argument, NULL, 0 },
394 	{ "colour", optional_argument, NULL, 0},
395 	{ "file-type", no_argument, NULL, 0 },
396 	{ "time-style", required_argument, NULL, 0 },
397 
398 	{0, 0, 0, 0}
399 };
400 
401 int
402 main(int argc, char *argv[])
403 {
404 	int		c;
405 	int		i;
406 	int		width;
407 	int		amino = 0;
408 	int		opterr = 0;
409 	int		option_index = 0;
410 	struct lbuf	*ep;
411 	struct lbuf	lb;
412 	struct ditem	*myinfo;
413 
414 	(void) setlocale(LC_ALL, "");
415 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
416 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
417 #endif
418 	(void) textdomain(TEXT_DOMAIN);
419 #ifdef STANDALONE
420 	if (argv[0][0] == '\0')
421 		argc = getargv("ls", &argv, 0);
422 #endif
423 
424 	lb.lmtime.tv_sec = time(NULL);
425 	lb.lmtime.tv_nsec = 0;
426 	year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */
427 	now = lb.lmtime.tv_sec + 60;
428 	if (isatty(1)) {
429 		Cflg = 1;
430 		mflg = 0;
431 	}
432 
433 	while ((c = getopt_long(argc, argv,
434 	    "+aAbBcCdeEfFghHiklLmnopqrRsStuUw:x1@vV/:%:", long_options,
435 	    &option_index)) != -1)
436 		switch (c) {
437 		case 0:
438 			/* non-short options */
439 			if (strcmp(long_options[option_index].name,
440 			    "color") == 0 ||
441 			    strcmp(long_options[option_index].name,
442 			    "colour") == 0) {
443 				if (optarg == NULL ||
444 				    strcmp(optarg, "always") == 0 ||
445 				    strcmp(optarg, "yes") == 0 ||
446 				    strcmp(optarg, "force") == 0) {
447 					colorflg++;
448 					statreq++;
449 					continue;
450 				}
451 
452 				if ((strcmp(optarg, "auto") == 0 ||
453 				    strcmp(optarg, "tty") == 0 ||
454 				    strcmp(optarg, "if-tty") == 0) &&
455 				    isatty(1) == 1) {
456 					colorflg++;
457 					statreq++;
458 					continue;
459 				}
460 
461 				if (strcmp(optarg, "never") == 0 ||
462 				    strcmp(optarg, "no") == 0 ||
463 				    strcmp(optarg, "none") == 0) {
464 					colorflg = 0;
465 					continue;
466 				}
467 				(void) fprintf(stderr,
468 				    gettext("Invalid argument '%s' for "
469 				    "--color\n"), optarg);
470 				++opterr;
471 				continue;
472 			}
473 
474 			if (strcmp(long_options[option_index].name,
475 			    "si") == 0) {
476 				hflg++;
477 				hscale = 1000;
478 				continue;
479 			}
480 
481 			if (strcmp(long_options[option_index].name,
482 			    "block-size") == 0) {
483 				size_t scale_len = strlen(optarg);
484 				uint64_t scale = 1;
485 				uint64_t kilo = 1024;
486 				char scale_c;
487 
488 				if (scale_len == 0) {
489 					(void) fprintf(stderr, gettext(
490 					    "Invalid block size \'%s\'\n"),
491 					    optarg);
492 					exit(1);
493 				}
494 
495 				scale_c = optarg[scale_len - 1];
496 				if (scale_c == 'B') {
497 					/* need at least digit, scale, B */
498 					if (scale_len < 3) {
499 						(void) fprintf(stderr, gettext(
500 						    "Invalid block size "
501 						    "\'%s\'\n"), optarg);
502 						exit(1);
503 					}
504 					kilo = 1000;
505 					scale_c = optarg[scale_len - 2];
506 					if (isdigit(scale_c)) {
507 						(void) fprintf(stderr,
508 						    gettext("Invalid block size"
509 						    " \'%s\'\n"), optarg);
510 						exit(1);
511 					}
512 					/*
513 					 * make optarg[scale_len - 1] point to
514 					 * the scale factor
515 					 */
516 					--scale_len;
517 				}
518 
519 				switch (scale_c) {
520 				case 'y':
521 				case 'Y':
522 					scale *= kilo;
523 					/*FALLTHROUGH*/
524 				case 'Z':
525 				case 'z':
526 					scale *= kilo;
527 					/*FALLTHROUGH*/
528 				case 'E':
529 				case 'e':
530 					scale *= kilo;
531 					/*FALLTHROUGH*/
532 				case 'P':
533 				case 'p':
534 					scale *= kilo;
535 					/*FALLTHROUGH*/
536 				case 'T':
537 				case 't':
538 					scale *= kilo;
539 					/*FALLTHROUGH*/
540 				case 'G':
541 				case 'g':
542 					scale *= kilo;
543 					/*FALLTHROUGH*/
544 				case 'M':
545 				case 'm':
546 					scale *= kilo;
547 					/*FALLTHROUGH*/
548 				case 'K':
549 				case 'k':
550 					scale *= kilo;
551 					break;
552 				default:
553 					if (!isdigit(scale_c)) {
554 						(void) fprintf(stderr,
555 						    gettext("Invalid character "
556 						    "following block size in "
557 						    "\'%s\'\n"), optarg);
558 						exit(1);
559 					}
560 				}
561 
562 				/* NULL out scale constant if present */
563 				if (scale > 1 && !isdigit(scale_c))
564 					optarg[scale_len - 1] = '\0';
565 
566 				/* Based on testing, this is what GNU ls does */
567 				block_size = strtoll(optarg, NULL, 0) * scale;
568 				if (block_size < 1) {
569 					(void) fprintf(stderr,
570 					    gettext("Invalid block size "
571 					    "\'%s\'\n"), optarg);
572 					exit(1);
573 				}
574 				continue;
575 			}
576 
577 			if (strcmp(long_options[option_index].name,
578 			    "file-type") == 0) {
579 				file_typeflg++;
580 				Fflg++;
581 				statreq++;
582 				continue;
583 			}
584 
585 
586 			if (strcmp(long_options[option_index].name,
587 			    "full-time") == 0) {
588 				Eflg++;
589 				statreq++;
590 				eflg = 0;
591 				time_fmt_old = FORMAT_ISO_FULL;
592 				time_fmt_new = FORMAT_ISO_FULL;
593 				continue;
594 			}
595 
596 			if (strcmp(long_options[option_index].name,
597 			    "time-style") == 0) {
598 				/* like -E, but doesn't imply -l */
599 				if (strcmp(optarg, "full-iso") == 0) {
600 					Eflg++;
601 					statreq++;
602 					eflg = 0;
603 					time_fmt_old = FORMAT_ISO_FULL;
604 					time_fmt_new = FORMAT_ISO_FULL;
605 					continue;
606 				}
607 				if (strcmp(optarg, "long-iso") == 0) {
608 					statreq++;
609 					Eflg = 0;
610 					eflg = 0;
611 					time_fmt_old = FORMAT_ISO_LONG;
612 					time_fmt_new = FORMAT_ISO_LONG;
613 					continue;
614 				}
615 				if (strcmp(optarg, "iso") == 0) {
616 					statreq++;
617 					Eflg = 0;
618 					eflg = 0;
619 					time_fmt_old = FORMAT_ISO_OLD;
620 					time_fmt_new = FORMAT_ISO_NEW;
621 					continue;
622 				}
623 				/* should be the default */
624 				if (strcmp(optarg, "locale") == 0) {
625 					time_fmt_old = FORMAT_OLD;
626 					time_fmt_new = FORMAT_NEW;
627 					continue;
628 				}
629 				if (optarg[0] == '+') {
630 					char	*told, *tnew;
631 					char	*p;
632 					size_t	timelen = strlen(optarg);
633 
634 					p = strchr(optarg, '\n');
635 					if (p != NULL)
636 						*p++ = '\0';
637 
638 					/*
639 					 * Time format requires a leading and
640 					 * trailing space
641 					 * Add room for 3 spaces + 2 nulls
642 					 * The + in optarg is replaced with
643 					 * a space.
644 					 */
645 					timelen += 2 + 3;
646 					told = malloc(timelen);
647 					if (told == NULL) {
648 						perror("Out of memory");
649 						exit(1);
650 					}
651 
652 					(void) memset(told, 0, timelen);
653 					told[0] = ' ';
654 					(void) strlcat(told, &optarg[1],
655 					    timelen);
656 					(void) strlcat(told, " ", timelen);
657 
658 					if (p != NULL) {
659 						size_t tnew_len;
660 
661 						tnew = told + strlen(told) + 1;
662 						tnew_len = timelen -
663 						    strlen(told) - 1;
664 
665 						tnew[0] = ' ';
666 						(void) strlcat(tnew, p,
667 						    tnew_len);
668 						(void) strlcat(tnew, " ",
669 						    tnew_len);
670 						time_fmt_new =
671 						    (const char *)tnew;
672 					} else {
673 						time_fmt_new =
674 						    (const char *)told;
675 					}
676 
677 					time_fmt_old = (const char *)told;
678 					time_custom = 1;
679 					continue;
680 				}
681 				continue;
682 			}
683 
684 			continue;
685 
686 		case 'a':
687 			aflg++;
688 			continue;
689 		case 'A':
690 			Aflg++;
691 			continue;
692 		case 'b':
693 			bflg = 1;
694 			qflg = 0;
695 			continue;
696 		case 'B':
697 			Bflg = 1;
698 			continue;
699 		case 'c':
700 			uflg = 0;
701 			atm = 0;
702 			ctm = 0;
703 			mtm = 0;
704 			crtm = 0;
705 			cflg++;
706 			continue;
707 		case 'C':
708 			Cflg = 1;
709 			mflg = 0;
710 #ifdef XPG4
711 			lflg = 0;
712 #endif
713 			continue;
714 		case 'd':
715 			dflg++;
716 			continue;
717 		case 'e':
718 			eflg++;
719 			lflg++;
720 			statreq++;
721 			Eflg = 0;
722 			time_fmt_old = FORMAT_LONG;
723 			time_fmt_new = FORMAT_LONG;
724 			continue;
725 		case 'E':
726 			Eflg++;
727 			lflg++;
728 			statreq++;
729 			eflg = 0;
730 			time_fmt_old = FORMAT_ISO_FULL;
731 			time_fmt_new = FORMAT_ISO_FULL;
732 			continue;
733 		case 'f':
734 			fflg++;
735 			continue;
736 		case 'F':
737 			Fflg++;
738 			statreq++;
739 			continue;
740 		case 'g':
741 			gflg++;
742 			lflg++;
743 			statreq++;
744 			continue;
745 		case 'h':
746 			hflg++;
747 			hscale = 1024;
748 			continue;
749 		case 'H':
750 			Hflg++;
751 			/* -H and -L are mutually exclusive */
752 			Lflg = 0;
753 			continue;
754 		case 'i':
755 			iflg++;
756 			continue;
757 		case 'k':
758 			block_size = 1024;
759 			continue;
760 		case 'l':
761 			lflg++;
762 			statreq++;
763 			Cflg = 0;
764 			xflg = 0;
765 			mflg = 0;
766 			atflg = 0;
767 			continue;
768 		case 'L':
769 			Lflg++;
770 			/* -H and -L are mutually exclusive */
771 			Hflg = 0;
772 			continue;
773 		case 'm':
774 			Cflg = 0;
775 			mflg = 1;
776 #ifdef XPG4
777 			lflg = 0;
778 #endif
779 			continue;
780 		case 'n':
781 			nflg++;
782 			lflg++;
783 			statreq++;
784 			Cflg = 0;
785 			xflg = 0;
786 			mflg = 0;
787 			atflg = 0;
788 			continue;
789 		case 'o':
790 			oflg++;
791 			lflg++;
792 			statreq++;
793 			continue;
794 		case 'p':
795 			pflg++;
796 			statreq++;
797 			continue;
798 		case 'q':
799 			qflg = 1;
800 			bflg = 0;
801 			continue;
802 		case 'r':
803 			rflg = -1;
804 			continue;
805 		case 'R':
806 			Rflg++;
807 			statreq++;
808 			continue;
809 		case 's':
810 			sflg++;
811 			statreq++;
812 			continue;
813 		case 'S':
814 			tflg = 0;
815 			Uflg = 0;
816 			Sflg++;
817 			statreq++;
818 			continue;
819 		case 't':
820 			Sflg = 0;
821 			Uflg = 0;
822 			tflg++;
823 			statreq++;
824 			continue;
825 		case 'U':
826 			Sflg = 0;
827 			tflg = 0;
828 			Uflg++;
829 			continue;
830 		case 'u':
831 			cflg = 0;
832 			atm = 0;
833 			ctm = 0;
834 			mtm = 0;
835 			crtm = 0;
836 			uflg++;
837 			continue;
838 		case 'V':
839 			Vflg++;
840 			/*FALLTHROUGH*/
841 		case 'v':
842 			vflg++;
843 #if !defined(XPG4)
844 			if (lflg)
845 				continue;
846 #endif
847 			lflg++;
848 			statreq++;
849 			Cflg = 0;
850 			xflg = 0;
851 			mflg = 0;
852 			continue;
853 		case 'w':
854 			wflg++;
855 			num_cols = atoi(optarg);
856 			continue;
857 		case 'x':
858 			xflg = 1;
859 			Cflg = 1;
860 			mflg = 0;
861 #ifdef XPG4
862 			lflg = 0;
863 #endif
864 			continue;
865 		case '1':
866 			Cflg = 0;
867 			continue;
868 		case '@':
869 #if !defined(XPG4)
870 			/*
871 			 * -l has precedence over -@
872 			 */
873 			if (lflg)
874 				continue;
875 #endif
876 			atflg++;
877 			lflg++;
878 			statreq++;
879 			Cflg = 0;
880 			xflg = 0;
881 			mflg = 0;
882 			continue;
883 		case '/':
884 			saflg++;
885 			if (optarg != NULL) {
886 				if (strcmp(optarg, "c") == 0) {
887 					copt++;
888 					vopt = 0;
889 				} else if (strcmp(optarg, "v") == 0) {
890 					vopt++;
891 					copt = 0;
892 				} else
893 					opterr++;
894 			} else
895 				opterr++;
896 			lflg++;
897 			statreq++;
898 			Cflg = 0;
899 			xflg = 0;
900 			mflg = 0;
901 			continue;
902 		case '%':
903 			tmflg++;
904 			if (optarg != NULL) {
905 				if (strcmp(optarg, "ctime") == 0) {
906 					ctm++;
907 					atm = 0;
908 					mtm = 0;
909 					crtm = 0;
910 				} else if (strcmp(optarg, "atime") == 0) {
911 					atm++;
912 					ctm = 0;
913 					mtm = 0;
914 					crtm = 0;
915 					uflg = 0;
916 					cflg = 0;
917 				} else if (strcmp(optarg, "mtime") == 0) {
918 					mtm++;
919 					atm = 0;
920 					ctm = 0;
921 					crtm = 0;
922 					uflg = 0;
923 					cflg = 0;
924 				} else if (strcmp(optarg, "crtime") == 0) {
925 					crtm++;
926 					atm = 0;
927 					ctm = 0;
928 					mtm = 0;
929 					uflg = 0;
930 					cflg = 0;
931 				} else if (strcmp(optarg, "all") == 0) {
932 					alltm++;
933 					atm = 0;
934 					ctm = 0;
935 					mtm = 0;
936 					crtm = 0;
937 				} else
938 					opterr++;
939 			} else
940 				opterr++;
941 
942 			Sflg = 0;
943 			statreq++;
944 			mflg = 0;
945 			continue;
946 		case '?':
947 			opterr++;
948 			continue;
949 		}
950 
951 	if (opterr) {
952 		(void) fprintf(stderr, gettext(
953 		    "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]"
954 		    "%%[atime | crtime | ctime | mtime | all]"
955 		    " [files]\n"));
956 		exit(2);
957 	}
958 
959 	if (fflg) {
960 		aflg++;
961 		lflg = 0;
962 		sflg = 0;
963 		tflg = 0;
964 		Sflg = 0;
965 		statreq = 0;
966 	}
967 
968 	fixedwidth = 2;
969 	if (pflg || Fflg)
970 		fixedwidth++;
971 	if (iflg)
972 		fixedwidth += 11;
973 	if (sflg)
974 		fixedwidth += 5;
975 
976 	if (lflg) {
977 		if (!gflg && !oflg)
978 			gflg = oflg = 1;
979 		else
980 		if (gflg && oflg)
981 			gflg = oflg = 0;
982 		Cflg = mflg = 0;
983 	}
984 
985 	if (!wflg && (Cflg || mflg)) {
986 		char *clptr;
987 		if ((clptr = getenv("COLUMNS")) != NULL)
988 			num_cols = atoi(clptr);
989 #ifdef TERMINFO
990 		else {
991 			if (ioctl(1, TIOCGWINSZ, &win) != -1)
992 				num_cols = (win.ws_col == 0 ? 80 : win.ws_col);
993 		}
994 #endif
995 	}
996 
997 	if (num_cols < 20 || num_cols > 1000)
998 		/* assume it is an error */
999 		num_cols = 80;
1000 
1001 	/* allocate space for flist and the associated	*/
1002 	/* data structures (lbufs)			*/
1003 	maxfils = quantn;
1004 	if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) ||
1005 	    ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) {
1006 		perror("ls");
1007 		exit(2);
1008 	}
1009 	if ((amino = (argc-optind)) == 0) {
1010 					/*
1011 					 * case when no names are given
1012 					 * in ls-command and current
1013 					 * directory is to be used
1014 					 */
1015 		argv[optind] = dotp;
1016 	}
1017 
1018 	for (i = 0; i < (amino ? amino : 1); i++) {
1019 
1020 		/*
1021 		 * If we are recursing, we need to make sure we don't
1022 		 * get into an endless loop.  To keep track of the inodes
1023 		 * (actually, just the directories) visited, we
1024 		 * maintain a directory ancestry list for a file
1025 		 * hierarchy.  As we go deeper into the hierarchy,
1026 		 * a parent directory passes its directory list
1027 		 * info (device id, inode number, and a pointer to
1028 		 * its parent) to each of its children.  As we
1029 		 * process a child that is a directory, we save
1030 		 * its own personal directory list info.  We then
1031 		 * check to see if the child has already been
1032 		 * processed by comparing its device id and inode
1033 		 * number from its own personal directory list info
1034 		 * to that of each of its ancestors.  If there is a
1035 		 * match, then we know we've detected a cycle.
1036 		 */
1037 		if (Rflg) {
1038 			/*
1039 			 * This is the first parent in this lineage
1040 			 * (first in a directory hierarchy), so
1041 			 * this parent's parent doesn't exist.  We
1042 			 * only initialize myinfo when we are
1043 			 * recursing, otherwise it's not used.
1044 			 */
1045 			if ((myinfo = (struct ditem *)malloc(
1046 			    sizeof (struct ditem))) == NULL) {
1047 				perror("ls");
1048 				exit(2);
1049 			} else {
1050 				myinfo->dev = 0;
1051 				myinfo->ino = 0;
1052 				myinfo->parent = NULL;
1053 			}
1054 		}
1055 
1056 		if (Cflg || mflg) {
1057 			width = strcol((unsigned char *)argv[optind]);
1058 			if (width > filewidth)
1059 				filewidth = width;
1060 		}
1061 		if ((ep = gstat((*argv[optind] ? argv[optind] : dotp),
1062 		    1, myinfo)) == NULL) {
1063 			if (nomocore)
1064 				exit(2);
1065 			err = 2;
1066 			optind++;
1067 			continue;
1068 		}
1069 		ep->ln.namep = (*argv[optind] ? argv[optind] : dotp);
1070 		ep->lflags |= ISARG;
1071 		optind++;
1072 		nargs++;	/* count good arguments stored in flist */
1073 		if (acl_err)
1074 			err = 2;
1075 	}
1076 	colwidth = fixedwidth + filewidth;
1077 	if (!Uflg)
1078 		qsort(flist, (unsigned)nargs, sizeof (struct lbuf *),
1079 		    (int (*)(const void *, const void *))compar);
1080 	for (i = 0; i < nargs; i++) {
1081 		if (flist[i]->ltype == 'd' && dflg == 0 || fflg)
1082 			break;
1083 	}
1084 
1085 	if (colorflg)
1086 		ls_color_init();
1087 
1088 	pem(&flist[0], &flist[i], 0);
1089 	for (; i < nargs; i++) {
1090 		pdirectory(flist[i]->ln.namep, Rflg ||
1091 		    (amino > 1), nargs, 0, flist[i]->ancinfo);
1092 		if (nomocore)
1093 			exit(2);
1094 		/* -R: print subdirectories found */
1095 		while (dfirst || cdfirst) {
1096 			/* Place direct subdirs on front in right order */
1097 			while (cdfirst) {
1098 				/* reverse cdfirst onto front of dfirst */
1099 				dtemp = cdfirst;
1100 				cdfirst = cdfirst -> dc_next;
1101 				dtemp -> dc_next = dfirst;
1102 				dfirst = dtemp;
1103 			}
1104 			/* take off first dir on dfirst & print it */
1105 			dtemp = dfirst;
1106 			dfirst = dfirst->dc_next;
1107 			pdirectory(dtemp->dc_name, 1, nargs,
1108 			    dtemp->cycle_detected, dtemp->myancinfo);
1109 			if (nomocore)
1110 				exit(2);
1111 			free(dtemp->dc_name);
1112 			free(dtemp);
1113 		}
1114 	}
1115 
1116 	return (err);
1117 }
1118 
1119 /*
1120  * pdirectory: print the directory name, labelling it if title is
1121  * nonzero, using lp as the place to start reading in the dir.
1122  */
1123 static void
1124 pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo)
1125 {
1126 	struct dchain *dp;
1127 	struct lbuf *ap;
1128 	char *pname;
1129 	int j;
1130 
1131 	filewidth = 0;
1132 	curdir = name;
1133 	if (title) {
1134 		if (!first)
1135 			(void) putc('\n', stdout);
1136 		pprintf(name, ":");
1137 		new_line();
1138 	}
1139 	/*
1140 	 * If there was a cycle detected, then notify and don't report
1141 	 * further.
1142 	 */
1143 	if (cdetect) {
1144 		if (lflg || sflg) {
1145 			curcol += printf(gettext("total %d"), 0);
1146 			new_line();
1147 		}
1148 		(void) fprintf(stderr, gettext(
1149 		    "ls: cycle detected for %s\n"), name);
1150 		return;
1151 	}
1152 
1153 	nfiles = lp;
1154 	rddir(name, myinfo);
1155 	if (nomocore)
1156 		return;
1157 	if (fflg == 0 && Uflg == 0)
1158 		qsort(&flist[lp], (unsigned)(nfiles - lp),
1159 		    sizeof (struct lbuf *),
1160 		    (int (*)(const void *, const void *))compar);
1161 	if (Rflg) {
1162 		for (j = nfiles - 1; j >= lp; j--) {
1163 			ap = flist[j];
1164 			if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") &&
1165 			    strcmp(ap->ln.lname, "..")) {
1166 				dp = malloc(sizeof (struct dchain));
1167 				if (dp == NULL) {
1168 					perror("ls");
1169 					exit(2);
1170 				}
1171 				pname = makename(curdir, ap->ln.lname);
1172 				if ((dp->dc_name = strdup(pname)) == NULL) {
1173 					perror("ls");
1174 					exit(2);
1175 				}
1176 				dp->cycle_detected = ap->cycle;
1177 				dp->myancinfo = ap->ancinfo;
1178 				dp->dc_next = dfirst;
1179 				dfirst = dp;
1180 			}
1181 		}
1182 	}
1183 	if (lflg || sflg) {
1184 		curcol += printf(gettext("total %llu"), tblocks);
1185 		new_line();
1186 	}
1187 	pem(&flist[lp], &flist[nfiles], lflg||sflg);
1188 }
1189 
1190 /*
1191  * pem: print 'em. Print a list of files (e.g. a directory) bounded
1192  * by slp and lp.
1193  */
1194 static void
1195 pem(struct lbuf **slp, struct lbuf **lp, int tot_flag)
1196 {
1197 	long row, nrows, i;
1198 	int col, ncols;
1199 	struct lbuf **ep;
1200 
1201 	if (Cflg || mflg) {
1202 		if (colwidth > num_cols) {
1203 			ncols = 1;
1204 		} else {
1205 			ncols = num_cols / colwidth;
1206 		}
1207 	}
1208 
1209 	if (ncols == 1 || mflg || xflg || !Cflg) {
1210 		for (ep = slp; ep < lp; ep++)
1211 			pentry(*ep);
1212 		new_line();
1213 		return;
1214 	}
1215 	/* otherwise print -C columns */
1216 	if (tot_flag) {
1217 		slp--;
1218 		row = 1;
1219 	}
1220 	else
1221 		row = 0;
1222 
1223 	nrows = (lp - slp - 1) / ncols + 1;
1224 	for (i = 0; i < nrows; i++, row++) {
1225 		for (col = 0; col < ncols; col++) {
1226 			ep = slp + (nrows * col) + row;
1227 			if (ep < lp)
1228 				pentry(*ep);
1229 		}
1230 		new_line();
1231 	}
1232 }
1233 
1234 /*
1235  * print one output entry;
1236  * if uid/gid is not found in the appropriate
1237  * file(passwd/group), then print uid/gid instead of
1238  * user/group name;
1239  */
1240 static void
1241 pentry(struct lbuf *ap)
1242 {
1243 	struct lbuf *p;
1244 	numbuf_t hbuf;
1245 	char buf[BUFSIZ];
1246 	char *dmark = "";	/* Used if -p or -F option active */
1247 	char *cp;
1248 
1249 	p = ap;
1250 	column();
1251 	if (iflg)
1252 		if (mflg && !lflg)
1253 			curcol += printf("%llu ", (long long)p->lnum);
1254 		else
1255 			curcol += printf("%10llu ", (long long)p->lnum);
1256 	if (sflg)
1257 		curcol += printf((mflg && !lflg) ? "%lld " :
1258 		    (p->lblocks < 10000) ? "%4lld " : "%lld ",
1259 		    (p->ltype != 'b' && p->ltype != 'c') ?
1260 		    p->lblocks : 0LL);
1261 	if (lflg) {
1262 		(void) putchar(p->ltype);
1263 		curcol++;
1264 		pmode(p->lflags);
1265 
1266 		/* ACL: additional access mode flag */
1267 		(void) putchar(p->acl);
1268 		curcol++;
1269 
1270 		curcol += printf("%3lu ", (ulong_t)p->lnl);
1271 		if (oflg)
1272 			if (!nflg) {
1273 				cp = getname(p->luid);
1274 				curcol += printf("%-8s ", cp);
1275 			} else
1276 				curcol += printf("%-8lu ", (ulong_t)p->luid);
1277 		if (gflg)
1278 			if (!nflg) {
1279 				cp = getgroup(p->lgid);
1280 				curcol += printf("%-8s ", cp);
1281 			} else
1282 				curcol += printf("%-8lu ", (ulong_t)p->lgid);
1283 		if (p->ltype == 'b' || p->ltype == 'c') {
1284 			curcol += printf("%3u, %2u",
1285 			    (uint_t)major((dev_t)p->lsize),
1286 			    (uint_t)minor((dev_t)p->lsize));
1287 		} else if (hflg && (p->lsize >= hscale)) {
1288 			curcol += printf("%7s",
1289 			    number_to_scaled_string(hbuf, p->lsize, hscale));
1290 		} else {
1291 			uint64_t bsize = p->lsize / block_size;
1292 
1293 			/*
1294 			 * Round up only when using blocks > 1 byte, otherwise
1295 			 * 'normal' sizes display 1 byte too large.
1296 			 */
1297 			if (p->lsize % block_size != 0)
1298 				bsize++;
1299 
1300 			curcol += printf("%7" PRIu64, bsize);
1301 		}
1302 		format_time(p->lmtime.tv_sec, p->lmtime.tv_nsec);
1303 		/* format extended system attribute time */
1304 		if (tmflg && crtm)
1305 			format_attrtime(p);
1306 
1307 		curcol += printf("%s", time_buf);
1308 
1309 	}
1310 	/*
1311 	 * prevent both "->" and trailing marks
1312 	 * from appearing
1313 	 */
1314 
1315 	if (pflg && p->ltype == 'd')
1316 		dmark = "/";
1317 
1318 	if (Fflg && !(lflg && p->flinkto)) {
1319 		if (p->ltype == 'd')
1320 			dmark = "/";
1321 		else if (p->ltype == 'D')
1322 			dmark = ">";
1323 		else if (p->ltype == 'p')
1324 			dmark = "|";
1325 		else if (p->ltype == 'l')
1326 			dmark = "@";
1327 		else if (p->ltype == 's')
1328 			dmark = "=";
1329 		else if (!file_typeflg &&
1330 		    (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)))
1331 			dmark = "*";
1332 		else
1333 			dmark = "";
1334 	}
1335 
1336 	if (lflg && p->flinkto) {
1337 		(void) strncpy(buf, " -> ", 4);
1338 		(void) strcpy(buf + 4, p->flinkto);
1339 		dmark = buf;
1340 	}
1341 
1342 	if (colorflg)
1343 		ls_start_color(p);
1344 
1345 	if (p->lflags & ISARG) {
1346 		if (qflg || bflg)
1347 			pprintf(p->ln.namep, dmark);
1348 		else {
1349 			(void) printf("%s%s", p->ln.namep, dmark);
1350 			curcol += strcol((unsigned char *)p->ln.namep);
1351 			curcol += strcol((unsigned char *)dmark);
1352 		}
1353 	} else {
1354 		if (qflg || bflg)
1355 			pprintf(p->ln.lname, dmark);
1356 		else {
1357 			(void) printf("%s%s", p->ln.lname, dmark);
1358 			curcol += strcol((unsigned char *)p->ln.lname);
1359 			curcol += strcol((unsigned char *)dmark);
1360 		}
1361 	}
1362 
1363 	if (colorflg)
1364 		ls_end_color();
1365 
1366 	/* Display extended system attributes */
1367 	if (saflg) {
1368 		int i;
1369 
1370 		new_line();
1371 		(void) printf("	\t{");
1372 		if (p->exttr != NULL) {
1373 			int k = 0;
1374 			for (i = 0; i < sacnt; i++) {
1375 				if (p->exttr[i].name != NULL)
1376 					k++;
1377 			}
1378 			for (i = 0; i < sacnt; i++) {
1379 				if (p->exttr[i].name != NULL) {
1380 					(void) printf("%s", p->exttr[i].name);
1381 					k--;
1382 					if (vopt && (k != 0))
1383 						(void) printf(",");
1384 				}
1385 			}
1386 		}
1387 		(void) printf("}\n");
1388 	}
1389 	/* Display file timestamps and extended system attribute timestamps */
1390 	if (tmflg && alltm) {
1391 		new_line();
1392 		print_time(p);
1393 		new_line();
1394 	}
1395 	if (vflg) {
1396 		new_line();
1397 		if (p->aclp) {
1398 			acl_printacl(p->aclp, num_cols, Vflg);
1399 		}
1400 	}
1401 	/* Free extended system attribute lists */
1402 	if (saflg || tmflg)
1403 		free_sysattr(p);
1404 }
1405 
1406 /* print various r,w,x permissions */
1407 static void
1408 pmode(mode_t aflag)
1409 {
1410 	/* these arrays are declared static to allow initializations */
1411 	static int	m0[] = { 1, S_IRUSR, 'r', '-' };
1412 	static int	m1[] = { 1, S_IWUSR, 'w', '-' };
1413 	static int	m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR,
1414 	    'x', S_ISUID, 'S', '-' };
1415 	static int	m3[] = { 1, S_IRGRP, 'r', '-' };
1416 	static int	m4[] = { 1, S_IWGRP, 'w', '-' };
1417 	static int	m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP,
1418 				'x', S_ISGID|LS_NOTREG, 'S',
1419 #ifdef XPG4
1420 		S_ISGID, 'L', '-'};
1421 #else
1422 		S_ISGID, 'l', '-'};
1423 #endif
1424 	static int	m6[] = { 1, S_IROTH, 'r', '-' };
1425 	static int	m7[] = { 1, S_IWOTH, 'w', '-' };
1426 	static int	m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH,
1427 	    'x', S_ISVTX, 'T', '-'};
1428 
1429 	static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8};
1430 
1431 	int **mp;
1432 
1433 	flags = aflag;
1434 	for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++)
1435 		selection(*mp);
1436 }
1437 
1438 static void
1439 selection(int *pairp)
1440 {
1441 	int n;
1442 
1443 	n = *pairp++;
1444 	while (n-->0) {
1445 		if ((flags & *pairp) == *pairp) {
1446 			pairp++;
1447 			break;
1448 		} else {
1449 			pairp += 2;
1450 		}
1451 	}
1452 	(void) putchar(*pairp);
1453 	curcol++;
1454 }
1455 
1456 /*
1457  * column: get to the beginning of the next column.
1458  */
1459 static void
1460 column(void)
1461 {
1462 	if (curcol == 0)
1463 		return;
1464 	if (mflg) {
1465 		(void) putc(',', stdout);
1466 		curcol++;
1467 		if (curcol + colwidth + 2 > num_cols) {
1468 			(void) putc('\n', stdout);
1469 			curcol = 0;
1470 			return;
1471 		}
1472 		(void) putc(' ', stdout);
1473 		curcol++;
1474 		return;
1475 	}
1476 	if (Cflg == 0) {
1477 		(void) putc('\n', stdout);
1478 		curcol = 0;
1479 		return;
1480 	}
1481 	if ((curcol / colwidth + 2) * colwidth > num_cols) {
1482 		(void) putc('\n', stdout);
1483 		curcol = 0;
1484 		return;
1485 	}
1486 	do {
1487 		(void) putc(' ', stdout);
1488 		curcol++;
1489 	} while (curcol % colwidth);
1490 }
1491 
1492 static void
1493 new_line(void)
1494 {
1495 	if (curcol) {
1496 		first = 0;
1497 		(void) putc('\n', stdout);
1498 		curcol = 0;
1499 	}
1500 }
1501 
1502 /*
1503  * read each filename in directory dir and store its
1504  * status in flist[nfiles]
1505  * use makename() to form pathname dir/filename;
1506  */
1507 static void
1508 rddir(char *dir, struct ditem *myinfo)
1509 {
1510 	struct dirent *dentry;
1511 	DIR *dirf;
1512 	int j;
1513 	struct lbuf *ep;
1514 	int width;
1515 
1516 	if ((dirf = opendir(dir)) == NULL) {
1517 		(void) fflush(stdout);
1518 		perror(dir);
1519 		err = 2;
1520 		return;
1521 	} else {
1522 		tblocks = 0;
1523 		for (;;) {
1524 			errno = 0;
1525 			if ((dentry = readdir(dirf)) == NULL)
1526 				break;
1527 			if (aflg == 0 && dentry->d_name[0] == '.' &&
1528 			    (Aflg == 0 ||
1529 			    dentry->d_name[1] == '\0' ||
1530 			    dentry->d_name[1] == '.' &&
1531 			    dentry->d_name[2] == '\0'))
1532 				/*
1533 				 * check for directory items '.', '..',
1534 				 *  and items without valid inode-number;
1535 				 */
1536 				continue;
1537 
1538 			/* skip entries ending in ~ if -B was given */
1539 			if (Bflg &&
1540 			    dentry->d_name[strlen(dentry->d_name) - 1] == '~')
1541 				continue;
1542 			if (Cflg || mflg) {
1543 				width = strcol((unsigned char *)dentry->d_name);
1544 				if (width > filewidth)
1545 					filewidth = width;
1546 			}
1547 			ep = gstat(makename(dir, dentry->d_name), 0, myinfo);
1548 			if (ep == NULL) {
1549 				if (nomocore)
1550 					exit(2);
1551 				continue;
1552 			} else {
1553 				ep->lnum = dentry->d_ino;
1554 				for (j = 0; dentry->d_name[j] != '\0'; j++)
1555 					ep->ln.lname[j] = dentry->d_name[j];
1556 				ep->ln.lname[j] = '\0';
1557 			}
1558 		}
1559 		if (errno) {
1560 			int sav_errno = errno;
1561 
1562 			(void) fprintf(stderr,
1563 			    gettext("ls: error reading directory %s: %s\n"),
1564 			    dir, strerror(sav_errno));
1565 		}
1566 		(void) closedir(dirf);
1567 		colwidth = fixedwidth + filewidth;
1568 	}
1569 }
1570 
1571 /*
1572  * Attaching a link to an inode's ancestors.  Search
1573  * through the ancestors to check for cycles (an inode which
1574  * we have already tracked in this inodes ancestry).  If a cycle
1575  * is detected, set the exit code and record the fact so that
1576  * it is reported at the right time when printing the directory.
1577  * In addition, set the exit code.  Note:  If the -a flag was
1578  * specified, we don't want to check for cycles for directories
1579  * ending in '/.' or '/..' unless they were specified on the
1580  * command line.
1581  */
1582 static void
1583 record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep,
1584     int argfl, struct ditem *myparent)
1585 {
1586 	size_t		file_len;
1587 	struct ditem	*myinfo;
1588 	struct ditem	*tptr;
1589 
1590 	file_len = strlen(file);
1591 	if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) &&
1592 	    NOTPARENTDIR(file, file_len))) {
1593 		/*
1594 		 * Add this inode's ancestry
1595 		 * info and insert it into the
1596 		 * ancestry list by pointing
1597 		 * back to its parent.  We save
1598 		 * it (in rep) with the other info
1599 		 * we're gathering for this inode.
1600 		 */
1601 		if ((myinfo = malloc(
1602 		    sizeof (struct ditem))) == NULL) {
1603 			perror("ls");
1604 			exit(2);
1605 		}
1606 		myinfo->dev = pstatb->st_dev;
1607 		myinfo->ino = pstatb->st_ino;
1608 		myinfo->parent = myparent;
1609 		rep->ancinfo = myinfo;
1610 
1611 		/*
1612 		 * If this node has the same device id and
1613 		 * inode number of one of its ancestors,
1614 		 * then we've detected a cycle.
1615 		 */
1616 		if (myparent != NULL) {
1617 			for (tptr = myparent; tptr->parent != NULL;
1618 			    tptr = tptr->parent) {
1619 				if ((tptr->dev == pstatb->st_dev) &&
1620 				    (tptr->ino == pstatb->st_ino)) {
1621 					/*
1622 					 * Cycle detected for this
1623 					 * directory.  Record the fact
1624 					 * it is a cycle so we don't
1625 					 * try to process this
1626 					 * directory as we are
1627 					 * walking through the
1628 					 * list of directories.
1629 					 */
1630 					rep->cycle = 1;
1631 					err = 2;
1632 					break;
1633 				}
1634 			}
1635 		}
1636 	}
1637 }
1638 
1639 /*
1640  * Do re-calculate the mode for group for ACE_T type of acls.
1641  * This is because, if the server's FS happens to be UFS, supporting
1642  * POSIX ACL's, then it does a special calculation of group mode
1643  * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.)
1644  *
1645  * This algorithm is from the NFSv4 ACL Draft. Here a part of that
1646  * algorithm is used for the group mode calculation only.
1647  * What is modified here from the algorithm is that only the
1648  * entries with flags ACE_GROUP are considered. For each entry
1649  * with ACE_GROUP flag, the first occurance of a specific access
1650  * is checked if it is allowed.
1651  * We are not interested in perms for user and other, as they
1652  * were taken from st_mode value.
1653  * We are not interested in a_who field of ACE, as we need just
1654  * unix mode bits for the group.
1655  */
1656 
1657 #define	OWNED_GROUP	(ACE_GROUP | ACE_IDENTIFIER_GROUP)
1658 #define	IS_TYPE_ALLOWED(type)	((type) == ACE_ACCESS_ALLOWED_ACE_TYPE)
1659 
1660 int
1661 grp_mask_to_mode(acl_t *acep)
1662 {
1663 	int mode = 0, seen = 0;
1664 	int acecnt;
1665 	int flags;
1666 	ace_t *ap;
1667 
1668 	acecnt = acl_cnt(acep);
1669 	for (ap = (ace_t *)acl_data(acep); acecnt--; ap++) {
1670 
1671 		if (ap->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE &&
1672 		    ap->a_type != ACE_ACCESS_DENIED_ACE_TYPE)
1673 			continue;
1674 
1675 		if (ap->a_flags & ACE_INHERIT_ONLY_ACE)
1676 			continue;
1677 
1678 		/*
1679 		 * if it is first group@ or first everyone@
1680 		 * for each of read, write and execute, then
1681 		 * that will be the group mode bit.
1682 		 */
1683 		flags = ap->a_flags & ACE_TYPE_FLAGS;
1684 		if (flags == OWNED_GROUP || flags == ACE_EVERYONE) {
1685 			if (ap->a_access_mask & ACE_READ_DATA) {
1686 				if (!(seen & S_IRGRP)) {
1687 					seen |= S_IRGRP;
1688 					if (IS_TYPE_ALLOWED(ap->a_type))
1689 						mode |= S_IRGRP;
1690 				}
1691 			}
1692 			if (ap->a_access_mask & ACE_WRITE_DATA) {
1693 				if (!(seen & S_IWGRP)) {
1694 					seen |= S_IWGRP;
1695 					if (IS_TYPE_ALLOWED(ap->a_type))
1696 						mode |= S_IWGRP;
1697 				}
1698 			}
1699 			if (ap->a_access_mask & ACE_EXECUTE) {
1700 				if (!(seen & S_IXGRP)) {
1701 					seen |= S_IXGRP;
1702 					if (IS_TYPE_ALLOWED(ap->a_type))
1703 						mode |= S_IXGRP;
1704 				}
1705 			}
1706 		}
1707 	}
1708 	return (mode);
1709 }
1710 
1711 /*
1712  * get status of file and recomputes tblocks;
1713  * argfl = 1 if file is a name in ls-command and = 0
1714  * for filename in a directory whose name is an
1715  * argument in the command;
1716  * stores a pointer in flist[nfiles] and
1717  * returns that pointer;
1718  * returns NULL if failed;
1719  */
1720 static struct lbuf *
1721 gstat(char *file, int argfl, struct ditem *myparent)
1722 {
1723 	struct stat statb, statb1;
1724 	struct lbuf *rep;
1725 	char buf[BUFSIZ];
1726 	ssize_t cc;
1727 	int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat;
1728 	int aclcnt;
1729 	int error;
1730 	aclent_t *tp;
1731 	o_mode_t groupperm, mask;
1732 	int grouppermfound, maskfound;
1733 
1734 	if (nomocore)
1735 		return (NULL);
1736 
1737 	if (nfiles >= maxfils) {
1738 		/*
1739 		 * all flist/lbuf pair assigned files, time to get some
1740 		 * more space
1741 		 */
1742 		maxfils += quantn;
1743 		if (((flist = realloc(flist,
1744 		    maxfils * sizeof (struct lbuf *))) == NULL) ||
1745 		    ((nxtlbf = malloc(quantn *
1746 		    sizeof (struct lbuf))) == NULL)) {
1747 			perror("ls");
1748 			nomocore = 1;
1749 			return (NULL);
1750 		}
1751 	}
1752 
1753 	/*
1754 	 * nfiles is reset to nargs for each directory
1755 	 * that is given as an argument maxn is checked
1756 	 * to prevent the assignment of an lbuf to a flist entry
1757 	 * that already has one assigned.
1758 	 */
1759 	if (nfiles >= maxn) {
1760 		rep = nxtlbf++;
1761 		flist[nfiles++] = rep;
1762 		maxn = nfiles;
1763 	} else {
1764 		rep = flist[nfiles++];
1765 	}
1766 
1767 	/* Initialize */
1768 
1769 	rep->lflags = (mode_t)0;
1770 	rep->flinkto = NULL;
1771 	rep->cycle = 0;
1772 	rep->lat.tv_sec = time(NULL);
1773 	rep->lat.tv_nsec = 0;
1774 	rep->lct.tv_sec = time(NULL);
1775 	rep->lct.tv_nsec = 0;
1776 	rep->lmt.tv_sec = time(NULL);
1777 	rep->lmt.tv_nsec = 0;
1778 	rep->exttr = NULL;
1779 	rep->extm = NULL;
1780 
1781 	if (argfl || statreq) {
1782 		int doacl;
1783 
1784 		if (lflg)
1785 			doacl = 1;
1786 		else
1787 			doacl = 0;
1788 
1789 		if ((*statf)(file, &statb) < 0) {
1790 			if (argfl || errno != ENOENT ||
1791 			    (Lflg && lstat(file, &statb) == 0)) {
1792 				/*
1793 				 * Avoid race between readdir and lstat.
1794 				 * Print error message in case of dangling link.
1795 				 */
1796 				perror(file);
1797 				err = 2;
1798 			}
1799 			nfiles--;
1800 			return (NULL);
1801 		}
1802 
1803 		/*
1804 		 * If -H was specified, and the file linked to was
1805 		 * not a directory, then we need to get the info
1806 		 * for the symlink itself.
1807 		 */
1808 		if ((Hflg) && (argfl) &&
1809 		    ((statb.st_mode & S_IFMT) != S_IFDIR)) {
1810 			if (lstat(file, &statb) < 0) {
1811 				perror(file);
1812 				err = 2;
1813 			}
1814 		}
1815 
1816 		rep->lnum = statb.st_ino;
1817 		rep->lsize = statb.st_size;
1818 		rep->lblocks = statb.st_blocks;
1819 		switch (statb.st_mode & S_IFMT) {
1820 		case S_IFDIR:
1821 			rep->ltype = 'd';
1822 			if (Rflg) {
1823 				record_ancestry(file, &statb, rep,
1824 				    argfl, myparent);
1825 			}
1826 			break;
1827 		case S_IFBLK:
1828 			rep->ltype = 'b';
1829 			rep->lsize = (off_t)statb.st_rdev;
1830 			break;
1831 		case S_IFCHR:
1832 			rep->ltype = 'c';
1833 			rep->lsize = (off_t)statb.st_rdev;
1834 			break;
1835 		case S_IFIFO:
1836 			rep->ltype = 'p';
1837 			break;
1838 		case S_IFSOCK:
1839 			rep->ltype = 's';
1840 			rep->lsize = 0;
1841 			break;
1842 		case S_IFLNK:
1843 			/* symbolic links may not have ACLs, so elide acl() */
1844 			if ((Lflg == 0) || (Hflg == 0) ||
1845 			    ((Hflg) && (!argfl))) {
1846 				doacl = 0;
1847 			}
1848 			rep->ltype = 'l';
1849 			if (lflg) {
1850 				cc = readlink(file, buf, BUFSIZ);
1851 				if (cc >= 0) {
1852 
1853 					/*
1854 					 * follow the symbolic link
1855 					 * to generate the appropriate
1856 					 * Fflg marker for the object
1857 					 * eg, /bin -> /sym/bin/
1858 					 */
1859 					if ((Fflg || pflg) &&
1860 					    (stat(file, &statb1) >= 0)) {
1861 						switch (statb1.st_mode &
1862 						    S_IFMT) {
1863 						case S_IFDIR:
1864 							buf[cc++] = '/';
1865 							break;
1866 						case S_IFSOCK:
1867 							buf[cc++] = '=';
1868 							break;
1869 						case S_IFDOOR:
1870 							buf[cc++] = '>';
1871 							break;
1872 						case S_IFIFO:
1873 							buf[cc++] = '|';
1874 							break;
1875 						default:
1876 							if ((statb1.st_mode &
1877 							    ~S_IFMT) &
1878 							    (S_IXUSR|S_IXGRP|
1879 							    S_IXOTH))
1880 								buf[cc++] = '*';
1881 							break;
1882 						}
1883 					}
1884 					buf[cc] = '\0';
1885 					rep->flinkto = strdup(buf);
1886 				}
1887 				break;
1888 			}
1889 
1890 			/*
1891 			 * ls /sym behaves differently from ls /sym/
1892 			 * when /sym is a symbolic link. This is fixed
1893 			 * when explicit arguments are specified.
1894 			 */
1895 
1896 #ifdef XPG6
1897 			/* Do not follow a symlink when -F is specified */
1898 			if ((!argfl) || (argfl && Fflg) ||
1899 			    (stat(file, &statb1) < 0))
1900 #else
1901 			/* Follow a symlink when -F is specified */
1902 			if (!argfl || stat(file, &statb1) < 0)
1903 #endif /* XPG6 */
1904 				break;
1905 			if ((statb1.st_mode & S_IFMT) == S_IFDIR) {
1906 				statb = statb1;
1907 				rep->ltype = 'd';
1908 				rep->lsize = statb1.st_size;
1909 				if (Rflg) {
1910 					record_ancestry(file, &statb, rep,
1911 					    argfl, myparent);
1912 				}
1913 			}
1914 			break;
1915 		case S_IFDOOR:
1916 			rep->ltype = 'D';
1917 			break;
1918 		case S_IFREG:
1919 			rep->ltype = '-';
1920 			break;
1921 		case S_IFPORT:
1922 			rep->ltype = 'P';
1923 			break;
1924 		default:
1925 			rep->ltype = '?';
1926 			break;
1927 		}
1928 		rep->lflags = statb.st_mode & ~S_IFMT;
1929 
1930 		if (!S_ISREG(statb.st_mode))
1931 			rep->lflags |= LS_NOTREG;
1932 
1933 		rep->luid = statb.st_uid;
1934 		rep->lgid = statb.st_gid;
1935 		rep->lnl = statb.st_nlink;
1936 		if (uflg || (tmflg && atm))
1937 			rep->lmtime = statb.st_atim;
1938 		else if (cflg || (tmflg && ctm))
1939 			rep->lmtime = statb.st_ctim;
1940 		else
1941 			rep->lmtime = statb.st_mtim;
1942 		rep->lat = statb.st_atim;
1943 		rep->lct = statb.st_ctim;
1944 		rep->lmt = statb.st_mtim;
1945 
1946 		/* ACL: check acl entries count */
1947 		if (doacl) {
1948 
1949 			error = acl_get(file, 0, &rep->aclp);
1950 			if (error) {
1951 				(void) fprintf(stderr,
1952 				    gettext("ls: can't read ACL on %s: %s\n"),
1953 				    file, acl_strerror(error));
1954 				rep->acl = ' ';
1955 				acl_err++;
1956 				return (rep);
1957 			}
1958 
1959 			rep->acl = ' ';
1960 
1961 			if (rep->aclp &&
1962 			    ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) {
1963 				rep->acl = '+';
1964 				/*
1965 				 * Special handling for ufs aka aclent_t ACL's
1966 				 */
1967 				if (acl_type(rep->aclp) == ACLENT_T) {
1968 					/*
1969 					 * For files with non-trivial acls, the
1970 					 * effective group permissions are the
1971 					 * intersection of the GROUP_OBJ value
1972 					 * and the CLASS_OBJ (acl mask) value.
1973 					 * Determine both the GROUP_OBJ and
1974 					 * CLASS_OBJ for this file and insert
1975 					 * the logical AND of those two values
1976 					 * in the group permissions field
1977 					 * of the lflags value for this file.
1978 					 */
1979 
1980 					/*
1981 					 * Until found in acl list, assume
1982 					 * maximum permissions for both group
1983 					 * a nd mask.  (Just in case the acl
1984 					 * lacks either value for some reason.)
1985 					 */
1986 					groupperm = 07;
1987 					mask = 07;
1988 					grouppermfound = 0;
1989 					maskfound = 0;
1990 					aclcnt = acl_cnt(rep->aclp);
1991 					for (tp =
1992 					    (aclent_t *)acl_data(rep->aclp);
1993 					    aclcnt--; tp++) {
1994 						if (tp->a_type == GROUP_OBJ) {
1995 							groupperm = tp->a_perm;
1996 							grouppermfound = 1;
1997 							continue;
1998 						}
1999 						if (tp->a_type == CLASS_OBJ) {
2000 							mask = tp->a_perm;
2001 							maskfound = 1;
2002 						}
2003 						if (grouppermfound && maskfound)
2004 							break;
2005 					}
2006 
2007 
2008 					/* reset all the group bits */
2009 					rep->lflags &= ~S_IRWXG;
2010 
2011 					/*
2012 					 * Now set them to the logical AND of
2013 					 * the GROUP_OBJ permissions and the
2014 					 * acl mask.
2015 					 */
2016 
2017 					rep->lflags |= (groupperm & mask) << 3;
2018 
2019 				} else if (acl_type(rep->aclp) == ACE_T) {
2020 					int mode;
2021 					mode = grp_mask_to_mode(rep->aclp);
2022 					rep->lflags &= ~S_IRWXG;
2023 					rep->lflags |= mode;
2024 				}
2025 			}
2026 
2027 			if (!vflg && !Vflg && rep->aclp) {
2028 				acl_free(rep->aclp);
2029 				rep->aclp = NULL;
2030 			}
2031 
2032 			if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1)
2033 				rep->acl = '@';
2034 
2035 		} else
2036 			rep->acl = ' ';
2037 
2038 		/* mask ISARG and other file-type bits */
2039 
2040 		if (rep->ltype != 'b' && rep->ltype != 'c')
2041 			tblocks += rep->lblocks;
2042 
2043 		/* Get extended system attributes */
2044 
2045 		if ((saflg || (tmflg && crtm) || (tmflg && alltm)) &&
2046 		    (sysattr_support(file, _PC_SATTR_EXISTS) == 1)) {
2047 			int i;
2048 
2049 			sacnt = attr_count();
2050 			/*
2051 			 * Allocate 'sacnt' size array to hold extended
2052 			 * system attribute name (verbose) or respective
2053 			 * symbol represenation (compact).
2054 			 */
2055 			rep->exttr = xmalloc(sacnt * sizeof (struct attrb),
2056 			    rep);
2057 
2058 			/* initialize boolean attribute list */
2059 			for (i = 0; i < sacnt; i++)
2060 				rep->exttr[i].name = NULL;
2061 			if (get_sysxattr(file, rep) != 0) {
2062 				(void) fprintf(stderr,
2063 				    gettext("ls:Failed to retrieve "
2064 				    "extended system attribute from "
2065 				    "%s\n"), file);
2066 				rep->exttr[0].name = xmalloc(2, rep);
2067 				(void) strlcpy(rep->exttr[0].name, "?", 2);
2068 			}
2069 		}
2070 	}
2071 	return (rep);
2072 }
2073 
2074 /*
2075  * returns pathname of the form dir/file;
2076  * dir and file are null-terminated strings.
2077  */
2078 static char *
2079 makename(char *dir, char *file)
2080 {
2081 	/*
2082 	 * PATH_MAX is the maximum length of a path name.
2083 	 * MAXNAMLEN is the maximum length of any path name component.
2084 	 * Allocate space for both, plus the '/' in the middle
2085 	 * and the null character at the end.
2086 	 * dfile is static as this is returned by makename().
2087 	 */
2088 	static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1];
2089 	char *dp, *fp;
2090 
2091 	dp = dfile;
2092 	fp = dir;
2093 	while (*fp)
2094 		*dp++ = *fp++;
2095 	if (dp > dfile && *(dp - 1) != '/')
2096 		*dp++ = '/';
2097 	fp = file;
2098 	while (*fp)
2099 		*dp++ = *fp++;
2100 	*dp = '\0';
2101 	return (dfile);
2102 }
2103 
2104 
2105 #include <pwd.h>
2106 #include <grp.h>
2107 #include <utmpx.h>
2108 
2109 struct	utmpx utmp;
2110 
2111 #define	NMAX	(sizeof (utmp.ut_name))
2112 #define	SCPYN(a, b)	(void) strncpy(a, b, NMAX)
2113 
2114 
2115 struct cachenode {		/* this struct must be zeroed before using */
2116 	struct cachenode *lesschild;	/* subtree whose entries < val */
2117 	struct cachenode *grtrchild;	/* subtree whose entries > val */
2118 	long val;			/* the uid or gid of this entry */
2119 	int initted;			/* name has been filled in */
2120 	char name[NMAX+1];		/* the string that val maps to */
2121 };
2122 static struct cachenode *names, *groups;
2123 
2124 static struct cachenode *
2125 findincache(struct cachenode **head, long val)
2126 {
2127 	struct cachenode **parent = head;
2128 	struct cachenode *c = *parent;
2129 
2130 	while (c != NULL) {
2131 		if (val == c->val) {
2132 			/* found it */
2133 			return (c);
2134 		} else if (val < c->val) {
2135 			parent = &c->lesschild;
2136 			c = c->lesschild;
2137 		} else {
2138 			parent = &c->grtrchild;
2139 			c = c->grtrchild;
2140 		}
2141 	}
2142 
2143 	/* not in the cache, make a new entry for it */
2144 	c = calloc(1, sizeof (struct cachenode));
2145 	if (c == NULL) {
2146 		perror("ls");
2147 		exit(2);
2148 	}
2149 	*parent = c;
2150 	c->val = val;
2151 	return (c);
2152 }
2153 
2154 /*
2155  * get name from cache, or passwd file for a given uid;
2156  * lastuid is set to uid.
2157  */
2158 static char *
2159 getname(uid_t uid)
2160 {
2161 	struct passwd *pwent;
2162 	struct cachenode *c;
2163 
2164 	if ((uid == lastuid) && lastuname)
2165 		return (lastuname);
2166 
2167 	c = findincache(&names, uid);
2168 	if (c->initted == 0) {
2169 		if ((pwent = getpwuid(uid)) != NULL) {
2170 			SCPYN(&c->name[0], pwent->pw_name);
2171 		} else {
2172 			(void) sprintf(&c->name[0], "%-8u", (int)uid);
2173 		}
2174 		c->initted = 1;
2175 	}
2176 	lastuid = uid;
2177 	lastuname = &c->name[0];
2178 	return (lastuname);
2179 }
2180 
2181 /*
2182  * get name from cache, or group file for a given gid;
2183  * lastgid is set to gid.
2184  */
2185 static char *
2186 getgroup(gid_t gid)
2187 {
2188 	struct group *grent;
2189 	struct cachenode *c;
2190 
2191 	if ((gid == lastgid) && lastgname)
2192 		return (lastgname);
2193 
2194 	c = findincache(&groups, gid);
2195 	if (c->initted == 0) {
2196 		if ((grent = getgrgid(gid)) != NULL) {
2197 			SCPYN(&c->name[0], grent->gr_name);
2198 		} else {
2199 			(void) sprintf(&c->name[0], "%-8u", (int)gid);
2200 		}
2201 		c->initted = 1;
2202 	}
2203 	lastgid = gid;
2204 	lastgname = &c->name[0];
2205 	return (lastgname);
2206 }
2207 
2208 /* return >0 if item pointed by pp2 should appear first */
2209 static int
2210 compar(struct lbuf **pp1, struct lbuf **pp2)
2211 {
2212 	struct lbuf *p1, *p2;
2213 
2214 	p1 = *pp1;
2215 	p2 = *pp2;
2216 	if (dflg == 0) {
2217 /*
2218  * compare two names in ls-command one of which is file
2219  * and the other is a directory;
2220  * this portion is not used for comparing files within
2221  * a directory name of ls-command;
2222  */
2223 		if (p1->lflags&ISARG && p1->ltype == 'd') {
2224 			if (!(p2->lflags&ISARG && p2->ltype == 'd'))
2225 				return (1);
2226 		} else {
2227 			if (p2->lflags&ISARG && p2->ltype == 'd')
2228 				return (-1);
2229 		}
2230 	}
2231 	if (tflg) {
2232 		if (p2->lmtime.tv_sec > p1->lmtime.tv_sec)
2233 			return (rflg);
2234 		else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec)
2235 			return (-rflg);
2236 		/* times are equal to the sec, check nsec */
2237 		if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec)
2238 			return (rflg);
2239 		else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec)
2240 			return (-rflg);
2241 		/* if times are equal, fall through and sort by name */
2242 	} else if (Sflg) {
2243 		/*
2244 		 * The size stored in lsize can be either the
2245 		 * size or the major minor number (in the case of
2246 		 * block and character special devices).  If it's
2247 		 * a major minor number, then the size is considered
2248 		 * to be zero and we want to fall through and sort
2249 		 * by name.  In addition, if the size of p2 is equal
2250 		 * to the size of p1 we want to fall through and
2251 		 * sort by name.
2252 		 */
2253 		off_t	p1size = (p1->ltype == 'b') ||
2254 		    (p1->ltype == 'c') ? 0 : p1->lsize;
2255 		off_t	p2size = (p2->ltype == 'b') ||
2256 		    (p2->ltype == 'c') ? 0 : p2->lsize;
2257 		if (p2size > p1size) {
2258 			return (rflg);
2259 		} else if (p2size < p1size) {
2260 			return (-rflg);
2261 		}
2262 		/* Sizes are equal, fall through and sort by name. */
2263 	}
2264 	return (rflg * strcoll(
2265 	    p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname,
2266 	    p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname));
2267 }
2268 
2269 static void
2270 pprintf(char *s1, char *s2)
2271 {
2272 	csi_pprintf((unsigned char *)s1);
2273 	csi_pprintf((unsigned char *)s2);
2274 }
2275 
2276 static void
2277 csi_pprintf(unsigned char *s)
2278 {
2279 	unsigned char *cp;
2280 	char	c;
2281 	int	i;
2282 	int	c_len;
2283 	int	p_col;
2284 	wchar_t	pcode;
2285 
2286 	if (!qflg && !bflg) {
2287 		for (cp = s; *cp != '\0'; cp++) {
2288 			(void) putchar(*cp);
2289 			curcol++;
2290 		}
2291 		return;
2292 	}
2293 
2294 	for (cp = s; *cp; ) {
2295 		if (isascii(c = *cp)) {
2296 			if (!isprint(c)) {
2297 				if (qflg) {
2298 					c = '?';
2299 				} else {
2300 					curcol += 3;
2301 					(void) putc('\\', stdout);
2302 					c = '0' + ((*cp >> 6) & 07);
2303 					(void) putc(c, stdout);
2304 					c = '0' + ((*cp >> 3) & 07);
2305 					(void) putc(c, stdout);
2306 					c = '0' + (*cp & 07);
2307 				}
2308 			}
2309 			curcol++;
2310 			cp++;
2311 			(void) putc(c, stdout);
2312 			continue;
2313 		}
2314 
2315 		if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) {
2316 			c_len = 1;
2317 			goto not_print;
2318 		}
2319 
2320 		if ((p_col = wcwidth(pcode)) > 0) {
2321 			(void) putwchar(pcode);
2322 			cp += c_len;
2323 			curcol += p_col;
2324 			continue;
2325 		}
2326 
2327 not_print:
2328 		for (i = 0; i < c_len; i++) {
2329 			if (qflg) {
2330 				c = '?';
2331 			} else {
2332 				curcol += 3;
2333 				(void) putc('\\', stdout);
2334 				c = '0' + ((*cp >> 6) & 07);
2335 				(void) putc(c, stdout);
2336 				c = '0' + ((*cp >> 3) & 07);
2337 				(void) putc(c, stdout);
2338 				c = '0' + (*cp & 07);
2339 			}
2340 			curcol++;
2341 			(void) putc(c, stdout);
2342 			cp++;
2343 		}
2344 	}
2345 }
2346 
2347 static int
2348 strcol(unsigned char *s1)
2349 {
2350 	int	w;
2351 	int	w_col;
2352 	int	len;
2353 	wchar_t	wc;
2354 
2355 	w = 0;
2356 	while (*s1) {
2357 		if (isascii(*s1)) {
2358 			w++;
2359 			s1++;
2360 			continue;
2361 		}
2362 
2363 		if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
2364 			w++;
2365 			s1++;
2366 			continue;
2367 		}
2368 
2369 		if ((w_col = wcwidth(wc)) < 0)
2370 			w_col = len;
2371 		s1 += len;
2372 		w += w_col;
2373 	}
2374 	return (w);
2375 }
2376 
2377 /*
2378  * Convert an unsigned long long to a string representation and place the
2379  * result in the caller-supplied buffer.
2380  *
2381  * The number provided is a size in bytes.  The number is first
2382  * converted to an integral multiple of 'scale' bytes.  This new
2383  * number is then scaled down until it is small enough to be in a good
2384  * human readable format, i.e.  in the range 0 thru scale-1.  If the
2385  * number used to derive the final number is not a multiple of scale, and
2386  * the final number has only a single significant digit, we compute
2387  * tenths of units to provide a second significant digit.
2388  *
2389  * The value "(unsigned long long)-1" is a special case and is always
2390  * converted to "-1".
2391  *
2392  * A pointer to the caller-supplied buffer is returned.
2393  */
2394 static char *
2395 number_to_scaled_string(
2396 			numbuf_t buf,		/* put the result here */
2397 			unsigned long long number, /* convert this number */
2398 			long scale)
2399 {
2400 	unsigned long long save;
2401 	/* Measurement: kilo, mega, giga, tera, peta, exa */
2402 	char *uom = "KMGTPE";
2403 
2404 	if ((long long)number == (long long)-1) {
2405 		(void) strlcpy(buf, "-1", sizeof (numbuf_t));
2406 		return (buf);
2407 	}
2408 
2409 	save = number;
2410 	number = number / scale;
2411 
2412 	/*
2413 	 * Now we have number as a count of scale units.
2414 	 * If no further scaling is necessary, we round up as appropriate.
2415 	 *
2416 	 * The largest value number could have had entering the routine is
2417 	 * 16 Exabytes, so running off the end of the uom array should
2418 	 * never happen.  We check for that, though, as a guard against
2419 	 * a breakdown elsewhere in the algorithm.
2420 	 */
2421 	if (number < (unsigned long long)scale) {
2422 		if ((save % scale) >= (unsigned long long)(scale / 2)) {
2423 			if (++number == (unsigned long long)scale) {
2424 				uom++;
2425 				number = 1;
2426 			}
2427 		}
2428 	} else {
2429 		while ((number >= (unsigned long long)scale) && (*uom != 'E')) {
2430 			uom++; /* next unit of measurement */
2431 			save = number;
2432 			/*
2433 			 * If we're over half way to the next unit of
2434 			 * 'scale' bytes (which means we should round
2435 			 * up), then adding half of 'scale' prior to
2436 			 * the division will push us into that next
2437 			 * unit of scale when we perform the division
2438 			 */
2439 			number = (number + (scale / 2)) / scale;
2440 		}
2441 	}
2442 
2443 	/* check if we should output a decimal place after the point */
2444 	if ((save / scale) < 10) {
2445 		/* snprintf() will round for us */
2446 		float fnum = (float)save / scale;
2447 		(void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c",
2448 		    fnum, *uom);
2449 	} else {
2450 		(void) snprintf(buf, sizeof (numbuf_t), "%4llu%c",
2451 		    number, *uom);
2452 	}
2453 	return (buf);
2454 }
2455 
2456 /* Get extended system attributes and set the display */
2457 
2458 int
2459 get_sysxattr(char *fname, struct lbuf *rep)
2460 {
2461 	boolean_t	value;
2462 	data_type_t	type;
2463 	int		error;
2464 	char		*name;
2465 	int		i;
2466 
2467 	if ((error = getattrat(AT_FDCWD, XATTR_VIEW_READWRITE, fname,
2468 	    &response)) != 0) {
2469 		perror("ls:getattrat");
2470 		return (error);
2471 	}
2472 
2473 	/*
2474 	 * Allocate 'sacnt' size array to hold extended timestamp
2475 	 * system attributes and initialize the array.
2476 	 */
2477 	rep->extm = xmalloc(sacnt * sizeof (struct attrtm), rep);
2478 	for (i = 0; i < sacnt; i++) {
2479 		rep->extm[i].stm = 0;
2480 		rep->extm[i].nstm = 0;
2481 		rep->extm[i].name = NULL;
2482 	}
2483 	while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
2484 		name = nvpair_name(pair);
2485 		type = nvpair_type(pair);
2486 		if (type == DATA_TYPE_BOOLEAN_VALUE) {
2487 			error = nvpair_value_boolean_value(pair, &value);
2488 			if (error) {
2489 				(void) fprintf(stderr,
2490 				    gettext("nvpair_value_boolean_value "
2491 				    "failed: error = %d\n"), error);
2492 				continue;
2493 			}
2494 			if (name != NULL)
2495 				set_sysattrb_display(name, value, rep);
2496 			continue;
2497 		} else if (type == DATA_TYPE_UINT64_ARRAY) {
2498 			if (name != NULL)
2499 				set_sysattrtm_display(name, rep);
2500 			continue;
2501 		}
2502 	}
2503 	nvlist_free(response);
2504 	return (0);
2505 }
2506 
2507 /* Set extended system attribute boolean display */
2508 
2509 void
2510 set_sysattrb_display(char *name, boolean_t val, struct lbuf *rep)
2511 {
2512 	f_attr_t	fattr;
2513 	const char	*opt;
2514 	size_t		len;
2515 
2516 	fattr = name_to_attr(name);
2517 	if (fattr != F_ATTR_INVAL && fattr < sacnt) {
2518 		if (vopt) {
2519 			len = strlen(name);
2520 			if (val) {
2521 				rep->exttr[fattr].name = xmalloc(len + 1, rep);
2522 				(void) strlcpy(rep->exttr[fattr].name, name,
2523 				    len + 1);
2524 			} else {
2525 				rep->exttr[fattr].name = xmalloc(len + 3, rep);
2526 				(void) snprintf(rep->exttr[fattr].name, len + 3,
2527 				    "no%s", name);
2528 			}
2529 		} else {
2530 			opt = attr_to_option(fattr);
2531 			if (opt != NULL) {
2532 				len = strlen(opt);
2533 				rep->exttr[fattr].name = xmalloc(len + 1, rep);
2534 				if (val)
2535 					(void) strlcpy(rep->exttr[fattr].name,
2536 					    opt, len + 1);
2537 				else
2538 					(void) strlcpy(rep->exttr[fattr].name,
2539 					    "-", len + 1);
2540 			}
2541 		}
2542 	}
2543 }
2544 
2545 /* Set extended system attribute timestamp display */
2546 
2547 void
2548 set_sysattrtm_display(char *name, struct lbuf *rep)
2549 {
2550 	uint_t		nelem;
2551 	uint64_t	*value;
2552 	int		i;
2553 	size_t		len;
2554 
2555 	if (nvpair_value_uint64_array(pair, &value, &nelem) == 0) {
2556 		if (*value != NULL) {
2557 			len = strlen(name);
2558 			i = 0;
2559 			while (rep->extm[i].stm != 0 && i < sacnt)
2560 				i++;
2561 			rep->extm[i].stm = value[0];
2562 			rep->extm[i].nstm = value[1];
2563 			rep->extm[i].name = xmalloc(len + 1, rep);
2564 			(void) strlcpy(rep->extm[i].name, name, len + 1);
2565 		}
2566 	}
2567 }
2568 
2569 void
2570 format_time(time_t sec, time_t nsec)
2571 {
2572 	const char *fstr = time_fmt_new;
2573 	char fmt_buf[FMTSIZE];
2574 
2575 	if (Eflg) {
2576 		(void) snprintf(fmt_buf, FMTSIZE, fstr, nsec);
2577 		(void) strftime(time_buf, sizeof (time_buf), fmt_buf,
2578 		    localtime(&sec));
2579 		return;
2580 	}
2581 
2582 	if (sec < year || sec > now)
2583 		fstr = time_fmt_old;
2584 
2585 	/* if a custom time was specified, shouldn't be localized */
2586 	(void) strftime(time_buf, sizeof (time_buf),
2587 	    (time_custom == 0) ? dcgettext(NULL, fstr, LC_TIME) : fstr,
2588 	    localtime(&sec));
2589 }
2590 
2591 void
2592 format_attrtime(struct lbuf *p)
2593 {
2594 	int tmattr = 0;
2595 	int i;
2596 
2597 	if (p->extm != NULL) {
2598 		for (i = 0; i < sacnt; i++) {
2599 			if (p->extm[i].name != NULL) {
2600 				tmattr = 1;
2601 				break;
2602 			}
2603 		}
2604 	}
2605 
2606 	if (tmattr) {
2607 		const char *old_save = time_fmt_old;
2608 		const char *new_save = time_fmt_new;
2609 
2610 		/* Eflg always sets format to FORMAT_ISO_FULL */
2611 		if (!Eflg && !time_custom) {
2612 			time_fmt_old = FORMAT_OLD;
2613 			time_fmt_new = FORMAT_NEW;
2614 		}
2615 
2616 		format_time((time_t)p->extm[i].stm, (time_t)p->extm[i].nstm);
2617 
2618 		time_fmt_old = old_save;
2619 		time_fmt_new = new_save;
2620 	}
2621 }
2622 
2623 void
2624 print_time(struct lbuf *p)
2625 {
2626 	const char *old_save = time_fmt_old;
2627 	const char *new_save = time_fmt_new;
2628 
2629 	int i = 0;
2630 
2631 	if (!Eflg) {
2632 		time_fmt_old = FORMAT_LONG;
2633 		time_fmt_new = FORMAT_LONG;
2634 	}
2635 
2636 	new_line();
2637 	format_time(p->lat.tv_sec, p->lat.tv_nsec);
2638 	(void) printf("         timestamp: atime        %s\n", time_buf);
2639 	format_time(p->lct.tv_sec, p->lct.tv_nsec);
2640 	(void) printf("         timestamp: ctime        %s\n", time_buf);
2641 	format_time(p->lmt.tv_sec, p->lmt.tv_nsec);
2642 	(void) printf("         timestamp: mtime        %s\n", time_buf);
2643 	if (p->extm != NULL) {
2644 		while (p->extm[i].nstm != 0 && i < sacnt) {
2645 			format_time(p->extm[i].stm, p->extm[i].nstm);
2646 			if (p->extm[i].name != NULL) {
2647 				(void) printf("         timestamp:"
2648 				    " %s        %s\n",
2649 				    p->extm[i].name, time_buf);
2650 			}
2651 			i++;
2652 		}
2653 	}
2654 
2655 	time_fmt_old = old_save;
2656 	time_fmt_new = new_save;
2657 }
2658 
2659 /*
2660  * Check if color definition applies to entry, returns 1 if yes, 0 if no
2661  */
2662 static int
2663 color_match(struct lbuf *entry, ls_color_t *color)
2664 {
2665 	switch (color->ftype) {
2666 	case LS_PAT:
2667 	{
2668 		char	*fname;
2669 		size_t	fname_len, sfx_len;
2670 
2671 		if (entry->lflags & ISARG)
2672 			fname = entry->ln.namep;
2673 		else
2674 			fname = entry->ln.lname;
2675 
2676 		fname_len = strlen(fname);
2677 		sfx_len = strlen(color->sfx);
2678 		if (sfx_len > fname_len)
2679 			return (0);
2680 
2681 		if (strcmp(color->sfx, fname + fname_len - sfx_len) == 0)
2682 			return (1);
2683 		else
2684 			return (0);
2685 	}
2686 
2687 	case LS_NORMAL:
2688 		return (1);
2689 
2690 	case LS_FILE:
2691 		return ((entry->ltype == '-'));
2692 
2693 	case LS_DIR:
2694 		return ((entry->ltype == 'd'));
2695 
2696 	case LS_LINK:
2697 		return ((entry->ltype == 'l'));
2698 
2699 	case LS_FIFO:
2700 		return ((entry->ltype == 'p'));
2701 
2702 	case LS_SOCK:
2703 		return ((entry->ltype == 's'));
2704 
2705 	case LS_DOOR:
2706 		return ((entry->ltype == 'D'));
2707 
2708 	case LS_BLK:
2709 		return ((entry->ltype == 'b'));
2710 
2711 	case LS_CHR:
2712 		return ((entry->ltype == 'c'));
2713 
2714 	case LS_PORT:
2715 		return ((entry->ltype == 'P'));
2716 
2717 	case LS_ORPHAN:
2718 	{
2719 		struct stat st;
2720 		int rc;
2721 
2722 		if (entry->ltype != 'l')
2723 			return (0);
2724 		if (entry->flinkto == NULL)
2725 			return (1);
2726 
2727 		if (entry->lflags & ISARG)
2728 			rc = stat(entry->ln.namep, &st);
2729 		else
2730 			rc = stat(entry->ln.lname, &st);
2731 
2732 		if (rc == -1 && errno == ENOENT)
2733 			return (1);
2734 
2735 		return (0);
2736 	}
2737 
2738 	case LS_SETUID:
2739 		return (entry->ltype != 'l' && (entry->lflags & (S_ISUID)));
2740 
2741 	case LS_SETGID:
2742 		return (entry->ltype != 'l' && (entry->lflags & (S_ISGID)));
2743 
2744 	case LS_STICKY_OTHER_WRITABLE:
2745 		return (entry->ltype != 'l' &&
2746 		    (entry->lflags & (S_IWOTH|S_ISVTX)));
2747 
2748 	case LS_OTHER_WRITABLE:
2749 		return (entry->ltype != 'l' && (entry->lflags & (S_IWOTH)));
2750 
2751 	case LS_STICKY:
2752 		return (entry->ltype != 'l' && (entry->lflags & (S_ISVTX)));
2753 
2754 	case LS_EXEC:
2755 		return (entry->ltype != 'l' &&
2756 		    (entry->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)));
2757 	}
2758 
2759 	return (0);
2760 }
2761 
2762 static void
2763 dump_color(ls_color_t *c)
2764 {
2765 	if (c == NULL)
2766 		return;
2767 
2768 	(void) printf("\n\ttype: ");
2769 	switch (c->ftype) {
2770 	case LS_NORMAL:
2771 		(void) printf("LS_NORMAL");
2772 		break;
2773 	case LS_FILE:
2774 		(void) printf("LS_FILE");
2775 		break;
2776 	case LS_EXEC:
2777 		(void) printf("LS_EXEC");
2778 		break;
2779 	case LS_DIR:
2780 		(void) printf("LS_DIR");
2781 		break;
2782 	case LS_LINK:
2783 		(void) printf("LS_LINK");
2784 		break;
2785 
2786 	case LS_FIFO:
2787 		(void) printf("LS_FIFO");
2788 		break;
2789 
2790 	case LS_SOCK:
2791 		(void) printf("LS_SOCK");
2792 		break;
2793 
2794 	case LS_DOOR:
2795 		(void) printf("LS_DOOR");
2796 		break;
2797 
2798 	case LS_BLK:
2799 		(void) printf("LS_BLK");
2800 		break;
2801 
2802 	case LS_CHR:
2803 		(void) printf("LS_CHR");
2804 		break;
2805 
2806 	case LS_PORT:
2807 		(void) printf("LS_PORT");
2808 		break;
2809 
2810 	case LS_STICKY:
2811 		(void) printf("LS_STICKY");
2812 		break;
2813 
2814 	case LS_ORPHAN:
2815 		(void) printf("LS_ORPHAN");
2816 		break;
2817 
2818 	case LS_SETGID:
2819 		(void) printf("LS_SETGID");
2820 		break;
2821 
2822 	case LS_SETUID:
2823 		(void) printf("LS_SETUID");
2824 		break;
2825 
2826 	case LS_OTHER_WRITABLE:
2827 		(void) printf("LS_OTHER_WRITABLE");
2828 		break;
2829 
2830 	case LS_STICKY_OTHER_WRITABLE:
2831 		(void) printf("LS_STICKY_OTHER_WRITABLE");
2832 		break;
2833 
2834 	case LS_PAT:
2835 		(void) printf("LS_PAT\n");
2836 		(void) printf("\tpattern: %s", c->sfx);
2837 		break;
2838 	}
2839 	(void) printf("\n");
2840 	(void) printf("\tattr: %d\n", c->attr);
2841 	(void) printf("\tfg: %d\n", c->fg);
2842 	(void) printf("\tbg: %d\n", c->bg);
2843 	(void) printf("\t");
2844 }
2845 
2846 static ls_color_t *
2847 get_color_attr(struct lbuf *l)
2848 {
2849 	int i;
2850 
2851 	/*
2852 	 * Colors are sorted from most general lsc_colors[0] to most specific
2853 	 * lsc_colors[lsc_ncolors - 1] by ls_color_init().  Start search with
2854 	 * most specific color rule and work towards most general.
2855 	 */
2856 	for (i = lsc_ncolors - 1; i >= 0; --i)
2857 		if (color_match(l, &lsc_colors[i]))
2858 			return (&lsc_colors[i]);
2859 
2860 	return (NULL);
2861 }
2862 
2863 static void
2864 ls_tprint(char *str, long int p1, long int p2, long int p3, long int p4,
2865     long int p5, long int p6, long int p7, long int p8, long int p9)
2866 {
2867 	char *s;
2868 
2869 	if (str == NULL)
2870 		return;
2871 
2872 	s = tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
2873 
2874 	if (s != NULL)
2875 		(void) putp(s);
2876 }
2877 
2878 static void
2879 ls_start_color(struct lbuf *l)
2880 {
2881 	ls_color_t *c = get_color_attr(l);
2882 
2883 	if (c == NULL)
2884 		return;
2885 
2886 	if (lsc_debug)
2887 		lsc_match = c;
2888 
2889 	if (c->attr & LSA_BOLD)
2890 		ls_tprint(lsc_bold, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2891 	if (c->attr & LSA_UNDERSCORE)
2892 		ls_tprint(lsc_underline, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2893 	if (c->attr & LSA_BLINK)
2894 		ls_tprint(lsc_blink, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2895 	if (c->attr & LSA_REVERSE)
2896 		ls_tprint(lsc_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2897 	if (c->attr & LSA_CONCEALED)
2898 		ls_tprint(lsc_concealed, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2899 	if (c->attr == LSA_NONE)
2900 		ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2901 
2902 	if (c->fg != -1)
2903 		ls_tprint(lsc_setfg, c->fg, 0, 0, 0, 0, 0, 0, 0, 0);
2904 	if (c->bg != -1)
2905 		ls_tprint(lsc_setbg, c->bg, 0, 0, 0, 0, 0, 0, 0, 0);
2906 }
2907 
2908 static void
2909 ls_end_color()
2910 {
2911 	ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2912 	if (lsc_debug)
2913 		dump_color(lsc_match);
2914 }
2915 
2916 static void
2917 new_color_entry(char *colorstr)
2918 {
2919 	static const struct {
2920 		const char	*s;
2921 		ls_cftype_t	stype;
2922 	} type_map[] = {
2923 		{ "no", LS_NORMAL },
2924 		{ "fi", LS_FILE },
2925 		{ "di", LS_DIR },
2926 		{ "ln", LS_LINK },
2927 		{ "pi", LS_FIFO },
2928 		{ "so", LS_SOCK },
2929 		{ "do", LS_DOOR },
2930 		{ "bd", LS_BLK },
2931 		{ "cd", LS_CHR },
2932 		{ "or", LS_ORPHAN },
2933 		{ "su", LS_SETUID },
2934 		{ "sg", LS_SETGID },
2935 		{ "tw", LS_STICKY_OTHER_WRITABLE },
2936 		{ "ow", LS_OTHER_WRITABLE },
2937 		{ "st", LS_STICKY },
2938 		{ "ex", LS_EXEC },
2939 		{ "po", LS_PORT },
2940 		{ NULL, LS_NORMAL }
2941 	};
2942 
2943 	char		*p, *lasts;
2944 	int		i;
2945 	int		color, attr;
2946 
2947 	p = strtok_r(colorstr, "=", &lasts);
2948 	if (p == NULL) {
2949 		colorflg = 0;
2950 		return;
2951 	}
2952 
2953 	if (p[0] == '*') {
2954 		lsc_colors[lsc_ncolors].ftype = LS_PAT;
2955 		/* don't include the * in the suffix */
2956 		if ((lsc_colors[lsc_ncolors].sfx = strdup(p + 1)) == NULL) {
2957 			colorflg = 0;
2958 			return;
2959 		}
2960 	} else {
2961 		lsc_colors[lsc_ncolors].sfx = NULL;
2962 
2963 		for (i = 0; type_map[i].s != NULL; ++i) {
2964 			if (strncmp(type_map[i].s, p, 2) == 0)
2965 				break;
2966 		}
2967 
2968 		/* ignore unknown file types */
2969 		if (type_map[i].s == NULL)
2970 			return;
2971 
2972 		lsc_colors[lsc_ncolors].ftype = type_map[i].stype;
2973 	}
2974 
2975 	attr = LSA_NONE;
2976 	lsc_colors[lsc_ncolors].fg = -1;
2977 	lsc_colors[lsc_ncolors].bg = -1;
2978 	for (p = strtok_r(NULL, ";", &lasts); p != NULL;
2979 	    p = strtok_r(NULL, ";", &lasts)) {
2980 		color = strtol(p, NULL, 10);
2981 
2982 		if (color < 10) {
2983 			switch (color) {
2984 			case 0:
2985 				attr = LSA_NONE;
2986 				continue;
2987 			case 1:
2988 				attr |= LSA_BOLD;
2989 				continue;
2990 			case 4:
2991 				attr |= LSA_UNDERSCORE;
2992 				continue;
2993 			case 5:
2994 				attr |= LSA_BLINK;
2995 				continue;
2996 			case 7:
2997 				attr |= LSA_REVERSE;
2998 				continue;
2999 			case 8:
3000 				attr |= LSA_CONCEALED;
3001 				continue;
3002 			default:
3003 				continue;
3004 			}
3005 		}
3006 
3007 		if (color < 40)
3008 			lsc_colors[lsc_ncolors].fg = color - 30;
3009 		else
3010 			lsc_colors[lsc_ncolors].bg = color - 40;
3011 	}
3012 
3013 	lsc_colors[lsc_ncolors].attr = attr;
3014 	++lsc_ncolors;
3015 }
3016 
3017 static int
3018 ls_color_compare(const void *p1, const void *p2)
3019 {
3020 	const ls_color_t *c1 = (const ls_color_t *)p1;
3021 	const ls_color_t *c2 = (const ls_color_t *)p2;
3022 
3023 	int ret = c1->ftype - c2->ftype;
3024 
3025 	if (ret != 0)
3026 		return (ret);
3027 
3028 	if (c1->ftype != LS_PAT)
3029 		return (ret);
3030 
3031 	return (strcmp(c1->sfx, c2->sfx));
3032 }
3033 
3034 static void
3035 ls_color_init()
3036 {
3037 	static char *default_colorstr = "no=00:fi=00:di=01;34:ln=01;36:po=01;35"
3038 	    ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01"
3039 	    ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31"
3040 	    ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31"
3041 	    ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31"
3042 	    ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35"
3043 	    ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"
3044 	    ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35"
3045 	    ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35"
3046 	    ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35"
3047 	    ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35";
3048 
3049 	char    *colorstr;
3050 	char    *p, *lasts;
3051 	size_t  color_sz;
3052 	int	termret;
3053 
3054 	(void) setupterm(NULL, 1, &termret);
3055 	if (termret != 1)
3056 		return;
3057 
3058 	if ((colorstr = getenv("LS_COLORS")) == NULL)
3059 		colorstr = default_colorstr;
3060 
3061 	/*
3062 	 * Determine the size of lsc_colors.  color_sz can be > lsc_ncolors
3063 	 * if there are invalid entries passed in the string (they are ignored)
3064 	 */
3065 	color_sz = 1;
3066 	for (p = strchr(colorstr, ':'); p != NULL && *p != '\0';
3067 	    p = strchr(++p, ':'))
3068 		++color_sz;
3069 
3070 	lsc_colors = calloc(color_sz, sizeof (ls_color_t));
3071 	if (lsc_colors == NULL) {
3072 		free(colorstr);
3073 		return;
3074 	}
3075 
3076 	for (p = strtok_r(colorstr, ":", &lasts);
3077 	    p != NULL && lsc_ncolors < color_sz;
3078 	    p = strtok_r(NULL, ":", &lasts))
3079 		new_color_entry(p);
3080 
3081 	qsort((void *)lsc_colors, lsc_ncolors, sizeof (ls_color_t),
3082 	    ls_color_compare);
3083 
3084 	if ((lsc_bold = tigetstr("bold")) == (char *)-1)
3085 		lsc_bold = NULL;
3086 
3087 	if ((lsc_underline = tigetstr("smul")) == (char *)-1)
3088 		lsc_underline = NULL;
3089 
3090 	if ((lsc_blink = tigetstr("blink")) == (char *)-1)
3091 		lsc_blink = NULL;
3092 
3093 	if ((lsc_reverse = tigetstr("rev")) == (char *)-1)
3094 		lsc_reverse = NULL;
3095 
3096 	if ((lsc_concealed = tigetstr("prot")) == (char *)-1)
3097 		lsc_concealed = NULL;
3098 
3099 	if ((lsc_none = tigetstr("sgr0")) == (char *)-1)
3100 		lsc_none = NULL;
3101 
3102 	if ((lsc_setfg = tigetstr("setaf")) == (char *)-1)
3103 		lsc_setfg = NULL;
3104 
3105 	if ((lsc_setbg = tigetstr("setab")) == (char *)-1)
3106 		lsc_setbg = NULL;
3107 
3108 	if (getenv("_LS_COLOR_DEBUG") != NULL) {
3109 		int i;
3110 
3111 		lsc_debug = 1;
3112 		for (i = 0; i < lsc_ncolors; ++i)
3113 			dump_color(&lsc_colors[i]);
3114 	}
3115 }
3116 
3117 /* Free extended system attribute lists */
3118 
3119 void
3120 free_sysattr(struct lbuf *p)
3121 {
3122 	int i;
3123 
3124 	if (p->exttr != NULL) {
3125 		for (i = 0; i < sacnt; i++) {
3126 			if (p->exttr[i].name != NULL)
3127 				free(p->exttr[i].name);
3128 		}
3129 		free(p->exttr);
3130 	}
3131 	if (p->extm != NULL) {
3132 		for (i = 0; i < sacnt; i++) {
3133 			if (p->extm[i].name != NULL)
3134 				free(p->extm[i].name);
3135 		}
3136 		free(p->extm);
3137 	}
3138 }
3139 
3140 /* Allocate extended system attribute list */
3141 
3142 void *
3143 xmalloc(size_t size, struct lbuf *p)
3144 {
3145 	if ((p = malloc(size)) == NULL) {
3146 		perror("ls");
3147 		free_sysattr(p);
3148 		nvlist_free(response);
3149 		exit(2);
3150 	}
3151 	return (p);
3152 }
3153