blob: f8416898b84a3b366bdeace8c6ee98e9d51dd06f [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
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200388static unsigned count_dirs(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000389{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200390 unsigned dirs, all;
Eric Andersen11c65522000-09-07 17:24:47 +0000391
Denis Vlasenko5c759602006-10-28 12:37:16 +0000392 if (!dn)
393 return 0;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200394
395 dirs = all = 0;
396 for (; *dn; dn++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000397 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200398
399 all++;
400 if (!S_ISDIR((*dn)->dstat.st_mode))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000401 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200402 name = (*dn)->name;
403 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
404 /* or if it's not . or .. */
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 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200410 return which != SPLIT_FILE ? dirs : all - 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
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200419 num++; /* so that we have terminating NULL */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000420 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000421}
422
Denis Vlasenko5c759602006-10-28 12:37:16 +0000423#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200424static void dfree(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000425{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200426 unsigned i;
Eric Andersen11c65522000-09-07 17:24:47 +0000427
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000428 if (dnp == NULL)
429 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000430
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200431 for (i = 0; dnp[i]; i++) {
Rob Landley26314862006-05-02 19:46:52 +0000432 struct dnode *cur = dnp[i];
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200433 if (cur->fname_allocated)
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200434 free((char*)cur->fullname);
435 free(cur);
Eric Andersen11c65522000-09-07 17:24:47 +0000436 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200437 free(dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000438}
Rob Landleyc44bc982006-05-28 01:19:06 +0000439#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000440#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000441#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000442
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200443/* Returns NULL-terminated malloced vector of pointers (or NULL) */
444static struct dnode **splitdnarray(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000445{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200446 unsigned dncnt, d;
Eric Andersen11c65522000-09-07 17:24:47 +0000447 struct dnode **dnp;
448
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200449 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000450 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000451
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200452 /* count how many dirs or files there are */
453 dncnt = count_dirs(dn, which);
Eric Andersen11c65522000-09-07 17:24:47 +0000454
455 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000456 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000457
458 /* copy the entrys into the file or dir array */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200459 for (d = 0; *dn; dn++) {
460 if (S_ISDIR((*dn)->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000461 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200462
Denis Vlasenko5c759602006-10-28 12:37:16 +0000463 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
464 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200465 name = (*dn)->name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000466 if ((which & SPLIT_DIR)
467 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
468 ) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200469 dnp[d++] = *dn;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000470 }
471 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200472 dnp[d++] = *dn;
Eric Andersen11c65522000-09-07 17:24:47 +0000473 }
474 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000475 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000476}
477
Denis Vlasenko5c759602006-10-28 12:37:16 +0000478#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000479static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000480{
Rob Landley425e7582006-05-03 20:22:03 +0000481 struct dnode *d1 = *(struct dnode **)a;
482 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000483 unsigned sort_opts = all_fmt & SORT_MASK;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000484 int dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000485
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000486 dif = 0; /* assume SORT_NAME */
487 // TODO: use pre-initialized function pointer
488 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000489 if (sort_opts == SORT_SIZE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000490 dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000491 } else if (sort_opts == SORT_ATIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000492 dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000493 } else if (sort_opts == SORT_CTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000494 dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000495 } else if (sort_opts == SORT_MTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000496 dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000497 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000498 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000499 /* } else if (sort_opts == SORT_VERSION) { */
500 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000501 }
502
Eric Andersen11c65522000-09-07 17:24:47 +0000503 if (dif == 0) {
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +0000504 /* sort by name - may be a tie_breaker for time or size cmp */
Rob Landleyea224be2006-06-18 20:20:07 +0000505 if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
506 else dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000507 }
508
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000509 if (all_fmt & SORT_REVERSE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000510 dif = -dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000511 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000512 return dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000513}
514
Rob Landley425e7582006-05-03 20:22:03 +0000515static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000516{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000517 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000518}
Rob Landleyea224be2006-06-18 20:20:07 +0000519#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000520#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000521#endif
522
Rob Landleyea224be2006-06-18 20:20:07 +0000523
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200524static void showfiles(struct dnode **dn, unsigned nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000525{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200526 unsigned i, ncols, nrows, row, nc;
527 unsigned column = 0;
528 unsigned nexttab = 0;
529 unsigned column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
Eric Andersen11c65522000-09-07 17:24:47 +0000530
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200531 /* Never happens:
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000532 if (dn == NULL || nfiles < 1)
533 return;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200534 */
Eric Andersen11c65522000-09-07 17:24:47 +0000535
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000536 if (all_fmt & STYLE_LONG) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000537 ncols = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000538 } else {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000539 /* find the longest file name, use that as the column width */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200540 for (i = 0; dn[i]; i++) {
Denys Vlasenkofda8f572009-07-11 22:26:48 +0200541 int len = bb_mbstrlen(dn[i]->name);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000542 if (column_width < len)
543 column_width = len;
544 }
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000545 column_width += tabstops +
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000546 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000547 ((all_fmt & LIST_INO) ? 8 : 0) +
548 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
Glenn L McGrath4d001292003-01-06 01:11:50 +0000549 ncols = (int) (terminal_width / column_width);
Eric Andersen11c65522000-09-07 17:24:47 +0000550 }
551
Eric Andersene57d54b2001-01-30 18:03:11 +0000552 if (ncols > 1) {
553 nrows = nfiles / ncols;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000554 if (nrows * ncols < nfiles)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000555 nrows++; /* round up fractionals */
Eric Andersene57d54b2001-01-30 18:03:11 +0000556 } else {
557 nrows = nfiles;
558 ncols = 1;
559 }
Eric Andersen11c65522000-09-07 17:24:47 +0000560
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000561 for (row = 0; row < nrows; row++) {
562 for (nc = 0; nc < ncols; nc++) {
Eric Andersen79565b62000-08-11 18:10:21 +0000563 /* reach into the array based on the column and row */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000564 if (all_fmt & DISP_ROWS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000565 i = (row * ncols) + nc; /* display across row */
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200566 else
567 i = (nc * nrows) + row; /* display by column */
Eric Andersen11c65522000-09-07 17:24:47 +0000568 if (i < nfiles) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000569 if (column > 0) {
570 nexttab -= column;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000571 printf("%*s", nexttab, "");
572 column += nexttab;
573 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000574 nexttab = column + column_width;
575 column += list_single(dn[i]);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000576 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000577 }
578 putchar('\n');
579 column = 0;
Eric Andersena42982e2000-06-07 17:28:53 +0000580 }
Eric Andersen11c65522000-09-07 17:24:47 +0000581}
582
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000583
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200584#if ENABLE_DESKTOP
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200585/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
586 * If any of the -l, -n, -s options is specified, each list
587 * of files within the directory shall be preceded by a
588 * status line indicating the number of file system blocks
589 * occupied by files in the directory in 512-byte units if
590 * the -k option is not specified, or 1024-byte units if the
591 * -k option is specified, rounded up to the next integral
592 * number of units.
593 */
594/* by Jorgen Overgaard (jorgen AT antistaten.se) */
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200595static off_t calculate_blocks(struct dnode **dn, int nfiles)
596{
597 uoff_t blocks = 1;
598 while (nfiles) {
599 blocks += (*dn)->dstat.st_blocks; /* in 512 byte blocks */
600 dn++;
601 nfiles--;
602 }
603
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200604 /* Even though standard says use 512 byte blocks, coreutils use 1k */
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200605 /* Actually, we round up by calculating (blocks + 1) / 2,
606 * "+ 1" was done when we initialized blocks to 1 */
607 return blocks >> 1;
608}
609#endif
610
611
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200612static void showdirs(struct dnode **dn, int first)
Eric Andersen11c65522000-09-07 17:24:47 +0000613{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200614 unsigned nfiles;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200615 unsigned dndirs;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200616 struct dnode **subdnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000617 struct dnode **dnd;
Eric Andersen11c65522000-09-07 17:24:47 +0000618
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200619 /* Never happens:
620 if (dn == NULL || ndirs < 1) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000621 return;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200622 }
623 */
Eric Andersen11c65522000-09-07 17:24:47 +0000624
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200625 for (; *dn; dn++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000626 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000627 if (!first)
Denis Vlasenko4daad902007-09-27 10:20:47 +0000628 bb_putchar('\n');
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000629 first = 0;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200630 printf("%s:\n", (*dn)->fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000631 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200632 subdnp = list_dir((*dn)->fullname, &nfiles);
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200633#if ENABLE_DESKTOP
634 if (all_fmt & STYLE_LONG)
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200635 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200636#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000637 if (nfiles > 0) {
638 /* list all files at this level */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000639 dnsort(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000640 showfiles(subdnp, nfiles);
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200641 if (ENABLE_FEATURE_LS_RECURSIVE
642 && (all_fmt & DISP_RECURSIVE)
643 ) {
644 /* recursive - list the sub-dirs */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200645 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
646 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200647 if (dndirs > 0) {
648 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200649 showdirs(dnd, 0);
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200650 /* free the array of dnode pointers to the dirs */
651 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +0000652 }
653 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200654 /* free the dnodes and the fullname mem */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200655 dfree(subdnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000656 }
657 }
658}
659
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000660
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200661/* Returns NULL-terminated malloced vector of pointers (or NULL) */
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 {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001073 dnd = splitdnarray(dnp, SPLIT_DIR);
1074 dnf = splitdnarray(dnp, SPLIT_FILE);
1075 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001076 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);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001085 showdirs(dnd, 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)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001091 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001092 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001093}