xref: /onnv-gate/usr/src/cmd/ls/ls.c (revision 9664:3ab9bde9a605)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51447Sakaplan  * Common Development and Distribution License (the "License").
61447Sakaplan  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
21*9664Sjason@ansipunx.net 
220Sstevel@tonic-gate /*
23*9664Sjason@ansipunx.net  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*9664Sjason@ansipunx.net  * Use is subject to license terms.
25*9664Sjason@ansipunx.net  */
26*9664Sjason@ansipunx.net 
27*9664Sjason@ansipunx.net /*
28*9664Sjason@ansipunx.net  * Copyright 2009 Jason King.  All rights reserved.
290Sstevel@tonic-gate  * Use is subject to license terms.
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
330Sstevel@tonic-gate /*	  All Rights Reserved  	*/
340Sstevel@tonic-gate 
350Sstevel@tonic-gate /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
360Sstevel@tonic-gate /*	  All Rights Reserved	*/
370Sstevel@tonic-gate 
380Sstevel@tonic-gate /*
390Sstevel@tonic-gate  * List files or directories
400Sstevel@tonic-gate  */
410Sstevel@tonic-gate 
420Sstevel@tonic-gate #include <sys/param.h>
430Sstevel@tonic-gate #include <sys/types.h>
440Sstevel@tonic-gate #include <sys/mkdev.h>
450Sstevel@tonic-gate #include <sys/stat.h>
460Sstevel@tonic-gate #include <sys/acl.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate #include <wchar.h>
490Sstevel@tonic-gate #include <stdio.h>
500Sstevel@tonic-gate #include <ctype.h>
510Sstevel@tonic-gate #include <dirent.h>
520Sstevel@tonic-gate #include <string.h>
530Sstevel@tonic-gate #include <locale.h>
540Sstevel@tonic-gate #include <curses.h>
55*9664Sjason@ansipunx.net #include <term.h>
560Sstevel@tonic-gate #include <termios.h>
570Sstevel@tonic-gate #include <stdlib.h>
580Sstevel@tonic-gate #include <widec.h>
590Sstevel@tonic-gate #include <locale.h>
600Sstevel@tonic-gate #include <wctype.h>
610Sstevel@tonic-gate #include <pwd.h>
620Sstevel@tonic-gate #include <grp.h>
630Sstevel@tonic-gate #include <limits.h>
640Sstevel@tonic-gate #include <fcntl.h>
650Sstevel@tonic-gate #include <unistd.h>
660Sstevel@tonic-gate #include <libgen.h>
670Sstevel@tonic-gate #include <errno.h>
68789Sahrens #include <aclutils.h>
695331Samw #include <libnvpair.h>
705331Samw #include <libcmdutils.h>
715331Samw #include <attr.h>
72*9664Sjason@ansipunx.net #include <getopt.h>
73*9664Sjason@ansipunx.net #include <inttypes.h>
740Sstevel@tonic-gate 
750Sstevel@tonic-gate #ifndef STANDALONE
760Sstevel@tonic-gate #define	TERMINFO
770Sstevel@tonic-gate #endif
780Sstevel@tonic-gate 
790Sstevel@tonic-gate /*
800Sstevel@tonic-gate  * -DNOTERMINFO can be defined on the cc command line to prevent
810Sstevel@tonic-gate  * the use of terminfo.  This should be done on systems not having
825331Samw  * the terminfo feature(pre 6.0 systems ?).
830Sstevel@tonic-gate  * As a result, columnar listings assume 80 columns for output,
840Sstevel@tonic-gate  * unless told otherwise via the COLUMNS environment variable.
850Sstevel@tonic-gate  */
860Sstevel@tonic-gate #ifdef NOTERMINFO
870Sstevel@tonic-gate #undef TERMINFO
880Sstevel@tonic-gate #endif
890Sstevel@tonic-gate 
900Sstevel@tonic-gate #include <term.h>
910Sstevel@tonic-gate 
920Sstevel@tonic-gate #define	BFSIZE	16
930Sstevel@tonic-gate /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */
940Sstevel@tonic-gate #define	ISARG	0100000
950Sstevel@tonic-gate 
960Sstevel@tonic-gate /*
970Sstevel@tonic-gate  * this flag has been added to manipulate the display of S instead of 'l' when
980Sstevel@tonic-gate  * the file is not a regular file and when group execution bit is off
990Sstevel@tonic-gate  */
1000Sstevel@tonic-gate #define	LS_NOTREG	010000
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate /*
1040Sstevel@tonic-gate  * Date and time formats
1050Sstevel@tonic-gate  *
1060Sstevel@tonic-gate  * b --- abbreviated month name
1070Sstevel@tonic-gate  * e --- day number
1080Sstevel@tonic-gate  * Y --- year in the form ccyy
1090Sstevel@tonic-gate  * H --- hour(24-hour version)
1100Sstevel@tonic-gate  * M --- minute
1110Sstevel@tonic-gate  * F --- yyyy-mm-dd
1120Sstevel@tonic-gate  * T --- hh:mm:ss
1130Sstevel@tonic-gate  * z --- time zone as hours displacement from UTC
1140Sstevel@tonic-gate  * note that %F and %z are from the ISO C99 standard and are
1150Sstevel@tonic-gate  * not present in older C libraries
1160Sstevel@tonic-gate  */
117*9664Sjason@ansipunx.net #define	FORMAT_OLD	" %b %e  %Y "
118*9664Sjason@ansipunx.net #define	FORMAT_NEW	" %b %e %H:%M "
119*9664Sjason@ansipunx.net #define	FORMAT_LONG	" %b %e %T %Y "
120*9664Sjason@ansipunx.net #define	FORMAT_ISO_FULL	" %%F %%T.%.09ld %%z "
121*9664Sjason@ansipunx.net #define	FORMAT_ISO_LONG	" %F %R "
122*9664Sjason@ansipunx.net #define	FORMAT_ISO_NEW	" %m-%d %H:%M "
123*9664Sjason@ansipunx.net #define	FORMAT_ISO_OLD	" %F "
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate #undef BUFSIZ
1260Sstevel@tonic-gate #define	BUFSIZ 4096
1270Sstevel@tonic-gate #define	NUMBER_WIDTH 40
1280Sstevel@tonic-gate #define	FMTSIZE 50
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate struct ditem {
1310Sstevel@tonic-gate 	dev_t	dev;			/* directory items device number */
1320Sstevel@tonic-gate 	ino_t	ino;			/* directory items inode number */
1330Sstevel@tonic-gate 	struct ditem *parent;		/* dir items ptr to its parent's info */
1340Sstevel@tonic-gate };
1355331Samw /* Holds boolean extended system attributes */
1365331Samw struct attrb {
1375331Samw 	char		*name;
1385331Samw };
1395331Samw /* Holds timestamp extended system attributes */
1405331Samw struct attrtm {
1415331Samw 	char		*name;
1425331Samw 	uint64_t	stm;
1435331Samw 	uint64_t	nstm;
1445331Samw };
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate struct	lbuf	{
1470Sstevel@tonic-gate 	union	{
1480Sstevel@tonic-gate 		char	lname[MAXNAMLEN]; /* used for filename in a directory */
1490Sstevel@tonic-gate 		char	*namep;		/* for name in ls-command; */
1500Sstevel@tonic-gate 	} ln;
1510Sstevel@tonic-gate 	char	ltype;		/* filetype */
1520Sstevel@tonic-gate 	ino_t	lnum;		/* inode number of file */
1530Sstevel@tonic-gate 	mode_t	lflags; 	/* 0777 bits used as r,w,x permissions */
1540Sstevel@tonic-gate 	nlink_t	lnl;		/* number of links to file */
1550Sstevel@tonic-gate 	uid_t	luid;
1560Sstevel@tonic-gate 	gid_t	lgid;
1570Sstevel@tonic-gate 	off_t	lsize;		/* filesize or major/minor dev numbers */
1580Sstevel@tonic-gate 	blkcnt_t	lblocks;	/* number of file blocks */
1590Sstevel@tonic-gate 	timestruc_t	lmtime;
1605331Samw 	timestruc_t	lat;
1615331Samw 	timestruc_t	lct;
1625331Samw 	timestruc_t	lmt;
1630Sstevel@tonic-gate 	char	*flinkto;	/* symbolic link contents */
1640Sstevel@tonic-gate 	char 	acl;		/* indicate there are additional acl entries */
1650Sstevel@tonic-gate 	int	cycle;		/* cycle detected flag */
1660Sstevel@tonic-gate 	struct ditem *ancinfo;	/* maintains ancestor info */
167789Sahrens 	acl_t *aclp;		/* ACL if present */
1685331Samw 	struct attrb *exttr;	/* boolean extended system attributes */
1695331Samw 	struct attrtm *extm;	/* timestamp extended system attributes */
1700Sstevel@tonic-gate };
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate struct dchain {
1730Sstevel@tonic-gate 	char *dc_name;		/* path name */
1740Sstevel@tonic-gate 	int cycle_detected;	/* cycle detected visiting this directory */
1750Sstevel@tonic-gate 	struct ditem *myancinfo;	/* this directory's ancestry info */
1760Sstevel@tonic-gate 	struct dchain *dc_next;	/* next directory in the chain */
1770Sstevel@tonic-gate };
1780Sstevel@tonic-gate 
179*9664Sjason@ansipunx.net #define	LSA_NONE	(0)
180*9664Sjason@ansipunx.net #define	LSA_BOLD	(1L << 0)
181*9664Sjason@ansipunx.net #define	LSA_UNDERSCORE	(1L << 1)
182*9664Sjason@ansipunx.net #define	LSA_BLINK	(1L << 2)
183*9664Sjason@ansipunx.net #define	LSA_REVERSE	(1L << 3)
184*9664Sjason@ansipunx.net #define	LSA_CONCEALED	(1L << 4)
185*9664Sjason@ansipunx.net 
186*9664Sjason@ansipunx.net /* these should be ordered most general to most specific */
187*9664Sjason@ansipunx.net typedef enum LS_CFTYPE {
188*9664Sjason@ansipunx.net 	LS_NORMAL,
189*9664Sjason@ansipunx.net 	LS_FILE,
190*9664Sjason@ansipunx.net 	LS_EXEC,
191*9664Sjason@ansipunx.net 	LS_DIR,
192*9664Sjason@ansipunx.net 	LS_LINK,
193*9664Sjason@ansipunx.net 	LS_FIFO,
194*9664Sjason@ansipunx.net 	LS_SOCK,
195*9664Sjason@ansipunx.net 	LS_DOOR,
196*9664Sjason@ansipunx.net 	LS_BLK,
197*9664Sjason@ansipunx.net 	LS_CHR,
198*9664Sjason@ansipunx.net 	LS_PORT,
199*9664Sjason@ansipunx.net 	LS_STICKY,
200*9664Sjason@ansipunx.net 	LS_ORPHAN,
201*9664Sjason@ansipunx.net 	LS_SETGID,
202*9664Sjason@ansipunx.net 	LS_SETUID,
203*9664Sjason@ansipunx.net 	LS_OTHER_WRITABLE,
204*9664Sjason@ansipunx.net 	LS_STICKY_OTHER_WRITABLE,
205*9664Sjason@ansipunx.net 	LS_PAT
206*9664Sjason@ansipunx.net } ls_cftype_t;
207*9664Sjason@ansipunx.net 
208*9664Sjason@ansipunx.net typedef struct ls_color {
209*9664Sjason@ansipunx.net 	char		*sfx;
210*9664Sjason@ansipunx.net 	ls_cftype_t	ftype;
211*9664Sjason@ansipunx.net 	int		attr;
212*9664Sjason@ansipunx.net 	int		fg;
213*9664Sjason@ansipunx.net 	int		bg;
214*9664Sjason@ansipunx.net } ls_color_t;
215*9664Sjason@ansipunx.net 
2160Sstevel@tonic-gate /*
2170Sstevel@tonic-gate  * A numbuf_t is used when converting a number to a string representation
2180Sstevel@tonic-gate  */
2190Sstevel@tonic-gate typedef char numbuf_t[NUMBER_WIDTH];
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate static struct dchain *dfirst;	/* start of the dir chain */
2220Sstevel@tonic-gate static struct dchain *cdfirst;	/* start of the current dir chain */
2230Sstevel@tonic-gate static struct dchain *dtemp;	/* temporary - used for linking */
2240Sstevel@tonic-gate static char *curdir;		/* the current directory */
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate static int	first = 1;	/* true if first line is not yet printed */
2270Sstevel@tonic-gate static int	nfiles = 0;	/* number of flist entries in current use */
2280Sstevel@tonic-gate static int	nargs = 0;	/* number of flist entries used for arguments */
2290Sstevel@tonic-gate static int	maxfils = 0;	/* number of flist/lbuf entries allocated */
2300Sstevel@tonic-gate static int	maxn = 0;	/* number of flist entries with lbufs asigned */
2310Sstevel@tonic-gate static int	quantn = 64;	/* allocation growth quantum */
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate static struct lbuf	*nxtlbf;	/* ptr to next lbuf to be assigned */
2340Sstevel@tonic-gate static struct lbuf	**flist;	/* ptr to list of lbuf pointers */
2350Sstevel@tonic-gate static struct lbuf	*gstat(char *, int, struct ditem *);
2360Sstevel@tonic-gate static char		*getname(uid_t);
2370Sstevel@tonic-gate static char		*getgroup(gid_t);
2380Sstevel@tonic-gate static char		*makename(char *, char *);
2390Sstevel@tonic-gate static void		pentry(struct lbuf *);
2400Sstevel@tonic-gate static void		column(void);
2410Sstevel@tonic-gate static void		pmode(mode_t aflag);
2420Sstevel@tonic-gate static void		selection(int *);
2430Sstevel@tonic-gate static void		new_line(void);
2440Sstevel@tonic-gate static void		rddir(char *, struct ditem *);
2450Sstevel@tonic-gate static int		strcol(unsigned char *);
2460Sstevel@tonic-gate static void		pem(struct lbuf **, struct lbuf **, int);
2470Sstevel@tonic-gate static void		pdirectory(char *, int, int, int, struct ditem *);
2480Sstevel@tonic-gate static struct cachenode *findincache(struct cachenode **, long);
2490Sstevel@tonic-gate static void		csi_pprintf(unsigned char *);
2500Sstevel@tonic-gate static void		pprintf(char *, char *);
2510Sstevel@tonic-gate static int		compar(struct lbuf **pp1, struct lbuf **pp2);
2520Sstevel@tonic-gate static char 		*number_to_scaled_string(numbuf_t buf,
2530Sstevel@tonic-gate 			    unsigned long long number,
2540Sstevel@tonic-gate 			    long scale);
2550Sstevel@tonic-gate static void		record_ancestry(char *, struct stat *, struct lbuf *,
2560Sstevel@tonic-gate 			    int, struct ditem *);
257*9664Sjason@ansipunx.net static void		ls_color_init(void);
258*9664Sjason@ansipunx.net static void		ls_start_color(struct lbuf *);
259*9664Sjason@ansipunx.net static void		ls_end_color(void);
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate static int		aflg;
2620Sstevel@tonic-gate static int		atflg;
2630Sstevel@tonic-gate static int		bflg;
2640Sstevel@tonic-gate static int		cflg;
2650Sstevel@tonic-gate static int		dflg;
2660Sstevel@tonic-gate static int		eflg;
2670Sstevel@tonic-gate static int		fflg;
2680Sstevel@tonic-gate static int		gflg;
2690Sstevel@tonic-gate static int		hflg;
2700Sstevel@tonic-gate static int		iflg;
2710Sstevel@tonic-gate static int		lflg;
2720Sstevel@tonic-gate static int		mflg;
2730Sstevel@tonic-gate static int		nflg;
2740Sstevel@tonic-gate static int		oflg;
2750Sstevel@tonic-gate static int		pflg;
2760Sstevel@tonic-gate static int		qflg;
2770Sstevel@tonic-gate static int		rflg = 1; /* init to 1 for special use in compar */
2780Sstevel@tonic-gate static int		sflg;
2790Sstevel@tonic-gate static int		tflg;
2800Sstevel@tonic-gate static int		uflg;
281*9664Sjason@ansipunx.net static int		Uflg;
282*9664Sjason@ansipunx.net static int		wflg;
2830Sstevel@tonic-gate static int		xflg;
2840Sstevel@tonic-gate static int		Aflg;
285*9664Sjason@ansipunx.net static int		Bflg;
2860Sstevel@tonic-gate static int		Cflg;
2870Sstevel@tonic-gate static int		Eflg;
2880Sstevel@tonic-gate static int		Fflg;
2890Sstevel@tonic-gate static int		Hflg;
2900Sstevel@tonic-gate static int		Lflg;
2910Sstevel@tonic-gate static int		Rflg;
2920Sstevel@tonic-gate static int		Sflg;
293789Sahrens static int		vflg;
2941420Smarks static int		Vflg;
2955331Samw static int		saflg;		/* boolean extended system attr. */
2965331Samw static int		sacnt;		/* number of extended system attr. */
2975331Samw static int		copt;
2985331Samw static int		vopt;
2995331Samw static int		tmflg;		/* create time ext. system attr. */
3005331Samw static int		ctm;
3015331Samw static int		atm;
3025331Samw static int		mtm;
3035331Samw static int		crtm;
3045331Samw static int		alltm;
3050Sstevel@tonic-gate static long		hscale;
3060Sstevel@tonic-gate static mode_t		flags;
3070Sstevel@tonic-gate static int		err = 0;	/* Contains return code */
308*9664Sjason@ansipunx.net static int		colorflg;
309*9664Sjason@ansipunx.net static int		file_typeflg;
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate static uid_t		lastuid	= (uid_t)-1;
3120Sstevel@tonic-gate static gid_t		lastgid = (gid_t)-1;
3130Sstevel@tonic-gate static char		*lastuname = NULL;
3140Sstevel@tonic-gate static char		*lastgname = NULL;
3150Sstevel@tonic-gate 
316*9664Sjason@ansipunx.net /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg, colorflg are on */
3170Sstevel@tonic-gate static int		statreq;
3180Sstevel@tonic-gate 
319*9664Sjason@ansipunx.net static uint64_t		block_size = 1;
3200Sstevel@tonic-gate static char		*dotp = ".";
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate static u_longlong_t 	tblocks; /* number of blocks of files in a directory */
3230Sstevel@tonic-gate static time_t		year, now;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate static int		num_cols = 80;
3260Sstevel@tonic-gate static int		colwidth;
3270Sstevel@tonic-gate static int		filewidth;
3280Sstevel@tonic-gate static int		fixedwidth;
3290Sstevel@tonic-gate static int		nomocore;
3300Sstevel@tonic-gate static int		curcol;
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate static struct	winsize	win;
3330Sstevel@tonic-gate 
334*9664Sjason@ansipunx.net /* if time_fmt_new is left NULL, time_fmt_old is used for all times */
335*9664Sjason@ansipunx.net static const char	*time_fmt_old = FORMAT_OLD;	/* non-recent files */
336*9664Sjason@ansipunx.net static const char	*time_fmt_new = FORMAT_NEW;	/* recent files */
337*9664Sjason@ansipunx.net static int		time_custom;	/* != 0 if a custom format */
3385331Samw static char	time_buf[FMTSIZE];	/* array to hold day and time */
3390Sstevel@tonic-gate 
340*9664Sjason@ansipunx.net static int		lsc_debug;
341*9664Sjason@ansipunx.net static ls_color_t	*lsc_match;
342*9664Sjason@ansipunx.net static ls_color_t	*lsc_colors;
343*9664Sjason@ansipunx.net static size_t		lsc_ncolors;
344*9664Sjason@ansipunx.net static char		*lsc_bold;
345*9664Sjason@ansipunx.net static char		*lsc_underline;
346*9664Sjason@ansipunx.net static char		*lsc_blink;
347*9664Sjason@ansipunx.net static char		*lsc_reverse;
348*9664Sjason@ansipunx.net static char		*lsc_concealed;
349*9664Sjason@ansipunx.net static char		*lsc_none;
350*9664Sjason@ansipunx.net static char		*lsc_setfg;
351*9664Sjason@ansipunx.net static char		*lsc_setbg;
352*9664Sjason@ansipunx.net 
3530Sstevel@tonic-gate #define	NOTWORKINGDIR(d, l)	(((l) < 2) || \
3540Sstevel@tonic-gate 				    (strcmp((d) + (l) - 2, "/.") != 0))
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate #define	NOTPARENTDIR(d, l)	(((l) < 3) || \
3570Sstevel@tonic-gate 				    (strcmp((d) + (l) - 3, "/..") != 0))
3585331Samw /* Extended system attributes support */
3595331Samw static int get_sysxattr(char *, struct lbuf *);
3605331Samw static void set_sysattrb_display(char *, boolean_t, struct lbuf *);
3615331Samw static void set_sysattrtm_display(char *, struct lbuf *);
362*9664Sjason@ansipunx.net static void format_time(time_t, time_t);
3635331Samw static void print_time(struct lbuf *);
3645331Samw static void format_attrtime(struct lbuf *);
3655331Samw static void *xmalloc(size_t, struct lbuf *);
3665331Samw static void free_sysattr(struct lbuf *);
3675331Samw static nvpair_t *pair;
3685331Samw static nvlist_t	*response;
3696866Sbasabi static int acl_err;
3700Sstevel@tonic-gate 
371*9664Sjason@ansipunx.net const struct option long_options[] = {
372*9664Sjason@ansipunx.net 	{ "all", no_argument, NULL, 'a' },
373*9664Sjason@ansipunx.net 	{ "almost-all", no_argument, NULL, 'A' },
374*9664Sjason@ansipunx.net 	{ "escape", no_argument, NULL, 'b' },
375*9664Sjason@ansipunx.net 	{ "classify", no_argument, NULL, 'F' },
376*9664Sjason@ansipunx.net 	{ "human-readable", no_argument, NULL, 'h' },
377*9664Sjason@ansipunx.net 	{ "dereference", no_argument, NULL, 'L' },
378*9664Sjason@ansipunx.net 	{ "dereference-command-line", no_argument, NULL, 'H' },
379*9664Sjason@ansipunx.net 	{ "ignore-backups", no_argument, NULL, 'B' },
380*9664Sjason@ansipunx.net 	{ "inode", no_argument, NULL, 'i' },
381*9664Sjason@ansipunx.net 	{ "numeric-uid-gid", no_argument, NULL, 'n' },
382*9664Sjason@ansipunx.net 	{ "no-group", no_argument, NULL, 'o' },
383*9664Sjason@ansipunx.net 	{ "hide-control-chars", no_argument, NULL, 'q' },
384*9664Sjason@ansipunx.net 	{ "reverse", no_argument, NULL, 'r' },
385*9664Sjason@ansipunx.net 	{ "recursive", no_argument, NULL, 'R' },
386*9664Sjason@ansipunx.net 	{ "size", no_argument, NULL, 's' },
387*9664Sjason@ansipunx.net 	{ "width", required_argument, NULL, 'w' },
388*9664Sjason@ansipunx.net 
389*9664Sjason@ansipunx.net 	/* no short options for these */
390*9664Sjason@ansipunx.net 	{ "block-size", required_argument, NULL, 0 },
391*9664Sjason@ansipunx.net 	{ "full-time", no_argument, NULL, 0 },
392*9664Sjason@ansipunx.net 	{ "si", no_argument, NULL, 0 },
393*9664Sjason@ansipunx.net 	{ "color", optional_argument, NULL, 0 },
394*9664Sjason@ansipunx.net 	{ "colour", optional_argument, NULL, 0},
395*9664Sjason@ansipunx.net 	{ "file-type", no_argument, NULL, 0 },
396*9664Sjason@ansipunx.net 	{ "time-style", required_argument, NULL, 0 },
397*9664Sjason@ansipunx.net 
398*9664Sjason@ansipunx.net 	{0, 0, 0, 0}
399*9664Sjason@ansipunx.net };
400*9664Sjason@ansipunx.net 
4010Sstevel@tonic-gate int
4020Sstevel@tonic-gate main(int argc, char *argv[])
4030Sstevel@tonic-gate {
4040Sstevel@tonic-gate 	int		c;
4050Sstevel@tonic-gate 	int		i;
4060Sstevel@tonic-gate 	int		width;
4070Sstevel@tonic-gate 	int		amino = 0;
4080Sstevel@tonic-gate 	int		opterr = 0;
409*9664Sjason@ansipunx.net 	int		option_index = 0;
4100Sstevel@tonic-gate 	struct lbuf	*ep;
4110Sstevel@tonic-gate 	struct lbuf	lb;
4120Sstevel@tonic-gate 	struct ditem	*myinfo;
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
4150Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
4160Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
4170Sstevel@tonic-gate #endif
4180Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
4190Sstevel@tonic-gate #ifdef STANDALONE
4200Sstevel@tonic-gate 	if (argv[0][0] == '\0')
4210Sstevel@tonic-gate 		argc = getargv("ls", &argv, 0);
4220Sstevel@tonic-gate #endif
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	lb.lmtime.tv_sec = time(NULL);
4250Sstevel@tonic-gate 	lb.lmtime.tv_nsec = 0;
4260Sstevel@tonic-gate 	year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */
4270Sstevel@tonic-gate 	now = lb.lmtime.tv_sec + 60;
4280Sstevel@tonic-gate 	if (isatty(1)) {
4290Sstevel@tonic-gate 		Cflg = 1;
4300Sstevel@tonic-gate 		mflg = 0;
4310Sstevel@tonic-gate 	}
4320Sstevel@tonic-gate 
433*9664Sjason@ansipunx.net 	while ((c = getopt_long(argc, argv,
434*9664Sjason@ansipunx.net 	    "+aAbBcCdeEfFghHiklLmnopqrRsStuUw:x1@vV/:%:", long_options,
435*9664Sjason@ansipunx.net 	    &option_index)) != -1)
4360Sstevel@tonic-gate 		switch (c) {
437*9664Sjason@ansipunx.net 		case 0:
438*9664Sjason@ansipunx.net 			/* non-short options */
439*9664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
440*9664Sjason@ansipunx.net 			    "color") == 0 ||
441*9664Sjason@ansipunx.net 			    strcmp(long_options[option_index].name,
442*9664Sjason@ansipunx.net 			    "colour") == 0) {
443*9664Sjason@ansipunx.net 				if (optarg == NULL ||
444*9664Sjason@ansipunx.net 				    strcmp(optarg, "always") == 0 ||
445*9664Sjason@ansipunx.net 				    strcmp(optarg, "yes") == 0 ||
446*9664Sjason@ansipunx.net 				    strcmp(optarg, "force") == 0) {
447*9664Sjason@ansipunx.net 					colorflg++;
448*9664Sjason@ansipunx.net 					statreq++;
449*9664Sjason@ansipunx.net 					continue;
450*9664Sjason@ansipunx.net 				}
451*9664Sjason@ansipunx.net 
452*9664Sjason@ansipunx.net 				if ((strcmp(optarg, "auto") == 0 ||
453*9664Sjason@ansipunx.net 				    strcmp(optarg, "tty") == 0 ||
454*9664Sjason@ansipunx.net 				    strcmp(optarg, "if-tty") == 0) &&
455*9664Sjason@ansipunx.net 				    isatty(1) == 1) {
456*9664Sjason@ansipunx.net 					colorflg++;
457*9664Sjason@ansipunx.net 					statreq++;
458*9664Sjason@ansipunx.net 					continue;
459*9664Sjason@ansipunx.net 				}
460*9664Sjason@ansipunx.net 
461*9664Sjason@ansipunx.net 				if (strcmp(optarg, "never") == 0 ||
462*9664Sjason@ansipunx.net 				    strcmp(optarg, "no") == 0 ||
463*9664Sjason@ansipunx.net 				    strcmp(optarg, "none") == 0) {
464*9664Sjason@ansipunx.net 					colorflg = 0;
465*9664Sjason@ansipunx.net 					continue;
466*9664Sjason@ansipunx.net 				}
467*9664Sjason@ansipunx.net 				(void) fprintf(stderr,
468*9664Sjason@ansipunx.net 				    gettext("Invalid argument '%s' for "
469*9664Sjason@ansipunx.net 				    "--color\n"), optarg);
470*9664Sjason@ansipunx.net 				++opterr;
471*9664Sjason@ansipunx.net 				continue;
472*9664Sjason@ansipunx.net 			}
473*9664Sjason@ansipunx.net 
474*9664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
475*9664Sjason@ansipunx.net 			    "si") == 0) {
476*9664Sjason@ansipunx.net 				hflg++;
477*9664Sjason@ansipunx.net 				hscale = 1000;
478*9664Sjason@ansipunx.net 				continue;
479*9664Sjason@ansipunx.net 			}
480*9664Sjason@ansipunx.net 
481*9664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
482*9664Sjason@ansipunx.net 			    "block-size") == 0) {
483*9664Sjason@ansipunx.net 				size_t scale_len = strlen(optarg);
484*9664Sjason@ansipunx.net 				uint64_t scale = 1;
485*9664Sjason@ansipunx.net 				uint64_t kilo = 1024;
486*9664Sjason@ansipunx.net 				char scale_c;
487*9664Sjason@ansipunx.net 
488*9664Sjason@ansipunx.net 				if (scale_len == 0) {
489*9664Sjason@ansipunx.net 					(void) fprintf(stderr, gettext(
490*9664Sjason@ansipunx.net 					    "Invalid block size \'%s\'\n"),
491*9664Sjason@ansipunx.net 					    optarg);
492*9664Sjason@ansipunx.net 					exit(1);
493*9664Sjason@ansipunx.net 				}
494*9664Sjason@ansipunx.net 
495*9664Sjason@ansipunx.net 				scale_c = optarg[scale_len - 1];
496*9664Sjason@ansipunx.net 				if (scale_c == 'B') {
497*9664Sjason@ansipunx.net 					/* need at least digit, scale, B */
498*9664Sjason@ansipunx.net 					if (scale_len < 3) {
499*9664Sjason@ansipunx.net 						(void) fprintf(stderr, gettext(
500*9664Sjason@ansipunx.net 						    "Invalid block size "
501*9664Sjason@ansipunx.net 						    "\'%s\'\n"), optarg);
502*9664Sjason@ansipunx.net 						exit(1);
503*9664Sjason@ansipunx.net 					}
504*9664Sjason@ansipunx.net 					kilo = 1000;
505*9664Sjason@ansipunx.net 					scale_c = optarg[scale_len - 2];
506*9664Sjason@ansipunx.net 					if (isdigit(scale_c)) {
507*9664Sjason@ansipunx.net 						(void) fprintf(stderr,
508*9664Sjason@ansipunx.net 						    gettext("Invalid block size"
509*9664Sjason@ansipunx.net 						    " \'%s\'\n"), optarg);
510*9664Sjason@ansipunx.net 						exit(1);
511*9664Sjason@ansipunx.net 					}
512*9664Sjason@ansipunx.net 					/*
513*9664Sjason@ansipunx.net 					 * make optarg[scale_len - 1] point to
514*9664Sjason@ansipunx.net 					 * the scale factor
515*9664Sjason@ansipunx.net 					 */
516*9664Sjason@ansipunx.net 					--scale_len;
517*9664Sjason@ansipunx.net 				}
518*9664Sjason@ansipunx.net 
519*9664Sjason@ansipunx.net 				switch (scale_c) {
520*9664Sjason@ansipunx.net 				case 'y':
521*9664Sjason@ansipunx.net 				case 'Y':
522*9664Sjason@ansipunx.net 					scale *= kilo;
523*9664Sjason@ansipunx.net 					/*FALLTHROUGH*/
524*9664Sjason@ansipunx.net 				case 'Z':
525*9664Sjason@ansipunx.net 				case 'z':
526*9664Sjason@ansipunx.net 					scale *= kilo;
527*9664Sjason@ansipunx.net 					/*FALLTHROUGH*/
528*9664Sjason@ansipunx.net 				case 'E':
529*9664Sjason@ansipunx.net 				case 'e':
530*9664Sjason@ansipunx.net 					scale *= kilo;
531*9664Sjason@ansipunx.net 					/*FALLTHROUGH*/
532*9664Sjason@ansipunx.net 				case 'P':
533*9664Sjason@ansipunx.net 				case 'p':
534*9664Sjason@ansipunx.net 					scale *= kilo;
535*9664Sjason@ansipunx.net 					/*FALLTHROUGH*/
536*9664Sjason@ansipunx.net 				case 'T':
537*9664Sjason@ansipunx.net 				case 't':
538*9664Sjason@ansipunx.net 					scale *= kilo;
539*9664Sjason@ansipunx.net 					/*FALLTHROUGH*/
540*9664Sjason@ansipunx.net 				case 'G':
541*9664Sjason@ansipunx.net 				case 'g':
542*9664Sjason@ansipunx.net 					scale *= kilo;
543*9664Sjason@ansipunx.net 					/*FALLTHROUGH*/
544*9664Sjason@ansipunx.net 				case 'M':
545*9664Sjason@ansipunx.net 				case 'm':
546*9664Sjason@ansipunx.net 					scale *= kilo;
547*9664Sjason@ansipunx.net 					/*FALLTHROUGH*/
548*9664Sjason@ansipunx.net 				case 'K':
549*9664Sjason@ansipunx.net 				case 'k':
550*9664Sjason@ansipunx.net 					scale *= kilo;
551*9664Sjason@ansipunx.net 					break;
552*9664Sjason@ansipunx.net 				default:
553*9664Sjason@ansipunx.net 					if (!isdigit(scale_c)) {
554*9664Sjason@ansipunx.net 						(void) fprintf(stderr,
555*9664Sjason@ansipunx.net 						    gettext("Invalid character "
556*9664Sjason@ansipunx.net 						    "following block size in "
557*9664Sjason@ansipunx.net 						    "\'%s\'\n"), optarg);
558*9664Sjason@ansipunx.net 						exit(1);
559*9664Sjason@ansipunx.net 					}
560*9664Sjason@ansipunx.net 				}
561*9664Sjason@ansipunx.net 
562*9664Sjason@ansipunx.net 				/* NULL out scale constant if present */
563*9664Sjason@ansipunx.net 				if (scale > 1 && !isdigit(scale_c))
564*9664Sjason@ansipunx.net 					optarg[scale_len - 1] = '\0';
565*9664Sjason@ansipunx.net 
566*9664Sjason@ansipunx.net 				/* Based on testing, this is what GNU ls does */
567*9664Sjason@ansipunx.net 				block_size = strtoll(optarg, NULL, 0) * scale;
568*9664Sjason@ansipunx.net 				if (block_size < 1) {
569*9664Sjason@ansipunx.net 					(void) fprintf(stderr,
570*9664Sjason@ansipunx.net 					    gettext("Invalid block size "
571*9664Sjason@ansipunx.net 					    "\'%s\'\n"), optarg);
572*9664Sjason@ansipunx.net 					exit(1);
573*9664Sjason@ansipunx.net 				}
574*9664Sjason@ansipunx.net 				continue;
575*9664Sjason@ansipunx.net 			}
576*9664Sjason@ansipunx.net 
577*9664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
578*9664Sjason@ansipunx.net 			    "file-type") == 0) {
579*9664Sjason@ansipunx.net 				file_typeflg++;
580*9664Sjason@ansipunx.net 				Fflg++;
581*9664Sjason@ansipunx.net 				statreq++;
582*9664Sjason@ansipunx.net 				continue;
583*9664Sjason@ansipunx.net 			}
584*9664Sjason@ansipunx.net 
585*9664Sjason@ansipunx.net 
586*9664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
587*9664Sjason@ansipunx.net 			    "full-time") == 0) {
588*9664Sjason@ansipunx.net 				Eflg++;
589*9664Sjason@ansipunx.net 				statreq++;
590*9664Sjason@ansipunx.net 				eflg = 0;
591*9664Sjason@ansipunx.net 				time_fmt_old = FORMAT_ISO_FULL;
592*9664Sjason@ansipunx.net 				time_fmt_new = FORMAT_ISO_FULL;
593*9664Sjason@ansipunx.net 				continue;
594*9664Sjason@ansipunx.net 			}
595*9664Sjason@ansipunx.net 
596*9664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
597*9664Sjason@ansipunx.net 			    "time-style") == 0) {
598*9664Sjason@ansipunx.net 				/* like -E, but doesn't imply -l */
599*9664Sjason@ansipunx.net 				if (strcmp(optarg, "full-iso") == 0) {
600*9664Sjason@ansipunx.net 					Eflg++;
601*9664Sjason@ansipunx.net 					statreq++;
602*9664Sjason@ansipunx.net 					eflg = 0;
603*9664Sjason@ansipunx.net 					time_fmt_old = FORMAT_ISO_FULL;
604*9664Sjason@ansipunx.net 					time_fmt_new = FORMAT_ISO_FULL;
605*9664Sjason@ansipunx.net 					continue;
606*9664Sjason@ansipunx.net 				}
607*9664Sjason@ansipunx.net 				if (strcmp(optarg, "long-iso") == 0) {
608*9664Sjason@ansipunx.net 					statreq++;
609*9664Sjason@ansipunx.net 					Eflg = 0;
610*9664Sjason@ansipunx.net 					eflg = 0;
611*9664Sjason@ansipunx.net 					time_fmt_old = FORMAT_ISO_LONG;
612*9664Sjason@ansipunx.net 					time_fmt_new = FORMAT_ISO_LONG;
613*9664Sjason@ansipunx.net 					continue;
614*9664Sjason@ansipunx.net 				}
615*9664Sjason@ansipunx.net 				if (strcmp(optarg, "iso") == 0) {
616*9664Sjason@ansipunx.net 					statreq++;
617*9664Sjason@ansipunx.net 					Eflg = 0;
618*9664Sjason@ansipunx.net 					eflg = 0;
619*9664Sjason@ansipunx.net 					time_fmt_old = FORMAT_ISO_OLD;
620*9664Sjason@ansipunx.net 					time_fmt_new = FORMAT_ISO_NEW;
621*9664Sjason@ansipunx.net 					continue;
622*9664Sjason@ansipunx.net 				}
623*9664Sjason@ansipunx.net 				/* should be the default */
624*9664Sjason@ansipunx.net 				if (strcmp(optarg, "locale") == 0) {
625*9664Sjason@ansipunx.net 					time_fmt_old = FORMAT_OLD;
626*9664Sjason@ansipunx.net 					time_fmt_new = FORMAT_NEW;
627*9664Sjason@ansipunx.net 					continue;
628*9664Sjason@ansipunx.net 				}
629*9664Sjason@ansipunx.net 				if (optarg[0] == '+') {
630*9664Sjason@ansipunx.net 					char	*told, *tnew;
631*9664Sjason@ansipunx.net 					char	*p;
632*9664Sjason@ansipunx.net 					size_t	timelen = strlen(optarg);
633*9664Sjason@ansipunx.net 
634*9664Sjason@ansipunx.net 					p = strchr(optarg, '\n');
635*9664Sjason@ansipunx.net 					if (p != NULL)
636*9664Sjason@ansipunx.net 						*p++ = '\0';
637*9664Sjason@ansipunx.net 
638*9664Sjason@ansipunx.net 					/*
639*9664Sjason@ansipunx.net 					 * Time format requires a leading and
640*9664Sjason@ansipunx.net 					 * trailing space
641*9664Sjason@ansipunx.net 					 * Add room for 3 spaces + 2 nulls
642*9664Sjason@ansipunx.net 					 * The + in optarg is replaced with
643*9664Sjason@ansipunx.net 					 * a space.
644*9664Sjason@ansipunx.net 					 */
645*9664Sjason@ansipunx.net 					timelen += 2 + 3;
646*9664Sjason@ansipunx.net 					told = malloc(timelen);
647*9664Sjason@ansipunx.net 					if (told == NULL) {
648*9664Sjason@ansipunx.net 						perror("Out of memory");
649*9664Sjason@ansipunx.net 						exit(1);
650*9664Sjason@ansipunx.net 					}
651*9664Sjason@ansipunx.net 
652*9664Sjason@ansipunx.net 					(void) memset(told, 0, timelen);
653*9664Sjason@ansipunx.net 					told[0] = ' ';
654*9664Sjason@ansipunx.net 					(void) strlcat(told, &optarg[1],
655*9664Sjason@ansipunx.net 					    timelen);
656*9664Sjason@ansipunx.net 					(void) strlcat(told, " ", timelen);
657*9664Sjason@ansipunx.net 
658*9664Sjason@ansipunx.net 					if (p != NULL) {
659*9664Sjason@ansipunx.net 						size_t tnew_len;
660*9664Sjason@ansipunx.net 
661*9664Sjason@ansipunx.net 						tnew = told + strlen(told) + 1;
662*9664Sjason@ansipunx.net 						tnew_len = timelen -
663*9664Sjason@ansipunx.net 						    strlen(told) - 1;
664*9664Sjason@ansipunx.net 
665*9664Sjason@ansipunx.net 						tnew[0] = ' ';
666*9664Sjason@ansipunx.net 						(void) strlcat(tnew, p,
667*9664Sjason@ansipunx.net 						    tnew_len);
668*9664Sjason@ansipunx.net 						(void) strlcat(tnew, " ",
669*9664Sjason@ansipunx.net 						    tnew_len);
670*9664Sjason@ansipunx.net 						time_fmt_new =
671*9664Sjason@ansipunx.net 						    (const char *)tnew;
672*9664Sjason@ansipunx.net 					} else {
673*9664Sjason@ansipunx.net 						time_fmt_new =
674*9664Sjason@ansipunx.net 						    (const char *)told;
675*9664Sjason@ansipunx.net 					}
676*9664Sjason@ansipunx.net 
677*9664Sjason@ansipunx.net 					time_fmt_old = (const char *)told;
678*9664Sjason@ansipunx.net 					time_custom = 1;
679*9664Sjason@ansipunx.net 					continue;
680*9664Sjason@ansipunx.net 				}
681*9664Sjason@ansipunx.net 				continue;
682*9664Sjason@ansipunx.net 			}
683*9664Sjason@ansipunx.net 
684*9664Sjason@ansipunx.net 			continue;
685*9664Sjason@ansipunx.net 
6860Sstevel@tonic-gate 		case 'a':
6870Sstevel@tonic-gate 			aflg++;
6880Sstevel@tonic-gate 			continue;
6890Sstevel@tonic-gate 		case 'A':
6900Sstevel@tonic-gate 			Aflg++;
6910Sstevel@tonic-gate 			continue;
6920Sstevel@tonic-gate 		case 'b':
6930Sstevel@tonic-gate 			bflg = 1;
6940Sstevel@tonic-gate 			qflg = 0;
6950Sstevel@tonic-gate 			continue;
696*9664Sjason@ansipunx.net 		case 'B':
697*9664Sjason@ansipunx.net 			Bflg = 1;
698*9664Sjason@ansipunx.net 			continue;
6990Sstevel@tonic-gate 		case 'c':
7000Sstevel@tonic-gate 			uflg = 0;
7015331Samw 			atm = 0;
7025331Samw 			ctm = 0;
7035331Samw 			mtm = 0;
7045331Samw 			crtm = 0;
7050Sstevel@tonic-gate 			cflg++;
7060Sstevel@tonic-gate 			continue;
7070Sstevel@tonic-gate 		case 'C':
7080Sstevel@tonic-gate 			Cflg = 1;
7090Sstevel@tonic-gate 			mflg = 0;
7100Sstevel@tonic-gate #ifdef XPG4
7110Sstevel@tonic-gate 			lflg = 0;
7120Sstevel@tonic-gate #endif
7130Sstevel@tonic-gate 			continue;
7140Sstevel@tonic-gate 		case 'd':
7150Sstevel@tonic-gate 			dflg++;
7160Sstevel@tonic-gate 			continue;
7170Sstevel@tonic-gate 		case 'e':
7180Sstevel@tonic-gate 			eflg++;
7190Sstevel@tonic-gate 			lflg++;
7200Sstevel@tonic-gate 			statreq++;
7210Sstevel@tonic-gate 			Eflg = 0;
722*9664Sjason@ansipunx.net 			time_fmt_old = FORMAT_LONG;
723*9664Sjason@ansipunx.net 			time_fmt_new = FORMAT_LONG;
7240Sstevel@tonic-gate 			continue;
7250Sstevel@tonic-gate 		case 'E':
7260Sstevel@tonic-gate 			Eflg++;
7270Sstevel@tonic-gate 			lflg++;
7280Sstevel@tonic-gate 			statreq++;
7290Sstevel@tonic-gate 			eflg = 0;
730*9664Sjason@ansipunx.net 			time_fmt_old = FORMAT_ISO_FULL;
731*9664Sjason@ansipunx.net 			time_fmt_new = FORMAT_ISO_FULL;
7320Sstevel@tonic-gate 			continue;
7330Sstevel@tonic-gate 		case 'f':
7340Sstevel@tonic-gate 			fflg++;
7350Sstevel@tonic-gate 			continue;
7360Sstevel@tonic-gate 		case 'F':
7370Sstevel@tonic-gate 			Fflg++;
7380Sstevel@tonic-gate 			statreq++;
7390Sstevel@tonic-gate 			continue;
7400Sstevel@tonic-gate 		case 'g':
7410Sstevel@tonic-gate 			gflg++;
7420Sstevel@tonic-gate 			lflg++;
7430Sstevel@tonic-gate 			statreq++;
7440Sstevel@tonic-gate 			continue;
7450Sstevel@tonic-gate 		case 'h':
7460Sstevel@tonic-gate 			hflg++;
7470Sstevel@tonic-gate 			hscale = 1024;
7480Sstevel@tonic-gate 			continue;
7490Sstevel@tonic-gate 		case 'H':
7500Sstevel@tonic-gate 			Hflg++;
7510Sstevel@tonic-gate 			/* -H and -L are mutually exclusive */
7520Sstevel@tonic-gate 			Lflg = 0;
7530Sstevel@tonic-gate 			continue;
7540Sstevel@tonic-gate 		case 'i':
7550Sstevel@tonic-gate 			iflg++;
7560Sstevel@tonic-gate 			continue;
757*9664Sjason@ansipunx.net 		case 'k':
758*9664Sjason@ansipunx.net 			block_size = 1024;
759*9664Sjason@ansipunx.net 			continue;
7600Sstevel@tonic-gate 		case 'l':
7610Sstevel@tonic-gate 			lflg++;
7620Sstevel@tonic-gate 			statreq++;
7630Sstevel@tonic-gate 			Cflg = 0;
7640Sstevel@tonic-gate 			xflg = 0;
7650Sstevel@tonic-gate 			mflg = 0;
7660Sstevel@tonic-gate 			atflg = 0;
7670Sstevel@tonic-gate 			continue;
7680Sstevel@tonic-gate 		case 'L':
7690Sstevel@tonic-gate 			Lflg++;
7700Sstevel@tonic-gate 			/* -H and -L are mutually exclusive */
7710Sstevel@tonic-gate 			Hflg = 0;
7720Sstevel@tonic-gate 			continue;
7730Sstevel@tonic-gate 		case 'm':
7740Sstevel@tonic-gate 			Cflg = 0;
7750Sstevel@tonic-gate 			mflg = 1;
7760Sstevel@tonic-gate #ifdef XPG4
7770Sstevel@tonic-gate 			lflg = 0;
7780Sstevel@tonic-gate #endif
7790Sstevel@tonic-gate 			continue;
7800Sstevel@tonic-gate 		case 'n':
7810Sstevel@tonic-gate 			nflg++;
7820Sstevel@tonic-gate 			lflg++;
7830Sstevel@tonic-gate 			statreq++;
7840Sstevel@tonic-gate 			Cflg = 0;
7850Sstevel@tonic-gate 			xflg = 0;
7860Sstevel@tonic-gate 			mflg = 0;
7870Sstevel@tonic-gate 			atflg = 0;
7880Sstevel@tonic-gate 			continue;
7890Sstevel@tonic-gate 		case 'o':
7900Sstevel@tonic-gate 			oflg++;
7910Sstevel@tonic-gate 			lflg++;
7920Sstevel@tonic-gate 			statreq++;
7930Sstevel@tonic-gate 			continue;
7940Sstevel@tonic-gate 		case 'p':
7950Sstevel@tonic-gate 			pflg++;
7960Sstevel@tonic-gate 			statreq++;
7970Sstevel@tonic-gate 			continue;
7980Sstevel@tonic-gate 		case 'q':
7990Sstevel@tonic-gate 			qflg = 1;
8000Sstevel@tonic-gate 			bflg = 0;
8010Sstevel@tonic-gate 			continue;
8020Sstevel@tonic-gate 		case 'r':
8030Sstevel@tonic-gate 			rflg = -1;
8040Sstevel@tonic-gate 			continue;
8050Sstevel@tonic-gate 		case 'R':
8060Sstevel@tonic-gate 			Rflg++;
8070Sstevel@tonic-gate 			statreq++;
8080Sstevel@tonic-gate 			continue;
8090Sstevel@tonic-gate 		case 's':
8100Sstevel@tonic-gate 			sflg++;
8110Sstevel@tonic-gate 			statreq++;
8120Sstevel@tonic-gate 			continue;
8130Sstevel@tonic-gate 		case 'S':
8140Sstevel@tonic-gate 			tflg = 0;
815*9664Sjason@ansipunx.net 			Uflg = 0;
8160Sstevel@tonic-gate 			Sflg++;
8170Sstevel@tonic-gate 			statreq++;
8180Sstevel@tonic-gate 			continue;
8190Sstevel@tonic-gate 		case 't':
8200Sstevel@tonic-gate 			Sflg = 0;
821*9664Sjason@ansipunx.net 			Uflg = 0;
8220Sstevel@tonic-gate 			tflg++;
8230Sstevel@tonic-gate 			statreq++;
8240Sstevel@tonic-gate 			continue;
825*9664Sjason@ansipunx.net 		case 'U':
826*9664Sjason@ansipunx.net 			Sflg = 0;
827*9664Sjason@ansipunx.net 			tflg = 0;
828*9664Sjason@ansipunx.net 			Uflg++;
829*9664Sjason@ansipunx.net 			continue;
8300Sstevel@tonic-gate 		case 'u':
8310Sstevel@tonic-gate 			cflg = 0;
8325331Samw 			atm = 0;
8335331Samw 			ctm = 0;
8345331Samw 			mtm = 0;
8355331Samw 			crtm = 0;
8360Sstevel@tonic-gate 			uflg++;
8370Sstevel@tonic-gate 			continue;
8381420Smarks 		case 'V':
8391420Smarks 			Vflg++;
8401420Smarks 			/*FALLTHROUGH*/
841789Sahrens 		case 'v':
842789Sahrens 			vflg++;
843789Sahrens #if !defined(XPG4)
844789Sahrens 			if (lflg)
845789Sahrens 				continue;
846789Sahrens #endif
847789Sahrens 			lflg++;
848789Sahrens 			statreq++;
849789Sahrens 			Cflg = 0;
850789Sahrens 			xflg = 0;
851789Sahrens 			mflg = 0;
852789Sahrens 			continue;
853*9664Sjason@ansipunx.net 		case 'w':
854*9664Sjason@ansipunx.net 			wflg++;
855*9664Sjason@ansipunx.net 			num_cols = atoi(optarg);
856*9664Sjason@ansipunx.net 			continue;
8570Sstevel@tonic-gate 		case 'x':
8580Sstevel@tonic-gate 			xflg = 1;
8590Sstevel@tonic-gate 			Cflg = 1;
8600Sstevel@tonic-gate 			mflg = 0;
8610Sstevel@tonic-gate #ifdef XPG4
8620Sstevel@tonic-gate 			lflg = 0;
8630Sstevel@tonic-gate #endif
8640Sstevel@tonic-gate 			continue;
8650Sstevel@tonic-gate 		case '1':
8660Sstevel@tonic-gate 			Cflg = 0;
8670Sstevel@tonic-gate 			continue;
8680Sstevel@tonic-gate 		case '@':
8690Sstevel@tonic-gate #if !defined(XPG4)
8700Sstevel@tonic-gate 			/*
8710Sstevel@tonic-gate 			 * -l has precedence over -@
8720Sstevel@tonic-gate 			 */
8730Sstevel@tonic-gate 			if (lflg)
8740Sstevel@tonic-gate 				continue;
8750Sstevel@tonic-gate #endif
8760Sstevel@tonic-gate 			atflg++;
8770Sstevel@tonic-gate 			lflg++;
8780Sstevel@tonic-gate 			statreq++;
8790Sstevel@tonic-gate 			Cflg = 0;
8800Sstevel@tonic-gate 			xflg = 0;
8810Sstevel@tonic-gate 			mflg = 0;
8820Sstevel@tonic-gate 			continue;
8835331Samw 		case '/':
8845331Samw 			saflg++;
8855331Samw 			if (optarg != NULL) {
8865331Samw 				if (strcmp(optarg, "c") == 0) {
8875331Samw 					copt++;
8885331Samw 					vopt = 0;
8895331Samw 				} else if (strcmp(optarg, "v") == 0) {
8905331Samw 					vopt++;
8915331Samw 					copt = 0;
8925331Samw 				} else
8935331Samw 					opterr++;
8945331Samw 			} else
8955331Samw 				opterr++;
8965331Samw 			lflg++;
8975331Samw 			statreq++;
8985331Samw 			Cflg = 0;
8995331Samw 			xflg = 0;
9005331Samw 			mflg = 0;
9015331Samw 			continue;
9025331Samw 		case '%':
9035331Samw 			tmflg++;
9045331Samw 			if (optarg != NULL) {
9055331Samw 				if (strcmp(optarg, "ctime") == 0) {
9065331Samw 					ctm++;
9075331Samw 					atm = 0;
9085331Samw 					mtm = 0;
9095331Samw 					crtm = 0;
9105331Samw 				} else if (strcmp(optarg, "atime") == 0) {
9115331Samw 					atm++;
9125331Samw 					ctm = 0;
9135331Samw 					mtm = 0;
9145331Samw 					crtm = 0;
9155331Samw 					uflg = 0;
9165331Samw 					cflg = 0;
9175331Samw 				} else if (strcmp(optarg, "mtime") == 0) {
9185331Samw 					mtm++;
9195331Samw 					atm = 0;
9205331Samw 					ctm = 0;
9215331Samw 					crtm = 0;
9225331Samw 					uflg = 0;
9235331Samw 					cflg = 0;
9245331Samw 				} else if (strcmp(optarg, "crtime") == 0) {
9255331Samw 					crtm++;
9265331Samw 					atm = 0;
9275331Samw 					ctm = 0;
9285331Samw 					mtm = 0;
9295331Samw 					uflg = 0;
9305331Samw 					cflg = 0;
9315331Samw 				} else if (strcmp(optarg, "all") == 0) {
9325331Samw 					alltm++;
9335331Samw 					atm = 0;
9345331Samw 					ctm = 0;
9355331Samw 					mtm = 0;
9365331Samw 					crtm = 0;
9375331Samw 				} else
9385331Samw 					opterr++;
9395331Samw 			} else
9405331Samw 				opterr++;
9415331Samw 
9425331Samw 			Sflg = 0;
9435331Samw 			statreq++;
9445331Samw 			mflg = 0;
9455331Samw 			continue;
9460Sstevel@tonic-gate 		case '?':
9470Sstevel@tonic-gate 			opterr++;
9480Sstevel@tonic-gate 			continue;
9490Sstevel@tonic-gate 		}
950*9664Sjason@ansipunx.net 
9510Sstevel@tonic-gate 	if (opterr) {
9520Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
953*9664Sjason@ansipunx.net 		    "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]"
9545331Samw 		    "%%[atime | crtime | ctime | mtime | all]"
9555331Samw 		    " [files]\n"));
9560Sstevel@tonic-gate 		exit(2);
9570Sstevel@tonic-gate 	}
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate 	if (fflg) {
9600Sstevel@tonic-gate 		aflg++;
9610Sstevel@tonic-gate 		lflg = 0;
9620Sstevel@tonic-gate 		sflg = 0;
9630Sstevel@tonic-gate 		tflg = 0;
9640Sstevel@tonic-gate 		Sflg = 0;
9650Sstevel@tonic-gate 		statreq = 0;
9660Sstevel@tonic-gate 	}
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 	fixedwidth = 2;
9690Sstevel@tonic-gate 	if (pflg || Fflg)
9700Sstevel@tonic-gate 		fixedwidth++;
9710Sstevel@tonic-gate 	if (iflg)
9720Sstevel@tonic-gate 		fixedwidth += 11;
9730Sstevel@tonic-gate 	if (sflg)
9740Sstevel@tonic-gate 		fixedwidth += 5;
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate 	if (lflg) {
9770Sstevel@tonic-gate 		if (!gflg && !oflg)
9780Sstevel@tonic-gate 			gflg = oflg = 1;
9790Sstevel@tonic-gate 		else
9800Sstevel@tonic-gate 		if (gflg && oflg)
9810Sstevel@tonic-gate 			gflg = oflg = 0;
9820Sstevel@tonic-gate 		Cflg = mflg = 0;
9830Sstevel@tonic-gate 	}
9840Sstevel@tonic-gate 
985*9664Sjason@ansipunx.net 	if (!wflg && (Cflg || mflg)) {
9860Sstevel@tonic-gate 		char *clptr;
9870Sstevel@tonic-gate 		if ((clptr = getenv("COLUMNS")) != NULL)
9880Sstevel@tonic-gate 			num_cols = atoi(clptr);
9890Sstevel@tonic-gate #ifdef TERMINFO
9900Sstevel@tonic-gate 		else {
9910Sstevel@tonic-gate 			if (ioctl(1, TIOCGWINSZ, &win) != -1)
9920Sstevel@tonic-gate 				num_cols = (win.ws_col == 0 ? 80 : win.ws_col);
9930Sstevel@tonic-gate 		}
9940Sstevel@tonic-gate #endif
9950Sstevel@tonic-gate 	}
9960Sstevel@tonic-gate 
997*9664Sjason@ansipunx.net 	if (num_cols < 20 || num_cols > 1000)
998*9664Sjason@ansipunx.net 		/* assume it is an error */
999*9664Sjason@ansipunx.net 		num_cols = 80;
1000*9664Sjason@ansipunx.net 
10010Sstevel@tonic-gate 	/* allocate space for flist and the associated	*/
10020Sstevel@tonic-gate 	/* data structures (lbufs)			*/
10030Sstevel@tonic-gate 	maxfils = quantn;
10040Sstevel@tonic-gate 	if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) ||
10050Sstevel@tonic-gate 	    ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) {
10060Sstevel@tonic-gate 		perror("ls");
10070Sstevel@tonic-gate 		exit(2);
10080Sstevel@tonic-gate 	}
10090Sstevel@tonic-gate 	if ((amino = (argc-optind)) == 0) {
10100Sstevel@tonic-gate 					/*
10110Sstevel@tonic-gate 					 * case when no names are given
10120Sstevel@tonic-gate 					 * in ls-command and current
10130Sstevel@tonic-gate 					 * directory is to be used
10140Sstevel@tonic-gate 					 */
10150Sstevel@tonic-gate 		argv[optind] = dotp;
10160Sstevel@tonic-gate 	}
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 	for (i = 0; i < (amino ? amino : 1); i++) {
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate 		/*
10210Sstevel@tonic-gate 		 * If we are recursing, we need to make sure we don't
10220Sstevel@tonic-gate 		 * get into an endless loop.  To keep track of the inodes
10230Sstevel@tonic-gate 		 * (actually, just the directories) visited, we
10240Sstevel@tonic-gate 		 * maintain a directory ancestry list for a file
10250Sstevel@tonic-gate 		 * hierarchy.  As we go deeper into the hierarchy,
10260Sstevel@tonic-gate 		 * a parent directory passes its directory list
10270Sstevel@tonic-gate 		 * info (device id, inode number, and a pointer to
10280Sstevel@tonic-gate 		 * its parent) to each of its children.  As we
10290Sstevel@tonic-gate 		 * process a child that is a directory, we save
10300Sstevel@tonic-gate 		 * its own personal directory list info.  We then
10310Sstevel@tonic-gate 		 * check to see if the child has already been
10320Sstevel@tonic-gate 		 * processed by comparing its device id and inode
10330Sstevel@tonic-gate 		 * number from its own personal directory list info
10340Sstevel@tonic-gate 		 * to that of each of its ancestors.  If there is a
10350Sstevel@tonic-gate 		 * match, then we know we've detected a cycle.
10360Sstevel@tonic-gate 		 */
10370Sstevel@tonic-gate 		if (Rflg) {
10380Sstevel@tonic-gate 			/*
10390Sstevel@tonic-gate 			 * This is the first parent in this lineage
10400Sstevel@tonic-gate 			 * (first in a directory hierarchy), so
10410Sstevel@tonic-gate 			 * this parent's parent doesn't exist.  We
10420Sstevel@tonic-gate 			 * only initialize myinfo when we are
10430Sstevel@tonic-gate 			 * recursing, otherwise it's not used.
10440Sstevel@tonic-gate 			 */
10450Sstevel@tonic-gate 			if ((myinfo = (struct ditem *)malloc(
10460Sstevel@tonic-gate 			    sizeof (struct ditem))) == NULL) {
10470Sstevel@tonic-gate 				perror("ls");
10480Sstevel@tonic-gate 				exit(2);
10490Sstevel@tonic-gate 			} else {
10500Sstevel@tonic-gate 				myinfo->dev = 0;
10510Sstevel@tonic-gate 				myinfo->ino = 0;
10520Sstevel@tonic-gate 				myinfo->parent = NULL;
10530Sstevel@tonic-gate 			}
10540Sstevel@tonic-gate 		}
10550Sstevel@tonic-gate 
10560Sstevel@tonic-gate 		if (Cflg || mflg) {
10570Sstevel@tonic-gate 			width = strcol((unsigned char *)argv[optind]);
10580Sstevel@tonic-gate 			if (width > filewidth)
10590Sstevel@tonic-gate 				filewidth = width;
10600Sstevel@tonic-gate 		}
10610Sstevel@tonic-gate 		if ((ep = gstat((*argv[optind] ? argv[optind] : dotp),
10620Sstevel@tonic-gate 		    1, myinfo)) == NULL) {
10630Sstevel@tonic-gate 			if (nomocore)
10640Sstevel@tonic-gate 				exit(2);
10650Sstevel@tonic-gate 			err = 2;
10660Sstevel@tonic-gate 			optind++;
10670Sstevel@tonic-gate 			continue;
10680Sstevel@tonic-gate 		}
10690Sstevel@tonic-gate 		ep->ln.namep = (*argv[optind] ? argv[optind] : dotp);
10700Sstevel@tonic-gate 		ep->lflags |= ISARG;
10710Sstevel@tonic-gate 		optind++;
10720Sstevel@tonic-gate 		nargs++;	/* count good arguments stored in flist */
10736866Sbasabi 		if (acl_err)
10746866Sbasabi 			err = 2;
10750Sstevel@tonic-gate 	}
10760Sstevel@tonic-gate 	colwidth = fixedwidth + filewidth;
1077*9664Sjason@ansipunx.net 	if (!Uflg)
1078*9664Sjason@ansipunx.net 		qsort(flist, (unsigned)nargs, sizeof (struct lbuf *),
1079*9664Sjason@ansipunx.net 		    (int (*)(const void *, const void *))compar);
10800Sstevel@tonic-gate 	for (i = 0; i < nargs; i++) {
10810Sstevel@tonic-gate 		if (flist[i]->ltype == 'd' && dflg == 0 || fflg)
10820Sstevel@tonic-gate 			break;
10830Sstevel@tonic-gate 	}
1084*9664Sjason@ansipunx.net 
1085*9664Sjason@ansipunx.net 	if (colorflg)
1086*9664Sjason@ansipunx.net 		ls_color_init();
1087*9664Sjason@ansipunx.net 
10880Sstevel@tonic-gate 	pem(&flist[0], &flist[i], 0);
10890Sstevel@tonic-gate 	for (; i < nargs; i++) {
10900Sstevel@tonic-gate 		pdirectory(flist[i]->ln.namep, Rflg ||
10910Sstevel@tonic-gate 		    (amino > 1), nargs, 0, flist[i]->ancinfo);
10920Sstevel@tonic-gate 		if (nomocore)
10930Sstevel@tonic-gate 			exit(2);
10940Sstevel@tonic-gate 		/* -R: print subdirectories found */
10950Sstevel@tonic-gate 		while (dfirst || cdfirst) {
10960Sstevel@tonic-gate 			/* Place direct subdirs on front in right order */
10970Sstevel@tonic-gate 			while (cdfirst) {
10980Sstevel@tonic-gate 				/* reverse cdfirst onto front of dfirst */
10990Sstevel@tonic-gate 				dtemp = cdfirst;
11000Sstevel@tonic-gate 				cdfirst = cdfirst -> dc_next;
11010Sstevel@tonic-gate 				dtemp -> dc_next = dfirst;
11020Sstevel@tonic-gate 				dfirst = dtemp;
11030Sstevel@tonic-gate 			}
11040Sstevel@tonic-gate 			/* take off first dir on dfirst & print it */
11050Sstevel@tonic-gate 			dtemp = dfirst;
11060Sstevel@tonic-gate 			dfirst = dfirst->dc_next;
11070Sstevel@tonic-gate 			pdirectory(dtemp->dc_name, 1, nargs,
11080Sstevel@tonic-gate 			    dtemp->cycle_detected, dtemp->myancinfo);
11090Sstevel@tonic-gate 			if (nomocore)
11100Sstevel@tonic-gate 				exit(2);
11110Sstevel@tonic-gate 			free(dtemp->dc_name);
11120Sstevel@tonic-gate 			free(dtemp);
11130Sstevel@tonic-gate 		}
11140Sstevel@tonic-gate 	}
1115*9664Sjason@ansipunx.net 
11160Sstevel@tonic-gate 	return (err);
11170Sstevel@tonic-gate }
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate /*
11200Sstevel@tonic-gate  * pdirectory: print the directory name, labelling it if title is
11210Sstevel@tonic-gate  * nonzero, using lp as the place to start reading in the dir.
11220Sstevel@tonic-gate  */
11230Sstevel@tonic-gate static void
11240Sstevel@tonic-gate pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo)
11250Sstevel@tonic-gate {
11260Sstevel@tonic-gate 	struct dchain *dp;
11270Sstevel@tonic-gate 	struct lbuf *ap;
11280Sstevel@tonic-gate 	char *pname;
11290Sstevel@tonic-gate 	int j;
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 	filewidth = 0;
11320Sstevel@tonic-gate 	curdir = name;
11330Sstevel@tonic-gate 	if (title) {
11340Sstevel@tonic-gate 		if (!first)
11350Sstevel@tonic-gate 			(void) putc('\n', stdout);
11360Sstevel@tonic-gate 		pprintf(name, ":");
11370Sstevel@tonic-gate 		new_line();
11380Sstevel@tonic-gate 	}
11390Sstevel@tonic-gate 	/*
11400Sstevel@tonic-gate 	 * If there was a cycle detected, then notify and don't report
11410Sstevel@tonic-gate 	 * further.
11420Sstevel@tonic-gate 	 */
11430Sstevel@tonic-gate 	if (cdetect) {
11440Sstevel@tonic-gate 		if (lflg || sflg) {
11450Sstevel@tonic-gate 			curcol += printf(gettext("total %d"), 0);
11460Sstevel@tonic-gate 			new_line();
11470Sstevel@tonic-gate 		}
11480Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
11490Sstevel@tonic-gate 		    "ls: cycle detected for %s\n"), name);
11500Sstevel@tonic-gate 		return;
11510Sstevel@tonic-gate 	}
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	nfiles = lp;
11540Sstevel@tonic-gate 	rddir(name, myinfo);
11550Sstevel@tonic-gate 	if (nomocore)
11560Sstevel@tonic-gate 		return;
1157*9664Sjason@ansipunx.net 	if (fflg == 0 && Uflg == 0)
11580Sstevel@tonic-gate 		qsort(&flist[lp], (unsigned)(nfiles - lp),
11590Sstevel@tonic-gate 		    sizeof (struct lbuf *),
11600Sstevel@tonic-gate 		    (int (*)(const void *, const void *))compar);
11610Sstevel@tonic-gate 	if (Rflg) {
11620Sstevel@tonic-gate 		for (j = nfiles - 1; j >= lp; j--) {
11630Sstevel@tonic-gate 			ap = flist[j];
11640Sstevel@tonic-gate 			if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") &&
11650Sstevel@tonic-gate 			    strcmp(ap->ln.lname, "..")) {
11660Sstevel@tonic-gate 				dp = malloc(sizeof (struct dchain));
11670Sstevel@tonic-gate 				if (dp == NULL) {
11680Sstevel@tonic-gate 					perror("ls");
11690Sstevel@tonic-gate 					exit(2);
11700Sstevel@tonic-gate 				}
11710Sstevel@tonic-gate 				pname = makename(curdir, ap->ln.lname);
11720Sstevel@tonic-gate 				if ((dp->dc_name = strdup(pname)) == NULL) {
11730Sstevel@tonic-gate 					perror("ls");
11740Sstevel@tonic-gate 					exit(2);
11750Sstevel@tonic-gate 				}
11760Sstevel@tonic-gate 				dp->cycle_detected = ap->cycle;
11770Sstevel@tonic-gate 				dp->myancinfo = ap->ancinfo;
11780Sstevel@tonic-gate 				dp->dc_next = dfirst;
11790Sstevel@tonic-gate 				dfirst = dp;
11800Sstevel@tonic-gate 			}
11810Sstevel@tonic-gate 		}
11820Sstevel@tonic-gate 	}
11830Sstevel@tonic-gate 	if (lflg || sflg) {
11840Sstevel@tonic-gate 		curcol += printf(gettext("total %llu"), tblocks);
11850Sstevel@tonic-gate 		new_line();
11860Sstevel@tonic-gate 	}
11870Sstevel@tonic-gate 	pem(&flist[lp], &flist[nfiles], lflg||sflg);
11880Sstevel@tonic-gate }
11890Sstevel@tonic-gate 
11900Sstevel@tonic-gate /*
11910Sstevel@tonic-gate  * pem: print 'em. Print a list of files (e.g. a directory) bounded
11920Sstevel@tonic-gate  * by slp and lp.
11930Sstevel@tonic-gate  */
11940Sstevel@tonic-gate static void
11950Sstevel@tonic-gate pem(struct lbuf **slp, struct lbuf **lp, int tot_flag)
11960Sstevel@tonic-gate {
11970Sstevel@tonic-gate 	long row, nrows, i;
11980Sstevel@tonic-gate 	int col, ncols;
11990Sstevel@tonic-gate 	struct lbuf **ep;
12000Sstevel@tonic-gate 
12010Sstevel@tonic-gate 	if (Cflg || mflg) {
12020Sstevel@tonic-gate 		if (colwidth > num_cols) {
12030Sstevel@tonic-gate 			ncols = 1;
12040Sstevel@tonic-gate 		} else {
12050Sstevel@tonic-gate 			ncols = num_cols / colwidth;
12060Sstevel@tonic-gate 		}
12070Sstevel@tonic-gate 	}
12080Sstevel@tonic-gate 
12090Sstevel@tonic-gate 	if (ncols == 1 || mflg || xflg || !Cflg) {
12100Sstevel@tonic-gate 		for (ep = slp; ep < lp; ep++)
12110Sstevel@tonic-gate 			pentry(*ep);
12120Sstevel@tonic-gate 		new_line();
12130Sstevel@tonic-gate 		return;
12140Sstevel@tonic-gate 	}
12150Sstevel@tonic-gate 	/* otherwise print -C columns */
12160Sstevel@tonic-gate 	if (tot_flag) {
12170Sstevel@tonic-gate 		slp--;
12180Sstevel@tonic-gate 		row = 1;
12190Sstevel@tonic-gate 	}
12200Sstevel@tonic-gate 	else
12210Sstevel@tonic-gate 		row = 0;
12220Sstevel@tonic-gate 
12230Sstevel@tonic-gate 	nrows = (lp - slp - 1) / ncols + 1;
12240Sstevel@tonic-gate 	for (i = 0; i < nrows; i++, row++) {
12250Sstevel@tonic-gate 		for (col = 0; col < ncols; col++) {
12260Sstevel@tonic-gate 			ep = slp + (nrows * col) + row;
12270Sstevel@tonic-gate 			if (ep < lp)
12280Sstevel@tonic-gate 				pentry(*ep);
12290Sstevel@tonic-gate 		}
12300Sstevel@tonic-gate 		new_line();
12310Sstevel@tonic-gate 	}
12320Sstevel@tonic-gate }
12330Sstevel@tonic-gate 
12340Sstevel@tonic-gate /*
12350Sstevel@tonic-gate  * print one output entry;
12360Sstevel@tonic-gate  * if uid/gid is not found in the appropriate
12370Sstevel@tonic-gate  * file(passwd/group), then print uid/gid instead of
12380Sstevel@tonic-gate  * user/group name;
12390Sstevel@tonic-gate  */
12400Sstevel@tonic-gate static void
12410Sstevel@tonic-gate pentry(struct lbuf *ap)
12420Sstevel@tonic-gate {
12430Sstevel@tonic-gate 	struct lbuf *p;
12440Sstevel@tonic-gate 	numbuf_t hbuf;
12450Sstevel@tonic-gate 	char buf[BUFSIZ];
12460Sstevel@tonic-gate 	char *dmark = "";	/* Used if -p or -F option active */
12470Sstevel@tonic-gate 	char *cp;
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate 	p = ap;
12500Sstevel@tonic-gate 	column();
12510Sstevel@tonic-gate 	if (iflg)
12520Sstevel@tonic-gate 		if (mflg && !lflg)
12530Sstevel@tonic-gate 			curcol += printf("%llu ", (long long)p->lnum);
12540Sstevel@tonic-gate 		else
12550Sstevel@tonic-gate 			curcol += printf("%10llu ", (long long)p->lnum);
12560Sstevel@tonic-gate 	if (sflg)
12570Sstevel@tonic-gate 		curcol += printf((mflg && !lflg) ? "%lld " :
12585331Samw 		    (p->lblocks < 10000) ? "%4lld " : "%lld ",
12595331Samw 		    (p->ltype != 'b' && p->ltype != 'c') ?
12605331Samw 		    p->lblocks : 0LL);
12610Sstevel@tonic-gate 	if (lflg) {
12620Sstevel@tonic-gate 		(void) putchar(p->ltype);
12630Sstevel@tonic-gate 		curcol++;
12640Sstevel@tonic-gate 		pmode(p->lflags);
12650Sstevel@tonic-gate 
12660Sstevel@tonic-gate 		/* ACL: additional access mode flag */
12670Sstevel@tonic-gate 		(void) putchar(p->acl);
12680Sstevel@tonic-gate 		curcol++;
12690Sstevel@tonic-gate 
12700Sstevel@tonic-gate 		curcol += printf("%3lu ", (ulong_t)p->lnl);
12710Sstevel@tonic-gate 		if (oflg)
12720Sstevel@tonic-gate 			if (!nflg) {
12730Sstevel@tonic-gate 				cp = getname(p->luid);
12740Sstevel@tonic-gate 				curcol += printf("%-8s ", cp);
12750Sstevel@tonic-gate 			} else
12760Sstevel@tonic-gate 				curcol += printf("%-8lu ", (ulong_t)p->luid);
12770Sstevel@tonic-gate 		if (gflg)
12780Sstevel@tonic-gate 			if (!nflg) {
12790Sstevel@tonic-gate 				cp = getgroup(p->lgid);
12800Sstevel@tonic-gate 				curcol += printf("%-8s ", cp);
12810Sstevel@tonic-gate 			} else
12820Sstevel@tonic-gate 				curcol += printf("%-8lu ", (ulong_t)p->lgid);
12830Sstevel@tonic-gate 		if (p->ltype == 'b' || p->ltype == 'c') {
12840Sstevel@tonic-gate 			curcol += printf("%3u, %2u",
12850Sstevel@tonic-gate 			    (uint_t)major((dev_t)p->lsize),
12860Sstevel@tonic-gate 			    (uint_t)minor((dev_t)p->lsize));
12870Sstevel@tonic-gate 		} else if (hflg && (p->lsize >= hscale)) {
12880Sstevel@tonic-gate 			curcol += printf("%7s",
12890Sstevel@tonic-gate 			    number_to_scaled_string(hbuf, p->lsize, hscale));
12900Sstevel@tonic-gate 		} else {
1291*9664Sjason@ansipunx.net 			uint64_t bsize = p->lsize / block_size;
1292*9664Sjason@ansipunx.net 
1293*9664Sjason@ansipunx.net 			/*
1294*9664Sjason@ansipunx.net 			 * Round up only when using blocks > 1 byte, otherwise
1295*9664Sjason@ansipunx.net 			 * 'normal' sizes display 1 byte too large.
1296*9664Sjason@ansipunx.net 			 */
1297*9664Sjason@ansipunx.net 			if (p->lsize % block_size != 0)
1298*9664Sjason@ansipunx.net 				bsize++;
1299*9664Sjason@ansipunx.net 
1300*9664Sjason@ansipunx.net 			curcol += printf("%7" PRIu64, bsize);
13010Sstevel@tonic-gate 		}
1302*9664Sjason@ansipunx.net 		format_time(p->lmtime.tv_sec, p->lmtime.tv_nsec);
13035331Samw 		/* format extended system attribute time */
13045331Samw 		if (tmflg && crtm)
13055331Samw 			format_attrtime(p);
13065331Samw 
13070Sstevel@tonic-gate 		curcol += printf("%s", time_buf);
13085331Samw 
13090Sstevel@tonic-gate 	}
13100Sstevel@tonic-gate 	/*
13110Sstevel@tonic-gate 	 * prevent both "->" and trailing marks
13120Sstevel@tonic-gate 	 * from appearing
13130Sstevel@tonic-gate 	 */
13140Sstevel@tonic-gate 
13150Sstevel@tonic-gate 	if (pflg && p->ltype == 'd')
13160Sstevel@tonic-gate 		dmark = "/";
13170Sstevel@tonic-gate 
13180Sstevel@tonic-gate 	if (Fflg && !(lflg && p->flinkto)) {
13190Sstevel@tonic-gate 		if (p->ltype == 'd')
13200Sstevel@tonic-gate 			dmark = "/";
13210Sstevel@tonic-gate 		else if (p->ltype == 'D')
13220Sstevel@tonic-gate 			dmark = ">";
13230Sstevel@tonic-gate 		else if (p->ltype == 'p')
13240Sstevel@tonic-gate 			dmark = "|";
13250Sstevel@tonic-gate 		else if (p->ltype == 'l')
13260Sstevel@tonic-gate 			dmark = "@";
13270Sstevel@tonic-gate 		else if (p->ltype == 's')
13280Sstevel@tonic-gate 			dmark = "=";
1329*9664Sjason@ansipunx.net 		else if (!file_typeflg &&
1330*9664Sjason@ansipunx.net 		    (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)))
13310Sstevel@tonic-gate 			dmark = "*";
13320Sstevel@tonic-gate 		else
13330Sstevel@tonic-gate 			dmark = "";
13340Sstevel@tonic-gate 	}
13350Sstevel@tonic-gate 
13360Sstevel@tonic-gate 	if (lflg && p->flinkto) {
13370Sstevel@tonic-gate 		(void) strncpy(buf, " -> ", 4);
13380Sstevel@tonic-gate 		(void) strcpy(buf + 4, p->flinkto);
13390Sstevel@tonic-gate 		dmark = buf;
13400Sstevel@tonic-gate 	}
1341*9664Sjason@ansipunx.net 
1342*9664Sjason@ansipunx.net 	if (colorflg)
1343*9664Sjason@ansipunx.net 		ls_start_color(p);
1344*9664Sjason@ansipunx.net 
13450Sstevel@tonic-gate 	if (p->lflags & ISARG) {
13460Sstevel@tonic-gate 		if (qflg || bflg)
13470Sstevel@tonic-gate 			pprintf(p->ln.namep, dmark);
13480Sstevel@tonic-gate 		else {
13490Sstevel@tonic-gate 			(void) printf("%s%s", p->ln.namep, dmark);
13500Sstevel@tonic-gate 			curcol += strcol((unsigned char *)p->ln.namep);
13510Sstevel@tonic-gate 			curcol += strcol((unsigned char *)dmark);
13520Sstevel@tonic-gate 		}
13530Sstevel@tonic-gate 	} else {
13540Sstevel@tonic-gate 		if (qflg || bflg)
13550Sstevel@tonic-gate 			pprintf(p->ln.lname, dmark);
13560Sstevel@tonic-gate 		else {
13570Sstevel@tonic-gate 			(void) printf("%s%s", p->ln.lname, dmark);
13580Sstevel@tonic-gate 			curcol += strcol((unsigned char *)p->ln.lname);
13590Sstevel@tonic-gate 			curcol += strcol((unsigned char *)dmark);
13600Sstevel@tonic-gate 		}
13610Sstevel@tonic-gate 	}
1362789Sahrens 
1363*9664Sjason@ansipunx.net 	if (colorflg)
1364*9664Sjason@ansipunx.net 		ls_end_color();
1365*9664Sjason@ansipunx.net 
13665331Samw 	/* Display extended system attributes */
13675331Samw 	if (saflg) {
13685331Samw 		int i;
13695331Samw 
13705331Samw 		new_line();
13715331Samw 		(void) printf("	\t{");
13725331Samw 		if (p->exttr != NULL) {
13735331Samw 			int k = 0;
13745331Samw 			for (i = 0; i < sacnt; i++) {
13755331Samw 				if (p->exttr[i].name != NULL)
13765331Samw 					k++;
13775331Samw 			}
13785331Samw 			for (i = 0; i < sacnt; i++) {
13795331Samw 				if (p->exttr[i].name != NULL) {
13805331Samw 					(void) printf("%s", p->exttr[i].name);
13815331Samw 					k--;
13825331Samw 					if (vopt && (k != 0))
13835331Samw 						(void) printf(",");
13845331Samw 				}
13855331Samw 			}
13865331Samw 		}
13875331Samw 		(void) printf("}\n");
13885331Samw 	}
13895331Samw 	/* Display file timestamps and extended system attribute timestamps */
13905331Samw 	if (tmflg && alltm) {
13915331Samw 		new_line();
13925331Samw 		print_time(p);
13935331Samw 		new_line();
13945331Samw 	}
1395789Sahrens 	if (vflg) {
1396789Sahrens 		new_line();
1397789Sahrens 		if (p->aclp) {
13981420Smarks 			acl_printacl(p->aclp, num_cols, Vflg);
1399789Sahrens 		}
1400789Sahrens 	}
14015331Samw 	/* Free extended system attribute lists */
14025331Samw 	if (saflg || tmflg)
14035331Samw 		free_sysattr(p);
14040Sstevel@tonic-gate }
14050Sstevel@tonic-gate 
14060Sstevel@tonic-gate /* print various r,w,x permissions */
14070Sstevel@tonic-gate static void
14080Sstevel@tonic-gate pmode(mode_t aflag)
14090Sstevel@tonic-gate {
14100Sstevel@tonic-gate 	/* these arrays are declared static to allow initializations */
14110Sstevel@tonic-gate 	static int	m0[] = { 1, S_IRUSR, 'r', '-' };
14120Sstevel@tonic-gate 	static int	m1[] = { 1, S_IWUSR, 'w', '-' };
14130Sstevel@tonic-gate 	static int	m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR,
14140Sstevel@tonic-gate 	    'x', S_ISUID, 'S', '-' };
14150Sstevel@tonic-gate 	static int	m3[] = { 1, S_IRGRP, 'r', '-' };
14160Sstevel@tonic-gate 	static int	m4[] = { 1, S_IWGRP, 'w', '-' };
14170Sstevel@tonic-gate 	static int	m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP,
14180Sstevel@tonic-gate 				'x', S_ISGID|LS_NOTREG, 'S',
14190Sstevel@tonic-gate #ifdef XPG4
14200Sstevel@tonic-gate 		S_ISGID, 'L', '-'};
14210Sstevel@tonic-gate #else
14220Sstevel@tonic-gate 		S_ISGID, 'l', '-'};
14230Sstevel@tonic-gate #endif
14240Sstevel@tonic-gate 	static int	m6[] = { 1, S_IROTH, 'r', '-' };
14250Sstevel@tonic-gate 	static int	m7[] = { 1, S_IWOTH, 'w', '-' };
14260Sstevel@tonic-gate 	static int	m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH,
14270Sstevel@tonic-gate 	    'x', S_ISVTX, 'T', '-'};
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate 	static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8};
14300Sstevel@tonic-gate 
14310Sstevel@tonic-gate 	int **mp;
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 	flags = aflag;
14340Sstevel@tonic-gate 	for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++)
14350Sstevel@tonic-gate 		selection(*mp);
14360Sstevel@tonic-gate }
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate static void
14390Sstevel@tonic-gate selection(int *pairp)
14400Sstevel@tonic-gate {
14410Sstevel@tonic-gate 	int n;
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate 	n = *pairp++;
14440Sstevel@tonic-gate 	while (n-->0) {
14450Sstevel@tonic-gate 		if ((flags & *pairp) == *pairp) {
14460Sstevel@tonic-gate 			pairp++;
14470Sstevel@tonic-gate 			break;
14480Sstevel@tonic-gate 		} else {
14490Sstevel@tonic-gate 			pairp += 2;
14500Sstevel@tonic-gate 		}
14510Sstevel@tonic-gate 	}
14520Sstevel@tonic-gate 	(void) putchar(*pairp);
14530Sstevel@tonic-gate 	curcol++;
14540Sstevel@tonic-gate }
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate /*
14570Sstevel@tonic-gate  * column: get to the beginning of the next column.
14580Sstevel@tonic-gate  */
14590Sstevel@tonic-gate static void
14600Sstevel@tonic-gate column(void)
14610Sstevel@tonic-gate {
14620Sstevel@tonic-gate 	if (curcol == 0)
14630Sstevel@tonic-gate 		return;
14640Sstevel@tonic-gate 	if (mflg) {
14650Sstevel@tonic-gate 		(void) putc(',', stdout);
14660Sstevel@tonic-gate 		curcol++;
14670Sstevel@tonic-gate 		if (curcol + colwidth + 2 > num_cols) {
14680Sstevel@tonic-gate 			(void) putc('\n', stdout);
14690Sstevel@tonic-gate 			curcol = 0;
14700Sstevel@tonic-gate 			return;
14710Sstevel@tonic-gate 		}
14720Sstevel@tonic-gate 		(void) putc(' ', stdout);
14730Sstevel@tonic-gate 		curcol++;
14740Sstevel@tonic-gate 		return;
14750Sstevel@tonic-gate 	}
14760Sstevel@tonic-gate 	if (Cflg == 0) {
14770Sstevel@tonic-gate 		(void) putc('\n', stdout);
14780Sstevel@tonic-gate 		curcol = 0;
14790Sstevel@tonic-gate 		return;
14800Sstevel@tonic-gate 	}
14810Sstevel@tonic-gate 	if ((curcol / colwidth + 2) * colwidth > num_cols) {
14820Sstevel@tonic-gate 		(void) putc('\n', stdout);
14830Sstevel@tonic-gate 		curcol = 0;
14840Sstevel@tonic-gate 		return;
14850Sstevel@tonic-gate 	}
14860Sstevel@tonic-gate 	do {
14870Sstevel@tonic-gate 		(void) putc(' ', stdout);
14880Sstevel@tonic-gate 		curcol++;
14890Sstevel@tonic-gate 	} while (curcol % colwidth);
14900Sstevel@tonic-gate }
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate static void
14930Sstevel@tonic-gate new_line(void)
14940Sstevel@tonic-gate {
14950Sstevel@tonic-gate 	if (curcol) {
14960Sstevel@tonic-gate 		first = 0;
14970Sstevel@tonic-gate 		(void) putc('\n', stdout);
14980Sstevel@tonic-gate 		curcol = 0;
14990Sstevel@tonic-gate 	}
15000Sstevel@tonic-gate }
15010Sstevel@tonic-gate 
15020Sstevel@tonic-gate /*
15030Sstevel@tonic-gate  * read each filename in directory dir and store its
15040Sstevel@tonic-gate  * status in flist[nfiles]
15050Sstevel@tonic-gate  * use makename() to form pathname dir/filename;
15060Sstevel@tonic-gate  */
15070Sstevel@tonic-gate static void
15080Sstevel@tonic-gate rddir(char *dir, struct ditem *myinfo)
15090Sstevel@tonic-gate {
15100Sstevel@tonic-gate 	struct dirent *dentry;
15110Sstevel@tonic-gate 	DIR *dirf;
15120Sstevel@tonic-gate 	int j;
15130Sstevel@tonic-gate 	struct lbuf *ep;
15140Sstevel@tonic-gate 	int width;
15150Sstevel@tonic-gate 
15160Sstevel@tonic-gate 	if ((dirf = opendir(dir)) == NULL) {
15170Sstevel@tonic-gate 		(void) fflush(stdout);
15180Sstevel@tonic-gate 		perror(dir);
15190Sstevel@tonic-gate 		err = 2;
15200Sstevel@tonic-gate 		return;
15210Sstevel@tonic-gate 	} else {
15220Sstevel@tonic-gate 		tblocks = 0;
15230Sstevel@tonic-gate 		for (;;) {
15240Sstevel@tonic-gate 			errno = 0;
15250Sstevel@tonic-gate 			if ((dentry = readdir(dirf)) == NULL)
15260Sstevel@tonic-gate 				break;
15270Sstevel@tonic-gate 			if (aflg == 0 && dentry->d_name[0] == '.' &&
15280Sstevel@tonic-gate 			    (Aflg == 0 ||
15290Sstevel@tonic-gate 			    dentry->d_name[1] == '\0' ||
15300Sstevel@tonic-gate 			    dentry->d_name[1] == '.' &&
15310Sstevel@tonic-gate 			    dentry->d_name[2] == '\0'))
15320Sstevel@tonic-gate 				/*
15330Sstevel@tonic-gate 				 * check for directory items '.', '..',
15340Sstevel@tonic-gate 				 *  and items without valid inode-number;
15350Sstevel@tonic-gate 				 */
15360Sstevel@tonic-gate 				continue;
15370Sstevel@tonic-gate 
1538*9664Sjason@ansipunx.net 			/* skip entries ending in ~ if -B was given */
1539*9664Sjason@ansipunx.net 			if (Bflg &&
1540*9664Sjason@ansipunx.net 			    dentry->d_name[strlen(dentry->d_name) - 1] == '~')
1541*9664Sjason@ansipunx.net 				continue;
15420Sstevel@tonic-gate 			if (Cflg || mflg) {
15430Sstevel@tonic-gate 				width = strcol((unsigned char *)dentry->d_name);
15440Sstevel@tonic-gate 				if (width > filewidth)
15450Sstevel@tonic-gate 					filewidth = width;
15460Sstevel@tonic-gate 			}
15470Sstevel@tonic-gate 			ep = gstat(makename(dir, dentry->d_name), 0, myinfo);
15480Sstevel@tonic-gate 			if (ep == NULL) {
15490Sstevel@tonic-gate 				if (nomocore)
15505331Samw 					exit(2);
15510Sstevel@tonic-gate 				continue;
15520Sstevel@tonic-gate 			} else {
15530Sstevel@tonic-gate 				ep->lnum = dentry->d_ino;
15540Sstevel@tonic-gate 				for (j = 0; dentry->d_name[j] != '\0'; j++)
15550Sstevel@tonic-gate 					ep->ln.lname[j] = dentry->d_name[j];
15560Sstevel@tonic-gate 				ep->ln.lname[j] = '\0';
15570Sstevel@tonic-gate 			}
15580Sstevel@tonic-gate 		}
15590Sstevel@tonic-gate 		if (errno) {
15600Sstevel@tonic-gate 			int sav_errno = errno;
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate 			(void) fprintf(stderr,
15630Sstevel@tonic-gate 			    gettext("ls: error reading directory %s: %s\n"),
15640Sstevel@tonic-gate 			    dir, strerror(sav_errno));
15650Sstevel@tonic-gate 		}
15660Sstevel@tonic-gate 		(void) closedir(dirf);
15670Sstevel@tonic-gate 		colwidth = fixedwidth + filewidth;
15680Sstevel@tonic-gate 	}
15690Sstevel@tonic-gate }
15700Sstevel@tonic-gate 
15710Sstevel@tonic-gate /*
15720Sstevel@tonic-gate  * Attaching a link to an inode's ancestors.  Search
15730Sstevel@tonic-gate  * through the ancestors to check for cycles (an inode which
15740Sstevel@tonic-gate  * we have already tracked in this inodes ancestry).  If a cycle
15750Sstevel@tonic-gate  * is detected, set the exit code and record the fact so that
15760Sstevel@tonic-gate  * it is reported at the right time when printing the directory.
15770Sstevel@tonic-gate  * In addition, set the exit code.  Note:  If the -a flag was
15780Sstevel@tonic-gate  * specified, we don't want to check for cycles for directories
15790Sstevel@tonic-gate  * ending in '/.' or '/..' unless they were specified on the
15800Sstevel@tonic-gate  * command line.
15810Sstevel@tonic-gate  */
15820Sstevel@tonic-gate static void
15830Sstevel@tonic-gate record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep,
15840Sstevel@tonic-gate     int argfl, struct ditem *myparent)
15850Sstevel@tonic-gate {
15860Sstevel@tonic-gate 	size_t		file_len;
15870Sstevel@tonic-gate 	struct ditem	*myinfo;
15880Sstevel@tonic-gate 	struct ditem	*tptr;
15890Sstevel@tonic-gate 
15900Sstevel@tonic-gate 	file_len = strlen(file);
15910Sstevel@tonic-gate 	if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) &&
15920Sstevel@tonic-gate 	    NOTPARENTDIR(file, file_len))) {
15930Sstevel@tonic-gate 		/*
15940Sstevel@tonic-gate 		 * Add this inode's ancestry
15950Sstevel@tonic-gate 		 * info and insert it into the
15960Sstevel@tonic-gate 		 * ancestry list by pointing
15970Sstevel@tonic-gate 		 * back to its parent.  We save
15980Sstevel@tonic-gate 		 * it (in rep) with the other info
15990Sstevel@tonic-gate 		 * we're gathering for this inode.
16000Sstevel@tonic-gate 		 */
16010Sstevel@tonic-gate 		if ((myinfo = malloc(
16020Sstevel@tonic-gate 		    sizeof (struct ditem))) == NULL) {
16030Sstevel@tonic-gate 			perror("ls");
16040Sstevel@tonic-gate 			exit(2);
16050Sstevel@tonic-gate 		}
16060Sstevel@tonic-gate 		myinfo->dev = pstatb->st_dev;
16070Sstevel@tonic-gate 		myinfo->ino = pstatb->st_ino;
16080Sstevel@tonic-gate 		myinfo->parent = myparent;
16090Sstevel@tonic-gate 		rep->ancinfo = myinfo;
16100Sstevel@tonic-gate 
16110Sstevel@tonic-gate 		/*
16120Sstevel@tonic-gate 		 * If this node has the same device id and
16130Sstevel@tonic-gate 		 * inode number of one of its ancestors,
16140Sstevel@tonic-gate 		 * then we've detected a cycle.
16150Sstevel@tonic-gate 		 */
16160Sstevel@tonic-gate 		if (myparent != NULL) {
16170Sstevel@tonic-gate 			for (tptr = myparent; tptr->parent != NULL;
16180Sstevel@tonic-gate 			    tptr = tptr->parent) {
16190Sstevel@tonic-gate 				if ((tptr->dev == pstatb->st_dev) &&
16200Sstevel@tonic-gate 				    (tptr->ino == pstatb->st_ino)) {
16210Sstevel@tonic-gate 					/*
16220Sstevel@tonic-gate 					 * Cycle detected for this
16230Sstevel@tonic-gate 					 * directory.  Record the fact
16240Sstevel@tonic-gate 					 * it is a cycle so we don't
16250Sstevel@tonic-gate 					 * try to process this
16260Sstevel@tonic-gate 					 * directory as we are
16270Sstevel@tonic-gate 					 * walking through the
16280Sstevel@tonic-gate 					 * list of directories.
16290Sstevel@tonic-gate 					 */
16300Sstevel@tonic-gate 					rep->cycle = 1;
16310Sstevel@tonic-gate 					err = 2;
16320Sstevel@tonic-gate 					break;
16330Sstevel@tonic-gate 				}
16340Sstevel@tonic-gate 			}
16350Sstevel@tonic-gate 		}
16360Sstevel@tonic-gate 	}
16370Sstevel@tonic-gate }
16380Sstevel@tonic-gate 
16390Sstevel@tonic-gate /*
16406178Sny155746  * Do re-calculate the mode for group for ACE_T type of acls.
16416178Sny155746  * This is because, if the server's FS happens to be UFS, supporting
16426178Sny155746  * POSIX ACL's, then it does a special calculation of group mode
16436178Sny155746  * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.)
16446178Sny155746  *
16456178Sny155746  * This algorithm is from the NFSv4 ACL Draft. Here a part of that
16466178Sny155746  * algorithm is used for the group mode calculation only.
16476178Sny155746  * What is modified here from the algorithm is that only the
16486178Sny155746  * entries with flags ACE_GROUP are considered. For each entry
16496178Sny155746  * with ACE_GROUP flag, the first occurance of a specific access
16506178Sny155746  * is checked if it is allowed.
16516373Sny155746  * We are not interested in perms for user and other, as they
16526178Sny155746  * were taken from st_mode value.
16536178Sny155746  * We are not interested in a_who field of ACE, as we need just
16546178Sny155746  * unix mode bits for the group.
16556178Sny155746  */
16566373Sny155746 
16576373Sny155746 #define	OWNED_GROUP	(ACE_GROUP | ACE_IDENTIFIER_GROUP)
16586373Sny155746 #define	IS_TYPE_ALLOWED(type)	((type) == ACE_ACCESS_ALLOWED_ACE_TYPE)
16596373Sny155746 
16606178Sny155746 int
16616178Sny155746 grp_mask_to_mode(acl_t *acep)
16626178Sny155746 {
16636178Sny155746 	int mode = 0, seen = 0;
16646178Sny155746 	int acecnt;
16656373Sny155746 	int flags;
16666178Sny155746 	ace_t *ap;
16676178Sny155746 
16686178Sny155746 	acecnt = acl_cnt(acep);
16696178Sny155746 	for (ap = (ace_t *)acl_data(acep); acecnt--; ap++) {
16706373Sny155746 
16716373Sny155746 		if (ap->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE &&
16726373Sny155746 		    ap->a_type != ACE_ACCESS_DENIED_ACE_TYPE)
16736373Sny155746 			continue;
16746373Sny155746 
16756373Sny155746 		if (ap->a_flags & ACE_INHERIT_ONLY_ACE)
16766373Sny155746 			continue;
16776373Sny155746 
16786373Sny155746 		/*
16796373Sny155746 		 * if it is first group@ or first everyone@
16806373Sny155746 		 * for each of read, write and execute, then
16816373Sny155746 		 * that will be the group mode bit.
16826373Sny155746 		 */
16836373Sny155746 		flags = ap->a_flags & ACE_TYPE_FLAGS;
16846373Sny155746 		if (flags == OWNED_GROUP || flags == ACE_EVERYONE) {
16856373Sny155746 			if (ap->a_access_mask & ACE_READ_DATA) {
16866373Sny155746 				if (!(seen & S_IRGRP)) {
16876373Sny155746 					seen |= S_IRGRP;
16886373Sny155746 					if (IS_TYPE_ALLOWED(ap->a_type))
16896373Sny155746 						mode |= S_IRGRP;
16906178Sny155746 				}
16916373Sny155746 			}
16926373Sny155746 			if (ap->a_access_mask & ACE_WRITE_DATA) {
16936373Sny155746 				if (!(seen & S_IWGRP)) {
16946373Sny155746 					seen |= S_IWGRP;
16956373Sny155746 					if (IS_TYPE_ALLOWED(ap->a_type))
16966373Sny155746 						mode |= S_IWGRP;
16976178Sny155746 				}
16986373Sny155746 			}
16996373Sny155746 			if (ap->a_access_mask & ACE_EXECUTE) {
17006373Sny155746 				if (!(seen & S_IXGRP)) {
17016373Sny155746 					seen |= S_IXGRP;
17026373Sny155746 					if (IS_TYPE_ALLOWED(ap->a_type))
17036373Sny155746 						mode |= S_IXGRP;
17046178Sny155746 				}
17056178Sny155746 			}
17066178Sny155746 		}
17076178Sny155746 	}
17086178Sny155746 	return (mode);
17096178Sny155746 }
17106178Sny155746 
17116178Sny155746 /*
17120Sstevel@tonic-gate  * get status of file and recomputes tblocks;
17130Sstevel@tonic-gate  * argfl = 1 if file is a name in ls-command and = 0
17140Sstevel@tonic-gate  * for filename in a directory whose name is an
17150Sstevel@tonic-gate  * argument in the command;
17160Sstevel@tonic-gate  * stores a pointer in flist[nfiles] and
17170Sstevel@tonic-gate  * returns that pointer;
17180Sstevel@tonic-gate  * returns NULL if failed;
17190Sstevel@tonic-gate  */
17200Sstevel@tonic-gate static struct lbuf *
17210Sstevel@tonic-gate gstat(char *file, int argfl, struct ditem *myparent)
17220Sstevel@tonic-gate {
17230Sstevel@tonic-gate 	struct stat statb, statb1;
17240Sstevel@tonic-gate 	struct lbuf *rep;
17250Sstevel@tonic-gate 	char buf[BUFSIZ];
17260Sstevel@tonic-gate 	ssize_t cc;
17270Sstevel@tonic-gate 	int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat;
17280Sstevel@tonic-gate 	int aclcnt;
1729789Sahrens 	int error;
17300Sstevel@tonic-gate 	aclent_t *tp;
17310Sstevel@tonic-gate 	o_mode_t groupperm, mask;
17320Sstevel@tonic-gate 	int grouppermfound, maskfound;
17330Sstevel@tonic-gate 
17340Sstevel@tonic-gate 	if (nomocore)
17350Sstevel@tonic-gate 		return (NULL);
17360Sstevel@tonic-gate 
17370Sstevel@tonic-gate 	if (nfiles >= maxfils) {
17380Sstevel@tonic-gate 		/*
17390Sstevel@tonic-gate 		 * all flist/lbuf pair assigned files, time to get some
17400Sstevel@tonic-gate 		 * more space
17410Sstevel@tonic-gate 		 */
17420Sstevel@tonic-gate 		maxfils += quantn;
17430Sstevel@tonic-gate 		if (((flist = realloc(flist,
17440Sstevel@tonic-gate 		    maxfils * sizeof (struct lbuf *))) == NULL) ||
17450Sstevel@tonic-gate 		    ((nxtlbf = malloc(quantn *
17460Sstevel@tonic-gate 		    sizeof (struct lbuf))) == NULL)) {
17470Sstevel@tonic-gate 			perror("ls");
17480Sstevel@tonic-gate 			nomocore = 1;
17490Sstevel@tonic-gate 			return (NULL);
17500Sstevel@tonic-gate 		}
17510Sstevel@tonic-gate 	}
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate 	/*
17540Sstevel@tonic-gate 	 * nfiles is reset to nargs for each directory
17550Sstevel@tonic-gate 	 * that is given as an argument maxn is checked
17560Sstevel@tonic-gate 	 * to prevent the assignment of an lbuf to a flist entry
17570Sstevel@tonic-gate 	 * that already has one assigned.
17580Sstevel@tonic-gate 	 */
17590Sstevel@tonic-gate 	if (nfiles >= maxn) {
17600Sstevel@tonic-gate 		rep = nxtlbf++;
17610Sstevel@tonic-gate 		flist[nfiles++] = rep;
17620Sstevel@tonic-gate 		maxn = nfiles;
17630Sstevel@tonic-gate 	} else {
17640Sstevel@tonic-gate 		rep = flist[nfiles++];
17650Sstevel@tonic-gate 	}
17666792Sbasabi 
17676792Sbasabi 	/* Initialize */
17686792Sbasabi 
17690Sstevel@tonic-gate 	rep->lflags = (mode_t)0;
17700Sstevel@tonic-gate 	rep->flinkto = NULL;
17710Sstevel@tonic-gate 	rep->cycle = 0;
17726792Sbasabi 	rep->lat.tv_sec = time(NULL);
17736792Sbasabi 	rep->lat.tv_nsec = 0;
17746792Sbasabi 	rep->lct.tv_sec = time(NULL);
17756792Sbasabi 	rep->lct.tv_nsec = 0;
17766792Sbasabi 	rep->lmt.tv_sec = time(NULL);
17776792Sbasabi 	rep->lmt.tv_nsec = 0;
17786792Sbasabi 	rep->exttr = NULL;
17796792Sbasabi 	rep->extm = NULL;
17806792Sbasabi 
17810Sstevel@tonic-gate 	if (argfl || statreq) {
17820Sstevel@tonic-gate 		int doacl;
17830Sstevel@tonic-gate 
17840Sstevel@tonic-gate 		if (lflg)
17850Sstevel@tonic-gate 			doacl = 1;
17860Sstevel@tonic-gate 		else
17870Sstevel@tonic-gate 			doacl = 0;
17886792Sbasabi 
17890Sstevel@tonic-gate 		if ((*statf)(file, &statb) < 0) {
17900Sstevel@tonic-gate 			if (argfl || errno != ENOENT ||
17910Sstevel@tonic-gate 			    (Lflg && lstat(file, &statb) == 0)) {
17920Sstevel@tonic-gate 				/*
17930Sstevel@tonic-gate 				 * Avoid race between readdir and lstat.
17940Sstevel@tonic-gate 				 * Print error message in case of dangling link.
17950Sstevel@tonic-gate 				 */
17960Sstevel@tonic-gate 				perror(file);
1797*9664Sjason@ansipunx.net 				err = 2;
17980Sstevel@tonic-gate 			}
17990Sstevel@tonic-gate 			nfiles--;
18000Sstevel@tonic-gate 			return (NULL);
18010Sstevel@tonic-gate 		}
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate 		/*
18040Sstevel@tonic-gate 		 * If -H was specified, and the file linked to was
18050Sstevel@tonic-gate 		 * not a directory, then we need to get the info
18060Sstevel@tonic-gate 		 * for the symlink itself.
18070Sstevel@tonic-gate 		 */
18080Sstevel@tonic-gate 		if ((Hflg) && (argfl) &&
18090Sstevel@tonic-gate 		    ((statb.st_mode & S_IFMT) != S_IFDIR)) {
18100Sstevel@tonic-gate 			if (lstat(file, &statb) < 0) {
18110Sstevel@tonic-gate 				perror(file);
1812*9664Sjason@ansipunx.net 				err = 2;
18130Sstevel@tonic-gate 			}
18140Sstevel@tonic-gate 		}
18150Sstevel@tonic-gate 
18160Sstevel@tonic-gate 		rep->lnum = statb.st_ino;
18170Sstevel@tonic-gate 		rep->lsize = statb.st_size;
18180Sstevel@tonic-gate 		rep->lblocks = statb.st_blocks;
18190Sstevel@tonic-gate 		switch (statb.st_mode & S_IFMT) {
18200Sstevel@tonic-gate 		case S_IFDIR:
18210Sstevel@tonic-gate 			rep->ltype = 'd';
18220Sstevel@tonic-gate 			if (Rflg) {
18230Sstevel@tonic-gate 				record_ancestry(file, &statb, rep,
18240Sstevel@tonic-gate 				    argfl, myparent);
18250Sstevel@tonic-gate 			}
18260Sstevel@tonic-gate 			break;
18270Sstevel@tonic-gate 		case S_IFBLK:
18280Sstevel@tonic-gate 			rep->ltype = 'b';
18290Sstevel@tonic-gate 			rep->lsize = (off_t)statb.st_rdev;
18300Sstevel@tonic-gate 			break;
18310Sstevel@tonic-gate 		case S_IFCHR:
18320Sstevel@tonic-gate 			rep->ltype = 'c';
18330Sstevel@tonic-gate 			rep->lsize = (off_t)statb.st_rdev;
18340Sstevel@tonic-gate 			break;
18350Sstevel@tonic-gate 		case S_IFIFO:
18360Sstevel@tonic-gate 			rep->ltype = 'p';
18370Sstevel@tonic-gate 			break;
18380Sstevel@tonic-gate 		case S_IFSOCK:
18390Sstevel@tonic-gate 			rep->ltype = 's';
18400Sstevel@tonic-gate 			rep->lsize = 0;
18410Sstevel@tonic-gate 			break;
18420Sstevel@tonic-gate 		case S_IFLNK:
18430Sstevel@tonic-gate 			/* symbolic links may not have ACLs, so elide acl() */
18440Sstevel@tonic-gate 			if ((Lflg == 0) || (Hflg == 0) ||
18450Sstevel@tonic-gate 			    ((Hflg) && (!argfl))) {
18460Sstevel@tonic-gate 				doacl = 0;
18470Sstevel@tonic-gate 			}
18480Sstevel@tonic-gate 			rep->ltype = 'l';
18490Sstevel@tonic-gate 			if (lflg) {
18500Sstevel@tonic-gate 				cc = readlink(file, buf, BUFSIZ);
18510Sstevel@tonic-gate 				if (cc >= 0) {
18520Sstevel@tonic-gate 
18530Sstevel@tonic-gate 					/*
18540Sstevel@tonic-gate 					 * follow the symbolic link
18550Sstevel@tonic-gate 					 * to generate the appropriate
18560Sstevel@tonic-gate 					 * Fflg marker for the object
18570Sstevel@tonic-gate 					 * eg, /bin -> /sym/bin/
18580Sstevel@tonic-gate 					 */
18590Sstevel@tonic-gate 					if ((Fflg || pflg) &&
18600Sstevel@tonic-gate 					    (stat(file, &statb1) >= 0)) {
18610Sstevel@tonic-gate 						switch (statb1.st_mode &
18620Sstevel@tonic-gate 						    S_IFMT) {
18630Sstevel@tonic-gate 						case S_IFDIR:
18640Sstevel@tonic-gate 							buf[cc++] = '/';
18650Sstevel@tonic-gate 							break;
18660Sstevel@tonic-gate 						case S_IFSOCK:
18670Sstevel@tonic-gate 							buf[cc++] = '=';
18680Sstevel@tonic-gate 							break;
18691447Sakaplan 						case S_IFDOOR:
18701447Sakaplan 							buf[cc++] = '>';
18711447Sakaplan 							break;
18721447Sakaplan 						case S_IFIFO:
18731447Sakaplan 							buf[cc++] = '|';
18741447Sakaplan 							break;
18750Sstevel@tonic-gate 						default:
18760Sstevel@tonic-gate 							if ((statb1.st_mode &
18770Sstevel@tonic-gate 							    ~S_IFMT) &
18780Sstevel@tonic-gate 							    (S_IXUSR|S_IXGRP|
18790Sstevel@tonic-gate 							    S_IXOTH))
18800Sstevel@tonic-gate 								buf[cc++] = '*';
18810Sstevel@tonic-gate 							break;
18820Sstevel@tonic-gate 						}
18830Sstevel@tonic-gate 					}
18840Sstevel@tonic-gate 					buf[cc] = '\0';
18850Sstevel@tonic-gate 					rep->flinkto = strdup(buf);
18860Sstevel@tonic-gate 				}
18870Sstevel@tonic-gate 				break;
18880Sstevel@tonic-gate 			}
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate 			/*
18910Sstevel@tonic-gate 			 * ls /sym behaves differently from ls /sym/
18920Sstevel@tonic-gate 			 * when /sym is a symbolic link. This is fixed
18930Sstevel@tonic-gate 			 * when explicit arguments are specified.
18940Sstevel@tonic-gate 			 */
18950Sstevel@tonic-gate 
18960Sstevel@tonic-gate #ifdef XPG6
18970Sstevel@tonic-gate 			/* Do not follow a symlink when -F is specified */
18980Sstevel@tonic-gate 			if ((!argfl) || (argfl && Fflg) ||
18990Sstevel@tonic-gate 			    (stat(file, &statb1) < 0))
19000Sstevel@tonic-gate #else
19010Sstevel@tonic-gate 			/* Follow a symlink when -F is specified */
19020Sstevel@tonic-gate 			if (!argfl || stat(file, &statb1) < 0)
19030Sstevel@tonic-gate #endif /* XPG6 */
19040Sstevel@tonic-gate 				break;
19050Sstevel@tonic-gate 			if ((statb1.st_mode & S_IFMT) == S_IFDIR) {
19060Sstevel@tonic-gate 				statb = statb1;
19070Sstevel@tonic-gate 				rep->ltype = 'd';
19080Sstevel@tonic-gate 				rep->lsize = statb1.st_size;
19090Sstevel@tonic-gate 				if (Rflg) {
19100Sstevel@tonic-gate 					record_ancestry(file, &statb, rep,
19110Sstevel@tonic-gate 					    argfl, myparent);
19120Sstevel@tonic-gate 				}
19130Sstevel@tonic-gate 			}
19140Sstevel@tonic-gate 			break;
19150Sstevel@tonic-gate 		case S_IFDOOR:
19160Sstevel@tonic-gate 			rep->ltype = 'D';
19170Sstevel@tonic-gate 			break;
19180Sstevel@tonic-gate 		case S_IFREG:
19190Sstevel@tonic-gate 			rep->ltype = '-';
19200Sstevel@tonic-gate 			break;
19210Sstevel@tonic-gate 		case S_IFPORT:
19220Sstevel@tonic-gate 			rep->ltype = 'P';
19230Sstevel@tonic-gate 			break;
19240Sstevel@tonic-gate 		default:
19250Sstevel@tonic-gate 			rep->ltype = '?';
19260Sstevel@tonic-gate 			break;
19270Sstevel@tonic-gate 		}
19280Sstevel@tonic-gate 		rep->lflags = statb.st_mode & ~S_IFMT;
19290Sstevel@tonic-gate 
19300Sstevel@tonic-gate 		if (!S_ISREG(statb.st_mode))
19310Sstevel@tonic-gate 			rep->lflags |= LS_NOTREG;
19320Sstevel@tonic-gate 
19336792Sbasabi 		rep->luid = statb.st_uid;
19346792Sbasabi 		rep->lgid = statb.st_gid;
19356792Sbasabi 		rep->lnl = statb.st_nlink;
19366792Sbasabi 		if (uflg || (tmflg && atm))
19376792Sbasabi 			rep->lmtime = statb.st_atim;
19386792Sbasabi 		else if (cflg || (tmflg && ctm))
19396792Sbasabi 			rep->lmtime = statb.st_ctim;
19406792Sbasabi 		else
19416792Sbasabi 			rep->lmtime = statb.st_mtim;
19426792Sbasabi 		rep->lat = statb.st_atim;
19436792Sbasabi 		rep->lct = statb.st_ctim;
19446792Sbasabi 		rep->lmt = statb.st_mtim;
19456792Sbasabi 
19460Sstevel@tonic-gate 		/* ACL: check acl entries count */
19470Sstevel@tonic-gate 		if (doacl) {
19480Sstevel@tonic-gate 
1949789Sahrens 			error = acl_get(file, 0, &rep->aclp);
1950789Sahrens 			if (error) {
1951789Sahrens 				(void) fprintf(stderr,
1952789Sahrens 				    gettext("ls: can't read ACL on %s: %s\n"),
1953789Sahrens 				    file, acl_strerror(error));
19546792Sbasabi 				rep->acl = ' ';
19556866Sbasabi 				acl_err++;
19566792Sbasabi 				return (rep);
1957789Sahrens 			}
19580Sstevel@tonic-gate 
1959789Sahrens 			rep->acl = ' ';
19600Sstevel@tonic-gate 
1961789Sahrens 			if (rep->aclp &&
1962789Sahrens 			    ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) {
1963789Sahrens 				rep->acl = '+';
19640Sstevel@tonic-gate 				/*
1965789Sahrens 				 * Special handling for ufs aka aclent_t ACL's
19660Sstevel@tonic-gate 				 */
19676178Sny155746 				if (acl_type(rep->aclp) == ACLENT_T) {
1968789Sahrens 					/*
1969789Sahrens 					 * For files with non-trivial acls, the
1970789Sahrens 					 * effective group permissions are the
1971789Sahrens 					 * intersection of the GROUP_OBJ value
1972789Sahrens 					 * and the CLASS_OBJ (acl mask) value.
1973789Sahrens 					 * Determine both the GROUP_OBJ and
1974789Sahrens 					 * CLASS_OBJ for this file and insert
1975789Sahrens 					 * the logical AND of those two values
1976789Sahrens 					 * in the group permissions field
1977789Sahrens 					 * of the lflags value for this file.
1978789Sahrens 					 */
1979789Sahrens 
1980789Sahrens 					/*
1981789Sahrens 					 * Until found in acl list, assume
1982789Sahrens 					 * maximum permissions for both group
1983789Sahrens 					 * a nd mask.  (Just in case the acl
1984789Sahrens 					 * lacks either value for some reason.)
1985789Sahrens 					 */
1986789Sahrens 					groupperm = 07;
1987789Sahrens 					mask = 07;
1988789Sahrens 					grouppermfound = 0;
1989789Sahrens 					maskfound = 0;
1990789Sahrens 					aclcnt = acl_cnt(rep->aclp);
1991789Sahrens 					for (tp =
1992789Sahrens 					    (aclent_t *)acl_data(rep->aclp);
1993789Sahrens 					    aclcnt--; tp++) {
1994789Sahrens 						if (tp->a_type == GROUP_OBJ) {
1995789Sahrens 							groupperm = tp->a_perm;
1996789Sahrens 							grouppermfound = 1;
1997789Sahrens 							continue;
1998789Sahrens 						}
1999789Sahrens 						if (tp->a_type == CLASS_OBJ) {
2000789Sahrens 							mask = tp->a_perm;
2001789Sahrens 							maskfound = 1;
2002789Sahrens 						}
2003789Sahrens 						if (grouppermfound && maskfound)
2004789Sahrens 							break;
20050Sstevel@tonic-gate 					}
2006789Sahrens 
20070Sstevel@tonic-gate 
2008789Sahrens 					/* reset all the group bits */
2009789Sahrens 					rep->lflags &= ~S_IRWXG;
20100Sstevel@tonic-gate 
2011789Sahrens 					/*
2012789Sahrens 					 * Now set them to the logical AND of
2013789Sahrens 					 * the GROUP_OBJ permissions and the
2014789Sahrens 					 * acl mask.
2015789Sahrens 					 */
20160Sstevel@tonic-gate 
2017789Sahrens 					rep->lflags |= (groupperm & mask) << 3;
20180Sstevel@tonic-gate 
20196178Sny155746 				} else if (acl_type(rep->aclp) == ACE_T) {
20206178Sny155746 					int mode;
20216178Sny155746 					mode = grp_mask_to_mode(rep->aclp);
20226178Sny155746 					rep->lflags &= ~S_IRWXG;
20236178Sny155746 					rep->lflags |= mode;
2024789Sahrens 				}
20250Sstevel@tonic-gate 			}
20260Sstevel@tonic-gate 
20271420Smarks 			if (!vflg && !Vflg && rep->aclp) {
20281420Smarks 				acl_free(rep->aclp);
20291420Smarks 				rep->aclp = NULL;
20301420Smarks 			}
20311420Smarks 
20320Sstevel@tonic-gate 			if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1)
20330Sstevel@tonic-gate 				rep->acl = '@';
20345331Samw 
20350Sstevel@tonic-gate 		} else
20360Sstevel@tonic-gate 			rep->acl = ' ';
20370Sstevel@tonic-gate 
20380Sstevel@tonic-gate 		/* mask ISARG and other file-type bits */
20390Sstevel@tonic-gate 
20400Sstevel@tonic-gate 		if (rep->ltype != 'b' && rep->ltype != 'c')
20410Sstevel@tonic-gate 			tblocks += rep->lblocks;
20425331Samw 
20435331Samw 		/* Get extended system attributes */
20445331Samw 
20455331Samw 		if ((saflg || (tmflg && crtm) || (tmflg && alltm)) &&
20465331Samw 		    (sysattr_support(file, _PC_SATTR_EXISTS) == 1)) {
20475331Samw 			int i;
20485331Samw 
20495331Samw 			sacnt = attr_count();
20505331Samw 			/*
20515331Samw 			 * Allocate 'sacnt' size array to hold extended
20525331Samw 			 * system attribute name (verbose) or respective
20535331Samw 			 * symbol represenation (compact).
20545331Samw 			 */
20555331Samw 			rep->exttr = xmalloc(sacnt * sizeof (struct attrb),
20565331Samw 			    rep);
20575331Samw 
20585331Samw 			/* initialize boolean attribute list */
20595331Samw 			for (i = 0; i < sacnt; i++)
20605331Samw 				rep->exttr[i].name = NULL;
20615331Samw 			if (get_sysxattr(file, rep) != 0) {
20625331Samw 				(void) fprintf(stderr,
20635331Samw 				    gettext("ls:Failed to retrieve "
20645331Samw 				    "extended system attribute from "
20655331Samw 				    "%s\n"), file);
20665331Samw 				rep->exttr[0].name = xmalloc(2, rep);
20675331Samw 				(void) strlcpy(rep->exttr[0].name, "?", 2);
20685331Samw 			}
20695331Samw 		}
20700Sstevel@tonic-gate 	}
20710Sstevel@tonic-gate 	return (rep);
20720Sstevel@tonic-gate }
20730Sstevel@tonic-gate 
20740Sstevel@tonic-gate /*
20750Sstevel@tonic-gate  * returns pathname of the form dir/file;
20760Sstevel@tonic-gate  * dir and file are null-terminated strings.
20770Sstevel@tonic-gate  */
20780Sstevel@tonic-gate static char *
20790Sstevel@tonic-gate makename(char *dir, char *file)
20800Sstevel@tonic-gate {
20810Sstevel@tonic-gate 	/*
20820Sstevel@tonic-gate 	 * PATH_MAX is the maximum length of a path name.
20830Sstevel@tonic-gate 	 * MAXNAMLEN is the maximum length of any path name component.
20840Sstevel@tonic-gate 	 * Allocate space for both, plus the '/' in the middle
20850Sstevel@tonic-gate 	 * and the null character at the end.
20860Sstevel@tonic-gate 	 * dfile is static as this is returned by makename().
20870Sstevel@tonic-gate 	 */
20880Sstevel@tonic-gate 	static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1];
20890Sstevel@tonic-gate 	char *dp, *fp;
20900Sstevel@tonic-gate 
20910Sstevel@tonic-gate 	dp = dfile;
20920Sstevel@tonic-gate 	fp = dir;
20930Sstevel@tonic-gate 	while (*fp)
20940Sstevel@tonic-gate 		*dp++ = *fp++;
20950Sstevel@tonic-gate 	if (dp > dfile && *(dp - 1) != '/')
20960Sstevel@tonic-gate 		*dp++ = '/';
20970Sstevel@tonic-gate 	fp = file;
20980Sstevel@tonic-gate 	while (*fp)
20990Sstevel@tonic-gate 		*dp++ = *fp++;
21000Sstevel@tonic-gate 	*dp = '\0';
21010Sstevel@tonic-gate 	return (dfile);
21020Sstevel@tonic-gate }
21030Sstevel@tonic-gate 
21040Sstevel@tonic-gate 
21050Sstevel@tonic-gate #include <pwd.h>
21060Sstevel@tonic-gate #include <grp.h>
21070Sstevel@tonic-gate #include <utmpx.h>
21080Sstevel@tonic-gate 
21090Sstevel@tonic-gate struct	utmpx utmp;
21100Sstevel@tonic-gate 
21110Sstevel@tonic-gate #define	NMAX	(sizeof (utmp.ut_name))
21120Sstevel@tonic-gate #define	SCPYN(a, b)	(void) strncpy(a, b, NMAX)
21130Sstevel@tonic-gate 
21140Sstevel@tonic-gate 
21150Sstevel@tonic-gate struct cachenode {		/* this struct must be zeroed before using */
21160Sstevel@tonic-gate 	struct cachenode *lesschild;	/* subtree whose entries < val */
21170Sstevel@tonic-gate 	struct cachenode *grtrchild;	/* subtree whose entries > val */
21180Sstevel@tonic-gate 	long val;			/* the uid or gid of this entry */
21190Sstevel@tonic-gate 	int initted;			/* name has been filled in */
21200Sstevel@tonic-gate 	char name[NMAX+1];		/* the string that val maps to */
21210Sstevel@tonic-gate };
21220Sstevel@tonic-gate static struct cachenode *names, *groups;
21230Sstevel@tonic-gate 
21240Sstevel@tonic-gate static struct cachenode *
21250Sstevel@tonic-gate findincache(struct cachenode **head, long val)
21260Sstevel@tonic-gate {
21270Sstevel@tonic-gate 	struct cachenode **parent = head;
21280Sstevel@tonic-gate 	struct cachenode *c = *parent;
21290Sstevel@tonic-gate 
21300Sstevel@tonic-gate 	while (c != NULL) {
21310Sstevel@tonic-gate 		if (val == c->val) {
21320Sstevel@tonic-gate 			/* found it */
21330Sstevel@tonic-gate 			return (c);
21340Sstevel@tonic-gate 		} else if (val < c->val) {
21350Sstevel@tonic-gate 			parent = &c->lesschild;
21360Sstevel@tonic-gate 			c = c->lesschild;
21370Sstevel@tonic-gate 		} else {
21380Sstevel@tonic-gate 			parent = &c->grtrchild;
21390Sstevel@tonic-gate 			c = c->grtrchild;
21400Sstevel@tonic-gate 		}
21410Sstevel@tonic-gate 	}
21420Sstevel@tonic-gate 
21430Sstevel@tonic-gate 	/* not in the cache, make a new entry for it */
21440Sstevel@tonic-gate 	c = calloc(1, sizeof (struct cachenode));
21450Sstevel@tonic-gate 	if (c == NULL) {
21460Sstevel@tonic-gate 		perror("ls");
21470Sstevel@tonic-gate 		exit(2);
21480Sstevel@tonic-gate 	}
21490Sstevel@tonic-gate 	*parent = c;
21500Sstevel@tonic-gate 	c->val = val;
21510Sstevel@tonic-gate 	return (c);
21520Sstevel@tonic-gate }
21530Sstevel@tonic-gate 
21540Sstevel@tonic-gate /*
21550Sstevel@tonic-gate  * get name from cache, or passwd file for a given uid;
21560Sstevel@tonic-gate  * lastuid is set to uid.
21570Sstevel@tonic-gate  */
21580Sstevel@tonic-gate static char *
21590Sstevel@tonic-gate getname(uid_t uid)
21600Sstevel@tonic-gate {
21610Sstevel@tonic-gate 	struct passwd *pwent;
21620Sstevel@tonic-gate 	struct cachenode *c;
21630Sstevel@tonic-gate 
21640Sstevel@tonic-gate 	if ((uid == lastuid) && lastuname)
21650Sstevel@tonic-gate 		return (lastuname);
21660Sstevel@tonic-gate 
21670Sstevel@tonic-gate 	c = findincache(&names, uid);
21680Sstevel@tonic-gate 	if (c->initted == 0) {
21690Sstevel@tonic-gate 		if ((pwent = getpwuid(uid)) != NULL) {
21700Sstevel@tonic-gate 			SCPYN(&c->name[0], pwent->pw_name);
21710Sstevel@tonic-gate 		} else {
21720Sstevel@tonic-gate 			(void) sprintf(&c->name[0], "%-8u", (int)uid);
21730Sstevel@tonic-gate 		}
21740Sstevel@tonic-gate 		c->initted = 1;
21750Sstevel@tonic-gate 	}
21760Sstevel@tonic-gate 	lastuid = uid;
21770Sstevel@tonic-gate 	lastuname = &c->name[0];
21780Sstevel@tonic-gate 	return (lastuname);
21790Sstevel@tonic-gate }
21800Sstevel@tonic-gate 
21810Sstevel@tonic-gate /*
21820Sstevel@tonic-gate  * get name from cache, or group file for a given gid;
21830Sstevel@tonic-gate  * lastgid is set to gid.
21840Sstevel@tonic-gate  */
21850Sstevel@tonic-gate static char *
21860Sstevel@tonic-gate getgroup(gid_t gid)
21870Sstevel@tonic-gate {
21880Sstevel@tonic-gate 	struct group *grent;
21890Sstevel@tonic-gate 	struct cachenode *c;
21900Sstevel@tonic-gate 
21910Sstevel@tonic-gate 	if ((gid == lastgid) && lastgname)
21920Sstevel@tonic-gate 		return (lastgname);
21930Sstevel@tonic-gate 
21940Sstevel@tonic-gate 	c = findincache(&groups, gid);
21950Sstevel@tonic-gate 	if (c->initted == 0) {
21960Sstevel@tonic-gate 		if ((grent = getgrgid(gid)) != NULL) {
21970Sstevel@tonic-gate 			SCPYN(&c->name[0], grent->gr_name);
21980Sstevel@tonic-gate 		} else {
21990Sstevel@tonic-gate 			(void) sprintf(&c->name[0], "%-8u", (int)gid);
22000Sstevel@tonic-gate 		}
22010Sstevel@tonic-gate 		c->initted = 1;
22020Sstevel@tonic-gate 	}
22030Sstevel@tonic-gate 	lastgid = gid;
22040Sstevel@tonic-gate 	lastgname = &c->name[0];
22050Sstevel@tonic-gate 	return (lastgname);
22060Sstevel@tonic-gate }
22070Sstevel@tonic-gate 
22080Sstevel@tonic-gate /* return >0 if item pointed by pp2 should appear first */
22090Sstevel@tonic-gate static int
22100Sstevel@tonic-gate compar(struct lbuf **pp1, struct lbuf **pp2)
22110Sstevel@tonic-gate {
22120Sstevel@tonic-gate 	struct lbuf *p1, *p2;
22130Sstevel@tonic-gate 
22140Sstevel@tonic-gate 	p1 = *pp1;
22150Sstevel@tonic-gate 	p2 = *pp2;
22160Sstevel@tonic-gate 	if (dflg == 0) {
22170Sstevel@tonic-gate /*
22180Sstevel@tonic-gate  * compare two names in ls-command one of which is file
22190Sstevel@tonic-gate  * and the other is a directory;
22200Sstevel@tonic-gate  * this portion is not used for comparing files within
22210Sstevel@tonic-gate  * a directory name of ls-command;
22220Sstevel@tonic-gate  */
22230Sstevel@tonic-gate 		if (p1->lflags&ISARG && p1->ltype == 'd') {
22240Sstevel@tonic-gate 			if (!(p2->lflags&ISARG && p2->ltype == 'd'))
22250Sstevel@tonic-gate 				return (1);
22260Sstevel@tonic-gate 		} else {
22270Sstevel@tonic-gate 			if (p2->lflags&ISARG && p2->ltype == 'd')
22280Sstevel@tonic-gate 				return (-1);
22290Sstevel@tonic-gate 		}
22300Sstevel@tonic-gate 	}
22310Sstevel@tonic-gate 	if (tflg) {
22320Sstevel@tonic-gate 		if (p2->lmtime.tv_sec > p1->lmtime.tv_sec)
22330Sstevel@tonic-gate 			return (rflg);
22340Sstevel@tonic-gate 		else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec)
22350Sstevel@tonic-gate 			return (-rflg);
22360Sstevel@tonic-gate 		/* times are equal to the sec, check nsec */
22370Sstevel@tonic-gate 		if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec)
22380Sstevel@tonic-gate 			return (rflg);
22390Sstevel@tonic-gate 		else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec)
22400Sstevel@tonic-gate 			return (-rflg);
22410Sstevel@tonic-gate 		/* if times are equal, fall through and sort by name */
22420Sstevel@tonic-gate 	} else if (Sflg) {
22430Sstevel@tonic-gate 		/*
22440Sstevel@tonic-gate 		 * The size stored in lsize can be either the
22450Sstevel@tonic-gate 		 * size or the major minor number (in the case of
22460Sstevel@tonic-gate 		 * block and character special devices).  If it's
22470Sstevel@tonic-gate 		 * a major minor number, then the size is considered
22480Sstevel@tonic-gate 		 * to be zero and we want to fall through and sort
22490Sstevel@tonic-gate 		 * by name.  In addition, if the size of p2 is equal
22500Sstevel@tonic-gate 		 * to the size of p1 we want to fall through and
22510Sstevel@tonic-gate 		 * sort by name.
22520Sstevel@tonic-gate 		 */
22530Sstevel@tonic-gate 		off_t	p1size = (p1->ltype == 'b') ||
22545331Samw 		    (p1->ltype == 'c') ? 0 : p1->lsize;
22550Sstevel@tonic-gate 		off_t	p2size = (p2->ltype == 'b') ||
22565331Samw 		    (p2->ltype == 'c') ? 0 : p2->lsize;
22570Sstevel@tonic-gate 		if (p2size > p1size) {
22580Sstevel@tonic-gate 			return (rflg);
22590Sstevel@tonic-gate 		} else if (p2size < p1size) {
22600Sstevel@tonic-gate 			return (-rflg);
22610Sstevel@tonic-gate 		}
22620Sstevel@tonic-gate 		/* Sizes are equal, fall through and sort by name. */
22630Sstevel@tonic-gate 	}
22640Sstevel@tonic-gate 	return (rflg * strcoll(
22650Sstevel@tonic-gate 	    p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname,
22660Sstevel@tonic-gate 	    p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname));
22670Sstevel@tonic-gate }
22680Sstevel@tonic-gate 
22690Sstevel@tonic-gate static void
22700Sstevel@tonic-gate pprintf(char *s1, char *s2)
22710Sstevel@tonic-gate {
22720Sstevel@tonic-gate 	csi_pprintf((unsigned char *)s1);
22730Sstevel@tonic-gate 	csi_pprintf((unsigned char *)s2);
22740Sstevel@tonic-gate }
22750Sstevel@tonic-gate 
22760Sstevel@tonic-gate static void
22770Sstevel@tonic-gate csi_pprintf(unsigned char *s)
22780Sstevel@tonic-gate {
22790Sstevel@tonic-gate 	unsigned char *cp;
22800Sstevel@tonic-gate 	char	c;
22810Sstevel@tonic-gate 	int	i;
22820Sstevel@tonic-gate 	int	c_len;
22830Sstevel@tonic-gate 	int	p_col;
22840Sstevel@tonic-gate 	wchar_t	pcode;
22850Sstevel@tonic-gate 
22860Sstevel@tonic-gate 	if (!qflg && !bflg) {
22870Sstevel@tonic-gate 		for (cp = s; *cp != '\0'; cp++) {
22880Sstevel@tonic-gate 			(void) putchar(*cp);
22890Sstevel@tonic-gate 			curcol++;
22900Sstevel@tonic-gate 		}
22910Sstevel@tonic-gate 		return;
22920Sstevel@tonic-gate 	}
22930Sstevel@tonic-gate 
22940Sstevel@tonic-gate 	for (cp = s; *cp; ) {
22950Sstevel@tonic-gate 		if (isascii(c = *cp)) {
22960Sstevel@tonic-gate 			if (!isprint(c)) {
22970Sstevel@tonic-gate 				if (qflg) {
22980Sstevel@tonic-gate 					c = '?';
22990Sstevel@tonic-gate 				} else {
23000Sstevel@tonic-gate 					curcol += 3;
23010Sstevel@tonic-gate 					(void) putc('\\', stdout);
23020Sstevel@tonic-gate 					c = '0' + ((*cp >> 6) & 07);
23030Sstevel@tonic-gate 					(void) putc(c, stdout);
23040Sstevel@tonic-gate 					c = '0' + ((*cp >> 3) & 07);
23050Sstevel@tonic-gate 					(void) putc(c, stdout);
23060Sstevel@tonic-gate 					c = '0' + (*cp & 07);
23070Sstevel@tonic-gate 				}
23080Sstevel@tonic-gate 			}
23090Sstevel@tonic-gate 			curcol++;
23100Sstevel@tonic-gate 			cp++;
23110Sstevel@tonic-gate 			(void) putc(c, stdout);
23120Sstevel@tonic-gate 			continue;
23130Sstevel@tonic-gate 		}
23140Sstevel@tonic-gate 
23150Sstevel@tonic-gate 		if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) {
23160Sstevel@tonic-gate 			c_len = 1;
23170Sstevel@tonic-gate 			goto not_print;
23180Sstevel@tonic-gate 		}
23190Sstevel@tonic-gate 
23200Sstevel@tonic-gate 		if ((p_col = wcwidth(pcode)) > 0) {
23210Sstevel@tonic-gate 			(void) putwchar(pcode);
23220Sstevel@tonic-gate 			cp += c_len;
23230Sstevel@tonic-gate 			curcol += p_col;
23240Sstevel@tonic-gate 			continue;
23250Sstevel@tonic-gate 		}
23260Sstevel@tonic-gate 
23270Sstevel@tonic-gate not_print:
23280Sstevel@tonic-gate 		for (i = 0; i < c_len; i++) {
23290Sstevel@tonic-gate 			if (qflg) {
23300Sstevel@tonic-gate 				c = '?';
23310Sstevel@tonic-gate 			} else {
23320Sstevel@tonic-gate 				curcol += 3;
23330Sstevel@tonic-gate 				(void) putc('\\', stdout);
23340Sstevel@tonic-gate 				c = '0' + ((*cp >> 6) & 07);
23350Sstevel@tonic-gate 				(void) putc(c, stdout);
23360Sstevel@tonic-gate 				c = '0' + ((*cp >> 3) & 07);
23370Sstevel@tonic-gate 				(void) putc(c, stdout);
23380Sstevel@tonic-gate 				c = '0' + (*cp & 07);
23390Sstevel@tonic-gate 			}
23400Sstevel@tonic-gate 			curcol++;
23410Sstevel@tonic-gate 			(void) putc(c, stdout);
23420Sstevel@tonic-gate 			cp++;
23430Sstevel@tonic-gate 		}
23440Sstevel@tonic-gate 	}
23450Sstevel@tonic-gate }
23460Sstevel@tonic-gate 
23470Sstevel@tonic-gate static int
23480Sstevel@tonic-gate strcol(unsigned char *s1)
23490Sstevel@tonic-gate {
23500Sstevel@tonic-gate 	int	w;
23510Sstevel@tonic-gate 	int	w_col;
23520Sstevel@tonic-gate 	int	len;
23530Sstevel@tonic-gate 	wchar_t	wc;
23540Sstevel@tonic-gate 
23550Sstevel@tonic-gate 	w = 0;
23560Sstevel@tonic-gate 	while (*s1) {
23570Sstevel@tonic-gate 		if (isascii(*s1)) {
23580Sstevel@tonic-gate 			w++;
23590Sstevel@tonic-gate 			s1++;
23600Sstevel@tonic-gate 			continue;
23610Sstevel@tonic-gate 		}
23620Sstevel@tonic-gate 
23630Sstevel@tonic-gate 		if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
23640Sstevel@tonic-gate 			w++;
23650Sstevel@tonic-gate 			s1++;
23660Sstevel@tonic-gate 			continue;
23670Sstevel@tonic-gate 		}
23680Sstevel@tonic-gate 
23690Sstevel@tonic-gate 		if ((w_col = wcwidth(wc)) < 0)
23700Sstevel@tonic-gate 			w_col = len;
23710Sstevel@tonic-gate 		s1 += len;
23720Sstevel@tonic-gate 		w += w_col;
23730Sstevel@tonic-gate 	}
23740Sstevel@tonic-gate 	return (w);
23750Sstevel@tonic-gate }
23760Sstevel@tonic-gate 
23770Sstevel@tonic-gate /*
23780Sstevel@tonic-gate  * Convert an unsigned long long to a string representation and place the
23790Sstevel@tonic-gate  * result in the caller-supplied buffer.
23800Sstevel@tonic-gate  *
23810Sstevel@tonic-gate  * The number provided is a size in bytes.  The number is first
23820Sstevel@tonic-gate  * converted to an integral multiple of 'scale' bytes.  This new
23830Sstevel@tonic-gate  * number is then scaled down until it is small enough to be in a good
23840Sstevel@tonic-gate  * human readable format, i.e.  in the range 0 thru scale-1.  If the
23850Sstevel@tonic-gate  * number used to derive the final number is not a multiple of scale, and
23860Sstevel@tonic-gate  * the final number has only a single significant digit, we compute
23870Sstevel@tonic-gate  * tenths of units to provide a second significant digit.
23880Sstevel@tonic-gate  *
23890Sstevel@tonic-gate  * The value "(unsigned long long)-1" is a special case and is always
23900Sstevel@tonic-gate  * converted to "-1".
23910Sstevel@tonic-gate  *
23920Sstevel@tonic-gate  * A pointer to the caller-supplied buffer is returned.
23930Sstevel@tonic-gate  */
23940Sstevel@tonic-gate static char *
23950Sstevel@tonic-gate number_to_scaled_string(
23960Sstevel@tonic-gate 			numbuf_t buf,		/* put the result here */
23970Sstevel@tonic-gate 			unsigned long long number, /* convert this number */
23980Sstevel@tonic-gate 			long scale)
23990Sstevel@tonic-gate {
24000Sstevel@tonic-gate 	unsigned long long save;
24010Sstevel@tonic-gate 	/* Measurement: kilo, mega, giga, tera, peta, exa */
24020Sstevel@tonic-gate 	char *uom = "KMGTPE";
24030Sstevel@tonic-gate 
24040Sstevel@tonic-gate 	if ((long long)number == (long long)-1) {
24050Sstevel@tonic-gate 		(void) strlcpy(buf, "-1", sizeof (numbuf_t));
24060Sstevel@tonic-gate 		return (buf);
24070Sstevel@tonic-gate 	}
24080Sstevel@tonic-gate 
24090Sstevel@tonic-gate 	save = number;
24100Sstevel@tonic-gate 	number = number / scale;
24110Sstevel@tonic-gate 
24120Sstevel@tonic-gate 	/*
24130Sstevel@tonic-gate 	 * Now we have number as a count of scale units.
24140Sstevel@tonic-gate 	 * If no further scaling is necessary, we round up as appropriate.
24150Sstevel@tonic-gate 	 *
24160Sstevel@tonic-gate 	 * The largest value number could have had entering the routine is
24170Sstevel@tonic-gate 	 * 16 Exabytes, so running off the end of the uom array should
24180Sstevel@tonic-gate 	 * never happen.  We check for that, though, as a guard against
24190Sstevel@tonic-gate 	 * a breakdown elsewhere in the algorithm.
24200Sstevel@tonic-gate 	 */
24210Sstevel@tonic-gate 	if (number < (unsigned long long)scale) {
24220Sstevel@tonic-gate 		if ((save % scale) >= (unsigned long long)(scale / 2)) {
24230Sstevel@tonic-gate 			if (++number == (unsigned long long)scale) {
24240Sstevel@tonic-gate 				uom++;
24250Sstevel@tonic-gate 				number = 1;
24260Sstevel@tonic-gate 			}
24270Sstevel@tonic-gate 		}
24280Sstevel@tonic-gate 	} else {
24290Sstevel@tonic-gate 		while ((number >= (unsigned long long)scale) && (*uom != 'E')) {
24300Sstevel@tonic-gate 			uom++; /* next unit of measurement */
24310Sstevel@tonic-gate 			save = number;
24320Sstevel@tonic-gate 			/*
24330Sstevel@tonic-gate 			 * If we're over half way to the next unit of
24340Sstevel@tonic-gate 			 * 'scale' bytes (which means we should round
24350Sstevel@tonic-gate 			 * up), then adding half of 'scale' prior to
24360Sstevel@tonic-gate 			 * the division will push us into that next
24370Sstevel@tonic-gate 			 * unit of scale when we perform the division
24380Sstevel@tonic-gate 			 */
24390Sstevel@tonic-gate 			number = (number + (scale / 2)) / scale;
24400Sstevel@tonic-gate 		}
24410Sstevel@tonic-gate 	}
24420Sstevel@tonic-gate 
24430Sstevel@tonic-gate 	/* check if we should output a decimal place after the point */
24440Sstevel@tonic-gate 	if ((save / scale) < 10) {
24450Sstevel@tonic-gate 		/* snprintf() will round for us */
24460Sstevel@tonic-gate 		float fnum = (float)save / scale;
24470Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c",
24480Sstevel@tonic-gate 		    fnum, *uom);
24490Sstevel@tonic-gate 	} else {
24500Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (numbuf_t), "%4llu%c",
24510Sstevel@tonic-gate 		    number, *uom);
24520Sstevel@tonic-gate 	}
24530Sstevel@tonic-gate 	return (buf);
24540Sstevel@tonic-gate }
24555331Samw 
24565331Samw /* Get extended system attributes and set the display */
24575331Samw 
24585331Samw int
24595331Samw get_sysxattr(char *fname, struct lbuf *rep)
24605331Samw {
24615331Samw 	boolean_t	value;
24625331Samw 	data_type_t	type;
24635331Samw 	int		error;
24645331Samw 	char		*name;
24655331Samw 	int		i;
24665331Samw 
24675331Samw 	if ((error = getattrat(AT_FDCWD, XATTR_VIEW_READWRITE, fname,
24685331Samw 	    &response)) != 0) {
24695331Samw 		perror("ls:getattrat");
24705331Samw 		return (error);
24715331Samw 	}
24725331Samw 
24735331Samw 	/*
24745331Samw 	 * Allocate 'sacnt' size array to hold extended timestamp
24755331Samw 	 * system attributes and initialize the array.
24765331Samw 	 */
24775331Samw 	rep->extm = xmalloc(sacnt * sizeof (struct attrtm), rep);
24785331Samw 	for (i = 0; i < sacnt; i++) {
24795331Samw 		rep->extm[i].stm = 0;
24805331Samw 		rep->extm[i].nstm = 0;
24815331Samw 		rep->extm[i].name = NULL;
24825331Samw 	}
24835331Samw 	while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
24845331Samw 		name = nvpair_name(pair);
24855331Samw 		type = nvpair_type(pair);
24865331Samw 		if (type == DATA_TYPE_BOOLEAN_VALUE) {
24875331Samw 			error = nvpair_value_boolean_value(pair, &value);
24885331Samw 			if (error) {
24895331Samw 				(void) fprintf(stderr,
24905331Samw 				    gettext("nvpair_value_boolean_value "
24915331Samw 				    "failed: error = %d\n"), error);
24925331Samw 				continue;
24935331Samw 			}
24945331Samw 			if (name != NULL)
24955331Samw 				set_sysattrb_display(name, value, rep);
24965331Samw 			continue;
24975331Samw 		} else if (type == DATA_TYPE_UINT64_ARRAY) {
24985331Samw 			if (name != NULL)
24995331Samw 				set_sysattrtm_display(name, rep);
25005331Samw 			continue;
25015331Samw 		}
25025331Samw 	}
25035331Samw 	nvlist_free(response);
25045331Samw 	return (0);
25055331Samw }
25065331Samw 
25075331Samw /* Set extended system attribute boolean display */
25085331Samw 
25095331Samw void
25105331Samw set_sysattrb_display(char *name, boolean_t val, struct lbuf *rep)
25115331Samw {
25125331Samw 	f_attr_t	fattr;
25135331Samw 	const char	*opt;
25145331Samw 	size_t		len;
25155331Samw 
25165331Samw 	fattr = name_to_attr(name);
25175331Samw 	if (fattr != F_ATTR_INVAL && fattr < sacnt) {
25185331Samw 		if (vopt) {
25195331Samw 			len = strlen(name);
25205331Samw 			if (val) {
25215331Samw 				rep->exttr[fattr].name = xmalloc(len + 1, rep);
25225331Samw 				(void) strlcpy(rep->exttr[fattr].name, name,
25235331Samw 				    len + 1);
25245331Samw 			} else {
25255331Samw 				rep->exttr[fattr].name = xmalloc(len + 3, rep);
25265331Samw 				(void) snprintf(rep->exttr[fattr].name, len + 3,
25275331Samw 				    "no%s", name);
25285331Samw 			}
25295331Samw 		} else {
25305331Samw 			opt = attr_to_option(fattr);
25315331Samw 			if (opt != NULL) {
25325331Samw 				len = strlen(opt);
25335331Samw 				rep->exttr[fattr].name = xmalloc(len + 1, rep);
25345331Samw 				if (val)
25355331Samw 					(void) strlcpy(rep->exttr[fattr].name,
25365331Samw 					    opt, len + 1);
25375331Samw 				else
25385331Samw 					(void) strlcpy(rep->exttr[fattr].name,
25395331Samw 					    "-", len + 1);
25405331Samw 			}
25415331Samw 		}
25425331Samw 	}
25435331Samw }
25445331Samw 
25455331Samw /* Set extended system attribute timestamp display */
25465331Samw 
25475331Samw void
25485331Samw set_sysattrtm_display(char *name, struct lbuf *rep)
25495331Samw {
25505331Samw 	uint_t		nelem;
25515331Samw 	uint64_t	*value;
25525331Samw 	int		i;
25535331Samw 	size_t		len;
25545331Samw 
25555331Samw 	if (nvpair_value_uint64_array(pair, &value, &nelem) == 0) {
25565331Samw 		if (*value != NULL) {
25575331Samw 			len = strlen(name);
25585331Samw 			i = 0;
25595331Samw 			while (rep->extm[i].stm != 0 && i < sacnt)
25605331Samw 				i++;
25615331Samw 			rep->extm[i].stm = value[0];
25625331Samw 			rep->extm[i].nstm = value[1];
25635331Samw 			rep->extm[i].name = xmalloc(len + 1, rep);
25645331Samw 			(void) strlcpy(rep->extm[i].name, name, len + 1);
25655331Samw 		}
25665331Samw 	}
25675331Samw }
25685331Samw 
25695331Samw void
2570*9664Sjason@ansipunx.net format_time(time_t sec, time_t nsec)
25715331Samw {
2572*9664Sjason@ansipunx.net 	const char *fstr = time_fmt_new;
2573*9664Sjason@ansipunx.net 	char fmt_buf[FMTSIZE];
25745331Samw 
2575*9664Sjason@ansipunx.net 	if (Eflg) {
2576*9664Sjason@ansipunx.net 		(void) snprintf(fmt_buf, FMTSIZE, fstr, nsec);
2577*9664Sjason@ansipunx.net 		(void) strftime(time_buf, sizeof (time_buf), fmt_buf,
2578*9664Sjason@ansipunx.net 		    localtime(&sec));
2579*9664Sjason@ansipunx.net 		return;
2580*9664Sjason@ansipunx.net 	}
2581*9664Sjason@ansipunx.net 
2582*9664Sjason@ansipunx.net 	if (sec < year || sec > now)
2583*9664Sjason@ansipunx.net 		fstr = time_fmt_old;
2584*9664Sjason@ansipunx.net 
2585*9664Sjason@ansipunx.net 	/* if a custom time was specified, shouldn't be localized */
25865331Samw 	(void) strftime(time_buf, sizeof (time_buf),
2587*9664Sjason@ansipunx.net 	    (time_custom == 0) ? dcgettext(NULL, fstr, LC_TIME) : fstr,
25885331Samw 	    localtime(&sec));
25895331Samw }
25905331Samw 
25915331Samw void
25925331Samw format_attrtime(struct lbuf *p)
25935331Samw {
2594*9664Sjason@ansipunx.net 	int tmattr = 0;
25955331Samw 	int i;
25965331Samw 
25975331Samw 	if (p->extm != NULL) {
25985331Samw 		for (i = 0; i < sacnt; i++) {
25995331Samw 			if (p->extm[i].name != NULL) {
26005331Samw 				tmattr = 1;
26015331Samw 				break;
26025331Samw 			}
26035331Samw 		}
26045331Samw 	}
2605*9664Sjason@ansipunx.net 
26065331Samw 	if (tmattr) {
2607*9664Sjason@ansipunx.net 		const char *old_save = time_fmt_old;
2608*9664Sjason@ansipunx.net 		const char *new_save = time_fmt_new;
2609*9664Sjason@ansipunx.net 
2610*9664Sjason@ansipunx.net 		/* Eflg always sets format to FORMAT_ISO_FULL */
2611*9664Sjason@ansipunx.net 		if (!Eflg && !time_custom) {
2612*9664Sjason@ansipunx.net 			time_fmt_old = FORMAT_OLD;
2613*9664Sjason@ansipunx.net 			time_fmt_new = FORMAT_NEW;
26145331Samw 		}
2615*9664Sjason@ansipunx.net 
2616*9664Sjason@ansipunx.net 		format_time((time_t)p->extm[i].stm, (time_t)p->extm[i].nstm);
2617*9664Sjason@ansipunx.net 
2618*9664Sjason@ansipunx.net 		time_fmt_old = old_save;
2619*9664Sjason@ansipunx.net 		time_fmt_new = new_save;
26205331Samw 	}
26215331Samw }
26225331Samw 
26235331Samw void
26245331Samw print_time(struct lbuf *p)
26255331Samw {
2626*9664Sjason@ansipunx.net 	const char *old_save = time_fmt_old;
2627*9664Sjason@ansipunx.net 	const char *new_save = time_fmt_new;
2628*9664Sjason@ansipunx.net 
26295331Samw 	int i = 0;
26305331Samw 
2631*9664Sjason@ansipunx.net 	if (!Eflg) {
2632*9664Sjason@ansipunx.net 		time_fmt_old = FORMAT_LONG;
2633*9664Sjason@ansipunx.net 		time_fmt_new = FORMAT_LONG;
2634*9664Sjason@ansipunx.net 	}
2635*9664Sjason@ansipunx.net 
26365331Samw 	new_line();
2637*9664Sjason@ansipunx.net 	format_time(p->lat.tv_sec, p->lat.tv_nsec);
2638*9664Sjason@ansipunx.net 	(void) printf("         timestamp: atime        %s\n", time_buf);
2639*9664Sjason@ansipunx.net 	format_time(p->lct.tv_sec, p->lct.tv_nsec);
2640*9664Sjason@ansipunx.net 	(void) printf("         timestamp: ctime        %s\n", time_buf);
2641*9664Sjason@ansipunx.net 	format_time(p->lmt.tv_sec, p->lmt.tv_nsec);
2642*9664Sjason@ansipunx.net 	(void) printf("         timestamp: mtime        %s\n", time_buf);
2643*9664Sjason@ansipunx.net 	if (p->extm != NULL) {
2644*9664Sjason@ansipunx.net 		while (p->extm[i].nstm != 0 && i < sacnt) {
2645*9664Sjason@ansipunx.net 			format_time(p->extm[i].stm, p->extm[i].nstm);
2646*9664Sjason@ansipunx.net 			if (p->extm[i].name != NULL) {
2647*9664Sjason@ansipunx.net 				(void) printf("         timestamp:"
2648*9664Sjason@ansipunx.net 				    " %s        %s\n",
2649*9664Sjason@ansipunx.net 				    p->extm[i].name, time_buf);
2650*9664Sjason@ansipunx.net 			}
2651*9664Sjason@ansipunx.net 			i++;
2652*9664Sjason@ansipunx.net 		}
2653*9664Sjason@ansipunx.net 	}
2654*9664Sjason@ansipunx.net 
2655*9664Sjason@ansipunx.net 	time_fmt_old = old_save;
2656*9664Sjason@ansipunx.net 	time_fmt_new = new_save;
2657*9664Sjason@ansipunx.net }
2658*9664Sjason@ansipunx.net 
2659*9664Sjason@ansipunx.net /*
2660*9664Sjason@ansipunx.net  * Check if color definition applies to entry, returns 1 if yes, 0 if no
2661*9664Sjason@ansipunx.net  */
2662*9664Sjason@ansipunx.net static int
2663*9664Sjason@ansipunx.net color_match(struct lbuf *entry, ls_color_t *color)
2664*9664Sjason@ansipunx.net {
2665*9664Sjason@ansipunx.net 	switch (color->ftype) {
2666*9664Sjason@ansipunx.net 	case LS_PAT:
2667*9664Sjason@ansipunx.net 	{
2668*9664Sjason@ansipunx.net 		char	*fname;
2669*9664Sjason@ansipunx.net 		size_t	fname_len, sfx_len;
2670*9664Sjason@ansipunx.net 
2671*9664Sjason@ansipunx.net 		if (entry->lflags & ISARG)
2672*9664Sjason@ansipunx.net 			fname = entry->ln.namep;
2673*9664Sjason@ansipunx.net 		else
2674*9664Sjason@ansipunx.net 			fname = entry->ln.lname;
2675*9664Sjason@ansipunx.net 
2676*9664Sjason@ansipunx.net 		fname_len = strlen(fname);
2677*9664Sjason@ansipunx.net 		sfx_len = strlen(color->sfx);
2678*9664Sjason@ansipunx.net 		if (sfx_len > fname_len)
2679*9664Sjason@ansipunx.net 			return (0);
2680*9664Sjason@ansipunx.net 
2681*9664Sjason@ansipunx.net 		if (strcmp(color->sfx, fname + fname_len - sfx_len) == 0)
2682*9664Sjason@ansipunx.net 			return (1);
2683*9664Sjason@ansipunx.net 		else
2684*9664Sjason@ansipunx.net 			return (0);
2685*9664Sjason@ansipunx.net 	}
2686*9664Sjason@ansipunx.net 
2687*9664Sjason@ansipunx.net 	case LS_NORMAL:
2688*9664Sjason@ansipunx.net 		return (1);
2689*9664Sjason@ansipunx.net 
2690*9664Sjason@ansipunx.net 	case LS_FILE:
2691*9664Sjason@ansipunx.net 		return ((entry->ltype == '-'));
2692*9664Sjason@ansipunx.net 
2693*9664Sjason@ansipunx.net 	case LS_DIR:
2694*9664Sjason@ansipunx.net 		return ((entry->ltype == 'd'));
2695*9664Sjason@ansipunx.net 
2696*9664Sjason@ansipunx.net 	case LS_LINK:
2697*9664Sjason@ansipunx.net 		return ((entry->ltype == 'l'));
2698*9664Sjason@ansipunx.net 
2699*9664Sjason@ansipunx.net 	case LS_FIFO:
2700*9664Sjason@ansipunx.net 		return ((entry->ltype == 'p'));
2701*9664Sjason@ansipunx.net 
2702*9664Sjason@ansipunx.net 	case LS_SOCK:
2703*9664Sjason@ansipunx.net 		return ((entry->ltype == 's'));
2704*9664Sjason@ansipunx.net 
2705*9664Sjason@ansipunx.net 	case LS_DOOR:
2706*9664Sjason@ansipunx.net 		return ((entry->ltype == 'D'));
2707*9664Sjason@ansipunx.net 
2708*9664Sjason@ansipunx.net 	case LS_BLK:
2709*9664Sjason@ansipunx.net 		return ((entry->ltype == 'b'));
2710*9664Sjason@ansipunx.net 
2711*9664Sjason@ansipunx.net 	case LS_CHR:
2712*9664Sjason@ansipunx.net 		return ((entry->ltype == 'c'));
2713*9664Sjason@ansipunx.net 
2714*9664Sjason@ansipunx.net 	case LS_PORT:
2715*9664Sjason@ansipunx.net 		return ((entry->ltype == 'P'));
2716*9664Sjason@ansipunx.net 
2717*9664Sjason@ansipunx.net 	case LS_ORPHAN:
2718*9664Sjason@ansipunx.net 	{
2719*9664Sjason@ansipunx.net 		struct stat st;
2720*9664Sjason@ansipunx.net 		int rc;
2721*9664Sjason@ansipunx.net 
2722*9664Sjason@ansipunx.net 		if (entry->ltype != 'l')
2723*9664Sjason@ansipunx.net 			return (0);
2724*9664Sjason@ansipunx.net 		if (entry->flinkto == NULL)
2725*9664Sjason@ansipunx.net 			return (1);
2726*9664Sjason@ansipunx.net 
2727*9664Sjason@ansipunx.net 		if (entry->lflags & ISARG)
2728*9664Sjason@ansipunx.net 			rc = stat(entry->ln.namep, &st);
2729*9664Sjason@ansipunx.net 		else
2730*9664Sjason@ansipunx.net 			rc = stat(entry->ln.lname, &st);
2731*9664Sjason@ansipunx.net 
2732*9664Sjason@ansipunx.net 		if (rc == -1 && errno == ENOENT)
2733*9664Sjason@ansipunx.net 			return (1);
2734*9664Sjason@ansipunx.net 
2735*9664Sjason@ansipunx.net 		return (0);
2736*9664Sjason@ansipunx.net 	}
2737*9664Sjason@ansipunx.net 
2738*9664Sjason@ansipunx.net 	case LS_SETUID:
2739*9664Sjason@ansipunx.net 		return (entry->ltype != 'l' && (entry->lflags & (S_ISUID)));
2740*9664Sjason@ansipunx.net 
2741*9664Sjason@ansipunx.net 	case LS_SETGID:
2742*9664Sjason@ansipunx.net 		return (entry->ltype != 'l' && (entry->lflags & (S_ISGID)));
2743*9664Sjason@ansipunx.net 
2744*9664Sjason@ansipunx.net 	case LS_STICKY_OTHER_WRITABLE:
2745*9664Sjason@ansipunx.net 		return (entry->ltype != 'l' &&
2746*9664Sjason@ansipunx.net 		    (entry->lflags & (S_IWOTH|S_ISVTX)));
2747*9664Sjason@ansipunx.net 
2748*9664Sjason@ansipunx.net 	case LS_OTHER_WRITABLE:
2749*9664Sjason@ansipunx.net 		return (entry->ltype != 'l' && (entry->lflags & (S_IWOTH)));
2750*9664Sjason@ansipunx.net 
2751*9664Sjason@ansipunx.net 	case LS_STICKY:
2752*9664Sjason@ansipunx.net 		return (entry->ltype != 'l' && (entry->lflags & (S_ISVTX)));
2753*9664Sjason@ansipunx.net 
2754*9664Sjason@ansipunx.net 	case LS_EXEC:
2755*9664Sjason@ansipunx.net 		return (entry->ltype != 'l' &&
2756*9664Sjason@ansipunx.net 		    (entry->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)));
2757*9664Sjason@ansipunx.net 	}
2758*9664Sjason@ansipunx.net 
2759*9664Sjason@ansipunx.net 	return (0);
2760*9664Sjason@ansipunx.net }
2761*9664Sjason@ansipunx.net 
2762*9664Sjason@ansipunx.net static void
2763*9664Sjason@ansipunx.net dump_color(ls_color_t *c)
2764*9664Sjason@ansipunx.net {
2765*9664Sjason@ansipunx.net 	if (c == NULL)
2766*9664Sjason@ansipunx.net 		return;
2767*9664Sjason@ansipunx.net 
2768*9664Sjason@ansipunx.net 	(void) printf("\n\ttype: ");
2769*9664Sjason@ansipunx.net 	switch (c->ftype) {
2770*9664Sjason@ansipunx.net 	case LS_NORMAL:
2771*9664Sjason@ansipunx.net 		(void) printf("LS_NORMAL");
2772*9664Sjason@ansipunx.net 		break;
2773*9664Sjason@ansipunx.net 	case LS_FILE:
2774*9664Sjason@ansipunx.net 		(void) printf("LS_FILE");
2775*9664Sjason@ansipunx.net 		break;
2776*9664Sjason@ansipunx.net 	case LS_EXEC:
2777*9664Sjason@ansipunx.net 		(void) printf("LS_EXEC");
2778*9664Sjason@ansipunx.net 		break;
2779*9664Sjason@ansipunx.net 	case LS_DIR:
2780*9664Sjason@ansipunx.net 		(void) printf("LS_DIR");
2781*9664Sjason@ansipunx.net 		break;
2782*9664Sjason@ansipunx.net 	case LS_LINK:
2783*9664Sjason@ansipunx.net 		(void) printf("LS_LINK");
2784*9664Sjason@ansipunx.net 		break;
2785*9664Sjason@ansipunx.net 
2786*9664Sjason@ansipunx.net 	case LS_FIFO:
2787*9664Sjason@ansipunx.net 		(void) printf("LS_FIFO");
2788*9664Sjason@ansipunx.net 		break;
2789*9664Sjason@ansipunx.net 
2790*9664Sjason@ansipunx.net 	case LS_SOCK:
2791*9664Sjason@ansipunx.net 		(void) printf("LS_SOCK");
2792*9664Sjason@ansipunx.net 		break;
2793*9664Sjason@ansipunx.net 
2794*9664Sjason@ansipunx.net 	case LS_DOOR:
2795*9664Sjason@ansipunx.net 		(void) printf("LS_DOOR");
2796*9664Sjason@ansipunx.net 		break;
2797*9664Sjason@ansipunx.net 
2798*9664Sjason@ansipunx.net 	case LS_BLK:
2799*9664Sjason@ansipunx.net 		(void) printf("LS_BLK");
2800*9664Sjason@ansipunx.net 		break;
2801*9664Sjason@ansipunx.net 
2802*9664Sjason@ansipunx.net 	case LS_CHR:
2803*9664Sjason@ansipunx.net 		(void) printf("LS_CHR");
2804*9664Sjason@ansipunx.net 		break;
2805*9664Sjason@ansipunx.net 
2806*9664Sjason@ansipunx.net 	case LS_PORT:
2807*9664Sjason@ansipunx.net 		(void) printf("LS_PORT");
2808*9664Sjason@ansipunx.net 		break;
2809*9664Sjason@ansipunx.net 
2810*9664Sjason@ansipunx.net 	case LS_STICKY:
2811*9664Sjason@ansipunx.net 		(void) printf("LS_STICKY");
2812*9664Sjason@ansipunx.net 		break;
2813*9664Sjason@ansipunx.net 
2814*9664Sjason@ansipunx.net 	case LS_ORPHAN:
2815*9664Sjason@ansipunx.net 		(void) printf("LS_ORPHAN");
2816*9664Sjason@ansipunx.net 		break;
2817*9664Sjason@ansipunx.net 
2818*9664Sjason@ansipunx.net 	case LS_SETGID:
2819*9664Sjason@ansipunx.net 		(void) printf("LS_SETGID");
2820*9664Sjason@ansipunx.net 		break;
2821*9664Sjason@ansipunx.net 
2822*9664Sjason@ansipunx.net 	case LS_SETUID:
2823*9664Sjason@ansipunx.net 		(void) printf("LS_SETUID");
2824*9664Sjason@ansipunx.net 		break;
2825*9664Sjason@ansipunx.net 
2826*9664Sjason@ansipunx.net 	case LS_OTHER_WRITABLE:
2827*9664Sjason@ansipunx.net 		(void) printf("LS_OTHER_WRITABLE");
2828*9664Sjason@ansipunx.net 		break;
2829*9664Sjason@ansipunx.net 
2830*9664Sjason@ansipunx.net 	case LS_STICKY_OTHER_WRITABLE:
2831*9664Sjason@ansipunx.net 		(void) printf("LS_STICKY_OTHER_WRITABLE");
2832*9664Sjason@ansipunx.net 		break;
2833*9664Sjason@ansipunx.net 
2834*9664Sjason@ansipunx.net 	case LS_PAT:
2835*9664Sjason@ansipunx.net 		(void) printf("LS_PAT\n");
2836*9664Sjason@ansipunx.net 		(void) printf("\tpattern: %s", c->sfx);
2837*9664Sjason@ansipunx.net 		break;
2838*9664Sjason@ansipunx.net 	}
2839*9664Sjason@ansipunx.net 	(void) printf("\n");
2840*9664Sjason@ansipunx.net 	(void) printf("\tattr: %d\n", c->attr);
2841*9664Sjason@ansipunx.net 	(void) printf("\tfg: %d\n", c->fg);
2842*9664Sjason@ansipunx.net 	(void) printf("\tbg: %d\n", c->bg);
2843*9664Sjason@ansipunx.net 	(void) printf("\t");
2844*9664Sjason@ansipunx.net }
2845*9664Sjason@ansipunx.net 
2846*9664Sjason@ansipunx.net static ls_color_t *
2847*9664Sjason@ansipunx.net get_color_attr(struct lbuf *l)
2848*9664Sjason@ansipunx.net {
2849*9664Sjason@ansipunx.net 	int i;
2850*9664Sjason@ansipunx.net 
2851*9664Sjason@ansipunx.net 	/*
2852*9664Sjason@ansipunx.net 	 * Colors are sorted from most general lsc_colors[0] to most specific
2853*9664Sjason@ansipunx.net 	 * lsc_colors[lsc_ncolors - 1] by ls_color_init().  Start search with
2854*9664Sjason@ansipunx.net 	 * most specific color rule and work towards most general.
2855*9664Sjason@ansipunx.net 	 */
2856*9664Sjason@ansipunx.net 	for (i = lsc_ncolors - 1; i >= 0; --i)
2857*9664Sjason@ansipunx.net 		if (color_match(l, &lsc_colors[i]))
2858*9664Sjason@ansipunx.net 			return (&lsc_colors[i]);
2859*9664Sjason@ansipunx.net 
2860*9664Sjason@ansipunx.net 	return (NULL);
2861*9664Sjason@ansipunx.net }
2862*9664Sjason@ansipunx.net 
2863*9664Sjason@ansipunx.net static void
2864*9664Sjason@ansipunx.net ls_tprint(char *str, long int p1, long int p2, long int p3, long int p4,
2865*9664Sjason@ansipunx.net     long int p5, long int p6, long int p7, long int p8, long int p9)
2866*9664Sjason@ansipunx.net {
2867*9664Sjason@ansipunx.net 	char *s;
2868*9664Sjason@ansipunx.net 
2869*9664Sjason@ansipunx.net 	if (str == NULL)
2870*9664Sjason@ansipunx.net 		return;
2871*9664Sjason@ansipunx.net 
2872*9664Sjason@ansipunx.net 	s = tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
2873*9664Sjason@ansipunx.net 
2874*9664Sjason@ansipunx.net 	if (s != NULL)
2875*9664Sjason@ansipunx.net 		(void) putp(s);
2876*9664Sjason@ansipunx.net }
2877*9664Sjason@ansipunx.net 
2878*9664Sjason@ansipunx.net static void
2879*9664Sjason@ansipunx.net ls_start_color(struct lbuf *l)
2880*9664Sjason@ansipunx.net {
2881*9664Sjason@ansipunx.net 	ls_color_t *c = get_color_attr(l);
2882*9664Sjason@ansipunx.net 
2883*9664Sjason@ansipunx.net 	if (c == NULL)
2884*9664Sjason@ansipunx.net 		return;
2885*9664Sjason@ansipunx.net 
2886*9664Sjason@ansipunx.net 	if (lsc_debug)
2887*9664Sjason@ansipunx.net 		lsc_match = c;
2888*9664Sjason@ansipunx.net 
2889*9664Sjason@ansipunx.net 	if (c->attr & LSA_BOLD)
2890*9664Sjason@ansipunx.net 		ls_tprint(lsc_bold, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2891*9664Sjason@ansipunx.net 	if (c->attr & LSA_UNDERSCORE)
2892*9664Sjason@ansipunx.net 		ls_tprint(lsc_underline, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2893*9664Sjason@ansipunx.net 	if (c->attr & LSA_BLINK)
2894*9664Sjason@ansipunx.net 		ls_tprint(lsc_blink, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2895*9664Sjason@ansipunx.net 	if (c->attr & LSA_REVERSE)
2896*9664Sjason@ansipunx.net 		ls_tprint(lsc_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2897*9664Sjason@ansipunx.net 	if (c->attr & LSA_CONCEALED)
2898*9664Sjason@ansipunx.net 		ls_tprint(lsc_concealed, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2899*9664Sjason@ansipunx.net 	if (c->attr == LSA_NONE)
2900*9664Sjason@ansipunx.net 		ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2901*9664Sjason@ansipunx.net 
2902*9664Sjason@ansipunx.net 	if (c->fg != -1)
2903*9664Sjason@ansipunx.net 		ls_tprint(lsc_setfg, c->fg, 0, 0, 0, 0, 0, 0, 0, 0);
2904*9664Sjason@ansipunx.net 	if (c->bg != -1)
2905*9664Sjason@ansipunx.net 		ls_tprint(lsc_setbg, c->bg, 0, 0, 0, 0, 0, 0, 0, 0);
2906*9664Sjason@ansipunx.net }
2907*9664Sjason@ansipunx.net 
2908*9664Sjason@ansipunx.net static void
2909*9664Sjason@ansipunx.net ls_end_color()
2910*9664Sjason@ansipunx.net {
2911*9664Sjason@ansipunx.net 	ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2912*9664Sjason@ansipunx.net 	if (lsc_debug)
2913*9664Sjason@ansipunx.net 		dump_color(lsc_match);
2914*9664Sjason@ansipunx.net }
2915*9664Sjason@ansipunx.net 
2916*9664Sjason@ansipunx.net static void
2917*9664Sjason@ansipunx.net new_color_entry(char *colorstr)
2918*9664Sjason@ansipunx.net {
2919*9664Sjason@ansipunx.net 	static const struct {
2920*9664Sjason@ansipunx.net 		const char	*s;
2921*9664Sjason@ansipunx.net 		ls_cftype_t	stype;
2922*9664Sjason@ansipunx.net 	} type_map[] = {
2923*9664Sjason@ansipunx.net 		{ "no", LS_NORMAL },
2924*9664Sjason@ansipunx.net 		{ "fi", LS_FILE },
2925*9664Sjason@ansipunx.net 		{ "di", LS_DIR },
2926*9664Sjason@ansipunx.net 		{ "ln", LS_LINK },
2927*9664Sjason@ansipunx.net 		{ "pi", LS_FIFO },
2928*9664Sjason@ansipunx.net 		{ "so", LS_SOCK },
2929*9664Sjason@ansipunx.net 		{ "do", LS_DOOR },
2930*9664Sjason@ansipunx.net 		{ "bd", LS_BLK },
2931*9664Sjason@ansipunx.net 		{ "cd", LS_CHR },
2932*9664Sjason@ansipunx.net 		{ "or", LS_ORPHAN },
2933*9664Sjason@ansipunx.net 		{ "su", LS_SETUID },
2934*9664Sjason@ansipunx.net 		{ "sg", LS_SETGID },
2935*9664Sjason@ansipunx.net 		{ "tw", LS_STICKY_OTHER_WRITABLE },
2936*9664Sjason@ansipunx.net 		{ "ow", LS_OTHER_WRITABLE },
2937*9664Sjason@ansipunx.net 		{ "st", LS_STICKY },
2938*9664Sjason@ansipunx.net 		{ "ex", LS_EXEC },
2939*9664Sjason@ansipunx.net 		{ "po", LS_PORT },
2940*9664Sjason@ansipunx.net 		{ NULL, LS_NORMAL }
2941*9664Sjason@ansipunx.net 	};
2942*9664Sjason@ansipunx.net 
2943*9664Sjason@ansipunx.net 	char		*p, *lasts;
2944*9664Sjason@ansipunx.net 	int		i;
2945*9664Sjason@ansipunx.net 	int		color, attr;
2946*9664Sjason@ansipunx.net 
2947*9664Sjason@ansipunx.net 	p = strtok_r(colorstr, "=", &lasts);
2948*9664Sjason@ansipunx.net 	if (p == NULL) {
2949*9664Sjason@ansipunx.net 		colorflg = 0;
2950*9664Sjason@ansipunx.net 		return;
2951*9664Sjason@ansipunx.net 	}
2952*9664Sjason@ansipunx.net 
2953*9664Sjason@ansipunx.net 	if (p[0] == '*') {
2954*9664Sjason@ansipunx.net 		lsc_colors[lsc_ncolors].ftype = LS_PAT;
2955*9664Sjason@ansipunx.net 		/* don't include the * in the suffix */
2956*9664Sjason@ansipunx.net 		if ((lsc_colors[lsc_ncolors].sfx = strdup(p + 1)) == NULL) {
2957*9664Sjason@ansipunx.net 			colorflg = 0;
2958*9664Sjason@ansipunx.net 			return;
2959*9664Sjason@ansipunx.net 		}
2960*9664Sjason@ansipunx.net 	} else {
2961*9664Sjason@ansipunx.net 		lsc_colors[lsc_ncolors].sfx = NULL;
2962*9664Sjason@ansipunx.net 
2963*9664Sjason@ansipunx.net 		for (i = 0; type_map[i].s != NULL; ++i) {
2964*9664Sjason@ansipunx.net 			if (strncmp(type_map[i].s, p, 2) == 0)
2965*9664Sjason@ansipunx.net 				break;
2966*9664Sjason@ansipunx.net 		}
2967*9664Sjason@ansipunx.net 
2968*9664Sjason@ansipunx.net 		/* ignore unknown file types */
2969*9664Sjason@ansipunx.net 		if (type_map[i].s == NULL)
2970*9664Sjason@ansipunx.net 			return;
2971*9664Sjason@ansipunx.net 
2972*9664Sjason@ansipunx.net 		lsc_colors[lsc_ncolors].ftype = type_map[i].stype;
2973*9664Sjason@ansipunx.net 	}
2974*9664Sjason@ansipunx.net 
2975*9664Sjason@ansipunx.net 	attr = LSA_NONE;
2976*9664Sjason@ansipunx.net 	lsc_colors[lsc_ncolors].fg = -1;
2977*9664Sjason@ansipunx.net 	lsc_colors[lsc_ncolors].bg = -1;
2978*9664Sjason@ansipunx.net 	for (p = strtok_r(NULL, ";", &lasts); p != NULL;
2979*9664Sjason@ansipunx.net 	    p = strtok_r(NULL, ";", &lasts)) {
2980*9664Sjason@ansipunx.net 		color = strtol(p, NULL, 10);
2981*9664Sjason@ansipunx.net 
2982*9664Sjason@ansipunx.net 		if (color < 10) {
2983*9664Sjason@ansipunx.net 			switch (color) {
2984*9664Sjason@ansipunx.net 			case 0:
2985*9664Sjason@ansipunx.net 				attr = LSA_NONE;
2986*9664Sjason@ansipunx.net 				continue;
2987*9664Sjason@ansipunx.net 			case 1:
2988*9664Sjason@ansipunx.net 				attr |= LSA_BOLD;
2989*9664Sjason@ansipunx.net 				continue;
2990*9664Sjason@ansipunx.net 			case 4:
2991*9664Sjason@ansipunx.net 				attr |= LSA_UNDERSCORE;
2992*9664Sjason@ansipunx.net 				continue;
2993*9664Sjason@ansipunx.net 			case 5:
2994*9664Sjason@ansipunx.net 				attr |= LSA_BLINK;
2995*9664Sjason@ansipunx.net 				continue;
2996*9664Sjason@ansipunx.net 			case 7:
2997*9664Sjason@ansipunx.net 				attr |= LSA_REVERSE;
2998*9664Sjason@ansipunx.net 				continue;
2999*9664Sjason@ansipunx.net 			case 8:
3000*9664Sjason@ansipunx.net 				attr |= LSA_CONCEALED;
3001*9664Sjason@ansipunx.net 				continue;
3002*9664Sjason@ansipunx.net 			default:
3003*9664Sjason@ansipunx.net 				continue;
30045331Samw 			}
30055331Samw 		}
3006*9664Sjason@ansipunx.net 
3007*9664Sjason@ansipunx.net 		if (color < 40)
3008*9664Sjason@ansipunx.net 			lsc_colors[lsc_ncolors].fg = color - 30;
3009*9664Sjason@ansipunx.net 		else
3010*9664Sjason@ansipunx.net 			lsc_colors[lsc_ncolors].bg = color - 40;
3011*9664Sjason@ansipunx.net 	}
3012*9664Sjason@ansipunx.net 
3013*9664Sjason@ansipunx.net 	lsc_colors[lsc_ncolors].attr = attr;
3014*9664Sjason@ansipunx.net 	++lsc_ncolors;
3015*9664Sjason@ansipunx.net }
3016*9664Sjason@ansipunx.net 
3017*9664Sjason@ansipunx.net static int
3018*9664Sjason@ansipunx.net ls_color_compare(const void *p1, const void *p2)
3019*9664Sjason@ansipunx.net {
3020*9664Sjason@ansipunx.net 	const ls_color_t *c1 = (const ls_color_t *)p1;
3021*9664Sjason@ansipunx.net 	const ls_color_t *c2 = (const ls_color_t *)p2;
3022*9664Sjason@ansipunx.net 
3023*9664Sjason@ansipunx.net 	int ret = c1->ftype - c2->ftype;
3024*9664Sjason@ansipunx.net 
3025*9664Sjason@ansipunx.net 	if (ret != 0)
3026*9664Sjason@ansipunx.net 		return (ret);
3027*9664Sjason@ansipunx.net 
3028*9664Sjason@ansipunx.net 	if (c1->ftype != LS_PAT)
3029*9664Sjason@ansipunx.net 		return (ret);
3030*9664Sjason@ansipunx.net 
3031*9664Sjason@ansipunx.net 	return (strcmp(c1->sfx, c2->sfx));
3032*9664Sjason@ansipunx.net }
3033*9664Sjason@ansipunx.net 
3034*9664Sjason@ansipunx.net static void
3035*9664Sjason@ansipunx.net ls_color_init()
3036*9664Sjason@ansipunx.net {
3037*9664Sjason@ansipunx.net 	static char *default_colorstr = "no=00:fi=00:di=01;34:ln=01;36:po=01;35"
3038*9664Sjason@ansipunx.net 	    ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01"
3039*9664Sjason@ansipunx.net 	    ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31"
3040*9664Sjason@ansipunx.net 	    ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31"
3041*9664Sjason@ansipunx.net 	    ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31"
3042*9664Sjason@ansipunx.net 	    ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35"
3043*9664Sjason@ansipunx.net 	    ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"
3044*9664Sjason@ansipunx.net 	    ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35"
3045*9664Sjason@ansipunx.net 	    ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35"
3046*9664Sjason@ansipunx.net 	    ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35"
3047*9664Sjason@ansipunx.net 	    ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35";
3048*9664Sjason@ansipunx.net 
3049*9664Sjason@ansipunx.net 	char    *colorstr;
3050*9664Sjason@ansipunx.net 	char    *p, *lasts;
3051*9664Sjason@ansipunx.net 	size_t  color_sz;
3052*9664Sjason@ansipunx.net 	int	termret;
3053*9664Sjason@ansipunx.net 
3054*9664Sjason@ansipunx.net 	(void) setupterm(NULL, 1, &termret);
3055*9664Sjason@ansipunx.net 	if (termret != 1)
3056*9664Sjason@ansipunx.net 		return;
3057*9664Sjason@ansipunx.net 
3058*9664Sjason@ansipunx.net 	if ((colorstr = getenv("LS_COLORS")) == NULL)
3059*9664Sjason@ansipunx.net 		colorstr = default_colorstr;
3060*9664Sjason@ansipunx.net 
3061*9664Sjason@ansipunx.net 	color_sz = 0;
3062*9664Sjason@ansipunx.net 	for (p = strchr(colorstr, ':'); p != NULL && *p != '\0';
3063*9664Sjason@ansipunx.net 	    p = strchr(++p, ':'))
3064*9664Sjason@ansipunx.net 		++color_sz;
3065*9664Sjason@ansipunx.net 
3066*9664Sjason@ansipunx.net 	lsc_colors = calloc(color_sz, sizeof (ls_color_t));
3067*9664Sjason@ansipunx.net 	if (lsc_colors == NULL) {
3068*9664Sjason@ansipunx.net 		free(colorstr);
3069*9664Sjason@ansipunx.net 		return;
3070*9664Sjason@ansipunx.net 	}
3071*9664Sjason@ansipunx.net 
3072*9664Sjason@ansipunx.net 	for (p = strtok_r(colorstr, ":", &lasts);
3073*9664Sjason@ansipunx.net 	    p != NULL && lsc_ncolors < color_sz;
3074*9664Sjason@ansipunx.net 	    p = strtok_r(NULL, ":", &lasts))
3075*9664Sjason@ansipunx.net 		new_color_entry(p);
3076*9664Sjason@ansipunx.net 
3077*9664Sjason@ansipunx.net 	qsort((void *)lsc_colors, lsc_ncolors, sizeof (ls_color_t),
3078*9664Sjason@ansipunx.net 	    ls_color_compare);
3079*9664Sjason@ansipunx.net 
3080*9664Sjason@ansipunx.net 	if ((lsc_bold = tigetstr("bold")) == (char *)-1)
3081*9664Sjason@ansipunx.net 		lsc_bold = NULL;
3082*9664Sjason@ansipunx.net 
3083*9664Sjason@ansipunx.net 	if ((lsc_underline = tigetstr("smul")) == (char *)-1)
3084*9664Sjason@ansipunx.net 		lsc_underline = NULL;
3085*9664Sjason@ansipunx.net 
3086*9664Sjason@ansipunx.net 	if ((lsc_blink = tigetstr("blink")) == (char *)-1)
3087*9664Sjason@ansipunx.net 		lsc_blink = NULL;
3088*9664Sjason@ansipunx.net 
3089*9664Sjason@ansipunx.net 	if ((lsc_reverse = tigetstr("rev")) == (char *)-1)
3090*9664Sjason@ansipunx.net 		lsc_reverse = NULL;
3091*9664Sjason@ansipunx.net 
3092*9664Sjason@ansipunx.net 	if ((lsc_concealed = tigetstr("prot")) == (char *)-1)
3093*9664Sjason@ansipunx.net 		lsc_concealed = NULL;
3094*9664Sjason@ansipunx.net 
3095*9664Sjason@ansipunx.net 	if ((lsc_none = tigetstr("sgr0")) == (char *)-1)
3096*9664Sjason@ansipunx.net 		lsc_none = NULL;
3097*9664Sjason@ansipunx.net 
3098*9664Sjason@ansipunx.net 	if ((lsc_setfg = tigetstr("setaf")) == (char *)-1)
3099*9664Sjason@ansipunx.net 		lsc_setfg = NULL;
3100*9664Sjason@ansipunx.net 
3101*9664Sjason@ansipunx.net 	if ((lsc_setbg = tigetstr("setab")) == (char *)-1)
3102*9664Sjason@ansipunx.net 		lsc_setbg = NULL;
3103*9664Sjason@ansipunx.net 
3104*9664Sjason@ansipunx.net 	if (getenv("_LS_COLOR_DEBUG") != NULL) {
3105*9664Sjason@ansipunx.net 		int i;
3106*9664Sjason@ansipunx.net 
3107*9664Sjason@ansipunx.net 		lsc_debug = 1;
3108*9664Sjason@ansipunx.net 		for (i = 0; i < lsc_ncolors; ++i)
3109*9664Sjason@ansipunx.net 			dump_color(&lsc_colors[i]);
31105331Samw 	}
31115331Samw }
31125331Samw 
31135331Samw /* Free extended system attribute lists */
31145331Samw 
31155331Samw void
31165331Samw free_sysattr(struct lbuf *p)
31175331Samw {
31185331Samw 	int i;
31195331Samw 
31205331Samw 	if (p->exttr != NULL) {
31215331Samw 		for (i = 0; i < sacnt; i++) {
31225331Samw 			if (p->exttr[i].name != NULL)
31235331Samw 				free(p->exttr[i].name);
31245331Samw 		}
31255331Samw 		free(p->exttr);
31265331Samw 	}
31275331Samw 	if (p->extm != NULL) {
31285331Samw 		for (i = 0; i < sacnt; i++) {
31295331Samw 			if (p->extm[i].name != NULL)
31305331Samw 				free(p->extm[i].name);
31315331Samw 		}
31325331Samw 		free(p->extm);
31335331Samw 	}
31345331Samw }
31355331Samw 
31365331Samw /* Allocate extended system attribute list */
31375331Samw 
31385331Samw void *
31395331Samw xmalloc(size_t size, struct lbuf *p)
31405331Samw {
31415331Samw 	if ((p = malloc(size)) == NULL) {
31425331Samw 		perror("ls");
31435331Samw 		free_sysattr(p);
31445331Samw 		nvlist_free(response);
31455331Samw 		exit(2);
31465331Samw 	}
31475331Samw 	return (p);
31485331Samw }
3149