xref: /titanic_44/usr/src/cmd/ls/ls.c (revision 7257d1b4d25bfac0c802847390e98a464fd787ac)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
30 /*	  All Rights Reserved	*/
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 
34 /*
35  * List files or directories
36  */
37 
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/mkdev.h>
41 #include <sys/stat.h>
42 #include <sys/acl.h>
43 
44 #include <wchar.h>
45 #include <stdio.h>
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <string.h>
49 #include <locale.h>
50 #include <curses.h>
51 #include <termios.h>
52 #include <stdlib.h>
53 #include <widec.h>
54 #include <locale.h>
55 #include <wctype.h>
56 #include <pwd.h>
57 #include <grp.h>
58 #include <limits.h>
59 #include <fcntl.h>
60 #include <unistd.h>
61 #include <libgen.h>
62 #include <errno.h>
63 #include <aclutils.h>
64 #include <libnvpair.h>
65 #include <libcmdutils.h>
66 #include <attr.h>
67 
68 #ifndef STANDALONE
69 #define	TERMINFO
70 #endif
71 
72 /*
73  * -DNOTERMINFO can be defined on the cc command line to prevent
74  * the use of terminfo.  This should be done on systems not having
75  * the terminfo feature(pre 6.0 systems ?).
76  * As a result, columnar listings assume 80 columns for output,
77  * unless told otherwise via the COLUMNS environment variable.
78  */
79 #ifdef NOTERMINFO
80 #undef TERMINFO
81 #endif
82 
83 #include <term.h>
84 
85 #define	BFSIZE	16
86 /* this bit equals 1 in lflags of structure lbuf if *namep is to be used */
87 #define	ISARG	0100000
88 
89 /*
90  * this flag has been added to manipulate the display of S instead of 'l' when
91  * the file is not a regular file and when group execution bit is off
92  */
93 #define	LS_NOTREG	010000
94 
95 
96 /*
97  * Date and time formats
98  *
99  * b --- abbreviated month name
100  * e --- day number
101  * Y --- year in the form ccyy
102  * H --- hour(24-hour version)
103  * M --- minute
104  * F --- yyyy-mm-dd
105  * T --- hh:mm:ss
106  * z --- time zone as hours displacement from UTC
107  * note that %F and %z are from the ISO C99 standard and are
108  * not present in older C libraries
109  */
110 #define	FORMAT1	 " %b %e  %Y "
111 #define	FORMAT2  " %b %e %H:%M "
112 #define	FORMAT3  " %b %e %T %Y "
113 #define	FORMAT4  " %%F %%T.%.09ld %%z "
114 
115 #undef BUFSIZ
116 #define	BUFSIZ 4096
117 #define	NUMBER_WIDTH 40
118 #define	FMTSIZE 50
119 
120 struct ditem {
121 	dev_t	dev;			/* directory items device number */
122 	ino_t	ino;			/* directory items inode number */
123 	struct ditem *parent;		/* dir items ptr to its parent's info */
124 };
125 /* Holds boolean extended system attributes */
126 struct attrb {
127 	char		*name;
128 };
129 /* Holds timestamp extended system attributes */
130 struct attrtm {
131 	char		*name;
132 	uint64_t	stm;
133 	uint64_t	nstm;
134 };
135 
136 struct	lbuf	{
137 	union	{
138 		char	lname[MAXNAMLEN]; /* used for filename in a directory */
139 		char	*namep;		/* for name in ls-command; */
140 	} ln;
141 	char	ltype;		/* filetype */
142 	ino_t	lnum;		/* inode number of file */
143 	mode_t	lflags; 	/* 0777 bits used as r,w,x permissions */
144 	nlink_t	lnl;		/* number of links to file */
145 	uid_t	luid;
146 	gid_t	lgid;
147 	off_t	lsize;		/* filesize or major/minor dev numbers */
148 	blkcnt_t	lblocks;	/* number of file blocks */
149 	timestruc_t	lmtime;
150 	timestruc_t	lat;
151 	timestruc_t	lct;
152 	timestruc_t	lmt;
153 	char	*flinkto;	/* symbolic link contents */
154 	char 	acl;		/* indicate there are additional acl entries */
155 	int	cycle;		/* cycle detected flag */
156 	struct ditem *ancinfo;	/* maintains ancestor info */
157 	acl_t *aclp;		/* ACL if present */
158 	struct attrb *exttr;	/* boolean extended system attributes */
159 	struct attrtm *extm;	/* timestamp extended system attributes */
160 };
161 
162 struct dchain {
163 	char *dc_name;		/* path name */
164 	int cycle_detected;	/* cycle detected visiting this directory */
165 	struct ditem *myancinfo;	/* this directory's ancestry info */
166 	struct dchain *dc_next;	/* next directory in the chain */
167 };
168 
169 /*
170  * A numbuf_t is used when converting a number to a string representation
171  */
172 typedef char numbuf_t[NUMBER_WIDTH];
173 
174 static struct dchain *dfirst;	/* start of the dir chain */
175 static struct dchain *cdfirst;	/* start of the current dir chain */
176 static struct dchain *dtemp;	/* temporary - used for linking */
177 static char *curdir;		/* the current directory */
178 
179 static int	first = 1;	/* true if first line is not yet printed */
180 static int	nfiles = 0;	/* number of flist entries in current use */
181 static int	nargs = 0;	/* number of flist entries used for arguments */
182 static int	maxfils = 0;	/* number of flist/lbuf entries allocated */
183 static int	maxn = 0;	/* number of flist entries with lbufs asigned */
184 static int	quantn = 64;	/* allocation growth quantum */
185 
186 static struct lbuf	*nxtlbf;	/* ptr to next lbuf to be assigned */
187 static struct lbuf	**flist;	/* ptr to list of lbuf pointers */
188 static struct lbuf	*gstat(char *, int, struct ditem *);
189 static char		*getname(uid_t);
190 static char		*getgroup(gid_t);
191 static char		*makename(char *, char *);
192 static void		pentry(struct lbuf *);
193 static void		column(void);
194 static void		pmode(mode_t aflag);
195 static void		selection(int *);
196 static void		new_line(void);
197 static void		rddir(char *, struct ditem *);
198 static int		strcol(unsigned char *);
199 static void		pem(struct lbuf **, struct lbuf **, int);
200 static void		pdirectory(char *, int, int, int, struct ditem *);
201 static struct cachenode *findincache(struct cachenode **, long);
202 static void		csi_pprintf(unsigned char *);
203 static void		pprintf(char *, char *);
204 static int		compar(struct lbuf **pp1, struct lbuf **pp2);
205 static char 		*number_to_scaled_string(numbuf_t buf,
206 			    unsigned long long number,
207 			    long scale);
208 static void		record_ancestry(char *, struct stat *, struct lbuf *,
209 			    int, struct ditem *);
210 
211 static int		aflg;
212 static int		atflg;
213 static int		bflg;
214 static int		cflg;
215 static int		dflg;
216 static int		eflg;
217 static int		fflg;
218 static int		gflg;
219 static int		hflg;
220 static int		iflg;
221 static int		lflg;
222 static int		mflg;
223 static int		nflg;
224 static int		oflg;
225 static int		pflg;
226 static int		qflg;
227 static int		rflg = 1; /* init to 1 for special use in compar */
228 static int		sflg;
229 static int		tflg;
230 static int		uflg;
231 static int		xflg;
232 static int		Aflg;
233 static int		Cflg;
234 static int		Eflg;
235 static int		Fflg;
236 static int		Hflg;
237 static int		Lflg;
238 static int		Rflg;
239 static int		Sflg;
240 static int		vflg;
241 static int		Vflg;
242 static int		saflg;		/* boolean extended system attr. */
243 static int		sacnt;		/* number of extended system attr. */
244 static int		copt;
245 static int		vopt;
246 static int		tmflg;		/* create time ext. system attr. */
247 static int		ctm;
248 static int		atm;
249 static int		mtm;
250 static int		crtm;
251 static int		alltm;
252 static long		hscale;
253 static mode_t		flags;
254 static int		err = 0;	/* Contains return code */
255 
256 static uid_t		lastuid	= (uid_t)-1;
257 static gid_t		lastgid = (gid_t)-1;
258 static char		*lastuname = NULL;
259 static char		*lastgname = NULL;
260 
261 /* statreq > 0 if any of sflg, (n)lflg, tflg, Sflg are on */
262 static int		statreq;
263 
264 static char		*dotp = ".";
265 
266 static u_longlong_t 	tblocks; /* number of blocks of files in a directory */
267 static time_t		year, now;
268 
269 static int		num_cols = 80;
270 static int		colwidth;
271 static int		filewidth;
272 static int		fixedwidth;
273 static int		nomocore;
274 static int		curcol;
275 
276 static struct	winsize	win;
277 
278 static char	time_buf[FMTSIZE];	/* array to hold day and time */
279 
280 #define	NOTWORKINGDIR(d, l)	(((l) < 2) || \
281 				    (strcmp((d) + (l) - 2, "/.") != 0))
282 
283 #define	NOTPARENTDIR(d, l)	(((l) < 3) || \
284 				    (strcmp((d) + (l) - 3, "/..") != 0))
285 /* Extended system attributes support */
286 static int get_sysxattr(char *, struct lbuf *);
287 static void set_sysattrb_display(char *, boolean_t, struct lbuf *);
288 static void set_sysattrtm_display(char *, struct lbuf *);
289 static void format_time(const char *, time_t);
290 static void format_etime(const char *, time_t, time_t);
291 static void print_time(struct lbuf *);
292 static void format_attrtime(struct lbuf *);
293 static void *xmalloc(size_t, struct lbuf *);
294 static void free_sysattr(struct lbuf *);
295 static nvpair_t *pair;
296 static nvlist_t	*response;
297 
298 int
299 main(int argc, char *argv[])
300 {
301 	int		c;
302 	int		i;
303 	int		width;
304 	int		amino = 0;
305 	int		opterr = 0;
306 	struct lbuf	*ep;
307 	struct lbuf	lb;
308 	struct ditem	*myinfo;
309 
310 	(void) setlocale(LC_ALL, "");
311 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
312 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
313 #endif
314 	(void) textdomain(TEXT_DOMAIN);
315 #ifdef STANDALONE
316 	if (argv[0][0] == '\0')
317 		argc = getargv("ls", &argv, 0);
318 #endif
319 
320 	lb.lmtime.tv_sec = time(NULL);
321 	lb.lmtime.tv_nsec = 0;
322 	year = lb.lmtime.tv_sec - 6L*30L*24L*60L*60L; /* 6 months ago */
323 	now = lb.lmtime.tv_sec + 60;
324 	if (isatty(1)) {
325 		Cflg = 1;
326 		mflg = 0;
327 	}
328 
329 	while ((c = getopt(argc, argv,
330 	    "aAbcCdeEfFghHilLmnopqrRsStux1@vV/:%:")) != EOF)
331 		switch (c) {
332 		case 'a':
333 			aflg++;
334 			continue;
335 		case 'A':
336 			Aflg++;
337 			continue;
338 		case 'b':
339 			bflg = 1;
340 			qflg = 0;
341 			continue;
342 		case 'c':
343 			uflg = 0;
344 			atm = 0;
345 			ctm = 0;
346 			mtm = 0;
347 			crtm = 0;
348 			cflg++;
349 			continue;
350 		case 'C':
351 			Cflg = 1;
352 			mflg = 0;
353 #ifdef XPG4
354 			lflg = 0;
355 #endif
356 			continue;
357 		case 'd':
358 			dflg++;
359 			continue;
360 		case 'e':
361 			eflg++;
362 			lflg++;
363 			statreq++;
364 			Eflg = 0;
365 			continue;
366 		case 'E':
367 			Eflg++;
368 			lflg++;
369 			statreq++;
370 			eflg = 0;
371 			continue;
372 		case 'f':
373 			fflg++;
374 			continue;
375 		case 'F':
376 			Fflg++;
377 			statreq++;
378 			continue;
379 		case 'g':
380 			gflg++;
381 			lflg++;
382 			statreq++;
383 			continue;
384 		case 'h':
385 			hflg++;
386 			hscale = 1024;
387 			continue;
388 		case 'H':
389 			Hflg++;
390 			/* -H and -L are mutually exclusive */
391 			Lflg = 0;
392 			continue;
393 		case 'i':
394 			iflg++;
395 			continue;
396 		case 'l':
397 			lflg++;
398 			statreq++;
399 			Cflg = 0;
400 			xflg = 0;
401 			mflg = 0;
402 			atflg = 0;
403 			continue;
404 		case 'L':
405 			Lflg++;
406 			/* -H and -L are mutually exclusive */
407 			Hflg = 0;
408 			continue;
409 		case 'm':
410 			Cflg = 0;
411 			mflg = 1;
412 #ifdef XPG4
413 			lflg = 0;
414 #endif
415 			continue;
416 		case 'n':
417 			nflg++;
418 			lflg++;
419 			statreq++;
420 			Cflg = 0;
421 			xflg = 0;
422 			mflg = 0;
423 			atflg = 0;
424 			continue;
425 		case 'o':
426 			oflg++;
427 			lflg++;
428 			statreq++;
429 			continue;
430 		case 'p':
431 			pflg++;
432 			statreq++;
433 			continue;
434 		case 'q':
435 			qflg = 1;
436 			bflg = 0;
437 			continue;
438 		case 'r':
439 			rflg = -1;
440 			continue;
441 		case 'R':
442 			Rflg++;
443 			statreq++;
444 			continue;
445 		case 's':
446 			sflg++;
447 			statreq++;
448 			continue;
449 		case 'S':
450 			tflg = 0;
451 			Sflg++;
452 			statreq++;
453 			continue;
454 		case 't':
455 			Sflg = 0;
456 			tflg++;
457 			statreq++;
458 			continue;
459 		case 'u':
460 			cflg = 0;
461 			atm = 0;
462 			ctm = 0;
463 			mtm = 0;
464 			crtm = 0;
465 			uflg++;
466 			continue;
467 		case 'V':
468 			Vflg++;
469 			/*FALLTHROUGH*/
470 		case 'v':
471 			vflg++;
472 #if !defined(XPG4)
473 			if (lflg)
474 				continue;
475 #endif
476 			lflg++;
477 			statreq++;
478 			Cflg = 0;
479 			xflg = 0;
480 			mflg = 0;
481 			continue;
482 		case 'x':
483 			xflg = 1;
484 			Cflg = 1;
485 			mflg = 0;
486 #ifdef XPG4
487 			lflg = 0;
488 #endif
489 			continue;
490 		case '1':
491 			Cflg = 0;
492 			continue;
493 		case '@':
494 #if !defined(XPG4)
495 			/*
496 			 * -l has precedence over -@
497 			 */
498 			if (lflg)
499 				continue;
500 #endif
501 			atflg++;
502 			lflg++;
503 			statreq++;
504 			Cflg = 0;
505 			xflg = 0;
506 			mflg = 0;
507 			continue;
508 		case '/':
509 			saflg++;
510 			if (optarg != NULL) {
511 				if (strcmp(optarg, "c") == 0) {
512 					copt++;
513 					vopt = 0;
514 				} else if (strcmp(optarg, "v") == 0) {
515 					vopt++;
516 					copt = 0;
517 				} else
518 					opterr++;
519 			} else
520 				opterr++;
521 			lflg++;
522 			statreq++;
523 			Cflg = 0;
524 			xflg = 0;
525 			mflg = 0;
526 			continue;
527 		case '%':
528 			tmflg++;
529 			if (optarg != NULL) {
530 				if (strcmp(optarg, "ctime") == 0) {
531 					ctm++;
532 					atm = 0;
533 					mtm = 0;
534 					crtm = 0;
535 				} else if (strcmp(optarg, "atime") == 0) {
536 					atm++;
537 					ctm = 0;
538 					mtm = 0;
539 					crtm = 0;
540 					uflg = 0;
541 					cflg = 0;
542 				} else if (strcmp(optarg, "mtime") == 0) {
543 					mtm++;
544 					atm = 0;
545 					ctm = 0;
546 					crtm = 0;
547 					uflg = 0;
548 					cflg = 0;
549 				} else if (strcmp(optarg, "crtime") == 0) {
550 					crtm++;
551 					atm = 0;
552 					ctm = 0;
553 					mtm = 0;
554 					uflg = 0;
555 					cflg = 0;
556 				} else if (strcmp(optarg, "all") == 0) {
557 					alltm++;
558 					atm = 0;
559 					ctm = 0;
560 					mtm = 0;
561 					crtm = 0;
562 				} else
563 					opterr++;
564 			} else
565 				opterr++;
566 
567 			Sflg = 0;
568 			statreq++;
569 			mflg = 0;
570 			continue;
571 		case '?':
572 			opterr++;
573 			continue;
574 		}
575 	if (opterr) {
576 		(void) fprintf(stderr, gettext(
577 		    "usage: ls -aAbcCdeEfFghHilLmnopqrRsStuxvV1@/%[c | v]"
578 		    "%%[atime | crtime | ctime | mtime | all]"
579 		    " [files]\n"));
580 		exit(2);
581 	}
582 
583 	if (fflg) {
584 		aflg++;
585 		lflg = 0;
586 		sflg = 0;
587 		tflg = 0;
588 		Sflg = 0;
589 		statreq = 0;
590 	}
591 
592 	fixedwidth = 2;
593 	if (pflg || Fflg)
594 		fixedwidth++;
595 	if (iflg)
596 		fixedwidth += 11;
597 	if (sflg)
598 		fixedwidth += 5;
599 
600 	if (lflg) {
601 		if (!gflg && !oflg)
602 			gflg = oflg = 1;
603 		else
604 		if (gflg && oflg)
605 			gflg = oflg = 0;
606 		Cflg = mflg = 0;
607 	}
608 
609 	if (Cflg || mflg) {
610 		char *clptr;
611 		if ((clptr = getenv("COLUMNS")) != NULL)
612 			num_cols = atoi(clptr);
613 #ifdef TERMINFO
614 		else {
615 			if (ioctl(1, TIOCGWINSZ, &win) != -1)
616 				num_cols = (win.ws_col == 0 ? 80 : win.ws_col);
617 		}
618 #endif
619 		if (num_cols < 20 || num_cols > 1000)
620 			/* assume it is an error */
621 			num_cols = 80;
622 	}
623 
624 	/* allocate space for flist and the associated	*/
625 	/* data structures (lbufs)			*/
626 	maxfils = quantn;
627 	if (((flist = malloc(maxfils * sizeof (struct lbuf *))) == NULL) ||
628 	    ((nxtlbf = malloc(quantn * sizeof (struct lbuf))) == NULL)) {
629 		perror("ls");
630 		exit(2);
631 	}
632 	if ((amino = (argc-optind)) == 0) {
633 					/*
634 					 * case when no names are given
635 					 * in ls-command and current
636 					 * directory is to be used
637 					 */
638 		argv[optind] = dotp;
639 	}
640 
641 	for (i = 0; i < (amino ? amino : 1); i++) {
642 
643 		/*
644 		 * If we are recursing, we need to make sure we don't
645 		 * get into an endless loop.  To keep track of the inodes
646 		 * (actually, just the directories) visited, we
647 		 * maintain a directory ancestry list for a file
648 		 * hierarchy.  As we go deeper into the hierarchy,
649 		 * a parent directory passes its directory list
650 		 * info (device id, inode number, and a pointer to
651 		 * its parent) to each of its children.  As we
652 		 * process a child that is a directory, we save
653 		 * its own personal directory list info.  We then
654 		 * check to see if the child has already been
655 		 * processed by comparing its device id and inode
656 		 * number from its own personal directory list info
657 		 * to that of each of its ancestors.  If there is a
658 		 * match, then we know we've detected a cycle.
659 		 */
660 		if (Rflg) {
661 			/*
662 			 * This is the first parent in this lineage
663 			 * (first in a directory hierarchy), so
664 			 * this parent's parent doesn't exist.  We
665 			 * only initialize myinfo when we are
666 			 * recursing, otherwise it's not used.
667 			 */
668 			if ((myinfo = (struct ditem *)malloc(
669 			    sizeof (struct ditem))) == NULL) {
670 				perror("ls");
671 				exit(2);
672 			} else {
673 				myinfo->dev = 0;
674 				myinfo->ino = 0;
675 				myinfo->parent = NULL;
676 			}
677 		}
678 
679 		if (Cflg || mflg) {
680 			width = strcol((unsigned char *)argv[optind]);
681 			if (width > filewidth)
682 				filewidth = width;
683 		}
684 		if ((ep = gstat((*argv[optind] ? argv[optind] : dotp),
685 		    1, myinfo)) == NULL) {
686 			if (nomocore)
687 				exit(2);
688 			err = 2;
689 			optind++;
690 			continue;
691 		}
692 		ep->ln.namep = (*argv[optind] ? argv[optind] : dotp);
693 		ep->lflags |= ISARG;
694 		optind++;
695 		nargs++;	/* count good arguments stored in flist */
696 	}
697 	colwidth = fixedwidth + filewidth;
698 	qsort(flist, (unsigned)nargs, sizeof (struct lbuf *),
699 	    (int (*)(const void *, const void *))compar);
700 	for (i = 0; i < nargs; i++) {
701 		if (flist[i]->ltype == 'd' && dflg == 0 || fflg)
702 			break;
703 	}
704 	pem(&flist[0], &flist[i], 0);
705 	for (; i < nargs; i++) {
706 		pdirectory(flist[i]->ln.namep, Rflg ||
707 		    (amino > 1), nargs, 0, flist[i]->ancinfo);
708 		if (nomocore)
709 			exit(2);
710 		/* -R: print subdirectories found */
711 		while (dfirst || cdfirst) {
712 			/* Place direct subdirs on front in right order */
713 			while (cdfirst) {
714 				/* reverse cdfirst onto front of dfirst */
715 				dtemp = cdfirst;
716 				cdfirst = cdfirst -> dc_next;
717 				dtemp -> dc_next = dfirst;
718 				dfirst = dtemp;
719 			}
720 			/* take off first dir on dfirst & print it */
721 			dtemp = dfirst;
722 			dfirst = dfirst->dc_next;
723 			pdirectory(dtemp->dc_name, 1, nargs,
724 			    dtemp->cycle_detected, dtemp->myancinfo);
725 			if (nomocore)
726 				exit(2);
727 			free(dtemp->dc_name);
728 			free(dtemp);
729 		}
730 	}
731 	return (err);
732 }
733 
734 /*
735  * pdirectory: print the directory name, labelling it if title is
736  * nonzero, using lp as the place to start reading in the dir.
737  */
738 static void
739 pdirectory(char *name, int title, int lp, int cdetect, struct ditem *myinfo)
740 {
741 	struct dchain *dp;
742 	struct lbuf *ap;
743 	char *pname;
744 	int j;
745 
746 	filewidth = 0;
747 	curdir = name;
748 	if (title) {
749 		if (!first)
750 			(void) putc('\n', stdout);
751 		pprintf(name, ":");
752 		new_line();
753 	}
754 	/*
755 	 * If there was a cycle detected, then notify and don't report
756 	 * further.
757 	 */
758 	if (cdetect) {
759 		if (lflg || sflg) {
760 			curcol += printf(gettext("total %d"), 0);
761 			new_line();
762 		}
763 		(void) fprintf(stderr, gettext(
764 		    "ls: cycle detected for %s\n"), name);
765 		return;
766 	}
767 
768 	nfiles = lp;
769 	rddir(name, myinfo);
770 	if (nomocore)
771 		return;
772 	if (fflg == 0)
773 		qsort(&flist[lp], (unsigned)(nfiles - lp),
774 		    sizeof (struct lbuf *),
775 		    (int (*)(const void *, const void *))compar);
776 	if (Rflg) {
777 		for (j = nfiles - 1; j >= lp; j--) {
778 			ap = flist[j];
779 			if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") &&
780 			    strcmp(ap->ln.lname, "..")) {
781 				dp = malloc(sizeof (struct dchain));
782 				if (dp == NULL) {
783 					perror("ls");
784 					exit(2);
785 				}
786 				pname = makename(curdir, ap->ln.lname);
787 				if ((dp->dc_name = strdup(pname)) == NULL) {
788 					perror("ls");
789 					exit(2);
790 				}
791 				dp->cycle_detected = ap->cycle;
792 				dp->myancinfo = ap->ancinfo;
793 				dp->dc_next = dfirst;
794 				dfirst = dp;
795 			}
796 		}
797 	}
798 	if (lflg || sflg) {
799 		curcol += printf(gettext("total %llu"), tblocks);
800 		new_line();
801 	}
802 	pem(&flist[lp], &flist[nfiles], lflg||sflg);
803 }
804 
805 /*
806  * pem: print 'em. Print a list of files (e.g. a directory) bounded
807  * by slp and lp.
808  */
809 static void
810 pem(struct lbuf **slp, struct lbuf **lp, int tot_flag)
811 {
812 	long row, nrows, i;
813 	int col, ncols;
814 	struct lbuf **ep;
815 
816 	if (Cflg || mflg) {
817 		if (colwidth > num_cols) {
818 			ncols = 1;
819 		} else {
820 			ncols = num_cols / colwidth;
821 		}
822 	}
823 
824 	if (ncols == 1 || mflg || xflg || !Cflg) {
825 		for (ep = slp; ep < lp; ep++)
826 			pentry(*ep);
827 		new_line();
828 		return;
829 	}
830 	/* otherwise print -C columns */
831 	if (tot_flag) {
832 		slp--;
833 		row = 1;
834 	}
835 	else
836 		row = 0;
837 
838 	nrows = (lp - slp - 1) / ncols + 1;
839 	for (i = 0; i < nrows; i++, row++) {
840 		for (col = 0; col < ncols; col++) {
841 			ep = slp + (nrows * col) + row;
842 			if (ep < lp)
843 				pentry(*ep);
844 		}
845 		new_line();
846 	}
847 }
848 
849 /*
850  * print one output entry;
851  * if uid/gid is not found in the appropriate
852  * file(passwd/group), then print uid/gid instead of
853  * user/group name;
854  */
855 static void
856 pentry(struct lbuf *ap)
857 {
858 	struct lbuf *p;
859 	numbuf_t hbuf;
860 	char buf[BUFSIZ];
861 	char *dmark = "";	/* Used if -p or -F option active */
862 	char *cp;
863 
864 	p = ap;
865 	column();
866 	if (iflg)
867 		if (mflg && !lflg)
868 			curcol += printf("%llu ", (long long)p->lnum);
869 		else
870 			curcol += printf("%10llu ", (long long)p->lnum);
871 	if (sflg)
872 		curcol += printf((mflg && !lflg) ? "%lld " :
873 		    (p->lblocks < 10000) ? "%4lld " : "%lld ",
874 		    (p->ltype != 'b' && p->ltype != 'c') ?
875 		    p->lblocks : 0LL);
876 	if (lflg) {
877 		(void) putchar(p->ltype);
878 		curcol++;
879 		pmode(p->lflags);
880 
881 		/* ACL: additional access mode flag */
882 		(void) putchar(p->acl);
883 		curcol++;
884 
885 		curcol += printf("%3lu ", (ulong_t)p->lnl);
886 		if (oflg)
887 			if (!nflg) {
888 				cp = getname(p->luid);
889 				curcol += printf("%-8s ", cp);
890 			} else
891 				curcol += printf("%-8lu ", (ulong_t)p->luid);
892 		if (gflg)
893 			if (!nflg) {
894 				cp = getgroup(p->lgid);
895 				curcol += printf("%-8s ", cp);
896 			} else
897 				curcol += printf("%-8lu ", (ulong_t)p->lgid);
898 		if (p->ltype == 'b' || p->ltype == 'c') {
899 			curcol += printf("%3u, %2u",
900 			    (uint_t)major((dev_t)p->lsize),
901 			    (uint_t)minor((dev_t)p->lsize));
902 		} else if (hflg && (p->lsize >= hscale)) {
903 			curcol += printf("%7s",
904 			    number_to_scaled_string(hbuf, p->lsize, hscale));
905 		} else {
906 			curcol += printf((p->lsize < (off_t)10000000) ?
907 			    "%7lld" : "%lld", p->lsize);
908 		}
909 		if (eflg)
910 			format_time(FORMAT3, p->lmtime.tv_sec);
911 		else if (Eflg)
912 			/* fill in nanoseconds first */
913 			format_etime(FORMAT4, p->lmtime.tv_sec,
914 			    p->lmtime.tv_nsec);
915 		else {
916 			if ((p->lmtime.tv_sec < year) ||
917 			    (p->lmtime.tv_sec > now))
918 				format_time(FORMAT1, p->lmtime.tv_sec);
919 			else
920 				format_time(FORMAT2, p->lmtime.tv_sec);
921 		}
922 		/* format extended system attribute time */
923 		if (tmflg && crtm)
924 			format_attrtime(p);
925 
926 		curcol += printf("%s", time_buf);
927 
928 	}
929 	/*
930 	 * prevent both "->" and trailing marks
931 	 * from appearing
932 	 */
933 
934 	if (pflg && p->ltype == 'd')
935 		dmark = "/";
936 
937 	if (Fflg && !(lflg && p->flinkto)) {
938 		if (p->ltype == 'd')
939 			dmark = "/";
940 		else if (p->ltype == 'D')
941 			dmark = ">";
942 		else if (p->ltype == 'p')
943 			dmark = "|";
944 		else if (p->ltype == 'l')
945 			dmark = "@";
946 		else if (p->ltype == 's')
947 			dmark = "=";
948 		else if (p->lflags & (S_IXUSR|S_IXGRP|S_IXOTH))
949 			dmark = "*";
950 		else
951 			dmark = "";
952 	}
953 
954 	if (lflg && p->flinkto) {
955 		(void) strncpy(buf, " -> ", 4);
956 		(void) strcpy(buf + 4, p->flinkto);
957 		dmark = buf;
958 	}
959 	if (p->lflags & ISARG) {
960 		if (qflg || bflg)
961 			pprintf(p->ln.namep, dmark);
962 		else {
963 			(void) printf("%s%s", p->ln.namep, dmark);
964 			curcol += strcol((unsigned char *)p->ln.namep);
965 			curcol += strcol((unsigned char *)dmark);
966 		}
967 	} else {
968 		if (qflg || bflg)
969 			pprintf(p->ln.lname, dmark);
970 		else {
971 			(void) printf("%s%s", p->ln.lname, dmark);
972 			curcol += strcol((unsigned char *)p->ln.lname);
973 			curcol += strcol((unsigned char *)dmark);
974 		}
975 	}
976 
977 	/* Display extended system attributes */
978 	if (saflg) {
979 		int i;
980 
981 		new_line();
982 		(void) printf("	\t{");
983 		if (p->exttr != NULL) {
984 			int k = 0;
985 			for (i = 0; i < sacnt; i++) {
986 				if (p->exttr[i].name != NULL)
987 					k++;
988 			}
989 			for (i = 0; i < sacnt; i++) {
990 				if (p->exttr[i].name != NULL) {
991 					(void) printf("%s", p->exttr[i].name);
992 					k--;
993 					if (vopt && (k != 0))
994 						(void) printf(",");
995 				}
996 			}
997 		}
998 		(void) printf("}\n");
999 	}
1000 	/* Display file timestamps and extended system attribute timestamps */
1001 	if (tmflg && alltm) {
1002 		new_line();
1003 		print_time(p);
1004 		new_line();
1005 	}
1006 	if (vflg) {
1007 		new_line();
1008 		if (p->aclp) {
1009 			acl_printacl(p->aclp, num_cols, Vflg);
1010 		}
1011 	}
1012 	/* Free extended system attribute lists */
1013 	if (saflg || tmflg)
1014 		free_sysattr(p);
1015 }
1016 
1017 /* print various r,w,x permissions */
1018 static void
1019 pmode(mode_t aflag)
1020 {
1021 	/* these arrays are declared static to allow initializations */
1022 	static int	m0[] = { 1, S_IRUSR, 'r', '-' };
1023 	static int	m1[] = { 1, S_IWUSR, 'w', '-' };
1024 	static int	m2[] = { 3, S_ISUID|S_IXUSR, 's', S_IXUSR,
1025 	    'x', S_ISUID, 'S', '-' };
1026 	static int	m3[] = { 1, S_IRGRP, 'r', '-' };
1027 	static int	m4[] = { 1, S_IWGRP, 'w', '-' };
1028 	static int	m5[] = { 4, S_ISGID|S_IXGRP, 's', S_IXGRP,
1029 				'x', S_ISGID|LS_NOTREG, 'S',
1030 #ifdef XPG4
1031 		S_ISGID, 'L', '-'};
1032 #else
1033 		S_ISGID, 'l', '-'};
1034 #endif
1035 	static int	m6[] = { 1, S_IROTH, 'r', '-' };
1036 	static int	m7[] = { 1, S_IWOTH, 'w', '-' };
1037 	static int	m8[] = { 3, S_ISVTX|S_IXOTH, 't', S_IXOTH,
1038 	    'x', S_ISVTX, 'T', '-'};
1039 
1040 	static int *m[] = { m0, m1, m2, m3, m4, m5, m6, m7, m8};
1041 
1042 	int **mp;
1043 
1044 	flags = aflag;
1045 	for (mp = &m[0]; mp < &m[sizeof (m) / sizeof (m[0])]; mp++)
1046 		selection(*mp);
1047 }
1048 
1049 static void
1050 selection(int *pairp)
1051 {
1052 	int n;
1053 
1054 	n = *pairp++;
1055 	while (n-->0) {
1056 		if ((flags & *pairp) == *pairp) {
1057 			pairp++;
1058 			break;
1059 		} else {
1060 			pairp += 2;
1061 		}
1062 	}
1063 	(void) putchar(*pairp);
1064 	curcol++;
1065 }
1066 
1067 /*
1068  * column: get to the beginning of the next column.
1069  */
1070 static void
1071 column(void)
1072 {
1073 	if (curcol == 0)
1074 		return;
1075 	if (mflg) {
1076 		(void) putc(',', stdout);
1077 		curcol++;
1078 		if (curcol + colwidth + 2 > num_cols) {
1079 			(void) putc('\n', stdout);
1080 			curcol = 0;
1081 			return;
1082 		}
1083 		(void) putc(' ', stdout);
1084 		curcol++;
1085 		return;
1086 	}
1087 	if (Cflg == 0) {
1088 		(void) putc('\n', stdout);
1089 		curcol = 0;
1090 		return;
1091 	}
1092 	if ((curcol / colwidth + 2) * colwidth > num_cols) {
1093 		(void) putc('\n', stdout);
1094 		curcol = 0;
1095 		return;
1096 	}
1097 	do {
1098 		(void) putc(' ', stdout);
1099 		curcol++;
1100 	} while (curcol % colwidth);
1101 }
1102 
1103 static void
1104 new_line(void)
1105 {
1106 	if (curcol) {
1107 		first = 0;
1108 		(void) putc('\n', stdout);
1109 		curcol = 0;
1110 	}
1111 }
1112 
1113 /*
1114  * read each filename in directory dir and store its
1115  * status in flist[nfiles]
1116  * use makename() to form pathname dir/filename;
1117  */
1118 static void
1119 rddir(char *dir, struct ditem *myinfo)
1120 {
1121 	struct dirent *dentry;
1122 	DIR *dirf;
1123 	int j;
1124 	struct lbuf *ep;
1125 	int width;
1126 
1127 	if ((dirf = opendir(dir)) == NULL) {
1128 		(void) fflush(stdout);
1129 		perror(dir);
1130 		err = 2;
1131 		return;
1132 	} else {
1133 		tblocks = 0;
1134 		for (;;) {
1135 			errno = 0;
1136 			if ((dentry = readdir(dirf)) == NULL)
1137 				break;
1138 			if (aflg == 0 && dentry->d_name[0] == '.' &&
1139 			    (Aflg == 0 ||
1140 			    dentry->d_name[1] == '\0' ||
1141 			    dentry->d_name[1] == '.' &&
1142 			    dentry->d_name[2] == '\0'))
1143 				/*
1144 				 * check for directory items '.', '..',
1145 				 *  and items without valid inode-number;
1146 				 */
1147 				continue;
1148 
1149 			if (Cflg || mflg) {
1150 				width = strcol((unsigned char *)dentry->d_name);
1151 				if (width > filewidth)
1152 					filewidth = width;
1153 			}
1154 			ep = gstat(makename(dir, dentry->d_name), 0, myinfo);
1155 			if (ep == NULL) {
1156 				if (nomocore)
1157 					exit(2);
1158 				continue;
1159 			} else {
1160 				ep->lnum = dentry->d_ino;
1161 				for (j = 0; dentry->d_name[j] != '\0'; j++)
1162 					ep->ln.lname[j] = dentry->d_name[j];
1163 				ep->ln.lname[j] = '\0';
1164 			}
1165 		}
1166 		if (errno) {
1167 			int sav_errno = errno;
1168 
1169 			(void) fprintf(stderr,
1170 			    gettext("ls: error reading directory %s: %s\n"),
1171 			    dir, strerror(sav_errno));
1172 		}
1173 		(void) closedir(dirf);
1174 		colwidth = fixedwidth + filewidth;
1175 	}
1176 }
1177 
1178 /*
1179  * Attaching a link to an inode's ancestors.  Search
1180  * through the ancestors to check for cycles (an inode which
1181  * we have already tracked in this inodes ancestry).  If a cycle
1182  * is detected, set the exit code and record the fact so that
1183  * it is reported at the right time when printing the directory.
1184  * In addition, set the exit code.  Note:  If the -a flag was
1185  * specified, we don't want to check for cycles for directories
1186  * ending in '/.' or '/..' unless they were specified on the
1187  * command line.
1188  */
1189 static void
1190 record_ancestry(char *file, struct stat *pstatb, struct lbuf *rep,
1191     int argfl, struct ditem *myparent)
1192 {
1193 	size_t		file_len;
1194 	struct ditem	*myinfo;
1195 	struct ditem	*tptr;
1196 
1197 	file_len = strlen(file);
1198 	if (!aflg || argfl || (NOTWORKINGDIR(file, file_len) &&
1199 	    NOTPARENTDIR(file, file_len))) {
1200 		/*
1201 		 * Add this inode's ancestry
1202 		 * info and insert it into the
1203 		 * ancestry list by pointing
1204 		 * back to its parent.  We save
1205 		 * it (in rep) with the other info
1206 		 * we're gathering for this inode.
1207 		 */
1208 		if ((myinfo = malloc(
1209 		    sizeof (struct ditem))) == NULL) {
1210 			perror("ls");
1211 			exit(2);
1212 		}
1213 		myinfo->dev = pstatb->st_dev;
1214 		myinfo->ino = pstatb->st_ino;
1215 		myinfo->parent = myparent;
1216 		rep->ancinfo = myinfo;
1217 
1218 		/*
1219 		 * If this node has the same device id and
1220 		 * inode number of one of its ancestors,
1221 		 * then we've detected a cycle.
1222 		 */
1223 		if (myparent != NULL) {
1224 			for (tptr = myparent; tptr->parent != NULL;
1225 			    tptr = tptr->parent) {
1226 				if ((tptr->dev == pstatb->st_dev) &&
1227 				    (tptr->ino == pstatb->st_ino)) {
1228 					/*
1229 					 * Cycle detected for this
1230 					 * directory.  Record the fact
1231 					 * it is a cycle so we don't
1232 					 * try to process this
1233 					 * directory as we are
1234 					 * walking through the
1235 					 * list of directories.
1236 					 */
1237 					rep->cycle = 1;
1238 					err = 2;
1239 					break;
1240 				}
1241 			}
1242 		}
1243 	}
1244 }
1245 
1246 /*
1247  * Do re-calculate the mode for group for ACE_T type of acls.
1248  * This is because, if the server's FS happens to be UFS, supporting
1249  * POSIX ACL's, then it does a special calculation of group mode
1250  * to be the bitwise OR of CLASS_OBJ and GROUP_OBJ (see PSARC/2001/717.)
1251  *
1252  * This algorithm is from the NFSv4 ACL Draft. Here a part of that
1253  * algorithm is used for the group mode calculation only.
1254  * What is modified here from the algorithm is that only the
1255  * entries with flags ACE_GROUP are considered. For each entry
1256  * with ACE_GROUP flag, the first occurance of a specific access
1257  * is checked if it is allowed.
1258  * We are not interested in perms for user and other, as they
1259  * were taken from st_mode value.
1260  * We are not interested in a_who field of ACE, as we need just
1261  * unix mode bits for the group.
1262  */
1263 
1264 #define	OWNED_GROUP	(ACE_GROUP | ACE_IDENTIFIER_GROUP)
1265 #define	IS_TYPE_ALLOWED(type)	((type) == ACE_ACCESS_ALLOWED_ACE_TYPE)
1266 
1267 int
1268 grp_mask_to_mode(acl_t *acep)
1269 {
1270 	int mode = 0, seen = 0;
1271 	int acecnt;
1272 	int flags;
1273 	ace_t *ap;
1274 
1275 	acecnt = acl_cnt(acep);
1276 	for (ap = (ace_t *)acl_data(acep); acecnt--; ap++) {
1277 
1278 		if (ap->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE &&
1279 		    ap->a_type != ACE_ACCESS_DENIED_ACE_TYPE)
1280 			continue;
1281 
1282 		if (ap->a_flags & ACE_INHERIT_ONLY_ACE)
1283 			continue;
1284 
1285 		/*
1286 		 * if it is first group@ or first everyone@
1287 		 * for each of read, write and execute, then
1288 		 * that will be the group mode bit.
1289 		 */
1290 		flags = ap->a_flags & ACE_TYPE_FLAGS;
1291 		if (flags == OWNED_GROUP || flags == ACE_EVERYONE) {
1292 			if (ap->a_access_mask & ACE_READ_DATA) {
1293 				if (!(seen & S_IRGRP)) {
1294 					seen |= S_IRGRP;
1295 					if (IS_TYPE_ALLOWED(ap->a_type))
1296 						mode |= S_IRGRP;
1297 				}
1298 			}
1299 			if (ap->a_access_mask & ACE_WRITE_DATA) {
1300 				if (!(seen & S_IWGRP)) {
1301 					seen |= S_IWGRP;
1302 					if (IS_TYPE_ALLOWED(ap->a_type))
1303 						mode |= S_IWGRP;
1304 				}
1305 			}
1306 			if (ap->a_access_mask & ACE_EXECUTE) {
1307 				if (!(seen & S_IXGRP)) {
1308 					seen |= S_IXGRP;
1309 					if (IS_TYPE_ALLOWED(ap->a_type))
1310 						mode |= S_IXGRP;
1311 				}
1312 			}
1313 		}
1314 	}
1315 	return (mode);
1316 }
1317 
1318 /*
1319  * get status of file and recomputes tblocks;
1320  * argfl = 1 if file is a name in ls-command and = 0
1321  * for filename in a directory whose name is an
1322  * argument in the command;
1323  * stores a pointer in flist[nfiles] and
1324  * returns that pointer;
1325  * returns NULL if failed;
1326  */
1327 static struct lbuf *
1328 gstat(char *file, int argfl, struct ditem *myparent)
1329 {
1330 	struct stat statb, statb1;
1331 	struct lbuf *rep;
1332 	char buf[BUFSIZ];
1333 	ssize_t cc;
1334 	int (*statf)() = ((Lflg) || (Hflg && argfl)) ? stat : lstat;
1335 	int aclcnt;
1336 	int error;
1337 	aclent_t *tp;
1338 	o_mode_t groupperm, mask;
1339 	int grouppermfound, maskfound;
1340 
1341 	if (nomocore)
1342 		return (NULL);
1343 
1344 	if (nfiles >= maxfils) {
1345 		/*
1346 		 * all flist/lbuf pair assigned files, time to get some
1347 		 * more space
1348 		 */
1349 		maxfils += quantn;
1350 		if (((flist = realloc(flist,
1351 		    maxfils * sizeof (struct lbuf *))) == NULL) ||
1352 		    ((nxtlbf = malloc(quantn *
1353 		    sizeof (struct lbuf))) == NULL)) {
1354 			perror("ls");
1355 			nomocore = 1;
1356 			return (NULL);
1357 		}
1358 	}
1359 
1360 	/*
1361 	 * nfiles is reset to nargs for each directory
1362 	 * that is given as an argument maxn is checked
1363 	 * to prevent the assignment of an lbuf to a flist entry
1364 	 * that already has one assigned.
1365 	 */
1366 	if (nfiles >= maxn) {
1367 		rep = nxtlbf++;
1368 		flist[nfiles++] = rep;
1369 		maxn = nfiles;
1370 	} else {
1371 		rep = flist[nfiles++];
1372 	}
1373 
1374 	/* Initialize */
1375 
1376 	rep->lflags = (mode_t)0;
1377 	rep->flinkto = NULL;
1378 	rep->cycle = 0;
1379 	rep->lat.tv_sec = time(NULL);
1380 	rep->lat.tv_nsec = 0;
1381 	rep->lct.tv_sec = time(NULL);
1382 	rep->lct.tv_nsec = 0;
1383 	rep->lmt.tv_sec = time(NULL);
1384 	rep->lmt.tv_nsec = 0;
1385 	rep->exttr = NULL;
1386 	rep->extm = NULL;
1387 
1388 	if (argfl || statreq) {
1389 		int doacl;
1390 
1391 		if (lflg)
1392 			doacl = 1;
1393 		else
1394 			doacl = 0;
1395 
1396 		if ((*statf)(file, &statb) < 0) {
1397 			if (argfl || errno != ENOENT ||
1398 			    (Lflg && lstat(file, &statb) == 0)) {
1399 				/*
1400 				 * Avoid race between readdir and lstat.
1401 				 * Print error message in case of dangling link.
1402 				 */
1403 				perror(file);
1404 			}
1405 			nfiles--;
1406 			return (NULL);
1407 		}
1408 
1409 		/*
1410 		 * If -H was specified, and the file linked to was
1411 		 * not a directory, then we need to get the info
1412 		 * for the symlink itself.
1413 		 */
1414 		if ((Hflg) && (argfl) &&
1415 		    ((statb.st_mode & S_IFMT) != S_IFDIR)) {
1416 			if (lstat(file, &statb) < 0) {
1417 				perror(file);
1418 			}
1419 		}
1420 
1421 		rep->lnum = statb.st_ino;
1422 		rep->lsize = statb.st_size;
1423 		rep->lblocks = statb.st_blocks;
1424 		switch (statb.st_mode & S_IFMT) {
1425 		case S_IFDIR:
1426 			rep->ltype = 'd';
1427 			if (Rflg) {
1428 				record_ancestry(file, &statb, rep,
1429 				    argfl, myparent);
1430 			}
1431 			break;
1432 		case S_IFBLK:
1433 			rep->ltype = 'b';
1434 			rep->lsize = (off_t)statb.st_rdev;
1435 			break;
1436 		case S_IFCHR:
1437 			rep->ltype = 'c';
1438 			rep->lsize = (off_t)statb.st_rdev;
1439 			break;
1440 		case S_IFIFO:
1441 			rep->ltype = 'p';
1442 			break;
1443 		case S_IFSOCK:
1444 			rep->ltype = 's';
1445 			rep->lsize = 0;
1446 			break;
1447 		case S_IFLNK:
1448 			/* symbolic links may not have ACLs, so elide acl() */
1449 			if ((Lflg == 0) || (Hflg == 0) ||
1450 			    ((Hflg) && (!argfl))) {
1451 				doacl = 0;
1452 			}
1453 			rep->ltype = 'l';
1454 			if (lflg) {
1455 				cc = readlink(file, buf, BUFSIZ);
1456 				if (cc >= 0) {
1457 
1458 					/*
1459 					 * follow the symbolic link
1460 					 * to generate the appropriate
1461 					 * Fflg marker for the object
1462 					 * eg, /bin -> /sym/bin/
1463 					 */
1464 					if ((Fflg || pflg) &&
1465 					    (stat(file, &statb1) >= 0)) {
1466 						switch (statb1.st_mode &
1467 						    S_IFMT) {
1468 						case S_IFDIR:
1469 							buf[cc++] = '/';
1470 							break;
1471 						case S_IFSOCK:
1472 							buf[cc++] = '=';
1473 							break;
1474 						case S_IFDOOR:
1475 							buf[cc++] = '>';
1476 							break;
1477 						case S_IFIFO:
1478 							buf[cc++] = '|';
1479 							break;
1480 						default:
1481 							if ((statb1.st_mode &
1482 							    ~S_IFMT) &
1483 							    (S_IXUSR|S_IXGRP|
1484 							    S_IXOTH))
1485 								buf[cc++] = '*';
1486 							break;
1487 						}
1488 					}
1489 					buf[cc] = '\0';
1490 					rep->flinkto = strdup(buf);
1491 				}
1492 				break;
1493 			}
1494 
1495 			/*
1496 			 * ls /sym behaves differently from ls /sym/
1497 			 * when /sym is a symbolic link. This is fixed
1498 			 * when explicit arguments are specified.
1499 			 */
1500 
1501 #ifdef XPG6
1502 			/* Do not follow a symlink when -F is specified */
1503 			if ((!argfl) || (argfl && Fflg) ||
1504 			    (stat(file, &statb1) < 0))
1505 #else
1506 			/* Follow a symlink when -F is specified */
1507 			if (!argfl || stat(file, &statb1) < 0)
1508 #endif /* XPG6 */
1509 				break;
1510 			if ((statb1.st_mode & S_IFMT) == S_IFDIR) {
1511 				statb = statb1;
1512 				rep->ltype = 'd';
1513 				rep->lsize = statb1.st_size;
1514 				if (Rflg) {
1515 					record_ancestry(file, &statb, rep,
1516 					    argfl, myparent);
1517 				}
1518 			}
1519 			break;
1520 		case S_IFDOOR:
1521 			rep->ltype = 'D';
1522 			break;
1523 		case S_IFREG:
1524 			rep->ltype = '-';
1525 			break;
1526 		case S_IFPORT:
1527 			rep->ltype = 'P';
1528 			break;
1529 		default:
1530 			rep->ltype = '?';
1531 			break;
1532 		}
1533 		rep->lflags = statb.st_mode & ~S_IFMT;
1534 
1535 		if (!S_ISREG(statb.st_mode))
1536 			rep->lflags |= LS_NOTREG;
1537 
1538 		rep->luid = statb.st_uid;
1539 		rep->lgid = statb.st_gid;
1540 		rep->lnl = statb.st_nlink;
1541 		if (uflg || (tmflg && atm))
1542 			rep->lmtime = statb.st_atim;
1543 		else if (cflg || (tmflg && ctm))
1544 			rep->lmtime = statb.st_ctim;
1545 		else
1546 			rep->lmtime = statb.st_mtim;
1547 		rep->lat = statb.st_atim;
1548 		rep->lct = statb.st_ctim;
1549 		rep->lmt = statb.st_mtim;
1550 
1551 		/* ACL: check acl entries count */
1552 		if (doacl) {
1553 
1554 			error = acl_get(file, 0, &rep->aclp);
1555 			if (error) {
1556 				(void) fprintf(stderr,
1557 				    gettext("ls: can't read ACL on %s: %s\n"),
1558 				    file, acl_strerror(error));
1559 				rep->acl = ' ';
1560 				return (rep);
1561 			}
1562 
1563 			rep->acl = ' ';
1564 
1565 			if (rep->aclp &&
1566 			    ((acl_flags(rep->aclp) & ACL_IS_TRIVIAL) == 0)) {
1567 				rep->acl = '+';
1568 				/*
1569 				 * Special handling for ufs aka aclent_t ACL's
1570 				 */
1571 				if (acl_type(rep->aclp) == ACLENT_T) {
1572 					/*
1573 					 * For files with non-trivial acls, the
1574 					 * effective group permissions are the
1575 					 * intersection of the GROUP_OBJ value
1576 					 * and the CLASS_OBJ (acl mask) value.
1577 					 * Determine both the GROUP_OBJ and
1578 					 * CLASS_OBJ for this file and insert
1579 					 * the logical AND of those two values
1580 					 * in the group permissions field
1581 					 * of the lflags value for this file.
1582 					 */
1583 
1584 					/*
1585 					 * Until found in acl list, assume
1586 					 * maximum permissions for both group
1587 					 * a nd mask.  (Just in case the acl
1588 					 * lacks either value for some reason.)
1589 					 */
1590 					groupperm = 07;
1591 					mask = 07;
1592 					grouppermfound = 0;
1593 					maskfound = 0;
1594 					aclcnt = acl_cnt(rep->aclp);
1595 					for (tp =
1596 					    (aclent_t *)acl_data(rep->aclp);
1597 					    aclcnt--; tp++) {
1598 						if (tp->a_type == GROUP_OBJ) {
1599 							groupperm = tp->a_perm;
1600 							grouppermfound = 1;
1601 							continue;
1602 						}
1603 						if (tp->a_type == CLASS_OBJ) {
1604 							mask = tp->a_perm;
1605 							maskfound = 1;
1606 						}
1607 						if (grouppermfound && maskfound)
1608 							break;
1609 					}
1610 
1611 
1612 					/* reset all the group bits */
1613 					rep->lflags &= ~S_IRWXG;
1614 
1615 					/*
1616 					 * Now set them to the logical AND of
1617 					 * the GROUP_OBJ permissions and the
1618 					 * acl mask.
1619 					 */
1620 
1621 					rep->lflags |= (groupperm & mask) << 3;
1622 
1623 				} else if (acl_type(rep->aclp) == ACE_T) {
1624 					int mode;
1625 					mode = grp_mask_to_mode(rep->aclp);
1626 					rep->lflags &= ~S_IRWXG;
1627 					rep->lflags |= mode;
1628 				}
1629 			}
1630 
1631 			if (!vflg && !Vflg && rep->aclp) {
1632 				acl_free(rep->aclp);
1633 				rep->aclp = NULL;
1634 			}
1635 
1636 			if (atflg && pathconf(file, _PC_XATTR_EXISTS) == 1)
1637 				rep->acl = '@';
1638 
1639 		} else
1640 			rep->acl = ' ';
1641 
1642 		/* mask ISARG and other file-type bits */
1643 
1644 		if (rep->ltype != 'b' && rep->ltype != 'c')
1645 			tblocks += rep->lblocks;
1646 
1647 		/* Get extended system attributes */
1648 
1649 		if ((saflg || (tmflg && crtm) || (tmflg && alltm)) &&
1650 		    (sysattr_support(file, _PC_SATTR_EXISTS) == 1)) {
1651 			int i;
1652 
1653 			sacnt = attr_count();
1654 			/*
1655 			 * Allocate 'sacnt' size array to hold extended
1656 			 * system attribute name (verbose) or respective
1657 			 * symbol represenation (compact).
1658 			 */
1659 			rep->exttr = xmalloc(sacnt * sizeof (struct attrb),
1660 			    rep);
1661 
1662 			/* initialize boolean attribute list */
1663 			for (i = 0; i < sacnt; i++)
1664 				rep->exttr[i].name = NULL;
1665 			if (get_sysxattr(file, rep) != 0) {
1666 				(void) fprintf(stderr,
1667 				    gettext("ls:Failed to retrieve "
1668 				    "extended system attribute from "
1669 				    "%s\n"), file);
1670 				rep->exttr[0].name = xmalloc(2, rep);
1671 				(void) strlcpy(rep->exttr[0].name, "?", 2);
1672 			}
1673 		}
1674 	}
1675 	return (rep);
1676 }
1677 
1678 /*
1679  * returns pathname of the form dir/file;
1680  * dir and file are null-terminated strings.
1681  */
1682 static char *
1683 makename(char *dir, char *file)
1684 {
1685 	/*
1686 	 * PATH_MAX is the maximum length of a path name.
1687 	 * MAXNAMLEN is the maximum length of any path name component.
1688 	 * Allocate space for both, plus the '/' in the middle
1689 	 * and the null character at the end.
1690 	 * dfile is static as this is returned by makename().
1691 	 */
1692 	static char dfile[PATH_MAX + 1 + MAXNAMLEN + 1];
1693 	char *dp, *fp;
1694 
1695 	dp = dfile;
1696 	fp = dir;
1697 	while (*fp)
1698 		*dp++ = *fp++;
1699 	if (dp > dfile && *(dp - 1) != '/')
1700 		*dp++ = '/';
1701 	fp = file;
1702 	while (*fp)
1703 		*dp++ = *fp++;
1704 	*dp = '\0';
1705 	return (dfile);
1706 }
1707 
1708 
1709 #include <pwd.h>
1710 #include <grp.h>
1711 #include <utmpx.h>
1712 
1713 struct	utmpx utmp;
1714 
1715 #define	NMAX	(sizeof (utmp.ut_name))
1716 #define	SCPYN(a, b)	(void) strncpy(a, b, NMAX)
1717 
1718 
1719 struct cachenode {		/* this struct must be zeroed before using */
1720 	struct cachenode *lesschild;	/* subtree whose entries < val */
1721 	struct cachenode *grtrchild;	/* subtree whose entries > val */
1722 	long val;			/* the uid or gid of this entry */
1723 	int initted;			/* name has been filled in */
1724 	char name[NMAX+1];		/* the string that val maps to */
1725 };
1726 static struct cachenode *names, *groups;
1727 
1728 static struct cachenode *
1729 findincache(struct cachenode **head, long val)
1730 {
1731 	struct cachenode **parent = head;
1732 	struct cachenode *c = *parent;
1733 
1734 	while (c != NULL) {
1735 		if (val == c->val) {
1736 			/* found it */
1737 			return (c);
1738 		} else if (val < c->val) {
1739 			parent = &c->lesschild;
1740 			c = c->lesschild;
1741 		} else {
1742 			parent = &c->grtrchild;
1743 			c = c->grtrchild;
1744 		}
1745 	}
1746 
1747 	/* not in the cache, make a new entry for it */
1748 	c = calloc(1, sizeof (struct cachenode));
1749 	if (c == NULL) {
1750 		perror("ls");
1751 		exit(2);
1752 	}
1753 	*parent = c;
1754 	c->val = val;
1755 	return (c);
1756 }
1757 
1758 /*
1759  * get name from cache, or passwd file for a given uid;
1760  * lastuid is set to uid.
1761  */
1762 static char *
1763 getname(uid_t uid)
1764 {
1765 	struct passwd *pwent;
1766 	struct cachenode *c;
1767 
1768 	if ((uid == lastuid) && lastuname)
1769 		return (lastuname);
1770 
1771 	c = findincache(&names, uid);
1772 	if (c->initted == 0) {
1773 		if ((pwent = getpwuid(uid)) != NULL) {
1774 			SCPYN(&c->name[0], pwent->pw_name);
1775 		} else {
1776 			(void) sprintf(&c->name[0], "%-8u", (int)uid);
1777 		}
1778 		c->initted = 1;
1779 	}
1780 	lastuid = uid;
1781 	lastuname = &c->name[0];
1782 	return (lastuname);
1783 }
1784 
1785 /*
1786  * get name from cache, or group file for a given gid;
1787  * lastgid is set to gid.
1788  */
1789 static char *
1790 getgroup(gid_t gid)
1791 {
1792 	struct group *grent;
1793 	struct cachenode *c;
1794 
1795 	if ((gid == lastgid) && lastgname)
1796 		return (lastgname);
1797 
1798 	c = findincache(&groups, gid);
1799 	if (c->initted == 0) {
1800 		if ((grent = getgrgid(gid)) != NULL) {
1801 			SCPYN(&c->name[0], grent->gr_name);
1802 		} else {
1803 			(void) sprintf(&c->name[0], "%-8u", (int)gid);
1804 		}
1805 		c->initted = 1;
1806 	}
1807 	lastgid = gid;
1808 	lastgname = &c->name[0];
1809 	return (lastgname);
1810 }
1811 
1812 /* return >0 if item pointed by pp2 should appear first */
1813 static int
1814 compar(struct lbuf **pp1, struct lbuf **pp2)
1815 {
1816 	struct lbuf *p1, *p2;
1817 
1818 	p1 = *pp1;
1819 	p2 = *pp2;
1820 	if (dflg == 0) {
1821 /*
1822  * compare two names in ls-command one of which is file
1823  * and the other is a directory;
1824  * this portion is not used for comparing files within
1825  * a directory name of ls-command;
1826  */
1827 		if (p1->lflags&ISARG && p1->ltype == 'd') {
1828 			if (!(p2->lflags&ISARG && p2->ltype == 'd'))
1829 				return (1);
1830 		} else {
1831 			if (p2->lflags&ISARG && p2->ltype == 'd')
1832 				return (-1);
1833 		}
1834 	}
1835 	if (tflg) {
1836 		if (p2->lmtime.tv_sec > p1->lmtime.tv_sec)
1837 			return (rflg);
1838 		else if (p2->lmtime.tv_sec < p1->lmtime.tv_sec)
1839 			return (-rflg);
1840 		/* times are equal to the sec, check nsec */
1841 		if (p2->lmtime.tv_nsec > p1->lmtime.tv_nsec)
1842 			return (rflg);
1843 		else if (p2->lmtime.tv_nsec < p1->lmtime.tv_nsec)
1844 			return (-rflg);
1845 		/* if times are equal, fall through and sort by name */
1846 	} else if (Sflg) {
1847 		/*
1848 		 * The size stored in lsize can be either the
1849 		 * size or the major minor number (in the case of
1850 		 * block and character special devices).  If it's
1851 		 * a major minor number, then the size is considered
1852 		 * to be zero and we want to fall through and sort
1853 		 * by name.  In addition, if the size of p2 is equal
1854 		 * to the size of p1 we want to fall through and
1855 		 * sort by name.
1856 		 */
1857 		off_t	p1size = (p1->ltype == 'b') ||
1858 		    (p1->ltype == 'c') ? 0 : p1->lsize;
1859 		off_t	p2size = (p2->ltype == 'b') ||
1860 		    (p2->ltype == 'c') ? 0 : p2->lsize;
1861 		if (p2size > p1size) {
1862 			return (rflg);
1863 		} else if (p2size < p1size) {
1864 			return (-rflg);
1865 		}
1866 		/* Sizes are equal, fall through and sort by name. */
1867 	}
1868 	return (rflg * strcoll(
1869 	    p1->lflags & ISARG ? p1->ln.namep : p1->ln.lname,
1870 	    p2->lflags&ISARG ? p2->ln.namep : p2->ln.lname));
1871 }
1872 
1873 static void
1874 pprintf(char *s1, char *s2)
1875 {
1876 	csi_pprintf((unsigned char *)s1);
1877 	csi_pprintf((unsigned char *)s2);
1878 }
1879 
1880 static void
1881 csi_pprintf(unsigned char *s)
1882 {
1883 	unsigned char *cp;
1884 	char	c;
1885 	int	i;
1886 	int	c_len;
1887 	int	p_col;
1888 	wchar_t	pcode;
1889 
1890 	if (!qflg && !bflg) {
1891 		for (cp = s; *cp != '\0'; cp++) {
1892 			(void) putchar(*cp);
1893 			curcol++;
1894 		}
1895 		return;
1896 	}
1897 
1898 	for (cp = s; *cp; ) {
1899 		if (isascii(c = *cp)) {
1900 			if (!isprint(c)) {
1901 				if (qflg) {
1902 					c = '?';
1903 				} else {
1904 					curcol += 3;
1905 					(void) putc('\\', stdout);
1906 					c = '0' + ((*cp >> 6) & 07);
1907 					(void) putc(c, stdout);
1908 					c = '0' + ((*cp >> 3) & 07);
1909 					(void) putc(c, stdout);
1910 					c = '0' + (*cp & 07);
1911 				}
1912 			}
1913 			curcol++;
1914 			cp++;
1915 			(void) putc(c, stdout);
1916 			continue;
1917 		}
1918 
1919 		if ((c_len = mbtowc(&pcode, (char *)cp, MB_LEN_MAX)) <= 0) {
1920 			c_len = 1;
1921 			goto not_print;
1922 		}
1923 
1924 		if ((p_col = wcwidth(pcode)) > 0) {
1925 			(void) putwchar(pcode);
1926 			cp += c_len;
1927 			curcol += p_col;
1928 			continue;
1929 		}
1930 
1931 not_print:
1932 		for (i = 0; i < c_len; i++) {
1933 			if (qflg) {
1934 				c = '?';
1935 			} else {
1936 				curcol += 3;
1937 				(void) putc('\\', stdout);
1938 				c = '0' + ((*cp >> 6) & 07);
1939 				(void) putc(c, stdout);
1940 				c = '0' + ((*cp >> 3) & 07);
1941 				(void) putc(c, stdout);
1942 				c = '0' + (*cp & 07);
1943 			}
1944 			curcol++;
1945 			(void) putc(c, stdout);
1946 			cp++;
1947 		}
1948 	}
1949 }
1950 
1951 static int
1952 strcol(unsigned char *s1)
1953 {
1954 	int	w;
1955 	int	w_col;
1956 	int	len;
1957 	wchar_t	wc;
1958 
1959 	w = 0;
1960 	while (*s1) {
1961 		if (isascii(*s1)) {
1962 			w++;
1963 			s1++;
1964 			continue;
1965 		}
1966 
1967 		if ((len = mbtowc(&wc, (char *)s1, MB_LEN_MAX)) <= 0) {
1968 			w++;
1969 			s1++;
1970 			continue;
1971 		}
1972 
1973 		if ((w_col = wcwidth(wc)) < 0)
1974 			w_col = len;
1975 		s1 += len;
1976 		w += w_col;
1977 	}
1978 	return (w);
1979 }
1980 
1981 /*
1982  * Convert an unsigned long long to a string representation and place the
1983  * result in the caller-supplied buffer.
1984  *
1985  * The number provided is a size in bytes.  The number is first
1986  * converted to an integral multiple of 'scale' bytes.  This new
1987  * number is then scaled down until it is small enough to be in a good
1988  * human readable format, i.e.  in the range 0 thru scale-1.  If the
1989  * number used to derive the final number is not a multiple of scale, and
1990  * the final number has only a single significant digit, we compute
1991  * tenths of units to provide a second significant digit.
1992  *
1993  * The value "(unsigned long long)-1" is a special case and is always
1994  * converted to "-1".
1995  *
1996  * A pointer to the caller-supplied buffer is returned.
1997  */
1998 static char *
1999 number_to_scaled_string(
2000 			numbuf_t buf,		/* put the result here */
2001 			unsigned long long number, /* convert this number */
2002 			long scale)
2003 {
2004 	unsigned long long save;
2005 	/* Measurement: kilo, mega, giga, tera, peta, exa */
2006 	char *uom = "KMGTPE";
2007 
2008 	if ((long long)number == (long long)-1) {
2009 		(void) strlcpy(buf, "-1", sizeof (numbuf_t));
2010 		return (buf);
2011 	}
2012 
2013 	save = number;
2014 	number = number / scale;
2015 
2016 	/*
2017 	 * Now we have number as a count of scale units.
2018 	 * If no further scaling is necessary, we round up as appropriate.
2019 	 *
2020 	 * The largest value number could have had entering the routine is
2021 	 * 16 Exabytes, so running off the end of the uom array should
2022 	 * never happen.  We check for that, though, as a guard against
2023 	 * a breakdown elsewhere in the algorithm.
2024 	 */
2025 	if (number < (unsigned long long)scale) {
2026 		if ((save % scale) >= (unsigned long long)(scale / 2)) {
2027 			if (++number == (unsigned long long)scale) {
2028 				uom++;
2029 				number = 1;
2030 			}
2031 		}
2032 	} else {
2033 		while ((number >= (unsigned long long)scale) && (*uom != 'E')) {
2034 			uom++; /* next unit of measurement */
2035 			save = number;
2036 			/*
2037 			 * If we're over half way to the next unit of
2038 			 * 'scale' bytes (which means we should round
2039 			 * up), then adding half of 'scale' prior to
2040 			 * the division will push us into that next
2041 			 * unit of scale when we perform the division
2042 			 */
2043 			number = (number + (scale / 2)) / scale;
2044 		}
2045 	}
2046 
2047 	/* check if we should output a decimal place after the point */
2048 	if ((save / scale) < 10) {
2049 		/* snprintf() will round for us */
2050 		float fnum = (float)save / scale;
2051 		(void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c",
2052 		    fnum, *uom);
2053 	} else {
2054 		(void) snprintf(buf, sizeof (numbuf_t), "%4llu%c",
2055 		    number, *uom);
2056 	}
2057 	return (buf);
2058 }
2059 
2060 /* Get extended system attributes and set the display */
2061 
2062 int
2063 get_sysxattr(char *fname, struct lbuf *rep)
2064 {
2065 	boolean_t	value;
2066 	data_type_t	type;
2067 	int		error;
2068 	char		*name;
2069 	int		i;
2070 
2071 	if ((error = getattrat(AT_FDCWD, XATTR_VIEW_READWRITE, fname,
2072 	    &response)) != 0) {
2073 		perror("ls:getattrat");
2074 		return (error);
2075 	}
2076 
2077 	/*
2078 	 * Allocate 'sacnt' size array to hold extended timestamp
2079 	 * system attributes and initialize the array.
2080 	 */
2081 	rep->extm = xmalloc(sacnt * sizeof (struct attrtm), rep);
2082 	for (i = 0; i < sacnt; i++) {
2083 		rep->extm[i].stm = 0;
2084 		rep->extm[i].nstm = 0;
2085 		rep->extm[i].name = NULL;
2086 	}
2087 	while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
2088 		name = nvpair_name(pair);
2089 		type = nvpair_type(pair);
2090 		if (type == DATA_TYPE_BOOLEAN_VALUE) {
2091 			error = nvpair_value_boolean_value(pair, &value);
2092 			if (error) {
2093 				(void) fprintf(stderr,
2094 				    gettext("nvpair_value_boolean_value "
2095 				    "failed: error = %d\n"), error);
2096 				continue;
2097 			}
2098 			if (name != NULL)
2099 				set_sysattrb_display(name, value, rep);
2100 			continue;
2101 		} else if (type == DATA_TYPE_UINT64_ARRAY) {
2102 			if (name != NULL)
2103 				set_sysattrtm_display(name, rep);
2104 			continue;
2105 		}
2106 	}
2107 	nvlist_free(response);
2108 	return (0);
2109 }
2110 
2111 /* Set extended system attribute boolean display */
2112 
2113 void
2114 set_sysattrb_display(char *name, boolean_t val, struct lbuf *rep)
2115 {
2116 	f_attr_t	fattr;
2117 	const char	*opt;
2118 	size_t		len;
2119 
2120 	fattr = name_to_attr(name);
2121 	if (fattr != F_ATTR_INVAL && fattr < sacnt) {
2122 		if (vopt) {
2123 			len = strlen(name);
2124 			if (val) {
2125 				rep->exttr[fattr].name = xmalloc(len + 1, rep);
2126 				(void) strlcpy(rep->exttr[fattr].name, name,
2127 				    len + 1);
2128 			} else {
2129 				rep->exttr[fattr].name = xmalloc(len + 3, rep);
2130 				(void) snprintf(rep->exttr[fattr].name, len + 3,
2131 				    "no%s", name);
2132 			}
2133 		} else {
2134 			opt = attr_to_option(fattr);
2135 			if (opt != NULL) {
2136 				len = strlen(opt);
2137 				rep->exttr[fattr].name = xmalloc(len + 1, rep);
2138 				if (val)
2139 					(void) strlcpy(rep->exttr[fattr].name,
2140 					    opt, len + 1);
2141 				else
2142 					(void) strlcpy(rep->exttr[fattr].name,
2143 					    "-", len + 1);
2144 			}
2145 		}
2146 	}
2147 }
2148 
2149 /* Set extended system attribute timestamp display */
2150 
2151 void
2152 set_sysattrtm_display(char *name, struct lbuf *rep)
2153 {
2154 	uint_t		nelem;
2155 	uint64_t	*value;
2156 	int		i;
2157 	size_t		len;
2158 
2159 	if (nvpair_value_uint64_array(pair, &value, &nelem) == 0) {
2160 		if (*value != NULL) {
2161 			len = strlen(name);
2162 			i = 0;
2163 			while (rep->extm[i].stm != 0 && i < sacnt)
2164 				i++;
2165 			rep->extm[i].stm = value[0];
2166 			rep->extm[i].nstm = value[1];
2167 			rep->extm[i].name = xmalloc(len + 1, rep);
2168 			(void) strlcpy(rep->extm[i].name, name, len + 1);
2169 		}
2170 	}
2171 }
2172 
2173 void
2174 format_time(const char *format, time_t sec)
2175 {
2176 
2177 	(void) strftime(time_buf, sizeof (time_buf),
2178 	    dcgettext(NULL, format, LC_TIME),
2179 	    localtime(&sec));
2180 }
2181 
2182 void
2183 format_etime(const char *format, time_t sec, time_t nsec)
2184 {
2185 	char fmt_buf[FMTSIZE];
2186 
2187 	(void) snprintf(fmt_buf, FMTSIZE,
2188 	    format, nsec);
2189 	(void) strftime(time_buf, sizeof (time_buf),
2190 	    fmt_buf, localtime(&sec));
2191 }
2192 
2193 /* Format timestamp extended system attributes */
2194 
2195 void
2196 format_attrtime(struct lbuf *p)
2197 {
2198 	int	tmattr = 0;
2199 	int i;
2200 
2201 	if (p->extm != NULL) {
2202 		for (i = 0; i < sacnt; i++) {
2203 			if (p->extm[i].name != NULL) {
2204 				tmattr = 1;
2205 				break;
2206 			}
2207 		}
2208 	}
2209 	if (tmattr) {
2210 		if (Eflg)
2211 			format_etime(FORMAT4, (time_t)p->extm[i].stm,
2212 			    (time_t)p->extm[i].nstm);
2213 		else  {
2214 			if ((p->lmtime.tv_sec < year) ||
2215 			    (p->lmtime.tv_sec > now))
2216 				format_time(FORMAT1,
2217 				    (time_t)p->extm[i].stm);
2218 			else
2219 				format_time(FORMAT2,
2220 				    (time_t)p->extm[i].stm);
2221 		}
2222 	}
2223 }
2224 
2225 void
2226 print_time(struct lbuf *p)
2227 {
2228 	int i = 0;
2229 
2230 	new_line();
2231 	if (Eflg) {
2232 		format_etime(FORMAT4, p->lat.tv_sec, p->lat.tv_nsec);
2233 		(void) printf("		timestamp: atime	%s\n",
2234 		    time_buf);
2235 		format_etime(FORMAT4, p->lct.tv_sec, p->lct.tv_nsec);
2236 		(void) printf("		timestamp: ctime	%s\n",
2237 		    time_buf);
2238 		format_etime(FORMAT4, p->lmt.tv_sec, p->lmt.tv_nsec);
2239 		(void) printf("		timestamp: mtime	%s\n",
2240 		    time_buf);
2241 		if (p->extm != NULL) {
2242 			while (p->extm[i].nstm != 0 && i < sacnt) {
2243 				format_etime(FORMAT4, p->extm[i].stm,
2244 				    p->extm[i].nstm);
2245 				if (p->extm[i].name != NULL) {
2246 					(void) printf("		timestamp:"
2247 					    " %s	%s\n",
2248 					    p->extm[i].name, time_buf);
2249 				}
2250 				i++;
2251 			}
2252 		}
2253 	} else {
2254 		format_time(FORMAT3, p->lat.tv_sec);
2255 		(void) printf("		timestamp: atime	%s\n",
2256 		    time_buf);
2257 		format_time(FORMAT3, p->lct.tv_sec);
2258 		(void) printf("		timestamp: ctime	%s\n",
2259 		    time_buf);
2260 		format_time(FORMAT3, p->lmt.tv_sec);
2261 		(void) printf("		timestamp: mtime	%s\n",
2262 		    time_buf);
2263 		if (p->extm != NULL) {
2264 			while (p->extm[i].stm != 0 && i < sacnt) {
2265 				format_time(FORMAT3, p->extm[i].stm);
2266 				if (p->extm[i].name != NULL) {
2267 					(void) printf("		timestamp:"
2268 					    " %s	%s\n",
2269 					    p->extm[i].name, time_buf);
2270 				}
2271 				i++;
2272 			}
2273 		}
2274 	}
2275 }
2276 
2277 /* Free extended system attribute lists */
2278 
2279 void
2280 free_sysattr(struct lbuf *p)
2281 {
2282 	int i;
2283 
2284 	if (p->exttr != NULL) {
2285 		for (i = 0; i < sacnt; i++) {
2286 			if (p->exttr[i].name != NULL)
2287 				free(p->exttr[i].name);
2288 		}
2289 		free(p->exttr);
2290 	}
2291 	if (p->extm != NULL) {
2292 		for (i = 0; i < sacnt; i++) {
2293 			if (p->extm[i].name != NULL)
2294 				free(p->extm[i].name);
2295 		}
2296 		free(p->extm);
2297 	}
2298 }
2299 
2300 /* Allocate extended system attribute list */
2301 
2302 void *
2303 xmalloc(size_t size, struct lbuf *p)
2304 {
2305 	if ((p = malloc(size)) == NULL) {
2306 		perror("ls");
2307 		free_sysattr(p);
2308 		nvlist_free(response);
2309 		exit(2);
2310 	}
2311 	return (p);
2312 }
2313