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