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