blob: 827b3508930f0366f20ec4db072764088a7ad2d0 [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 */
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000033#include "libbb.h"
Denys Vlasenko42a8fd02009-07-11 21:36:13 +020034#include "unicode.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +000035
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +000036
Denis Vlasenko99912ca2007-04-10 15:43:37 +000037/* This is a NOEXEC applet. Be very careful! */
38
Eric Andersenf1142c52001-02-20 06:16:29 +000039
Denis Vlasenko245f91b2009-03-09 22:37:23 +000040#if ENABLE_FTPD
41/* ftpd uses ls, and without timestamps Mozilla won't understand
42 * ftpd's LIST output.
43 */
44# undef CONFIG_FEATURE_LS_TIMESTAMPS
45# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000046# undef IF_FEATURE_LS_TIMESTAMPS
47# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +000048# define CONFIG_FEATURE_LS_TIMESTAMPS 1
49# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000050# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
51# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +000052#endif
53
54
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000055enum {
Manuel Novoa III cad53642003-03-19 09:13:01 +000056
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000057TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
58COLUMN_GAP = 2, /* includes the file type char */
59
60/* what is the overall style of the listing */
61STYLE_COLUMNS = 1 << 21, /* fill columns */
62STYLE_LONG = 2 << 21, /* one record per line, extended info */
63STYLE_SINGLE = 3 << 21, /* one record per line */
64STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +000065
Eric Andersen11c65522000-09-07 17:24:47 +000066/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
67/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000068LIST_INO = 1 << 0,
69LIST_BLOCKS = 1 << 1,
70LIST_MODEBITS = 1 << 2,
71LIST_NLINKS = 1 << 3,
72LIST_ID_NAME = 1 << 4,
73LIST_ID_NUMERIC = 1 << 5,
74LIST_CONTEXT = 1 << 6,
75LIST_SIZE = 1 << 7,
Denis Vlasenko3a014b82009-03-21 19:11:23 +000076//LIST_DEV = 1 << 8, - unused, synonym to LIST_SIZE
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000077LIST_DATE_TIME = 1 << 9,
78LIST_FULLTIME = 1 << 10,
79LIST_FILENAME = 1 << 11,
80LIST_SYMLINK = 1 << 12,
81LIST_FILETYPE = 1 << 13,
82LIST_EXEC = 1 << 14,
83LIST_MASK = (LIST_EXEC << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +000084
85/* what files will be displayed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000086DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */
Denis Vlasenko656f7462006-10-28 13:02:55 +000087DISP_HIDDEN = 1 << 16, /* show filenames starting with . */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000088DISP_DOT = 1 << 17, /* show . and .. */
89DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */
90DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */
91DISP_ROWS = 1 << 20, /* print across rows */
92DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +000093
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000094/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
95SORT_FORWARD = 0, /* sort in reverse order */
96SORT_REVERSE = 1 << 27, /* sort in reverse order */
Eric Andersen11c65522000-09-07 17:24:47 +000097
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000098SORT_NAME = 0, /* sort by file name */
99SORT_SIZE = 1 << 28, /* sort by file size */
100SORT_ATIME = 2 << 28, /* sort by last access time */
101SORT_CTIME = 3 << 28, /* sort by last change time */
102SORT_MTIME = 4 << 28, /* sort by last modification time */
103SORT_VERSION = 5 << 28, /* sort by version */
104SORT_EXT = 6 << 28, /* sort by file name extension */
105SORT_DIR = 7 << 28, /* sort by file or directory */
106SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000107
Eric Andersen11c65522000-09-07 17:24:47 +0000108/* which of the three times will be used */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000109TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
110TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
111TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000112
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000113FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
Eric Andersencc8ed391999-10-05 16:24:54 +0000114
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000115LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000116
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000117LIST_SHORT = LIST_FILENAME,
118LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
119 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
120
121SPLIT_DIR = 1,
122SPLIT_FILE = 0,
123SPLIT_SUBDIR = 2,
124
125};
Eric Andersencc8ed391999-10-05 16:24:54 +0000126
Denis Vlasenko248ce912009-03-03 14:09:04 +0000127/* "[-]Cadil1", POSIX mandated options, busybox always supports */
128/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
129/* "[-]Q" GNU option? busybox always supports */
130/* "[-]Ak" GNU options, busybox always supports */
131/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
132/* "[-]p", POSIX non-mandated options, busybox optionally supports */
133/* "[-]SXvThw", GNU options, busybox optionally supports */
134/* "[-]K", SELinux mandated options, busybox optionally supports */
135/* "[-]e", I think we made this one up */
136static const char ls_options[] ALIGN1 =
137 "Cadil1gnsxQAk" /* 13 opts, total 13 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000138 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
139 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
140 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
141 IF_FEATURE_LS_FOLLOWLINKS("L") /* 1, 24 */
142 IF_FEATURE_LS_RECURSIVE("R") /* 1, 25 */
143 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */
Denys Vlasenko55083632009-07-02 14:25:51 +0200144 IF_SELINUX("KZ") /* 2, 28 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000145 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000146 ;
147enum {
148 //OPT_C = (1 << 0),
149 //OPT_a = (1 << 1),
150 //OPT_d = (1 << 2),
151 //OPT_i = (1 << 3),
152 //OPT_l = (1 << 4),
153 //OPT_1 = (1 << 5),
154 OPT_g = (1 << 6),
155 //OPT_n = (1 << 7),
156 //OPT_s = (1 << 8),
157 //OPT_x = (1 << 9),
158 OPT_Q = (1 << 10),
159 //OPT_A = (1 << 11),
160 //OPT_k = (1 << 12),
Denys Vlasenko55083632009-07-02 14:25:51 +0200161 OPTBIT_color = 13
162 + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
163 + 4 * ENABLE_FEATURE_LS_SORTFILES
164 + 2 * ENABLE_FEATURE_LS_FILETYPES
165 + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
166 + 1 * ENABLE_FEATURE_LS_RECURSIVE
167 + 1 * ENABLE_FEATURE_HUMAN_READABLE
168 + 2 * ENABLE_SELINUX
169 + 2 * ENABLE_FEATURE_AUTOWIDTH,
170 OPT_color = 1 << OPTBIT_color,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000171};
172
173enum {
174 LIST_MASK_TRIGGER = 0,
175 STYLE_MASK_TRIGGER = STYLE_MASK,
176 DISP_MASK_TRIGGER = DISP_ROWS,
177 SORT_MASK_TRIGGER = SORT_MASK,
178};
179
180/* TODO: simple toggles may be stored as OPT_xxx bits instead */
181static const unsigned opt_flags[] = {
182 LIST_SHORT | STYLE_COLUMNS, /* C */
183 DISP_HIDDEN | DISP_DOT, /* a */
184 DISP_NOLIST, /* d */
185 LIST_INO, /* i */
186 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
187 LIST_SHORT | STYLE_SINGLE, /* 1 */
188 0, /* g (don't show group) - handled via OPT_g */
189 LIST_ID_NUMERIC, /* n */
190 LIST_BLOCKS, /* s */
191 DISP_ROWS, /* x */
192 0, /* Q (quote filename) - handled via OPT_Q */
193 DISP_HIDDEN, /* A */
194 ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
195#if ENABLE_FEATURE_LS_TIMESTAMPS
196 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
197 LIST_FULLTIME, /* e */
198 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
199 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
200#endif
201#if ENABLE_FEATURE_LS_SORTFILES
202 SORT_SIZE, /* S */
203 SORT_EXT, /* X */
204 SORT_REVERSE, /* r */
205 SORT_VERSION, /* v */
206#endif
207#if ENABLE_FEATURE_LS_FILETYPES
208 LIST_FILETYPE | LIST_EXEC, /* F */
209 LIST_FILETYPE, /* p */
210#endif
211#if ENABLE_FEATURE_LS_FOLLOWLINKS
212 FOLLOW_LINKS, /* L */
213#endif
214#if ENABLE_FEATURE_LS_RECURSIVE
215 DISP_RECURSIVE, /* R */
216#endif
217#if ENABLE_FEATURE_HUMAN_READABLE
218 LS_DISP_HR, /* h */
219#endif
220#if ENABLE_SELINUX
221 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
222#endif
223#if ENABLE_SELINUX
224 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
225#endif
226 (1U<<31)
227 /* options after Z are not processed through opt_flags:
228 * T, w - ignored
229 */
230};
231
232
Eric Andersen11c65522000-09-07 17:24:47 +0000233/*
234 * a directory entry and its stat info are stored here
235 */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000236struct dnode { /* the basic node */
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000237 const char *name; /* the dir entry name */
238 const char *fullname; /* the dir entry name */
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +0000239 int allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000240 struct stat dstat; /* the file stat info */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000241 IF_SELINUX(security_context_t sid;)
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000242 struct dnode *next; /* point at the next node */
Eric Andersen11c65522000-09-07 17:24:47 +0000243};
Eric Andersen11c65522000-09-07 17:24:47 +0000244
Glenn L McGrath4d001292003-01-06 01:11:50 +0000245static struct dnode **list_dir(const char *);
Eric Andersen3e6ff902001-03-09 21:24:12 +0000246static struct dnode **dnalloc(int);
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000247static int list_single(const struct dnode *);
Eric Andersen11c65522000-09-07 17:24:47 +0000248
Eric Andersen11c65522000-09-07 17:24:47 +0000249
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000250struct globals {
251#if ENABLE_FEATURE_LS_COLOR
252 smallint show_color;
253#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000254 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000255 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000256#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000257 unsigned tabstops; // = COLUMN_GAP;
258 unsigned terminal_width; // = TERMINAL_WIDTH;
259#endif
260#if ENABLE_FEATURE_LS_TIMESTAMPS
261 /* Do time() just once. Saves one syscall per file for "ls -l" */
262 time_t current_time_t;
263#endif
264};
265#define G (*(struct globals*)&bb_common_bufsiz1)
266#if ENABLE_FEATURE_LS_COLOR
267#define show_color (G.show_color )
268#else
269enum { show_color = 0 };
270#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000271#define exit_code (G.exit_code )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000272#define all_fmt (G.all_fmt )
273#if ENABLE_FEATURE_AUTOWIDTH
274#define tabstops (G.tabstops )
275#define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000276#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000277enum {
278 tabstops = COLUMN_GAP,
279 terminal_width = TERMINAL_WIDTH,
280};
Eric Andersen11c65522000-09-07 17:24:47 +0000281#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000282#define current_time_t (G.current_time_t)
283/* memset: we have to zero it out because of NOEXEC */
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000284#define INIT_G() do { \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000285 memset(&G, 0, sizeof(G)); \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000286 IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
287 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
288 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000289} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000290
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000291
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000292static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000293{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000294 struct stat dstat;
295 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000296 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000297
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000298 if ((all_fmt & FOLLOW_LINKS) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000299#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000300 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000301 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000302 }
Eric Andersen9e480452003-07-03 10:07:04 +0000303#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000304 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000305 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000306 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000307 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000308 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000309 } else {
310#if ENABLE_SELINUX
311 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000312 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000313 }
Eric Andersen9e480452003-07-03 10:07:04 +0000314#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000315 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000316 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000317 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000318 return 0;
319 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000320 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000321
Denis Vlasenko5c759602006-10-28 12:37:16 +0000322 cur = xmalloc(sizeof(struct dnode));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000323 cur->fullname = fullname;
324 cur->name = name;
325 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000326 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000327 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000328}
329
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000330
331/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
332 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
333 * 3/7:multiplexed char/block device)
334 * and we use 0 for unknown and 15 for executables (see below) */
335#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
336#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
337#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
338/* 036 black foreground 050 black background
339 037 red foreground 051 red background
340 040 green foreground 052 green background
341 041 brown foreground 053 brown background
342 042 blue foreground 054 blue background
343 043 magenta (purple) foreground 055 magenta background
344 044 cyan (light blue) foreground 056 cyan background
345 045 gray foreground 057 white background
346*/
347#define COLOR(mode) ( \
348 /*un fi chr dir blk file link sock exe */ \
349 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
350 [TYPEINDEX(mode)])
351/* Select normal (0) [actually "reset all"] or bold (1)
352 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
353 * let's use 7 for "impossible" types, just for fun)
354 * Note: coreutils 6.9 uses inverted red for setuid binaries.
355 */
356#define ATTR(mode) ( \
357 /*un fi chr dir blk file link sock exe */ \
358 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
359 [TYPEINDEX(mode)])
360
Denis Vlasenko5c759602006-10-28 12:37:16 +0000361#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000362/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000363static char fgcolor(mode_t mode)
364{
Rob Landley9947a242006-06-15 22:11:10 +0000365 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000366 return COLOR(0xF000); /* File is executable ... */
367 return COLOR(mode);
368}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000369static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000370{
Rob Landley9947a242006-06-15 22:11:10 +0000371 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000372 return ATTR(0xF000); /* File is executable ... */
373 return ATTR(mode);
374}
375#endif
376
Denis Vlasenko5c759602006-10-28 12:37:16 +0000377#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000378static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000379{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000380 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000381 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000382 if (S_ISDIR(mode))
383 return '/';
384 if (!(all_fmt & LIST_EXEC))
385 return '\0';
386 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000387 return '*';
388 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000389}
390#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000391
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000392
Denis Vlasenko5c759602006-10-28 12:37:16 +0000393#define countdirs(A, B) count_dirs((A), (B), 1)
394#define countsubdirs(A, B) count_dirs((A), (B), 0)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000395static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
Eric Andersen11c65522000-09-07 17:24:47 +0000396{
397 int i, dirs;
398
Denis Vlasenko5c759602006-10-28 12:37:16 +0000399 if (!dn)
400 return 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000401 dirs = 0;
402 for (i = 0; i < nfiles; i++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000403 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000404 if (!S_ISDIR(dn[i]->dstat.st_mode))
405 continue;
406 name = dn[i]->name;
407 if (notsubdirs
408 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
409 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000410 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000411 }
Eric Andersen11c65522000-09-07 17:24:47 +0000412 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000413 return dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000414}
415
Eric Andersen3e6ff902001-03-09 21:24:12 +0000416static int countfiles(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000417{
418 int nfiles;
419 struct dnode *cur;
420
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000421 if (dnp == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000422 return 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000423 nfiles = 0;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000424 for (cur = dnp[0]; cur->next; cur = cur->next)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000425 nfiles++;
Eric Andersen11c65522000-09-07 17:24:47 +0000426 nfiles++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000427 return nfiles;
Eric Andersen11c65522000-09-07 17:24:47 +0000428}
429
430/* get memory to hold an array of pointers */
Eric Andersen3e6ff902001-03-09 21:24:12 +0000431static struct dnode **dnalloc(int num)
Eric Andersen11c65522000-09-07 17:24:47 +0000432{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000433 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000434 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000435
Denis Vlasenko5c759602006-10-28 12:37:16 +0000436 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000437}
438
Denis Vlasenko5c759602006-10-28 12:37:16 +0000439#if ENABLE_FEATURE_LS_RECURSIVE
Rob Landley26314862006-05-02 19:46:52 +0000440static void dfree(struct dnode **dnp, int nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000441{
Rob Landley26314862006-05-02 19:46:52 +0000442 int i;
Eric Andersen11c65522000-09-07 17:24:47 +0000443
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000444 if (dnp == NULL)
445 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000446
Rob Landley26314862006-05-02 19:46:52 +0000447 for (i = 0; i < nfiles; i++) {
448 struct dnode *cur = dnp[i];
Denis Vlasenko5c759602006-10-28 12:37:16 +0000449 if (cur->allocated)
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000450 free((char*)cur->fullname); /* free the filename */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000451 free(cur); /* free the dnode */
Eric Andersen11c65522000-09-07 17:24:47 +0000452 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000453 free(dnp); /* free the array holding the dnode pointers */
Eric Andersen11c65522000-09-07 17:24:47 +0000454}
Rob Landleyc44bc982006-05-28 01:19:06 +0000455#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000456#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000457#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000458
Eric Andersen3e6ff902001-03-09 21:24:12 +0000459static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000460{
461 int dncnt, i, d;
462 struct dnode **dnp;
463
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000464 if (dn == NULL || nfiles < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000465 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000466
467 /* count how many dirs and regular files there are */
Eric Andersencf1189f2000-11-29 21:52:06 +0000468 if (which == SPLIT_SUBDIR)
469 dncnt = countsubdirs(dn, nfiles);
470 else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000471 dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
Eric Andersencf1189f2000-11-29 21:52:06 +0000472 if (which == SPLIT_FILE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000473 dncnt = nfiles - dncnt; /* looking for files */
Eric Andersencf1189f2000-11-29 21:52:06 +0000474 }
Eric Andersen11c65522000-09-07 17:24:47 +0000475
476 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000477 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000478
479 /* copy the entrys into the file or dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000480 for (d = i = 0; i < nfiles; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000481 if (S_ISDIR(dn[i]->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000482 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000483 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
484 continue;
485 name = dn[i]->name;
486 if ((which & SPLIT_DIR)
487 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
488 ) {
489 dnp[d++] = dn[i];
Manuel Novoa III cad53642003-03-19 09:13:01 +0000490 }
491 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
492 dnp[d++] = dn[i];
Eric Andersen11c65522000-09-07 17:24:47 +0000493 }
494 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000495 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000496}
497
Denis Vlasenko5c759602006-10-28 12:37:16 +0000498#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000499static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000500{
Rob Landley425e7582006-05-03 20:22:03 +0000501 struct dnode *d1 = *(struct dnode **)a;
502 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000503 unsigned sort_opts = all_fmt & SORT_MASK;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000504 int dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000505
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000506 dif = 0; /* assume SORT_NAME */
507 // TODO: use pre-initialized function pointer
508 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000509 if (sort_opts == SORT_SIZE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000510 dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000511 } else if (sort_opts == SORT_ATIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000512 dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000513 } else if (sort_opts == SORT_CTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000514 dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000515 } else if (sort_opts == SORT_MTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000516 dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000517 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000518 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000519 /* } else if (sort_opts == SORT_VERSION) { */
520 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000521 }
522
Eric Andersen11c65522000-09-07 17:24:47 +0000523 if (dif == 0) {
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +0000524 /* sort by name - may be a tie_breaker for time or size cmp */
Rob Landleyea224be2006-06-18 20:20:07 +0000525 if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
526 else dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000527 }
528
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000529 if (all_fmt & SORT_REVERSE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000530 dif = -dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000531 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000532 return dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000533}
534
Rob Landley425e7582006-05-03 20:22:03 +0000535static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000536{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000537 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000538}
Rob Landleyea224be2006-06-18 20:20:07 +0000539#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000540#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000541#endif
542
Rob Landleyea224be2006-06-18 20:20:07 +0000543
Eric Andersen3e6ff902001-03-09 21:24:12 +0000544static void showfiles(struct dnode **dn, int nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000545{
546 int i, ncols, nrows, row, nc;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000547 int column = 0;
548 int nexttab = 0;
549 int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
Eric Andersen11c65522000-09-07 17:24:47 +0000550
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000551 if (dn == NULL || nfiles < 1)
552 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000553
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000554 if (all_fmt & STYLE_LONG) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000555 ncols = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000556 } else {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000557 /* find the longest file name, use that as the column width */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000558 for (i = 0; i < nfiles; i++) {
Denys Vlasenkofda8f572009-07-11 22:26:48 +0200559 int len = bb_mbstrlen(dn[i]->name);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000560 if (column_width < len)
561 column_width = len;
562 }
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000563 column_width += tabstops +
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000564 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000565 ((all_fmt & LIST_INO) ? 8 : 0) +
566 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
Glenn L McGrath4d001292003-01-06 01:11:50 +0000567 ncols = (int) (terminal_width / column_width);
Eric Andersen11c65522000-09-07 17:24:47 +0000568 }
569
Eric Andersene57d54b2001-01-30 18:03:11 +0000570 if (ncols > 1) {
571 nrows = nfiles / ncols;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000572 if (nrows * ncols < nfiles)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000573 nrows++; /* round up fractionals */
Eric Andersene57d54b2001-01-30 18:03:11 +0000574 } else {
575 nrows = nfiles;
576 ncols = 1;
577 }
Eric Andersen11c65522000-09-07 17:24:47 +0000578
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000579 for (row = 0; row < nrows; row++) {
580 for (nc = 0; nc < ncols; nc++) {
Eric Andersen79565b62000-08-11 18:10:21 +0000581 /* reach into the array based on the column and row */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000582 i = (nc * nrows) + row; /* assume display by column */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000583 if (all_fmt & DISP_ROWS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000584 i = (row * ncols) + nc; /* display across row */
Eric Andersen11c65522000-09-07 17:24:47 +0000585 if (i < nfiles) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000586 if (column > 0) {
587 nexttab -= column;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000588 printf("%*s", nexttab, "");
589 column += nexttab;
590 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000591 nexttab = column + column_width;
592 column += list_single(dn[i]);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000593 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000594 }
595 putchar('\n');
596 column = 0;
Eric Andersena42982e2000-06-07 17:28:53 +0000597 }
Eric Andersen11c65522000-09-07 17:24:47 +0000598}
599
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000600
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000601static void showdirs(struct dnode **dn, int ndirs, int first)
Eric Andersen11c65522000-09-07 17:24:47 +0000602{
603 int i, nfiles;
604 struct dnode **subdnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000605 int dndirs;
606 struct dnode **dnd;
Eric Andersen11c65522000-09-07 17:24:47 +0000607
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000608 if (dn == NULL || ndirs < 1)
609 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000610
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000611 for (i = 0; i < ndirs; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000612 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000613 if (!first)
Denis Vlasenko4daad902007-09-27 10:20:47 +0000614 bb_putchar('\n');
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000615 first = 0;
616 printf("%s:\n", dn[i]->fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000617 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000618 subdnp = list_dir(dn[i]->fullname);
619 nfiles = countfiles(subdnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000620 if (nfiles > 0) {
621 /* list all files at this level */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000622 dnsort(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000623 showfiles(subdnp, nfiles);
Rob Landley2b8a05a2006-06-20 17:43:01 +0000624 if (ENABLE_FEATURE_LS_RECURSIVE) {
625 if (all_fmt & DISP_RECURSIVE) {
626 /* recursive- list the sub-dirs */
627 dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
628 dndirs = countsubdirs(subdnp, nfiles);
629 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000630 dnsort(dnd, dndirs);
Rob Landley2b8a05a2006-06-20 17:43:01 +0000631 showdirs(dnd, dndirs, 0);
632 /* free the array of dnode pointers to the dirs */
633 free(dnd);
634 }
Eric Andersen11c65522000-09-07 17:24:47 +0000635 }
Rob Landley2b8a05a2006-06-20 17:43:01 +0000636 /* free the dnodes and the fullname mem */
637 dfree(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000638 }
Eric Andersen11c65522000-09-07 17:24:47 +0000639 }
640 }
641}
642
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000643
Glenn L McGrath4d001292003-01-06 01:11:50 +0000644static struct dnode **list_dir(const char *path)
Eric Andersen11c65522000-09-07 17:24:47 +0000645{
646 struct dnode *dn, *cur, **dnp;
647 struct dirent *entry;
648 DIR *dir;
Eric Andersen11c65522000-09-07 17:24:47 +0000649 int i, nfiles;
650
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000651 if (path == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000652 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000653
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000654 dn = NULL;
655 nfiles = 0;
Rob Landleyd921b2e2006-08-03 15:41:12 +0000656 dir = warn_opendir(path);
Eric Andersen11c65522000-09-07 17:24:47 +0000657 if (dir == NULL) {
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000658 exit_code = EXIT_FAILURE;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000659 return NULL; /* could not open the dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000660 }
661 while ((entry = readdir(dir)) != NULL) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000662 char *fullname;
663
Eric Andersen11c65522000-09-07 17:24:47 +0000664 /* are we going to list the file- it may be . or .. or a hidden file */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000665 if (entry->d_name[0] == '.') {
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000666 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
667 && !(all_fmt & DISP_DOT)
668 ) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000669 continue;
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000670 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000671 if (!(all_fmt & DISP_HIDDEN))
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000672 continue;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000673 }
674 fullname = concat_path_file(path, entry->d_name);
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000675 cur = my_stat(fullname, bb_basename(fullname), 0);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000676 if (!cur) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000677 free(fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000678 continue;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000679 }
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +0000680 cur->allocated = 1;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000681 cur->next = dn;
682 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +0000683 nfiles++;
684 }
685 closedir(dir);
686
687 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +0000688 * allocate memory for an array to hold dnode pointers
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000689 */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000690 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000691 return NULL;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000692 dnp = dnalloc(nfiles);
693 for (i = 0, cur = dn; i < nfiles; i++) {
694 dnp[i] = cur; /* save pointer to node in array */
695 cur = cur->next;
Eric Andersen11c65522000-09-07 17:24:47 +0000696 }
697
Denis Vlasenko5c759602006-10-28 12:37:16 +0000698 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000699}
700
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000701
Denis Vlasenko248ce912009-03-03 14:09:04 +0000702static int print_name(const char *name)
703{
704 if (option_mask32 & OPT_Q) {
705#if ENABLE_FEATURE_ASSUME_UNICODE
Denys Vlasenkofda8f572009-07-11 22:26:48 +0200706 int len = 2 + bb_mbstrlen(name);
Denis Vlasenko248ce912009-03-03 14:09:04 +0000707#else
708 int len = 2;
709#endif
710 putchar('"');
711 while (*name) {
712 if (*name == '"') {
713 putchar('\\');
714 len++;
715 }
716 putchar(*name++);
717 if (!ENABLE_FEATURE_ASSUME_UNICODE)
718 len++;
719 }
720 putchar('"');
721 return len;
722 }
723 /* No -Q: */
724#if ENABLE_FEATURE_ASSUME_UNICODE
725 fputs(name, stdout);
Denys Vlasenkofda8f572009-07-11 22:26:48 +0200726 return bb_mbstrlen(name);
Denis Vlasenko248ce912009-03-03 14:09:04 +0000727#else
728 return printf("%s", name);
729#endif
730}
731
732
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000733static int list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000734{
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000735 int column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000736 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000737#if ENABLE_FEATURE_LS_TIMESTAMPS
Eric Andersen11c65522000-09-07 17:24:47 +0000738 char *filetime;
739 time_t ttime, age;
740#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000741#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000742 struct stat info;
743 char append;
744#endif
745
Glenn L McGrath4d001292003-01-06 01:11:50 +0000746 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000747 return 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000748
Denis Vlasenko5c759602006-10-28 12:37:16 +0000749#if ENABLE_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000750 ttime = dn->dstat.st_mtime; /* the default time */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000751 if (all_fmt & TIME_ACCESS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000752 ttime = dn->dstat.st_atime;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000753 if (all_fmt & TIME_CHANGE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000754 ttime = dn->dstat.st_ctime;
755 filetime = ctime(&ttime);
Eric Andersen11c65522000-09-07 17:24:47 +0000756#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000757#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000758 append = append_char(dn->dstat.st_mode);
759#endif
760
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000761 /* Do readlink early, so that if it fails, error message
762 * does not appear *inside* of the "ls -l" line */
763 if (all_fmt & LIST_SYMLINK)
764 if (S_ISLNK(dn->dstat.st_mode))
765 lpath = xmalloc_readlink_or_warn(dn->fullname);
766
767 if (all_fmt & LIST_INO)
768 column += printf("%7lu ", (long) dn->dstat.st_ino);
769 if (all_fmt & LIST_BLOCKS)
770 column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1);
771 if (all_fmt & LIST_MODEBITS)
772 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
773 if (all_fmt & LIST_NLINKS)
774 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000775#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000776 if (all_fmt & LIST_ID_NAME) {
777 if (option_mask32 & OPT_g) {
778 column += printf("%-8.8s",
779 get_cached_username(dn->dstat.st_uid));
780 } else {
781 column += printf("%-8.8s %-8.8s",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000782 get_cached_username(dn->dstat.st_uid),
783 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000784 }
785 }
Eric Andersen11c65522000-09-07 17:24:47 +0000786#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000787 if (all_fmt & LIST_ID_NUMERIC) {
788 if (option_mask32 & OPT_g)
789 column += printf("%-8u", (int) dn->dstat.st_uid);
790 else
791 column += printf("%-8u %-8u",
792 (int) dn->dstat.st_uid,
793 (int) dn->dstat.st_gid);
794 }
795 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
796 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
797 column += printf("%4u, %3u ",
798 (int) major(dn->dstat.st_rdev),
799 (int) minor(dn->dstat.st_rdev));
800 } else {
801 if (all_fmt & LS_DISP_HR) {
802 column += printf("%9s ",
803 make_human_readable_str(dn->dstat.st_size, 1, 0));
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000804 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000805 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000806 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000807 }
808 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000809#if ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000810 if (all_fmt & LIST_FULLTIME)
811 column += printf("%24.24s ", filetime);
812 if (all_fmt & LIST_DATE_TIME)
813 if ((all_fmt & LIST_FULLTIME) == 0) {
814 /* current_time_t ~== time(NULL) */
815 age = current_time_t - ttime;
816 printf("%6.6s ", filetime + 4);
817 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
818 /* hh:mm if less than 6 months old */
819 printf("%5.5s ", filetime + 11);
820 } else {
821 printf(" %4.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000822 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000823 column += 13;
824 }
Eric Andersen11c65522000-09-07 17:24:47 +0000825#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000826#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000827 if (all_fmt & LIST_CONTEXT) {
828 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
829 freecon(dn->sid);
830 }
Eric Andersen9e480452003-07-03 10:07:04 +0000831#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000832 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000833#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000834 if (show_color) {
835 info.st_mode = 0; /* for fgcolor() */
836 lstat(dn->fullname, &info);
837 printf("\033[%u;%um", bold(info.st_mode),
838 fgcolor(info.st_mode));
839 }
840#endif
841 column += print_name(dn->name);
842 if (show_color) {
843 printf("\033[0m");
844 }
845 }
846 if (all_fmt & LIST_SYMLINK) {
847 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
848 printf(" -> ");
849#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
850#if ENABLE_FEATURE_LS_COLOR
851 info.st_mode = 0; /* for fgcolor() */
852#endif
853 if (stat(dn->fullname, &info) == 0) {
854 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000855 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000856#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000857#if ENABLE_FEATURE_LS_COLOR
858 if (show_color) {
859 printf("\033[%u;%um", bold(info.st_mode),
860 fgcolor(info.st_mode));
861 }
862#endif
863 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000864 if (show_color) {
865 printf("\033[0m");
866 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000867 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000868 }
869 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000870#if ENABLE_FEATURE_LS_FILETYPES
871 if (all_fmt & LIST_FILETYPE) {
872 if (append) {
873 putchar(append);
874 column++;
875 }
876 }
877#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000878
Glenn L McGrath4d001292003-01-06 01:11:50 +0000879 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000880}
881
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000882
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000883int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000884{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000885 struct dnode **dnd;
886 struct dnode **dnf;
887 struct dnode **dnp;
888 struct dnode *dn;
889 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000890 unsigned opt;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000891 int nfiles;
Glenn L McGrath792cae52004-01-18 05:15:16 +0000892 int dnfiles;
893 int dndirs;
Glenn L McGrath792cae52004-01-18 05:15:16 +0000894 int i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200895#if ENABLE_FEATURE_LS_COLOR
896 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
897 /* coreutils 6.10:
898 * # ls --color=BOGUS
899 * ls: invalid argument 'BOGUS' for '--color'
900 * Valid arguments are:
901 * 'always', 'yes', 'force'
902 * 'never', 'no', 'none'
903 * 'auto', 'tty', 'if-tty'
904 * (and substrings: "--color=alwa" work too)
905 */
906 static const char ls_longopts[] ALIGN1 =
907 "color\0" Optional_argument "\xff"; /* no short equivalent */
908 static const char color_str[] ALIGN1 =
909 "always\0""yes\0""force\0"
910 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000911 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200912 const char *color_opt = color_str; /* "always" */
913#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +0000914
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000915 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000916
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200917 check_unicode_in_env();
918
Rob Landley2b8a05a2006-06-20 17:43:01 +0000919 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000920 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000921
Denis Vlasenko5c759602006-10-28 12:37:16 +0000922#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200923 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000924 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200925 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +0000926 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000927#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000928
Eric Andersen11c65522000-09-07 17:24:47 +0000929 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200930 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000931#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko1d426652008-03-17 09:09:09 +0000932 opt_complementary = "T+:w+"; /* -T N, -w N */
933 opt = getopt32(argv, ls_options, &tabstops, &terminal_width
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000934 IF_FEATURE_LS_COLOR(, &color_opt));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000935#else
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000936 opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
Manuel Novoa III cad53642003-03-19 09:13:01 +0000937#endif
Eric Andersend07cf592004-02-05 13:52:03 +0000938 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +0000939 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000940 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000941
Denis Vlasenko5c759602006-10-28 12:37:16 +0000942 if (flags & LIST_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000943 all_fmt &= ~LIST_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000944 if (flags & STYLE_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000945 all_fmt &= ~STYLE_MASK;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000946 if (flags & SORT_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000947 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000948 if (flags & DISP_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000949 all_fmt &= ~DISP_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000950 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000951 all_fmt &= ~TIME_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000952 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +0000953 all_fmt |= STYLE_SINGLE;
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000954 /* huh?? opt cannot be 'l' */
955 //if (LS_DISP_HR && opt == 'l')
956 // all_fmt &= ~LS_DISP_HR;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000957 all_fmt |= flags;
958 }
Eric Andersen11c65522000-09-07 17:24:47 +0000959 }
960
Denis Vlasenko5c759602006-10-28 12:37:16 +0000961#if ENABLE_FEATURE_LS_COLOR
962 /* find color bit value - last position for short getopt */
963 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
964 char *p = getenv("LS_COLORS");
965 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000966 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000967 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000968 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200969 if (opt & OPT_color) {
970 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +0000971 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200972 else switch (index_in_substrings(color_str, color_opt)) {
973 case 3:
974 case 4:
975 case 5:
976 if (isatty(STDOUT_FILENO)) {
977 case 0:
978 case 1:
979 case 2:
980 show_color = 1;
981 }
982 }
Paul Fox156dc412005-08-01 19:33:30 +0000983 }
984#endif
985
Eric Andersen11c65522000-09-07 17:24:47 +0000986 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000987 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000988 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +0000989 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
990 if (all_fmt & TIME_CHANGE)
991 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
992 if (all_fmt & TIME_ACCESS)
993 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
994 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000995 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
996 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000997 if (ENABLE_FEATURE_LS_USERNAME)
998 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
999 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001000
Eric Andersen11c65522000-09-07 17:24:47 +00001001 /* choose a display format */
Rob Landleyea224be2006-06-18 20:20:07 +00001002 if (!(all_fmt & STYLE_MASK))
Eric Andersen70060d22004-03-27 10:02:48 +00001003 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001004
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001005 argv += optind;
1006 if (!argv[0])
1007 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001008
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001009 if (argv[1])
1010 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001011
Denis Vlasenko5c759602006-10-28 12:37:16 +00001012 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001013 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001014 nfiles = 0;
1015 do {
Denis Vlasenko11a6f9b2009-03-03 13:20:22 +00001016 /* NB: follow links on command line unless -l or -s */
1017 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001018 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001019 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001020 continue;
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001021 cur->allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001022 cur->next = dn;
1023 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001024 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001025 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001026
1027 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001028 * allocate memory for an array to hold dnode pointers
1029 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001030 dnp = dnalloc(nfiles);
1031 for (i = 0, cur = dn; i < nfiles; i++) {
1032 dnp[i] = cur; /* save pointer to node in array */
1033 cur = cur->next;
Eric Andersen11c65522000-09-07 17:24:47 +00001034 }
1035
Manuel Novoa III cad53642003-03-19 09:13:01 +00001036 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001037 dnsort(dnp, nfiles);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001038 if (nfiles > 0)
1039 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001040 } else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001041 dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
1042 dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
1043 dndirs = countdirs(dnp, nfiles);
1044 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001045 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001046 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001047 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001048 if (ENABLE_FEATURE_CLEAN_UP)
1049 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001050 }
1051 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001052 dnsort(dnd, dndirs);
Glenn L McGrathc4db0832004-03-06 09:12:55 +00001053 showdirs(dnd, dndirs, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001054 if (ENABLE_FEATURE_CLEAN_UP)
1055 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001056 }
1057 }
Rob Landley26314862006-05-02 19:46:52 +00001058 if (ENABLE_FEATURE_CLEAN_UP)
1059 dfree(dnp, nfiles);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001060 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001061}