xref: /dragonfly/bin/ls/ls.c (revision 3d201fd0)
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 }
798