blob: 20b979db6bb9d91571dd893ce978cf0f91af5128 [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
292#if ENABLE_FEATURE_ASSUME_UNICODE
293/* libbb candidate */
294static size_t mbstrlen(const char *string)
295{
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200296 size_t width = mbstowcs(NULL, string, INT_MAX);
297 if (width == (size_t)-1L)
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000298 return strlen(string);
299 return width;
300}
301#else
302#define mbstrlen(string) strlen(string)
303#endif
304
Matt Kraai33fdae52000-10-13 17:59:43 +0000305
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000306static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000307{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000308 struct stat dstat;
309 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000310 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000311
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000312 if ((all_fmt & FOLLOW_LINKS) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000313#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000314 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000315 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000316 }
Eric Andersen9e480452003-07-03 10:07:04 +0000317#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000318 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000319 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000320 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000321 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000322 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000323 } else {
324#if ENABLE_SELINUX
325 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000326 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000327 }
Eric Andersen9e480452003-07-03 10:07:04 +0000328#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000329 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000330 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000331 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000332 return 0;
333 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000334 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000335
Denis Vlasenko5c759602006-10-28 12:37:16 +0000336 cur = xmalloc(sizeof(struct dnode));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000337 cur->fullname = fullname;
338 cur->name = name;
339 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000340 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000341 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000342}
343
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000344
345/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
346 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
347 * 3/7:multiplexed char/block device)
348 * and we use 0 for unknown and 15 for executables (see below) */
349#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
350#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
351#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
352/* 036 black foreground 050 black background
353 037 red foreground 051 red background
354 040 green foreground 052 green background
355 041 brown foreground 053 brown background
356 042 blue foreground 054 blue background
357 043 magenta (purple) foreground 055 magenta background
358 044 cyan (light blue) foreground 056 cyan background
359 045 gray foreground 057 white background
360*/
361#define COLOR(mode) ( \
362 /*un fi chr dir blk file link sock exe */ \
363 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
364 [TYPEINDEX(mode)])
365/* Select normal (0) [actually "reset all"] or bold (1)
366 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
367 * let's use 7 for "impossible" types, just for fun)
368 * Note: coreutils 6.9 uses inverted red for setuid binaries.
369 */
370#define ATTR(mode) ( \
371 /*un fi chr dir blk file link sock exe */ \
372 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
373 [TYPEINDEX(mode)])
374
Denis Vlasenko5c759602006-10-28 12:37:16 +0000375#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000376/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000377static char fgcolor(mode_t mode)
378{
Rob Landley9947a242006-06-15 22:11:10 +0000379 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000380 return COLOR(0xF000); /* File is executable ... */
381 return COLOR(mode);
382}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000383static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000384{
Rob Landley9947a242006-06-15 22:11:10 +0000385 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000386 return ATTR(0xF000); /* File is executable ... */
387 return ATTR(mode);
388}
389#endif
390
Denis Vlasenko5c759602006-10-28 12:37:16 +0000391#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000392static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000393{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000394 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000395 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000396 if (S_ISDIR(mode))
397 return '/';
398 if (!(all_fmt & LIST_EXEC))
399 return '\0';
400 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000401 return '*';
402 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000403}
404#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000405
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000406
Denis Vlasenko5c759602006-10-28 12:37:16 +0000407#define countdirs(A, B) count_dirs((A), (B), 1)
408#define countsubdirs(A, B) count_dirs((A), (B), 0)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000409static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
Eric Andersen11c65522000-09-07 17:24:47 +0000410{
411 int i, dirs;
412
Denis Vlasenko5c759602006-10-28 12:37:16 +0000413 if (!dn)
414 return 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000415 dirs = 0;
416 for (i = 0; i < nfiles; i++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000417 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000418 if (!S_ISDIR(dn[i]->dstat.st_mode))
419 continue;
420 name = dn[i]->name;
421 if (notsubdirs
422 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
423 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000424 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000425 }
Eric Andersen11c65522000-09-07 17:24:47 +0000426 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000427 return dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000428}
429
Eric Andersen3e6ff902001-03-09 21:24:12 +0000430static int countfiles(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000431{
432 int nfiles;
433 struct dnode *cur;
434
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000435 if (dnp == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000436 return 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000437 nfiles = 0;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000438 for (cur = dnp[0]; cur->next; cur = cur->next)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000439 nfiles++;
Eric Andersen11c65522000-09-07 17:24:47 +0000440 nfiles++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000441 return nfiles;
Eric Andersen11c65522000-09-07 17:24:47 +0000442}
443
444/* get memory to hold an array of pointers */
Eric Andersen3e6ff902001-03-09 21:24:12 +0000445static struct dnode **dnalloc(int num)
Eric Andersen11c65522000-09-07 17:24:47 +0000446{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000447 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000448 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000449
Denis Vlasenko5c759602006-10-28 12:37:16 +0000450 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000451}
452
Denis Vlasenko5c759602006-10-28 12:37:16 +0000453#if ENABLE_FEATURE_LS_RECURSIVE
Rob Landley26314862006-05-02 19:46:52 +0000454static void dfree(struct dnode **dnp, int nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000455{
Rob Landley26314862006-05-02 19:46:52 +0000456 int i;
Eric Andersen11c65522000-09-07 17:24:47 +0000457
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000458 if (dnp == NULL)
459 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000460
Rob Landley26314862006-05-02 19:46:52 +0000461 for (i = 0; i < nfiles; i++) {
462 struct dnode *cur = dnp[i];
Denis Vlasenko5c759602006-10-28 12:37:16 +0000463 if (cur->allocated)
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000464 free((char*)cur->fullname); /* free the filename */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000465 free(cur); /* free the dnode */
Eric Andersen11c65522000-09-07 17:24:47 +0000466 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000467 free(dnp); /* free the array holding the dnode pointers */
Eric Andersen11c65522000-09-07 17:24:47 +0000468}
Rob Landleyc44bc982006-05-28 01:19:06 +0000469#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000470#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000471#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000472
Eric Andersen3e6ff902001-03-09 21:24:12 +0000473static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000474{
475 int dncnt, i, d;
476 struct dnode **dnp;
477
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000478 if (dn == NULL || nfiles < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000479 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000480
481 /* count how many dirs and regular files there are */
Eric Andersencf1189f2000-11-29 21:52:06 +0000482 if (which == SPLIT_SUBDIR)
483 dncnt = countsubdirs(dn, nfiles);
484 else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000485 dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
Eric Andersencf1189f2000-11-29 21:52:06 +0000486 if (which == SPLIT_FILE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000487 dncnt = nfiles - dncnt; /* looking for files */
Eric Andersencf1189f2000-11-29 21:52:06 +0000488 }
Eric Andersen11c65522000-09-07 17:24:47 +0000489
490 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000491 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000492
493 /* copy the entrys into the file or dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000494 for (d = i = 0; i < nfiles; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000495 if (S_ISDIR(dn[i]->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000496 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000497 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
498 continue;
499 name = dn[i]->name;
500 if ((which & SPLIT_DIR)
501 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
502 ) {
503 dnp[d++] = dn[i];
Manuel Novoa III cad53642003-03-19 09:13:01 +0000504 }
505 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
506 dnp[d++] = dn[i];
Eric Andersen11c65522000-09-07 17:24:47 +0000507 }
508 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000509 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000510}
511
Denis Vlasenko5c759602006-10-28 12:37:16 +0000512#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000513static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000514{
Rob Landley425e7582006-05-03 20:22:03 +0000515 struct dnode *d1 = *(struct dnode **)a;
516 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000517 unsigned sort_opts = all_fmt & SORT_MASK;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000518 int dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000519
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000520 dif = 0; /* assume SORT_NAME */
521 // TODO: use pre-initialized function pointer
522 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000523 if (sort_opts == SORT_SIZE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000524 dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000525 } else if (sort_opts == SORT_ATIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000526 dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000527 } else if (sort_opts == SORT_CTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000528 dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000529 } else if (sort_opts == SORT_MTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000530 dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000531 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000532 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000533 /* } else if (sort_opts == SORT_VERSION) { */
534 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000535 }
536
Eric Andersen11c65522000-09-07 17:24:47 +0000537 if (dif == 0) {
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +0000538 /* sort by name - may be a tie_breaker for time or size cmp */
Rob Landleyea224be2006-06-18 20:20:07 +0000539 if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
540 else dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000541 }
542
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000543 if (all_fmt & SORT_REVERSE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000544 dif = -dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000545 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000546 return dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000547}
548
Rob Landley425e7582006-05-03 20:22:03 +0000549static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000550{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000551 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000552}
Rob Landleyea224be2006-06-18 20:20:07 +0000553#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000554#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000555#endif
556
Rob Landleyea224be2006-06-18 20:20:07 +0000557
Eric Andersen3e6ff902001-03-09 21:24:12 +0000558static void showfiles(struct dnode **dn, int nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000559{
560 int i, ncols, nrows, row, nc;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000561 int column = 0;
562 int nexttab = 0;
563 int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
Eric Andersen11c65522000-09-07 17:24:47 +0000564
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000565 if (dn == NULL || nfiles < 1)
566 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000567
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000568 if (all_fmt & STYLE_LONG) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000569 ncols = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000570 } else {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000571 /* find the longest file name, use that as the column width */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000572 for (i = 0; i < nfiles; i++) {
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000573 int len = mbstrlen(dn[i]->name);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000574 if (column_width < len)
575 column_width = len;
576 }
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000577 column_width += tabstops +
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000578 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000579 ((all_fmt & LIST_INO) ? 8 : 0) +
580 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
Glenn L McGrath4d001292003-01-06 01:11:50 +0000581 ncols = (int) (terminal_width / column_width);
Eric Andersen11c65522000-09-07 17:24:47 +0000582 }
583
Eric Andersene57d54b2001-01-30 18:03:11 +0000584 if (ncols > 1) {
585 nrows = nfiles / ncols;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000586 if (nrows * ncols < nfiles)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000587 nrows++; /* round up fractionals */
Eric Andersene57d54b2001-01-30 18:03:11 +0000588 } else {
589 nrows = nfiles;
590 ncols = 1;
591 }
Eric Andersen11c65522000-09-07 17:24:47 +0000592
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000593 for (row = 0; row < nrows; row++) {
594 for (nc = 0; nc < ncols; nc++) {
Eric Andersen79565b62000-08-11 18:10:21 +0000595 /* reach into the array based on the column and row */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000596 i = (nc * nrows) + row; /* assume display by column */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000597 if (all_fmt & DISP_ROWS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000598 i = (row * ncols) + nc; /* display across row */
Eric Andersen11c65522000-09-07 17:24:47 +0000599 if (i < nfiles) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000600 if (column > 0) {
601 nexttab -= column;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000602 printf("%*s", nexttab, "");
603 column += nexttab;
604 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000605 nexttab = column + column_width;
606 column += list_single(dn[i]);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000607 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000608 }
609 putchar('\n');
610 column = 0;
Eric Andersena42982e2000-06-07 17:28:53 +0000611 }
Eric Andersen11c65522000-09-07 17:24:47 +0000612}
613
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000614
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000615static void showdirs(struct dnode **dn, int ndirs, int first)
Eric Andersen11c65522000-09-07 17:24:47 +0000616{
617 int i, nfiles;
618 struct dnode **subdnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000619 int dndirs;
620 struct dnode **dnd;
Eric Andersen11c65522000-09-07 17:24:47 +0000621
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000622 if (dn == NULL || ndirs < 1)
623 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000624
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000625 for (i = 0; i < ndirs; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000626 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000627 if (!first)
Denis Vlasenko4daad902007-09-27 10:20:47 +0000628 bb_putchar('\n');
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000629 first = 0;
630 printf("%s:\n", dn[i]->fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000631 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000632 subdnp = list_dir(dn[i]->fullname);
633 nfiles = countfiles(subdnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000634 if (nfiles > 0) {
635 /* list all files at this level */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000636 dnsort(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000637 showfiles(subdnp, nfiles);
Rob Landley2b8a05a2006-06-20 17:43:01 +0000638 if (ENABLE_FEATURE_LS_RECURSIVE) {
639 if (all_fmt & DISP_RECURSIVE) {
640 /* recursive- list the sub-dirs */
641 dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
642 dndirs = countsubdirs(subdnp, nfiles);
643 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000644 dnsort(dnd, dndirs);
Rob Landley2b8a05a2006-06-20 17:43:01 +0000645 showdirs(dnd, dndirs, 0);
646 /* free the array of dnode pointers to the dirs */
647 free(dnd);
648 }
Eric Andersen11c65522000-09-07 17:24:47 +0000649 }
Rob Landley2b8a05a2006-06-20 17:43:01 +0000650 /* free the dnodes and the fullname mem */
651 dfree(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000652 }
Eric Andersen11c65522000-09-07 17:24:47 +0000653 }
654 }
655}
656
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000657
Glenn L McGrath4d001292003-01-06 01:11:50 +0000658static struct dnode **list_dir(const char *path)
Eric Andersen11c65522000-09-07 17:24:47 +0000659{
660 struct dnode *dn, *cur, **dnp;
661 struct dirent *entry;
662 DIR *dir;
Eric Andersen11c65522000-09-07 17:24:47 +0000663 int i, nfiles;
664
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000665 if (path == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000666 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000667
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000668 dn = NULL;
669 nfiles = 0;
Rob Landleyd921b2e2006-08-03 15:41:12 +0000670 dir = warn_opendir(path);
Eric Andersen11c65522000-09-07 17:24:47 +0000671 if (dir == NULL) {
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000672 exit_code = EXIT_FAILURE;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000673 return NULL; /* could not open the dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000674 }
675 while ((entry = readdir(dir)) != NULL) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000676 char *fullname;
677
Eric Andersen11c65522000-09-07 17:24:47 +0000678 /* are we going to list the file- it may be . or .. or a hidden file */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000679 if (entry->d_name[0] == '.') {
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000680 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
681 && !(all_fmt & DISP_DOT)
682 ) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000683 continue;
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000684 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000685 if (!(all_fmt & DISP_HIDDEN))
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000686 continue;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000687 }
688 fullname = concat_path_file(path, entry->d_name);
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000689 cur = my_stat(fullname, bb_basename(fullname), 0);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000690 if (!cur) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000691 free(fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000692 continue;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000693 }
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +0000694 cur->allocated = 1;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000695 cur->next = dn;
696 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +0000697 nfiles++;
698 }
699 closedir(dir);
700
701 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +0000702 * allocate memory for an array to hold dnode pointers
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000703 */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000704 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000705 return NULL;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000706 dnp = dnalloc(nfiles);
707 for (i = 0, cur = dn; i < nfiles; i++) {
708 dnp[i] = cur; /* save pointer to node in array */
709 cur = cur->next;
Eric Andersen11c65522000-09-07 17:24:47 +0000710 }
711
Denis Vlasenko5c759602006-10-28 12:37:16 +0000712 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000713}
714
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000715
Denis Vlasenko248ce912009-03-03 14:09:04 +0000716static int print_name(const char *name)
717{
718 if (option_mask32 & OPT_Q) {
719#if ENABLE_FEATURE_ASSUME_UNICODE
720 int len = 2 + mbstrlen(name);
721#else
722 int len = 2;
723#endif
724 putchar('"');
725 while (*name) {
726 if (*name == '"') {
727 putchar('\\');
728 len++;
729 }
730 putchar(*name++);
731 if (!ENABLE_FEATURE_ASSUME_UNICODE)
732 len++;
733 }
734 putchar('"');
735 return len;
736 }
737 /* No -Q: */
738#if ENABLE_FEATURE_ASSUME_UNICODE
739 fputs(name, stdout);
740 return mbstrlen(name);
741#else
742 return printf("%s", name);
743#endif
744}
745
746
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000747static int list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000748{
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000749 int column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000750 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000751#if ENABLE_FEATURE_LS_TIMESTAMPS
Eric Andersen11c65522000-09-07 17:24:47 +0000752 char *filetime;
753 time_t ttime, age;
754#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000755#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000756 struct stat info;
757 char append;
758#endif
759
Glenn L McGrath4d001292003-01-06 01:11:50 +0000760 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000761 return 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000762
Denis Vlasenko5c759602006-10-28 12:37:16 +0000763#if ENABLE_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000764 ttime = dn->dstat.st_mtime; /* the default time */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000765 if (all_fmt & TIME_ACCESS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000766 ttime = dn->dstat.st_atime;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000767 if (all_fmt & TIME_CHANGE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000768 ttime = dn->dstat.st_ctime;
769 filetime = ctime(&ttime);
Eric Andersen11c65522000-09-07 17:24:47 +0000770#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000771#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000772 append = append_char(dn->dstat.st_mode);
773#endif
774
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000775 /* Do readlink early, so that if it fails, error message
776 * does not appear *inside* of the "ls -l" line */
777 if (all_fmt & LIST_SYMLINK)
778 if (S_ISLNK(dn->dstat.st_mode))
779 lpath = xmalloc_readlink_or_warn(dn->fullname);
780
781 if (all_fmt & LIST_INO)
782 column += printf("%7lu ", (long) dn->dstat.st_ino);
783 if (all_fmt & LIST_BLOCKS)
784 column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1);
785 if (all_fmt & LIST_MODEBITS)
786 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
787 if (all_fmt & LIST_NLINKS)
788 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000789#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000790 if (all_fmt & LIST_ID_NAME) {
791 if (option_mask32 & OPT_g) {
792 column += printf("%-8.8s",
793 get_cached_username(dn->dstat.st_uid));
794 } else {
795 column += printf("%-8.8s %-8.8s",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000796 get_cached_username(dn->dstat.st_uid),
797 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000798 }
799 }
Eric Andersen11c65522000-09-07 17:24:47 +0000800#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000801 if (all_fmt & LIST_ID_NUMERIC) {
802 if (option_mask32 & OPT_g)
803 column += printf("%-8u", (int) dn->dstat.st_uid);
804 else
805 column += printf("%-8u %-8u",
806 (int) dn->dstat.st_uid,
807 (int) dn->dstat.st_gid);
808 }
809 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
810 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
811 column += printf("%4u, %3u ",
812 (int) major(dn->dstat.st_rdev),
813 (int) minor(dn->dstat.st_rdev));
814 } else {
815 if (all_fmt & LS_DISP_HR) {
816 column += printf("%9s ",
817 make_human_readable_str(dn->dstat.st_size, 1, 0));
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000818 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000819 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000820 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000821 }
822 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000823#if ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000824 if (all_fmt & LIST_FULLTIME)
825 column += printf("%24.24s ", filetime);
826 if (all_fmt & LIST_DATE_TIME)
827 if ((all_fmt & LIST_FULLTIME) == 0) {
828 /* current_time_t ~== time(NULL) */
829 age = current_time_t - ttime;
830 printf("%6.6s ", filetime + 4);
831 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
832 /* hh:mm if less than 6 months old */
833 printf("%5.5s ", filetime + 11);
834 } else {
835 printf(" %4.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000836 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000837 column += 13;
838 }
Eric Andersen11c65522000-09-07 17:24:47 +0000839#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000840#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000841 if (all_fmt & LIST_CONTEXT) {
842 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
843 freecon(dn->sid);
844 }
Eric Andersen9e480452003-07-03 10:07:04 +0000845#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000846 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000847#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000848 if (show_color) {
849 info.st_mode = 0; /* for fgcolor() */
850 lstat(dn->fullname, &info);
851 printf("\033[%u;%um", bold(info.st_mode),
852 fgcolor(info.st_mode));
853 }
854#endif
855 column += print_name(dn->name);
856 if (show_color) {
857 printf("\033[0m");
858 }
859 }
860 if (all_fmt & LIST_SYMLINK) {
861 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
862 printf(" -> ");
863#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
864#if ENABLE_FEATURE_LS_COLOR
865 info.st_mode = 0; /* for fgcolor() */
866#endif
867 if (stat(dn->fullname, &info) == 0) {
868 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000869 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000870#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000871#if ENABLE_FEATURE_LS_COLOR
872 if (show_color) {
873 printf("\033[%u;%um", bold(info.st_mode),
874 fgcolor(info.st_mode));
875 }
876#endif
877 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000878 if (show_color) {
879 printf("\033[0m");
880 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000881 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000882 }
883 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000884#if ENABLE_FEATURE_LS_FILETYPES
885 if (all_fmt & LIST_FILETYPE) {
886 if (append) {
887 putchar(append);
888 column++;
889 }
890 }
891#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000892
Glenn L McGrath4d001292003-01-06 01:11:50 +0000893 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000894}
895
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000896
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000897int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000898{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000899 struct dnode **dnd;
900 struct dnode **dnf;
901 struct dnode **dnp;
902 struct dnode *dn;
903 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000904 unsigned opt;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000905 int nfiles;
Glenn L McGrath792cae52004-01-18 05:15:16 +0000906 int dnfiles;
907 int dndirs;
Glenn L McGrath792cae52004-01-18 05:15:16 +0000908 int i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200909#if ENABLE_FEATURE_LS_COLOR
910 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
911 /* coreutils 6.10:
912 * # ls --color=BOGUS
913 * ls: invalid argument 'BOGUS' for '--color'
914 * Valid arguments are:
915 * 'always', 'yes', 'force'
916 * 'never', 'no', 'none'
917 * 'auto', 'tty', 'if-tty'
918 * (and substrings: "--color=alwa" work too)
919 */
920 static const char ls_longopts[] ALIGN1 =
921 "color\0" Optional_argument "\xff"; /* no short equivalent */
922 static const char color_str[] ALIGN1 =
923 "always\0""yes\0""force\0"
924 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000925 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200926 const char *color_opt = color_str; /* "always" */
927#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +0000928
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000929 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000930
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200931 check_unicode_in_env();
932
Rob Landley2b8a05a2006-06-20 17:43:01 +0000933 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000934 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000935
Denis Vlasenko5c759602006-10-28 12:37:16 +0000936#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200937 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000938 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200939 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +0000940 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000941#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000942
Eric Andersen11c65522000-09-07 17:24:47 +0000943 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200944 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000945#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko1d426652008-03-17 09:09:09 +0000946 opt_complementary = "T+:w+"; /* -T N, -w N */
947 opt = getopt32(argv, ls_options, &tabstops, &terminal_width
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000948 IF_FEATURE_LS_COLOR(, &color_opt));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000949#else
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000950 opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
Manuel Novoa III cad53642003-03-19 09:13:01 +0000951#endif
Eric Andersend07cf592004-02-05 13:52:03 +0000952 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +0000953 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000954 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000955
Denis Vlasenko5c759602006-10-28 12:37:16 +0000956 if (flags & LIST_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000957 all_fmt &= ~LIST_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000958 if (flags & STYLE_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000959 all_fmt &= ~STYLE_MASK;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000960 if (flags & SORT_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000961 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000962 if (flags & DISP_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000963 all_fmt &= ~DISP_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000964 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000965 all_fmt &= ~TIME_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000966 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +0000967 all_fmt |= STYLE_SINGLE;
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000968 /* huh?? opt cannot be 'l' */
969 //if (LS_DISP_HR && opt == 'l')
970 // all_fmt &= ~LS_DISP_HR;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000971 all_fmt |= flags;
972 }
Eric Andersen11c65522000-09-07 17:24:47 +0000973 }
974
Denis Vlasenko5c759602006-10-28 12:37:16 +0000975#if ENABLE_FEATURE_LS_COLOR
976 /* find color bit value - last position for short getopt */
977 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
978 char *p = getenv("LS_COLORS");
979 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000980 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000981 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000982 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200983 if (opt & OPT_color) {
984 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +0000985 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200986 else switch (index_in_substrings(color_str, color_opt)) {
987 case 3:
988 case 4:
989 case 5:
990 if (isatty(STDOUT_FILENO)) {
991 case 0:
992 case 1:
993 case 2:
994 show_color = 1;
995 }
996 }
Paul Fox156dc412005-08-01 19:33:30 +0000997 }
998#endif
999
Eric Andersen11c65522000-09-07 17:24:47 +00001000 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001001 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001002 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001003 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1004 if (all_fmt & TIME_CHANGE)
1005 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1006 if (all_fmt & TIME_ACCESS)
1007 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1008 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001009 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1010 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +00001011 if (ENABLE_FEATURE_LS_USERNAME)
1012 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1013 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001014
Eric Andersen11c65522000-09-07 17:24:47 +00001015 /* choose a display format */
Rob Landleyea224be2006-06-18 20:20:07 +00001016 if (!(all_fmt & STYLE_MASK))
Eric Andersen70060d22004-03-27 10:02:48 +00001017 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001018
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001019 argv += optind;
1020 if (!argv[0])
1021 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001022
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001023 if (argv[1])
1024 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001025
Denis Vlasenko5c759602006-10-28 12:37:16 +00001026 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001027 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001028 nfiles = 0;
1029 do {
Denis Vlasenko11a6f9b2009-03-03 13:20:22 +00001030 /* NB: follow links on command line unless -l or -s */
1031 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001032 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001033 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001034 continue;
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001035 cur->allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001036 cur->next = dn;
1037 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001038 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001039 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001040
1041 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001042 * allocate memory for an array to hold dnode pointers
1043 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001044 dnp = dnalloc(nfiles);
1045 for (i = 0, cur = dn; i < nfiles; i++) {
1046 dnp[i] = cur; /* save pointer to node in array */
1047 cur = cur->next;
Eric Andersen11c65522000-09-07 17:24:47 +00001048 }
1049
Manuel Novoa III cad53642003-03-19 09:13:01 +00001050 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001051 dnsort(dnp, nfiles);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001052 if (nfiles > 0)
1053 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001054 } else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001055 dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
1056 dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
1057 dndirs = countdirs(dnp, nfiles);
1058 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001059 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001060 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001061 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001062 if (ENABLE_FEATURE_CLEAN_UP)
1063 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001064 }
1065 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001066 dnsort(dnd, dndirs);
Glenn L McGrathc4db0832004-03-06 09:12:55 +00001067 showdirs(dnd, dndirs, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001068 if (ENABLE_FEATURE_CLEAN_UP)
1069 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001070 }
1071 }
Rob Landley26314862006-05-02 19:46:52 +00001072 if (ENABLE_FEATURE_CLEAN_UP)
1073 dfree(dnp, nfiles);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001074 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001075}