1/*-
2 * Copyright (c) 1989, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Michael Fischbein.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34static const char copyright[] =
35"@(#) Copyright (c) 1989, 1993, 1994\n\
36	The Regents of the University of California.  All rights reserved.\n";
37#endif /* not lint */
38
39#if 0
40#ifndef lint
41static char sccsid[] = "@(#)ls.c	8.5 (Berkeley) 4/2/94";
42#endif /* not lint */
43#endif
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD$");
46
47#include <sys/param.h>
48#include <sys/stat.h>
49#include <sys/ioctl.h>
50#include <sys/mac.h>
51
52#include <dirent.h>
53#include <err.h>
54#include <errno.h>
55#include <fts.h>
56#include <grp.h>
57#include <inttypes.h>
58#include <limits.h>
59#include <locale.h>
60#include <pwd.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#include <unistd.h>
65#ifdef COLORLS
66#include <termcap.h>
67#include <signal.h>
68#endif
69#include <libxo/xo.h>
70
71#include "ls.h"
72#include "extern.h"
73
74/*
75 * Upward approximation of the maximum number of characters needed to
76 * represent a value of integral type t as a string, excluding the
77 * NUL terminator, with provision for a sign.
78 */
79#define	STRBUF_SIZEOF(t)	(1 + CHAR_BIT * sizeof(t) / 3 + 1)
80
81/*
82 * MAKENINES(n) turns n into (10**n)-1.  This is useful for converting a width
83 * into a number that wide in decimal.
84 * XXX: Overflows are not considered.
85 */
86#define MAKENINES(n)							\
87	do {								\
88		intmax_t i;						\
89									\
90		/* Use a loop as all values of n are small. */		\
91		for (i = 1; n > 0; i *= 10)				\
92			n--;						\
93		n = i - 1;						\
94	} while(0)
95
96static void	 display(const FTSENT *, FTSENT *, int);
97static int	 mastercmp(const FTSENT * const *, const FTSENT * const *);
98static void	 traverse(int, char **, int);
99
100static void (*printfcn)(const DISPLAY *);
101static int (*sortfcn)(const FTSENT *, const FTSENT *);
102
103long blocksize;			/* block size units */
104int termwidth = 80;		/* default terminal width */
105
106/* flags */
107       int f_accesstime;	/* use time of last access */
108       int f_birthtime;		/* use time of birth */
109       int f_flags;		/* show flags associated with a file */
110       int f_humanval;		/* show human-readable file sizes */
111       int f_inode;		/* print inode */
112static int f_kblocks;		/* print size in kilobytes */
113       int f_label;		/* show MAC label */
114static int f_listdir;		/* list actual directory, not contents */
115static int f_listdot;		/* list files beginning with . */
116       int f_longform;		/* long listing format */
117static int f_noautodot;		/* do not automatically enable -A for root */
118static int f_nofollow;		/* don't follow symbolic link arguments */
119       int f_nonprint;		/* show unprintables as ? */
120static int f_nosort;		/* don't sort output */
121       int f_notabs;		/* don't use tab-separated multi-col output */
122       int f_numericonly;	/* don't convert uid/gid to name */
123       int f_octal;		/* show unprintables as \xxx */
124       int f_octal_escape;	/* like f_octal but use C escapes if possible */
125static int f_recursive;		/* ls subdirectories also */
126static int f_reversesort;	/* reverse whatever sort is used */
127       int f_samesort;		/* sort time and name in same direction */
128       int f_sectime;		/* print full time information */
129static int f_singlecol;		/* use single column output */
130       int f_size;		/* list size in short listing */
131static int f_sizesort;
132       int f_slash;		/* similar to f_type, but only for dirs */
133       int f_sortacross;	/* sort across rows, not down columns */
134       int f_statustime;	/* use time of last mode change */
135static int f_stream;		/* stream the output, separate with commas */
136       int f_thousands;		/* show file sizes with thousands separators */
137       char *f_timeformat;	/* user-specified time format */
138static int f_timesort;		/* sort by time vice name */
139       int f_type;		/* add type character for non-regular files */
140static int f_whiteout;		/* show whiteout entries */
141
142#ifdef COLORLS
143       int f_color;		/* add type in color for non-regular files */
144
145char *ansi_bgcol;		/* ANSI sequence to set background colour */
146char *ansi_fgcol;		/* ANSI sequence to set foreground colour */
147char *ansi_coloff;		/* ANSI sequence to reset colours */
148char *attrs_off;		/* ANSI sequence to turn off attributes */
149char *enter_bold;		/* ANSI sequence to set color to bold mode */
150#endif
151
152static int rval;
153
154int
155main(int argc, char *argv[])
156{
157	static char dot[] = ".", *dotav[] = {dot, NULL};
158	struct winsize win;
159	int ch, fts_options, notused;
160	char *p;
161	const char *errstr = NULL;
162#ifdef COLORLS
163	char termcapbuf[1024];	/* termcap definition buffer */
164	char tcapbuf[512];	/* capability buffer */
165	char *bp = tcapbuf;
166#endif
167
168	(void)setlocale(LC_ALL, "");
169
170	/* Terminal defaults to -Cq, non-terminal defaults to -1. */
171	if (isatty(STDOUT_FILENO)) {
172		termwidth = 80;
173		if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
174			termwidth = strtonum(p, 0, INT_MAX, &errstr);
175		else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 &&
176		    win.ws_col > 0)
177			termwidth = win.ws_col;
178		f_nonprint = 1;
179	} else {
180		f_singlecol = 1;
181		/* retrieve environment variable, in case of explicit -C */
182		p = getenv("COLUMNS");
183		if (p)
184			termwidth = strtonum(p, 0, INT_MAX, &errstr);
185	}
186
187	if (errstr)
188		termwidth = 80;
189
190	fts_options = FTS_PHYSICAL;
191	if (getenv("LS_SAMESORT"))
192		f_samesort = 1;
193
194	argc = xo_parse_args(argc, argv);
195	if (argc < 0)
196		return (1);
197	xo_set_flags(NULL, XOF_COLUMNS);
198	xo_set_version(LS_XO_VERSION);
199
200	while ((ch = getopt(argc, argv,
201	    "1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,")) != -1) {
202		switch (ch) {
203		/*
204		 * The -1, -C, -x and -l options all override each other so
205		 * shell aliasing works right.
206		 */
207		case '1':
208			f_singlecol = 1;
209			f_longform = 0;
210			f_stream = 0;
211			break;
212		case 'C':
213			f_sortacross = f_longform = f_singlecol = 0;
214			break;
215		case 'l':
216			f_longform = 1;
217			f_singlecol = 0;
218			f_stream = 0;
219			break;
220		case 'x':
221			f_sortacross = 1;
222			f_longform = 0;
223			f_singlecol = 0;
224			break;
225		/* The -c, -u, and -U options override each other. */
226		case 'c':
227			f_statustime = 1;
228			f_accesstime = 0;
229			f_birthtime = 0;
230			break;
231		case 'u':
232			f_accesstime = 1;
233			f_statustime = 0;
234			f_birthtime = 0;
235			break;
236		case 'U':
237			f_birthtime = 1;
238			f_accesstime = 0;
239			f_statustime = 0;
240			break;
241		case 'f':
242			f_nosort = 1;
243		       /* FALLTHROUGH */
244		case 'a':
245			fts_options |= FTS_SEEDOT;
246			/* FALLTHROUGH */
247		case 'A':
248			f_listdot = 1;
249			break;
250		/* The -t and -S options override each other. */
251		case 'S':
252			f_sizesort = 1;
253			f_timesort = 0;
254			break;
255		case 't':
256			f_timesort = 1;
257			f_sizesort = 0;
258			break;
259		/* Other flags.  Please keep alphabetic. */
260		case ',':
261			f_thousands = 1;
262			break;
263		case 'B':
264			f_nonprint = 0;
265			f_octal = 1;
266			f_octal_escape = 0;
267			break;
268		case 'D':
269			f_timeformat = optarg;
270			break;
271		case 'F':
272			f_type = 1;
273			f_slash = 0;
274			break;
275		case 'G':
276			setenv("CLICOLOR", "", 1);
277			break;
278		case 'H':
279			fts_options |= FTS_COMFOLLOW;
280			f_nofollow = 0;
281			break;
282		case 'I':
283			f_noautodot = 1;
284			break;
285		case 'L':
286			fts_options &= ~FTS_PHYSICAL;
287			fts_options |= FTS_LOGICAL;
288			f_nofollow = 0;
289			break;
290		case 'P':
291			fts_options &= ~FTS_COMFOLLOW;
292			fts_options &= ~FTS_LOGICAL;
293			fts_options |= FTS_PHYSICAL;
294			f_nofollow = 1;
295			break;
296		case 'R':
297			f_recursive = 1;
298			break;
299		case 'T':
300			f_sectime = 1;
301			break;
302		case 'W':
303			f_whiteout = 1;
304			break;
305		case 'Z':
306			f_label = 1;
307			break;
308		case 'b':
309			f_nonprint = 0;
310			f_octal = 0;
311			f_octal_escape = 1;
312			break;
313		/* The -d option turns off the -R option. */
314		case 'd':
315			f_listdir = 1;
316			f_recursive = 0;
317			break;
318		case 'g':	/* Compatibility with 4.3BSD. */
319			break;
320		case 'h':
321			f_humanval = 1;
322			break;
323		case 'i':
324			f_inode = 1;
325			break;
326		case 'k':
327			f_humanval = 0;
328			f_kblocks = 1;
329			break;
330		case 'm':
331			f_stream = 1;
332			f_singlecol = 0;
333			f_longform = 0;
334			break;
335		case 'n':
336			f_numericonly = 1;
337			break;
338		case 'o':
339			f_flags = 1;
340			break;
341		case 'p':
342			f_slash = 1;
343			f_type = 1;
344			break;
345		case 'q':
346			f_nonprint = 1;
347			f_octal = 0;
348			f_octal_escape = 0;
349			break;
350		case 'r':
351			f_reversesort = 1;
352			break;
353		case 's':
354			f_size = 1;
355			break;
356		case 'w':
357			f_nonprint = 0;
358			f_octal = 0;
359			f_octal_escape = 0;
360			break;
361		case 'y':
362			f_samesort = 1;
363			break;
364		default:
365		case '?':
366			usage();
367		}
368	}
369	argc -= optind;
370	argv += optind;
371
372	/* Root is -A automatically unless -I. */
373	if (!f_listdot && getuid() == (uid_t)0 && !f_noautodot)
374		f_listdot = 1;
375
376	/* Enabling of colours is conditional on the environment. */
377	if (getenv("CLICOLOR") &&
378	    (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE")))
379#ifdef COLORLS
380		if (tgetent(termcapbuf, getenv("TERM")) == 1) {
381			ansi_fgcol = tgetstr("AF", &bp);
382			ansi_bgcol = tgetstr("AB", &bp);
383			attrs_off = tgetstr("me", &bp);
384			enter_bold = tgetstr("md", &bp);
385
386			/* To switch colours off use 'op' if
387			 * available, otherwise use 'oc', or
388			 * don't do colours at all. */
389			ansi_coloff = tgetstr("op", &bp);
390			if (!ansi_coloff)
391				ansi_coloff = tgetstr("oc", &bp);
392			if (ansi_fgcol && ansi_bgcol && ansi_coloff)
393				f_color = 1;
394		}
395#else
396		xo_warnx("color support not compiled in");
397#endif /*COLORLS*/
398
399#ifdef COLORLS
400	if (f_color) {
401		/*
402		 * We can't put tabs and color sequences together:
403		 * column number will be incremented incorrectly
404		 * for "stty oxtabs" mode.
405		 */
406		f_notabs = 1;
407		(void)signal(SIGINT, colorquit);
408		(void)signal(SIGQUIT, colorquit);
409		parsecolors(getenv("LSCOLORS"));
410	}
411#endif
412
413	/*
414	 * If not -F, -i, -l, -s, -S or -t options, don't require stat
415	 * information, unless in color mode in which case we do
416	 * need this to determine which colors to display.
417	 */
418	if (!f_inode && !f_longform && !f_size && !f_timesort &&
419	    !f_sizesort && !f_type
420#ifdef COLORLS
421	    && !f_color
422#endif
423	    )
424		fts_options |= FTS_NOSTAT;
425
426	/*
427	 * If not -F, -P, -d or -l options, follow any symbolic links listed on
428	 * the command line, unless in color mode in which case we need to
429	 * distinguish file type for a symbolic link itself and its target.
430	 */
431	if (!f_nofollow && !f_longform && !f_listdir && (!f_type || f_slash)
432#ifdef COLORLS
433	    && !f_color
434#endif
435	    )
436		fts_options |= FTS_COMFOLLOW;
437
438	/*
439	 * If -W, show whiteout entries
440	 */
441#ifdef FTS_WHITEOUT
442	if (f_whiteout)
443		fts_options |= FTS_WHITEOUT;
444#endif
445
446	/* If -i, -l or -s, figure out block size. */
447	if (f_inode || f_longform || f_size) {
448		if (f_kblocks)
449			blocksize = 2;
450		else {
451			(void)getbsize(&notused, &blocksize);
452			blocksize /= 512;
453		}
454	}
455	/* Select a sort function. */
456	if (f_reversesort) {
457		if (!f_timesort && !f_sizesort)
458			sortfcn = revnamecmp;
459		else if (f_sizesort)
460			sortfcn = revsizecmp;
461		else if (f_accesstime)
462			sortfcn = revacccmp;
463		else if (f_birthtime)
464			sortfcn = revbirthcmp;
465		else if (f_statustime)
466			sortfcn = revstatcmp;
467		else		/* Use modification time. */
468			sortfcn = revmodcmp;
469	} else {
470		if (!f_timesort && !f_sizesort)
471			sortfcn = namecmp;
472		else if (f_sizesort)
473			sortfcn = sizecmp;
474		else if (f_accesstime)
475			sortfcn = acccmp;
476		else if (f_birthtime)
477			sortfcn = birthcmp;
478		else if (f_statustime)
479			sortfcn = statcmp;
480		else		/* Use modification time. */
481			sortfcn = modcmp;
482	}
483
484	/* Select a print function. */
485	if (f_singlecol)
486		printfcn = printscol;
487	else if (f_longform)
488		printfcn = printlong;
489	else if (f_stream)
490		printfcn = printstream;
491	else
492		printfcn = printcol;
493
494	xo_open_container("file-information");
495	if (argc)
496		traverse(argc, argv, fts_options);
497	else
498		traverse(1, dotav, fts_options);
499	xo_close_container("file-information");
500	xo_finish();
501	exit(rval);
502}
503
504static int output;		/* If anything output. */
505
506/*
507 * Traverse() walks the logical directory structure specified by the argv list
508 * in the order specified by the mastercmp() comparison function.  During the
509 * traversal it passes linked lists of structures to display() which represent
510 * a superset (may be exact set) of the files to be displayed.
511 */
512static void
513traverse(int argc, char *argv[], int options)
514{
515	FTS *ftsp;
516	FTSENT *p, *chp;
517	int ch_options;
518	int first = 1;
519
520	if ((ftsp =
521	    fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
522		xo_err(1, "fts_open");
523
524	/*
525	 * We ignore errors from fts_children here since they will be
526	 * replicated and signalled on the next call to fts_read() below.
527	 */
528	chp = fts_children(ftsp, 0);
529	if (chp != NULL)
530		display(NULL, chp, options);
531	if (f_listdir)
532		return;
533
534	/*
535	 * If not recursing down this tree and don't need stat info, just get
536	 * the names.
537	 */
538	ch_options = !f_recursive && !f_label &&
539	    options & FTS_NOSTAT ? FTS_NAMEONLY : 0;
540
541	while ((p = fts_read(ftsp)) != NULL)
542		switch (p->fts_info) {
543		case FTS_DC:
544			xo_warnx("%s: directory causes a cycle", p->fts_name);
545			break;
546		case FTS_DNR:
547		case FTS_ERR:
548			xo_warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
549			rval = 1;
550			break;
551		case FTS_D:
552			if (p->fts_level != FTS_ROOTLEVEL &&
553			    p->fts_name[0] == '.' && !f_listdot)
554				break;
555
556			if (first) {
557				first = 0;
558				xo_open_list("directory");
559			}
560			xo_open_instance("directory");
561
562			/*
563			 * If already output something, put out a newline as
564			 * a separator.  If multiple arguments, precede each
565			 * directory with its name.
566			 */
567			if (output) {
568				xo_emit("\n");
569				(void)printname("path", p->fts_path);
570				xo_emit(":\n");
571			} else if (argc > 1) {
572				(void)printname("path", p->fts_path);
573				xo_emit(":\n");
574				output = 1;
575			}
576			chp = fts_children(ftsp, ch_options);
577			display(p, chp, options);
578
579			xo_close_instance("directory");
580			if (!f_recursive && chp != NULL)
581				(void)fts_set(ftsp, p, FTS_SKIP);
582			break;
583		default:
584			break;
585		}
586	if (!first)
587		xo_close_list("directory");
588	if (errno)
589		xo_err(1, "fts_read");
590}
591
592/*
593 * Display() takes a linked list of FTSENT structures and passes the list
594 * along with any other necessary information to the print function.  P
595 * points to the parent directory of the display list.
596 */
597static void
598display(const FTSENT *p, FTSENT *list, int options)
599{
600	struct stat *sp;
601	DISPLAY d;
602	FTSENT *cur;
603	NAMES *np;
604	off_t maxsize;
605	long maxblock;
606	uintmax_t maxinode;
607	u_long btotal, labelstrlen, maxlen, maxnlink;
608	u_long maxlabelstr;
609	u_int sizelen;
610	int maxflags;
611	gid_t maxgroup;
612	uid_t maxuser;
613	size_t flen, ulen, glen;
614	char *initmax;
615	int entries, needstats;
616	const char *user, *group;
617	char *flags, *labelstr = NULL;
618	char ngroup[STRBUF_SIZEOF(uid_t) + 1];
619	char nuser[STRBUF_SIZEOF(gid_t) + 1];
620
621	needstats = f_inode || f_longform || f_size;
622	flen = 0;
623	btotal = 0;
624	initmax = getenv("LS_COLWIDTHS");
625	/* Fields match -lios order.  New ones should be added at the end. */
626	maxlabelstr = maxblock = maxlen = maxnlink = 0;
627	maxuser = maxgroup = maxflags = maxsize = 0;
628	maxinode = 0;
629	if (initmax != NULL && *initmax != '\0') {
630		char *initmax2, *jinitmax;
631		int ninitmax;
632
633		/* Fill-in "::" as "0:0:0" for the sake of scanf. */
634		jinitmax = malloc(strlen(initmax) * 2 + 2);
635		if (jinitmax == NULL)
636			xo_err(1, "malloc");
637		initmax2 = jinitmax;
638		if (*initmax == ':')
639			strcpy(initmax2, "0:"), initmax2 += 2;
640		else
641			*initmax2++ = *initmax, *initmax2 = '\0';
642		for (initmax++; *initmax != '\0'; initmax++) {
643			if (initmax[-1] == ':' && initmax[0] == ':') {
644				*initmax2++ = '0';
645				*initmax2++ = initmax[0];
646				initmax2[1] = '\0';
647			} else {
648				*initmax2++ = initmax[0];
649				initmax2[1] = '\0';
650			}
651		}
652		if (initmax2[-1] == ':')
653			strcpy(initmax2, "0");
654
655		ninitmax = sscanf(jinitmax,
656		    " %ju : %ld : %lu : %u : %u : %i : %jd : %lu : %lu ",
657		    &maxinode, &maxblock, &maxnlink, &maxuser,
658		    &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr);
659		f_notabs = 1;
660		switch (ninitmax) {
661		case 0:
662			maxinode = 0;
663			/* FALLTHROUGH */
664		case 1:
665			maxblock = 0;
666			/* FALLTHROUGH */
667		case 2:
668			maxnlink = 0;
669			/* FALLTHROUGH */
670		case 3:
671			maxuser = 0;
672			/* FALLTHROUGH */
673		case 4:
674			maxgroup = 0;
675			/* FALLTHROUGH */
676		case 5:
677			maxflags = 0;
678			/* FALLTHROUGH */
679		case 6:
680			maxsize = 0;
681			/* FALLTHROUGH */
682		case 7:
683			maxlen = 0;
684			/* FALLTHROUGH */
685		case 8:
686			maxlabelstr = 0;
687			/* FALLTHROUGH */
688#ifdef COLORLS
689			if (!f_color)
690#endif
691				f_notabs = 0;
692			/* FALLTHROUGH */
693		default:
694			break;
695		}
696		MAKENINES(maxinode);
697		MAKENINES(maxblock);
698		MAKENINES(maxnlink);
699		MAKENINES(maxsize);
700		free(jinitmax);
701	}
702	d.s_size = 0;
703	sizelen = 0;
704	flags = NULL;
705	for (cur = list, entries = 0; cur; cur = cur->fts_link) {
706		if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) {
707			xo_warnx("%s: %s",
708			    cur->fts_name, strerror(cur->fts_errno));
709			cur->fts_number = NO_PRINT;
710			rval = 1;
711			continue;
712		}
713		/*
714		 * P is NULL if list is the argv list, to which different rules
715		 * apply.
716		 */
717		if (p == NULL) {
718			/* Directories will be displayed later. */
719			if (cur->fts_info == FTS_D && !f_listdir) {
720				cur->fts_number = NO_PRINT;
721				continue;
722			}
723		} else {
724			/* Only display dot file if -a/-A set. */
725			if (cur->fts_name[0] == '.' && !f_listdot) {
726				cur->fts_number = NO_PRINT;
727				continue;
728			}
729		}
730		if (cur->fts_namelen > maxlen)
731			maxlen = cur->fts_namelen;
732		if (f_octal || f_octal_escape) {
733			u_long t = len_octal(cur->fts_name, cur->fts_namelen);
734
735			if (t > maxlen)
736				maxlen = t;
737		}
738		if (needstats) {
739			sp = cur->fts_statp;
740			if (sp->st_blocks > maxblock)
741				maxblock = sp->st_blocks;
742			if (sp->st_ino > maxinode)
743				maxinode = sp->st_ino;
744			if (sp->st_nlink > maxnlink)
745				maxnlink = sp->st_nlink;
746			if (sp->st_size > maxsize)
747				maxsize = sp->st_size;
748
749			btotal += sp->st_blocks;
750			if (f_longform) {
751				if (f_numericonly) {
752					(void)snprintf(nuser, sizeof(nuser),
753					    "%u", sp->st_uid);
754					(void)snprintf(ngroup, sizeof(ngroup),
755					    "%u", sp->st_gid);
756					user = nuser;
757					group = ngroup;
758				} else {
759					user = user_from_uid(sp->st_uid, 0);
760					group = group_from_gid(sp->st_gid, 0);
761				}
762				if ((ulen = strlen(user)) > maxuser)
763					maxuser = ulen;
764				if ((glen = strlen(group)) > maxgroup)
765					maxgroup = glen;
766				if (f_flags) {
767					flags = fflagstostr(sp->st_flags);
768					if (flags != NULL && *flags == '\0') {
769						free(flags);
770						flags = strdup("-");
771					}
772					if (flags == NULL)
773						xo_err(1, "fflagstostr");
774					flen = strlen(flags);
775					if (flen > (size_t)maxflags)
776						maxflags = flen;
777				} else
778					flen = 0;
779				labelstr = NULL;
780				if (f_label) {
781					char name[PATH_MAX + 1];
782					mac_t label;
783					int error;
784
785					error = mac_prepare_file_label(&label);
786					if (error == -1) {
787						xo_warn("MAC label for %s/%s",
788						    cur->fts_parent->fts_path,
789						    cur->fts_name);
790						goto label_out;
791					}
792
793					if (cur->fts_level == FTS_ROOTLEVEL)
794						snprintf(name, sizeof(name),
795						    "%s", cur->fts_name);
796					else
797						snprintf(name, sizeof(name),
798						    "%s/%s", cur->fts_parent->
799						    fts_accpath, cur->fts_name);
800
801					if (options & FTS_LOGICAL)
802						error = mac_get_file(name,
803						    label);
804					else
805						error = mac_get_link(name,
806						    label);
807					if (error == -1) {
808						xo_warn("MAC label for %s/%s",
809						    cur->fts_parent->fts_path,
810						    cur->fts_name);
811						mac_free(label);
812						goto label_out;
813					}
814
815					error = mac_to_text(label,
816					    &labelstr);
817					if (error == -1) {
818						xo_warn("MAC label for %s/%s",
819						    cur->fts_parent->fts_path,
820						    cur->fts_name);
821						mac_free(label);
822						goto label_out;
823					}
824					mac_free(label);
825label_out:
826					if (labelstr == NULL)
827						labelstr = strdup("-");
828					labelstrlen = strlen(labelstr);
829					if (labelstrlen > maxlabelstr)
830						maxlabelstr = labelstrlen;
831				} else
832					labelstrlen = 0;
833
834				if ((np = malloc(sizeof(NAMES) + labelstrlen +
835				    ulen + glen + flen + 4)) == NULL)
836					xo_err(1, "malloc");
837
838				np->user = &np->data[0];
839				(void)strcpy(np->user, user);
840				np->group = &np->data[ulen + 1];
841				(void)strcpy(np->group, group);
842
843				if (S_ISCHR(sp->st_mode) ||
844				    S_ISBLK(sp->st_mode)) {
845					sizelen = snprintf(NULL, 0,
846					    "%#jx", (uintmax_t)sp->st_rdev);
847					if (d.s_size < sizelen)
848						d.s_size = sizelen;
849				}
850
851				if (f_flags) {
852					np->flags = &np->data[ulen + glen + 2];
853					(void)strcpy(np->flags, flags);
854					free(flags);
855				}
856				if (f_label) {
857					np->label = &np->data[ulen + glen + 2
858					    + (f_flags ? flen + 1 : 0)];
859					(void)strcpy(np->label, labelstr);
860					free(labelstr);
861				}
862				cur->fts_pointer = np;
863			}
864		}
865		++entries;
866	}
867
868	/*
869	 * If there are no entries to display, we normally stop right
870	 * here.  However, we must continue if we have to display the
871	 * total block count.  In this case, we display the total only
872	 * on the second (p != NULL) pass.
873	 */
874	if (!entries && (!(f_longform || f_size) || p == NULL))
875		return;
876
877	d.list = list;
878	d.entries = entries;
879	d.maxlen = maxlen;
880	if (needstats) {
881		d.btotal = btotal;
882		d.s_block = snprintf(NULL, 0, "%lu", howmany(maxblock, blocksize));
883		d.s_flags = maxflags;
884		d.s_label = maxlabelstr;
885		d.s_group = maxgroup;
886		d.s_inode = snprintf(NULL, 0, "%ju", maxinode);
887		d.s_nlink = snprintf(NULL, 0, "%lu", maxnlink);
888		sizelen = f_humanval ? HUMANVALSTR_LEN :
889		    snprintf(NULL, 0, "%ju", maxsize);
890		if (d.s_size < sizelen)
891			d.s_size = sizelen;
892		d.s_user = maxuser;
893	}
894	if (f_thousands)			/* make space for commas */
895		d.s_size += (d.s_size - 1) / 3;
896	printfcn(&d);
897	output = 1;
898
899	if (f_longform)
900		for (cur = list; cur; cur = cur->fts_link)
901			free(cur->fts_pointer);
902}
903
904/*
905 * Ordering for mastercmp:
906 * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories
907 * as larger than directories.  Within either group, use the sort function.
908 * All other levels use the sort function.  Error entries remain unsorted.
909 */
910static int
911mastercmp(const FTSENT * const *a, const FTSENT * const *b)
912{
913	int a_info, b_info;
914
915	a_info = (*a)->fts_info;
916	if (a_info == FTS_ERR)
917		return (0);
918	b_info = (*b)->fts_info;
919	if (b_info == FTS_ERR)
920		return (0);
921
922	if (a_info == FTS_NS || b_info == FTS_NS)
923		return (namecmp(*a, *b));
924
925	if (a_info != b_info &&
926	    (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) {
927		if (a_info == FTS_D)
928			return (1);
929		if (b_info == FTS_D)
930			return (-1);
931	}
932	return (sortfcn(*a, *b));
933}
934