blob: 02e26bedbb7cbeb5101bad188fed20f7a7ca7eea [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 Vlasenko163d8642010-12-19 06:16:28 +0100158 OPTBIT_F = 13
Denys Vlasenko55083632009-07-02 14:25:51 +0200159 + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko163d8642010-12-19 06:16:28 +0100160 + 4 * ENABLE_FEATURE_LS_SORTFILES,
161 OPTBIT_color = OPTBIT_F
Denys Vlasenko55083632009-07-02 14:25:51 +0200162 + 2 * ENABLE_FEATURE_LS_FILETYPES
163 + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
164 + 1 * ENABLE_FEATURE_LS_RECURSIVE
165 + 1 * ENABLE_FEATURE_HUMAN_READABLE
166 + 2 * ENABLE_SELINUX
167 + 2 * ENABLE_FEATURE_AUTOWIDTH,
Denys Vlasenko163d8642010-12-19 06:16:28 +0100168 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
Denys Vlasenko55083632009-07-02 14:25:51 +0200169 OPT_color = 1 << OPTBIT_color,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000170};
171
Denis Vlasenko248ce912009-03-03 14:09:04 +0000172/* TODO: simple toggles may be stored as OPT_xxx bits instead */
173static const unsigned opt_flags[] = {
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100174 LIST_SHORT | STYLE_COLUMNAR, /* C */
175 DISP_HIDDEN | DISP_DOT, /* a */
176 DISP_NOLIST, /* d */
177 LIST_INO, /* i */
178 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
179 LIST_SHORT | STYLE_SINGLE, /* 1 */
180 0, /* g (don't show owner) - handled via OPT_g */
181 LIST_ID_NUMERIC, /* n */
182 LIST_BLOCKS, /* s */
183 LIST_SHORT | DISP_ROWS | STYLE_COLUMNAR, /* x */
184 0, /* Q (quote filename) - handled via OPT_Q */
185 DISP_HIDDEN, /* A */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000186 ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
187#if ENABLE_FEATURE_LS_TIMESTAMPS
188 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100189 LIST_FULLTIME, /* e */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000190 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
191 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
192#endif
193#if ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100194 SORT_SIZE, /* S */
195 SORT_EXT, /* X */
196 SORT_REVERSE, /* r */
197 SORT_VERSION, /* v */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000198#endif
199#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100200 LIST_FILETYPE | LIST_EXEC, /* F */
201 LIST_FILETYPE, /* p */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000202#endif
203#if ENABLE_FEATURE_LS_FOLLOWLINKS
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100204 FOLLOW_LINKS, /* L */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000205#endif
206#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100207 DISP_RECURSIVE, /* R */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000208#endif
209#if ENABLE_FEATURE_HUMAN_READABLE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100210 LS_DISP_HR, /* h */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000211#endif
212#if ENABLE_SELINUX
213 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
214#endif
215#if ENABLE_SELINUX
216 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
217#endif
218 (1U<<31)
219 /* options after Z are not processed through opt_flags:
220 * T, w - ignored
221 */
222};
223
224
Eric Andersen11c65522000-09-07 17:24:47 +0000225/*
226 * a directory entry and its stat info are stored here
227 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200228struct dnode {
229 const char *name; /* the dir entry name */
230 const char *fullname; /* the dir entry name */
231 struct dnode *next; /* point at the next node */
232 smallint fname_allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000233 struct stat dstat; /* the file stat info */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000234 IF_SELINUX(security_context_t sid;)
Eric Andersen11c65522000-09-07 17:24:47 +0000235};
Eric Andersen11c65522000-09-07 17:24:47 +0000236
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000237struct globals {
238#if ENABLE_FEATURE_LS_COLOR
239 smallint show_color;
240#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000241 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000242 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000243#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000244 unsigned tabstops; // = COLUMN_GAP;
245 unsigned terminal_width; // = TERMINAL_WIDTH;
246#endif
247#if ENABLE_FEATURE_LS_TIMESTAMPS
248 /* Do time() just once. Saves one syscall per file for "ls -l" */
249 time_t current_time_t;
250#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100251} FIX_ALIASING;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000252#define G (*(struct globals*)&bb_common_bufsiz1)
253#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200254# define show_color (G.show_color )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000255#else
256enum { show_color = 0 };
257#endif
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200258#define exit_code (G.exit_code )
259#define all_fmt (G.all_fmt )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000260#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200261# define tabstops (G.tabstops )
262# define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000263#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000264enum {
265 tabstops = COLUMN_GAP,
266 terminal_width = TERMINAL_WIDTH,
267};
Eric Andersen11c65522000-09-07 17:24:47 +0000268#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000269#define current_time_t (G.current_time_t)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000270#define INIT_G() do { \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200271 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000272 memset(&G, 0, sizeof(G)); \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000273 IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
274 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
275 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000276} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000277
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000278
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000279static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000280{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000281 struct stat dstat;
282 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000283 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000284
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000285 if ((all_fmt & FOLLOW_LINKS) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000286#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000287 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000288 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000289 }
Eric Andersen9e480452003-07-03 10:07:04 +0000290#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000291 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000292 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000293 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000294 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000295 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000296 } else {
297#if ENABLE_SELINUX
298 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000299 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000300 }
Eric Andersen9e480452003-07-03 10:07:04 +0000301#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000302 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000303 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000304 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000305 return 0;
306 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000307 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000308
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200309 cur = xmalloc(sizeof(*cur));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000310 cur->fullname = fullname;
311 cur->name = name;
312 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000313 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000314 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000315}
316
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000317/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
318 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
319 * 3/7:multiplexed char/block device)
320 * and we use 0 for unknown and 15 for executables (see below) */
321#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
322#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
323#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
324/* 036 black foreground 050 black background
325 037 red foreground 051 red background
326 040 green foreground 052 green background
327 041 brown foreground 053 brown background
328 042 blue foreground 054 blue background
329 043 magenta (purple) foreground 055 magenta background
330 044 cyan (light blue) foreground 056 cyan background
331 045 gray foreground 057 white background
332*/
333#define COLOR(mode) ( \
334 /*un fi chr dir blk file link sock exe */ \
335 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
336 [TYPEINDEX(mode)])
337/* Select normal (0) [actually "reset all"] or bold (1)
338 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
339 * let's use 7 for "impossible" types, just for fun)
340 * Note: coreutils 6.9 uses inverted red for setuid binaries.
341 */
342#define ATTR(mode) ( \
343 /*un fi chr dir blk file link sock exe */ \
344 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
345 [TYPEINDEX(mode)])
346
Denis Vlasenko5c759602006-10-28 12:37:16 +0000347#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000348/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000349static char fgcolor(mode_t mode)
350{
Rob Landley9947a242006-06-15 22:11:10 +0000351 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000352 return COLOR(0xF000); /* File is executable ... */
353 return COLOR(mode);
354}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000355static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000356{
Rob Landley9947a242006-06-15 22:11:10 +0000357 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000358 return ATTR(0xF000); /* File is executable ... */
359 return ATTR(mode);
360}
361#endif
362
Denis Vlasenko5c759602006-10-28 12:37:16 +0000363#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000364static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000365{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000366 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000367 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000368 if (S_ISDIR(mode))
369 return '/';
370 if (!(all_fmt & LIST_EXEC))
371 return '\0';
372 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000373 return '*';
374 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000375}
376#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000377
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200378static unsigned count_dirs(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000379{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200380 unsigned dirs, all;
Eric Andersen11c65522000-09-07 17:24:47 +0000381
Denis Vlasenko5c759602006-10-28 12:37:16 +0000382 if (!dn)
383 return 0;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200384
385 dirs = all = 0;
386 for (; *dn; dn++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000387 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200388
389 all++;
390 if (!S_ISDIR((*dn)->dstat.st_mode))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000391 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200392 name = (*dn)->name;
393 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
394 /* or if it's not . or .. */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200395 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000396 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000397 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000398 }
Eric Andersen11c65522000-09-07 17:24:47 +0000399 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200400 return which != SPLIT_FILE ? dirs : all - dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000401}
402
Eric Andersen11c65522000-09-07 17:24:47 +0000403/* get memory to hold an array of pointers */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200404static struct dnode **dnalloc(unsigned num)
Eric Andersen11c65522000-09-07 17:24:47 +0000405{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000406 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000407 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000408
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200409 num++; /* so that we have terminating NULL */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000410 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000411}
412
Denis Vlasenko5c759602006-10-28 12:37:16 +0000413#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200414static void dfree(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000415{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200416 unsigned i;
Eric Andersen11c65522000-09-07 17:24:47 +0000417
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000418 if (dnp == NULL)
419 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000420
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200421 for (i = 0; dnp[i]; i++) {
Rob Landley26314862006-05-02 19:46:52 +0000422 struct dnode *cur = dnp[i];
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200423 if (cur->fname_allocated)
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200424 free((char*)cur->fullname);
425 free(cur);
Eric Andersen11c65522000-09-07 17:24:47 +0000426 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200427 free(dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000428}
Rob Landleyc44bc982006-05-28 01:19:06 +0000429#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000430#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000431#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000432
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200433/* Returns NULL-terminated malloced vector of pointers (or NULL) */
434static struct dnode **splitdnarray(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000435{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200436 unsigned dncnt, d;
Eric Andersen11c65522000-09-07 17:24:47 +0000437 struct dnode **dnp;
438
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200439 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000440 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000441
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200442 /* count how many dirs or files there are */
443 dncnt = count_dirs(dn, which);
Eric Andersen11c65522000-09-07 17:24:47 +0000444
445 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000446 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000447
448 /* copy the entrys into the file or dir array */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200449 for (d = 0; *dn; dn++) {
450 if (S_ISDIR((*dn)->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000451 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200452
Denis Vlasenko5c759602006-10-28 12:37:16 +0000453 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
454 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200455 name = (*dn)->name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000456 if ((which & SPLIT_DIR)
457 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
458 ) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200459 dnp[d++] = *dn;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000460 }
461 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200462 dnp[d++] = *dn;
Eric Andersen11c65522000-09-07 17:24:47 +0000463 }
464 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000465 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000466}
467
Denis Vlasenko5c759602006-10-28 12:37:16 +0000468#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000469static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000470{
Rob Landley425e7582006-05-03 20:22:03 +0000471 struct dnode *d1 = *(struct dnode **)a;
472 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000473 unsigned sort_opts = all_fmt & SORT_MASK;
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100474 off_t dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000475
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000476 dif = 0; /* assume SORT_NAME */
477 // TODO: use pre-initialized function pointer
478 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000479 if (sort_opts == SORT_SIZE) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100480 dif = (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000481 } else if (sort_opts == SORT_ATIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100482 dif = (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000483 } else if (sort_opts == SORT_CTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100484 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000485 } else if (sort_opts == SORT_MTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100486 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000487 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000488 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000489 /* } else if (sort_opts == SORT_VERSION) { */
490 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000491 }
Eric Andersen11c65522000-09-07 17:24:47 +0000492 if (dif == 0) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100493 /* sort by name, or tie_breaker for other sorts */
494 if (ENABLE_LOCALE_SUPPORT)
495 dif = strcoll(d1->name, d2->name);
496 else
497 dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000498 }
499
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100500 /* Make dif fit into an int */
501 if (sizeof(dif) > sizeof(int)) {
Denys Vlasenko6b01b712010-01-24 22:52:21 +0100502 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
503 /* shift leaving only "int" worth of bits */
504 if (dif != 0) {
505 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100506 }
Eric Andersen11c65522000-09-07 17:24:47 +0000507 }
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100508
509 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000510}
511
Rob Landley425e7582006-05-03 20:22:03 +0000512static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000513{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000514 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000515}
Rob Landleyea224be2006-06-18 20:20:07 +0000516#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000517#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000518#endif
519
Rob Landleyea224be2006-06-18 20:20:07 +0000520
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100521static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000522{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100523 unsigned len;
524 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000525
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100526 // TODO: quote tab as \t, etc, if -Q
527 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000528
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100529 if (!(option_mask32 & OPT_Q)) {
530 return uni_stat.unicode_width;
531 }
532
533 len = 2 + uni_stat.unicode_width;
534 while (*name) {
535 if (*name == '"' || *name == '\\') {
536 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000537 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100538 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000539 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100540 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000541}
542
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000543
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100544/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100545 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100546 * STYLE_SINGLE and STYLE_LONG don't care.
547 * coreutils 7.2 also supports:
548 * ls -b (--escape) = octal escapes (although it doesn't look like working)
549 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200550 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100551static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200552{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100553 unsigned len;
554 uni_stat_t uni_stat;
555
556 // TODO: quote tab as \t, etc, if -Q
557 name = printable_string(&uni_stat, name);
558
559 if (!(option_mask32 & OPT_Q)) {
560 fputs(name, stdout);
561 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200562 }
563
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100564 len = 2 + uni_stat.unicode_width;
565 putchar('"');
566 while (*name) {
567 if (*name == '"' || *name == '\\') {
568 putchar('\\');
569 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000570 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700571 putchar(*name);
572 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000573 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100574 putchar('"');
575 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000576}
577
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100578/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100579 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100580 * STYLE_SINGLE and STYLE_LONG don't care.
581 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200582static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000583{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200584 unsigned column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000585 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000586#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000587 struct stat info;
588 char append;
589#endif
590
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200591 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000592 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000593 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200594 */
Eric Andersen11c65522000-09-07 17:24:47 +0000595
Denis Vlasenko5c759602006-10-28 12:37:16 +0000596#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000597 append = append_char(dn->dstat.st_mode);
598#endif
599
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000600 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100601 * does not appear *inside* the "ls -l" line */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000602 if (all_fmt & LIST_SYMLINK)
603 if (S_ISLNK(dn->dstat.st_mode))
604 lpath = xmalloc_readlink_or_warn(dn->fullname);
605
606 if (all_fmt & LIST_INO)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100607 column += printf("%7llu ", (long long) dn->dstat.st_ino);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000608 if (all_fmt & LIST_BLOCKS)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100609 column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000610 if (all_fmt & LIST_MODEBITS)
611 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
612 if (all_fmt & LIST_NLINKS)
613 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000614#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000615 if (all_fmt & LIST_ID_NAME) {
616 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100617 column += printf("%-8.8s ",
Denys Vlasenko4909fec2010-11-06 00:46:57 +0100618 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000619 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100620 column += printf("%-8.8s %-8.8s ",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000621 get_cached_username(dn->dstat.st_uid),
622 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000623 }
624 }
Eric Andersen11c65522000-09-07 17:24:47 +0000625#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000626 if (all_fmt & LIST_ID_NUMERIC) {
627 if (option_mask32 & OPT_g)
Denys Vlasenko4909fec2010-11-06 00:46:57 +0100628 column += printf("%-8u ", (int) dn->dstat.st_gid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000629 else
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100630 column += printf("%-8u %-8u ",
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000631 (int) dn->dstat.st_uid,
632 (int) dn->dstat.st_gid);
633 }
634 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
635 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
636 column += printf("%4u, %3u ",
637 (int) major(dn->dstat.st_rdev),
638 (int) minor(dn->dstat.st_rdev));
639 } else {
640 if (all_fmt & LS_DISP_HR) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100641 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200642 /* print st_size, show one fractional, use suffixes */
643 make_human_readable_str(dn->dstat.st_size, 1, 0)
644 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000645 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000646 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000647 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000648 }
649 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000650#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100651 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
652 char *filetime;
653 time_t ttime = dn->dstat.st_mtime;
654 if (all_fmt & TIME_ACCESS)
655 ttime = dn->dstat.st_atime;
656 if (all_fmt & TIME_CHANGE)
657 ttime = dn->dstat.st_ctime;
658 filetime = ctime(&ttime);
659 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
660 if (all_fmt & LIST_FULLTIME)
661 column += printf("%.24s ", filetime);
662 else { /* LIST_DATE_TIME */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000663 /* current_time_t ~== time(NULL) */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100664 time_t age = current_time_t - ttime;
665 printf("%.6s ", filetime + 4); /* "Jun 30" */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000666 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
667 /* hh:mm if less than 6 months old */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100668 printf("%.5s ", filetime + 11);
669 } else { /* year. buggy if year > 9999 ;) */
670 printf(" %.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000671 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000672 column += 13;
673 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100674 }
Eric Andersen11c65522000-09-07 17:24:47 +0000675#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000676#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000677 if (all_fmt & LIST_CONTEXT) {
678 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
679 freecon(dn->sid);
680 }
Eric Andersen9e480452003-07-03 10:07:04 +0000681#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000682 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000683#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000684 if (show_color) {
685 info.st_mode = 0; /* for fgcolor() */
686 lstat(dn->fullname, &info);
687 printf("\033[%u;%um", bold(info.st_mode),
688 fgcolor(info.st_mode));
689 }
690#endif
691 column += print_name(dn->name);
692 if (show_color) {
693 printf("\033[0m");
694 }
695 }
696 if (all_fmt & LIST_SYMLINK) {
697 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
698 printf(" -> ");
699#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
700#if ENABLE_FEATURE_LS_COLOR
701 info.st_mode = 0; /* for fgcolor() */
702#endif
703 if (stat(dn->fullname, &info) == 0) {
704 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000705 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000706#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000707#if ENABLE_FEATURE_LS_COLOR
708 if (show_color) {
709 printf("\033[%u;%um", bold(info.st_mode),
710 fgcolor(info.st_mode));
711 }
712#endif
713 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000714 if (show_color) {
715 printf("\033[0m");
716 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000717 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000718 }
719 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000720#if ENABLE_FEATURE_LS_FILETYPES
721 if (all_fmt & LIST_FILETYPE) {
722 if (append) {
723 putchar(append);
724 column++;
725 }
726 }
727#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000728
Glenn L McGrath4d001292003-01-06 01:11:50 +0000729 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000730}
731
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100732static void showfiles(struct dnode **dn, unsigned nfiles)
733{
734 unsigned i, ncols, nrows, row, nc;
735 unsigned column = 0;
736 unsigned nexttab = 0;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100737 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100738
739 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
740 ncols = 1;
741 } else {
742 /* find the longest file name, use that as the column width */
743 for (i = 0; dn[i]; i++) {
744 int len = calc_name_len(dn[i]->name);
745 if (column_width < len)
746 column_width = len;
747 }
748 column_width += tabstops +
749 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
750 ((all_fmt & LIST_INO) ? 8 : 0) +
751 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
752 ncols = (int) (terminal_width / column_width);
753 }
754
755 if (ncols > 1) {
756 nrows = nfiles / ncols;
757 if (nrows * ncols < nfiles)
758 nrows++; /* round up fractionals */
759 } else {
760 nrows = nfiles;
761 ncols = 1;
762 }
763
764 for (row = 0; row < nrows; row++) {
765 for (nc = 0; nc < ncols; nc++) {
766 /* reach into the array based on the column and row */
767 if (all_fmt & DISP_ROWS)
768 i = (row * ncols) + nc; /* display across row */
769 else
770 i = (nc * nrows) + row; /* display by column */
771 if (i < nfiles) {
772 if (column > 0) {
773 nexttab -= column;
774 printf("%*s", nexttab, "");
775 column += nexttab;
776 }
777 nexttab = column + column_width;
778 column += list_single(dn[i]);
779 }
780 }
781 putchar('\n');
782 column = 0;
783 }
784}
785
786
787#if ENABLE_DESKTOP
788/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
789 * If any of the -l, -n, -s options is specified, each list
790 * of files within the directory shall be preceded by a
791 * status line indicating the number of file system blocks
792 * occupied by files in the directory in 512-byte units if
793 * the -k option is not specified, or 1024-byte units if the
794 * -k option is specified, rounded up to the next integral
795 * number of units.
796 */
797/* by Jorgen Overgaard (jorgen AT antistaten.se) */
798static off_t calculate_blocks(struct dnode **dn)
799{
800 uoff_t blocks = 1;
801 if (dn) {
802 while (*dn) {
803 /* st_blocks is in 512 byte blocks */
804 blocks += (*dn)->dstat.st_blocks;
805 dn++;
806 }
807 }
808
809 /* Even though standard says use 512 byte blocks, coreutils use 1k */
810 /* Actually, we round up by calculating (blocks + 1) / 2,
811 * "+ 1" was done when we initialized blocks to 1 */
812 return blocks >> 1;
813}
814#endif
815
816
817static struct dnode **list_dir(const char *, unsigned *);
818
819static void showdirs(struct dnode **dn, int first)
820{
821 unsigned nfiles;
822 unsigned dndirs;
823 struct dnode **subdnp;
824 struct dnode **dnd;
825
826 /* Never happens:
827 if (dn == NULL || ndirs < 1) {
828 return;
829 }
830 */
831
832 for (; *dn; dn++) {
833 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
834 if (!first)
835 bb_putchar('\n');
836 first = 0;
837 printf("%s:\n", (*dn)->fullname);
838 }
839 subdnp = list_dir((*dn)->fullname, &nfiles);
840#if ENABLE_DESKTOP
841 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
842 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
843#endif
844 if (nfiles > 0) {
845 /* list all files at this level */
846 dnsort(subdnp, nfiles);
847 showfiles(subdnp, nfiles);
848 if (ENABLE_FEATURE_LS_RECURSIVE
849 && (all_fmt & DISP_RECURSIVE)
850 ) {
851 /* recursive - list the sub-dirs */
852 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
853 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
854 if (dndirs > 0) {
855 dnsort(dnd, dndirs);
856 showdirs(dnd, 0);
857 /* free the array of dnode pointers to the dirs */
858 free(dnd);
859 }
860 }
861 /* free the dnodes and the fullname mem */
862 dfree(subdnp);
863 }
864 }
865}
866
867
868/* Returns NULL-terminated malloced vector of pointers (or NULL) */
869static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
870{
871 struct dnode *dn, *cur, **dnp;
872 struct dirent *entry;
873 DIR *dir;
874 unsigned i, nfiles;
875
876 /* Never happens:
877 if (path == NULL)
878 return NULL;
879 */
880
881 *nfiles_p = 0;
882 dir = warn_opendir(path);
883 if (dir == NULL) {
884 exit_code = EXIT_FAILURE;
885 return NULL; /* could not open the dir */
886 }
887 dn = NULL;
888 nfiles = 0;
889 while ((entry = readdir(dir)) != NULL) {
890 char *fullname;
891
892 /* are we going to list the file- it may be . or .. or a hidden file */
893 if (entry->d_name[0] == '.') {
894 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
895 && !(all_fmt & DISP_DOT)
896 ) {
897 continue;
898 }
899 if (!(all_fmt & DISP_HIDDEN))
900 continue;
901 }
902 fullname = concat_path_file(path, entry->d_name);
903 cur = my_stat(fullname, bb_basename(fullname), 0);
904 if (!cur) {
905 free(fullname);
906 continue;
907 }
908 cur->fname_allocated = 1;
909 cur->next = dn;
910 dn = cur;
911 nfiles++;
912 }
913 closedir(dir);
914
915 if (dn == NULL)
916 return NULL;
917
918 /* now that we know how many files there are
919 * allocate memory for an array to hold dnode pointers
920 */
921 *nfiles_p = nfiles;
922 dnp = dnalloc(nfiles);
923 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
924 dnp[i] = dn; /* save pointer to node in array */
925 dn = dn->next;
926 if (!dn)
927 break;
928 }
929
930 return dnp;
931}
932
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000933
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000934int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000935{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000936 struct dnode **dnd;
937 struct dnode **dnf;
938 struct dnode **dnp;
939 struct dnode *dn;
940 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000941 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200942 unsigned nfiles;
943 unsigned dnfiles;
944 unsigned dndirs;
945 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200946#if ENABLE_FEATURE_LS_COLOR
947 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
948 /* coreutils 6.10:
949 * # ls --color=BOGUS
950 * ls: invalid argument 'BOGUS' for '--color'
951 * Valid arguments are:
952 * 'always', 'yes', 'force'
953 * 'never', 'no', 'none'
954 * 'auto', 'tty', 'if-tty'
955 * (and substrings: "--color=alwa" work too)
956 */
957 static const char ls_longopts[] ALIGN1 =
958 "color\0" Optional_argument "\xff"; /* no short equivalent */
959 static const char color_str[] ALIGN1 =
960 "always\0""yes\0""force\0"
961 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000962 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200963 const char *color_opt = color_str; /* "always" */
964#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +0000965
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000966 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000967
Denys Vlasenko28055022010-01-04 20:49:58 +0100968 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200969
Rob Landley2b8a05a2006-06-20 17:43:01 +0000970 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000971 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000972
Denis Vlasenko5c759602006-10-28 12:37:16 +0000973#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200974 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000975 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200976 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +0000977 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000978#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000979
Eric Andersen11c65522000-09-07 17:24:47 +0000980 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200981 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +0100982 opt_complementary =
983 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
984 * in some pairs of opts, only last one takes effect:
985 */
986 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES("t-S:S-t")) /* time/size */
987 // ":H-L:L-H:" - we don't have -H
988 // ":m-l:l-m:" - we don't have -m
989 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
990 ":C-1:1-C" /* bycols/oneline */
991 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
992 ":c-u:u-c" /* mtime/atime */
993 /* -T NUM, -w NUM: */
994 IF_FEATURE_AUTOWIDTH(":T+:w+");
995 opt = getopt32(argv, ls_options
996 IF_FEATURE_AUTOWIDTH(, &tabstops, &terminal_width)
997 IF_FEATURE_LS_COLOR(, &color_opt)
998 );
Eric Andersend07cf592004-02-05 13:52:03 +0000999 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001000 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +00001001 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001002
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001003 if (flags & STYLE_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001004 all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001005 if (flags & SORT_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001006 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001007 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001008 all_fmt &= ~TIME_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001009
Denis Vlasenko5c759602006-10-28 12:37:16 +00001010 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +00001011 all_fmt |= STYLE_SINGLE;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001012 all_fmt |= flags;
1013 }
Eric Andersen11c65522000-09-07 17:24:47 +00001014 }
1015
Denis Vlasenko5c759602006-10-28 12:37:16 +00001016#if ENABLE_FEATURE_LS_COLOR
1017 /* find color bit value - last position for short getopt */
1018 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1019 char *p = getenv("LS_COLORS");
1020 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001021 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001022 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001023 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001024 if (opt & OPT_color) {
1025 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +00001026 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001027 else switch (index_in_substrings(color_str, color_opt)) {
1028 case 3:
1029 case 4:
1030 case 5:
1031 if (isatty(STDOUT_FILENO)) {
1032 case 0:
1033 case 1:
1034 case 2:
1035 show_color = 1;
1036 }
1037 }
Paul Fox156dc412005-08-01 19:33:30 +00001038 }
1039#endif
1040
Eric Andersen11c65522000-09-07 17:24:47 +00001041 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001042 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001043 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001044 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1045 if (all_fmt & TIME_CHANGE)
1046 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1047 if (all_fmt & TIME_ACCESS)
1048 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1049 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001050 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1051 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +00001052 if (ENABLE_FEATURE_LS_USERNAME)
1053 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1054 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001055
Denys Vlasenkof3137462010-12-19 05:05:34 +01001056 /* choose a display format if one was not already specified by an option */
Rob Landleyea224be2006-06-18 20:20:07 +00001057 if (!(all_fmt & STYLE_MASK))
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001058 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001059
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001060 argv += optind;
1061 if (!argv[0])
1062 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001063
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001064 if (argv[1])
1065 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001066
Denis Vlasenko5c759602006-10-28 12:37:16 +00001067 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001068 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001069 nfiles = 0;
1070 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001071 /* NB: follow links on command line unless -l, -s or -F */
1072 cur = my_stat(*argv, *argv,
1073 !((all_fmt & (STYLE_LONG|LIST_BLOCKS)) || (option_mask32 & OPT_F))
1074 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001075 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001076 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001077 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001078 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001079 cur->next = dn;
1080 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001081 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001082 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001083
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001084 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1085 if (nfiles == 0)
1086 return exit_code;
1087
Eric Andersen11c65522000-09-07 17:24:47 +00001088 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001089 * allocate memory for an array to hold dnode pointers
1090 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001091 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001092 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1093 dnp[i] = dn; /* save pointer to node in array */
1094 dn = dn->next;
1095 if (!dn)
1096 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001097 }
1098
Manuel Novoa III cad53642003-03-19 09:13:01 +00001099 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001100 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001101 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001102 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001103 dnd = splitdnarray(dnp, SPLIT_DIR);
1104 dnf = splitdnarray(dnp, SPLIT_FILE);
1105 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001106 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001107 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001108 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001109 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001110 if (ENABLE_FEATURE_CLEAN_UP)
1111 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001112 }
1113 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001114 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001115 showdirs(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001116 if (ENABLE_FEATURE_CLEAN_UP)
1117 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001118 }
1119 }
Rob Landley26314862006-05-02 19:46:52 +00001120 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001121 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001122 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001123}