blob: d1521529f6bd44d8a4383a42efc05c94eaff7ae5 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersen9d3aba71999-10-06 09:04:55 +00002/*
Eric Andersencc8ed391999-10-05 16:24:54 +00003 * tiny-ls.c version 0.1.0: A minimalist 'ls'
4 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
Eric Andersen11c65522000-09-07 17:24:47 +00005 *
Bernhard Reutner-Fischercb448162006-04-12 07:35:12 +00006 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Eric Andersencc8ed391999-10-05 16:24:54 +00007 */
8
Denis Vlasenko5e4fda02009-03-08 23:46:48 +00009/* [date unknown. Perhaps before year 2000]
Eric Andersencc8ed391999-10-05 16:24:54 +000010 * To achieve a small memory footprint, this version of 'ls' doesn't do any
11 * file sorting, and only has the most essential command line switches
Eric Andersen77d92682001-05-23 20:32:09 +000012 * (i.e., the ones I couldn't live without :-) All features which involve
Eric Andersencc8ed391999-10-05 16:24:54 +000013 * linking in substantial chunks of libc can be disabled.
14 *
15 * Although I don't really want to add new features to this program to
16 * keep it small, I *am* interested to receive bug fixes and ways to make
17 * it more portable.
18 *
19 * KNOWN BUGS:
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 {
Manuel Novoa III cad53642003-03-19 09:13:01 +000055
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000056TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
57COLUMN_GAP = 2, /* includes the file type char */
58
59/* what is the overall style of the listing */
60STYLE_COLUMNS = 1 << 21, /* fill columns */
61STYLE_LONG = 2 << 21, /* one record per line, extended info */
62STYLE_SINGLE = 3 << 21, /* one record per line */
63STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +000064
Eric Andersen11c65522000-09-07 17:24:47 +000065/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
66/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000067LIST_INO = 1 << 0,
68LIST_BLOCKS = 1 << 1,
69LIST_MODEBITS = 1 << 2,
70LIST_NLINKS = 1 << 3,
71LIST_ID_NAME = 1 << 4,
72LIST_ID_NUMERIC = 1 << 5,
73LIST_CONTEXT = 1 << 6,
74LIST_SIZE = 1 << 7,
Denis Vlasenko3a014b82009-03-21 19:11:23 +000075//LIST_DEV = 1 << 8, - unused, synonym to LIST_SIZE
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000076LIST_DATE_TIME = 1 << 9,
77LIST_FULLTIME = 1 << 10,
78LIST_FILENAME = 1 << 11,
79LIST_SYMLINK = 1 << 12,
80LIST_FILETYPE = 1 << 13,
81LIST_EXEC = 1 << 14,
82LIST_MASK = (LIST_EXEC << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +000083
84/* what files will be displayed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000085DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */
Denis Vlasenko656f7462006-10-28 13:02:55 +000086DISP_HIDDEN = 1 << 16, /* show filenames starting with . */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000087DISP_DOT = 1 << 17, /* show . and .. */
88DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */
89DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */
90DISP_ROWS = 1 << 20, /* print across rows */
91DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +000092
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000093/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
94SORT_FORWARD = 0, /* sort in reverse order */
95SORT_REVERSE = 1 << 27, /* sort in reverse order */
Eric Andersen11c65522000-09-07 17:24:47 +000096
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000097SORT_NAME = 0, /* sort by file name */
98SORT_SIZE = 1 << 28, /* sort by file size */
99SORT_ATIME = 2 << 28, /* sort by last access time */
100SORT_CTIME = 3 << 28, /* sort by last change time */
101SORT_MTIME = 4 << 28, /* sort by last modification time */
102SORT_VERSION = 5 << 28, /* sort by version */
103SORT_EXT = 6 << 28, /* sort by file name extension */
104SORT_DIR = 7 << 28, /* sort by file or directory */
105SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000106
Eric Andersen11c65522000-09-07 17:24:47 +0000107/* which of the three times will be used */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000108TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
109TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
110TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000111
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000112FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
Eric Andersencc8ed391999-10-05 16:24:54 +0000113
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000114LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000115
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000116LIST_SHORT = LIST_FILENAME,
117LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
118 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
119
120SPLIT_DIR = 1,
121SPLIT_FILE = 0,
122SPLIT_SUBDIR = 2,
123
124};
Eric Andersencc8ed391999-10-05 16:24:54 +0000125
Denis Vlasenko248ce912009-03-03 14:09:04 +0000126/* "[-]Cadil1", POSIX mandated options, busybox always supports */
127/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
128/* "[-]Q" GNU option? busybox always supports */
129/* "[-]Ak" GNU options, busybox always supports */
130/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
131/* "[-]p", POSIX non-mandated options, busybox optionally supports */
132/* "[-]SXvThw", GNU options, busybox optionally supports */
133/* "[-]K", SELinux mandated options, busybox optionally supports */
134/* "[-]e", I think we made this one up */
135static const char ls_options[] ALIGN1 =
136 "Cadil1gnsxQAk" /* 13 opts, total 13 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000137 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
138 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
139 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
140 IF_FEATURE_LS_FOLLOWLINKS("L") /* 1, 24 */
141 IF_FEATURE_LS_RECURSIVE("R") /* 1, 25 */
142 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */
Denys Vlasenko55083632009-07-02 14:25:51 +0200143 IF_SELINUX("KZ") /* 2, 28 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000144 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000145 ;
146enum {
147 //OPT_C = (1 << 0),
148 //OPT_a = (1 << 1),
149 //OPT_d = (1 << 2),
150 //OPT_i = (1 << 3),
151 //OPT_l = (1 << 4),
152 //OPT_1 = (1 << 5),
153 OPT_g = (1 << 6),
154 //OPT_n = (1 << 7),
155 //OPT_s = (1 << 8),
156 //OPT_x = (1 << 9),
157 OPT_Q = (1 << 10),
158 //OPT_A = (1 << 11),
159 //OPT_k = (1 << 12),
Denys Vlasenko55083632009-07-02 14:25:51 +0200160 OPTBIT_color = 13
161 + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
162 + 4 * ENABLE_FEATURE_LS_SORTFILES
163 + 2 * ENABLE_FEATURE_LS_FILETYPES
164 + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
165 + 1 * ENABLE_FEATURE_LS_RECURSIVE
166 + 1 * ENABLE_FEATURE_HUMAN_READABLE
167 + 2 * ENABLE_SELINUX
168 + 2 * ENABLE_FEATURE_AUTOWIDTH,
169 OPT_color = 1 << OPTBIT_color,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000170};
171
172enum {
173 LIST_MASK_TRIGGER = 0,
174 STYLE_MASK_TRIGGER = STYLE_MASK,
175 DISP_MASK_TRIGGER = DISP_ROWS,
176 SORT_MASK_TRIGGER = SORT_MASK,
177};
178
179/* TODO: simple toggles may be stored as OPT_xxx bits instead */
180static const unsigned opt_flags[] = {
181 LIST_SHORT | STYLE_COLUMNS, /* C */
182 DISP_HIDDEN | DISP_DOT, /* a */
183 DISP_NOLIST, /* d */
184 LIST_INO, /* i */
185 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
186 LIST_SHORT | STYLE_SINGLE, /* 1 */
187 0, /* g (don't show group) - handled via OPT_g */
188 LIST_ID_NUMERIC, /* n */
189 LIST_BLOCKS, /* s */
190 DISP_ROWS, /* x */
191 0, /* Q (quote filename) - handled via OPT_Q */
192 DISP_HIDDEN, /* A */
193 ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
194#if ENABLE_FEATURE_LS_TIMESTAMPS
195 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
196 LIST_FULLTIME, /* e */
197 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
198 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
199#endif
200#if ENABLE_FEATURE_LS_SORTFILES
201 SORT_SIZE, /* S */
202 SORT_EXT, /* X */
203 SORT_REVERSE, /* r */
204 SORT_VERSION, /* v */
205#endif
206#if ENABLE_FEATURE_LS_FILETYPES
207 LIST_FILETYPE | LIST_EXEC, /* F */
208 LIST_FILETYPE, /* p */
209#endif
210#if ENABLE_FEATURE_LS_FOLLOWLINKS
211 FOLLOW_LINKS, /* L */
212#endif
213#if ENABLE_FEATURE_LS_RECURSIVE
214 DISP_RECURSIVE, /* R */
215#endif
216#if ENABLE_FEATURE_HUMAN_READABLE
217 LS_DISP_HR, /* h */
218#endif
219#if ENABLE_SELINUX
220 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
221#endif
222#if ENABLE_SELINUX
223 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
224#endif
225 (1U<<31)
226 /* options after Z are not processed through opt_flags:
227 * T, w - ignored
228 */
229};
230
231
Eric Andersen11c65522000-09-07 17:24:47 +0000232/*
233 * a directory entry and its stat info are stored here
234 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200235struct dnode {
236 const char *name; /* the dir entry name */
237 const char *fullname; /* the dir entry name */
238 struct dnode *next; /* point at the next node */
239 smallint fname_allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000240 struct stat dstat; /* the file stat info */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000241 IF_SELINUX(security_context_t sid;)
Eric Andersen11c65522000-09-07 17:24:47 +0000242};
Eric Andersen11c65522000-09-07 17:24:47 +0000243
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200244static struct dnode **list_dir(const char *, unsigned *);
245static unsigned list_single(const struct dnode *);
Eric Andersen11c65522000-09-07 17:24:47 +0000246
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000247struct globals {
248#if ENABLE_FEATURE_LS_COLOR
249 smallint show_color;
250#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000251 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000252 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000253#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000254 unsigned tabstops; // = COLUMN_GAP;
255 unsigned terminal_width; // = TERMINAL_WIDTH;
256#endif
257#if ENABLE_FEATURE_LS_TIMESTAMPS
258 /* Do time() just once. Saves one syscall per file for "ls -l" */
259 time_t current_time_t;
260#endif
261};
262#define G (*(struct globals*)&bb_common_bufsiz1)
263#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200264# define show_color (G.show_color )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000265#else
266enum { show_color = 0 };
267#endif
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200268#define exit_code (G.exit_code )
269#define all_fmt (G.all_fmt )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000270#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200271# define tabstops (G.tabstops )
272# define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000273#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000274enum {
275 tabstops = COLUMN_GAP,
276 terminal_width = TERMINAL_WIDTH,
277};
Eric Andersen11c65522000-09-07 17:24:47 +0000278#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000279#define current_time_t (G.current_time_t)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000280#define INIT_G() do { \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200281 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000282 memset(&G, 0, sizeof(G)); \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000283 IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
284 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
285 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000286} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000287
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000288
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000289static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000290{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000291 struct stat dstat;
292 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000293 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000294
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000295 if ((all_fmt & FOLLOW_LINKS) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000296#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000297 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000298 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000299 }
Eric Andersen9e480452003-07-03 10:07:04 +0000300#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000301 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000302 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000303 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000304 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000305 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000306 } else {
307#if ENABLE_SELINUX
308 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000309 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000310 }
Eric Andersen9e480452003-07-03 10:07:04 +0000311#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000312 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000313 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000314 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000315 return 0;
316 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000317 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000318
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200319 cur = xmalloc(sizeof(*cur));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000320 cur->fullname = fullname;
321 cur->name = name;
322 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000323 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000324 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000325}
326
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000327/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
328 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
329 * 3/7:multiplexed char/block device)
330 * and we use 0 for unknown and 15 for executables (see below) */
331#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
332#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
333#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
334/* 036 black foreground 050 black background
335 037 red foreground 051 red background
336 040 green foreground 052 green background
337 041 brown foreground 053 brown background
338 042 blue foreground 054 blue background
339 043 magenta (purple) foreground 055 magenta background
340 044 cyan (light blue) foreground 056 cyan background
341 045 gray foreground 057 white background
342*/
343#define COLOR(mode) ( \
344 /*un fi chr dir blk file link sock exe */ \
345 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
346 [TYPEINDEX(mode)])
347/* Select normal (0) [actually "reset all"] or bold (1)
348 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
349 * let's use 7 for "impossible" types, just for fun)
350 * Note: coreutils 6.9 uses inverted red for setuid binaries.
351 */
352#define ATTR(mode) ( \
353 /*un fi chr dir blk file link sock exe */ \
354 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
355 [TYPEINDEX(mode)])
356
Denis Vlasenko5c759602006-10-28 12:37:16 +0000357#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000358/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000359static char fgcolor(mode_t mode)
360{
Rob Landley9947a242006-06-15 22:11:10 +0000361 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000362 return COLOR(0xF000); /* File is executable ... */
363 return COLOR(mode);
364}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000365static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000366{
Rob Landley9947a242006-06-15 22:11:10 +0000367 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000368 return ATTR(0xF000); /* File is executable ... */
369 return ATTR(mode);
370}
371#endif
372
Denis Vlasenko5c759602006-10-28 12:37:16 +0000373#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000374static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000375{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000376 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000377 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000378 if (S_ISDIR(mode))
379 return '/';
380 if (!(all_fmt & LIST_EXEC))
381 return '\0';
382 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000383 return '*';
384 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000385}
386#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000387
Denis Vlasenko5c759602006-10-28 12:37:16 +0000388#define countdirs(A, B) count_dirs((A), (B), 1)
389#define countsubdirs(A, B) count_dirs((A), (B), 0)
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200390static unsigned count_dirs(struct dnode **dn, unsigned nfiles, int notsubdirs)
Eric Andersen11c65522000-09-07 17:24:47 +0000391{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200392 unsigned i, dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000393
Denis Vlasenko5c759602006-10-28 12:37:16 +0000394 if (!dn)
395 return 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000396 dirs = 0;
397 for (i = 0; i < nfiles; i++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000398 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000399 if (!S_ISDIR(dn[i]->dstat.st_mode))
400 continue;
401 name = dn[i]->name;
402 if (notsubdirs
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200403 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000404 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000405 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000406 }
Eric Andersen11c65522000-09-07 17:24:47 +0000407 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000408 return dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000409}
410
Eric Andersen11c65522000-09-07 17:24:47 +0000411/* get memory to hold an array of pointers */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200412static struct dnode **dnalloc(unsigned num)
Eric Andersen11c65522000-09-07 17:24:47 +0000413{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000414 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000415 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000416
Denis Vlasenko5c759602006-10-28 12:37:16 +0000417 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000418}
419
Denis Vlasenko5c759602006-10-28 12:37:16 +0000420#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200421static void dfree(struct dnode **dnp, unsigned nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000422{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200423 unsigned i;
Eric Andersen11c65522000-09-07 17:24:47 +0000424
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000425 if (dnp == NULL)
426 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000427
Rob Landley26314862006-05-02 19:46:52 +0000428 for (i = 0; i < nfiles; i++) {
429 struct dnode *cur = dnp[i];
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200430 if (cur->fname_allocated)
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200431 free((char*)cur->fullname);
432 free(cur);
Eric Andersen11c65522000-09-07 17:24:47 +0000433 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200434 free(dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000435}
Rob Landleyc44bc982006-05-28 01:19:06 +0000436#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000437#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000438#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000439
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200440static struct dnode **splitdnarray(struct dnode **dn, unsigned nfiles, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000441{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200442 unsigned dncnt, i, d;
Eric Andersen11c65522000-09-07 17:24:47 +0000443 struct dnode **dnp;
444
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200445 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000446 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000447
448 /* count how many dirs and regular files there are */
Eric Andersencf1189f2000-11-29 21:52:06 +0000449 if (which == SPLIT_SUBDIR)
450 dncnt = countsubdirs(dn, nfiles);
451 else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000452 dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
Eric Andersencf1189f2000-11-29 21:52:06 +0000453 if (which == SPLIT_FILE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000454 dncnt = nfiles - dncnt; /* looking for files */
Eric Andersencf1189f2000-11-29 21:52:06 +0000455 }
Eric Andersen11c65522000-09-07 17:24:47 +0000456
457 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000458 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000459
460 /* copy the entrys into the file or dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000461 for (d = i = 0; i < nfiles; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000462 if (S_ISDIR(dn[i]->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000463 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000464 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
465 continue;
466 name = dn[i]->name;
467 if ((which & SPLIT_DIR)
468 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
469 ) {
470 dnp[d++] = dn[i];
Manuel Novoa III cad53642003-03-19 09:13:01 +0000471 }
472 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
473 dnp[d++] = dn[i];
Eric Andersen11c65522000-09-07 17:24:47 +0000474 }
475 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000476 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000477}
478
Denis Vlasenko5c759602006-10-28 12:37:16 +0000479#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000480static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000481{
Rob Landley425e7582006-05-03 20:22:03 +0000482 struct dnode *d1 = *(struct dnode **)a;
483 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000484 unsigned sort_opts = all_fmt & SORT_MASK;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000485 int dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000486
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000487 dif = 0; /* assume SORT_NAME */
488 // TODO: use pre-initialized function pointer
489 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000490 if (sort_opts == SORT_SIZE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000491 dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000492 } else if (sort_opts == SORT_ATIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000493 dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000494 } else if (sort_opts == SORT_CTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000495 dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000496 } else if (sort_opts == SORT_MTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000497 dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000498 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000499 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000500 /* } else if (sort_opts == SORT_VERSION) { */
501 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000502 }
503
Eric Andersen11c65522000-09-07 17:24:47 +0000504 if (dif == 0) {
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +0000505 /* sort by name - may be a tie_breaker for time or size cmp */
Rob Landleyea224be2006-06-18 20:20:07 +0000506 if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
507 else dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000508 }
509
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000510 if (all_fmt & SORT_REVERSE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000511 dif = -dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000512 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000513 return dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000514}
515
Rob Landley425e7582006-05-03 20:22:03 +0000516static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000517{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000518 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000519}
Rob Landleyea224be2006-06-18 20:20:07 +0000520#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000521#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000522#endif
523
Rob Landleyea224be2006-06-18 20:20:07 +0000524
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200525static void showfiles(struct dnode **dn, unsigned nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000526{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200527 unsigned i, ncols, nrows, row, nc;
528 unsigned column = 0;
529 unsigned nexttab = 0;
530 unsigned column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
Eric Andersen11c65522000-09-07 17:24:47 +0000531
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200532 /* Never happens:
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000533 if (dn == NULL || nfiles < 1)
534 return;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200535 */
Eric Andersen11c65522000-09-07 17:24:47 +0000536
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000537 if (all_fmt & STYLE_LONG) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000538 ncols = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000539 } else {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000540 /* find the longest file name, use that as the column width */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000541 for (i = 0; i < nfiles; i++) {
Denys Vlasenkofda8f572009-07-11 22:26:48 +0200542 int len = bb_mbstrlen(dn[i]->name);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000543 if (column_width < len)
544 column_width = len;
545 }
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000546 column_width += tabstops +
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000547 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000548 ((all_fmt & LIST_INO) ? 8 : 0) +
549 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
Glenn L McGrath4d001292003-01-06 01:11:50 +0000550 ncols = (int) (terminal_width / column_width);
Eric Andersen11c65522000-09-07 17:24:47 +0000551 }
552
Eric Andersene57d54b2001-01-30 18:03:11 +0000553 if (ncols > 1) {
554 nrows = nfiles / ncols;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000555 if (nrows * ncols < nfiles)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000556 nrows++; /* round up fractionals */
Eric Andersene57d54b2001-01-30 18:03:11 +0000557 } else {
558 nrows = nfiles;
559 ncols = 1;
560 }
Eric Andersen11c65522000-09-07 17:24:47 +0000561
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000562 for (row = 0; row < nrows; row++) {
563 for (nc = 0; nc < ncols; nc++) {
Eric Andersen79565b62000-08-11 18:10:21 +0000564 /* reach into the array based on the column and row */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000565 if (all_fmt & DISP_ROWS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000566 i = (row * ncols) + nc; /* display across row */
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200567 else
568 i = (nc * nrows) + row; /* display by column */
Eric Andersen11c65522000-09-07 17:24:47 +0000569 if (i < nfiles) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000570 if (column > 0) {
571 nexttab -= column;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000572 printf("%*s", nexttab, "");
573 column += nexttab;
574 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000575 nexttab = column + column_width;
576 column += list_single(dn[i]);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000577 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000578 }
579 putchar('\n');
580 column = 0;
Eric Andersena42982e2000-06-07 17:28:53 +0000581 }
Eric Andersen11c65522000-09-07 17:24:47 +0000582}
583
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000584
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200585#if ENABLE_DESKTOP
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200586/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
587 * If any of the -l, -n, -s options is specified, each list
588 * of files within the directory shall be preceded by a
589 * status line indicating the number of file system blocks
590 * occupied by files in the directory in 512-byte units if
591 * the -k option is not specified, or 1024-byte units if the
592 * -k option is specified, rounded up to the next integral
593 * number of units.
594 */
595/* by Jorgen Overgaard (jorgen AT antistaten.se) */
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200596static off_t calculate_blocks(struct dnode **dn, int nfiles)
597{
598 uoff_t blocks = 1;
599 while (nfiles) {
600 blocks += (*dn)->dstat.st_blocks; /* in 512 byte blocks */
601 dn++;
602 nfiles--;
603 }
604
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200605 /* Even though standard says use 512 byte blocks, coreutils use 1k */
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200606 /* Actually, we round up by calculating (blocks + 1) / 2,
607 * "+ 1" was done when we initialized blocks to 1 */
608 return blocks >> 1;
609}
610#endif
611
612
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200613static void showdirs(struct dnode **dn, unsigned ndirs, int first)
Eric Andersen11c65522000-09-07 17:24:47 +0000614{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200615 unsigned i, nfiles;
Eric Andersen11c65522000-09-07 17:24:47 +0000616 struct dnode **subdnp;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200617 unsigned dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000618 struct dnode **dnd;
Eric Andersen11c65522000-09-07 17:24:47 +0000619
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200620 /* Never happens:
621 if (dn == NULL || ndirs < 1) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000622 return;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200623 }
624 */
Eric Andersen11c65522000-09-07 17:24:47 +0000625
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000626 for (i = 0; i < ndirs; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000627 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000628 if (!first)
Denis Vlasenko4daad902007-09-27 10:20:47 +0000629 bb_putchar('\n');
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000630 first = 0;
631 printf("%s:\n", dn[i]->fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000632 }
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200633 subdnp = list_dir(dn[i]->fullname, &nfiles);
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200634#if ENABLE_DESKTOP
635 if (all_fmt & STYLE_LONG)
636 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp, nfiles));
637#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000638 if (nfiles > 0) {
639 /* list all files at this level */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000640 dnsort(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000641 showfiles(subdnp, nfiles);
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200642 if (ENABLE_FEATURE_LS_RECURSIVE
643 && (all_fmt & DISP_RECURSIVE)
644 ) {
645 /* recursive - list the sub-dirs */
646 dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
647 dndirs = countsubdirs(subdnp, nfiles);
648 if (dndirs > 0) {
649 dnsort(dnd, dndirs);
650 showdirs(dnd, dndirs, 0);
651 /* free the array of dnode pointers to the dirs */
652 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +0000653 }
654 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200655 /* free the dnodes and the fullname mem */
656 dfree(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000657 }
658 }
659}
660
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000661
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200662static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
Eric Andersen11c65522000-09-07 17:24:47 +0000663{
664 struct dnode *dn, *cur, **dnp;
665 struct dirent *entry;
666 DIR *dir;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200667 unsigned i, nfiles;
Eric Andersen11c65522000-09-07 17:24:47 +0000668
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200669 /* Never happens:
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000670 if (path == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000671 return NULL;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200672 */
Eric Andersen11c65522000-09-07 17:24:47 +0000673
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200674 *nfiles_p = 0;
Rob Landleyd921b2e2006-08-03 15:41:12 +0000675 dir = warn_opendir(path);
Eric Andersen11c65522000-09-07 17:24:47 +0000676 if (dir == NULL) {
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000677 exit_code = EXIT_FAILURE;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000678 return NULL; /* could not open the dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000679 }
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200680 dn = NULL;
681 nfiles = 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000682 while ((entry = readdir(dir)) != NULL) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000683 char *fullname;
684
Eric Andersen11c65522000-09-07 17:24:47 +0000685 /* are we going to list the file- it may be . or .. or a hidden file */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000686 if (entry->d_name[0] == '.') {
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000687 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
688 && !(all_fmt & DISP_DOT)
689 ) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000690 continue;
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000691 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000692 if (!(all_fmt & DISP_HIDDEN))
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000693 continue;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000694 }
695 fullname = concat_path_file(path, entry->d_name);
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000696 cur = my_stat(fullname, bb_basename(fullname), 0);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000697 if (!cur) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000698 free(fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000699 continue;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000700 }
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200701 cur->fname_allocated = 1;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000702 cur->next = dn;
703 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +0000704 nfiles++;
705 }
706 closedir(dir);
707
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200708 if (dn == NULL)
709 return NULL;
710
Eric Andersen11c65522000-09-07 17:24:47 +0000711 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +0000712 * allocate memory for an array to hold dnode pointers
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000713 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200714 *nfiles_p = nfiles;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000715 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200716 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
717 dnp[i] = dn; /* save pointer to node in array */
718 dn = dn->next;
719 if (!dn)
720 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000721 }
722
Denis Vlasenko5c759602006-10-28 12:37:16 +0000723 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000724}
725
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000726
Denis Vlasenko248ce912009-03-03 14:09:04 +0000727static int print_name(const char *name)
728{
729 if (option_mask32 & OPT_Q) {
730#if ENABLE_FEATURE_ASSUME_UNICODE
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200731 unsigned len = 2 + bb_mbstrlen(name);
Denis Vlasenko248ce912009-03-03 14:09:04 +0000732#else
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200733 unsigned len = 2;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000734#endif
735 putchar('"');
736 while (*name) {
737 if (*name == '"') {
738 putchar('\\');
739 len++;
740 }
741 putchar(*name++);
742 if (!ENABLE_FEATURE_ASSUME_UNICODE)
743 len++;
744 }
745 putchar('"');
746 return len;
747 }
748 /* No -Q: */
749#if ENABLE_FEATURE_ASSUME_UNICODE
750 fputs(name, stdout);
Denys Vlasenkofda8f572009-07-11 22:26:48 +0200751 return bb_mbstrlen(name);
Denis Vlasenko248ce912009-03-03 14:09:04 +0000752#else
753 return printf("%s", name);
754#endif
755}
756
757
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200758static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000759{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200760 unsigned column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000761 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000762#if ENABLE_FEATURE_LS_TIMESTAMPS
Eric Andersen11c65522000-09-07 17:24:47 +0000763 char *filetime;
764 time_t ttime, age;
765#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000766#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000767 struct stat info;
768 char append;
769#endif
770
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200771 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000772 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000773 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200774 */
Eric Andersen11c65522000-09-07 17:24:47 +0000775
Denis Vlasenko5c759602006-10-28 12:37:16 +0000776#if ENABLE_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000777 ttime = dn->dstat.st_mtime; /* the default time */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000778 if (all_fmt & TIME_ACCESS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000779 ttime = dn->dstat.st_atime;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000780 if (all_fmt & TIME_CHANGE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000781 ttime = dn->dstat.st_ctime;
782 filetime = ctime(&ttime);
Eric Andersen11c65522000-09-07 17:24:47 +0000783#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000784#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000785 append = append_char(dn->dstat.st_mode);
786#endif
787
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000788 /* Do readlink early, so that if it fails, error message
789 * does not appear *inside* of the "ls -l" line */
790 if (all_fmt & LIST_SYMLINK)
791 if (S_ISLNK(dn->dstat.st_mode))
792 lpath = xmalloc_readlink_or_warn(dn->fullname);
793
794 if (all_fmt & LIST_INO)
795 column += printf("%7lu ", (long) dn->dstat.st_ino);
796 if (all_fmt & LIST_BLOCKS)
797 column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1);
798 if (all_fmt & LIST_MODEBITS)
799 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
800 if (all_fmt & LIST_NLINKS)
801 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000802#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000803 if (all_fmt & LIST_ID_NAME) {
804 if (option_mask32 & OPT_g) {
805 column += printf("%-8.8s",
806 get_cached_username(dn->dstat.st_uid));
807 } else {
808 column += printf("%-8.8s %-8.8s",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000809 get_cached_username(dn->dstat.st_uid),
810 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000811 }
812 }
Eric Andersen11c65522000-09-07 17:24:47 +0000813#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000814 if (all_fmt & LIST_ID_NUMERIC) {
815 if (option_mask32 & OPT_g)
816 column += printf("%-8u", (int) dn->dstat.st_uid);
817 else
818 column += printf("%-8u %-8u",
819 (int) dn->dstat.st_uid,
820 (int) dn->dstat.st_gid);
821 }
822 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
823 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
824 column += printf("%4u, %3u ",
825 (int) major(dn->dstat.st_rdev),
826 (int) minor(dn->dstat.st_rdev));
827 } else {
828 if (all_fmt & LS_DISP_HR) {
829 column += printf("%9s ",
830 make_human_readable_str(dn->dstat.st_size, 1, 0));
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000831 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000832 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000833 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000834 }
835 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000836#if ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000837 if (all_fmt & LIST_FULLTIME)
838 column += printf("%24.24s ", filetime);
839 if (all_fmt & LIST_DATE_TIME)
840 if ((all_fmt & LIST_FULLTIME) == 0) {
841 /* current_time_t ~== time(NULL) */
842 age = current_time_t - ttime;
843 printf("%6.6s ", filetime + 4);
844 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
845 /* hh:mm if less than 6 months old */
846 printf("%5.5s ", filetime + 11);
847 } else {
848 printf(" %4.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000849 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000850 column += 13;
851 }
Eric Andersen11c65522000-09-07 17:24:47 +0000852#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000853#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000854 if (all_fmt & LIST_CONTEXT) {
855 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
856 freecon(dn->sid);
857 }
Eric Andersen9e480452003-07-03 10:07:04 +0000858#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000859 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000860#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000861 if (show_color) {
862 info.st_mode = 0; /* for fgcolor() */
863 lstat(dn->fullname, &info);
864 printf("\033[%u;%um", bold(info.st_mode),
865 fgcolor(info.st_mode));
866 }
867#endif
868 column += print_name(dn->name);
869 if (show_color) {
870 printf("\033[0m");
871 }
872 }
873 if (all_fmt & LIST_SYMLINK) {
874 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
875 printf(" -> ");
876#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
877#if ENABLE_FEATURE_LS_COLOR
878 info.st_mode = 0; /* for fgcolor() */
879#endif
880 if (stat(dn->fullname, &info) == 0) {
881 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000882 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000883#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000884#if ENABLE_FEATURE_LS_COLOR
885 if (show_color) {
886 printf("\033[%u;%um", bold(info.st_mode),
887 fgcolor(info.st_mode));
888 }
889#endif
890 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000891 if (show_color) {
892 printf("\033[0m");
893 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000894 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000895 }
896 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000897#if ENABLE_FEATURE_LS_FILETYPES
898 if (all_fmt & LIST_FILETYPE) {
899 if (append) {
900 putchar(append);
901 column++;
902 }
903 }
904#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000905
Glenn L McGrath4d001292003-01-06 01:11:50 +0000906 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000907}
908
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000909
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000910int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000911{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000912 struct dnode **dnd;
913 struct dnode **dnf;
914 struct dnode **dnp;
915 struct dnode *dn;
916 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000917 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200918 unsigned nfiles;
919 unsigned dnfiles;
920 unsigned dndirs;
921 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200922#if ENABLE_FEATURE_LS_COLOR
923 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
924 /* coreutils 6.10:
925 * # ls --color=BOGUS
926 * ls: invalid argument 'BOGUS' for '--color'
927 * Valid arguments are:
928 * 'always', 'yes', 'force'
929 * 'never', 'no', 'none'
930 * 'auto', 'tty', 'if-tty'
931 * (and substrings: "--color=alwa" work too)
932 */
933 static const char ls_longopts[] ALIGN1 =
934 "color\0" Optional_argument "\xff"; /* no short equivalent */
935 static const char color_str[] ALIGN1 =
936 "always\0""yes\0""force\0"
937 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000938 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200939 const char *color_opt = color_str; /* "always" */
940#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +0000941
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000942 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000943
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200944 check_unicode_in_env();
945
Rob Landley2b8a05a2006-06-20 17:43:01 +0000946 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000947 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000948
Denis Vlasenko5c759602006-10-28 12:37:16 +0000949#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200950 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000951 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200952 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +0000953 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000954#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000955
Eric Andersen11c65522000-09-07 17:24:47 +0000956 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200957 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000958#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko1d426652008-03-17 09:09:09 +0000959 opt_complementary = "T+:w+"; /* -T N, -w N */
960 opt = getopt32(argv, ls_options, &tabstops, &terminal_width
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000961 IF_FEATURE_LS_COLOR(, &color_opt));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000962#else
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000963 opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
Manuel Novoa III cad53642003-03-19 09:13:01 +0000964#endif
Eric Andersend07cf592004-02-05 13:52:03 +0000965 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +0000966 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000967 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000968
Denis Vlasenko5c759602006-10-28 12:37:16 +0000969 if (flags & LIST_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000970 all_fmt &= ~LIST_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000971 if (flags & STYLE_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000972 all_fmt &= ~STYLE_MASK;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000973 if (flags & SORT_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000974 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000975 if (flags & DISP_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000976 all_fmt &= ~DISP_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000977 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000978 all_fmt &= ~TIME_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000979 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +0000980 all_fmt |= STYLE_SINGLE;
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000981 /* huh?? opt cannot be 'l' */
982 //if (LS_DISP_HR && opt == 'l')
983 // all_fmt &= ~LS_DISP_HR;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000984 all_fmt |= flags;
985 }
Eric Andersen11c65522000-09-07 17:24:47 +0000986 }
987
Denis Vlasenko5c759602006-10-28 12:37:16 +0000988#if ENABLE_FEATURE_LS_COLOR
989 /* find color bit value - last position for short getopt */
990 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
991 char *p = getenv("LS_COLORS");
992 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000993 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000994 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000995 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200996 if (opt & OPT_color) {
997 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +0000998 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200999 else switch (index_in_substrings(color_str, color_opt)) {
1000 case 3:
1001 case 4:
1002 case 5:
1003 if (isatty(STDOUT_FILENO)) {
1004 case 0:
1005 case 1:
1006 case 2:
1007 show_color = 1;
1008 }
1009 }
Paul Fox156dc412005-08-01 19:33:30 +00001010 }
1011#endif
1012
Eric Andersen11c65522000-09-07 17:24:47 +00001013 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001014 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001015 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001016 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1017 if (all_fmt & TIME_CHANGE)
1018 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1019 if (all_fmt & TIME_ACCESS)
1020 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1021 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001022 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1023 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +00001024 if (ENABLE_FEATURE_LS_USERNAME)
1025 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1026 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001027
Eric Andersen11c65522000-09-07 17:24:47 +00001028 /* choose a display format */
Rob Landleyea224be2006-06-18 20:20:07 +00001029 if (!(all_fmt & STYLE_MASK))
Eric Andersen70060d22004-03-27 10:02:48 +00001030 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001031
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001032 argv += optind;
1033 if (!argv[0])
1034 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001035
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001036 if (argv[1])
1037 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001038
Denis Vlasenko5c759602006-10-28 12:37:16 +00001039 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001040 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001041 nfiles = 0;
1042 do {
Denis Vlasenko11a6f9b2009-03-03 13:20:22 +00001043 /* NB: follow links on command line unless -l or -s */
1044 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001045 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001046 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001047 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001048 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001049 cur->next = dn;
1050 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001051 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001052 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001053
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001054 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1055 if (nfiles == 0)
1056 return exit_code;
1057
Eric Andersen11c65522000-09-07 17:24:47 +00001058 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001059 * allocate memory for an array to hold dnode pointers
1060 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001061 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001062 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1063 dnp[i] = dn; /* save pointer to node in array */
1064 dn = dn->next;
1065 if (!dn)
1066 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001067 }
1068
Manuel Novoa III cad53642003-03-19 09:13:01 +00001069 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001070 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001071 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001072 } else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001073 dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
1074 dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
1075 dndirs = countdirs(dnp, nfiles);
1076 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001077 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001078 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001079 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001080 if (ENABLE_FEATURE_CLEAN_UP)
1081 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001082 }
1083 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001084 dnsort(dnd, dndirs);
Glenn L McGrathc4db0832004-03-06 09:12:55 +00001085 showdirs(dnd, dndirs, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001086 if (ENABLE_FEATURE_CLEAN_UP)
1087 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001088 }
1089 }
Rob Landley26314862006-05-02 19:46:52 +00001090 if (ENABLE_FEATURE_CLEAN_UP)
1091 dfree(dnp, nfiles);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001092 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001093}