blob: a30422a8c34ee7a83ddfc32db526957b1c8e5532 [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"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020045//usage: "\n -1 One column output"
46//usage: "\n -a Include entries which start with ."
47//usage: "\n -A Like -a, but exclude . and .."
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010048//usage: "\n -C List by columns"
49//usage: "\n -x List by lines"
50//usage: "\n -d List directory entries instead of contents"
51//usage: IF_FEATURE_LS_FOLLOWLINKS(
Denys Vlasenko982aa262010-12-19 21:54:39 +010052//usage: "\n -L Follow symlinks"
Denys Vlasenko4ad95e62011-05-12 18:40:59 +020053//usage: "\n -H Follow symlinks on command line"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010054//usage: )
55//usage: IF_FEATURE_LS_RECURSIVE(
56//usage: "\n -R Recurse"
57//usage: )
58//usage: IF_FEATURE_LS_FILETYPES(
Denys Vlasenko4ad95e62011-05-12 18:40:59 +020059//usage: "\n -p Append / to dir entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010060//usage: "\n -F Append indicator (one of */=@|) to entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010061//usage: )
62//usage: "\n -l Long listing format"
63//usage: "\n -i List inode numbers"
64//usage: "\n -n List numeric UIDs and GIDs instead of names"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020065//usage: "\n -s List allocated blocks"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010066//usage: IF_FEATURE_LS_TIMESTAMPS(
67//usage: "\n -e List full date and time"
68//usage: )
69//usage: IF_FEATURE_HUMAN_READABLE(
70//usage: "\n -h List sizes in human readable format (1K 243M 2G)"
71//usage: )
72//usage: IF_FEATURE_LS_SORTFILES(
73//usage: "\n -r Sort in reverse order"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020074//usage: "\n -S Sort by size"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010075//usage: "\n -X Sort by extension"
76//usage: "\n -v Sort by version"
77//usage: )
78//usage: IF_FEATURE_LS_TIMESTAMPS(
79//usage: "\n -c With -l: sort by ctime"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020080//usage: "\n -t With -l: sort by mtime"
81//usage: "\n -u With -l: sort by atime"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010082//usage: )
83//usage: IF_SELINUX(
84//usage: "\n -k List security context"
85//usage: "\n -K List security context in long format"
86//usage: "\n -Z List security context and permission"
87//usage: )
88//usage: IF_FEATURE_AUTOWIDTH(
89//usage: "\n -w N Assume the terminal is N columns wide"
90//usage: )
91//usage: IF_FEATURE_LS_COLOR(
92//usage: "\n --color[={always,never,auto}] Control coloring"
93//usage: )
94
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000095#include "libbb.h"
Denys Vlasenko42a8fd02009-07-11 21:36:13 +020096#include "unicode.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +000097
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +000098
Denis Vlasenko99912ca2007-04-10 15:43:37 +000099/* This is a NOEXEC applet. Be very careful! */
100
Eric Andersenf1142c52001-02-20 06:16:29 +0000101
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000102#if ENABLE_FTPD
103/* ftpd uses ls, and without timestamps Mozilla won't understand
104 * ftpd's LIST output.
105 */
106# undef CONFIG_FEATURE_LS_TIMESTAMPS
107# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000108# undef IF_FEATURE_LS_TIMESTAMPS
109# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000110# define CONFIG_FEATURE_LS_TIMESTAMPS 1
111# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000112# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
113# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000114#endif
115
116
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000117enum {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000118TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000119
Denys Vlasenko982aa262010-12-19 21:54:39 +0100120SPLIT_FILE = 0,
Denys Vlasenko2a816392011-05-13 17:28:09 +0200121SPLIT_DIR = 1,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100122SPLIT_SUBDIR = 2,
123
Denys Vlasenko2a816392011-05-13 17:28:09 +0200124/* Bits in G.all_fmt: */
Eric Andersencc8ed391999-10-05 16:24:54 +0000125
Eric Andersen11c65522000-09-07 17:24:47 +0000126/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
127/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000128LIST_INO = 1 << 0,
129LIST_BLOCKS = 1 << 1,
130LIST_MODEBITS = 1 << 2,
131LIST_NLINKS = 1 << 3,
132LIST_ID_NAME = 1 << 4,
133LIST_ID_NUMERIC = 1 << 5,
134LIST_CONTEXT = 1 << 6,
135LIST_SIZE = 1 << 7,
James Youngmana4bc10c2010-12-20 01:36:16 +0100136LIST_DATE_TIME = 1 << 8,
137LIST_FULLTIME = 1 << 9,
138LIST_SYMLINK = 1 << 10,
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200139LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */
140LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */
141LIST_MASK = (LIST_CLASSIFY << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +0000142
143/* what files will be displayed */
James Youngmana4bc10c2010-12-20 01:36:16 +0100144DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */
145DISP_HIDDEN = 1 << 14, /* show filenames starting with . */
146DISP_DOT = 1 << 15, /* show . and .. */
147DISP_NOLIST = 1 << 16, /* show directory as itself, not contents */
148DISP_RECURSIVE = 1 << 17, /* show directory and everything below it */
149DISP_ROWS = 1 << 18, /* print across rows */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000150DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +0000151
Denys Vlasenko982aa262010-12-19 21:54:39 +0100152/* what is the overall style of the listing */
James Youngmana4bc10c2010-12-20 01:36:16 +0100153STYLE_COLUMNAR = 1 << 19, /* many records per line */
154STYLE_LONG = 2 << 19, /* one record per line, extended info */
155STYLE_SINGLE = 3 << 19, /* one record per line */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100156STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000157
Eric Andersen11c65522000-09-07 17:24:47 +0000158/* which of the three times will be used */
James Youngmana4bc10c2010-12-20 01:36:16 +0100159TIME_CHANGE = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100160TIME_ACCESS = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
James Youngmana4bc10c2010-12-20 01:36:16 +0100161TIME_MASK = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000162
Denys Vlasenko982aa262010-12-19 21:54:39 +0100163/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
James Youngmana4bc10c2010-12-20 01:36:16 +0100164SORT_REVERSE = 1 << 23,
Eric Andersencc8ed391999-10-05 16:24:54 +0000165
Denys Vlasenko982aa262010-12-19 21:54:39 +0100166SORT_NAME = 0, /* sort by file name */
James Youngmana4bc10c2010-12-20 01:36:16 +0100167SORT_SIZE = 1 << 24, /* sort by file size */
168SORT_ATIME = 2 << 24, /* sort by last access time */
169SORT_CTIME = 3 << 24, /* sort by last change time */
170SORT_MTIME = 4 << 24, /* sort by last modification time */
171SORT_VERSION = 5 << 24, /* sort by version */
172SORT_EXT = 6 << 24, /* sort by file name extension */
173SORT_DIR = 7 << 24, /* sort by file or directory */
174SORT_MASK = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000175
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000176LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
James Youngmana4bc10c2010-12-20 01:36:16 +0100177 LIST_DATE_TIME | LIST_SYMLINK,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000178};
Eric Andersencc8ed391999-10-05 16:24:54 +0000179
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100180/* -Cadil1 Std options, busybox always supports */
181/* -gnsxA Std options, busybox always supports */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100182/* -Q GNU option, busybox always supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100183/* -k SELinux option, busybox always supports (ignores if !SELinux) */
184/* Std has -k which means "show sizes in kbytes" */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200185/* -LHRctur Std options, busybox optionally supports */
186/* -Fp Std options, busybox optionally supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100187/* -SXvhTw GNU options, busybox optionally supports */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200188/* -T WIDTH Ignored (we don't use tabs on output) */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100189/* -KZ SELinux mandated options, busybox optionally supports */
190/* (coreutils 8.4 has no -K, remove it?) */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100191/* -e I think we made this one up (looks similar to GNU --full-time) */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100192/* We already used up all 32 bits, if we need to add more, candidates for removal: */
193/* -K, -T, -e (add --full-time instead) */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000194static const char ls_options[] ALIGN1 =
Denys Vlasenko982aa262010-12-19 21:54:39 +0100195 "Cadil1gnsxQAk" /* 13 opts, total 13 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000196 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
197 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
198 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100199 IF_FEATURE_LS_RECURSIVE("R") /* 1, 24 */
200 IF_SELINUX("KZ") /* 2, 26 */
201 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 28 */
202 IF_FEATURE_HUMAN_READABLE("h") /* 1, 29 */
203 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 31 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100204 /* with --color, we use all 32 bits */;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000205enum {
206 //OPT_C = (1 << 0),
207 //OPT_a = (1 << 1),
208 //OPT_d = (1 << 2),
209 //OPT_i = (1 << 3),
210 //OPT_l = (1 << 4),
211 //OPT_1 = (1 << 5),
212 OPT_g = (1 << 6),
213 //OPT_n = (1 << 7),
214 //OPT_s = (1 << 8),
215 //OPT_x = (1 << 9),
216 OPT_Q = (1 << 10),
217 //OPT_A = (1 << 11),
218 //OPT_k = (1 << 12),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100219
220 OPTBIT_c = 13,
221 OPTBIT_e,
222 OPTBIT_t,
223 OPTBIT_u,
224 OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS,
225 OPTBIT_X, /* 18 */
226 OPTBIT_r,
227 OPTBIT_v,
228 OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
229 OPTBIT_p, /* 22 */
230 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
231 OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
232 OPTBIT_Z, /* 25 */
233 OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX,
234 OPTBIT_H, /* 27 */
Denys Vlasenkocd387f22011-02-27 04:10:00 +0100235 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100236 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100237 OPTBIT_w, /* 30 */
238 OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH,
239
240 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
241 OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS,
242 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
243 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
244 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
245 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
246 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
247 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
248 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
249 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
250 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
251 OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX,
252 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
253 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
254 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
255 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
256 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH,
257 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH,
258 OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000259};
260
Denis Vlasenko248ce912009-03-03 14:09:04 +0000261/* TODO: simple toggles may be stored as OPT_xxx bits instead */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100262static const uint32_t opt_flags[] = {
Denys Vlasenko69675782013-01-14 01:34:48 +0100263 STYLE_COLUMNAR, /* C */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100264 DISP_HIDDEN | DISP_DOT, /* a */
265 DISP_NOLIST, /* d */
266 LIST_INO, /* i */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100267 LIST_LONG | STYLE_LONG, /* l */
James Youngmana4bc10c2010-12-20 01:36:16 +0100268 STYLE_SINGLE, /* 1 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100269 LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100270 LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100271 LIST_BLOCKS, /* s */
James Youngmana4bc10c2010-12-20 01:36:16 +0100272 DISP_ROWS | STYLE_COLUMNAR, /* x */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100273 0, /* Q (quote filename) - handled via OPT_Q */
274 DISP_HIDDEN, /* A */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100275 ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000276#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100277 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100278 LIST_FULLTIME, /* e */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100279 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
280 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000281#endif
282#if ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100283 SORT_SIZE, /* S */
284 SORT_EXT, /* X */
285 SORT_REVERSE, /* r */
286 SORT_VERSION, /* v */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000287#endif
288#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200289 LIST_FILETYPE | LIST_CLASSIFY, /* F */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100290 LIST_FILETYPE, /* p */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000291#endif
Denis Vlasenko248ce912009-03-03 14:09:04 +0000292#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100293 DISP_RECURSIVE, /* R */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000294#endif
Denis Vlasenko248ce912009-03-03 14:09:04 +0000295#if ENABLE_SELINUX
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100296 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */
297 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000298#endif
Denys Vlasenko982aa262010-12-19 21:54:39 +0100299 (1U << 31)
300 /* options after Z are not processed through opt_flags */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000301};
302
303
Eric Andersen11c65522000-09-07 17:24:47 +0000304/*
Denys Vlasenko2a816392011-05-13 17:28:09 +0200305 * a directory entry and its stat info
Eric Andersen11c65522000-09-07 17:24:47 +0000306 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200307struct dnode {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200308 const char *name; /* usually basename, but think "ls -l dir/file" */
309 const char *fullname; /* full name (usable for stat etc) */
310 struct dnode *dn_next; /* for linked list */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000311 IF_SELINUX(security_context_t sid;)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200312 smallint fname_allocated;
313
314 /* Used to avoid re-doing [l]stat at printout stage
315 * if we already collected needed data in scan stage:
316 */
317 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
318 mode_t dn_mode_stat; /* obtained with stat, or 0 */
319
320// struct stat dstat;
321// struct stat is huge. We don't need it in full.
322// At least we don't need st_dev and st_blksize,
323// but there are invisible fields as well
324// (such as nanosecond-resolution timespamps)
325// and padding, which we also don't want to store.
326// We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
Denys Vlasenko4029e212011-05-13 17:28:46 +0200327// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
Denys Vlasenko2a816392011-05-13 17:28:09 +0200328//
329 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
330 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
331 off_t dn_size;
332#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
333 time_t dn_atime;
334 time_t dn_mtime;
335 time_t dn_ctime;
336#endif
337 ino_t dn_ino;
338 blkcnt_t dn_blocks;
339 nlink_t dn_nlink;
340 uid_t dn_uid;
341 gid_t dn_gid;
342 int dn_rdev_maj;
343 int dn_rdev_min;
344// dev_t dn_dev;
345// blksize_t dn_blksize;
Eric Andersen11c65522000-09-07 17:24:47 +0000346};
Eric Andersen11c65522000-09-07 17:24:47 +0000347
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000348struct globals {
349#if ENABLE_FEATURE_LS_COLOR
350 smallint show_color;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200351# define G_show_color (G.show_color)
352#else
353# define G_show_color 0
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000354#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000355 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000356 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000357#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko2a816392011-05-13 17:28:09 +0200358 unsigned terminal_width;
359# define G_terminal_width (G.terminal_width)
360#else
361# define G_terminal_width TERMINAL_WIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000362#endif
363#if ENABLE_FEATURE_LS_TIMESTAMPS
364 /* Do time() just once. Saves one syscall per file for "ls -l" */
365 time_t current_time_t;
366#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100367} FIX_ALIASING;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000368#define G (*(struct globals*)&bb_common_bufsiz1)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000369#define INIT_G() do { \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200370 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000371 memset(&G, 0, sizeof(G)); \
Denys Vlasenko2a816392011-05-13 17:28:09 +0200372 IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
373 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000374} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000375
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000376
Denys Vlasenko4029e212011-05-13 17:28:46 +0200377/*** Output code ***/
Denys Vlasenko2a816392011-05-13 17:28:09 +0200378
Eric Andersen79565b62000-08-11 18:10:21 +0000379
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000380/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
381 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
382 * 3/7:multiplexed char/block device)
383 * and we use 0 for unknown and 15 for executables (see below) */
384#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200385/* un fi chr - dir - blk - file - link - sock - - exe */
386#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000387/* 036 black foreground 050 black background
388 037 red foreground 051 red background
389 040 green foreground 052 green background
390 041 brown foreground 053 brown background
391 042 blue foreground 054 blue background
392 043 magenta (purple) foreground 055 magenta background
393 044 cyan (light blue) foreground 056 cyan background
394 045 gray foreground 057 white background
395*/
396#define COLOR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200397 /*un fi chr - dir - blk - file - link - sock - - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000398 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
399 [TYPEINDEX(mode)])
400/* Select normal (0) [actually "reset all"] or bold (1)
401 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
402 * let's use 7 for "impossible" types, just for fun)
403 * Note: coreutils 6.9 uses inverted red for setuid binaries.
404 */
405#define ATTR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200406 /*un fi chr - dir - blk - file- link- sock- - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000407 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
408 [TYPEINDEX(mode)])
409
Denis Vlasenko5c759602006-10-28 12:37:16 +0000410#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000411/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000412static char fgcolor(mode_t mode)
413{
Rob Landley9947a242006-06-15 22:11:10 +0000414 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000415 return COLOR(0xF000); /* File is executable ... */
416 return COLOR(mode);
417}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000418static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000419{
Rob Landley9947a242006-06-15 22:11:10 +0000420 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000421 return ATTR(0xF000); /* File is executable ... */
422 return ATTR(mode);
423}
424#endif
425
Denys Vlasenko2a816392011-05-13 17:28:09 +0200426#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000427static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000428{
Denys Vlasenko2a816392011-05-13 17:28:09 +0200429 if (!(G.all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000430 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000431 if (S_ISDIR(mode))
432 return '/';
Denys Vlasenko2a816392011-05-13 17:28:09 +0200433 if (!(G.all_fmt & LIST_CLASSIFY))
Rob Landley9947a242006-06-15 22:11:10 +0000434 return '\0';
435 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000436 return '*';
437 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000438}
439#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000440
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100441static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000442{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100443 unsigned len;
444 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000445
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100446 // TODO: quote tab as \t, etc, if -Q
447 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000448
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100449 if (!(option_mask32 & OPT_Q)) {
450 return uni_stat.unicode_width;
451 }
452
453 len = 2 + uni_stat.unicode_width;
454 while (*name) {
455 if (*name == '"' || *name == '\\') {
456 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000457 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100458 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000459 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100460 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000461}
462
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100463/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100464 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100465 * STYLE_SINGLE and STYLE_LONG don't care.
466 * coreutils 7.2 also supports:
467 * ls -b (--escape) = octal escapes (although it doesn't look like working)
468 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200469 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100470static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200471{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100472 unsigned len;
473 uni_stat_t uni_stat;
474
475 // TODO: quote tab as \t, etc, if -Q
476 name = printable_string(&uni_stat, name);
477
478 if (!(option_mask32 & OPT_Q)) {
479 fputs(name, stdout);
480 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200481 }
482
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100483 len = 2 + uni_stat.unicode_width;
484 putchar('"');
485 while (*name) {
486 if (*name == '"' || *name == '\\') {
487 putchar('\\');
488 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000489 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700490 putchar(*name);
491 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000492 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100493 putchar('"');
494 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000495}
496
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100497/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100498 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100499 * STYLE_SINGLE and STYLE_LONG don't care.
500 */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200501static NOINLINE unsigned display_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000502{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200503 unsigned column = 0;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200504 char *lpath;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000505#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200506 struct stat statbuf;
Eric Andersen11c65522000-09-07 17:24:47 +0000507 char append;
508#endif
509
Denis Vlasenko5c759602006-10-28 12:37:16 +0000510#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200511 append = append_char(dn->dn_mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000512#endif
513
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000514 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100515 * does not appear *inside* the "ls -l" line */
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200516 lpath = NULL;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200517 if (G.all_fmt & LIST_SYMLINK)
518 if (S_ISLNK(dn->dn_mode))
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000519 lpath = xmalloc_readlink_or_warn(dn->fullname);
520
Denys Vlasenko2a816392011-05-13 17:28:09 +0200521 if (G.all_fmt & LIST_INO)
522 column += printf("%7llu ", (long long) dn->dn_ino);
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100523//TODO: -h should affect -s too:
Denys Vlasenko2a816392011-05-13 17:28:09 +0200524 if (G.all_fmt & LIST_BLOCKS)
525 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
526 if (G.all_fmt & LIST_MODEBITS)
527 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
528 if (G.all_fmt & LIST_NLINKS)
529 column += printf("%4lu ", (long) dn->dn_nlink);
530 if (G.all_fmt & LIST_ID_NUMERIC) {
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100531 if (option_mask32 & OPT_g)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200532 column += printf("%-8u ", (int) dn->dn_gid);
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100533 else
534 column += printf("%-8u %-8u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200535 (int) dn->dn_uid,
536 (int) dn->dn_gid);
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100537 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000538#if ENABLE_FEATURE_LS_USERNAME
Denys Vlasenko2a816392011-05-13 17:28:09 +0200539 else if (G.all_fmt & LIST_ID_NAME) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000540 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100541 column += printf("%-8.8s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200542 get_cached_groupname(dn->dn_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000543 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100544 column += printf("%-8.8s %-8.8s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200545 get_cached_username(dn->dn_uid),
546 get_cached_groupname(dn->dn_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000547 }
548 }
Eric Andersen11c65522000-09-07 17:24:47 +0000549#endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200550 if (G.all_fmt & LIST_SIZE) {
551 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000552 column += printf("%4u, %3u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200553 dn->dn_rdev_maj,
554 dn->dn_rdev_min);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000555 } else {
Denys Vlasenko982aa262010-12-19 21:54:39 +0100556 if (option_mask32 & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100557 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200558 /* print size, show one fractional, use suffixes */
559 make_human_readable_str(dn->dn_size, 1, 0)
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200560 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000561 } else {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200562 column += printf("%9"OFF_FMT"u ", dn->dn_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000563 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000564 }
565 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000566#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko2a816392011-05-13 17:28:09 +0200567 if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100568 char *filetime;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200569 time_t ttime = dn->dn_mtime;
570 if (G.all_fmt & TIME_ACCESS)
571 ttime = dn->dn_atime;
572 if (G.all_fmt & TIME_CHANGE)
573 ttime = dn->dn_ctime;
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100574 filetime = ctime(&ttime);
575 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200576 if (G.all_fmt & LIST_FULLTIME) { /* -e */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100577 /* Note: coreutils 8.4 ls --full-time prints:
578 * 2009-07-13 17:49:27.000000000 +0200
579 */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100580 column += printf("%.24s ", filetime);
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100581 } else { /* LIST_DATE_TIME */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200582 /* G.current_time_t ~== time(NULL) */
583 time_t age = G.current_time_t - ttime;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000584 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
Denys Vlasenko8e92df12015-02-16 15:36:25 +0100585 /* less than 6 months old */
586 /* "mmm dd hh:mm " */
587 printf("%.12s ", filetime + 4);
588 } else {
589 /* "mmm dd yyyy " */
590 /* "mmm dd yyyyy " after year 9999 :) */
591 strchr(filetime + 20, '\n')[0] = ' ';
592 printf("%.7s%6s", filetime + 4, filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000593 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000594 column += 13;
595 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100596 }
Eric Andersen11c65522000-09-07 17:24:47 +0000597#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000598#if ENABLE_SELINUX
Denys Vlasenko2a816392011-05-13 17:28:09 +0200599 if (G.all_fmt & LIST_CONTEXT) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000600 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
601 freecon(dn->sid);
602 }
Eric Andersen9e480452003-07-03 10:07:04 +0000603#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100604
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000605#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200606 if (G_show_color) {
607 mode_t mode = dn->dn_mode_lstat;
608 if (!mode)
609 if (lstat(dn->fullname, &statbuf) == 0)
610 mode = statbuf.st_mode;
611 printf("\033[%u;%um", bold(mode), fgcolor(mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000612 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100613#endif
614 column += print_name(dn->name);
Denys Vlasenko2a816392011-05-13 17:28:09 +0200615 if (G_show_color) {
James Youngmana4bc10c2010-12-20 01:36:16 +0100616 printf("\033[0m");
617 }
618
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200619 if (lpath) {
620 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000621#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200622 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
623 mode_t mode = dn->dn_mode_stat;
624 if (!mode)
625 if (stat(dn->fullname, &statbuf) == 0)
626 mode = statbuf.st_mode;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200627# if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200628 append = append_char(mode);
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200629# endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200630# if ENABLE_FEATURE_LS_COLOR
631 if (G_show_color) {
632 printf("\033[%u;%um", bold(mode), fgcolor(mode));
633 }
634# endif
Eric Andersen11c65522000-09-07 17:24:47 +0000635 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200636#endif
637 column += print_name(lpath) + 4;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200638 free(lpath);
639 if (G_show_color) {
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200640 printf("\033[0m");
641 }
Eric Andersen11c65522000-09-07 17:24:47 +0000642 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000643#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200644 if (G.all_fmt & LIST_FILETYPE) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000645 if (append) {
646 putchar(append);
647 column++;
648 }
649 }
650#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000651
Glenn L McGrath4d001292003-01-06 01:11:50 +0000652 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000653}
654
Denys Vlasenko4029e212011-05-13 17:28:46 +0200655static void display_files(struct dnode **dn, unsigned nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100656{
657 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100658 unsigned column;
659 unsigned nexttab;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100660 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100661
Denys Vlasenko2a816392011-05-13 17:28:09 +0200662 if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100663 ncols = 1;
664 } else {
665 /* find the longest file name, use that as the column width */
666 for (i = 0; dn[i]; i++) {
667 int len = calc_name_len(dn[i]->name);
668 if (column_width < len)
669 column_width = len;
670 }
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100671 column_width += 1 +
Denys Vlasenko2a816392011-05-13 17:28:09 +0200672 IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
673 ((G.all_fmt & LIST_INO) ? 8 : 0) +
674 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
675 ncols = (unsigned)G_terminal_width / column_width;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100676 }
677
678 if (ncols > 1) {
679 nrows = nfiles / ncols;
680 if (nrows * ncols < nfiles)
681 nrows++; /* round up fractionals */
682 } else {
683 nrows = nfiles;
684 ncols = 1;
685 }
686
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100687 column = 0;
688 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100689 for (row = 0; row < nrows; row++) {
690 for (nc = 0; nc < ncols; nc++) {
691 /* reach into the array based on the column and row */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200692 if (G.all_fmt & DISP_ROWS)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100693 i = (row * ncols) + nc; /* display across row */
694 else
695 i = (nc * nrows) + row; /* display by column */
696 if (i < nfiles) {
697 if (column > 0) {
698 nexttab -= column;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100699 printf("%*s ", nexttab, "");
700 column += nexttab + 1;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100701 }
702 nexttab = column + column_width;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200703 column += display_single(dn[i]);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100704 }
705 }
706 putchar('\n');
707 column = 0;
708 }
709}
710
711
Denys Vlasenko4029e212011-05-13 17:28:46 +0200712/*** Dir scanning code ***/
713
714static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100715{
Denys Vlasenko4029e212011-05-13 17:28:46 +0200716 struct stat statbuf;
717 struct dnode *cur;
718
719 cur = xzalloc(sizeof(*cur));
720 cur->fullname = fullname;
721 cur->name = name;
722
723 if ((option_mask32 & OPT_L) || force_follow) {
724#if ENABLE_SELINUX
725 if (is_selinux_enabled()) {
Denys Vlasenko69675782013-01-14 01:34:48 +0100726 getfilecon(fullname, &cur->sid);
Denys Vlasenko4029e212011-05-13 17:28:46 +0200727 }
728#endif
729 if (stat(fullname, &statbuf)) {
730 bb_simple_perror_msg(fullname);
731 G.exit_code = EXIT_FAILURE;
732 free(cur);
733 return NULL;
734 }
735 cur->dn_mode_stat = statbuf.st_mode;
736 } else {
737#if ENABLE_SELINUX
738 if (is_selinux_enabled()) {
739 lgetfilecon(fullname, &cur->sid);
740 }
741#endif
742 if (lstat(fullname, &statbuf)) {
743 bb_simple_perror_msg(fullname);
744 G.exit_code = EXIT_FAILURE;
745 free(cur);
746 return NULL;
747 }
748 cur->dn_mode_lstat = statbuf.st_mode;
749 }
750
751 /* cur->dstat = statbuf: */
752 cur->dn_mode = statbuf.st_mode ;
753 cur->dn_size = statbuf.st_size ;
754#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
755 cur->dn_atime = statbuf.st_atime ;
756 cur->dn_mtime = statbuf.st_mtime ;
757 cur->dn_ctime = statbuf.st_ctime ;
758#endif
759 cur->dn_ino = statbuf.st_ino ;
760 cur->dn_blocks = statbuf.st_blocks;
761 cur->dn_nlink = statbuf.st_nlink ;
762 cur->dn_uid = statbuf.st_uid ;
763 cur->dn_gid = statbuf.st_gid ;
764 cur->dn_rdev_maj = major(statbuf.st_rdev);
765 cur->dn_rdev_min = minor(statbuf.st_rdev);
766
767 return cur;
768}
769
770static unsigned count_dirs(struct dnode **dn, int which)
771{
772 unsigned dirs, all;
773
774 if (!dn)
775 return 0;
776
777 dirs = all = 0;
778 for (; *dn; dn++) {
779 const char *name;
780
781 all++;
782 if (!S_ISDIR((*dn)->dn_mode))
783 continue;
784
785 name = (*dn)->name;
786 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
787 /* or if it's not . or .. */
788 || name[0] != '.'
789 || (name[1] && (name[1] != '.' || name[2]))
790 ) {
791 dirs++;
792 }
793 }
794 return which != SPLIT_FILE ? dirs : all - dirs;
795}
796
797/* get memory to hold an array of pointers */
798static struct dnode **dnalloc(unsigned num)
799{
800 if (num < 1)
801 return NULL;
802
803 num++; /* so that we have terminating NULL */
804 return xzalloc(num * sizeof(struct dnode *));
805}
806
807#if ENABLE_FEATURE_LS_RECURSIVE
808static void dfree(struct dnode **dnp)
809{
810 unsigned i;
811
812 if (dnp == NULL)
813 return;
814
815 for (i = 0; dnp[i]; i++) {
816 struct dnode *cur = dnp[i];
817 if (cur->fname_allocated)
818 free((char*)cur->fullname);
819 free(cur);
820 }
821 free(dnp);
822}
823#else
824#define dfree(...) ((void)0)
825#endif
826
827/* Returns NULL-terminated malloced vector of pointers (or NULL) */
828static struct dnode **splitdnarray(struct dnode **dn, int which)
829{
830 unsigned dncnt, d;
831 struct dnode **dnp;
832
833 if (dn == NULL)
834 return NULL;
835
836 /* count how many dirs or files there are */
837 dncnt = count_dirs(dn, which);
838
839 /* allocate a file array and a dir array */
840 dnp = dnalloc(dncnt);
841
842 /* copy the entrys into the file or dir array */
843 for (d = 0; *dn; dn++) {
844 if (S_ISDIR((*dn)->dn_mode)) {
845 const char *name;
846
847 if (which == SPLIT_FILE)
848 continue;
849
850 name = (*dn)->name;
851 if ((which & SPLIT_DIR) /* any dir... */
852 /* ... or not . or .. */
853 || name[0] != '.'
854 || (name[1] && (name[1] != '.' || name[2]))
855 ) {
856 dnp[d++] = *dn;
857 }
858 } else
859 if (which == SPLIT_FILE) {
860 dnp[d++] = *dn;
861 }
862 }
863 return dnp;
864}
865
866#if ENABLE_FEATURE_LS_SORTFILES
867static int sortcmp(const void *a, const void *b)
868{
869 struct dnode *d1 = *(struct dnode **)a;
870 struct dnode *d2 = *(struct dnode **)b;
871 unsigned sort_opts = G.all_fmt & SORT_MASK;
872 off_t dif;
873
874 dif = 0; /* assume SORT_NAME */
875 // TODO: use pre-initialized function pointer
876 // instead of branch forest
877 if (sort_opts == SORT_SIZE) {
878 dif = (d2->dn_size - d1->dn_size);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200879 } else
880 if (sort_opts == SORT_ATIME) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200881 dif = (d2->dn_atime - d1->dn_atime);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200882 } else
883 if (sort_opts == SORT_CTIME) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200884 dif = (d2->dn_ctime - d1->dn_ctime);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200885 } else
886 if (sort_opts == SORT_MTIME) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200887 dif = (d2->dn_mtime - d1->dn_mtime);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200888 } else
889 if (sort_opts == SORT_DIR) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200890 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200891 } else
Denys Vlasenko1e18a012011-06-21 17:12:52 +0200892#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200893 if (sort_opts == SORT_VERSION) {
894 dif = strverscmp(d1->name, d2->name);
895 } else
Denys Vlasenko561f9c82011-06-21 16:38:29 +0200896#endif
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200897 if (sort_opts == SORT_EXT) {
898 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
Denys Vlasenko4029e212011-05-13 17:28:46 +0200899 }
900 if (dif == 0) {
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200901 /* sort by name, use as tie breaker for other sorts */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200902 if (ENABLE_LOCALE_SUPPORT)
903 dif = strcoll(d1->name, d2->name);
904 else
905 dif = strcmp(d1->name, d2->name);
906 }
907
908 /* Make dif fit into an int */
909 if (sizeof(dif) > sizeof(int)) {
910 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
911 /* shift leaving only "int" worth of bits */
912 if (dif != 0) {
913 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100914 }
915 }
916
Denys Vlasenko4029e212011-05-13 17:28:46 +0200917 return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100918}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200919
920static void dnsort(struct dnode **dn, int size)
921{
922 qsort(dn, size, sizeof(*dn), sortcmp);
923}
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200924
925static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
926{
927 dnsort(dn, nfiles);
928 display_files(dn, nfiles);
929}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200930#else
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200931# define dnsort(dn, size) ((void)0)
932# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100933#endif
934
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100935/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200936static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100937{
938 struct dnode *dn, *cur, **dnp;
939 struct dirent *entry;
940 DIR *dir;
941 unsigned i, nfiles;
942
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100943 *nfiles_p = 0;
944 dir = warn_opendir(path);
945 if (dir == NULL) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200946 G.exit_code = EXIT_FAILURE;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100947 return NULL; /* could not open the dir */
948 }
949 dn = NULL;
950 nfiles = 0;
951 while ((entry = readdir(dir)) != NULL) {
952 char *fullname;
953
954 /* are we going to list the file- it may be . or .. or a hidden file */
955 if (entry->d_name[0] == '.') {
956 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
Denys Vlasenko2a816392011-05-13 17:28:09 +0200957 && !(G.all_fmt & DISP_DOT)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100958 ) {
959 continue;
960 }
Denys Vlasenko2a816392011-05-13 17:28:09 +0200961 if (!(G.all_fmt & DISP_HIDDEN))
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100962 continue;
963 }
964 fullname = concat_path_file(path, entry->d_name);
965 cur = my_stat(fullname, bb_basename(fullname), 0);
966 if (!cur) {
967 free(fullname);
968 continue;
969 }
970 cur->fname_allocated = 1;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200971 cur->dn_next = dn;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100972 dn = cur;
973 nfiles++;
974 }
975 closedir(dir);
976
977 if (dn == NULL)
978 return NULL;
979
980 /* now that we know how many files there are
981 * allocate memory for an array to hold dnode pointers
982 */
983 *nfiles_p = nfiles;
984 dnp = dnalloc(nfiles);
985 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
986 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200987 dn = dn->dn_next;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100988 if (!dn)
989 break;
990 }
991
992 return dnp;
993}
994
Denys Vlasenko4029e212011-05-13 17:28:46 +0200995#if ENABLE_DESKTOP
996/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
997 * If any of the -l, -n, -s options is specified, each list
998 * of files within the directory shall be preceded by a
999 * status line indicating the number of file system blocks
1000 * occupied by files in the directory in 512-byte units if
1001 * the -k option is not specified, or 1024-byte units if the
1002 * -k option is specified, rounded up to the next integral
1003 * number of units.
1004 */
1005/* by Jorgen Overgaard (jorgen AT antistaten.se) */
1006static off_t calculate_blocks(struct dnode **dn)
1007{
1008 uoff_t blocks = 1;
1009 if (dn) {
1010 while (*dn) {
1011 /* st_blocks is in 512 byte blocks */
1012 blocks += (*dn)->dn_blocks;
1013 dn++;
1014 }
1015 }
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001016
Denys Vlasenko4029e212011-05-13 17:28:46 +02001017 /* Even though standard says use 512 byte blocks, coreutils use 1k */
1018 /* Actually, we round up by calculating (blocks + 1) / 2,
1019 * "+ 1" was done when we initialized blocks to 1 */
1020 return blocks >> 1;
1021}
1022#endif
1023
1024static void scan_and_display_dirs_recur(struct dnode **dn, int first)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001025{
1026 unsigned nfiles;
1027 struct dnode **subdnp;
1028
1029 for (; *dn; dn++) {
Denys Vlasenko2a816392011-05-13 17:28:09 +02001030 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001031 if (!first)
1032 bb_putchar('\n');
1033 first = 0;
1034 printf("%s:\n", (*dn)->fullname);
1035 }
Denys Vlasenko4029e212011-05-13 17:28:46 +02001036 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001037#if ENABLE_DESKTOP
Denys Vlasenkofca0ee52014-02-27 15:40:26 +01001038 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS))
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001039 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1040#endif
1041 if (nfiles > 0) {
1042 /* list all files at this level */
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001043 sort_and_display_files(subdnp, nfiles);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001044
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001045 if (ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko2a816392011-05-13 17:28:09 +02001046 && (G.all_fmt & DISP_RECURSIVE)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001047 ) {
1048 struct dnode **dnd;
1049 unsigned dndirs;
1050 /* recursive - list the sub-dirs */
1051 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1052 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1053 if (dndirs > 0) {
1054 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001055 scan_and_display_dirs_recur(dnd, 0);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001056 /* free the array of dnode pointers to the dirs */
1057 free(dnd);
1058 }
1059 }
1060 /* free the dnodes and the fullname mem */
1061 dfree(subdnp);
1062 }
1063 }
1064}
1065
1066
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001067int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +00001068{
Glenn L McGrath792cae52004-01-18 05:15:16 +00001069 struct dnode **dnd;
1070 struct dnode **dnf;
1071 struct dnode **dnp;
1072 struct dnode *dn;
1073 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001074 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001075 unsigned nfiles;
1076 unsigned dnfiles;
1077 unsigned dndirs;
1078 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001079#if ENABLE_FEATURE_LS_COLOR
1080 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1081 /* coreutils 6.10:
1082 * # ls --color=BOGUS
1083 * ls: invalid argument 'BOGUS' for '--color'
1084 * Valid arguments are:
1085 * 'always', 'yes', 'force'
1086 * 'never', 'no', 'none'
1087 * 'auto', 'tty', 'if-tty'
1088 * (and substrings: "--color=alwa" work too)
1089 */
1090 static const char ls_longopts[] ALIGN1 =
1091 "color\0" Optional_argument "\xff"; /* no short equivalent */
1092 static const char color_str[] ALIGN1 =
1093 "always\0""yes\0""force\0"
1094 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001095 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001096 const char *color_opt = color_str; /* "always" */
1097#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001098
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001099 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001100
Denys Vlasenko28055022010-01-04 20:49:58 +01001101 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001102
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001103 if (ENABLE_FEATURE_LS_SORTFILES)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001104 G.all_fmt = SORT_NAME;
Glenn L McGrath792cae52004-01-18 05:15:16 +00001105
Denis Vlasenko5c759602006-10-28 12:37:16 +00001106#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001107 /* obtain the terminal width */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001108 get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001109 /* go one less... */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001110 G_terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001111#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001112
Eric Andersen11c65522000-09-07 17:24:47 +00001113 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001114 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001115 opt_complementary =
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001116 /* -e implies -l */
Denys Vlasenkob47b3ce2011-08-10 00:51:29 +02001117 IF_FEATURE_LS_TIMESTAMPS("el")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001118 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1119 * in some pairs of opts, only last one takes effect:
1120 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001121 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001122 // ":m-l:l-m" - we don't have -m
1123 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001124 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1125 ":C-1:1-C" /* bycols/oneline */
1126 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
Denys Vlasenkob47b3ce2011-08-10 00:51:29 +02001127 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001128 /* -w NUM: */
1129 IF_FEATURE_AUTOWIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001130 opt = getopt32(argv, ls_options
Denys Vlasenko2a816392011-05-13 17:28:09 +02001131 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001132 IF_FEATURE_LS_COLOR(, &color_opt)
1133 );
Denys Vlasenko982aa262010-12-19 21:54:39 +01001134 for (i = 0; opt_flags[i] != (1U << 31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001135 if (opt & (1 << i)) {
Denys Vlasenko982aa262010-12-19 21:54:39 +01001136 uint32_t flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001137
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001138 if (flags & STYLE_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001139 G.all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001140 if (flags & SORT_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001141 G.all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001142 if (flags & TIME_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001143 G.all_fmt &= ~TIME_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001144
Denys Vlasenko2a816392011-05-13 17:28:09 +02001145 G.all_fmt |= flags;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001146 }
Eric Andersen11c65522000-09-07 17:24:47 +00001147 }
1148
Denis Vlasenko5c759602006-10-28 12:37:16 +00001149#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +02001150 /* set G_show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001151 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1152 char *p = getenv("LS_COLORS");
1153 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001154 if (!p || (p[0] && strcmp(p, "none") != 0))
Denys Vlasenko2a816392011-05-13 17:28:09 +02001155 G_show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001156 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001157 if (opt & OPT_color) {
1158 if (color_opt[0] == 'n')
Denys Vlasenko2a816392011-05-13 17:28:09 +02001159 G_show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001160 else switch (index_in_substrings(color_str, color_opt)) {
1161 case 3:
1162 case 4:
1163 case 5:
1164 if (isatty(STDOUT_FILENO)) {
1165 case 0:
1166 case 1:
1167 case 2:
Denys Vlasenko2a816392011-05-13 17:28:09 +02001168 G_show_color = 1;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001169 }
1170 }
Paul Fox156dc412005-08-01 19:33:30 +00001171 }
1172#endif
1173
Eric Andersen11c65522000-09-07 17:24:47 +00001174 /* sort out which command line options take precedence */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001175 if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1176 G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001177 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
Denys Vlasenko2a816392011-05-13 17:28:09 +02001178 if (G.all_fmt & TIME_CHANGE)
1179 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1180 if (G.all_fmt & TIME_ACCESS)
1181 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
Rob Landleyea224be2006-06-18 20:20:07 +00001182 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001183 if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
1184 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001185
Denys Vlasenkof3137462010-12-19 05:05:34 +01001186 /* choose a display format if one was not already specified by an option */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001187 if (!(G.all_fmt & STYLE_MASK))
1188 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001189
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001190 argv += optind;
1191 if (!argv[0])
1192 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001193
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001194 if (argv[1])
Denys Vlasenko2a816392011-05-13 17:28:09 +02001195 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001196
Denis Vlasenko5c759602006-10-28 12:37:16 +00001197 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001198 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001199 nfiles = 0;
1200 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001201 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001202 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001203 !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1204 || (G.all_fmt & LIST_BLOCKS)
Denys Vlasenkoea684c62011-03-12 03:12:36 +01001205 || (option_mask32 & OPT_F)
1206 )
Denys Vlasenko982aa262010-12-19 21:54:39 +01001207 /* ... or if -H: */
1208 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001209 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001210 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001211 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001212 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001213 continue;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001214 /*cur->fname_allocated = 0; - already is */
1215 cur->dn_next = dn;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001216 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001217 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001218 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001219
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001220 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1221 if (nfiles == 0)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001222 return G.exit_code;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001223
Eric Andersen11c65522000-09-07 17:24:47 +00001224 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001225 * allocate memory for an array to hold dnode pointers
1226 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001227 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001228 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1229 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001230 dn = dn->dn_next;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001231 if (!dn)
1232 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001233 }
1234
Denys Vlasenko2a816392011-05-13 17:28:09 +02001235 if (G.all_fmt & DISP_NOLIST) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001236 sort_and_display_files(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001237 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001238 dnd = splitdnarray(dnp, SPLIT_DIR);
1239 dnf = splitdnarray(dnp, SPLIT_FILE);
1240 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001241 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001242 if (dnfiles > 0) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001243 sort_and_display_files(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001244 if (ENABLE_FEATURE_CLEAN_UP)
1245 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001246 }
1247 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001248 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001249 scan_and_display_dirs_recur(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001250 if (ENABLE_FEATURE_CLEAN_UP)
1251 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001252 }
1253 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001254
Rob Landley26314862006-05-02 19:46:52 +00001255 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001256 dfree(dnp);
Denys Vlasenko2a816392011-05-13 17:28:09 +02001257 return G.exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001258}