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