xref: /onnv-gate/usr/src/cmd/ls/ls.c (revision 12164:0eb8d6741e37)
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  */
219664Sjason@ansipunx.net 
220Sstevel@tonic-gate /*
23*12164SMark.Shellenbaum@Sun.COM  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
249664Sjason@ansipunx.net  */
259664Sjason@ansipunx.net 
269664Sjason@ansipunx.net /*
279664Sjason@ansipunx.net  * Copyright 2009 Jason King.  All rights reserved.
280Sstevel@tonic-gate  * Use is subject to license terms.
290Sstevel@tonic-gate  */
300Sstevel@tonic-gate 
310Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
320Sstevel@tonic-gate /*	  All Rights Reserved  	*/
330Sstevel@tonic-gate 
340Sstevel@tonic-gate /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
350Sstevel@tonic-gate /*	  All Rights Reserved	*/
360Sstevel@tonic-gate 
370Sstevel@tonic-gate /*
380Sstevel@tonic-gate  * List files or directories
390Sstevel@tonic-gate  */
400Sstevel@tonic-gate 
410Sstevel@tonic-gate #include <sys/param.h>
420Sstevel@tonic-gate #include <sys/types.h>
430Sstevel@tonic-gate #include <sys/mkdev.h>
440Sstevel@tonic-gate #include <sys/stat.h>
450Sstevel@tonic-gate #include <sys/acl.h>
460Sstevel@tonic-gate 
470Sstevel@tonic-gate #include <wchar.h>
480Sstevel@tonic-gate #include <stdio.h>
490Sstevel@tonic-gate #include <ctype.h>
500Sstevel@tonic-gate #include <dirent.h>
510Sstevel@tonic-gate #include <string.h>
520Sstevel@tonic-gate #include <locale.h>
530Sstevel@tonic-gate #include <curses.h>
549664Sjason@ansipunx.net #include <term.h>
550Sstevel@tonic-gate #include <termios.h>
560Sstevel@tonic-gate #include <stdlib.h>
570Sstevel@tonic-gate #include <widec.h>
580Sstevel@tonic-gate #include <locale.h>
590Sstevel@tonic-gate #include <wctype.h>
600Sstevel@tonic-gate #include <pwd.h>
610Sstevel@tonic-gate #include <grp.h>
620Sstevel@tonic-gate #include <limits.h>
630Sstevel@tonic-gate #include <fcntl.h>
640Sstevel@tonic-gate #include <unistd.h>
650Sstevel@tonic-gate #include <libgen.h>
660Sstevel@tonic-gate #include <errno.h>
67789Sahrens #include <aclutils.h>
685331Samw #include <libnvpair.h>
695331Samw #include <libcmdutils.h>
705331Samw #include <attr.h>
719664Sjason@ansipunx.net #include <getopt.h>
729664Sjason@ansipunx.net #include <inttypes.h>
730Sstevel@tonic-gate 
740Sstevel@tonic-gate #ifndef STANDALONE
750Sstevel@tonic-gate #define	TERMINFO
760Sstevel@tonic-gate #endif
770Sstevel@tonic-gate 
780Sstevel@tonic-gate /*
790Sstevel@tonic-gate  * -DNOTERMINFO can be defined on the cc command line to prevent
800Sstevel@tonic-gate  * the use of terminfo.  This should be done on systems not having
815331Samw  * the terminfo feature(pre 6.0 systems ?).
820Sstevel@tonic-gate  * As a result, columnar listings assume 80 columns for output,
830Sstevel@tonic-gate  * unless told otherwise via the COLUMNS environment variable.
840Sstevel@tonic-gate  */
850Sstevel@tonic-gate #ifdef NOTERMINFO
860Sstevel@tonic-gate #undef TERMINFO
870Sstevel@tonic-gate #endif
880Sstevel@tonic-gate 
890Sstevel@tonic-gate #include <term.h>
900Sstevel@tonic-gate 
910Sstevel@tonic-gate #define	BFSIZE	16
920Sstevel@tonic-gate /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */
930Sstevel@tonic-gate #define	ISARG	0100000
940Sstevel@tonic-gate 
950Sstevel@tonic-gate /*
960Sstevel@tonic-gate  * this flag has been added to manipulate the display of S instead of 'l' when
970Sstevel@tonic-gate  * the file is not a regular file and when group execution bit is off
980Sstevel@tonic-gate  */
990Sstevel@tonic-gate #define	LS_NOTREG	010000
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate /*
1030Sstevel@tonic-gate  * Date and time formats
1040Sstevel@tonic-gate  *
1050Sstevel@tonic-gate  * b --- abbreviated month name
1060Sstevel@tonic-gate  * e --- day number
1070Sstevel@tonic-gate  * Y --- year in the form ccyy
1080Sstevel@tonic-gate  * H --- hour(24-hour version)
1090Sstevel@tonic-gate  * M --- minute
1100Sstevel@tonic-gate  * F --- yyyy-mm-dd
1110Sstevel@tonic-gate  * T --- hh:mm:ss
1120Sstevel@tonic-gate  * z --- time zone as hours displacement from UTC
1130Sstevel@tonic-gate  * note that %F and %z are from the ISO C99 standard and are
1140Sstevel@tonic-gate  * not present in older C libraries
1150Sstevel@tonic-gate  */
1169664Sjason@ansipunx.net #define	FORMAT_OLD	" %b %e  %Y "
1179664Sjason@ansipunx.net #define	FORMAT_NEW	" %b %e %H:%M "
1189664Sjason@ansipunx.net #define	FORMAT_LONG	" %b %e %T %Y "
1199664Sjason@ansipunx.net #define	FORMAT_ISO_FULL	" %%F %%T.%.09ld %%z "
1209664Sjason@ansipunx.net #define	FORMAT_ISO_LONG	" %F %R "
1219664Sjason@ansipunx.net #define	FORMAT_ISO_NEW	" %m-%d %H:%M "
1229664Sjason@ansipunx.net #define	FORMAT_ISO_OLD	" %F "
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate #undef BUFSIZ
1250Sstevel@tonic-gate #define	BUFSIZ 4096
1260Sstevel@tonic-gate #define	NUMBER_WIDTH 40
1270Sstevel@tonic-gate #define	FMTSIZE 50
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate struct ditem {
1300Sstevel@tonic-gate 	dev_t	dev;			/* directory items device number */
1310Sstevel@tonic-gate 	ino_t	ino;			/* directory items inode number */
1320Sstevel@tonic-gate 	struct ditem *parent;		/* dir items ptr to its parent's info */
1330Sstevel@tonic-gate };
1345331Samw /* Holds boolean extended system attributes */
1355331Samw struct attrb {
1365331Samw 	char		*name;
1375331Samw };
1385331Samw /* Holds timestamp extended system attributes */
1395331Samw struct attrtm {
1405331Samw 	char		*name;
1415331Samw 	uint64_t	stm;
1425331Samw 	uint64_t	nstm;
1435331Samw };
1440Sstevel@tonic-gate 
1459664Sjason@ansipunx.net #define	LSA_NONE	(0)
1469664Sjason@ansipunx.net #define	LSA_BOLD	(1L << 0)
1479664Sjason@ansipunx.net #define	LSA_UNDERSCORE	(1L << 1)
1489664Sjason@ansipunx.net #define	LSA_BLINK	(1L << 2)
1499664Sjason@ansipunx.net #define	LSA_REVERSE	(1L << 3)
1509664Sjason@ansipunx.net #define	LSA_CONCEALED	(1L << 4)
1519664Sjason@ansipunx.net 
1529664Sjason@ansipunx.net /* these should be ordered most general to most specific */
1539664Sjason@ansipunx.net typedef enum LS_CFTYPE {
1549664Sjason@ansipunx.net 	LS_NORMAL,
1559664Sjason@ansipunx.net 	LS_FILE,
1569664Sjason@ansipunx.net 	LS_EXEC,
1579664Sjason@ansipunx.net 	LS_DIR,
1589664Sjason@ansipunx.net 	LS_LINK,
1599664Sjason@ansipunx.net 	LS_FIFO,
1609664Sjason@ansipunx.net 	LS_SOCK,
1619664Sjason@ansipunx.net 	LS_DOOR,
1629664Sjason@ansipunx.net 	LS_BLK,
1639664Sjason@ansipunx.net 	LS_CHR,
1649664Sjason@ansipunx.net 	LS_PORT,
1659664Sjason@ansipunx.net 	LS_STICKY,
1669664Sjason@ansipunx.net 	LS_ORPHAN,
1679664Sjason@ansipunx.net 	LS_SETGID,
1689664Sjason@ansipunx.net 	LS_SETUID,
1699664Sjason@ansipunx.net 	LS_OTHER_WRITABLE,
1709664Sjason@ansipunx.net 	LS_STICKY_OTHER_WRITABLE,
1719664Sjason@ansipunx.net 	LS_PAT
1729664Sjason@ansipunx.net } ls_cftype_t;
1739664Sjason@ansipunx.net 
17410059Sjason@ansipunx.net typedef struct {
1759664Sjason@ansipunx.net 	char		*sfx;
1769664Sjason@ansipunx.net 	ls_cftype_t	ftype;
1779664Sjason@ansipunx.net 	int		attr;
1789664Sjason@ansipunx.net 	int		fg;
1799664Sjason@ansipunx.net 	int		bg;
1809664Sjason@ansipunx.net } ls_color_t;
1819664Sjason@ansipunx.net 
18210059Sjason@ansipunx.net struct	lbuf	{
18310059Sjason@ansipunx.net 	union	{
18410059Sjason@ansipunx.net 		char	lname[MAXNAMLEN]; /* used for filename in a directory */
18510059Sjason@ansipunx.net 		char	*namep;		/* for name in ls-command; */
18610059Sjason@ansipunx.net 	} ln;
18710059Sjason@ansipunx.net 	char	ltype;		/* filetype */
18810059Sjason@ansipunx.net 	ino_t	lnum;		/* inode number of file */
18910059Sjason@ansipunx.net 	mode_t	lflags; 	/* 0777 bits used as r,w,x permissions */
19010059Sjason@ansipunx.net 	nlink_t	lnl;		/* number of links to file */
19110059Sjason@ansipunx.net 	uid_t	luid;
19210059Sjason@ansipunx.net 	gid_t	lgid;
19310059Sjason@ansipunx.net 	off_t	lsize;		/* filesize or major/minor dev numbers */
19410059Sjason@ansipunx.net 	blkcnt_t	lblocks;	/* number of file blocks */
19510059Sjason@ansipunx.net 	timestruc_t	lmtime;
19610059Sjason@ansipunx.net 	timestruc_t	lat;
19710059Sjason@ansipunx.net 	timestruc_t	lct;
19810059Sjason@ansipunx.net 	timestruc_t	lmt;
19910059Sjason@ansipunx.net 	char	*flinkto;	/* symbolic link contents */
20010059Sjason@ansipunx.net 	char 	acl;		/* indicate there are additional acl entries */
20110059Sjason@ansipunx.net 	int	cycle;		/* cycle detected flag */
20210059Sjason@ansipunx.net 	struct ditem *ancinfo;	/* maintains ancestor info */
20310059Sjason@ansipunx.net 	acl_t *aclp;		/* ACL if present */
20410059Sjason@ansipunx.net 	struct attrb *exttr;	/* boolean extended system attributes */
20510059Sjason@ansipunx.net 	struct attrtm *extm;	/* timestamp extended system attributes */
20610059Sjason@ansipunx.net 	ls_color_t	*color;	/* color for entry */
20710059Sjason@ansipunx.net 	ls_color_t	*link_color;	/* color for symlink */
20810059Sjason@ansipunx.net };
20910059Sjason@ansipunx.net 
21010059Sjason@ansipunx.net struct dchain {
21110059Sjason@ansipunx.net 	char *dc_name;		/* path name */
21210059Sjason@ansipunx.net 	int cycle_detected;	/* cycle detected visiting this directory */
21310059Sjason@ansipunx.net 	struct ditem *myancinfo;	/* this directory's ancestry info */
21410059Sjason@ansipunx.net 	struct dchain *dc_next;	/* next directory in the chain */
21510059Sjason@ansipunx.net };
21610059Sjason@ansipunx.net 
2170Sstevel@tonic-gate /*
2180Sstevel@tonic-gate  * A numbuf_t is used when converting a number to a string representation
2190Sstevel@tonic-gate  */
2200Sstevel@tonic-gate typedef char numbuf_t[NUMBER_WIDTH];
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate static struct dchain *dfirst;	/* start of the dir chain */
2230Sstevel@tonic-gate static struct dchain *cdfirst;	/* start of the current dir chain */
2240Sstevel@tonic-gate static struct dchain *dtemp;	/* temporary - used for linking */
2250Sstevel@tonic-gate static char *curdir;		/* the current directory */
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate static int	first = 1;	/* true if first line is not yet printed */
2280Sstevel@tonic-gate static int	nfiles = 0;	/* number of flist entries in current use */
2290Sstevel@tonic-gate static int	nargs = 0;	/* number of flist entries used for arguments */
2300Sstevel@tonic-gate static int	maxfils = 0;	/* number of flist/lbuf entries allocated */
2310Sstevel@tonic-gate static int	maxn = 0;	/* number of flist entries with lbufs asigned */
2320Sstevel@tonic-gate static int	quantn = 64;	/* allocation growth quantum */
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate static struct lbuf	*nxtlbf;	/* ptr to next lbuf to be assigned */
2350Sstevel@tonic-gate static struct lbuf	**flist;	/* ptr to list of lbuf pointers */
2360Sstevel@tonic-gate static struct lbuf	*gstat(char *, int, struct ditem *);
2370Sstevel@tonic-gate static char		*getname(uid_t);
2380Sstevel@tonic-gate static char		*getgroup(gid_t);
2390Sstevel@tonic-gate static char		*makename(char *, char *);
2400Sstevel@tonic-gate static void		pentry(struct lbuf *);
2410Sstevel@tonic-gate static void		column(void);
2420Sstevel@tonic-gate static void		pmode(mode_t aflag);
2430Sstevel@tonic-gate static void		selection(int *);
2440Sstevel@tonic-gate static void		new_line(void);
2450Sstevel@tonic-gate static void		rddir(char *, struct ditem *);
2460Sstevel@tonic-gate static int		strcol(unsigned char *);
2470Sstevel@tonic-gate static void		pem(struct lbuf **, struct lbuf **, int);
2480Sstevel@tonic-gate static void		pdirectory(char *, int, int, int, struct ditem *);
2490Sstevel@tonic-gate static struct cachenode *findincache(struct cachenode **, long);
2500Sstevel@tonic-gate static void		csi_pprintf(unsigned char *);
2510Sstevel@tonic-gate static void		pprintf(char *, char *);
2520Sstevel@tonic-gate static int		compar(struct lbuf **pp1, struct lbuf **pp2);
2530Sstevel@tonic-gate static char 		*number_to_scaled_string(numbuf_t buf,
2540Sstevel@tonic-gate 			    unsigned long long number,
2550Sstevel@tonic-gate 			    long scale);
2560Sstevel@tonic-gate static void		record_ancestry(char *, struct stat *, struct lbuf *,
2570Sstevel@tonic-gate 			    int, struct ditem *);
2589664Sjason@ansipunx.net static void		ls_color_init(void);
25910059Sjason@ansipunx.net static ls_color_t	*ls_color_find(const char *, mode_t);
26010059Sjason@ansipunx.net static void		ls_start_color(ls_color_t *);
2619664Sjason@ansipunx.net static void		ls_end_color(void);
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate static int		aflg;
2640Sstevel@tonic-gate static int		atflg;
2650Sstevel@tonic-gate static int		bflg;
2660Sstevel@tonic-gate static int		cflg;
2670Sstevel@tonic-gate static int		dflg;
2680Sstevel@tonic-gate static int		eflg;
2690Sstevel@tonic-gate static int		fflg;
2700Sstevel@tonic-gate static int		gflg;
2710Sstevel@tonic-gate static int		hflg;
2720Sstevel@tonic-gate static int		iflg;
2730Sstevel@tonic-gate static int		lflg;
2740Sstevel@tonic-gate static int		mflg;
2750Sstevel@tonic-gate static int		nflg;
2760Sstevel@tonic-gate static int		oflg;
2770Sstevel@tonic-gate static int		pflg;
2780Sstevel@tonic-gate static int		qflg;
2790Sstevel@tonic-gate static int		rflg = 1; /* init to 1 for special use in compar */
2800Sstevel@tonic-gate static int		sflg;
2810Sstevel@tonic-gate static int		tflg;
2820Sstevel@tonic-gate static int		uflg;
2839664Sjason@ansipunx.net static int		Uflg;
2849664Sjason@ansipunx.net static int		wflg;
2850Sstevel@tonic-gate static int		xflg;
2860Sstevel@tonic-gate static int		Aflg;
2879664Sjason@ansipunx.net static int		Bflg;
2880Sstevel@tonic-gate static int		Cflg;
2890Sstevel@tonic-gate static int		Eflg;
2900Sstevel@tonic-gate static int		Fflg;
2910Sstevel@tonic-gate static int		Hflg;
2920Sstevel@tonic-gate static int		Lflg;
2930Sstevel@tonic-gate static int		Rflg;
2940Sstevel@tonic-gate static int		Sflg;
295789Sahrens static int		vflg;
2961420Smarks static int		Vflg;
2975331Samw static int		saflg;		/* boolean extended system attr. */
2985331Samw static int		sacnt;		/* number of extended system attr. */
2995331Samw static int		copt;
3005331Samw static int		vopt;
3015331Samw static int		tmflg;		/* create time ext. system attr. */
3025331Samw static int		ctm;
3035331Samw static int		atm;
3045331Samw static int		mtm;
3055331Samw static int		crtm;
3065331Samw static int		alltm;
3070Sstevel@tonic-gate static long		hscale;
3080Sstevel@tonic-gate static mode_t		flags;
3090Sstevel@tonic-gate static int		err = 0;	/* Contains return code */
3109664Sjason@ansipunx.net static int		colorflg;
3119664Sjason@ansipunx.net static int		file_typeflg;
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate static uid_t		lastuid	= (uid_t)-1;
3140Sstevel@tonic-gate static gid_t		lastgid = (gid_t)-1;
3150Sstevel@tonic-gate static char		*lastuname = NULL;
3160Sstevel@tonic-gate static char		*lastgname = NULL;
3170Sstevel@tonic-gate 
3189664Sjason@ansipunx.net /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg, colorflg are on */
3190Sstevel@tonic-gate static int		statreq;
3200Sstevel@tonic-gate 
3219664Sjason@ansipunx.net static uint64_t		block_size = 1;
3220Sstevel@tonic-gate static char		*dotp = ".";
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate static u_longlong_t 	tblocks; /* number of blocks of files in a directory */
3250Sstevel@tonic-gate static time_t		year, now;
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate static int		num_cols = 80;
3280Sstevel@tonic-gate static int		colwidth;
3290Sstevel@tonic-gate static int		filewidth;
3300Sstevel@tonic-gate static int		fixedwidth;
3310Sstevel@tonic-gate static int		nomocore;
3320Sstevel@tonic-gate static int		curcol;
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate static struct	winsize	win;
3350Sstevel@tonic-gate 
3369664Sjason@ansipunx.net /* if time_fmt_new is left NULL, time_fmt_old is used for all times */
3379664Sjason@ansipunx.net static const char	*time_fmt_old = FORMAT_OLD;	/* non-recent files */
3389664Sjason@ansipunx.net static const char	*time_fmt_new = FORMAT_NEW;	/* recent files */
3399664Sjason@ansipunx.net static int		time_custom;	/* != 0 if a custom format */
3405331Samw static char	time_buf[FMTSIZE];	/* array to hold day and time */
3410Sstevel@tonic-gate 
3429664Sjason@ansipunx.net static int		lsc_debug;
3439664Sjason@ansipunx.net static ls_color_t	*lsc_match;
3449664Sjason@ansipunx.net static ls_color_t	*lsc_colors;
3459664Sjason@ansipunx.net static size_t		lsc_ncolors;
3469664Sjason@ansipunx.net static char		*lsc_bold;
3479664Sjason@ansipunx.net static char		*lsc_underline;
3489664Sjason@ansipunx.net static char		*lsc_blink;
3499664Sjason@ansipunx.net static char		*lsc_reverse;
3509664Sjason@ansipunx.net static char		*lsc_concealed;
3519664Sjason@ansipunx.net static char		*lsc_none;
3529664Sjason@ansipunx.net static char		*lsc_setfg;
3539664Sjason@ansipunx.net static char		*lsc_setbg;
35410059Sjason@ansipunx.net static ls_color_t	*lsc_orphan;
3559664Sjason@ansipunx.net 
3560Sstevel@tonic-gate #define	NOTWORKINGDIR(d, l)	(((l) < 2) || \
3570Sstevel@tonic-gate 				    (strcmp((d) + (l) - 2, "/.") != 0))
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate #define	NOTPARENTDIR(d, l)	(((l) < 3) || \
3600Sstevel@tonic-gate 				    (strcmp((d) + (l) - 3, "/..") != 0))
3615331Samw /* Extended system attributes support */
3625331Samw static int get_sysxattr(char *, struct lbuf *);
3635331Samw static void set_sysattrb_display(char *, boolean_t, struct lbuf *);
3645331Samw static void set_sysattrtm_display(char *, struct lbuf *);
3659664Sjason@ansipunx.net static void format_time(time_t, time_t);
3665331Samw static void print_time(struct lbuf *);
3675331Samw static void format_attrtime(struct lbuf *);
3685331Samw static void *xmalloc(size_t, struct lbuf *);
3695331Samw static void free_sysattr(struct lbuf *);
3705331Samw static nvpair_t *pair;
3715331Samw static nvlist_t	*response;
3726866Sbasabi static int acl_err;
3730Sstevel@tonic-gate 
3749664Sjason@ansipunx.net const struct option long_options[] = {
3759664Sjason@ansipunx.net 	{ "all", no_argument, NULL, 'a' },
3769664Sjason@ansipunx.net 	{ "almost-all", no_argument, NULL, 'A' },
3779664Sjason@ansipunx.net 	{ "escape", no_argument, NULL, 'b' },
3789664Sjason@ansipunx.net 	{ "classify", no_argument, NULL, 'F' },
3799664Sjason@ansipunx.net 	{ "human-readable", no_argument, NULL, 'h' },
3809664Sjason@ansipunx.net 	{ "dereference", no_argument, NULL, 'L' },
3819664Sjason@ansipunx.net 	{ "dereference-command-line", no_argument, NULL, 'H' },
3829664Sjason@ansipunx.net 	{ "ignore-backups", no_argument, NULL, 'B' },
3839664Sjason@ansipunx.net 	{ "inode", no_argument, NULL, 'i' },
3849664Sjason@ansipunx.net 	{ "numeric-uid-gid", no_argument, NULL, 'n' },
3859664Sjason@ansipunx.net 	{ "no-group", no_argument, NULL, 'o' },
3869664Sjason@ansipunx.net 	{ "hide-control-chars", no_argument, NULL, 'q' },
3879664Sjason@ansipunx.net 	{ "reverse", no_argument, NULL, 'r' },
3889664Sjason@ansipunx.net 	{ "recursive", no_argument, NULL, 'R' },
3899664Sjason@ansipunx.net 	{ "size", no_argument, NULL, 's' },
3909664Sjason@ansipunx.net 	{ "width", required_argument, NULL, 'w' },
3919664Sjason@ansipunx.net 
3929664Sjason@ansipunx.net 	/* no short options for these */
3939664Sjason@ansipunx.net 	{ "block-size", required_argument, NULL, 0 },
3949664Sjason@ansipunx.net 	{ "full-time", no_argument, NULL, 0 },
3959664Sjason@ansipunx.net 	{ "si", no_argument, NULL, 0 },
3969664Sjason@ansipunx.net 	{ "color", optional_argument, NULL, 0 },
3979664Sjason@ansipunx.net 	{ "colour", optional_argument, NULL, 0},
3989664Sjason@ansipunx.net 	{ "file-type", no_argument, NULL, 0 },
3999664Sjason@ansipunx.net 	{ "time-style", required_argument, NULL, 0 },
4009664Sjason@ansipunx.net 
4019664Sjason@ansipunx.net 	{0, 0, 0, 0}
4029664Sjason@ansipunx.net };
4039664Sjason@ansipunx.net 
4040Sstevel@tonic-gate int
main(int argc,char * argv[])4050Sstevel@tonic-gate main(int argc, char *argv[])
4060Sstevel@tonic-gate {
4070Sstevel@tonic-gate 	int		c;
4080Sstevel@tonic-gate 	int		i;
4090Sstevel@tonic-gate 	int		width;
4100Sstevel@tonic-gate 	int		amino = 0;
4110Sstevel@tonic-gate 	int		opterr = 0;
4129664Sjason@ansipunx.net 	int		option_index = 0;
4130Sstevel@tonic-gate 	struct lbuf	*ep;
4140Sstevel@tonic-gate 	struct lbuf	lb;
4150Sstevel@tonic-gate 	struct ditem	*myinfo;
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
4180Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
4190Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
4200Sstevel@tonic-gate #endif
4210Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
4220Sstevel@tonic-gate #ifdef STANDALONE
4230Sstevel@tonic-gate 	if (argv[0][0] == '\0')
4240Sstevel@tonic-gate 		argc = getargv("ls", &argv, 0);
4250Sstevel@tonic-gate #endif
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	lb.lmtime.tv_sec = time(NULL);
4280Sstevel@tonic-gate 	lb.lmtime.tv_nsec = 0;
4290Sstevel@tonic-gate 	year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */
4300Sstevel@tonic-gate 	now = lb.lmtime.tv_sec + 60;
4310Sstevel@tonic-gate 	if (isatty(1)) {
4320Sstevel@tonic-gate 		Cflg = 1;
4330Sstevel@tonic-gate 		mflg = 0;
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 
4369664Sjason@ansipunx.net 	while ((c = getopt_long(argc, argv,
4379664Sjason@ansipunx.net 	    "+aAbBcCdeEfFghHiklLmnopqrRsStuUw:x1@vV/:%:", long_options,
4389664Sjason@ansipunx.net 	    &option_index)) != -1)
4390Sstevel@tonic-gate 		switch (c) {
4409664Sjason@ansipunx.net 		case 0:
4419664Sjason@ansipunx.net 			/* non-short options */
4429664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
4439664Sjason@ansipunx.net 			    "color") == 0 ||
4449664Sjason@ansipunx.net 			    strcmp(long_options[option_index].name,
4459664Sjason@ansipunx.net 			    "colour") == 0) {
4469664Sjason@ansipunx.net 				if (optarg == NULL ||
4479664Sjason@ansipunx.net 				    strcmp(optarg, "always") == 0 ||
4489664Sjason@ansipunx.net 				    strcmp(optarg, "yes") == 0 ||
4499664Sjason@ansipunx.net 				    strcmp(optarg, "force") == 0) {
4509664Sjason@ansipunx.net 					colorflg++;
4519664Sjason@ansipunx.net 					statreq++;
4529664Sjason@ansipunx.net 					continue;
4539664Sjason@ansipunx.net 				}
4549664Sjason@ansipunx.net 
45510456Sjason@ansipunx.net 				if (strcmp(optarg, "auto") == 0 ||
4569664Sjason@ansipunx.net 				    strcmp(optarg, "tty") == 0 ||
45710456Sjason@ansipunx.net 				    strcmp(optarg, "if-tty") == 0) {
45810456Sjason@ansipunx.net 					if (isatty(1) == 1) {
45910456Sjason@ansipunx.net 						colorflg++;
46010456Sjason@ansipunx.net 						statreq++;
46110456Sjason@ansipunx.net 					}
4629664Sjason@ansipunx.net 					continue;
4639664Sjason@ansipunx.net 				}
4649664Sjason@ansipunx.net 
4659664Sjason@ansipunx.net 				if (strcmp(optarg, "never") == 0 ||
4669664Sjason@ansipunx.net 				    strcmp(optarg, "no") == 0 ||
4679664Sjason@ansipunx.net 				    strcmp(optarg, "none") == 0) {
4689664Sjason@ansipunx.net 					colorflg = 0;
4699664Sjason@ansipunx.net 					continue;
4709664Sjason@ansipunx.net 				}
4719664Sjason@ansipunx.net 				(void) fprintf(stderr,
4729664Sjason@ansipunx.net 				    gettext("Invalid argument '%s' for "
4739664Sjason@ansipunx.net 				    "--color\n"), optarg);
4749664Sjason@ansipunx.net 				++opterr;
4759664Sjason@ansipunx.net 				continue;
4769664Sjason@ansipunx.net 			}
4779664Sjason@ansipunx.net 
4789664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
4799664Sjason@ansipunx.net 			    "si") == 0) {
4809664Sjason@ansipunx.net 				hflg++;
4819664Sjason@ansipunx.net 				hscale = 1000;
4829664Sjason@ansipunx.net 				continue;
4839664Sjason@ansipunx.net 			}
4849664Sjason@ansipunx.net 
4859664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
4869664Sjason@ansipunx.net 			    "block-size") == 0) {
4879664Sjason@ansipunx.net 				size_t scale_len = strlen(optarg);
4889664Sjason@ansipunx.net 				uint64_t scale = 1;
4899664Sjason@ansipunx.net 				uint64_t kilo = 1024;
4909664Sjason@ansipunx.net 				char scale_c;
4919664Sjason@ansipunx.net 
4929664Sjason@ansipunx.net 				if (scale_len == 0) {
4939664Sjason@ansipunx.net 					(void) fprintf(stderr, gettext(
4949664Sjason@ansipunx.net 					    "Invalid block size \'%s\'\n"),
4959664Sjason@ansipunx.net 					    optarg);
4969664Sjason@ansipunx.net 					exit(1);
4979664Sjason@ansipunx.net 				}
4989664Sjason@ansipunx.net 
4999664Sjason@ansipunx.net 				scale_c = optarg[scale_len - 1];
5009664Sjason@ansipunx.net 				if (scale_c == 'B') {
5019664Sjason@ansipunx.net 					/* need at least digit, scale, B */
5029664Sjason@ansipunx.net 					if (scale_len < 3) {
5039664Sjason@ansipunx.net 						(void) fprintf(stderr, gettext(
5049664Sjason@ansipunx.net 						    "Invalid block size "
5059664Sjason@ansipunx.net 						    "\'%s\'\n"), optarg);
5069664Sjason@ansipunx.net 						exit(1);
5079664Sjason@ansipunx.net 					}
5089664Sjason@ansipunx.net 					kilo = 1000;
5099664Sjason@ansipunx.net 					scale_c = optarg[scale_len - 2];
5109664Sjason@ansipunx.net 					if (isdigit(scale_c)) {
5119664Sjason@ansipunx.net 						(void) fprintf(stderr,
5129664Sjason@ansipunx.net 						    gettext("Invalid block size"
5139664Sjason@ansipunx.net 						    " \'%s\'\n"), optarg);
5149664Sjason@ansipunx.net 						exit(1);
5159664Sjason@ansipunx.net 					}
5169664Sjason@ansipunx.net 					/*
5179664Sjason@ansipunx.net 					 * make optarg[scale_len - 1] point to
5189664Sjason@ansipunx.net 					 * the scale factor
5199664Sjason@ansipunx.net 					 */
5209664Sjason@ansipunx.net 					--scale_len;
5219664Sjason@ansipunx.net 				}
5229664Sjason@ansipunx.net 
5239664Sjason@ansipunx.net 				switch (scale_c) {
5249664Sjason@ansipunx.net 				case 'y':
5259664Sjason@ansipunx.net 				case 'Y':
5269664Sjason@ansipunx.net 					scale *= kilo;
5279664Sjason@ansipunx.net 					/*FALLTHROUGH*/
5289664Sjason@ansipunx.net 				case 'Z':
5299664Sjason@ansipunx.net 				case 'z':
5309664Sjason@ansipunx.net 					scale *= kilo;
5319664Sjason@ansipunx.net 					/*FALLTHROUGH*/
5329664Sjason@ansipunx.net 				case 'E':
5339664Sjason@ansipunx.net 				case 'e':
5349664Sjason@ansipunx.net 					scale *= kilo;
5359664Sjason@ansipunx.net 					/*FALLTHROUGH*/
5369664Sjason@ansipunx.net 				case 'P':
5379664Sjason@ansipunx.net 				case 'p':
5389664Sjason@ansipunx.net 					scale *= kilo;
5399664Sjason@ansipunx.net 					/*FALLTHROUGH*/
5409664Sjason@ansipunx.net 				case 'T':
5419664Sjason@ansipunx.net 				case 't':
5429664Sjason@ansipunx.net 					scale *= kilo;
5439664Sjason@ansipunx.net 					/*FALLTHROUGH*/
5449664Sjason@ansipunx.net 				case 'G':
5459664Sjason@ansipunx.net 				case 'g':
5469664Sjason@ansipunx.net 					scale *= kilo;
5479664Sjason@ansipunx.net 					/*FALLTHROUGH*/
5489664Sjason@ansipunx.net 				case 'M':
5499664Sjason@ansipunx.net 				case 'm':
5509664Sjason@ansipunx.net 					scale *= kilo;
5519664Sjason@ansipunx.net 					/*FALLTHROUGH*/
5529664Sjason@ansipunx.net 				case 'K':
5539664Sjason@ansipunx.net 				case 'k':
5549664Sjason@ansipunx.net 					scale *= kilo;
5559664Sjason@ansipunx.net 					break;
5569664Sjason@ansipunx.net 				default:
5579664Sjason@ansipunx.net 					if (!isdigit(scale_c)) {
5589664Sjason@ansipunx.net 						(void) fprintf(stderr,
5599664Sjason@ansipunx.net 						    gettext("Invalid character "
5609664Sjason@ansipunx.net 						    "following block size in "
5619664Sjason@ansipunx.net 						    "\'%s\'\n"), optarg);
5629664Sjason@ansipunx.net 						exit(1);
5639664Sjason@ansipunx.net 					}
5649664Sjason@ansipunx.net 				}
5659664Sjason@ansipunx.net 
5669664Sjason@ansipunx.net 				/* NULL out scale constant if present */
5679664Sjason@ansipunx.net 				if (scale > 1 && !isdigit(scale_c))
5689664Sjason@ansipunx.net 					optarg[scale_len - 1] = '\0';
5699664Sjason@ansipunx.net 
5709664Sjason@ansipunx.net 				/* Based on testing, this is what GNU ls does */
5719664Sjason@ansipunx.net 				block_size = strtoll(optarg, NULL, 0) * scale;
5729664Sjason@ansipunx.net 				if (block_size < 1) {
5739664Sjason@ansipunx.net 					(void) fprintf(stderr,
5749664Sjason@ansipunx.net 					    gettext("Invalid block size "
5759664Sjason@ansipunx.net 					    "\'%s\'\n"), optarg);
5769664Sjason@ansipunx.net 					exit(1);
5779664Sjason@ansipunx.net 				}
5789664Sjason@ansipunx.net 				continue;
5799664Sjason@ansipunx.net 			}
5809664Sjason@ansipunx.net 
5819664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
5829664Sjason@ansipunx.net 			    "file-type") == 0) {
5839664Sjason@ansipunx.net 				file_typeflg++;
5849664Sjason@ansipunx.net 				Fflg++;
5859664Sjason@ansipunx.net 				statreq++;
5869664Sjason@ansipunx.net 				continue;
5879664Sjason@ansipunx.net 			}
5889664Sjason@ansipunx.net 
5899664Sjason@ansipunx.net 
5909664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
5919664Sjason@ansipunx.net 			    "full-time") == 0) {
5929664Sjason@ansipunx.net 				Eflg++;
5939664Sjason@ansipunx.net 				statreq++;
5949664Sjason@ansipunx.net 				eflg = 0;
5959664Sjason@ansipunx.net 				time_fmt_old = FORMAT_ISO_FULL;
5969664Sjason@ansipunx.net 				time_fmt_new = FORMAT_ISO_FULL;
5979664Sjason@ansipunx.net 				continue;
5989664Sjason@ansipunx.net 			}
5999664Sjason@ansipunx.net 
6009664Sjason@ansipunx.net 			if (strcmp(long_options[option_index].name,
6019664Sjason@ansipunx.net 			    "time-style") == 0) {
6029664Sjason@ansipunx.net 				/* like -E, but doesn't imply -l */
6039664Sjason@ansipunx.net 				if (strcmp(optarg, "full-iso") == 0) {
6049664Sjason@ansipunx.net 					Eflg++;
6059664Sjason@ansipunx.net 					statreq++;
6069664Sjason@ansipunx.net 					eflg = 0;
6079664Sjason@ansipunx.net 					time_fmt_old = FORMAT_ISO_FULL;
6089664Sjason@ansipunx.net 					time_fmt_new = FORMAT_ISO_FULL;
6099664Sjason@ansipunx.net 					continue;
6109664Sjason@ansipunx.net 				}
6119664Sjason@ansipunx.net 				if (strcmp(optarg, "long-iso") == 0) {
6129664Sjason@ansipunx.net 					statreq++;
6139664Sjason@ansipunx.net 					Eflg = 0;
6149664Sjason@ansipunx.net 					eflg = 0;
6159664Sjason@ansipunx.net 					time_fmt_old = FORMAT_ISO_LONG;
6169664Sjason@ansipunx.net 					time_fmt_new = FORMAT_ISO_LONG;
6179664Sjason@ansipunx.net 					continue;
6189664Sjason@ansipunx.net 				}
6199664Sjason@ansipunx.net 				if (strcmp(optarg, "iso") == 0) {
6209664Sjason@ansipunx.net 					statreq++;
6219664Sjason@ansipunx.net 					Eflg = 0;
6229664Sjason@ansipunx.net 					eflg = 0;
6239664Sjason@ansipunx.net 					time_fmt_old = FORMAT_ISO_OLD;
6249664Sjason@ansipunx.net 					time_fmt_new = FORMAT_ISO_NEW;
6259664Sjason@ansipunx.net 					continue;
6269664Sjason@ansipunx.net 				}
6279664Sjason@ansipunx.net 				/* should be the default */
6289664Sjason@ansipunx.net 				if (strcmp(optarg, "locale") == 0) {
6299664Sjason@ansipunx.net 					time_fmt_old = FORMAT_OLD;
6309664Sjason@ansipunx.net 					time_fmt_new = FORMAT_NEW;
6319664Sjason@ansipunx.net 					continue;
6329664Sjason@ansipunx.net 				}
6339664Sjason@ansipunx.net 				if (optarg[0] == '+') {
6349664Sjason@ansipunx.net 					char	*told, *tnew;
6359664Sjason@ansipunx.net 					char	*p;
6369664Sjason@ansipunx.net 					size_t	timelen = strlen(optarg);
6379664Sjason@ansipunx.net 
6389664Sjason@ansipunx.net 					p = strchr(optarg, '\n');
6399664Sjason@ansipunx.net 					if (p != NULL)
6409664Sjason@ansipunx.net 						*p++ = '\0';
6419664Sjason@ansipunx.net 
6429664Sjason@ansipunx.net 					/*
6439664Sjason@ansipunx.net 					 * Time format requires a leading and
6449664Sjason@ansipunx.net 					 * trailing space
6459664Sjason@ansipunx.net 					 * Add room for 3 spaces + 2 nulls
6469664Sjason@ansipunx.net 					 * The + in optarg is replaced with
6479664Sjason@ansipunx.net 					 * a space.
6489664Sjason@ansipunx.net 					 */
6499664Sjason@ansipunx.net 					timelen += 2 + 3;
6509664Sjason@ansipunx.net 					told = malloc(timelen);
6519664Sjason@ansipunx.net 					if (told == NULL) {
6529664Sjason@ansipunx.net 						perror("Out of memory");
6539664Sjason@ansipunx.net 						exit(1);
6549664Sjason@ansipunx.net 					}
6559664Sjason@ansipunx.net 
6569664Sjason@ansipunx.net 					(void) memset(told, 0, timelen);
6579664Sjason@ansipunx.net 					told[0] = ' ';
6589664Sjason@ansipunx.net 					(void) strlcat(told, &optarg[1],
6599664Sjason@ansipunx.net 					    timelen);
6609664Sjason@ansipunx.net 					(void) strlcat(told, " ", timelen);
6619664Sjason@ansipunx.net 
6629664Sjason@ansipunx.net 					if (p != NULL) {
6639664Sjason@ansipunx.net 						size_t tnew_len;
6649664Sjason@ansipunx.net 
6659664Sjason@ansipunx.net 						tnew = told + strlen(told) + 1;
6669664Sjason@ansipunx.net 						tnew_len = timelen -
6679664Sjason@ansipunx.net 						    strlen(told) - 1;
6689664Sjason@ansipunx.net 
6699664Sjason@ansipunx.net 						tnew[0] = ' ';
6709664Sjason@ansipunx.net 						(void) strlcat(tnew, p,
6719664Sjason@ansipunx.net 						    tnew_len);
6729664Sjason@ansipunx.net 						(void) strlcat(tnew, " ",
6739664Sjason@ansipunx.net 						    tnew_len);
6749664Sjason@ansipunx.net 						time_fmt_new =
6759664Sjason@ansipunx.net 						    (const char *)tnew;
6769664Sjason@ansipunx.net 					} else {
6779664Sjason@ansipunx.net 						time_fmt_new =
6789664Sjason@ansipunx.net 						    (const char *)told;
6799664Sjason@ansipunx.net 					}
6809664Sjason@ansipunx.net 
6819664Sjason@ansipunx.net 					time_fmt_old = (const char *)told;
6829664Sjason@ansipunx.net 					time_custom = 1;
6839664Sjason@ansipunx.net 					continue;
6849664Sjason@ansipunx.net 				}
6859664Sjason@ansipunx.net 				continue;
6869664Sjason@ansipunx.net 			}
6879664Sjason@ansipunx.net 
6889664Sjason@ansipunx.net 			continue;
6899664Sjason@ansipunx.net 
6900Sstevel@tonic-gate 		case 'a':
6910Sstevel@tonic-gate 			aflg++;
6920Sstevel@tonic-gate 			continue;
6930Sstevel@tonic-gate 		case 'A':
6940Sstevel@tonic-gate 			Aflg++;
6950Sstevel@tonic-gate 			continue;
6960Sstevel@tonic-gate 		case 'b':
6970Sstevel@tonic-gate 			bflg = 1;
6980Sstevel@tonic-gate 			qflg = 0;
6990Sstevel@tonic-gate 			continue;
7009664Sjason@ansipunx.net 		case 'B':
7019664Sjason@ansipunx.net 			Bflg = 1;
7029664Sjason@ansipunx.net 			continue;
7030Sstevel@tonic-gate 		case 'c':
7040Sstevel@tonic-gate 			uflg = 0;
7055331Samw 			atm = 0;
7065331Samw 			ctm = 0;
7075331Samw 			mtm = 0;
7085331Samw 			crtm = 0;
7090Sstevel@tonic-gate 			cflg++;
7100Sstevel@tonic-gate 			continue;
7110Sstevel@tonic-gate 		case 'C':
7120Sstevel@tonic-gate 			Cflg = 1;
7130Sstevel@tonic-gate 			mflg = 0;
7140Sstevel@tonic-gate #ifdef XPG4
7150Sstevel@tonic-gate 			lflg = 0;
7160Sstevel@tonic-gate #endif
7170Sstevel@tonic-gate 			continue;
7180Sstevel@tonic-gate 		case 'd':
7190Sstevel@tonic-gate 			dflg++;
7200Sstevel@tonic-gate 			continue;
7210Sstevel@tonic-gate 		case 'e':
7220Sstevel@tonic-gate 			eflg++;
7230Sstevel@tonic-gate 			lflg++;
7240Sstevel@tonic-gate 			statreq++;
7250Sstevel@tonic-gate 			Eflg = 0;
7269664Sjason@ansipunx.net 			time_fmt_old = FORMAT_LONG;
7279664Sjason@ansipunx.net 			time_fmt_new = FORMAT_LONG;
7280Sstevel@tonic-gate 			continue;
7290Sstevel@tonic-gate 		case 'E':
7300Sstevel@tonic-gate 			Eflg++;
7310Sstevel@tonic-gate 			lflg++;
7320Sstevel@tonic-gate 			statreq++;
7330Sstevel@tonic-gate 			eflg = 0;
7349664Sjason@ansipunx.net 			time_fmt_old = FORMAT_ISO_FULL;
7359664Sjason@ansipunx.net 			time_fmt_new = FORMAT_ISO_FULL;
7360Sstevel@tonic-gate 			continue;
7370Sstevel@tonic-gate 		case 'f':
7380Sstevel@tonic-gate 			fflg++;
7390Sstevel@tonic-gate 			continue;
7400Sstevel@tonic-gate 		case 'F':
7410Sstevel@tonic-gate 			Fflg++;
7420Sstevel@tonic-gate 			statreq++;
7430Sstevel@tonic-gate 			continue;
7440Sstevel@tonic-gate 		case 'g':
7450Sstevel@tonic-gate 			gflg++;
7460Sstevel@tonic-gate 			lflg++;
7470Sstevel@tonic-gate 			statreq++;
7480Sstevel@tonic-gate 			continue;
7490Sstevel@tonic-gate 		case 'h':
7500Sstevel@tonic-gate 			hflg++;
7510Sstevel@tonic-gate 			hscale = 1024;
7520Sstevel@tonic-gate 			continue;
7530Sstevel@tonic-gate 		case 'H':
7540Sstevel@tonic-gate 			Hflg++;
7550Sstevel@tonic-gate 			/* -H and -L are mutually exclusive */
7560Sstevel@tonic-gate 			Lflg = 0;
7570Sstevel@tonic-gate 			continue;
7580Sstevel@tonic-gate 		case 'i':
7590Sstevel@tonic-gate 			iflg++;
7600Sstevel@tonic-gate 			continue;
7619664Sjason@ansipunx.net 		case 'k':
7629664Sjason@ansipunx.net 			block_size = 1024;
7639664Sjason@ansipunx.net 			continue;
7640Sstevel@tonic-gate 		case 'l':
7650Sstevel@tonic-gate 			lflg++;
7660Sstevel@tonic-gate 			statreq++;
7670Sstevel@tonic-gate 			Cflg = 0;
7680Sstevel@tonic-gate 			xflg = 0;
7690Sstevel@tonic-gate 			mflg = 0;
7700Sstevel@tonic-gate 			atflg = 0;
7710Sstevel@tonic-gate 			continue;
7720Sstevel@tonic-gate 		case 'L':
7730Sstevel@tonic-gate 			Lflg++;
7740Sstevel@tonic-gate 			/* -H and -L are mutually exclusive */
7750Sstevel@tonic-gate 			Hflg = 0;
7760Sstevel@tonic-gate 			continue;
7770Sstevel@tonic-gate 		case 'm':
7780Sstevel@tonic-gate 			Cflg = 0;
7790Sstevel@tonic-gate 			mflg = 1;
7800Sstevel@tonic-gate #ifdef XPG4
7810Sstevel@tonic-gate 			lflg = 0;
7820Sstevel@tonic-gate #endif
7830Sstevel@tonic-gate 			continue;
7840Sstevel@tonic-gate 		case 'n':
7850Sstevel@tonic-gate 			nflg++;
7860Sstevel@tonic-gate 			lflg++;
7870Sstevel@tonic-gate 			statreq++;
7880Sstevel@tonic-gate 			Cflg = 0;
7890Sstevel@tonic-gate 			xflg = 0;
7900Sstevel@tonic-gate 			mflg = 0;
7910Sstevel@tonic-gate 			atflg = 0;
7920Sstevel@tonic-gate 			continue;
7930Sstevel@tonic-gate 		case 'o':
7940Sstevel@tonic-gate 			oflg++;
7950Sstevel@tonic-gate 			lflg++;
7960Sstevel@tonic-gate 			statreq++;
7970Sstevel@tonic-gate 			continue;
7980Sstevel@tonic-gate 		case 'p':
7990Sstevel@tonic-gate 			pflg++;
8000Sstevel@tonic-gate 			statreq++;
8010Sstevel@tonic-gate 			continue;
8020Sstevel@tonic-gate 		case 'q':
8030Sstevel@tonic-gate 			qflg = 1;
8040Sstevel@tonic-gate 			bflg = 0;
8050Sstevel@tonic-gate 			continue;
8060Sstevel@tonic-gate 		case 'r':
8070Sstevel@tonic-gate 			rflg = -1;
8080Sstevel@tonic-gate 			continue;
8090Sstevel@tonic-gate 		case 'R':
8100Sstevel@tonic-gate 			Rflg++;
8110Sstevel@tonic-gate 			statreq++;
8120Sstevel@tonic-gate 			continue;
8130Sstevel@tonic-gate 		case 's':
8140Sstevel@tonic-gate 			sflg++;
8150Sstevel@tonic-gate 			statreq++;
8160Sstevel@tonic-gate 			continue;
8170Sstevel@tonic-gate 		case 'S':
8180Sstevel@tonic-gate 			tflg = 0;
8199664Sjason@ansipunx.net 			Uflg = 0;
8200Sstevel@tonic-gate 			Sflg++;
8210Sstevel@tonic-gate 			statreq++;
8220Sstevel@tonic-gate 			continue;
8230Sstevel@tonic-gate 		case 't':
8240Sstevel@tonic-gate 			Sflg = 0;
8259664Sjason@ansipunx.net 			Uflg = 0;
8260Sstevel@tonic-gate 			tflg++;
8270Sstevel@tonic-gate 			statreq++;
8280Sstevel@tonic-gate 			continue;
8299664Sjason@ansipunx.net 		case 'U':
8309664Sjason@ansipunx.net 			Sflg = 0;
8319664Sjason@ansipunx.net 			tflg = 0;
8329664Sjason@ansipunx.net 			Uflg++;
8339664Sjason@ansipunx.net 			continue;
8340Sstevel@tonic-gate 		case 'u':
8350Sstevel@tonic-gate 			cflg = 0;
8365331Samw 			atm = 0;
8375331Samw 			ctm = 0;
8385331Samw 			mtm = 0;
8395331Samw 			crtm = 0;
8400Sstevel@tonic-gate 			uflg++;
8410Sstevel@tonic-gate 			continue;
8421420Smarks 		case 'V':
8431420Smarks 			Vflg++;
8441420Smarks 			/*FALLTHROUGH*/
845789Sahrens 		case 'v':
846789Sahrens 			vflg++;
847789Sahrens #if !defined(XPG4)
848789Sahrens 			if (lflg)
849789Sahrens 				continue;
850789Sahrens #endif
851789Sahrens 			lflg++;
852789Sahrens 			statreq++;
853789Sahrens 			Cflg = 0;
854789Sahrens 			xflg = 0;
855789Sahrens 			mflg = 0;
856789Sahrens 			continue;
8579664Sjason@ansipunx.net 		case 'w':
8589664Sjason@ansipunx.net 			wflg++;
8599664Sjason@ansipunx.net 			num_cols = atoi(optarg);
8609664Sjason@ansipunx.net 			continue;
8610Sstevel@tonic-gate 		case 'x':
8620Sstevel@tonic-gate 			xflg = 1;
8630Sstevel@tonic-gate 			Cflg = 1;
8640Sstevel@tonic-gate 			mflg = 0;
8650Sstevel@tonic-gate #ifdef XPG4
8660Sstevel@tonic-gate 			lflg = 0;
8670Sstevel@tonic-gate #endif
8680Sstevel@tonic-gate 			continue;
8690Sstevel@tonic-gate 		case '1':
8700Sstevel@tonic-gate 			Cflg = 0;
8710Sstevel@tonic-gate 			continue;
8720Sstevel@tonic-gate 		case '@':
8730Sstevel@tonic-gate #if !defined(XPG4)
8740Sstevel@tonic-gate 			/*
8750Sstevel@tonic-gate 			 * -l has precedence over -@
8760Sstevel@tonic-gate 			 */
8770Sstevel@tonic-gate 			if (lflg)
8780Sstevel@tonic-gate 				continue;
8790Sstevel@tonic-gate #endif
8800Sstevel@tonic-gate 			atflg++;
8810Sstevel@tonic-gate 			lflg++;
8820Sstevel@tonic-gate 			statreq++;
8830Sstevel@tonic-gate 			Cflg = 0;
8840Sstevel@tonic-gate 			xflg = 0;
8850Sstevel@tonic-gate 			mflg = 0;
8860Sstevel@tonic-gate 			continue;
8875331Samw 		case '/':
8885331Samw 			saflg++;
8895331Samw 			if (optarg != NULL) {
8905331Samw 				if (strcmp(optarg, "c") == 0) {
8915331Samw 					copt++;
8925331Samw 					vopt = 0;
8935331Samw 				} else if (strcmp(optarg, "v") == 0) {
8945331Samw 					vopt++;
8955331Samw 					copt = 0;
8965331Samw 				} else
8975331Samw 					opterr++;
8985331Samw 			} else
8995331Samw 				opterr++;
9005331Samw 			lflg++;
9015331Samw 			statreq++;
9025331Samw 			Cflg = 0;
9035331Samw 			xflg = 0;
9045331Samw 			mflg = 0;
9055331Samw 			continue;
9065331Samw 		case '%':
9075331Samw 			tmflg++;
9085331Samw 			if (optarg != NULL) {
9095331Samw 				if (strcmp(optarg, "ctime") == 0) {
9105331Samw 					ctm++;
9115331Samw 					atm = 0;
9125331Samw 					mtm = 0;
9135331Samw 					crtm = 0;
9145331Samw 				} else if (strcmp(optarg, "atime") == 0) {
9155331Samw 					atm++;
9165331Samw 					ctm = 0;
9175331Samw 					mtm = 0;
9185331Samw 					crtm = 0;
9195331Samw 					uflg = 0;
9205331Samw 					cflg = 0;
9215331Samw 				} else if (strcmp(optarg, "mtime") == 0) {
9225331Samw 					mtm++;
9235331Samw 					atm = 0;
9245331Samw 					ctm = 0;
9255331Samw 					crtm = 0;
9265331Samw 					uflg = 0;
9275331Samw 					cflg = 0;
9285331Samw 				} else if (strcmp(optarg, "crtime") == 0) {
9295331Samw 					crtm++;
9305331Samw 					atm = 0;
9315331Samw 					ctm = 0;
9325331Samw 					mtm = 0;
9335331Samw 					uflg = 0;
9345331Samw 					cflg = 0;
9355331Samw 				} else if (strcmp(optarg, "all") == 0) {
9365331Samw 					alltm++;
9375331Samw 					atm = 0;
9385331Samw 					ctm = 0;
9395331Samw 					mtm = 0;
9405331Samw 					crtm = 0;
9415331Samw 				} else
9425331Samw 					opterr++;
9435331Samw 			} else
9445331Samw 				opterr++;
9455331Samw 
9465331Samw 			Sflg = 0;
9475331Samw 			statreq++;
9485331Samw 			mflg = 0;
9495331Samw 			continue;
9500Sstevel@tonic-gate 		case '?':
9510Sstevel@tonic-gate 			opterr++;
9520Sstevel@tonic-gate 			continue;
9530Sstevel@tonic-gate 		}
9549664Sjason@ansipunx.net 
9550Sstevel@tonic-gate 	if (opterr) {
9560Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
9579664Sjason@ansipunx.net 		    "usage: ls -aAbBcCdeEfFghHiklLmnopqrRsStuUwxvV1@/%[c | v]"
9585331Samw 		    "%%[atime | crtime | ctime | mtime | all]"
9595331Samw 		    " [files]\n"));
9600Sstevel@tonic-gate 		exit(2);
9610Sstevel@tonic-gate 	}
9620Sstevel@tonic-gate 
9630Sstevel@tonic-gate 	if (fflg) {
9640Sstevel@tonic-gate 		aflg++;
9650Sstevel@tonic-gate 		lflg = 0;
9660Sstevel@tonic-gate 		sflg = 0;
9670Sstevel@tonic-gate 		tflg = 0;
9680Sstevel@tonic-gate 		Sflg = 0;
9690Sstevel@tonic-gate 		statreq = 0;
9700Sstevel@tonic-gate 	}
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 	fixedwidth = 2;
9730Sstevel@tonic-gate 	if (pflg || Fflg)
9740Sstevel@tonic-gate 		fixedwidth++;
9750Sstevel@tonic-gate 	if (iflg)
9760Sstevel@tonic-gate 		fixedwidth += 11;
9770Sstevel@tonic-gate 	if (sflg)
9780Sstevel@tonic-gate 		fixedwidth += 5;
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 	if (lflg) {
9810Sstevel@tonic-gate 		if (!gflg && !oflg)
9820Sstevel@tonic-gate 			gflg = oflg = 1;
9830Sstevel@tonic-gate 		else
9840Sstevel@tonic-gate 		if (gflg && oflg)
9850Sstevel@tonic-gate 			gflg = oflg = 0;
9860Sstevel@tonic-gate 		Cflg = mflg = 0;
9870Sstevel@tonic-gate 	}
9880Sstevel@tonic-gate 
9899664Sjason@ansipunx.net 	if (!wflg && (Cflg || mflg)) {
9900Sstevel@tonic-gate 		char *clptr;
9910Sstevel@tonic-gate 		if ((clptr = getenv("COLUMNS")) != NULL)
9920Sstevel@tonic-gate 			num_cols = atoi(clptr);
9930Sstevel@tonic-gate #ifdef TERMINFO
9940Sstevel@tonic-gate 		else {
9950Sstevel@tonic-gate 			if (ioctl(1, TIOCGWINSZ, &win) != -1)
9960Sstevel@tonic-gate 				num_cols = (win.ws_col == 0 ? 80 : win.ws_col);
9970Sstevel@tonic-gate 		}
9980Sstevel@tonic-gate #endif
9990Sstevel@tonic-gate 	}
10000Sstevel@tonic-gate 
10019664Sjason@ansipunx.net 	if (num_cols < 20 || num_cols > 1000)
10029664Sjason@ansipunx.net 		/* assume it is an error */
10039664Sjason@ansipunx.net 		num_cols = 80;
10049664Sjason@ansipunx.net 
10050Sstevel@tonic-gate 	/* allocate space for flist and the associated	*/
10060Sstevel@tonic-gate 	/* data structures (lbufs)			*/
10070Sstevel@tonic-gate 	maxfils = quantn;
10080Sstevel@tonic-gate 	if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) ||
10090Sstevel@tonic-gate 	    ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) {
10100Sstevel@tonic-gate 		perror("ls");
10110Sstevel@tonic-gate 		exit(2);
10120Sstevel@tonic-gate 	}
10130Sstevel@tonic-gate 	if ((amino = (argc-optind)) == 0) {
10140Sstevel@tonic-gate 					/*
10150Sstevel@tonic-gate 					 * case when no names are given
10160Sstevel@tonic-gate 					 * in ls-command and current
10170Sstevel@tonic-gate 					 * directory is to be used
10180Sstevel@tonic-gate 					 */
10190Sstevel@tonic-gate 		argv[optind] = dotp;
10200Sstevel@tonic-gate 	}
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate 	for (i = 0; i < (amino ? amino : 1); i++) {
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 		/*
10250Sstevel@tonic-gate 		 * If we are recursing, we need to make sure we don't
10260Sstevel@tonic-gate 		 * get into an endless loop.  To keep track of the inodes
10270Sstevel@tonic-gate 		 * (actually, just the directories) visited, we
10280Sstevel@tonic-gate 		 * maintain a directory ancestry list for a file
10290Sstevel@tonic-gate 		 * hierarchy.  As we go deeper into the hierarchy,
10300Sstevel@tonic-gate 		 * a parent directory passes its directory list
10310Sstevel@tonic-gate 		 * info (device id, inode number, and a pointer to
10320Sstevel@tonic-gate 		 * its parent) to each of its children.  As we
10330Sstevel@tonic-gate 		 * process a child that is a directory, we save
10340Sstevel@tonic-gate 		 * its own personal directory list info.  We then
10350Sstevel@tonic-gate 		 * check to see if the child has already been
10360Sstevel@tonic-gate 		 * processed by comparing its device id and inode
10370Sstevel@tonic-gate 		 * number from its own personal directory list info
10380Sstevel@tonic-gate 		 * to that of each of its ancestors.  If there is a
10390Sstevel@tonic-gate 		 * match, then we know we've detected a cycle.
10400Sstevel@tonic-gate 		 */
10410Sstevel@tonic-gate 		if (Rflg) {
10420Sstevel@tonic-gate 			/*
10430Sstevel@tonic-gate 			 * This is the first parent in this lineage
10440Sstevel@tonic-gate 			 * (first in a directory hierarchy), so
10450Sstevel@tonic-gate 			 * this parent's parent doesn't exist.  We
10460Sstevel@tonic-gate 			 * only initialize myinfo when we are
10470Sstevel@tonic-gate 			 * recursing, otherwise it's not used.
10480Sstevel@tonic-gate 			 */
10490Sstevel@tonic-gate 			if ((myinfo = (struct ditem *)malloc(
10500Sstevel@tonic-gate 			    sizeof (struct ditem))) == NULL) {
10510Sstevel@tonic-gate 				perror("ls");
10520Sstevel@tonic-gate 				exit(2);
10530Sstevel@tonic-gate 			} else {
10540Sstevel@tonic-gate 				myinfo->dev = 0;
10550Sstevel@tonic-gate 				myinfo->ino = 0;
10560Sstevel@tonic-gate 				myinfo->parent = NULL;
10570Sstevel@tonic-gate 			}
10580Sstevel@tonic-gate 		}
10590Sstevel@tonic-gate 
10600Sstevel@tonic-gate 		if (Cflg || mflg) {
10610Sstevel@tonic-gate 			width = strcol((unsigned char *)argv[optind]);
10620Sstevel@tonic-gate 			if (width > filewidth)
10630Sstevel@tonic-gate 				filewidth = width;
10640Sstevel@tonic-gate 		}
10650Sstevel@tonic-gate 		if ((ep = gstat((*argv[optind] ? argv[optind] : dotp),
10660Sstevel@tonic-gate 		    1, myinfo)) == NULL) {
10670Sstevel@tonic-gate 			if (nomocore)
10680Sstevel@tonic-gate 				exit(2);
10690Sstevel@tonic-gate 			err = 2;
10700Sstevel@tonic-gate 			optind++;
10710Sstevel@tonic-gate 			continue;
10720Sstevel@tonic-gate 		}
10730Sstevel@tonic-gate 		ep->ln.namep = (*argv[optind] ? argv[optind] : dotp);
10740Sstevel@tonic-gate 		ep->lflags |= ISARG;
10750Sstevel@tonic-gate 		optind++;
10760Sstevel@tonic-gate 		nargs++;	/* count good arguments stored in flist */
10776866Sbasabi 		if (acl_err)
10786866Sbasabi 			err = 2;
10790Sstevel@tonic-gate 	}
10800Sstevel@tonic-gate 	colwidth = fixedwidth + filewidth;
10819664Sjason@ansipunx.net 	if (!Uflg)
10829664Sjason@ansipunx.net 		qsort(flist, (unsigned)nargs, sizeof (struct lbuf *),
10839664Sjason@ansipunx.net 		    (int (*)(const void *, const void *))compar);
10840Sstevel@tonic-gate 	for (i = 0; i < nargs; i++) {
10850Sstevel@tonic-gate 		if (flist[i]->ltype == 'd' && dflg == 0 || fflg)
10860Sstevel@tonic-gate 			break;
10870Sstevel@tonic-gate 	}
10889664Sjason@ansipunx.net 
10899664Sjason@ansipunx.net 	if (colorflg)
10909664Sjason@ansipunx.net 		ls_color_init();
10919664Sjason@ansipunx.net 
10920Sstevel@tonic-gate 	pem(&flist[0], &flist[i], 0);
10930Sstevel@tonic-gate 	for (; i < nargs; i++) {
10940Sstevel@tonic-gate 		pdirectory(flist[i]->ln.namep, Rflg ||
10950Sstevel@tonic-gate 		    (amino > 1), nargs, 0, flist[i]->ancinfo);
10960Sstevel@tonic-gate 		if (nomocore)
10970Sstevel@tonic-gate 			exit(2);
10980Sstevel@tonic-gate 		/* -R: print subdirectories found */
10990Sstevel@tonic-gate 		while (dfirst || cdfirst) {
11000Sstevel@tonic-gate 			/* Place direct subdirs on front in right order */
11010Sstevel@tonic-gate 			while (cdfirst) {
11020Sstevel@tonic-gate 				/* reverse cdfirst onto front of dfirst */
11030Sstevel@tonic-gate 				dtemp = cdfirst;
11040Sstevel@tonic-gate 				cdfirst = cdfirst -> dc_next;
11050Sstevel@tonic-gate 				dtemp -> dc_next = dfirst;
11060Sstevel@tonic-gate 				dfirst = dtemp;
11070Sstevel@tonic-gate 			}
11080Sstevel@tonic-gate 			/* take off first dir on dfirst & print it */
11090Sstevel@tonic-gate 			dtemp = dfirst;
11100Sstevel@tonic-gate 			dfirst = dfirst->dc_next;
11110Sstevel@tonic-gate 			pdirectory(dtemp->dc_name, 1, nargs,
11120Sstevel@tonic-gate 			    dtemp->cycle_detected, dtemp->myancinfo);
11130Sstevel@tonic-gate 			if (nomocore)
11140Sstevel@tonic-gate 				exit(2);
11150Sstevel@tonic-gate 			free(dtemp->dc_name);
11160Sstevel@tonic-gate 			free(dtemp);
11170Sstevel@tonic-gate 		}
11180Sstevel@tonic-gate 	}
11199664Sjason@ansipunx.net 
11200Sstevel@tonic-gate 	return (err);
11210Sstevel@tonic-gate }
11220Sstevel@tonic-gate 
11230Sstevel@tonic-gate /*
11240Sstevel@tonic-gate  * pdirectory: print the directory name, labelling it if title is
11250Sstevel@tonic-gate  * nonzero, using lp as the place to start reading in the dir.
11260Sstevel@tonic-gate  */
11270Sstevel@tonic-gate static void
pdirectory(char * name,int title,int lp,int cdetect,struct ditem * myinfo)11280Sstevel@tonic-gate pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo)
11290Sstevel@tonic-gate {
11300Sstevel@tonic-gate 	struct dchain *dp;
11310Sstevel@tonic-gate 	struct lbuf *ap;
11320Sstevel@tonic-gate 	char *pname;
11330Sstevel@tonic-gate 	int j;
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 	filewidth = 0;
11360Sstevel@tonic-gate 	curdir = name;
11370Sstevel@tonic-gate 	if (title) {
11380Sstevel@tonic-gate 		if (!first)
11390Sstevel@tonic-gate 			(void) putc('\n', stdout);
11400Sstevel@tonic-gate 		pprintf(name, ":");
11410Sstevel@tonic-gate 		new_line();
11420Sstevel@tonic-gate 	}
11430Sstevel@tonic-gate 	/*
11440Sstevel@tonic-gate 	 * If there was a cycle detected, then notify and don't report
11450Sstevel@tonic-gate 	 * further.
11460Sstevel@tonic-gate 	 */
11470Sstevel@tonic-gate 	if (cdetect) {
11480Sstevel@tonic-gate 		if (lflg || sflg) {
11490Sstevel@tonic-gate 			curcol += printf(gettext("total %d"), 0);
11500Sstevel@tonic-gate 			new_line();
11510Sstevel@tonic-gate 		}
11520Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
11530Sstevel@tonic-gate 		    "ls: cycle detected for %s\n"), name);
11540Sstevel@tonic-gate 		return;
11550Sstevel@tonic-gate 	}
11560Sstevel@tonic-gate 
11570Sstevel@tonic-gate 	nfiles = lp;
11580Sstevel@tonic-gate 	rddir(name, myinfo);
11590Sstevel@tonic-gate 	if (nomocore)
11600Sstevel@tonic-gate 		return;
11619664Sjason@ansipunx.net 	if (fflg == 0 && Uflg == 0)
11620Sstevel@tonic-gate 		qsort(&flist[lp], (unsigned)(nfiles - lp),
11630Sstevel@tonic-gate 		    sizeof (struct lbuf *),
11640Sstevel@tonic-gate 		    (int (*)(const void *, const void *))compar);
11650Sstevel@tonic-gate 	if (Rflg) {
11660Sstevel@tonic-gate 		for (j = nfiles - 1; j >= lp; j--) {
11670Sstevel@tonic-gate 			ap = flist[j];
11680Sstevel@tonic-gate 			if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") &&
11690Sstevel@tonic-gate 			    strcmp(ap->ln.lname, "..")) {
11700Sstevel@tonic-gate 				dp = malloc(sizeof (struct dchain));
11710Sstevel@tonic-gate 				if (dp == NULL) {
11720Sstevel@tonic-gate 					perror("ls");
11730Sstevel@tonic-gate 					exit(2);
11740Sstevel@tonic-gate 				}
11750Sstevel@tonic-gate 				pname = makename(curdir, ap->ln.lname);
11760Sstevel@tonic-gate 				if ((dp->dc_name = strdup(pname)) == NULL) {
11770Sstevel@tonic-gate 					perror("ls");
11780Sstevel@tonic-gate 					exit(2);
11790Sstevel@tonic-gate 				}
11800Sstevel@tonic-gate 				dp->cycle_detected = ap->cycle;
11810Sstevel@tonic-gate 				dp->myancinfo = ap->ancinfo;
11820Sstevel@tonic-gate 				dp->dc_next = dfirst;
11830Sstevel@tonic-gate 				dfirst = dp;
11840Sstevel@tonic-gate 			}
11850Sstevel@tonic-gate 		}
11860Sstevel@tonic-gate 	}
11870Sstevel@tonic-gate 	if (lflg || sflg) {
11880Sstevel@tonic-gate 		curcol += printf(gettext("total %llu"), tblocks);
11890Sstevel@tonic-gate 		new_line();
11900Sstevel@tonic-gate 	}
11910Sstevel@tonic-gate 	pem(&flist[lp], &flist[nfiles], lflg||sflg);
11920Sstevel@tonic-gate }
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate /*
11950Sstevel@tonic-gate  * pem: print 'em. Print a list of files (e.g. a directory) bounded
11960Sstevel@tonic-gate  * by slp and lp.
11970Sstevel@tonic-gate  */
11980Sstevel@tonic-gate static void
pem(struct lbuf ** slp,struct lbuf ** lp,int tot_flag)11990Sstevel@tonic-gate pem(struct lbuf **slp, struct lbuf **lp, int tot_flag)
12000Sstevel@tonic-gate {
12010Sstevel@tonic-gate 	long row, nrows, i;
12020Sstevel@tonic-gate 	int col, ncols;
12030Sstevel@tonic-gate 	struct lbuf **ep;
12040Sstevel@tonic-gate 
12050Sstevel@tonic-gate 	if (Cflg || mflg) {
12060Sstevel@tonic-gate 		if (colwidth > num_cols) {
12070Sstevel@tonic-gate 			ncols = 1;
12080Sstevel@tonic-gate 		} else {
12090Sstevel@tonic-gate 			ncols = num_cols / colwidth;
12100Sstevel@tonic-gate 		}
12110Sstevel@tonic-gate 	}
12120Sstevel@tonic-gate 
12130Sstevel@tonic-gate 	if (ncols == 1 || mflg || xflg || !Cflg) {
12140Sstevel@tonic-gate 		for (ep = slp; ep < lp; ep++)
12150Sstevel@tonic-gate 			pentry(*ep);
12160Sstevel@tonic-gate 		new_line();
12170Sstevel@tonic-gate 		return;
12180Sstevel@tonic-gate 	}
12190Sstevel@tonic-gate 	/* otherwise print -C columns */
12200Sstevel@tonic-gate 	if (tot_flag) {
12210Sstevel@tonic-gate 		slp--;
12220Sstevel@tonic-gate 		row = 1;
12230Sstevel@tonic-gate 	}
12240Sstevel@tonic-gate 	else
12250Sstevel@tonic-gate 		row = 0;
12260Sstevel@tonic-gate 
12270Sstevel@tonic-gate 	nrows = (lp - slp - 1) / ncols + 1;
12280Sstevel@tonic-gate 	for (i = 0; i < nrows; i++, row++) {
12290Sstevel@tonic-gate 		for (col = 0; col < ncols; col++) {
12300Sstevel@tonic-gate 			ep = slp + (nrows * col) + row;
12310Sstevel@tonic-gate 			if (ep < lp)
12320Sstevel@tonic-gate 				pentry(*ep);
12330Sstevel@tonic-gate 		}
12340Sstevel@tonic-gate 		new_line();
12350Sstevel@tonic-gate 	}
12360Sstevel@tonic-gate }
12370Sstevel@tonic-gate 
12380Sstevel@tonic-gate /*
12390Sstevel@tonic-gate  * print one output entry;
12400Sstevel@tonic-gate  * if uid/gid is not found in the appropriate
12410Sstevel@tonic-gate  * file(passwd/group), then print uid/gid instead of
12420Sstevel@tonic-gate  * user/group name;
12430Sstevel@tonic-gate  */
12440Sstevel@tonic-gate static void
pentry(struct lbuf * ap)12450Sstevel@tonic-gate pentry(struct lbuf *ap)
12460Sstevel@tonic-gate {
12470Sstevel@tonic-gate 	struct lbuf *p;
12480Sstevel@tonic-gate 	numbuf_t hbuf;
12490Sstevel@tonic-gate 	char *dmark = "";	/* Used if -p or -F option active */
12500Sstevel@tonic-gate 	char *cp;
125110059Sjason@ansipunx.net 	char *str;
12520Sstevel@tonic-gate 
12530Sstevel@tonic-gate 	p = ap;
12540Sstevel@tonic-gate 	column();
12550Sstevel@tonic-gate 	if (iflg)
12560Sstevel@tonic-gate 		if (mflg && !lflg)
12570Sstevel@tonic-gate 			curcol += printf("%llu ", (long long)p->lnum);
12580Sstevel@tonic-gate 		else
12590Sstevel@tonic-gate 			curcol += printf("%10llu ", (long long)p->lnum);
12600Sstevel@tonic-gate 	if (sflg)
12610Sstevel@tonic-gate 		curcol += printf((mflg && !lflg) ? "%lld " :
12625331Samw 		    (p->lblocks < 10000) ? "%4lld " : "%lld ",
12635331Samw 		    (p->ltype != 'b' && p->ltype != 'c') ?
12645331Samw 		    p->lblocks : 0LL);
12650Sstevel@tonic-gate 	if (lflg) {
12660Sstevel@tonic-gate 		(void) putchar(p->ltype);
12670Sstevel@tonic-gate 		curcol++;
12680Sstevel@tonic-gate 		pmode(p->lflags);
12690Sstevel@tonic-gate 
12700Sstevel@tonic-gate 		/* ACL: additional access mode flag */
12710Sstevel@tonic-gate 		(void) putchar(p->acl);
12720Sstevel@tonic-gate 		curcol++;
12730Sstevel@tonic-gate 
12740Sstevel@tonic-gate 		curcol += printf("%3lu ", (ulong_t)p->lnl);
12750Sstevel@tonic-gate 		if (oflg)
12760Sstevel@tonic-gate 			if (!nflg) {
12770Sstevel@tonic-gate 				cp = getname(p->luid);
12780Sstevel@tonic-gate 				curcol += printf("%-8s ", cp);
12790Sstevel@tonic-gate 			} else
12800Sstevel@tonic-gate 				curcol += printf("%-8lu ", (ulong_t)p->luid);
12810Sstevel@tonic-gate 		if (gflg)
12820Sstevel@tonic-gate 			if (!nflg) {
12830Sstevel@tonic-gate 				cp = getgroup(p->lgid);
12840Sstevel@tonic-gate 				curcol += printf("%-8s ", cp);
12850Sstevel@tonic-gate 			} else
12860Sstevel@tonic-gate 				curcol += printf("%-8lu ", (ulong_t)p->lgid);
12870Sstevel@tonic-gate 		if (p->ltype == 'b' || p->ltype == 'c') {
12880Sstevel@tonic-gate 			curcol += printf("%3u, %2u",
12890Sstevel@tonic-gate 			    (uint_t)major((dev_t)p->lsize),
12900Sstevel@tonic-gate 			    (uint_t)minor((dev_t)p->lsize));
12910Sstevel@tonic-gate 		} else if (hflg && (p->lsize >= hscale)) {
12920Sstevel@tonic-gate 			curcol += printf("%7s",
12930Sstevel@tonic-gate 			    number_to_scaled_string(hbuf, p->lsize, hscale));
12940Sstevel@tonic-gate 		} else {
12959664Sjason@ansipunx.net 			uint64_t bsize = p->lsize / block_size;
12969664Sjason@ansipunx.net 
12979664Sjason@ansipunx.net 			/*
12989664Sjason@ansipunx.net 			 * Round up only when using blocks > 1 byte, otherwise
12999664Sjason@ansipunx.net 			 * 'normal' sizes display 1 byte too large.
13009664Sjason@ansipunx.net 			 */
13019664Sjason@ansipunx.net 			if (p->lsize % block_size != 0)
13029664Sjason@ansipunx.net 				bsize++;
13039664Sjason@ansipunx.net 
13049664Sjason@ansipunx.net 			curcol += printf("%7" PRIu64, bsize);
13050Sstevel@tonic-gate 		}
13069664Sjason@ansipunx.net 		format_time(p->lmtime.tv_sec, p->lmtime.tv_nsec);
13075331Samw 		/* format extended system attribute time */
13085331Samw 		if (tmflg && crtm)
13095331Samw 			format_attrtime(p);
13105331Samw 
13110Sstevel@tonic-gate 		curcol += printf("%s", time_buf);
13125331Samw 
13130Sstevel@tonic-gate 	}
13140Sstevel@tonic-gate 	/*
13150Sstevel@tonic-gate 	 * prevent both "->" and trailing marks
13160Sstevel@tonic-gate 	 * from appearing
13170Sstevel@tonic-gate 	 */
13180Sstevel@tonic-gate 
13190Sstevel@tonic-gate 	if (pflg && p->ltype == 'd')
13200Sstevel@tonic-gate 		dmark = "/";
13210Sstevel@tonic-gate 
13220Sstevel@tonic-gate 	if (Fflg && !(lflg && p->flinkto)) {
13230Sstevel@tonic-gate 		if (p->ltype == 'd')
13240Sstevel@tonic-gate 			dmark = "/";
13250Sstevel@tonic-gate 		else if (p->ltype == 'D')
13260Sstevel@tonic-gate 			dmark = ">";
13270Sstevel@tonic-gate 		else if (p->ltype == 'p')
13280Sstevel@tonic-gate 			dmark = "|";
13290Sstevel@tonic-gate 		else if (p->ltype == 'l')
13300Sstevel@tonic-gate 			dmark = "@";
13310Sstevel@tonic-gate 		else if (p->ltype == 's')
13320Sstevel@tonic-gate 			dmark = "=";
13339664Sjason@ansipunx.net 		else if (!file_typeflg &&
13349664Sjason@ansipunx.net 		    (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH)))
13350Sstevel@tonic-gate 			dmark = "*";
13360Sstevel@tonic-gate 		else
13370Sstevel@tonic-gate 			dmark = "";
13380Sstevel@tonic-gate 	}
13390Sstevel@tonic-gate 
134010059Sjason@ansipunx.net 	if (colorflg)
134110059Sjason@ansipunx.net 		ls_start_color(p->color);
134210059Sjason@ansipunx.net 
134310059Sjason@ansipunx.net 	if (p->lflags & ISARG)
134410059Sjason@ansipunx.net 		str = p->ln.namep;
134510059Sjason@ansipunx.net 	else
134610059Sjason@ansipunx.net 		str = p->ln.lname;
13479664Sjason@ansipunx.net 
134810059Sjason@ansipunx.net 	if (qflg || bflg) {
134910059Sjason@ansipunx.net 		csi_pprintf((unsigned char *)str);
13509664Sjason@ansipunx.net 
135110059Sjason@ansipunx.net 		if (lflg && p->flinkto) {
135210059Sjason@ansipunx.net 			if (colorflg)
135310059Sjason@ansipunx.net 				ls_end_color();
135410059Sjason@ansipunx.net 			csi_pprintf((unsigned char *)" -> ");
135510059Sjason@ansipunx.net 			if (colorflg)
135610059Sjason@ansipunx.net 				ls_start_color(p->link_color);
135710059Sjason@ansipunx.net 			csi_pprintf((unsigned char *)p->flinkto);
135810059Sjason@ansipunx.net 		} else {
135910059Sjason@ansipunx.net 			csi_pprintf((unsigned char *)dmark);
13600Sstevel@tonic-gate 		}
13610Sstevel@tonic-gate 	} else {
136210059Sjason@ansipunx.net 		(void) printf("%s", str);
136310059Sjason@ansipunx.net 		curcol += strcol((unsigned char *)str);
136410059Sjason@ansipunx.net 
136510059Sjason@ansipunx.net 		if (lflg && p->flinkto) {
136610059Sjason@ansipunx.net 			if (colorflg)
136710059Sjason@ansipunx.net 				ls_end_color();
136810059Sjason@ansipunx.net 			str = " -> ";
136910059Sjason@ansipunx.net 			(void) printf("%s", str);
137010059Sjason@ansipunx.net 			curcol += strcol((unsigned char *)str);
137110059Sjason@ansipunx.net 			if (colorflg)
137210059Sjason@ansipunx.net 				ls_start_color(p->link_color);
137310059Sjason@ansipunx.net 			(void) printf("%s", p->flinkto);
137410059Sjason@ansipunx.net 			curcol += strcol((unsigned char *)p->flinkto);
137510059Sjason@ansipunx.net 		} else {
137610059Sjason@ansipunx.net 			(void) printf("%s", dmark);
13770Sstevel@tonic-gate 			curcol += strcol((unsigned char *)dmark);
13780Sstevel@tonic-gate 		}
13790Sstevel@tonic-gate 	}
1380789Sahrens 
13819664Sjason@ansipunx.net 	if (colorflg)
13829664Sjason@ansipunx.net 		ls_end_color();
13839664Sjason@ansipunx.net 
13845331Samw 	/* Display extended system attributes */
13855331Samw 	if (saflg) {
13865331Samw 		int i;
13875331Samw 
13885331Samw 		new_line();
13895331Samw 		(void) printf("	\t{");
13905331Samw 		if (p->exttr != NULL) {
13915331Samw 			int k = 0;
13925331Samw 			for (i = 0; i < sacnt; i++) {
13935331Samw 				if (p->exttr[i].name != NULL)
13945331Samw 					k++;
13955331Samw 			}
13965331Samw 			for (i = 0; i < sacnt; i++) {
13975331Samw 				if (p->exttr[i].name != NULL) {
13985331Samw 					(void) printf("%s", p->exttr[i].name);
13995331Samw 					k--;
14005331Samw 					if (vopt && (k != 0))
14015331Samw 						(void) printf(",");
14025331Samw 				}
14035331Samw 			}
14045331Samw 		}
14055331Samw 		(void) printf("}\n");
14065331Samw 	}
14075331Samw 	/* Display file timestamps and extended system attribute timestamps */
14085331Samw 	if (tmflg && alltm) {
14095331Samw 		new_line();
14105331Samw 		print_time(p);
14115331Samw 		new_line();
14125331Samw 	}
1413789Sahrens 	if (vflg) {
1414789Sahrens 		new_line();
1415789Sahrens 		if (p->aclp) {
14161420Smarks 			acl_printacl(p->aclp, num_cols, Vflg);
1417789Sahrens 		}
1418789Sahrens 	}
14195331Samw 	/* Free extended system attribute lists */
14205331Samw 	if (saflg || tmflg)
14215331Samw 		free_sysattr(p);
14220Sstevel@tonic-gate }
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate /* print various r,w,x permissions */
14250Sstevel@tonic-gate static void
pmode(mode_t aflag)14260Sstevel@tonic-gate pmode(mode_t aflag)
14270Sstevel@tonic-gate {
14280Sstevel@tonic-gate 	/* these arrays are declared static to allow initializations */
14290Sstevel@tonic-gate 	static int	m0[] = { 1, S_IRUSR, 'r', '-' };
14300Sstevel@tonic-gate 	static int	m1[] = { 1, S_IWUSR, 'w', '-' };
14310Sstevel@tonic-gate 	static int	m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR,
14320Sstevel@tonic-gate 	    'x', S_ISUID, 'S', '-' };
14330Sstevel@tonic-gate 	static int	m3[] = { 1, S_IRGRP, 'r', '-' };
14340Sstevel@tonic-gate 	static int	m4[] = { 1, S_IWGRP, 'w', '-' };
14350Sstevel@tonic-gate 	static int	m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP,
14360Sstevel@tonic-gate 				'x', S_ISGID|LS_NOTREG, 'S',
14370Sstevel@tonic-gate #ifdef XPG4
14380Sstevel@tonic-gate 		S_ISGID, 'L', '-'};
14390Sstevel@tonic-gate #else
14400Sstevel@tonic-gate 		S_ISGID, 'l', '-'};
14410Sstevel@tonic-gate #endif
14420Sstevel@tonic-gate 	static int	m6[] = { 1, S_IROTH, 'r', '-' };
14430Sstevel@tonic-gate 	static int	m7[] = { 1, S_IWOTH, 'w', '-' };
14440Sstevel@tonic-gate 	static int	m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH,
14450Sstevel@tonic-gate 	    'x', S_ISVTX, 'T', '-'};
14460Sstevel@tonic-gate 
14470Sstevel@tonic-gate 	static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8};
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate 	int **mp;
14500Sstevel@tonic-gate 
14510Sstevel@tonic-gate 	flags = aflag;
14520Sstevel@tonic-gate 	for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++)
14530Sstevel@tonic-gate 		selection(*mp);
14540Sstevel@tonic-gate }
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate static void
selection(int * pairp)14570Sstevel@tonic-gate selection(int *pairp)
14580Sstevel@tonic-gate {
14590Sstevel@tonic-gate 	int n;
14600Sstevel@tonic-gate 
14610Sstevel@tonic-gate 	n = *pairp++;
14620Sstevel@tonic-gate 	while (n-->0) {
14630Sstevel@tonic-gate 		if ((flags & *pairp) == *pairp) {
14640Sstevel@tonic-gate 			pairp++;
14650Sstevel@tonic-gate 			break;
14660Sstevel@tonic-gate 		} else {
14670Sstevel@tonic-gate 			pairp += 2;
14680Sstevel@tonic-gate 		}
14690Sstevel@tonic-gate 	}
14700Sstevel@tonic-gate 	(void) putchar(*pairp);
14710Sstevel@tonic-gate 	curcol++;
14720Sstevel@tonic-gate }
14730Sstevel@tonic-gate 
14740Sstevel@tonic-gate /*
14750Sstevel@tonic-gate  * column: get to the beginning of the next column.
14760Sstevel@tonic-gate  */
14770Sstevel@tonic-gate static void
column(void)14780Sstevel@tonic-gate column(void)
14790Sstevel@tonic-gate {
14800Sstevel@tonic-gate 	if (curcol == 0)
14810Sstevel@tonic-gate 		return;
14820Sstevel@tonic-gate 	if (mflg) {
14830Sstevel@tonic-gate 		(void) putc(',', stdout);
14840Sstevel@tonic-gate 		curcol++;
14850Sstevel@tonic-gate 		if (curcol + colwidth + 2 > num_cols) {
14860Sstevel@tonic-gate 			(void) putc('\n', stdout);
14870Sstevel@tonic-gate 			curcol = 0;
14880Sstevel@tonic-gate 			return;
14890Sstevel@tonic-gate 		}
14900Sstevel@tonic-gate 		(void) putc(' ', stdout);
14910Sstevel@tonic-gate 		curcol++;
14920Sstevel@tonic-gate 		return;
14930Sstevel@tonic-gate 	}
14940Sstevel@tonic-gate 	if (Cflg == 0) {
14950Sstevel@tonic-gate 		(void) putc('\n', stdout);
14960Sstevel@tonic-gate 		curcol = 0;
14970Sstevel@tonic-gate 		return;
14980Sstevel@tonic-gate 	}
14990Sstevel@tonic-gate 	if ((curcol / colwidth + 2) * colwidth > num_cols) {
15000Sstevel@tonic-gate 		(void) putc('\n', stdout);
15010Sstevel@tonic-gate 		curcol = 0;
15020Sstevel@tonic-gate 		return;
15030Sstevel@tonic-gate 	}
15040Sstevel@tonic-gate 	do {
15050Sstevel@tonic-gate 		(void) putc(' ', stdout);
15060Sstevel@tonic-gate 		curcol++;
15070Sstevel@tonic-gate 	} while (curcol % colwidth);
15080Sstevel@tonic-gate }
15090Sstevel@tonic-gate 
15100Sstevel@tonic-gate static void
new_line(void)15110Sstevel@tonic-gate new_line(void)
15120Sstevel@tonic-gate {
15130Sstevel@tonic-gate 	if (curcol) {
15140Sstevel@tonic-gate 		first = 0;
15150Sstevel@tonic-gate 		(void) putc('\n', stdout);
15160Sstevel@tonic-gate 		curcol = 0;
15170Sstevel@tonic-gate 	}
15180Sstevel@tonic-gate }
15190Sstevel@tonic-gate 
15200Sstevel@tonic-gate /*
15210Sstevel@tonic-gate  * read each filename in directory dir and store its
15220Sstevel@tonic-gate  * status in flist[nfiles]
15230Sstevel@tonic-gate  * use makename() to form pathname dir/filename;
15240Sstevel@tonic-gate  */
15250Sstevel@tonic-gate static void
rddir(char * dir,struct ditem * myinfo)15260Sstevel@tonic-gate rddir(char *dir, struct ditem *myinfo)
15270Sstevel@tonic-gate {
15280Sstevel@tonic-gate 	struct dirent *dentry;
15290Sstevel@tonic-gate 	DIR *dirf;
15300Sstevel@tonic-gate 	int j;
15310Sstevel@tonic-gate 	struct lbuf *ep;
15320Sstevel@tonic-gate 	int width;
15330Sstevel@tonic-gate 
15340Sstevel@tonic-gate 	if ((dirf = opendir(dir)) == NULL) {
15350Sstevel@tonic-gate 		(void) fflush(stdout);
15360Sstevel@tonic-gate 		perror(dir);
15370Sstevel@tonic-gate 		err = 2;
15380Sstevel@tonic-gate 		return;
15390Sstevel@tonic-gate 	} else {
15400Sstevel@tonic-gate 		tblocks = 0;
15410Sstevel@tonic-gate 		for (;;) {
15420Sstevel@tonic-gate 			errno = 0;
15430Sstevel@tonic-gate 			if ((dentry = readdir(dirf)) == NULL)
15440Sstevel@tonic-gate 				break;
15450Sstevel@tonic-gate 			if (aflg == 0 && dentry->d_name[0] == '.' &&
15460Sstevel@tonic-gate 			    (Aflg == 0 ||
15470Sstevel@tonic-gate 			    dentry->d_name[1] == '\0' ||
15480Sstevel@tonic-gate 			    dentry->d_name[1] == '.' &&
15490Sstevel@tonic-gate 			    dentry->d_name[2] == '\0'))
15500Sstevel@tonic-gate 				/*
15510Sstevel@tonic-gate 				 * check for directory items '.', '..',
15520Sstevel@tonic-gate 				 *  and items without valid inode-number;
15530Sstevel@tonic-gate 				 */
15540Sstevel@tonic-gate 				continue;
15550Sstevel@tonic-gate 
15569664Sjason@ansipunx.net 			/* skip entries ending in ~ if -B was given */
15579664Sjason@ansipunx.net 			if (Bflg &&
15589664Sjason@ansipunx.net 			    dentry->d_name[strlen(dentry->d_name) - 1] == '~')
15599664Sjason@ansipunx.net 				continue;
15600Sstevel@tonic-gate 			if (Cflg || mflg) {
15610Sstevel@tonic-gate 				width = strcol((unsigned char *)dentry->d_name);
15620Sstevel@tonic-gate 				if (width > filewidth)
15630Sstevel@tonic-gate 					filewidth = width;
15640Sstevel@tonic-gate 			}
15650Sstevel@tonic-gate 			ep = gstat(makename(dir, dentry->d_name), 0, myinfo);
15660Sstevel@tonic-gate 			if (ep == NULL) {
15670Sstevel@tonic-gate 				if (nomocore)
15685331Samw 					exit(2);
15690Sstevel@tonic-gate 				continue;
15700Sstevel@tonic-gate 			} else {
15710Sstevel@tonic-gate 				ep->lnum = dentry->d_ino;
15720Sstevel@tonic-gate 				for (j = 0; dentry->d_name[j] != '\0'; j++)
15730Sstevel@tonic-gate 					ep->ln.lname[j] = dentry->d_name[j];
15740Sstevel@tonic-gate 				ep->ln.lname[j] = '\0';
15750Sstevel@tonic-gate 			}
15760Sstevel@tonic-gate 		}
15770Sstevel@tonic-gate 		if (errno) {
15780Sstevel@tonic-gate 			int sav_errno = errno;
15790Sstevel@tonic-gate 
15800Sstevel@tonic-gate 			(void) fprintf(stderr,
15810Sstevel@tonic-gate 			    gettext("ls: error reading directory %s: %s\n"),
15820Sstevel@tonic-gate 			    dir, strerror(sav_errno));
15830Sstevel@tonic-gate 		}
15840Sstevel@tonic-gate 		(void) closedir(dirf);
15850Sstevel@tonic-gate 		colwidth = fixedwidth + filewidth;
15860Sstevel@tonic-gate 	}
15870Sstevel@tonic-gate }
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate /*
15900Sstevel@tonic-gate  * Attaching a link to an inode's ancestors.  Search
15910Sstevel@tonic-gate  * through the ancestors to check for cycles (an inode which
15920Sstevel@tonic-gate  * we have already tracked in this inodes ancestry).  If a cycle
15930Sstevel@tonic-gate  * is detected, set the exit code and record the fact so that
15940Sstevel@tonic-gate  * it is reported at the right time when printing the directory.
15950Sstevel@tonic-gate  * In addition, set the exit code.  Note:  If the -a flag was
15960Sstevel@tonic-gate  * specified, we don't want to check for cycles for directories
15970Sstevel@tonic-gate  * ending in '/.' or '/..' unless they were specified on the
15980Sstevel@tonic-gate  * command line.
15990Sstevel@tonic-gate  */
16000Sstevel@tonic-gate static void
record_ancestry(char * file,struct stat * pstatb,struct lbuf * rep,int argfl,struct ditem * myparent)16010Sstevel@tonic-gate record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep,
16020Sstevel@tonic-gate     int argfl, struct ditem *myparent)
16030Sstevel@tonic-gate {
16040Sstevel@tonic-gate 	size_t		file_len;
16050Sstevel@tonic-gate 	struct ditem	*myinfo;
16060Sstevel@tonic-gate 	struct ditem	*tptr;
16070Sstevel@tonic-gate 
16080Sstevel@tonic-gate 	file_len = strlen(file);
16090Sstevel@tonic-gate 	if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) &&
16100Sstevel@tonic-gate 	    NOTPARENTDIR(file, file_len))) {
16110Sstevel@tonic-gate 		/*
16120Sstevel@tonic-gate 		 * Add this inode's ancestry
16130Sstevel@tonic-gate 		 * info and insert it into the
16140Sstevel@tonic-gate 		 * ancestry list by pointing
16150Sstevel@tonic-gate 		 * back to its parent.  We save
16160Sstevel@tonic-gate 		 * it (in rep) with the other info
16170Sstevel@tonic-gate 		 * we're gathering for this inode.
16180Sstevel@tonic-gate 		 */
16190Sstevel@tonic-gate 		if ((myinfo = malloc(
16200Sstevel@tonic-gate 		    sizeof (struct ditem))) == NULL) {
16210Sstevel@tonic-gate 			perror("ls");
16220Sstevel@tonic-gate 			exit(2);
16230Sstevel@tonic-gate 		}
16240Sstevel@tonic-gate 		myinfo->dev = pstatb->st_dev;
16250Sstevel@tonic-gate 		myinfo->ino = pstatb->st_ino;
16260Sstevel@tonic-gate 		myinfo->parent = myparent;
16270Sstevel@tonic-gate 		rep->ancinfo = myinfo;
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate 		/*
16300Sstevel@tonic-gate 		 * If this node has the same device id and
16310Sstevel@tonic-gate 		 * inode number of one of its ancestors,
16320Sstevel@tonic-gate 		 * then we've detected a cycle.
16330Sstevel@tonic-gate 		 */
16340Sstevel@tonic-gate 		if (myparent != NULL) {
16350Sstevel@tonic-gate 			for (tptr = myparent; tptr->parent != NULL;
16360Sstevel@tonic-gate 			    tptr = tptr->parent) {
16370Sstevel@tonic-gate 				if ((tptr->dev == pstatb->st_dev) &&
16380Sstevel@tonic-gate 				    (tptr->ino == pstatb->st_ino)) {
16390Sstevel@tonic-gate 					/*
16400Sstevel@tonic-gate 					 * Cycle detected for this
16410Sstevel@tonic-gate 					 * directory.  Record the fact
16420Sstevel@tonic-gate 					 * it is a cycle so we don't
16430Sstevel@tonic-gate 					 * try to process this
16440Sstevel@tonic-gate 					 * directory as we are
16450Sstevel@tonic-gate 					 * walking through the
16460Sstevel@tonic-gate 					 * list of directories.
16470Sstevel@tonic-gate 					 */
16480Sstevel@tonic-gate 					rep->cycle = 1;
16490Sstevel@tonic-gate 					err = 2;
16500Sstevel@tonic-gate 					break;
16510Sstevel@tonic-gate 				}
16520Sstevel@tonic-gate 			}
16530Sstevel@tonic-gate 		}
16540Sstevel@tonic-gate 	}
16550Sstevel@tonic-gate }
16560Sstevel@tonic-gate 
16570Sstevel@tonic-gate /*
16586178Sny155746  * Do re-calculate the mode for group for ACE_T type of acls.
16596178Sny155746  * This is because, if the server's FS happens to be UFS, supporting
16606178Sny155746  * POSIX ACL's, then it does a special calculation of group mode
16616178Sny155746  * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.)
16626178Sny155746  *
16636178Sny155746  * This algorithm is from the NFSv4 ACL Draft. Here a part of that
16646178Sny155746  * algorithm is used for the group mode calculation only.
16656178Sny155746  * What is modified here from the algorithm is that only the
16666178Sny155746  * entries with flags ACE_GROUP are considered. For each entry
16676178Sny155746  * with ACE_GROUP flag, the first occurance of a specific access
16686178Sny155746  * is checked if it is allowed.
16696373Sny155746  * We are not interested in perms for user and other, as they
16706178Sny155746  * were taken from st_mode value.
16716178Sny155746  * We are not interested in a_who field of ACE, as we need just
16726178Sny155746  * unix mode bits for the group.
16736178Sny155746  */
16746373Sny155746 
16756373Sny155746 #define	OWNED_GROUP	(ACE_GROUP | ACE_IDENTIFIER_GROUP)
16766373Sny155746 #define	IS_TYPE_ALLOWED(type)	((type) == ACE_ACCESS_ALLOWED_ACE_TYPE)
16776373Sny155746 
16786178Sny155746 int
grp_mask_to_mode(struct lbuf * p)1679*12164SMark.Shellenbaum@Sun.COM grp_mask_to_mode(struct lbuf *p)
16806178Sny155746 {
16816178Sny155746 	int mode = 0, seen = 0;
16826178Sny155746 	int acecnt;
16836373Sny155746 	int flags;
16846178Sny155746 	ace_t *ap;
1685*12164SMark.Shellenbaum@Sun.COM 	acl_t *acep = p->aclp;
16866178Sny155746 
16876178Sny155746 	acecnt = acl_cnt(acep);
16886178Sny155746 	for (ap = (ace_t *)acl_data(acep); acecnt--; ap++) {
16896373Sny155746 
16906373Sny155746 		if (ap->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE &&
16916373Sny155746 		    ap->a_type != ACE_ACCESS_DENIED_ACE_TYPE)
16926373Sny155746 			continue;
16936373Sny155746 
16946373Sny155746 		if (ap->a_flags & ACE_INHERIT_ONLY_ACE)
16956373Sny155746 			continue;
16966373Sny155746 
16976373Sny155746 		/*
16986373Sny155746 		 * if it is first group@ or first everyone@
16996373Sny155746 		 * for each of read, write and execute, then
17006373Sny155746 		 * that will be the group mode bit.
17016373Sny155746 		 */
17026373Sny155746 		flags = ap->a_flags & ACE_TYPE_FLAGS;
1703*12164SMark.Shellenbaum@Sun.COM 		if (flags == OWNED_GROUP || (flags == ACE_IDENTIFIER_GROUP &&
1704*12164SMark.Shellenbaum@Sun.COM 		    ap->a_who == p->lgid) || flags == ACE_EVERYONE) {
17056373Sny155746 			if (ap->a_access_mask & ACE_READ_DATA) {
17066373Sny155746 				if (!(seen & S_IRGRP)) {
17076373Sny155746 					seen |= S_IRGRP;
17086373Sny155746 					if (IS_TYPE_ALLOWED(ap->a_type))
17096373Sny155746 						mode |= S_IRGRP;
17106178Sny155746 				}
17116373Sny155746 			}
17126373Sny155746 			if (ap->a_access_mask & ACE_WRITE_DATA) {
17136373Sny155746 				if (!(seen & S_IWGRP)) {
17146373Sny155746 					seen |= S_IWGRP;
17156373Sny155746 					if (IS_TYPE_ALLOWED(ap->a_type))
17166373Sny155746 						mode |= S_IWGRP;
17176178Sny155746 				}
17186373Sny155746 			}
17196373Sny155746 			if (ap->a_access_mask & ACE_EXECUTE) {
17206373Sny155746 				if (!(seen & S_IXGRP)) {
17216373Sny155746 					seen |= S_IXGRP;
17226373Sny155746 					if (IS_TYPE_ALLOWED(ap->a_type))
17236373Sny155746 						mode |= S_IXGRP;
17246178Sny155746 				}
17256178Sny155746 			}
17266178Sny155746 		}
17276178Sny155746 	}
17286178Sny155746 	return (mode);
17296178Sny155746 }
17306178Sny155746 
17316178Sny155746 /*
17320Sstevel@tonic-gate  * get status of file and recomputes tblocks;
17330Sstevel@tonic-gate  * argfl = 1 if file is a name in ls-command and = 0
17340Sstevel@tonic-gate  * for filename in a directory whose name is an
17350Sstevel@tonic-gate  * argument in the command;
17360Sstevel@tonic-gate  * stores a pointer in flist[nfiles] and
17370Sstevel@tonic-gate  * returns that pointer;
17380Sstevel@tonic-gate  * returns NULL if failed;
17390Sstevel@tonic-gate  */
17400Sstevel@tonic-gate static struct lbuf *
gstat(char * file,int argfl,struct ditem * myparent)17410Sstevel@tonic-gate gstat(char *file, int argfl, struct ditem *myparent)
17420Sstevel@tonic-gate {
17430Sstevel@tonic-gate 	struct stat statb, statb1;
17440Sstevel@tonic-gate 	struct lbuf *rep;
17450Sstevel@tonic-gate 	char buf[BUFSIZ];
17460Sstevel@tonic-gate 	ssize_t cc;
17470Sstevel@tonic-gate 	int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat;
17480Sstevel@tonic-gate 	int aclcnt;
1749789Sahrens 	int error;
17500Sstevel@tonic-gate 	aclent_t *tp;
17510Sstevel@tonic-gate 	o_mode_t groupperm, mask;
17520Sstevel@tonic-gate 	int grouppermfound, maskfound;
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate 	if (nomocore)
17550Sstevel@tonic-gate 		return (NULL);
17560Sstevel@tonic-gate 
17570Sstevel@tonic-gate 	if (nfiles >= maxfils) {
17580Sstevel@tonic-gate 		/*
17590Sstevel@tonic-gate 		 * all flist/lbuf pair assigned files, time to get some
17600Sstevel@tonic-gate 		 * more space
17610Sstevel@tonic-gate 		 */
17620Sstevel@tonic-gate 		maxfils += quantn;
17630Sstevel@tonic-gate 		if (((flist = realloc(flist,
17640Sstevel@tonic-gate 		    maxfils * sizeof (struct lbuf *))) == NULL) ||
17650Sstevel@tonic-gate 		    ((nxtlbf = malloc(quantn *
17660Sstevel@tonic-gate 		    sizeof (struct lbuf))) == NULL)) {
17670Sstevel@tonic-gate 			perror("ls");
17680Sstevel@tonic-gate 			nomocore = 1;
17690Sstevel@tonic-gate 			return (NULL);
17700Sstevel@tonic-gate 		}
17710Sstevel@tonic-gate 	}
17720Sstevel@tonic-gate 
17730Sstevel@tonic-gate 	/*
17740Sstevel@tonic-gate 	 * nfiles is reset to nargs for each directory
17750Sstevel@tonic-gate 	 * that is given as an argument maxn is checked
17760Sstevel@tonic-gate 	 * to prevent the assignment of an lbuf to a flist entry
17770Sstevel@tonic-gate 	 * that already has one assigned.
17780Sstevel@tonic-gate 	 */
17790Sstevel@tonic-gate 	if (nfiles >= maxn) {
17800Sstevel@tonic-gate 		rep = nxtlbf++;
17810Sstevel@tonic-gate 		flist[nfiles++] = rep;
17820Sstevel@tonic-gate 		maxn = nfiles;
17830Sstevel@tonic-gate 	} else {
17840Sstevel@tonic-gate 		rep = flist[nfiles++];
17850Sstevel@tonic-gate 	}
17866792Sbasabi 
17876792Sbasabi 	/* Initialize */
17886792Sbasabi 
17890Sstevel@tonic-gate 	rep->lflags = (mode_t)0;
17900Sstevel@tonic-gate 	rep->flinkto = NULL;
17910Sstevel@tonic-gate 	rep->cycle = 0;
17926792Sbasabi 	rep->lat.tv_sec = time(NULL);
17936792Sbasabi 	rep->lat.tv_nsec = 0;
17946792Sbasabi 	rep->lct.tv_sec = time(NULL);
17956792Sbasabi 	rep->lct.tv_nsec = 0;
17966792Sbasabi 	rep->lmt.tv_sec = time(NULL);
17976792Sbasabi 	rep->lmt.tv_nsec = 0;
17986792Sbasabi 	rep->exttr = NULL;
17996792Sbasabi 	rep->extm = NULL;
180010059Sjason@ansipunx.net 	rep->color = NULL;
180110059Sjason@ansipunx.net 	rep->link_color = NULL;
18026792Sbasabi 
18030Sstevel@tonic-gate 	if (argfl || statreq) {
18040Sstevel@tonic-gate 		int doacl;
18050Sstevel@tonic-gate 
18060Sstevel@tonic-gate 		if (lflg)
18070Sstevel@tonic-gate 			doacl = 1;
18080Sstevel@tonic-gate 		else
18090Sstevel@tonic-gate 			doacl = 0;
18106792Sbasabi 
18110Sstevel@tonic-gate 		if ((*statf)(file, &statb) < 0) {
18120Sstevel@tonic-gate 			if (argfl || errno != ENOENT ||
18130Sstevel@tonic-gate 			    (Lflg && lstat(file, &statb) == 0)) {
18140Sstevel@tonic-gate 				/*
18150Sstevel@tonic-gate 				 * Avoid race between readdir and lstat.
18160Sstevel@tonic-gate 				 * Print error message in case of dangling link.
18170Sstevel@tonic-gate 				 */
18180Sstevel@tonic-gate 				perror(file);
18199664Sjason@ansipunx.net 				err = 2;
18200Sstevel@tonic-gate 			}
18210Sstevel@tonic-gate 			nfiles--;
18220Sstevel@tonic-gate 			return (NULL);
18230Sstevel@tonic-gate 		}
18240Sstevel@tonic-gate 
18250Sstevel@tonic-gate 		/*
18260Sstevel@tonic-gate 		 * If -H was specified, and the file linked to was
18270Sstevel@tonic-gate 		 * not a directory, then we need to get the info
18280Sstevel@tonic-gate 		 * for the symlink itself.
18290Sstevel@tonic-gate 		 */
18300Sstevel@tonic-gate 		if ((Hflg) && (argfl) &&
18310Sstevel@tonic-gate 		    ((statb.st_mode & S_IFMT) != S_IFDIR)) {
18320Sstevel@tonic-gate 			if (lstat(file, &statb) < 0) {
18330Sstevel@tonic-gate 				perror(file);
18349664Sjason@ansipunx.net 				err = 2;
18350Sstevel@tonic-gate 			}
18360Sstevel@tonic-gate 		}
18370Sstevel@tonic-gate 
18380Sstevel@tonic-gate 		rep->lnum = statb.st_ino;
18390Sstevel@tonic-gate 		rep->lsize = statb.st_size;
18400Sstevel@tonic-gate 		rep->lblocks = statb.st_blocks;
184110059Sjason@ansipunx.net 		if (colorflg)
184210059Sjason@ansipunx.net 			rep->color = ls_color_find(file, statb.st_mode);
184310059Sjason@ansipunx.net 
18440Sstevel@tonic-gate 		switch (statb.st_mode & S_IFMT) {
18450Sstevel@tonic-gate 		case S_IFDIR:
18460Sstevel@tonic-gate 			rep->ltype = 'd';
18470Sstevel@tonic-gate 			if (Rflg) {
18480Sstevel@tonic-gate 				record_ancestry(file, &statb, rep,
18490Sstevel@tonic-gate 				    argfl, myparent);
18500Sstevel@tonic-gate 			}
18510Sstevel@tonic-gate 			break;
18520Sstevel@tonic-gate 		case S_IFBLK:
18530Sstevel@tonic-gate 			rep->ltype = 'b';
18540Sstevel@tonic-gate 			rep->lsize = (off_t)statb.st_rdev;
18550Sstevel@tonic-gate 			break;
18560Sstevel@tonic-gate 		case S_IFCHR:
18570Sstevel@tonic-gate 			rep->ltype = 'c';
18580Sstevel@tonic-gate 			rep->lsize = (off_t)statb.st_rdev;
18590Sstevel@tonic-gate 			break;
18600Sstevel@tonic-gate 		case S_IFIFO:
18610Sstevel@tonic-gate 			rep->ltype = 'p';
18620Sstevel@tonic-gate 			break;
18630Sstevel@tonic-gate 		case S_IFSOCK:
18640Sstevel@tonic-gate 			rep->ltype = 's';
18650Sstevel@tonic-gate 			rep->lsize = 0;
18660Sstevel@tonic-gate 			break;
18670Sstevel@tonic-gate 		case S_IFLNK:
18680Sstevel@tonic-gate 			/* symbolic links may not have ACLs, so elide acl() */
18690Sstevel@tonic-gate 			if ((Lflg == 0) || (Hflg == 0) ||
18700Sstevel@tonic-gate 			    ((Hflg) && (!argfl))) {
18710Sstevel@tonic-gate 				doacl = 0;
18720Sstevel@tonic-gate 			}
18730Sstevel@tonic-gate 			rep->ltype = 'l';
187410059Sjason@ansipunx.net 			if (lflg || colorflg) {
18750Sstevel@tonic-gate 				cc = readlink(file, buf, BUFSIZ);
187610059Sjason@ansipunx.net 				if (cc < 0)
187710059Sjason@ansipunx.net 					break;
187810059Sjason@ansipunx.net 
187910059Sjason@ansipunx.net 				/*
188010059Sjason@ansipunx.net 				 * follow the symbolic link
188110059Sjason@ansipunx.net 				 * to generate the appropriate
188210059Sjason@ansipunx.net 				 * Fflg marker for the object
188310059Sjason@ansipunx.net 				 * eg, /bin -> /sym/bin/
188410059Sjason@ansipunx.net 				 */
188510059Sjason@ansipunx.net 				error = 0;
188610059Sjason@ansipunx.net 				if (Fflg || pflg || colorflg)
188710059Sjason@ansipunx.net 					error = stat(file, &statb1);
18880Sstevel@tonic-gate 
188910059Sjason@ansipunx.net 				if (colorflg) {
189010059Sjason@ansipunx.net 					if (error >= 0)
189110059Sjason@ansipunx.net 						rep->link_color =
189210059Sjason@ansipunx.net 						    ls_color_find(file,
189310059Sjason@ansipunx.net 						    statb1.st_mode);
189410059Sjason@ansipunx.net 					else
189510059Sjason@ansipunx.net 						rep->link_color =
189610059Sjason@ansipunx.net 						    lsc_orphan;
189710059Sjason@ansipunx.net 				}
189810059Sjason@ansipunx.net 
189910059Sjason@ansipunx.net 				if ((Fflg || pflg) && error >= 0) {
190010059Sjason@ansipunx.net 					switch (statb1.st_mode & S_IFMT) {
190110059Sjason@ansipunx.net 					case S_IFDIR:
190210059Sjason@ansipunx.net 						buf[cc++] = '/';
190310059Sjason@ansipunx.net 						break;
190410059Sjason@ansipunx.net 					case S_IFSOCK:
190510059Sjason@ansipunx.net 						buf[cc++] = '=';
190610059Sjason@ansipunx.net 						break;
190710059Sjason@ansipunx.net 					case S_IFDOOR:
190810059Sjason@ansipunx.net 						buf[cc++] = '>';
190910059Sjason@ansipunx.net 						break;
191010059Sjason@ansipunx.net 					case S_IFIFO:
191110059Sjason@ansipunx.net 						buf[cc++] = '|';
191210059Sjason@ansipunx.net 						break;
191310059Sjason@ansipunx.net 					default:
191410059Sjason@ansipunx.net 						if ((statb1.st_mode & ~S_IFMT) &
191510059Sjason@ansipunx.net 						    (S_IXUSR|S_IXGRP| S_IXOTH))
191610059Sjason@ansipunx.net 							buf[cc++] = '*';
191710059Sjason@ansipunx.net 						break;
19180Sstevel@tonic-gate 					}
19190Sstevel@tonic-gate 				}
192010059Sjason@ansipunx.net 				buf[cc] = '\0';
192110059Sjason@ansipunx.net 				rep->flinkto = strdup(buf);
19220Sstevel@tonic-gate 				break;
19230Sstevel@tonic-gate 			}
19240Sstevel@tonic-gate 
19250Sstevel@tonic-gate 			/*
19260Sstevel@tonic-gate 			 * ls /sym behaves differently from ls /sym/
19270Sstevel@tonic-gate 			 * when /sym is a symbolic link. This is fixed
19280Sstevel@tonic-gate 			 * when explicit arguments are specified.
19290Sstevel@tonic-gate 			 */
19300Sstevel@tonic-gate 
19310Sstevel@tonic-gate #ifdef XPG6
19320Sstevel@tonic-gate 			/* Do not follow a symlink when -F is specified */
19330Sstevel@tonic-gate 			if ((!argfl) || (argfl && Fflg) ||
19340Sstevel@tonic-gate 			    (stat(file, &statb1) < 0))
19350Sstevel@tonic-gate #else
19360Sstevel@tonic-gate 			/* Follow a symlink when -F is specified */
19370Sstevel@tonic-gate 			if (!argfl || stat(file, &statb1) < 0)
19380Sstevel@tonic-gate #endif /* XPG6 */
19390Sstevel@tonic-gate 				break;
19400Sstevel@tonic-gate 			if ((statb1.st_mode & S_IFMT) == S_IFDIR) {
19410Sstevel@tonic-gate 				statb = statb1;
19420Sstevel@tonic-gate 				rep->ltype = 'd';
19430Sstevel@tonic-gate 				rep->lsize = statb1.st_size;
19440Sstevel@tonic-gate 				if (Rflg) {
19450Sstevel@tonic-gate 					record_ancestry(file, &statb, rep,
19460Sstevel@tonic-gate 					    argfl, myparent);
19470Sstevel@tonic-gate 				}
19480Sstevel@tonic-gate 			}
19490Sstevel@tonic-gate 			break;
19500Sstevel@tonic-gate 		case S_IFDOOR:
19510Sstevel@tonic-gate 			rep->ltype = 'D';
19520Sstevel@tonic-gate 			break;
19530Sstevel@tonic-gate 		case S_IFREG:
19540Sstevel@tonic-gate 			rep->ltype = '-';
19550Sstevel@tonic-gate 			break;
19560Sstevel@tonic-gate 		case S_IFPORT:
19570Sstevel@tonic-gate 			rep->ltype = 'P';
19580Sstevel@tonic-gate 			break;
19590Sstevel@tonic-gate 		default:
19600Sstevel@tonic-gate 			rep->ltype = '?';
19610Sstevel@tonic-gate 			break;
19620Sstevel@tonic-gate 		}
19630Sstevel@tonic-gate 		rep->lflags = statb.st_mode & ~S_IFMT;
19640Sstevel@tonic-gate 
19650Sstevel@tonic-gate 		if (!S_ISREG(statb.st_mode))
19660Sstevel@tonic-gate 			rep->lflags |= LS_NOTREG;
19670Sstevel@tonic-gate 
19686792Sbasabi 		rep->luid = statb.st_uid;
19696792Sbasabi 		rep->lgid = statb.st_gid;
19706792Sbasabi 		rep->lnl = statb.st_nlink;
19716792Sbasabi 		if (uflg || (tmflg && atm))
19726792Sbasabi 			rep->lmtime = statb.st_atim;
19736792Sbasabi 		else if (cflg || (tmflg && ctm))
19746792Sbasabi 			rep->lmtime = statb.st_ctim;
19756792Sbasabi 		else
19766792Sbasabi 			rep->lmtime = statb.st_mtim;
19776792Sbasabi 		rep->lat = statb.st_atim;
19786792Sbasabi 		rep->lct = statb.st_ctim;
19796792Sbasabi 		rep->lmt = statb.st_mtim;
19806792Sbasabi 
19810Sstevel@tonic-gate 		/* ACL: check acl entries count */
19820Sstevel@tonic-gate 		if (doacl) {
19830Sstevel@tonic-gate 
1984789Sahrens 			error = acl_get(file, 0, &rep->aclp);
1985789Sahrens 			if (error) {
1986789Sahrens 				(void) fprintf(stderr,
1987789Sahrens 				    gettext("ls: can't read ACL on %s: %s\n"),
1988789Sahrens 				    file, acl_strerror(error));
19896792Sbasabi 				rep->acl = ' ';
19906866Sbasabi 				acl_err++;
19916792Sbasabi 				return (rep);
1992789Sahrens 			}
19930Sstevel@tonic-gate 
1994789Sahrens 			rep->acl = ' ';
19950Sstevel@tonic-gate 
1996789Sahrens 			if (rep->aclp &&
1997789Sahrens 			    ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) {
1998789Sahrens 				rep->acl = '+';
19990Sstevel@tonic-gate 				/*
2000789Sahrens 				 * Special handling for ufs aka aclent_t ACL's
20010Sstevel@tonic-gate 				 */
20026178Sny155746 				if (acl_type(rep->aclp) == ACLENT_T) {
2003789Sahrens 					/*
2004789Sahrens 					 * For files with non-trivial acls, the
2005789Sahrens 					 * effective group permissions are the
2006789Sahrens 					 * intersection of the GROUP_OBJ value
2007789Sahrens 					 * and the CLASS_OBJ (acl mask) value.
2008789Sahrens 					 * Determine both the GROUP_OBJ and
2009789Sahrens 					 * CLASS_OBJ for this file and insert
2010789Sahrens 					 * the logical AND of those two values
2011789Sahrens 					 * in the group permissions field
2012789Sahrens 					 * of the lflags value for this file.
2013789Sahrens 					 */
2014789Sahrens 
2015789Sahrens 					/*
2016789Sahrens 					 * Until found in acl list, assume
2017789Sahrens 					 * maximum permissions for both group
2018789Sahrens 					 * a nd mask.  (Just in case the acl
2019789Sahrens 					 * lacks either value for some reason.)
2020789Sahrens 					 */
2021789Sahrens 					groupperm = 07;
2022789Sahrens 					mask = 07;
2023789Sahrens 					grouppermfound = 0;
2024789Sahrens 					maskfound = 0;
2025789Sahrens 					aclcnt = acl_cnt(rep->aclp);
2026789Sahrens 					for (tp =
2027789Sahrens 					    (aclent_t *)acl_data(rep->aclp);
2028789Sahrens 					    aclcnt--; tp++) {
2029789Sahrens 						if (tp->a_type == GROUP_OBJ) {
2030789Sahrens 							groupperm = tp->a_perm;
2031789Sahrens 							grouppermfound = 1;
2032789Sahrens 							continue;
2033789Sahrens 						}
2034789Sahrens 						if (tp->a_type == CLASS_OBJ) {
2035789Sahrens 							mask = tp->a_perm;
2036789Sahrens 							maskfound = 1;
2037789Sahrens 						}
2038789Sahrens 						if (grouppermfound && maskfound)
2039789Sahrens 							break;
20400Sstevel@tonic-gate 					}
2041789Sahrens 
20420Sstevel@tonic-gate 
2043789Sahrens 					/* reset all the group bits */
2044789Sahrens 					rep->lflags &= ~S_IRWXG;
20450Sstevel@tonic-gate 
2046789Sahrens 					/*
2047789Sahrens 					 * Now set them to the logical AND of
2048789Sahrens 					 * the GROUP_OBJ permissions and the
2049789Sahrens 					 * acl mask.
2050789Sahrens 					 */
20510Sstevel@tonic-gate 
2052789Sahrens 					rep->lflags |= (groupperm & mask) << 3;
20530Sstevel@tonic-gate 
20546178Sny155746 				} else if (acl_type(rep->aclp) == ACE_T) {
20556178Sny155746 					int mode;
2056*12164SMark.Shellenbaum@Sun.COM 					mode = grp_mask_to_mode(rep);
20576178Sny155746 					rep->lflags &= ~S_IRWXG;
20586178Sny155746 					rep->lflags |= mode;
2059789Sahrens 				}
20600Sstevel@tonic-gate 			}
20610Sstevel@tonic-gate 
20621420Smarks 			if (!vflg && !Vflg && rep->aclp) {
20631420Smarks 				acl_free(rep->aclp);
20641420Smarks 				rep->aclp = NULL;
20651420Smarks 			}
20661420Smarks 
20670Sstevel@tonic-gate 			if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1)
20680Sstevel@tonic-gate 				rep->acl = '@';
20695331Samw 
20700Sstevel@tonic-gate 		} else
20710Sstevel@tonic-gate 			rep->acl = ' ';
20720Sstevel@tonic-gate 
20730Sstevel@tonic-gate 		/* mask ISARG and other file-type bits */
20740Sstevel@tonic-gate 
20750Sstevel@tonic-gate 		if (rep->ltype != 'b' && rep->ltype != 'c')
20760Sstevel@tonic-gate 			tblocks += rep->lblocks;
20775331Samw 
20785331Samw 		/* Get extended system attributes */
20795331Samw 
20805331Samw 		if ((saflg || (tmflg && crtm) || (tmflg && alltm)) &&
20815331Samw 		    (sysattr_support(file, _PC_SATTR_EXISTS) == 1)) {
20825331Samw 			int i;
20835331Samw 
20845331Samw 			sacnt = attr_count();
20855331Samw 			/*
20865331Samw 			 * Allocate 'sacnt' size array to hold extended
20875331Samw 			 * system attribute name (verbose) or respective
20885331Samw 			 * symbol represenation (compact).
20895331Samw 			 */
20905331Samw 			rep->exttr = xmalloc(sacnt * sizeof (struct attrb),
20915331Samw 			    rep);
20925331Samw 
20935331Samw 			/* initialize boolean attribute list */
20945331Samw 			for (i = 0; i < sacnt; i++)
20955331Samw 				rep->exttr[i].name = NULL;
20965331Samw 			if (get_sysxattr(file, rep) != 0) {
20975331Samw 				(void) fprintf(stderr,
20985331Samw 				    gettext("ls:Failed to retrieve "
20995331Samw 				    "extended system attribute from "
21005331Samw 				    "%s\n"), file);
21015331Samw 				rep->exttr[0].name = xmalloc(2, rep);
21025331Samw 				(void) strlcpy(rep->exttr[0].name, "?", 2);
21035331Samw 			}
21045331Samw 		}
21050Sstevel@tonic-gate 	}
21060Sstevel@tonic-gate 	return (rep);
21070Sstevel@tonic-gate }
21080Sstevel@tonic-gate 
21090Sstevel@tonic-gate /*
21100Sstevel@tonic-gate  * returns pathname of the form dir/file;
21110Sstevel@tonic-gate  * dir and file are null-terminated strings.
21120Sstevel@tonic-gate  */
21130Sstevel@tonic-gate static char *
makename(char * dir,char * file)21140Sstevel@tonic-gate makename(char *dir, char *file)
21150Sstevel@tonic-gate {
21160Sstevel@tonic-gate 	/*
21170Sstevel@tonic-gate 	 * PATH_MAX is the maximum length of a path name.
21180Sstevel@tonic-gate 	 * MAXNAMLEN is the maximum length of any path name component.
21190Sstevel@tonic-gate 	 * Allocate space for both, plus the '/' in the middle
21200Sstevel@tonic-gate 	 * and the null character at the end.
21210Sstevel@tonic-gate 	 * dfile is static as this is returned by makename().
21220Sstevel@tonic-gate 	 */
21230Sstevel@tonic-gate 	static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1];
21240Sstevel@tonic-gate 	char *dp, *fp;
21250Sstevel@tonic-gate 
21260Sstevel@tonic-gate 	dp = dfile;
21270Sstevel@tonic-gate 	fp = dir;
21280Sstevel@tonic-gate 	while (*fp)
21290Sstevel@tonic-gate 		*dp++ = *fp++;
21300Sstevel@tonic-gate 	if (dp > dfile && *(dp - 1) != '/')
21310Sstevel@tonic-gate 		*dp++ = '/';
21320Sstevel@tonic-gate 	fp = file;
21330Sstevel@tonic-gate 	while (*fp)
21340Sstevel@tonic-gate 		*dp++ = *fp++;
21350Sstevel@tonic-gate 	*dp = '\0';
21360Sstevel@tonic-gate 	return (dfile);
21370Sstevel@tonic-gate }
21380Sstevel@tonic-gate 
21390Sstevel@tonic-gate 
21400Sstevel@tonic-gate #include <pwd.h>
21410Sstevel@tonic-gate #include <grp.h>
21420Sstevel@tonic-gate #include <utmpx.h>
21430Sstevel@tonic-gate 
21440Sstevel@tonic-gate struct	utmpx utmp;
21450Sstevel@tonic-gate 
21460Sstevel@tonic-gate #define	NMAX	(sizeof (utmp.ut_name))
21470Sstevel@tonic-gate #define	SCPYN(a, b)	(void) strncpy(a, b, NMAX)
21480Sstevel@tonic-gate 
21490Sstevel@tonic-gate 
21500Sstevel@tonic-gate struct cachenode {		/* this struct must be zeroed before using */
21510Sstevel@tonic-gate 	struct cachenode *lesschild;	/* subtree whose entries < val */
21520Sstevel@tonic-gate 	struct cachenode *grtrchild;	/* subtree whose entries > val */
21530Sstevel@tonic-gate 	long val;			/* the uid or gid of this entry */
21540Sstevel@tonic-gate 	int initted;			/* name has been filled in */
21550Sstevel@tonic-gate 	char name[NMAX+1];		/* the string that val maps to */
21560Sstevel@tonic-gate };
21570Sstevel@tonic-gate static struct cachenode *names, *groups;
21580Sstevel@tonic-gate 
21590Sstevel@tonic-gate static struct cachenode *
findincache(struct cachenode ** head,long val)21600Sstevel@tonic-gate findincache(struct cachenode **head, long val)
21610Sstevel@tonic-gate {
21620Sstevel@tonic-gate 	struct cachenode **parent = head;
21630Sstevel@tonic-gate 	struct cachenode *c = *parent;
21640Sstevel@tonic-gate 
21650Sstevel@tonic-gate 	while (c != NULL) {
21660Sstevel@tonic-gate 		if (val == c->val) {
21670Sstevel@tonic-gate 			/* found it */
21680Sstevel@tonic-gate 			return (c);
21690Sstevel@tonic-gate 		} else if (val < c->val) {
21700Sstevel@tonic-gate 			parent = &c->lesschild;
21710Sstevel@tonic-gate 			c = c->lesschild;
21720Sstevel@tonic-gate 		} else {
21730Sstevel@tonic-gate 			parent = &c->grtrchild;
21740Sstevel@tonic-gate 			c = c->grtrchild;
21750Sstevel@tonic-gate 		}
21760Sstevel@tonic-gate 	}
21770Sstevel@tonic-gate 
21780Sstevel@tonic-gate 	/* not in the cache, make a new entry for it */
21790Sstevel@tonic-gate 	c = calloc(1, sizeof (struct cachenode));
21800Sstevel@tonic-gate 	if (c == NULL) {
21810Sstevel@tonic-gate 		perror("ls");
21820Sstevel@tonic-gate 		exit(2);
21830Sstevel@tonic-gate 	}
21840Sstevel@tonic-gate 	*parent = c;
21850Sstevel@tonic-gate 	c->val = val;
21860Sstevel@tonic-gate 	return (c);
21870Sstevel@tonic-gate }
21880Sstevel@tonic-gate 
21890Sstevel@tonic-gate /*
21900Sstevel@tonic-gate  * get name from cache, or passwd file for a given uid;
21910Sstevel@tonic-gate  * lastuid is set to uid.
21920Sstevel@tonic-gate  */
21930Sstevel@tonic-gate static char *
getname(uid_t uid)21940Sstevel@tonic-gate getname(uid_t uid)
21950Sstevel@tonic-gate {
21960Sstevel@tonic-gate 	struct passwd *pwent;
21970Sstevel@tonic-gate 	struct cachenode *c;
21980Sstevel@tonic-gate 
21990Sstevel@tonic-gate 	if ((uid == lastuid) && lastuname)
22000Sstevel@tonic-gate 		return (lastuname);
22010Sstevel@tonic-gate 
22020Sstevel@tonic-gate 	c = findincache(&names, uid);
22030Sstevel@tonic-gate 	if (c->initted == 0) {
22040Sstevel@tonic-gate 		if ((pwent = getpwuid(uid)) != NULL) {
22050Sstevel@tonic-gate 			SCPYN(&c->name[0], pwent->pw_name);
22060Sstevel@tonic-gate 		} else {
22070Sstevel@tonic-gate 			(void) sprintf(&c->name[0], "%-8u", (int)uid);
22080Sstevel@tonic-gate 		}
22090Sstevel@tonic-gate 		c->initted = 1;
22100Sstevel@tonic-gate 	}
22110Sstevel@tonic-gate 	lastuid = uid;
22120Sstevel@tonic-gate 	lastuname = &c->name[0];
22130Sstevel@tonic-gate 	return (lastuname);
22140Sstevel@tonic-gate }
22150Sstevel@tonic-gate 
22160Sstevel@tonic-gate /*
22170Sstevel@tonic-gate  * get name from cache, or group file for a given gid;
22180Sstevel@tonic-gate  * lastgid is set to gid.
22190Sstevel@tonic-gate  */
22200Sstevel@tonic-gate static char *
getgroup(gid_t gid)22210Sstevel@tonic-gate getgroup(gid_t gid)
22220Sstevel@tonic-gate {
22230Sstevel@tonic-gate 	struct group *grent;
22240Sstevel@tonic-gate 	struct cachenode *c;
22250Sstevel@tonic-gate 
22260Sstevel@tonic-gate 	if ((gid == lastgid) && lastgname)
22270Sstevel@tonic-gate 		return (lastgname);
22280Sstevel@tonic-gate 
22290Sstevel@tonic-gate 	c = findincache(&groups, gid);
22300Sstevel@tonic-gate 	if (c->initted == 0) {
22310Sstevel@tonic-gate 		if ((grent = getgrgid(gid)) != NULL) {
22320Sstevel@tonic-gate 			SCPYN(&c->name[0], grent->gr_name);
22330Sstevel@tonic-gate 		} else {
22340Sstevel@tonic-gate 			(void) sprintf(&c->name[0], "%-8u", (int)gid);
22350Sstevel@tonic-gate 		}
22360Sstevel@tonic-gate 		c->initted = 1;
22370Sstevel@tonic-gate 	}
22380Sstevel@tonic-gate 	lastgid = gid;
22390Sstevel@tonic-gate 	lastgname = &c->name[0];
22400Sstevel@tonic-gate 	return (lastgname);
22410Sstevel@tonic-gate }
22420Sstevel@tonic-gate 
22430Sstevel@tonic-gate /* return >0 if item pointed by pp2 should appear first */
22440Sstevel@tonic-gate static int
compar(struct lbuf ** pp1,struct lbuf ** pp2)22450Sstevel@tonic-gate compar(struct lbuf **pp1, struct lbuf **pp2)
22460Sstevel@tonic-gate {
22470Sstevel@tonic-gate 	struct lbuf *p1, *p2;
22480Sstevel@tonic-gate 
22490Sstevel@tonic-gate 	p1 = *pp1;
22500Sstevel@tonic-gate 	p2 = *pp2;
22510Sstevel@tonic-gate 	if (dflg == 0) {
22520Sstevel@tonic-gate /*
22530Sstevel@tonic-gate  * compare two names in ls-command one of which is file
22540Sstevel@tonic-gate  * and the other is a directory;
22550Sstevel@tonic-gate  * this portion is not used for comparing files within
22560Sstevel@tonic-gate  * a directory name of ls-command;
22570Sstevel@tonic-gate  */
22580Sstevel@tonic-gate 		if (p1->lflags&ISARG && p1->ltype == 'd') {
22590Sstevel@tonic-gate 			if (!(p2->lflags&ISARG && p2->ltype == 'd'))
22600Sstevel@tonic-gate 				return (1);
22610Sstevel@tonic-gate 		} else {
22620Sstevel@tonic-gate 			if (p2->lflags&ISARG && p2->ltype == 'd')
22630Sstevel@tonic-gate 				return (-1);
22640Sstevel@tonic-gate 		}
22650Sstevel@tonic-gate 	}
22660Sstevel@tonic-gate 	if (tflg) {
22670Sstevel@tonic-gate 		if (p2->lmtime.tv_sec > p1->lmtime.tv_sec)
22680Sstevel@tonic-gate 			return (rflg);
22690Sstevel@tonic-gate 		else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec)
22700Sstevel@tonic-gate 			return (-rflg);
22710Sstevel@tonic-gate 		/* times are equal to the sec, check nsec */
22720Sstevel@tonic-gate 		if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec)
22730Sstevel@tonic-gate 			return (rflg);
22740Sstevel@tonic-gate 		else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec)
22750Sstevel@tonic-gate 			return (-rflg);
22760Sstevel@tonic-gate 		/* if times are equal, fall through and sort by name */
22770Sstevel@tonic-gate 	} else if (Sflg) {
22780Sstevel@tonic-gate 		/*
22790Sstevel@tonic-gate 		 * The size stored in lsize can be either the
22800Sstevel@tonic-gate 		 * size or the major minor number (in the case of
22810Sstevel@tonic-gate 		 * block and character special devices).  If it's
22820Sstevel@tonic-gate 		 * a major minor number, then the size is considered
22830Sstevel@tonic-gate 		 * to be zero and we want to fall through and sort
22840Sstevel@tonic-gate 		 * by name.  In addition, if the size of p2 is equal
22850Sstevel@tonic-gate 		 * to the size of p1 we want to fall through and
22860Sstevel@tonic-gate 		 * sort by name.
22870Sstevel@tonic-gate 		 */
22880Sstevel@tonic-gate 		off_t	p1size = (p1->ltype == 'b') ||
22895331Samw 		    (p1->ltype == 'c') ? 0 : p1->lsize;
22900Sstevel@tonic-gate 		off_t	p2size = (p2->ltype == 'b') ||
22915331Samw 		    (p2->ltype == 'c') ? 0 : p2->lsize;
22920Sstevel@tonic-gate 		if (p2size > p1size) {
22930Sstevel@tonic-gate 			return (rflg);
22940Sstevel@tonic-gate 		} else if (p2size < p1size) {
22950Sstevel@tonic-gate 			return (-rflg);
22960Sstevel@tonic-gate 		}
22970Sstevel@tonic-gate 		/* Sizes are equal, fall through and sort by name. */
22980Sstevel@tonic-gate 	}
22990Sstevel@tonic-gate 	return (rflg * strcoll(
23000Sstevel@tonic-gate 	    p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname,
23010Sstevel@tonic-gate 	    p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname));
23020Sstevel@tonic-gate }
23030Sstevel@tonic-gate 
23040Sstevel@tonic-gate static void
pprintf(char * s1,char * s2)23050Sstevel@tonic-gate pprintf(char *s1, char *s2)
23060Sstevel@tonic-gate {
23070Sstevel@tonic-gate 	csi_pprintf((unsigned char *)s1);
23080Sstevel@tonic-gate 	csi_pprintf((unsigned char *)s2);
23090Sstevel@tonic-gate }
23100Sstevel@tonic-gate 
23110Sstevel@tonic-gate static void
csi_pprintf(unsigned char * s)23120Sstevel@tonic-gate csi_pprintf(unsigned char *s)
23130Sstevel@tonic-gate {
23140Sstevel@tonic-gate 	unsigned char *cp;
23150Sstevel@tonic-gate 	char	c;
23160Sstevel@tonic-gate 	int	i;
23170Sstevel@tonic-gate 	int	c_len;
23180Sstevel@tonic-gate 	int	p_col;
23190Sstevel@tonic-gate 	wchar_t	pcode;
23200Sstevel@tonic-gate 
23210Sstevel@tonic-gate 	if (!qflg && !bflg) {
23220Sstevel@tonic-gate 		for (cp = s; *cp != '\0'; cp++) {
23230Sstevel@tonic-gate 			(void) putchar(*cp);
23240Sstevel@tonic-gate 			curcol++;
23250Sstevel@tonic-gate 		}
23260Sstevel@tonic-gate 		return;
23270Sstevel@tonic-gate 	}
23280Sstevel@tonic-gate 
23290Sstevel@tonic-gate 	for (cp = s; *cp; ) {
23300Sstevel@tonic-gate 		if (isascii(c = *cp)) {
23310Sstevel@tonic-gate 			if (!isprint(c)) {
23320Sstevel@tonic-gate 				if (qflg) {
23330Sstevel@tonic-gate 					c = '?';
23340Sstevel@tonic-gate 				} else {
23350Sstevel@tonic-gate 					curcol += 3;
23360Sstevel@tonic-gate 					(void) putc('\\', stdout);
23370Sstevel@tonic-gate 					c = '0' + ((*cp >> 6) & 07);
23380Sstevel@tonic-gate 					(void) putc(c, stdout);
23390Sstevel@tonic-gate 					c = '0' + ((*cp >> 3) & 07);
23400Sstevel@tonic-gate 					(void) putc(c, stdout);
23410Sstevel@tonic-gate 					c = '0' + (*cp & 07);
23420Sstevel@tonic-gate 				}
23430Sstevel@tonic-gate 			}
23440Sstevel@tonic-gate 			curcol++;
23450Sstevel@tonic-gate 			cp++;
23460Sstevel@tonic-gate 			(void) putc(c, stdout);
23470Sstevel@tonic-gate 			continue;
23480Sstevel@tonic-gate 		}
23490Sstevel@tonic-gate 
23500Sstevel@tonic-gate 		if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) {
23510Sstevel@tonic-gate 			c_len = 1;
23520Sstevel@tonic-gate 			goto not_print;
23530Sstevel@tonic-gate 		}
23540Sstevel@tonic-gate 
23550Sstevel@tonic-gate 		if ((p_col = wcwidth(pcode)) > 0) {
23560Sstevel@tonic-gate 			(void) putwchar(pcode);
23570Sstevel@tonic-gate 			cp += c_len;
23580Sstevel@tonic-gate 			curcol += p_col;
23590Sstevel@tonic-gate 			continue;
23600Sstevel@tonic-gate 		}
23610Sstevel@tonic-gate 
23620Sstevel@tonic-gate not_print:
23630Sstevel@tonic-gate 		for (i = 0; i < c_len; i++) {
23640Sstevel@tonic-gate 			if (qflg) {
23650Sstevel@tonic-gate 				c = '?';
23660Sstevel@tonic-gate 			} else {
23670Sstevel@tonic-gate 				curcol += 3;
23680Sstevel@tonic-gate 				(void) putc('\\', stdout);
23690Sstevel@tonic-gate 				c = '0' + ((*cp >> 6) & 07);
23700Sstevel@tonic-gate 				(void) putc(c, stdout);
23710Sstevel@tonic-gate 				c = '0' + ((*cp >> 3) & 07);
23720Sstevel@tonic-gate 				(void) putc(c, stdout);
23730Sstevel@tonic-gate 				c = '0' + (*cp & 07);
23740Sstevel@tonic-gate 			}
23750Sstevel@tonic-gate 			curcol++;
23760Sstevel@tonic-gate 			(void) putc(c, stdout);
23770Sstevel@tonic-gate 			cp++;
23780Sstevel@tonic-gate 		}
23790Sstevel@tonic-gate 	}
23800Sstevel@tonic-gate }
23810Sstevel@tonic-gate 
23820Sstevel@tonic-gate static int
strcol(unsigned char * s1)23830Sstevel@tonic-gate strcol(unsigned char *s1)
23840Sstevel@tonic-gate {
23850Sstevel@tonic-gate 	int	w;
23860Sstevel@tonic-gate 	int	w_col;
23870Sstevel@tonic-gate 	int	len;
23880Sstevel@tonic-gate 	wchar_t	wc;
23890Sstevel@tonic-gate 
23900Sstevel@tonic-gate 	w = 0;
23910Sstevel@tonic-gate 	while (*s1) {
23920Sstevel@tonic-gate 		if (isascii(*s1)) {
23930Sstevel@tonic-gate 			w++;
23940Sstevel@tonic-gate 			s1++;
23950Sstevel@tonic-gate 			continue;
23960Sstevel@tonic-gate 		}
23970Sstevel@tonic-gate 
23980Sstevel@tonic-gate 		if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
23990Sstevel@tonic-gate 			w++;
24000Sstevel@tonic-gate 			s1++;
24010Sstevel@tonic-gate 			continue;
24020Sstevel@tonic-gate 		}
24030Sstevel@tonic-gate 
24040Sstevel@tonic-gate 		if ((w_col = wcwidth(wc)) < 0)
24050Sstevel@tonic-gate 			w_col = len;
24060Sstevel@tonic-gate 		s1 += len;
24070Sstevel@tonic-gate 		w += w_col;
24080Sstevel@tonic-gate 	}
24090Sstevel@tonic-gate 	return (w);
24100Sstevel@tonic-gate }
24110Sstevel@tonic-gate 
24120Sstevel@tonic-gate /*
24130Sstevel@tonic-gate  * Convert an unsigned long long to a string representation and place the
24140Sstevel@tonic-gate  * result in the caller-supplied buffer.
24150Sstevel@tonic-gate  *
24160Sstevel@tonic-gate  * The number provided is a size in bytes.  The number is first
24170Sstevel@tonic-gate  * converted to an integral multiple of 'scale' bytes.  This new
24180Sstevel@tonic-gate  * number is then scaled down until it is small enough to be in a good
24190Sstevel@tonic-gate  * human readable format, i.e.  in the range 0 thru scale-1.  If the
24200Sstevel@tonic-gate  * number used to derive the final number is not a multiple of scale, and
24210Sstevel@tonic-gate  * the final number has only a single significant digit, we compute
24220Sstevel@tonic-gate  * tenths of units to provide a second significant digit.
24230Sstevel@tonic-gate  *
24240Sstevel@tonic-gate  * The value "(unsigned long long)-1" is a special case and is always
24250Sstevel@tonic-gate  * converted to "-1".
24260Sstevel@tonic-gate  *
24270Sstevel@tonic-gate  * A pointer to the caller-supplied buffer is returned.
24280Sstevel@tonic-gate  */
24290Sstevel@tonic-gate static char *
number_to_scaled_string(numbuf_t buf,unsigned long long number,long scale)24300Sstevel@tonic-gate number_to_scaled_string(
24310Sstevel@tonic-gate 			numbuf_t buf,		/* put the result here */
24320Sstevel@tonic-gate 			unsigned long long number, /* convert this number */
24330Sstevel@tonic-gate 			long scale)
24340Sstevel@tonic-gate {
24350Sstevel@tonic-gate 	unsigned long long save;
24360Sstevel@tonic-gate 	/* Measurement: kilo, mega, giga, tera, peta, exa */
24370Sstevel@tonic-gate 	char *uom = "KMGTPE";
24380Sstevel@tonic-gate 
24390Sstevel@tonic-gate 	if ((long long)number == (long long)-1) {
24400Sstevel@tonic-gate 		(void) strlcpy(buf, "-1", sizeof (numbuf_t));
24410Sstevel@tonic-gate 		return (buf);
24420Sstevel@tonic-gate 	}
24430Sstevel@tonic-gate 
24440Sstevel@tonic-gate 	save = number;
24450Sstevel@tonic-gate 	number = number / scale;
24460Sstevel@tonic-gate 
24470Sstevel@tonic-gate 	/*
24480Sstevel@tonic-gate 	 * Now we have number as a count of scale units.
24490Sstevel@tonic-gate 	 * If no further scaling is necessary, we round up as appropriate.
24500Sstevel@tonic-gate 	 *
24510Sstevel@tonic-gate 	 * The largest value number could have had entering the routine is
24520Sstevel@tonic-gate 	 * 16 Exabytes, so running off the end of the uom array should
24530Sstevel@tonic-gate 	 * never happen.  We check for that, though, as a guard against
24540Sstevel@tonic-gate 	 * a breakdown elsewhere in the algorithm.
24550Sstevel@tonic-gate 	 */
24560Sstevel@tonic-gate 	if (number < (unsigned long long)scale) {
24570Sstevel@tonic-gate 		if ((save % scale) >= (unsigned long long)(scale / 2)) {
24580Sstevel@tonic-gate 			if (++number == (unsigned long long)scale) {
24590Sstevel@tonic-gate 				uom++;
24600Sstevel@tonic-gate 				number = 1;
24610Sstevel@tonic-gate 			}
24620Sstevel@tonic-gate 		}
24630Sstevel@tonic-gate 	} else {
24640Sstevel@tonic-gate 		while ((number >= (unsigned long long)scale) && (*uom != 'E')) {
24650Sstevel@tonic-gate 			uom++; /* next unit of measurement */
24660Sstevel@tonic-gate 			save = number;
24670Sstevel@tonic-gate 			/*
24680Sstevel@tonic-gate 			 * If we're over half way to the next unit of
24690Sstevel@tonic-gate 			 * 'scale' bytes (which means we should round
24700Sstevel@tonic-gate 			 * up), then adding half of 'scale' prior to
24710Sstevel@tonic-gate 			 * the division will push us into that next
24720Sstevel@tonic-gate 			 * unit of scale when we perform the division
24730Sstevel@tonic-gate 			 */
24740Sstevel@tonic-gate 			number = (number + (scale / 2)) / scale;
24750Sstevel@tonic-gate 		}
24760Sstevel@tonic-gate 	}
24770Sstevel@tonic-gate 
24780Sstevel@tonic-gate 	/* check if we should output a decimal place after the point */
24790Sstevel@tonic-gate 	if ((save / scale) < 10) {
24800Sstevel@tonic-gate 		/* snprintf() will round for us */
24810Sstevel@tonic-gate 		float fnum = (float)save / scale;
24820Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c",
24830Sstevel@tonic-gate 		    fnum, *uom);
24840Sstevel@tonic-gate 	} else {
24850Sstevel@tonic-gate 		(void) snprintf(buf, sizeof (numbuf_t), "%4llu%c",
24860Sstevel@tonic-gate 		    number, *uom);
24870Sstevel@tonic-gate 	}
24880Sstevel@tonic-gate 	return (buf);
24890Sstevel@tonic-gate }
24905331Samw 
24915331Samw /* Get extended system attributes and set the display */
24925331Samw 
24935331Samw int
get_sysxattr(char * fname,struct lbuf * rep)24945331Samw get_sysxattr(char *fname, struct lbuf *rep)
24955331Samw {
24965331Samw 	boolean_t	value;
24975331Samw 	data_type_t	type;
24985331Samw 	int		error;
24995331Samw 	char		*name;
25005331Samw 	int		i;
25015331Samw 
25025331Samw 	if ((error = getattrat(AT_FDCWD, XATTR_VIEW_READWRITE, fname,
25035331Samw 	    &response)) != 0) {
25045331Samw 		perror("ls:getattrat");
25055331Samw 		return (error);
25065331Samw 	}
25075331Samw 
25085331Samw 	/*
25095331Samw 	 * Allocate 'sacnt' size array to hold extended timestamp
25105331Samw 	 * system attributes and initialize the array.
25115331Samw 	 */
25125331Samw 	rep->extm = xmalloc(sacnt * sizeof (struct attrtm), rep);
25135331Samw 	for (i = 0; i < sacnt; i++) {
25145331Samw 		rep->extm[i].stm = 0;
25155331Samw 		rep->extm[i].nstm = 0;
25165331Samw 		rep->extm[i].name = NULL;
25175331Samw 	}
25185331Samw 	while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
25195331Samw 		name = nvpair_name(pair);
25205331Samw 		type = nvpair_type(pair);
25215331Samw 		if (type == DATA_TYPE_BOOLEAN_VALUE) {
25225331Samw 			error = nvpair_value_boolean_value(pair, &value);
25235331Samw 			if (error) {
25245331Samw 				(void) fprintf(stderr,
25255331Samw 				    gettext("nvpair_value_boolean_value "
25265331Samw 				    "failed: error = %d\n"), error);
25275331Samw 				continue;
25285331Samw 			}
25295331Samw 			if (name != NULL)
25305331Samw 				set_sysattrb_display(name, value, rep);
25315331Samw 			continue;
25325331Samw 		} else if (type == DATA_TYPE_UINT64_ARRAY) {
25335331Samw 			if (name != NULL)
25345331Samw 				set_sysattrtm_display(name, rep);
25355331Samw 			continue;
25365331Samw 		}
25375331Samw 	}
25385331Samw 	nvlist_free(response);
25395331Samw 	return (0);
25405331Samw }
25415331Samw 
25425331Samw /* Set extended system attribute boolean display */
25435331Samw 
25445331Samw void
set_sysattrb_display(char * name,boolean_t val,struct lbuf * rep)25455331Samw set_sysattrb_display(char *name, boolean_t val, struct lbuf *rep)
25465331Samw {
25475331Samw 	f_attr_t	fattr;
25485331Samw 	const char	*opt;
25495331Samw 	size_t		len;
25505331Samw 
25515331Samw 	fattr = name_to_attr(name);
25525331Samw 	if (fattr != F_ATTR_INVAL && fattr < sacnt) {
25535331Samw 		if (vopt) {
25545331Samw 			len = strlen(name);
25555331Samw 			if (val) {
25565331Samw 				rep->exttr[fattr].name = xmalloc(len + 1, rep);
25575331Samw 				(void) strlcpy(rep->exttr[fattr].name, name,
25585331Samw 				    len + 1);
25595331Samw 			} else {
25605331Samw 				rep->exttr[fattr].name = xmalloc(len + 3, rep);
25615331Samw 				(void) snprintf(rep->exttr[fattr].name, len + 3,
25625331Samw 				    "no%s", name);
25635331Samw 			}
25645331Samw 		} else {
25655331Samw 			opt = attr_to_option(fattr);
25665331Samw 			if (opt != NULL) {
25675331Samw 				len = strlen(opt);
25685331Samw 				rep->exttr[fattr].name = xmalloc(len + 1, rep);
25695331Samw 				if (val)
25705331Samw 					(void) strlcpy(rep->exttr[fattr].name,
25715331Samw 					    opt, len + 1);
25725331Samw 				else
25735331Samw 					(void) strlcpy(rep->exttr[fattr].name,
25745331Samw 					    "-", len + 1);
25755331Samw 			}
25765331Samw 		}
25775331Samw 	}
25785331Samw }
25795331Samw 
25805331Samw /* Set extended system attribute timestamp display */
25815331Samw 
25825331Samw void
set_sysattrtm_display(char * name,struct lbuf * rep)25835331Samw set_sysattrtm_display(char *name, struct lbuf *rep)
25845331Samw {
25855331Samw 	uint_t		nelem;
25865331Samw 	uint64_t	*value;
25875331Samw 	int		i;
25885331Samw 	size_t		len;
25895331Samw 
25905331Samw 	if (nvpair_value_uint64_array(pair, &value, &nelem) == 0) {
25915331Samw 		if (*value != NULL) {
25925331Samw 			len = strlen(name);
25935331Samw 			i = 0;
25945331Samw 			while (rep->extm[i].stm != 0 && i < sacnt)
25955331Samw 				i++;
25965331Samw 			rep->extm[i].stm = value[0];
25975331Samw 			rep->extm[i].nstm = value[1];
25985331Samw 			rep->extm[i].name = xmalloc(len + 1, rep);
25995331Samw 			(void) strlcpy(rep->extm[i].name, name, len + 1);
26005331Samw 		}
26015331Samw 	}
26025331Samw }
26035331Samw 
26045331Samw void
format_time(time_t sec,time_t nsec)26059664Sjason@ansipunx.net format_time(time_t sec, time_t nsec)
26065331Samw {
26079664Sjason@ansipunx.net 	const char *fstr = time_fmt_new;
26089664Sjason@ansipunx.net 	char fmt_buf[FMTSIZE];
26095331Samw 
26109664Sjason@ansipunx.net 	if (Eflg) {
26119664Sjason@ansipunx.net 		(void) snprintf(fmt_buf, FMTSIZE, fstr, nsec);
26129664Sjason@ansipunx.net 		(void) strftime(time_buf, sizeof (time_buf), fmt_buf,
26139664Sjason@ansipunx.net 		    localtime(&sec));
26149664Sjason@ansipunx.net 		return;
26159664Sjason@ansipunx.net 	}
26169664Sjason@ansipunx.net 
26179664Sjason@ansipunx.net 	if (sec < year || sec > now)
26189664Sjason@ansipunx.net 		fstr = time_fmt_old;
26199664Sjason@ansipunx.net 
26209664Sjason@ansipunx.net 	/* if a custom time was specified, shouldn't be localized */
26215331Samw 	(void) strftime(time_buf, sizeof (time_buf),
26229664Sjason@ansipunx.net 	    (time_custom == 0) ? dcgettext(NULL, fstr, LC_TIME) : fstr,
26235331Samw 	    localtime(&sec));
26245331Samw }
26255331Samw 
26265331Samw void
format_attrtime(struct lbuf * p)26275331Samw format_attrtime(struct lbuf *p)
26285331Samw {
26299664Sjason@ansipunx.net 	int tmattr = 0;
26305331Samw 	int i;
26315331Samw 
26325331Samw 	if (p->extm != NULL) {
26335331Samw 		for (i = 0; i < sacnt; i++) {
26345331Samw 			if (p->extm[i].name != NULL) {
26355331Samw 				tmattr = 1;
26365331Samw 				break;
26375331Samw 			}
26385331Samw 		}
26395331Samw 	}
26409664Sjason@ansipunx.net 
26415331Samw 	if (tmattr) {
26429664Sjason@ansipunx.net 		const char *old_save = time_fmt_old;
26439664Sjason@ansipunx.net 		const char *new_save = time_fmt_new;
26449664Sjason@ansipunx.net 
26459664Sjason@ansipunx.net 		/* Eflg always sets format to FORMAT_ISO_FULL */
26469664Sjason@ansipunx.net 		if (!Eflg && !time_custom) {
26479664Sjason@ansipunx.net 			time_fmt_old = FORMAT_OLD;
26489664Sjason@ansipunx.net 			time_fmt_new = FORMAT_NEW;
26495331Samw 		}
26509664Sjason@ansipunx.net 
26519664Sjason@ansipunx.net 		format_time((time_t)p->extm[i].stm, (time_t)p->extm[i].nstm);
26529664Sjason@ansipunx.net 
26539664Sjason@ansipunx.net 		time_fmt_old = old_save;
26549664Sjason@ansipunx.net 		time_fmt_new = new_save;
26555331Samw 	}
26565331Samw }
26575331Samw 
26585331Samw void
print_time(struct lbuf * p)26595331Samw print_time(struct lbuf *p)
26605331Samw {
26619664Sjason@ansipunx.net 	const char *old_save = time_fmt_old;
26629664Sjason@ansipunx.net 	const char *new_save = time_fmt_new;
26639664Sjason@ansipunx.net 
26645331Samw 	int i = 0;
26655331Samw 
26669664Sjason@ansipunx.net 	if (!Eflg) {
26679664Sjason@ansipunx.net 		time_fmt_old = FORMAT_LONG;
26689664Sjason@ansipunx.net 		time_fmt_new = FORMAT_LONG;
26699664Sjason@ansipunx.net 	}
26709664Sjason@ansipunx.net 
26715331Samw 	new_line();
26729664Sjason@ansipunx.net 	format_time(p->lat.tv_sec, p->lat.tv_nsec);
26739664Sjason@ansipunx.net 	(void) printf("         timestamp: atime        %s\n", time_buf);
26749664Sjason@ansipunx.net 	format_time(p->lct.tv_sec, p->lct.tv_nsec);
26759664Sjason@ansipunx.net 	(void) printf("         timestamp: ctime        %s\n", time_buf);
26769664Sjason@ansipunx.net 	format_time(p->lmt.tv_sec, p->lmt.tv_nsec);
26779664Sjason@ansipunx.net 	(void) printf("         timestamp: mtime        %s\n", time_buf);
26789664Sjason@ansipunx.net 	if (p->extm != NULL) {
26799664Sjason@ansipunx.net 		while (p->extm[i].nstm != 0 && i < sacnt) {
26809664Sjason@ansipunx.net 			format_time(p->extm[i].stm, p->extm[i].nstm);
26819664Sjason@ansipunx.net 			if (p->extm[i].name != NULL) {
26829664Sjason@ansipunx.net 				(void) printf("         timestamp:"
26839664Sjason@ansipunx.net 				    " %s        %s\n",
26849664Sjason@ansipunx.net 				    p->extm[i].name, time_buf);
26859664Sjason@ansipunx.net 			}
26869664Sjason@ansipunx.net 			i++;
26879664Sjason@ansipunx.net 		}
26889664Sjason@ansipunx.net 	}
26899664Sjason@ansipunx.net 
26909664Sjason@ansipunx.net 	time_fmt_old = old_save;
26919664Sjason@ansipunx.net 	time_fmt_new = new_save;
26929664Sjason@ansipunx.net }
26939664Sjason@ansipunx.net 
26949664Sjason@ansipunx.net /*
26959664Sjason@ansipunx.net  * Check if color definition applies to entry, returns 1 if yes, 0 if no
26969664Sjason@ansipunx.net  */
26979664Sjason@ansipunx.net static int
color_match(const char * fname,mode_t mode,ls_color_t * color)269810059Sjason@ansipunx.net color_match(const char *fname, mode_t mode, ls_color_t *color)
26999664Sjason@ansipunx.net {
27009664Sjason@ansipunx.net 	switch (color->ftype) {
27019664Sjason@ansipunx.net 	case LS_PAT:
27029664Sjason@ansipunx.net 	{
27039664Sjason@ansipunx.net 		size_t	fname_len, sfx_len;
27049664Sjason@ansipunx.net 
27059664Sjason@ansipunx.net 		fname_len = strlen(fname);
27069664Sjason@ansipunx.net 		sfx_len = strlen(color->sfx);
27079664Sjason@ansipunx.net 		if (sfx_len > fname_len)
27089664Sjason@ansipunx.net 			return (0);
27099664Sjason@ansipunx.net 
27109664Sjason@ansipunx.net 		if (strcmp(color->sfx, fname + fname_len - sfx_len) == 0)
27119664Sjason@ansipunx.net 			return (1);
27129664Sjason@ansipunx.net 		else
27139664Sjason@ansipunx.net 			return (0);
27149664Sjason@ansipunx.net 	}
27159664Sjason@ansipunx.net 
27169664Sjason@ansipunx.net 	case LS_NORMAL:
27179664Sjason@ansipunx.net 		return (1);
27189664Sjason@ansipunx.net 
27199664Sjason@ansipunx.net 	case LS_FILE:
272010059Sjason@ansipunx.net 		return (S_ISREG(mode));
27219664Sjason@ansipunx.net 
27229664Sjason@ansipunx.net 	case LS_DIR:
272310059Sjason@ansipunx.net 		return (S_ISDIR(mode));
27249664Sjason@ansipunx.net 
27259664Sjason@ansipunx.net 	case LS_LINK:
272610059Sjason@ansipunx.net 		return (S_ISLNK(mode));
27279664Sjason@ansipunx.net 
27289664Sjason@ansipunx.net 	case LS_FIFO:
272910059Sjason@ansipunx.net 		return (S_ISFIFO(mode));
27309664Sjason@ansipunx.net 
27319664Sjason@ansipunx.net 	case LS_SOCK:
273210059Sjason@ansipunx.net 		return (S_ISSOCK(mode));
27339664Sjason@ansipunx.net 
27349664Sjason@ansipunx.net 	case LS_DOOR:
273510059Sjason@ansipunx.net 		return (S_ISDOOR(mode));
27369664Sjason@ansipunx.net 
27379664Sjason@ansipunx.net 	case LS_BLK:
273810059Sjason@ansipunx.net 		return (S_ISBLK(mode));
27399664Sjason@ansipunx.net 
27409664Sjason@ansipunx.net 	case LS_CHR:
274110059Sjason@ansipunx.net 		return (S_ISCHR(mode));
27429664Sjason@ansipunx.net 
27439664Sjason@ansipunx.net 	case LS_PORT:
274410059Sjason@ansipunx.net 		return (S_ISPORT(mode));
27459664Sjason@ansipunx.net 
27469664Sjason@ansipunx.net 	case LS_ORPHAN:
274710059Sjason@ansipunx.net 		/* this is tested for by gstat */
27489664Sjason@ansipunx.net 		return (0);
27499664Sjason@ansipunx.net 
27509664Sjason@ansipunx.net 	case LS_SETUID:
275110059Sjason@ansipunx.net 		return (!S_ISLNK(mode) && (mode & S_ISUID));
27529664Sjason@ansipunx.net 
27539664Sjason@ansipunx.net 	case LS_SETGID:
275410059Sjason@ansipunx.net 		return (!S_ISLNK(mode) && (mode & S_ISGID));
27559664Sjason@ansipunx.net 
27569664Sjason@ansipunx.net 	case LS_STICKY_OTHER_WRITABLE:
275710059Sjason@ansipunx.net 		return (!S_ISLNK(mode) && (mode & (S_IWOTH|S_ISVTX)));
27589664Sjason@ansipunx.net 
27599664Sjason@ansipunx.net 	case LS_OTHER_WRITABLE:
276010059Sjason@ansipunx.net 		return (!S_ISLNK(mode) && (mode & S_IWOTH));
27619664Sjason@ansipunx.net 
27629664Sjason@ansipunx.net 	case LS_STICKY:
276310059Sjason@ansipunx.net 		return (!S_ISLNK(mode) && (mode & S_ISVTX));
27649664Sjason@ansipunx.net 
27659664Sjason@ansipunx.net 	case LS_EXEC:
276610059Sjason@ansipunx.net 		return (!S_ISLNK(mode) && (mode & (S_IXUSR|S_IXGRP|S_IXOTH)));
27679664Sjason@ansipunx.net 	}
27689664Sjason@ansipunx.net 
27699664Sjason@ansipunx.net 	return (0);
27709664Sjason@ansipunx.net }
27719664Sjason@ansipunx.net 
27729664Sjason@ansipunx.net static void
dump_color(ls_color_t * c)27739664Sjason@ansipunx.net dump_color(ls_color_t *c)
27749664Sjason@ansipunx.net {
27759664Sjason@ansipunx.net 	if (c == NULL)
27769664Sjason@ansipunx.net 		return;
27779664Sjason@ansipunx.net 
27789664Sjason@ansipunx.net 	(void) printf("\n\ttype: ");
27799664Sjason@ansipunx.net 	switch (c->ftype) {
27809664Sjason@ansipunx.net 	case LS_NORMAL:
27819664Sjason@ansipunx.net 		(void) printf("LS_NORMAL");
27829664Sjason@ansipunx.net 		break;
27839664Sjason@ansipunx.net 	case LS_FILE:
27849664Sjason@ansipunx.net 		(void) printf("LS_FILE");
27859664Sjason@ansipunx.net 		break;
27869664Sjason@ansipunx.net 	case LS_EXEC:
27879664Sjason@ansipunx.net 		(void) printf("LS_EXEC");
27889664Sjason@ansipunx.net 		break;
27899664Sjason@ansipunx.net 	case LS_DIR:
27909664Sjason@ansipunx.net 		(void) printf("LS_DIR");
27919664Sjason@ansipunx.net 		break;
27929664Sjason@ansipunx.net 	case LS_LINK:
27939664Sjason@ansipunx.net 		(void) printf("LS_LINK");
27949664Sjason@ansipunx.net 		break;
27959664Sjason@ansipunx.net 
27969664Sjason@ansipunx.net 	case LS_FIFO:
27979664Sjason@ansipunx.net 		(void) printf("LS_FIFO");
27989664Sjason@ansipunx.net 		break;
27999664Sjason@ansipunx.net 
28009664Sjason@ansipunx.net 	case LS_SOCK:
28019664Sjason@ansipunx.net 		(void) printf("LS_SOCK");
28029664Sjason@ansipunx.net 		break;
28039664Sjason@ansipunx.net 
28049664Sjason@ansipunx.net 	case LS_DOOR:
28059664Sjason@ansipunx.net 		(void) printf("LS_DOOR");
28069664Sjason@ansipunx.net 		break;
28079664Sjason@ansipunx.net 
28089664Sjason@ansipunx.net 	case LS_BLK:
28099664Sjason@ansipunx.net 		(void) printf("LS_BLK");
28109664Sjason@ansipunx.net 		break;
28119664Sjason@ansipunx.net 
28129664Sjason@ansipunx.net 	case LS_CHR:
28139664Sjason@ansipunx.net 		(void) printf("LS_CHR");
28149664Sjason@ansipunx.net 		break;
28159664Sjason@ansipunx.net 
28169664Sjason@ansipunx.net 	case LS_PORT:
28179664Sjason@ansipunx.net 		(void) printf("LS_PORT");
28189664Sjason@ansipunx.net 		break;
28199664Sjason@ansipunx.net 
28209664Sjason@ansipunx.net 	case LS_STICKY:
28219664Sjason@ansipunx.net 		(void) printf("LS_STICKY");
28229664Sjason@ansipunx.net 		break;
28239664Sjason@ansipunx.net 
28249664Sjason@ansipunx.net 	case LS_ORPHAN:
28259664Sjason@ansipunx.net 		(void) printf("LS_ORPHAN");
28269664Sjason@ansipunx.net 		break;
28279664Sjason@ansipunx.net 
28289664Sjason@ansipunx.net 	case LS_SETGID:
28299664Sjason@ansipunx.net 		(void) printf("LS_SETGID");
28309664Sjason@ansipunx.net 		break;
28319664Sjason@ansipunx.net 
28329664Sjason@ansipunx.net 	case LS_SETUID:
28339664Sjason@ansipunx.net 		(void) printf("LS_SETUID");
28349664Sjason@ansipunx.net 		break;
28359664Sjason@ansipunx.net 
28369664Sjason@ansipunx.net 	case LS_OTHER_WRITABLE:
28379664Sjason@ansipunx.net 		(void) printf("LS_OTHER_WRITABLE");
28389664Sjason@ansipunx.net 		break;
28399664Sjason@ansipunx.net 
28409664Sjason@ansipunx.net 	case LS_STICKY_OTHER_WRITABLE:
28419664Sjason@ansipunx.net 		(void) printf("LS_STICKY_OTHER_WRITABLE");
28429664Sjason@ansipunx.net 		break;
28439664Sjason@ansipunx.net 
28449664Sjason@ansipunx.net 	case LS_PAT:
28459664Sjason@ansipunx.net 		(void) printf("LS_PAT\n");
28469664Sjason@ansipunx.net 		(void) printf("\tpattern: %s", c->sfx);
28479664Sjason@ansipunx.net 		break;
28489664Sjason@ansipunx.net 	}
28499664Sjason@ansipunx.net 	(void) printf("\n");
28509664Sjason@ansipunx.net 	(void) printf("\tattr: %d\n", c->attr);
28519664Sjason@ansipunx.net 	(void) printf("\tfg: %d\n", c->fg);
28529664Sjason@ansipunx.net 	(void) printf("\tbg: %d\n", c->bg);
28539664Sjason@ansipunx.net 	(void) printf("\t");
28549664Sjason@ansipunx.net }
28559664Sjason@ansipunx.net 
28569664Sjason@ansipunx.net static ls_color_t *
ls_color_find(const char * fname,mode_t mode)285710059Sjason@ansipunx.net ls_color_find(const char *fname, mode_t mode)
28589664Sjason@ansipunx.net {
28599664Sjason@ansipunx.net 	int i;
28609664Sjason@ansipunx.net 
28619664Sjason@ansipunx.net 	/*
28629664Sjason@ansipunx.net 	 * Colors are sorted from most general lsc_colors[0] to most specific
28639664Sjason@ansipunx.net 	 * lsc_colors[lsc_ncolors - 1] by ls_color_init().  Start search with
28649664Sjason@ansipunx.net 	 * most specific color rule and work towards most general.
28659664Sjason@ansipunx.net 	 */
28669664Sjason@ansipunx.net 	for (i = lsc_ncolors - 1; i >= 0; --i)
286710059Sjason@ansipunx.net 		if (color_match(fname, mode, &lsc_colors[i]))
28689664Sjason@ansipunx.net 			return (&lsc_colors[i]);
28699664Sjason@ansipunx.net 
28709664Sjason@ansipunx.net 	return (NULL);
28719664Sjason@ansipunx.net }
28729664Sjason@ansipunx.net 
28739664Sjason@ansipunx.net static void
ls_tprint(char * str,long int p1,long int p2,long int p3,long int p4,long int p5,long int p6,long int p7,long int p8,long int p9)28749664Sjason@ansipunx.net ls_tprint(char *str, long int p1, long int p2, long int p3, long int p4,
28759664Sjason@ansipunx.net     long int p5, long int p6, long int p7, long int p8, long int p9)
28769664Sjason@ansipunx.net {
28779664Sjason@ansipunx.net 	char *s;
28789664Sjason@ansipunx.net 
28799664Sjason@ansipunx.net 	if (str == NULL)
28809664Sjason@ansipunx.net 		return;
28819664Sjason@ansipunx.net 
28829664Sjason@ansipunx.net 	s = tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
28839664Sjason@ansipunx.net 
28849664Sjason@ansipunx.net 	if (s != NULL)
28859664Sjason@ansipunx.net 		(void) putp(s);
28869664Sjason@ansipunx.net }
28879664Sjason@ansipunx.net 
28889664Sjason@ansipunx.net static void
ls_start_color(ls_color_t * c)288910059Sjason@ansipunx.net ls_start_color(ls_color_t *c)
28909664Sjason@ansipunx.net {
28919664Sjason@ansipunx.net 	if (c == NULL)
28929664Sjason@ansipunx.net 		return;
28939664Sjason@ansipunx.net 
28949664Sjason@ansipunx.net 	if (lsc_debug)
28959664Sjason@ansipunx.net 		lsc_match = c;
28969664Sjason@ansipunx.net 
28979664Sjason@ansipunx.net 	if (c->attr & LSA_BOLD)
28989664Sjason@ansipunx.net 		ls_tprint(lsc_bold, 0, 0, 0, 0, 0, 0, 0, 0, 0);
28999664Sjason@ansipunx.net 	if (c->attr & LSA_UNDERSCORE)
29009664Sjason@ansipunx.net 		ls_tprint(lsc_underline, 0, 0, 0, 0, 0, 0, 0, 0, 0);
29019664Sjason@ansipunx.net 	if (c->attr & LSA_BLINK)
29029664Sjason@ansipunx.net 		ls_tprint(lsc_blink, 0, 0, 0, 0, 0, 0, 0, 0, 0);
29039664Sjason@ansipunx.net 	if (c->attr & LSA_REVERSE)
29049664Sjason@ansipunx.net 		ls_tprint(lsc_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0);
29059664Sjason@ansipunx.net 	if (c->attr & LSA_CONCEALED)
29069664Sjason@ansipunx.net 		ls_tprint(lsc_concealed, 0, 0, 0, 0, 0, 0, 0, 0, 0);
29079664Sjason@ansipunx.net 	if (c->attr == LSA_NONE)
29089664Sjason@ansipunx.net 		ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
29099664Sjason@ansipunx.net 
29109664Sjason@ansipunx.net 	if (c->fg != -1)
29119664Sjason@ansipunx.net 		ls_tprint(lsc_setfg, c->fg, 0, 0, 0, 0, 0, 0, 0, 0);
29129664Sjason@ansipunx.net 	if (c->bg != -1)
29139664Sjason@ansipunx.net 		ls_tprint(lsc_setbg, c->bg, 0, 0, 0, 0, 0, 0, 0, 0);
29149664Sjason@ansipunx.net }
29159664Sjason@ansipunx.net 
29169664Sjason@ansipunx.net static void
ls_end_color()29179664Sjason@ansipunx.net ls_end_color()
29189664Sjason@ansipunx.net {
29199664Sjason@ansipunx.net 	ls_tprint(lsc_none, 0, 0, 0, 0, 0, 0, 0, 0, 0);
29209664Sjason@ansipunx.net 	if (lsc_debug)
29219664Sjason@ansipunx.net 		dump_color(lsc_match);
29229664Sjason@ansipunx.net }
29239664Sjason@ansipunx.net 
29249664Sjason@ansipunx.net static void
new_color_entry(char * colorstr)29259664Sjason@ansipunx.net new_color_entry(char *colorstr)
29269664Sjason@ansipunx.net {
29279664Sjason@ansipunx.net 	static const struct {
29289664Sjason@ansipunx.net 		const char	*s;
29299664Sjason@ansipunx.net 		ls_cftype_t	stype;
29309664Sjason@ansipunx.net 	} type_map[] = {
29319664Sjason@ansipunx.net 		{ "no", LS_NORMAL },
29329664Sjason@ansipunx.net 		{ "fi", LS_FILE },
29339664Sjason@ansipunx.net 		{ "di", LS_DIR },
29349664Sjason@ansipunx.net 		{ "ln", LS_LINK },
29359664Sjason@ansipunx.net 		{ "pi", LS_FIFO },
29369664Sjason@ansipunx.net 		{ "so", LS_SOCK },
29379664Sjason@ansipunx.net 		{ "do", LS_DOOR },
29389664Sjason@ansipunx.net 		{ "bd", LS_BLK },
29399664Sjason@ansipunx.net 		{ "cd", LS_CHR },
29409664Sjason@ansipunx.net 		{ "or", LS_ORPHAN },
29419664Sjason@ansipunx.net 		{ "su", LS_SETUID },
29429664Sjason@ansipunx.net 		{ "sg", LS_SETGID },
29439664Sjason@ansipunx.net 		{ "tw", LS_STICKY_OTHER_WRITABLE },
29449664Sjason@ansipunx.net 		{ "ow", LS_OTHER_WRITABLE },
29459664Sjason@ansipunx.net 		{ "st", LS_STICKY },
29469664Sjason@ansipunx.net 		{ "ex", LS_EXEC },
29479664Sjason@ansipunx.net 		{ "po", LS_PORT },
29489664Sjason@ansipunx.net 		{ NULL, LS_NORMAL }
29499664Sjason@ansipunx.net 	};
29509664Sjason@ansipunx.net 
29519664Sjason@ansipunx.net 	char		*p, *lasts;
29529664Sjason@ansipunx.net 	int		i;
29539664Sjason@ansipunx.net 	int		color, attr;
29549664Sjason@ansipunx.net 
29559664Sjason@ansipunx.net 	p = strtok_r(colorstr, "=", &lasts);
29569664Sjason@ansipunx.net 	if (p == NULL) {
29579664Sjason@ansipunx.net 		colorflg = 0;
29589664Sjason@ansipunx.net 		return;
29599664Sjason@ansipunx.net 	}
29609664Sjason@ansipunx.net 
29619664Sjason@ansipunx.net 	if (p[0] == '*') {
29629664Sjason@ansipunx.net 		lsc_colors[lsc_ncolors].ftype = LS_PAT;
29639664Sjason@ansipunx.net 		/* don't include the * in the suffix */
29649664Sjason@ansipunx.net 		if ((lsc_colors[lsc_ncolors].sfx = strdup(p + 1)) == NULL) {
29659664Sjason@ansipunx.net 			colorflg = 0;
29669664Sjason@ansipunx.net 			return;
29679664Sjason@ansipunx.net 		}
29689664Sjason@ansipunx.net 	} else {
29699664Sjason@ansipunx.net 		lsc_colors[lsc_ncolors].sfx = NULL;
29709664Sjason@ansipunx.net 
29719664Sjason@ansipunx.net 		for (i = 0; type_map[i].s != NULL; ++i) {
29729664Sjason@ansipunx.net 			if (strncmp(type_map[i].s, p, 2) == 0)
29739664Sjason@ansipunx.net 				break;
29749664Sjason@ansipunx.net 		}
29759664Sjason@ansipunx.net 
29769664Sjason@ansipunx.net 		/* ignore unknown file types */
29779664Sjason@ansipunx.net 		if (type_map[i].s == NULL)
29789664Sjason@ansipunx.net 			return;
29799664Sjason@ansipunx.net 
29809664Sjason@ansipunx.net 		lsc_colors[lsc_ncolors].ftype = type_map[i].stype;
29819664Sjason@ansipunx.net 	}
29829664Sjason@ansipunx.net 
29839664Sjason@ansipunx.net 	attr = LSA_NONE;
29849664Sjason@ansipunx.net 	lsc_colors[lsc_ncolors].fg = -1;
29859664Sjason@ansipunx.net 	lsc_colors[lsc_ncolors].bg = -1;
29869664Sjason@ansipunx.net 	for (p = strtok_r(NULL, ";", &lasts); p != NULL;
29879664Sjason@ansipunx.net 	    p = strtok_r(NULL, ";", &lasts)) {
29889664Sjason@ansipunx.net 		color = strtol(p, NULL, 10);
29899664Sjason@ansipunx.net 
29909664Sjason@ansipunx.net 		if (color < 10) {
29919664Sjason@ansipunx.net 			switch (color) {
29929664Sjason@ansipunx.net 			case 0:
29939664Sjason@ansipunx.net 				attr = LSA_NONE;
29949664Sjason@ansipunx.net 				continue;
29959664Sjason@ansipunx.net 			case 1:
29969664Sjason@ansipunx.net 				attr |= LSA_BOLD;
29979664Sjason@ansipunx.net 				continue;
29989664Sjason@ansipunx.net 			case 4:
29999664Sjason@ansipunx.net 				attr |= LSA_UNDERSCORE;
30009664Sjason@ansipunx.net 				continue;
30019664Sjason@ansipunx.net 			case 5:
30029664Sjason@ansipunx.net 				attr |= LSA_BLINK;
30039664Sjason@ansipunx.net 				continue;
30049664Sjason@ansipunx.net 			case 7:
30059664Sjason@ansipunx.net 				attr |= LSA_REVERSE;
30069664Sjason@ansipunx.net 				continue;
30079664Sjason@ansipunx.net 			case 8:
30089664Sjason@ansipunx.net 				attr |= LSA_CONCEALED;
30099664Sjason@ansipunx.net 				continue;
30109664Sjason@ansipunx.net 			default:
30119664Sjason@ansipunx.net 				continue;
30125331Samw 			}
30135331Samw 		}
30149664Sjason@ansipunx.net 
30159664Sjason@ansipunx.net 		if (color < 40)
30169664Sjason@ansipunx.net 			lsc_colors[lsc_ncolors].fg = color - 30;
30179664Sjason@ansipunx.net 		else
30189664Sjason@ansipunx.net 			lsc_colors[lsc_ncolors].bg = color - 40;
30199664Sjason@ansipunx.net 	}
30209664Sjason@ansipunx.net 
30219664Sjason@ansipunx.net 	lsc_colors[lsc_ncolors].attr = attr;
30229664Sjason@ansipunx.net 	++lsc_ncolors;
30239664Sjason@ansipunx.net }
30249664Sjason@ansipunx.net 
30259664Sjason@ansipunx.net static int
ls_color_compare(const void * p1,const void * p2)30269664Sjason@ansipunx.net ls_color_compare(const void *p1, const void *p2)
30279664Sjason@ansipunx.net {
30289664Sjason@ansipunx.net 	const ls_color_t *c1 = (const ls_color_t *)p1;
30299664Sjason@ansipunx.net 	const ls_color_t *c2 = (const ls_color_t *)p2;
30309664Sjason@ansipunx.net 
30319664Sjason@ansipunx.net 	int ret = c1->ftype - c2->ftype;
30329664Sjason@ansipunx.net 
30339664Sjason@ansipunx.net 	if (ret != 0)
30349664Sjason@ansipunx.net 		return (ret);
30359664Sjason@ansipunx.net 
30369664Sjason@ansipunx.net 	if (c1->ftype != LS_PAT)
30379664Sjason@ansipunx.net 		return (ret);
30389664Sjason@ansipunx.net 
30399664Sjason@ansipunx.net 	return (strcmp(c1->sfx, c2->sfx));
30409664Sjason@ansipunx.net }
30419664Sjason@ansipunx.net 
30429664Sjason@ansipunx.net static void
ls_color_init()30439664Sjason@ansipunx.net ls_color_init()
30449664Sjason@ansipunx.net {
30459664Sjason@ansipunx.net 	static char *default_colorstr = "no=00:fi=00:di=01;34:ln=01;36:po=01;35"
30469664Sjason@ansipunx.net 	    ":pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01"
30479664Sjason@ansipunx.net 	    ":su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31"
30489664Sjason@ansipunx.net 	    ":*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31"
30499664Sjason@ansipunx.net 	    ":*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31"
30509664Sjason@ansipunx.net 	    ":*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35"
30519664Sjason@ansipunx.net 	    ":*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"
30529664Sjason@ansipunx.net 	    ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35"
30539664Sjason@ansipunx.net 	    ":*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35"
30549664Sjason@ansipunx.net 	    ":*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35"
30559664Sjason@ansipunx.net 	    ":*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35";
30569664Sjason@ansipunx.net 
30579664Sjason@ansipunx.net 	char    *colorstr;
30589664Sjason@ansipunx.net 	char    *p, *lasts;
30599664Sjason@ansipunx.net 	size_t  color_sz;
30609664Sjason@ansipunx.net 	int	termret;
306110059Sjason@ansipunx.net 	int	i;
30629664Sjason@ansipunx.net 
30639664Sjason@ansipunx.net 	(void) setupterm(NULL, 1, &termret);
30649664Sjason@ansipunx.net 	if (termret != 1)
30659664Sjason@ansipunx.net 		return;
30669664Sjason@ansipunx.net 
30679664Sjason@ansipunx.net 	if ((colorstr = getenv("LS_COLORS")) == NULL)
30689664Sjason@ansipunx.net 		colorstr = default_colorstr;
30699664Sjason@ansipunx.net 
30709689Sjason@ansipunx.net 	/*
30719689Sjason@ansipunx.net 	 * Determine the size of lsc_colors.  color_sz can be > lsc_ncolors
30729689Sjason@ansipunx.net 	 * if there are invalid entries passed in the string (they are ignored)
30739689Sjason@ansipunx.net 	 */
30749689Sjason@ansipunx.net 	color_sz = 1;
30759664Sjason@ansipunx.net 	for (p = strchr(colorstr, ':'); p != NULL && *p != '\0';
30769664Sjason@ansipunx.net 	    p = strchr(++p, ':'))
30779664Sjason@ansipunx.net 		++color_sz;
30789664Sjason@ansipunx.net 
30799664Sjason@ansipunx.net 	lsc_colors = calloc(color_sz, sizeof (ls_color_t));
30809664Sjason@ansipunx.net 	if (lsc_colors == NULL) {
30819664Sjason@ansipunx.net 		free(colorstr);
30829664Sjason@ansipunx.net 		return;
30839664Sjason@ansipunx.net 	}
30849664Sjason@ansipunx.net 
30859664Sjason@ansipunx.net 	for (p = strtok_r(colorstr, ":", &lasts);
30869664Sjason@ansipunx.net 	    p != NULL && lsc_ncolors < color_sz;
30879664Sjason@ansipunx.net 	    p = strtok_r(NULL, ":", &lasts))
30889664Sjason@ansipunx.net 		new_color_entry(p);
30899664Sjason@ansipunx.net 
30909664Sjason@ansipunx.net 	qsort((void *)lsc_colors, lsc_ncolors, sizeof (ls_color_t),
30919664Sjason@ansipunx.net 	    ls_color_compare);
30929664Sjason@ansipunx.net 
309310059Sjason@ansipunx.net 	for (i = 0; i < lsc_ncolors; ++i)
309410059Sjason@ansipunx.net 		if (lsc_colors[i].ftype == LS_ORPHAN) {
309510059Sjason@ansipunx.net 			lsc_orphan = &lsc_colors[i];
309610059Sjason@ansipunx.net 			break;
309710059Sjason@ansipunx.net 		}
309810059Sjason@ansipunx.net 
30999664Sjason@ansipunx.net 	if ((lsc_bold = tigetstr("bold")) == (char *)-1)
31009664Sjason@ansipunx.net 		lsc_bold = NULL;
31019664Sjason@ansipunx.net 
31029664Sjason@ansipunx.net 	if ((lsc_underline = tigetstr("smul")) == (char *)-1)
31039664Sjason@ansipunx.net 		lsc_underline = NULL;
31049664Sjason@ansipunx.net 
31059664Sjason@ansipunx.net 	if ((lsc_blink = tigetstr("blink")) == (char *)-1)
31069664Sjason@ansipunx.net 		lsc_blink = NULL;
31079664Sjason@ansipunx.net 
31089664Sjason@ansipunx.net 	if ((lsc_reverse = tigetstr("rev")) == (char *)-1)
31099664Sjason@ansipunx.net 		lsc_reverse = NULL;
31109664Sjason@ansipunx.net 
31119664Sjason@ansipunx.net 	if ((lsc_concealed = tigetstr("prot")) == (char *)-1)
31129664Sjason@ansipunx.net 		lsc_concealed = NULL;
31139664Sjason@ansipunx.net 
31149664Sjason@ansipunx.net 	if ((lsc_none = tigetstr("sgr0")) == (char *)-1)
31159664Sjason@ansipunx.net 		lsc_none = NULL;
31169664Sjason@ansipunx.net 
31179664Sjason@ansipunx.net 	if ((lsc_setfg = tigetstr("setaf")) == (char *)-1)
31189664Sjason@ansipunx.net 		lsc_setfg = NULL;
31199664Sjason@ansipunx.net 
31209664Sjason@ansipunx.net 	if ((lsc_setbg = tigetstr("setab")) == (char *)-1)
31219664Sjason@ansipunx.net 		lsc_setbg = NULL;
31229664Sjason@ansipunx.net 
31239664Sjason@ansipunx.net 	if (getenv("_LS_COLOR_DEBUG") != NULL) {
31249664Sjason@ansipunx.net 		int i;
31259664Sjason@ansipunx.net 
31269664Sjason@ansipunx.net 		lsc_debug = 1;
31279664Sjason@ansipunx.net 		for (i = 0; i < lsc_ncolors; ++i)
31289664Sjason@ansipunx.net 			dump_color(&lsc_colors[i]);
31295331Samw 	}
31305331Samw }
31315331Samw 
31325331Samw /* Free extended system attribute lists */
31335331Samw 
31345331Samw void
free_sysattr(struct lbuf * p)31355331Samw free_sysattr(struct lbuf *p)
31365331Samw {
31375331Samw 	int i;
31385331Samw 
31395331Samw 	if (p->exttr != NULL) {
31405331Samw 		for (i = 0; i < sacnt; i++) {
31415331Samw 			if (p->exttr[i].name != NULL)
31425331Samw 				free(p->exttr[i].name);
31435331Samw 		}
31445331Samw 		free(p->exttr);
31455331Samw 	}
31465331Samw 	if (p->extm != NULL) {
31475331Samw 		for (i = 0; i < sacnt; i++) {
31485331Samw 			if (p->extm[i].name != NULL)
31495331Samw 				free(p->extm[i].name);
31505331Samw 		}
31515331Samw 		free(p->extm);
31525331Samw 	}
31535331Samw }
31545331Samw 
31555331Samw /* Allocate extended system attribute list */
31565331Samw 
31575331Samw void *
xmalloc(size_t size,struct lbuf * p)31585331Samw xmalloc(size_t size, struct lbuf *p)
31595331Samw {
31605331Samw 	if ((p = malloc(size)) == NULL) {
31615331Samw 		perror("ls");
31625331Samw 		free_sysattr(p);
31635331Samw 		nvlist_free(response);
31645331Samw 		exit(2);
31655331Samw 	}
31665331Samw 	return (p);
31675331Samw }
3168