blob: f515f9f8f97b5fad7061a5eba9fd20a894b7e546 [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 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02006 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
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:
Denys Vlasenko87c150c2009-10-03 01:14:15 +020020 * 1. hidden files can make column width too large
Erik Andersen9ffdaa62000-02-11 21:55:04 +000021 *
Eric Andersencc8ed391999-10-05 16:24:54 +000022 * NON-OPTIMAL BEHAVIOUR:
23 * 1. autowidth reads directories twice
24 * 2. if you do a short directory listing without filetype characters
25 * appended, there's no need to stat each one
26 * PORTABILITY:
27 * 1. requires lstat (BSD) - how do you do it without?
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000028 *
29 * [2009-03]
30 * ls sorts listing now, and supports almost all options.
Eric Andersencc8ed391999-10-05 16:24:54 +000031 */
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000032#include "libbb.h"
Denys Vlasenko42a8fd02009-07-11 21:36:13 +020033#include "unicode.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +000034
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +000035
Denis Vlasenko99912ca2007-04-10 15:43:37 +000036/* This is a NOEXEC applet. Be very careful! */
37
Eric Andersenf1142c52001-02-20 06:16:29 +000038
Denis Vlasenko245f91b2009-03-09 22:37:23 +000039#if ENABLE_FTPD
40/* ftpd uses ls, and without timestamps Mozilla won't understand
41 * ftpd's LIST output.
42 */
43# undef CONFIG_FEATURE_LS_TIMESTAMPS
44# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000045# undef IF_FEATURE_LS_TIMESTAMPS
46# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +000047# define CONFIG_FEATURE_LS_TIMESTAMPS 1
48# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000049# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
50# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +000051#endif
52
53
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000054enum {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000055TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
56COLUMN_GAP = 2, /* includes the file type char */
57
58/* what is the overall style of the listing */
Denys Vlasenkod87815d2010-12-19 05:43:44 +010059STYLE_COLUMNAR = 1 << 21, /* many records per line */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000060STYLE_LONG = 2 << 21, /* one record per line, extended info */
61STYLE_SINGLE = 3 << 21, /* one record per line */
62STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +000063
Eric Andersen11c65522000-09-07 17:24:47 +000064/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
65/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000066LIST_INO = 1 << 0,
67LIST_BLOCKS = 1 << 1,
68LIST_MODEBITS = 1 << 2,
69LIST_NLINKS = 1 << 3,
70LIST_ID_NAME = 1 << 4,
71LIST_ID_NUMERIC = 1 << 5,
72LIST_CONTEXT = 1 << 6,
73LIST_SIZE = 1 << 7,
Denis Vlasenko3a014b82009-03-21 19:11:23 +000074//LIST_DEV = 1 << 8, - unused, synonym to LIST_SIZE
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000075LIST_DATE_TIME = 1 << 9,
76LIST_FULLTIME = 1 << 10,
77LIST_FILENAME = 1 << 11,
78LIST_SYMLINK = 1 << 12,
79LIST_FILETYPE = 1 << 13,
80LIST_EXEC = 1 << 14,
81LIST_MASK = (LIST_EXEC << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +000082
83/* what files will be displayed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000084DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */
Denis Vlasenko656f7462006-10-28 13:02:55 +000085DISP_HIDDEN = 1 << 16, /* show filenames starting with . */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000086DISP_DOT = 1 << 17, /* show . and .. */
87DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */
88DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */
89DISP_ROWS = 1 << 20, /* print across rows */
90DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +000091
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000092/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
93SORT_FORWARD = 0, /* sort in reverse order */
94SORT_REVERSE = 1 << 27, /* sort in reverse order */
Eric Andersen11c65522000-09-07 17:24:47 +000095
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000096SORT_NAME = 0, /* sort by file name */
97SORT_SIZE = 1 << 28, /* sort by file size */
98SORT_ATIME = 2 << 28, /* sort by last access time */
99SORT_CTIME = 3 << 28, /* sort by last change time */
100SORT_MTIME = 4 << 28, /* sort by last modification time */
101SORT_VERSION = 5 << 28, /* sort by version */
102SORT_EXT = 6 << 28, /* sort by file name extension */
103SORT_DIR = 7 << 28, /* sort by file or directory */
104SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000105
Eric Andersen11c65522000-09-07 17:24:47 +0000106/* which of the three times will be used */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000107TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
108TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
109TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000110
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000111FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
Eric Andersencc8ed391999-10-05 16:24:54 +0000112
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000113LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000114
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000115LIST_SHORT = LIST_FILENAME,
116LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
117 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
118
119SPLIT_DIR = 1,
120SPLIT_FILE = 0,
121SPLIT_SUBDIR = 2,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000122};
Eric Andersencc8ed391999-10-05 16:24:54 +0000123
Denis Vlasenko248ce912009-03-03 14:09:04 +0000124/* "[-]Cadil1", POSIX mandated options, busybox always supports */
125/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
126/* "[-]Q" GNU option? busybox always supports */
127/* "[-]Ak" GNU options, busybox always supports */
128/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
129/* "[-]p", POSIX non-mandated options, busybox optionally supports */
130/* "[-]SXvThw", GNU options, busybox optionally supports */
131/* "[-]K", SELinux mandated options, busybox optionally supports */
132/* "[-]e", I think we made this one up */
133static const char ls_options[] ALIGN1 =
134 "Cadil1gnsxQAk" /* 13 opts, total 13 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000135 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
136 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
137 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
138 IF_FEATURE_LS_FOLLOWLINKS("L") /* 1, 24 */
139 IF_FEATURE_LS_RECURSIVE("R") /* 1, 25 */
140 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */
Denys Vlasenko55083632009-07-02 14:25:51 +0200141 IF_SELINUX("KZ") /* 2, 28 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000142 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000143 ;
144enum {
145 //OPT_C = (1 << 0),
146 //OPT_a = (1 << 1),
147 //OPT_d = (1 << 2),
148 //OPT_i = (1 << 3),
149 //OPT_l = (1 << 4),
150 //OPT_1 = (1 << 5),
151 OPT_g = (1 << 6),
152 //OPT_n = (1 << 7),
153 //OPT_s = (1 << 8),
154 //OPT_x = (1 << 9),
155 OPT_Q = (1 << 10),
156 //OPT_A = (1 << 11),
157 //OPT_k = (1 << 12),
Denys Vlasenko55083632009-07-02 14:25:51 +0200158 OPTBIT_color = 13
159 + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
160 + 4 * ENABLE_FEATURE_LS_SORTFILES
161 + 2 * ENABLE_FEATURE_LS_FILETYPES
162 + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
163 + 1 * ENABLE_FEATURE_LS_RECURSIVE
164 + 1 * ENABLE_FEATURE_HUMAN_READABLE
165 + 2 * ENABLE_SELINUX
166 + 2 * ENABLE_FEATURE_AUTOWIDTH,
167 OPT_color = 1 << OPTBIT_color,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000168};
169
Denis Vlasenko248ce912009-03-03 14:09:04 +0000170/* TODO: simple toggles may be stored as OPT_xxx bits instead */
171static const unsigned opt_flags[] = {
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100172 LIST_SHORT | STYLE_COLUMNAR, /* C */
173 DISP_HIDDEN | DISP_DOT, /* a */
174 DISP_NOLIST, /* d */
175 LIST_INO, /* i */
176 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
177 LIST_SHORT | STYLE_SINGLE, /* 1 */
178 0, /* g (don't show owner) - handled via OPT_g */
179 LIST_ID_NUMERIC, /* n */
180 LIST_BLOCKS, /* s */
181 LIST_SHORT | DISP_ROWS | STYLE_COLUMNAR, /* x */
182 0, /* Q (quote filename) - handled via OPT_Q */
183 DISP_HIDDEN, /* A */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000184 ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
185#if ENABLE_FEATURE_LS_TIMESTAMPS
186 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100187 LIST_FULLTIME, /* e */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000188 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
189 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
190#endif
191#if ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100192 SORT_SIZE, /* S */
193 SORT_EXT, /* X */
194 SORT_REVERSE, /* r */
195 SORT_VERSION, /* v */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000196#endif
197#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100198 LIST_FILETYPE | LIST_EXEC, /* F */
199 LIST_FILETYPE, /* p */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000200#endif
201#if ENABLE_FEATURE_LS_FOLLOWLINKS
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100202 FOLLOW_LINKS, /* L */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000203#endif
204#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100205 DISP_RECURSIVE, /* R */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000206#endif
207#if ENABLE_FEATURE_HUMAN_READABLE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100208 LS_DISP_HR, /* h */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000209#endif
210#if ENABLE_SELINUX
211 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
212#endif
213#if ENABLE_SELINUX
214 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
215#endif
216 (1U<<31)
217 /* options after Z are not processed through opt_flags:
218 * T, w - ignored
219 */
220};
221
222
Eric Andersen11c65522000-09-07 17:24:47 +0000223/*
224 * a directory entry and its stat info are stored here
225 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200226struct dnode {
227 const char *name; /* the dir entry name */
228 const char *fullname; /* the dir entry name */
229 struct dnode *next; /* point at the next node */
230 smallint fname_allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000231 struct stat dstat; /* the file stat info */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000232 IF_SELINUX(security_context_t sid;)
Eric Andersen11c65522000-09-07 17:24:47 +0000233};
Eric Andersen11c65522000-09-07 17:24:47 +0000234
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000235struct globals {
236#if ENABLE_FEATURE_LS_COLOR
237 smallint show_color;
238#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000239 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000240 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000241#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000242 unsigned tabstops; // = COLUMN_GAP;
243 unsigned terminal_width; // = TERMINAL_WIDTH;
244#endif
245#if ENABLE_FEATURE_LS_TIMESTAMPS
246 /* Do time() just once. Saves one syscall per file for "ls -l" */
247 time_t current_time_t;
248#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100249} FIX_ALIASING;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000250#define G (*(struct globals*)&bb_common_bufsiz1)
251#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200252# define show_color (G.show_color )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000253#else
254enum { show_color = 0 };
255#endif
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200256#define exit_code (G.exit_code )
257#define all_fmt (G.all_fmt )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000258#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200259# define tabstops (G.tabstops )
260# define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000261#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000262enum {
263 tabstops = COLUMN_GAP,
264 terminal_width = TERMINAL_WIDTH,
265};
Eric Andersen11c65522000-09-07 17:24:47 +0000266#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000267#define current_time_t (G.current_time_t)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000268#define INIT_G() do { \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200269 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000270 memset(&G, 0, sizeof(G)); \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000271 IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
272 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
273 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000274} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000275
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000276
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000277static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000278{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000279 struct stat dstat;
280 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000281 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000282
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000283 if ((all_fmt & FOLLOW_LINKS) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000284#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000285 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000286 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000287 }
Eric Andersen9e480452003-07-03 10:07:04 +0000288#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000289 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000290 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000291 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000292 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000293 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000294 } else {
295#if ENABLE_SELINUX
296 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000297 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000298 }
Eric Andersen9e480452003-07-03 10:07:04 +0000299#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000300 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000301 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000302 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000303 return 0;
304 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000305 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000306
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200307 cur = xmalloc(sizeof(*cur));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000308 cur->fullname = fullname;
309 cur->name = name;
310 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000311 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000312 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000313}
314
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000315/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
316 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
317 * 3/7:multiplexed char/block device)
318 * and we use 0 for unknown and 15 for executables (see below) */
319#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
320#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
321#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
322/* 036 black foreground 050 black background
323 037 red foreground 051 red background
324 040 green foreground 052 green background
325 041 brown foreground 053 brown background
326 042 blue foreground 054 blue background
327 043 magenta (purple) foreground 055 magenta background
328 044 cyan (light blue) foreground 056 cyan background
329 045 gray foreground 057 white background
330*/
331#define COLOR(mode) ( \
332 /*un fi chr dir blk file link sock exe */ \
333 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
334 [TYPEINDEX(mode)])
335/* Select normal (0) [actually "reset all"] or bold (1)
336 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
337 * let's use 7 for "impossible" types, just for fun)
338 * Note: coreutils 6.9 uses inverted red for setuid binaries.
339 */
340#define ATTR(mode) ( \
341 /*un fi chr dir blk file link sock exe */ \
342 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
343 [TYPEINDEX(mode)])
344
Denis Vlasenko5c759602006-10-28 12:37:16 +0000345#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000346/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000347static char fgcolor(mode_t mode)
348{
Rob Landley9947a242006-06-15 22:11:10 +0000349 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000350 return COLOR(0xF000); /* File is executable ... */
351 return COLOR(mode);
352}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000353static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000354{
Rob Landley9947a242006-06-15 22:11:10 +0000355 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000356 return ATTR(0xF000); /* File is executable ... */
357 return ATTR(mode);
358}
359#endif
360
Denis Vlasenko5c759602006-10-28 12:37:16 +0000361#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000362static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000363{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000364 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000365 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000366 if (S_ISDIR(mode))
367 return '/';
368 if (!(all_fmt & LIST_EXEC))
369 return '\0';
370 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000371 return '*';
372 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000373}
374#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000375
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200376static unsigned count_dirs(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000377{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200378 unsigned dirs, all;
Eric Andersen11c65522000-09-07 17:24:47 +0000379
Denis Vlasenko5c759602006-10-28 12:37:16 +0000380 if (!dn)
381 return 0;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200382
383 dirs = all = 0;
384 for (; *dn; dn++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000385 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200386
387 all++;
388 if (!S_ISDIR((*dn)->dstat.st_mode))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000389 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200390 name = (*dn)->name;
391 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
392 /* or if it's not . or .. */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200393 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000394 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000395 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000396 }
Eric Andersen11c65522000-09-07 17:24:47 +0000397 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200398 return which != SPLIT_FILE ? dirs : all - dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000399}
400
Eric Andersen11c65522000-09-07 17:24:47 +0000401/* get memory to hold an array of pointers */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200402static struct dnode **dnalloc(unsigned num)
Eric Andersen11c65522000-09-07 17:24:47 +0000403{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000404 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000405 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000406
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200407 num++; /* so that we have terminating NULL */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000408 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000409}
410
Denis Vlasenko5c759602006-10-28 12:37:16 +0000411#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200412static void dfree(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000413{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200414 unsigned i;
Eric Andersen11c65522000-09-07 17:24:47 +0000415
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000416 if (dnp == NULL)
417 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000418
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200419 for (i = 0; dnp[i]; i++) {
Rob Landley26314862006-05-02 19:46:52 +0000420 struct dnode *cur = dnp[i];
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200421 if (cur->fname_allocated)
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200422 free((char*)cur->fullname);
423 free(cur);
Eric Andersen11c65522000-09-07 17:24:47 +0000424 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200425 free(dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000426}
Rob Landleyc44bc982006-05-28 01:19:06 +0000427#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000428#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000429#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000430
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200431/* Returns NULL-terminated malloced vector of pointers (or NULL) */
432static struct dnode **splitdnarray(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000433{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200434 unsigned dncnt, d;
Eric Andersen11c65522000-09-07 17:24:47 +0000435 struct dnode **dnp;
436
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200437 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000438 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000439
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200440 /* count how many dirs or files there are */
441 dncnt = count_dirs(dn, which);
Eric Andersen11c65522000-09-07 17:24:47 +0000442
443 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000444 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000445
446 /* copy the entrys into the file or dir array */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200447 for (d = 0; *dn; dn++) {
448 if (S_ISDIR((*dn)->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000449 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200450
Denis Vlasenko5c759602006-10-28 12:37:16 +0000451 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
452 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200453 name = (*dn)->name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000454 if ((which & SPLIT_DIR)
455 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
456 ) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200457 dnp[d++] = *dn;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000458 }
459 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200460 dnp[d++] = *dn;
Eric Andersen11c65522000-09-07 17:24:47 +0000461 }
462 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000463 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000464}
465
Denis Vlasenko5c759602006-10-28 12:37:16 +0000466#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000467static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000468{
Rob Landley425e7582006-05-03 20:22:03 +0000469 struct dnode *d1 = *(struct dnode **)a;
470 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000471 unsigned sort_opts = all_fmt & SORT_MASK;
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100472 off_t dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000473
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000474 dif = 0; /* assume SORT_NAME */
475 // TODO: use pre-initialized function pointer
476 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000477 if (sort_opts == SORT_SIZE) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100478 dif = (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000479 } else if (sort_opts == SORT_ATIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100480 dif = (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000481 } else if (sort_opts == SORT_CTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100482 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000483 } else if (sort_opts == SORT_MTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100484 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000485 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000486 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000487 /* } else if (sort_opts == SORT_VERSION) { */
488 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000489 }
Eric Andersen11c65522000-09-07 17:24:47 +0000490 if (dif == 0) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100491 /* sort by name, or tie_breaker for other sorts */
492 if (ENABLE_LOCALE_SUPPORT)
493 dif = strcoll(d1->name, d2->name);
494 else
495 dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000496 }
497
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100498 /* Make dif fit into an int */
499 if (sizeof(dif) > sizeof(int)) {
Denys Vlasenko6b01b712010-01-24 22:52:21 +0100500 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
501 /* shift leaving only "int" worth of bits */
502 if (dif != 0) {
503 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100504 }
Eric Andersen11c65522000-09-07 17:24:47 +0000505 }
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100506
507 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000508}
509
Rob Landley425e7582006-05-03 20:22:03 +0000510static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000511{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000512 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000513}
Rob Landleyea224be2006-06-18 20:20:07 +0000514#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000515#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000516#endif
517
Rob Landleyea224be2006-06-18 20:20:07 +0000518
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100519static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000520{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100521 unsigned len;
522 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000523
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100524 // TODO: quote tab as \t, etc, if -Q
525 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000526
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100527 if (!(option_mask32 & OPT_Q)) {
528 return uni_stat.unicode_width;
529 }
530
531 len = 2 + uni_stat.unicode_width;
532 while (*name) {
533 if (*name == '"' || *name == '\\') {
534 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000535 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100536 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000537 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100538 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000539}
540
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000541
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100542/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100543 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100544 * STYLE_SINGLE and STYLE_LONG don't care.
545 * coreutils 7.2 also supports:
546 * ls -b (--escape) = octal escapes (although it doesn't look like working)
547 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200548 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100549static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200550{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100551 unsigned len;
552 uni_stat_t uni_stat;
553
554 // TODO: quote tab as \t, etc, if -Q
555 name = printable_string(&uni_stat, name);
556
557 if (!(option_mask32 & OPT_Q)) {
558 fputs(name, stdout);
559 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200560 }
561
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100562 len = 2 + uni_stat.unicode_width;
563 putchar('"');
564 while (*name) {
565 if (*name == '"' || *name == '\\') {
566 putchar('\\');
567 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000568 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700569 putchar(*name);
570 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000571 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100572 putchar('"');
573 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000574}
575
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100576/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100577 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100578 * STYLE_SINGLE and STYLE_LONG don't care.
579 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200580static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000581{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200582 unsigned column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000583 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000584#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000585 struct stat info;
586 char append;
587#endif
588
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200589 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000590 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000591 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200592 */
Eric Andersen11c65522000-09-07 17:24:47 +0000593
Denis Vlasenko5c759602006-10-28 12:37:16 +0000594#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000595 append = append_char(dn->dstat.st_mode);
596#endif
597
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000598 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100599 * does not appear *inside* the "ls -l" line */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000600 if (all_fmt & LIST_SYMLINK)
601 if (S_ISLNK(dn->dstat.st_mode))
602 lpath = xmalloc_readlink_or_warn(dn->fullname);
603
604 if (all_fmt & LIST_INO)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100605 column += printf("%7llu ", (long long) dn->dstat.st_ino);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000606 if (all_fmt & LIST_BLOCKS)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100607 column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000608 if (all_fmt & LIST_MODEBITS)
609 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
610 if (all_fmt & LIST_NLINKS)
611 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000612#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000613 if (all_fmt & LIST_ID_NAME) {
614 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100615 column += printf("%-8.8s ",
Denys Vlasenko4909fec2010-11-06 00:46:57 +0100616 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000617 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100618 column += printf("%-8.8s %-8.8s ",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000619 get_cached_username(dn->dstat.st_uid),
620 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000621 }
622 }
Eric Andersen11c65522000-09-07 17:24:47 +0000623#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000624 if (all_fmt & LIST_ID_NUMERIC) {
625 if (option_mask32 & OPT_g)
Denys Vlasenko4909fec2010-11-06 00:46:57 +0100626 column += printf("%-8u ", (int) dn->dstat.st_gid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000627 else
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100628 column += printf("%-8u %-8u ",
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000629 (int) dn->dstat.st_uid,
630 (int) dn->dstat.st_gid);
631 }
632 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
633 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
634 column += printf("%4u, %3u ",
635 (int) major(dn->dstat.st_rdev),
636 (int) minor(dn->dstat.st_rdev));
637 } else {
638 if (all_fmt & LS_DISP_HR) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100639 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200640 /* print st_size, show one fractional, use suffixes */
641 make_human_readable_str(dn->dstat.st_size, 1, 0)
642 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000643 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000644 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000645 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000646 }
647 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000648#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100649 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
650 char *filetime;
651 time_t ttime = dn->dstat.st_mtime;
652 if (all_fmt & TIME_ACCESS)
653 ttime = dn->dstat.st_atime;
654 if (all_fmt & TIME_CHANGE)
655 ttime = dn->dstat.st_ctime;
656 filetime = ctime(&ttime);
657 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
658 if (all_fmt & LIST_FULLTIME)
659 column += printf("%.24s ", filetime);
660 else { /* LIST_DATE_TIME */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000661 /* current_time_t ~== time(NULL) */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100662 time_t age = current_time_t - ttime;
663 printf("%.6s ", filetime + 4); /* "Jun 30" */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000664 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
665 /* hh:mm if less than 6 months old */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100666 printf("%.5s ", filetime + 11);
667 } else { /* year. buggy if year > 9999 ;) */
668 printf(" %.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000669 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000670 column += 13;
671 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100672 }
Eric Andersen11c65522000-09-07 17:24:47 +0000673#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000674#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000675 if (all_fmt & LIST_CONTEXT) {
676 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
677 freecon(dn->sid);
678 }
Eric Andersen9e480452003-07-03 10:07:04 +0000679#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000680 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000681#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000682 if (show_color) {
683 info.st_mode = 0; /* for fgcolor() */
684 lstat(dn->fullname, &info);
685 printf("\033[%u;%um", bold(info.st_mode),
686 fgcolor(info.st_mode));
687 }
688#endif
689 column += print_name(dn->name);
690 if (show_color) {
691 printf("\033[0m");
692 }
693 }
694 if (all_fmt & LIST_SYMLINK) {
695 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
696 printf(" -> ");
697#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
698#if ENABLE_FEATURE_LS_COLOR
699 info.st_mode = 0; /* for fgcolor() */
700#endif
701 if (stat(dn->fullname, &info) == 0) {
702 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000703 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000704#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000705#if ENABLE_FEATURE_LS_COLOR
706 if (show_color) {
707 printf("\033[%u;%um", bold(info.st_mode),
708 fgcolor(info.st_mode));
709 }
710#endif
711 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000712 if (show_color) {
713 printf("\033[0m");
714 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000715 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000716 }
717 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000718#if ENABLE_FEATURE_LS_FILETYPES
719 if (all_fmt & LIST_FILETYPE) {
720 if (append) {
721 putchar(append);
722 column++;
723 }
724 }
725#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000726
Glenn L McGrath4d001292003-01-06 01:11:50 +0000727 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000728}
729
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100730static void showfiles(struct dnode **dn, unsigned nfiles)
731{
732 unsigned i, ncols, nrows, row, nc;
733 unsigned column = 0;
734 unsigned nexttab = 0;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100735 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100736
737 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
738 ncols = 1;
739 } else {
740 /* find the longest file name, use that as the column width */
741 for (i = 0; dn[i]; i++) {
742 int len = calc_name_len(dn[i]->name);
743 if (column_width < len)
744 column_width = len;
745 }
746 column_width += tabstops +
747 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
748 ((all_fmt & LIST_INO) ? 8 : 0) +
749 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
750 ncols = (int) (terminal_width / column_width);
751 }
752
753 if (ncols > 1) {
754 nrows = nfiles / ncols;
755 if (nrows * ncols < nfiles)
756 nrows++; /* round up fractionals */
757 } else {
758 nrows = nfiles;
759 ncols = 1;
760 }
761
762 for (row = 0; row < nrows; row++) {
763 for (nc = 0; nc < ncols; nc++) {
764 /* reach into the array based on the column and row */
765 if (all_fmt & DISP_ROWS)
766 i = (row * ncols) + nc; /* display across row */
767 else
768 i = (nc * nrows) + row; /* display by column */
769 if (i < nfiles) {
770 if (column > 0) {
771 nexttab -= column;
772 printf("%*s", nexttab, "");
773 column += nexttab;
774 }
775 nexttab = column + column_width;
776 column += list_single(dn[i]);
777 }
778 }
779 putchar('\n');
780 column = 0;
781 }
782}
783
784
785#if ENABLE_DESKTOP
786/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
787 * If any of the -l, -n, -s options is specified, each list
788 * of files within the directory shall be preceded by a
789 * status line indicating the number of file system blocks
790 * occupied by files in the directory in 512-byte units if
791 * the -k option is not specified, or 1024-byte units if the
792 * -k option is specified, rounded up to the next integral
793 * number of units.
794 */
795/* by Jorgen Overgaard (jorgen AT antistaten.se) */
796static off_t calculate_blocks(struct dnode **dn)
797{
798 uoff_t blocks = 1;
799 if (dn) {
800 while (*dn) {
801 /* st_blocks is in 512 byte blocks */
802 blocks += (*dn)->dstat.st_blocks;
803 dn++;
804 }
805 }
806
807 /* Even though standard says use 512 byte blocks, coreutils use 1k */
808 /* Actually, we round up by calculating (blocks + 1) / 2,
809 * "+ 1" was done when we initialized blocks to 1 */
810 return blocks >> 1;
811}
812#endif
813
814
815static struct dnode **list_dir(const char *, unsigned *);
816
817static void showdirs(struct dnode **dn, int first)
818{
819 unsigned nfiles;
820 unsigned dndirs;
821 struct dnode **subdnp;
822 struct dnode **dnd;
823
824 /* Never happens:
825 if (dn == NULL || ndirs < 1) {
826 return;
827 }
828 */
829
830 for (; *dn; dn++) {
831 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
832 if (!first)
833 bb_putchar('\n');
834 first = 0;
835 printf("%s:\n", (*dn)->fullname);
836 }
837 subdnp = list_dir((*dn)->fullname, &nfiles);
838#if ENABLE_DESKTOP
839 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
840 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
841#endif
842 if (nfiles > 0) {
843 /* list all files at this level */
844 dnsort(subdnp, nfiles);
845 showfiles(subdnp, nfiles);
846 if (ENABLE_FEATURE_LS_RECURSIVE
847 && (all_fmt & DISP_RECURSIVE)
848 ) {
849 /* recursive - list the sub-dirs */
850 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
851 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
852 if (dndirs > 0) {
853 dnsort(dnd, dndirs);
854 showdirs(dnd, 0);
855 /* free the array of dnode pointers to the dirs */
856 free(dnd);
857 }
858 }
859 /* free the dnodes and the fullname mem */
860 dfree(subdnp);
861 }
862 }
863}
864
865
866/* Returns NULL-terminated malloced vector of pointers (or NULL) */
867static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
868{
869 struct dnode *dn, *cur, **dnp;
870 struct dirent *entry;
871 DIR *dir;
872 unsigned i, nfiles;
873
874 /* Never happens:
875 if (path == NULL)
876 return NULL;
877 */
878
879 *nfiles_p = 0;
880 dir = warn_opendir(path);
881 if (dir == NULL) {
882 exit_code = EXIT_FAILURE;
883 return NULL; /* could not open the dir */
884 }
885 dn = NULL;
886 nfiles = 0;
887 while ((entry = readdir(dir)) != NULL) {
888 char *fullname;
889
890 /* are we going to list the file- it may be . or .. or a hidden file */
891 if (entry->d_name[0] == '.') {
892 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
893 && !(all_fmt & DISP_DOT)
894 ) {
895 continue;
896 }
897 if (!(all_fmt & DISP_HIDDEN))
898 continue;
899 }
900 fullname = concat_path_file(path, entry->d_name);
901 cur = my_stat(fullname, bb_basename(fullname), 0);
902 if (!cur) {
903 free(fullname);
904 continue;
905 }
906 cur->fname_allocated = 1;
907 cur->next = dn;
908 dn = cur;
909 nfiles++;
910 }
911 closedir(dir);
912
913 if (dn == NULL)
914 return NULL;
915
916 /* now that we know how many files there are
917 * allocate memory for an array to hold dnode pointers
918 */
919 *nfiles_p = nfiles;
920 dnp = dnalloc(nfiles);
921 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
922 dnp[i] = dn; /* save pointer to node in array */
923 dn = dn->next;
924 if (!dn)
925 break;
926 }
927
928 return dnp;
929}
930
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000931
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000932int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000933{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000934 struct dnode **dnd;
935 struct dnode **dnf;
936 struct dnode **dnp;
937 struct dnode *dn;
938 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000939 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200940 unsigned nfiles;
941 unsigned dnfiles;
942 unsigned dndirs;
943 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200944#if ENABLE_FEATURE_LS_COLOR
945 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
946 /* coreutils 6.10:
947 * # ls --color=BOGUS
948 * ls: invalid argument 'BOGUS' for '--color'
949 * Valid arguments are:
950 * 'always', 'yes', 'force'
951 * 'never', 'no', 'none'
952 * 'auto', 'tty', 'if-tty'
953 * (and substrings: "--color=alwa" work too)
954 */
955 static const char ls_longopts[] ALIGN1 =
956 "color\0" Optional_argument "\xff"; /* no short equivalent */
957 static const char color_str[] ALIGN1 =
958 "always\0""yes\0""force\0"
959 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000960 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200961 const char *color_opt = color_str; /* "always" */
962#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +0000963
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000964 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000965
Denys Vlasenko28055022010-01-04 20:49:58 +0100966 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200967
Rob Landley2b8a05a2006-06-20 17:43:01 +0000968 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000969 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000970
Denis Vlasenko5c759602006-10-28 12:37:16 +0000971#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200972 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000973 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200974 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +0000975 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000976#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000977
Eric Andersen11c65522000-09-07 17:24:47 +0000978 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200979 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +0100980 opt_complementary =
981 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
982 * in some pairs of opts, only last one takes effect:
983 */
984 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES("t-S:S-t")) /* time/size */
985 // ":H-L:L-H:" - we don't have -H
986 // ":m-l:l-m:" - we don't have -m
987 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
988 ":C-1:1-C" /* bycols/oneline */
989 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
990 ":c-u:u-c" /* mtime/atime */
991 /* -T NUM, -w NUM: */
992 IF_FEATURE_AUTOWIDTH(":T+:w+");
993 opt = getopt32(argv, ls_options
994 IF_FEATURE_AUTOWIDTH(, &tabstops, &terminal_width)
995 IF_FEATURE_LS_COLOR(, &color_opt)
996 );
Eric Andersend07cf592004-02-05 13:52:03 +0000997 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +0000998 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000999 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001000
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001001 if (flags & STYLE_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001002 all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001003 if (flags & SORT_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001004 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001005 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001006 all_fmt &= ~TIME_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001007
Denis Vlasenko5c759602006-10-28 12:37:16 +00001008 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +00001009 all_fmt |= STYLE_SINGLE;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001010 all_fmt |= flags;
1011 }
Eric Andersen11c65522000-09-07 17:24:47 +00001012 }
1013
Denis Vlasenko5c759602006-10-28 12:37:16 +00001014#if ENABLE_FEATURE_LS_COLOR
1015 /* find color bit value - last position for short getopt */
1016 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1017 char *p = getenv("LS_COLORS");
1018 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001019 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001020 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001021 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001022 if (opt & OPT_color) {
1023 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +00001024 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001025 else switch (index_in_substrings(color_str, color_opt)) {
1026 case 3:
1027 case 4:
1028 case 5:
1029 if (isatty(STDOUT_FILENO)) {
1030 case 0:
1031 case 1:
1032 case 2:
1033 show_color = 1;
1034 }
1035 }
Paul Fox156dc412005-08-01 19:33:30 +00001036 }
1037#endif
1038
Eric Andersen11c65522000-09-07 17:24:47 +00001039 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001040 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001041 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001042 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1043 if (all_fmt & TIME_CHANGE)
1044 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1045 if (all_fmt & TIME_ACCESS)
1046 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1047 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001048 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1049 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +00001050 if (ENABLE_FEATURE_LS_USERNAME)
1051 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1052 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001053
Denys Vlasenkof3137462010-12-19 05:05:34 +01001054 /* choose a display format if one was not already specified by an option */
Rob Landleyea224be2006-06-18 20:20:07 +00001055 if (!(all_fmt & STYLE_MASK))
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001056 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001057
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001058 argv += optind;
1059 if (!argv[0])
1060 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001061
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001062 if (argv[1])
1063 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001064
Denis Vlasenko5c759602006-10-28 12:37:16 +00001065 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001066 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001067 nfiles = 0;
1068 do {
Denis Vlasenko11a6f9b2009-03-03 13:20:22 +00001069 /* NB: follow links on command line unless -l or -s */
1070 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001071 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001072 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001073 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001074 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001075 cur->next = dn;
1076 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001077 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001078 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001079
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001080 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1081 if (nfiles == 0)
1082 return exit_code;
1083
Eric Andersen11c65522000-09-07 17:24:47 +00001084 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001085 * allocate memory for an array to hold dnode pointers
1086 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001087 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001088 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1089 dnp[i] = dn; /* save pointer to node in array */
1090 dn = dn->next;
1091 if (!dn)
1092 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001093 }
1094
Manuel Novoa III cad53642003-03-19 09:13:01 +00001095 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001096 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001097 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001098 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001099 dnd = splitdnarray(dnp, SPLIT_DIR);
1100 dnf = splitdnarray(dnp, SPLIT_FILE);
1101 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001102 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001103 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001104 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001105 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001106 if (ENABLE_FEATURE_CLEAN_UP)
1107 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001108 }
1109 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001110 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001111 showdirs(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001112 if (ENABLE_FEATURE_CLEAN_UP)
1113 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001114 }
1115 }
Rob Landley26314862006-05-02 19:46:52 +00001116 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001117 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001118 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001119}