blob: 0f35c70d51df9f9cdedb5b0755716024e93c5d4a [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 */
Denis Vlasenko5e4fda02009-03-08 23:46:48 +00007/* [date unknown. Perhaps before year 2000]
Eric Andersencc8ed391999-10-05 16:24:54 +00008 * To achieve a small memory footprint, this version of 'ls' doesn't do any
9 * file sorting, and only has the most essential command line switches
Eric Andersen77d92682001-05-23 20:32:09 +000010 * (i.e., the ones I couldn't live without :-) All features which involve
Eric Andersencc8ed391999-10-05 16:24:54 +000011 * linking in substantial chunks of libc can be disabled.
12 *
13 * Although I don't really want to add new features to this program to
14 * keep it small, I *am* interested to receive bug fixes and ways to make
15 * it more portable.
16 *
17 * KNOWN BUGS:
Denys Vlasenko87c150c2009-10-03 01:14:15 +020018 * 1. hidden files can make column width too large
Erik Andersen9ffdaa62000-02-11 21:55:04 +000019 *
Eric Andersencc8ed391999-10-05 16:24:54 +000020 * NON-OPTIMAL BEHAVIOUR:
21 * 1. autowidth reads directories twice
22 * 2. if you do a short directory listing without filetype characters
23 * appended, there's no need to stat each one
24 * PORTABILITY:
25 * 1. requires lstat (BSD) - how do you do it without?
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000026 *
27 * [2009-03]
28 * ls sorts listing now, and supports almost all options.
Eric Andersencc8ed391999-10-05 16:24:54 +000029 */
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010030//config:config LS
31//config: bool "ls"
32//config: default y
33//config: help
34//config: ls is used to list the contents of directories.
35//config:
36//config:config FEATURE_LS_FILETYPES
37//config: bool "Enable filetyping options (-p and -F)"
38//config: default y
39//config: depends on LS
40//config: help
41//config: Enable the ls options (-p and -F).
42//config:
43//config:config FEATURE_LS_FOLLOWLINKS
44//config: bool "Enable symlinks dereferencing (-L)"
45//config: default y
46//config: depends on LS
47//config: help
48//config: Enable the ls option (-L).
49//config:
50//config:config FEATURE_LS_RECURSIVE
51//config: bool "Enable recursion (-R)"
52//config: default y
53//config: depends on LS
54//config: help
55//config: Enable the ls option (-R).
56//config:
57//config:config FEATURE_LS_SORTFILES
58//config: bool "Sort the file names"
59//config: default y
60//config: depends on LS
61//config: help
62//config: Allow ls to sort file names alphabetically.
63//config:
64//config:config FEATURE_LS_TIMESTAMPS
65//config: bool "Show file timestamps"
66//config: default y
67//config: depends on LS
68//config: help
69//config: Allow ls to display timestamps for files.
70//config:
71//config:config FEATURE_LS_USERNAME
72//config: bool "Show username/groupnames"
73//config: default y
74//config: depends on LS
75//config: help
76//config: Allow ls to display username/groupname for files.
77//config:
78//config:config FEATURE_LS_COLOR
79//config: bool "Allow use of color to identify file types"
80//config: default y
81//config: depends on LS && LONG_OPTS
82//config: help
83//config: This enables the --color option to ls.
84//config:
85//config:config FEATURE_LS_COLOR_IS_DEFAULT
86//config: bool "Produce colored ls output by default"
87//config: default y
88//config: depends on FEATURE_LS_COLOR
89//config: help
90//config: Saying yes here will turn coloring on by default,
91//config: even if no "--color" option is given to the ls command.
92//config: This is not recommended, since the colors are not
93//config: configurable, and the output may not be legible on
94//config: many output screens.
95
96//applet:IF_LS(APPLET_NOEXEC(ls, ls, BB_DIR_BIN, BB_SUID_DROP, ls))
97
98//kbuild:lib-$(CONFIG_LS) += ls.o
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010099
100//usage:#define ls_trivial_usage
101//usage: "[-1AaCxd"
Denys Vlasenko982aa262010-12-19 21:54:39 +0100102//usage: IF_FEATURE_LS_FOLLOWLINKS("LH")
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100103//usage: IF_FEATURE_LS_RECURSIVE("R")
104//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins"
105//usage: IF_FEATURE_LS_TIMESTAMPS("e")
106//usage: IF_FEATURE_HUMAN_READABLE("h")
107//usage: IF_FEATURE_LS_SORTFILES("rSXv")
108//usage: IF_FEATURE_LS_TIMESTAMPS("ctu")
109//usage: IF_SELINUX("kKZ") "]"
Denys Vlasenko279a7ac2011-05-12 18:44:51 +0200110//usage: IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..."
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100111//usage:#define ls_full_usage "\n\n"
112//usage: "List directory contents\n"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200113//usage: "\n -1 One column output"
114//usage: "\n -a Include entries which start with ."
115//usage: "\n -A Like -a, but exclude . and .."
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100116//usage: "\n -C List by columns"
117//usage: "\n -x List by lines"
118//usage: "\n -d List directory entries instead of contents"
119//usage: IF_FEATURE_LS_FOLLOWLINKS(
Denys Vlasenko982aa262010-12-19 21:54:39 +0100120//usage: "\n -L Follow symlinks"
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200121//usage: "\n -H Follow symlinks on command line"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100122//usage: )
123//usage: IF_FEATURE_LS_RECURSIVE(
124//usage: "\n -R Recurse"
125//usage: )
126//usage: IF_FEATURE_LS_FILETYPES(
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200127//usage: "\n -p Append / to dir entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100128//usage: "\n -F Append indicator (one of */=@|) to entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100129//usage: )
130//usage: "\n -l Long listing format"
131//usage: "\n -i List inode numbers"
132//usage: "\n -n List numeric UIDs and GIDs instead of names"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200133//usage: "\n -s List allocated blocks"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100134//usage: IF_FEATURE_LS_TIMESTAMPS(
135//usage: "\n -e List full date and time"
136//usage: )
137//usage: IF_FEATURE_HUMAN_READABLE(
138//usage: "\n -h List sizes in human readable format (1K 243M 2G)"
139//usage: )
140//usage: IF_FEATURE_LS_SORTFILES(
141//usage: "\n -r Sort in reverse order"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200142//usage: "\n -S Sort by size"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100143//usage: "\n -X Sort by extension"
144//usage: "\n -v Sort by version"
145//usage: )
146//usage: IF_FEATURE_LS_TIMESTAMPS(
147//usage: "\n -c With -l: sort by ctime"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200148//usage: "\n -t With -l: sort by mtime"
149//usage: "\n -u With -l: sort by atime"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100150//usage: )
151//usage: IF_SELINUX(
152//usage: "\n -k List security context"
153//usage: "\n -K List security context in long format"
154//usage: "\n -Z List security context and permission"
155//usage: )
156//usage: IF_FEATURE_AUTOWIDTH(
157//usage: "\n -w N Assume the terminal is N columns wide"
158//usage: )
159//usage: IF_FEATURE_LS_COLOR(
160//usage: "\n --color[={always,never,auto}] Control coloring"
161//usage: )
162
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000163#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200164#include "common_bufsiz.h"
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200165#include "unicode.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +0000166
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000167
Denis Vlasenko99912ca2007-04-10 15:43:37 +0000168/* This is a NOEXEC applet. Be very careful! */
169
Eric Andersenf1142c52001-02-20 06:16:29 +0000170
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000171#if ENABLE_FTPD
172/* ftpd uses ls, and without timestamps Mozilla won't understand
173 * ftpd's LIST output.
174 */
175# undef CONFIG_FEATURE_LS_TIMESTAMPS
176# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000177# undef IF_FEATURE_LS_TIMESTAMPS
178# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000179# define CONFIG_FEATURE_LS_TIMESTAMPS 1
180# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000181# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
182# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000183#endif
184
185
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000186enum {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000187TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000188
Denys Vlasenko982aa262010-12-19 21:54:39 +0100189SPLIT_FILE = 0,
Denys Vlasenko2a816392011-05-13 17:28:09 +0200190SPLIT_DIR = 1,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100191SPLIT_SUBDIR = 2,
192
Denys Vlasenko2a816392011-05-13 17:28:09 +0200193/* Bits in G.all_fmt: */
Eric Andersencc8ed391999-10-05 16:24:54 +0000194
Eric Andersen11c65522000-09-07 17:24:47 +0000195/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
196/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000197LIST_INO = 1 << 0,
198LIST_BLOCKS = 1 << 1,
199LIST_MODEBITS = 1 << 2,
200LIST_NLINKS = 1 << 3,
201LIST_ID_NAME = 1 << 4,
202LIST_ID_NUMERIC = 1 << 5,
203LIST_CONTEXT = 1 << 6,
204LIST_SIZE = 1 << 7,
James Youngmana4bc10c2010-12-20 01:36:16 +0100205LIST_DATE_TIME = 1 << 8,
206LIST_FULLTIME = 1 << 9,
207LIST_SYMLINK = 1 << 10,
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200208LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */
209LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */
210LIST_MASK = (LIST_CLASSIFY << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +0000211
212/* what files will be displayed */
James Youngmana4bc10c2010-12-20 01:36:16 +0100213DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */
214DISP_HIDDEN = 1 << 14, /* show filenames starting with . */
215DISP_DOT = 1 << 15, /* show . and .. */
216DISP_NOLIST = 1 << 16, /* show directory as itself, not contents */
217DISP_RECURSIVE = 1 << 17, /* show directory and everything below it */
218DISP_ROWS = 1 << 18, /* print across rows */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000219DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +0000220
Denys Vlasenko982aa262010-12-19 21:54:39 +0100221/* what is the overall style of the listing */
James Youngmana4bc10c2010-12-20 01:36:16 +0100222STYLE_COLUMNAR = 1 << 19, /* many records per line */
223STYLE_LONG = 2 << 19, /* one record per line, extended info */
224STYLE_SINGLE = 3 << 19, /* one record per line */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100225STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000226
Eric Andersen11c65522000-09-07 17:24:47 +0000227/* which of the three times will be used */
James Youngmana4bc10c2010-12-20 01:36:16 +0100228TIME_CHANGE = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100229TIME_ACCESS = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
James Youngmana4bc10c2010-12-20 01:36:16 +0100230TIME_MASK = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000231
Denys Vlasenko982aa262010-12-19 21:54:39 +0100232/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
James Youngmana4bc10c2010-12-20 01:36:16 +0100233SORT_REVERSE = 1 << 23,
Eric Andersencc8ed391999-10-05 16:24:54 +0000234
Denys Vlasenko982aa262010-12-19 21:54:39 +0100235SORT_NAME = 0, /* sort by file name */
James Youngmana4bc10c2010-12-20 01:36:16 +0100236SORT_SIZE = 1 << 24, /* sort by file size */
237SORT_ATIME = 2 << 24, /* sort by last access time */
238SORT_CTIME = 3 << 24, /* sort by last change time */
239SORT_MTIME = 4 << 24, /* sort by last modification time */
240SORT_VERSION = 5 << 24, /* sort by version */
241SORT_EXT = 6 << 24, /* sort by file name extension */
242SORT_DIR = 7 << 24, /* sort by file or directory */
243SORT_MASK = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000244
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000245LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
James Youngmana4bc10c2010-12-20 01:36:16 +0100246 LIST_DATE_TIME | LIST_SYMLINK,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000247};
Eric Andersencc8ed391999-10-05 16:24:54 +0000248
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100249/* -Cadil1 Std options, busybox always supports */
250/* -gnsxA Std options, busybox always supports */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100251/* -Q GNU option, busybox always supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100252/* -k SELinux option, busybox always supports (ignores if !SELinux) */
253/* Std has -k which means "show sizes in kbytes" */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200254/* -LHRctur Std options, busybox optionally supports */
255/* -Fp Std options, busybox optionally supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100256/* -SXvhTw GNU options, busybox optionally supports */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200257/* -T WIDTH Ignored (we don't use tabs on output) */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100258/* -KZ SELinux mandated options, busybox optionally supports */
259/* (coreutils 8.4 has no -K, remove it?) */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100260/* -e I think we made this one up (looks similar to GNU --full-time) */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100261/* We already used up all 32 bits, if we need to add more, candidates for removal: */
262/* -K, -T, -e (add --full-time instead) */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000263static const char ls_options[] ALIGN1 =
Denys Vlasenko982aa262010-12-19 21:54:39 +0100264 "Cadil1gnsxQAk" /* 13 opts, total 13 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000265 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
266 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
267 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100268 IF_FEATURE_LS_RECURSIVE("R") /* 1, 24 */
269 IF_SELINUX("KZ") /* 2, 26 */
270 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 28 */
271 IF_FEATURE_HUMAN_READABLE("h") /* 1, 29 */
272 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 31 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100273 /* with --color, we use all 32 bits */;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000274enum {
275 //OPT_C = (1 << 0),
276 //OPT_a = (1 << 1),
277 //OPT_d = (1 << 2),
278 //OPT_i = (1 << 3),
279 //OPT_l = (1 << 4),
280 //OPT_1 = (1 << 5),
281 OPT_g = (1 << 6),
282 //OPT_n = (1 << 7),
283 //OPT_s = (1 << 8),
284 //OPT_x = (1 << 9),
285 OPT_Q = (1 << 10),
286 //OPT_A = (1 << 11),
287 //OPT_k = (1 << 12),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100288
289 OPTBIT_c = 13,
290 OPTBIT_e,
291 OPTBIT_t,
292 OPTBIT_u,
293 OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS,
294 OPTBIT_X, /* 18 */
295 OPTBIT_r,
296 OPTBIT_v,
297 OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
298 OPTBIT_p, /* 22 */
299 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
300 OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
301 OPTBIT_Z, /* 25 */
302 OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX,
303 OPTBIT_H, /* 27 */
Denys Vlasenkocd387f22011-02-27 04:10:00 +0100304 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100305 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100306 OPTBIT_w, /* 30 */
307 OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH,
308
309 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
310 OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS,
311 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
312 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
313 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
314 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
315 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
316 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
317 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
318 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
319 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
320 OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX,
321 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
322 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
323 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
324 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
325 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH,
326 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH,
327 OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000328};
329
Denis Vlasenko248ce912009-03-03 14:09:04 +0000330/* TODO: simple toggles may be stored as OPT_xxx bits instead */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100331static const uint32_t opt_flags[] = {
Denys Vlasenko69675782013-01-14 01:34:48 +0100332 STYLE_COLUMNAR, /* C */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100333 DISP_HIDDEN | DISP_DOT, /* a */
334 DISP_NOLIST, /* d */
335 LIST_INO, /* i */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100336 LIST_LONG | STYLE_LONG, /* l */
James Youngmana4bc10c2010-12-20 01:36:16 +0100337 STYLE_SINGLE, /* 1 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100338 LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100339 LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100340 LIST_BLOCKS, /* s */
James Youngmana4bc10c2010-12-20 01:36:16 +0100341 DISP_ROWS | STYLE_COLUMNAR, /* x */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100342 0, /* Q (quote filename) - handled via OPT_Q */
343 DISP_HIDDEN, /* A */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100344 ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000345#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100346 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100347 LIST_FULLTIME, /* e */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100348 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
349 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000350#endif
351#if ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100352 SORT_SIZE, /* S */
353 SORT_EXT, /* X */
354 SORT_REVERSE, /* r */
355 SORT_VERSION, /* v */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000356#endif
357#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200358 LIST_FILETYPE | LIST_CLASSIFY, /* F */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100359 LIST_FILETYPE, /* p */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000360#endif
Denis Vlasenko248ce912009-03-03 14:09:04 +0000361#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100362 DISP_RECURSIVE, /* R */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000363#endif
Denis Vlasenko248ce912009-03-03 14:09:04 +0000364#if ENABLE_SELINUX
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100365 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */
366 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000367#endif
Denys Vlasenko982aa262010-12-19 21:54:39 +0100368 (1U << 31)
369 /* options after Z are not processed through opt_flags */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000370};
371
372
Eric Andersen11c65522000-09-07 17:24:47 +0000373/*
Denys Vlasenko2a816392011-05-13 17:28:09 +0200374 * a directory entry and its stat info
Eric Andersen11c65522000-09-07 17:24:47 +0000375 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200376struct dnode {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200377 const char *name; /* usually basename, but think "ls -l dir/file" */
378 const char *fullname; /* full name (usable for stat etc) */
379 struct dnode *dn_next; /* for linked list */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000380 IF_SELINUX(security_context_t sid;)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200381 smallint fname_allocated;
382
383 /* Used to avoid re-doing [l]stat at printout stage
384 * if we already collected needed data in scan stage:
385 */
386 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
387 mode_t dn_mode_stat; /* obtained with stat, or 0 */
388
389// struct stat dstat;
390// struct stat is huge. We don't need it in full.
391// At least we don't need st_dev and st_blksize,
392// but there are invisible fields as well
393// (such as nanosecond-resolution timespamps)
394// and padding, which we also don't want to store.
395// We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
Denys Vlasenko4029e212011-05-13 17:28:46 +0200396// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
Denys Vlasenko2a816392011-05-13 17:28:09 +0200397//
398 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
399 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
400 off_t dn_size;
401#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
402 time_t dn_atime;
403 time_t dn_mtime;
404 time_t dn_ctime;
405#endif
406 ino_t dn_ino;
407 blkcnt_t dn_blocks;
408 nlink_t dn_nlink;
409 uid_t dn_uid;
410 gid_t dn_gid;
411 int dn_rdev_maj;
412 int dn_rdev_min;
413// dev_t dn_dev;
414// blksize_t dn_blksize;
Eric Andersen11c65522000-09-07 17:24:47 +0000415};
Eric Andersen11c65522000-09-07 17:24:47 +0000416
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000417struct globals {
418#if ENABLE_FEATURE_LS_COLOR
419 smallint show_color;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200420# define G_show_color (G.show_color)
421#else
422# define G_show_color 0
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000423#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000424 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000425 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000426#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko2a816392011-05-13 17:28:09 +0200427 unsigned terminal_width;
428# define G_terminal_width (G.terminal_width)
429#else
430# define G_terminal_width TERMINAL_WIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000431#endif
432#if ENABLE_FEATURE_LS_TIMESTAMPS
433 /* Do time() just once. Saves one syscall per file for "ls -l" */
434 time_t current_time_t;
435#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100436} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200437#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000438#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200439 setup_common_bufsiz(); \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200440 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000441 memset(&G, 0, sizeof(G)); \
Denys Vlasenko2a816392011-05-13 17:28:09 +0200442 IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
443 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000444} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000445
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000446
Denys Vlasenko4029e212011-05-13 17:28:46 +0200447/*** Output code ***/
Denys Vlasenko2a816392011-05-13 17:28:09 +0200448
Eric Andersen79565b62000-08-11 18:10:21 +0000449
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000450/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
451 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
452 * 3/7:multiplexed char/block device)
453 * and we use 0 for unknown and 15 for executables (see below) */
454#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200455/* un fi chr - dir - blk - file - link - sock - - exe */
456#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000457/* 036 black foreground 050 black background
458 037 red foreground 051 red background
459 040 green foreground 052 green background
460 041 brown foreground 053 brown background
461 042 blue foreground 054 blue background
462 043 magenta (purple) foreground 055 magenta background
463 044 cyan (light blue) foreground 056 cyan background
464 045 gray foreground 057 white background
465*/
466#define COLOR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200467 /*un fi chr - dir - blk - file - link - sock - - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000468 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
469 [TYPEINDEX(mode)])
470/* Select normal (0) [actually "reset all"] or bold (1)
471 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
472 * let's use 7 for "impossible" types, just for fun)
473 * Note: coreutils 6.9 uses inverted red for setuid binaries.
474 */
475#define ATTR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200476 /*un fi chr - dir - blk - file- link- sock- - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000477 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
478 [TYPEINDEX(mode)])
479
Denis Vlasenko5c759602006-10-28 12:37:16 +0000480#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000481/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000482static char fgcolor(mode_t mode)
483{
Rob Landley9947a242006-06-15 22:11:10 +0000484 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000485 return COLOR(0xF000); /* File is executable ... */
486 return COLOR(mode);
487}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000488static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000489{
Rob Landley9947a242006-06-15 22:11:10 +0000490 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000491 return ATTR(0xF000); /* File is executable ... */
492 return ATTR(mode);
493}
494#endif
495
Denys Vlasenko2a816392011-05-13 17:28:09 +0200496#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000497static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000498{
Denys Vlasenko2a816392011-05-13 17:28:09 +0200499 if (!(G.all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000500 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000501 if (S_ISDIR(mode))
502 return '/';
Denys Vlasenko2a816392011-05-13 17:28:09 +0200503 if (!(G.all_fmt & LIST_CLASSIFY))
Rob Landley9947a242006-06-15 22:11:10 +0000504 return '\0';
505 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000506 return '*';
507 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000508}
509#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000510
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100511static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000512{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100513 unsigned len;
514 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000515
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100516 // TODO: quote tab as \t, etc, if -Q
517 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000518
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100519 if (!(option_mask32 & OPT_Q)) {
520 return uni_stat.unicode_width;
521 }
522
523 len = 2 + uni_stat.unicode_width;
524 while (*name) {
525 if (*name == '"' || *name == '\\') {
526 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000527 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100528 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000529 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100530 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000531}
532
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100533/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100534 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100535 * STYLE_SINGLE and STYLE_LONG don't care.
536 * coreutils 7.2 also supports:
537 * ls -b (--escape) = octal escapes (although it doesn't look like working)
538 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200539 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100540static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200541{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100542 unsigned len;
543 uni_stat_t uni_stat;
544
545 // TODO: quote tab as \t, etc, if -Q
546 name = printable_string(&uni_stat, name);
547
548 if (!(option_mask32 & OPT_Q)) {
549 fputs(name, stdout);
550 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200551 }
552
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100553 len = 2 + uni_stat.unicode_width;
554 putchar('"');
555 while (*name) {
556 if (*name == '"' || *name == '\\') {
557 putchar('\\');
558 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000559 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700560 putchar(*name);
561 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000562 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100563 putchar('"');
564 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000565}
566
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100567/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100568 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100569 * STYLE_SINGLE and STYLE_LONG don't care.
570 */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200571static NOINLINE unsigned display_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000572{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200573 unsigned column = 0;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200574 char *lpath;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000575#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200576 struct stat statbuf;
Eric Andersen11c65522000-09-07 17:24:47 +0000577 char append;
578#endif
579
Denis Vlasenko5c759602006-10-28 12:37:16 +0000580#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200581 append = append_char(dn->dn_mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000582#endif
583
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000584 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100585 * does not appear *inside* the "ls -l" line */
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200586 lpath = NULL;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200587 if (G.all_fmt & LIST_SYMLINK)
588 if (S_ISLNK(dn->dn_mode))
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000589 lpath = xmalloc_readlink_or_warn(dn->fullname);
590
Denys Vlasenko2a816392011-05-13 17:28:09 +0200591 if (G.all_fmt & LIST_INO)
592 column += printf("%7llu ", (long long) dn->dn_ino);
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100593//TODO: -h should affect -s too:
Denys Vlasenko2a816392011-05-13 17:28:09 +0200594 if (G.all_fmt & LIST_BLOCKS)
595 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
596 if (G.all_fmt & LIST_MODEBITS)
597 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
598 if (G.all_fmt & LIST_NLINKS)
599 column += printf("%4lu ", (long) dn->dn_nlink);
600 if (G.all_fmt & LIST_ID_NUMERIC) {
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100601 if (option_mask32 & OPT_g)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200602 column += printf("%-8u ", (int) dn->dn_gid);
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100603 else
604 column += printf("%-8u %-8u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200605 (int) dn->dn_uid,
606 (int) dn->dn_gid);
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100607 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000608#if ENABLE_FEATURE_LS_USERNAME
Denys Vlasenko2a816392011-05-13 17:28:09 +0200609 else if (G.all_fmt & LIST_ID_NAME) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000610 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100611 column += printf("%-8.8s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200612 get_cached_groupname(dn->dn_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000613 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100614 column += printf("%-8.8s %-8.8s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200615 get_cached_username(dn->dn_uid),
616 get_cached_groupname(dn->dn_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000617 }
618 }
Eric Andersen11c65522000-09-07 17:24:47 +0000619#endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200620 if (G.all_fmt & LIST_SIZE) {
621 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000622 column += printf("%4u, %3u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200623 dn->dn_rdev_maj,
624 dn->dn_rdev_min);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000625 } else {
Denys Vlasenko982aa262010-12-19 21:54:39 +0100626 if (option_mask32 & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100627 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200628 /* print size, show one fractional, use suffixes */
629 make_human_readable_str(dn->dn_size, 1, 0)
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200630 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000631 } else {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200632 column += printf("%9"OFF_FMT"u ", dn->dn_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000633 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000634 }
635 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000636#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko2a816392011-05-13 17:28:09 +0200637 if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100638 char *filetime;
Denys Vlasenkoc5beaa02015-02-23 15:25:58 +0100639 const time_t *ttime = &dn->dn_mtime;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200640 if (G.all_fmt & TIME_ACCESS)
Denys Vlasenko0cb981c2015-02-18 11:29:07 +0100641 ttime = &dn->dn_atime;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200642 if (G.all_fmt & TIME_CHANGE)
Denys Vlasenko0cb981c2015-02-18 11:29:07 +0100643 ttime = &dn->dn_ctime;
644 filetime = ctime(ttime);
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100645 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200646 if (G.all_fmt & LIST_FULLTIME) { /* -e */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100647 /* Note: coreutils 8.4 ls --full-time prints:
648 * 2009-07-13 17:49:27.000000000 +0200
649 */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100650 column += printf("%.24s ", filetime);
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100651 } else { /* LIST_DATE_TIME */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200652 /* G.current_time_t ~== time(NULL) */
Denys Vlasenko0cb981c2015-02-18 11:29:07 +0100653 time_t age = G.current_time_t - *ttime;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000654 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
Denys Vlasenko8e92df12015-02-16 15:36:25 +0100655 /* less than 6 months old */
656 /* "mmm dd hh:mm " */
657 printf("%.12s ", filetime + 4);
658 } else {
659 /* "mmm dd yyyy " */
660 /* "mmm dd yyyyy " after year 9999 :) */
661 strchr(filetime + 20, '\n')[0] = ' ';
662 printf("%.7s%6s", filetime + 4, filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000663 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000664 column += 13;
665 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100666 }
Eric Andersen11c65522000-09-07 17:24:47 +0000667#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000668#if ENABLE_SELINUX
Denys Vlasenko2a816392011-05-13 17:28:09 +0200669 if (G.all_fmt & LIST_CONTEXT) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000670 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
671 freecon(dn->sid);
672 }
Eric Andersen9e480452003-07-03 10:07:04 +0000673#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100674
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000675#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200676 if (G_show_color) {
677 mode_t mode = dn->dn_mode_lstat;
678 if (!mode)
679 if (lstat(dn->fullname, &statbuf) == 0)
680 mode = statbuf.st_mode;
681 printf("\033[%u;%um", bold(mode), fgcolor(mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000682 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100683#endif
684 column += print_name(dn->name);
Denys Vlasenko2a816392011-05-13 17:28:09 +0200685 if (G_show_color) {
James Youngmana4bc10c2010-12-20 01:36:16 +0100686 printf("\033[0m");
687 }
688
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200689 if (lpath) {
690 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000691#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200692 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
693 mode_t mode = dn->dn_mode_stat;
694 if (!mode)
695 if (stat(dn->fullname, &statbuf) == 0)
696 mode = statbuf.st_mode;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200697# if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200698 append = append_char(mode);
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200699# endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200700# if ENABLE_FEATURE_LS_COLOR
701 if (G_show_color) {
702 printf("\033[%u;%um", bold(mode), fgcolor(mode));
703 }
704# endif
Eric Andersen11c65522000-09-07 17:24:47 +0000705 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200706#endif
707 column += print_name(lpath) + 4;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200708 free(lpath);
709 if (G_show_color) {
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200710 printf("\033[0m");
711 }
Eric Andersen11c65522000-09-07 17:24:47 +0000712 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000713#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200714 if (G.all_fmt & LIST_FILETYPE) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000715 if (append) {
716 putchar(append);
717 column++;
718 }
719 }
720#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000721
Glenn L McGrath4d001292003-01-06 01:11:50 +0000722 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000723}
724
Denys Vlasenko4029e212011-05-13 17:28:46 +0200725static void display_files(struct dnode **dn, unsigned nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100726{
727 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100728 unsigned column;
729 unsigned nexttab;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100730 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100731
Denys Vlasenko2a816392011-05-13 17:28:09 +0200732 if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100733 ncols = 1;
734 } else {
735 /* find the longest file name, use that as the column width */
736 for (i = 0; dn[i]; i++) {
737 int len = calc_name_len(dn[i]->name);
738 if (column_width < len)
739 column_width = len;
740 }
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100741 column_width += 2 +
Denys Vlasenko2a816392011-05-13 17:28:09 +0200742 IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
743 ((G.all_fmt & LIST_INO) ? 8 : 0) +
744 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
745 ncols = (unsigned)G_terminal_width / column_width;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100746 }
747
748 if (ncols > 1) {
749 nrows = nfiles / ncols;
750 if (nrows * ncols < nfiles)
751 nrows++; /* round up fractionals */
752 } else {
753 nrows = nfiles;
754 ncols = 1;
755 }
756
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100757 column = 0;
758 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100759 for (row = 0; row < nrows; row++) {
760 for (nc = 0; nc < ncols; nc++) {
761 /* reach into the array based on the column and row */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200762 if (G.all_fmt & DISP_ROWS)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100763 i = (row * ncols) + nc; /* display across row */
764 else
765 i = (nc * nrows) + row; /* display by column */
766 if (i < nfiles) {
767 if (column > 0) {
768 nexttab -= column;
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100769 printf("%*s", nexttab, "");
770 column += nexttab;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100771 }
772 nexttab = column + column_width;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200773 column += display_single(dn[i]);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100774 }
775 }
776 putchar('\n');
777 column = 0;
778 }
779}
780
781
Denys Vlasenko4029e212011-05-13 17:28:46 +0200782/*** Dir scanning code ***/
783
784static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100785{
Denys Vlasenko4029e212011-05-13 17:28:46 +0200786 struct stat statbuf;
787 struct dnode *cur;
788
789 cur = xzalloc(sizeof(*cur));
790 cur->fullname = fullname;
791 cur->name = name;
792
793 if ((option_mask32 & OPT_L) || force_follow) {
794#if ENABLE_SELINUX
795 if (is_selinux_enabled()) {
Denys Vlasenko69675782013-01-14 01:34:48 +0100796 getfilecon(fullname, &cur->sid);
Denys Vlasenko4029e212011-05-13 17:28:46 +0200797 }
798#endif
799 if (stat(fullname, &statbuf)) {
800 bb_simple_perror_msg(fullname);
801 G.exit_code = EXIT_FAILURE;
802 free(cur);
803 return NULL;
804 }
805 cur->dn_mode_stat = statbuf.st_mode;
806 } else {
807#if ENABLE_SELINUX
808 if (is_selinux_enabled()) {
809 lgetfilecon(fullname, &cur->sid);
810 }
811#endif
812 if (lstat(fullname, &statbuf)) {
813 bb_simple_perror_msg(fullname);
814 G.exit_code = EXIT_FAILURE;
815 free(cur);
816 return NULL;
817 }
818 cur->dn_mode_lstat = statbuf.st_mode;
819 }
820
821 /* cur->dstat = statbuf: */
822 cur->dn_mode = statbuf.st_mode ;
823 cur->dn_size = statbuf.st_size ;
824#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
825 cur->dn_atime = statbuf.st_atime ;
826 cur->dn_mtime = statbuf.st_mtime ;
827 cur->dn_ctime = statbuf.st_ctime ;
828#endif
829 cur->dn_ino = statbuf.st_ino ;
830 cur->dn_blocks = statbuf.st_blocks;
831 cur->dn_nlink = statbuf.st_nlink ;
832 cur->dn_uid = statbuf.st_uid ;
833 cur->dn_gid = statbuf.st_gid ;
834 cur->dn_rdev_maj = major(statbuf.st_rdev);
835 cur->dn_rdev_min = minor(statbuf.st_rdev);
836
837 return cur;
838}
839
840static unsigned count_dirs(struct dnode **dn, int which)
841{
842 unsigned dirs, all;
843
844 if (!dn)
845 return 0;
846
847 dirs = all = 0;
848 for (; *dn; dn++) {
849 const char *name;
850
851 all++;
852 if (!S_ISDIR((*dn)->dn_mode))
853 continue;
854
855 name = (*dn)->name;
856 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
857 /* or if it's not . or .. */
858 || name[0] != '.'
859 || (name[1] && (name[1] != '.' || name[2]))
860 ) {
861 dirs++;
862 }
863 }
864 return which != SPLIT_FILE ? dirs : all - dirs;
865}
866
867/* get memory to hold an array of pointers */
868static struct dnode **dnalloc(unsigned num)
869{
870 if (num < 1)
871 return NULL;
872
873 num++; /* so that we have terminating NULL */
874 return xzalloc(num * sizeof(struct dnode *));
875}
876
877#if ENABLE_FEATURE_LS_RECURSIVE
878static void dfree(struct dnode **dnp)
879{
880 unsigned i;
881
882 if (dnp == NULL)
883 return;
884
885 for (i = 0; dnp[i]; i++) {
886 struct dnode *cur = dnp[i];
887 if (cur->fname_allocated)
888 free((char*)cur->fullname);
889 free(cur);
890 }
891 free(dnp);
892}
893#else
894#define dfree(...) ((void)0)
895#endif
896
897/* Returns NULL-terminated malloced vector of pointers (or NULL) */
898static struct dnode **splitdnarray(struct dnode **dn, int which)
899{
900 unsigned dncnt, d;
901 struct dnode **dnp;
902
903 if (dn == NULL)
904 return NULL;
905
906 /* count how many dirs or files there are */
907 dncnt = count_dirs(dn, which);
908
909 /* allocate a file array and a dir array */
910 dnp = dnalloc(dncnt);
911
912 /* copy the entrys into the file or dir array */
913 for (d = 0; *dn; dn++) {
914 if (S_ISDIR((*dn)->dn_mode)) {
915 const char *name;
916
917 if (which == SPLIT_FILE)
918 continue;
919
920 name = (*dn)->name;
921 if ((which & SPLIT_DIR) /* any dir... */
922 /* ... or not . or .. */
923 || name[0] != '.'
924 || (name[1] && (name[1] != '.' || name[2]))
925 ) {
926 dnp[d++] = *dn;
927 }
928 } else
929 if (which == SPLIT_FILE) {
930 dnp[d++] = *dn;
931 }
932 }
933 return dnp;
934}
935
936#if ENABLE_FEATURE_LS_SORTFILES
937static int sortcmp(const void *a, const void *b)
938{
939 struct dnode *d1 = *(struct dnode **)a;
940 struct dnode *d2 = *(struct dnode **)b;
941 unsigned sort_opts = G.all_fmt & SORT_MASK;
942 off_t dif;
943
944 dif = 0; /* assume SORT_NAME */
945 // TODO: use pre-initialized function pointer
946 // instead of branch forest
947 if (sort_opts == SORT_SIZE) {
948 dif = (d2->dn_size - d1->dn_size);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200949 } else
950 if (sort_opts == SORT_ATIME) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200951 dif = (d2->dn_atime - d1->dn_atime);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200952 } else
953 if (sort_opts == SORT_CTIME) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200954 dif = (d2->dn_ctime - d1->dn_ctime);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200955 } else
956 if (sort_opts == SORT_MTIME) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200957 dif = (d2->dn_mtime - d1->dn_mtime);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200958 } else
959 if (sort_opts == SORT_DIR) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200960 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200961 } else
Denys Vlasenko1e18a012011-06-21 17:12:52 +0200962#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200963 if (sort_opts == SORT_VERSION) {
964 dif = strverscmp(d1->name, d2->name);
965 } else
Denys Vlasenko561f9c82011-06-21 16:38:29 +0200966#endif
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200967 if (sort_opts == SORT_EXT) {
968 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
Denys Vlasenko4029e212011-05-13 17:28:46 +0200969 }
970 if (dif == 0) {
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200971 /* sort by name, use as tie breaker for other sorts */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200972 if (ENABLE_LOCALE_SUPPORT)
973 dif = strcoll(d1->name, d2->name);
974 else
975 dif = strcmp(d1->name, d2->name);
976 }
977
978 /* Make dif fit into an int */
979 if (sizeof(dif) > sizeof(int)) {
980 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
981 /* shift leaving only "int" worth of bits */
982 if (dif != 0) {
983 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100984 }
985 }
986
Denys Vlasenko4029e212011-05-13 17:28:46 +0200987 return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100988}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200989
990static void dnsort(struct dnode **dn, int size)
991{
992 qsort(dn, size, sizeof(*dn), sortcmp);
993}
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200994
995static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
996{
997 dnsort(dn, nfiles);
998 display_files(dn, nfiles);
999}
Denys Vlasenko4029e212011-05-13 17:28:46 +02001000#else
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001001# define dnsort(dn, size) ((void)0)
1002# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +01001003#endif
1004
Denys Vlasenkod8528b82010-01-31 05:15:38 +01001005/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko4029e212011-05-13 17:28:46 +02001006static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
Denys Vlasenkod8528b82010-01-31 05:15:38 +01001007{
1008 struct dnode *dn, *cur, **dnp;
1009 struct dirent *entry;
1010 DIR *dir;
1011 unsigned i, nfiles;
1012
Denys Vlasenkod8528b82010-01-31 05:15:38 +01001013 *nfiles_p = 0;
1014 dir = warn_opendir(path);
1015 if (dir == NULL) {
Denys Vlasenko2a816392011-05-13 17:28:09 +02001016 G.exit_code = EXIT_FAILURE;
Denys Vlasenkod8528b82010-01-31 05:15:38 +01001017 return NULL; /* could not open the dir */
1018 }
1019 dn = NULL;
1020 nfiles = 0;
1021 while ((entry = readdir(dir)) != NULL) {
1022 char *fullname;
1023
1024 /* are we going to list the file- it may be . or .. or a hidden file */
1025 if (entry->d_name[0] == '.') {
1026 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
Denys Vlasenko2a816392011-05-13 17:28:09 +02001027 && !(G.all_fmt & DISP_DOT)
Denys Vlasenkod8528b82010-01-31 05:15:38 +01001028 ) {
1029 continue;
1030 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001031 if (!(G.all_fmt & DISP_HIDDEN))
Denys Vlasenkod8528b82010-01-31 05:15:38 +01001032 continue;
1033 }
1034 fullname = concat_path_file(path, entry->d_name);
1035 cur = my_stat(fullname, bb_basename(fullname), 0);
1036 if (!cur) {
1037 free(fullname);
1038 continue;
1039 }
1040 cur->fname_allocated = 1;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001041 cur->dn_next = dn;
Denys Vlasenkod8528b82010-01-31 05:15:38 +01001042 dn = cur;
1043 nfiles++;
1044 }
1045 closedir(dir);
1046
1047 if (dn == NULL)
1048 return NULL;
1049
1050 /* now that we know how many files there are
1051 * allocate memory for an array to hold dnode pointers
1052 */
1053 *nfiles_p = nfiles;
1054 dnp = dnalloc(nfiles);
1055 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1056 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001057 dn = dn->dn_next;
Denys Vlasenkod8528b82010-01-31 05:15:38 +01001058 if (!dn)
1059 break;
1060 }
1061
1062 return dnp;
1063}
1064
Denys Vlasenko4029e212011-05-13 17:28:46 +02001065#if ENABLE_DESKTOP
1066/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
1067 * If any of the -l, -n, -s options is specified, each list
1068 * of files within the directory shall be preceded by a
1069 * status line indicating the number of file system blocks
1070 * occupied by files in the directory in 512-byte units if
1071 * the -k option is not specified, or 1024-byte units if the
1072 * -k option is specified, rounded up to the next integral
1073 * number of units.
1074 */
1075/* by Jorgen Overgaard (jorgen AT antistaten.se) */
1076static off_t calculate_blocks(struct dnode **dn)
1077{
1078 uoff_t blocks = 1;
1079 if (dn) {
1080 while (*dn) {
1081 /* st_blocks is in 512 byte blocks */
1082 blocks += (*dn)->dn_blocks;
1083 dn++;
1084 }
1085 }
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001086
Denys Vlasenko4029e212011-05-13 17:28:46 +02001087 /* Even though standard says use 512 byte blocks, coreutils use 1k */
1088 /* Actually, we round up by calculating (blocks + 1) / 2,
1089 * "+ 1" was done when we initialized blocks to 1 */
1090 return blocks >> 1;
1091}
1092#endif
1093
1094static void scan_and_display_dirs_recur(struct dnode **dn, int first)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001095{
1096 unsigned nfiles;
1097 struct dnode **subdnp;
1098
1099 for (; *dn; dn++) {
Denys Vlasenko2a816392011-05-13 17:28:09 +02001100 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001101 if (!first)
1102 bb_putchar('\n');
1103 first = 0;
1104 printf("%s:\n", (*dn)->fullname);
1105 }
Denys Vlasenko4029e212011-05-13 17:28:46 +02001106 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001107#if ENABLE_DESKTOP
Denys Vlasenkofca0ee52014-02-27 15:40:26 +01001108 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS))
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001109 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1110#endif
1111 if (nfiles > 0) {
1112 /* list all files at this level */
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001113 sort_and_display_files(subdnp, nfiles);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001114
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001115 if (ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko2a816392011-05-13 17:28:09 +02001116 && (G.all_fmt & DISP_RECURSIVE)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001117 ) {
1118 struct dnode **dnd;
1119 unsigned dndirs;
1120 /* recursive - list the sub-dirs */
1121 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1122 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1123 if (dndirs > 0) {
1124 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001125 scan_and_display_dirs_recur(dnd, 0);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001126 /* free the array of dnode pointers to the dirs */
1127 free(dnd);
1128 }
1129 }
1130 /* free the dnodes and the fullname mem */
1131 dfree(subdnp);
1132 }
1133 }
1134}
1135
1136
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001137int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +00001138{
Glenn L McGrath792cae52004-01-18 05:15:16 +00001139 struct dnode **dnd;
1140 struct dnode **dnf;
1141 struct dnode **dnp;
1142 struct dnode *dn;
1143 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001144 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001145 unsigned nfiles;
1146 unsigned dnfiles;
1147 unsigned dndirs;
1148 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001149#if ENABLE_FEATURE_LS_COLOR
1150 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1151 /* coreutils 6.10:
1152 * # ls --color=BOGUS
1153 * ls: invalid argument 'BOGUS' for '--color'
1154 * Valid arguments are:
1155 * 'always', 'yes', 'force'
1156 * 'never', 'no', 'none'
1157 * 'auto', 'tty', 'if-tty'
1158 * (and substrings: "--color=alwa" work too)
1159 */
1160 static const char ls_longopts[] ALIGN1 =
1161 "color\0" Optional_argument "\xff"; /* no short equivalent */
1162 static const char color_str[] ALIGN1 =
1163 "always\0""yes\0""force\0"
1164 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001165 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001166 const char *color_opt = color_str; /* "always" */
1167#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001168
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001169 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001170
Denys Vlasenko28055022010-01-04 20:49:58 +01001171 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001172
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001173 if (ENABLE_FEATURE_LS_SORTFILES)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001174 G.all_fmt = SORT_NAME;
Glenn L McGrath792cae52004-01-18 05:15:16 +00001175
Denis Vlasenko5c759602006-10-28 12:37:16 +00001176#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001177 /* obtain the terminal width */
Denys Vlasenko641caae2015-10-23 01:44:22 +02001178 G_terminal_width = get_terminal_width(STDIN_FILENO);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001179 /* go one less... */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001180 G_terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001181#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001182
Eric Andersen11c65522000-09-07 17:24:47 +00001183 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001184 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001185 opt_complementary =
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001186 /* -e implies -l */
Denys Vlasenkob47b3ce2011-08-10 00:51:29 +02001187 IF_FEATURE_LS_TIMESTAMPS("el")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001188 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1189 * in some pairs of opts, only last one takes effect:
1190 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001191 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001192 // ":m-l:l-m" - we don't have -m
1193 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001194 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1195 ":C-1:1-C" /* bycols/oneline */
1196 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
Denys Vlasenkob47b3ce2011-08-10 00:51:29 +02001197 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001198 /* -w NUM: */
1199 IF_FEATURE_AUTOWIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001200 opt = getopt32(argv, ls_options
Denys Vlasenko2a816392011-05-13 17:28:09 +02001201 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001202 IF_FEATURE_LS_COLOR(, &color_opt)
1203 );
Denys Vlasenko982aa262010-12-19 21:54:39 +01001204 for (i = 0; opt_flags[i] != (1U << 31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001205 if (opt & (1 << i)) {
Denys Vlasenko982aa262010-12-19 21:54:39 +01001206 uint32_t flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001207
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001208 if (flags & STYLE_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001209 G.all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001210 if (flags & SORT_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001211 G.all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001212 if (flags & TIME_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001213 G.all_fmt &= ~TIME_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001214
Denys Vlasenko2a816392011-05-13 17:28:09 +02001215 G.all_fmt |= flags;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001216 }
Eric Andersen11c65522000-09-07 17:24:47 +00001217 }
1218
Denis Vlasenko5c759602006-10-28 12:37:16 +00001219#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +02001220 /* set G_show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001221 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1222 char *p = getenv("LS_COLORS");
1223 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001224 if (!p || (p[0] && strcmp(p, "none") != 0))
Denys Vlasenko2a816392011-05-13 17:28:09 +02001225 G_show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001226 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001227 if (opt & OPT_color) {
1228 if (color_opt[0] == 'n')
Denys Vlasenko2a816392011-05-13 17:28:09 +02001229 G_show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001230 else switch (index_in_substrings(color_str, color_opt)) {
1231 case 3:
1232 case 4:
1233 case 5:
1234 if (isatty(STDOUT_FILENO)) {
1235 case 0:
1236 case 1:
1237 case 2:
Denys Vlasenko2a816392011-05-13 17:28:09 +02001238 G_show_color = 1;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001239 }
1240 }
Paul Fox156dc412005-08-01 19:33:30 +00001241 }
1242#endif
1243
Eric Andersen11c65522000-09-07 17:24:47 +00001244 /* sort out which command line options take precedence */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001245 if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1246 G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001247 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
Denys Vlasenko2a816392011-05-13 17:28:09 +02001248 if (G.all_fmt & TIME_CHANGE)
1249 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1250 if (G.all_fmt & TIME_ACCESS)
1251 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
Rob Landleyea224be2006-06-18 20:20:07 +00001252 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001253 if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
1254 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001255
Denys Vlasenkof3137462010-12-19 05:05:34 +01001256 /* choose a display format if one was not already specified by an option */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001257 if (!(G.all_fmt & STYLE_MASK))
1258 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001259
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001260 argv += optind;
1261 if (!argv[0])
1262 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001263
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001264 if (argv[1])
Denys Vlasenko2a816392011-05-13 17:28:09 +02001265 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001266
Denis Vlasenko5c759602006-10-28 12:37:16 +00001267 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001268 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001269 nfiles = 0;
1270 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001271 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001272 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001273 !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1274 || (G.all_fmt & LIST_BLOCKS)
Denys Vlasenkoea684c62011-03-12 03:12:36 +01001275 || (option_mask32 & OPT_F)
1276 )
Denys Vlasenko982aa262010-12-19 21:54:39 +01001277 /* ... or if -H: */
1278 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001279 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001280 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001281 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001282 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001283 continue;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001284 /*cur->fname_allocated = 0; - already is */
1285 cur->dn_next = dn;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001286 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001287 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001288 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001289
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001290 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1291 if (nfiles == 0)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001292 return G.exit_code;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001293
Eric Andersen11c65522000-09-07 17:24:47 +00001294 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001295 * allocate memory for an array to hold dnode pointers
1296 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001297 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001298 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1299 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001300 dn = dn->dn_next;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001301 if (!dn)
1302 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001303 }
1304
Denys Vlasenko2a816392011-05-13 17:28:09 +02001305 if (G.all_fmt & DISP_NOLIST) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001306 sort_and_display_files(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001307 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001308 dnd = splitdnarray(dnp, SPLIT_DIR);
1309 dnf = splitdnarray(dnp, SPLIT_FILE);
1310 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001311 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001312 if (dnfiles > 0) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001313 sort_and_display_files(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001314 if (ENABLE_FEATURE_CLEAN_UP)
1315 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001316 }
1317 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001318 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001319 scan_and_display_dirs_recur(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001320 if (ENABLE_FEATURE_CLEAN_UP)
1321 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001322 }
1323 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001324
Rob Landley26314862006-05-02 19:46:52 +00001325 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001326 dfree(dnp);
Denys Vlasenko2a816392011-05-13 17:28:09 +02001327 return G.exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001328}