blob: db42601e231ba81de82a4052febdb1f3d5501942 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersen9d3aba71999-10-06 09:04:55 +00002/*
Eric Andersencc8ed391999-10-05 16:24:54 +00003 * tiny-ls.c version 0.1.0: A minimalist 'ls'
4 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
Eric Andersen11c65522000-09-07 17:24:47 +00005 *
Bernhard Reutner-Fischercb448162006-04-12 07:35:12 +00006 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Eric Andersencc8ed391999-10-05 16:24:54 +00007 */
8
Denis Vlasenko5e4fda02009-03-08 23:46:48 +00009/* [date unknown. Perhaps before year 2000]
Eric Andersencc8ed391999-10-05 16:24:54 +000010 * To achieve a small memory footprint, this version of 'ls' doesn't do any
11 * file sorting, and only has the most essential command line switches
Eric Andersen77d92682001-05-23 20:32:09 +000012 * (i.e., the ones I couldn't live without :-) All features which involve
Eric Andersencc8ed391999-10-05 16:24:54 +000013 * linking in substantial chunks of libc can be disabled.
14 *
15 * Although I don't really want to add new features to this program to
16 * keep it small, I *am* interested to receive bug fixes and ways to make
17 * it more portable.
18 *
19 * KNOWN BUGS:
Erik Andersen9ffdaa62000-02-11 21:55:04 +000020 * 1. ls -l of a directory doesn't give "total <blocks>" header
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000021 * 2. hidden files can make column width too large
Erik Andersen9ffdaa62000-02-11 21:55:04 +000022 *
Eric Andersencc8ed391999-10-05 16:24:54 +000023 * NON-OPTIMAL BEHAVIOUR:
24 * 1. autowidth reads directories twice
25 * 2. if you do a short directory listing without filetype characters
26 * appended, there's no need to stat each one
27 * PORTABILITY:
28 * 1. requires lstat (BSD) - how do you do it without?
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000029 *
30 * [2009-03]
31 * ls sorts listing now, and supports almost all options.
Eric Andersencc8ed391999-10-05 16:24:54 +000032 */
33
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000034#include "libbb.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +000035
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +000036#if ENABLE_FEATURE_ASSUME_UNICODE
37#include <wchar.h>
38#endif
39
Denis Vlasenko99912ca2007-04-10 15:43:37 +000040/* This is a NOEXEC applet. Be very careful! */
41
Eric Andersenf1142c52001-02-20 06:16:29 +000042
Denis Vlasenko245f91b2009-03-09 22:37:23 +000043#if ENABLE_FTPD
44/* ftpd uses ls, and without timestamps Mozilla won't understand
45 * ftpd's LIST output.
46 */
47# undef CONFIG_FEATURE_LS_TIMESTAMPS
48# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000049# undef IF_FEATURE_LS_TIMESTAMPS
50# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +000051# define CONFIG_FEATURE_LS_TIMESTAMPS 1
52# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000053# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
54# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +000055#endif
56
57
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000058enum {
Manuel Novoa III cad53642003-03-19 09:13:01 +000059
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000060TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
61COLUMN_GAP = 2, /* includes the file type char */
62
63/* what is the overall style of the listing */
64STYLE_COLUMNS = 1 << 21, /* fill columns */
65STYLE_LONG = 2 << 21, /* one record per line, extended info */
66STYLE_SINGLE = 3 << 21, /* one record per line */
67STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +000068
Eric Andersen11c65522000-09-07 17:24:47 +000069/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
70/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000071LIST_INO = 1 << 0,
72LIST_BLOCKS = 1 << 1,
73LIST_MODEBITS = 1 << 2,
74LIST_NLINKS = 1 << 3,
75LIST_ID_NAME = 1 << 4,
76LIST_ID_NUMERIC = 1 << 5,
77LIST_CONTEXT = 1 << 6,
78LIST_SIZE = 1 << 7,
Denis Vlasenko3a014b82009-03-21 19:11:23 +000079//LIST_DEV = 1 << 8, - unused, synonym to LIST_SIZE
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000080LIST_DATE_TIME = 1 << 9,
81LIST_FULLTIME = 1 << 10,
82LIST_FILENAME = 1 << 11,
83LIST_SYMLINK = 1 << 12,
84LIST_FILETYPE = 1 << 13,
85LIST_EXEC = 1 << 14,
86LIST_MASK = (LIST_EXEC << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +000087
88/* what files will be displayed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000089DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */
Denis Vlasenko656f7462006-10-28 13:02:55 +000090DISP_HIDDEN = 1 << 16, /* show filenames starting with . */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000091DISP_DOT = 1 << 17, /* show . and .. */
92DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */
93DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */
94DISP_ROWS = 1 << 20, /* print across rows */
95DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +000096
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000097/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
98SORT_FORWARD = 0, /* sort in reverse order */
99SORT_REVERSE = 1 << 27, /* sort in reverse order */
Eric Andersen11c65522000-09-07 17:24:47 +0000100
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000101SORT_NAME = 0, /* sort by file name */
102SORT_SIZE = 1 << 28, /* sort by file size */
103SORT_ATIME = 2 << 28, /* sort by last access time */
104SORT_CTIME = 3 << 28, /* sort by last change time */
105SORT_MTIME = 4 << 28, /* sort by last modification time */
106SORT_VERSION = 5 << 28, /* sort by version */
107SORT_EXT = 6 << 28, /* sort by file name extension */
108SORT_DIR = 7 << 28, /* sort by file or directory */
109SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000110
Eric Andersen11c65522000-09-07 17:24:47 +0000111/* which of the three times will be used */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000112TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
113TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
114TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000115
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000116FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
Eric Andersencc8ed391999-10-05 16:24:54 +0000117
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000118LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000119
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000120LIST_SHORT = LIST_FILENAME,
121LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
122 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
123
124SPLIT_DIR = 1,
125SPLIT_FILE = 0,
126SPLIT_SUBDIR = 2,
127
128};
Eric Andersencc8ed391999-10-05 16:24:54 +0000129
Denis Vlasenko248ce912009-03-03 14:09:04 +0000130/* "[-]Cadil1", POSIX mandated options, busybox always supports */
131/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
132/* "[-]Q" GNU option? busybox always supports */
133/* "[-]Ak" GNU options, busybox always supports */
134/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
135/* "[-]p", POSIX non-mandated options, busybox optionally supports */
136/* "[-]SXvThw", GNU options, busybox optionally supports */
137/* "[-]K", SELinux mandated options, busybox optionally supports */
138/* "[-]e", I think we made this one up */
139static const char ls_options[] ALIGN1 =
140 "Cadil1gnsxQAk" /* 13 opts, total 13 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000141 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
142 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
143 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
144 IF_FEATURE_LS_FOLLOWLINKS("L") /* 1, 24 */
145 IF_FEATURE_LS_RECURSIVE("R") /* 1, 25 */
146 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */
Denys Vlasenko55083632009-07-02 14:25:51 +0200147 IF_SELINUX("KZ") /* 2, 28 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000148 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000149 ;
150enum {
151 //OPT_C = (1 << 0),
152 //OPT_a = (1 << 1),
153 //OPT_d = (1 << 2),
154 //OPT_i = (1 << 3),
155 //OPT_l = (1 << 4),
156 //OPT_1 = (1 << 5),
157 OPT_g = (1 << 6),
158 //OPT_n = (1 << 7),
159 //OPT_s = (1 << 8),
160 //OPT_x = (1 << 9),
161 OPT_Q = (1 << 10),
162 //OPT_A = (1 << 11),
163 //OPT_k = (1 << 12),
Denys Vlasenko55083632009-07-02 14:25:51 +0200164 OPTBIT_color = 13
165 + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
166 + 4 * ENABLE_FEATURE_LS_SORTFILES
167 + 2 * ENABLE_FEATURE_LS_FILETYPES
168 + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
169 + 1 * ENABLE_FEATURE_LS_RECURSIVE
170 + 1 * ENABLE_FEATURE_HUMAN_READABLE
171 + 2 * ENABLE_SELINUX
172 + 2 * ENABLE_FEATURE_AUTOWIDTH,
173 OPT_color = 1 << OPTBIT_color,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000174};
175
176enum {
177 LIST_MASK_TRIGGER = 0,
178 STYLE_MASK_TRIGGER = STYLE_MASK,
179 DISP_MASK_TRIGGER = DISP_ROWS,
180 SORT_MASK_TRIGGER = SORT_MASK,
181};
182
183/* TODO: simple toggles may be stored as OPT_xxx bits instead */
184static const unsigned opt_flags[] = {
185 LIST_SHORT | STYLE_COLUMNS, /* C */
186 DISP_HIDDEN | DISP_DOT, /* a */
187 DISP_NOLIST, /* d */
188 LIST_INO, /* i */
189 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
190 LIST_SHORT | STYLE_SINGLE, /* 1 */
191 0, /* g (don't show group) - handled via OPT_g */
192 LIST_ID_NUMERIC, /* n */
193 LIST_BLOCKS, /* s */
194 DISP_ROWS, /* x */
195 0, /* Q (quote filename) - handled via OPT_Q */
196 DISP_HIDDEN, /* A */
197 ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
198#if ENABLE_FEATURE_LS_TIMESTAMPS
199 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
200 LIST_FULLTIME, /* e */
201 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
202 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
203#endif
204#if ENABLE_FEATURE_LS_SORTFILES
205 SORT_SIZE, /* S */
206 SORT_EXT, /* X */
207 SORT_REVERSE, /* r */
208 SORT_VERSION, /* v */
209#endif
210#if ENABLE_FEATURE_LS_FILETYPES
211 LIST_FILETYPE | LIST_EXEC, /* F */
212 LIST_FILETYPE, /* p */
213#endif
214#if ENABLE_FEATURE_LS_FOLLOWLINKS
215 FOLLOW_LINKS, /* L */
216#endif
217#if ENABLE_FEATURE_LS_RECURSIVE
218 DISP_RECURSIVE, /* R */
219#endif
220#if ENABLE_FEATURE_HUMAN_READABLE
221 LS_DISP_HR, /* h */
222#endif
223#if ENABLE_SELINUX
224 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
225#endif
226#if ENABLE_SELINUX
227 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
228#endif
229 (1U<<31)
230 /* options after Z are not processed through opt_flags:
231 * T, w - ignored
232 */
233};
234
235
Eric Andersen11c65522000-09-07 17:24:47 +0000236/*
237 * a directory entry and its stat info are stored here
238 */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000239struct dnode { /* the basic node */
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000240 const char *name; /* the dir entry name */
241 const char *fullname; /* the dir entry name */
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +0000242 int allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000243 struct stat dstat; /* the file stat info */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000244 IF_SELINUX(security_context_t sid;)
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000245 struct dnode *next; /* point at the next node */
Eric Andersen11c65522000-09-07 17:24:47 +0000246};
Eric Andersen11c65522000-09-07 17:24:47 +0000247
Glenn L McGrath4d001292003-01-06 01:11:50 +0000248static struct dnode **list_dir(const char *);
Eric Andersen3e6ff902001-03-09 21:24:12 +0000249static struct dnode **dnalloc(int);
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000250static int list_single(const struct dnode *);
Eric Andersen11c65522000-09-07 17:24:47 +0000251
Eric Andersen11c65522000-09-07 17:24:47 +0000252
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000253struct globals {
254#if ENABLE_FEATURE_LS_COLOR
255 smallint show_color;
256#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000257 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000258 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000259#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000260 unsigned tabstops; // = COLUMN_GAP;
261 unsigned terminal_width; // = TERMINAL_WIDTH;
262#endif
263#if ENABLE_FEATURE_LS_TIMESTAMPS
264 /* Do time() just once. Saves one syscall per file for "ls -l" */
265 time_t current_time_t;
266#endif
267};
268#define G (*(struct globals*)&bb_common_bufsiz1)
269#if ENABLE_FEATURE_LS_COLOR
270#define show_color (G.show_color )
271#else
272enum { show_color = 0 };
273#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000274#define exit_code (G.exit_code )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000275#define all_fmt (G.all_fmt )
276#if ENABLE_FEATURE_AUTOWIDTH
277#define tabstops (G.tabstops )
278#define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000279#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000280enum {
281 tabstops = COLUMN_GAP,
282 terminal_width = TERMINAL_WIDTH,
283};
Eric Andersen11c65522000-09-07 17:24:47 +0000284#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000285#define current_time_t (G.current_time_t)
286/* memset: we have to zero it out because of NOEXEC */
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000287#define INIT_G() do { \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000288 memset(&G, 0, sizeof(G)); \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000289 IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
290 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
291 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000292} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000293
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000294
295#if ENABLE_FEATURE_ASSUME_UNICODE
296/* libbb candidate */
297static size_t mbstrlen(const char *string)
298{
299 size_t width = mbsrtowcs(NULL /*dest*/, &string,
300 MAXINT(size_t) /*len*/, NULL /*state*/);
301 if (width == (size_t)-1)
302 return strlen(string);
303 return width;
304}
305#else
306#define mbstrlen(string) strlen(string)
307#endif
308
Matt Kraai33fdae52000-10-13 17:59:43 +0000309
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000310static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000311{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000312 struct stat dstat;
313 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000314 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000315
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000316 if ((all_fmt & FOLLOW_LINKS) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000317#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000318 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000319 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000320 }
Eric Andersen9e480452003-07-03 10:07:04 +0000321#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000322 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000323 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000324 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000325 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000326 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000327 } else {
328#if ENABLE_SELINUX
329 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000330 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000331 }
Eric Andersen9e480452003-07-03 10:07:04 +0000332#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000333 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000334 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000335 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000336 return 0;
337 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000338 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000339
Denis Vlasenko5c759602006-10-28 12:37:16 +0000340 cur = xmalloc(sizeof(struct dnode));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000341 cur->fullname = fullname;
342 cur->name = name;
343 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000344 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000345 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000346}
347
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000348
349/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
350 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
351 * 3/7:multiplexed char/block device)
352 * and we use 0 for unknown and 15 for executables (see below) */
353#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
354#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
355#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
356/* 036 black foreground 050 black background
357 037 red foreground 051 red background
358 040 green foreground 052 green background
359 041 brown foreground 053 brown background
360 042 blue foreground 054 blue background
361 043 magenta (purple) foreground 055 magenta background
362 044 cyan (light blue) foreground 056 cyan background
363 045 gray foreground 057 white background
364*/
365#define COLOR(mode) ( \
366 /*un fi chr dir blk file link sock exe */ \
367 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
368 [TYPEINDEX(mode)])
369/* Select normal (0) [actually "reset all"] or bold (1)
370 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
371 * let's use 7 for "impossible" types, just for fun)
372 * Note: coreutils 6.9 uses inverted red for setuid binaries.
373 */
374#define ATTR(mode) ( \
375 /*un fi chr dir blk file link sock exe */ \
376 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
377 [TYPEINDEX(mode)])
378
Denis Vlasenko5c759602006-10-28 12:37:16 +0000379#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000380/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000381static char fgcolor(mode_t mode)
382{
Rob Landley9947a242006-06-15 22:11:10 +0000383 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000384 return COLOR(0xF000); /* File is executable ... */
385 return COLOR(mode);
386}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000387static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000388{
Rob Landley9947a242006-06-15 22:11:10 +0000389 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000390 return ATTR(0xF000); /* File is executable ... */
391 return ATTR(mode);
392}
393#endif
394
Denis Vlasenko5c759602006-10-28 12:37:16 +0000395#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000396static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000397{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000398 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000399 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000400 if (S_ISDIR(mode))
401 return '/';
402 if (!(all_fmt & LIST_EXEC))
403 return '\0';
404 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000405 return '*';
406 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000407}
408#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000409
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000410
Denis Vlasenko5c759602006-10-28 12:37:16 +0000411#define countdirs(A, B) count_dirs((A), (B), 1)
412#define countsubdirs(A, B) count_dirs((A), (B), 0)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000413static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
Eric Andersen11c65522000-09-07 17:24:47 +0000414{
415 int i, dirs;
416
Denis Vlasenko5c759602006-10-28 12:37:16 +0000417 if (!dn)
418 return 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000419 dirs = 0;
420 for (i = 0; i < nfiles; i++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000421 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000422 if (!S_ISDIR(dn[i]->dstat.st_mode))
423 continue;
424 name = dn[i]->name;
425 if (notsubdirs
426 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
427 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000428 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000429 }
Eric Andersen11c65522000-09-07 17:24:47 +0000430 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000431 return dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000432}
433
Eric Andersen3e6ff902001-03-09 21:24:12 +0000434static int countfiles(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000435{
436 int nfiles;
437 struct dnode *cur;
438
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000439 if (dnp == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000440 return 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000441 nfiles = 0;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000442 for (cur = dnp[0]; cur->next; cur = cur->next)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000443 nfiles++;
Eric Andersen11c65522000-09-07 17:24:47 +0000444 nfiles++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000445 return nfiles;
Eric Andersen11c65522000-09-07 17:24:47 +0000446}
447
448/* get memory to hold an array of pointers */
Eric Andersen3e6ff902001-03-09 21:24:12 +0000449static struct dnode **dnalloc(int num)
Eric Andersen11c65522000-09-07 17:24:47 +0000450{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000451 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000452 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000453
Denis Vlasenko5c759602006-10-28 12:37:16 +0000454 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000455}
456
Denis Vlasenko5c759602006-10-28 12:37:16 +0000457#if ENABLE_FEATURE_LS_RECURSIVE
Rob Landley26314862006-05-02 19:46:52 +0000458static void dfree(struct dnode **dnp, int nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000459{
Rob Landley26314862006-05-02 19:46:52 +0000460 int i;
Eric Andersen11c65522000-09-07 17:24:47 +0000461
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000462 if (dnp == NULL)
463 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000464
Rob Landley26314862006-05-02 19:46:52 +0000465 for (i = 0; i < nfiles; i++) {
466 struct dnode *cur = dnp[i];
Denis Vlasenko5c759602006-10-28 12:37:16 +0000467 if (cur->allocated)
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000468 free((char*)cur->fullname); /* free the filename */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000469 free(cur); /* free the dnode */
Eric Andersen11c65522000-09-07 17:24:47 +0000470 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000471 free(dnp); /* free the array holding the dnode pointers */
Eric Andersen11c65522000-09-07 17:24:47 +0000472}
Rob Landleyc44bc982006-05-28 01:19:06 +0000473#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000474#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000475#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000476
Eric Andersen3e6ff902001-03-09 21:24:12 +0000477static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000478{
479 int dncnt, i, d;
480 struct dnode **dnp;
481
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000482 if (dn == NULL || nfiles < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000483 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000484
485 /* count how many dirs and regular files there are */
Eric Andersencf1189f2000-11-29 21:52:06 +0000486 if (which == SPLIT_SUBDIR)
487 dncnt = countsubdirs(dn, nfiles);
488 else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000489 dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
Eric Andersencf1189f2000-11-29 21:52:06 +0000490 if (which == SPLIT_FILE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000491 dncnt = nfiles - dncnt; /* looking for files */
Eric Andersencf1189f2000-11-29 21:52:06 +0000492 }
Eric Andersen11c65522000-09-07 17:24:47 +0000493
494 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000495 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000496
497 /* copy the entrys into the file or dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000498 for (d = i = 0; i < nfiles; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000499 if (S_ISDIR(dn[i]->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000500 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000501 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
502 continue;
503 name = dn[i]->name;
504 if ((which & SPLIT_DIR)
505 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
506 ) {
507 dnp[d++] = dn[i];
Manuel Novoa III cad53642003-03-19 09:13:01 +0000508 }
509 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
510 dnp[d++] = dn[i];
Eric Andersen11c65522000-09-07 17:24:47 +0000511 }
512 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000513 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000514}
515
Denis Vlasenko5c759602006-10-28 12:37:16 +0000516#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000517static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000518{
Rob Landley425e7582006-05-03 20:22:03 +0000519 struct dnode *d1 = *(struct dnode **)a;
520 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000521 unsigned sort_opts = all_fmt & SORT_MASK;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000522 int dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000523
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000524 dif = 0; /* assume SORT_NAME */
525 // TODO: use pre-initialized function pointer
526 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000527 if (sort_opts == SORT_SIZE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000528 dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000529 } else if (sort_opts == SORT_ATIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000530 dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000531 } else if (sort_opts == SORT_CTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000532 dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000533 } else if (sort_opts == SORT_MTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000534 dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000535 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000536 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000537 /* } else if (sort_opts == SORT_VERSION) { */
538 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000539 }
540
Eric Andersen11c65522000-09-07 17:24:47 +0000541 if (dif == 0) {
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +0000542 /* sort by name - may be a tie_breaker for time or size cmp */
Rob Landleyea224be2006-06-18 20:20:07 +0000543 if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
544 else dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000545 }
546
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000547 if (all_fmt & SORT_REVERSE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000548 dif = -dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000549 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000550 return dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000551}
552
Rob Landley425e7582006-05-03 20:22:03 +0000553static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000554{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000555 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000556}
Rob Landleyea224be2006-06-18 20:20:07 +0000557#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000558#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000559#endif
560
Rob Landleyea224be2006-06-18 20:20:07 +0000561
Eric Andersen3e6ff902001-03-09 21:24:12 +0000562static void showfiles(struct dnode **dn, int nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000563{
564 int i, ncols, nrows, row, nc;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000565 int column = 0;
566 int nexttab = 0;
567 int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
Eric Andersen11c65522000-09-07 17:24:47 +0000568
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000569 if (dn == NULL || nfiles < 1)
570 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000571
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000572 if (all_fmt & STYLE_LONG) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000573 ncols = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000574 } else {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000575 /* find the longest file name, use that as the column width */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000576 for (i = 0; i < nfiles; i++) {
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000577 int len = mbstrlen(dn[i]->name);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000578 if (column_width < len)
579 column_width = len;
580 }
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000581 column_width += tabstops +
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000582 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000583 ((all_fmt & LIST_INO) ? 8 : 0) +
584 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
Glenn L McGrath4d001292003-01-06 01:11:50 +0000585 ncols = (int) (terminal_width / column_width);
Eric Andersen11c65522000-09-07 17:24:47 +0000586 }
587
Eric Andersene57d54b2001-01-30 18:03:11 +0000588 if (ncols > 1) {
589 nrows = nfiles / ncols;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000590 if (nrows * ncols < nfiles)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000591 nrows++; /* round up fractionals */
Eric Andersene57d54b2001-01-30 18:03:11 +0000592 } else {
593 nrows = nfiles;
594 ncols = 1;
595 }
Eric Andersen11c65522000-09-07 17:24:47 +0000596
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000597 for (row = 0; row < nrows; row++) {
598 for (nc = 0; nc < ncols; nc++) {
Eric Andersen79565b62000-08-11 18:10:21 +0000599 /* reach into the array based on the column and row */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000600 i = (nc * nrows) + row; /* assume display by column */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000601 if (all_fmt & DISP_ROWS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000602 i = (row * ncols) + nc; /* display across row */
Eric Andersen11c65522000-09-07 17:24:47 +0000603 if (i < nfiles) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000604 if (column > 0) {
605 nexttab -= column;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000606 printf("%*s", nexttab, "");
607 column += nexttab;
608 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000609 nexttab = column + column_width;
610 column += list_single(dn[i]);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000611 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000612 }
613 putchar('\n');
614 column = 0;
Eric Andersena42982e2000-06-07 17:28:53 +0000615 }
Eric Andersen11c65522000-09-07 17:24:47 +0000616}
617
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000618
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000619static void showdirs(struct dnode **dn, int ndirs, int first)
Eric Andersen11c65522000-09-07 17:24:47 +0000620{
621 int i, nfiles;
622 struct dnode **subdnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000623 int dndirs;
624 struct dnode **dnd;
Eric Andersen11c65522000-09-07 17:24:47 +0000625
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000626 if (dn == NULL || ndirs < 1)
627 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000628
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000629 for (i = 0; i < ndirs; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000630 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000631 if (!first)
Denis Vlasenko4daad902007-09-27 10:20:47 +0000632 bb_putchar('\n');
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000633 first = 0;
634 printf("%s:\n", dn[i]->fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000635 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000636 subdnp = list_dir(dn[i]->fullname);
637 nfiles = countfiles(subdnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000638 if (nfiles > 0) {
639 /* list all files at this level */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000640 dnsort(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000641 showfiles(subdnp, nfiles);
Rob Landley2b8a05a2006-06-20 17:43:01 +0000642 if (ENABLE_FEATURE_LS_RECURSIVE) {
643 if (all_fmt & DISP_RECURSIVE) {
644 /* recursive- list the sub-dirs */
645 dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
646 dndirs = countsubdirs(subdnp, nfiles);
647 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000648 dnsort(dnd, dndirs);
Rob Landley2b8a05a2006-06-20 17:43:01 +0000649 showdirs(dnd, dndirs, 0);
650 /* free the array of dnode pointers to the dirs */
651 free(dnd);
652 }
Eric Andersen11c65522000-09-07 17:24:47 +0000653 }
Rob Landley2b8a05a2006-06-20 17:43:01 +0000654 /* free the dnodes and the fullname mem */
655 dfree(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000656 }
Eric Andersen11c65522000-09-07 17:24:47 +0000657 }
658 }
659}
660
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000661
Glenn L McGrath4d001292003-01-06 01:11:50 +0000662static struct dnode **list_dir(const char *path)
Eric Andersen11c65522000-09-07 17:24:47 +0000663{
664 struct dnode *dn, *cur, **dnp;
665 struct dirent *entry;
666 DIR *dir;
Eric Andersen11c65522000-09-07 17:24:47 +0000667 int i, nfiles;
668
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000669 if (path == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000670 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000671
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000672 dn = NULL;
673 nfiles = 0;
Rob Landleyd921b2e2006-08-03 15:41:12 +0000674 dir = warn_opendir(path);
Eric Andersen11c65522000-09-07 17:24:47 +0000675 if (dir == NULL) {
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000676 exit_code = EXIT_FAILURE;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000677 return NULL; /* could not open the dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000678 }
679 while ((entry = readdir(dir)) != NULL) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000680 char *fullname;
681
Eric Andersen11c65522000-09-07 17:24:47 +0000682 /* are we going to list the file- it may be . or .. or a hidden file */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000683 if (entry->d_name[0] == '.') {
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000684 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
685 && !(all_fmt & DISP_DOT)
686 ) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000687 continue;
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000688 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000689 if (!(all_fmt & DISP_HIDDEN))
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000690 continue;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000691 }
692 fullname = concat_path_file(path, entry->d_name);
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000693 cur = my_stat(fullname, bb_basename(fullname), 0);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000694 if (!cur) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000695 free(fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000696 continue;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000697 }
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +0000698 cur->allocated = 1;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000699 cur->next = dn;
700 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +0000701 nfiles++;
702 }
703 closedir(dir);
704
705 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +0000706 * allocate memory for an array to hold dnode pointers
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000707 */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000708 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000709 return NULL;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000710 dnp = dnalloc(nfiles);
711 for (i = 0, cur = dn; i < nfiles; i++) {
712 dnp[i] = cur; /* save pointer to node in array */
713 cur = cur->next;
Eric Andersen11c65522000-09-07 17:24:47 +0000714 }
715
Denis Vlasenko5c759602006-10-28 12:37:16 +0000716 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000717}
718
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000719
Denis Vlasenko248ce912009-03-03 14:09:04 +0000720static int print_name(const char *name)
721{
722 if (option_mask32 & OPT_Q) {
723#if ENABLE_FEATURE_ASSUME_UNICODE
724 int len = 2 + mbstrlen(name);
725#else
726 int len = 2;
727#endif
728 putchar('"');
729 while (*name) {
730 if (*name == '"') {
731 putchar('\\');
732 len++;
733 }
734 putchar(*name++);
735 if (!ENABLE_FEATURE_ASSUME_UNICODE)
736 len++;
737 }
738 putchar('"');
739 return len;
740 }
741 /* No -Q: */
742#if ENABLE_FEATURE_ASSUME_UNICODE
743 fputs(name, stdout);
744 return mbstrlen(name);
745#else
746 return printf("%s", name);
747#endif
748}
749
750
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000751static int list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000752{
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000753 int column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000754 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000755#if ENABLE_FEATURE_LS_TIMESTAMPS
Eric Andersen11c65522000-09-07 17:24:47 +0000756 char *filetime;
757 time_t ttime, age;
758#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000759#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000760 struct stat info;
761 char append;
762#endif
763
Glenn L McGrath4d001292003-01-06 01:11:50 +0000764 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000765 return 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000766
Denis Vlasenko5c759602006-10-28 12:37:16 +0000767#if ENABLE_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000768 ttime = dn->dstat.st_mtime; /* the default time */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000769 if (all_fmt & TIME_ACCESS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000770 ttime = dn->dstat.st_atime;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000771 if (all_fmt & TIME_CHANGE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000772 ttime = dn->dstat.st_ctime;
773 filetime = ctime(&ttime);
Eric Andersen11c65522000-09-07 17:24:47 +0000774#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000775#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000776 append = append_char(dn->dstat.st_mode);
777#endif
778
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000779 /* Do readlink early, so that if it fails, error message
780 * does not appear *inside* of the "ls -l" line */
781 if (all_fmt & LIST_SYMLINK)
782 if (S_ISLNK(dn->dstat.st_mode))
783 lpath = xmalloc_readlink_or_warn(dn->fullname);
784
785 if (all_fmt & LIST_INO)
786 column += printf("%7lu ", (long) dn->dstat.st_ino);
787 if (all_fmt & LIST_BLOCKS)
788 column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1);
789 if (all_fmt & LIST_MODEBITS)
790 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
791 if (all_fmt & LIST_NLINKS)
792 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000793#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000794 if (all_fmt & LIST_ID_NAME) {
795 if (option_mask32 & OPT_g) {
796 column += printf("%-8.8s",
797 get_cached_username(dn->dstat.st_uid));
798 } else {
799 column += printf("%-8.8s %-8.8s",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000800 get_cached_username(dn->dstat.st_uid),
801 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000802 }
803 }
Eric Andersen11c65522000-09-07 17:24:47 +0000804#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000805 if (all_fmt & LIST_ID_NUMERIC) {
806 if (option_mask32 & OPT_g)
807 column += printf("%-8u", (int) dn->dstat.st_uid);
808 else
809 column += printf("%-8u %-8u",
810 (int) dn->dstat.st_uid,
811 (int) dn->dstat.st_gid);
812 }
813 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
814 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
815 column += printf("%4u, %3u ",
816 (int) major(dn->dstat.st_rdev),
817 (int) minor(dn->dstat.st_rdev));
818 } else {
819 if (all_fmt & LS_DISP_HR) {
820 column += printf("%9s ",
821 make_human_readable_str(dn->dstat.st_size, 1, 0));
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000822 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000823 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000824 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000825 }
826 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000827#if ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000828 if (all_fmt & LIST_FULLTIME)
829 column += printf("%24.24s ", filetime);
830 if (all_fmt & LIST_DATE_TIME)
831 if ((all_fmt & LIST_FULLTIME) == 0) {
832 /* current_time_t ~== time(NULL) */
833 age = current_time_t - ttime;
834 printf("%6.6s ", filetime + 4);
835 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
836 /* hh:mm if less than 6 months old */
837 printf("%5.5s ", filetime + 11);
838 } else {
839 printf(" %4.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000840 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000841 column += 13;
842 }
Eric Andersen11c65522000-09-07 17:24:47 +0000843#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000844#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000845 if (all_fmt & LIST_CONTEXT) {
846 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
847 freecon(dn->sid);
848 }
Eric Andersen9e480452003-07-03 10:07:04 +0000849#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000850 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000851#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000852 if (show_color) {
853 info.st_mode = 0; /* for fgcolor() */
854 lstat(dn->fullname, &info);
855 printf("\033[%u;%um", bold(info.st_mode),
856 fgcolor(info.st_mode));
857 }
858#endif
859 column += print_name(dn->name);
860 if (show_color) {
861 printf("\033[0m");
862 }
863 }
864 if (all_fmt & LIST_SYMLINK) {
865 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
866 printf(" -> ");
867#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
868#if ENABLE_FEATURE_LS_COLOR
869 info.st_mode = 0; /* for fgcolor() */
870#endif
871 if (stat(dn->fullname, &info) == 0) {
872 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000873 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000874#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000875#if ENABLE_FEATURE_LS_COLOR
876 if (show_color) {
877 printf("\033[%u;%um", bold(info.st_mode),
878 fgcolor(info.st_mode));
879 }
880#endif
881 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000882 if (show_color) {
883 printf("\033[0m");
884 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000885 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000886 }
887 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000888#if ENABLE_FEATURE_LS_FILETYPES
889 if (all_fmt & LIST_FILETYPE) {
890 if (append) {
891 putchar(append);
892 column++;
893 }
894 }
895#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000896
Glenn L McGrath4d001292003-01-06 01:11:50 +0000897 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000898}
899
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000900
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000901/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
902#if ENABLE_FEATURE_LS_COLOR
903/* long option entry used only for --color, which has no short option
904 * equivalent */
905static const char ls_color_opt[] ALIGN1 =
906 "color\0" Optional_argument "\xff" /* no short equivalent */
907 ;
908#endif
909
Denis Vlasenko97fd6d82007-03-19 20:59:20 +0000910
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000911int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000912{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000913 struct dnode **dnd;
914 struct dnode **dnf;
915 struct dnode **dnp;
916 struct dnode *dn;
917 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000918 unsigned opt;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000919 int nfiles;
Glenn L McGrath792cae52004-01-18 05:15:16 +0000920 int dnfiles;
921 int dndirs;
Glenn L McGrath792cae52004-01-18 05:15:16 +0000922 int i;
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000923 /* need to initialize since --color has _an optional_ argument */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000924 IF_FEATURE_LS_COLOR(const char *color_opt = "always";)
Glenn L McGrath792cae52004-01-18 05:15:16 +0000925
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000926 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000927
Rob Landley2b8a05a2006-06-20 17:43:01 +0000928 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000929 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000930
Denis Vlasenko5c759602006-10-28 12:37:16 +0000931#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko656f7462006-10-28 13:02:55 +0000932 /* Obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000933 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Eric Andersen8efe9672003-09-15 08:33:45 +0000934 /* Go one less... */
935 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000936#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000937
Eric Andersen11c65522000-09-07 17:24:47 +0000938 /* process options */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000939 IF_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000940#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko1d426652008-03-17 09:09:09 +0000941 opt_complementary = "T+:w+"; /* -T N, -w N */
942 opt = getopt32(argv, ls_options, &tabstops, &terminal_width
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000943 IF_FEATURE_LS_COLOR(, &color_opt));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000944#else
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000945 opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
Manuel Novoa III cad53642003-03-19 09:13:01 +0000946#endif
Eric Andersend07cf592004-02-05 13:52:03 +0000947 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +0000948 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000949 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000950
Denis Vlasenko5c759602006-10-28 12:37:16 +0000951 if (flags & LIST_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000952 all_fmt &= ~LIST_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000953 if (flags & STYLE_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000954 all_fmt &= ~STYLE_MASK;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000955 if (flags & SORT_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000956 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000957 if (flags & DISP_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000958 all_fmt &= ~DISP_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000959 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000960 all_fmt &= ~TIME_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000961 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +0000962 all_fmt |= STYLE_SINGLE;
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000963 /* huh?? opt cannot be 'l' */
964 //if (LS_DISP_HR && opt == 'l')
965 // all_fmt &= ~LS_DISP_HR;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000966 all_fmt |= flags;
967 }
Eric Andersen11c65522000-09-07 17:24:47 +0000968 }
969
Denis Vlasenko5c759602006-10-28 12:37:16 +0000970#if ENABLE_FEATURE_LS_COLOR
971 /* find color bit value - last position for short getopt */
972 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
973 char *p = getenv("LS_COLORS");
974 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000975 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000976 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000977 }
Denys Vlasenko55083632009-07-02 14:25:51 +0200978 if (opt & OPT_color) { /* next flag after short options */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000979 if (strcmp("always", color_opt) == 0)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000980 show_color = 1;
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000981 else if (strcmp("never", color_opt) == 0)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000982 show_color = 0;
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000983 else if (strcmp("auto", color_opt) == 0 && isatty(STDOUT_FILENO))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000984 show_color = 1;
Paul Fox156dc412005-08-01 19:33:30 +0000985 }
986#endif
987
Eric Andersen11c65522000-09-07 17:24:47 +0000988 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000989 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000990 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +0000991 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
992 if (all_fmt & TIME_CHANGE)
993 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
994 if (all_fmt & TIME_ACCESS)
995 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
996 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000997 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
998 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000999 if (ENABLE_FEATURE_LS_USERNAME)
1000 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1001 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001002
Eric Andersen11c65522000-09-07 17:24:47 +00001003 /* choose a display format */
Rob Landleyea224be2006-06-18 20:20:07 +00001004 if (!(all_fmt & STYLE_MASK))
Eric Andersen70060d22004-03-27 10:02:48 +00001005 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001006
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001007 argv += optind;
1008 if (!argv[0])
1009 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001010
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001011 if (argv[1])
1012 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001013
Denis Vlasenko5c759602006-10-28 12:37:16 +00001014 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001015 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001016 nfiles = 0;
1017 do {
Denis Vlasenko11a6f9b2009-03-03 13:20:22 +00001018 /* NB: follow links on command line unless -l or -s */
1019 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001020 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001021 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001022 continue;
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001023 cur->allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001024 cur->next = dn;
1025 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001026 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001027 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001028
1029 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001030 * allocate memory for an array to hold dnode pointers
1031 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001032 dnp = dnalloc(nfiles);
1033 for (i = 0, cur = dn; i < nfiles; i++) {
1034 dnp[i] = cur; /* save pointer to node in array */
1035 cur = cur->next;
Eric Andersen11c65522000-09-07 17:24:47 +00001036 }
1037
Manuel Novoa III cad53642003-03-19 09:13:01 +00001038 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001039 dnsort(dnp, nfiles);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001040 if (nfiles > 0)
1041 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001042 } else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001043 dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
1044 dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
1045 dndirs = countdirs(dnp, nfiles);
1046 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001047 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001048 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001049 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001050 if (ENABLE_FEATURE_CLEAN_UP)
1051 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001052 }
1053 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001054 dnsort(dnd, dndirs);
Glenn L McGrathc4db0832004-03-06 09:12:55 +00001055 showdirs(dnd, dndirs, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001056 if (ENABLE_FEATURE_CLEAN_UP)
1057 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001058 }
1059 }
Rob Landley26314862006-05-02 19:46:52 +00001060 if (ENABLE_FEATURE_CLEAN_UP)
1061 dfree(dnp, nfiles);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001062 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001063}