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(¬used, &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