ls.c revision 99109
155682Smarkm/*
2233294Sstas * Copyright (c) 1989, 1993, 1994
3233294Sstas *	The Regents of the University of California.  All rights reserved.
4233294Sstas *
555682Smarkm * This code is derived from software contributed to Berkeley by
6233294Sstas * Michael Fischbein.
7233294Sstas *
8233294Sstas * Redistribution and use in source and binary forms, with or without
955682Smarkm * modification, are permitted provided that the following conditions
10233294Sstas * are met:
11233294Sstas * 1. Redistributions of source code must retain the above copyright
1255682Smarkm *    notice, this list of conditions and the following disclaimer.
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm * 3. All advertising materials mentioning features or use of this software
1755682Smarkm *    must display the following acknowledgement:
1855682Smarkm *	This product includes software developed by the University of
1955682Smarkm *	California, Berkeley and its contributors.
2055682Smarkm * 4. Neither the name of the University nor the names of its contributors
2155682Smarkm *    may be used to endorse or promote products derived from this software
2255682Smarkm *    without specific prior written permission.
2355682Smarkm *
2455682Smarkm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2555682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2655682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2755682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2855682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2955682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3055682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3155682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3255682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3372445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3455682Smarkm * SUCH DAMAGE.
3555682Smarkm */
36233294Sstas
3755682Smarkm#ifndef lint
3872445Sassarstatic const char copyright[] =
3972445Sassar"@(#) Copyright (c) 1989, 1993, 1994\n\
4072445Sassar	The Regents of the University of California.  All rights reserved.\n";
4172445Sassar#endif /* not lint */
4272445Sassar
4372445Sassar#if 0
4472445Sassar#ifndef lint
4572445Sassarstatic char sccsid[] = "@(#)ls.c	8.5 (Berkeley) 4/2/94";
4672445Sassar#endif /* not lint */
4772445Sassar#endif
4872445Sassar#include <sys/cdefs.h>
4972445Sassar__FBSDID("$FreeBSD: head/bin/ls/ls.c 99109 2002-06-30 05:13:54Z obrien $");
5072445Sassar
5172445Sassar#include <sys/types.h>
5290926Snectar#include <sys/stat.h>
5390926Snectar#include <sys/ioctl.h>
5490926Snectar
5572445Sassar#include <dirent.h>
5672445Sassar#include <err.h>
5772445Sassar#include <errno.h>
5890926Snectar#include <fts.h>
5990926Snectar#include <grp.h>
6090926Snectar#include <limits.h>
6190926Snectar#include <locale.h>
6290926Snectar#include <pwd.h>
6390926Snectar#include <stdio.h>
6490926Snectar#include <stdlib.h>
6590926Snectar#include <string.h>
6690926Snectar#include <unistd.h>
6772445Sassar#ifdef COLORLS
6872445Sassar#include <termcap.h>
6972445Sassar#include <signal.h>
7072445Sassar#endif
7155682Smarkm
7255682Smarkm#include "ls.h"
7355682Smarkm#include "extern.h"
7455682Smarkm#include "lomac.h"
7555682Smarkm
7655682Smarkm/*
7755682Smarkm * Upward approximation of the maximum number of characters needed to
7855682Smarkm * represent a value of integral type t as a string, excluding the
7955682Smarkm * NUL terminator, with provision for a sign.
8055682Smarkm */
8155682Smarkm#define	STRBUF_SIZEOF(t)	(1 + CHAR_BIT * sizeof(t) / 3 + 1)
8255682Smarkm
8355682Smarkmstatic void	 display(FTSENT *, FTSENT *);
8455682Smarkmstatic u_quad_t	 makenines(u_long);
8555682Smarkmstatic int	 mastercmp(const FTSENT **, const FTSENT **);
8655682Smarkmstatic void	 traverse(int, char **, int);
8755682Smarkm
8855682Smarkmstatic void (*printfcn)(DISPLAY *);
8955682Smarkmstatic int (*sortfcn)(const FTSENT *, const FTSENT *);
9055682Smarkm
9155682Smarkmlong blocksize;			/* block size units */
9255682Smarkmint termwidth = 80;		/* default terminal width */
9355682Smarkm
9455682Smarkm/* flags */
9555682Smarkm       int f_accesstime;	/* use time of last access */
9655682Smarkm       int f_flags;		/* show flags associated with a file */
9755682Smarkm       int f_humanval;		/* show human-readable file sizes */
9855682Smarkm       int f_inode;		/* print inode */
9955682Smarkmstatic int f_kblocks;		/* print size in kilobytes */
10072445Sassarstatic int f_listdir;		/* list actual directory, not contents */
10172445Sassarstatic int f_listdot;		/* list files beginning with . */
10272445Sassar       int f_longform;		/* long listing format */
10355682Smarkm       int f_nonprint;		/* show unprintables as ? */
10472445Sassarstatic int f_nosort;		/* don't sort output */
10572445Sassar       int f_notabs;		/* don't use tab-separated multi-col output */
10672445Sassarstatic int f_numericonly;	/* don't convert uid/gid to name */
10772445Sassar       int f_octal;		/* show unprintables as \xxx */
10855682Smarkm       int f_octal_escape;	/* like f_octal but use C escapes if possible */
10972445Sassarstatic int f_recursive;		/* ls subdirectories also */
11072445Sassarstatic int f_reversesort;	/* reverse whatever sort is used */
11172445Sassar       int f_sectime;		/* print the real time for all files */
11272445Sassarstatic int f_singlecol;		/* use single column output */
11372445Sassar       int f_size;		/* list size in short listing */
11472445Sassar       int f_slash;		/* similar to f_type, but only for dirs */
11572445Sassar       int f_sortacross;	/* sort across rows, not down columns */
11672445Sassar       int f_statustime;	/* use time of last mode change */
11790926Snectar       int f_stream;		/* stream the output, seperate with commas */
11890926Snectarstatic int f_timesort;		/* sort by time vice name */
11990926Snectar       int f_type;		/* add type character for non-regular files */
12090926Snectarstatic int f_whiteout;		/* show whiteout entries */
12190926Snectar       int f_lomac;		/* show LOMAC attributes */
12255682Smarkm#ifdef COLORLS
12355682Smarkm       int f_color;		/* add type in color for non-regular files */
12455682Smarkm
12555682Smarkmchar *ansi_bgcol;		/* ANSI sequence to set background colour */
12655682Smarkmchar *ansi_fgcol;		/* ANSI sequence to set foreground colour */
127102644Snectarchar *ansi_coloff;		/* ANSI sequence to reset colours */
128102644Snectarchar *attrs_off;		/* ANSI sequence to turn off attributes */
129102644Snectarchar *enter_bold;		/* ANSI sequence to set color to bold mode */
130102644Snectar#endif
13155682Smarkm
13255682Smarkmstatic int rval;
13355682Smarkm
13455682Smarkmint
13555682Smarkmmain(int argc, char *argv[])
13655682Smarkm{
13755682Smarkm	static char dot[] = ".", *dotav[] = {dot, NULL};
13855682Smarkm	struct winsize win;
13990926Snectar	int ch, fts_options, notused;
14090926Snectar	char *p;
14190926Snectar#ifdef COLORLS
14290926Snectar	char termcapbuf[1024];	/* termcap definition buffer */
14390926Snectar	char tcapbuf[512];	/* capability buffer */
14490926Snectar	char *bp = tcapbuf;
14590926Snectar#endif
14690926Snectar
14790926Snectar	(void)setlocale(LC_ALL, "");
14890926Snectar
149178825Sdfr	/* Terminal defaults to -Cq, non-terminal defaults to -1. */
15090926Snectar	if (isatty(STDOUT_FILENO)) {
15155682Smarkm		termwidth = 80;
15255682Smarkm		if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
15372445Sassar			termwidth = atoi(p);
15455682Smarkm		else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 &&
155233294Sstas		    win.ws_col > 0)
15655682Smarkm			termwidth = win.ws_col;
15790926Snectar		f_nonprint = 1;
158233294Sstas	} else {
15972445Sassar		f_singlecol = 1;
16055682Smarkm		/* retrieve environment variable, in case of explicit -C */
16172445Sassar		p = getenv("COLUMNS");
16272445Sassar		if (p)
16355682Smarkm			termwidth = atoi(p);
16455682Smarkm	}
16555682Smarkm
16655682Smarkm	/* Root is -A automatically. */
16772445Sassar	if (!getuid())
16855682Smarkm		f_listdot = 1;
16972445Sassar
17072445Sassar	fts_options = FTS_PHYSICAL;
17172445Sassar 	while ((ch = getopt(argc, argv, "1ABCFGHLPRTWZabcdfghiklmnopqrstuwx"))
17272445Sassar	    != -1) {
17355682Smarkm		switch (ch) {
17472445Sassar		/*
17572445Sassar		 * The -1, -C, -x and -l options all override each other so
17672445Sassar		 * shell aliasing works right.
17755682Smarkm		 */
17872445Sassar		case '1':
17972445Sassar			f_singlecol = 1;
18072445Sassar			f_longform = 0;
18155682Smarkm			f_stream = 0;
18272445Sassar			break;
18372445Sassar		case 'B':
18455682Smarkm			f_nonprint = 0;
18572445Sassar			f_octal = 1;
18655682Smarkm			f_octal_escape = 0;
18772445Sassar			break;
18872445Sassar		case 'C':
18955682Smarkm			f_sortacross = f_longform = f_singlecol = 0;
190233294Sstas			break;
19155682Smarkm		case 'l':
19255682Smarkm			f_longform = 1;
193233294Sstas			f_singlecol = 0;
19455682Smarkm			f_stream = 0;
19555682Smarkm			break;
19655682Smarkm		case 'x':
19755682Smarkm			f_sortacross = 1;
19855682Smarkm			f_longform = 0;
19955682Smarkm			f_singlecol = 0;
20055682Smarkm			break;
20155682Smarkm		/* The -c and -u options override each other. */
20255682Smarkm		case 'c':
20355682Smarkm			f_statustime = 1;
20455682Smarkm			f_accesstime = 0;
20555682Smarkm			break;
20655682Smarkm		case 'u':
20755682Smarkm			f_accesstime = 1;
20855682Smarkm			f_statustime = 0;
20955682Smarkm			break;
21055682Smarkm		case 'F':
21155682Smarkm			f_type = 1;
21255682Smarkm			f_slash = 0;
21355682Smarkm			break;
21455682Smarkm		case 'H':
21555682Smarkm			fts_options |= FTS_COMFOLLOW;
21655682Smarkm			break;
21755682Smarkm		case 'G':
21855682Smarkm			setenv("CLICOLOR", "", 1);
21955682Smarkm			break;
22055682Smarkm		case 'L':
221178825Sdfr			fts_options &= ~FTS_PHYSICAL;
222178825Sdfr			fts_options |= FTS_LOGICAL;
223178825Sdfr			break;
224178825Sdfr		case 'P':
22555682Smarkm			fts_options &= ~FTS_COMFOLLOW;
226178825Sdfr			fts_options &= ~FTS_LOGICAL;
227178825Sdfr			fts_options |= FTS_PHYSICAL;
228178825Sdfr			break;
229178825Sdfr		case 'R':
23055682Smarkm			f_recursive = 1;
23155682Smarkm			break;
23255682Smarkm		case 'a':
23355682Smarkm			fts_options |= FTS_SEEDOT;
234178825Sdfr			/* FALLTHROUGH */
235178825Sdfr		case 'A':
236178825Sdfr			f_listdot = 1;
237178825Sdfr			break;
23855682Smarkm		/* The -d option turns off the -R option. */
239178825Sdfr		case 'd':
240178825Sdfr			f_listdir = 1;
241178825Sdfr			f_recursive = 0;
242178825Sdfr			break;
24355682Smarkm		case 'f':
244233294Sstas			f_nosort = 1;
24555682Smarkm			break;
24655682Smarkm		case 'g':	/* Compatibility with 4.3BSD. */
247178825Sdfr			break;
248178825Sdfr		case 'h':
249178825Sdfr			f_humanval = 1;
250178825Sdfr			break;
25155682Smarkm		case 'i':
25255682Smarkm			f_inode = 1;
253178825Sdfr			break;
254178825Sdfr		case 'k':
255178825Sdfr			f_kblocks = 1;
256178825Sdfr			break;
25755682Smarkm		case 'm':
258178825Sdfr			f_stream = 1;
259178825Sdfr			f_singlecol = 0;
260178825Sdfr			f_longform = 0;
261178825Sdfr			break;
262178825Sdfr		case 'n':
263178825Sdfr			f_numericonly = 1;
264178825Sdfr			break;
265178825Sdfr		case 'o':
26655682Smarkm			f_flags = 1;
26755682Smarkm			break;
26855682Smarkm		case 'p':
26972445Sassar			f_slash = 1;
27072445Sassar			f_type = 1;
27172445Sassar			break;
27272445Sassar		case 'q':
27355682Smarkm			f_nonprint = 1;
27455682Smarkm			f_octal = 0;
27555682Smarkm			f_octal_escape = 0;
27655682Smarkm			break;
277178825Sdfr		case 'r':
278178825Sdfr			f_reversesort = 1;
279178825Sdfr			break;
280178825Sdfr		case 's':
28155682Smarkm			f_size = 1;
28255682Smarkm			break;
28355682Smarkm		case 'T':
28455682Smarkm			f_sectime = 1;
28555682Smarkm			break;
28655682Smarkm		case 't':
28755682Smarkm			f_timesort = 1;
288178825Sdfr			break;
289178825Sdfr		case 'W':
290178825Sdfr			f_whiteout = 1;
291178825Sdfr			break;
29272445Sassar		case 'b':
293178825Sdfr			f_nonprint = 0;
294178825Sdfr			f_octal = 0;
295178825Sdfr			f_octal_escape = 1;
296178825Sdfr			break;
29755682Smarkm		case 'w':
29855682Smarkm			f_nonprint = 0;
29955682Smarkm			f_octal = 0;
300120945Snectar			f_octal_escape = 0;
30155682Smarkm			break;
30255682Smarkm		case 'Z':
30355682Smarkm			f_lomac = 1;
304178825Sdfr			break;
305178825Sdfr		default:
306178825Sdfr		case '?':
307178825Sdfr			usage();
30855682Smarkm		}
30990926Snectar	}
31055682Smarkm	argc -= optind;
311178825Sdfr	argv += optind;
31255682Smarkm
31355682Smarkm	/* Enabling of colours is conditional on the environment. */
31455682Smarkm	if (getenv("CLICOLOR") &&
31555682Smarkm	    (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE")))
31655682Smarkm#ifdef COLORLS
31755682Smarkm		if (tgetent(termcapbuf, getenv("TERM")) == 1) {
31855682Smarkm			ansi_fgcol = tgetstr("AF", &bp);
31955682Smarkm			ansi_bgcol = tgetstr("AB", &bp);
32055682Smarkm			attrs_off = tgetstr("me", &bp);
32155682Smarkm			enter_bold = tgetstr("md", &bp);
32255682Smarkm
32355682Smarkm			/* To switch colours off use 'op' if
32455682Smarkm			 * available, otherwise use 'oc', or
32555682Smarkm			 * don't do colours at all. */
32655682Smarkm			ansi_coloff = tgetstr("op", &bp);
32755682Smarkm			if (!ansi_coloff)
32855682Smarkm				ansi_coloff = tgetstr("oc", &bp);
32955682Smarkm			if (ansi_fgcol && ansi_bgcol && ansi_coloff)
33055682Smarkm				f_color = 1;
33155682Smarkm		}
33255682Smarkm#else
33355682Smarkm		(void)fprintf(stderr, "Color support not compiled in.\n");
33455682Smarkm#endif /*COLORLS*/
33555682Smarkm
33655682Smarkm#ifdef COLORLS
33755682Smarkm	if (f_color) {
33855682Smarkm		/*
33955682Smarkm		 * We can't put tabs and color sequences together:
34055682Smarkm		 * column number will be incremented incorrectly
34155682Smarkm		 * for "stty oxtabs" mode.
34255682Smarkm		 */
34355682Smarkm		f_notabs = 1;
34455682Smarkm		(void)signal(SIGINT, colorquit);
34555682Smarkm		(void)signal(SIGQUIT, colorquit);
34655682Smarkm		parsecolors(getenv("LSCOLORS"));
34755682Smarkm	}
34855682Smarkm#endif
34955682Smarkm
35055682Smarkm	/*
35155682Smarkm	 * If not -F, -i, -l, -s or -t options, don't require stat
35255682Smarkm	 * information, unless in color mode in which case we do
35355682Smarkm	 * need this to determine which colors to display.
35455682Smarkm	 */
35555682Smarkm	if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type
35655682Smarkm#ifdef COLORLS
35755682Smarkm	    && !f_color
35855682Smarkm#endif
35955682Smarkm	    )
36055682Smarkm		fts_options |= FTS_NOSTAT;
36155682Smarkm
36255682Smarkm	/*
36355682Smarkm	 * If not -F, -d or -l options, follow any symbolic links listed on
36455682Smarkm	 * the command line.
36555682Smarkm	 */
36655682Smarkm	if (!f_longform && !f_listdir && !f_type)
36755682Smarkm		fts_options |= FTS_COMFOLLOW;
36855682Smarkm
36955682Smarkm	/*
37055682Smarkm	 * If -W, show whiteout entries
37155682Smarkm	 */
37255682Smarkm#ifdef FTS_WHITEOUT
37355682Smarkm	if (f_whiteout)
37455682Smarkm		fts_options |= FTS_WHITEOUT;
37555682Smarkm#endif
37655682Smarkm
37772445Sassar	/* If -l or -s, figure out block size. */
37855682Smarkm	if (f_longform || f_size) {
37955682Smarkm		if (f_kblocks)
38055682Smarkm			blocksize = 2;
38155682Smarkm		else {
38255682Smarkm			(void)getbsize(&notused, &blocksize);
38355682Smarkm			blocksize /= 512;
38455682Smarkm		}
38555682Smarkm	}
38655682Smarkm	/* Select a sort function. */
38772445Sassar	if (f_reversesort) {
38855682Smarkm		if (!f_timesort)
38955682Smarkm			sortfcn = revnamecmp;
390102644Snectar		else if (f_accesstime)
39155682Smarkm			sortfcn = revacccmp;
39255682Smarkm		else if (f_statustime)
393178825Sdfr			sortfcn = revstatcmp;
39455682Smarkm		else		/* Use modification time. */
39555682Smarkm			sortfcn = revmodcmp;
39655682Smarkm	} else {
39755682Smarkm		if (!f_timesort)
39855682Smarkm			sortfcn = namecmp;
39955682Smarkm		else if (f_accesstime)
40055682Smarkm			sortfcn = acccmp;
40155682Smarkm		else if (f_statustime)
40255682Smarkm			sortfcn = statcmp;
40355682Smarkm		else		/* Use modification time. */
40455682Smarkm			sortfcn = modcmp;
40555682Smarkm	}
40655682Smarkm
40755682Smarkm	/* Select a print function. */
408233294Sstas	if (f_singlecol)
40972445Sassar		printfcn = printscol;
41072445Sassar	else if (f_longform)
41172445Sassar		printfcn = printlong;
41255682Smarkm	else if (f_stream)
41355682Smarkm		printfcn = printstream;
41455682Smarkm	else
415233294Sstas		printfcn = printcol;
41672445Sassar
417233294Sstas	if (argc)
41855682Smarkm		traverse(argc, argv, fts_options);
41972445Sassar	else
42072445Sassar		traverse(1, dotav, fts_options);
42155682Smarkm	exit(rval);
42255682Smarkm}
42372445Sassar
42455682Smarkmstatic int output;		/* If anything output. */
42555682Smarkm
42655682Smarkm/*
42755682Smarkm * Traverse() walks the logical directory structure specified by the argv list
428233294Sstas * in the order specified by the mastercmp() comparison function.  During the
42955682Smarkm * traversal it passes linked lists of structures to display() which represent
43055682Smarkm * a superset (may be exact set) of the files to be displayed.
43155682Smarkm */
432233294Sstasstatic void
43355682Smarkmtraverse(int argc, char *argv[], int options)
434233294Sstas{
43555682Smarkm	FTS *ftsp;
43655682Smarkm	FTSENT *p, *chp;
43755682Smarkm	int ch_options;
438233294Sstas
43955682Smarkm	if ((ftsp =
440233294Sstas	    fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
44155682Smarkm		err(1, NULL);
44255682Smarkm
44372445Sassar	display(NULL, fts_children(ftsp, 0));
44472445Sassar	if (f_listdir)
44572445Sassar		return;
44672445Sassar
44772445Sassar	/*
44872445Sassar	 * If not recursing down this tree and don't need stat info, just get
44972445Sassar	 * the names.
45072445Sassar	 */
45172445Sassar	ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0;
45272445Sassar
45372445Sassar	while ((p = fts_read(ftsp)) != NULL)
45472445Sassar		switch (p->fts_info) {
45572445Sassar		case FTS_DC:
45672445Sassar			warnx("%s: directory causes a cycle", p->fts_name);
45772445Sassar			break;
45872445Sassar		case FTS_DNR:
45972445Sassar		case FTS_ERR:
460233294Sstas			warnx("%s: %s", p->fts_name, strerror(p->fts_errno));
46155682Smarkm			rval = 1;
46255682Smarkm			break;
463233294Sstas		case FTS_D:
46472445Sassar			if (p->fts_level != FTS_ROOTLEVEL &&
46555682Smarkm			    p->fts_name[0] == '.' && !f_listdot)
46655682Smarkm				break;
46755682Smarkm
46855682Smarkm			/*
46955682Smarkm			 * If already output something, put out a newline as
47055682Smarkm			 * a separator.  If multiple arguments, precede each
47155682Smarkm			 * directory with its name.
47255682Smarkm			 */
47355682Smarkm			if (output)
47455682Smarkm				(void)printf("\n%s:\n", p->fts_path);
47555682Smarkm			else if (argc > 1) {
476233294Sstas				(void)printf("%s:\n", p->fts_path);
47755682Smarkm				output = 1;
47855682Smarkm			}
47955682Smarkm			chp = fts_children(ftsp, ch_options);
48055682Smarkm			display(p, chp);
48172445Sassar
48255682Smarkm			if (!f_recursive && chp != NULL)
48355682Smarkm				(void)fts_set(ftsp, p, FTS_SKIP);
48455682Smarkm			break;
48555682Smarkm		default:
48655682Smarkm			break;
48755682Smarkm		}
48855682Smarkm	if (errno)
48955682Smarkm		err(1, "fts_read");
49055682Smarkm}
49155682Smarkm
49255682Smarkm/*
49355682Smarkm * Display() takes a linked list of FTSENT structures and passes the list
49455682Smarkm * along with any other necessary information to the print function.  P
49555682Smarkm * points to the parent directory of the display list.
49655682Smarkm */
497233294Sstasstatic void
49855682Smarkmdisplay(FTSENT *p, FTSENT *list)
49955682Smarkm{
50055682Smarkm	struct stat *sp;
50190926Snectar	DISPLAY d;
50290926Snectar	FTSENT *cur;
50390926Snectar	NAMES *np;
504102644Snectar	off_t maxsize;
50555682Smarkm	u_long btotal, lattrlen, maxblock, maxinode, maxlen, maxnlink, maxlattr;
50655682Smarkm	int bcfile, maxflags;
50755682Smarkm	gid_t maxgroup;
50855682Smarkm	uid_t maxuser;
50990926Snectar	size_t flen, ulen, glen;
51090926Snectar	char *initmax;
51190926Snectar	int entries, needstats;
512102644Snectar	const char *user, *group;
51355682Smarkm	char *flags, *lattr = NULL;
514102644Snectar	char buf[STRBUF_SIZEOF(u_quad_t) + 1];
515102644Snectar	char ngroup[STRBUF_SIZEOF(uid_t) + 1];
516102644Snectar	char nuser[STRBUF_SIZEOF(gid_t) + 1];
51790926Snectar
51890926Snectar	/*
51990926Snectar	 * If list is NULL there are two possibilities: that the parent
52055682Smarkm	 * directory p has no children, or that fts_children() returned an
52155682Smarkm	 * error.  We ignore the error case since it will be replicated
522102644Snectar	 * on the next call to fts_read() on the post-order visit to the
523102644Snectar	 * directory p, and will be signaled in traverse().
52455682Smarkm	 */
52555682Smarkm	if (list == NULL)
52655682Smarkm		return;
52755682Smarkm
52855682Smarkm	needstats = f_inode || f_longform || f_size;
52955682Smarkm	flen = 0;
53090926Snectar	btotal = 0;
53190926Snectar	initmax = getenv("LS_COLWIDTHS");
53290926Snectar	/* Fields match -lios order.  New ones should be added at the end. */
53390926Snectar	maxlattr = maxblock = maxinode = maxlen = maxnlink =
53490926Snectar	    maxuser = maxgroup = maxflags = maxsize = 0;
53590926Snectar	if (initmax != NULL && *initmax != '\0') {
536102644Snectar		char *initmax2, *jinitmax;
537102644Snectar		int ninitmax;
53890926Snectar
53990926Snectar		/* Fill-in "::" as "0:0:0" for the sake of scanf. */
54090926Snectar		jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2);
54190926Snectar		if (jinitmax == NULL)
54290926Snectar			err(1, NULL);
54355682Smarkm		if (*initmax == ':')
54490926Snectar			strcpy(initmax2, "0:"), initmax2 += 2;
545178825Sdfr		else
546178825Sdfr			*initmax2++ = *initmax, *initmax2 = '\0';
547178825Sdfr		for (initmax++; *initmax != '\0'; initmax++) {
54890926Snectar			if (initmax[-1] == ':' && initmax[0] == ':') {
54990926Snectar				*initmax2++ = '0';
55055682Smarkm				*initmax2++ = initmax[0];
55155682Smarkm				initmax2[1] = '\0';
55255682Smarkm			} else {
55355682Smarkm				*initmax2++ = initmax[0];
554233294Sstas				initmax2[1] = '\0';
55555682Smarkm			}
55655682Smarkm		}
55755682Smarkm		if (initmax2[-1] == ':')
558233294Sstas			strcpy(initmax2, "0");
55955682Smarkm
56055682Smarkm		ninitmax = sscanf(jinitmax,
56155682Smarkm		    " %lu : %lu : %lu : %i : %i : %i : %llu : %lu : %lu ",
562233294Sstas		    &maxinode, &maxblock, &maxnlink, &maxuser,
56355682Smarkm		    &maxgroup, &maxflags, &maxsize, &maxlen, &maxlattr);
56455682Smarkm		f_notabs = 1;
56555682Smarkm		switch (ninitmax) {
56672445Sassar		case 0:
56755682Smarkm			maxinode = 0;
56855682Smarkm			/* fall through */
56955682Smarkm		case 1:
57055682Smarkm			maxblock = 0;
57155682Smarkm			/* fall through */
57255682Smarkm		case 2:
57355682Smarkm			maxnlink = 0;
57455682Smarkm			/* fall through */
57555682Smarkm		case 3:
57655682Smarkm			maxuser = 0;
57755682Smarkm			/* fall through */
57855682Smarkm		case 4:
57955682Smarkm			maxgroup = 0;
58055682Smarkm			/* fall through */
58155682Smarkm		case 5:
58255682Smarkm			maxflags = 0;
58355682Smarkm			/* fall through */
58455682Smarkm		case 6:
58555682Smarkm			maxsize = 0;
58655682Smarkm			/* fall through */
58755682Smarkm		case 7:
58855682Smarkm			maxlen = 0;
58955682Smarkm			/* fall through */
59055682Smarkm		case 8:
59155682Smarkm			maxlattr = 0;
59255682Smarkm			/* fall through */
59355682Smarkm#ifdef COLORLS
59455682Smarkm			if (!f_color)
59555682Smarkm#endif
59655682Smarkm				f_notabs = 0;
59755682Smarkm			/* fall through */
59855682Smarkm		default:
59955682Smarkm			break;
60055682Smarkm		}
60155682Smarkm		maxinode = makenines(maxinode);
602178825Sdfr		maxblock = makenines(maxblock);
603178825Sdfr		maxnlink = makenines(maxnlink);
604178825Sdfr		maxsize = makenines(maxsize);
605233294Sstas	}
60690926Snectar	if (f_lomac)
60790926Snectar		lomac_start();
60855682Smarkm	bcfile = 0;
60955682Smarkm	flags = NULL;
61055682Smarkm	for (cur = list, entries = 0; cur; cur = cur->fts_link) {
61155682Smarkm		if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) {
61255682Smarkm			warnx("%s: %s",
61355682Smarkm			    cur->fts_name, strerror(cur->fts_errno));
61455682Smarkm			cur->fts_number = NO_PRINT;
61555682Smarkm			rval = 1;
61655682Smarkm			continue;
61755682Smarkm		}
61855682Smarkm		/*
61955682Smarkm		 * P is NULL if list is the argv list, to which different rules
62055682Smarkm		 * apply.
62155682Smarkm		 */
62255682Smarkm		if (p == NULL) {
62355682Smarkm			/* Directories will be displayed later. */
62455682Smarkm			if (cur->fts_info == FTS_D && !f_listdir) {
62555682Smarkm				cur->fts_number = NO_PRINT;
62655682Smarkm				continue;
62755682Smarkm			}
62855682Smarkm		} else {
62955682Smarkm			/* Only display dot file if -a/-A set. */
63055682Smarkm			if (cur->fts_name[0] == '.' && !f_listdot) {
63155682Smarkm				cur->fts_number = NO_PRINT;
63255682Smarkm				continue;
63355682Smarkm			}
63455682Smarkm		}
63555682Smarkm		if (cur->fts_namelen > maxlen)
636233294Sstas			maxlen = cur->fts_namelen;
63772445Sassar		if (f_octal || f_octal_escape) {
63872445Sassar			u_long t = len_octal(cur->fts_name, cur->fts_namelen);
63990926Snectar
64072445Sassar			if (t > maxlen)
64172445Sassar				maxlen = t;
64272445Sassar		}
64372445Sassar		if (needstats) {
64472445Sassar			sp = cur->fts_statp;
64572445Sassar			if (sp->st_blocks > maxblock)
64672445Sassar				maxblock = sp->st_blocks;
64772445Sassar			if (sp->st_ino > maxinode)
64872445Sassar				maxinode = sp->st_ino;
64972445Sassar			if (sp->st_nlink > maxnlink)
650178825Sdfr				maxnlink = sp->st_nlink;
651178825Sdfr			if (sp->st_size > maxsize)
65272445Sassar				maxsize = sp->st_size;
65390926Snectar
65490926Snectar			btotal += sp->st_blocks;
65590926Snectar			if (f_longform) {
65690926Snectar				if (f_numericonly) {
65790926Snectar					(void)snprintf(nuser, sizeof(nuser),
65890926Snectar					    "%u", sp->st_uid);
65990926Snectar					(void)snprintf(ngroup, sizeof(ngroup),
66090926Snectar					    "%u", sp->st_gid);
66190926Snectar					user = nuser;
66290926Snectar					group = ngroup;
663233294Sstas				} else {
66490926Snectar					user = user_from_uid(sp->st_uid, 0);
66572445Sassar					group = group_from_gid(sp->st_gid, 0);
66672445Sassar				}
66772445Sassar				if ((ulen = strlen(user)) > maxuser)
66872445Sassar					maxuser = ulen;
66990926Snectar				if ((glen = strlen(group)) > maxgroup)
670233294Sstas					maxgroup = glen;
67190926Snectar				if (f_flags) {
67290926Snectar					flags = fflagstostr(sp->st_flags);
67390926Snectar					if (flags != NULL && *flags == '\0') {
67472445Sassar						free(flags);
67572445Sassar						flags = strdup("-");
67672445Sassar					}
67772445Sassar					if (flags == NULL)
67872445Sassar						err(1, NULL);
67972445Sassar					flen = strlen(flags);
68072445Sassar					if (flen > (size_t)maxflags)
68190926Snectar						maxflags = flen;
68272445Sassar				} else
68372445Sassar					flen = 0;
684233294Sstas				lattr = NULL;
68572445Sassar				if (f_lomac) {
68672445Sassar					lattr = get_lattr(cur);
68772445Sassar					lattrlen = strlen(lattr);
68872445Sassar					if (lattrlen > maxlattr)
68990926Snectar						maxlattr = lattrlen;
690233294Sstas				} else
69190926Snectar					lattrlen = 0;
69290926Snectar
69390926Snectar				if ((np = malloc(sizeof(NAMES) + lattrlen +
69472445Sassar				    ulen + glen + flen + 4)) == NULL)
69572445Sassar					err(1, NULL);
69672445Sassar
69772445Sassar				np->user = &np->data[0];
69872445Sassar				(void)strcpy(np->user, user);
69972445Sassar				np->group = &np->data[ulen + 1];
70072445Sassar				(void)strcpy(np->group, group);
70172445Sassar
70272445Sassar				if (S_ISCHR(sp->st_mode) ||
70372445Sassar				    S_ISBLK(sp->st_mode))
70472445Sassar					bcfile = 1;
70555682Smarkm
70690926Snectar				if (f_flags) {
70790926Snectar					np->flags = &np->data[ulen + glen + 2];
70890926Snectar					(void)strcpy(np->flags, flags);
70990926Snectar					free(flags);
71090926Snectar				}
71190926Snectar				if (f_lomac) {
71290926Snectar					np->lattr = &np->data[ulen + glen + 2
713233294Sstas					    + (f_flags ? flen + 1 : 0)];
71490926Snectar					(void)strcpy(np->lattr, lattr);
71590926Snectar					free(lattr);
71690926Snectar				}
71790926Snectar				cur->fts_pointer = np;
71890926Snectar			}
71990926Snectar		}
72090926Snectar		++entries;
72190926Snectar	}
72290926Snectar
72390926Snectar	if (!entries)
72490926Snectar		return;
72590926Snectar
72690926Snectar	d.list = list;
72772445Sassar	d.entries = entries;
72872445Sassar	d.maxlen = maxlen;
72972445Sassar	if (needstats) {
73090926Snectar		d.bcfile = bcfile;
73190926Snectar		d.btotal = btotal;
732102644Snectar		(void)snprintf(buf, sizeof(buf), "%lu", maxblock);
73355682Smarkm		d.s_block = strlen(buf);
73455682Smarkm		d.s_flags = maxflags;
73555682Smarkm		d.s_lattr = maxlattr;
73655682Smarkm		d.s_group = maxgroup;
73755682Smarkm		(void)snprintf(buf, sizeof(buf), "%lu", maxinode);
73855682Smarkm		d.s_inode = strlen(buf);
73955682Smarkm		(void)snprintf(buf, sizeof(buf), "%lu", maxnlink);
74055682Smarkm		d.s_nlink = strlen(buf);
74155682Smarkm		(void)snprintf(buf, sizeof(buf), "%qu", maxsize);
74255682Smarkm		d.s_size = strlen(buf);
74355682Smarkm		d.s_user = maxuser;
74455682Smarkm	}
74590926Snectar	printfcn(&d);
74690926Snectar	output = 1;
74790926Snectar
74890926Snectar	if (f_longform)
74990926Snectar		for (cur = list; cur; cur = cur->fts_link)
75090926Snectar			free(cur->fts_pointer);
75190926Snectar	if (f_lomac)
75290926Snectar		lomac_stop();
75390926Snectar}
75490926Snectar
75590926Snectar/*
75690926Snectar * Ordering for mastercmp:
75790926Snectar * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories
75890926Snectar * as larger than directories.  Within either group, use the sort function.
75990926Snectar * All other levels use the sort function.  Error entries remain unsorted.
76090926Snectar */
761102644Snectarstatic int
76255682Smarkmmastercmp(const FTSENT **a, const FTSENT **b)
76355682Smarkm{
76455682Smarkm	int a_info, b_info;
76555682Smarkm
76655682Smarkm	a_info = (*a)->fts_info;
76755682Smarkm	if (a_info == FTS_ERR)
768178825Sdfr		return (0);
76955682Smarkm	b_info = (*b)->fts_info;
77055682Smarkm	if (b_info == FTS_ERR)
771102644Snectar		return (0);
772102644Snectar
77355682Smarkm	if (a_info == FTS_NS || b_info == FTS_NS)
77455682Smarkm		return (namecmp(*a, *b));
77555682Smarkm
77655682Smarkm	if (a_info != b_info &&
77790926Snectar	    (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) {
77890926Snectar		if (a_info == FTS_D)
77955682Smarkm			return (1);
78055682Smarkm		if (b_info == FTS_D)
781102644Snectar			return (-1);
78255682Smarkm	}
78355682Smarkm	return (sortfcn(*a, *b));
784102644Snectar}
78555682Smarkm
78655682Smarkm/*
787178825Sdfr * Makenines() returns (10**n)-1.  This is useful for converting a width
788178825Sdfr * into a number that wide in decimal.
789102644Snectar */
79055682Smarkmstatic u_quad_t
79155682Smarkmmakenines(u_long n)
792102644Snectar{
79355682Smarkm	u_long i;
79455682Smarkm	u_quad_t reg;
79555682Smarkm
79655682Smarkm	reg = 1;
797102644Snectar	/* Use a loop instead of pow(), since all values of n are small. */
79855682Smarkm	for (i = 0; i < n; i++)
79955682Smarkm		reg *= 10;
80090926Snectar	reg--;
80190926Snectar
80290926Snectar	return reg;
80390926Snectar}
80490926Snectar