ls.c revision 285437
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: head/bin/ls/ls.c 285437 2015-07-13 05:56:27Z bapt $");
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 */
122static 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
199	while ((ch = getopt(argc, argv,
200	    "1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,")) != -1) {
201		switch (ch) {
202		/*
203		 * The -1, -C, -x and -l options all override each other so
204		 * shell aliasing works right.
205		 */
206		case '1':
207			f_singlecol = 1;
208			f_longform = 0;
209			f_stream = 0;
210			break;
211		case 'C':
212			f_sortacross = f_longform = f_singlecol = 0;
213			break;
214		case 'l':
215			f_longform = 1;
216			f_singlecol = 0;
217			f_stream = 0;
218			break;
219		case 'x':
220			f_sortacross = 1;
221			f_longform = 0;
222			f_singlecol = 0;
223			break;
224		/* The -c, -u, and -U options override each other. */
225		case 'c':
226			f_statustime = 1;
227			f_accesstime = 0;
228			f_birthtime = 0;
229			break;
230		case 'u':
231			f_accesstime = 1;
232			f_statustime = 0;
233			f_birthtime = 0;
234			break;
235		case 'U':
236			f_birthtime = 1;
237			f_accesstime = 0;
238			f_statustime = 0;
239			break;
240		case 'f':
241			f_nosort = 1;
242		       /* FALLTHROUGH */
243		case 'a':
244			fts_options |= FTS_SEEDOT;
245			/* FALLTHROUGH */
246		case 'A':
247			f_listdot = 1;
248			break;
249		/* The -t and -S options override each other. */
250		case 'S':
251			f_sizesort = 1;
252			f_timesort = 0;
253			break;
254		case 't':
255			f_timesort = 1;
256			f_sizesort = 0;
257			break;
258		/* Other flags.  Please keep alphabetic. */
259		case ',':
260			f_thousands = 1;
261			break;
262		case 'B':
263			f_nonprint = 0;
264			f_octal = 1;
265			f_octal_escape = 0;
266			break;
267		case 'D':
268			f_timeformat = optarg;
269			break;
270		case 'F':
271			f_type = 1;
272			f_slash = 0;
273			break;
274		case 'G':
275			setenv("CLICOLOR", "", 1);
276			break;
277		case 'H':
278			fts_options |= FTS_COMFOLLOW;
279			f_nofollow = 0;
280			break;
281		case 'I':
282			f_noautodot = 1;
283			break;
284		case 'L':
285			fts_options &= ~FTS_PHYSICAL;
286			fts_options |= FTS_LOGICAL;
287			f_nofollow = 0;
288			break;
289		case 'P':
290			fts_options &= ~FTS_COMFOLLOW;
291			fts_options &= ~FTS_LOGICAL;
292			fts_options |= FTS_PHYSICAL;
293			f_nofollow = 1;
294			break;
295		case 'R':
296			f_recursive = 1;
297			break;
298		case 'T':
299			f_sectime = 1;
300			break;
301		case 'W':
302			f_whiteout = 1;
303			break;
304		case 'Z':
305			f_label = 1;
306			break;
307		case 'b':
308			f_nonprint = 0;
309			f_octal = 0;
310			f_octal_escape = 1;
311			break;
312		/* The -d option turns off the -R option. */
313		case 'd':
314			f_listdir = 1;
315			f_recursive = 0;
316			break;
317		case 'g':	/* Compatibility with 4.3BSD. */
318			break;
319		case 'h':
320			f_humanval = 1;
321			break;
322		case 'i':
323			f_inode = 1;
324			break;
325		case 'k':
326			f_humanval = 0;
327			f_kblocks = 1;
328			break;
329		case 'm':
330			f_stream = 1;
331			f_singlecol = 0;
332			f_longform = 0;
333			break;
334		case 'n':
335			f_numericonly = 1;
336			break;
337		case 'o':
338			f_flags = 1;
339			break;
340		case 'p':
341			f_slash = 1;
342			f_type = 1;
343			break;
344		case 'q':
345			f_nonprint = 1;
346			f_octal = 0;
347			f_octal_escape = 0;
348			break;
349		case 'r':
350			f_reversesort = 1;
351			break;
352		case 's':
353			f_size = 1;
354			break;
355		case 'w':
356			f_nonprint = 0;
357			f_octal = 0;
358			f_octal_escape = 0;
359			break;
360		case 'y':
361			f_samesort = 1;
362			break;
363		default:
364		case '?':
365			usage();
366		}
367	}
368	argc -= optind;
369	argv += optind;
370
371	/* Root is -A automatically unless -I. */
372	if (!f_listdot && getuid() == (uid_t)0 && !f_noautodot)
373		f_listdot = 1;
374
375	/* Enabling of colours is conditional on the environment. */
376	if (getenv("CLICOLOR") &&
377	    (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE")))
378#ifdef COLORLS
379		if (tgetent(termcapbuf, getenv("TERM")) == 1) {
380			ansi_fgcol = tgetstr("AF", &bp);
381			ansi_bgcol = tgetstr("AB", &bp);
382			attrs_off = tgetstr("me", &bp);
383			enter_bold = tgetstr("md", &bp);
384
385			/* To switch colours off use 'op' if
386			 * available, otherwise use 'oc', or
387			 * don't do colours at all. */
388			ansi_coloff = tgetstr("op", &bp);
389			if (!ansi_coloff)
390				ansi_coloff = tgetstr("oc", &bp);
391			if (ansi_fgcol && ansi_bgcol && ansi_coloff)
392				f_color = 1;
393		}
394#else
395		xo_warnx("color support not compiled in");
396#endif /*COLORLS*/
397
398#ifdef COLORLS
399	if (f_color) {
400		/*
401		 * We can't put tabs and color sequences together:
402		 * column number will be incremented incorrectly
403		 * for "stty oxtabs" mode.
404		 */
405		f_notabs = 1;
406		(void)signal(SIGINT, colorquit);
407		(void)signal(SIGQUIT, colorquit);
408		parsecolors(getenv("LSCOLORS"));
409	}
410#endif
411
412	/*
413	 * If not -F, -i, -l, -s, -S or -t options, don't require stat
414	 * information, unless in color mode in which case we do
415	 * need this to determine which colors to display.
416	 */
417	if (!f_inode && !f_longform && !f_size && !f_timesort &&
418	    !f_sizesort && !f_type
419#ifdef COLORLS
420	    && !f_color
421#endif
422	    )
423		fts_options |= FTS_NOSTAT;
424
425	/*
426	 * If not -F, -P, -d or -l options, follow any symbolic links listed on
427	 * the command line, unless in color mode in which case we need to
428	 * distinguish file type for a symbolic link itself and its target.
429	 */
430	if (!f_nofollow && !f_longform && !f_listdir && (!f_type || f_slash)
431#ifdef COLORLS
432	    && !f_color
433#endif
434	    )
435		fts_options |= FTS_COMFOLLOW;
436
437	/*
438	 * If -W, show whiteout entries
439	 */
440#ifdef FTS_WHITEOUT
441	if (f_whiteout)
442		fts_options |= FTS_WHITEOUT;
443#endif
444
445	/* If -i, -l or -s, figure out block size. */
446	if (f_inode || f_longform || f_size) {
447		if (f_kblocks)
448			blocksize = 2;
449		else {
450			(void)getbsize(&notused, &blocksize);
451			blocksize /= 512;
452		}
453	}
454	/* Select a sort function. */
455	if (f_reversesort) {
456		if (!f_timesort && !f_sizesort)
457			sortfcn = revnamecmp;
458		else if (f_sizesort)
459			sortfcn = revsizecmp;
460		else if (f_accesstime)
461			sortfcn = revacccmp;
462		else if (f_birthtime)
463			sortfcn = revbirthcmp;
464		else if (f_statustime)
465			sortfcn = revstatcmp;
466		else		/* Use modification time. */
467			sortfcn = revmodcmp;
468	} else {
469		if (!f_timesort && !f_sizesort)
470			sortfcn = namecmp;
471		else if (f_sizesort)
472			sortfcn = sizecmp;
473		else if (f_accesstime)
474			sortfcn = acccmp;
475		else if (f_birthtime)
476			sortfcn = birthcmp;
477		else if (f_statustime)
478			sortfcn = statcmp;
479		else		/* Use modification time. */
480			sortfcn = modcmp;
481	}
482
483	/* Select a print function. */
484	if (f_singlecol)
485		printfcn = printscol;
486	else if (f_longform)
487		printfcn = printlong;
488	else if (f_stream)
489		printfcn = printstream;
490	else
491		printfcn = printcol;
492
493	xo_open_container("file-information");
494	if (argc)
495		traverse(argc, argv, fts_options);
496	else
497		traverse(1, dotav, fts_options);
498	xo_close_container("file-information");
499	xo_finish();
500	exit(rval);
501}
502
503static int output;		/* If anything output. */
504
505/*
506 * Traverse() walks the logical directory structure specified by the argv list
507 * in the order specified by the mastercmp() comparison function.  During the
508 * traversal it passes linked lists of structures to display() which represent
509 * a superset (may be exact set) of the files to be displayed.
510 */
511static void
512traverse(int argc, char *argv[], int options)
513{
514	FTS *ftsp;
515	FTSENT *p, *chp;
516	int ch_options;
517	int first = 1;
518
519	if ((ftsp =
520	    fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
521		xo_err(1, "fts_open");
522
523	/*
524	 * We ignore errors from fts_children here since they will be
525	 * replicated and signalled on the next call to fts_read() below.
526	 */
527	chp = fts_children(ftsp, 0);
528	if (chp != NULL)
529		display(NULL, chp, options);
530	if (f_listdir)
531		return;
532
533	/*
534	 * If not recursing down this tree and don't need stat info, just get
535	 * the names.
536	 */
537	ch_options = !f_recursive && !f_label &&
538	    options & FTS_NOSTAT ? FTS_NAMEONLY : 0;
539
540	while ((p = fts_read(ftsp)) != NULL)
541		switch (p->fts_info) {
542		case FTS_DC:
543			xo_warnx("%s: directory causes a cycle", p->fts_name);
544			break;
545		case FTS_DNR:
546		case FTS_ERR:
547			xo_warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
548			rval = 1;
549			break;
550		case FTS_D:
551			if (p->fts_level != FTS_ROOTLEVEL &&
552			    p->fts_name[0] == '.' && !f_listdot)
553				break;
554
555			if (first) {
556				first = 0;
557				xo_open_list("directory");
558			}
559			xo_open_instance("directory");
560
561			/*
562			 * If already output something, put out a newline as
563			 * a separator.  If multiple arguments, precede each
564			 * directory with its name.
565			 */
566			if (output) {
567				xo_emit("\n");
568				(void)printname("path", p->fts_path);
569				xo_emit(":\n");
570			} else if (argc > 1) {
571				(void)printname("path", p->fts_path);
572				xo_emit(":\n");
573				output = 1;
574			}
575			chp = fts_children(ftsp, ch_options);
576			display(p, chp, options);
577
578			xo_close_instance("directory");
579			if (!f_recursive && chp != NULL)
580				(void)fts_set(ftsp, p, FTS_SKIP);
581			break;
582		default:
583			break;
584		}
585	if (!first)
586		xo_close_list("directory");
587	if (errno)
588		xo_err(1, "fts_read");
589}
590
591/*
592 * Display() takes a linked list of FTSENT structures and passes the list
593 * along with any other necessary information to the print function.  P
594 * points to the parent directory of the display list.
595 */
596static void
597display(const FTSENT *p, FTSENT *list, int options)
598{
599	struct stat *sp;
600	DISPLAY d;
601	FTSENT *cur;
602	NAMES *np;
603	off_t maxsize;
604	long maxblock;
605	uintmax_t maxinode;
606	u_long btotal, labelstrlen, maxlen, maxnlink;
607	u_long maxlabelstr;
608	u_int sizelen;
609	int maxflags;
610	gid_t maxgroup;
611	uid_t maxuser;
612	size_t flen, ulen, glen;
613	char *initmax;
614	int entries, needstats;
615	const char *user, *group;
616	char *flags, *labelstr = NULL;
617	char ngroup[STRBUF_SIZEOF(uid_t) + 1];
618	char nuser[STRBUF_SIZEOF(gid_t) + 1];
619
620	needstats = f_inode || f_longform || f_size;
621	flen = 0;
622	btotal = 0;
623	initmax = getenv("LS_COLWIDTHS");
624	/* Fields match -lios order.  New ones should be added at the end. */
625	maxlabelstr = maxblock = maxlen = maxnlink = 0;
626	maxuser = maxgroup = maxflags = maxsize = 0;
627	maxinode = 0;
628	if (initmax != NULL && *initmax != '\0') {
629		char *initmax2, *jinitmax;
630		int ninitmax;
631
632		/* Fill-in "::" as "0:0:0" for the sake of scanf. */
633		jinitmax = malloc(strlen(initmax) * 2 + 2);
634		if (jinitmax == NULL)
635			xo_err(1, "malloc");
636		initmax2 = jinitmax;
637		if (*initmax == ':')
638			strcpy(initmax2, "0:"), initmax2 += 2;
639		else
640			*initmax2++ = *initmax, *initmax2 = '\0';
641		for (initmax++; *initmax != '\0'; initmax++) {
642			if (initmax[-1] == ':' && initmax[0] == ':') {
643				*initmax2++ = '0';
644				*initmax2++ = initmax[0];
645				initmax2[1] = '\0';
646			} else {
647				*initmax2++ = initmax[0];
648				initmax2[1] = '\0';
649			}
650		}
651		if (initmax2[-1] == ':')
652			strcpy(initmax2, "0");
653
654		ninitmax = sscanf(jinitmax,
655		    " %ju : %ld : %lu : %u : %u : %i : %jd : %lu : %lu ",
656		    &maxinode, &maxblock, &maxnlink, &maxuser,
657		    &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr);
658		f_notabs = 1;
659		switch (ninitmax) {
660		case 0:
661			maxinode = 0;
662			/* FALLTHROUGH */
663		case 1:
664			maxblock = 0;
665			/* FALLTHROUGH */
666		case 2:
667			maxnlink = 0;
668			/* FALLTHROUGH */
669		case 3:
670			maxuser = 0;
671			/* FALLTHROUGH */
672		case 4:
673			maxgroup = 0;
674			/* FALLTHROUGH */
675		case 5:
676			maxflags = 0;
677			/* FALLTHROUGH */
678		case 6:
679			maxsize = 0;
680			/* FALLTHROUGH */
681		case 7:
682			maxlen = 0;
683			/* FALLTHROUGH */
684		case 8:
685			maxlabelstr = 0;
686			/* FALLTHROUGH */
687#ifdef COLORLS
688			if (!f_color)
689#endif
690				f_notabs = 0;
691			/* FALLTHROUGH */
692		default:
693			break;
694		}
695		MAKENINES(maxinode);
696		MAKENINES(maxblock);
697		MAKENINES(maxnlink);
698		MAKENINES(maxsize);
699		free(jinitmax);
700	}
701	d.s_size = 0;
702	sizelen = 0;
703	flags = NULL;
704	for (cur = list, entries = 0; cur; cur = cur->fts_link) {
705		if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) {
706			xo_warnx("%s: %s",
707			    cur->fts_name, strerror(cur->fts_errno));
708			cur->fts_number = NO_PRINT;
709			rval = 1;
710			continue;
711		}
712		/*
713		 * P is NULL if list is the argv list, to which different rules
714		 * apply.
715		 */
716		if (p == NULL) {
717			/* Directories will be displayed later. */
718			if (cur->fts_info == FTS_D && !f_listdir) {
719				cur->fts_number = NO_PRINT;
720				continue;
721			}
722		} else {
723			/* Only display dot file if -a/-A set. */
724			if (cur->fts_name[0] == '.' && !f_listdot) {
725				cur->fts_number = NO_PRINT;
726				continue;
727			}
728		}
729		if (cur->fts_namelen > maxlen)
730			maxlen = cur->fts_namelen;
731		if (f_octal || f_octal_escape) {
732			u_long t = len_octal(cur->fts_name, cur->fts_namelen);
733
734			if (t > maxlen)
735				maxlen = t;
736		}
737		if (needstats) {
738			sp = cur->fts_statp;
739			if (sp->st_blocks > maxblock)
740				maxblock = sp->st_blocks;
741			if (sp->st_ino > maxinode)
742				maxinode = sp->st_ino;
743			if (sp->st_nlink > maxnlink)
744				maxnlink = sp->st_nlink;
745			if (sp->st_size > maxsize)
746				maxsize = sp->st_size;
747
748			btotal += sp->st_blocks;
749			if (f_longform) {
750				if (f_numericonly) {
751					(void)snprintf(nuser, sizeof(nuser),
752					    "%u", sp->st_uid);
753					(void)snprintf(ngroup, sizeof(ngroup),
754					    "%u", sp->st_gid);
755					user = nuser;
756					group = ngroup;
757				} else {
758					user = user_from_uid(sp->st_uid, 0);
759					group = group_from_gid(sp->st_gid, 0);
760				}
761				if ((ulen = strlen(user)) > maxuser)
762					maxuser = ulen;
763				if ((glen = strlen(group)) > maxgroup)
764					maxgroup = glen;
765				if (f_flags) {
766					flags = fflagstostr(sp->st_flags);
767					if (flags != NULL && *flags == '\0') {
768						free(flags);
769						flags = strdup("-");
770					}
771					if (flags == NULL)
772						xo_err(1, "fflagstostr");
773					flen = strlen(flags);
774					if (flen > (size_t)maxflags)
775						maxflags = flen;
776				} else
777					flen = 0;
778				labelstr = NULL;
779				if (f_label) {
780					char name[PATH_MAX + 1];
781					mac_t label;
782					int error;
783
784					error = mac_prepare_file_label(&label);
785					if (error == -1) {
786						xo_warn("MAC label for %s/%s",
787						    cur->fts_parent->fts_path,
788						    cur->fts_name);
789						goto label_out;
790					}
791
792					if (cur->fts_level == FTS_ROOTLEVEL)
793						snprintf(name, sizeof(name),
794						    "%s", cur->fts_name);
795					else
796						snprintf(name, sizeof(name),
797						    "%s/%s", cur->fts_parent->
798						    fts_accpath, cur->fts_name);
799
800					if (options & FTS_LOGICAL)
801						error = mac_get_file(name,
802						    label);
803					else
804						error = mac_get_link(name,
805						    label);
806					if (error == -1) {
807						xo_warn("MAC label for %s/%s",
808						    cur->fts_parent->fts_path,
809						    cur->fts_name);
810						mac_free(label);
811						goto label_out;
812					}
813
814					error = mac_to_text(label,
815					    &labelstr);
816					if (error == -1) {
817						xo_warn("MAC label for %s/%s",
818						    cur->fts_parent->fts_path,
819						    cur->fts_name);
820						mac_free(label);
821						goto label_out;
822					}
823					mac_free(label);
824label_out:
825					if (labelstr == NULL)
826						labelstr = strdup("-");
827					labelstrlen = strlen(labelstr);
828					if (labelstrlen > maxlabelstr)
829						maxlabelstr = labelstrlen;
830				} else
831					labelstrlen = 0;
832
833				if ((np = malloc(sizeof(NAMES) + labelstrlen +
834				    ulen + glen + flen + 4)) == NULL)
835					xo_err(1, "malloc");
836
837				np->user = &np->data[0];
838				(void)strcpy(np->user, user);
839				np->group = &np->data[ulen + 1];
840				(void)strcpy(np->group, group);
841
842				if (S_ISCHR(sp->st_mode) ||
843				    S_ISBLK(sp->st_mode)) {
844					sizelen = snprintf(NULL, 0,
845					    "%#jx", (uintmax_t)sp->st_rdev);
846					if (d.s_size < sizelen)
847						d.s_size = sizelen;
848				}
849
850				if (f_flags) {
851					np->flags = &np->data[ulen + glen + 2];
852					(void)strcpy(np->flags, flags);
853					free(flags);
854				}
855				if (f_label) {
856					np->label = &np->data[ulen + glen + 2
857					    + (f_flags ? flen + 1 : 0)];
858					(void)strcpy(np->label, labelstr);
859					free(labelstr);
860				}
861				cur->fts_pointer = np;
862			}
863		}
864		++entries;
865	}
866
867	/*
868	 * If there are no entries to display, we normally stop right
869	 * here.  However, we must continue if we have to display the
870	 * total block count.  In this case, we display the total only
871	 * on the second (p != NULL) pass.
872	 */
873	if (!entries && (!(f_longform || f_size) || p == NULL))
874		return;
875
876	d.list = list;
877	d.entries = entries;
878	d.maxlen = maxlen;
879	if (needstats) {
880		d.btotal = btotal;
881		d.s_block = snprintf(NULL, 0, "%lu", howmany(maxblock, blocksize));
882		d.s_flags = maxflags;
883		d.s_label = maxlabelstr;
884		d.s_group = maxgroup;
885		d.s_inode = snprintf(NULL, 0, "%ju", maxinode);
886		d.s_nlink = snprintf(NULL, 0, "%lu", maxnlink);
887		sizelen = f_humanval ? HUMANVALSTR_LEN :
888		    snprintf(NULL, 0, "%ju", maxsize);
889		if (d.s_size < sizelen)
890			d.s_size = sizelen;
891		d.s_user = maxuser;
892	}
893	if (f_thousands)			/* make space for commas */
894		d.s_size += (d.s_size - 1) / 3;
895	printfcn(&d);
896	output = 1;
897
898	if (f_longform)
899		for (cur = list; cur; cur = cur->fts_link)
900			free(cur->fts_pointer);
901}
902
903/*
904 * Ordering for mastercmp:
905 * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories
906 * as larger than directories.  Within either group, use the sort function.
907 * All other levels use the sort function.  Error entries remain unsorted.
908 */
909static int
910mastercmp(const FTSENT * const *a, const FTSENT * const *b)
911{
912	int a_info, b_info;
913
914	a_info = (*a)->fts_info;
915	if (a_info == FTS_ERR)
916		return (0);
917	b_info = (*b)->fts_info;
918	if (b_info == FTS_ERR)
919		return (0);
920
921	if (a_info == FTS_NS || b_info == FTS_NS)
922		return (namecmp(*a, *b));
923
924	if (a_info != b_info &&
925	    (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) {
926		if (a_info == FTS_D)
927			return (1);
928		if (b_info == FTS_D)
929			return (-1);
930	}
931	return (sortfcn(*a, *b));
932}
933