blob: 7bc7b28c8709b604504ce039b4700f4c8b417aab [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 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02006 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
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 */
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010032
33//usage:#define ls_trivial_usage
34//usage: "[-1AaCxd"
35//usage: IF_FEATURE_LS_FOLLOWLINKS("L")
36//usage: IF_FEATURE_LS_RECURSIVE("R")
37//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins"
38//usage: IF_FEATURE_LS_TIMESTAMPS("e")
39//usage: IF_FEATURE_HUMAN_READABLE("h")
40//usage: IF_FEATURE_LS_SORTFILES("rSXv")
41//usage: IF_FEATURE_LS_TIMESTAMPS("ctu")
42//usage: IF_SELINUX("kKZ") "]"
43//usage: IF_FEATURE_AUTOWIDTH(" -w WIDTH") " [FILE]..."
44//usage:#define ls_full_usage "\n\n"
45//usage: "List directory contents\n"
46//usage: "\nOptions:"
47//usage: "\n -1 List in a single column"
48//usage: "\n -A Don't list . and .."
49//usage: "\n -a Don't hide entries starting with ."
50//usage: "\n -C List by columns"
51//usage: "\n -x List by lines"
52//usage: "\n -d List directory entries instead of contents"
53//usage: IF_FEATURE_LS_FOLLOWLINKS(
54//usage: "\n -L List entries pointed to by symlinks"
55//usage: )
56//usage: IF_FEATURE_LS_RECURSIVE(
57//usage: "\n -R Recurse"
58//usage: )
59//usage: IF_FEATURE_LS_FILETYPES(
60//usage: "\n -F Append indicator (one of */=@|) to entries"
61//usage: "\n -p Append indicator (one of /=@|) to entries"
62//usage: )
63//usage: "\n -l Long listing format"
64//usage: "\n -i List inode numbers"
65//usage: "\n -n List numeric UIDs and GIDs instead of names"
66//usage: "\n -s List the size of each file, in blocks"
67//usage: IF_FEATURE_LS_TIMESTAMPS(
68//usage: "\n -e List full date and time"
69//usage: )
70//usage: IF_FEATURE_HUMAN_READABLE(
71//usage: "\n -h List sizes in human readable format (1K 243M 2G)"
72//usage: )
73//usage: IF_FEATURE_LS_SORTFILES(
74//usage: "\n -r Sort in reverse order"
75//usage: "\n -S Sort by file size"
76//usage: "\n -X Sort by extension"
77//usage: "\n -v Sort by version"
78//usage: )
79//usage: IF_FEATURE_LS_TIMESTAMPS(
80//usage: "\n -c With -l: sort by ctime"
81//usage: "\n -t With -l: sort by modification time"
82//usage: "\n -u With -l: sort by access time"
83//usage: )
84//usage: IF_SELINUX(
85//usage: "\n -k List security context"
86//usage: "\n -K List security context in long format"
87//usage: "\n -Z List security context and permission"
88//usage: )
89//usage: IF_FEATURE_AUTOWIDTH(
90//usage: "\n -w N Assume the terminal is N columns wide"
91//usage: )
92//usage: IF_FEATURE_LS_COLOR(
93//usage: "\n --color[={always,never,auto}] Control coloring"
94//usage: )
95
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000096#include "libbb.h"
Denys Vlasenko42a8fd02009-07-11 21:36:13 +020097#include "unicode.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +000098
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +000099
Denis Vlasenko99912ca2007-04-10 15:43:37 +0000100/* This is a NOEXEC applet. Be very careful! */
101
Eric Andersenf1142c52001-02-20 06:16:29 +0000102
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000103#if ENABLE_FTPD
104/* ftpd uses ls, and without timestamps Mozilla won't understand
105 * ftpd's LIST output.
106 */
107# undef CONFIG_FEATURE_LS_TIMESTAMPS
108# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000109# undef IF_FEATURE_LS_TIMESTAMPS
110# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000111# define CONFIG_FEATURE_LS_TIMESTAMPS 1
112# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000113# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
114# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000115#endif
116
117
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000118enum {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000119TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000120
121/* what is the overall style of the listing */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100122STYLE_COLUMNAR = 1 << 21, /* many records per line */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000123STYLE_LONG = 2 << 21, /* one record per line, extended info */
124STYLE_SINGLE = 3 << 21, /* one record per line */
125STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000126
Eric Andersen11c65522000-09-07 17:24:47 +0000127/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
128/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000129LIST_INO = 1 << 0,
130LIST_BLOCKS = 1 << 1,
131LIST_MODEBITS = 1 << 2,
132LIST_NLINKS = 1 << 3,
133LIST_ID_NAME = 1 << 4,
134LIST_ID_NUMERIC = 1 << 5,
135LIST_CONTEXT = 1 << 6,
136LIST_SIZE = 1 << 7,
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000137//LIST_DEV = 1 << 8, - unused, synonym to LIST_SIZE
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000138LIST_DATE_TIME = 1 << 9,
139LIST_FULLTIME = 1 << 10,
140LIST_FILENAME = 1 << 11,
141LIST_SYMLINK = 1 << 12,
142LIST_FILETYPE = 1 << 13,
143LIST_EXEC = 1 << 14,
144LIST_MASK = (LIST_EXEC << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +0000145
146/* what files will be displayed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000147DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */
Denis Vlasenko656f7462006-10-28 13:02:55 +0000148DISP_HIDDEN = 1 << 16, /* show filenames starting with . */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000149DISP_DOT = 1 << 17, /* show . and .. */
150DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */
151DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */
152DISP_ROWS = 1 << 20, /* print across rows */
153DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +0000154
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000155/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
156SORT_FORWARD = 0, /* sort in reverse order */
157SORT_REVERSE = 1 << 27, /* sort in reverse order */
Eric Andersen11c65522000-09-07 17:24:47 +0000158
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000159SORT_NAME = 0, /* sort by file name */
160SORT_SIZE = 1 << 28, /* sort by file size */
161SORT_ATIME = 2 << 28, /* sort by last access time */
162SORT_CTIME = 3 << 28, /* sort by last change time */
163SORT_MTIME = 4 << 28, /* sort by last modification time */
164SORT_VERSION = 5 << 28, /* sort by version */
165SORT_EXT = 6 << 28, /* sort by file name extension */
166SORT_DIR = 7 << 28, /* sort by file or directory */
167SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000168
Eric Andersen11c65522000-09-07 17:24:47 +0000169/* which of the three times will be used */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000170TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
171TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
172TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000173
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000174FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
Eric Andersencc8ed391999-10-05 16:24:54 +0000175
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000176LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000177
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000178LIST_SHORT = LIST_FILENAME,
179LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
180 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
181
182SPLIT_DIR = 1,
183SPLIT_FILE = 0,
184SPLIT_SUBDIR = 2,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000185};
Eric Andersencc8ed391999-10-05 16:24:54 +0000186
Denis Vlasenko248ce912009-03-03 14:09:04 +0000187/* "[-]Cadil1", POSIX mandated options, busybox always supports */
188/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
189/* "[-]Q" GNU option? busybox always supports */
190/* "[-]Ak" GNU options, busybox always supports */
191/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
192/* "[-]p", POSIX non-mandated options, busybox optionally supports */
193/* "[-]SXvThw", GNU options, busybox optionally supports */
194/* "[-]K", SELinux mandated options, busybox optionally supports */
195/* "[-]e", I think we made this one up */
196static const char ls_options[] ALIGN1 =
197 "Cadil1gnsxQAk" /* 13 opts, total 13 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000198 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
199 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
200 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
201 IF_FEATURE_LS_FOLLOWLINKS("L") /* 1, 24 */
202 IF_FEATURE_LS_RECURSIVE("R") /* 1, 25 */
203 IF_FEATURE_HUMAN_READABLE("h") /* 1, 26 */
Denys Vlasenko55083632009-07-02 14:25:51 +0200204 IF_SELINUX("KZ") /* 2, 28 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000205 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000206 ;
207enum {
208 //OPT_C = (1 << 0),
209 //OPT_a = (1 << 1),
210 //OPT_d = (1 << 2),
211 //OPT_i = (1 << 3),
212 //OPT_l = (1 << 4),
213 //OPT_1 = (1 << 5),
214 OPT_g = (1 << 6),
215 //OPT_n = (1 << 7),
216 //OPT_s = (1 << 8),
217 //OPT_x = (1 << 9),
218 OPT_Q = (1 << 10),
219 //OPT_A = (1 << 11),
220 //OPT_k = (1 << 12),
Denys Vlasenko163d8642010-12-19 06:16:28 +0100221 OPTBIT_F = 13
Denys Vlasenko55083632009-07-02 14:25:51 +0200222 + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko163d8642010-12-19 06:16:28 +0100223 + 4 * ENABLE_FEATURE_LS_SORTFILES,
224 OPTBIT_color = OPTBIT_F
Denys Vlasenko55083632009-07-02 14:25:51 +0200225 + 2 * ENABLE_FEATURE_LS_FILETYPES
226 + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
227 + 1 * ENABLE_FEATURE_LS_RECURSIVE
228 + 1 * ENABLE_FEATURE_HUMAN_READABLE
229 + 2 * ENABLE_SELINUX
230 + 2 * ENABLE_FEATURE_AUTOWIDTH,
Denys Vlasenko163d8642010-12-19 06:16:28 +0100231 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
Denys Vlasenko55083632009-07-02 14:25:51 +0200232 OPT_color = 1 << OPTBIT_color,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000233};
234
Denis Vlasenko248ce912009-03-03 14:09:04 +0000235/* TODO: simple toggles may be stored as OPT_xxx bits instead */
236static const unsigned opt_flags[] = {
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100237 LIST_SHORT | STYLE_COLUMNAR, /* C */
238 DISP_HIDDEN | DISP_DOT, /* a */
239 DISP_NOLIST, /* d */
240 LIST_INO, /* i */
241 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
242 LIST_SHORT | STYLE_SINGLE, /* 1 */
243 0, /* g (don't show owner) - handled via OPT_g */
244 LIST_ID_NUMERIC, /* n */
245 LIST_BLOCKS, /* s */
246 LIST_SHORT | DISP_ROWS | STYLE_COLUMNAR, /* x */
247 0, /* Q (quote filename) - handled via OPT_Q */
248 DISP_HIDDEN, /* A */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000249 ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
250#if ENABLE_FEATURE_LS_TIMESTAMPS
251 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100252 LIST_FULLTIME, /* e */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000253 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
254 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
255#endif
256#if ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100257 SORT_SIZE, /* S */
258 SORT_EXT, /* X */
259 SORT_REVERSE, /* r */
260 SORT_VERSION, /* v */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000261#endif
262#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100263 LIST_FILETYPE | LIST_EXEC, /* F */
264 LIST_FILETYPE, /* p */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000265#endif
266#if ENABLE_FEATURE_LS_FOLLOWLINKS
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100267 FOLLOW_LINKS, /* L */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000268#endif
269#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100270 DISP_RECURSIVE, /* R */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000271#endif
272#if ENABLE_FEATURE_HUMAN_READABLE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100273 LS_DISP_HR, /* h */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000274#endif
275#if ENABLE_SELINUX
276 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
277#endif
278#if ENABLE_SELINUX
279 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
280#endif
281 (1U<<31)
282 /* options after Z are not processed through opt_flags:
283 * T, w - ignored
284 */
285};
286
287
Eric Andersen11c65522000-09-07 17:24:47 +0000288/*
289 * a directory entry and its stat info are stored here
290 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200291struct dnode {
292 const char *name; /* the dir entry name */
293 const char *fullname; /* the dir entry name */
294 struct dnode *next; /* point at the next node */
295 smallint fname_allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000296 struct stat dstat; /* the file stat info */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000297 IF_SELINUX(security_context_t sid;)
Eric Andersen11c65522000-09-07 17:24:47 +0000298};
Eric Andersen11c65522000-09-07 17:24:47 +0000299
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000300struct globals {
301#if ENABLE_FEATURE_LS_COLOR
302 smallint show_color;
303#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000304 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000305 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000306#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000307 unsigned terminal_width; // = TERMINAL_WIDTH;
308#endif
309#if ENABLE_FEATURE_LS_TIMESTAMPS
310 /* Do time() just once. Saves one syscall per file for "ls -l" */
311 time_t current_time_t;
312#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100313} FIX_ALIASING;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000314#define G (*(struct globals*)&bb_common_bufsiz1)
315#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200316# define show_color (G.show_color )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000317#else
318enum { show_color = 0 };
319#endif
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200320#define exit_code (G.exit_code )
321#define all_fmt (G.all_fmt )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000322#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200323# define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000324#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000325enum {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000326 terminal_width = TERMINAL_WIDTH,
327};
Eric Andersen11c65522000-09-07 17:24:47 +0000328#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000329#define current_time_t (G.current_time_t)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000330#define INIT_G() do { \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200331 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000332 memset(&G, 0, sizeof(G)); \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000333 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
334 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000335} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000336
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000337
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000338static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000339{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000340 struct stat dstat;
341 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000342 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000343
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000344 if ((all_fmt & FOLLOW_LINKS) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000345#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000346 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000347 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000348 }
Eric Andersen9e480452003-07-03 10:07:04 +0000349#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000350 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000351 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000352 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000353 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000354 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000355 } else {
356#if ENABLE_SELINUX
357 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000358 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000359 }
Eric Andersen9e480452003-07-03 10:07:04 +0000360#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000361 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000362 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000363 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000364 return 0;
365 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000366 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000367
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200368 cur = xmalloc(sizeof(*cur));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000369 cur->fullname = fullname;
370 cur->name = name;
371 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000372 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000373 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000374}
375
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000376/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
377 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
378 * 3/7:multiplexed char/block device)
379 * and we use 0 for unknown and 15 for executables (see below) */
380#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
381#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
382#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
383/* 036 black foreground 050 black background
384 037 red foreground 051 red background
385 040 green foreground 052 green background
386 041 brown foreground 053 brown background
387 042 blue foreground 054 blue background
388 043 magenta (purple) foreground 055 magenta background
389 044 cyan (light blue) foreground 056 cyan background
390 045 gray foreground 057 white background
391*/
392#define COLOR(mode) ( \
393 /*un fi chr dir blk file link sock exe */ \
394 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
395 [TYPEINDEX(mode)])
396/* Select normal (0) [actually "reset all"] or bold (1)
397 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
398 * let's use 7 for "impossible" types, just for fun)
399 * Note: coreutils 6.9 uses inverted red for setuid binaries.
400 */
401#define ATTR(mode) ( \
402 /*un fi chr dir blk file link sock exe */ \
403 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
404 [TYPEINDEX(mode)])
405
Denis Vlasenko5c759602006-10-28 12:37:16 +0000406#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000407/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000408static char fgcolor(mode_t mode)
409{
Rob Landley9947a242006-06-15 22:11:10 +0000410 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000411 return COLOR(0xF000); /* File is executable ... */
412 return COLOR(mode);
413}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000414static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000415{
Rob Landley9947a242006-06-15 22:11:10 +0000416 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000417 return ATTR(0xF000); /* File is executable ... */
418 return ATTR(mode);
419}
420#endif
421
Denis Vlasenko5c759602006-10-28 12:37:16 +0000422#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000423static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000424{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000425 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000426 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000427 if (S_ISDIR(mode))
428 return '/';
429 if (!(all_fmt & LIST_EXEC))
430 return '\0';
431 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000432 return '*';
433 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000434}
435#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000436
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200437static unsigned count_dirs(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000438{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200439 unsigned dirs, all;
Eric Andersen11c65522000-09-07 17:24:47 +0000440
Denis Vlasenko5c759602006-10-28 12:37:16 +0000441 if (!dn)
442 return 0;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200443
444 dirs = all = 0;
445 for (; *dn; dn++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000446 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200447
448 all++;
449 if (!S_ISDIR((*dn)->dstat.st_mode))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000450 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200451 name = (*dn)->name;
452 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
453 /* or if it's not . or .. */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200454 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000455 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000456 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000457 }
Eric Andersen11c65522000-09-07 17:24:47 +0000458 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200459 return which != SPLIT_FILE ? dirs : all - dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000460}
461
Eric Andersen11c65522000-09-07 17:24:47 +0000462/* get memory to hold an array of pointers */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200463static struct dnode **dnalloc(unsigned num)
Eric Andersen11c65522000-09-07 17:24:47 +0000464{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000465 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000466 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000467
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200468 num++; /* so that we have terminating NULL */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000469 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000470}
471
Denis Vlasenko5c759602006-10-28 12:37:16 +0000472#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200473static void dfree(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000474{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200475 unsigned i;
Eric Andersen11c65522000-09-07 17:24:47 +0000476
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000477 if (dnp == NULL)
478 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000479
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200480 for (i = 0; dnp[i]; i++) {
Rob Landley26314862006-05-02 19:46:52 +0000481 struct dnode *cur = dnp[i];
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200482 if (cur->fname_allocated)
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200483 free((char*)cur->fullname);
484 free(cur);
Eric Andersen11c65522000-09-07 17:24:47 +0000485 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200486 free(dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000487}
Rob Landleyc44bc982006-05-28 01:19:06 +0000488#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000489#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000490#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000491
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200492/* Returns NULL-terminated malloced vector of pointers (or NULL) */
493static struct dnode **splitdnarray(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000494{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200495 unsigned dncnt, d;
Eric Andersen11c65522000-09-07 17:24:47 +0000496 struct dnode **dnp;
497
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200498 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000499 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000500
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200501 /* count how many dirs or files there are */
502 dncnt = count_dirs(dn, which);
Eric Andersen11c65522000-09-07 17:24:47 +0000503
504 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000505 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000506
507 /* copy the entrys into the file or dir array */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200508 for (d = 0; *dn; dn++) {
509 if (S_ISDIR((*dn)->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000510 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200511
Denis Vlasenko5c759602006-10-28 12:37:16 +0000512 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
513 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200514 name = (*dn)->name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000515 if ((which & SPLIT_DIR)
516 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
517 ) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200518 dnp[d++] = *dn;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000519 }
520 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200521 dnp[d++] = *dn;
Eric Andersen11c65522000-09-07 17:24:47 +0000522 }
523 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000524 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000525}
526
Denis Vlasenko5c759602006-10-28 12:37:16 +0000527#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000528static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000529{
Rob Landley425e7582006-05-03 20:22:03 +0000530 struct dnode *d1 = *(struct dnode **)a;
531 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000532 unsigned sort_opts = all_fmt & SORT_MASK;
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100533 off_t dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000534
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000535 dif = 0; /* assume SORT_NAME */
536 // TODO: use pre-initialized function pointer
537 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000538 if (sort_opts == SORT_SIZE) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100539 dif = (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000540 } else if (sort_opts == SORT_ATIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100541 dif = (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000542 } else if (sort_opts == SORT_CTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100543 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000544 } else if (sort_opts == SORT_MTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100545 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000546 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000547 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000548 /* } else if (sort_opts == SORT_VERSION) { */
549 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000550 }
Eric Andersen11c65522000-09-07 17:24:47 +0000551 if (dif == 0) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100552 /* sort by name, or tie_breaker for other sorts */
553 if (ENABLE_LOCALE_SUPPORT)
554 dif = strcoll(d1->name, d2->name);
555 else
556 dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000557 }
558
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100559 /* Make dif fit into an int */
560 if (sizeof(dif) > sizeof(int)) {
Denys Vlasenko6b01b712010-01-24 22:52:21 +0100561 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
562 /* shift leaving only "int" worth of bits */
563 if (dif != 0) {
564 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100565 }
Eric Andersen11c65522000-09-07 17:24:47 +0000566 }
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100567
568 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000569}
570
Rob Landley425e7582006-05-03 20:22:03 +0000571static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000572{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000573 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000574}
Rob Landleyea224be2006-06-18 20:20:07 +0000575#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000576#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000577#endif
578
Rob Landleyea224be2006-06-18 20:20:07 +0000579
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100580static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000581{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100582 unsigned len;
583 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000584
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100585 // TODO: quote tab as \t, etc, if -Q
586 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000587
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100588 if (!(option_mask32 & OPT_Q)) {
589 return uni_stat.unicode_width;
590 }
591
592 len = 2 + uni_stat.unicode_width;
593 while (*name) {
594 if (*name == '"' || *name == '\\') {
595 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000596 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100597 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000598 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100599 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000600}
601
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000602
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100603/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100604 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100605 * STYLE_SINGLE and STYLE_LONG don't care.
606 * coreutils 7.2 also supports:
607 * ls -b (--escape) = octal escapes (although it doesn't look like working)
608 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200609 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100610static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200611{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100612 unsigned len;
613 uni_stat_t uni_stat;
614
615 // TODO: quote tab as \t, etc, if -Q
616 name = printable_string(&uni_stat, name);
617
618 if (!(option_mask32 & OPT_Q)) {
619 fputs(name, stdout);
620 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200621 }
622
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100623 len = 2 + uni_stat.unicode_width;
624 putchar('"');
625 while (*name) {
626 if (*name == '"' || *name == '\\') {
627 putchar('\\');
628 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000629 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700630 putchar(*name);
631 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000632 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100633 putchar('"');
634 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000635}
636
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100637/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100638 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100639 * STYLE_SINGLE and STYLE_LONG don't care.
640 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200641static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000642{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200643 unsigned column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000644 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000645#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000646 struct stat info;
647 char append;
648#endif
649
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200650 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000651 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000652 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200653 */
Eric Andersen11c65522000-09-07 17:24:47 +0000654
Denis Vlasenko5c759602006-10-28 12:37:16 +0000655#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000656 append = append_char(dn->dstat.st_mode);
657#endif
658
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000659 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100660 * does not appear *inside* the "ls -l" line */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000661 if (all_fmt & LIST_SYMLINK)
662 if (S_ISLNK(dn->dstat.st_mode))
663 lpath = xmalloc_readlink_or_warn(dn->fullname);
664
665 if (all_fmt & LIST_INO)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100666 column += printf("%7llu ", (long long) dn->dstat.st_ino);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000667 if (all_fmt & LIST_BLOCKS)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100668 column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000669 if (all_fmt & LIST_MODEBITS)
670 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
671 if (all_fmt & LIST_NLINKS)
672 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000673#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000674 if (all_fmt & LIST_ID_NAME) {
675 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100676 column += printf("%-8.8s ",
Denys Vlasenko4909fec2010-11-06 00:46:57 +0100677 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000678 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100679 column += printf("%-8.8s %-8.8s ",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000680 get_cached_username(dn->dstat.st_uid),
681 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000682 }
683 }
Eric Andersen11c65522000-09-07 17:24:47 +0000684#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000685 if (all_fmt & LIST_ID_NUMERIC) {
686 if (option_mask32 & OPT_g)
Denys Vlasenko4909fec2010-11-06 00:46:57 +0100687 column += printf("%-8u ", (int) dn->dstat.st_gid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000688 else
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100689 column += printf("%-8u %-8u ",
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000690 (int) dn->dstat.st_uid,
691 (int) dn->dstat.st_gid);
692 }
693 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
694 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
695 column += printf("%4u, %3u ",
696 (int) major(dn->dstat.st_rdev),
697 (int) minor(dn->dstat.st_rdev));
698 } else {
699 if (all_fmt & LS_DISP_HR) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100700 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200701 /* print st_size, show one fractional, use suffixes */
702 make_human_readable_str(dn->dstat.st_size, 1, 0)
703 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000704 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000705 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000706 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000707 }
708 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000709#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100710 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
711 char *filetime;
712 time_t ttime = dn->dstat.st_mtime;
713 if (all_fmt & TIME_ACCESS)
714 ttime = dn->dstat.st_atime;
715 if (all_fmt & TIME_CHANGE)
716 ttime = dn->dstat.st_ctime;
717 filetime = ctime(&ttime);
718 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
719 if (all_fmt & LIST_FULLTIME)
720 column += printf("%.24s ", filetime);
721 else { /* LIST_DATE_TIME */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000722 /* current_time_t ~== time(NULL) */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100723 time_t age = current_time_t - ttime;
724 printf("%.6s ", filetime + 4); /* "Jun 30" */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000725 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
726 /* hh:mm if less than 6 months old */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100727 printf("%.5s ", filetime + 11);
728 } else { /* year. buggy if year > 9999 ;) */
729 printf(" %.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000730 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000731 column += 13;
732 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100733 }
Eric Andersen11c65522000-09-07 17:24:47 +0000734#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000735#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000736 if (all_fmt & LIST_CONTEXT) {
737 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
738 freecon(dn->sid);
739 }
Eric Andersen9e480452003-07-03 10:07:04 +0000740#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000741 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000742#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000743 if (show_color) {
744 info.st_mode = 0; /* for fgcolor() */
745 lstat(dn->fullname, &info);
746 printf("\033[%u;%um", bold(info.st_mode),
747 fgcolor(info.st_mode));
748 }
749#endif
750 column += print_name(dn->name);
751 if (show_color) {
752 printf("\033[0m");
753 }
754 }
755 if (all_fmt & LIST_SYMLINK) {
756 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
757 printf(" -> ");
758#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
759#if ENABLE_FEATURE_LS_COLOR
760 info.st_mode = 0; /* for fgcolor() */
761#endif
762 if (stat(dn->fullname, &info) == 0) {
763 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000764 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000765#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000766#if ENABLE_FEATURE_LS_COLOR
767 if (show_color) {
768 printf("\033[%u;%um", bold(info.st_mode),
769 fgcolor(info.st_mode));
770 }
771#endif
772 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000773 if (show_color) {
774 printf("\033[0m");
775 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000776 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000777 }
778 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000779#if ENABLE_FEATURE_LS_FILETYPES
780 if (all_fmt & LIST_FILETYPE) {
781 if (append) {
782 putchar(append);
783 column++;
784 }
785 }
786#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000787
Glenn L McGrath4d001292003-01-06 01:11:50 +0000788 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000789}
790
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100791static void showfiles(struct dnode **dn, unsigned nfiles)
792{
793 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100794 unsigned column;
795 unsigned nexttab;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100796 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100797
798 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
799 ncols = 1;
800 } else {
801 /* find the longest file name, use that as the column width */
802 for (i = 0; dn[i]; i++) {
803 int len = calc_name_len(dn[i]->name);
804 if (column_width < len)
805 column_width = len;
806 }
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100807 column_width += 1 +
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100808 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
809 ((all_fmt & LIST_INO) ? 8 : 0) +
810 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
811 ncols = (int) (terminal_width / column_width);
812 }
813
814 if (ncols > 1) {
815 nrows = nfiles / ncols;
816 if (nrows * ncols < nfiles)
817 nrows++; /* round up fractionals */
818 } else {
819 nrows = nfiles;
820 ncols = 1;
821 }
822
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100823 column = 0;
824 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100825 for (row = 0; row < nrows; row++) {
826 for (nc = 0; nc < ncols; nc++) {
827 /* reach into the array based on the column and row */
828 if (all_fmt & DISP_ROWS)
829 i = (row * ncols) + nc; /* display across row */
830 else
831 i = (nc * nrows) + row; /* display by column */
832 if (i < nfiles) {
833 if (column > 0) {
834 nexttab -= column;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100835 printf("%*s ", nexttab, "");
836 column += nexttab + 1;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100837 }
838 nexttab = column + column_width;
839 column += list_single(dn[i]);
840 }
841 }
842 putchar('\n');
843 column = 0;
844 }
845}
846
847
848#if ENABLE_DESKTOP
849/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
850 * If any of the -l, -n, -s options is specified, each list
851 * of files within the directory shall be preceded by a
852 * status line indicating the number of file system blocks
853 * occupied by files in the directory in 512-byte units if
854 * the -k option is not specified, or 1024-byte units if the
855 * -k option is specified, rounded up to the next integral
856 * number of units.
857 */
858/* by Jorgen Overgaard (jorgen AT antistaten.se) */
859static off_t calculate_blocks(struct dnode **dn)
860{
861 uoff_t blocks = 1;
862 if (dn) {
863 while (*dn) {
864 /* st_blocks is in 512 byte blocks */
865 blocks += (*dn)->dstat.st_blocks;
866 dn++;
867 }
868 }
869
870 /* Even though standard says use 512 byte blocks, coreutils use 1k */
871 /* Actually, we round up by calculating (blocks + 1) / 2,
872 * "+ 1" was done when we initialized blocks to 1 */
873 return blocks >> 1;
874}
875#endif
876
877
878static struct dnode **list_dir(const char *, unsigned *);
879
880static void showdirs(struct dnode **dn, int first)
881{
882 unsigned nfiles;
883 unsigned dndirs;
884 struct dnode **subdnp;
885 struct dnode **dnd;
886
887 /* Never happens:
888 if (dn == NULL || ndirs < 1) {
889 return;
890 }
891 */
892
893 for (; *dn; dn++) {
894 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
895 if (!first)
896 bb_putchar('\n');
897 first = 0;
898 printf("%s:\n", (*dn)->fullname);
899 }
900 subdnp = list_dir((*dn)->fullname, &nfiles);
901#if ENABLE_DESKTOP
902 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
903 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
904#endif
905 if (nfiles > 0) {
906 /* list all files at this level */
907 dnsort(subdnp, nfiles);
908 showfiles(subdnp, nfiles);
909 if (ENABLE_FEATURE_LS_RECURSIVE
910 && (all_fmt & DISP_RECURSIVE)
911 ) {
912 /* recursive - list the sub-dirs */
913 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
914 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
915 if (dndirs > 0) {
916 dnsort(dnd, dndirs);
917 showdirs(dnd, 0);
918 /* free the array of dnode pointers to the dirs */
919 free(dnd);
920 }
921 }
922 /* free the dnodes and the fullname mem */
923 dfree(subdnp);
924 }
925 }
926}
927
928
929/* Returns NULL-terminated malloced vector of pointers (or NULL) */
930static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
931{
932 struct dnode *dn, *cur, **dnp;
933 struct dirent *entry;
934 DIR *dir;
935 unsigned i, nfiles;
936
937 /* Never happens:
938 if (path == NULL)
939 return NULL;
940 */
941
942 *nfiles_p = 0;
943 dir = warn_opendir(path);
944 if (dir == NULL) {
945 exit_code = EXIT_FAILURE;
946 return NULL; /* could not open the dir */
947 }
948 dn = NULL;
949 nfiles = 0;
950 while ((entry = readdir(dir)) != NULL) {
951 char *fullname;
952
953 /* are we going to list the file- it may be . or .. or a hidden file */
954 if (entry->d_name[0] == '.') {
955 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
956 && !(all_fmt & DISP_DOT)
957 ) {
958 continue;
959 }
960 if (!(all_fmt & DISP_HIDDEN))
961 continue;
962 }
963 fullname = concat_path_file(path, entry->d_name);
964 cur = my_stat(fullname, bb_basename(fullname), 0);
965 if (!cur) {
966 free(fullname);
967 continue;
968 }
969 cur->fname_allocated = 1;
970 cur->next = dn;
971 dn = cur;
972 nfiles++;
973 }
974 closedir(dir);
975
976 if (dn == NULL)
977 return NULL;
978
979 /* now that we know how many files there are
980 * allocate memory for an array to hold dnode pointers
981 */
982 *nfiles_p = nfiles;
983 dnp = dnalloc(nfiles);
984 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
985 dnp[i] = dn; /* save pointer to node in array */
986 dn = dn->next;
987 if (!dn)
988 break;
989 }
990
991 return dnp;
992}
993
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000994
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000995int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000996{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000997 struct dnode **dnd;
998 struct dnode **dnf;
999 struct dnode **dnp;
1000 struct dnode *dn;
1001 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001002 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001003 unsigned nfiles;
1004 unsigned dnfiles;
1005 unsigned dndirs;
1006 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001007#if ENABLE_FEATURE_LS_COLOR
1008 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1009 /* coreutils 6.10:
1010 * # ls --color=BOGUS
1011 * ls: invalid argument 'BOGUS' for '--color'
1012 * Valid arguments are:
1013 * 'always', 'yes', 'force'
1014 * 'never', 'no', 'none'
1015 * 'auto', 'tty', 'if-tty'
1016 * (and substrings: "--color=alwa" work too)
1017 */
1018 static const char ls_longopts[] ALIGN1 =
1019 "color\0" Optional_argument "\xff"; /* no short equivalent */
1020 static const char color_str[] ALIGN1 =
1021 "always\0""yes\0""force\0"
1022 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001023 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001024 const char *color_opt = color_str; /* "always" */
1025#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001026
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001027 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001028
Denys Vlasenko28055022010-01-04 20:49:58 +01001029 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001030
Rob Landley2b8a05a2006-06-20 17:43:01 +00001031 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001032 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +00001033
Denis Vlasenko5c759602006-10-28 12:37:16 +00001034#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001035 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +00001036 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001037 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +00001038 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001039#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001040
Eric Andersen11c65522000-09-07 17:24:47 +00001041 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001042 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001043 opt_complementary =
1044 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1045 * in some pairs of opts, only last one takes effect:
1046 */
1047 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES("t-S:S-t")) /* time/size */
1048 // ":H-L:L-H:" - we don't have -H
1049 // ":m-l:l-m:" - we don't have -m
1050 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1051 ":C-1:1-C" /* bycols/oneline */
1052 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1053 ":c-u:u-c" /* mtime/atime */
1054 /* -T NUM, -w NUM: */
1055 IF_FEATURE_AUTOWIDTH(":T+:w+");
1056 opt = getopt32(argv, ls_options
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +01001057 IF_FEATURE_AUTOWIDTH(, NULL, &terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001058 IF_FEATURE_LS_COLOR(, &color_opt)
1059 );
Eric Andersend07cf592004-02-05 13:52:03 +00001060 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001061 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +00001062 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001063
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001064 if (flags & STYLE_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001065 all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001066 if (flags & SORT_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001067 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001068 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001069 all_fmt &= ~TIME_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001070
Denis Vlasenko5c759602006-10-28 12:37:16 +00001071 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +00001072 all_fmt |= STYLE_SINGLE;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001073 all_fmt |= flags;
1074 }
Eric Andersen11c65522000-09-07 17:24:47 +00001075 }
1076
Denis Vlasenko5c759602006-10-28 12:37:16 +00001077#if ENABLE_FEATURE_LS_COLOR
1078 /* find color bit value - last position for short getopt */
1079 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1080 char *p = getenv("LS_COLORS");
1081 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001082 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001083 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001084 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001085 if (opt & OPT_color) {
1086 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +00001087 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001088 else switch (index_in_substrings(color_str, color_opt)) {
1089 case 3:
1090 case 4:
1091 case 5:
1092 if (isatty(STDOUT_FILENO)) {
1093 case 0:
1094 case 1:
1095 case 2:
1096 show_color = 1;
1097 }
1098 }
Paul Fox156dc412005-08-01 19:33:30 +00001099 }
1100#endif
1101
Eric Andersen11c65522000-09-07 17:24:47 +00001102 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001103 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001104 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001105 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1106 if (all_fmt & TIME_CHANGE)
1107 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1108 if (all_fmt & TIME_ACCESS)
1109 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1110 }
Manuel Novoa III cad53642003-03-19 09:13:01 +00001111 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1112 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +00001113 if (ENABLE_FEATURE_LS_USERNAME)
1114 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1115 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001116
Denys Vlasenkof3137462010-12-19 05:05:34 +01001117 /* choose a display format if one was not already specified by an option */
Rob Landleyea224be2006-06-18 20:20:07 +00001118 if (!(all_fmt & STYLE_MASK))
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001119 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001120
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001121 argv += optind;
1122 if (!argv[0])
1123 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001124
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001125 if (argv[1])
1126 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001127
Denis Vlasenko5c759602006-10-28 12:37:16 +00001128 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001129 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001130 nfiles = 0;
1131 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001132 /* NB: follow links on command line unless -l, -s or -F */
1133 cur = my_stat(*argv, *argv,
1134 !((all_fmt & (STYLE_LONG|LIST_BLOCKS)) || (option_mask32 & OPT_F))
1135 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001136 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001137 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001138 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001139 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001140 cur->next = dn;
1141 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001142 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001143 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001144
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001145 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1146 if (nfiles == 0)
1147 return exit_code;
1148
Eric Andersen11c65522000-09-07 17:24:47 +00001149 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001150 * allocate memory for an array to hold dnode pointers
1151 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001152 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001153 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1154 dnp[i] = dn; /* save pointer to node in array */
1155 dn = dn->next;
1156 if (!dn)
1157 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001158 }
1159
Manuel Novoa III cad53642003-03-19 09:13:01 +00001160 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001161 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001162 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001163 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001164 dnd = splitdnarray(dnp, SPLIT_DIR);
1165 dnf = splitdnarray(dnp, SPLIT_FILE);
1166 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001167 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001168 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001169 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001170 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001171 if (ENABLE_FEATURE_CLEAN_UP)
1172 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001173 }
1174 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001175 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001176 showdirs(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001177 if (ENABLE_FEATURE_CLEAN_UP)
1178 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001179 }
1180 }
Rob Landley26314862006-05-02 19:46:52 +00001181 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001182 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001183 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001184}