Initial import from FreeBSD RELENG_4:
[games.git] / bin / ls / ls.c
1 /*
2  * Copyright (c) 1989, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Michael Fischbein.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #ifndef lint
38 static const char copyright[] =
39 "@(#) Copyright (c) 1989, 1993, 1994\n\
40         The Regents of the University of California.  All rights reserved.\n";
41 #endif /* not lint */
42
43 #if 0
44 #ifndef lint
45 static char sccsid[] = "@(#)ls.c        8.5 (Berkeley) 4/2/94";
46 #endif /* not lint */
47 #endif
48 #include <sys/cdefs.h>
49 __FBSDID("$FreeBSD: src/bin/ls/ls.c,v 1.32.2.8 2002/11/17 10:27:34 tjr Exp $");
50
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/ioctl.h>
54
55 #include <dirent.h>
56 #include <err.h>
57 #include <errno.h>
58 #include <fts.h>
59 #include <grp.h>
60 #include <limits.h>
61 #include <locale.h>
62 #include <pwd.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67 #ifdef COLORLS
68 #include <termcap.h>
69 #include <signal.h>
70 #endif
71
72 #include "ls.h"
73 #include "extern.h"
74
75 /*
76  * Upward approximation of the maximum number of characters needed to
77  * represent a value of integral type t as a string, excluding the
78  * NUL terminator, with provision for a sign.
79  */
80 #define STRBUF_SIZEOF(t)        (1 + CHAR_BIT * sizeof(t) / 3 + 1)
81
82 static void      display(FTSENT *, FTSENT *);
83 static u_quad_t  makenines(u_long);
84 static int       mastercmp(const FTSENT **, const FTSENT **);
85 static void      traverse(int, char **, int);
86
87 static void (*printfcn)(DISPLAY *);
88 static int (*sortfcn)(const FTSENT *, const FTSENT *);
89
90 long blocksize;                 /* block size units */
91 int termwidth = 80;             /* default terminal width */
92
93 /* flags */
94        int f_accesstime;        /* use time of last access */
95        int f_flags;             /* show flags associated with a file */
96        int f_humanval;          /* show human-readable file sizes */
97        int f_inode;             /* print inode */
98 static int f_kblocks;           /* print size in kilobytes */
99 static int f_listdir;           /* list actual directory, not contents */
100 static int f_listdot;           /* list files beginning with . */
101        int f_longform;          /* long listing format */
102        int f_nonprint;          /* show unprintables as ? */
103 static int f_nosort;            /* don't sort output */
104        int f_notabs;            /* don't use tab-separated multi-col output */
105 static int f_numericonly;       /* don't convert uid/gid to name */
106        int f_octal;             /* show unprintables as \xxx */
107        int f_octal_escape;      /* like f_octal but use C escapes if possible */
108 static int f_recursive;         /* ls subdirectories also */
109 static int f_reversesort;       /* reverse whatever sort is used */
110        int f_sectime;           /* print the real time for all files */
111 static int f_singlecol;         /* use single column output */
112        int f_size;              /* list size in short listing */
113        int f_slash;             /* similar to f_type, but only for dirs */
114        int f_sortacross;        /* sort across rows, not down columns */ 
115        int f_statustime;        /* use time of last mode change */
116        int f_stream;            /* stream the output, separate with commas */
117 static int f_timesort;          /* sort by time vice name */
118        int f_type;              /* add type character for non-regular files */
119 static int f_whiteout;          /* show whiteout entries */
120 #ifdef COLORLS
121        int f_color;             /* add type in color for non-regular files */
122
123 char *ansi_bgcol;               /* ANSI sequence to set background colour */
124 char *ansi_fgcol;               /* ANSI sequence to set foreground colour */
125 char *ansi_coloff;              /* ANSI sequence to reset colours */
126 char *attrs_off;                /* ANSI sequence to turn off attributes */
127 char *enter_bold;               /* ANSI sequence to set color to bold mode */
128 #endif
129
130 static int rval;
131
132 int
133 main(int argc, char *argv[])
134 {
135         static char dot[] = ".", *dotav[] = {dot, NULL};
136         struct winsize win;
137         int ch, fts_options, notused;
138         char *p;
139 #ifdef COLORLS
140         char termcapbuf[1024];  /* termcap definition buffer */
141         char tcapbuf[512];      /* capability buffer */
142         char *bp = tcapbuf;
143 #endif
144
145         (void)setlocale(LC_ALL, "");
146
147         /* Terminal defaults to -Cq, non-terminal defaults to -1. */
148         if (isatty(STDOUT_FILENO)) {
149                 termwidth = 80;
150                 if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
151                         termwidth = atoi(p);
152                 else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 &&
153                     win.ws_col > 0)
154                         termwidth = win.ws_col;
155                 f_nonprint = 1;
156         } else {
157                 f_singlecol = 1;
158                 /* retrieve environment variable, in case of explicit -C */
159                 p = getenv("COLUMNS");
160                 if (p)
161                         termwidth = atoi(p);
162         }
163
164         /* Root is -A automatically. */
165         if (!getuid())
166                 f_listdot = 1;
167
168         fts_options = FTS_PHYSICAL;
169         while ((ch = getopt(argc, argv, "1ABCFGHLPRTWabcdfghiklmnopqrstuwx")) 
170             != -1) {
171                 switch (ch) {
172                 /*
173                  * The -1, -C, -x and -l options all override each other so
174                  * shell aliasing works right.
175                  */
176                 case '1':
177                         f_singlecol = 1;
178                         f_longform = 0;
179                         f_stream = 0;
180                         break;
181                 case 'B':
182                         f_nonprint = 0;
183                         f_octal = 1;
184                         f_octal_escape = 0;
185                         break;
186                 case 'C':
187                         f_sortacross = f_longform = f_singlecol = 0;
188                         break;
189                 case 'l':
190                         f_longform = 1;
191                         f_singlecol = 0;
192                         f_stream = 0;
193                         break;
194                 case 'x':
195                         f_sortacross = 1;
196                         f_longform = 0;
197                         f_singlecol = 0;
198                         break;
199                 /* The -c and -u options override each other. */
200                 case 'c':
201                         f_statustime = 1;
202                         f_accesstime = 0;
203                         break;
204                 case 'u':
205                         f_accesstime = 1;
206                         f_statustime = 0;
207                         break;
208                 case 'F':
209                         f_type = 1;
210                         f_slash = 0;
211                         break;
212                 case 'H':
213                         fts_options |= FTS_COMFOLLOW;
214                         break;
215                 case 'G':
216                         setenv("CLICOLOR", "", 1);
217                         break;
218                 case 'L':
219                         fts_options &= ~FTS_PHYSICAL;
220                         fts_options |= FTS_LOGICAL;
221                         break;
222                 case 'P':
223                         fts_options &= ~FTS_COMFOLLOW;
224                         fts_options &= ~FTS_LOGICAL;
225                         fts_options |= FTS_PHYSICAL;
226                         break;
227                 case 'R':
228                         f_recursive = 1;
229                         break;
230                 case 'a':
231                         fts_options |= FTS_SEEDOT;
232                         /* FALLTHROUGH */
233                 case 'A':
234                         f_listdot = 1;
235                         break;
236                 /* The -d option turns off the -R option. */
237                 case 'd':
238                         f_listdir = 1;
239                         f_recursive = 0;
240                         break;
241                 case 'f':
242                         f_nosort = 1;
243                         break;
244                 case 'g':       /* Compatibility with 4.3BSD. */
245                         break;
246                 case 'h':
247                         f_humanval = 1;
248                         break;
249                 case 'i':
250                         f_inode = 1;
251                         break;
252                 case 'k':
253                         f_kblocks = 1;
254                         break;
255                 case 'm':
256                         f_stream = 1;
257                         f_singlecol = 0;
258                         f_longform = 0;
259                         break;
260                 case 'n':
261                         f_numericonly = 1;
262                         break;
263                 case 'o':
264                         f_flags = 1;
265                         break;
266                 case 'p':
267                         f_slash = 1;
268                         f_type = 1;
269                         break;
270                 case 'q':
271                         f_nonprint = 1;
272                         f_octal = 0;
273                         f_octal_escape = 0;
274                         break;
275                 case 'r':
276                         f_reversesort = 1;
277                         break;
278                 case 's':
279                         f_size = 1;
280                         break;
281                 case 'T':
282                         f_sectime = 1;
283                         break;
284                 case 't':
285                         f_timesort = 1;
286                         break;
287                 case 'W':
288                         f_whiteout = 1;
289                         break;
290                 case 'b':
291                         f_nonprint = 0;
292                         f_octal = 0;
293                         f_octal_escape = 1;
294                         break;
295                 case 'w':
296                         f_nonprint = 0;
297                         f_octal = 0;
298                         f_octal_escape = 0;
299                         break;
300                 default:
301                 case '?':
302                         usage();
303                 }
304         }
305         argc -= optind;
306         argv += optind;
307
308         /* Enabling of colours is conditional on the environment. */
309         if (getenv("CLICOLOR") &&
310             (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE")))
311 #ifdef COLORLS
312                 if (tgetent(termcapbuf, getenv("TERM")) == 1) {
313                         ansi_fgcol = tgetstr("AF", &bp);
314                         ansi_bgcol = tgetstr("AB", &bp);
315                         attrs_off = tgetstr("me", &bp);
316                         enter_bold = tgetstr("md", &bp);
317
318                         /* To switch colours off use 'op' if
319                          * available, otherwise use 'oc', or
320                          * don't do colours at all. */
321                         ansi_coloff = tgetstr("op", &bp);
322                         if (!ansi_coloff)
323                                 ansi_coloff = tgetstr("oc", &bp);
324                         if (ansi_fgcol && ansi_bgcol && ansi_coloff)
325                                 f_color = 1;
326                 }
327 #else
328                 (void)fprintf(stderr, "Color support not compiled in.\n");
329 #endif /*COLORLS*/
330
331 #ifdef COLORLS
332         if (f_color) {
333                 /*
334                  * We can't put tabs and color sequences together:
335                  * column number will be incremented incorrectly
336                  * for "stty oxtabs" mode.
337                  */
338                 f_notabs = 1;
339                 (void)signal(SIGINT, colorquit);
340                 (void)signal(SIGQUIT, colorquit);
341                 parsecolors(getenv("LSCOLORS"));
342         }
343 #endif
344
345         /*
346          * If not -F, -i, -l, -s or -t options, don't require stat
347          * information, unless in color mode in which case we do
348          * need this to determine which colors to display.
349          */
350         if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type
351 #ifdef COLORLS
352             && !f_color
353 #endif
354             )
355                 fts_options |= FTS_NOSTAT;
356
357         /*
358          * If not -F, -d or -l options, follow any symbolic links listed on
359          * the command line.
360          */
361         if (!f_longform && !f_listdir && !f_type)
362                 fts_options |= FTS_COMFOLLOW;
363
364         /*
365          * If -W, show whiteout entries
366          */
367 #ifdef FTS_WHITEOUT
368         if (f_whiteout)
369                 fts_options |= FTS_WHITEOUT;
370 #endif
371
372         /* If -l or -s, figure out block size. */
373         if (f_longform || f_size) {
374                 if (f_kblocks)
375                         blocksize = 2;
376                 else {
377                         (void)getbsize(&notused, &blocksize);
378                         blocksize /= 512;
379                 }
380         }
381         /* Select a sort function. */
382         if (f_reversesort) {
383                 if (!f_timesort)
384                         sortfcn = revnamecmp;
385                 else if (f_accesstime)
386                         sortfcn = revacccmp;
387                 else if (f_statustime)
388                         sortfcn = revstatcmp;
389                 else            /* Use modification time. */
390                         sortfcn = revmodcmp;
391         } else {
392                 if (!f_timesort)
393                         sortfcn = namecmp;
394                 else if (f_accesstime)
395                         sortfcn = acccmp;
396                 else if (f_statustime)
397                         sortfcn = statcmp;
398                 else            /* Use modification time. */
399                         sortfcn = modcmp;
400         }
401
402         /* Select a print function. */
403         if (f_singlecol)
404                 printfcn = printscol;
405         else if (f_longform)
406                 printfcn = printlong;
407         else if (f_stream)
408                 printfcn = printstream;
409         else
410                 printfcn = printcol;
411
412         if (argc)
413                 traverse(argc, argv, fts_options);
414         else
415                 traverse(1, dotav, fts_options);
416         exit(rval);
417 }
418
419 static int output;              /* If anything output. */
420
421 /*
422  * Traverse() walks the logical directory structure specified by the argv list
423  * in the order specified by the mastercmp() comparison function.  During the
424  * traversal it passes linked lists of structures to display() which represent
425  * a superset (may be exact set) of the files to be displayed.
426  */
427 static void
428 traverse(int argc, char *argv[], int options)
429 {
430         FTS *ftsp;
431         FTSENT *p, *chp;
432         int ch_options;
433
434         if ((ftsp =
435             fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
436                 err(1, NULL);
437
438         display(NULL, fts_children(ftsp, 0));
439         if (f_listdir)
440                 return;
441
442         /*
443          * If not recursing down this tree and don't need stat info, just get
444          * the names.
445          */
446         ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0;
447
448         while ((p = fts_read(ftsp)) != NULL)
449                 switch (p->fts_info) {
450                 case FTS_DC:
451                         warnx("%s: directory causes a cycle", p->fts_name);
452                         break;
453                 case FTS_DNR:
454                 case FTS_ERR:
455                         warnx("%s: %s", p->fts_name, strerror(p->fts_errno));
456                         rval = 1;
457                         break;
458                 case FTS_D:
459                         if (p->fts_level != FTS_ROOTLEVEL &&
460                             p->fts_name[0] == '.' && !f_listdot)
461                                 break;
462
463                         /*
464                          * If already output something, put out a newline as
465                          * a separator.  If multiple arguments, precede each
466                          * directory with its name.
467                          */
468                         if (output) {
469                                 putchar('\n');
470                                 printname(p->fts_path);
471                                 puts(":");
472                         } else if (argc > 1) {
473                                 printname(p->fts_path);
474                                 puts(":");
475                                 output = 1;
476                         }
477                         chp = fts_children(ftsp, ch_options);
478                         display(p, chp);
479
480                         if (!f_recursive && chp != NULL)
481                                 (void)fts_set(ftsp, p, FTS_SKIP);
482                         break;
483                 default:
484                         break;
485                 }
486         if (errno)
487                 err(1, "fts_read");
488 }
489
490 /*
491  * Display() takes a linked list of FTSENT structures and passes the list
492  * along with any other necessary information to the print function.  P
493  * points to the parent directory of the display list.
494  */
495 static void
496 display(FTSENT *p, FTSENT *list)
497 {
498         struct stat *sp;
499         DISPLAY d;
500         FTSENT *cur;
501         NAMES *np;
502         off_t maxsize;
503         u_long btotal, maxblock, maxinode, maxlen, maxnlink;
504         int bcfile, maxflags;
505         gid_t maxgroup;
506         uid_t maxuser;
507         size_t flen, ulen, glen;
508         char *initmax;
509         int entries, needstats;
510         const char *user, *group;
511         char *flags;
512         char buf[STRBUF_SIZEOF(u_quad_t) + 1];
513         char ngroup[STRBUF_SIZEOF(uid_t) + 1];
514         char nuser[STRBUF_SIZEOF(gid_t) + 1];
515
516         /*
517          * If list is NULL there are two possibilities: that the parent
518          * directory p has no children, or that fts_children() returned an
519          * error.  We ignore the error case since it will be replicated
520          * on the next call to fts_read() on the post-order visit to the
521          * directory p, and will be signaled in traverse().
522          */
523         if (list == NULL)
524                 return;
525
526         needstats = f_inode || f_longform || f_size;
527         flen = 0;
528         btotal = 0;
529         initmax = getenv("LS_COLWIDTHS");
530         /* Fields match -lios order.  New ones should be added at the end. */
531         maxblock = maxinode = maxlen = maxnlink =
532             maxuser = maxgroup = maxflags = maxsize = 0;
533         if (initmax != NULL && *initmax != '\0') {
534                 char *initmax2, *jinitmax;
535                 int ninitmax;
536
537                 /* Fill-in "::" as "0:0:0" for the sake of scanf. */
538                 jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2);
539                 if (jinitmax == NULL)
540                         err(1, NULL);
541                 if (*initmax == ':')
542                         strcpy(initmax2, "0:"), initmax2 += 2;
543                 else
544                         *initmax2++ = *initmax, *initmax2 = '\0';
545                 for (initmax++; *initmax != '\0'; initmax++) {
546                         if (initmax[-1] == ':' && initmax[0] == ':') {
547                                 *initmax2++ = '0';
548                                 *initmax2++ = initmax[0];
549                                 initmax2[1] = '\0';
550                         } else {
551                                 *initmax2++ = initmax[0];
552                                 initmax2[1] = '\0';
553                         }
554                 }
555                 if (initmax2[-1] == ':')
556                         strcpy(initmax2, "0");
557
558                 ninitmax = sscanf(jinitmax,
559                     " %lu : %lu : %lu : %i : %i : %i : %llu : %lu ",
560                     &maxinode, &maxblock, &maxnlink, &maxuser,
561                     &maxgroup, &maxflags, &maxsize, &maxlen);
562                 f_notabs = 1;
563                 switch (ninitmax) {
564                 case 0:
565                         maxinode = 0;
566                         /* fall through */
567                 case 1:
568                         maxblock = 0;
569                         /* fall through */
570                 case 2:
571                         maxnlink = 0;
572                         /* fall through */
573                 case 3:
574                         maxuser = 0;
575                         /* fall through */
576                 case 4:
577                         maxgroup = 0;
578                         /* fall through */
579                 case 5:
580                         maxflags = 0;
581                         /* fall through */
582                 case 6:
583                         maxsize = 0;
584                         /* fall through */
585                 case 7:
586                         maxlen = 0;
587                         /* fall through */
588 #ifdef COLORLS
589                         if (!f_color)
590 #endif
591                                 f_notabs = 0;
592                         /* fall through */
593                 default:
594                         break;
595                 }
596                 maxinode = makenines(maxinode);
597                 maxblock = makenines(maxblock);
598                 maxnlink = makenines(maxnlink);
599                 maxsize = makenines(maxsize);
600         }
601         bcfile = 0;
602         flags = NULL;
603         for (cur = list, entries = 0; cur; cur = cur->fts_link) {
604                 if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) {
605                         warnx("%s: %s",
606                             cur->fts_name, strerror(cur->fts_errno));
607                         cur->fts_number = NO_PRINT;
608                         rval = 1;
609                         continue;
610                 }
611                 /*
612                  * P is NULL if list is the argv list, to which different rules
613                  * apply.
614                  */
615                 if (p == NULL) {
616                         /* Directories will be displayed later. */
617                         if (cur->fts_info == FTS_D && !f_listdir) {
618                                 cur->fts_number = NO_PRINT;
619                                 continue;
620                         }
621                 } else {
622                         /* Only display dot file if -a/-A set. */
623                         if (cur->fts_name[0] == '.' && !f_listdot) {
624                                 cur->fts_number = NO_PRINT;
625                                 continue;
626                         }
627                 }
628                 if (cur->fts_namelen > maxlen)
629                         maxlen = cur->fts_namelen;
630                 if (f_octal || f_octal_escape) {
631                         u_long t = len_octal(cur->fts_name, cur->fts_namelen);
632
633                         if (t > maxlen)
634                                 maxlen = t;
635                 }
636                 if (needstats) {
637                         sp = cur->fts_statp;
638                         if (sp->st_blocks > maxblock)
639                                 maxblock = sp->st_blocks;
640                         if (sp->st_ino > maxinode)
641                                 maxinode = sp->st_ino;
642                         if (sp->st_nlink > maxnlink)
643                                 maxnlink = sp->st_nlink;
644                         if (sp->st_size > maxsize)
645                                 maxsize = sp->st_size;
646
647                         btotal += sp->st_blocks;
648                         if (f_longform) {
649                                 if (f_numericonly) {
650                                         (void)snprintf(nuser, sizeof(nuser),
651                                             "%u", sp->st_uid);
652                                         (void)snprintf(ngroup, sizeof(ngroup),
653                                             "%u", sp->st_gid);
654                                         user = nuser;
655                                         group = ngroup;
656                                 } else {
657                                         user = user_from_uid(sp->st_uid, 0);
658                                         group = group_from_gid(sp->st_gid, 0);
659                                 }
660                                 if ((ulen = strlen(user)) > maxuser)
661                                         maxuser = ulen;
662                                 if ((glen = strlen(group)) > maxgroup)
663                                         maxgroup = glen;
664                                 if (f_flags) {
665                                         flags = fflagstostr(sp->st_flags);
666                                         if (flags != NULL && *flags == '\0') {
667                                                 free(flags);
668                                                 flags = strdup("-");
669                                         }
670                                         if (flags == NULL)
671                                                 err(1, NULL);
672                                         flen = strlen(flags);
673                                         if (flen > (size_t)maxflags)
674                                                 maxflags = flen;
675                                 } else
676                                         flen = 0;
677
678                                 if ((np = malloc(sizeof(NAMES) +
679                                     ulen + glen + flen + 3)) == NULL)
680                                         err(1, NULL);
681
682                                 np->user = &np->data[0];
683                                 (void)strcpy(np->user, user);
684                                 np->group = &np->data[ulen + 1];
685                                 (void)strcpy(np->group, group);
686
687                                 if (S_ISCHR(sp->st_mode) ||
688                                     S_ISBLK(sp->st_mode))
689                                         bcfile = 1;
690
691                                 if (f_flags) {
692                                         np->flags = &np->data[ulen + glen + 2];
693                                         (void)strcpy(np->flags, flags);
694                                         free(flags);
695                                 }
696                                 cur->fts_pointer = np;
697                         }
698                 }
699                 ++entries;
700         }
701
702         if (!entries)
703                 return;
704
705         d.list = list;
706         d.entries = entries;
707         d.maxlen = maxlen;
708         if (needstats) {
709                 d.bcfile = bcfile;
710                 d.btotal = btotal;
711                 (void)snprintf(buf, sizeof(buf), "%lu", maxblock);
712                 d.s_block = strlen(buf);
713                 d.s_flags = maxflags;
714                 d.s_group = maxgroup;
715                 (void)snprintf(buf, sizeof(buf), "%lu", maxinode);
716                 d.s_inode = strlen(buf);
717                 (void)snprintf(buf, sizeof(buf), "%lu", maxnlink);
718                 d.s_nlink = strlen(buf);
719                 (void)snprintf(buf, sizeof(buf), "%qu", maxsize);
720                 d.s_size = strlen(buf);
721                 d.s_user = maxuser;
722         }
723         printfcn(&d);
724         output = 1;
725
726         if (f_longform)
727                 for (cur = list; cur; cur = cur->fts_link)
728                         free(cur->fts_pointer);
729 }
730
731 /*
732  * Ordering for mastercmp:
733  * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories
734  * as larger than directories.  Within either group, use the sort function.
735  * All other levels use the sort function.  Error entries remain unsorted.
736  */
737 static int
738 mastercmp(const FTSENT **a, const FTSENT **b)
739 {
740         int a_info, b_info;
741
742         a_info = (*a)->fts_info;
743         if (a_info == FTS_ERR)
744                 return (0);
745         b_info = (*b)->fts_info;
746         if (b_info == FTS_ERR)
747                 return (0);
748
749         if (a_info == FTS_NS || b_info == FTS_NS)
750                 return (namecmp(*a, *b));
751
752         if (a_info != b_info &&
753             (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) {
754                 if (a_info == FTS_D)
755                         return (1);
756                 if (b_info == FTS_D)
757                         return (-1);
758         }
759         return (sortfcn(*a, *b));
760 }
761
762 /*
763  * Makenines() returns (10**n)-1.  This is useful for converting a width
764  * into a number that wide in decimal.
765  */
766 static u_quad_t
767 makenines(u_long n)
768 {
769         u_long i;
770         u_quad_t reg;
771
772         reg = 1;
773         /* Use a loop instead of pow(), since all values of n are small. */
774         for (i = 0; i < n; i++)
775                 reg *= 10;
776         reg--;
777
778         return reg;
779 }