blob: 46abb620114882d20be1543da36fbc1af454c398 [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 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
Eric Andersen11c65522000-09-07 17:24:47 +00004 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02005 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencc8ed391999-10-05 16:24:54 +00006 */
7
Denis Vlasenko5e4fda02009-03-08 23:46:48 +00008/* [date unknown. Perhaps before year 2000]
Eric Andersencc8ed391999-10-05 16:24:54 +00009 * To achieve a small memory footprint, this version of 'ls' doesn't do any
10 * file sorting, and only has the most essential command line switches
Eric Andersen77d92682001-05-23 20:32:09 +000011 * (i.e., the ones I couldn't live without :-) All features which involve
Eric Andersencc8ed391999-10-05 16:24:54 +000012 * linking in substantial chunks of libc can be disabled.
13 *
14 * Although I don't really want to add new features to this program to
15 * keep it small, I *am* interested to receive bug fixes and ways to make
16 * it more portable.
17 *
18 * KNOWN BUGS:
Denys Vlasenko87c150c2009-10-03 01:14:15 +020019 * 1. hidden files can make column width too large
Erik Andersen9ffdaa62000-02-11 21:55:04 +000020 *
Eric Andersencc8ed391999-10-05 16:24:54 +000021 * NON-OPTIMAL BEHAVIOUR:
22 * 1. autowidth reads directories twice
23 * 2. if you do a short directory listing without filetype characters
24 * appended, there's no need to stat each one
25 * PORTABILITY:
26 * 1. requires lstat (BSD) - how do you do it without?
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000027 *
28 * [2009-03]
29 * ls sorts listing now, and supports almost all options.
Eric Andersencc8ed391999-10-05 16:24:54 +000030 */
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010031
32//usage:#define ls_trivial_usage
33//usage: "[-1AaCxd"
Denys Vlasenko982aa262010-12-19 21:54:39 +010034//usage: IF_FEATURE_LS_FOLLOWLINKS("LH")
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010035//usage: IF_FEATURE_LS_RECURSIVE("R")
36//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins"
37//usage: IF_FEATURE_LS_TIMESTAMPS("e")
38//usage: IF_FEATURE_HUMAN_READABLE("h")
39//usage: IF_FEATURE_LS_SORTFILES("rSXv")
40//usage: IF_FEATURE_LS_TIMESTAMPS("ctu")
41//usage: IF_SELINUX("kKZ") "]"
Denys Vlasenko279a7ac2011-05-12 18:44:51 +020042//usage: IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..."
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010043//usage:#define ls_full_usage "\n\n"
44//usage: "List directory contents\n"
45//usage: "\nOptions:"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020046//usage: "\n -1 One column output"
47//usage: "\n -a Include entries which start with ."
48//usage: "\n -A Like -a, but exclude . and .."
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010049//usage: "\n -C List by columns"
50//usage: "\n -x List by lines"
51//usage: "\n -d List directory entries instead of contents"
52//usage: IF_FEATURE_LS_FOLLOWLINKS(
Denys Vlasenko982aa262010-12-19 21:54:39 +010053//usage: "\n -L Follow symlinks"
Denys Vlasenko4ad95e62011-05-12 18:40:59 +020054//usage: "\n -H Follow symlinks on command line"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010055//usage: )
56//usage: IF_FEATURE_LS_RECURSIVE(
57//usage: "\n -R Recurse"
58//usage: )
59//usage: IF_FEATURE_LS_FILETYPES(
Denys Vlasenko4ad95e62011-05-12 18:40:59 +020060//usage: "\n -p Append / to dir entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010061//usage: "\n -F Append indicator (one of */=@|) to entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010062//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"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020066//usage: "\n -s List allocated blocks"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010067//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"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020075//usage: "\n -S Sort by size"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010076//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"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020081//usage: "\n -t With -l: sort by mtime"
82//usage: "\n -u With -l: sort by atime"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010083//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
Denys Vlasenko982aa262010-12-19 21:54:39 +0100121SPLIT_DIR = 1,
122SPLIT_FILE = 0,
123SPLIT_SUBDIR = 2,
124
125/* Bits in all_fmt: */
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,
James Youngmana4bc10c2010-12-20 01:36:16 +0100137LIST_DATE_TIME = 1 << 8,
138LIST_FULLTIME = 1 << 9,
139LIST_SYMLINK = 1 << 10,
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200140LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */
141LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */
142LIST_MASK = (LIST_CLASSIFY << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +0000143
144/* what files will be displayed */
James Youngmana4bc10c2010-12-20 01:36:16 +0100145DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */
146DISP_HIDDEN = 1 << 14, /* show filenames starting with . */
147DISP_DOT = 1 << 15, /* show . and .. */
148DISP_NOLIST = 1 << 16, /* show directory as itself, not contents */
149DISP_RECURSIVE = 1 << 17, /* show directory and everything below it */
150DISP_ROWS = 1 << 18, /* print across rows */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000151DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +0000152
Denys Vlasenko982aa262010-12-19 21:54:39 +0100153/* what is the overall style of the listing */
James Youngmana4bc10c2010-12-20 01:36:16 +0100154STYLE_COLUMNAR = 1 << 19, /* many records per line */
155STYLE_LONG = 2 << 19, /* one record per line, extended info */
156STYLE_SINGLE = 3 << 19, /* one record per line */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100157STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000158
Eric Andersen11c65522000-09-07 17:24:47 +0000159/* which of the three times will be used */
James Youngmana4bc10c2010-12-20 01:36:16 +0100160TIME_CHANGE = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100161TIME_ACCESS = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
James Youngmana4bc10c2010-12-20 01:36:16 +0100162TIME_MASK = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000163
Denys Vlasenko982aa262010-12-19 21:54:39 +0100164/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
James Youngmana4bc10c2010-12-20 01:36:16 +0100165SORT_REVERSE = 1 << 23,
Eric Andersencc8ed391999-10-05 16:24:54 +0000166
Denys Vlasenko982aa262010-12-19 21:54:39 +0100167SORT_NAME = 0, /* sort by file name */
James Youngmana4bc10c2010-12-20 01:36:16 +0100168SORT_SIZE = 1 << 24, /* sort by file size */
169SORT_ATIME = 2 << 24, /* sort by last access time */
170SORT_CTIME = 3 << 24, /* sort by last change time */
171SORT_MTIME = 4 << 24, /* sort by last modification time */
172SORT_VERSION = 5 << 24, /* sort by version */
173SORT_EXT = 6 << 24, /* sort by file name extension */
174SORT_DIR = 7 << 24, /* sort by file or directory */
175SORT_MASK = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000176
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000177LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
James Youngmana4bc10c2010-12-20 01:36:16 +0100178 LIST_DATE_TIME | LIST_SYMLINK,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000179};
Eric Andersencc8ed391999-10-05 16:24:54 +0000180
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100181/* -Cadil1 Std options, busybox always supports */
182/* -gnsxA Std options, busybox always supports */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100183/* -Q GNU option, busybox always supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100184/* -k SELinux option, busybox always supports (ignores if !SELinux) */
185/* Std has -k which means "show sizes in kbytes" */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100186/* -FLHRctur Std options, busybox optionally supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100187/* -p Std option, busybox optionally supports */
188/* Not fully compatible - we show not only '/' but other chars too */
189/* -SXvhTw GNU options, busybox optionally supports */
190/* -T TABWIDTH is ignored (we don't use tabs on output) */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100191/* -KZ SELinux mandated options, busybox optionally supports */
192/* (coreutils 8.4 has no -K, remove it?) */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100193/* -e I think we made this one up (looks similar to GNU --full-time) */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100194/* We already used up all 32 bits, if we need to add more, candidates for removal: */
195/* -K, -T, -e (add --full-time instead) */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000196static const char ls_options[] ALIGN1 =
Denys Vlasenko982aa262010-12-19 21:54:39 +0100197 "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 */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100201 IF_FEATURE_LS_RECURSIVE("R") /* 1, 24 */
202 IF_SELINUX("KZ") /* 2, 26 */
203 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 28 */
204 IF_FEATURE_HUMAN_READABLE("h") /* 1, 29 */
205 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 31 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100206 /* with --color, we use all 32 bits */;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000207enum {
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 Vlasenko982aa262010-12-19 21:54:39 +0100221
222 OPTBIT_c = 13,
223 OPTBIT_e,
224 OPTBIT_t,
225 OPTBIT_u,
226 OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS,
227 OPTBIT_X, /* 18 */
228 OPTBIT_r,
229 OPTBIT_v,
230 OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
231 OPTBIT_p, /* 22 */
232 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
233 OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
234 OPTBIT_Z, /* 25 */
235 OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX,
236 OPTBIT_H, /* 27 */
Denys Vlasenkocd387f22011-02-27 04:10:00 +0100237 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100238 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100239 OPTBIT_w, /* 30 */
240 OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH,
241
242 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
243 OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS,
244 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
245 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
246 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
247 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
248 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
249 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
250 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
251 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
252 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
253 OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX,
254 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
255 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
256 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
257 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
258 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH,
259 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH,
260 OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000261};
262
Denis Vlasenko248ce912009-03-03 14:09:04 +0000263/* TODO: simple toggles may be stored as OPT_xxx bits instead */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100264static const uint32_t opt_flags[] = {
James Youngmana4bc10c2010-12-20 01:36:16 +0100265 STYLE_COLUMNAR, /* C */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100266 DISP_HIDDEN | DISP_DOT, /* a */
267 DISP_NOLIST, /* d */
268 LIST_INO, /* i */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100269 LIST_LONG | STYLE_LONG, /* l */
James Youngmana4bc10c2010-12-20 01:36:16 +0100270 STYLE_SINGLE, /* 1 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100271 LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100272 LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100273 LIST_BLOCKS, /* s */
James Youngmana4bc10c2010-12-20 01:36:16 +0100274 DISP_ROWS | STYLE_COLUMNAR, /* x */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100275 0, /* Q (quote filename) - handled via OPT_Q */
276 DISP_HIDDEN, /* A */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100277 ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000278#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100279 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100280 LIST_FULLTIME, /* e */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100281 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
282 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000283#endif
284#if ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100285 SORT_SIZE, /* S */
286 SORT_EXT, /* X */
287 SORT_REVERSE, /* r */
288 SORT_VERSION, /* v */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000289#endif
290#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200291 LIST_FILETYPE | LIST_CLASSIFY, /* F */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100292 LIST_FILETYPE, /* p */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000293#endif
Denis Vlasenko248ce912009-03-03 14:09:04 +0000294#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100295 DISP_RECURSIVE, /* R */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000296#endif
Denis Vlasenko248ce912009-03-03 14:09:04 +0000297#if ENABLE_SELINUX
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100298 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */
299 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000300#endif
Denys Vlasenko982aa262010-12-19 21:54:39 +0100301 (1U << 31)
302 /* options after Z are not processed through opt_flags */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000303};
304
305
Eric Andersen11c65522000-09-07 17:24:47 +0000306/*
307 * a directory entry and its stat info are stored here
308 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200309struct dnode {
310 const char *name; /* the dir entry name */
311 const char *fullname; /* the dir entry name */
312 struct dnode *next; /* point at the next node */
313 smallint fname_allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000314 struct stat dstat; /* the file stat info */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000315 IF_SELINUX(security_context_t sid;)
Eric Andersen11c65522000-09-07 17:24:47 +0000316};
Eric Andersen11c65522000-09-07 17:24:47 +0000317
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000318struct globals {
319#if ENABLE_FEATURE_LS_COLOR
320 smallint show_color;
321#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000322 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000323 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000324#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000325 unsigned terminal_width; // = TERMINAL_WIDTH;
326#endif
327#if ENABLE_FEATURE_LS_TIMESTAMPS
328 /* Do time() just once. Saves one syscall per file for "ls -l" */
329 time_t current_time_t;
330#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100331} FIX_ALIASING;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000332#define G (*(struct globals*)&bb_common_bufsiz1)
333#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200334# define show_color (G.show_color )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000335#else
336enum { show_color = 0 };
337#endif
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200338#define exit_code (G.exit_code )
339#define all_fmt (G.all_fmt )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000340#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200341# define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000342#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000343enum {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000344 terminal_width = TERMINAL_WIDTH,
345};
Eric Andersen11c65522000-09-07 17:24:47 +0000346#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000347#define current_time_t (G.current_time_t)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000348#define INIT_G() do { \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200349 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000350 memset(&G, 0, sizeof(G)); \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000351 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
352 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000353} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000354
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000355
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000356static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000357{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000358 struct stat dstat;
359 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000360 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000361
Denys Vlasenko982aa262010-12-19 21:54:39 +0100362 if ((option_mask32 & OPT_L) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000363#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000364 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000365 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000366 }
Eric Andersen9e480452003-07-03 10:07:04 +0000367#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000368 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000369 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000370 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000371 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000372 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000373 } else {
374#if ENABLE_SELINUX
375 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000376 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000377 }
Eric Andersen9e480452003-07-03 10:07:04 +0000378#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000379 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000380 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000381 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000382 return 0;
383 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000384 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000385
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200386 cur = xmalloc(sizeof(*cur));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000387 cur->fullname = fullname;
388 cur->name = name;
389 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000390 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000391 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000392}
393
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000394/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
395 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
396 * 3/7:multiplexed char/block device)
397 * and we use 0 for unknown and 15 for executables (see below) */
398#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200399/* un fi chr - dir - blk - file - link - sock - - exe */
400#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000401/* 036 black foreground 050 black background
402 037 red foreground 051 red background
403 040 green foreground 052 green background
404 041 brown foreground 053 brown background
405 042 blue foreground 054 blue background
406 043 magenta (purple) foreground 055 magenta background
407 044 cyan (light blue) foreground 056 cyan background
408 045 gray foreground 057 white background
409*/
410#define COLOR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200411 /*un fi chr - dir - blk - file - link - sock - - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000412 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
413 [TYPEINDEX(mode)])
414/* Select normal (0) [actually "reset all"] or bold (1)
415 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
416 * let's use 7 for "impossible" types, just for fun)
417 * Note: coreutils 6.9 uses inverted red for setuid binaries.
418 */
419#define ATTR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200420 /*un fi chr - dir - blk - file- link- sock- - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000421 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
422 [TYPEINDEX(mode)])
423
Denis Vlasenko5c759602006-10-28 12:37:16 +0000424#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000425/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000426static char fgcolor(mode_t mode)
427{
Rob Landley9947a242006-06-15 22:11:10 +0000428 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000429 return COLOR(0xF000); /* File is executable ... */
430 return COLOR(mode);
431}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000432static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000433{
Rob Landley9947a242006-06-15 22:11:10 +0000434 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000435 return ATTR(0xF000); /* File is executable ... */
436 return ATTR(mode);
437}
438#endif
439
Denis Vlasenko5c759602006-10-28 12:37:16 +0000440#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000441static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000442{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000443 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000444 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000445 if (S_ISDIR(mode))
446 return '/';
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200447 if (!(all_fmt & LIST_CLASSIFY))
Rob Landley9947a242006-06-15 22:11:10 +0000448 return '\0';
449 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000450 return '*';
451 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000452}
453#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000454
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200455static unsigned count_dirs(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000456{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200457 unsigned dirs, all;
Eric Andersen11c65522000-09-07 17:24:47 +0000458
Denis Vlasenko5c759602006-10-28 12:37:16 +0000459 if (!dn)
460 return 0;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200461
462 dirs = all = 0;
463 for (; *dn; dn++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000464 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200465
466 all++;
467 if (!S_ISDIR((*dn)->dstat.st_mode))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000468 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200469 name = (*dn)->name;
470 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
471 /* or if it's not . or .. */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200472 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000473 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000474 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000475 }
Eric Andersen11c65522000-09-07 17:24:47 +0000476 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200477 return which != SPLIT_FILE ? dirs : all - dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000478}
479
Eric Andersen11c65522000-09-07 17:24:47 +0000480/* get memory to hold an array of pointers */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200481static struct dnode **dnalloc(unsigned num)
Eric Andersen11c65522000-09-07 17:24:47 +0000482{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000483 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000484 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000485
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200486 num++; /* so that we have terminating NULL */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000487 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000488}
489
Denis Vlasenko5c759602006-10-28 12:37:16 +0000490#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200491static void dfree(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000492{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200493 unsigned i;
Eric Andersen11c65522000-09-07 17:24:47 +0000494
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000495 if (dnp == NULL)
496 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000497
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200498 for (i = 0; dnp[i]; i++) {
Rob Landley26314862006-05-02 19:46:52 +0000499 struct dnode *cur = dnp[i];
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200500 if (cur->fname_allocated)
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200501 free((char*)cur->fullname);
502 free(cur);
Eric Andersen11c65522000-09-07 17:24:47 +0000503 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200504 free(dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000505}
Rob Landleyc44bc982006-05-28 01:19:06 +0000506#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000507#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000508#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000509
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200510/* Returns NULL-terminated malloced vector of pointers (or NULL) */
511static struct dnode **splitdnarray(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000512{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200513 unsigned dncnt, d;
Eric Andersen11c65522000-09-07 17:24:47 +0000514 struct dnode **dnp;
515
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200516 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000517 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000518
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200519 /* count how many dirs or files there are */
520 dncnt = count_dirs(dn, which);
Eric Andersen11c65522000-09-07 17:24:47 +0000521
522 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000523 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000524
525 /* copy the entrys into the file or dir array */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200526 for (d = 0; *dn; dn++) {
527 if (S_ISDIR((*dn)->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000528 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200529
Denis Vlasenko5c759602006-10-28 12:37:16 +0000530 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
531 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200532 name = (*dn)->name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000533 if ((which & SPLIT_DIR)
534 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
535 ) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200536 dnp[d++] = *dn;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000537 }
538 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200539 dnp[d++] = *dn;
Eric Andersen11c65522000-09-07 17:24:47 +0000540 }
541 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000542 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000543}
544
Denis Vlasenko5c759602006-10-28 12:37:16 +0000545#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000546static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000547{
Rob Landley425e7582006-05-03 20:22:03 +0000548 struct dnode *d1 = *(struct dnode **)a;
549 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000550 unsigned sort_opts = all_fmt & SORT_MASK;
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100551 off_t dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000552
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000553 dif = 0; /* assume SORT_NAME */
554 // TODO: use pre-initialized function pointer
555 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000556 if (sort_opts == SORT_SIZE) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100557 dif = (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000558 } else if (sort_opts == SORT_ATIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100559 dif = (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000560 } else if (sort_opts == SORT_CTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100561 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000562 } else if (sort_opts == SORT_MTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100563 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000564 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000565 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000566 /* } else if (sort_opts == SORT_VERSION) { */
567 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000568 }
Eric Andersen11c65522000-09-07 17:24:47 +0000569 if (dif == 0) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100570 /* sort by name, or tie_breaker for other sorts */
571 if (ENABLE_LOCALE_SUPPORT)
572 dif = strcoll(d1->name, d2->name);
573 else
574 dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000575 }
576
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100577 /* Make dif fit into an int */
578 if (sizeof(dif) > sizeof(int)) {
Denys Vlasenko6b01b712010-01-24 22:52:21 +0100579 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
580 /* shift leaving only "int" worth of bits */
581 if (dif != 0) {
582 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100583 }
Eric Andersen11c65522000-09-07 17:24:47 +0000584 }
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100585
586 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000587}
588
Rob Landley425e7582006-05-03 20:22:03 +0000589static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000590{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000591 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000592}
Rob Landleyea224be2006-06-18 20:20:07 +0000593#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000594#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000595#endif
596
Rob Landleyea224be2006-06-18 20:20:07 +0000597
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100598static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000599{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100600 unsigned len;
601 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000602
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100603 // TODO: quote tab as \t, etc, if -Q
604 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000605
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100606 if (!(option_mask32 & OPT_Q)) {
607 return uni_stat.unicode_width;
608 }
609
610 len = 2 + uni_stat.unicode_width;
611 while (*name) {
612 if (*name == '"' || *name == '\\') {
613 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000614 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100615 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000616 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100617 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000618}
619
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000620
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100621/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100622 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100623 * STYLE_SINGLE and STYLE_LONG don't care.
624 * coreutils 7.2 also supports:
625 * ls -b (--escape) = octal escapes (although it doesn't look like working)
626 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200627 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100628static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200629{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100630 unsigned len;
631 uni_stat_t uni_stat;
632
633 // TODO: quote tab as \t, etc, if -Q
634 name = printable_string(&uni_stat, name);
635
636 if (!(option_mask32 & OPT_Q)) {
637 fputs(name, stdout);
638 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200639 }
640
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100641 len = 2 + uni_stat.unicode_width;
642 putchar('"');
643 while (*name) {
644 if (*name == '"' || *name == '\\') {
645 putchar('\\');
646 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000647 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700648 putchar(*name);
649 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000650 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100651 putchar('"');
652 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000653}
654
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100655/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100656 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100657 * STYLE_SINGLE and STYLE_LONG don't care.
658 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200659static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000660{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200661 unsigned column = 0;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200662 char *lpath;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000663#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000664 struct stat info;
665 char append;
666#endif
667
Denis Vlasenko5c759602006-10-28 12:37:16 +0000668#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000669 append = append_char(dn->dstat.st_mode);
670#endif
671
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000672 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100673 * does not appear *inside* the "ls -l" line */
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200674 lpath = NULL;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000675 if (all_fmt & LIST_SYMLINK)
676 if (S_ISLNK(dn->dstat.st_mode))
677 lpath = xmalloc_readlink_or_warn(dn->fullname);
678
679 if (all_fmt & LIST_INO)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100680 column += printf("%7llu ", (long long) dn->dstat.st_ino);
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100681//TODO: -h should affect -s too:
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000682 if (all_fmt & LIST_BLOCKS)
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100683 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000684 if (all_fmt & LIST_MODEBITS)
685 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
686 if (all_fmt & LIST_NLINKS)
687 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100688 if (all_fmt & LIST_ID_NUMERIC) {
689 if (option_mask32 & OPT_g)
690 column += printf("%-8u ", (int) dn->dstat.st_gid);
691 else
692 column += printf("%-8u %-8u ",
693 (int) dn->dstat.st_uid,
694 (int) dn->dstat.st_gid);
695 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000696#if ENABLE_FEATURE_LS_USERNAME
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100697 else if (all_fmt & LIST_ID_NAME) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000698 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100699 column += printf("%-8.8s ",
Denys Vlasenko4909fec2010-11-06 00:46:57 +0100700 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000701 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100702 column += printf("%-8.8s %-8.8s ",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000703 get_cached_username(dn->dstat.st_uid),
704 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000705 }
706 }
Eric Andersen11c65522000-09-07 17:24:47 +0000707#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100708 if (all_fmt & LIST_SIZE) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000709 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
710 column += printf("%4u, %3u ",
711 (int) major(dn->dstat.st_rdev),
712 (int) minor(dn->dstat.st_rdev));
713 } else {
Denys Vlasenko982aa262010-12-19 21:54:39 +0100714 if (option_mask32 & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100715 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200716 /* print st_size, show one fractional, use suffixes */
717 make_human_readable_str(dn->dstat.st_size, 1, 0)
718 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000719 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000720 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000721 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000722 }
723 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000724#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100725 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
726 char *filetime;
727 time_t ttime = dn->dstat.st_mtime;
728 if (all_fmt & TIME_ACCESS)
729 ttime = dn->dstat.st_atime;
730 if (all_fmt & TIME_CHANGE)
731 ttime = dn->dstat.st_ctime;
732 filetime = ctime(&ttime);
733 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100734 if (all_fmt & LIST_FULLTIME) { /* -e */
735 /* Note: coreutils 8.4 ls --full-time prints:
736 * 2009-07-13 17:49:27.000000000 +0200
737 */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100738 column += printf("%.24s ", filetime);
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100739 } else { /* LIST_DATE_TIME */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000740 /* current_time_t ~== time(NULL) */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100741 time_t age = current_time_t - ttime;
742 printf("%.6s ", filetime + 4); /* "Jun 30" */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000743 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
744 /* hh:mm if less than 6 months old */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100745 printf("%.5s ", filetime + 11);
746 } else { /* year. buggy if year > 9999 ;) */
747 printf(" %.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000748 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000749 column += 13;
750 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100751 }
Eric Andersen11c65522000-09-07 17:24:47 +0000752#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000753#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000754 if (all_fmt & LIST_CONTEXT) {
755 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
756 freecon(dn->sid);
757 }
Eric Andersen9e480452003-07-03 10:07:04 +0000758#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100759
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000760#if ENABLE_FEATURE_LS_COLOR
James Youngmana4bc10c2010-12-20 01:36:16 +0100761 if (show_color) {
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200762 info.st_mode = 0;
James Youngmana4bc10c2010-12-20 01:36:16 +0100763 lstat(dn->fullname, &info);
764 printf("\033[%u;%um", bold(info.st_mode),
765 fgcolor(info.st_mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000766 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100767#endif
768 column += print_name(dn->name);
769 if (show_color) {
770 printf("\033[0m");
771 }
772
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200773 if (lpath) {
774 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000775#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200776 info.st_mode = 0;
777 stat(dn->fullname, &info);
778# if ENABLE_FEATURE_LS_FILETYPES
779 append = append_char(info.st_mode);
780# endif
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000781#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000782#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200783 if (show_color) {
784 printf("\033[%u;%um", bold(info.st_mode),
785 fgcolor(info.st_mode));
Eric Andersen11c65522000-09-07 17:24:47 +0000786 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200787#endif
788 column += print_name(lpath) + 4;
789 if (show_color) {
790 printf("\033[0m");
791 }
792 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000793 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000794#if ENABLE_FEATURE_LS_FILETYPES
795 if (all_fmt & LIST_FILETYPE) {
796 if (append) {
797 putchar(append);
798 column++;
799 }
800 }
801#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000802
Glenn L McGrath4d001292003-01-06 01:11:50 +0000803 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000804}
805
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100806static void showfiles(struct dnode **dn, unsigned nfiles)
807{
808 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100809 unsigned column;
810 unsigned nexttab;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100811 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100812
813 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
814 ncols = 1;
815 } else {
816 /* find the longest file name, use that as the column width */
817 for (i = 0; dn[i]; i++) {
818 int len = calc_name_len(dn[i]->name);
819 if (column_width < len)
820 column_width = len;
821 }
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100822 column_width += 1 +
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100823 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
824 ((all_fmt & LIST_INO) ? 8 : 0) +
825 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
826 ncols = (int) (terminal_width / column_width);
827 }
828
829 if (ncols > 1) {
830 nrows = nfiles / ncols;
831 if (nrows * ncols < nfiles)
832 nrows++; /* round up fractionals */
833 } else {
834 nrows = nfiles;
835 ncols = 1;
836 }
837
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100838 column = 0;
839 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100840 for (row = 0; row < nrows; row++) {
841 for (nc = 0; nc < ncols; nc++) {
842 /* reach into the array based on the column and row */
843 if (all_fmt & DISP_ROWS)
844 i = (row * ncols) + nc; /* display across row */
845 else
846 i = (nc * nrows) + row; /* display by column */
847 if (i < nfiles) {
848 if (column > 0) {
849 nexttab -= column;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100850 printf("%*s ", nexttab, "");
851 column += nexttab + 1;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100852 }
853 nexttab = column + column_width;
854 column += list_single(dn[i]);
855 }
856 }
857 putchar('\n');
858 column = 0;
859 }
860}
861
862
863#if ENABLE_DESKTOP
864/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
865 * If any of the -l, -n, -s options is specified, each list
866 * of files within the directory shall be preceded by a
867 * status line indicating the number of file system blocks
868 * occupied by files in the directory in 512-byte units if
869 * the -k option is not specified, or 1024-byte units if the
870 * -k option is specified, rounded up to the next integral
871 * number of units.
872 */
873/* by Jorgen Overgaard (jorgen AT antistaten.se) */
874static off_t calculate_blocks(struct dnode **dn)
875{
876 uoff_t blocks = 1;
877 if (dn) {
878 while (*dn) {
879 /* st_blocks is in 512 byte blocks */
880 blocks += (*dn)->dstat.st_blocks;
881 dn++;
882 }
883 }
884
885 /* Even though standard says use 512 byte blocks, coreutils use 1k */
886 /* Actually, we round up by calculating (blocks + 1) / 2,
887 * "+ 1" was done when we initialized blocks to 1 */
888 return blocks >> 1;
889}
890#endif
891
892
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100893/* Returns NULL-terminated malloced vector of pointers (or NULL) */
894static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
895{
896 struct dnode *dn, *cur, **dnp;
897 struct dirent *entry;
898 DIR *dir;
899 unsigned i, nfiles;
900
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100901 *nfiles_p = 0;
902 dir = warn_opendir(path);
903 if (dir == NULL) {
904 exit_code = EXIT_FAILURE;
905 return NULL; /* could not open the dir */
906 }
907 dn = NULL;
908 nfiles = 0;
909 while ((entry = readdir(dir)) != NULL) {
910 char *fullname;
911
912 /* are we going to list the file- it may be . or .. or a hidden file */
913 if (entry->d_name[0] == '.') {
914 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
915 && !(all_fmt & DISP_DOT)
916 ) {
917 continue;
918 }
919 if (!(all_fmt & DISP_HIDDEN))
920 continue;
921 }
922 fullname = concat_path_file(path, entry->d_name);
923 cur = my_stat(fullname, bb_basename(fullname), 0);
924 if (!cur) {
925 free(fullname);
926 continue;
927 }
928 cur->fname_allocated = 1;
929 cur->next = dn;
930 dn = cur;
931 nfiles++;
932 }
933 closedir(dir);
934
935 if (dn == NULL)
936 return NULL;
937
938 /* now that we know how many files there are
939 * allocate memory for an array to hold dnode pointers
940 */
941 *nfiles_p = nfiles;
942 dnp = dnalloc(nfiles);
943 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
944 dnp[i] = dn; /* save pointer to node in array */
945 dn = dn->next;
946 if (!dn)
947 break;
948 }
949
950 return dnp;
951}
952
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000953
Denys Vlasenko66ca2412011-05-13 17:27:36 +0200954static void showdirs(struct dnode **dn, int first)
955{
956 unsigned nfiles;
957 struct dnode **subdnp;
958
959 for (; *dn; dn++) {
960 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
961 if (!first)
962 bb_putchar('\n');
963 first = 0;
964 printf("%s:\n", (*dn)->fullname);
965 }
966 subdnp = list_dir((*dn)->fullname, &nfiles);
967#if ENABLE_DESKTOP
968 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
969 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
970#endif
971 if (nfiles > 0) {
972 /* list all files at this level */
973 dnsort(subdnp, nfiles);
974 showfiles(subdnp, nfiles);
975 if (ENABLE_FEATURE_LS_RECURSIVE
976 && (all_fmt & DISP_RECURSIVE)
977 ) {
978 struct dnode **dnd;
979 unsigned dndirs;
980 /* recursive - list the sub-dirs */
981 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
982 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
983 if (dndirs > 0) {
984 dnsort(dnd, dndirs);
985 showdirs(dnd, 0);
986 /* free the array of dnode pointers to the dirs */
987 free(dnd);
988 }
989 }
990 /* free the dnodes and the fullname mem */
991 dfree(subdnp);
992 }
993 }
994}
995
996
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000997int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000998{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000999 struct dnode **dnd;
1000 struct dnode **dnf;
1001 struct dnode **dnp;
1002 struct dnode *dn;
1003 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001004 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001005 unsigned nfiles;
1006 unsigned dnfiles;
1007 unsigned dndirs;
1008 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001009#if ENABLE_FEATURE_LS_COLOR
1010 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1011 /* coreutils 6.10:
1012 * # ls --color=BOGUS
1013 * ls: invalid argument 'BOGUS' for '--color'
1014 * Valid arguments are:
1015 * 'always', 'yes', 'force'
1016 * 'never', 'no', 'none'
1017 * 'auto', 'tty', 'if-tty'
1018 * (and substrings: "--color=alwa" work too)
1019 */
1020 static const char ls_longopts[] ALIGN1 =
1021 "color\0" Optional_argument "\xff"; /* no short equivalent */
1022 static const char color_str[] ALIGN1 =
1023 "always\0""yes\0""force\0"
1024 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001025 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001026 const char *color_opt = color_str; /* "always" */
1027#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001028
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001029 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001030
Denys Vlasenko28055022010-01-04 20:49:58 +01001031 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001032
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001033 if (ENABLE_FEATURE_LS_SORTFILES)
1034 all_fmt = SORT_NAME;
Glenn L McGrath792cae52004-01-18 05:15:16 +00001035
Denis Vlasenko5c759602006-10-28 12:37:16 +00001036#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001037 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +00001038 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001039 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +00001040 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001041#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001042
Eric Andersen11c65522000-09-07 17:24:47 +00001043 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001044 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001045 opt_complementary =
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001046 /* -e implies -l */
1047 "el"
Denys Vlasenkof3137462010-12-19 05:05:34 +01001048 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1049 * in some pairs of opts, only last one takes effect:
1050 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001051 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001052 // ":m-l:l-m" - we don't have -m
1053 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001054 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1055 ":C-1:1-C" /* bycols/oneline */
1056 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1057 ":c-u:u-c" /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001058 /* -w NUM: */
1059 IF_FEATURE_AUTOWIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001060 opt = getopt32(argv, ls_options
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +01001061 IF_FEATURE_AUTOWIDTH(, NULL, &terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001062 IF_FEATURE_LS_COLOR(, &color_opt)
1063 );
Denys Vlasenko982aa262010-12-19 21:54:39 +01001064 for (i = 0; opt_flags[i] != (1U << 31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001065 if (opt & (1 << i)) {
Denys Vlasenko982aa262010-12-19 21:54:39 +01001066 uint32_t flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001067
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001068 if (flags & STYLE_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001069 all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001070 if (flags & SORT_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001071 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001072 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001073 all_fmt &= ~TIME_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001074
Manuel Novoa III cad53642003-03-19 09:13:01 +00001075 all_fmt |= flags;
1076 }
Eric Andersen11c65522000-09-07 17:24:47 +00001077 }
1078
Denis Vlasenko5c759602006-10-28 12:37:16 +00001079#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko26d11b82011-02-28 12:38:08 +01001080 /* set show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001081 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1082 char *p = getenv("LS_COLORS");
1083 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001084 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001085 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001086 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001087 if (opt & OPT_color) {
1088 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +00001089 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001090 else switch (index_in_substrings(color_str, color_opt)) {
1091 case 3:
1092 case 4:
1093 case 5:
1094 if (isatty(STDOUT_FILENO)) {
1095 case 0:
1096 case 1:
1097 case 2:
1098 show_color = 1;
1099 }
1100 }
Paul Fox156dc412005-08-01 19:33:30 +00001101 }
1102#endif
1103
Eric Andersen11c65522000-09-07 17:24:47 +00001104 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001105 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001106 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001107 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1108 if (all_fmt & TIME_CHANGE)
1109 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1110 if (all_fmt & TIME_ACCESS)
1111 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1112 }
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001113 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001114 all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001115
Denys Vlasenkof3137462010-12-19 05:05:34 +01001116 /* choose a display format if one was not already specified by an option */
Rob Landleyea224be2006-06-18 20:20:07 +00001117 if (!(all_fmt & STYLE_MASK))
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001118 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001119
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001120 argv += optind;
1121 if (!argv[0])
1122 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001123
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001124 if (argv[1])
1125 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001126
Denis Vlasenko5c759602006-10-28 12:37:16 +00001127 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001128 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001129 nfiles = 0;
1130 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001131 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001132 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenkoea684c62011-03-12 03:12:36 +01001133 !((all_fmt & STYLE_MASK) == STYLE_LONG
1134 || (all_fmt & LIST_BLOCKS)
1135 || (option_mask32 & OPT_F)
1136 )
Denys Vlasenko982aa262010-12-19 21:54:39 +01001137 /* ... or if -H: */
1138 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001139 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001140 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001141 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001142 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001143 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001144 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001145 cur->next = dn;
1146 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001147 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001148 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001149
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001150 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1151 if (nfiles == 0)
1152 return exit_code;
1153
Eric Andersen11c65522000-09-07 17:24:47 +00001154 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001155 * allocate memory for an array to hold dnode pointers
1156 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001157 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001158 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1159 dnp[i] = dn; /* save pointer to node in array */
1160 dn = dn->next;
1161 if (!dn)
1162 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001163 }
1164
Manuel Novoa III cad53642003-03-19 09:13:01 +00001165 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001166 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001167 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001168 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001169 dnd = splitdnarray(dnp, SPLIT_DIR);
1170 dnf = splitdnarray(dnp, SPLIT_FILE);
1171 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001172 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001173 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001174 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001175 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001176 if (ENABLE_FEATURE_CLEAN_UP)
1177 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001178 }
1179 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001180 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001181 showdirs(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001182 if (ENABLE_FEATURE_CLEAN_UP)
1183 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001184 }
1185 }
Rob Landley26314862006-05-02 19:46:52 +00001186 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001187 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001188 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001189}