blob: 31abbc9b5cb3556070529274a97a8a176365643b [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
328/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
329 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
330 * 3/7:multiplexed char/block device)
331 * and we use 0 for unknown and 15 for executables (see below) */
332#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
333#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
334#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
335/* 036 black foreground 050 black background
336 037 red foreground 051 red background
337 040 green foreground 052 green background
338 041 brown foreground 053 brown background
339 042 blue foreground 054 blue background
340 043 magenta (purple) foreground 055 magenta background
341 044 cyan (light blue) foreground 056 cyan background
342 045 gray foreground 057 white background
343*/
344#define COLOR(mode) ( \
345 /*un fi chr dir blk file link sock exe */ \
346 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
347 [TYPEINDEX(mode)])
348/* Select normal (0) [actually "reset all"] or bold (1)
349 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
350 * let's use 7 for "impossible" types, just for fun)
351 * Note: coreutils 6.9 uses inverted red for setuid binaries.
352 */
353#define ATTR(mode) ( \
354 /*un fi chr dir blk file link sock exe */ \
355 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
356 [TYPEINDEX(mode)])
357
Denis Vlasenko5c759602006-10-28 12:37:16 +0000358#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000359/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000360static char fgcolor(mode_t mode)
361{
Rob Landley9947a242006-06-15 22:11:10 +0000362 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000363 return COLOR(0xF000); /* File is executable ... */
364 return COLOR(mode);
365}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000366static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000367{
Rob Landley9947a242006-06-15 22:11:10 +0000368 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000369 return ATTR(0xF000); /* File is executable ... */
370 return ATTR(mode);
371}
372#endif
373
Denis Vlasenko5c759602006-10-28 12:37:16 +0000374#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000375static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000376{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000377 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000378 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000379 if (S_ISDIR(mode))
380 return '/';
381 if (!(all_fmt & LIST_EXEC))
382 return '\0';
383 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000384 return '*';
385 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000386}
387#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000388
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000389
Denis Vlasenko5c759602006-10-28 12:37:16 +0000390#define countdirs(A, B) count_dirs((A), (B), 1)
391#define countsubdirs(A, B) count_dirs((A), (B), 0)
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200392static unsigned count_dirs(struct dnode **dn, unsigned nfiles, int notsubdirs)
Eric Andersen11c65522000-09-07 17:24:47 +0000393{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200394 unsigned i, dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000395
Denis Vlasenko5c759602006-10-28 12:37:16 +0000396 if (!dn)
397 return 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000398 dirs = 0;
399 for (i = 0; i < nfiles; i++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000400 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000401 if (!S_ISDIR(dn[i]->dstat.st_mode))
402 continue;
403 name = dn[i]->name;
404 if (notsubdirs
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200405 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000406 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000407 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000408 }
Eric Andersen11c65522000-09-07 17:24:47 +0000409 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000410 return dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000411}
412
Eric Andersen11c65522000-09-07 17:24:47 +0000413/* get memory to hold an array of pointers */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200414static struct dnode **dnalloc(unsigned num)
Eric Andersen11c65522000-09-07 17:24:47 +0000415{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000416 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000417 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000418
Denis Vlasenko5c759602006-10-28 12:37:16 +0000419 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000420}
421
Denis Vlasenko5c759602006-10-28 12:37:16 +0000422#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200423static void dfree(struct dnode **dnp, unsigned nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000424{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200425 unsigned i;
Eric Andersen11c65522000-09-07 17:24:47 +0000426
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000427 if (dnp == NULL)
428 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000429
Rob Landley26314862006-05-02 19:46:52 +0000430 for (i = 0; i < nfiles; i++) {
431 struct dnode *cur = dnp[i];
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200432 if (cur->fname_allocated)
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000433 free((char*)cur->fullname); /* free the filename */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000434 free(cur); /* free the dnode */
Eric Andersen11c65522000-09-07 17:24:47 +0000435 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000436 free(dnp); /* free the array holding the dnode pointers */
Eric Andersen11c65522000-09-07 17:24:47 +0000437}
Rob Landleyc44bc982006-05-28 01:19:06 +0000438#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000439#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000440#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000441
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200442static struct dnode **splitdnarray(struct dnode **dn, unsigned nfiles, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000443{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200444 unsigned dncnt, i, d;
Eric Andersen11c65522000-09-07 17:24:47 +0000445 struct dnode **dnp;
446
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200447 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000448 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000449
450 /* count how many dirs and regular files there are */
Eric Andersencf1189f2000-11-29 21:52:06 +0000451 if (which == SPLIT_SUBDIR)
452 dncnt = countsubdirs(dn, nfiles);
453 else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000454 dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
Eric Andersencf1189f2000-11-29 21:52:06 +0000455 if (which == SPLIT_FILE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000456 dncnt = nfiles - dncnt; /* looking for files */
Eric Andersencf1189f2000-11-29 21:52:06 +0000457 }
Eric Andersen11c65522000-09-07 17:24:47 +0000458
459 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000460 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000461
462 /* copy the entrys into the file or dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000463 for (d = i = 0; i < nfiles; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000464 if (S_ISDIR(dn[i]->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000465 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000466 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
467 continue;
468 name = dn[i]->name;
469 if ((which & SPLIT_DIR)
470 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
471 ) {
472 dnp[d++] = dn[i];
Manuel Novoa III cad53642003-03-19 09:13:01 +0000473 }
474 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
475 dnp[d++] = dn[i];
Eric Andersen11c65522000-09-07 17:24:47 +0000476 }
477 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000478 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000479}
480
Denis Vlasenko5c759602006-10-28 12:37:16 +0000481#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000482static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000483{
Rob Landley425e7582006-05-03 20:22:03 +0000484 struct dnode *d1 = *(struct dnode **)a;
485 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000486 unsigned sort_opts = all_fmt & SORT_MASK;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000487 int dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000488
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000489 dif = 0; /* assume SORT_NAME */
490 // TODO: use pre-initialized function pointer
491 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000492 if (sort_opts == SORT_SIZE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000493 dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000494 } else if (sort_opts == SORT_ATIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000495 dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000496 } else if (sort_opts == SORT_CTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000497 dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000498 } else if (sort_opts == SORT_MTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000499 dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000500 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000501 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000502 /* } else if (sort_opts == SORT_VERSION) { */
503 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000504 }
505
Eric Andersen11c65522000-09-07 17:24:47 +0000506 if (dif == 0) {
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +0000507 /* sort by name - may be a tie_breaker for time or size cmp */
Rob Landleyea224be2006-06-18 20:20:07 +0000508 if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
509 else dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000510 }
511
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000512 if (all_fmt & SORT_REVERSE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000513 dif = -dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000514 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000515 return dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000516}
517
Rob Landley425e7582006-05-03 20:22:03 +0000518static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000519{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000520 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000521}
Rob Landleyea224be2006-06-18 20:20:07 +0000522#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000523#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000524#endif
525
Rob Landleyea224be2006-06-18 20:20:07 +0000526
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200527static void showfiles(struct dnode **dn, unsigned nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000528{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200529 unsigned i, ncols, nrows, row, nc;
530 unsigned column = 0;
531 unsigned nexttab = 0;
532 unsigned column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
Eric Andersen11c65522000-09-07 17:24:47 +0000533
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200534 /* Never happens:
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000535 if (dn == NULL || nfiles < 1)
536 return;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200537 */
Eric Andersen11c65522000-09-07 17:24:47 +0000538
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000539 if (all_fmt & STYLE_LONG) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000540 ncols = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000541 } else {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000542 /* find the longest file name, use that as the column width */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000543 for (i = 0; i < nfiles; i++) {
Denys Vlasenkofda8f572009-07-11 22:26:48 +0200544 int len = bb_mbstrlen(dn[i]->name);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000545 if (column_width < len)
546 column_width = len;
547 }
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000548 column_width += tabstops +
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000549 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000550 ((all_fmt & LIST_INO) ? 8 : 0) +
551 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
Glenn L McGrath4d001292003-01-06 01:11:50 +0000552 ncols = (int) (terminal_width / column_width);
Eric Andersen11c65522000-09-07 17:24:47 +0000553 }
554
Eric Andersene57d54b2001-01-30 18:03:11 +0000555 if (ncols > 1) {
556 nrows = nfiles / ncols;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000557 if (nrows * ncols < nfiles)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000558 nrows++; /* round up fractionals */
Eric Andersene57d54b2001-01-30 18:03:11 +0000559 } else {
560 nrows = nfiles;
561 ncols = 1;
562 }
Eric Andersen11c65522000-09-07 17:24:47 +0000563
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000564 for (row = 0; row < nrows; row++) {
565 for (nc = 0; nc < ncols; nc++) {
Eric Andersen79565b62000-08-11 18:10:21 +0000566 /* reach into the array based on the column and row */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000567 i = (nc * nrows) + row; /* assume display by column */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000568 if (all_fmt & DISP_ROWS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000569 i = (row * ncols) + nc; /* display across row */
Eric Andersen11c65522000-09-07 17:24:47 +0000570 if (i < nfiles) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000571 if (column > 0) {
572 nexttab -= column;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000573 printf("%*s", nexttab, "");
574 column += nexttab;
575 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000576 nexttab = column + column_width;
577 column += list_single(dn[i]);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000578 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000579 }
580 putchar('\n');
581 column = 0;
Eric Andersena42982e2000-06-07 17:28:53 +0000582 }
Eric Andersen11c65522000-09-07 17:24:47 +0000583}
584
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000585
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200586#if ENABLE_DESKTOP
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200587/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
588 * If any of the -l, -n, -s options is specified, each list
589 * of files within the directory shall be preceded by a
590 * status line indicating the number of file system blocks
591 * occupied by files in the directory in 512-byte units if
592 * the -k option is not specified, or 1024-byte units if the
593 * -k option is specified, rounded up to the next integral
594 * number of units.
595 */
596/* by Jorgen Overgaard (jorgen AT antistaten.se) */
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200597static off_t calculate_blocks(struct dnode **dn, int nfiles)
598{
599 uoff_t blocks = 1;
600 while (nfiles) {
601 blocks += (*dn)->dstat.st_blocks; /* in 512 byte blocks */
602 dn++;
603 nfiles--;
604 }
605
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200606 /* Even though standard says use 512 byte blocks, coreutils use 1k */
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200607 /* Actually, we round up by calculating (blocks + 1) / 2,
608 * "+ 1" was done when we initialized blocks to 1 */
609 return blocks >> 1;
610}
611#endif
612
613
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200614static void showdirs(struct dnode **dn, unsigned ndirs, int first)
Eric Andersen11c65522000-09-07 17:24:47 +0000615{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200616 unsigned i, nfiles;
Eric Andersen11c65522000-09-07 17:24:47 +0000617 struct dnode **subdnp;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200618 unsigned dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000619 struct dnode **dnd;
Eric Andersen11c65522000-09-07 17:24:47 +0000620
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200621 /* Never happens:
622 if (dn == NULL || ndirs < 1) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000623 return;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200624 }
625 */
Eric Andersen11c65522000-09-07 17:24:47 +0000626
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000627 for (i = 0; i < ndirs; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000628 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000629 if (!first)
Denis Vlasenko4daad902007-09-27 10:20:47 +0000630 bb_putchar('\n');
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000631 first = 0;
632 printf("%s:\n", dn[i]->fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000633 }
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200634 subdnp = list_dir(dn[i]->fullname, &nfiles);
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200635#if ENABLE_DESKTOP
636 if (all_fmt & STYLE_LONG)
637 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp, nfiles));
638#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000639 if (nfiles > 0) {
640 /* list all files at this level */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000641 dnsort(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000642 showfiles(subdnp, nfiles);
Rob Landley2b8a05a2006-06-20 17:43:01 +0000643 if (ENABLE_FEATURE_LS_RECURSIVE) {
644 if (all_fmt & DISP_RECURSIVE) {
645 /* recursive- list the sub-dirs */
646 dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
647 dndirs = countsubdirs(subdnp, nfiles);
648 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000649 dnsort(dnd, dndirs);
Rob Landley2b8a05a2006-06-20 17:43:01 +0000650 showdirs(dnd, dndirs, 0);
651 /* free the array of dnode pointers to the dirs */
652 free(dnd);
653 }
Eric Andersen11c65522000-09-07 17:24:47 +0000654 }
Rob Landley2b8a05a2006-06-20 17:43:01 +0000655 /* free the dnodes and the fullname mem */
656 dfree(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000657 }
Eric Andersen11c65522000-09-07 17:24:47 +0000658 }
659 }
660}
661
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000662
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200663static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
Eric Andersen11c65522000-09-07 17:24:47 +0000664{
665 struct dnode *dn, *cur, **dnp;
666 struct dirent *entry;
667 DIR *dir;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200668 unsigned i, nfiles;
Eric Andersen11c65522000-09-07 17:24:47 +0000669
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200670 /* Never happens:
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000671 if (path == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000672 return NULL;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200673 */
Eric Andersen11c65522000-09-07 17:24:47 +0000674
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200675 *nfiles_p = 0;
Rob Landleyd921b2e2006-08-03 15:41:12 +0000676 dir = warn_opendir(path);
Eric Andersen11c65522000-09-07 17:24:47 +0000677 if (dir == NULL) {
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000678 exit_code = EXIT_FAILURE;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000679 return NULL; /* could not open the dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000680 }
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200681 dn = NULL;
682 nfiles = 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000683 while ((entry = readdir(dir)) != NULL) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000684 char *fullname;
685
Eric Andersen11c65522000-09-07 17:24:47 +0000686 /* are we going to list the file- it may be . or .. or a hidden file */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000687 if (entry->d_name[0] == '.') {
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000688 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
689 && !(all_fmt & DISP_DOT)
690 ) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000691 continue;
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000692 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000693 if (!(all_fmt & DISP_HIDDEN))
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000694 continue;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000695 }
696 fullname = concat_path_file(path, entry->d_name);
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000697 cur = my_stat(fullname, bb_basename(fullname), 0);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000698 if (!cur) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000699 free(fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000700 continue;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000701 }
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200702 cur->fname_allocated = 1;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000703 cur->next = dn;
704 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +0000705 nfiles++;
706 }
707 closedir(dir);
708
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200709 if (dn == NULL)
710 return NULL;
711
Eric Andersen11c65522000-09-07 17:24:47 +0000712 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +0000713 * allocate memory for an array to hold dnode pointers
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000714 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200715 *nfiles_p = nfiles;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000716 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200717 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
718 dnp[i] = dn; /* save pointer to node in array */
719 dn = dn->next;
720 if (!dn)
721 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000722 }
723
Denis Vlasenko5c759602006-10-28 12:37:16 +0000724 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000725}
726
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000727
Denis Vlasenko248ce912009-03-03 14:09:04 +0000728static int print_name(const char *name)
729{
730 if (option_mask32 & OPT_Q) {
731#if ENABLE_FEATURE_ASSUME_UNICODE
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200732 unsigned len = 2 + bb_mbstrlen(name);
Denis Vlasenko248ce912009-03-03 14:09:04 +0000733#else
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200734 unsigned len = 2;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000735#endif
736 putchar('"');
737 while (*name) {
738 if (*name == '"') {
739 putchar('\\');
740 len++;
741 }
742 putchar(*name++);
743 if (!ENABLE_FEATURE_ASSUME_UNICODE)
744 len++;
745 }
746 putchar('"');
747 return len;
748 }
749 /* No -Q: */
750#if ENABLE_FEATURE_ASSUME_UNICODE
751 fputs(name, stdout);
Denys Vlasenkofda8f572009-07-11 22:26:48 +0200752 return bb_mbstrlen(name);
Denis Vlasenko248ce912009-03-03 14:09:04 +0000753#else
754 return printf("%s", name);
755#endif
756}
757
758
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200759static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000760{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200761 unsigned column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000762 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000763#if ENABLE_FEATURE_LS_TIMESTAMPS
Eric Andersen11c65522000-09-07 17:24:47 +0000764 char *filetime;
765 time_t ttime, age;
766#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000767#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000768 struct stat info;
769 char append;
770#endif
771
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200772 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000773 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000774 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200775 */
Eric Andersen11c65522000-09-07 17:24:47 +0000776
Denis Vlasenko5c759602006-10-28 12:37:16 +0000777#if ENABLE_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000778 ttime = dn->dstat.st_mtime; /* the default time */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000779 if (all_fmt & TIME_ACCESS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000780 ttime = dn->dstat.st_atime;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000781 if (all_fmt & TIME_CHANGE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000782 ttime = dn->dstat.st_ctime;
783 filetime = ctime(&ttime);
Eric Andersen11c65522000-09-07 17:24:47 +0000784#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000785#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000786 append = append_char(dn->dstat.st_mode);
787#endif
788
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000789 /* Do readlink early, so that if it fails, error message
790 * does not appear *inside* of the "ls -l" line */
791 if (all_fmt & LIST_SYMLINK)
792 if (S_ISLNK(dn->dstat.st_mode))
793 lpath = xmalloc_readlink_or_warn(dn->fullname);
794
795 if (all_fmt & LIST_INO)
796 column += printf("%7lu ", (long) dn->dstat.st_ino);
797 if (all_fmt & LIST_BLOCKS)
798 column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1);
799 if (all_fmt & LIST_MODEBITS)
800 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
801 if (all_fmt & LIST_NLINKS)
802 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000803#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000804 if (all_fmt & LIST_ID_NAME) {
805 if (option_mask32 & OPT_g) {
806 column += printf("%-8.8s",
807 get_cached_username(dn->dstat.st_uid));
808 } else {
809 column += printf("%-8.8s %-8.8s",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000810 get_cached_username(dn->dstat.st_uid),
811 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000812 }
813 }
Eric Andersen11c65522000-09-07 17:24:47 +0000814#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000815 if (all_fmt & LIST_ID_NUMERIC) {
816 if (option_mask32 & OPT_g)
817 column += printf("%-8u", (int) dn->dstat.st_uid);
818 else
819 column += printf("%-8u %-8u",
820 (int) dn->dstat.st_uid,
821 (int) dn->dstat.st_gid);
822 }
823 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
824 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
825 column += printf("%4u, %3u ",
826 (int) major(dn->dstat.st_rdev),
827 (int) minor(dn->dstat.st_rdev));
828 } else {
829 if (all_fmt & LS_DISP_HR) {
830 column += printf("%9s ",
831 make_human_readable_str(dn->dstat.st_size, 1, 0));
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000832 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000833 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000834 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000835 }
836 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000837#if ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000838 if (all_fmt & LIST_FULLTIME)
839 column += printf("%24.24s ", filetime);
840 if (all_fmt & LIST_DATE_TIME)
841 if ((all_fmt & LIST_FULLTIME) == 0) {
842 /* current_time_t ~== time(NULL) */
843 age = current_time_t - ttime;
844 printf("%6.6s ", filetime + 4);
845 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
846 /* hh:mm if less than 6 months old */
847 printf("%5.5s ", filetime + 11);
848 } else {
849 printf(" %4.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000850 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000851 column += 13;
852 }
Eric Andersen11c65522000-09-07 17:24:47 +0000853#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000854#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000855 if (all_fmt & LIST_CONTEXT) {
856 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
857 freecon(dn->sid);
858 }
Eric Andersen9e480452003-07-03 10:07:04 +0000859#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000860 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000861#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000862 if (show_color) {
863 info.st_mode = 0; /* for fgcolor() */
864 lstat(dn->fullname, &info);
865 printf("\033[%u;%um", bold(info.st_mode),
866 fgcolor(info.st_mode));
867 }
868#endif
869 column += print_name(dn->name);
870 if (show_color) {
871 printf("\033[0m");
872 }
873 }
874 if (all_fmt & LIST_SYMLINK) {
875 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
876 printf(" -> ");
877#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
878#if ENABLE_FEATURE_LS_COLOR
879 info.st_mode = 0; /* for fgcolor() */
880#endif
881 if (stat(dn->fullname, &info) == 0) {
882 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000883 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000884#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000885#if ENABLE_FEATURE_LS_COLOR
886 if (show_color) {
887 printf("\033[%u;%um", bold(info.st_mode),
888 fgcolor(info.st_mode));
889 }
890#endif
891 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000892 if (show_color) {
893 printf("\033[0m");
894 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000895 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000896 }
897 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000898#if ENABLE_FEATURE_LS_FILETYPES
899 if (all_fmt & LIST_FILETYPE) {
900 if (append) {
901 putchar(append);
902 column++;
903 }
904 }
905#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000906
Glenn L McGrath4d001292003-01-06 01:11:50 +0000907 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000908}
909
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000910
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000911int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000912{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000913 struct dnode **dnd;
914 struct dnode **dnf;
915 struct dnode **dnp;
916 struct dnode *dn;
917 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000918 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200919 unsigned nfiles;
920 unsigned dnfiles;
921 unsigned dndirs;
922 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200923#if ENABLE_FEATURE_LS_COLOR
924 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
925 /* coreutils 6.10:
926 * # ls --color=BOGUS
927 * ls: invalid argument 'BOGUS' for '--color'
928 * Valid arguments are:
929 * 'always', 'yes', 'force'
930 * 'never', 'no', 'none'
931 * 'auto', 'tty', 'if-tty'
932 * (and substrings: "--color=alwa" work too)
933 */
934 static const char ls_longopts[] ALIGN1 =
935 "color\0" Optional_argument "\xff"; /* no short equivalent */
936 static const char color_str[] ALIGN1 =
937 "always\0""yes\0""force\0"
938 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000939 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200940 const char *color_opt = color_str; /* "always" */
941#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +0000942
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000943 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000944
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200945 check_unicode_in_env();
946
Rob Landley2b8a05a2006-06-20 17:43:01 +0000947 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000948 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000949
Denis Vlasenko5c759602006-10-28 12:37:16 +0000950#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200951 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000952 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200953 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +0000954 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000955#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000956
Eric Andersen11c65522000-09-07 17:24:47 +0000957 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200958 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000959#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko1d426652008-03-17 09:09:09 +0000960 opt_complementary = "T+:w+"; /* -T N, -w N */
961 opt = getopt32(argv, ls_options, &tabstops, &terminal_width
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000962 IF_FEATURE_LS_COLOR(, &color_opt));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000963#else
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000964 opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
Manuel Novoa III cad53642003-03-19 09:13:01 +0000965#endif
Eric Andersend07cf592004-02-05 13:52:03 +0000966 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +0000967 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000968 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000969
Denis Vlasenko5c759602006-10-28 12:37:16 +0000970 if (flags & LIST_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000971 all_fmt &= ~LIST_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000972 if (flags & STYLE_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000973 all_fmt &= ~STYLE_MASK;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000974 if (flags & SORT_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000975 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000976 if (flags & DISP_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000977 all_fmt &= ~DISP_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000978 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000979 all_fmt &= ~TIME_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000980 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +0000981 all_fmt |= STYLE_SINGLE;
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000982 /* huh?? opt cannot be 'l' */
983 //if (LS_DISP_HR && opt == 'l')
984 // all_fmt &= ~LS_DISP_HR;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000985 all_fmt |= flags;
986 }
Eric Andersen11c65522000-09-07 17:24:47 +0000987 }
988
Denis Vlasenko5c759602006-10-28 12:37:16 +0000989#if ENABLE_FEATURE_LS_COLOR
990 /* find color bit value - last position for short getopt */
991 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
992 char *p = getenv("LS_COLORS");
993 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000994 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000995 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000996 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200997 if (opt & OPT_color) {
998 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +0000999 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001000 else switch (index_in_substrings(color_str, color_opt)) {
1001 case 3:
1002 case 4:
1003 case 5:
1004 if (isatty(STDOUT_FILENO)) {
1005 case 0:
1006 case 1:
1007 case 2:
1008 show_color = 1;
1009 }
1010 }
Paul Fox156dc412005-08-01 19:33:30 +00001011 }
1012#endif
1013
Eric Andersen11c65522000-09-07 17:24:47 +00001014 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001015 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001016 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001017 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1018 if (all_fmt & TIME_CHANGE)
1019 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1020 if (all_fmt & TIME_ACCESS)
1021 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1022 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001023 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1024 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +00001025 if (ENABLE_FEATURE_LS_USERNAME)
1026 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1027 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001028
Eric Andersen11c65522000-09-07 17:24:47 +00001029 /* choose a display format */
Rob Landleyea224be2006-06-18 20:20:07 +00001030 if (!(all_fmt & STYLE_MASK))
Eric Andersen70060d22004-03-27 10:02:48 +00001031 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001032
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001033 argv += optind;
1034 if (!argv[0])
1035 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001036
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001037 if (argv[1])
1038 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001039
Denis Vlasenko5c759602006-10-28 12:37:16 +00001040 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001041 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001042 nfiles = 0;
1043 do {
Denis Vlasenko11a6f9b2009-03-03 13:20:22 +00001044 /* NB: follow links on command line unless -l or -s */
1045 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001046 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001047 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001048 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001049 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001050 cur->next = dn;
1051 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001052 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001053 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001054
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001055 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1056 if (nfiles == 0)
1057 return exit_code;
1058
Eric Andersen11c65522000-09-07 17:24:47 +00001059 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001060 * allocate memory for an array to hold dnode pointers
1061 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001062 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001063 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1064 dnp[i] = dn; /* save pointer to node in array */
1065 dn = dn->next;
1066 if (!dn)
1067 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001068 }
1069
Manuel Novoa III cad53642003-03-19 09:13:01 +00001070 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001071 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001072 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001073 } else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001074 dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
1075 dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
1076 dndirs = countdirs(dnp, nfiles);
1077 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001078 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001079 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001080 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001081 if (ENABLE_FEATURE_CLEAN_UP)
1082 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001083 }
1084 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001085 dnsort(dnd, dndirs);
Glenn L McGrathc4db0832004-03-06 09:12:55 +00001086 showdirs(dnd, dndirs, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001087 if (ENABLE_FEATURE_CLEAN_UP)
1088 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001089 }
1090 }
Rob Landley26314862006-05-02 19:46:52 +00001091 if (ENABLE_FEATURE_CLEAN_UP)
1092 dfree(dnp, nfiles);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001093 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001094}