ls.c revision 2889
1155408Srwatson/*
2155408Srwatson * Copyright (c) 1989, 1993, 1994
3184488Srwatson *	The Regents of the University of California.  All rights reserved.
4155408Srwatson *
5155408Srwatson * This code is derived from software contributed to Berkeley by
6155408Srwatson * Michael Fischbein.
7155408Srwatson *
8155408Srwatson * Redistribution and use in source and binary forms, with or without
9155408Srwatson * modification, are permitted provided that the following conditions
10155408Srwatson * are met:
11155408Srwatson * 1. Redistributions of source code must retain the above copyright
12155408Srwatson *    notice, this list of conditions and the following disclaimer.
13155408Srwatson * 2. Redistributions in binary form must reproduce the above copyright
14155408Srwatson *    notice, this list of conditions and the following disclaimer in the
15155408Srwatson *    documentation and/or other materials provided with the distribution.
16155408Srwatson * 3. All advertising materials mentioning features or use of this software
17155408Srwatson *    must display the following acknowledgement:
18155408Srwatson *	This product includes software developed by the University of
19155408Srwatson *	California, Berkeley and its contributors.
20155408Srwatson * 4. Neither the name of the University nor the names of its contributors
21155408Srwatson *    may be used to endorse or promote products derived from this software
22155408Srwatson *    without specific prior written permission.
23155408Srwatson *
24155408Srwatson * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25155408Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26155408Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27155408Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28155408Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29155408Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30178186Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31178186Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32178186Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33155408Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34155408Srwatson * SUCH DAMAGE.
35155408Srwatson */
36155408Srwatson
37155408Srwatson#ifndef lint
38155408Srwatsonstatic char copyright[] =
39155408Srwatson"@(#) Copyright (c) 1989, 1993, 1994\n\
40155408Srwatson	The Regents of the University of California.  All rights reserved.\n";
41155408Srwatson#endif /* not lint */
42155408Srwatson
43155408Srwatson#ifndef lint
44155408Srwatsonstatic char sccsid[] = "@(#)ls.c	8.5 (Berkeley) 4/2/94";
45184488Srwatson#endif /* not lint */
46155408Srwatson
47155408Srwatson#include <sys/types.h>
48155408Srwatson#include <sys/stat.h>
49155408Srwatson#include <sys/ioctl.h>
50184508Srwatson
51155408Srwatson#include <dirent.h>
52155408Srwatson#include <err.h>
53155408Srwatson#include <errno.h>
54155408Srwatson#include <fts.h>
55156880Srwatson#include <stdio.h>
56155408Srwatson#include <stdlib.h>
57155408Srwatson#include <string.h>
58155408Srwatson#include <unistd.h>
59155408Srwatson
60155408Srwatson#include "ls.h"
61155408Srwatson#include "extern.h"
62159269Srwatson
63159269Srwatsonstatic void	 display __P((FTSENT *, FTSENT *));
64155408Srwatsonstatic int	 mastercmp __P((const FTSENT **, const FTSENT **));
65155408Srwatsonstatic void	 traverse __P((int, char **, int));
66155408Srwatson
67155408Srwatsonstatic void (*printfcn) __P((DISPLAY *));
68155408Srwatsonstatic int (*sortfcn) __P((const FTSENT *, const FTSENT *));
69155408Srwatson
70155408Srwatsonlong blocksize;			/* block size units */
71155408Srwatsonint termwidth = 80;		/* default terminal width */
72174894Swkoszek
73159269Srwatson/* flags */
74155408Srwatsonint f_accesstime;		/* use time of last access */
75155408Srwatsonint f_column;			/* columnated format */
76155408Srwatsonint f_flags;			/* show flags associated with a file */
77155408Srwatsonint f_inode;			/* print inode */
78156883Srwatsonint f_kblocks;			/* print size in kilobytes */
79156880Srwatsonint f_listdir;			/* list actual directory, not contents */
80155408Srwatsonint f_listdot;			/* list files beginning with . */
81155408Srwatsonint f_longform;			/* long listing format */
82155408Srwatsonint f_newline;			/* if precede with newline */
83155408Srwatsonint f_nonprint;			/* show unprintables as ? */
84155408Srwatsonint f_nosort;			/* don't sort output */
85155408Srwatsonint f_recursive;		/* ls subdirectories also */
86155408Srwatsonint f_reversesort;		/* reverse whatever sort is used */
87155408Srwatsonint f_sectime;			/* print the real time for all files */
88155408Srwatsonint f_singlecol;		/* use single column output */
89155408Srwatsonint f_size;			/* list size in short listing */
90155408Srwatsonint f_statustime;		/* use time of last mode change */
91155408Srwatsonint f_dirname;			/* if precede with directory name */
92159269Srwatsonint f_timesort;			/* sort by time vice name */
93159269Srwatsonint f_type;			/* add type character for non-regular files */
94159269Srwatson
95159269Srwatsonint
96159269Srwatsonmain(argc, argv)
97159269Srwatson	int argc;
98159269Srwatson	char *argv[];
99159269Srwatson{
100159269Srwatson	static char dot[] = ".", *dotav[] = { dot, NULL };
101159269Srwatson	struct winsize win;
102159269Srwatson	int ch, fts_options, notused;
103159269Srwatson	char *p;
104159269Srwatson
105159269Srwatson	/* Terminal defaults to -Cq, non-terminal defaults to -1. */
106159269Srwatson	if (isatty(STDOUT_FILENO)) {
107159269Srwatson		if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == -1 ||
108159269Srwatson		    !win.ws_col) {
109155408Srwatson			if ((p = getenv("COLUMNS")) != NULL)
110155408Srwatson				termwidth = atoi(p);
111155408Srwatson		}
112155408Srwatson		else
113155408Srwatson			termwidth = win.ws_col;
114155408Srwatson		f_column = f_nonprint = 1;
115155408Srwatson	} else
116155408Srwatson		f_singlecol = 1;
117155408Srwatson
118155408Srwatson	/* Root is -A automatically. */
119155408Srwatson	if (!getuid())
120155408Srwatson		f_listdot = 1;
121184488Srwatson
122184488Srwatson	fts_options = FTS_PHYSICAL;
123184488Srwatson	while ((ch = getopt(argc, argv, "1ACFLRTacdfgikloqrstu")) != EOF) {
124184508Srwatson		switch (ch) {
125184488Srwatson		/*
126184488Srwatson		 * The -1, -C and -l options all override each other so shell
127184508Srwatson		 * aliasing works right.
128184508Srwatson		 */
129184508Srwatson		case '1':
130184508Srwatson			f_singlecol = 1;
131184508Srwatson			f_column = f_longform = 0;
132184508Srwatson			break;
133184508Srwatson		case 'C':
134184508Srwatson			f_column = 1;
135184488Srwatson			f_longform = f_singlecol = 0;
136184488Srwatson			break;
137184488Srwatson		case 'l':
138184488Srwatson			f_longform = 1;
139184488Srwatson			f_column = f_singlecol = 0;
140184536Srwatson			break;
141184536Srwatson		/* The -c and -u options override each other. */
142184536Srwatson		case 'c':
143184536Srwatson			f_statustime = 1;
144184536Srwatson			f_accesstime = 0;
145184536Srwatson			break;
146184536Srwatson		case 'u':
147155408Srwatson			f_accesstime = 1;
148155408Srwatson			f_statustime = 0;
149184536Srwatson			break;
150184536Srwatson		case 'F':
151155408Srwatson			f_type = 1;
152184540Srwatson			break;
153184540Srwatson		case 'L':
154184540Srwatson			fts_options &= ~FTS_PHYSICAL;
155155408Srwatson			fts_options |= FTS_LOGICAL;
156155408Srwatson			break;
157155408Srwatson		case 'R':
158155408Srwatson			f_recursive = 1;
159159269Srwatson			break;
160159269Srwatson		case 'a':
161159269Srwatson			fts_options |= FTS_SEEDOT;
162159269Srwatson			/* FALLTHROUGH */
163159269Srwatson		case 'A':
164159269Srwatson			f_listdot = 1;
165159269Srwatson			break;
166159269Srwatson		/* The -d option turns off the -R option. */
167159269Srwatson		case 'd':
168159269Srwatson			f_listdir = 1;
169159269Srwatson			f_recursive = 0;
170184508Srwatson			break;
171184508Srwatson		case 'f':
172184508Srwatson			f_nosort = 1;
173159269Srwatson			break;
174155408Srwatson		case 'g':		/* Compatibility with 4.3BSD. */
175155408Srwatson			break;
176159269Srwatson		case 'i':
177159269Srwatson			f_inode = 1;
178159269Srwatson			break;
179155408Srwatson		case 'k':
180155408Srwatson			f_kblocks = 1;
181155408Srwatson			break;
182184508Srwatson		case 'o':
183184508Srwatson			f_flags = 1;
184184508Srwatson			break;
185184508Srwatson		case 'q':
186184508Srwatson			f_nonprint = 1;
187184508Srwatson			break;
188184508Srwatson		case 'r':
189184488Srwatson			f_reversesort = 1;
190184508Srwatson			break;
191184508Srwatson		case 's':
192184508Srwatson			f_size = 1;
193184508Srwatson			break;
194184508Srwatson		case 'T':
195184508Srwatson			f_sectime = 1;
196155408Srwatson			break;
197184488Srwatson		case 't':
198184488Srwatson			f_timesort = 1;
199184488Srwatson			break;
200184488Srwatson		default:
201155408Srwatson		case '?':
202155408Srwatson			usage();
203184488Srwatson		}
204155408Srwatson	}
205184488Srwatson	argc -= optind;
206184488Srwatson	argv += optind;
207184488Srwatson
208184488Srwatson	/*
209184488Srwatson	 * If not -F, -i, -l, -s or -t options, don't require stat
210184488Srwatson	 * information.
211184488Srwatson	 */
212184488Srwatson	if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type)
213155408Srwatson		fts_options |= FTS_NOSTAT;
214155408Srwatson
215155408Srwatson	/*
216155408Srwatson	 * If not -F, -d or -l options, follow any symbolic links listed on
217155408Srwatson	 * the command line.
218155408Srwatson	 */
219155408Srwatson	if (!f_longform && !f_listdir && !f_type)
220155408Srwatson		fts_options |= FTS_COMFOLLOW;
221155408Srwatson
222155408Srwatson	/* If -l or -s, figure out block size. */
223155408Srwatson	if (f_longform || f_size) {
224155408Srwatson		(void)getbsize(&notused, &blocksize);
225155408Srwatson		    blocksize /= 512;
226155408Srwatson		if (f_kblocks)
227155408Srwatson		    blocksize *= 2;
228155408Srwatson	}
229161582Srwatson
230155408Srwatson	/* Select a sort function. */
231155408Srwatson	if (f_reversesort) {
232155408Srwatson		if (!f_timesort)
233179726Sed			sortfcn = revnamecmp;
234155408Srwatson		else if (f_accesstime)
235155408Srwatson			sortfcn = revacccmp;
236155408Srwatson		else if (f_statustime)
237155408Srwatson			sortfcn = revstatcmp;
238155408Srwatson		else /* Use modification time. */
239161582Srwatson			sortfcn = revmodcmp;
240155408Srwatson	} else {
241155408Srwatson		if (!f_timesort)
242155408Srwatson			sortfcn = namecmp;
243161582Srwatson		else if (f_accesstime)
244161582Srwatson			sortfcn = acccmp;
245161582Srwatson		else if (f_statustime)
246161582Srwatson			sortfcn = statcmp;
247161582Srwatson		else /* Use modification time. */
248161582Srwatson			sortfcn = modcmp;
249161582Srwatson	}
250161582Srwatson
251161582Srwatson	/* Select a print function. */
252161582Srwatson	if (f_singlecol)
253155408Srwatson		printfcn = printscol;
254155408Srwatson	else if (f_longform)
255155408Srwatson		printfcn = printlong;
256155408Srwatson	else
257155408Srwatson		printfcn = printcol;
258155408Srwatson
259155408Srwatson	if (argc)
260155408Srwatson		traverse(argc, argv, fts_options);
261155408Srwatson	else
262155408Srwatson		traverse(1, dotav, fts_options);
263155408Srwatson	exit(0);
264155408Srwatson}
265155408Srwatson
266155408Srwatsonstatic int output;			/* If anything output. */
267155408Srwatson
268155408Srwatson/*
269155408Srwatson * Traverse() walks the logical directory structure specified by the argv list
270155408Srwatson * in the order specified by the mastercmp() comparison function.  During the
271155408Srwatson * traversal it passes linked lists of structures to display() which represent
272155408Srwatson * a superset (may be exact set) of the files to be displayed.
273159269Srwatson */
274159269Srwatsonstatic void
275159269Srwatsontraverse(argc, argv, options)
276159269Srwatson	int argc, options;
277159269Srwatson	char *argv[];
278159269Srwatson{
279159269Srwatson	FTS *ftsp;
280184488Srwatson	FTSENT *p, *chp;
281159269Srwatson	int ch_options;
282159269Srwatson
283159269Srwatson	if ((ftsp =
284159269Srwatson	    fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
285159269Srwatson		err(1, NULL);
286159269Srwatson
287159269Srwatson	display(NULL, fts_children(ftsp, 0));
288159269Srwatson	if (f_listdir)
289159269Srwatson		return;
290159269Srwatson
291159269Srwatson	/*
292159269Srwatson	 * If not recursing down this tree and don't need stat info, just get
293159269Srwatson	 * the names.
294159269Srwatson	 */
295159269Srwatson	ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0;
296159269Srwatson
297159269Srwatson	while ((p = fts_read(ftsp)) != NULL)
298159269Srwatson		switch (p->fts_info) {
299184488Srwatson		case FTS_DC:
300159269Srwatson			warnx("%s: directory causes a cycle", p->fts_name);
301159269Srwatson			break;
302159269Srwatson		case FTS_DNR:
303159269Srwatson		case FTS_ERR:
304159269Srwatson			warnx("%s: %s", p->fts_name, strerror(p->fts_errno));
305159269Srwatson			break;
306184488Srwatson		case FTS_D:
307159269Srwatson			if (p->fts_level != FTS_ROOTLEVEL &&
308159269Srwatson			    p->fts_name[0] == '.' && !f_listdot)
309159269Srwatson				break;
310159269Srwatson
311159269Srwatson			/*
312159269Srwatson			 * If already output something, put out a newline as
313159269Srwatson			 * a separator.  If multiple arguments, precede each
314159269Srwatson			 * directory with its name.
315159269Srwatson			 */
316159269Srwatson			if (output)
317159269Srwatson				(void)printf("\n%s:\n", p->fts_path);
318159269Srwatson			else if (argc > 1) {
319159269Srwatson				(void)printf("%s:\n", p->fts_path);
320159269Srwatson				output = 1;
321159269Srwatson			}
322159269Srwatson
323159269Srwatson			chp = fts_children(ftsp, ch_options);
324184488Srwatson			display(p, chp);
325159269Srwatson
326159269Srwatson			if (!f_recursive && chp != NULL)
327159269Srwatson				(void)fts_set(ftsp, p, FTS_SKIP);
328159269Srwatson			break;
329159269Srwatson		}
330159269Srwatson	if (errno)
331159269Srwatson		err(1, "fts_read");
332159269Srwatson}
333184488Srwatson
334159269Srwatson/*
335159269Srwatson * Display() takes a linked list of FTSENT structures and passes the list
336159269Srwatson * along with any other necessary information to the print function.  P
337159269Srwatson * points to the parent directory of the display list.
338159269Srwatson */
339159269Srwatsonstatic void
340159269Srwatsondisplay(p, list)
341159269Srwatson	FTSENT *p, *list;
342159269Srwatson{
343159269Srwatson	struct stat *sp;
344159269Srwatson	DISPLAY d;
345159269Srwatson	FTSENT *cur;
346159269Srwatson	NAMES *np;
347184488Srwatson	u_quad_t maxsize;
348159269Srwatson	u_long btotal, maxblock, maxinode, maxlen, maxnlink;
349159269Srwatson	int bcfile, flen, glen, ulen, maxflags, maxgroup, maxuser;
350159269Srwatson	int entries, needstats;
351159269Srwatson	char *user, *group, *flags, buf[20];	/* 32 bits == 10 digits */
352159269Srwatson
353159269Srwatson	/*
354184488Srwatson	 * If list is NULL there are two possibilities: that the parent
355159269Srwatson	 * directory p has no children, or that fts_children() returned an
356159269Srwatson	 * error.  We ignore the error case since it will be replicated
357159269Srwatson	 * on the next call to fts_read() on the post-order visit to the
358159269Srwatson	 * directory p, and will be signalled in traverse().
359159269Srwatson	 */
360159269Srwatson	if (list == NULL)
361159269Srwatson		return;
362159269Srwatson
363159269Srwatson	needstats = f_inode || f_longform || f_size;
364159269Srwatson	flen = 0;
365159269Srwatson	btotal = maxblock = maxinode = maxlen = maxnlink = 0;
366159269Srwatson	bcfile = 0;
367159269Srwatson	maxuser = maxgroup = maxflags = 0;
368184488Srwatson	maxsize = 0;
369159269Srwatson	for (cur = list, entries = 0; cur; cur = cur->fts_link) {
370159269Srwatson		if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) {
371159269Srwatson			warnx("%s: %s",
372159269Srwatson			    cur->fts_name, strerror(cur->fts_errno));
373159269Srwatson			cur->fts_number = NO_PRINT;
374159269Srwatson			continue;
375159269Srwatson		}
376159269Srwatson
377159269Srwatson		/*
378159269Srwatson		 * P is NULL if list is the argv list, to which different rules
379159269Srwatson		 * apply.
380184488Srwatson		 */
381159269Srwatson		if (p == NULL) {
382184488Srwatson			/* Directories will be displayed later. */
383159269Srwatson			if (cur->fts_info == FTS_D && !f_listdir) {
384159269Srwatson				cur->fts_number = NO_PRINT;
385170196Srwatson				continue;
386159269Srwatson			}
387159269Srwatson		} else {
388159269Srwatson			/* Only display dot file if -a/-A set. */
389159269Srwatson			if (cur->fts_name[0] == '.' && !f_listdot) {
390159269Srwatson				cur->fts_number = NO_PRINT;
391159269Srwatson				continue;
392159269Srwatson			}
393159269Srwatson		}
394159269Srwatson		if (f_nonprint)
395159269Srwatson			prcopy(cur->fts_name, cur->fts_name, cur->fts_namelen);
396159269Srwatson		if (cur->fts_namelen > maxlen)
397159269Srwatson			maxlen = cur->fts_namelen;
398159269Srwatson		if (needstats) {
399159269Srwatson			sp = cur->fts_statp;
400159269Srwatson			if (sp->st_blocks > maxblock)
401184488Srwatson				maxblock = sp->st_blocks;
402159269Srwatson			if (sp->st_ino > maxinode)
403159269Srwatson				maxinode = sp->st_ino;
404159269Srwatson			if (sp->st_nlink > maxnlink)
405159269Srwatson				maxnlink = sp->st_nlink;
406159269Srwatson			if (sp->st_size > maxsize)
407159269Srwatson				maxsize = sp->st_size;
408159269Srwatson
409159269Srwatson			btotal += sp->st_blocks;
410159269Srwatson			if (f_longform) {
411159269Srwatson				user = user_from_uid(sp->st_uid, 0);
412159269Srwatson				if ((ulen = strlen(user)) > maxuser)
413159269Srwatson					maxuser = ulen;
414159269Srwatson				group = group_from_gid(sp->st_gid, 0);
415159269Srwatson				if ((glen = strlen(group)) > maxgroup)
416159269Srwatson					maxgroup = glen;
417159269Srwatson				if (f_flags) {
418159269Srwatson					flags =
419159269Srwatson					    flags_to_string(sp->st_flags, "-");
420159269Srwatson					if ((flen = strlen(flags)) > maxflags)
421159269Srwatson						maxflags = flen;
422159269Srwatson				} else
423159269Srwatson					flen = 0;
424159269Srwatson
425159269Srwatson				if ((np = malloc(sizeof(NAMES) +
426159269Srwatson				    ulen + glen + flen + 3)) == NULL)
427159269Srwatson					err(1, NULL);
428159269Srwatson
429159269Srwatson				np->user = &np->data[0];
430159269Srwatson				(void)strcpy(np->user, user);
431159269Srwatson				np->group = &np->data[ulen + 1];
432159269Srwatson				(void)strcpy(np->group, group);
433159269Srwatson
434159269Srwatson				if (S_ISCHR(sp->st_mode) ||
435159269Srwatson				    S_ISBLK(sp->st_mode))
436159269Srwatson					bcfile = 1;
437159269Srwatson
438184488Srwatson				if (f_flags) {
439159269Srwatson					np->flags = &np->data[ulen + glen + 2];
440184488Srwatson				  	(void)strcpy(np->flags, flags);
441159269Srwatson				}
442159269Srwatson				cur->fts_pointer = np;
443184488Srwatson			}
444184488Srwatson		}
445159269Srwatson		++entries;
446159269Srwatson	}
447184488Srwatson
448159269Srwatson	if (!entries)
449184488Srwatson		return;
450159269Srwatson
451159269Srwatson	d.list = list;
452159269Srwatson	d.entries = entries;
453159269Srwatson	d.maxlen = maxlen;
454159269Srwatson	if (needstats) {
455184489Srwatson		d.bcfile = bcfile;
456184489Srwatson		d.btotal = btotal;
457155408Srwatson		(void)snprintf(buf, sizeof(buf), "%lu", maxblock);
458155408Srwatson		d.s_block = strlen(buf);
459155408Srwatson		d.s_flags = maxflags;
460155408Srwatson		d.s_group = maxgroup;
461184489Srwatson		(void)snprintf(buf, sizeof(buf), "%lu", maxinode);
462155408Srwatson		d.s_inode = strlen(buf);
463184488Srwatson		(void)snprintf(buf, sizeof(buf), "%lu", maxnlink);
464155408Srwatson		d.s_nlink = strlen(buf);
465184489Srwatson		(void)snprintf(buf, sizeof(buf), "%qu", maxsize);
466184489Srwatson		d.s_size = strlen(buf);
467184489Srwatson		d.s_user = maxuser;
468184489Srwatson	}
469184489Srwatson
470184489Srwatson	printfcn(&d);
471155408Srwatson	output = 1;
472155408Srwatson
473155408Srwatson	if (f_longform)
474156292Srwatson		for (cur = list; cur; cur = cur->fts_link)
475155408Srwatson			free(cur->fts_pointer);
476155408Srwatson}
477155408Srwatson
478155408Srwatson/*
479155408Srwatson * Ordering for mastercmp:
480155408Srwatson * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories
481155408Srwatson * as larger than directories.  Within either group, use the sort function.
482155408Srwatson * All other levels use the sort function.  Error entries remain unsorted.
483155408Srwatson */
484155408Srwatsonstatic int
485155408Srwatsonmastercmp(a, b)
486155408Srwatson	const FTSENT **a, **b;
487155408Srwatson{
488155408Srwatson	int a_info, b_info;
489155408Srwatson
490155408Srwatson	a_info = (*a)->fts_info;
491155408Srwatson	if (a_info == FTS_ERR)
492184536Srwatson		return (0);
493155408Srwatson	b_info = (*b)->fts_info;
494161582Srwatson	if (b_info == FTS_ERR)
495155408Srwatson		return (0);
496155408Srwatson
497184488Srwatson	if (a_info == FTS_NS || b_info == FTS_NS)
498155408Srwatson		return (namecmp(*a, *b));
499155408Srwatson
500155408Srwatson	if (a_info == b_info)
501155408Srwatson		return (sortfcn(*a, *b));
502155408Srwatson
503155408Srwatson	if ((*a)->fts_level == FTS_ROOTLEVEL)
504155408Srwatson		if (a_info == FTS_D)
505159269Srwatson			return (1);
506159269Srwatson		else if (b_info == FTS_D)
507155408Srwatson			return (-1);
508155408Srwatson		else
509155408Srwatson			return (sortfcn(*a, *b));
510155408Srwatson	else
511184488Srwatson		return (sortfcn(*a, *b));
512155408Srwatson}
513155408Srwatson