blob: 13c863c6eb0bb918efa102c8a49554679829163f [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 Vlasenko1d63f042009-10-03 11:45:07 +0200595static off_t calculate_blocks(struct dnode **dn)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200596{
597 uoff_t blocks = 1;
Denys Vlasenko1d63f042009-10-03 11:45:07 +0200598 if (dn) {
599 while (*dn) {
600 /* st_blocks is in 512 byte blocks */
601 blocks += (*dn)->dstat.st_blocks;
602 dn++;
603 }
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200604 }
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 Vlasenkocae409c2009-10-03 11:43:48 +0200614static void showdirs(struct dnode **dn, int first)
Eric Andersen11c65522000-09-07 17:24:47 +0000615{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200616 unsigned nfiles;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200617 unsigned dndirs;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200618 struct dnode **subdnp;
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
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200627 for (; *dn; dn++) {
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;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200632 printf("%s:\n", (*dn)->fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000633 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200634 subdnp = list_dir((*dn)->fullname, &nfiles);
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200635#if ENABLE_DESKTOP
Denys Vlasenkod93fc612009-10-23 16:22:25 +0200636 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200637 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200638#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);
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200643 if (ENABLE_FEATURE_LS_RECURSIVE
644 && (all_fmt & DISP_RECURSIVE)
645 ) {
646 /* recursive - list the sub-dirs */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200647 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
648 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200649 if (dndirs > 0) {
650 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200651 showdirs(dnd, 0);
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200652 /* free the array of dnode pointers to the dirs */
653 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +0000654 }
655 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200656 /* free the dnodes and the fullname mem */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200657 dfree(subdnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000658 }
659 }
660}
661
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000662
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200663/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200664static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
Eric Andersen11c65522000-09-07 17:24:47 +0000665{
666 struct dnode *dn, *cur, **dnp;
667 struct dirent *entry;
668 DIR *dir;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200669 unsigned i, nfiles;
Eric Andersen11c65522000-09-07 17:24:47 +0000670
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200671 /* Never happens:
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000672 if (path == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000673 return NULL;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200674 */
Eric Andersen11c65522000-09-07 17:24:47 +0000675
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200676 *nfiles_p = 0;
Rob Landleyd921b2e2006-08-03 15:41:12 +0000677 dir = warn_opendir(path);
Eric Andersen11c65522000-09-07 17:24:47 +0000678 if (dir == NULL) {
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000679 exit_code = EXIT_FAILURE;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000680 return NULL; /* could not open the dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000681 }
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200682 dn = NULL;
683 nfiles = 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000684 while ((entry = readdir(dir)) != NULL) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000685 char *fullname;
686
Eric Andersen11c65522000-09-07 17:24:47 +0000687 /* are we going to list the file- it may be . or .. or a hidden file */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000688 if (entry->d_name[0] == '.') {
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000689 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
690 && !(all_fmt & DISP_DOT)
691 ) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000692 continue;
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000693 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000694 if (!(all_fmt & DISP_HIDDEN))
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000695 continue;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000696 }
697 fullname = concat_path_file(path, entry->d_name);
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000698 cur = my_stat(fullname, bb_basename(fullname), 0);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000699 if (!cur) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000700 free(fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000701 continue;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000702 }
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200703 cur->fname_allocated = 1;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000704 cur->next = dn;
705 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +0000706 nfiles++;
707 }
708 closedir(dir);
709
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200710 if (dn == NULL)
711 return NULL;
712
Eric Andersen11c65522000-09-07 17:24:47 +0000713 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +0000714 * allocate memory for an array to hold dnode pointers
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000715 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200716 *nfiles_p = nfiles;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000717 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200718 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
719 dnp[i] = dn; /* save pointer to node in array */
720 dn = dn->next;
721 if (!dn)
722 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000723 }
724
Denis Vlasenko5c759602006-10-28 12:37:16 +0000725 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000726}
727
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000728
Denis Vlasenko248ce912009-03-03 14:09:04 +0000729static int print_name(const char *name)
730{
731 if (option_mask32 & OPT_Q) {
732#if ENABLE_FEATURE_ASSUME_UNICODE
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200733 unsigned len = 2 + bb_mbstrlen(name);
Denis Vlasenko248ce912009-03-03 14:09:04 +0000734#else
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200735 unsigned len = 2;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000736#endif
737 putchar('"');
738 while (*name) {
739 if (*name == '"') {
740 putchar('\\');
741 len++;
742 }
743 putchar(*name++);
744 if (!ENABLE_FEATURE_ASSUME_UNICODE)
745 len++;
746 }
747 putchar('"');
748 return len;
749 }
750 /* No -Q: */
751#if ENABLE_FEATURE_ASSUME_UNICODE
752 fputs(name, stdout);
Denys Vlasenkofda8f572009-07-11 22:26:48 +0200753 return bb_mbstrlen(name);
Denis Vlasenko248ce912009-03-03 14:09:04 +0000754#else
755 return printf("%s", name);
756#endif
757}
758
759
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200760static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000761{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200762 unsigned column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000763 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000764#if ENABLE_FEATURE_LS_TIMESTAMPS
Eric Andersen11c65522000-09-07 17:24:47 +0000765 char *filetime;
766 time_t ttime, age;
767#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000768#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000769 struct stat info;
770 char append;
771#endif
772
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200773 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000774 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000775 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200776 */
Eric Andersen11c65522000-09-07 17:24:47 +0000777
Denis Vlasenko5c759602006-10-28 12:37:16 +0000778#if ENABLE_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000779 ttime = dn->dstat.st_mtime; /* the default time */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000780 if (all_fmt & TIME_ACCESS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000781 ttime = dn->dstat.st_atime;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000782 if (all_fmt & TIME_CHANGE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000783 ttime = dn->dstat.st_ctime;
784 filetime = ctime(&ttime);
Eric Andersen11c65522000-09-07 17:24:47 +0000785#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000786#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000787 append = append_char(dn->dstat.st_mode);
788#endif
789
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000790 /* Do readlink early, so that if it fails, error message
791 * does not appear *inside* of the "ls -l" line */
792 if (all_fmt & LIST_SYMLINK)
793 if (S_ISLNK(dn->dstat.st_mode))
794 lpath = xmalloc_readlink_or_warn(dn->fullname);
795
796 if (all_fmt & LIST_INO)
797 column += printf("%7lu ", (long) dn->dstat.st_ino);
798 if (all_fmt & LIST_BLOCKS)
799 column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1);
800 if (all_fmt & LIST_MODEBITS)
801 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
802 if (all_fmt & LIST_NLINKS)
803 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000804#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000805 if (all_fmt & LIST_ID_NAME) {
806 if (option_mask32 & OPT_g) {
807 column += printf("%-8.8s",
808 get_cached_username(dn->dstat.st_uid));
809 } else {
810 column += printf("%-8.8s %-8.8s",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000811 get_cached_username(dn->dstat.st_uid),
812 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000813 }
814 }
Eric Andersen11c65522000-09-07 17:24:47 +0000815#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000816 if (all_fmt & LIST_ID_NUMERIC) {
817 if (option_mask32 & OPT_g)
818 column += printf("%-8u", (int) dn->dstat.st_uid);
819 else
820 column += printf("%-8u %-8u",
821 (int) dn->dstat.st_uid,
822 (int) dn->dstat.st_gid);
823 }
824 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
825 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
826 column += printf("%4u, %3u ",
827 (int) major(dn->dstat.st_rdev),
828 (int) minor(dn->dstat.st_rdev));
829 } else {
830 if (all_fmt & LS_DISP_HR) {
831 column += printf("%9s ",
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200832 /* print st_size, show one fractional, use suffixes */
833 make_human_readable_str(dn->dstat.st_size, 1, 0)
834 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000835 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000836 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000837 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000838 }
839 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000840#if ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000841 if (all_fmt & LIST_FULLTIME)
842 column += printf("%24.24s ", filetime);
843 if (all_fmt & LIST_DATE_TIME)
844 if ((all_fmt & LIST_FULLTIME) == 0) {
845 /* current_time_t ~== time(NULL) */
846 age = current_time_t - ttime;
847 printf("%6.6s ", filetime + 4);
848 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
849 /* hh:mm if less than 6 months old */
850 printf("%5.5s ", filetime + 11);
851 } else {
852 printf(" %4.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000853 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000854 column += 13;
855 }
Eric Andersen11c65522000-09-07 17:24:47 +0000856#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000857#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000858 if (all_fmt & LIST_CONTEXT) {
859 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
860 freecon(dn->sid);
861 }
Eric Andersen9e480452003-07-03 10:07:04 +0000862#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000863 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000864#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000865 if (show_color) {
866 info.st_mode = 0; /* for fgcolor() */
867 lstat(dn->fullname, &info);
868 printf("\033[%u;%um", bold(info.st_mode),
869 fgcolor(info.st_mode));
870 }
871#endif
872 column += print_name(dn->name);
873 if (show_color) {
874 printf("\033[0m");
875 }
876 }
877 if (all_fmt & LIST_SYMLINK) {
878 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
879 printf(" -> ");
880#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
881#if ENABLE_FEATURE_LS_COLOR
882 info.st_mode = 0; /* for fgcolor() */
883#endif
884 if (stat(dn->fullname, &info) == 0) {
885 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000886 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000887#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000888#if ENABLE_FEATURE_LS_COLOR
889 if (show_color) {
890 printf("\033[%u;%um", bold(info.st_mode),
891 fgcolor(info.st_mode));
892 }
893#endif
894 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000895 if (show_color) {
896 printf("\033[0m");
897 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000898 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000899 }
900 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000901#if ENABLE_FEATURE_LS_FILETYPES
902 if (all_fmt & LIST_FILETYPE) {
903 if (append) {
904 putchar(append);
905 column++;
906 }
907 }
908#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000909
Glenn L McGrath4d001292003-01-06 01:11:50 +0000910 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000911}
912
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000913
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000914int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000915{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000916 struct dnode **dnd;
917 struct dnode **dnf;
918 struct dnode **dnp;
919 struct dnode *dn;
920 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000921 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200922 unsigned nfiles;
923 unsigned dnfiles;
924 unsigned dndirs;
925 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200926#if ENABLE_FEATURE_LS_COLOR
927 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
928 /* coreutils 6.10:
929 * # ls --color=BOGUS
930 * ls: invalid argument 'BOGUS' for '--color'
931 * Valid arguments are:
932 * 'always', 'yes', 'force'
933 * 'never', 'no', 'none'
934 * 'auto', 'tty', 'if-tty'
935 * (and substrings: "--color=alwa" work too)
936 */
937 static const char ls_longopts[] ALIGN1 =
938 "color\0" Optional_argument "\xff"; /* no short equivalent */
939 static const char color_str[] ALIGN1 =
940 "always\0""yes\0""force\0"
941 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000942 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200943 const char *color_opt = color_str; /* "always" */
944#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +0000945
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000946 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000947
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200948 check_unicode_in_env();
949
Rob Landley2b8a05a2006-06-20 17:43:01 +0000950 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000951 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000952
Denis Vlasenko5c759602006-10-28 12:37:16 +0000953#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200954 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000955 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200956 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +0000957 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000958#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000959
Eric Andersen11c65522000-09-07 17:24:47 +0000960 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200961 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000962#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko1d426652008-03-17 09:09:09 +0000963 opt_complementary = "T+:w+"; /* -T N, -w N */
964 opt = getopt32(argv, ls_options, &tabstops, &terminal_width
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000965 IF_FEATURE_LS_COLOR(, &color_opt));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000966#else
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000967 opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
Manuel Novoa III cad53642003-03-19 09:13:01 +0000968#endif
Eric Andersend07cf592004-02-05 13:52:03 +0000969 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +0000970 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000971 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000972
Denis Vlasenko5c759602006-10-28 12:37:16 +0000973 if (flags & LIST_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000974 all_fmt &= ~LIST_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000975 if (flags & STYLE_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000976 all_fmt &= ~STYLE_MASK;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000977 if (flags & SORT_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000978 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000979 if (flags & DISP_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000980 all_fmt &= ~DISP_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000981 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000982 all_fmt &= ~TIME_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000983 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +0000984 all_fmt |= STYLE_SINGLE;
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000985 /* huh?? opt cannot be 'l' */
986 //if (LS_DISP_HR && opt == 'l')
987 // all_fmt &= ~LS_DISP_HR;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000988 all_fmt |= flags;
989 }
Eric Andersen11c65522000-09-07 17:24:47 +0000990 }
991
Denis Vlasenko5c759602006-10-28 12:37:16 +0000992#if ENABLE_FEATURE_LS_COLOR
993 /* find color bit value - last position for short getopt */
994 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
995 char *p = getenv("LS_COLORS");
996 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000997 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000998 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000999 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001000 if (opt & OPT_color) {
1001 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +00001002 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001003 else switch (index_in_substrings(color_str, color_opt)) {
1004 case 3:
1005 case 4:
1006 case 5:
1007 if (isatty(STDOUT_FILENO)) {
1008 case 0:
1009 case 1:
1010 case 2:
1011 show_color = 1;
1012 }
1013 }
Paul Fox156dc412005-08-01 19:33:30 +00001014 }
1015#endif
1016
Eric Andersen11c65522000-09-07 17:24:47 +00001017 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001018 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001019 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001020 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1021 if (all_fmt & TIME_CHANGE)
1022 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1023 if (all_fmt & TIME_ACCESS)
1024 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1025 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001026 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1027 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +00001028 if (ENABLE_FEATURE_LS_USERNAME)
1029 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1030 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001031
Eric Andersen11c65522000-09-07 17:24:47 +00001032 /* choose a display format */
Rob Landleyea224be2006-06-18 20:20:07 +00001033 if (!(all_fmt & STYLE_MASK))
Eric Andersen70060d22004-03-27 10:02:48 +00001034 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001035
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001036 argv += optind;
1037 if (!argv[0])
1038 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001039
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001040 if (argv[1])
1041 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001042
Denis Vlasenko5c759602006-10-28 12:37:16 +00001043 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001044 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001045 nfiles = 0;
1046 do {
Denis Vlasenko11a6f9b2009-03-03 13:20:22 +00001047 /* NB: follow links on command line unless -l or -s */
1048 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001049 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001050 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001051 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001052 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001053 cur->next = dn;
1054 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001055 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001056 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001057
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001058 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1059 if (nfiles == 0)
1060 return exit_code;
1061
Eric Andersen11c65522000-09-07 17:24:47 +00001062 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001063 * allocate memory for an array to hold dnode pointers
1064 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001065 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001066 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1067 dnp[i] = dn; /* save pointer to node in array */
1068 dn = dn->next;
1069 if (!dn)
1070 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001071 }
1072
Manuel Novoa III cad53642003-03-19 09:13:01 +00001073 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001074 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001075 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001076 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001077 dnd = splitdnarray(dnp, SPLIT_DIR);
1078 dnf = splitdnarray(dnp, SPLIT_FILE);
1079 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001080 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001081 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001082 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001083 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001084 if (ENABLE_FEATURE_CLEAN_UP)
1085 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001086 }
1087 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001088 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001089 showdirs(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001090 if (ENABLE_FEATURE_CLEAN_UP)
1091 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001092 }
1093 }
Rob Landley26314862006-05-02 19:46:52 +00001094 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001095 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001096 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001097}