xref: /titanic_53/usr/src/cmd/ls/ls.c (revision 5e1c72e1529d41fe7e1babf69acebeed897f9610)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52236845bSakaplan  * Common Development and Distribution License (the "License").
62236845bSakaplan  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21*5e1c72e1SJason King 
227c478bd9Sstevel@tonic-gate /*
23*5e1c72e1SJason King  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*5e1c72e1SJason King  * Use is subject to license terms.
25*5e1c72e1SJason King  */
26*5e1c72e1SJason King 
27*5e1c72e1SJason King /*
28*5e1c72e1SJason King  * Copyright 2009 Jason King.  All rights reserved.
297c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
337c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
367c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate /*
397c478bd9Sstevel@tonic-gate  * List files or directories
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate #include <sys/param.h>
437c478bd9Sstevel@tonic-gate #include <sys/types.h>
447c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
457c478bd9Sstevel@tonic-gate #include <sys/stat.h>
467c478bd9Sstevel@tonic-gate #include <sys/acl.h>
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #include <wchar.h>
497c478bd9Sstevel@tonic-gate #include <stdio.h>
507c478bd9Sstevel@tonic-gate #include <ctype.h>
517c478bd9Sstevel@tonic-gate #include <dirent.h>
527c478bd9Sstevel@tonic-gate #include <string.h>
537c478bd9Sstevel@tonic-gate #include <locale.h>
547c478bd9Sstevel@tonic-gate #include <curses.h>
55*5e1c72e1SJason King #include <term.h>
567c478bd9Sstevel@tonic-gate #include <termios.h>
577c478bd9Sstevel@tonic-gate #include <stdlib.h>
587c478bd9Sstevel@tonic-gate #include <widec.h>
597c478bd9Sstevel@tonic-gate #include <locale.h>
607c478bd9Sstevel@tonic-gate #include <wctype.h>
617c478bd9Sstevel@tonic-gate #include <pwd.h>
627c478bd9Sstevel@tonic-gate #include <grp.h>
637c478bd9Sstevel@tonic-gate #include <limits.h>
647c478bd9Sstevel@tonic-gate #include <fcntl.h>
657c478bd9Sstevel@tonic-gate #include <unistd.h>
667c478bd9Sstevel@tonic-gate #include <libgen.h>
677c478bd9Sstevel@tonic-gate #include <errno.h>
68fa9e4066Sahrens #include <aclutils.h>
69da6c28aaSamw #include <libnvpair.h>
70da6c28aaSamw #include <libcmdutils.h>
71da6c28aaSamw #include <attr.h>
72*5e1c72e1SJason King #include <getopt.h>
73*5e1c72e1SJason King #include <inttypes.h>
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate #ifndef STANDALONE
767c478bd9Sstevel@tonic-gate #define	TERMINFO
777c478bd9Sstevel@tonic-gate #endif
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /*
807c478bd9Sstevel@tonic-gate  * -DNOTERMINFO can be defined on the cc command line to prevent
817c478bd9Sstevel@tonic-gate  * the use of terminfo.  This should be done on systems not having
82da6c28aaSamw  * the terminfo feature(pre 6.0 systems ?).
837c478bd9Sstevel@tonic-gate  * As a result, columnar listings assume 80 columns for output,
847c478bd9Sstevel@tonic-gate  * unless told otherwise via the COLUMNS environment variable.
857c478bd9Sstevel@tonic-gate  */
867c478bd9Sstevel@tonic-gate #ifdef NOTERMINFO
877c478bd9Sstevel@tonic-gate #undef TERMINFO
887c478bd9Sstevel@tonic-gate #endif
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate #include <term.h>
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate #define	BFSIZE	16
937c478bd9Sstevel@tonic-gate /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */
947c478bd9Sstevel@tonic-gate #define	ISARG	0100000
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate /*
977c478bd9Sstevel@tonic-gate  * this flag has been added to manipulate the display of S instead of 'l' when
987c478bd9Sstevel@tonic-gate  * the file is not a regular file and when group execution bit is off
997c478bd9Sstevel@tonic-gate  */
1007c478bd9Sstevel@tonic-gate #define	LS_NOTREG	010000
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate /*
1047c478bd9Sstevel@tonic-gate  * Date and time formats
1057c478bd9Sstevel@tonic-gate  *
1067c478bd9Sstevel@tonic-gate  * b --- abbreviated month name
1077c478bd9Sstevel@tonic-gate  * e --- day number
1087c478bd9Sstevel@tonic-gate  * Y --- year in the form ccyy
1097c478bd9Sstevel@tonic-gate  * H --- hour(24-hour version)
1107c478bd9Sstevel@tonic-gate  * M --- minute
1117c478bd9Sstevel@tonic-gate  * F --- yyyy-mm-dd
1127c478bd9Sstevel@tonic-gate  * T --- hh:mm:ss
1137c478bd9Sstevel@tonic-gate  * z --- time zone as hours displacement from UTC
1147c478bd9Sstevel@tonic-gate  * note that %F and %z are from the ISO C99 standard and are
1157c478bd9Sstevel@tonic-gate  * not present in older C libraries
1167c478bd9Sstevel@tonic-gate  */
117*5e1c72e1SJason King #define	FORMAT_OLD	" %b %e  %Y "
118*5e1c72e1SJason King #define	FORMAT_NEW	" %b %e %H:%M "
119*5e1c72e1SJason King #define	FORMAT_LONG	" %b %e %T %Y "
120*5e1c72e1SJason King #define	FORMAT_ISO_FULL	" %%F %%T.%.09ld %%z "
121*5e1c72e1SJason King #define	FORMAT_ISO_LONG	" %F %R "
122*5e1c72e1SJason King #define	FORMAT_ISO_NEW	" %m-%d %H:%M "
123*5e1c72e1SJason King #define	FORMAT_ISO_OLD	" %F "
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate #undef BUFSIZ
1267c478bd9Sstevel@tonic-gate #define	BUFSIZ 4096
1277c478bd9Sstevel@tonic-gate #define	NUMBER_WIDTH 40
1287c478bd9Sstevel@tonic-gate #define	FMTSIZE 50
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate struct ditem {
1317c478bd9Sstevel@tonic-gate 	dev_t	dev;			/* directory items device number */
1327c478bd9Sstevel@tonic-gate 	ino_t	ino;			/* directory items inode number */
1337c478bd9Sstevel@tonic-gate 	struct ditem *parent;		/* dir items ptr to its parent's info */
1347c478bd9Sstevel@tonic-gate };
135da6c28aaSamw /* Holds boolean extended system attributes */
136da6c28aaSamw struct attrb {
137da6c28aaSamw 	char		*name;
138da6c28aaSamw };
139da6c28aaSamw /* Holds timestamp extended system attributes */
140da6c28aaSamw struct attrtm {
141da6c28aaSamw 	char		*name;
142da6c28aaSamw 	uint64_t	stm;
143da6c28aaSamw 	uint64_t	nstm;
144da6c28aaSamw };
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate struct	lbuf	{
1477c478bd9Sstevel@tonic-gate 	union	{
1487c478bd9Sstevel@tonic-gate 		char	lname[MAXNAMLEN]; /* used for filename in a directory */
1497c478bd9Sstevel@tonic-gate 		char	*namep;		/* for name in ls-command; */
1507c478bd9Sstevel@tonic-gate 	} ln;
1517c478bd9Sstevel@tonic-gate 	char	ltype;		/* filetype */
1527c478bd9Sstevel@tonic-gate 	ino_t	lnum;		/* inode number of file */
1537c478bd9Sstevel@tonic-gate 	mode_t	lflags; 	/* 0777 bits used as r,w,x permissions */
1547c478bd9Sstevel@tonic-gate 	nlink_t	lnl;		/* number of links to file */
1557c478bd9Sstevel@tonic-gate 	uid_t	luid;
1567c478bd9Sstevel@tonic-gate 	gid_t	lgid;
1577c478bd9Sstevel@tonic-gate 	off_t	lsize;		/* filesize or major/minor dev numbers */
1587c478bd9Sstevel@tonic-gate 	blkcnt_t	lblocks;	/* number of file blocks */
1597c478bd9Sstevel@tonic-gate 	timestruc_t	lmtime;
160da6c28aaSamw 	timestruc_t	lat;
161da6c28aaSamw 	timestruc_t	lct;
162da6c28aaSamw 	timestruc_t	lmt;
1637c478bd9Sstevel@tonic-gate 	char	*flinkto;	/* symbolic link contents */
1647c478bd9Sstevel@tonic-gate 	char 	acl;		/* indicate there are additional acl entries */
1657c478bd9Sstevel@tonic-gate 	int	cycle;		/* cycle detected flag */
1667c478bd9Sstevel@tonic-gate 	struct ditem *ancinfo;	/* maintains ancestor info */
167fa9e4066Sahrens 	acl_t *aclp;		/* ACL if present */
168da6c28aaSamw 	struct attrb *exttr;	/* boolean extended system attributes */
169da6c28aaSamw 	struct attrtm *extm;	/* timestamp extended system attributes */
1707c478bd9Sstevel@tonic-gate };
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate struct dchain {
1737c478bd9Sstevel@tonic-gate 	char *dc_name;		/* path name */
1747c478bd9Sstevel@tonic-gate 	int cycle_detected;	/* cycle detected visiting this directory */
1757c478bd9Sstevel@tonic-gate 	struct ditem *myancinfo;	/* this directory's ancestry info */
1767c478bd9Sstevel@tonic-gate 	struct dchain *dc_next;	/* next directory in the chain */
1777c478bd9Sstevel@tonic-gate };
1787c478bd9Sstevel@tonic-gate 
179*5e1c72e1SJason King #define	LSA_NONE	(0)
180*5e1c72e1SJason King #define	LSA_BOLD	(1L << 0)
181*5e1c72e1SJason King #define	LSA_UNDERSCORE	(1L << 1)
182*5e1c72e1SJason King #define	LSA_BLINK	(1L << 2)
183*5e1c72e1SJason King #define	LSA_REVERSE	(1L << 3)
184*5e1c72e1SJason King #define	LSA_CONCEALED	(1L << 4)
185*5e1c72e1SJason King 
186*5e1c72e1SJason King /* these should be ordered most general to most specific */
187*5e1c72e1SJason King typedef enum LS_CFTYPE {
188*5e1c72e1SJason King 	LS_NORMAL,
189*5e1c72e1SJason King 	LS_FILE,
190*5e1c72e1SJason King 	LS_EXEC,
191*5e1c72e1SJason King 	LS_DIR,
192*5e1c72e1SJason King 	LS_LINK,
193*5e1c72e1SJason King 	LS_FIFO,
194*5e1c72e1SJason King 	LS_SOCK,
195*5e1c72e1SJason King 	LS_DOOR,
196*5e1c72e1SJason King 	LS_BLK,
197*5e1c72e1SJason King 	LS_CHR,
198*5e1c72e1SJason King 	LS_PORT,
199*5e1c72e1SJason King 	LS_STICKY,
200*5e1c72e1SJason King 	LS_ORPHAN,
201*5e1c72e1SJason King 	LS_SETGID,
202*5e1c72e1SJason King 	LS_SETUID,
203*5e1c72e1SJason King 	LS_OTHER_WRITABLE,
204*5e1c72e1SJason King 	LS_STICKY_OTHER_WRITABLE,
205*5e1c72e1SJason King 	LS_PAT
206*5e1c72e1SJason King } ls_cftype_t;
207*5e1c72e1SJason King 
208*5e1c72e1SJason King typedef struct ls_color {
209*5e1c72e1SJason King 	char		*sfx;
210*5e1c72e1SJason King 	ls_cftype_t	ftype;
211*5e1c72e1SJason King 	int		attr;
212*5e1c72e1SJason King 	int		fg;
213*5e1c72e1SJason King 	int		bg;
214*5e1c72e1SJason King } ls_color_t;
215*5e1c72e1SJason King 
2167c478bd9Sstevel@tonic-gate /*
2177c478bd9Sstevel@tonic-gate  * A numbuf_t is used when converting a number to a string representation
2187c478bd9Sstevel@tonic-gate  */
2197c478bd9Sstevel@tonic-gate typedef char numbuf_t[NUMBER_WIDTH];
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate static struct dchain *dfirst;	/* start of the dir chain */
2227c478bd9Sstevel@tonic-gate static struct dchain *cdfirst;	/* start of the current dir chain */
2237c478bd9Sstevel@tonic-gate static struct dchain *dtemp;	/* temporary - used for linking */
2247c478bd9Sstevel@tonic-gate static char *curdir;		/* the current directory */
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate static int	first = 1;	/* true if first line is not yet printed */
2277c478bd9Sstevel@tonic-gate static int	nfiles = 0;	/* number of flist entries in current use */
2287c478bd9Sstevel@tonic-gate static int	nargs = 0;	/* number of flist entries used for arguments */
2297c478bd9Sstevel@tonic-gate static int	maxfils = 0;	/* number of flist/lbuf entries allocated */
2307c478bd9Sstevel@tonic-gate static int	maxn = 0;	/* number of flist entries with lbufs asigned */
2317c478bd9Sstevel@tonic-gate static int	quantn = 64;	/* allocation growth quantum */
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate static struct lbuf	*nxtlbf;	/* ptr to next lbuf to be assigned */
2347c478bd9Sstevel@tonic-gate static struct lbuf	**flist;	/* ptr to list of lbuf pointers */
2357c478bd9Sstevel@tonic-gate static struct lbuf	*gstat(char *, int, struct ditem *);
2367c478bd9Sstevel@tonic-gate static char		*getname(uid_t);
2377c478bd9Sstevel@tonic-gate static char		*getgroup(gid_t);
2387c478bd9Sstevel@tonic-gate static char		*makename(char *, char *);
2397c478bd9Sstevel@tonic-gate static void		pentry(struct lbuf *);
2407c478bd9Sstevel@tonic-gate static void		column(void);
2417c478bd9Sstevel@tonic-gate static void		pmode(mode_t aflag);
2427c478bd9Sstevel@tonic-gate static void		selection(int *);
2437c478bd9Sstevel@tonic-gate static void		new_line(void);
2447c478bd9Sstevel@tonic-gate static void		rddir(char *, struct ditem *);
2457c478bd9Sstevel@tonic-gate static int		strcol(unsigned char *);
2467c478bd9Sstevel@tonic-gate static void		pem(struct lbuf **, struct lbuf **, int);
2477c478bd9Sstevel@tonic-gate static void		pdirectory(char *, int, int, int, struct ditem *);
2487c478bd9Sstevel@tonic-gate static struct cachenode *findincache(struct cachenode **, long);
2497c478bd9Sstevel@tonic-gate static void		csi_pprintf(unsigned char *);
2507c478bd9Sstevel@tonic-gate static void		pprintf(char *, char *);
2517c478bd9Sstevel@tonic-gate static int		compar(struct lbuf **pp1, struct lbuf **pp2);
2527c478bd9Sstevel@tonic-gate static char 		*number_to_scaled_string(numbuf_t buf,
2537c478bd9Sstevel@tonic-gate 			    unsigned long long number,
2547c478bd9Sstevel@tonic-gate 			    long scale);
2557c478bd9Sstevel@tonic-gate static void		record_ancestry(char *, struct stat *, struct lbuf *,
2567c478bd9Sstevel@tonic-gate 			    int, struct ditem *);
257*5e1c72e1SJason King static void		ls_color_init(void);
258*5e1c72e1SJason King static void		ls_start_color(struct lbuf *);
259*5e1c72e1SJason King static void		ls_end_color(void);
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate static int		aflg;
2627c478bd9Sstevel@tonic-gate static int		atflg;
2637c478bd9Sstevel@tonic-gate static int		bflg;
2647c478bd9Sstevel@tonic-gate static int		cflg;
2657c478bd9Sstevel@tonic-gate static int		dflg;
2667c478bd9Sstevel@tonic-gate static int		eflg;
2677c478bd9Sstevel@tonic-gate static int		fflg;
2687c478bd9Sstevel@tonic-gate static int		gflg;
2697c478bd9Sstevel@tonic-gate static int		hflg;
2707c478bd9Sstevel@tonic-gate static int		iflg;
2717c478bd9Sstevel@tonic-gate static int		lflg;
2727c478bd9Sstevel@tonic-gate static int		mflg;
2737c478bd9Sstevel@tonic-gate static int		nflg;
2747c478bd9Sstevel@tonic-gate static int		oflg;
2757c478bd9Sstevel@tonic-gate static int		pflg;
2767c478bd9Sstevel@tonic-gate static int		qflg;
2777c478bd9Sstevel@tonic-gate static int		rflg = 1; /* init to 1 for special use in compar */
2787c478bd9Sstevel@tonic-gate static int		sflg;
2797c478bd9Sstevel@tonic-gate static int		tflg;
2807c478bd9Sstevel@tonic-gate static int		uflg;
281*5e1c72e1SJason King static int		Uflg;
282*5e1c72e1SJason King static int		wflg;
2837c478bd9Sstevel@tonic-gate static int		xflg;
2847c478bd9Sstevel@tonic-gate static int		Aflg;
285*5e1c72e1SJason King static int		Bflg;
2867c478bd9Sstevel@tonic-gate static int		Cflg;
2877c478bd9Sstevel@tonic-gate static int		Eflg;
2887c478bd9Sstevel@tonic-gate static int		Fflg;
2897c478bd9Sstevel@tonic-gate static int		Hflg;
2907c478bd9Sstevel@tonic-gate static int		Lflg;
2917c478bd9Sstevel@tonic-gate static int		Rflg;
2927c478bd9Sstevel@tonic-gate static int		Sflg;
293fa9e4066Sahrens static int		vflg;
2945a5eeccaSmarks static int		Vflg;
295da6c28aaSamw static int		saflg;		/* boolean extended system attr. */
296da6c28aaSamw static int		sacnt;		/* number of extended system attr. */
297da6c28aaSamw static int		copt;
298da6c28aaSamw static int		vopt;
299da6c28aaSamw static int		tmflg;		/* create time ext. system attr. */
300da6c28aaSamw static int		ctm;
301da6c28aaSamw static int		atm;
302da6c28aaSamw static int		mtm;
303da6c28aaSamw static int		crtm;
304da6c28aaSamw static int		alltm;
3057c478bd9Sstevel@tonic-gate static long		hscale;
3067c478bd9Sstevel@tonic-gate static mode_t		flags;
3077c478bd9Sstevel@tonic-gate static int		err = 0;	/* Contains return code */
308*5e1c72e1SJason King static int		colorflg;
309*5e1c72e1SJason King static int		file_typeflg;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate static uid_t		lastuid	= (uid_t)-1;
3127c478bd9Sstevel@tonic-gate static gid_t		lastgid = (gid_t)-1;
3137c478bd9Sstevel@tonic-gate static char		*lastuname = NULL;
3147c478bd9Sstevel@tonic-gate static char		*lastgname = NULL;
3157c478bd9Sstevel@tonic-gate 
316*5e1c72e1SJason King /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg, colorflg are on */
3177c478bd9Sstevel@tonic-gate static int		statreq;
3187c478bd9Sstevel@tonic-gate 
319*5e1c72e1SJason King static uint64_t		block_size = 1;
3207c478bd9Sstevel@tonic-gate static char		*dotp = ".";
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate static u_longlong_t 	tblocks; /* number of blocks of files in a directory */
3237c478bd9Sstevel@tonic-gate static time_t		year, now;
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate static int		num_cols = 80;
3267c478bd9Sstevel@tonic-gate static int		colwidth;
3277c478bd9Sstevel@tonic-gate static int		filewidth;
3287c478bd9Sstevel@tonic-gate static int		fixedwidth;
3297c478bd9Sstevel@tonic-gate static int		nomocore;
3307c478bd9Sstevel@tonic-gate static int		curcol;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate static struct	winsize	win;
3337c478bd9Sstevel@tonic-gate 
334*5e1c72e1SJason King /* if time_fmt_new is left NULL, time_fmt_old is used for all times */
335*5e1c72e1SJason King static const char	*time_fmt_old = FORMAT_OLD;	/* non-recent files */
336*5e1c72e1SJason King static const char	*time_fmt_new = FORMAT_NEW;	/* recent files */
337*5e1c72e1SJason King static int		time_custom;	/* != 0 if a custom format */
338da6c28aaSamw static char	time_buf[FMTSIZE];	/* array to hold day and time */
3397c478bd9Sstevel@tonic-gate 
340*5e1c72e1SJason King static int		lsc_debug;
341*5e1c72e1SJason King static ls_color_t	*lsc_match;
342*5e1c72e1SJason King static ls_color_t	*lsc_colors;
343*5e1c72e1SJason King static size_t		lsc_ncolors;
344*5e1c72e1SJason King static char		*lsc_bold;
345*5e1c72e1SJason King static char		*lsc_underline;
346*5e1c72e1SJason King static char		*lsc_blink;
347*5e1c72e1SJason King static char		*lsc_reverse;
348*5e1c72e1SJason King static char		*lsc_concealed;
349*5e1c72e1SJason King static char		*lsc_none;
350*5e1c72e1SJason King static char		*lsc_setfg;
351*5e1c72e1SJason King static char		*lsc_setbg;
352*5e1c72e1SJason King 
3537c478bd9Sstevel@tonic-gate #define	NOTWORKINGDIR(d, l)	(((l) < 2) || \
3547c478bd9Sstevel@tonic-gate 				    (strcmp((d) + (l) - 2, "/.") != 0))
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate #define	NOTPARENTDIR(d, l)	(((l) < 3) || \
3577c478bd9Sstevel@tonic-gate 				    (strcmp((d) + (l) - 3, "/..") != 0))
358da6c28aaSamw /* Extended system attributes support */
359da6c28aaSamw static int get_sysxattr(char *, struct lbuf *);
360da6c28aaSamw static void set_sysattrb_display(char *, boolean_t, struct lbuf *);
361da6c28aaSamw static void set_sysattrtm_display(char *, struct lbuf *);
362*5e1c72e1SJason King static void format_time(time_t, time_t);
363da6c28aaSamw static void print_time(struct lbuf *);
364da6c28aaSamw static void format_attrtime(struct lbuf *);
365da6c28aaSamw static void *xmalloc(size_t, struct lbuf *);
366da6c28aaSamw static void free_sysattr(struct lbuf *);
367da6c28aaSamw static nvpair_t *pair;
368da6c28aaSamw static nvlist_t	*response;
36956798e90Sbasabi static int acl_err;
3707c478bd9Sstevel@tonic-gate 
371*5e1c72e1SJason King const struct option long_options[] = {
372*5e1c72e1SJason King 	{ "all", no_argument, NULL, 'a' },
373*5e1c72e1SJason King 	{ "almost-all", no_argument, NULL, 'A' },
374*5e1c72e1SJason King 	{ "escape", no_argument, NULL, 'b' },
375*5e1c72e1SJason King 	{ "classify", no_argument, NULL, 'F' },
376*5e1c72e1SJason King 	{ "human-readable", no_argument, NULL, 'h' },
377*5e1c72e1SJason King 	{ "dereference", no_argument, NULL, 'L' },
378*5e1c72e1SJason King 	{ "dereference-command-line", no_argument, NULL, 'H' },
379*5e1c72e1SJason King 	{ "ignore-backups", no_argument, NULL, 'B' },
380*5e1c72e1SJason King 	{ "inode", no_argument, NULL, 'i' },
381*5e1c72e1SJason King 	{ "numeric-uid-gid", no_argument, NULL, 'n' },
382*5e1c72e1SJason King 	{ "no-group", no_argument, NULL, 'o' },
383*5e1c72e1SJason King 	{ "hide-control-chars", no_argument, NULL, 'q' },
384*5e1c72e1SJason King 	{ "reverse", no_argument, NULL, 'r' },
385*5e1c72e1SJason King 	{ "recursive", no_argument, NULL, 'R' },
386*5e1c72e1SJason King 	{ "size", no_argument, NULL, 's' },
387*5e1c72e1SJason King 	{ "width", required_argument, NULL, 'w' },
388*5e1c72e1SJason King 
389*5e1c72e1SJason King 	/* no short options for these */
390*5e1c72e1SJason King 	{ "block-size", required_argument, NULL, 0 },
391*5e1c72e1SJason King 	{ "full-time", no_argument, NULL, 0 },
392*5e1c72e1SJason King 	{ "si", no_argument, NULL, 0 },
393*5e1c72e1SJason King 	{ "color", optional_argument, NULL, 0 },
394*5e1c72e1SJason King 	{ "colour", optional_argument, NULL, 0},
395*5e1c72e1SJason King 	{ "file-type", no_argument, NULL, 0 },
396*5e1c72e1SJason King 	{ "time-style", required_argument, NULL, 0 },
397*5e1c72e1SJason King 
398*5e1c72e1SJason King 	{0, 0, 0, 0}
399*5e1c72e1SJason King };
400*5e1c72e1SJason King 
4017c478bd9Sstevel@tonic-gate int
4027c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
4037c478bd9Sstevel@tonic-gate {
4047c478bd9Sstevel@tonic-gate 	int		c;
4057c478bd9Sstevel@tonic-gate 	int		i;
4067c478bd9Sstevel@tonic-gate 	int		width;
4077c478bd9Sstevel@tonic-gate 	int		amino = 0;
4087c478bd9Sstevel@tonic-gate 	int		opterr = 0;
409*5e1c72e1SJason King 	int		option_index = 0;
4107c478bd9Sstevel@tonic-gate 	struct lbuf	*ep;
4117c478bd9Sstevel@tonic-gate 	struct lbuf	lb;
4127c478bd9Sstevel@tonic-gate 	struct ditem	*myinfo;
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
4157c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
4167c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
4177c478bd9Sstevel@tonic-gate #endif
4187c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
4197c478bd9Sstevel@tonic-gate #ifdef STANDALONE
4207c478bd9Sstevel@tonic-gate 	if (argv[0][0] == '\0')
4217c478bd9Sstevel@tonic-gate 		argc = getargv("ls", &argv, 0);
4227c478bd9Sstevel@tonic-gate #endif
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	lb.lmtime.tv_sec = time(NULL);
4257c478bd9Sstevel@tonic-gate 	lb.lmtime.tv_nsec = 0;
4267c478bd9Sstevel@tonic-gate 	year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */
4277c478bd9Sstevel@tonic-gate 	now = lb.lmtime.tv_sec + 60;
4287c478bd9Sstevel@tonic-gate 	if (isatty(1)) {
4297c478bd9Sstevel@tonic-gate 		Cflg = 1;
4307c478bd9Sstevel@tonic-gate 		mflg = 0;
4317c478bd9Sstevel@tonic-gate 	}
4327c478bd9Sstevel@tonic-gate 
433*5e1c72e1SJason King 	while ((c = getopt_long(argc, argv,
434*5e1c72e1SJason King 	    "+aAbBcCdeEfFghHiklLmnopqrRsStuUw:x1@vV/:%:", long_options,
435*5e1c72e1SJason King 	    &option_index)) != -1)
4367c478bd9Sstevel@tonic-gate 		switch (c) {
437*5e1c72e1SJason King 		case 0:
438*5e1c72e1SJason King 			/* non-short options */
439*5e1c72e1SJason King 			if (strcmp(long_options[option_index].name,
440*5e1c72e1SJason King 			    "color") == 0 ||
441*5e1c72e1SJason King 			    strcmp(long_options[option_index].name,
442*5e1c72e1SJason King 			    "colour") == 0) {
443*5e1c72e1SJason King 				if (optarg == NULL ||
444*5e1c72e1SJason King 				    strcmp(optarg, "always") == 0 ||
445*5e1c72e1SJason King 				    strcmp(optarg, "yes") == 0 ||
446*5e1c72e1SJason King 				    strcmp(optarg, "force") == 0) {
447*5e1c72e1SJason King 					colorflg++;
448*5e1c72e1SJason King 					statreq++;
449*5e1c72e1SJason King 					continue;
450*5e1c72e1SJason King 				}
451*5e1c72e1SJason King 
452*5e1c72e1SJason King 				if ((strcmp(optarg, "auto") == 0 ||
453*5e1c72e1SJason King 				    strcmp(optarg, "tty") == 0 ||
454*5e1c72e1SJason King 				    strcmp(optarg, "if-tty") == 0) &&
455*5e1c72e1SJason King 				    isatty(1) == 1) {
456*5e1c72e1SJason King 					colorflg++;
457*5e1c72e1SJason King 					statreq++;
458*5e1c72e1SJason King 					continue;
459*5e1c72e1SJason King 				}
460*5e1c72e1SJason King 
461*5e1c72e1SJason King 				if (strcmp(optarg, "never") == 0 ||
462*5e1c72e1SJason King 				    strcmp(optarg, "no") == 0 ||
463*5e1c72e1SJason King 				    strcmp(optarg, "none") == 0) {
464*5e1c72e1SJason King 					colorflg = 0;
465*5e1c72e1SJason King 					continue;
466*5e1c72e1SJason King 				}
467*5e1c72e1SJason King 				(void) fprintf(stderr,
468*5e1c72e1SJason King 				    gettext("Invalid argument '%s' for "
469*5e1c72e1SJason King 				    "--color\n"), optarg);
470*5e1c72e1SJason King 				++opterr;
471*5e1c72e1SJason King 				continue;
472*5e1c72e1SJason King 			}
473*5e1c72e1SJason King 
474*5e1c72e1SJason King 			if (strcmp(long_options[option_index].name,
475*5e1c72e1SJason King 			    "si") == 0) {
476*5e1c72e1SJason King 				hflg++;
477*5e1c72e1SJason King 				hscale = 1000;
478*5e1c72e1SJason King 				continue;
479*5e1c72e1SJason King 			}
480*5e1c72e1SJason King 
481*5e1c72e1SJason King 			if (strcmp(long_options[option_index].name,
482*5e1c72e1SJason King 			    "block-size") == 0) {
483*5e1c72e1SJason King 				size_t scale_len = strlen(optarg);
484*5e1c72e1SJason King 				uint64_t scale = 1;
485*5e1c72e1SJason King 				uint64_t kilo = 1024;
486*5e1c72e1SJason King 				char scale_c;
487*5e1c72e1SJason King 
488*5e1c72e1SJason King 				if (scale_len == 0) {
489*5e1c72e1SJason King 					(void) fprintf(stderr, gettext(
490*5e1c72e1SJason King 					    "Invalid block size \'%s\'\n"),
491*5e1c72e1SJason King 					    optarg);
492*5e1c72e1SJason King 					exit(1);
493*5e1c72e1SJason King 				}
494*5e1c72e1SJason King 
495*5e1c72e1SJason King 				scale_c = optarg[scale_len - 1];
496*5e1c72e1SJason King 				if (scale_c == 'B') {
497*5e1c72e1SJason King 					/* need at least digit, scale, B */
498*5e1c72e1SJason King 					if (scale_len < 3) {
499*5e1c72e1SJason King 						(void) fprintf(stderr, gettext(
500*5e1c72e1SJason King 						    "Invalid block size "
501*5e1c72e1SJason King 						    "\'%s\'\n"), optarg);
502*5e1c72e1SJason King 						exit(1);
503*5e1c72e1SJason King 					}
504*5e1c72e1SJason King 					kilo = 1000;
505*5e1c72e1SJason King 					scale_c = optarg[scale_len - 2];
506*5e1c72e1SJason King 					if (isdigit(scale_c)) {
507*5e1c72e1SJason King 						(void) fprintf(stderr,
508*5e1c72e1SJason King 						    gettext("Invalid block size"
509*5e1c72e1SJason King 						    " \'%s\'\n"), optarg);
510*5e1c72e1SJason King 						exit(1);
511*5e1c72e1SJason King 					}
512*5e1c72e1SJason King 					/*
513*5e1c72e1SJason King 					 * make optarg[scale_len - 1] point to
514*5e1c72e1SJason King 					 * the scale factor
515*5e1c72e1SJason King 					 */
516*5e1c72e1SJason King 					--scale_len;
517*5e1c72e1SJason King 				}
518*5e1c72e1SJason King 
519*5e1c72e1SJason King 				switch (scale_c) {
520*5e1c72e1SJason King 				case 'y':
521*5e1c72e1SJason King 				case 'Y':
522*5e1c72e1SJason King 					scale *= kilo;
523*5e1c72e1SJason King 					/*FALLTHROUGH*/
524*5e1c72e1SJason King 				case 'Z':
525*5e1c72e1SJason King 				case 'z':
526*5e1c72e1SJason King 					scale *= kilo;
527*5e1c72e1SJason King 					/*FALLTHROUGH*/
528*5e1c72e1SJason King 				case 'E':
529*5e1c72e1SJason King 				case 'e':
530*5e1c72e1SJason King 					scale *= kilo;
531*5e1c72e1SJason King 					/*FALLTHROUGH*/
532*5e1c72e1SJason King 				case 'P':
533*5e1c72e1SJason King 				case 'p':
534*5e1c72e1SJason King 					scale *= kilo;
535*5e1c72e1SJason King 					/*FALLTHROUGH*/
536*5e1c72e1SJason King 				case 'T':
537*5e1c72e1SJason King 				case 't':
538*5e1c72e1SJason King 					scale *= kilo;
539*5e1c72e1SJason King 					/*FALLTHROUGH*/
540*5e1c72e1SJason King 				case 'G':
541*5e1c72e1SJason King 				case 'g':
542*5e1c72e1SJason King 					scale *= kilo;
543*5e1c72e1SJason King 					/*FALLTHROUGH*/
544*5e1c72e1SJason King 				case 'M':
545*5e1c72e1SJason King 				case 'm':
546*5e1c72e1SJason King 					scale *= kilo;
547*5e1c72e1SJason King 					/*FALLTHROUGH*/
548*5e1c72e1SJason King 				case 'K':
549*5e1c72e1SJason King 				case 'k':
550*5e1c72e1SJason King 					scale *= kilo;
551*5e1c72e1SJason King 					break;
552*5e1c72e1SJason King 				default:
553*5e1c72e1SJason King 					if (!isdigit(scale_c)) {
554*5e1c72e1SJason King 						(void) fprintf(stderr,
555*5e1c72e1SJason King 						    gettext("Invalid character "
556*5e1c72e1SJason King 						    "following block size in "
557*5e1c72e1SJason King 						    "\'%s\'\n"), optarg);
558*5e1c72e1SJason King 						exit(1);
559*5e1c72e1SJason King 					}
560*5e1c72e1SJason King 				}
561*5e1c72e1SJason King 
562*5e1c72e1SJason King 				/* NULL out scale constant if present */
563*5e1c72e1SJason King 				if (scale > 1 && !isdigit(scale_c))
564*5e1c72e1SJason King 					optarg[scale_len - 1] = '\0';
565*5e1c72e1SJason King 
566*5e1c72e1SJason King 				/* Based on testing, this is what GNU ls does */
567*5e1c72e1SJason King 				block_size = strtoll(optarg, NULL, 0) * scale;
568*5e1c72e1SJason King 				if (block_size < 1) {
569*5e1c72e1SJason King 					(void) fprintf(stderr,
570*5e1c72e1SJason King 					    gettext("Invalid block size "
571*5e1c72e1SJason King 					    "\'%s\'\n"), optarg);
572*5e1c72e1SJason King 					exit(1);
573*5e1c72e1SJason King 				}
574*5e1c72e1SJason King 				continue;
575*5e1c72e1SJason King 			}
576*5e1c72e1SJason King 
577*5e1c72e1SJason King 			if (strcmp(long_options[option_index].name,
578*5e1c72e1SJason King 			    "file-type") == 0) {
579*5e1c72e1SJason King 				file_typeflg++;
580*5e1c72e1SJason King 				Fflg++;
581*5e1c72e1SJason King 				statreq++;
582*5e1c72e1SJason King 				continue;
583*5e1c72e1SJason King 			}
584*5e1c72e1SJason King 
585*5e1c72e1SJason King 
586*5e1c72e1SJason King 			if (strcmp(long_options[option_index].name,
587*5e1c72e1SJason King 			    "full-time") == 0) {
588*5e1c72e1SJason King 				Eflg++;
589*5e1c72e1SJason King 				statreq++;
590*5e1c72e1SJason King 				eflg = 0;
591*5e1c72e1SJason King 				time_fmt_old = FORMAT_ISO_FULL;
592*5e1c72e1SJason King 				time_fmt_new = FORMAT_ISO_FULL;
593*5e1c72e1SJason King 				continue;
594*5e1c72e1SJason King 			}
595*5e1c72e1SJason King 
596*5e1c72e1SJason King 			if (strcmp(long_options[option_index].name,
597*5e1c72e1SJason King 			    "time-style") == 0) {
598*5e1c72e1SJason King 				/* like -E, but doesn't imply -l */
599*5e1c72e1SJason King 				if (strcmp(optarg, "full-iso") == 0) {
600*5e1c72e1SJason King 					Eflg++;
601*5e1c72e1SJason King 					statreq++;
602*5e1c72e1SJason King 					eflg = 0;
603*5e1c72e1SJason King 					time_fmt_old = FORMAT_ISO_FULL;
604*5e1c72e1SJason King 					time_fmt_new = FORMAT_ISO_FULL;
605*5e1c72e1SJason King 					continue;
606*5e1c72e1SJason King 				}
607*5e1c72e1SJason King 				if (strcmp(optarg, "long-iso") == 0) {
608*5e1c72e1SJason King 					statreq++;
609*5e1c72e1SJason King 					Eflg = 0;
610*5e1c72e1SJason King 					eflg = 0;
611*5e1c72e1SJason King 					time_fmt_old = FORMAT_ISO_LONG;
612*5e1c72e1SJason King 					time_fmt_new = FORMAT_ISO_LONG;
613*5e1c72e1SJason King 					continue;
614*5e1c72e1SJason King 				}
615*5e1c72e1SJason King 				if (strcmp(optarg, "iso") == 0) {
616*5e1c72e1SJason King 					statreq++;
617*5e1c72e1SJason King 					Eflg = 0;
618*5e1c72e1SJason King 					eflg = 0;
619*5e1c72e1SJason King 					time_fmt_old = FORMAT_ISO_OLD;
620*5e1c72e1SJason King 					time_fmt_new = FORMAT_ISO_NEW;
621*5e1c72e1SJason King 					continue;
622*5e1c72e1SJason King 				}
623*5e1c72e1SJason King 				/* should be the default */
624*5e1c72e1SJason King 				if (strcmp(optarg, "locale") == 0) {
625*5e1c72e1SJason King 					time_fmt_old = FORMAT_OLD;
626*5e1c72e1SJason King 					time_fmt_new = FORMAT_NEW;
627*5e1c72e1SJason King 					continue;
628*5e1c72e1SJason King 				}
629*5e1c72e1SJason King 				if (optarg[0] == '+') {
630*5e1c72e1SJason King 					char	*told, *tnew;
631*5e1c72e1SJason King 					char	*p;
632*5e1c72e1SJason King 					size_t	timelen = strlen(optarg);
633*5e1c72e1SJason King 
634*5e1c72e1SJason King 					p = strchr(optarg, '\n');
635*5e1c72e1SJason King 					if (p != NULL)
636*5e1c72e1SJason King 						*p++ = '\0';
637*5e1c72e1SJason King 
638*5e1c72e1SJason King 					/*
639*5e1c72e1SJason King 					 * Time format requires a leading and
640*5e1c72e1SJason King 					 * trailing space
641*5e1c72e1SJason King 					 * Add room for 3 spaces + 2 nulls
642*5e1c72e1SJason King 					 * The + in optarg is replaced with
643*5e1c72e1SJason King 					 * a space.
644*5e1c72e1SJason King 					 */
645*5e1c72e1SJason King 					timelen += 2 + 3;
646*5e1c72e1SJason King 					told = malloc(timelen);
647*5e1c72e1SJason King 					if (told == NULL) {
648*5e1c72e1SJason King 						perror("Out of memory");
649*5e1c72e1SJason King 						exit(1);
650*5e1c72e1SJason King 					}
651*5e1c72e1SJason King 
652*5e1c72e1SJason King 					(void) memset(told, 0, timelen);
653*5e1c72e1SJason King 					told[0] = ' ';
654*5e1c72e1SJason King 					(void) strlcat(told, &optarg[1],
655*5e1c72e1SJason King 					    timelen);
656*5e1c72e1SJason King 					(void) strlcat(told, " ", timelen);
657*5e1c72e1SJason King 
658*5e1c72e1SJason King 					if (p != NULL) {
659*5e1c72e1SJason King 						size_t tnew_len;
660*5e1c72e1SJason King 
661*5e1c72e1SJason King 						tnew = told + strlen(told) + 1;
662*5e1c72e1SJason King 						tnew_len = timelen -
663*5e1c72e1SJason King 						    strlen(told) - 1;
664*5e1c72e1SJason King 
665*5e1c72e1SJason King 						tnew[0] = ' ';
666*5e1c72e1SJason King 						(void) strlcat(tnew, p,
667*5e1c72e1SJason King 						    tnew_len);
668*5e1c72e1SJason King 						(void) strlcat(tnew, " ",
669*5e1c72e1SJason King 						    tnew_len);
670*5e1c72e1SJason King 						time_fmt_new =
671*5e1c72e1SJason King 						    (const char *)tnew;
672*5e1c72e1SJason King 					} else {
673*5e1c72e1SJason King 						time_fmt_new =
674*5e1c72e1SJason King 						    (const char *)told;
675*5e1c72e1SJason King 					}
676*5e1c72e1SJason King 
677*5e1c72e1SJason King 					time_fmt_old = (const char *)told;
678*5e1c72e1SJason King 					time_custom = 1;
679*5e1c72e1SJason King 					continue;
680*5e1c72e1SJason King 				}
681*5e1c72e1SJason King 				continue;
682*5e1c72e1SJason King 			}
683*5e1c72e1SJason King 
684*5e1c72e1SJason King 			continue;
685*5e1c72e1SJason King 
6867c478bd9Sstevel@tonic-gate 		case 'a':
6877c478bd9Sstevel@tonic-gate 			aflg++;
6887c478bd9Sstevel@tonic-gate 			continue;
6897c478bd9Sstevel@tonic-gate 		case 'A':
6907c478bd9Sstevel@tonic-gate 			Aflg++;
6917c478bd9Sstevel@tonic-gate 			continue;
6927c478bd9Sstevel@tonic-gate 		case 'b':
6937c478bd9Sstevel@tonic-gate 			bflg = 1;
6947c478bd9Sstevel@tonic-gate 			qflg = 0;
6957c478bd9Sstevel@tonic-gate 			continue;
696*5e1c72e1SJason King 		case 'B':
697*5e1c72e1SJason King 			Bflg = 1;
698*5e1c72e1SJason King 			continue;
6997c478bd9Sstevel@tonic-gate 		case 'c':
7007c478bd9Sstevel@tonic-gate 			uflg = 0;
701da6c28aaSamw 			atm = 0;
702da6c28aaSamw 			ctm = 0;
703da6c28aaSamw 			mtm = 0;
704da6c28aaSamw 			crtm = 0;
7057c478bd9Sstevel@tonic-gate 			cflg++;
7067c478bd9Sstevel@tonic-gate 			continue;
7077c478bd9Sstevel@tonic-gate 		case 'C':
7087c478bd9Sstevel@tonic-gate 			Cflg = 1;
7097c478bd9Sstevel@tonic-gate 			mflg = 0;
7107c478bd9Sstevel@tonic-gate #ifdef XPG4
7117c478bd9Sstevel@tonic-gate 			lflg = 0;
7127c478bd9Sstevel@tonic-gate #endif
7137c478bd9Sstevel@tonic-gate 			continue;
7147c478bd9Sstevel@tonic-gate 		case 'd':
7157c478bd9Sstevel@tonic-gate 			dflg++;
7167c478bd9Sstevel@tonic-gate 			continue;
7177c478bd9Sstevel@tonic-gate 		case 'e':
7187c478bd9Sstevel@tonic-gate 			eflg++;
7197c478bd9Sstevel@tonic-gate 			lflg++;
7207c478bd9Sstevel@tonic-gate 			statreq++;
7217c478bd9Sstevel@tonic-gate 			Eflg = 0;
722*5e1c72e1SJason King 			time_fmt_old = FORMAT_LONG;
723*5e1c72e1SJason King 			time_fmt_new = FORMAT_LONG;
7247c478bd9Sstevel@tonic-gate 			continue;
7257c478bd9Sstevel@tonic-gate 		case 'E':
7267c478bd9Sstevel@tonic-gate 			Eflg++;
7277c478bd9Sstevel@tonic-gate 			lflg++;
7287c478bd9Sstevel@tonic-gate 			statreq++;
7297c478bd9Sstevel@tonic-gate 			eflg = 0;
730*5e1c72e1SJason King 			time_fmt_old = FORMAT_ISO_FULL;
731*5e1c72e1SJason King 			time_fmt_new = FORMAT_ISO_FULL;
7327c478bd9Sstevel@tonic-gate 			continue;
7337c478bd9Sstevel@tonic-gate 		case 'f':
7347c478bd9Sstevel@tonic-gate 			fflg++;
7357c478bd9Sstevel@tonic-gate 			continue;
7367c478bd9Sstevel@tonic-gate 		case 'F':
7377c478bd9Sstevel@tonic-gate 			Fflg++;
7387c478bd9Sstevel@tonic-gate 			statreq++;
7397c478bd9Sstevel@tonic-gate 			continue;
7407c478bd9Sstevel@tonic-gate 		case 'g':
7417c478bd9Sstevel@tonic-gate 			gflg++;
7427c478bd9Sstevel@tonic-gate 			lflg++;
7437c478bd9Sstevel@tonic-gate 			statreq++;
7447c478bd9Sstevel@tonic-gate 			continue;
7457c478bd9Sstevel@tonic-gate 		case 'h':
7467c478bd9Sstevel@tonic-gate 			hflg++;
7477c478bd9Sstevel@tonic-gate 			hscale = 1024;
7487c478bd9Sstevel@tonic-gate 			continue;
7497c478bd9Sstevel@tonic-gate 		case 'H':
7507c478bd9Sstevel@tonic-gate 			Hflg++;
7517c478bd9Sstevel@tonic-gate 			/* -H and -L are mutually exclusive */
7527c478bd9Sstevel@tonic-gate 			Lflg = 0;
7537c478bd9Sstevel@tonic-gate 			continue;
7547c478bd9Sstevel@tonic-gate 		case 'i':
7557c478bd9Sstevel@tonic-gate 			iflg++;
7567c478bd9Sstevel@tonic-gate 			continue;
757*5e1c72e1SJason King 		case 'k':
758*5e1c72e1SJason King 			block_size = 1024;
759*5e1c72e1SJason King 			continue;
7607c478bd9Sstevel@tonic-gate 		case 'l':
7617c478bd9Sstevel@tonic-gate 			lflg++;
7627c478bd9Sstevel@tonic-gate 			statreq++;
7637c478bd9Sstevel@tonic-gate 			Cflg = 0;
7647c478bd9Sstevel@tonic-gate 			xflg = 0;
7657c478bd9Sstevel@tonic-gate 			mflg = 0;
7667c478bd9Sstevel@tonic-gate 			atflg = 0;
7677c478bd9Sstevel@tonic-gate 			continue;
7687c478bd9Sstevel@tonic-gate 		case 'L':
7697c478bd9Sstevel@tonic-gate 			Lflg++;
7707c478bd9Sstevel@tonic-gate 			/* -H and -L are mutually exclusive */
7717c478bd9Sstevel@tonic-gate 			Hflg = 0;
7727c478bd9Sstevel@tonic-gate 			continue;
7737c478bd9Sstevel@tonic-gate 		case 'm':
7747c478bd9Sstevel@tonic-gate 			Cflg = 0;
7757c478bd9Sstevel@tonic-gate 			mflg = 1;
7767c478bd9Sstevel@tonic-gate #ifdef XPG4
7777c478bd9Sstevel@tonic-gate 			lflg = 0;
7787c478bd9Sstevel@tonic-gate #endif
7797c478bd9Sstevel@tonic-gate 			continue;
7807c478bd9Sstevel@tonic-gate 		case 'n':
7817c478bd9Sstevel@tonic-gate 			nflg++;
7827c478bd9Sstevel@tonic-gate 			lflg++;
7837c478bd9Sstevel@tonic-gate 			statreq++;
7847c478bd9Sstevel@tonic-gate 			Cflg = 0;
7857c478bd9Sstevel@tonic-gate 			xflg = 0;
7867c478bd9Sstevel@tonic-gate 			mflg = 0;
7877c478bd9Sstevel@tonic-gate 			atflg = 0;
7887c478bd9Sstevel@tonic-gate 			continue;
7897c478bd9Sstevel@tonic-gate 		case 'o':
7907c478bd9Sstevel@tonic-gate 			oflg++;
7917c478bd9Sstevel@tonic-gate 			lflg++;
7927c478bd9Sstevel@tonic-gate 			statreq++;
7937c478bd9Sstevel@tonic-gate 			continue;
7947c478bd9Sstevel@tonic-gate 		case 'p':
7957c478bd9Sstevel@tonic-gate 			pflg++;
7967c478bd9Sstevel@tonic-gate 			statreq++;
7977c478bd9Sstevel@tonic-gate 			continue;
7987c478bd9Sstevel@tonic-gate 		case 'q':
7997c478bd9Sstevel@tonic-gate 			qflg = 1;
8007c478bd9Sstevel@tonic-gate 			bflg = 0;
8017c478bd9Sstevel@tonic-gate 			continue;
8027c478bd9Sstevel@tonic-gate 		case 'r':
8037c478bd9Sstevel@tonic-gate 			rflg = -1;
8047c478bd9Sstevel@tonic-gate 			continue;
8057c478bd9Sstevel@tonic-gate 		case 'R':
8067c478bd9Sstevel@tonic-gate 			Rflg++;
8077c478bd9Sstevel@tonic-gate 			statreq++;
8087c478bd9Sstevel@tonic-gate 			continue;
8097c478bd9Sstevel@tonic-gate 		case 's':
8107c478bd9Sstevel@tonic-gate 			sflg++;
8117c478bd9Sstevel@tonic-gate 			statreq++;
8127c478bd9Sstevel@tonic-gate 			continue;
8137c478bd9Sstevel@tonic-gate 		case 'S':
8147c478bd9Sstevel@tonic-gate 			tflg = 0;
815*5e1c72e1SJason King 			Uflg = 0;
8167c478bd9Sstevel@tonic-gate 			Sflg++;
8177c478bd9Sstevel@tonic-gate 			statreq++;
8187c478bd9Sstevel@tonic-gate 			continue;
8197c478bd9Sstevel@tonic-gate 		case 't':
8207c478bd9Sstevel@tonic-gate 			Sflg = 0;
821*5e1c72e1SJason King 			Uflg = 0;
8227c478bd9Sstevel@tonic-gate 			tflg++;
8237c478bd9Sstevel@tonic-gate 			statreq++;
8247c478bd9Sstevel@tonic-gate 			continue;
825*5e1c72e1SJason King 		case 'U':
826*5e1c72e1SJason King 			Sflg = 0;
827*5e1c72e1SJason King 			tflg = 0;
828*5e1c72e1SJason King 			Uflg++;
829*5e1c72e1SJason King 			continue;
8307c478bd9Sstevel@tonic-gate 		case 'u':
8317c478bd9Sstevel@tonic-gate 			cflg = 0;
832da6c28aaSamw 			atm = 0;
833da6c28aaSamw 			ctm = 0;
834da6c28aaSamw 			mtm = 0;
835da6c28aaSamw 			crtm = 0;
8367c478bd9Sstevel@tonic-gate 			uflg++;
8377c478bd9Sstevel@tonic-gate 			continue;
8385a5eeccaSmarks 		case 'V':
8395a5eeccaSmarks 			Vflg++;
8405a5eeccaSmarks 			/*FALLTHROUGH*/
841fa9e4066Sahrens 		case 'v':
842fa9e4066Sahrens 			vflg++;
843fa9e4066Sahrens #if !defined(XPG4)
844fa9e4066Sahrens 			if (lflg)
845fa9e4066Sahrens 				continue;
846fa9e4066Sahrens #endif
847fa9e4066Sahrens 			lflg++;
848fa9e4066Sahrens 			statreq++;
849fa9e4066Sahrens 			Cflg = 0;
850fa9e4066Sahrens 			xflg = 0;
851fa9e4066Sahrens 			mflg = 0;
852fa9e4066Sahrens 			continue;
853*5e1c72e1SJason King 		case 'w':
854*5e1c72e1SJason King 			wflg++;
855*5e1c72e1SJason King 			num_cols = atoi(optarg);
856*5e1c72e1SJason King 			continue;
8577c478bd9Sstevel@tonic-gate 		case 'x':
8587c478bd9Sstevel@tonic-gate 			xflg = 1;
8597c478bd9Sstevel@tonic-gate 			Cflg = 1;
8607c478bd9Sstevel@tonic-gate 			mflg = 0;
8617c478bd9Sstevel@tonic-gate #ifdef XPG4
8627c478bd9Sstevel@tonic-gate 			lflg = 0;
8637c478bd9Sstevel@tonic-gate #endif
8647c478bd9Sstevel@tonic-gate 			continue;
8657c478bd9Sstevel@tonic-gate 		case '1':
8667c478bd9Sstevel@tonic-gate 			Cflg = 0;
8677c478bd9Sstevel@tonic-gate 			continue;
8687c478bd9Sstevel@tonic-gate 		case '@':
8697c478bd9Sstevel@tonic-gate #if !defined(XPG4)
8707c478bd9Sstevel@tonic-gate 			/*
8717c478bd9Sstevel@tonic-gate 			 * -l has precedence over -@
8727c478bd9Sstevel@tonic-gate 			 */
8737c478bd9Sstevel@tonic-gate 			if (lflg)
8747c478bd9Sstevel@tonic-gate 				continue;
8757c478bd9Sstevel@tonic-gate #endif
8767c478bd9Sstevel@tonic-gate 			atflg++;
8777c478bd9Sstevel@tonic-gate 			lflg++;
8787c478bd9Sstevel@tonic-gate 			statreq++;
8797c478bd9Sstevel@tonic-gate 			Cflg = 0;
8807c478bd9Sstevel@tonic-gate 			xflg = 0;
8817c478bd9Sstevel@tonic-gate 			mflg = 0;
8827c478bd9Sstevel@tonic-gate 			continue;
883da6c28aaSamw 		case '/':
884da6c28aaSamw 			saflg++;
885da6c28aaSamw 			if (optarg != NULL) {
886da6c28aaSamw 				if (strcmp(optarg, "c") == 0) {
887da6c28aaSamw 					copt++;
888da6c28aaSamw 					vopt = 0;
889da6c28aaSamw 				} else if (strcmp(optarg, "v") == 0) {
890da6c28aaSamw 					vopt++;
891da6c28aaSamw 					copt = 0;
892da6c28aaSamw 				} else
893da6c28aaSamw 					opterr++;
894da6c28aaSamw 			} else
895da6c28aaSamw 				opterr++;
896da6c28aaSamw 			lflg++;
897da6c28aaSamw 			statreq++;
898da6c28aaSamw 			Cflg = 0;
899da6c28aaSamw 			xflg = 0;
900da6c28aaSamw 			mflg = 0;
901da6c28aaSamw 			continue;
902da6c28aaSamw 		case '%':
903da6c28aaSamw 			tmflg++;
904da6c28aaSamw 			if (optarg != NULL) {
905da6c28aaSamw 				if (strcmp(optarg, "ctime") == 0) {
906da6c28aaSamw 					ctm++;
907da6c28aaSamw 					atm = 0;
908da6c28aaSamw 					mtm = 0;
909da6c28aaSamw 					crtm = 0;
910da6c28aaSamw 				} else if (strcmp(optarg, "atime") == 0) {
911da6c28aaSamw 					atm++;
912da6c28aaSamw 					ctm = 0;
913da6c28aaSamw 					mtm = 0;
914da6c28aaSamw 					crtm = 0;
915da6c28aaSamw 					uflg = 0;
916da6c28aaSamw 					cflg = 0;
917da6c28aaSamw 				} else if (strcmp(optarg, "mtime") == 0) {
918da6c28aaSamw 					mtm++;
919da6c28aaSamw 					atm = 0;
920da6c28aaSamw 					ctm = 0;
921da6c28aaSamw 					crtm = 0;
922da6c28aaSamw 					uflg = 0;
923da6c28aaSamw 					cflg = 0;
924da6c28aaSamw 				} else if (strcmp(optarg, "crtime") == 0) {
925da6c28aaSamw 					crtm++;
926da6c28aaSamw 					atm = 0;
927da6c28aaSamw 					ctm = 0;
928da6c28aaSamw 					mtm = 0;
929da6c28aaSamw 					uflg = 0;
930da6c28aaSamw 					cflg = 0;
931da6c28aaSamw 				} else if (strcmp(optarg, "all") == 0) {
932da6c28aaSamw 					alltm++;
933da6c28aaSamw 					atm = 0;
934da6c28aaSamw 					ctm = 0;
935da6c28aaSamw 					mtm = 0;
936da6c28aaSamw 					crtm = 0;
937da6c28aaSamw 				} else
938da6c28aaSamw 					opterr++;
939da6c28aaSamw 			} else
940da6c28aaSamw 				opterr++;
941da6c28aaSamw 
942da6c28aaSamw 			Sflg = 0;
943da6c28aaSamw 			statreq++;
944da6c28aaSamw 			mflg = 0;
945da6c28aaSamw 			continue;
9467c478bd9Sstevel@tonic-gate 		case '?':
9477c478bd9Sstevel@tonic-gate 			opterr++;
9487c478bd9Sstevel@tonic-gate 			continue;
9497c478bd9Sstevel@tonic-gate 		}
950*5e1c72e1SJason King 
9517c478bd9Sstevel@tonic-gate 	if (opterr) {
9527c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
953*5e1c72e1SJason King 		    "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]"
954da6c28aaSamw 		    "%%[atime | crtime | ctime | mtime | all]"
955da6c28aaSamw 		    " [files]\n"));
9567c478bd9Sstevel@tonic-gate 		exit(2);
9577c478bd9Sstevel@tonic-gate 	}
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 	if (fflg) {
9607c478bd9Sstevel@tonic-gate 		aflg++;
9617c478bd9Sstevel@tonic-gate 		lflg = 0;
9627c478bd9Sstevel@tonic-gate 		sflg = 0;
9637c478bd9Sstevel@tonic-gate 		tflg = 0;
9647c478bd9Sstevel@tonic-gate 		Sflg = 0;
9657c478bd9Sstevel@tonic-gate 		statreq = 0;
9667c478bd9Sstevel@tonic-gate 	}
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	fixedwidth = 2;
9697c478bd9Sstevel@tonic-gate 	if (pflg || Fflg)
9707c478bd9Sstevel@tonic-gate 		fixedwidth++;
9717c478bd9Sstevel@tonic-gate 	if (iflg)
9727c478bd9Sstevel@tonic-gate 		fixedwidth += 11;
9737c478bd9Sstevel@tonic-gate 	if (sflg)
9747c478bd9Sstevel@tonic-gate 		fixedwidth += 5;
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 	if (lflg) {
9777c478bd9Sstevel@tonic-gate 		if (!gflg && !oflg)
9787c478bd9Sstevel@tonic-gate 			gflg = oflg = 1;
9797c478bd9Sstevel@tonic-gate 		else
9807c478bd9Sstevel@tonic-gate 		if (gflg && oflg)
9817c478bd9Sstevel@tonic-gate 			gflg = oflg = 0;
9827c478bd9Sstevel@tonic-gate 		Cflg = mflg = 0;
9837c478bd9Sstevel@tonic-gate 	}
9847c478bd9Sstevel@tonic-gate 
985*5e1c72e1SJason King 	if (!wflg && (Cflg || mflg)) {
9867c478bd9Sstevel@tonic-gate 		char *clptr;
9877c478bd9Sstevel@tonic-gate 		if ((clptr = getenv("COLUMNS")) != NULL)
9887c478bd9Sstevel@tonic-gate 			num_cols = atoi(clptr);
9897c478bd9Sstevel@tonic-gate #ifdef TERMINFO
9907c478bd9Sstevel@tonic-gate 		else {
9917c478bd9Sstevel@tonic-gate 			if (ioctl(1, TIOCGWINSZ, &win) != -1)
9927c478bd9Sstevel@tonic-gate 				num_cols = (win.ws_col == 0 ? 80 : win.ws_col);
9937c478bd9Sstevel@tonic-gate 		}
9947c478bd9Sstevel@tonic-gate #endif
995*5e1c72e1SJason King 	}
996*5e1c72e1SJason King 
9977c478bd9Sstevel@tonic-gate 	if (num_cols < 20 || num_cols > 1000)
9987c478bd9Sstevel@tonic-gate 		/* assume it is an error */
9997c478bd9Sstevel@tonic-gate 		num_cols = 80;
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 	/* allocate space for flist and the associated	*/
10027c478bd9Sstevel@tonic-gate 	/* data structures (lbufs)			*/
10037c478bd9Sstevel@tonic-gate 	maxfils = quantn;
10047c478bd9Sstevel@tonic-gate 	if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) ||
10057c478bd9Sstevel@tonic-gate 	    ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) {
10067c478bd9Sstevel@tonic-gate 		perror("ls");
10077c478bd9Sstevel@tonic-gate 		exit(2);
10087c478bd9Sstevel@tonic-gate 	}
10097c478bd9Sstevel@tonic-gate 	if ((amino = (argc-optind)) == 0) {
10107c478bd9Sstevel@tonic-gate 					/*
10117c478bd9Sstevel@tonic-gate 					 * case when no names are given
10127c478bd9Sstevel@tonic-gate 					 * in ls-command and current
10137c478bd9Sstevel@tonic-gate 					 * directory is to be used
10147c478bd9Sstevel@tonic-gate 					 */
10157c478bd9Sstevel@tonic-gate 		argv[optind] = dotp;
10167c478bd9Sstevel@tonic-gate 	}
10177c478bd9Sstevel@tonic-gate 
10187c478bd9Sstevel@tonic-gate 	for (i = 0; i < (amino ? amino : 1); i++) {
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate 		/*
10217c478bd9Sstevel@tonic-gate 		 * If we are recursing, we need to make sure we don't
10227c478bd9Sstevel@tonic-gate 		 * get into an endless loop.  To keep track of the inodes
10237c478bd9Sstevel@tonic-gate 		 * (actually, just the directories) visited, we
10247c478bd9Sstevel@tonic-gate 		 * maintain a directory ancestry list for a file
10257c478bd9Sstevel@tonic-gate 		 * hierarchy.  As we go deeper into the hierarchy,
10267c478bd9Sstevel@tonic-gate 		 * a parent directory passes its directory list
10277c478bd9Sstevel@tonic-gate 		 * info (device id, inode number, and a pointer to
10287c478bd9Sstevel@tonic-gate 		 * its parent) to each of its children.  As we
10297c478bd9Sstevel@tonic-gate 		 * process a child that is a directory, we save
10307c478bd9Sstevel@tonic-gate 		 * its own personal directory list info.  We then
10317c478bd9Sstevel@tonic-gate 		 * check to see if the child has already been
10327c478bd9Sstevel@tonic-gate 		 * processed by comparing its device id and inode
10337c478bd9Sstevel@tonic-gate 		 * number from its own personal directory list info
10347c478bd9Sstevel@tonic-gate 		 * to that of each of its ancestors.  If there is a
10357c478bd9Sstevel@tonic-gate 		 * match, then we know we've detected a cycle.
10367c478bd9Sstevel@tonic-gate 		 */
10377c478bd9Sstevel@tonic-gate 		if (Rflg) {
10387c478bd9Sstevel@tonic-gate 			/*
10397c478bd9Sstevel@tonic-gate 			 * This is the first parent in this lineage
10407c478bd9Sstevel@tonic-gate 			 * (first in a directory hierarchy), so
10417c478bd9Sstevel@tonic-gate 			 * this parent's parent doesn't exist.  We
10427c478bd9Sstevel@tonic-gate 			 * only initialize myinfo when we are
10437c478bd9Sstevel@tonic-gate 			 * recursing, otherwise it's not used.
10447c478bd9Sstevel@tonic-gate 			 */
10457c478bd9Sstevel@tonic-gate 			if ((myinfo = (struct ditem *)malloc(
10467c478bd9Sstevel@tonic-gate 			    sizeof (struct ditem))) == NULL) {
10477c478bd9Sstevel@tonic-gate 				perror("ls");
10487c478bd9Sstevel@tonic-gate 				exit(2);
10497c478bd9Sstevel@tonic-gate 			} else {
10507c478bd9Sstevel@tonic-gate 				myinfo->dev = 0;
10517c478bd9Sstevel@tonic-gate 				myinfo->ino = 0;
10527c478bd9Sstevel@tonic-gate 				myinfo->parent = NULL;
10537c478bd9Sstevel@tonic-gate 			}
10547c478bd9Sstevel@tonic-gate 		}
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 		if (Cflg || mflg) {
10577c478bd9Sstevel@tonic-gate 			width = strcol((unsigned char *)argv[optind]);
10587c478bd9Sstevel@tonic-gate 			if (width > filewidth)
10597c478bd9Sstevel@tonic-gate 				filewidth = width;
10607c478bd9Sstevel@tonic-gate 		}
10617c478bd9Sstevel@tonic-gate 		if ((ep = gstat((*argv[optind] ? argv[optind] : dotp),
10627c478bd9Sstevel@tonic-gate 		    1, myinfo)) == NULL) {
10637c478bd9Sstevel@tonic-gate 			if (nomocore)
10647c478bd9Sstevel@tonic-gate 				exit(2);
10657c478bd9Sstevel@tonic-gate 			err = 2;
10667c478bd9Sstevel@tonic-gate 			optind++;
10677c478bd9Sstevel@tonic-gate 			continue;
10687c478bd9Sstevel@tonic-gate 		}
10697c478bd9Sstevel@tonic-gate 		ep->ln.namep = (*argv[optind] ? argv[optind] : dotp);
10707c478bd9Sstevel@tonic-gate 		ep->lflags |= ISARG;
10717c478bd9Sstevel@tonic-gate 		optind++;
10727c478bd9Sstevel@tonic-gate 		nargs++;	/* count good arguments stored in flist */
107356798e90Sbasabi 		if (acl_err)
107456798e90Sbasabi 			err = 2;
10757c478bd9Sstevel@tonic-gate 	}
10767c478bd9Sstevel@tonic-gate 	colwidth = fixedwidth + filewidth;
1077*5e1c72e1SJason King 	if (!Uflg)
10787c478bd9Sstevel@tonic-gate 		qsort(flist, (unsigned)nargs, sizeof (struct lbuf *),
10797c478bd9Sstevel@tonic-gate 		    (int (*)(const void *, const void *))compar);
10807c478bd9Sstevel@tonic-gate 	for (i = 0; i < nargs; i++) {
10817c478bd9Sstevel@tonic-gate 		if (flist[i]->ltype == 'd' && dflg == 0 || fflg)
10827c478bd9Sstevel@tonic-gate 			break;
10837c478bd9Sstevel@tonic-gate 	}
1084*5e1c72e1SJason King 
1085*5e1c72e1SJason King 	if (colorflg)
1086*5e1c72e1SJason King 		ls_color_init();
1087*5e1c72e1SJason King 
10887c478bd9Sstevel@tonic-gate 	pem(&flist[0], &flist[i], 0);
10897c478bd9Sstevel@tonic-gate 	for (; i < nargs; i++) {
10907c478bd9Sstevel@tonic-gate 		pdirectory(flist[i]->ln.namep, Rflg ||
10917c478bd9Sstevel@tonic-gate 		    (amino > 1), nargs, 0, flist[i]->ancinfo);
10927c478bd9Sstevel@tonic-gate 		if (nomocore)
10937c478bd9Sstevel@tonic-gate 			exit(2);
10947c478bd9Sstevel@tonic-gate 		/* -R: print subdirectories found */
10957c478bd9Sstevel@tonic-gate 		while (dfirst || cdfirst) {
10967c478bd9Sstevel@tonic-gate 			/* Place direct subdirs on front in right order */
10977c478bd9Sstevel@tonic-gate 			while (cdfirst) {
10987c478bd9Sstevel@tonic-gate 				/* reverse cdfirst onto front of dfirst */
10997c478bd9Sstevel@tonic-gate 				dtemp = cdfirst;
11007c478bd9Sstevel@tonic-gate 				cdfirst = cdfirst -> dc_next;
11017c478bd9Sstevel@tonic-gate 				dtemp -> dc_next = dfirst;
11027c478bd9Sstevel@tonic-gate 				dfirst = dtemp;
11037c478bd9Sstevel@tonic-gate 			}
11047c478bd9Sstevel@tonic-gate 			/* take off first dir on dfirst & print it */
11057c478bd9Sstevel@tonic-gate 			dtemp = dfirst;
11067c478bd9Sstevel@tonic-gate 			dfirst = dfirst->dc_next;
11077c478bd9Sstevel@tonic-gate 			pdirectory(dtemp->dc_name, 1, nargs,
11087c478bd9Sstevel@tonic-gate 			    dtemp->cycle_detected, dtemp->myancinfo);
11097c478bd9Sstevel@tonic-gate 			if (nomocore)
11107c478bd9Sstevel@tonic-gate 				exit(2);
11117c478bd9Sstevel@tonic-gate 			free(dtemp->dc_name);
11127c478bd9Sstevel@tonic-gate 			free(dtemp);
11137c478bd9Sstevel@tonic-gate 		}
11147c478bd9Sstevel@tonic-gate 	}
1115*5e1c72e1SJason King 
11167c478bd9Sstevel@tonic-gate 	return (err);
11177c478bd9Sstevel@tonic-gate }
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate /*
11207c478bd9Sstevel@tonic-gate  * pdirectory: print the directory name, labelling it if title is
11217c478bd9Sstevel@tonic-gate  * nonzero, using lp as the place to start reading in the dir.
11227c478bd9Sstevel@tonic-gate  */
11237c478bd9Sstevel@tonic-gate static void
11247c478bd9Sstevel@tonic-gate pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo)
11257c478bd9Sstevel@tonic-gate {
11267c478bd9Sstevel@tonic-gate 	struct dchain *dp;
11277c478bd9Sstevel@tonic-gate 	struct lbuf *ap;
11287c478bd9Sstevel@tonic-gate 	char *pname;
11297c478bd9Sstevel@tonic-gate 	int j;
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate 	filewidth = 0;
11327c478bd9Sstevel@tonic-gate 	curdir = name;
11337c478bd9Sstevel@tonic-gate 	if (title) {
11347c478bd9Sstevel@tonic-gate 		if (!first)
11357c478bd9Sstevel@tonic-gate 			(void) putc('\n', stdout);
11367c478bd9Sstevel@tonic-gate 		pprintf(name, ":");
11377c478bd9Sstevel@tonic-gate 		new_line();
11387c478bd9Sstevel@tonic-gate 	}
11397c478bd9Sstevel@tonic-gate 	/*
11407c478bd9Sstevel@tonic-gate 	 * If there was a cycle detected, then notify and don't report
11417c478bd9Sstevel@tonic-gate 	 * further.
11427c478bd9Sstevel@tonic-gate 	 */
11437c478bd9Sstevel@tonic-gate 	if (cdetect) {
11447c478bd9Sstevel@tonic-gate 		if (lflg || sflg) {
11457c478bd9Sstevel@tonic-gate 			curcol += printf(gettext("total %d"), 0);
11467c478bd9Sstevel@tonic-gate 			new_line();
11477c478bd9Sstevel@tonic-gate 		}
11487c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
11497c478bd9Sstevel@tonic-gate 		    "ls: cycle detected for %s\n"), name);
11507c478bd9Sstevel@tonic-gate 		return;
11517c478bd9Sstevel@tonic-gate 	}
11527c478bd9Sstevel@tonic-gate 
11537c478bd9Sstevel@tonic-gate 	nfiles = lp;
11547c478bd9Sstevel@tonic-gate 	rddir(name, myinfo);
11557c478bd9Sstevel@tonic-gate 	if (nomocore)
11567c478bd9Sstevel@tonic-gate 		return;
1157*5e1c72e1SJason King 	if (fflg == 0 && Uflg == 0)
11587c478bd9Sstevel@tonic-gate 		qsort(&flist[lp], (unsigned)(nfiles - lp),
11597c478bd9Sstevel@tonic-gate 		    sizeof (struct lbuf *),
11607c478bd9Sstevel@tonic-gate 		    (int (*)(const void *, const void *))compar);
11617c478bd9Sstevel@tonic-gate 	if (Rflg) {
11627c478bd9Sstevel@tonic-gate 		for (j = nfiles - 1; j >= lp; j--) {
11637c478bd9Sstevel@tonic-gate 			ap = flist[j];
11647c478bd9Sstevel@tonic-gate 			if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") &&
11657c478bd9Sstevel@tonic-gate 			    strcmp(ap->ln.lname, "..")) {
11667c478bd9Sstevel@tonic-gate 				dp = malloc(sizeof (struct dchain));
11677c478bd9Sstevel@tonic-gate 				if (dp == NULL) {
11687c478bd9Sstevel@tonic-gate 					perror("ls");
11697c478bd9Sstevel@tonic-gate 					exit(2);
11707c478bd9Sstevel@tonic-gate 				}
11717c478bd9Sstevel@tonic-gate 				pname = makename(curdir, ap->ln.lname);
11727c478bd9Sstevel@tonic-gate 				if ((dp->dc_name = strdup(pname)) == NULL) {
11737c478bd9Sstevel@tonic-gate 					perror("ls");
11747c478bd9Sstevel@tonic-gate 					exit(2);
11757c478bd9Sstevel@tonic-gate 				}
11767c478bd9Sstevel@tonic-gate 				dp->cycle_detected = ap->cycle;
11777c478bd9Sstevel@tonic-gate 				dp->myancinfo = ap->ancinfo;
11787c478bd9Sstevel@tonic-gate 				dp->dc_next = dfirst;
11797c478bd9Sstevel@tonic-gate 				dfirst = dp;
11807c478bd9Sstevel@tonic-gate 			}
11817c478bd9Sstevel@tonic-gate 		}
11827c478bd9Sstevel@tonic-gate 	}
11837c478bd9Sstevel@tonic-gate 	if (lflg || sflg) {
11847c478bd9Sstevel@tonic-gate 		curcol += printf(gettext("total %llu"), tblocks);
11857c478bd9Sstevel@tonic-gate 		new_line();
11867c478bd9Sstevel@tonic-gate 	}
11877c478bd9Sstevel@tonic-gate 	pem(&flist[lp], &flist[nfiles], lflg||sflg);
11887c478bd9Sstevel@tonic-gate }
11897c478bd9Sstevel@tonic-gate 
11907c478bd9Sstevel@tonic-gate /*
11917c478bd9Sstevel@tonic-gate  * pem: print 'em. Print a list of files (e.g. a directory) bounded
11927c478bd9Sstevel@tonic-gate  * by slp and lp.
11937c478bd9Sstevel@tonic-gate  */
11947c478bd9Sstevel@tonic-gate static void
11957c478bd9Sstevel@tonic-gate pem(struct lbuf **slp, struct lbuf **lp, int tot_flag)
11967c478bd9Sstevel@tonic-gate {
11977c478bd9Sstevel@tonic-gate 	long row, nrows, i;
11987c478bd9Sstevel@tonic-gate 	int col, ncols;
11997c478bd9Sstevel@tonic-gate 	struct lbuf **ep;
12007c478bd9Sstevel@tonic-gate 
12017c478bd9Sstevel@tonic-gate 	if (Cflg || mflg) {
12027c478bd9Sstevel@tonic-gate 		if (colwidth > num_cols) {
12037c478bd9Sstevel@tonic-gate 			ncols = 1;
12047c478bd9Sstevel@tonic-gate 		} else {
12057c478bd9Sstevel@tonic-gate 			ncols = num_cols / colwidth;
12067c478bd9Sstevel@tonic-gate 		}
12077c478bd9Sstevel@tonic-gate 	}
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 	if (ncols == 1 || mflg || xflg || !Cflg) {
12107c478bd9Sstevel@tonic-gate 		for (ep = slp; ep < lp; ep++)
12117c478bd9Sstevel@tonic-gate 			pentry(*ep);
12127c478bd9Sstevel@tonic-gate 		new_line();
12137c478bd9Sstevel@tonic-gate 		return;
12147c478bd9Sstevel@tonic-gate 	}
12157c478bd9Sstevel@tonic-gate 	/* otherwise print -C columns */
12167c478bd9Sstevel@tonic-gate 	if (tot_flag) {
12177c478bd9Sstevel@tonic-gate 		slp--;
12187c478bd9Sstevel@tonic-gate 		row = 1;
12197c478bd9Sstevel@tonic-gate 	}
12207c478bd9Sstevel@tonic-gate 	else
12217c478bd9Sstevel@tonic-gate 		row = 0;
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 	nrows = (lp - slp - 1) / ncols + 1;
12247c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrows; i++, row++) {
12257c478bd9Sstevel@tonic-gate 		for (col = 0; col < ncols; col++) {
12267c478bd9Sstevel@tonic-gate 			ep = slp + (nrows * col) + row;
12277c478bd9Sstevel@tonic-gate 			if (ep < lp)
12287c478bd9Sstevel@tonic-gate 				pentry(*ep);
12297c478bd9Sstevel@tonic-gate 		}
12307c478bd9Sstevel@tonic-gate 		new_line();
12317c478bd9Sstevel@tonic-gate 	}
12327c478bd9Sstevel@tonic-gate }
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate /*
12357c478bd9Sstevel@tonic-gate  * print one output entry;
12367c478bd9Sstevel@tonic-gate  * if uid/gid is not found in the appropriate
12377c478bd9Sstevel@tonic-gate  * file(passwd/group), then print uid/gid instead of
12387c478bd9Sstevel@tonic-gate  * user/group name;
12397c478bd9Sstevel@tonic-gate  */
12407c478bd9Sstevel@tonic-gate static void
12417c478bd9Sstevel@tonic-gate pentry(struct lbuf *ap)
12427c478bd9Sstevel@tonic-gate {
12437c478bd9Sstevel@tonic-gate 	struct lbuf *p;
12447c478bd9Sstevel@tonic-gate 	numbuf_t hbuf;
12457c478bd9Sstevel@tonic-gate 	char buf[BUFSIZ];
12467c478bd9Sstevel@tonic-gate 	char *dmark = "";	/* Used if -p or -F option active */
12477c478bd9Sstevel@tonic-gate 	char *cp;
12487c478bd9Sstevel@tonic-gate 
12497c478bd9Sstevel@tonic-gate 	p = ap;
12507c478bd9Sstevel@tonic-gate 	column();
12517c478bd9Sstevel@tonic-gate 	if (iflg)
12527c478bd9Sstevel@tonic-gate 		if (mflg && !lflg)
12537c478bd9Sstevel@tonic-gate 			curcol += printf("%llu ", (long long)p->lnum);
12547c478bd9Sstevel@tonic-gate 		else
12557c478bd9Sstevel@tonic-gate 			curcol += printf("%10llu ", (long long)p->lnum);
12567c478bd9Sstevel@tonic-gate 	if (sflg)
12577c478bd9Sstevel@tonic-gate 		curcol += printf((mflg && !lflg) ? "%lld " :
12587c478bd9Sstevel@tonic-gate 		    (p->lblocks < 10000) ? "%4lld " : "%lld ",
12597c478bd9Sstevel@tonic-gate 		    (p->ltype != 'b' && p->ltype != 'c') ?
12607c478bd9Sstevel@tonic-gate 		    p->lblocks : 0LL);
12617c478bd9Sstevel@tonic-gate 	if (lflg) {
12627c478bd9Sstevel@tonic-gate 		(void) putchar(p->ltype);
12637c478bd9Sstevel@tonic-gate 		curcol++;
12647c478bd9Sstevel@tonic-gate 		pmode(p->lflags);
12657c478bd9Sstevel@tonic-gate 
12667c478bd9Sstevel@tonic-gate 		/* ACL: additional access mode flag */
12677c478bd9Sstevel@tonic-gate 		(void) putchar(p->acl);
12687c478bd9Sstevel@tonic-gate 		curcol++;
12697c478bd9Sstevel@tonic-gate 
12707c478bd9Sstevel@tonic-gate 		curcol += printf("%3lu ", (ulong_t)p->lnl);
12717c478bd9Sstevel@tonic-gate 		if (oflg)
12727c478bd9Sstevel@tonic-gate 			if (!nflg) {
12737c478bd9Sstevel@tonic-gate 				cp = getname(p->luid);
12747c478bd9Sstevel@tonic-gate 				curcol += printf("%-8s ", cp);
12757c478bd9Sstevel@tonic-gate 			} else
12767c478bd9Sstevel@tonic-gate 				curcol += printf("%-8lu ", (ulong_t)p->luid);
12777c478bd9Sstevel@tonic-gate 		if (gflg)
12787c478bd9Sstevel@tonic-gate 			if (!nflg) {
12797c478bd9Sstevel@tonic-gate 				cp = getgroup(p->lgid);
12807c478bd9Sstevel@tonic-gate 				curcol += printf("%-8s ", cp);
12817c478bd9Sstevel@tonic-gate 			} else
12827c478bd9Sstevel@tonic-gate 				curcol += printf("%-8lu ", (ulong_t)p->lgid);
12837c478bd9Sstevel@tonic-gate 		if (p->ltype == 'b' || p->ltype == 'c') {
12847c478bd9Sstevel@tonic-gate 			curcol += printf("%3u, %2u",
12857c478bd9Sstevel@tonic-gate 			    (uint_t)major((dev_t)p->lsize),
12867c478bd9Sstevel@tonic-gate 			    (uint_t)minor((dev_t)p->lsize));
12877c478bd9Sstevel@tonic-gate 		} else if (hflg && (p->lsize >= hscale)) {
12887c478bd9Sstevel@tonic-gate 			curcol += printf("%7s",
12897c478bd9Sstevel@tonic-gate 			    number_to_scaled_string(hbuf, p->lsize, hscale));
12907c478bd9Sstevel@tonic-gate 		} else {
1291*5e1c72e1SJason King 			uint64_t bsize = p->lsize / block_size;
1292*5e1c72e1SJason King 
1293*5e1c72e1SJason King 			/*
1294*5e1c72e1SJason King 			 * Round up only when using blocks > 1 byte, otherwise
1295*5e1c72e1SJason King 			 * 'normal' sizes display 1 byte too large.
1296*5e1c72e1SJason King 			 */
1297*5e1c72e1SJason King 			if (p->lsize % block_size != 0)
1298*5e1c72e1SJason King 				bsize++;
1299*5e1c72e1SJason King 
1300*5e1c72e1SJason King 			curcol += printf("%7" PRIu64, bsize);
13017c478bd9Sstevel@tonic-gate 		}
1302*5e1c72e1SJason King 		format_time(p->lmtime.tv_sec, p->lmtime.tv_nsec);
1303da6c28aaSamw 		/* format extended system attribute time */
1304da6c28aaSamw 		if (tmflg && crtm)
1305da6c28aaSamw 			format_attrtime(p);
13067c478bd9Sstevel@tonic-gate 
1307da6c28aaSamw 		curcol += printf("%s", time_buf);
1308da6c28aaSamw 
1309da6c28aaSamw 	}
13107c478bd9Sstevel@tonic-gate 	/*
13117c478bd9Sstevel@tonic-gate 	 * prevent both "->" and trailing marks
13127c478bd9Sstevel@tonic-gate 	 * from appearing
13137c478bd9Sstevel@tonic-gate 	 */
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate 	if (pflg && p->ltype == 'd')
13167c478bd9Sstevel@tonic-gate 		dmark = "/";
13177c478bd9Sstevel@tonic-gate 
13187c478bd9Sstevel@tonic-gate 	if (Fflg && !(lflg && p->flinkto)) {
13197c478bd9Sstevel@tonic-gate 		if (p->ltype == 'd')
13207c478bd9Sstevel@tonic-gate 			dmark = "/";
13217c478bd9Sstevel@tonic-gate 		else if (p->ltype == 'D')
13227c478bd9Sstevel@tonic-gate 			dmark = ">";
13237c478bd9Sstevel@tonic-gate 		else if (p->ltype == 'p')
13247c478bd9Sstevel@tonic-gate 			dmark = "|";
13257c478bd9Sstevel@tonic-gate 		else if (p->ltype == 'l')
13267c478bd9Sstevel@tonic-gate 			dmark = "@";
13277c478bd9Sstevel@tonic-gate 		else if (p->ltype == 's')
13287c478bd9Sstevel@tonic-gate 			dmark = "=";
1329*5e1c72e1SJason King 		else if (!file_typeflg &&
1330*5e1c72e1SJason King 		    (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)))
13317c478bd9Sstevel@tonic-gate 			dmark = "*";
13327c478bd9Sstevel@tonic-gate 		else
13337c478bd9Sstevel@tonic-gate 			dmark = "";
13347c478bd9Sstevel@tonic-gate 	}
13357c478bd9Sstevel@tonic-gate 
13367c478bd9Sstevel@tonic-gate 	if (lflg && p->flinkto) {
13377c478bd9Sstevel@tonic-gate 		(void) strncpy(buf, " -> ", 4);
13387c478bd9Sstevel@tonic-gate 		(void) strcpy(buf + 4, p->flinkto);
13397c478bd9Sstevel@tonic-gate 		dmark = buf;
13407c478bd9Sstevel@tonic-gate 	}
1341*5e1c72e1SJason King 
1342*5e1c72e1SJason King 	if (colorflg)
1343*5e1c72e1SJason King 		ls_start_color(p);
1344*5e1c72e1SJason King 
13457c478bd9Sstevel@tonic-gate 	if (p->lflags & ISARG) {
13467c478bd9Sstevel@tonic-gate 		if (qflg || bflg)
13477c478bd9Sstevel@tonic-gate 			pprintf(p->ln.namep, dmark);
13487c478bd9Sstevel@tonic-gate 		else {
13497c478bd9Sstevel@tonic-gate 			(void) printf("%s%s", p->ln.namep, dmark);
13507c478bd9Sstevel@tonic-gate 			curcol += strcol((unsigned char *)p->ln.namep);
13517c478bd9Sstevel@tonic-gate 			curcol += strcol((unsigned char *)dmark);
13527c478bd9Sstevel@tonic-gate 		}
13537c478bd9Sstevel@tonic-gate 	} else {
13547c478bd9Sstevel@tonic-gate 		if (qflg || bflg)
13557c478bd9Sstevel@tonic-gate 			pprintf(p->ln.lname, dmark);
13567c478bd9Sstevel@tonic-gate 		else {
13577c478bd9Sstevel@tonic-gate 			(void) printf("%s%s", p->ln.lname, dmark);
13587c478bd9Sstevel@tonic-gate 			curcol += strcol((unsigned char *)p->ln.lname);
13597c478bd9Sstevel@tonic-gate 			curcol += strcol((unsigned char *)dmark);
13607c478bd9Sstevel@tonic-gate 		}
13617c478bd9Sstevel@tonic-gate 	}
1362fa9e4066Sahrens 
1363*5e1c72e1SJason King 	if (colorflg)
1364*5e1c72e1SJason King 		ls_end_color();
1365*5e1c72e1SJason King 
1366da6c28aaSamw 	/* Display extended system attributes */
1367da6c28aaSamw 	if (saflg) {
1368da6c28aaSamw 		int i;
1369da6c28aaSamw 
1370da6c28aaSamw 		new_line();
1371da6c28aaSamw 		(void) printf("	\t{");
1372da6c28aaSamw 		if (p->exttr != NULL) {
1373da6c28aaSamw 			int k = 0;
1374da6c28aaSamw 			for (i = 0; i < sacnt; i++) {
1375da6c28aaSamw 				if (p->exttr[i].name != NULL)
1376da6c28aaSamw 					k++;
1377da6c28aaSamw 			}
1378da6c28aaSamw 			for (i = 0; i < sacnt; i++) {
1379da6c28aaSamw 				if (p->exttr[i].name != NULL) {
1380da6c28aaSamw 					(void) printf("%s", p->exttr[i].name);
1381da6c28aaSamw 					k--;
1382da6c28aaSamw 					if (vopt && (k != 0))
1383da6c28aaSamw 						(void) printf(",");
1384da6c28aaSamw 				}
1385da6c28aaSamw 			}
1386da6c28aaSamw 		}
1387da6c28aaSamw 		(void) printf("}\n");
1388da6c28aaSamw 	}
1389da6c28aaSamw 	/* Display file timestamps and extended system attribute timestamps */
1390da6c28aaSamw 	if (tmflg && alltm) {
1391da6c28aaSamw 		new_line();
1392da6c28aaSamw 		print_time(p);
1393da6c28aaSamw 		new_line();
1394da6c28aaSamw 	}
1395fa9e4066Sahrens 	if (vflg) {
1396fa9e4066Sahrens 		new_line();
1397fa9e4066Sahrens 		if (p->aclp) {
13985a5eeccaSmarks 			acl_printacl(p->aclp, num_cols, Vflg);
1399fa9e4066Sahrens 		}
1400fa9e4066Sahrens 	}
1401da6c28aaSamw 	/* Free extended system attribute lists */
1402da6c28aaSamw 	if (saflg || tmflg)
1403da6c28aaSamw 		free_sysattr(p);
14047c478bd9Sstevel@tonic-gate }
14057c478bd9Sstevel@tonic-gate 
14067c478bd9Sstevel@tonic-gate /* print various r,w,x permissions */
14077c478bd9Sstevel@tonic-gate static void
14087c478bd9Sstevel@tonic-gate pmode(mode_t aflag)
14097c478bd9Sstevel@tonic-gate {
14107c478bd9Sstevel@tonic-gate 	/* these arrays are declared static to allow initializations */
14117c478bd9Sstevel@tonic-gate 	static int	m0[] = { 1, S_IRUSR, 'r', '-' };
14127c478bd9Sstevel@tonic-gate 	static int	m1[] = { 1, S_IWUSR, 'w', '-' };
14137c478bd9Sstevel@tonic-gate 	static int	m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR,
14147c478bd9Sstevel@tonic-gate 	    'x', S_ISUID, 'S', '-' };
14157c478bd9Sstevel@tonic-gate 	static int	m3[] = { 1, S_IRGRP, 'r', '-' };
14167c478bd9Sstevel@tonic-gate 	static int	m4[] = { 1, S_IWGRP, 'w', '-' };
14177c478bd9Sstevel@tonic-gate 	static int	m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP,
14187c478bd9Sstevel@tonic-gate 				'x', S_ISGID|LS_NOTREG, 'S',
14197c478bd9Sstevel@tonic-gate #ifdef XPG4
14207c478bd9Sstevel@tonic-gate 		S_ISGID, 'L', '-'};
14217c478bd9Sstevel@tonic-gate #else
14227c478bd9Sstevel@tonic-gate 		S_ISGID, 'l', '-'};
14237c478bd9Sstevel@tonic-gate #endif
14247c478bd9Sstevel@tonic-gate 	static int	m6[] = { 1, S_IROTH, 'r', '-' };
14257c478bd9Sstevel@tonic-gate 	static int	m7[] = { 1, S_IWOTH, 'w', '-' };
14267c478bd9Sstevel@tonic-gate 	static int	m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH,
14277c478bd9Sstevel@tonic-gate 	    'x', S_ISVTX, 'T', '-'};
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 	static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8};
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 	int **mp;
14327c478bd9Sstevel@tonic-gate 
14337c478bd9Sstevel@tonic-gate 	flags = aflag;
14347c478bd9Sstevel@tonic-gate 	for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++)
14357c478bd9Sstevel@tonic-gate 		selection(*mp);
14367c478bd9Sstevel@tonic-gate }
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate static void
14397c478bd9Sstevel@tonic-gate selection(int *pairp)
14407c478bd9Sstevel@tonic-gate {
14417c478bd9Sstevel@tonic-gate 	int n;
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate 	n = *pairp++;
14447c478bd9Sstevel@tonic-gate 	while (n-->0) {
14457c478bd9Sstevel@tonic-gate 		if ((flags & *pairp) == *pairp) {
14467c478bd9Sstevel@tonic-gate 			pairp++;
14477c478bd9Sstevel@tonic-gate 			break;
14487c478bd9Sstevel@tonic-gate 		} else {
14497c478bd9Sstevel@tonic-gate 			pairp += 2;
14507c478bd9Sstevel@tonic-gate 		}
14517c478bd9Sstevel@tonic-gate 	}
14527c478bd9Sstevel@tonic-gate 	(void) putchar(*pairp);
14537c478bd9Sstevel@tonic-gate 	curcol++;
14547c478bd9Sstevel@tonic-gate }
14557c478bd9Sstevel@tonic-gate 
14567c478bd9Sstevel@tonic-gate /*
14577c478bd9Sstevel@tonic-gate  * column: get to the beginning of the next column.
14587c478bd9Sstevel@tonic-gate  */
14597c478bd9Sstevel@tonic-gate static void
14607c478bd9Sstevel@tonic-gate column(void)
14617c478bd9Sstevel@tonic-gate {
14627c478bd9Sstevel@tonic-gate 	if (curcol == 0)
14637c478bd9Sstevel@tonic-gate 		return;
14647c478bd9Sstevel@tonic-gate 	if (mflg) {
14657c478bd9Sstevel@tonic-gate 		(void) putc(',', stdout);
14667c478bd9Sstevel@tonic-gate 		curcol++;
14677c478bd9Sstevel@tonic-gate 		if (curcol + colwidth + 2 > num_cols) {
14687c478bd9Sstevel@tonic-gate 			(void) putc('\n', stdout);
14697c478bd9Sstevel@tonic-gate 			curcol = 0;
14707c478bd9Sstevel@tonic-gate 			return;
14717c478bd9Sstevel@tonic-gate 		}
14727c478bd9Sstevel@tonic-gate 		(void) putc(' ', stdout);
14737c478bd9Sstevel@tonic-gate 		curcol++;
14747c478bd9Sstevel@tonic-gate 		return;
14757c478bd9Sstevel@tonic-gate 	}
14767c478bd9Sstevel@tonic-gate 	if (Cflg == 0) {
14777c478bd9Sstevel@tonic-gate 		(void) putc('\n', stdout);
14787c478bd9Sstevel@tonic-gate 		curcol = 0;
14797c478bd9Sstevel@tonic-gate 		return;
14807c478bd9Sstevel@tonic-gate 	}
14817c478bd9Sstevel@tonic-gate 	if ((curcol / colwidth + 2) * colwidth > num_cols) {
14827c478bd9Sstevel@tonic-gate 		(void) putc('\n', stdout);
14837c478bd9Sstevel@tonic-gate 		curcol = 0;
14847c478bd9Sstevel@tonic-gate 		return;
14857c478bd9Sstevel@tonic-gate 	}
14867c478bd9Sstevel@tonic-gate 	do {
14877c478bd9Sstevel@tonic-gate 		(void) putc(' ', stdout);
14887c478bd9Sstevel@tonic-gate 		curcol++;
14897c478bd9Sstevel@tonic-gate 	} while (curcol % colwidth);
14907c478bd9Sstevel@tonic-gate }
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate static void
14937c478bd9Sstevel@tonic-gate new_line(void)
14947c478bd9Sstevel@tonic-gate {
14957c478bd9Sstevel@tonic-gate 	if (curcol) {
14967c478bd9Sstevel@tonic-gate 		first = 0;
14977c478bd9Sstevel@tonic-gate 		(void) putc('\n', stdout);
14987c478bd9Sstevel@tonic-gate 		curcol = 0;
14997c478bd9Sstevel@tonic-gate 	}
15007c478bd9Sstevel@tonic-gate }
15017c478bd9Sstevel@tonic-gate 
15027c478bd9Sstevel@tonic-gate /*
15037c478bd9Sstevel@tonic-gate  * read each filename in directory dir and store its
15047c478bd9Sstevel@tonic-gate  * status in flist[nfiles]
15057c478bd9Sstevel@tonic-gate  * use makename() to form pathname dir/filename;
15067c478bd9Sstevel@tonic-gate  */
15077c478bd9Sstevel@tonic-gate static void
15087c478bd9Sstevel@tonic-gate rddir(char *dir, struct ditem *myinfo)
15097c478bd9Sstevel@tonic-gate {
15107c478bd9Sstevel@tonic-gate 	struct dirent *dentry;
15117c478bd9Sstevel@tonic-gate 	DIR *dirf;
15127c478bd9Sstevel@tonic-gate 	int j;
15137c478bd9Sstevel@tonic-gate 	struct lbuf *ep;
15147c478bd9Sstevel@tonic-gate 	int width;
15157c478bd9Sstevel@tonic-gate 
15167c478bd9Sstevel@tonic-gate 	if ((dirf = opendir(dir)) == NULL) {
15177c478bd9Sstevel@tonic-gate 		(void) fflush(stdout);
15187c478bd9Sstevel@tonic-gate 		perror(dir);
15197c478bd9Sstevel@tonic-gate 		err = 2;
15207c478bd9Sstevel@tonic-gate 		return;
15217c478bd9Sstevel@tonic-gate 	} else {
15227c478bd9Sstevel@tonic-gate 		tblocks = 0;
15237c478bd9Sstevel@tonic-gate 		for (;;) {
15247c478bd9Sstevel@tonic-gate 			errno = 0;
15257c478bd9Sstevel@tonic-gate 			if ((dentry = readdir(dirf)) == NULL)
15267c478bd9Sstevel@tonic-gate 				break;
15277c478bd9Sstevel@tonic-gate 			if (aflg == 0 && dentry->d_name[0] == '.' &&
15287c478bd9Sstevel@tonic-gate 			    (Aflg == 0 ||
15297c478bd9Sstevel@tonic-gate 			    dentry->d_name[1] == '\0' ||
15307c478bd9Sstevel@tonic-gate 			    dentry->d_name[1] == '.' &&
15317c478bd9Sstevel@tonic-gate 			    dentry->d_name[2] == '\0'))
15327c478bd9Sstevel@tonic-gate 				/*
15337c478bd9Sstevel@tonic-gate 				 * check for directory items '.', '..',
15347c478bd9Sstevel@tonic-gate 				 *  and items without valid inode-number;
15357c478bd9Sstevel@tonic-gate 				 */
15367c478bd9Sstevel@tonic-gate 				continue;
15377c478bd9Sstevel@tonic-gate 
1538*5e1c72e1SJason King 			/* skip entries ending in ~ if -B was given */
1539*5e1c72e1SJason King 			if (Bflg &&
1540*5e1c72e1SJason King 			    dentry->d_name[strlen(dentry->d_name) - 1] == '~')
1541*5e1c72e1SJason King 				continue;
15427c478bd9Sstevel@tonic-gate 			if (Cflg || mflg) {
15437c478bd9Sstevel@tonic-gate 				width = strcol((unsigned char *)dentry->d_name);
15447c478bd9Sstevel@tonic-gate 				if (width > filewidth)
15457c478bd9Sstevel@tonic-gate 					filewidth = width;
15467c478bd9Sstevel@tonic-gate 			}
15477c478bd9Sstevel@tonic-gate 			ep = gstat(makename(dir, dentry->d_name), 0, myinfo);
15487c478bd9Sstevel@tonic-gate 			if (ep == NULL) {
15497c478bd9Sstevel@tonic-gate 				if (nomocore)
1550da6c28aaSamw 					exit(2);
15517c478bd9Sstevel@tonic-gate 				continue;
15527c478bd9Sstevel@tonic-gate 			} else {
15537c478bd9Sstevel@tonic-gate 				ep->lnum = dentry->d_ino;
15547c478bd9Sstevel@tonic-gate 				for (j = 0; dentry->d_name[j] != '\0'; j++)
15557c478bd9Sstevel@tonic-gate 					ep->ln.lname[j] = dentry->d_name[j];
15567c478bd9Sstevel@tonic-gate 				ep->ln.lname[j] = '\0';
15577c478bd9Sstevel@tonic-gate 			}
15587c478bd9Sstevel@tonic-gate 		}
15597c478bd9Sstevel@tonic-gate 		if (errno) {
15607c478bd9Sstevel@tonic-gate 			int sav_errno = errno;
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
15637c478bd9Sstevel@tonic-gate 			    gettext("ls: error reading directory %s: %s\n"),
15647c478bd9Sstevel@tonic-gate 			    dir, strerror(sav_errno));
15657c478bd9Sstevel@tonic-gate 		}
15667c478bd9Sstevel@tonic-gate 		(void) closedir(dirf);
15677c478bd9Sstevel@tonic-gate 		colwidth = fixedwidth + filewidth;
15687c478bd9Sstevel@tonic-gate 	}
15697c478bd9Sstevel@tonic-gate }
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate /*
15727c478bd9Sstevel@tonic-gate  * Attaching a link to an inode's ancestors.  Search
15737c478bd9Sstevel@tonic-gate  * through the ancestors to check for cycles (an inode which
15747c478bd9Sstevel@tonic-gate  * we have already tracked in this inodes ancestry).  If a cycle
15757c478bd9Sstevel@tonic-gate  * is detected, set the exit code and record the fact so that
15767c478bd9Sstevel@tonic-gate  * it is reported at the right time when printing the directory.
15777c478bd9Sstevel@tonic-gate  * In addition, set the exit code.  Note:  If the -a flag was
15787c478bd9Sstevel@tonic-gate  * specified, we don't want to check for cycles for directories
15797c478bd9Sstevel@tonic-gate  * ending in '/.' or '/..' unless they were specified on the
15807c478bd9Sstevel@tonic-gate  * command line.
15817c478bd9Sstevel@tonic-gate  */
15827c478bd9Sstevel@tonic-gate static void
15837c478bd9Sstevel@tonic-gate record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep,
15847c478bd9Sstevel@tonic-gate     int argfl, struct ditem *myparent)
15857c478bd9Sstevel@tonic-gate {
15867c478bd9Sstevel@tonic-gate 	size_t		file_len;
15877c478bd9Sstevel@tonic-gate 	struct ditem	*myinfo;
15887c478bd9Sstevel@tonic-gate 	struct ditem	*tptr;
15897c478bd9Sstevel@tonic-gate 
15907c478bd9Sstevel@tonic-gate 	file_len = strlen(file);
15917c478bd9Sstevel@tonic-gate 	if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) &&
15927c478bd9Sstevel@tonic-gate 	    NOTPARENTDIR(file, file_len))) {
15937c478bd9Sstevel@tonic-gate 		/*
15947c478bd9Sstevel@tonic-gate 		 * Add this inode's ancestry
15957c478bd9Sstevel@tonic-gate 		 * info and insert it into the
15967c478bd9Sstevel@tonic-gate 		 * ancestry list by pointing
15977c478bd9Sstevel@tonic-gate 		 * back to its parent.  We save
15987c478bd9Sstevel@tonic-gate 		 * it (in rep) with the other info
15997c478bd9Sstevel@tonic-gate 		 * we're gathering for this inode.
16007c478bd9Sstevel@tonic-gate 		 */
16017c478bd9Sstevel@tonic-gate 		if ((myinfo = malloc(
16027c478bd9Sstevel@tonic-gate 		    sizeof (struct ditem))) == NULL) {
16037c478bd9Sstevel@tonic-gate 			perror("ls");
16047c478bd9Sstevel@tonic-gate 			exit(2);
16057c478bd9Sstevel@tonic-gate 		}
16067c478bd9Sstevel@tonic-gate 		myinfo->dev = pstatb->st_dev;
16077c478bd9Sstevel@tonic-gate 		myinfo->ino = pstatb->st_ino;
16087c478bd9Sstevel@tonic-gate 		myinfo->parent = myparent;
16097c478bd9Sstevel@tonic-gate 		rep->ancinfo = myinfo;
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 		/*
16127c478bd9Sstevel@tonic-gate 		 * If this node has the same device id and
16137c478bd9Sstevel@tonic-gate 		 * inode number of one of its ancestors,
16147c478bd9Sstevel@tonic-gate 		 * then we've detected a cycle.
16157c478bd9Sstevel@tonic-gate 		 */
16167c478bd9Sstevel@tonic-gate 		if (myparent != NULL) {
16177c478bd9Sstevel@tonic-gate 			for (tptr = myparent; tptr->parent != NULL;
16187c478bd9Sstevel@tonic-gate 			    tptr = tptr->parent) {
16197c478bd9Sstevel@tonic-gate 				if ((tptr->dev == pstatb->st_dev) &&
16207c478bd9Sstevel@tonic-gate 				    (tptr->ino == pstatb->st_ino)) {
16217c478bd9Sstevel@tonic-gate 					/*
16227c478bd9Sstevel@tonic-gate 					 * Cycle detected for this
16237c478bd9Sstevel@tonic-gate 					 * directory.  Record the fact
16247c478bd9Sstevel@tonic-gate 					 * it is a cycle so we don't
16257c478bd9Sstevel@tonic-gate 					 * try to process this
16267c478bd9Sstevel@tonic-gate 					 * directory as we are
16277c478bd9Sstevel@tonic-gate 					 * walking through the
16287c478bd9Sstevel@tonic-gate 					 * list of directories.
16297c478bd9Sstevel@tonic-gate 					 */
16307c478bd9Sstevel@tonic-gate 					rep->cycle = 1;
16317c478bd9Sstevel@tonic-gate 					err = 2;
16327c478bd9Sstevel@tonic-gate 					break;
16337c478bd9Sstevel@tonic-gate 				}
16347c478bd9Sstevel@tonic-gate 			}
16357c478bd9Sstevel@tonic-gate 		}
16367c478bd9Sstevel@tonic-gate 	}
16377c478bd9Sstevel@tonic-gate }
16387c478bd9Sstevel@tonic-gate 
16397c478bd9Sstevel@tonic-gate /*
164064d425a7Sny155746  * Do re-calculate the mode for group for ACE_T type of acls.
164164d425a7Sny155746  * This is because, if the server's FS happens to be UFS, supporting
164264d425a7Sny155746  * POSIX ACL's, then it does a special calculation of group mode
164364d425a7Sny155746  * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.)
164464d425a7Sny155746  *
164564d425a7Sny155746  * This algorithm is from the NFSv4 ACL Draft. Here a part of that
164664d425a7Sny155746  * algorithm is used for the group mode calculation only.
164764d425a7Sny155746  * What is modified here from the algorithm is that only the
164864d425a7Sny155746  * entries with flags ACE_GROUP are considered. For each entry
164964d425a7Sny155746  * with ACE_GROUP flag, the first occurance of a specific access
165064d425a7Sny155746  * is checked if it is allowed.
1651e2442894Sny155746  * We are not interested in perms for user and other, as they
165264d425a7Sny155746  * were taken from st_mode value.
165364d425a7Sny155746  * We are not interested in a_who field of ACE, as we need just
165464d425a7Sny155746  * unix mode bits for the group.
165564d425a7Sny155746  */
1656e2442894Sny155746 
1657e2442894Sny155746 #define	OWNED_GROUP	(ACE_GROUP | ACE_IDENTIFIER_GROUP)
1658e2442894Sny155746 #define	IS_TYPE_ALLOWED(type)	((type) == ACE_ACCESS_ALLOWED_ACE_TYPE)
1659e2442894Sny155746 
166064d425a7Sny155746 int
166164d425a7Sny155746 grp_mask_to_mode(acl_t *acep)
166264d425a7Sny155746 {
166364d425a7Sny155746 	int mode = 0, seen = 0;
166464d425a7Sny155746 	int acecnt;
1665e2442894Sny155746 	int flags;
166664d425a7Sny155746 	ace_t *ap;
166764d425a7Sny155746 
166864d425a7Sny155746 	acecnt = acl_cnt(acep);
166964d425a7Sny155746 	for (ap = (ace_t *)acl_data(acep); acecnt--; ap++) {
1670e2442894Sny155746 
1671e2442894Sny155746 		if (ap->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE &&
1672e2442894Sny155746 		    ap->a_type != ACE_ACCESS_DENIED_ACE_TYPE)
1673e2442894Sny155746 			continue;
1674e2442894Sny155746 
1675e2442894Sny155746 		if (ap->a_flags & ACE_INHERIT_ONLY_ACE)
1676e2442894Sny155746 			continue;
1677e2442894Sny155746 
1678e2442894Sny155746 		/*
1679e2442894Sny155746 		 * if it is first group@ or first everyone@
1680e2442894Sny155746 		 * for each of read, write and execute, then
1681e2442894Sny155746 		 * that will be the group mode bit.
1682e2442894Sny155746 		 */
1683e2442894Sny155746 		flags = ap->a_flags & ACE_TYPE_FLAGS;
1684e2442894Sny155746 		if (flags == OWNED_GROUP || flags == ACE_EVERYONE) {
168564d425a7Sny155746 			if (ap->a_access_mask & ACE_READ_DATA) {
168664d425a7Sny155746 				if (!(seen & S_IRGRP)) {
168764d425a7Sny155746 					seen |= S_IRGRP;
1688e2442894Sny155746 					if (IS_TYPE_ALLOWED(ap->a_type))
168964d425a7Sny155746 						mode |= S_IRGRP;
169064d425a7Sny155746 				}
169164d425a7Sny155746 			}
169264d425a7Sny155746 			if (ap->a_access_mask & ACE_WRITE_DATA) {
169364d425a7Sny155746 				if (!(seen & S_IWGRP)) {
169464d425a7Sny155746 					seen |= S_IWGRP;
1695e2442894Sny155746 					if (IS_TYPE_ALLOWED(ap->a_type))
169664d425a7Sny155746 						mode |= S_IWGRP;
169764d425a7Sny155746 				}
169864d425a7Sny155746 			}
169964d425a7Sny155746 			if (ap->a_access_mask & ACE_EXECUTE) {
170064d425a7Sny155746 				if (!(seen & S_IXGRP)) {
170164d425a7Sny155746 					seen |= S_IXGRP;
1702e2442894Sny155746 					if (IS_TYPE_ALLOWED(ap->a_type))
170364d425a7Sny155746 						mode |= S_IXGRP;
170464d425a7Sny155746 				}
170564d425a7Sny155746 			}
170664d425a7Sny155746 		}
170764d425a7Sny155746 	}
170864d425a7Sny155746 	return (mode);
170964d425a7Sny155746 }
171064d425a7Sny155746 
171164d425a7Sny155746 /*
17127c478bd9Sstevel@tonic-gate  * get status of file and recomputes tblocks;
17137c478bd9Sstevel@tonic-gate  * argfl = 1 if file is a name in ls-command and = 0
17147c478bd9Sstevel@tonic-gate  * for filename in a directory whose name is an
17157c478bd9Sstevel@tonic-gate  * argument in the command;
17167c478bd9Sstevel@tonic-gate  * stores a pointer in flist[nfiles] and
17177c478bd9Sstevel@tonic-gate  * returns that pointer;
17187c478bd9Sstevel@tonic-gate  * returns NULL if failed;
17197c478bd9Sstevel@tonic-gate  */
17207c478bd9Sstevel@tonic-gate static struct lbuf *
17217c478bd9Sstevel@tonic-gate gstat(char *file, int argfl, struct ditem *myparent)
17227c478bd9Sstevel@tonic-gate {
17237c478bd9Sstevel@tonic-gate 	struct stat statb, statb1;
17247c478bd9Sstevel@tonic-gate 	struct lbuf *rep;
17257c478bd9Sstevel@tonic-gate 	char buf[BUFSIZ];
17267c478bd9Sstevel@tonic-gate 	ssize_t cc;
17277c478bd9Sstevel@tonic-gate 	int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat;
17287c478bd9Sstevel@tonic-gate 	int aclcnt;
1729fa9e4066Sahrens 	int error;
17307c478bd9Sstevel@tonic-gate 	aclent_t *tp;
17317c478bd9Sstevel@tonic-gate 	o_mode_t groupperm, mask;
17327c478bd9Sstevel@tonic-gate 	int grouppermfound, maskfound;
17337c478bd9Sstevel@tonic-gate 
17347c478bd9Sstevel@tonic-gate 	if (nomocore)
17357c478bd9Sstevel@tonic-gate 		return (NULL);
17367c478bd9Sstevel@tonic-gate 
17377c478bd9Sstevel@tonic-gate 	if (nfiles >= maxfils) {
17387c478bd9Sstevel@tonic-gate 		/*
17397c478bd9Sstevel@tonic-gate 		 * all flist/lbuf pair assigned files, time to get some
17407c478bd9Sstevel@tonic-gate 		 * more space
17417c478bd9Sstevel@tonic-gate 		 */
17427c478bd9Sstevel@tonic-gate 		maxfils += quantn;
17437c478bd9Sstevel@tonic-gate 		if (((flist = realloc(flist,
17447c478bd9Sstevel@tonic-gate 		    maxfils * sizeof (struct lbuf *))) == NULL) ||
17457c478bd9Sstevel@tonic-gate 		    ((nxtlbf = malloc(quantn *
17467c478bd9Sstevel@tonic-gate 		    sizeof (struct lbuf))) == NULL)) {
17477c478bd9Sstevel@tonic-gate 			perror("ls");
17487c478bd9Sstevel@tonic-gate 			nomocore = 1;
17497c478bd9Sstevel@tonic-gate 			return (NULL);
17507c478bd9Sstevel@tonic-gate 		}
17517c478bd9Sstevel@tonic-gate 	}
17527c478bd9Sstevel@tonic-gate 
17537c478bd9Sstevel@tonic-gate 	/*
17547c478bd9Sstevel@tonic-gate 	 * nfiles is reset to nargs for each directory
17557c478bd9Sstevel@tonic-gate 	 * that is given as an argument maxn is checked
17567c478bd9Sstevel@tonic-gate 	 * to prevent the assignment of an lbuf to a flist entry
17577c478bd9Sstevel@tonic-gate 	 * that already has one assigned.
17587c478bd9Sstevel@tonic-gate 	 */
17597c478bd9Sstevel@tonic-gate 	if (nfiles >= maxn) {
17607c478bd9Sstevel@tonic-gate 		rep = nxtlbf++;
17617c478bd9Sstevel@tonic-gate 		flist[nfiles++] = rep;
17627c478bd9Sstevel@tonic-gate 		maxn = nfiles;
17637c478bd9Sstevel@tonic-gate 	} else {
17647c478bd9Sstevel@tonic-gate 		rep = flist[nfiles++];
17657c478bd9Sstevel@tonic-gate 	}
176644f31f13Sbasabi 
176744f31f13Sbasabi 	/* Initialize */
176844f31f13Sbasabi 
17697c478bd9Sstevel@tonic-gate 	rep->lflags = (mode_t)0;
17707c478bd9Sstevel@tonic-gate 	rep->flinkto = NULL;
17717c478bd9Sstevel@tonic-gate 	rep->cycle = 0;
177244f31f13Sbasabi 	rep->lat.tv_sec = time(NULL);
177344f31f13Sbasabi 	rep->lat.tv_nsec = 0;
177444f31f13Sbasabi 	rep->lct.tv_sec = time(NULL);
177544f31f13Sbasabi 	rep->lct.tv_nsec = 0;
177644f31f13Sbasabi 	rep->lmt.tv_sec = time(NULL);
177744f31f13Sbasabi 	rep->lmt.tv_nsec = 0;
177844f31f13Sbasabi 	rep->exttr = NULL;
177944f31f13Sbasabi 	rep->extm = NULL;
178044f31f13Sbasabi 
17817c478bd9Sstevel@tonic-gate 	if (argfl || statreq) {
17827c478bd9Sstevel@tonic-gate 		int doacl;
17837c478bd9Sstevel@tonic-gate 
17847c478bd9Sstevel@tonic-gate 		if (lflg)
17857c478bd9Sstevel@tonic-gate 			doacl = 1;
17867c478bd9Sstevel@tonic-gate 		else
17877c478bd9Sstevel@tonic-gate 			doacl = 0;
178844f31f13Sbasabi 
17897c478bd9Sstevel@tonic-gate 		if ((*statf)(file, &statb) < 0) {
17907c478bd9Sstevel@tonic-gate 			if (argfl || errno != ENOENT ||
17917c478bd9Sstevel@tonic-gate 			    (Lflg && lstat(file, &statb) == 0)) {
17927c478bd9Sstevel@tonic-gate 				/*
17937c478bd9Sstevel@tonic-gate 				 * Avoid race between readdir and lstat.
17947c478bd9Sstevel@tonic-gate 				 * Print error message in case of dangling link.
17957c478bd9Sstevel@tonic-gate 				 */
17967c478bd9Sstevel@tonic-gate 				perror(file);
1797*5e1c72e1SJason King 				err = 2;
17987c478bd9Sstevel@tonic-gate 			}
17997c478bd9Sstevel@tonic-gate 			nfiles--;
18007c478bd9Sstevel@tonic-gate 			return (NULL);
18017c478bd9Sstevel@tonic-gate 		}
18027c478bd9Sstevel@tonic-gate 
18037c478bd9Sstevel@tonic-gate 		/*
18047c478bd9Sstevel@tonic-gate 		 * If -H was specified, and the file linked to was
18057c478bd9Sstevel@tonic-gate 		 * not a directory, then we need to get the info
18067c478bd9Sstevel@tonic-gate 		 * for the symlink itself.
18077c478bd9Sstevel@tonic-gate 		 */
18087c478bd9Sstevel@tonic-gate 		if ((Hflg) && (argfl) &&
18097c478bd9Sstevel@tonic-gate 		    ((statb.st_mode & S_IFMT) != S_IFDIR)) {
18107c478bd9Sstevel@tonic-gate 			if (lstat(file, &statb) < 0) {
18117c478bd9Sstevel@tonic-gate 				perror(file);
1812*5e1c72e1SJason King 				err = 2;
18137c478bd9Sstevel@tonic-gate 			}
18147c478bd9Sstevel@tonic-gate 		}
18157c478bd9Sstevel@tonic-gate 
18167c478bd9Sstevel@tonic-gate 		rep->lnum = statb.st_ino;
18177c478bd9Sstevel@tonic-gate 		rep->lsize = statb.st_size;
18187c478bd9Sstevel@tonic-gate 		rep->lblocks = statb.st_blocks;
18197c478bd9Sstevel@tonic-gate 		switch (statb.st_mode & S_IFMT) {
18207c478bd9Sstevel@tonic-gate 		case S_IFDIR:
18217c478bd9Sstevel@tonic-gate 			rep->ltype = 'd';
18227c478bd9Sstevel@tonic-gate 			if (Rflg) {
18237c478bd9Sstevel@tonic-gate 				record_ancestry(file, &statb, rep,
18247c478bd9Sstevel@tonic-gate 				    argfl, myparent);
18257c478bd9Sstevel@tonic-gate 			}
18267c478bd9Sstevel@tonic-gate 			break;
18277c478bd9Sstevel@tonic-gate 		case S_IFBLK:
18287c478bd9Sstevel@tonic-gate 			rep->ltype = 'b';
18297c478bd9Sstevel@tonic-gate 			rep->lsize = (off_t)statb.st_rdev;
18307c478bd9Sstevel@tonic-gate 			break;
18317c478bd9Sstevel@tonic-gate 		case S_IFCHR:
18327c478bd9Sstevel@tonic-gate 			rep->ltype = 'c';
18337c478bd9Sstevel@tonic-gate 			rep->lsize = (off_t)statb.st_rdev;
18347c478bd9Sstevel@tonic-gate 			break;
18357c478bd9Sstevel@tonic-gate 		case S_IFIFO:
18367c478bd9Sstevel@tonic-gate 			rep->ltype = 'p';
18377c478bd9Sstevel@tonic-gate 			break;
18387c478bd9Sstevel@tonic-gate 		case S_IFSOCK:
18397c478bd9Sstevel@tonic-gate 			rep->ltype = 's';
18407c478bd9Sstevel@tonic-gate 			rep->lsize = 0;
18417c478bd9Sstevel@tonic-gate 			break;
18427c478bd9Sstevel@tonic-gate 		case S_IFLNK:
18437c478bd9Sstevel@tonic-gate 			/* symbolic links may not have ACLs, so elide acl() */
18447c478bd9Sstevel@tonic-gate 			if ((Lflg == 0) || (Hflg == 0) ||
18457c478bd9Sstevel@tonic-gate 			    ((Hflg) && (!argfl))) {
18467c478bd9Sstevel@tonic-gate 				doacl = 0;
18477c478bd9Sstevel@tonic-gate 			}
18487c478bd9Sstevel@tonic-gate 			rep->ltype = 'l';
18497c478bd9Sstevel@tonic-gate 			if (lflg) {
18507c478bd9Sstevel@tonic-gate 				cc = readlink(file, buf, BUFSIZ);
18517c478bd9Sstevel@tonic-gate 				if (cc >= 0) {
18527c478bd9Sstevel@tonic-gate 
18537c478bd9Sstevel@tonic-gate 					/*
18547c478bd9Sstevel@tonic-gate 					 * follow the symbolic link
18557c478bd9Sstevel@tonic-gate 					 * to generate the appropriate
18567c478bd9Sstevel@tonic-gate 					 * Fflg marker for the object
18577c478bd9Sstevel@tonic-gate 					 * eg, /bin -> /sym/bin/
18587c478bd9Sstevel@tonic-gate 					 */
18597c478bd9Sstevel@tonic-gate 					if ((Fflg || pflg) &&
18607c478bd9Sstevel@tonic-gate 					    (stat(file, &statb1) >= 0)) {
18617c478bd9Sstevel@tonic-gate 						switch (statb1.st_mode &
18627c478bd9Sstevel@tonic-gate 						    S_IFMT) {
18637c478bd9Sstevel@tonic-gate 						case S_IFDIR:
18647c478bd9Sstevel@tonic-gate 							buf[cc++] = '/';
18657c478bd9Sstevel@tonic-gate 							break;
18667c478bd9Sstevel@tonic-gate 						case S_IFSOCK:
18677c478bd9Sstevel@tonic-gate 							buf[cc++] = '=';
18687c478bd9Sstevel@tonic-gate 							break;
18692236845bSakaplan 						case S_IFDOOR:
18702236845bSakaplan 							buf[cc++] = '>';
18712236845bSakaplan 							break;
18722236845bSakaplan 						case S_IFIFO:
18732236845bSakaplan 							buf[cc++] = '|';
18742236845bSakaplan 							break;
18757c478bd9Sstevel@tonic-gate 						default:
18767c478bd9Sstevel@tonic-gate 							if ((statb1.st_mode &
18777c478bd9Sstevel@tonic-gate 							    ~S_IFMT) &
18787c478bd9Sstevel@tonic-gate 							    (S_IXUSR|S_IXGRP|
18797c478bd9Sstevel@tonic-gate 							    S_IXOTH))
18807c478bd9Sstevel@tonic-gate 								buf[cc++] = '*';
18817c478bd9Sstevel@tonic-gate 							break;
18827c478bd9Sstevel@tonic-gate 						}
18837c478bd9Sstevel@tonic-gate 					}
18847c478bd9Sstevel@tonic-gate 					buf[cc] = '\0';
18857c478bd9Sstevel@tonic-gate 					rep->flinkto = strdup(buf);
18867c478bd9Sstevel@tonic-gate 				}
18877c478bd9Sstevel@tonic-gate 				break;
18887c478bd9Sstevel@tonic-gate 			}
18897c478bd9Sstevel@tonic-gate 
18907c478bd9Sstevel@tonic-gate 			/*
18917c478bd9Sstevel@tonic-gate 			 * ls /sym behaves differently from ls /sym/
18927c478bd9Sstevel@tonic-gate 			 * when /sym is a symbolic link. This is fixed
18937c478bd9Sstevel@tonic-gate 			 * when explicit arguments are specified.
18947c478bd9Sstevel@tonic-gate 			 */
18957c478bd9Sstevel@tonic-gate 
18967c478bd9Sstevel@tonic-gate #ifdef XPG6
18977c478bd9Sstevel@tonic-gate 			/* Do not follow a symlink when -F is specified */
18987c478bd9Sstevel@tonic-gate 			if ((!argfl) || (argfl && Fflg) ||
18997c478bd9Sstevel@tonic-gate 			    (stat(file, &statb1) < 0))
19007c478bd9Sstevel@tonic-gate #else
19017c478bd9Sstevel@tonic-gate 			/* Follow a symlink when -F is specified */
19027c478bd9Sstevel@tonic-gate 			if (!argfl || stat(file, &statb1) < 0)
19037c478bd9Sstevel@tonic-gate #endif /* XPG6 */
19047c478bd9Sstevel@tonic-gate 				break;
19057c478bd9Sstevel@tonic-gate 			if ((statb1.st_mode & S_IFMT) == S_IFDIR) {
19067c478bd9Sstevel@tonic-gate 				statb = statb1;
19077c478bd9Sstevel@tonic-gate 				rep->ltype = 'd';
19087c478bd9Sstevel@tonic-gate 				rep->lsize = statb1.st_size;
19097c478bd9Sstevel@tonic-gate 				if (Rflg) {
19107c478bd9Sstevel@tonic-gate 					record_ancestry(file, &statb, rep,
19117c478bd9Sstevel@tonic-gate 					    argfl, myparent);
19127c478bd9Sstevel@tonic-gate 				}
19137c478bd9Sstevel@tonic-gate 			}
19147c478bd9Sstevel@tonic-gate 			break;
19157c478bd9Sstevel@tonic-gate 		case S_IFDOOR:
19167c478bd9Sstevel@tonic-gate 			rep->ltype = 'D';
19177c478bd9Sstevel@tonic-gate 			break;
19187c478bd9Sstevel@tonic-gate 		case S_IFREG:
19197c478bd9Sstevel@tonic-gate 			rep->ltype = '-';
19207c478bd9Sstevel@tonic-gate 			break;
19217c478bd9Sstevel@tonic-gate 		case S_IFPORT:
19227c478bd9Sstevel@tonic-gate 			rep->ltype = 'P';
19237c478bd9Sstevel@tonic-gate 			break;
19247c478bd9Sstevel@tonic-gate 		default:
19257c478bd9Sstevel@tonic-gate 			rep->ltype = '?';
19267c478bd9Sstevel@tonic-gate 			break;
19277c478bd9Sstevel@tonic-gate 		}
19287c478bd9Sstevel@tonic-gate 		rep->lflags = statb.st_mode & ~S_IFMT;
19297c478bd9Sstevel@tonic-gate 
19307c478bd9Sstevel@tonic-gate 		if (!S_ISREG(statb.st_mode))
19317c478bd9Sstevel@tonic-gate 			rep->lflags |= LS_NOTREG;
19327c478bd9Sstevel@tonic-gate 
193344f31f13Sbasabi 		rep->luid = statb.st_uid;
193444f31f13Sbasabi 		rep->lgid = statb.st_gid;
193544f31f13Sbasabi 		rep->lnl = statb.st_nlink;
193644f31f13Sbasabi 		if (uflg || (tmflg && atm))
193744f31f13Sbasabi 			rep->lmtime = statb.st_atim;
193844f31f13Sbasabi 		else if (cflg || (tmflg && ctm))
193944f31f13Sbasabi 			rep->lmtime = statb.st_ctim;
194044f31f13Sbasabi 		else
194144f31f13Sbasabi 			rep->lmtime = statb.st_mtim;
194244f31f13Sbasabi 		rep->lat = statb.st_atim;
194344f31f13Sbasabi 		rep->lct = statb.st_ctim;
194444f31f13Sbasabi 		rep->lmt = statb.st_mtim;
194544f31f13Sbasabi 
19467c478bd9Sstevel@tonic-gate 		/* ACL: check acl entries count */
19477c478bd9Sstevel@tonic-gate 		if (doacl) {
19487c478bd9Sstevel@tonic-gate 
1949fa9e4066Sahrens 			error = acl_get(file, 0, &rep->aclp);
1950fa9e4066Sahrens 			if (error) {
1951fa9e4066Sahrens 				(void) fprintf(stderr,
1952fa9e4066Sahrens 				    gettext("ls: can't read ACL on %s: %s\n"),
1953fa9e4066Sahrens 				    file, acl_strerror(error));
195444f31f13Sbasabi 				rep->acl = ' ';
195556798e90Sbasabi 				acl_err++;
195644f31f13Sbasabi 				return (rep);
19577c478bd9Sstevel@tonic-gate 			}
19587c478bd9Sstevel@tonic-gate 
1959fa9e4066Sahrens 			rep->acl = ' ';
1960fa9e4066Sahrens 
1961fa9e4066Sahrens 			if (rep->aclp &&
1962fa9e4066Sahrens 			    ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) {
1963fa9e4066Sahrens 				rep->acl = '+';
19647c478bd9Sstevel@tonic-gate 				/*
1965fa9e4066Sahrens 				 * Special handling for ufs aka aclent_t ACL's
1966fa9e4066Sahrens 				 */
196764d425a7Sny155746 				if (acl_type(rep->aclp) == ACLENT_T) {
1968fa9e4066Sahrens 					/*
1969fa9e4066Sahrens 					 * For files with non-trivial acls, the
1970fa9e4066Sahrens 					 * effective group permissions are the
1971fa9e4066Sahrens 					 * intersection of the GROUP_OBJ value
1972fa9e4066Sahrens 					 * and the CLASS_OBJ (acl mask) value.
1973fa9e4066Sahrens 					 * Determine both the GROUP_OBJ and
1974fa9e4066Sahrens 					 * CLASS_OBJ for this file and insert
1975fa9e4066Sahrens 					 * the logical AND of those two values
1976fa9e4066Sahrens 					 * in the group permissions field
1977fa9e4066Sahrens 					 * of the lflags value for this file.
1978fa9e4066Sahrens 					 */
1979fa9e4066Sahrens 
1980fa9e4066Sahrens 					/*
1981fa9e4066Sahrens 					 * Until found in acl list, assume
1982fa9e4066Sahrens 					 * maximum permissions for both group
1983fa9e4066Sahrens 					 * a nd mask.  (Just in case the acl
1984fa9e4066Sahrens 					 * lacks either value for some reason.)
19857c478bd9Sstevel@tonic-gate 					 */
19867c478bd9Sstevel@tonic-gate 					groupperm = 07;
19877c478bd9Sstevel@tonic-gate 					mask = 07;
19887c478bd9Sstevel@tonic-gate 					grouppermfound = 0;
19897c478bd9Sstevel@tonic-gate 					maskfound = 0;
1990fa9e4066Sahrens 					aclcnt = acl_cnt(rep->aclp);
1991fa9e4066Sahrens 					for (tp =
1992fa9e4066Sahrens 					    (aclent_t *)acl_data(rep->aclp);
1993fa9e4066Sahrens 					    aclcnt--; tp++) {
19947c478bd9Sstevel@tonic-gate 						if (tp->a_type == GROUP_OBJ) {
19957c478bd9Sstevel@tonic-gate 							groupperm = tp->a_perm;
19967c478bd9Sstevel@tonic-gate 							grouppermfound = 1;
19977c478bd9Sstevel@tonic-gate 							continue;
19987c478bd9Sstevel@tonic-gate 						}
19997c478bd9Sstevel@tonic-gate 						if (tp->a_type == CLASS_OBJ) {
20007c478bd9Sstevel@tonic-gate 							mask = tp->a_perm;
20017c478bd9Sstevel@tonic-gate 							maskfound = 1;
20027c478bd9Sstevel@tonic-gate 						}
20037c478bd9Sstevel@tonic-gate 						if (grouppermfound && maskfound)
20047c478bd9Sstevel@tonic-gate 							break;
20057c478bd9Sstevel@tonic-gate 					}
20067c478bd9Sstevel@tonic-gate 
20077c478bd9Sstevel@tonic-gate 
20087c478bd9Sstevel@tonic-gate 					/* reset all the group bits */
20097c478bd9Sstevel@tonic-gate 					rep->lflags &= ~S_IRWXG;
20107c478bd9Sstevel@tonic-gate 
20117c478bd9Sstevel@tonic-gate 					/*
2012fa9e4066Sahrens 					 * Now set them to the logical AND of
2013fa9e4066Sahrens 					 * the GROUP_OBJ permissions and the
2014fa9e4066Sahrens 					 * acl mask.
20157c478bd9Sstevel@tonic-gate 					 */
20167c478bd9Sstevel@tonic-gate 
20177c478bd9Sstevel@tonic-gate 					rep->lflags |= (groupperm & mask) << 3;
2018fa9e4066Sahrens 
201964d425a7Sny155746 				} else if (acl_type(rep->aclp) == ACE_T) {
202064d425a7Sny155746 					int mode;
202164d425a7Sny155746 					mode = grp_mask_to_mode(rep->aclp);
202264d425a7Sny155746 					rep->lflags &= ~S_IRWXG;
202364d425a7Sny155746 					rep->lflags |= mode;
2024fa9e4066Sahrens 				}
20257c478bd9Sstevel@tonic-gate 			}
20267c478bd9Sstevel@tonic-gate 
20275a5eeccaSmarks 			if (!vflg && !Vflg && rep->aclp) {
20285a5eeccaSmarks 				acl_free(rep->aclp);
20295a5eeccaSmarks 				rep->aclp = NULL;
20305a5eeccaSmarks 			}
20315a5eeccaSmarks 
20327c478bd9Sstevel@tonic-gate 			if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1)
20337c478bd9Sstevel@tonic-gate 				rep->acl = '@';
2034da6c28aaSamw 
20357c478bd9Sstevel@tonic-gate 		} else
20367c478bd9Sstevel@tonic-gate 			rep->acl = ' ';
20377c478bd9Sstevel@tonic-gate 
20387c478bd9Sstevel@tonic-gate 		/* mask ISARG and other file-type bits */
20397c478bd9Sstevel@tonic-gate 
20407c478bd9Sstevel@tonic-gate 		if (rep->ltype != 'b' && rep->ltype != 'c')
20417c478bd9Sstevel@tonic-gate 			tblocks += rep->lblocks;
2042da6c28aaSamw 
2043da6c28aaSamw 		/* Get extended system attributes */
2044da6c28aaSamw 
2045da6c28aaSamw 		if ((saflg || (tmflg && crtm) || (tmflg && alltm)) &&
2046da6c28aaSamw 		    (sysattr_support(file, _PC_SATTR_EXISTS) == 1)) {
2047da6c28aaSamw 			int i;
2048da6c28aaSamw 
2049da6c28aaSamw 			sacnt = attr_count();
2050da6c28aaSamw 			/*
2051da6c28aaSamw 			 * Allocate 'sacnt' size array to hold extended
2052da6c28aaSamw 			 * system attribute name (verbose) or respective
2053da6c28aaSamw 			 * symbol represenation (compact).
2054da6c28aaSamw 			 */
2055da6c28aaSamw 			rep->exttr = xmalloc(sacnt * sizeof (struct attrb),
2056da6c28aaSamw 			    rep);
2057da6c28aaSamw 
2058da6c28aaSamw 			/* initialize boolean attribute list */
2059da6c28aaSamw 			for (i = 0; i < sacnt; i++)
2060da6c28aaSamw 				rep->exttr[i].name = NULL;
2061da6c28aaSamw 			if (get_sysxattr(file, rep) != 0) {
2062da6c28aaSamw 				(void) fprintf(stderr,
2063da6c28aaSamw 				    gettext("ls:Failed to retrieve "
2064da6c28aaSamw 				    "extended system attribute from "
2065da6c28aaSamw 				    "%s\n"), file);
2066da6c28aaSamw 				rep->exttr[0].name = xmalloc(2, rep);
2067da6c28aaSamw 				(void) strlcpy(rep->exttr[0].name, "?", 2);
2068da6c28aaSamw 			}
2069da6c28aaSamw 		}
20707c478bd9Sstevel@tonic-gate 	}
20717c478bd9Sstevel@tonic-gate 	return (rep);
20727c478bd9Sstevel@tonic-gate }
20737c478bd9Sstevel@tonic-gate 
20747c478bd9Sstevel@tonic-gate /*
20757c478bd9Sstevel@tonic-gate  * returns pathname of the form dir/file;
20767c478bd9Sstevel@tonic-gate  * dir and file are null-terminated strings.
20777c478bd9Sstevel@tonic-gate  */
20787c478bd9Sstevel@tonic-gate static char *
20797c478bd9Sstevel@tonic-gate makename(char *dir, char *file)
20807c478bd9Sstevel@tonic-gate {
20817c478bd9Sstevel@tonic-gate 	/*
20827c478bd9Sstevel@tonic-gate 	 * PATH_MAX is the maximum length of a path name.
20837c478bd9Sstevel@tonic-gate 	 * MAXNAMLEN is the maximum length of any path name component.
20847c478bd9Sstevel@tonic-gate 	 * Allocate space for both, plus the '/' in the middle
20857c478bd9Sstevel@tonic-gate 	 * and the null character at the end.
20867c478bd9Sstevel@tonic-gate 	 * dfile is static as this is returned by makename().
20877c478bd9Sstevel@tonic-gate 	 */
20887c478bd9Sstevel@tonic-gate 	static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1];
20897c478bd9Sstevel@tonic-gate 	char *dp, *fp;
20907c478bd9Sstevel@tonic-gate 
20917c478bd9Sstevel@tonic-gate 	dp = dfile;
20927c478bd9Sstevel@tonic-gate 	fp = dir;
20937c478bd9Sstevel@tonic-gate 	while (*fp)
20947c478bd9Sstevel@tonic-gate 		*dp++ = *fp++;
20957c478bd9Sstevel@tonic-gate 	if (dp > dfile && *(dp - 1) != '/')
20967c478bd9Sstevel@tonic-gate 		*dp++ = '/';
20977c478bd9Sstevel@tonic-gate 	fp = file;
20987c478bd9Sstevel@tonic-gate 	while (*fp)
20997c478bd9Sstevel@tonic-gate 		*dp++ = *fp++;
21007c478bd9Sstevel@tonic-gate 	*dp = '\0';
21017c478bd9Sstevel@tonic-gate 	return (dfile);
21027c478bd9Sstevel@tonic-gate }
21037c478bd9Sstevel@tonic-gate 
21047c478bd9Sstevel@tonic-gate 
21057c478bd9Sstevel@tonic-gate #include <pwd.h>
21067c478bd9Sstevel@tonic-gate #include <grp.h>
21077c478bd9Sstevel@tonic-gate #include <utmpx.h>
21087c478bd9Sstevel@tonic-gate 
21097c478bd9Sstevel@tonic-gate struct	utmpx utmp;
21107c478bd9Sstevel@tonic-gate 
21117c478bd9Sstevel@tonic-gate #define	NMAX	(sizeof (utmp.ut_name))
21127c478bd9Sstevel@tonic-gate #define	SCPYN(a, b)	(void) strncpy(a, b, NMAX)
21137c478bd9Sstevel@tonic-gate 
21147c478bd9Sstevel@tonic-gate 
21157c478bd9Sstevel@tonic-gate struct cachenode {		/* this struct must be zeroed before using */
21167c478bd9Sstevel@tonic-gate 	struct cachenode *lesschild;	/* subtree whose entries < val */
21177c478bd9Sstevel@tonic-gate 	struct cachenode *grtrchild;	/* subtree whose entries > val */
21187c478bd9Sstevel@tonic-gate 	long val;			/* the uid or gid of this entry */
21197c478bd9Sstevel@tonic-gate 	int initted;			/* name has been filled in */
21207c478bd9Sstevel@tonic-gate 	char name[NMAX+1];		/* the string that val maps to */
21217c478bd9Sstevel@tonic-gate };
21227c478bd9Sstevel@tonic-gate static struct cachenode *names, *groups;
21237c478bd9Sstevel@tonic-gate 
21247c478bd9Sstevel@tonic-gate static struct cachenode *
21257c478bd9Sstevel@tonic-gate findincache(struct cachenode **head, long val)
21267c478bd9Sstevel@tonic-gate {
21277c478bd9Sstevel@tonic-gate 	struct cachenode **parent = head;
21287c478bd9Sstevel@tonic-gate 	struct cachenode *c = *parent;
21297c478bd9Sstevel@tonic-gate 
21307c478bd9Sstevel@tonic-gate 	while (c != NULL) {
21317c478bd9Sstevel@tonic-gate 		if (val == c->val) {
21327c478bd9Sstevel@tonic-gate 			/* found it */
21337c478bd9Sstevel@tonic-gate 			return (c);
21347c478bd9Sstevel@tonic-gate 		} else if (val < c->val) {
21357c478bd9Sstevel@tonic-gate 			parent = &c->lesschild;
21367c478bd9Sstevel@tonic-gate 			c = c->lesschild;
21377c478bd9Sstevel@tonic-gate 		} else {
21387c478bd9Sstevel@tonic-gate 			parent = &c->grtrchild;
21397c478bd9Sstevel@tonic-gate 			c = c->grtrchild;
21407c478bd9Sstevel@tonic-gate 		}
21417c478bd9Sstevel@tonic-gate 	}
21427c478bd9Sstevel@tonic-gate 
21437c478bd9Sstevel@tonic-gate 	/* not in the cache, make a new entry for it */
21447c478bd9Sstevel@tonic-gate 	c = calloc(1, sizeof (struct cachenode));
21457c478bd9Sstevel@tonic-gate 	if (c == NULL) {
21467c478bd9Sstevel@tonic-gate 		perror("ls");
21477c478bd9Sstevel@tonic-gate 		exit(2);
21487c478bd9Sstevel@tonic-gate 	}
21497c478bd9Sstevel@tonic-gate 	*parent = c;
21507c478bd9Sstevel@tonic-gate 	c->val = val;
21517c478bd9Sstevel@tonic-gate 	return (c);
21527c478bd9Sstevel@tonic-gate }
21537c478bd9Sstevel@tonic-gate 
21547c478bd9Sstevel@tonic-gate /*
21557c478bd9Sstevel@tonic-gate  * get name from cache, or passwd file for a given uid;
21567c478bd9Sstevel@tonic-gate  * lastuid is set to uid.
21577c478bd9Sstevel@tonic-gate  */
21587c478bd9Sstevel@tonic-gate static char *
21597c478bd9Sstevel@tonic-gate getname(uid_t uid)
21607c478bd9Sstevel@tonic-gate {
21617c478bd9Sstevel@tonic-gate 	struct passwd *pwent;
21627c478bd9Sstevel@tonic-gate 	struct cachenode *c;
21637c478bd9Sstevel@tonic-gate 
21647c478bd9Sstevel@tonic-gate 	if ((uid == lastuid) && lastuname)
21657c478bd9Sstevel@tonic-gate 		return (lastuname);
21667c478bd9Sstevel@tonic-gate 
21677c478bd9Sstevel@tonic-gate 	c = findincache(&names, uid);
21687c478bd9Sstevel@tonic-gate 	if (c->initted == 0) {
21697c478bd9Sstevel@tonic-gate 		if ((pwent = getpwuid(uid)) != NULL) {
21707c478bd9Sstevel@tonic-gate 			SCPYN(&c->name[0], pwent->pw_name);
21717c478bd9Sstevel@tonic-gate 		} else {
21727c478bd9Sstevel@tonic-gate 			(void) sprintf(&c->name[0], "%-8u", (int)uid);
21737c478bd9Sstevel@tonic-gate 		}
21747c478bd9Sstevel@tonic-gate 		c->initted = 1;
21757c478bd9Sstevel@tonic-gate 	}
21767c478bd9Sstevel@tonic-gate 	lastuid = uid;
21777c478bd9Sstevel@tonic-gate 	lastuname = &c->name[0];
21787c478bd9Sstevel@tonic-gate 	return (lastuname);
21797c478bd9Sstevel@tonic-gate }
21807c478bd9Sstevel@tonic-gate 
21817c478bd9Sstevel@tonic-gate /*
21827c478bd9Sstevel@tonic-gate  * get name from cache, or group file for a given gid;
21837c478bd9Sstevel@tonic-gate  * lastgid is set to gid.
21847c478bd9Sstevel@tonic-gate  */
21857c478bd9Sstevel@tonic-gate static char *
21867c478bd9Sstevel@tonic-gate getgroup(gid_t gid)
21877c478bd9Sstevel@tonic-gate {
21887c478bd9Sstevel@tonic-gate 	struct group *grent;
21897c478bd9Sstevel@tonic-gate 	struct cachenode *c;
21907c478bd9Sstevel@tonic-gate 
21917c478bd9Sstevel@tonic-gate 	if ((gid == lastgid) && lastgname)
21927c478bd9Sstevel@tonic-gate 		return (lastgname);
21937c478bd9Sstevel@tonic-gate 
21947c478bd9Sstevel@tonic-gate 	c = findincache(&groups, gid);
21957c478bd9Sstevel@tonic-gate 	if (c->initted == 0) {
21967c478bd9Sstevel@tonic-gate 		if ((grent = getgrgid(gid)) != NULL) {
21977c478bd9Sstevel@tonic-gate 			SCPYN(&c->name[0], grent->gr_name);
21987c478bd9Sstevel@tonic-gate 		} else {
21997c478bd9Sstevel@tonic-gate 			(void) sprintf(&c->name[0], "%-8u", (int)gid);
22007c478bd9Sstevel@tonic-gate 		}
22017c478bd9Sstevel@tonic-gate 		c->initted = 1;
22027c478bd9Sstevel@tonic-gate 	}
22037c478bd9Sstevel@tonic-gate 	lastgid = gid;
22047c478bd9Sstevel@tonic-gate 	lastgname = &c->name[0];
22057c478bd9Sstevel@tonic-gate 	return (lastgname);
22067c478bd9Sstevel@tonic-gate }
22077c478bd9Sstevel@tonic-gate 
22087c478bd9Sstevel@tonic-gate /* return >0 if item pointed by pp2 should appear first */
22097c478bd9Sstevel@tonic-gate static int
22107c478bd9Sstevel@tonic-gate compar(struct lbuf **pp1, struct lbuf **pp2)
22117c478bd9Sstevel@tonic-gate {
22127c478bd9Sstevel@tonic-gate 	struct lbuf *p1, *p2;
22137c478bd9Sstevel@tonic-gate 
22147c478bd9Sstevel@tonic-gate 	p1 = *pp1;
22157c478bd9Sstevel@tonic-gate 	p2 = *pp2;
22167c478bd9Sstevel@tonic-gate 	if (dflg == 0) {
22177c478bd9Sstevel@tonic-gate /*
22187c478bd9Sstevel@tonic-gate  * compare two names in ls-command one of which is file
22197c478bd9Sstevel@tonic-gate  * and the other is a directory;
22207c478bd9Sstevel@tonic-gate  * this portion is not used for comparing files within
22217c478bd9Sstevel@tonic-gate  * a directory name of ls-command;
22227c478bd9Sstevel@tonic-gate  */
22237c478bd9Sstevel@tonic-gate 		if (p1->lflags&ISARG && p1->ltype == 'd') {
22247c478bd9Sstevel@tonic-gate 			if (!(p2->lflags&ISARG && p2->ltype == 'd'))
22257c478bd9Sstevel@tonic-gate 				return (1);
22267c478bd9Sstevel@tonic-gate 		} else {
22277c478bd9Sstevel@tonic-gate 			if (p2->lflags&ISARG && p2->ltype == 'd')
22287c478bd9Sstevel@tonic-gate 				return (-1);
22297c478bd9Sstevel@tonic-gate 		}
22307c478bd9Sstevel@tonic-gate 	}
22317c478bd9Sstevel@tonic-gate 	if (tflg) {
22327c478bd9Sstevel@tonic-gate 		if (p2->lmtime.tv_sec > p1->lmtime.tv_sec)
22337c478bd9Sstevel@tonic-gate 			return (rflg);
22347c478bd9Sstevel@tonic-gate 		else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec)
22357c478bd9Sstevel@tonic-gate 			return (-rflg);
22367c478bd9Sstevel@tonic-gate 		/* times are equal to the sec, check nsec */
22377c478bd9Sstevel@tonic-gate 		if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec)
22387c478bd9Sstevel@tonic-gate 			return (rflg);
22397c478bd9Sstevel@tonic-gate 		else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec)
22407c478bd9Sstevel@tonic-gate 			return (-rflg);
22417c478bd9Sstevel@tonic-gate 		/* if times are equal, fall through and sort by name */
22427c478bd9Sstevel@tonic-gate 	} else if (Sflg) {
22437c478bd9Sstevel@tonic-gate 		/*
22447c478bd9Sstevel@tonic-gate 		 * The size stored in lsize can be either the
22457c478bd9Sstevel@tonic-gate 		 * size or the major minor number (in the case of
22467c478bd9Sstevel@tonic-gate 		 * block and character special devices).  If it's
22477c478bd9Sstevel@tonic-gate 		 * a major minor number, then the size is considered
22487c478bd9Sstevel@tonic-gate 		 * to be zero and we want to fall through and sort
22497c478bd9Sstevel@tonic-gate 		 * by name.  In addition, if the size of p2 is equal
22507c478bd9Sstevel@tonic-gate 		 * to the size of p1 we want to fall through and
22517c478bd9Sstevel@tonic-gate 		 * sort by name.
22527c478bd9Sstevel@tonic-gate 		 */
22537c478bd9Sstevel@tonic-gate 		off_t	p1size = (p1->ltype == 'b') ||
22547c478bd9Sstevel@tonic-gate 		    (p1->ltype == 'c') ? 0 : p1->lsize;
22557c478bd9Sstevel@tonic-gate 		off_t	p2size = (p2->ltype == 'b') ||
22567c478bd9Sstevel@tonic-gate 		    (p2->ltype == 'c') ? 0 : p2->lsize;
22577c478bd9Sstevel@tonic-gate 		if (p2size > p1size) {
22587c478bd9Sstevel@tonic-gate 			return (rflg);
22597c478bd9Sstevel@tonic-gate 		} else if (p2size < p1size) {
22607c478bd9Sstevel@tonic-gate 			return (-rflg);
22617c478bd9Sstevel@tonic-gate 		}
22627c478bd9Sstevel@tonic-gate 		/* Sizes are equal, fall through and sort by name. */
22637c478bd9Sstevel@tonic-gate 	}
22647c478bd9Sstevel@tonic-gate 	return (rflg * strcoll(
22657c478bd9Sstevel@tonic-gate 	    p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname,
22667c478bd9Sstevel@tonic-gate 	    p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname));
22677c478bd9Sstevel@tonic-gate }
22687c478bd9Sstevel@tonic-gate 
22697c478bd9Sstevel@tonic-gate static void
22707c478bd9Sstevel@tonic-gate pprintf(char *s1, char *s2)
22717c478bd9Sstevel@tonic-gate {
22727c478bd9Sstevel@tonic-gate 	csi_pprintf((unsigned char *)s1);
22737c478bd9Sstevel@tonic-gate 	csi_pprintf((unsigned char *)s2);
22747c478bd9Sstevel@tonic-gate }
22757c478bd9Sstevel@tonic-gate 
22767c478bd9Sstevel@tonic-gate static void
22777c478bd9Sstevel@tonic-gate csi_pprintf(unsigned char *s)
22787c478bd9Sstevel@tonic-gate {
22797c478bd9Sstevel@tonic-gate 	unsigned char *cp;
22807c478bd9Sstevel@tonic-gate 	char	c;
22817c478bd9Sstevel@tonic-gate 	int	i;
22827c478bd9Sstevel@tonic-gate 	int	c_len;
22837c478bd9Sstevel@tonic-gate 	int	p_col;
22847c478bd9Sstevel@tonic-gate 	wchar_t	pcode;
22857c478bd9Sstevel@tonic-gate 
22867c478bd9Sstevel@tonic-gate 	if (!qflg && !bflg) {
22877c478bd9Sstevel@tonic-gate 		for (cp = s; *cp != '\0'; cp++) {
22887c478bd9Sstevel@tonic-gate 			(void) putchar(*cp);
22897c478bd9Sstevel@tonic-gate 			curcol++;
22907c478bd9Sstevel@tonic-gate 		}
22917c478bd9Sstevel@tonic-gate 		return;
22927c478bd9Sstevel@tonic-gate 	}
22937c478bd9Sstevel@tonic-gate 
22947c478bd9Sstevel@tonic-gate 	for (cp = s; *cp; ) {
22957c478bd9Sstevel@tonic-gate 		if (isascii(c = *cp)) {
22967c478bd9Sstevel@tonic-gate 			if (!isprint(c)) {
22977c478bd9Sstevel@tonic-gate 				if (qflg) {
22987c478bd9Sstevel@tonic-gate 					c = '?';
22997c478bd9Sstevel@tonic-gate 				} else {
23007c478bd9Sstevel@tonic-gate 					curcol += 3;
23017c478bd9Sstevel@tonic-gate 					(void) putc('\\', stdout);
23027c478bd9Sstevel@tonic-gate 					c = '0' + ((*cp >> 6) & 07);
23037c478bd9Sstevel@tonic-gate 					(void) putc(c, stdout);
23047c478bd9Sstevel@tonic-gate 					c = '0' + ((*cp >> 3) & 07);
23057c478bd9Sstevel@tonic-gate 					(void) putc(c, stdout);
23067c478bd9Sstevel@tonic-gate 					c = '0' + (*cp & 07);
23077c478bd9Sstevel@tonic-gate 				}
23087c478bd9Sstevel@tonic-gate 			}
23097c478bd9Sstevel@tonic-gate 			curcol++;
23107c478bd9Sstevel@tonic-gate 			cp++;
23117c478bd9Sstevel@tonic-gate 			(void) putc(c, stdout);
23127c478bd9Sstevel@tonic-gate 			continue;
23137c478bd9Sstevel@tonic-gate 		}
23147c478bd9Sstevel@tonic-gate 
23157c478bd9Sstevel@tonic-gate 		if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) {
23167c478bd9Sstevel@tonic-gate 			c_len = 1;
23177c478bd9Sstevel@tonic-gate 			goto not_print;
23187c478bd9Sstevel@tonic-gate 		}
23197c478bd9Sstevel@tonic-gate 
23207c478bd9Sstevel@tonic-gate 		if ((p_col = wcwidth(pcode)) > 0) {
23217c478bd9Sstevel@tonic-gate 			(void) putwchar(pcode);
23227c478bd9Sstevel@tonic-gate 			cp += c_len;
23237c478bd9Sstevel@tonic-gate 			curcol += p_col;
23247c478bd9Sstevel@tonic-gate 			continue;
23257c478bd9Sstevel@tonic-gate 		}
23267c478bd9Sstevel@tonic-gate 
23277c478bd9Sstevel@tonic-gate not_print:
23287c478bd9Sstevel@tonic-gate 		for (i = 0; i < c_len; i++) {
23297c478bd9Sstevel@tonic-gate 			if (qflg) {
23307c478bd9Sstevel@tonic-gate 				c = '?';
23317c478bd9Sstevel@tonic-gate 			} else {
23327c478bd9Sstevel@tonic-gate 				curcol += 3;
23337c478bd9Sstevel@tonic-gate 				(void) putc('\\', stdout);
23347c478bd9Sstevel@tonic-gate 				c = '0' + ((*cp >> 6) & 07);
23357c478bd9Sstevel@tonic-gate 				(void) putc(c, stdout);
23367c478bd9Sstevel@tonic-gate 				c = '0' + ((*cp >> 3) & 07);
23377c478bd9Sstevel@tonic-gate 				(void) putc(c, stdout);
23387c478bd9Sstevel@tonic-gate 				c = '0' + (*cp & 07);
23397c478bd9Sstevel@tonic-gate 			}
23407c478bd9Sstevel@tonic-gate 			curcol++;
23417c478bd9Sstevel@tonic-gate 			(void) putc(c, stdout);
23427c478bd9Sstevel@tonic-gate 			cp++;
23437c478bd9Sstevel@tonic-gate 		}
23447c478bd9Sstevel@tonic-gate 	}
23457c478bd9Sstevel@tonic-gate }
23467c478bd9Sstevel@tonic-gate 
23477c478bd9Sstevel@tonic-gate static int
23487c478bd9Sstevel@tonic-gate strcol(unsigned char *s1)
23497c478bd9Sstevel@tonic-gate {
23507c478bd9Sstevel@tonic-gate 	int	w;
23517c478bd9Sstevel@tonic-gate 	int	w_col;
23527c478bd9Sstevel@tonic-gate 	int	len;
23537c478bd9Sstevel@tonic-gate 	wchar_t	wc;
23547c478bd9Sstevel@tonic-gate 
23557c478bd9Sstevel@tonic-gate 	w = 0;
23567c478bd9Sstevel@tonic-gate 	while (*s1) {
23577c478bd9Sstevel@tonic-gate 		if (isascii(*s1)) {
23587c478bd9Sstevel@tonic-gate 			w++;
23597c478bd9Sstevel@tonic-gate 			s1++;
23607c478bd9Sstevel@tonic-gate 			continue;
23617c478bd9Sstevel@tonic-gate 		}
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 		if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
23647c478bd9Sstevel@tonic-gate 			w++;
23657c478bd9Sstevel@tonic-gate 			s1++;
23667c478bd9Sstevel@tonic-gate 			continue;
23677c478bd9Sstevel@tonic-gate 		}
23687c478bd9Sstevel@tonic-gate 
23697c478bd9Sstevel@tonic-gate 		if ((w_col = wcwidth(wc)) < 0)
23707c478bd9Sstevel@tonic-gate 			w_col = len;
23717c478bd9Sstevel@tonic-gate 		s1 += len;
23727c478bd9Sstevel@tonic-gate 		w += w_col;
23737c478bd9Sstevel@tonic-gate 	}
23747c478bd9Sstevel@tonic-gate 	return (w);
23757c478bd9Sstevel@tonic-gate }
23767c478bd9Sstevel@tonic-gate 
23777c478bd9Sstevel@tonic-gate /*
23787c478bd9Sstevel@tonic-gate  * Convert an unsigned long long to a string representation and place the
23797c478bd9Sstevel@tonic-gate  * result in the caller-supplied buffer.
23807c478bd9Sstevel@tonic-gate  *
23817c478bd9Sstevel@tonic-gate  * The number provided is a size in bytes.  The number is first
23827c478bd9Sstevel@tonic-gate  * converted to an integral multiple of 'scale' bytes.  This new
23837c478bd9Sstevel@tonic-gate  * number is then scaled down until it is small enough to be in a good
23847c478bd9Sstevel@tonic-gate  * human readable format, i.e.  in the range 0 thru scale-1.  If the
23857c478bd9Sstevel@tonic-gate  * number used to derive the final number is not a multiple of scale, and
23867c478bd9Sstevel@tonic-gate  * the final number has only a single significant digit, we compute
23877c478bd9Sstevel@tonic-gate  * tenths of units to provide a second significant digit.
23887c478bd9Sstevel@tonic-gate  *
23897c478bd9Sstevel@tonic-gate  * The value "(unsigned long long)-1" is a special case and is always
23907c478bd9Sstevel@tonic-gate  * converted to "-1".
23917c478bd9Sstevel@tonic-gate  *
23927c478bd9Sstevel@tonic-gate  * A pointer to the caller-supplied buffer is returned.
23937c478bd9Sstevel@tonic-gate  */
23947c478bd9Sstevel@tonic-gate static char *
23957c478bd9Sstevel@tonic-gate number_to_scaled_string(
23967c478bd9Sstevel@tonic-gate 			numbuf_t buf,		/* put the result here */
23977c478bd9Sstevel@tonic-gate 			unsigned long long number, /* convert this number */
23987c478bd9Sstevel@tonic-gate 			long scale)
23997c478bd9Sstevel@tonic-gate {
24007c478bd9Sstevel@tonic-gate 	unsigned long long save;
24017c478bd9Sstevel@tonic-gate 	/* Measurement: kilo, mega, giga, tera, peta, exa */
24027c478bd9Sstevel@tonic-gate 	char *uom = "KMGTPE";
24037c478bd9Sstevel@tonic-gate 
24047c478bd9Sstevel@tonic-gate 	if ((long long)number == (long long)-1) {
24057c478bd9Sstevel@tonic-gate 		(void) strlcpy(buf, "-1", sizeof (numbuf_t));
24067c478bd9Sstevel@tonic-gate 		return (buf);
24077c478bd9Sstevel@tonic-gate 	}
24087c478bd9Sstevel@tonic-gate 
24097c478bd9Sstevel@tonic-gate 	save = number;
24107c478bd9Sstevel@tonic-gate 	number = number / scale;
24117c478bd9Sstevel@tonic-gate 
24127c478bd9Sstevel@tonic-gate 	/*
24137c478bd9Sstevel@tonic-gate 	 * Now we have number as a count of scale units.
24147c478bd9Sstevel@tonic-gate 	 * If no further scaling is necessary, we round up as appropriate.
24157c478bd9Sstevel@tonic-gate 	 *
24167c478bd9Sstevel@tonic-gate 	 * The largest value number could have had entering the routine is
24177c478bd9Sstevel@tonic-gate 	 * 16 Exabytes, so running off the end of the uom array should
24187c478bd9Sstevel@tonic-gate 	 * never happen.  We check for that, though, as a guard against
24197c478bd9Sstevel@tonic-gate 	 * a breakdown elsewhere in the algorithm.
24207c478bd9Sstevel@tonic-gate 	 */
24217c478bd9Sstevel@tonic-gate 	if (number < (unsigned long long)scale) {
24227c478bd9Sstevel@tonic-gate 		if ((save % scale) >= (unsigned long long)(scale / 2)) {
24237c478bd9Sstevel@tonic-gate 			if (++number == (unsigned long long)scale) {
24247c478bd9Sstevel@tonic-gate 				uom++;
24257c478bd9Sstevel@tonic-gate 				number = 1;
24267c478bd9Sstevel@tonic-gate 			}
24277c478bd9Sstevel@tonic-gate 		}
24287c478bd9Sstevel@tonic-gate 	} else {
24297c478bd9Sstevel@tonic-gate 		while ((number >= (unsigned long long)scale) && (*uom != 'E')) {
24307c478bd9Sstevel@tonic-gate 			uom++; /* next unit of measurement */
24317c478bd9Sstevel@tonic-gate 			save = number;
24327c478bd9Sstevel@tonic-gate 			/*
24337c478bd9Sstevel@tonic-gate 			 * If we're over half way to the next unit of
24347c478bd9Sstevel@tonic-gate 			 * 'scale' bytes (which means we should round
24357c478bd9Sstevel@tonic-gate 			 * up), then adding half of 'scale' prior to
24367c478bd9Sstevel@tonic-gate 			 * the division will push us into that next
24377c478bd9Sstevel@tonic-gate 			 * unit of scale when we perform the division
24387c478bd9Sstevel@tonic-gate 			 */
24397c478bd9Sstevel@tonic-gate 			number = (number + (scale / 2)) / scale;
24407c478bd9Sstevel@tonic-gate 		}
24417c478bd9Sstevel@tonic-gate 	}
24427c478bd9Sstevel@tonic-gate 
24437c478bd9Sstevel@tonic-gate 	/* check if we should output a decimal place after the point */
24447c478bd9Sstevel@tonic-gate 	if ((save / scale) < 10) {
24457c478bd9Sstevel@tonic-gate 		/* snprintf() will round for us */
24467c478bd9Sstevel@tonic-gate 		float fnum = (float)save / scale;
24477c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c",
24487c478bd9Sstevel@tonic-gate 		    fnum, *uom);
24497c478bd9Sstevel@tonic-gate 	} else {
24507c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (numbuf_t), "%4llu%c",
24517c478bd9Sstevel@tonic-gate 		    number, *uom);
24527c478bd9Sstevel@tonic-gate 	}
24537c478bd9Sstevel@tonic-gate 	return (buf);
24547c478bd9Sstevel@tonic-gate }
2455da6c28aaSamw 
2456da6c28aaSamw /* Get extended system attributes and set the display */
2457da6c28aaSamw 
2458da6c28aaSamw int
2459da6c28aaSamw get_sysxattr(char *fname, struct lbuf *rep)
2460da6c28aaSamw {
2461da6c28aaSamw 	boolean_t	value;
2462da6c28aaSamw 	data_type_t	type;
2463da6c28aaSamw 	int		error;
2464da6c28aaSamw 	char		*name;
2465da6c28aaSamw 	int		i;
2466da6c28aaSamw 
2467da6c28aaSamw 	if ((error = getattrat(AT_FDCWD, XATTR_VIEW_READWRITE, fname,
2468da6c28aaSamw 	    &response)) != 0) {
2469da6c28aaSamw 		perror("ls:getattrat");
2470da6c28aaSamw 		return (error);
2471da6c28aaSamw 	}
2472da6c28aaSamw 
2473da6c28aaSamw 	/*
2474da6c28aaSamw 	 * Allocate 'sacnt' size array to hold extended timestamp
2475da6c28aaSamw 	 * system attributes and initialize the array.
2476da6c28aaSamw 	 */
2477da6c28aaSamw 	rep->extm = xmalloc(sacnt * sizeof (struct attrtm), rep);
2478da6c28aaSamw 	for (i = 0; i < sacnt; i++) {
2479da6c28aaSamw 		rep->extm[i].stm = 0;
2480da6c28aaSamw 		rep->extm[i].nstm = 0;
2481da6c28aaSamw 		rep->extm[i].name = NULL;
2482da6c28aaSamw 	}
2483da6c28aaSamw 	while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
2484da6c28aaSamw 		name = nvpair_name(pair);
2485da6c28aaSamw 		type = nvpair_type(pair);
2486da6c28aaSamw 		if (type == DATA_TYPE_BOOLEAN_VALUE) {
2487da6c28aaSamw 			error = nvpair_value_boolean_value(pair, &value);
2488da6c28aaSamw 			if (error) {
2489da6c28aaSamw 				(void) fprintf(stderr,
2490da6c28aaSamw 				    gettext("nvpair_value_boolean_value "
2491da6c28aaSamw 				    "failed: error = %d\n"), error);
2492da6c28aaSamw 				continue;
2493da6c28aaSamw 			}
2494da6c28aaSamw 			if (name != NULL)
2495da6c28aaSamw 				set_sysattrb_display(name, value, rep);
2496da6c28aaSamw 			continue;
2497da6c28aaSamw 		} else if (type == DATA_TYPE_UINT64_ARRAY) {
2498da6c28aaSamw 			if (name != NULL)
2499da6c28aaSamw 				set_sysattrtm_display(name, rep);
2500da6c28aaSamw 			continue;
2501da6c28aaSamw 		}
2502da6c28aaSamw 	}
2503da6c28aaSamw 	nvlist_free(response);
2504da6c28aaSamw 	return (0);
2505da6c28aaSamw }
2506da6c28aaSamw 
2507da6c28aaSamw /* Set extended system attribute boolean display */
2508da6c28aaSamw 
2509da6c28aaSamw void
2510da6c28aaSamw set_sysattrb_display(char *name, boolean_t val, struct lbuf *rep)
2511da6c28aaSamw {
2512da6c28aaSamw 	f_attr_t	fattr;
2513da6c28aaSamw 	const char	*opt;
2514da6c28aaSamw 	size_t		len;
2515da6c28aaSamw 
2516da6c28aaSamw 	fattr = name_to_attr(name);
2517da6c28aaSamw 	if (fattr != F_ATTR_INVAL && fattr < sacnt) {
2518da6c28aaSamw 		if (vopt) {
2519da6c28aaSamw 			len = strlen(name);
2520da6c28aaSamw 			if (val) {
2521da6c28aaSamw 				rep->exttr[fattr].name = xmalloc(len + 1, rep);
2522da6c28aaSamw 				(void) strlcpy(rep->exttr[fattr].name, name,
2523da6c28aaSamw 				    len + 1);
2524da6c28aaSamw 			} else {
2525da6c28aaSamw 				rep->exttr[fattr].name = xmalloc(len + 3, rep);
2526da6c28aaSamw 				(void) snprintf(rep->exttr[fattr].name, len + 3,
2527da6c28aaSamw 				    "no%s", name);
2528da6c28aaSamw 			}
2529da6c28aaSamw 		} else {
2530da6c28aaSamw 			opt = attr_to_option(fattr);
2531da6c28aaSamw 			if (opt != NULL) {
2532da6c28aaSamw 				len = strlen(opt);
2533da6c28aaSamw 				rep->exttr[fattr].name = xmalloc(len + 1, rep);
2534da6c28aaSamw 				if (val)
2535da6c28aaSamw 					(void) strlcpy(rep->exttr[fattr].name,
2536da6c28aaSamw 					    opt, len + 1);
2537da6c28aaSamw 				else
2538da6c28aaSamw 					(void) strlcpy(rep->exttr[fattr].name,
2539da6c28aaSamw 					    "-", len + 1);
2540da6c28aaSamw 			}
2541da6c28aaSamw 		}
2542da6c28aaSamw 	}
2543da6c28aaSamw }
2544da6c28aaSamw 
2545da6c28aaSamw /* Set extended system attribute timestamp display */
2546da6c28aaSamw 
2547da6c28aaSamw void
2548da6c28aaSamw set_sysattrtm_display(char *name, struct lbuf *rep)
2549da6c28aaSamw {
2550da6c28aaSamw 	uint_t		nelem;
2551da6c28aaSamw 	uint64_t	*value;
2552da6c28aaSamw 	int		i;
2553da6c28aaSamw 	size_t		len;
2554da6c28aaSamw 
2555da6c28aaSamw 	if (nvpair_value_uint64_array(pair, &value, &nelem) == 0) {
2556da6c28aaSamw 		if (*value != NULL) {
2557da6c28aaSamw 			len = strlen(name);
2558da6c28aaSamw 			i = 0;
2559da6c28aaSamw 			while (rep->extm[i].stm != 0 && i < sacnt)
2560da6c28aaSamw 				i++;
2561da6c28aaSamw 			rep->extm[i].stm = value[0];
2562da6c28aaSamw 			rep->extm[i].nstm = value[1];
2563da6c28aaSamw 			rep->extm[i].name = xmalloc(len + 1, rep);
2564da6c28aaSamw 			(void) strlcpy(rep->extm[i].name, name, len + 1);
2565da6c28aaSamw 		}
2566da6c28aaSamw 	}
2567da6c28aaSamw }
2568da6c28aaSamw 
2569da6c28aaSamw void
2570*5e1c72e1SJason King format_time(time_t sec, time_t nsec)
2571da6c28aaSamw {
2572*5e1c72e1SJason King 	const char *fstr = time_fmt_new;
2573da6c28aaSamw 	char fmt_buf[FMTSIZE];
2574da6c28aaSamw 
2575*5e1c72e1SJason King 	if (Eflg) {
2576*5e1c72e1SJason King 		(void) snprintf(fmt_buf, FMTSIZE, fstr, nsec);
2577*5e1c72e1SJason King 		(void) strftime(time_buf, sizeof (time_buf), fmt_buf,
2578*5e1c72e1SJason King 		    localtime(&sec));
2579*5e1c72e1SJason King 		return;
2580da6c28aaSamw 	}
2581da6c28aaSamw 
2582*5e1c72e1SJason King 	if (sec < year || sec > now)
2583*5e1c72e1SJason King 		fstr = time_fmt_old;
2584*5e1c72e1SJason King 
2585*5e1c72e1SJason King 	/* if a custom time was specified, shouldn't be localized */
2586*5e1c72e1SJason King 	(void) strftime(time_buf, sizeof (time_buf),
2587*5e1c72e1SJason King 	    (time_custom == 0) ? dcgettext(NULL, fstr, LC_TIME) : fstr,
2588*5e1c72e1SJason King 	    localtime(&sec));
2589*5e1c72e1SJason King }
2590da6c28aaSamw 
2591da6c28aaSamw void
2592da6c28aaSamw format_attrtime(struct lbuf *p)
2593da6c28aaSamw {
2594da6c28aaSamw 	int tmattr = 0;
2595da6c28aaSamw 	int i;
2596da6c28aaSamw 
2597da6c28aaSamw 	if (p->extm != NULL) {
2598da6c28aaSamw 		for (i = 0; i < sacnt; i++) {
2599da6c28aaSamw 			if (p->extm[i].name != NULL) {
2600da6c28aaSamw 				tmattr = 1;
2601da6c28aaSamw 				break;
2602da6c28aaSamw 			}
2603da6c28aaSamw 		}
2604da6c28aaSamw 	}
2605*5e1c72e1SJason King 
2606da6c28aaSamw 	if (tmattr) {
2607*5e1c72e1SJason King 		const char *old_save = time_fmt_old;
2608*5e1c72e1SJason King 		const char *new_save = time_fmt_new;
2609*5e1c72e1SJason King 
2610*5e1c72e1SJason King 		/* Eflg always sets format to FORMAT_ISO_FULL */
2611*5e1c72e1SJason King 		if (!Eflg && !time_custom) {
2612*5e1c72e1SJason King 			time_fmt_old = FORMAT_OLD;
2613*5e1c72e1SJason King 			time_fmt_new = FORMAT_NEW;
2614da6c28aaSamw 		}
2615*5e1c72e1SJason King 
2616*5e1c72e1SJason King 		format_time((time_t)p->extm[i].stm, (time_t)p->extm[i].nstm);
2617*5e1c72e1SJason King 
2618*5e1c72e1SJason King 		time_fmt_old = old_save;
2619*5e1c72e1SJason King 		time_fmt_new = new_save;
2620da6c28aaSamw 	}
2621da6c28aaSamw }
2622da6c28aaSamw 
2623da6c28aaSamw void
2624da6c28aaSamw print_time(struct lbuf *p)
2625da6c28aaSamw {
2626*5e1c72e1SJason King 	const char *old_save = time_fmt_old;
2627*5e1c72e1SJason King 	const char *new_save = time_fmt_new;
2628*5e1c72e1SJason King 
2629da6c28aaSamw 	int i = 0;
2630da6c28aaSamw 
2631*5e1c72e1SJason King 	if (!Eflg) {
2632*5e1c72e1SJason King 		time_fmt_old = FORMAT_LONG;
2633*5e1c72e1SJason King 		time_fmt_new = FORMAT_LONG;
2634*5e1c72e1SJason King 	}
2635*5e1c72e1SJason King 
2636da6c28aaSamw 	new_line();
2637*5e1c72e1SJason King 	format_time(p->lat.tv_sec, p->lat.tv_nsec);
2638*5e1c72e1SJason King 	(void) printf("         timestamp: atime        %s\n", time_buf);
2639*5e1c72e1SJason King 	format_time(p->lct.tv_sec, p->lct.tv_nsec);
2640*5e1c72e1SJason King 	(void) printf("         timestamp: ctime        %s\n", time_buf);
2641*5e1c72e1SJason King 	format_time(p->lmt.tv_sec, p->lmt.tv_nsec);
2642*5e1c72e1SJason King 	(void) printf("         timestamp: mtime        %s\n", time_buf);
2643da6c28aaSamw 	if (p->extm != NULL) {
2644da6c28aaSamw 		while (p->extm[i].nstm != 0 && i < sacnt) {
2645*5e1c72e1SJason King 			format_time(p->extm[i].stm, p->extm[i].nstm);
2646da6c28aaSamw 			if (p->extm[i].name != NULL) {
2647da6c28aaSamw 				(void) printf("         timestamp:"
2648da6c28aaSamw 				    " %s        %s\n",
2649da6c28aaSamw 				    p->extm[i].name, time_buf);
2650da6c28aaSamw 			}
2651da6c28aaSamw 			i++;
2652da6c28aaSamw 		}
2653da6c28aaSamw 	}
2654*5e1c72e1SJason King 
2655*5e1c72e1SJason King 	time_fmt_old = old_save;
2656*5e1c72e1SJason King 	time_fmt_new = new_save;
2657*5e1c72e1SJason King }
2658*5e1c72e1SJason King 
2659*5e1c72e1SJason King /*
2660*5e1c72e1SJason King  * Check if color definition applies to entry, returns 1 if yes, 0 if no
2661*5e1c72e1SJason King  */
2662*5e1c72e1SJason King static int
2663*5e1c72e1SJason King color_match(struct lbuf *entry, ls_color_t *color)
2664*5e1c72e1SJason King {
2665*5e1c72e1SJason King 	switch (color->ftype) {
2666*5e1c72e1SJason King 	case LS_PAT:
2667*5e1c72e1SJason King 	{
2668*5e1c72e1SJason King 		char	*fname;
2669*5e1c72e1SJason King 		size_t	fname_len, sfx_len;
2670*5e1c72e1SJason King 
2671*5e1c72e1SJason King 		if (entry->lflags & ISARG)
2672*5e1c72e1SJason King 			fname = entry->ln.namep;
2673*5e1c72e1SJason King 		else
2674*5e1c72e1SJason King 			fname = entry->ln.lname;
2675*5e1c72e1SJason King 
2676*5e1c72e1SJason King 		fname_len = strlen(fname);
2677*5e1c72e1SJason King 		sfx_len = strlen(color->sfx);
2678*5e1c72e1SJason King 		if (sfx_len > fname_len)
2679*5e1c72e1SJason King 			return (0);
2680*5e1c72e1SJason King 
2681*5e1c72e1SJason King 		if (strcmp(color->sfx, fname + fname_len - sfx_len) == 0)
2682*5e1c72e1SJason King 			return (1);
2683*5e1c72e1SJason King 		else
2684*5e1c72e1SJason King 			return (0);
2685*5e1c72e1SJason King 	}
2686*5e1c72e1SJason King 
2687*5e1c72e1SJason King 	case LS_NORMAL:
2688*5e1c72e1SJason King 		return (1);
2689*5e1c72e1SJason King 
2690*5e1c72e1SJason King 	case LS_FILE:
2691*5e1c72e1SJason King 		return ((entry->ltype == '-'));
2692*5e1c72e1SJason King 
2693*5e1c72e1SJason King 	case LS_DIR:
2694*5e1c72e1SJason King 		return ((entry->ltype == 'd'));
2695*5e1c72e1SJason King 
2696*5e1c72e1SJason King 	case LS_LINK:
2697*5e1c72e1SJason King 		return ((entry->ltype == 'l'));
2698*5e1c72e1SJason King 
2699*5e1c72e1SJason King 	case LS_FIFO:
2700*5e1c72e1SJason King 		return ((entry->ltype == 'p'));
2701*5e1c72e1SJason King 
2702*5e1c72e1SJason King 	case LS_SOCK:
2703*5e1c72e1SJason King 		return ((entry->ltype == 's'));
2704*5e1c72e1SJason King 
2705*5e1c72e1SJason King 	case LS_DOOR:
2706*5e1c72e1SJason King 		return ((entry->ltype == 'D'));
2707*5e1c72e1SJason King 
2708*5e1c72e1SJason King 	case LS_BLK:
2709*5e1c72e1SJason King 		return ((entry->ltype == 'b'));
2710*5e1c72e1SJason King 
2711*5e1c72e1SJason King 	case LS_CHR:
2712*5e1c72e1SJason King 		return ((entry->ltype == 'c'));
2713*5e1c72e1SJason King 
2714*5e1c72e1SJason King 	case LS_PORT:
2715*5e1c72e1SJason King 		return ((entry->ltype == 'P'));
2716*5e1c72e1SJason King 
2717*5e1c72e1SJason King 	case LS_ORPHAN:
2718*5e1c72e1SJason King 	{
2719*5e1c72e1SJason King 		struct stat st;
2720*5e1c72e1SJason King 		int rc;
2721*5e1c72e1SJason King 
2722*5e1c72e1SJason King 		if (entry->ltype != 'l')
2723*5e1c72e1SJason King 			return (0);
2724*5e1c72e1SJason King 		if (entry->flinkto == NULL)
2725*5e1c72e1SJason King 			return (1);
2726*5e1c72e1SJason King 
2727*5e1c72e1SJason King 		if (entry->lflags & ISARG)
2728*5e1c72e1SJason King 			rc = stat(entry->ln.namep, &st);
2729*5e1c72e1SJason King 		else
2730*5e1c72e1SJason King 			rc = stat(entry->ln.lname, &st);
2731*5e1c72e1SJason King 
2732*5e1c72e1SJason King 		if (rc == -1 && errno == ENOENT)
2733*5e1c72e1SJason King 			return (1);
2734*5e1c72e1SJason King 
2735*5e1c72e1SJason King 		return (0);
2736*5e1c72e1SJason King 	}
2737*5e1c72e1SJason King 
2738*5e1c72e1SJason King 	case LS_SETUID:
2739*5e1c72e1SJason King 		return (entry->ltype != 'l' && (entry->lflags & (S_ISUID)));
2740*5e1c72e1SJason King 
2741*5e1c72e1SJason King 	case LS_SETGID:
2742*5e1c72e1SJason King 		return (entry->ltype != 'l' && (entry->lflags & (S_ISGID)));
2743*5e1c72e1SJason King 
2744*5e1c72e1SJason King 	case LS_STICKY_OTHER_WRITABLE:
2745*5e1c72e1SJason King 		return (entry->ltype != 'l' &&
2746*5e1c72e1SJason King 		    (entry->lflags & (S_IWOTH|S_ISVTX)));
2747*5e1c72e1SJason King 
2748*5e1c72e1SJason King 	case LS_OTHER_WRITABLE:
2749*5e1c72e1SJason King 		return (entry->ltype != 'l' && (entry->lflags & (S_IWOTH)));
2750*5e1c72e1SJason King 
2751*5e1c72e1SJason King 	case LS_STICKY:
2752*5e1c72e1SJason King 		return (entry->ltype != 'l' && (entry->lflags & (S_ISVTX)));
2753*5e1c72e1SJason King 
2754*5e1c72e1SJason King 	case LS_EXEC:
2755*5e1c72e1SJason King 		return (entry->ltype != 'l' &&
2756*5e1c72e1SJason King 		    (entry->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)));
2757*5e1c72e1SJason King 	}
2758*5e1c72e1SJason King 
2759*5e1c72e1SJason King 	return (0);
2760*5e1c72e1SJason King }
2761*5e1c72e1SJason King 
2762*5e1c72e1SJason King static void
2763*5e1c72e1SJason King dump_color(ls_color_t *c)
2764*5e1c72e1SJason King {
2765*5e1c72e1SJason King 	if (c == NULL)
2766*5e1c72e1SJason King 		return;
2767*5e1c72e1SJason King 
2768*5e1c72e1SJason King 	(void) printf("\n\ttype: ");
2769*5e1c72e1SJason King 	switch (c->ftype) {
2770*5e1c72e1SJason King 	case LS_NORMAL:
2771*5e1c72e1SJason King 		(void) printf("LS_NORMAL");
2772*5e1c72e1SJason King 		break;
2773*5e1c72e1SJason King 	case LS_FILE:
2774*5e1c72e1SJason King 		(void) printf("LS_FILE");
2775*5e1c72e1SJason King 		break;
2776*5e1c72e1SJason King 	case LS_EXEC:
2777*5e1c72e1SJason King 		(void) printf("LS_EXEC");
2778*5e1c72e1SJason King 		break;
2779*5e1c72e1SJason King 	case LS_DIR:
2780*5e1c72e1SJason King 		(void) printf("LS_DIR");
2781*5e1c72e1SJason King 		break;
2782*5e1c72e1SJason King 	case LS_LINK:
2783*5e1c72e1SJason King 		(void) printf("LS_LINK");
2784*5e1c72e1SJason King 		break;
2785*5e1c72e1SJason King 
2786*5e1c72e1SJason King 	case LS_FIFO:
2787*5e1c72e1SJason King 		(void) printf("LS_FIFO");
2788*5e1c72e1SJason King 		break;
2789*5e1c72e1SJason King 
2790*5e1c72e1SJason King 	case LS_SOCK:
2791*5e1c72e1SJason King 		(void) printf("LS_SOCK");
2792*5e1c72e1SJason King 		break;
2793*5e1c72e1SJason King 
2794*5e1c72e1SJason King 	case LS_DOOR:
2795*5e1c72e1SJason King 		(void) printf("LS_DOOR");
2796*5e1c72e1SJason King 		break;
2797*5e1c72e1SJason King 
2798*5e1c72e1SJason King 	case LS_BLK:
2799*5e1c72e1SJason King 		(void) printf("LS_BLK");
2800*5e1c72e1SJason King 		break;
2801*5e1c72e1SJason King 
2802*5e1c72e1SJason King 	case LS_CHR:
2803*5e1c72e1SJason King 		(void) printf("LS_CHR");
2804*5e1c72e1SJason King 		break;
2805*5e1c72e1SJason King 
2806*5e1c72e1SJason King 	case LS_PORT:
2807*5e1c72e1SJason King 		(void) printf("LS_PORT");
2808*5e1c72e1SJason King 		break;
2809*5e1c72e1SJason King 
2810*5e1c72e1SJason King 	case LS_STICKY:
2811*5e1c72e1SJason King 		(void) printf("LS_STICKY");
2812*5e1c72e1SJason King 		break;
2813*5e1c72e1SJason King 
2814*5e1c72e1SJason King 	case LS_ORPHAN:
2815*5e1c72e1SJason King 		(void) printf("LS_ORPHAN");
2816*5e1c72e1SJason King 		break;
2817*5e1c72e1SJason King 
2818*5e1c72e1SJason King 	case LS_SETGID:
2819*5e1c72e1SJason King 		(void) printf("LS_SETGID");
2820*5e1c72e1SJason King 		break;
2821*5e1c72e1SJason King 
2822*5e1c72e1SJason King 	case LS_SETUID:
2823*5e1c72e1SJason King 		(void) printf("LS_SETUID");
2824*5e1c72e1SJason King 		break;
2825*5e1c72e1SJason King 
2826*5e1c72e1SJason King 	case LS_OTHER_WRITABLE:
2827*5e1c72e1SJason King 		(void) printf("LS_OTHER_WRITABLE");
2828*5e1c72e1SJason King 		break;
2829*5e1c72e1SJason King 
2830*5e1c72e1SJason King 	case LS_STICKY_OTHER_WRITABLE:
2831*5e1c72e1SJason King 		(void) printf("LS_STICKY_OTHER_WRITABLE");
2832*5e1c72e1SJason King 		break;
2833*5e1c72e1SJason King 
2834*5e1c72e1SJason King 	case LS_PAT:
2835*5e1c72e1SJason King 		(void) printf("LS_PAT\n");
2836*5e1c72e1SJason King 		(void) printf("\tpattern: %s", c->sfx);
2837*5e1c72e1SJason King 		break;
2838*5e1c72e1SJason King 	}
2839*5e1c72e1SJason King 	(void) printf("\n");
2840*5e1c72e1SJason King 	(void) printf("\tattr: %d\n", c->attr);
2841*5e1c72e1SJason King 	(void) printf("\tfg: %d\n", c->fg);
2842*5e1c72e1SJason King 	(void) printf("\tbg: %d\n", c->bg);
2843*5e1c72e1SJason King 	(void) printf("\t");
2844*5e1c72e1SJason King }
2845*5e1c72e1SJason King 
2846*5e1c72e1SJason King static ls_color_t *
2847*5e1c72e1SJason King get_color_attr(struct lbuf *l)
2848*5e1c72e1SJason King {
2849*5e1c72e1SJason King 	int i;
2850*5e1c72e1SJason King 
2851*5e1c72e1SJason King 	/*
2852*5e1c72e1SJason King 	 * Colors are sorted from most general lsc_colors[0] to most specific
2853*5e1c72e1SJason King 	 * lsc_colors[lsc_ncolors - 1] by ls_color_init().  Start search with
2854*5e1c72e1SJason King 	 * most specific color rule and work towards most general.
2855*5e1c72e1SJason King 	 */
2856*5e1c72e1SJason King 	for (i = lsc_ncolors - 1; i >= 0; --i)
2857*5e1c72e1SJason King 		if (color_match(l, &lsc_colors[i]))
2858*5e1c72e1SJason King 			return (&lsc_colors[i]);
2859*5e1c72e1SJason King 
2860*5e1c72e1SJason King 	return (NULL);
2861*5e1c72e1SJason King }
2862*5e1c72e1SJason King 
2863*5e1c72e1SJason King static void
2864*5e1c72e1SJason King ls_tprint(char *str, long int p1, long int p2, long int p3, long int p4,
2865*5e1c72e1SJason King     long int p5, long int p6, long int p7, long int p8, long int p9)
2866*5e1c72e1SJason King {
2867*5e1c72e1SJason King 	char *s;
2868*5e1c72e1SJason King 
2869*5e1c72e1SJason King 	if (str == NULL)
2870*5e1c72e1SJason King 		return;
2871*5e1c72e1SJason King 
2872*5e1c72e1SJason King 	s = tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
2873*5e1c72e1SJason King 
2874*5e1c72e1SJason King 	if (s != NULL)
2875*5e1c72e1SJason King 		(void) putp(s);
2876*5e1c72e1SJason King }
2877*5e1c72e1SJason King 
2878*5e1c72e1SJason King static void
2879*5e1c72e1SJason King ls_start_color(struct lbuf *l)
2880*5e1c72e1SJason King {
2881*5e1c72e1SJason King 	ls_color_t *c = get_color_attr(l);
2882*5e1c72e1SJason King 
2883*5e1c72e1SJason King 	if (c == NULL)
2884*5e1c72e1SJason King 		return;
2885*5e1c72e1SJason King 
2886*5e1c72e1SJason King 	if (lsc_debug)
2887*5e1c72e1SJason King 		lsc_match = c;
2888*5e1c72e1SJason King 
2889*5e1c72e1SJason King 	if (c->attr & LSA_BOLD)
2890*5e1c72e1SJason King 		ls_tprint(lsc_bold, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2891*5e1c72e1SJason King 	if (c->attr & LSA_UNDERSCORE)
2892*5e1c72e1SJason King 		ls_tprint(lsc_underline, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2893*5e1c72e1SJason King 	if (c->attr & LSA_BLINK)
2894*5e1c72e1SJason King 		ls_tprint(lsc_blink, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2895*5e1c72e1SJason King 	if (c->attr & LSA_REVERSE)
2896*5e1c72e1SJason King 		ls_tprint(lsc_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2897*5e1c72e1SJason King 	if (c->attr & LSA_CONCEALED)
2898*5e1c72e1SJason King 		ls_tprint(lsc_concealed, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2899*5e1c72e1SJason King 	if (c->attr == LSA_NONE)
2900*5e1c72e1SJason King 		ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2901*5e1c72e1SJason King 
2902*5e1c72e1SJason King 	if (c->fg != -1)
2903*5e1c72e1SJason King 		ls_tprint(lsc_setfg, c->fg, 0, 0, 0, 0, 0, 0, 0, 0);
2904*5e1c72e1SJason King 	if (c->bg != -1)
2905*5e1c72e1SJason King 		ls_tprint(lsc_setbg, c->bg, 0, 0, 0, 0, 0, 0, 0, 0);
2906*5e1c72e1SJason King }
2907*5e1c72e1SJason King 
2908*5e1c72e1SJason King static void
2909*5e1c72e1SJason King ls_end_color()
2910*5e1c72e1SJason King {
2911*5e1c72e1SJason King 	ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2912*5e1c72e1SJason King 	if (lsc_debug)
2913*5e1c72e1SJason King 		dump_color(lsc_match);
2914*5e1c72e1SJason King }
2915*5e1c72e1SJason King 
2916*5e1c72e1SJason King static void
2917*5e1c72e1SJason King new_color_entry(char *colorstr)
2918*5e1c72e1SJason King {
2919*5e1c72e1SJason King 	static const struct {
2920*5e1c72e1SJason King 		const char	*s;
2921*5e1c72e1SJason King 		ls_cftype_t	stype;
2922*5e1c72e1SJason King 	} type_map[] = {
2923*5e1c72e1SJason King 		{ "no", LS_NORMAL },
2924*5e1c72e1SJason King 		{ "fi", LS_FILE },
2925*5e1c72e1SJason King 		{ "di", LS_DIR },
2926*5e1c72e1SJason King 		{ "ln", LS_LINK },
2927*5e1c72e1SJason King 		{ "pi", LS_FIFO },
2928*5e1c72e1SJason King 		{ "so", LS_SOCK },
2929*5e1c72e1SJason King 		{ "do", LS_DOOR },
2930*5e1c72e1SJason King 		{ "bd", LS_BLK },
2931*5e1c72e1SJason King 		{ "cd", LS_CHR },
2932*5e1c72e1SJason King 		{ "or", LS_ORPHAN },
2933*5e1c72e1SJason King 		{ "su", LS_SETUID },
2934*5e1c72e1SJason King 		{ "sg", LS_SETGID },
2935*5e1c72e1SJason King 		{ "tw", LS_STICKY_OTHER_WRITABLE },
2936*5e1c72e1SJason King 		{ "ow", LS_OTHER_WRITABLE },
2937*5e1c72e1SJason King 		{ "st", LS_STICKY },
2938*5e1c72e1SJason King 		{ "ex", LS_EXEC },
2939*5e1c72e1SJason King 		{ "po", LS_PORT },
2940*5e1c72e1SJason King 		{ NULL, LS_NORMAL }
2941*5e1c72e1SJason King 	};
2942*5e1c72e1SJason King 
2943*5e1c72e1SJason King 	char		*p, *lasts;
2944*5e1c72e1SJason King 	int		i;
2945*5e1c72e1SJason King 	int		color, attr;
2946*5e1c72e1SJason King 
2947*5e1c72e1SJason King 	p = strtok_r(colorstr, "=", &lasts);
2948*5e1c72e1SJason King 	if (p == NULL) {
2949*5e1c72e1SJason King 		colorflg = 0;
2950*5e1c72e1SJason King 		return;
2951*5e1c72e1SJason King 	}
2952*5e1c72e1SJason King 
2953*5e1c72e1SJason King 	if (p[0] == '*') {
2954*5e1c72e1SJason King 		lsc_colors[lsc_ncolors].ftype = LS_PAT;
2955*5e1c72e1SJason King 		/* don't include the * in the suffix */
2956*5e1c72e1SJason King 		if ((lsc_colors[lsc_ncolors].sfx = strdup(p + 1)) == NULL) {
2957*5e1c72e1SJason King 			colorflg = 0;
2958*5e1c72e1SJason King 			return;
2959*5e1c72e1SJason King 		}
2960da6c28aaSamw 	} else {
2961*5e1c72e1SJason King 		lsc_colors[lsc_ncolors].sfx = NULL;
2962*5e1c72e1SJason King 
2963*5e1c72e1SJason King 		for (i = 0; type_map[i].s != NULL; ++i) {
2964*5e1c72e1SJason King 			if (strncmp(type_map[i].s, p, 2) == 0)
2965*5e1c72e1SJason King 				break;
2966da6c28aaSamw 		}
2967*5e1c72e1SJason King 
2968*5e1c72e1SJason King 		/* ignore unknown file types */
2969*5e1c72e1SJason King 		if (type_map[i].s == NULL)
2970*5e1c72e1SJason King 			return;
2971*5e1c72e1SJason King 
2972*5e1c72e1SJason King 		lsc_colors[lsc_ncolors].ftype = type_map[i].stype;
2973*5e1c72e1SJason King 	}
2974*5e1c72e1SJason King 
2975*5e1c72e1SJason King 	attr = LSA_NONE;
2976*5e1c72e1SJason King 	lsc_colors[lsc_ncolors].fg = -1;
2977*5e1c72e1SJason King 	lsc_colors[lsc_ncolors].bg = -1;
2978*5e1c72e1SJason King 	for (p = strtok_r(NULL, ";", &lasts); p != NULL;
2979*5e1c72e1SJason King 	    p = strtok_r(NULL, ";", &lasts)) {
2980*5e1c72e1SJason King 		color = strtol(p, NULL, 10);
2981*5e1c72e1SJason King 
2982*5e1c72e1SJason King 		if (color < 10) {
2983*5e1c72e1SJason King 			switch (color) {
2984*5e1c72e1SJason King 			case 0:
2985*5e1c72e1SJason King 				attr = LSA_NONE;
2986*5e1c72e1SJason King 				continue;
2987*5e1c72e1SJason King 			case 1:
2988*5e1c72e1SJason King 				attr |= LSA_BOLD;
2989*5e1c72e1SJason King 				continue;
2990*5e1c72e1SJason King 			case 4:
2991*5e1c72e1SJason King 				attr |= LSA_UNDERSCORE;
2992*5e1c72e1SJason King 				continue;
2993*5e1c72e1SJason King 			case 5:
2994*5e1c72e1SJason King 				attr |= LSA_BLINK;
2995*5e1c72e1SJason King 				continue;
2996*5e1c72e1SJason King 			case 7:
2997*5e1c72e1SJason King 				attr |= LSA_REVERSE;
2998*5e1c72e1SJason King 				continue;
2999*5e1c72e1SJason King 			case 8:
3000*5e1c72e1SJason King 				attr |= LSA_CONCEALED;
3001*5e1c72e1SJason King 				continue;
3002*5e1c72e1SJason King 			default:
3003*5e1c72e1SJason King 				continue;
3004da6c28aaSamw 			}
3005da6c28aaSamw 		}
3006*5e1c72e1SJason King 
3007*5e1c72e1SJason King 		if (color < 40)
3008*5e1c72e1SJason King 			lsc_colors[lsc_ncolors].fg = color - 30;
3009*5e1c72e1SJason King 		else
3010*5e1c72e1SJason King 			lsc_colors[lsc_ncolors].bg = color - 40;
3011*5e1c72e1SJason King 	}
3012*5e1c72e1SJason King 
3013*5e1c72e1SJason King 	lsc_colors[lsc_ncolors].attr = attr;
3014*5e1c72e1SJason King 	++lsc_ncolors;
3015*5e1c72e1SJason King }
3016*5e1c72e1SJason King 
3017*5e1c72e1SJason King static int
3018*5e1c72e1SJason King ls_color_compare(const void *p1, const void *p2)
3019*5e1c72e1SJason King {
3020*5e1c72e1SJason King 	const ls_color_t *c1 = (const ls_color_t *)p1;
3021*5e1c72e1SJason King 	const ls_color_t *c2 = (const ls_color_t *)p2;
3022*5e1c72e1SJason King 
3023*5e1c72e1SJason King 	int ret = c1->ftype - c2->ftype;
3024*5e1c72e1SJason King 
3025*5e1c72e1SJason King 	if (ret != 0)
3026*5e1c72e1SJason King 		return (ret);
3027*5e1c72e1SJason King 
3028*5e1c72e1SJason King 	if (c1->ftype != LS_PAT)
3029*5e1c72e1SJason King 		return (ret);
3030*5e1c72e1SJason King 
3031*5e1c72e1SJason King 	return (strcmp(c1->sfx, c2->sfx));
3032*5e1c72e1SJason King }
3033*5e1c72e1SJason King 
3034*5e1c72e1SJason King static void
3035*5e1c72e1SJason King ls_color_init()
3036*5e1c72e1SJason King {
3037*5e1c72e1SJason King 	static char *default_colorstr = "no=00:fi=00:di=01;34:ln=01;36:po=01;35"
3038*5e1c72e1SJason King 	    ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01"
3039*5e1c72e1SJason King 	    ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31"
3040*5e1c72e1SJason King 	    ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31"
3041*5e1c72e1SJason King 	    ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31"
3042*5e1c72e1SJason King 	    ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35"
3043*5e1c72e1SJason King 	    ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"
3044*5e1c72e1SJason King 	    ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35"
3045*5e1c72e1SJason King 	    ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35"
3046*5e1c72e1SJason King 	    ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35"
3047*5e1c72e1SJason King 	    ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35";
3048*5e1c72e1SJason King 
3049*5e1c72e1SJason King 	char    *colorstr;
3050*5e1c72e1SJason King 	char    *p, *lasts;
3051*5e1c72e1SJason King 	size_t  color_sz;
3052*5e1c72e1SJason King 	int	termret;
3053*5e1c72e1SJason King 
3054*5e1c72e1SJason King 	(void) setupterm(NULL, 1, &termret);
3055*5e1c72e1SJason King 	if (termret != 1)
3056*5e1c72e1SJason King 		return;
3057*5e1c72e1SJason King 
3058*5e1c72e1SJason King 	if ((colorstr = getenv("LS_COLORS")) == NULL)
3059*5e1c72e1SJason King 		colorstr = default_colorstr;
3060*5e1c72e1SJason King 
3061*5e1c72e1SJason King 	color_sz = 0;
3062*5e1c72e1SJason King 	for (p = strchr(colorstr, ':'); p != NULL && *p != '\0';
3063*5e1c72e1SJason King 	    p = strchr(++p, ':'))
3064*5e1c72e1SJason King 		++color_sz;
3065*5e1c72e1SJason King 
3066*5e1c72e1SJason King 	lsc_colors = calloc(color_sz, sizeof (ls_color_t));
3067*5e1c72e1SJason King 	if (lsc_colors == NULL) {
3068*5e1c72e1SJason King 		free(colorstr);
3069*5e1c72e1SJason King 		return;
3070*5e1c72e1SJason King 	}
3071*5e1c72e1SJason King 
3072*5e1c72e1SJason King 	for (p = strtok_r(colorstr, ":", &lasts);
3073*5e1c72e1SJason King 	    p != NULL && lsc_ncolors < color_sz;
3074*5e1c72e1SJason King 	    p = strtok_r(NULL, ":", &lasts))
3075*5e1c72e1SJason King 		new_color_entry(p);
3076*5e1c72e1SJason King 
3077*5e1c72e1SJason King 	qsort((void *)lsc_colors, lsc_ncolors, sizeof (ls_color_t),
3078*5e1c72e1SJason King 	    ls_color_compare);
3079*5e1c72e1SJason King 
3080*5e1c72e1SJason King 	if ((lsc_bold = tigetstr("bold")) == (char *)-1)
3081*5e1c72e1SJason King 		lsc_bold = NULL;
3082*5e1c72e1SJason King 
3083*5e1c72e1SJason King 	if ((lsc_underline = tigetstr("smul")) == (char *)-1)
3084*5e1c72e1SJason King 		lsc_underline = NULL;
3085*5e1c72e1SJason King 
3086*5e1c72e1SJason King 	if ((lsc_blink = tigetstr("blink")) == (char *)-1)
3087*5e1c72e1SJason King 		lsc_blink = NULL;
3088*5e1c72e1SJason King 
3089*5e1c72e1SJason King 	if ((lsc_reverse = tigetstr("rev")) == (char *)-1)
3090*5e1c72e1SJason King 		lsc_reverse = NULL;
3091*5e1c72e1SJason King 
3092*5e1c72e1SJason King 	if ((lsc_concealed = tigetstr("prot")) == (char *)-1)
3093*5e1c72e1SJason King 		lsc_concealed = NULL;
3094*5e1c72e1SJason King 
3095*5e1c72e1SJason King 	if ((lsc_none = tigetstr("sgr0")) == (char *)-1)
3096*5e1c72e1SJason King 		lsc_none = NULL;
3097*5e1c72e1SJason King 
3098*5e1c72e1SJason King 	if ((lsc_setfg = tigetstr("setaf")) == (char *)-1)
3099*5e1c72e1SJason King 		lsc_setfg = NULL;
3100*5e1c72e1SJason King 
3101*5e1c72e1SJason King 	if ((lsc_setbg = tigetstr("setab")) == (char *)-1)
3102*5e1c72e1SJason King 		lsc_setbg = NULL;
3103*5e1c72e1SJason King 
3104*5e1c72e1SJason King 	if (getenv("_LS_COLOR_DEBUG") != NULL) {
3105*5e1c72e1SJason King 		int i;
3106*5e1c72e1SJason King 
3107*5e1c72e1SJason King 		lsc_debug = 1;
3108*5e1c72e1SJason King 		for (i = 0; i < lsc_ncolors; ++i)
3109*5e1c72e1SJason King 			dump_color(&lsc_colors[i]);
3110da6c28aaSamw 	}
3111da6c28aaSamw }
3112da6c28aaSamw 
3113da6c28aaSamw /* Free extended system attribute lists */
3114da6c28aaSamw 
3115da6c28aaSamw void
3116da6c28aaSamw free_sysattr(struct lbuf *p)
3117da6c28aaSamw {
3118da6c28aaSamw 	int i;
3119da6c28aaSamw 
3120da6c28aaSamw 	if (p->exttr != NULL) {
3121da6c28aaSamw 		for (i = 0; i < sacnt; i++) {
3122da6c28aaSamw 			if (p->exttr[i].name != NULL)
3123da6c28aaSamw 				free(p->exttr[i].name);
3124da6c28aaSamw 		}
3125da6c28aaSamw 		free(p->exttr);
3126da6c28aaSamw 	}
3127da6c28aaSamw 	if (p->extm != NULL) {
3128da6c28aaSamw 		for (i = 0; i < sacnt; i++) {
3129da6c28aaSamw 			if (p->extm[i].name != NULL)
3130da6c28aaSamw 				free(p->extm[i].name);
3131da6c28aaSamw 		}
3132da6c28aaSamw 		free(p->extm);
3133da6c28aaSamw 	}
3134da6c28aaSamw }
3135da6c28aaSamw 
3136da6c28aaSamw /* Allocate extended system attribute list */
3137da6c28aaSamw 
3138da6c28aaSamw void *
3139da6c28aaSamw xmalloc(size_t size, struct lbuf *p)
3140da6c28aaSamw {
3141da6c28aaSamw 	if ((p = malloc(size)) == NULL) {
3142da6c28aaSamw 		perror("ls");
3143da6c28aaSamw 		free_sysattr(p);
3144da6c28aaSamw 		nvlist_free(response);
3145da6c28aaSamw 		exit(2);
3146da6c28aaSamw 	}
3147da6c28aaSamw 	return (p);
3148da6c28aaSamw }
3149