xref: /openbsd-src/bin/ls/ls.c (revision 772e31c15284f9f3c896d48f629e8bf45f90de86)
1*772e31c1Sschwarze /*	$OpenBSD: ls.c,v 1.56 2023/10/07 13:29:08 schwarze Exp $	*/
24bd83475Smillert /*	$NetBSD: ls.c,v 1.18 1996/07/09 09:16:29 mycroft Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1989, 1993, 1994
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
9df930be7Sderaadt  * Michael Fischbein.
10df930be7Sderaadt  *
11df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
12df930be7Sderaadt  * modification, are permitted provided that the following conditions
13df930be7Sderaadt  * are met:
14df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
15df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
16df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
17df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
18df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
1929295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
20df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
21df930be7Sderaadt  *    without specific prior written permission.
22df930be7Sderaadt  *
23df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33df930be7Sderaadt  * SUCH DAMAGE.
34df930be7Sderaadt  */
35df930be7Sderaadt 
36df930be7Sderaadt #include <sys/types.h>
37df930be7Sderaadt #include <sys/stat.h>
38df930be7Sderaadt #include <sys/ioctl.h>
39df930be7Sderaadt 
40df930be7Sderaadt #include <dirent.h>
41df930be7Sderaadt #include <err.h>
42df930be7Sderaadt #include <errno.h>
43df930be7Sderaadt #include <fts.h>
4421162ec0Smillert #include <grp.h>
4521162ec0Smillert #include <pwd.h>
46df930be7Sderaadt #include <stdio.h>
47df930be7Sderaadt #include <stdlib.h>
48df930be7Sderaadt #include <string.h>
49df930be7Sderaadt #include <unistd.h>
50a47b6461Sderaadt #include <limits.h>
51b6203726Sschwarze #include <locale.h>
52445ea396Sotto #include <util.h>
53df930be7Sderaadt 
54df930be7Sderaadt #include "ls.h"
55df930be7Sderaadt #include "extern.h"
56df930be7Sderaadt 
57c72b5b24Smillert static void	 display(FTSENT *, FTSENT *);
58c72b5b24Smillert static int	 mastercmp(const FTSENT **, const FTSENT **);
59c72b5b24Smillert static void	 traverse(int, char **, int);
60df930be7Sderaadt 
61c72b5b24Smillert static void (*printfcn)(DISPLAY *);
62c72b5b24Smillert static int (*sortfcn)(const FTSENT *, const FTSENT *);
63df930be7Sderaadt 
64df930be7Sderaadt #define	BY_NAME 0
65df930be7Sderaadt #define	BY_SIZE 1
66df930be7Sderaadt #define	BY_TIME	2
67df930be7Sderaadt 
68df930be7Sderaadt long blocksize;			/* block size units */
697220d8ecSbentley int termwidth;			/* default terminal width */
70df930be7Sderaadt int sortkey = BY_NAME;
71df930be7Sderaadt 
72df930be7Sderaadt /* flags */
73df930be7Sderaadt int f_accesstime;		/* use time of last access */
74df930be7Sderaadt int f_column;			/* columnated format */
75b94f2f87Sderaadt int f_columnacross;		/* columnated format, sorted across */
76df930be7Sderaadt int f_flags;			/* show flags associated with a file */
77947f7ba6Smillert int f_grouponly;		/* long listing format without owner */
7891ea06e0Stedu int f_humanval;			/* show human-readable file sizes */
79df930be7Sderaadt int f_inode;			/* print inode */
80df930be7Sderaadt int f_listdir;			/* list actual directory, not contents */
81df930be7Sderaadt int f_listdot;			/* list files beginning with . */
82df930be7Sderaadt int f_longform;			/* long listing format */
83df930be7Sderaadt int f_nonprint;			/* show unprintables as ? */
84df930be7Sderaadt int f_nosort;			/* don't sort output */
85404227c8Sderaadt int f_numericonly;		/* don't expand uid to symbolic name */
86df930be7Sderaadt int f_recursive;		/* ls subdirectories also */
87df930be7Sderaadt int f_reversesort;		/* reverse whatever sort is used */
88df930be7Sderaadt int f_sectime;			/* print the real time for all files */
89df930be7Sderaadt int f_singlecol;		/* use single column output */
90df930be7Sderaadt int f_size;			/* list size in short listing */
91df930be7Sderaadt int f_statustime;		/* use time of last mode change */
92b94f2f87Sderaadt int f_stream;			/* stream format */
93df930be7Sderaadt int f_type;			/* add type character for non-regular files */
94b94f2f87Sderaadt int f_typedir;			/* add type character for directories */
95df930be7Sderaadt 
96fa398925Sderaadt int rval;
97fa398925Sderaadt 
98df930be7Sderaadt int
ls_main(int argc,char * argv[])9928416801Sderaadt ls_main(int argc, char *argv[])
100df930be7Sderaadt {
101df930be7Sderaadt 	static char dot[] = ".", *dotav[] = { dot, NULL };
102df930be7Sderaadt 	struct winsize win;
103df930be7Sderaadt 	int ch, fts_options, notused;
104cf280624Schl 	int kflag = 0;
105df930be7Sderaadt 	char *p;
106df930be7Sderaadt 
107b6203726Sschwarze #ifndef SMALL
108b6203726Sschwarze 	setlocale(LC_CTYPE, "");
109b6203726Sschwarze #endif
110b6203726Sschwarze 
111df930be7Sderaadt 	/* Terminal defaults to -Cq, non-terminal defaults to -1. */
112df930be7Sderaadt 	if (isatty(STDOUT_FILENO)) {
113df930be7Sderaadt 		f_column = f_nonprint = 1;
11492806c0eStodd 	} else {
115df930be7Sderaadt 		f_singlecol = 1;
11692806c0eStodd 	}
117df930be7Sderaadt 
1187220d8ecSbentley 	termwidth = 0;
1197220d8ecSbentley 	if ((p = getenv("COLUMNS")) != NULL)
1207220d8ecSbentley 		termwidth = strtonum(p, 1, INT_MAX, NULL);
1217220d8ecSbentley 	if (termwidth == 0 && ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
1227220d8ecSbentley 	    win.ws_col > 0)
1237220d8ecSbentley 		termwidth = win.ws_col;
1247220d8ecSbentley 	if (termwidth == 0)
1257220d8ecSbentley 		termwidth = 80;
1267220d8ecSbentley 
1270bd1216cSderaadt 	if (pledge("stdio rpath getpw", NULL) == -1)
1280bd1216cSderaadt 		err(1, "pledge");
1299978c3c0Sderaadt 
130df930be7Sderaadt 	/* Root is -A automatically. */
131df930be7Sderaadt 	if (!getuid())
132df930be7Sderaadt 		f_listdot = 1;
133df930be7Sderaadt 
134df930be7Sderaadt 	fts_options = FTS_PHYSICAL;
1359079317dSokan 	while ((ch = getopt(argc, argv, "1ACFHLRSTacdfghiklmnopqrstux")) != -1) {
136df930be7Sderaadt 		switch (ch) {
137df930be7Sderaadt 		/*
138a40a3c37Sjmc 		 * The -1, -C and -l, -m, -n and -x options all override each
139b94f2f87Sderaadt 		 * other so shell aliasing works right.
140df930be7Sderaadt 		 */
141df930be7Sderaadt 		case '1':
142df930be7Sderaadt 			f_singlecol = 1;
143a40a3c37Sjmc 			f_column = f_columnacross = f_longform = 0;
144a40a3c37Sjmc 			f_numericonly = f_stream = 0;
145df930be7Sderaadt 			break;
146df930be7Sderaadt 		case 'C':
147df930be7Sderaadt 			f_column = 1;
148a40a3c37Sjmc 			f_columnacross = f_longform = f_numericonly = 0;
149a40a3c37Sjmc 			f_singlecol = f_stream = 0;
150df930be7Sderaadt 			break;
151947f7ba6Smillert 		case 'g':
152947f7ba6Smillert 			f_longform = 1;
153947f7ba6Smillert 			if (f_grouponly != -1)
154947f7ba6Smillert 				f_grouponly = 1;
155947f7ba6Smillert 			f_column = f_columnacross = f_singlecol = f_stream = 0;
156947f7ba6Smillert 			break;
157df930be7Sderaadt 		case 'l':
158df930be7Sderaadt 			f_longform = 1;
159947f7ba6Smillert 			f_grouponly = -1;	/* -l always overrides -g */
160b94f2f87Sderaadt 			f_column = f_columnacross = f_singlecol = f_stream = 0;
161b94f2f87Sderaadt 			break;
162b94f2f87Sderaadt 		case 'm':
163b94f2f87Sderaadt 			f_stream = 1;
164a40a3c37Sjmc 			f_column = f_columnacross = f_longform = 0;
165a40a3c37Sjmc 			f_numericonly = f_singlecol = 0;
166b94f2f87Sderaadt 			break;
167b94f2f87Sderaadt 		case 'x':
168b94f2f87Sderaadt 			f_columnacross = 1;
169a40a3c37Sjmc 			f_column = f_longform = f_numericonly = 0;
170a40a3c37Sjmc 			f_singlecol = f_stream = 0;
171404227c8Sderaadt 			break;
172404227c8Sderaadt 		case 'n':
173404227c8Sderaadt 			f_longform = 1;
174404227c8Sderaadt 			f_numericonly = 1;
175a40a3c37Sjmc 			f_column = f_columnacross = f_singlecol = f_stream = 0;
176df930be7Sderaadt 			break;
177df930be7Sderaadt 		/* The -c and -u options override each other. */
178df930be7Sderaadt 		case 'c':
179df930be7Sderaadt 			f_statustime = 1;
180df930be7Sderaadt 			f_accesstime = 0;
181df930be7Sderaadt 			break;
182df930be7Sderaadt 		case 'u':
183df930be7Sderaadt 			f_accesstime = 1;
184df930be7Sderaadt 			f_statustime = 0;
185df930be7Sderaadt 			break;
186df930be7Sderaadt 		case 'F':
187df930be7Sderaadt 			f_type = 1;
188df930be7Sderaadt 			break;
1899079317dSokan 		case 'H':
1909079317dSokan 			fts_options |= FTS_COMFOLLOW;
1919079317dSokan 			break;
192df930be7Sderaadt 		case 'L':
193df930be7Sderaadt 			fts_options &= ~FTS_PHYSICAL;
194df930be7Sderaadt 			fts_options |= FTS_LOGICAL;
195df930be7Sderaadt 			break;
196df930be7Sderaadt 		case 'R':
197df930be7Sderaadt 			f_recursive = 1;
198df930be7Sderaadt 			break;
199cad8a15cSsobrado 		case 'f':
200cad8a15cSsobrado 			f_nosort = 1;
201cad8a15cSsobrado 			/* FALLTHROUGH */
202df930be7Sderaadt 		case 'a':
203df930be7Sderaadt 			fts_options |= FTS_SEEDOT;
204df930be7Sderaadt 			/* FALLTHROUGH */
205df930be7Sderaadt 		case 'A':
206df930be7Sderaadt 			f_listdot = 1;
207df930be7Sderaadt 			break;
208df930be7Sderaadt 		/* The -d option turns off the -R option. */
209df930be7Sderaadt 		case 'd':
210df930be7Sderaadt 			f_listdir = 1;
211df930be7Sderaadt 			f_recursive = 0;
212df930be7Sderaadt 			break;
21391ea06e0Stedu 		case 'h':
21491ea06e0Stedu 			f_humanval = 1;
21591ea06e0Stedu 			break;
216df930be7Sderaadt 		case 'i':
217df930be7Sderaadt 			f_inode = 1;
218df930be7Sderaadt 			break;
219df930be7Sderaadt 		case 'k':
220df930be7Sderaadt 			blocksize = 1024;
221df930be7Sderaadt 			kflag = 1;
222df930be7Sderaadt 			break;
223df930be7Sderaadt 		case 'o':
224df930be7Sderaadt 			f_flags = 1;
225df930be7Sderaadt 			break;
226b94f2f87Sderaadt 		case 'p':
227b94f2f87Sderaadt 			f_typedir = 1;
228b94f2f87Sderaadt 			break;
229df930be7Sderaadt 		case 'q':
230df930be7Sderaadt 			f_nonprint = 1;
231df930be7Sderaadt 			break;
232df930be7Sderaadt 		case 'r':
233df930be7Sderaadt 			f_reversesort = 1;
234df930be7Sderaadt 			break;
235df930be7Sderaadt 		case 'S':
236df930be7Sderaadt 			sortkey = BY_SIZE;
237df930be7Sderaadt 			break;
238df930be7Sderaadt 		case 's':
239df930be7Sderaadt 			f_size = 1;
240df930be7Sderaadt 			break;
241df930be7Sderaadt 		case 'T':
242df930be7Sderaadt 			f_sectime = 1;
243df930be7Sderaadt 			break;
244df930be7Sderaadt 		case 't':
245df930be7Sderaadt 			sortkey = BY_TIME;
246df930be7Sderaadt 			break;
247df930be7Sderaadt 		default:
248df930be7Sderaadt 			usage();
249df930be7Sderaadt 		}
250df930be7Sderaadt 	}
251df930be7Sderaadt 	argc -= optind;
252df930be7Sderaadt 	argv += optind;
253df930be7Sderaadt 
254df930be7Sderaadt 	/*
255947f7ba6Smillert 	 * If both -g and -l options, let -l take precedence.
256947f7ba6Smillert 	 * This preserves compatibility with the historic BSD ls -lg.
257947f7ba6Smillert 	 */
258947f7ba6Smillert 	if (f_grouponly == -1)
259947f7ba6Smillert 		f_grouponly = 0;
260947f7ba6Smillert 
261947f7ba6Smillert 	/*
262b94f2f87Sderaadt 	 * If not -F, -i, -l, -p, -S, -s or -t options, don't require stat
263df930be7Sderaadt 	 * information.
264df930be7Sderaadt 	 */
2654884558fSaaron 	if (!f_longform && !f_inode && !f_size && !f_type && !f_typedir &&
266df930be7Sderaadt 	    sortkey == BY_NAME)
267df930be7Sderaadt 		fts_options |= FTS_NOSTAT;
268df930be7Sderaadt 
269df930be7Sderaadt 	/*
270df930be7Sderaadt 	 * If not -F, -d or -l options, follow any symbolic links listed on
271df930be7Sderaadt 	 * the command line.
272df930be7Sderaadt 	 */
273df930be7Sderaadt 	if (!f_longform && !f_listdir && !f_type)
274df930be7Sderaadt 		fts_options |= FTS_COMFOLLOW;
275df930be7Sderaadt 
276df930be7Sderaadt 	/* If -l or -s, figure out block size. */
277df930be7Sderaadt 	if (f_longform || f_size) {
278df930be7Sderaadt 		if (!kflag)
279df930be7Sderaadt 			(void)getbsize(&notused, &blocksize);
280df930be7Sderaadt 		blocksize /= 512;
281df930be7Sderaadt 	}
282df930be7Sderaadt 
283df930be7Sderaadt 	/* Select a sort function. */
284df930be7Sderaadt 	if (f_reversesort) {
285df930be7Sderaadt 		switch (sortkey) {
286df930be7Sderaadt 		case BY_NAME:
287df930be7Sderaadt 			sortfcn = revnamecmp;
288df930be7Sderaadt 			break;
289df930be7Sderaadt 		case BY_SIZE:
290df930be7Sderaadt 			sortfcn = revsizecmp;
291df930be7Sderaadt 			break;
292df930be7Sderaadt 		case BY_TIME:
293df930be7Sderaadt 			if (f_accesstime)
294df930be7Sderaadt 				sortfcn = revacccmp;
295df930be7Sderaadt 			else if (f_statustime)
296df930be7Sderaadt 				sortfcn = revstatcmp;
297df930be7Sderaadt 			else /* Use modification time. */
298df930be7Sderaadt 				sortfcn = revmodcmp;
299df930be7Sderaadt 			break;
300df930be7Sderaadt 		}
301df930be7Sderaadt 	} else {
302df930be7Sderaadt 		switch (sortkey) {
303df930be7Sderaadt 		case BY_NAME:
304df930be7Sderaadt 			sortfcn = namecmp;
305df930be7Sderaadt 			break;
306df930be7Sderaadt 		case BY_SIZE:
307df930be7Sderaadt 			sortfcn = sizecmp;
308df930be7Sderaadt 			break;
309df930be7Sderaadt 		case BY_TIME:
310df930be7Sderaadt 			if (f_accesstime)
311df930be7Sderaadt 				sortfcn = acccmp;
312df930be7Sderaadt 			else if (f_statustime)
313df930be7Sderaadt 				sortfcn = statcmp;
314df930be7Sderaadt 			else /* Use modification time. */
315df930be7Sderaadt 				sortfcn = modcmp;
316df930be7Sderaadt 			break;
317df930be7Sderaadt 		}
318df930be7Sderaadt 	}
319df930be7Sderaadt 
320df930be7Sderaadt 	/* Select a print function. */
321df930be7Sderaadt 	if (f_singlecol)
322df930be7Sderaadt 		printfcn = printscol;
323b94f2f87Sderaadt 	else if (f_columnacross)
324b94f2f87Sderaadt 		printfcn = printacol;
325df930be7Sderaadt 	else if (f_longform)
326df930be7Sderaadt 		printfcn = printlong;
327b94f2f87Sderaadt 	else if (f_stream)
328b94f2f87Sderaadt 		printfcn = printstream;
329df930be7Sderaadt 	else
330df930be7Sderaadt 		printfcn = printcol;
331df930be7Sderaadt 
332df930be7Sderaadt 	if (argc)
333df930be7Sderaadt 		traverse(argc, argv, fts_options);
334df930be7Sderaadt 	else
335df930be7Sderaadt 		traverse(1, dotav, fts_options);
3364e4bc841Sderaadt 	return (rval);
337df930be7Sderaadt }
338df930be7Sderaadt 
339df930be7Sderaadt static int output;			/* If anything output. */
340df930be7Sderaadt 
341df930be7Sderaadt /*
342df930be7Sderaadt  * Traverse() walks the logical directory structure specified by the argv list
343df930be7Sderaadt  * in the order specified by the mastercmp() comparison function.  During the
344df930be7Sderaadt  * traversal it passes linked lists of structures to display() which represent
345df930be7Sderaadt  * a superset (may be exact set) of the files to be displayed.
346df930be7Sderaadt  */
347df930be7Sderaadt static void
traverse(int argc,char * argv[],int options)34828416801Sderaadt traverse(int argc, char *argv[], int options)
349df930be7Sderaadt {
350df930be7Sderaadt 	FTS *ftsp;
351df930be7Sderaadt 	FTSENT *p, *chp;
3521bc65825Sotto 	int ch_options, saved_errno;
353df930be7Sderaadt 
354df930be7Sderaadt 	if ((ftsp =
355df930be7Sderaadt 	    fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
356df930be7Sderaadt 		err(1, NULL);
357df930be7Sderaadt 
358bc2593c5Smillert 	/*
359bc2593c5Smillert 	 * We ignore errors from fts_children here since they will be
360bc2593c5Smillert 	 * replicated and signalled on the next call to fts_read() below.
361bc2593c5Smillert 	 */
362bc2593c5Smillert 	chp = fts_children(ftsp, 0);
363bc2593c5Smillert 	if (chp != NULL)
364bc2593c5Smillert 		display(NULL, chp);
365df930be7Sderaadt 	if (f_listdir)
366df930be7Sderaadt 		return;
367df930be7Sderaadt 
368df930be7Sderaadt 	/*
369df930be7Sderaadt 	 * If not recursing down this tree and don't need stat info, just get
370df930be7Sderaadt 	 * the names.
371df930be7Sderaadt 	 */
372df930be7Sderaadt 	ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0;
373df930be7Sderaadt 
374df930be7Sderaadt 	while ((p = fts_read(ftsp)) != NULL)
375df930be7Sderaadt 		switch (p->fts_info) {
376df930be7Sderaadt 		case FTS_D:
3774884558fSaaron 			if (p->fts_name[0] == '.' &&
378b352e555Smillert 			    p->fts_level != FTS_ROOTLEVEL && !f_listdot) {
379b352e555Smillert 				(void)fts_set(ftsp, p, FTS_SKIP);
380df930be7Sderaadt 				break;
381b352e555Smillert 			}
382df930be7Sderaadt 
383df930be7Sderaadt 			/*
384df930be7Sderaadt 			 * If already output something, put out a newline as
385df930be7Sderaadt 			 * a separator.  If multiple arguments, precede each
386df930be7Sderaadt 			 * directory with its name.
387df930be7Sderaadt 			 */
388df930be7Sderaadt 			if (output)
389df930be7Sderaadt 				(void)printf("\n%s:\n", p->fts_path);
390b3b44669Smillert 			else if (f_recursive || argc > 1) {
391df930be7Sderaadt 				(void)printf("%s:\n", p->fts_path);
392df930be7Sderaadt 				output = 1;
393df930be7Sderaadt 			}
394df930be7Sderaadt 
395df930be7Sderaadt 			chp = fts_children(ftsp, ch_options);
3961bc65825Sotto 			saved_errno = errno;
397df930be7Sderaadt 			display(p, chp);
398df930be7Sderaadt 
399b721e737Sotto 			/*
400b721e737Sotto 			 * On fts_children() returning error do recurse to see
401b721e737Sotto 			 * the error.
402b721e737Sotto 			 */
4031bc65825Sotto 			if (!f_recursive && !(chp == NULL && saved_errno != 0))
404df930be7Sderaadt 				(void)fts_set(ftsp, p, FTS_SKIP);
405df930be7Sderaadt 			break;
4064884558fSaaron 		case FTS_DC:
4074884558fSaaron 			warnx("%s: directory causes a cycle", p->fts_name);
4084884558fSaaron 			break;
4094884558fSaaron 		case FTS_DNR:
4104884558fSaaron 		case FTS_ERR:
411bb6ffffeSjaredy 			warnx("%s: %s", p->fts_name[0] == '\0' ? p->fts_path :
412bb6ffffeSjaredy 			    p->fts_name, strerror(p->fts_errno));
4134884558fSaaron 			rval = 1;
4144884558fSaaron 			break;
415df930be7Sderaadt 		}
416df930be7Sderaadt 	if (errno)
417df930be7Sderaadt 		err(1, "fts_read");
418f12e5d26Suebayasi 
419f12e5d26Suebayasi 	fts_close(ftsp);
420df930be7Sderaadt }
421df930be7Sderaadt 
422df930be7Sderaadt /*
423df930be7Sderaadt  * Display() takes a linked list of FTSENT structures and passes the list
424df930be7Sderaadt  * along with any other necessary information to the print function.  P
425df930be7Sderaadt  * points to the parent directory of the display list.
426df930be7Sderaadt  */
427df930be7Sderaadt static void
display(FTSENT * p,FTSENT * list)42828416801Sderaadt display(FTSENT *p, FTSENT *list)
429df930be7Sderaadt {
430df930be7Sderaadt 	struct stat *sp;
431df930be7Sderaadt 	DISPLAY d;
432df930be7Sderaadt 	FTSENT *cur;
433df930be7Sderaadt 	NAMES *np;
4344857c473Sdhill 	off_t maxsize;
4354442fdb7Skrw 	nlink_t maxnlink;
4364442fdb7Skrw 	unsigned long long btotal;
4374442fdb7Skrw 	blkcnt_t maxblock;
4383e12fe88Sguenther 	ino_t maxinode;
43909b34e9cSschwarze 	unsigned int maxmajor, maxminor;
4404442fdb7Skrw 	int bcfile, flen, glen, ulen, maxflags, maxgroup, maxuser, maxlen;
441df930be7Sderaadt 	int entries, needstats;
442b6203726Sschwarze 	int width;
443cdc5a29bSmillert 	const char *user, *group;
444404227c8Sderaadt 	char nuser[12], ngroup[12];
4454bd83475Smillert 	char *flags = NULL;
446764064c4Smickey 
447df930be7Sderaadt 	needstats = f_inode || f_longform || f_size;
448df930be7Sderaadt 	flen = 0;
449df930be7Sderaadt 	btotal = maxblock = maxinode = maxlen = maxnlink = 0;
450df930be7Sderaadt 	bcfile = 0;
451df930be7Sderaadt 	maxuser = maxgroup = maxflags = 0;
45209b34e9cSschwarze 	maxmajor = maxminor = 0;
453df930be7Sderaadt 	maxsize = 0;
45421162ec0Smillert 	for (cur = list, entries = 0; cur != NULL; cur = cur->fts_link) {
455df930be7Sderaadt 		if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) {
456df930be7Sderaadt 			warnx("%s: %s",
457df930be7Sderaadt 			    cur->fts_name, strerror(cur->fts_errno));
458df930be7Sderaadt 			cur->fts_number = NO_PRINT;
459fa398925Sderaadt 			rval = 1;
460df930be7Sderaadt 			continue;
461df930be7Sderaadt 		}
462df930be7Sderaadt 
463df930be7Sderaadt 		/*
464df930be7Sderaadt 		 * P is NULL if list is the argv list, to which different rules
465df930be7Sderaadt 		 * apply.
466df930be7Sderaadt 		 */
467df930be7Sderaadt 		if (p == NULL) {
468df930be7Sderaadt 			/* Directories will be displayed later. */
469df930be7Sderaadt 			if (cur->fts_info == FTS_D && !f_listdir) {
470df930be7Sderaadt 				cur->fts_number = NO_PRINT;
471df930be7Sderaadt 				continue;
472df930be7Sderaadt 			}
473df930be7Sderaadt 		} else {
474df930be7Sderaadt 			/* Only display dot file if -a/-A set. */
475df930be7Sderaadt 			if (cur->fts_name[0] == '.' && !f_listdot) {
476df930be7Sderaadt 				cur->fts_number = NO_PRINT;
477df930be7Sderaadt 				continue;
478df930be7Sderaadt 			}
479df930be7Sderaadt 		}
480b6203726Sschwarze 		if ((width = mbsprint(cur->fts_name, 0)) > maxlen)
481b6203726Sschwarze 			maxlen = width;
482df930be7Sderaadt 		if (needstats) {
483df930be7Sderaadt 			sp = cur->fts_statp;
484df930be7Sderaadt 			if (sp->st_blocks > maxblock)
485df930be7Sderaadt 				maxblock = sp->st_blocks;
486df930be7Sderaadt 			if (sp->st_ino > maxinode)
487df930be7Sderaadt 				maxinode = sp->st_ino;
488df930be7Sderaadt 			if (sp->st_nlink > maxnlink)
489df930be7Sderaadt 				maxnlink = sp->st_nlink;
490df930be7Sderaadt 			if (sp->st_size > maxsize)
491df930be7Sderaadt 				maxsize = sp->st_size;
492df930be7Sderaadt 
493df930be7Sderaadt 			btotal += sp->st_blocks;
494df930be7Sderaadt 			if (f_longform) {
495404227c8Sderaadt 				if (f_numericonly) {
496c4db6faaSderaadt 					snprintf(nuser, sizeof nuser, "%u", sp->st_uid);
497c4db6faaSderaadt 					snprintf(ngroup, sizeof nuser, "%u", sp->st_gid);
498404227c8Sderaadt 					user = nuser;
499404227c8Sderaadt 					group = ngroup;
500404227c8Sderaadt 				} else {
501df930be7Sderaadt 					user = user_from_uid(sp->st_uid, 0);
502404227c8Sderaadt 					group = group_from_gid(sp->st_gid, 0);
503404227c8Sderaadt 				}
504df930be7Sderaadt 				if ((ulen = strlen(user)) > maxuser)
505df930be7Sderaadt 					maxuser = ulen;
506df930be7Sderaadt 				if ((glen = strlen(group)) > maxgroup)
507df930be7Sderaadt 					maxgroup = glen;
508df930be7Sderaadt 				if (f_flags) {
509b802a6c2Smickey 					flags = fflagstostr(sp->st_flags);
510b802a6c2Smickey 					if (*flags == '\0')
511b802a6c2Smickey 						flags = "-";
512df930be7Sderaadt 					if ((flen = strlen(flags)) > maxflags)
513df930be7Sderaadt 						maxflags = flen;
514df930be7Sderaadt 				} else
515df930be7Sderaadt 					flen = 0;
516df930be7Sderaadt 
517df930be7Sderaadt 				if ((np = malloc(sizeof(NAMES) +
518150e4ac3Sderaadt 				    ulen + 1 + glen + 1 + flen + 1)) == NULL)
519df930be7Sderaadt 					err(1, NULL);
520df930be7Sderaadt 
521df930be7Sderaadt 				np->user = &np->data[0];
522150e4ac3Sderaadt 				(void)strlcpy(np->user, user, ulen + 1);
523df930be7Sderaadt 				np->group = &np->data[ulen + 1];
524150e4ac3Sderaadt 				(void)strlcpy(np->group, group, glen + 1);
525df930be7Sderaadt 
526df930be7Sderaadt 				if (S_ISCHR(sp->st_mode) ||
52709b34e9cSschwarze 				    S_ISBLK(sp->st_mode)) {
528df930be7Sderaadt 					bcfile = 1;
52909b34e9cSschwarze 					if (maxmajor < major(sp->st_rdev))
53009b34e9cSschwarze 						maxmajor = major(sp->st_rdev);
53109b34e9cSschwarze 					if (maxminor < minor(sp->st_rdev))
53209b34e9cSschwarze 						maxminor = minor(sp->st_rdev);
53309b34e9cSschwarze 				}
534df930be7Sderaadt 				if (f_flags) {
535150e4ac3Sderaadt 					np->flags = &np->data[ulen + 1 + glen + 1];
536150e4ac3Sderaadt 					(void)strlcpy(np->flags, flags, flen + 1);
537b802a6c2Smickey 					if (*flags != '-')
538b802a6c2Smickey 						free(flags);
539df930be7Sderaadt 				}
540df930be7Sderaadt 				cur->fts_pointer = np;
541df930be7Sderaadt 			}
542df930be7Sderaadt 		}
543df930be7Sderaadt 		++entries;
544df930be7Sderaadt 	}
545df930be7Sderaadt 
546bc2593c5Smillert 	/*
547bc2593c5Smillert 	 * If there are no entries to display, we normally stop right
548bc2593c5Smillert 	 * here.  However, we must continue if we have to display the
549bc2593c5Smillert 	 * total block count.  In this case, we display the total only
550bc2593c5Smillert 	 * on the second (p != NULL) pass.
551bc2593c5Smillert 	 */
552bc2593c5Smillert 	if (!entries && (!(f_longform || f_size) || p == NULL))
553df930be7Sderaadt 		return;
554df930be7Sderaadt 
555df930be7Sderaadt 	d.list = list;
556df930be7Sderaadt 	d.entries = entries;
557df930be7Sderaadt 	d.maxlen = maxlen;
558df930be7Sderaadt 	if (needstats) {
559df930be7Sderaadt 		d.btotal = btotal;
560*772e31c1Sschwarze 		d.s_block = snprintf(NULL, 0, "%llu",
5614442fdb7Skrw 		    (unsigned long long)maxblock);
562df930be7Sderaadt 		d.s_flags = maxflags;
563df930be7Sderaadt 		d.s_group = maxgroup;
564*772e31c1Sschwarze 		d.s_inode = snprintf(NULL, 0, "%llu",
5653e12fe88Sguenther 		    (unsigned long long)maxinode);
566*772e31c1Sschwarze 		d.s_nlink = snprintf(NULL, 0, "%lu",
5674442fdb7Skrw 		    (unsigned long)maxnlink);
568*772e31c1Sschwarze 		if (!f_humanval)
569*772e31c1Sschwarze 			d.s_size = snprintf(NULL, 0, "%lld",
5704857c473Sdhill 			    (long long)maxsize);
571*772e31c1Sschwarze 		else
572445ea396Sotto 			d.s_size = FMT_SCALED_STRSIZE-2; /* no - or '\0' */
57309b34e9cSschwarze 		d.s_major = d.s_minor = 3;
57409b34e9cSschwarze 		if (bcfile) {
575*772e31c1Sschwarze 			d.s_major = snprintf(NULL, 0, "%u", maxmajor);
576*772e31c1Sschwarze 			d.s_minor = snprintf(NULL, 0, "%u", maxminor);
57709b34e9cSschwarze 			if (d.s_size <= d.s_major + 2 + d.s_minor)
57809b34e9cSschwarze 				d.s_size = d.s_major + 2 + d.s_minor;
57909b34e9cSschwarze 			else
58009b34e9cSschwarze 				d.s_major = d.s_size - 2 - d.s_minor;
58109b34e9cSschwarze 		}
582df930be7Sderaadt 		d.s_user = maxuser;
583df930be7Sderaadt 	}
584df930be7Sderaadt 
585df930be7Sderaadt 	printfcn(&d);
586df930be7Sderaadt 	output = 1;
587df930be7Sderaadt 
588df930be7Sderaadt 	if (f_longform)
58921162ec0Smillert 		for (cur = list; cur != NULL; cur = cur->fts_link)
590df930be7Sderaadt 			free(cur->fts_pointer);
591df930be7Sderaadt }
592df930be7Sderaadt 
593df930be7Sderaadt /*
594df930be7Sderaadt  * Ordering for mastercmp:
595df930be7Sderaadt  * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories
596df930be7Sderaadt  * as larger than directories.  Within either group, use the sort function.
597df930be7Sderaadt  * All other levels use the sort function.  Error entries remain unsorted.
598df930be7Sderaadt  */
599df930be7Sderaadt static int
mastercmp(const FTSENT ** a,const FTSENT ** b)60028416801Sderaadt mastercmp(const FTSENT **a, const FTSENT **b)
601df930be7Sderaadt {
602df930be7Sderaadt 	int a_info, b_info;
603df930be7Sderaadt 
604df930be7Sderaadt 	a_info = (*a)->fts_info;
605df930be7Sderaadt 	if (a_info == FTS_ERR)
606df930be7Sderaadt 		return (0);
607df930be7Sderaadt 	b_info = (*b)->fts_info;
608df930be7Sderaadt 	if (b_info == FTS_ERR)
609df930be7Sderaadt 		return (0);
610df930be7Sderaadt 
611c28fedd7Sderaadt 	if (a_info == FTS_NS || b_info == FTS_NS) {
6124bd83475Smillert 		if (b_info != FTS_NS)
6134bd83475Smillert 			return (1);
6144bd83475Smillert 		else if (a_info != FTS_NS)
6154bd83475Smillert 			return (-1);
6164bd83475Smillert 		else
617df930be7Sderaadt 			return (namecmp(*a, *b));
618c28fedd7Sderaadt 	}
619df930be7Sderaadt 
620817b1966Sderaadt 	if (a_info != b_info &&
621817b1966Sderaadt 	    (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) {
622df930be7Sderaadt 		if (a_info == FTS_D)
623df930be7Sderaadt 			return (1);
624817b1966Sderaadt 		if (b_info == FTS_D)
625df930be7Sderaadt 			return (-1);
626817b1966Sderaadt 	}
627df930be7Sderaadt 	return (sortfcn(*a, *b));
628df930be7Sderaadt }
629