blob: 145e7781f20e4b48a0684bfc66c7c6f9f3783d47 [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;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000662 char *lpath = lpath; /* for compiler */
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
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200668 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000669 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000670 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200671 */
Eric Andersen11c65522000-09-07 17:24:47 +0000672
Denis Vlasenko5c759602006-10-28 12:37:16 +0000673#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000674 append = append_char(dn->dstat.st_mode);
675#endif
676
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000677 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100678 * does not appear *inside* the "ls -l" line */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000679 if (all_fmt & LIST_SYMLINK)
680 if (S_ISLNK(dn->dstat.st_mode))
681 lpath = xmalloc_readlink_or_warn(dn->fullname);
682
683 if (all_fmt & LIST_INO)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100684 column += printf("%7llu ", (long long) dn->dstat.st_ino);
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100685//TODO: -h should affect -s too:
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000686 if (all_fmt & LIST_BLOCKS)
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100687 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000688 if (all_fmt & LIST_MODEBITS)
689 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
690 if (all_fmt & LIST_NLINKS)
691 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100692 if (all_fmt & LIST_ID_NUMERIC) {
693 if (option_mask32 & OPT_g)
694 column += printf("%-8u ", (int) dn->dstat.st_gid);
695 else
696 column += printf("%-8u %-8u ",
697 (int) dn->dstat.st_uid,
698 (int) dn->dstat.st_gid);
699 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000700#if ENABLE_FEATURE_LS_USERNAME
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100701 else if (all_fmt & LIST_ID_NAME) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000702 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100703 column += printf("%-8.8s ",
Denys Vlasenko4909fec2010-11-06 00:46:57 +0100704 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000705 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100706 column += printf("%-8.8s %-8.8s ",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000707 get_cached_username(dn->dstat.st_uid),
708 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000709 }
710 }
Eric Andersen11c65522000-09-07 17:24:47 +0000711#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100712 if (all_fmt & LIST_SIZE) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000713 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
714 column += printf("%4u, %3u ",
715 (int) major(dn->dstat.st_rdev),
716 (int) minor(dn->dstat.st_rdev));
717 } else {
Denys Vlasenko982aa262010-12-19 21:54:39 +0100718 if (option_mask32 & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100719 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200720 /* print st_size, show one fractional, use suffixes */
721 make_human_readable_str(dn->dstat.st_size, 1, 0)
722 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000723 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000724 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000725 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000726 }
727 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000728#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100729 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
730 char *filetime;
731 time_t ttime = dn->dstat.st_mtime;
732 if (all_fmt & TIME_ACCESS)
733 ttime = dn->dstat.st_atime;
734 if (all_fmt & TIME_CHANGE)
735 ttime = dn->dstat.st_ctime;
736 filetime = ctime(&ttime);
737 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100738 if (all_fmt & LIST_FULLTIME) { /* -e */
739 /* Note: coreutils 8.4 ls --full-time prints:
740 * 2009-07-13 17:49:27.000000000 +0200
741 */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100742 column += printf("%.24s ", filetime);
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100743 } else { /* LIST_DATE_TIME */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000744 /* current_time_t ~== time(NULL) */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100745 time_t age = current_time_t - ttime;
746 printf("%.6s ", filetime + 4); /* "Jun 30" */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000747 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
748 /* hh:mm if less than 6 months old */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100749 printf("%.5s ", filetime + 11);
750 } else { /* year. buggy if year > 9999 ;) */
751 printf(" %.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000752 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000753 column += 13;
754 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100755 }
Eric Andersen11c65522000-09-07 17:24:47 +0000756#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000757#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000758 if (all_fmt & LIST_CONTEXT) {
759 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
760 freecon(dn->sid);
761 }
Eric Andersen9e480452003-07-03 10:07:04 +0000762#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100763
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000764#if ENABLE_FEATURE_LS_COLOR
James Youngmana4bc10c2010-12-20 01:36:16 +0100765 if (show_color) {
766 info.st_mode = 0; /* for fgcolor() */
767 lstat(dn->fullname, &info);
768 printf("\033[%u;%um", bold(info.st_mode),
769 fgcolor(info.st_mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000770 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100771#endif
772 column += print_name(dn->name);
773 if (show_color) {
774 printf("\033[0m");
775 }
776
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000777 if (all_fmt & LIST_SYMLINK) {
778 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
779 printf(" -> ");
780#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
781#if ENABLE_FEATURE_LS_COLOR
782 info.st_mode = 0; /* for fgcolor() */
783#endif
784 if (stat(dn->fullname, &info) == 0) {
785 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000786 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000787#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000788#if ENABLE_FEATURE_LS_COLOR
789 if (show_color) {
790 printf("\033[%u;%um", bold(info.st_mode),
791 fgcolor(info.st_mode));
792 }
793#endif
794 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000795 if (show_color) {
796 printf("\033[0m");
797 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000798 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000799 }
800 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000801#if ENABLE_FEATURE_LS_FILETYPES
802 if (all_fmt & LIST_FILETYPE) {
803 if (append) {
804 putchar(append);
805 column++;
806 }
807 }
808#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000809
Glenn L McGrath4d001292003-01-06 01:11:50 +0000810 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000811}
812
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100813static void showfiles(struct dnode **dn, unsigned nfiles)
814{
815 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100816 unsigned column;
817 unsigned nexttab;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100818 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100819
820 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
821 ncols = 1;
822 } else {
823 /* find the longest file name, use that as the column width */
824 for (i = 0; dn[i]; i++) {
825 int len = calc_name_len(dn[i]->name);
826 if (column_width < len)
827 column_width = len;
828 }
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100829 column_width += 1 +
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100830 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
831 ((all_fmt & LIST_INO) ? 8 : 0) +
832 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
833 ncols = (int) (terminal_width / column_width);
834 }
835
836 if (ncols > 1) {
837 nrows = nfiles / ncols;
838 if (nrows * ncols < nfiles)
839 nrows++; /* round up fractionals */
840 } else {
841 nrows = nfiles;
842 ncols = 1;
843 }
844
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100845 column = 0;
846 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100847 for (row = 0; row < nrows; row++) {
848 for (nc = 0; nc < ncols; nc++) {
849 /* reach into the array based on the column and row */
850 if (all_fmt & DISP_ROWS)
851 i = (row * ncols) + nc; /* display across row */
852 else
853 i = (nc * nrows) + row; /* display by column */
854 if (i < nfiles) {
855 if (column > 0) {
856 nexttab -= column;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100857 printf("%*s ", nexttab, "");
858 column += nexttab + 1;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100859 }
860 nexttab = column + column_width;
861 column += list_single(dn[i]);
862 }
863 }
864 putchar('\n');
865 column = 0;
866 }
867}
868
869
870#if ENABLE_DESKTOP
871/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
872 * If any of the -l, -n, -s options is specified, each list
873 * of files within the directory shall be preceded by a
874 * status line indicating the number of file system blocks
875 * occupied by files in the directory in 512-byte units if
876 * the -k option is not specified, or 1024-byte units if the
877 * -k option is specified, rounded up to the next integral
878 * number of units.
879 */
880/* by Jorgen Overgaard (jorgen AT antistaten.se) */
881static off_t calculate_blocks(struct dnode **dn)
882{
883 uoff_t blocks = 1;
884 if (dn) {
885 while (*dn) {
886 /* st_blocks is in 512 byte blocks */
887 blocks += (*dn)->dstat.st_blocks;
888 dn++;
889 }
890 }
891
892 /* Even though standard says use 512 byte blocks, coreutils use 1k */
893 /* Actually, we round up by calculating (blocks + 1) / 2,
894 * "+ 1" was done when we initialized blocks to 1 */
895 return blocks >> 1;
896}
897#endif
898
899
900static struct dnode **list_dir(const char *, unsigned *);
901
902static void showdirs(struct dnode **dn, int first)
903{
904 unsigned nfiles;
905 unsigned dndirs;
906 struct dnode **subdnp;
907 struct dnode **dnd;
908
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100909 for (; *dn; dn++) {
910 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
911 if (!first)
912 bb_putchar('\n');
913 first = 0;
914 printf("%s:\n", (*dn)->fullname);
915 }
916 subdnp = list_dir((*dn)->fullname, &nfiles);
917#if ENABLE_DESKTOP
918 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
919 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
920#endif
921 if (nfiles > 0) {
922 /* list all files at this level */
923 dnsort(subdnp, nfiles);
924 showfiles(subdnp, nfiles);
925 if (ENABLE_FEATURE_LS_RECURSIVE
926 && (all_fmt & DISP_RECURSIVE)
927 ) {
928 /* recursive - list the sub-dirs */
929 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
930 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
931 if (dndirs > 0) {
932 dnsort(dnd, dndirs);
933 showdirs(dnd, 0);
934 /* free the array of dnode pointers to the dirs */
935 free(dnd);
936 }
937 }
938 /* free the dnodes and the fullname mem */
939 dfree(subdnp);
940 }
941 }
942}
943
944
945/* Returns NULL-terminated malloced vector of pointers (or NULL) */
946static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
947{
948 struct dnode *dn, *cur, **dnp;
949 struct dirent *entry;
950 DIR *dir;
951 unsigned i, nfiles;
952
953 /* Never happens:
954 if (path == NULL)
955 return NULL;
956 */
957
958 *nfiles_p = 0;
959 dir = warn_opendir(path);
960 if (dir == NULL) {
961 exit_code = EXIT_FAILURE;
962 return NULL; /* could not open the dir */
963 }
964 dn = NULL;
965 nfiles = 0;
966 while ((entry = readdir(dir)) != NULL) {
967 char *fullname;
968
969 /* are we going to list the file- it may be . or .. or a hidden file */
970 if (entry->d_name[0] == '.') {
971 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
972 && !(all_fmt & DISP_DOT)
973 ) {
974 continue;
975 }
976 if (!(all_fmt & DISP_HIDDEN))
977 continue;
978 }
979 fullname = concat_path_file(path, entry->d_name);
980 cur = my_stat(fullname, bb_basename(fullname), 0);
981 if (!cur) {
982 free(fullname);
983 continue;
984 }
985 cur->fname_allocated = 1;
986 cur->next = dn;
987 dn = cur;
988 nfiles++;
989 }
990 closedir(dir);
991
992 if (dn == NULL)
993 return NULL;
994
995 /* now that we know how many files there are
996 * allocate memory for an array to hold dnode pointers
997 */
998 *nfiles_p = nfiles;
999 dnp = dnalloc(nfiles);
1000 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1001 dnp[i] = dn; /* save pointer to node in array */
1002 dn = dn->next;
1003 if (!dn)
1004 break;
1005 }
1006
1007 return dnp;
1008}
1009
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001010
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001011int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +00001012{
Glenn L McGrath792cae52004-01-18 05:15:16 +00001013 struct dnode **dnd;
1014 struct dnode **dnf;
1015 struct dnode **dnp;
1016 struct dnode *dn;
1017 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001018 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001019 unsigned nfiles;
1020 unsigned dnfiles;
1021 unsigned dndirs;
1022 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001023#if ENABLE_FEATURE_LS_COLOR
1024 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1025 /* coreutils 6.10:
1026 * # ls --color=BOGUS
1027 * ls: invalid argument 'BOGUS' for '--color'
1028 * Valid arguments are:
1029 * 'always', 'yes', 'force'
1030 * 'never', 'no', 'none'
1031 * 'auto', 'tty', 'if-tty'
1032 * (and substrings: "--color=alwa" work too)
1033 */
1034 static const char ls_longopts[] ALIGN1 =
1035 "color\0" Optional_argument "\xff"; /* no short equivalent */
1036 static const char color_str[] ALIGN1 =
1037 "always\0""yes\0""force\0"
1038 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001039 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001040 const char *color_opt = color_str; /* "always" */
1041#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001042
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001043 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001044
Denys Vlasenko28055022010-01-04 20:49:58 +01001045 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001046
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001047 if (ENABLE_FEATURE_LS_SORTFILES)
1048 all_fmt = SORT_NAME;
Glenn L McGrath792cae52004-01-18 05:15:16 +00001049
Denis Vlasenko5c759602006-10-28 12:37:16 +00001050#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001051 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +00001052 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001053 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +00001054 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001055#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001056
Eric Andersen11c65522000-09-07 17:24:47 +00001057 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001058 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001059 opt_complementary =
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001060 /* -e implies -l */
1061 "el"
Denys Vlasenkof3137462010-12-19 05:05:34 +01001062 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1063 * in some pairs of opts, only last one takes effect:
1064 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001065 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001066 // ":m-l:l-m" - we don't have -m
1067 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001068 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1069 ":C-1:1-C" /* bycols/oneline */
1070 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1071 ":c-u:u-c" /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001072 /* -w NUM: */
1073 IF_FEATURE_AUTOWIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001074 opt = getopt32(argv, ls_options
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +01001075 IF_FEATURE_AUTOWIDTH(, NULL, &terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001076 IF_FEATURE_LS_COLOR(, &color_opt)
1077 );
Denys Vlasenko982aa262010-12-19 21:54:39 +01001078 for (i = 0; opt_flags[i] != (1U << 31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001079 if (opt & (1 << i)) {
Denys Vlasenko982aa262010-12-19 21:54:39 +01001080 uint32_t flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001081
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001082 if (flags & STYLE_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001083 all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001084 if (flags & SORT_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001085 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001086 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001087 all_fmt &= ~TIME_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001088
Manuel Novoa III cad53642003-03-19 09:13:01 +00001089 all_fmt |= flags;
1090 }
Eric Andersen11c65522000-09-07 17:24:47 +00001091 }
1092
Denis Vlasenko5c759602006-10-28 12:37:16 +00001093#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko26d11b82011-02-28 12:38:08 +01001094 /* set show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001095 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1096 char *p = getenv("LS_COLORS");
1097 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001098 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001099 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001100 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001101 if (opt & OPT_color) {
1102 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +00001103 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001104 else switch (index_in_substrings(color_str, color_opt)) {
1105 case 3:
1106 case 4:
1107 case 5:
1108 if (isatty(STDOUT_FILENO)) {
1109 case 0:
1110 case 1:
1111 case 2:
1112 show_color = 1;
1113 }
1114 }
Paul Fox156dc412005-08-01 19:33:30 +00001115 }
1116#endif
1117
Eric Andersen11c65522000-09-07 17:24:47 +00001118 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001119 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001120 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001121 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1122 if (all_fmt & TIME_CHANGE)
1123 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1124 if (all_fmt & TIME_ACCESS)
1125 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1126 }
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001127 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001128 all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001129
Denys Vlasenkof3137462010-12-19 05:05:34 +01001130 /* choose a display format if one was not already specified by an option */
Rob Landleyea224be2006-06-18 20:20:07 +00001131 if (!(all_fmt & STYLE_MASK))
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001132 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001133
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001134 argv += optind;
1135 if (!argv[0])
1136 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001137
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001138 if (argv[1])
1139 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001140
Denis Vlasenko5c759602006-10-28 12:37:16 +00001141 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001142 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001143 nfiles = 0;
1144 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001145 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001146 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenkoea684c62011-03-12 03:12:36 +01001147 !((all_fmt & STYLE_MASK) == STYLE_LONG
1148 || (all_fmt & LIST_BLOCKS)
1149 || (option_mask32 & OPT_F)
1150 )
Denys Vlasenko982aa262010-12-19 21:54:39 +01001151 /* ... or if -H: */
1152 || (option_mask32 & OPT_H)
Denys Vlasenko163d8642010-12-19 06:16:28 +01001153 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001154 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001155 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001156 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001157 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001158 cur->next = dn;
1159 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001160 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001161 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001162
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001163 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1164 if (nfiles == 0)
1165 return exit_code;
1166
Eric Andersen11c65522000-09-07 17:24:47 +00001167 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001168 * allocate memory for an array to hold dnode pointers
1169 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001170 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001171 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1172 dnp[i] = dn; /* save pointer to node in array */
1173 dn = dn->next;
1174 if (!dn)
1175 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001176 }
1177
Manuel Novoa III cad53642003-03-19 09:13:01 +00001178 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001179 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001180 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001181 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001182 dnd = splitdnarray(dnp, SPLIT_DIR);
1183 dnf = splitdnarray(dnp, SPLIT_FILE);
1184 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001185 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001186 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001187 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001188 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001189 if (ENABLE_FEATURE_CLEAN_UP)
1190 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001191 }
1192 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001193 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001194 showdirs(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001195 if (ENABLE_FEATURE_CLEAN_UP)
1196 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001197 }
1198 }
Rob Landley26314862006-05-02 19:46:52 +00001199 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001200 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001201 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001202}