xref: /netbsd-src/bin/ls/ls.c (revision c98285893d1176364826ef6654c2348bab485a19)
1*c9828589Ssimonb /*	$NetBSD: ls.c,v 1.79 2024/12/11 12:56:31 simonb Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*
483ede345Smycroft  * Copyright (c) 1989, 1993, 1994
583ede345Smycroft  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Michael Fischbein.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35b22592e8Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
372fe2731dSlukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
382fe2731dSlukem  The Regents of the University of California.  All rights reserved.");
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
4161f28255Scgd #ifndef lint
4249f0ad86Scgd #if 0
4349f0ad86Scgd static char sccsid[] = "@(#)ls.c	8.7 (Berkeley) 8/5/94";
4449f0ad86Scgd #else
45*c9828589Ssimonb __RCSID("$NetBSD: ls.c,v 1.79 2024/12/11 12:56:31 simonb Exp $");
4649f0ad86Scgd #endif
4761f28255Scgd #endif /* not lint */
4861f28255Scgd 
491126bbaeSrmind #include <sys/param.h>
50203e4227Smycroft #include <sys/types.h>
5161f28255Scgd #include <sys/stat.h>
5261f28255Scgd #include <sys/ioctl.h>
5383ede345Smycroft 
5461f28255Scgd #include <dirent.h>
5583ede345Smycroft #include <err.h>
5683ede345Smycroft #include <errno.h>
57203e4227Smycroft #include <fts.h>
58c87c6d5cStron #include <locale.h>
5983ede345Smycroft #include <stdio.h>
60203e4227Smycroft #include <stdlib.h>
6161f28255Scgd #include <string.h>
6283ede345Smycroft #include <unistd.h>
637f10b1cfSchristos #include <termios.h>
64d91f759eSchristos #include <pwd.h>
65d91f759eSchristos #include <grp.h>
660a97c3f6She #include <util.h>
6783ede345Smycroft 
6861f28255Scgd #include "ls.h"
69203e4227Smycroft #include "extern.h"
7061f28255Scgd 
71ba2e04dcSlukem static void	 display(FTSENT *, FTSENT *);
72ba2e04dcSlukem static int	 mastercmp(const FTSENT **, const FTSENT **);
73ba2e04dcSlukem static void	 traverse(int, char **, int);
74203e4227Smycroft 
75ba2e04dcSlukem static void (*printfcn)(DISPLAY *);
76ba2e04dcSlukem static int (*sortfcn)(const FTSENT *, const FTSENT *);
77203e4227Smycroft 
78f6a07377Smycroft #define	BY_NAME 0
79f6a07377Smycroft #define	BY_SIZE 1
80f6a07377Smycroft #define	BY_TIME	2
81f6a07377Smycroft 
82203e4227Smycroft long blocksize;			/* block size units */
8361f28255Scgd int termwidth = 80;		/* default terminal width */
84f6a07377Smycroft int sortkey = BY_NAME;
85a4b250cdSsimonb int rval = EXIT_SUCCESS;	/* exit value - set if error encountered */
8661f28255Scgd 
8761f28255Scgd /* flags */
8861f28255Scgd int f_accesstime;		/* use time of last access */
8961f28255Scgd int f_column;			/* columnated format */
90ac591fc0Slukem int f_columnacross;		/* columnated format, sorted across */
91203e4227Smycroft int f_flags;			/* show flags associated with a file */
9275d0e9d0Sgrant int f_grouponly;		/* long listing without owner */
93f4c47802Sgrant int f_humanize;			/* humanize the size field */
944aaf499cSerh int f_commas;			/* separate size field with comma */
9561f28255Scgd int f_inode;			/* print inode */
9661f28255Scgd int f_listdir;			/* list actual directory, not contents */
9761f28255Scgd int f_listdot;			/* list files beginning with . */
9861f28255Scgd int f_longform;			/* long listing format */
9961f28255Scgd int f_nonprint;			/* show unprintables as ? */
10061f28255Scgd int f_nosort;			/* don't sort output */
101ac591fc0Slukem int f_numericonly;		/* don't convert uid/gid to name */
10221ab6335Sjschauma int f_octal;			/* print octal escapes for nongraphic characters */
10321ab6335Sjschauma int f_octal_escape;		/* like f_octal but use C escapes if possible */
10461f28255Scgd int f_recursive;		/* ls subdirectories also */
10561f28255Scgd int f_reversesort;		/* reverse whatever sort is used */
10661f28255Scgd int f_sectime;			/* print the real time for all files */
10761f28255Scgd int f_singlecol;		/* use single column output */
10861f28255Scgd int f_size;			/* list size in short listing */
10961f28255Scgd int f_statustime;		/* use time of last mode change */
110b7443b0fSkleink int f_stream;			/* stream format */
11161f28255Scgd int f_type;			/* add type character for non-regular files */
112b424b8feSkleink int f_typedir;			/* add type character for directories */
113d966913fSmycroft int f_whiteout;			/* show whiteout entries */
1140c47a5c3Schristos int f_fullpath;			/* print full pathname, not filename */
1150c47a5c3Schristos int f_leafonly;			/* when recursing, print leaf names only */
11661f28255Scgd 
11744db7ee6Sjoerg __dead static void
11844db7ee6Sjoerg usage(void)
11944db7ee6Sjoerg {
12044db7ee6Sjoerg 
12144db7ee6Sjoerg 	(void)fprintf(stderr,
122c7dda21cSchristos 	    "usage: %s [-1AaBbCcdFfghikLlMmnOoPpqRrSsTtuWwXx] [file ...]\n",
12344db7ee6Sjoerg 	    getprogname());
12444db7ee6Sjoerg 	exit(EXIT_FAILURE);
12544db7ee6Sjoerg 	/* NOTREACHED */
12644db7ee6Sjoerg }
12744db7ee6Sjoerg 
128203e4227Smycroft int
129ba2e04dcSlukem ls_main(int argc, char *argv[])
13061f28255Scgd {
131203e4227Smycroft 	static char dot[] = ".", *dotav[] = { dot, NULL };
13261f28255Scgd 	struct winsize win;
133ae46649fSsimonb 	int ch, fts_options;
134203e4227Smycroft 	int kflag = 0;
1350e2f9ea9Smycroft 	const char *p;
13661f28255Scgd 
137a2ed3bbeShira 	setprogname(argv[0]);
13878509e7fSchristos 	(void)setlocale(LC_ALL, "");
139c87c6d5cStron 
140203e4227Smycroft 	/* Terminal defaults to -Cq, non-terminal defaults to -1. */
141203e4227Smycroft 	if (isatty(STDOUT_FILENO)) {
142ff54312fSjschauma 		if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
143645e4d83Sjtc 		    win.ws_col > 0)
14461f28255Scgd 			termwidth = win.ws_col;
145203e4227Smycroft 		f_column = f_nonprint = 1;
14661f28255Scgd 	} else
14761f28255Scgd 		f_singlecol = 1;
14861f28255Scgd 
149203e4227Smycroft 	/* Root is -A automatically. */
15061f28255Scgd 	if (!getuid())
15161f28255Scgd 		f_listdot = 1;
15261f28255Scgd 
153203e4227Smycroft 	fts_options = FTS_PHYSICAL;
154d85eb2bfSmlelstv 	while ((ch = getopt(argc, argv, "1AaBbCcdFfghikLlMmnOoPpqRrSsTtuWwXx"))
155c7dda21cSchristos 	    != -1) {
15661f28255Scgd 		switch (ch) {
15761f28255Scgd 		/*
158b7443b0fSkleink 		 * The -1, -C, -l, -m and -x options all override each other so
159ac591fc0Slukem 		 * shell aliasing works correctly.
16061f28255Scgd 		 */
16161f28255Scgd 		case '1':
16261f28255Scgd 			f_singlecol = 1;
163b7443b0fSkleink 			f_column = f_columnacross = f_longform = f_stream = 0;
16461f28255Scgd 			break;
16561f28255Scgd 		case 'C':
16661f28255Scgd 			f_column = 1;
167b7443b0fSkleink 			f_columnacross = f_longform = f_singlecol = f_stream =
168b7443b0fSkleink 			    0;
16961f28255Scgd 			break;
17075d0e9d0Sgrant 		case 'g':
1717c8d837eSkleink 			if (f_grouponly != -1)
17275d0e9d0Sgrant 				f_grouponly = 1;
1737c8d837eSkleink 			f_longform = 1;
1747c8d837eSkleink 			f_column = f_columnacross = f_singlecol = f_stream = 0;
1757c8d837eSkleink 			break;
17661f28255Scgd 		case 'l':
17761f28255Scgd 			f_longform = 1;
178b7443b0fSkleink 			f_column = f_columnacross = f_singlecol = f_stream = 0;
1797c8d837eSkleink 			/* Never let -g take precedence over -l. */
1807c8d837eSkleink 			f_grouponly = -1;
181b7443b0fSkleink 			break;
182b7443b0fSkleink 		case 'm':
183b7443b0fSkleink 			f_stream = 1;
184b7443b0fSkleink 			f_column = f_columnacross = f_longform = f_singlecol =
185b7443b0fSkleink 			    0;
186ac591fc0Slukem 			break;
187ac591fc0Slukem 		case 'x':
188ac591fc0Slukem 			f_columnacross = 1;
189b7443b0fSkleink 			f_column = f_longform = f_singlecol = f_stream = 0;
19061f28255Scgd 			break;
191203e4227Smycroft 		/* The -c and -u options override each other. */
19261f28255Scgd 		case 'c':
19361f28255Scgd 			f_statustime = 1;
19461f28255Scgd 			f_accesstime = 0;
19561f28255Scgd 			break;
19661f28255Scgd 		case 'u':
19761f28255Scgd 			f_accesstime = 1;
19861f28255Scgd 			f_statustime = 0;
19961f28255Scgd 			break;
20061f28255Scgd 		case 'F':
20161f28255Scgd 			f_type = 1;
20261f28255Scgd 			break;
20361f28255Scgd 		case 'L':
204203e4227Smycroft 			fts_options &= ~FTS_PHYSICAL;
205203e4227Smycroft 			fts_options |= FTS_LOGICAL;
20661f28255Scgd 			break;
20761f28255Scgd 		case 'R':
20861f28255Scgd 			f_recursive = 1;
20961f28255Scgd 			break;
210ac632886Swiz 		case 'f':
211ac632886Swiz 			f_nosort = 1;
212ac632886Swiz 			/* FALLTHROUGH */
21361f28255Scgd 		case 'a':
214203e4227Smycroft 			fts_options |= FTS_SEEDOT;
21561f28255Scgd 			/* FALLTHROUGH */
21661f28255Scgd 		case 'A':
21761f28255Scgd 			f_listdot = 1;
21861f28255Scgd 			break;
219ea274fd6Sjschauma 		/* The -B option turns off the -b, -q and -w options. */
22021ab6335Sjschauma 		case 'B':
2212ccef82cSjschauma 			f_nonprint = 0;
22221ab6335Sjschauma 			f_octal = 1;
22321ab6335Sjschauma 			f_octal_escape = 0;
22421ab6335Sjschauma 			break;
225ea274fd6Sjschauma 		/* The -b option turns off the -B, -q and -w options. */
22621ab6335Sjschauma 		case 'b':
22721ab6335Sjschauma 			f_nonprint = 0;
22821ab6335Sjschauma 			f_octal = 0;
22921ab6335Sjschauma 			f_octal_escape = 1;
2302ccef82cSjschauma 			break;
231203e4227Smycroft 		/* The -d option turns off the -R option. */
23261f28255Scgd 		case 'd':
23361f28255Scgd 			f_listdir = 1;
234203e4227Smycroft 			f_recursive = 0;
23561f28255Scgd 			break;
23661f28255Scgd 		case 'i':
23761f28255Scgd 			f_inode = 1;
23861f28255Scgd 			break;
23961f28255Scgd 		case 'k':
240203e4227Smycroft 			blocksize = 1024;
241203e4227Smycroft 			kflag = 1;
242eae5877eSchristos 			f_humanize = 0;
243203e4227Smycroft 			break;
244ea274fd6Sjschauma 		/* The -h option forces all sizes to be measured in bytes. */
245f4c47802Sgrant 		case 'h':
246f4c47802Sgrant 			f_humanize = 1;
247eae5877eSchristos 			kflag = 0;
2484aaf499cSerh 			f_commas = 0;
2494aaf499cSerh 			break;
2504aaf499cSerh 		case 'M':
2514aaf499cSerh 			f_humanize = 0;
2524aaf499cSerh 			f_commas = 1;
253f4c47802Sgrant 			break;
254ac591fc0Slukem 		case 'n':
255ac591fc0Slukem 			f_numericonly = 1;
25641f5d0fdSlukem 			f_longform = 1;
25741f5d0fdSlukem 			f_column = f_columnacross = f_singlecol = f_stream = 0;
258ac591fc0Slukem 			break;
2590c47a5c3Schristos 		case 'O':
2600c47a5c3Schristos 			f_leafonly = 1;
2610c47a5c3Schristos 			break;
262203e4227Smycroft 		case 'o':
263203e4227Smycroft 			f_flags = 1;
26461f28255Scgd 			break;
2650c47a5c3Schristos 		case 'P':
2660c47a5c3Schristos 			f_fullpath = 1;
2670c47a5c3Schristos 			break;
268b424b8feSkleink 		case 'p':
269b424b8feSkleink 			f_typedir = 1;
270b424b8feSkleink 			break;
271ea274fd6Sjschauma 		/* The -q option turns off the -B, -b and -w options. */
27261f28255Scgd 		case 'q':
27361f28255Scgd 			f_nonprint = 1;
27421ab6335Sjschauma 			f_octal = 0;
27521ab6335Sjschauma 			f_octal_escape = 0;
27661f28255Scgd 			break;
27761f28255Scgd 		case 'r':
27861f28255Scgd 			f_reversesort = 1;
27961f28255Scgd 			break;
280f6a07377Smycroft 		case 'S':
281f6a07377Smycroft 			sortkey = BY_SIZE;
282f6a07377Smycroft 			break;
28361f28255Scgd 		case 's':
28461f28255Scgd 			f_size = 1;
28561f28255Scgd 			break;
28661f28255Scgd 		case 'T':
28761f28255Scgd 			f_sectime = 1;
28861f28255Scgd 			break;
28961f28255Scgd 		case 't':
290f6a07377Smycroft 			sortkey = BY_TIME;
29161f28255Scgd 			break;
292d966913fSmycroft 		case 'W':
293d966913fSmycroft 			f_whiteout = 1;
294d966913fSmycroft 			break;
295ea274fd6Sjschauma 		/* The -w option turns off the -B, -b and -q options. */
29621ab6335Sjschauma 		case 'w':
29721ab6335Sjschauma 			f_nonprint = 0;
29821ab6335Sjschauma 			f_octal = 0;
29921ab6335Sjschauma 			f_octal_escape = 0;
30021ab6335Sjschauma 			break;
301c7dda21cSchristos 		case 'X':
302c7dda21cSchristos 			fts_options |= FTS_XDEV;
303c7dda21cSchristos 			break;
30461f28255Scgd 		default:
30561f28255Scgd 		case '?':
30661f28255Scgd 			usage();
30761f28255Scgd 		}
30861f28255Scgd 	}
30961f28255Scgd 	argc -= optind;
31061f28255Scgd 	argv += optind;
31161f28255Scgd 
312ff54312fSjschauma 	if (f_column || f_columnacross || f_stream) {
313ff54312fSjschauma 		if ((p = getenv("COLUMNS")) != NULL)
314ff54312fSjschauma 			termwidth = atoi(p);
315ff54312fSjschauma 	}
316ff54312fSjschauma 
317203e4227Smycroft 	/*
3187c8d837eSkleink 	 * If both -g and -l options, let -l take precedence.
3197c8d837eSkleink 	 */
3207c8d837eSkleink 	if (f_grouponly == -1)
3217c8d837eSkleink 		f_grouponly = 0;
3227c8d837eSkleink 
3237c8d837eSkleink 	/*
324b424b8feSkleink 	 * If not -F, -i, -l, -p, -S, -s or -t options, don't require stat
325203e4227Smycroft 	 * information.
326203e4227Smycroft 	 */
327b424b8feSkleink 	if (!f_inode && !f_longform && !f_size && !f_type && !f_typedir &&
328f6a07377Smycroft 	    sortkey == BY_NAME)
329203e4227Smycroft 		fts_options |= FTS_NOSTAT;
33061f28255Scgd 
331203e4227Smycroft 	/*
332203e4227Smycroft 	 * If not -F, -d or -l options, follow any symbolic links listed on
333203e4227Smycroft 	 * the command line.
334203e4227Smycroft 	 */
335203e4227Smycroft 	if (!f_longform && !f_listdir && !f_type)
336203e4227Smycroft 		fts_options |= FTS_COMFOLLOW;
33761f28255Scgd 
338d966913fSmycroft 	/*
339d943cdadSjtc 	 * If -W, show whiteout entries
340d966913fSmycroft 	 */
341d966913fSmycroft #ifdef FTS_WHITEOUT
342d966913fSmycroft 	if (f_whiteout)
343d966913fSmycroft 		fts_options |= FTS_WHITEOUT;
344d966913fSmycroft #endif
345d966913fSmycroft 
3466f08f9dbSabs 	/* If -i, -l, or -s, figure out block size. */
3474bce9043Ssimonb 	if (f_inode || f_longform || f_size) {
348006b4dddScgd 		if (!kflag)
349ae46649fSsimonb 			(void)getbsize(NULL, &blocksize);
350*c9828589Ssimonb 		blocksize /= POSIX_BLOCK_SIZE;
351203e4227Smycroft 	}
352203e4227Smycroft 
353203e4227Smycroft 	/* Select a sort function. */
35461f28255Scgd 	if (f_reversesort) {
355f6a07377Smycroft 		switch (sortkey) {
356f6a07377Smycroft 		case BY_NAME:
35761f28255Scgd 			sortfcn = revnamecmp;
358f6a07377Smycroft 			break;
359f6a07377Smycroft 		case BY_SIZE:
360f6a07377Smycroft 			sortfcn = revsizecmp;
361f6a07377Smycroft 			break;
362f6a07377Smycroft 		case BY_TIME:
363f6a07377Smycroft 			if (f_accesstime)
36461f28255Scgd 				sortfcn = revacccmp;
36561f28255Scgd 			else if (f_statustime)
36661f28255Scgd 				sortfcn = revstatcmp;
36783ede345Smycroft 			else /* Use modification time. */
36861f28255Scgd 				sortfcn = revmodcmp;
369f6a07377Smycroft 			break;
370f6a07377Smycroft 		}
37161f28255Scgd 	} else {
372f6a07377Smycroft 		switch (sortkey) {
373f6a07377Smycroft 		case BY_NAME:
37461f28255Scgd 			sortfcn = namecmp;
375f6a07377Smycroft 			break;
376f6a07377Smycroft 		case BY_SIZE:
377f6a07377Smycroft 			sortfcn = sizecmp;
378f6a07377Smycroft 			break;
379f6a07377Smycroft 		case BY_TIME:
380f6a07377Smycroft 			if (f_accesstime)
38161f28255Scgd 				sortfcn = acccmp;
38261f28255Scgd 			else if (f_statustime)
38361f28255Scgd 				sortfcn = statcmp;
38483ede345Smycroft 			else /* Use modification time. */
38561f28255Scgd 				sortfcn = modcmp;
386f6a07377Smycroft 			break;
387f6a07377Smycroft 		}
38861f28255Scgd 	}
38961f28255Scgd 
390203e4227Smycroft 	/* Select a print function. */
39161f28255Scgd 	if (f_singlecol)
39261f28255Scgd 		printfcn = printscol;
393ac591fc0Slukem 	else if (f_columnacross)
394ac591fc0Slukem 		printfcn = printacol;
39561f28255Scgd 	else if (f_longform)
39661f28255Scgd 		printfcn = printlong;
397b7443b0fSkleink 	else if (f_stream)
398b7443b0fSkleink 		printfcn = printstream;
39961f28255Scgd 	else
40061f28255Scgd 		printfcn = printcol;
40161f28255Scgd 
402203e4227Smycroft 	if (argc)
403203e4227Smycroft 		traverse(argc, argv, fts_options);
404203e4227Smycroft 	else
405203e4227Smycroft 		traverse(1, dotav, fts_options);
40678509e7fSchristos 	return rval;
4079dc385beSmycroft 	/* NOTREACHED */
40861f28255Scgd }
40961f28255Scgd 
410203e4227Smycroft static int output;			/* If anything output. */
41161f28255Scgd 
41261f28255Scgd /*
413203e4227Smycroft  * Traverse() walks the logical directory structure specified by the argv list
414203e4227Smycroft  * in the order specified by the mastercmp() comparison function.  During the
415203e4227Smycroft  * traversal it passes linked lists of structures to display() which represent
416203e4227Smycroft  * a superset (may be exact set) of the files to be displayed.
41761f28255Scgd  */
418203e4227Smycroft static void
419ba2e04dcSlukem traverse(int argc, char *argv[], int options)
420203e4227Smycroft {
42183ede345Smycroft 	FTS *ftsp;
42283ede345Smycroft 	FTSENT *p, *chp;
4237e2f5551Schristos 	int ch_options, error;
424203e4227Smycroft 
425203e4227Smycroft 	if ((ftsp =
426203e4227Smycroft 	    fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
42785cbf55dSdrochner 		err(EXIT_FAILURE, NULL);
428203e4227Smycroft 
429203e4227Smycroft 	display(NULL, fts_children(ftsp, 0));
4307e2f5551Schristos 	if (f_listdir) {
43178509e7fSchristos 		(void)fts_close(ftsp);
432203e4227Smycroft 		return;
4337e2f5551Schristos 	}
434203e4227Smycroft 
435203e4227Smycroft 	/*
436203e4227Smycroft 	 * If not recursing down this tree and don't need stat info, just get
437203e4227Smycroft 	 * the names.
438203e4227Smycroft 	 */
439203e4227Smycroft 	ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0;
440203e4227Smycroft 
44183ede345Smycroft 	while ((p = fts_read(ftsp)) != NULL)
442203e4227Smycroft 		switch (p->fts_info) {
443203e4227Smycroft 		case FTS_DC:
444006b4dddScgd 			warnx("%s: directory causes a cycle", p->fts_name);
445203e4227Smycroft 			break;
44683ede345Smycroft 		case FTS_DNR:
44783ede345Smycroft 		case FTS_ERR:
448f2413af6Schristos 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
449a4b250cdSsimonb 			rval = EXIT_FAILURE;
45083ede345Smycroft 			break;
451203e4227Smycroft 		case FTS_D:
452203e4227Smycroft 			if (p->fts_level != FTS_ROOTLEVEL &&
4531d895808Schristos 			    p->fts_name[0] == '.' && !f_listdot) {
4541d895808Schristos 				(void)fts_set(ftsp, p, FTS_SKIP);
455203e4227Smycroft 				break;
4561d895808Schristos 			}
457203e4227Smycroft 
458203e4227Smycroft 			/*
459203e4227Smycroft 			 * If already output something, put out a newline as
460203e4227Smycroft 			 * a separator.  If multiple arguments, precede each
461203e4227Smycroft 			 * directory with its name.
462203e4227Smycroft 			 */
4630c47a5c3Schristos 			if (!f_leafonly) {
464203e4227Smycroft 				if (output)
465203e4227Smycroft 					(void)printf("\n%s:\n", p->fts_path);
466203e4227Smycroft 				else if (argc > 1) {
467203e4227Smycroft 					(void)printf("%s:\n", p->fts_path);
468203e4227Smycroft 					output = 1;
469203e4227Smycroft 				}
4700c47a5c3Schristos 			}
471203e4227Smycroft 
47283ede345Smycroft 			chp = fts_children(ftsp, ch_options);
47383ede345Smycroft 			display(p, chp);
474203e4227Smycroft 
47583ede345Smycroft 			if (!f_recursive && chp != NULL)
476203e4227Smycroft 				(void)fts_set(ftsp, p, FTS_SKIP);
477203e4227Smycroft 			break;
478203e4227Smycroft 		}
4797e2f5551Schristos 	error = errno;
48078509e7fSchristos 	(void)fts_close(ftsp);
4817e2f5551Schristos 	errno = error;
48283ede345Smycroft 	if (errno)
483b7443b0fSkleink 		err(EXIT_FAILURE, "fts_read");
484203e4227Smycroft }
485203e4227Smycroft 
486203e4227Smycroft /*
487203e4227Smycroft  * Display() takes a linked list of FTSENT structures and passes the list
488203e4227Smycroft  * along with any other necessary information to the print function.  P
489203e4227Smycroft  * points to the parent directory of the display list.
490203e4227Smycroft  */
491203e4227Smycroft static void
492ba2e04dcSlukem display(FTSENT *p, FTSENT *list)
493203e4227Smycroft {
494203e4227Smycroft 	struct stat *sp;
495203e4227Smycroft 	DISPLAY d;
49683ede345Smycroft 	FTSENT *cur;
497203e4227Smycroft 	NAMES *np;
498*c9828589Ssimonb 	u_int64_t btotal;
499990d25a9Slukem 	off_t maxsize;
500990d25a9Slukem 	blkcnt_t maxblock;
50178509e7fSchristos 	ino_t maxinode;
502990d25a9Slukem 	int maxmajor, maxminor;
503990d25a9Slukem 	uint32_t maxnlink;
504990d25a9Slukem 	int bcfile, entries, flen, glen, ulen, maxflags, maxgroup;
505990d25a9Slukem 	unsigned int maxlen;
506e30b7628Slukem 	int maxuser, needstats;
5070e2f9ea9Smycroft 	const char *user, *group;
508c3a9dd2eSenami 	char buf[21];		/* 64 bits == 20 digits, +1 for NUL */
509ac591fc0Slukem 	char nuser[12], ngroup[12];
510e30b7628Slukem 	char *flags = NULL;
511203e4227Smycroft 
512203e4227Smycroft 	/*
513203e4227Smycroft 	 * If list is NULL there are two possibilities: that the parent
514203e4227Smycroft 	 * directory p has no children, or that fts_children() returned an
51583ede345Smycroft 	 * error.  We ignore the error case since it will be replicated
51683ede345Smycroft 	 * on the next call to fts_read() on the post-order visit to the
51783ede345Smycroft 	 * directory p, and will be signalled in traverse().
518203e4227Smycroft 	 */
51983ede345Smycroft 	if (list == NULL)
520203e4227Smycroft 		return;
521203e4227Smycroft 
522203e4227Smycroft 	needstats = f_inode || f_longform || f_size;
523203e4227Smycroft 	flen = 0;
524e30b7628Slukem 	maxinode = maxnlink = 0;
525203e4227Smycroft 	bcfile = 0;
526e30b7628Slukem 	maxuser = maxgroup = maxflags = maxlen = 0;
527*c9828589Ssimonb 	btotal = maxblock = maxsize = 0;
5289ded7f63Smycroft 	maxmajor = maxminor = 0;
529203e4227Smycroft 	for (cur = list, entries = 0; cur; cur = cur->fts_link) {
530203e4227Smycroft 		if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) {
53183ede345Smycroft 			warnx("%s: %s",
53283ede345Smycroft 			    cur->fts_name, strerror(cur->fts_errno));
533203e4227Smycroft 			cur->fts_number = NO_PRINT;
534a4b250cdSsimonb 			rval = EXIT_FAILURE;
53561f28255Scgd 			continue;
53661f28255Scgd 		}
53761f28255Scgd 
538203e4227Smycroft 		/*
539203e4227Smycroft 		 * P is NULL if list is the argv list, to which different rules
540203e4227Smycroft 		 * apply.
541203e4227Smycroft 		 */
542203e4227Smycroft 		if (p == NULL) {
543203e4227Smycroft 			/* Directories will be displayed later. */
544203e4227Smycroft 			if (cur->fts_info == FTS_D && !f_listdir) {
545203e4227Smycroft 				cur->fts_number = NO_PRINT;
546203e4227Smycroft 				continue;
547203e4227Smycroft 			}
548203e4227Smycroft 		} else {
549203e4227Smycroft 			/* Only display dot file if -a/-A set. */
550203e4227Smycroft 			if (cur->fts_name[0] == '.' && !f_listdot) {
551203e4227Smycroft 				cur->fts_number = NO_PRINT;
552203e4227Smycroft 				continue;
553203e4227Smycroft 			}
554203e4227Smycroft 		}
555203e4227Smycroft 		if (cur->fts_namelen > maxlen)
556203e4227Smycroft 			maxlen = cur->fts_namelen;
557203e4227Smycroft 		if (needstats) {
558203e4227Smycroft 			sp = cur->fts_statp;
559203e4227Smycroft 			if (sp->st_blocks > maxblock)
560203e4227Smycroft 				maxblock = sp->st_blocks;
561203e4227Smycroft 			if (sp->st_ino > maxinode)
562203e4227Smycroft 				maxinode = sp->st_ino;
563203e4227Smycroft 			if (sp->st_nlink > maxnlink)
564203e4227Smycroft 				maxnlink = sp->st_nlink;
565203e4227Smycroft 			if (sp->st_size > maxsize)
566203e4227Smycroft 				maxsize = sp->st_size;
5679ded7f63Smycroft 			if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) {
5689ded7f63Smycroft 				bcfile = 1;
5699ded7f63Smycroft 				if (major(sp->st_rdev) > maxmajor)
5709ded7f63Smycroft 					maxmajor = major(sp->st_rdev);
5719ded7f63Smycroft 				if (minor(sp->st_rdev) > maxminor)
5729ded7f63Smycroft 					maxminor = minor(sp->st_rdev);
5739ded7f63Smycroft 			}
57461f28255Scgd 
575203e4227Smycroft 			btotal += sp->st_blocks;
576203e4227Smycroft 			if (f_longform) {
577c3a9dd2eSenami 				if (f_numericonly ||
578c3a9dd2eSenami 				    (user = user_from_uid(sp->st_uid, 0)) ==
579c3a9dd2eSenami 				    NULL) {
5801381f684Smycroft 					(void)snprintf(nuser, sizeof(nuser),
5811381f684Smycroft 					    "%u", sp->st_uid);
582c3a9dd2eSenami 					user = nuser;
583c3a9dd2eSenami 				}
584c3a9dd2eSenami 				if (f_numericonly ||
585c3a9dd2eSenami 				    (group = group_from_gid(sp->st_gid, 0)) ==
586c3a9dd2eSenami 				    NULL) {
5871381f684Smycroft 					(void)snprintf(ngroup, sizeof(ngroup),
5881381f684Smycroft 					    "%u", sp->st_gid);
589ac591fc0Slukem 					group = ngroup;
590ac591fc0Slukem 				}
591203e4227Smycroft 				if ((ulen = strlen(user)) > maxuser)
592203e4227Smycroft 					maxuser = ulen;
593203e4227Smycroft 				if ((glen = strlen(group)) > maxgroup)
594203e4227Smycroft 					maxgroup = glen;
595203e4227Smycroft 				if (f_flags) {
59683ede345Smycroft 					flags =
59778509e7fSchristos 					    flags_to_string((u_long)sp->st_flags, "-");
598203e4227Smycroft 					if ((flen = strlen(flags)) > maxflags)
599203e4227Smycroft 						maxflags = flen;
600203e4227Smycroft 				} else
601203e4227Smycroft 					flen = 0;
60261f28255Scgd 
603203e4227Smycroft 				if ((np = malloc(sizeof(NAMES) +
6048fea6043Selad 				    ulen + glen + flen + 2)) == NULL)
60585cbf55dSdrochner 					err(EXIT_FAILURE, NULL);
60661f28255Scgd 
607203e4227Smycroft 				np->user = &np->data[0];
608203e4227Smycroft 				(void)strcpy(np->user, user);
609203e4227Smycroft 				np->group = &np->data[ulen + 1];
610203e4227Smycroft 				(void)strcpy(np->group, group);
611203e4227Smycroft 
612203e4227Smycroft 				if (f_flags) {
613203e4227Smycroft 					np->flags = &np->data[ulen + glen + 2];
614203e4227Smycroft 				  	(void)strcpy(np->flags, flags);
6150a97c3f6She 					free(flags);
616203e4227Smycroft 				}
617203e4227Smycroft 				cur->fts_pointer = np;
61861f28255Scgd 			}
61961f28255Scgd 		}
620203e4227Smycroft 		++entries;
62161f28255Scgd 	}
622203e4227Smycroft 
623203e4227Smycroft 	if (!entries)
624203e4227Smycroft 		return;
625203e4227Smycroft 
626203e4227Smycroft 	d.list = list;
627203e4227Smycroft 	d.entries = entries;
628203e4227Smycroft 	d.maxlen = maxlen;
629203e4227Smycroft 	if (needstats) {
630203e4227Smycroft 		d.btotal = btotal;
631ba2dda9fSsimonb 		if (f_humanize) {
632ba2dda9fSsimonb 			d.s_block = 4; /* min buf length for humanize_number */
633ba2dda9fSsimonb 		} else {
634067a1826Sdholland 			(void)snprintf(buf, sizeof(buf), "%lld",
635af723a4dSsommerfeld 			    (long long)howmany(maxblock, blocksize));
636203e4227Smycroft 			d.s_block = strlen(buf);
6374aaf499cSerh 			if (f_commas) /* allow for commas before every third digit */
6384aaf499cSerh 				d.s_block += (d.s_block - 1) / 3;
639f4c47802Sgrant 		}
640203e4227Smycroft 		d.s_flags = maxflags;
641203e4227Smycroft 		d.s_group = maxgroup;
64278509e7fSchristos 		(void)snprintf(buf, sizeof(buf), "%llu",
64378509e7fSchristos 		    (unsigned long long)maxinode);
644203e4227Smycroft 		d.s_inode = strlen(buf);
645e30b7628Slukem 		(void)snprintf(buf, sizeof(buf), "%u", maxnlink);
646203e4227Smycroft 		d.s_nlink = strlen(buf);
647f4c47802Sgrant 		if (f_humanize) {
648c4d01568Ssimonb 			d.s_size = 4; /* min buf length for humanize_number */
649f4c47802Sgrant 		} else {
650067a1826Sdholland 			(void)snprintf(buf, sizeof(buf), "%lld",
651c4d01568Ssimonb 			    (long long)maxsize);
652203e4227Smycroft 			d.s_size = strlen(buf);
6534aaf499cSerh 			if (f_commas) /* allow for commas before every third digit */
6544aaf499cSerh 				d.s_size += (d.s_size - 1) / 3;
655f4c47802Sgrant 		}
656203e4227Smycroft 		d.s_user = maxuser;
6579ded7f63Smycroft 		if (bcfile) {
658067a1826Sdholland 			(void)snprintf(buf, sizeof(buf), "%d", maxmajor);
6599ded7f63Smycroft 			d.s_major = strlen(buf);
660067a1826Sdholland 			(void)snprintf(buf, sizeof(buf), "%d", maxminor);
6619ded7f63Smycroft 			d.s_minor = strlen(buf);
6629ded7f63Smycroft 			if (d.s_major + d.s_minor + 2 > d.s_size)
6639ded7f63Smycroft 				d.s_size = d.s_major + d.s_minor + 2;
6649ded7f63Smycroft 			else if (d.s_size - d.s_minor - 2 > d.s_major)
6659ded7f63Smycroft 				d.s_major = d.s_size - d.s_minor - 2;
6669ded7f63Smycroft 		} else {
6679ded7f63Smycroft 			d.s_major = 0;
6689ded7f63Smycroft 			d.s_minor = 0;
6699ded7f63Smycroft 		}
670203e4227Smycroft 	}
671203e4227Smycroft 
672203e4227Smycroft 	printfcn(&d);
673203e4227Smycroft 	output = 1;
674203e4227Smycroft 
675203e4227Smycroft 	if (f_longform)
676203e4227Smycroft 		for (cur = list; cur; cur = cur->fts_link)
677203e4227Smycroft 			free(cur->fts_pointer);
678203e4227Smycroft }
679203e4227Smycroft 
680203e4227Smycroft /*
681203e4227Smycroft  * Ordering for mastercmp:
682203e4227Smycroft  * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories
683203e4227Smycroft  * as larger than directories.  Within either group, use the sort function.
684203e4227Smycroft  * All other levels use the sort function.  Error entries remain unsorted.
685203e4227Smycroft  */
686203e4227Smycroft static int
687ba2e04dcSlukem mastercmp(const FTSENT **a, const FTSENT **b)
688203e4227Smycroft {
68983ede345Smycroft 	int a_info, b_info;
690203e4227Smycroft 
691203e4227Smycroft 	a_info = (*a)->fts_info;
692203e4227Smycroft 	if (a_info == FTS_ERR)
693203e4227Smycroft 		return (0);
694203e4227Smycroft 	b_info = (*b)->fts_info;
695203e4227Smycroft 	if (b_info == FTS_ERR)
696203e4227Smycroft 		return (0);
697203e4227Smycroft 
69845e5a869Sthorpej 	if (a_info == FTS_NS || b_info == FTS_NS) {
699c603307dSmycroft 		if (b_info != FTS_NS)
700c603307dSmycroft 			return (1);
701c603307dSmycroft 		else if (a_info != FTS_NS)
702c603307dSmycroft 			return (-1);
703c603307dSmycroft 		else
70400e5d89aSmycroft 			return (namecmp(*a, *b));
70545e5a869Sthorpej 	}
706203e4227Smycroft 
707ac591fc0Slukem 	if (a_info != b_info && !f_listdir &&
708ac591fc0Slukem 	    (*a)->fts_level == FTS_ROOTLEVEL) {
709203e4227Smycroft 		if (a_info == FTS_D)
710203e4227Smycroft 			return (1);
711203e4227Smycroft 		else if (b_info == FTS_D)
712203e4227Smycroft 			return (-1);
713ac591fc0Slukem 	}
714203e4227Smycroft 	return (sortfcn(*a, *b));
715203e4227Smycroft }
716