blob: e7544474b217545b141bb63071610029d5269474 [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;
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100484 off_t 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) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100490 dif = (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000491 } else if (sort_opts == SORT_ATIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100492 dif = (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000493 } else if (sort_opts == SORT_CTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100494 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000495 } else if (sort_opts == SORT_MTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100496 dif = (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 }
Eric Andersen11c65522000-09-07 17:24:47 +0000502 if (dif == 0) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100503 /* sort by name, or tie_breaker for other sorts */
504 if (ENABLE_LOCALE_SUPPORT)
505 dif = strcoll(d1->name, d2->name);
506 else
507 dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000508 }
509
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100510 /* Make dif fit into an int */
511 if (sizeof(dif) > sizeof(int)) {
512 if (sizeof(dif) == sizeof(int)*2) {
513 /* typical on many arches */
514 if (dif != 0) {
515 dif = 1 | (int)((uoff_t)dif >> (sizeof(int)*8));
516 }
517 } else {
518 while ((dif & ~(off_t)INT_MAX) != 0) {
519 dif >>= (sizeof(int)*8 / 2);
520 }
521 }
Eric Andersen11c65522000-09-07 17:24:47 +0000522 }
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100523
524 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000525}
526
Rob Landley425e7582006-05-03 20:22:03 +0000527static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000528{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000529 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000530}
Rob Landleyea224be2006-06-18 20:20:07 +0000531#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000532#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000533#endif
534
Rob Landleyea224be2006-06-18 20:20:07 +0000535
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200536static void showfiles(struct dnode **dn, unsigned nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000537{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200538 unsigned i, ncols, nrows, row, nc;
539 unsigned column = 0;
540 unsigned nexttab = 0;
541 unsigned column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
Eric Andersen11c65522000-09-07 17:24:47 +0000542
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200543 /* Never happens:
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000544 if (dn == NULL || nfiles < 1)
545 return;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200546 */
Eric Andersen11c65522000-09-07 17:24:47 +0000547
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000548 if (all_fmt & STYLE_LONG) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000549 ncols = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000550 } else {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000551 /* find the longest file name, use that as the column width */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200552 for (i = 0; dn[i]; i++) {
Denys Vlasenko9f93d622010-01-24 07:44:03 +0100553 int len = unicode_strlen(dn[i]->name);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000554 if (column_width < len)
555 column_width = len;
556 }
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000557 column_width += tabstops +
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000558 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000559 ((all_fmt & LIST_INO) ? 8 : 0) +
560 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
Glenn L McGrath4d001292003-01-06 01:11:50 +0000561 ncols = (int) (terminal_width / column_width);
Eric Andersen11c65522000-09-07 17:24:47 +0000562 }
563
Eric Andersene57d54b2001-01-30 18:03:11 +0000564 if (ncols > 1) {
565 nrows = nfiles / ncols;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000566 if (nrows * ncols < nfiles)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000567 nrows++; /* round up fractionals */
Eric Andersene57d54b2001-01-30 18:03:11 +0000568 } else {
569 nrows = nfiles;
570 ncols = 1;
571 }
Eric Andersen11c65522000-09-07 17:24:47 +0000572
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000573 for (row = 0; row < nrows; row++) {
574 for (nc = 0; nc < ncols; nc++) {
Eric Andersen79565b62000-08-11 18:10:21 +0000575 /* reach into the array based on the column and row */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000576 if (all_fmt & DISP_ROWS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000577 i = (row * ncols) + nc; /* display across row */
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200578 else
579 i = (nc * nrows) + row; /* display by column */
Eric Andersen11c65522000-09-07 17:24:47 +0000580 if (i < nfiles) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000581 if (column > 0) {
582 nexttab -= column;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000583 printf("%*s", nexttab, "");
584 column += nexttab;
585 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000586 nexttab = column + column_width;
587 column += list_single(dn[i]);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000588 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000589 }
590 putchar('\n');
591 column = 0;
Eric Andersena42982e2000-06-07 17:28:53 +0000592 }
Eric Andersen11c65522000-09-07 17:24:47 +0000593}
594
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000595
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200596#if ENABLE_DESKTOP
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200597/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
598 * If any of the -l, -n, -s options is specified, each list
599 * of files within the directory shall be preceded by a
600 * status line indicating the number of file system blocks
601 * occupied by files in the directory in 512-byte units if
602 * the -k option is not specified, or 1024-byte units if the
603 * -k option is specified, rounded up to the next integral
604 * number of units.
605 */
606/* by Jorgen Overgaard (jorgen AT antistaten.se) */
Denys Vlasenko1d63f042009-10-03 11:45:07 +0200607static off_t calculate_blocks(struct dnode **dn)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200608{
609 uoff_t blocks = 1;
Denys Vlasenko1d63f042009-10-03 11:45:07 +0200610 if (dn) {
611 while (*dn) {
612 /* st_blocks is in 512 byte blocks */
613 blocks += (*dn)->dstat.st_blocks;
614 dn++;
615 }
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200616 }
617
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200618 /* Even though standard says use 512 byte blocks, coreutils use 1k */
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200619 /* Actually, we round up by calculating (blocks + 1) / 2,
620 * "+ 1" was done when we initialized blocks to 1 */
621 return blocks >> 1;
622}
623#endif
624
625
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200626static void showdirs(struct dnode **dn, int first)
Eric Andersen11c65522000-09-07 17:24:47 +0000627{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200628 unsigned nfiles;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200629 unsigned dndirs;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200630 struct dnode **subdnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000631 struct dnode **dnd;
Eric Andersen11c65522000-09-07 17:24:47 +0000632
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200633 /* Never happens:
634 if (dn == NULL || ndirs < 1) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000635 return;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200636 }
637 */
Eric Andersen11c65522000-09-07 17:24:47 +0000638
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200639 for (; *dn; dn++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000640 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000641 if (!first)
Denis Vlasenko4daad902007-09-27 10:20:47 +0000642 bb_putchar('\n');
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000643 first = 0;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200644 printf("%s:\n", (*dn)->fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000645 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200646 subdnp = list_dir((*dn)->fullname, &nfiles);
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200647#if ENABLE_DESKTOP
Denys Vlasenkod93fc612009-10-23 16:22:25 +0200648 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200649 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200650#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000651 if (nfiles > 0) {
652 /* list all files at this level */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000653 dnsort(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000654 showfiles(subdnp, nfiles);
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200655 if (ENABLE_FEATURE_LS_RECURSIVE
656 && (all_fmt & DISP_RECURSIVE)
657 ) {
658 /* recursive - list the sub-dirs */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200659 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
660 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200661 if (dndirs > 0) {
662 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200663 showdirs(dnd, 0);
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200664 /* free the array of dnode pointers to the dirs */
665 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +0000666 }
667 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200668 /* free the dnodes and the fullname mem */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200669 dfree(subdnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000670 }
671 }
672}
673
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000674
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200675/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200676static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
Eric Andersen11c65522000-09-07 17:24:47 +0000677{
678 struct dnode *dn, *cur, **dnp;
679 struct dirent *entry;
680 DIR *dir;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200681 unsigned i, nfiles;
Eric Andersen11c65522000-09-07 17:24:47 +0000682
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200683 /* Never happens:
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000684 if (path == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000685 return NULL;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200686 */
Eric Andersen11c65522000-09-07 17:24:47 +0000687
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200688 *nfiles_p = 0;
Rob Landleyd921b2e2006-08-03 15:41:12 +0000689 dir = warn_opendir(path);
Eric Andersen11c65522000-09-07 17:24:47 +0000690 if (dir == NULL) {
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000691 exit_code = EXIT_FAILURE;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000692 return NULL; /* could not open the dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000693 }
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200694 dn = NULL;
695 nfiles = 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000696 while ((entry = readdir(dir)) != NULL) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000697 char *fullname;
698
Eric Andersen11c65522000-09-07 17:24:47 +0000699 /* are we going to list the file- it may be . or .. or a hidden file */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000700 if (entry->d_name[0] == '.') {
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000701 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
702 && !(all_fmt & DISP_DOT)
703 ) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000704 continue;
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000705 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000706 if (!(all_fmt & DISP_HIDDEN))
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000707 continue;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000708 }
709 fullname = concat_path_file(path, entry->d_name);
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000710 cur = my_stat(fullname, bb_basename(fullname), 0);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000711 if (!cur) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000712 free(fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000713 continue;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000714 }
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200715 cur->fname_allocated = 1;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000716 cur->next = dn;
717 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +0000718 nfiles++;
719 }
720 closedir(dir);
721
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200722 if (dn == NULL)
723 return NULL;
724
Eric Andersen11c65522000-09-07 17:24:47 +0000725 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +0000726 * allocate memory for an array to hold dnode pointers
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000727 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200728 *nfiles_p = nfiles;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000729 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200730 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
731 dnp[i] = dn; /* save pointer to node in array */
732 dn = dn->next;
733 if (!dn)
734 break;
Eric Andersen11c65522000-09-07 17:24:47 +0000735 }
736
Denis Vlasenko5c759602006-10-28 12:37:16 +0000737 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000738}
739
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000740
Denis Vlasenko248ce912009-03-03 14:09:04 +0000741static int print_name(const char *name)
742{
743 if (option_mask32 & OPT_Q) {
744#if ENABLE_FEATURE_ASSUME_UNICODE
Denys Vlasenko9f93d622010-01-24 07:44:03 +0100745 unsigned len = 2 + unicode_strlen(name);
Denis Vlasenko248ce912009-03-03 14:09:04 +0000746#else
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200747 unsigned len = 2;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000748#endif
749 putchar('"');
750 while (*name) {
751 if (*name == '"') {
752 putchar('\\');
753 len++;
754 }
755 putchar(*name++);
756 if (!ENABLE_FEATURE_ASSUME_UNICODE)
757 len++;
758 }
759 putchar('"');
760 return len;
761 }
762 /* No -Q: */
763#if ENABLE_FEATURE_ASSUME_UNICODE
764 fputs(name, stdout);
Denys Vlasenko9f93d622010-01-24 07:44:03 +0100765 return unicode_strlen(name);
Denis Vlasenko248ce912009-03-03 14:09:04 +0000766#else
767 return printf("%s", name);
768#endif
769}
770
771
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200772static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000773{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200774 unsigned column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000775 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000776#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000777 struct stat info;
778 char append;
779#endif
780
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200781 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000782 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000783 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200784 */
Eric Andersen11c65522000-09-07 17:24:47 +0000785
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
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100791 * does not appear *inside* the "ls -l" line */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000792 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)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100797 column += printf("%7llu ", (long long) dn->dstat.st_ino);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000798 if (all_fmt & LIST_BLOCKS)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100799 column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000800 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) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100807 column += printf("%-8.8s ",
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000808 get_cached_username(dn->dstat.st_uid));
809 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100810 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)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100818 column += printf("%-8u ", (int) dn->dstat.st_uid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000819 else
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100820 column += printf("%-8u %-8u ",
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000821 (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) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100831 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
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
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100841 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
842 char *filetime;
843 time_t ttime = dn->dstat.st_mtime;
844 if (all_fmt & TIME_ACCESS)
845 ttime = dn->dstat.st_atime;
846 if (all_fmt & TIME_CHANGE)
847 ttime = dn->dstat.st_ctime;
848 filetime = ctime(&ttime);
849 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
850 if (all_fmt & LIST_FULLTIME)
851 column += printf("%.24s ", filetime);
852 else { /* LIST_DATE_TIME */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000853 /* current_time_t ~== time(NULL) */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100854 time_t age = current_time_t - ttime;
855 printf("%.6s ", filetime + 4); /* "Jun 30" */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000856 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
857 /* hh:mm if less than 6 months old */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100858 printf("%.5s ", filetime + 11);
859 } else { /* year. buggy if year > 9999 ;) */
860 printf(" %.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000861 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000862 column += 13;
863 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100864 }
Eric Andersen11c65522000-09-07 17:24:47 +0000865#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000866#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000867 if (all_fmt & LIST_CONTEXT) {
868 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
869 freecon(dn->sid);
870 }
Eric Andersen9e480452003-07-03 10:07:04 +0000871#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000872 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000873#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000874 if (show_color) {
875 info.st_mode = 0; /* for fgcolor() */
876 lstat(dn->fullname, &info);
877 printf("\033[%u;%um", bold(info.st_mode),
878 fgcolor(info.st_mode));
879 }
880#endif
881 column += print_name(dn->name);
882 if (show_color) {
883 printf("\033[0m");
884 }
885 }
886 if (all_fmt & LIST_SYMLINK) {
887 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
888 printf(" -> ");
889#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
890#if ENABLE_FEATURE_LS_COLOR
891 info.st_mode = 0; /* for fgcolor() */
892#endif
893 if (stat(dn->fullname, &info) == 0) {
894 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000895 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000896#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000897#if ENABLE_FEATURE_LS_COLOR
898 if (show_color) {
899 printf("\033[%u;%um", bold(info.st_mode),
900 fgcolor(info.st_mode));
901 }
902#endif
903 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000904 if (show_color) {
905 printf("\033[0m");
906 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000907 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000908 }
909 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000910#if ENABLE_FEATURE_LS_FILETYPES
911 if (all_fmt & LIST_FILETYPE) {
912 if (append) {
913 putchar(append);
914 column++;
915 }
916 }
917#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000918
Glenn L McGrath4d001292003-01-06 01:11:50 +0000919 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000920}
921
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000922
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000923int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000924{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000925 struct dnode **dnd;
926 struct dnode **dnf;
927 struct dnode **dnp;
928 struct dnode *dn;
929 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000930 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200931 unsigned nfiles;
932 unsigned dnfiles;
933 unsigned dndirs;
934 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200935#if ENABLE_FEATURE_LS_COLOR
936 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
937 /* coreutils 6.10:
938 * # ls --color=BOGUS
939 * ls: invalid argument 'BOGUS' for '--color'
940 * Valid arguments are:
941 * 'always', 'yes', 'force'
942 * 'never', 'no', 'none'
943 * 'auto', 'tty', 'if-tty'
944 * (and substrings: "--color=alwa" work too)
945 */
946 static const char ls_longopts[] ALIGN1 =
947 "color\0" Optional_argument "\xff"; /* no short equivalent */
948 static const char color_str[] ALIGN1 =
949 "always\0""yes\0""force\0"
950 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000951 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200952 const char *color_opt = color_str; /* "always" */
953#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +0000954
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000955 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000956
Denys Vlasenko28055022010-01-04 20:49:58 +0100957 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200958
Rob Landley2b8a05a2006-06-20 17:43:01 +0000959 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000960 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000961
Denis Vlasenko5c759602006-10-28 12:37:16 +0000962#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200963 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000964 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200965 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +0000966 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000967#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000968
Eric Andersen11c65522000-09-07 17:24:47 +0000969 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +0200970 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000971#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko1d426652008-03-17 09:09:09 +0000972 opt_complementary = "T+:w+"; /* -T N, -w N */
973 opt = getopt32(argv, ls_options, &tabstops, &terminal_width
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000974 IF_FEATURE_LS_COLOR(, &color_opt));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000975#else
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000976 opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
Manuel Novoa III cad53642003-03-19 09:13:01 +0000977#endif
Eric Andersend07cf592004-02-05 13:52:03 +0000978 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +0000979 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000980 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000981
Denis Vlasenko5c759602006-10-28 12:37:16 +0000982 if (flags & LIST_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000983 all_fmt &= ~LIST_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000984 if (flags & STYLE_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000985 all_fmt &= ~STYLE_MASK;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000986 if (flags & SORT_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000987 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000988 if (flags & DISP_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000989 all_fmt &= ~DISP_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000990 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000991 all_fmt &= ~TIME_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000992 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +0000993 all_fmt |= STYLE_SINGLE;
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000994 /* huh?? opt cannot be 'l' */
995 //if (LS_DISP_HR && opt == 'l')
996 // all_fmt &= ~LS_DISP_HR;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000997 all_fmt |= flags;
998 }
Eric Andersen11c65522000-09-07 17:24:47 +0000999 }
1000
Denis Vlasenko5c759602006-10-28 12:37:16 +00001001#if ENABLE_FEATURE_LS_COLOR
1002 /* find color bit value - last position for short getopt */
1003 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1004 char *p = getenv("LS_COLORS");
1005 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001006 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001007 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001008 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001009 if (opt & OPT_color) {
1010 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +00001011 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001012 else switch (index_in_substrings(color_str, color_opt)) {
1013 case 3:
1014 case 4:
1015 case 5:
1016 if (isatty(STDOUT_FILENO)) {
1017 case 0:
1018 case 1:
1019 case 2:
1020 show_color = 1;
1021 }
1022 }
Paul Fox156dc412005-08-01 19:33:30 +00001023 }
1024#endif
1025
Eric Andersen11c65522000-09-07 17:24:47 +00001026 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001027 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001028 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001029 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1030 if (all_fmt & TIME_CHANGE)
1031 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1032 if (all_fmt & TIME_ACCESS)
1033 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1034 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001035 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1036 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +00001037 if (ENABLE_FEATURE_LS_USERNAME)
1038 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1039 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001040
Eric Andersen11c65522000-09-07 17:24:47 +00001041 /* choose a display format */
Rob Landleyea224be2006-06-18 20:20:07 +00001042 if (!(all_fmt & STYLE_MASK))
Eric Andersen70060d22004-03-27 10:02:48 +00001043 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001044
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001045 argv += optind;
1046 if (!argv[0])
1047 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001048
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001049 if (argv[1])
1050 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001051
Denis Vlasenko5c759602006-10-28 12:37:16 +00001052 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001053 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001054 nfiles = 0;
1055 do {
Denis Vlasenko11a6f9b2009-03-03 13:20:22 +00001056 /* NB: follow links on command line unless -l or -s */
1057 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001058 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001059 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001060 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001061 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001062 cur->next = dn;
1063 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001064 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001065 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001066
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001067 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1068 if (nfiles == 0)
1069 return exit_code;
1070
Eric Andersen11c65522000-09-07 17:24:47 +00001071 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001072 * allocate memory for an array to hold dnode pointers
1073 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001074 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001075 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1076 dnp[i] = dn; /* save pointer to node in array */
1077 dn = dn->next;
1078 if (!dn)
1079 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001080 }
1081
Manuel Novoa III cad53642003-03-19 09:13:01 +00001082 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001083 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001084 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001085 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001086 dnd = splitdnarray(dnp, SPLIT_DIR);
1087 dnf = splitdnarray(dnp, SPLIT_FILE);
1088 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001089 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001090 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001091 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001092 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001093 if (ENABLE_FEATURE_CLEAN_UP)
1094 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001095 }
1096 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001097 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001098 showdirs(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001099 if (ENABLE_FEATURE_CLEAN_UP)
1100 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001101 }
1102 }
Rob Landley26314862006-05-02 19:46:52 +00001103 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001104 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001105 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001106}