blob: ac142c36b9a86182638207057a6b05d5215ed932 [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
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010040//config:
41//config:config FEATURE_LS_FOLLOWLINKS
42//config: bool "Enable symlinks dereferencing (-L)"
43//config: default y
44//config: depends on LS
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010045//config:
46//config:config FEATURE_LS_RECURSIVE
47//config: bool "Enable recursion (-R)"
48//config: default y
49//config: depends on LS
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010050//config:
Denys Vlasenkoed15dde2017-01-11 16:35:52 +010051//config:config FEATURE_LS_WIDTH
52//config: bool "Enable -w WIDTH and window size autodetection"
53//config: default y
54//config: depends on LS
55//config:
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010056//config:config FEATURE_LS_SORTFILES
57//config: bool "Sort the file names"
58//config: default y
59//config: depends on LS
60//config: help
61//config: Allow ls to sort file names alphabetically.
62//config:
63//config:config FEATURE_LS_TIMESTAMPS
64//config: bool "Show file timestamps"
65//config: default y
66//config: depends on LS
67//config: help
68//config: Allow ls to display timestamps for files.
69//config:
70//config:config FEATURE_LS_USERNAME
71//config: bool "Show username/groupnames"
72//config: default y
73//config: depends on LS
74//config: help
75//config: Allow ls to display username/groupname for files.
76//config:
77//config:config FEATURE_LS_COLOR
78//config: bool "Allow use of color to identify file types"
79//config: default y
80//config: depends on LS && LONG_OPTS
81//config: help
82//config: This enables the --color option to ls.
83//config:
84//config:config FEATURE_LS_COLOR_IS_DEFAULT
85//config: bool "Produce colored ls output by default"
86//config: default y
87//config: depends on FEATURE_LS_COLOR
88//config: help
89//config: Saying yes here will turn coloring on by default,
90//config: even if no "--color" option is given to the ls command.
91//config: This is not recommended, since the colors are not
92//config: configurable, and the output may not be legible on
93//config: many output screens.
94
95//applet:IF_LS(APPLET_NOEXEC(ls, ls, BB_DIR_BIN, BB_SUID_DROP, ls))
96
97//kbuild:lib-$(CONFIG_LS) += ls.o
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010098
99//usage:#define ls_trivial_usage
100//usage: "[-1AaCxd"
Denys Vlasenko982aa262010-12-19 21:54:39 +0100101//usage: IF_FEATURE_LS_FOLLOWLINKS("LH")
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100102//usage: IF_FEATURE_LS_RECURSIVE("R")
103//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100104//usage: IF_FEATURE_HUMAN_READABLE("h")
105//usage: IF_FEATURE_LS_SORTFILES("rSXv")
106//usage: IF_FEATURE_LS_TIMESTAMPS("ctu")
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100107//usage: IF_SELINUX("kZ") "]"
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100108//usage: IF_FEATURE_LS_WIDTH(" [-w WIDTH]") " [FILE]..."
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100109//usage:#define ls_full_usage "\n\n"
110//usage: "List directory contents\n"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200111//usage: "\n -1 One column output"
112//usage: "\n -a Include entries which start with ."
113//usage: "\n -A Like -a, but exclude . and .."
Denys Vlasenko92c68982017-01-23 20:21:14 +0100114////usage: "\n -C List by columns" - don't show, this is a default anyway
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100115//usage: "\n -x List by lines"
116//usage: "\n -d List directory entries instead of contents"
117//usage: IF_FEATURE_LS_FOLLOWLINKS(
Denys Vlasenko982aa262010-12-19 21:54:39 +0100118//usage: "\n -L Follow symlinks"
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200119//usage: "\n -H Follow symlinks on command line"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100120//usage: )
121//usage: IF_FEATURE_LS_RECURSIVE(
122//usage: "\n -R Recurse"
123//usage: )
124//usage: IF_FEATURE_LS_FILETYPES(
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200125//usage: "\n -p Append / to dir entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100126//usage: "\n -F Append indicator (one of */=@|) to entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100127//usage: )
128//usage: "\n -l Long listing format"
129//usage: "\n -i List inode numbers"
130//usage: "\n -n List numeric UIDs and GIDs instead of names"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200131//usage: "\n -s List allocated blocks"
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100132//usage: IF_FEATURE_LS_TIMESTAMPS(
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100133//usage: "\n -lc List ctime"
134//usage: "\n -lu List atime"
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100135//usage: )
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100136//usage: IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(
137//usage: "\n --full-time List full date and time"
138//usage: ))
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100139//usage: IF_FEATURE_HUMAN_READABLE(
Denys Vlasenko11540a82017-01-23 18:01:48 +0100140//usage: "\n -h Human readable sizes (1K 243M 2G)"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100141//usage: )
142//usage: IF_FEATURE_LS_SORTFILES(
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100143//usage: IF_LONG_OPTS(
144//usage: "\n --group-directories-first"
145//usage: )
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200146//usage: "\n -S Sort by size"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100147//usage: "\n -X Sort by extension"
148//usage: "\n -v Sort by version"
149//usage: )
150//usage: IF_FEATURE_LS_TIMESTAMPS(
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100151//usage: "\n -t Sort by mtime"
152//usage: "\n -tc Sort by ctime"
153//usage: "\n -tu Sort by atime"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100154//usage: )
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100155//usage: "\n -r Reverse sort order"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100156//usage: IF_SELINUX(
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100157//usage: "\n -Z List security context and permission"
158//usage: )
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100159//usage: IF_FEATURE_LS_WIDTH(
Denys Vlasenko11540a82017-01-23 18:01:48 +0100160//usage: "\n -w N Format N columns wide"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100161//usage: )
162//usage: IF_FEATURE_LS_COLOR(
163//usage: "\n --color[={always,never,auto}] Control coloring"
164//usage: )
165
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000166#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200167#include "common_bufsiz.h"
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200168#include "unicode.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +0000169
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000170
Denis Vlasenko99912ca2007-04-10 15:43:37 +0000171/* This is a NOEXEC applet. Be very careful! */
172
Eric Andersenf1142c52001-02-20 06:16:29 +0000173
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000174#if ENABLE_FTPD
175/* ftpd uses ls, and without timestamps Mozilla won't understand
176 * ftpd's LIST output.
177 */
178# undef CONFIG_FEATURE_LS_TIMESTAMPS
179# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000180# undef IF_FEATURE_LS_TIMESTAMPS
181# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000182# define CONFIG_FEATURE_LS_TIMESTAMPS 1
183# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000184# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
185# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000186#endif
187
188
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000189enum {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000190TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000191
Denys Vlasenko982aa262010-12-19 21:54:39 +0100192SPLIT_FILE = 0,
Denys Vlasenko2a816392011-05-13 17:28:09 +0200193SPLIT_DIR = 1,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100194SPLIT_SUBDIR = 2,
195
Denys Vlasenko2a816392011-05-13 17:28:09 +0200196/* Bits in G.all_fmt: */
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100197LIST_LONG = 1 << 0, /* long listing (-l and equivalents) */
Eric Andersen11c65522000-09-07 17:24:47 +0000198
199/* what files will be displayed */
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100200DISP_DIRNAME = 1 << 1, /* 2 or more items? label directories */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000201
Denys Vlasenko982aa262010-12-19 21:54:39 +0100202/* what is the overall style of the listing */
Denys Vlasenko92c68982017-01-23 20:21:14 +0100203STYLE_COLUMNAR = 1 << 2, /* many records per line */
204STYLE_LONG = 2 << 2, /* one record per line, extended info */
205STYLE_SINGLE = 3 << 2, /* one record per line */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100206STYLE_MASK = STYLE_SINGLE,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000207};
Eric Andersencc8ed391999-10-05 16:24:54 +0000208
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100209/* -Cadi1l Std options, busybox always supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100210/* -gnsxA Std options, busybox always supports */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100211/* -Q GNU option, busybox always supports */
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100212/* -k Std option, busybox always supports (by ignoring) */
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100213/* It means "for -s, show sizes in kbytes" */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100214/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
215/* since otherwise -s shows kbytes anyway */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200216/* -LHRctur Std options, busybox optionally supports */
217/* -Fp Std options, busybox optionally supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100218/* -SXvhTw GNU options, busybox optionally supports */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200219/* -T WIDTH Ignored (we don't use tabs on output) */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100220/* -Z SELinux mandated option, busybox optionally supports */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000221static const char ls_options[] ALIGN1 =
Denys Vlasenko11540a82017-01-23 18:01:48 +0100222 "Cadi1lgnsxAk" /* 12 opts, total 12 */
223 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */
224 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */
225 IF_SELINUX("Z") /* 1, 16 */
226 "Q" /* 1, 17 */
227 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */
228 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100229 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */
230 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100231 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */
232;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000233enum {
234 //OPT_C = (1 << 0),
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100235 OPT_a = (1 << 1),
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100236 OPT_d = (1 << 2),
Denys Vlasenkob60686c2017-01-23 18:34:11 +0100237 OPT_i = (1 << 3),
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100238 //OPT_1 = (1 << 4),
239 OPT_l = (1 << 5),
Denis Vlasenko248ce912009-03-03 14:09:04 +0000240 OPT_g = (1 << 6),
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100241 OPT_n = (1 << 7),
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100242 OPT_s = (1 << 8),
Denys Vlasenko92c68982017-01-23 20:21:14 +0100243 OPT_x = (1 << 9),
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100244 OPT_A = (1 << 10),
Denys Vlasenko11540a82017-01-23 18:01:48 +0100245 //OPT_k = (1 << 11),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100246
Denys Vlasenko11540a82017-01-23 18:01:48 +0100247 OPTBIT_F = 12,
248 OPTBIT_p, /* 13 */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100249 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100250 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
Denys Vlasenko11540a82017-01-23 18:01:48 +0100251 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
252 OPTBIT_c, /* 17 */
253 OPTBIT_t, /* 18 */
254 OPTBIT_u, /* 19 */
255 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
256 OPTBIT_X, /* 21 */
257 OPTBIT_r, /* 22 */
258 OPTBIT_v, /* 23 */
259 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100260 OPTBIT_H, /* 25 */
Denys Vlasenkocd387f22011-02-27 04:10:00 +0100261 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100262 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100263 OPTBIT_w, /* 28 */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100264 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
265 OPTBIT_dirs_first,
266 OPTBIT_color, /* 31 */
267 /* with long opts, we use all 32 bits */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100268
Denys Vlasenko11540a82017-01-23 18:01:48 +0100269 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
270 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
271 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
272 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
273 OPT_Q = (1 << OPTBIT_Q),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100274 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100275 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
276 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
277 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
278 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
279 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
280 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100281 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
282 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
283 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100284 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_LS_WIDTH,
285 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_LS_WIDTH,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100286 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100287 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
288 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000289};
290
Denis Vlasenko248ce912009-03-03 14:09:04 +0000291/* TODO: simple toggles may be stored as OPT_xxx bits instead */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100292static const uint8_t opt_flags[] = {
Denys Vlasenko69675782013-01-14 01:34:48 +0100293 STYLE_COLUMNAR, /* C */
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100294 0, /* a */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100295 0, /* d */
Denys Vlasenkob60686c2017-01-23 18:34:11 +0100296 0, /* i */
James Youngmana4bc10c2010-12-20 01:36:16 +0100297 STYLE_SINGLE, /* 1 */
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100298 LIST_LONG | STYLE_LONG, /* l - by keeping it after -1, "ls -l -1" ignores -1 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100299 LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100300 LIST_LONG | STYLE_LONG, /* n (numeris uid/gid) - handled via OPT_n. assumes l */
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100301 0, /* s */
Denys Vlasenko92c68982017-01-23 20:21:14 +0100302 STYLE_COLUMNAR, /* x */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100303 0xff
304 /* options after -x are not processed through opt_flags */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000305};
306
307
Eric Andersen11c65522000-09-07 17:24:47 +0000308/*
Denys Vlasenko2a816392011-05-13 17:28:09 +0200309 * a directory entry and its stat info
Eric Andersen11c65522000-09-07 17:24:47 +0000310 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200311struct dnode {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200312 const char *name; /* usually basename, but think "ls -l dir/file" */
313 const char *fullname; /* full name (usable for stat etc) */
314 struct dnode *dn_next; /* for linked list */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000315 IF_SELINUX(security_context_t sid;)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200316 smallint fname_allocated;
317
318 /* Used to avoid re-doing [l]stat at printout stage
319 * if we already collected needed data in scan stage:
320 */
321 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
322 mode_t dn_mode_stat; /* obtained with stat, or 0 */
323
324// struct stat dstat;
325// struct stat is huge. We don't need it in full.
326// At least we don't need st_dev and st_blksize,
327// but there are invisible fields as well
328// (such as nanosecond-resolution timespamps)
329// and padding, which we also don't want to store.
330// We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
Denys Vlasenko4029e212011-05-13 17:28:46 +0200331// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
Denys Vlasenko2a816392011-05-13 17:28:09 +0200332//
333 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
334 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
335 off_t dn_size;
336#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100337 time_t dn_time;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200338#endif
339 ino_t dn_ino;
340 blkcnt_t dn_blocks;
341 nlink_t dn_nlink;
342 uid_t dn_uid;
343 gid_t dn_gid;
344 int dn_rdev_maj;
345 int dn_rdev_min;
346// dev_t dn_dev;
347// blksize_t dn_blksize;
Eric Andersen11c65522000-09-07 17:24:47 +0000348};
Eric Andersen11c65522000-09-07 17:24:47 +0000349
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000350struct globals {
351#if ENABLE_FEATURE_LS_COLOR
352 smallint show_color;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200353# define G_show_color (G.show_color)
354#else
355# define G_show_color 0
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000356#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000357 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000358 unsigned all_fmt;
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100359#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenko2a816392011-05-13 17:28:09 +0200360 unsigned terminal_width;
361# define G_terminal_width (G.terminal_width)
362#else
363# define G_terminal_width TERMINAL_WIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000364#endif
365#if ENABLE_FEATURE_LS_TIMESTAMPS
366 /* Do time() just once. Saves one syscall per file for "ls -l" */
367 time_t current_time_t;
368#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100369} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200370#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000371#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200372 setup_common_bufsiz(); \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200373 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000374 memset(&G, 0, sizeof(G)); \
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100375 IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \
Denys Vlasenko2a816392011-05-13 17:28:09 +0200376 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000377} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000378
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000379
Denys Vlasenko4029e212011-05-13 17:28:46 +0200380/*** Output code ***/
Denys Vlasenko2a816392011-05-13 17:28:09 +0200381
Eric Andersen79565b62000-08-11 18:10:21 +0000382
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000383/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
384 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
385 * 3/7:multiplexed char/block device)
386 * and we use 0 for unknown and 15 for executables (see below) */
387#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200388/* un fi chr - dir - blk - file - link - sock - - exe */
389#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000390/* 036 black foreground 050 black background
391 037 red foreground 051 red background
392 040 green foreground 052 green background
393 041 brown foreground 053 brown background
394 042 blue foreground 054 blue background
395 043 magenta (purple) foreground 055 magenta background
396 044 cyan (light blue) foreground 056 cyan background
397 045 gray foreground 057 white background
398*/
399#define COLOR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200400 /*un fi chr - dir - blk - file - link - sock - - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000401 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
402 [TYPEINDEX(mode)])
403/* Select normal (0) [actually "reset all"] or bold (1)
404 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
405 * let's use 7 for "impossible" types, just for fun)
406 * Note: coreutils 6.9 uses inverted red for setuid binaries.
407 */
408#define ATTR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200409 /*un fi chr - dir - blk - file- link- sock- - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000410 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
411 [TYPEINDEX(mode)])
412
Denis Vlasenko5c759602006-10-28 12:37:16 +0000413#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000414/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000415static char fgcolor(mode_t mode)
416{
Rob Landley9947a242006-06-15 22:11:10 +0000417 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000418 return COLOR(0xF000); /* File is executable ... */
419 return COLOR(mode);
420}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000421static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000422{
Rob Landley9947a242006-06-15 22:11:10 +0000423 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000424 return ATTR(0xF000); /* File is executable ... */
425 return ATTR(mode);
426}
427#endif
428
Denys Vlasenko2a816392011-05-13 17:28:09 +0200429#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000430static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000431{
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100432 if (!(option_mask32 & (OPT_F|OPT_p)))
Eric Andersen11c65522000-09-07 17:24:47 +0000433 return '\0';
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100434
Rob Landley9947a242006-06-15 22:11:10 +0000435 if (S_ISDIR(mode))
436 return '/';
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100437 if (!(option_mask32 & OPT_F))
Rob Landley9947a242006-06-15 22:11:10 +0000438 return '\0';
439 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000440 return '*';
441 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000442}
443#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000444
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100445static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000446{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100447 unsigned len;
448 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000449
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100450 // TODO: quote tab as \t, etc, if -Q
451 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000452
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100453 if (!(option_mask32 & OPT_Q)) {
454 return uni_stat.unicode_width;
455 }
456
457 len = 2 + uni_stat.unicode_width;
458 while (*name) {
459 if (*name == '"' || *name == '\\') {
460 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000461 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100462 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000463 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100464 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000465}
466
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100467/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100468 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100469 * STYLE_SINGLE and STYLE_LONG don't care.
470 * coreutils 7.2 also supports:
471 * ls -b (--escape) = octal escapes (although it doesn't look like working)
472 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200473 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100474static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200475{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100476 unsigned len;
477 uni_stat_t uni_stat;
478
479 // TODO: quote tab as \t, etc, if -Q
480 name = printable_string(&uni_stat, name);
481
482 if (!(option_mask32 & OPT_Q)) {
483 fputs(name, stdout);
484 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200485 }
486
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100487 len = 2 + uni_stat.unicode_width;
488 putchar('"');
489 while (*name) {
490 if (*name == '"' || *name == '\\') {
491 putchar('\\');
492 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000493 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700494 putchar(*name);
495 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000496 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100497 putchar('"');
498 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000499}
500
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100501/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100502 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100503 * STYLE_SINGLE and STYLE_LONG don't care.
504 */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200505static NOINLINE unsigned display_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000506{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200507 unsigned column = 0;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200508 char *lpath;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000509#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200510 struct stat statbuf;
Eric Andersen11c65522000-09-07 17:24:47 +0000511 char append;
512#endif
513
Denis Vlasenko5c759602006-10-28 12:37:16 +0000514#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200515 append = append_char(dn->dn_mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000516#endif
517
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000518 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100519 * does not appear *inside* the "ls -l" line */
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200520 lpath = NULL;
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100521 if (G.all_fmt & LIST_LONG)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200522 if (S_ISLNK(dn->dn_mode))
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000523 lpath = xmalloc_readlink_or_warn(dn->fullname);
524
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100525 if (option_mask32 & OPT_i) /* show inode# */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200526 column += printf("%7llu ", (long long) dn->dn_ino);
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100527//TODO: -h should affect -s too:
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100528 if (option_mask32 & OPT_s) /* show allocated blocks */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200529 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100530 if (G.all_fmt & LIST_LONG) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100531 /* long listing: show mode */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200532 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100533 /* long listing: show number of links */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200534 column += printf("%4lu ", (long) dn->dn_nlink);
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100535 /* long listing: show user/group */
536 if (option_mask32 & OPT_n) {
537 if (option_mask32 & OPT_g)
538 column += printf("%-8u ", (int) dn->dn_gid);
539 else
540 column += printf("%-8u %-8u ",
541 (int) dn->dn_uid,
542 (int) dn->dn_gid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000543 }
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100544#if ENABLE_FEATURE_LS_USERNAME
545 else {
546 if (option_mask32 & OPT_g) {
547 column += printf("%-8.8s ",
548 get_cached_groupname(dn->dn_gid));
549 } else {
550 column += printf("%-8.8s %-8.8s ",
551 get_cached_username(dn->dn_uid),
552 get_cached_groupname(dn->dn_gid));
553 }
554 }
Eric Andersen11c65522000-09-07 17:24:47 +0000555#endif
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100556#if ENABLE_SELINUX
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100557 }
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100558 if (option_mask32 & OPT_Z) {
559 column += printf("%-32s ", dn->sid ? dn->sid : "?");
560 freecon(dn->sid);
561 }
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100562 if (G.all_fmt & LIST_LONG) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100563#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100564 /* long listing: show size */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200565 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000566 column += printf("%4u, %3u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200567 dn->dn_rdev_maj,
568 dn->dn_rdev_min);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000569 } else {
Denys Vlasenko982aa262010-12-19 21:54:39 +0100570 if (option_mask32 & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100571 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200572 /* print size, show one fractional, use suffixes */
573 make_human_readable_str(dn->dn_size, 1, 0)
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200574 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000575 } else {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200576 column += printf("%9"OFF_FMT"u ", dn->dn_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000577 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000578 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000579#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100580 /* long listing: show {m,c,a}time */
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100581 if (option_mask32 & OPT_full_time) { /* --full-time */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100582 /* coreutils 8.4 ls --full-time prints:
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100583 * 2009-07-13 17:49:27.000000000 +0200
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100584 * we don't show fractional seconds.
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100585 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100586 char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")];
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100587 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z",
588 localtime(&dn->dn_time));
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100589 column += printf("%s ", buf);
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100590 } else { /* ordinary time format */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100591 /* G.current_time_t is ~== time(NULL) */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100592 char *filetime = ctime(&dn->dn_time);
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100593 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100594 time_t age = G.current_time_t - dn->dn_time;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000595 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
Denys Vlasenko8e92df12015-02-16 15:36:25 +0100596 /* less than 6 months old */
597 /* "mmm dd hh:mm " */
598 printf("%.12s ", filetime + 4);
599 } else {
600 /* "mmm dd yyyy " */
601 /* "mmm dd yyyyy " after year 9999 :) */
602 strchr(filetime + 20, '\n')[0] = ' ';
603 printf("%.7s%6s", filetime + 4, filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000604 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000605 column += 13;
606 }
Eric Andersen11c65522000-09-07 17:24:47 +0000607#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100608 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100609
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000610#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200611 if (G_show_color) {
612 mode_t mode = dn->dn_mode_lstat;
613 if (!mode)
614 if (lstat(dn->fullname, &statbuf) == 0)
615 mode = statbuf.st_mode;
616 printf("\033[%u;%um", bold(mode), fgcolor(mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000617 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100618#endif
619 column += print_name(dn->name);
Denys Vlasenko2a816392011-05-13 17:28:09 +0200620 if (G_show_color) {
James Youngmana4bc10c2010-12-20 01:36:16 +0100621 printf("\033[0m");
622 }
623
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200624 if (lpath) {
625 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000626#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100627 if ((option_mask32 & (OPT_F|OPT_p))
628 || G_show_color
629 ) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200630 mode_t mode = dn->dn_mode_stat;
631 if (!mode)
632 if (stat(dn->fullname, &statbuf) == 0)
633 mode = statbuf.st_mode;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200634# if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200635 append = append_char(mode);
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200636# endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200637# if ENABLE_FEATURE_LS_COLOR
638 if (G_show_color) {
639 printf("\033[%u;%um", bold(mode), fgcolor(mode));
640 }
641# endif
Eric Andersen11c65522000-09-07 17:24:47 +0000642 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200643#endif
644 column += print_name(lpath) + 4;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200645 free(lpath);
646 if (G_show_color) {
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200647 printf("\033[0m");
648 }
Eric Andersen11c65522000-09-07 17:24:47 +0000649 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000650#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100651 if (option_mask32 & (OPT_F|OPT_p)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000652 if (append) {
653 putchar(append);
654 column++;
655 }
656 }
657#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000658
Glenn L McGrath4d001292003-01-06 01:11:50 +0000659 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000660}
661
Denys Vlasenko4029e212011-05-13 17:28:46 +0200662static void display_files(struct dnode **dn, unsigned nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100663{
664 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100665 unsigned column;
666 unsigned nexttab;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100667 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100668
Denys Vlasenko2a816392011-05-13 17:28:09 +0200669 if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100670 ncols = 1;
671 } else {
672 /* find the longest file name, use that as the column width */
673 for (i = 0; dn[i]; i++) {
674 int len = calc_name_len(dn[i]->name);
675 if (column_width < len)
676 column_width = len;
677 }
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100678 column_width += 2
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100679 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100680 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
681 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
682 ;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200683 ncols = (unsigned)G_terminal_width / column_width;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100684 }
685
686 if (ncols > 1) {
687 nrows = nfiles / ncols;
688 if (nrows * ncols < nfiles)
689 nrows++; /* round up fractionals */
690 } else {
691 nrows = nfiles;
692 ncols = 1;
693 }
694
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100695 column = 0;
696 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100697 for (row = 0; row < nrows; row++) {
698 for (nc = 0; nc < ncols; nc++) {
699 /* reach into the array based on the column and row */
Denys Vlasenko92c68982017-01-23 20:21:14 +0100700 if (option_mask32 & OPT_x)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100701 i = (row * ncols) + nc; /* display across row */
702 else
703 i = (nc * nrows) + row; /* display by column */
704 if (i < nfiles) {
705 if (column > 0) {
706 nexttab -= column;
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100707 printf("%*s", nexttab, "");
708 column += nexttab;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100709 }
710 nexttab = column + column_width;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200711 column += display_single(dn[i]);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100712 }
713 }
714 putchar('\n');
715 column = 0;
716 }
717}
718
719
Denys Vlasenko4029e212011-05-13 17:28:46 +0200720/*** Dir scanning code ***/
721
722static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100723{
Denys Vlasenko4029e212011-05-13 17:28:46 +0200724 struct stat statbuf;
725 struct dnode *cur;
726
727 cur = xzalloc(sizeof(*cur));
728 cur->fullname = fullname;
729 cur->name = name;
730
731 if ((option_mask32 & OPT_L) || force_follow) {
732#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100733 if (option_mask32 & OPT_Z) {
Denys Vlasenko69675782013-01-14 01:34:48 +0100734 getfilecon(fullname, &cur->sid);
Denys Vlasenko4029e212011-05-13 17:28:46 +0200735 }
736#endif
737 if (stat(fullname, &statbuf)) {
738 bb_simple_perror_msg(fullname);
739 G.exit_code = EXIT_FAILURE;
740 free(cur);
741 return NULL;
742 }
743 cur->dn_mode_stat = statbuf.st_mode;
744 } else {
745#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100746 if (option_mask32 & OPT_Z) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200747 lgetfilecon(fullname, &cur->sid);
748 }
749#endif
750 if (lstat(fullname, &statbuf)) {
751 bb_simple_perror_msg(fullname);
752 G.exit_code = EXIT_FAILURE;
753 free(cur);
754 return NULL;
755 }
756 cur->dn_mode_lstat = statbuf.st_mode;
757 }
758
759 /* cur->dstat = statbuf: */
760 cur->dn_mode = statbuf.st_mode ;
761 cur->dn_size = statbuf.st_size ;
762#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100763 cur->dn_time = statbuf.st_mtime ;
764 if (option_mask32 & OPT_u)
765 cur->dn_time = statbuf.st_atime;
766 if (option_mask32 & OPT_c)
767 cur->dn_time = statbuf.st_ctime;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200768#endif
769 cur->dn_ino = statbuf.st_ino ;
770 cur->dn_blocks = statbuf.st_blocks;
771 cur->dn_nlink = statbuf.st_nlink ;
772 cur->dn_uid = statbuf.st_uid ;
773 cur->dn_gid = statbuf.st_gid ;
774 cur->dn_rdev_maj = major(statbuf.st_rdev);
775 cur->dn_rdev_min = minor(statbuf.st_rdev);
776
777 return cur;
778}
779
780static unsigned count_dirs(struct dnode **dn, int which)
781{
782 unsigned dirs, all;
783
784 if (!dn)
785 return 0;
786
787 dirs = all = 0;
788 for (; *dn; dn++) {
789 const char *name;
790
791 all++;
792 if (!S_ISDIR((*dn)->dn_mode))
793 continue;
794
795 name = (*dn)->name;
796 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
797 /* or if it's not . or .. */
798 || name[0] != '.'
799 || (name[1] && (name[1] != '.' || name[2]))
800 ) {
801 dirs++;
802 }
803 }
804 return which != SPLIT_FILE ? dirs : all - dirs;
805}
806
807/* get memory to hold an array of pointers */
808static struct dnode **dnalloc(unsigned num)
809{
810 if (num < 1)
811 return NULL;
812
813 num++; /* so that we have terminating NULL */
814 return xzalloc(num * sizeof(struct dnode *));
815}
816
817#if ENABLE_FEATURE_LS_RECURSIVE
818static void dfree(struct dnode **dnp)
819{
820 unsigned i;
821
822 if (dnp == NULL)
823 return;
824
825 for (i = 0; dnp[i]; i++) {
826 struct dnode *cur = dnp[i];
827 if (cur->fname_allocated)
828 free((char*)cur->fullname);
829 free(cur);
830 }
831 free(dnp);
832}
833#else
834#define dfree(...) ((void)0)
835#endif
836
837/* Returns NULL-terminated malloced vector of pointers (or NULL) */
838static struct dnode **splitdnarray(struct dnode **dn, int which)
839{
840 unsigned dncnt, d;
841 struct dnode **dnp;
842
843 if (dn == NULL)
844 return NULL;
845
846 /* count how many dirs or files there are */
847 dncnt = count_dirs(dn, which);
848
849 /* allocate a file array and a dir array */
850 dnp = dnalloc(dncnt);
851
852 /* copy the entrys into the file or dir array */
853 for (d = 0; *dn; dn++) {
854 if (S_ISDIR((*dn)->dn_mode)) {
855 const char *name;
856
857 if (which == SPLIT_FILE)
858 continue;
859
860 name = (*dn)->name;
861 if ((which & SPLIT_DIR) /* any dir... */
862 /* ... or not . or .. */
863 || name[0] != '.'
864 || (name[1] && (name[1] != '.' || name[2]))
865 ) {
866 dnp[d++] = *dn;
867 }
868 } else
869 if (which == SPLIT_FILE) {
870 dnp[d++] = *dn;
871 }
872 }
873 return dnp;
874}
875
876#if ENABLE_FEATURE_LS_SORTFILES
877static int sortcmp(const void *a, const void *b)
878{
879 struct dnode *d1 = *(struct dnode **)a;
880 struct dnode *d2 = *(struct dnode **)b;
Denys Vlasenko11540a82017-01-23 18:01:48 +0100881 unsigned opt = option_mask32;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200882 off_t dif;
883
Denys Vlasenko11540a82017-01-23 18:01:48 +0100884 dif = 0; /* assume sort by name */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200885 // TODO: use pre-initialized function pointer
886 // instead of branch forest
Denys Vlasenko11540a82017-01-23 18:01:48 +0100887 if (opt & OPT_dirs_first) {
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100888 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
889 if (dif != 0)
890 goto maybe_invert_and_ret;
891 }
892
Denys Vlasenko11540a82017-01-23 18:01:48 +0100893 if (opt & OPT_S) { /* sort by size */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200894 dif = (d2->dn_size - d1->dn_size);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200895 } else
Denys Vlasenko11540a82017-01-23 18:01:48 +0100896 if (opt & OPT_t) { /* sort by time */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100897 dif = (d2->dn_time - d1->dn_time);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200898 } else
Denys Vlasenko1e18a012011-06-21 17:12:52 +0200899#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
Denys Vlasenko11540a82017-01-23 18:01:48 +0100900 if (opt & OPT_v) { /* sort by version */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200901 dif = strverscmp(d1->name, d2->name);
902 } else
Denys Vlasenko561f9c82011-06-21 16:38:29 +0200903#endif
Denys Vlasenko11540a82017-01-23 18:01:48 +0100904 if (opt & OPT_X) { /* sort by extension */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200905 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
Denys Vlasenko4029e212011-05-13 17:28:46 +0200906 }
907 if (dif == 0) {
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200908 /* sort by name, use as tie breaker for other sorts */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200909 if (ENABLE_LOCALE_SUPPORT)
910 dif = strcoll(d1->name, d2->name);
911 else
912 dif = strcmp(d1->name, d2->name);
Denys Vlasenko11540a82017-01-23 18:01:48 +0100913 } else {
914 /* Make dif fit into an int */
915 if (sizeof(dif) > sizeof(int)) {
916 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
917 /* shift leaving only "int" worth of bits */
918 /* (this requires dif != 0, and here it is nonzero) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200919 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100920 }
921 }
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100922 maybe_invert_and_ret:
Denys Vlasenko11540a82017-01-23 18:01:48 +0100923 return (opt & OPT_r) ? -(int)dif : (int)dif;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100924}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200925
926static void dnsort(struct dnode **dn, int size)
927{
928 qsort(dn, size, sizeof(*dn), sortcmp);
929}
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200930
931static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
932{
933 dnsort(dn, nfiles);
934 display_files(dn, nfiles);
935}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200936#else
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200937# define dnsort(dn, size) ((void)0)
938# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100939#endif
940
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100941/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200942static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100943{
944 struct dnode *dn, *cur, **dnp;
945 struct dirent *entry;
946 DIR *dir;
947 unsigned i, nfiles;
948
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100949 *nfiles_p = 0;
950 dir = warn_opendir(path);
951 if (dir == NULL) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200952 G.exit_code = EXIT_FAILURE;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100953 return NULL; /* could not open the dir */
954 }
955 dn = NULL;
956 nfiles = 0;
957 while ((entry = readdir(dir)) != NULL) {
958 char *fullname;
959
960 /* are we going to list the file- it may be . or .. or a hidden file */
961 if (entry->d_name[0] == '.') {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100962 if (!(option_mask32 & (OPT_a|OPT_A)))
963 continue; /* skip all dotfiles if no -a/-A */
964 if (!(option_mask32 & OPT_a)
965 && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100966 ) {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100967 continue; /* if only -A, skip . and .. but show other dotfiles */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100968 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100969 }
970 fullname = concat_path_file(path, entry->d_name);
971 cur = my_stat(fullname, bb_basename(fullname), 0);
972 if (!cur) {
973 free(fullname);
974 continue;
975 }
976 cur->fname_allocated = 1;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200977 cur->dn_next = dn;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100978 dn = cur;
979 nfiles++;
980 }
981 closedir(dir);
982
983 if (dn == NULL)
984 return NULL;
985
986 /* now that we know how many files there are
987 * allocate memory for an array to hold dnode pointers
988 */
989 *nfiles_p = nfiles;
990 dnp = dnalloc(nfiles);
991 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
992 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200993 dn = dn->dn_next;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100994 if (!dn)
995 break;
996 }
997
998 return dnp;
999}
1000
Denys Vlasenko4029e212011-05-13 17:28:46 +02001001#if ENABLE_DESKTOP
1002/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
1003 * If any of the -l, -n, -s options is specified, each list
1004 * of files within the directory shall be preceded by a
1005 * status line indicating the number of file system blocks
1006 * occupied by files in the directory in 512-byte units if
1007 * the -k option is not specified, or 1024-byte units if the
1008 * -k option is specified, rounded up to the next integral
1009 * number of units.
1010 */
1011/* by Jorgen Overgaard (jorgen AT antistaten.se) */
1012static off_t calculate_blocks(struct dnode **dn)
1013{
1014 uoff_t blocks = 1;
1015 if (dn) {
1016 while (*dn) {
1017 /* st_blocks is in 512 byte blocks */
1018 blocks += (*dn)->dn_blocks;
1019 dn++;
1020 }
1021 }
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001022
Denys Vlasenko4029e212011-05-13 17:28:46 +02001023 /* Even though standard says use 512 byte blocks, coreutils use 1k */
1024 /* Actually, we round up by calculating (blocks + 1) / 2,
1025 * "+ 1" was done when we initialized blocks to 1 */
1026 return blocks >> 1;
1027}
1028#endif
1029
1030static void scan_and_display_dirs_recur(struct dnode **dn, int first)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001031{
1032 unsigned nfiles;
1033 struct dnode **subdnp;
1034
1035 for (; *dn; dn++) {
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001036 if ((G.all_fmt & DISP_DIRNAME)
1037 || (option_mask32 & OPT_R)
1038 ) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001039 if (!first)
1040 bb_putchar('\n');
1041 first = 0;
1042 printf("%s:\n", (*dn)->fullname);
1043 }
Denys Vlasenko4029e212011-05-13 17:28:46 +02001044 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001045#if ENABLE_DESKTOP
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001046 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG
1047 || (option_mask32 & OPT_s)
1048 ) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001049 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001050 }
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001051#endif
1052 if (nfiles > 0) {
1053 /* list all files at this level */
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001054 sort_and_display_files(subdnp, nfiles);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001055
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001056 if (ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001057 && (option_mask32 & OPT_R)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001058 ) {
1059 struct dnode **dnd;
1060 unsigned dndirs;
1061 /* recursive - list the sub-dirs */
1062 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1063 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1064 if (dndirs > 0) {
1065 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001066 scan_and_display_dirs_recur(dnd, 0);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001067 /* free the array of dnode pointers to the dirs */
1068 free(dnd);
1069 }
1070 }
1071 /* free the dnodes and the fullname mem */
1072 dfree(subdnp);
1073 }
1074 }
1075}
1076
1077
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001078int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +00001079{
Glenn L McGrath792cae52004-01-18 05:15:16 +00001080 struct dnode **dnd;
1081 struct dnode **dnf;
1082 struct dnode **dnp;
1083 struct dnode *dn;
1084 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001085 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001086 unsigned nfiles;
1087 unsigned dnfiles;
1088 unsigned dndirs;
1089 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001090#if ENABLE_FEATURE_LS_COLOR
1091 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1092 /* coreutils 6.10:
1093 * # ls --color=BOGUS
1094 * ls: invalid argument 'BOGUS' for '--color'
1095 * Valid arguments are:
1096 * 'always', 'yes', 'force'
1097 * 'never', 'no', 'none'
1098 * 'auto', 'tty', 'if-tty'
1099 * (and substrings: "--color=alwa" work too)
1100 */
1101 static const char ls_longopts[] ALIGN1 =
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001102 "full-time\0" No_argument "\xff"
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001103 "group-directories-first\0" No_argument "\xfe"
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001104 "color\0" Optional_argument "\xfd"
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001105 ;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001106 static const char color_str[] ALIGN1 =
1107 "always\0""yes\0""force\0"
1108 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001109 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001110 const char *color_opt = color_str; /* "always" */
1111#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001112
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001113 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001114
Denys Vlasenko28055022010-01-04 20:49:58 +01001115 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001116
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001117#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001118 /* obtain the terminal width */
Denys Vlasenko641caae2015-10-23 01:44:22 +02001119 G_terminal_width = get_terminal_width(STDIN_FILENO);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001120 /* go one less... */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001121 G_terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001122#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001123
Eric Andersen11c65522000-09-07 17:24:47 +00001124 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001125 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001126 opt_complementary =
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001127 /* --full-time implies -l */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001128 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS("\xff""l"))
Denys Vlasenkof3137462010-12-19 05:05:34 +01001129 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1130 * in some pairs of opts, only last one takes effect:
1131 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001132 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001133 // ":m-l:l-m" - we don't have -m
1134 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001135 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1136 ":C-1:1-C" /* bycols/oneline */
1137 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
Denys Vlasenkob47b3ce2011-08-10 00:51:29 +02001138 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001139 /* -w NUM: */
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001140 IF_FEATURE_LS_WIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001141 opt = getopt32(argv, ls_options
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001142 IF_FEATURE_LS_WIDTH(, NULL, &G_terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001143 IF_FEATURE_LS_COLOR(, &color_opt)
1144 );
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001145#if 0 /* option bits debug */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001146 bb_error_msg("opt:0x%08x l:%x H:%x color:%x dirs:%x", opt, OPT_l, OPT_H, OPT_color, OPT_dirs_first);
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001147 if (opt & OPT_c ) bb_error_msg("-c");
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001148 if (opt & OPT_l ) bb_error_msg("-l");
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001149 if (opt & OPT_H ) bb_error_msg("-H");
1150 if (opt & OPT_color ) bb_error_msg("--color");
1151 if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
1152 if (opt & OPT_full_time ) bb_error_msg("--full-time");
1153 exit(0);
1154#endif
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001155 for (i = 0; opt_flags[i] != 0xff; i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001156 if (opt & (1 << i)) {
Denys Vlasenko982aa262010-12-19 21:54:39 +01001157 uint32_t flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001158
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001159 if (flags & STYLE_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001160 G.all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001161
Denys Vlasenko2a816392011-05-13 17:28:09 +02001162 G.all_fmt |= flags;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001163 }
Eric Andersen11c65522000-09-07 17:24:47 +00001164 }
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +01001165#if ENABLE_SELINUX
1166 if (opt & OPT_Z) {
1167 if (!is_selinux_enabled())
1168 option_mask32 &= ~OPT_Z;
1169 }
1170#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001171
Denis Vlasenko5c759602006-10-28 12:37:16 +00001172#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +02001173 /* set G_show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001174 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1175 char *p = getenv("LS_COLORS");
1176 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001177 if (!p || (p[0] && strcmp(p, "none") != 0))
Denys Vlasenko2a816392011-05-13 17:28:09 +02001178 G_show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001179 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001180 if (opt & OPT_color) {
1181 if (color_opt[0] == 'n')
Denys Vlasenko2a816392011-05-13 17:28:09 +02001182 G_show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001183 else switch (index_in_substrings(color_str, color_opt)) {
1184 case 3:
1185 case 4:
1186 case 5:
1187 if (isatty(STDOUT_FILENO)) {
1188 case 0:
1189 case 1:
1190 case 2:
Denys Vlasenko2a816392011-05-13 17:28:09 +02001191 G_show_color = 1;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001192 }
1193 }
Paul Fox156dc412005-08-01 19:33:30 +00001194 }
1195#endif
1196
Eric Andersen11c65522000-09-07 17:24:47 +00001197 /* sort out which command line options take precedence */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001198 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1199 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001200 if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) { /* not -l? */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001201 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1202 /* when to sort by time? -t[cu] sorts by time even with -l */
1203 /* (this is achieved by opt_flags[] element for -t) */
1204 /* without -l, bare -c or -u enable sort too */
1205 /* (with -l, bare -c or -u just select which time to show) */
1206 if (opt & (OPT_c|OPT_u)) {
Denys Vlasenko11540a82017-01-23 18:01:48 +01001207 option_mask32 |= OPT_t;
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001208 }
1209 }
1210 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001211
Denys Vlasenkof3137462010-12-19 05:05:34 +01001212 /* choose a display format if one was not already specified by an option */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001213 if (!(G.all_fmt & STYLE_MASK))
1214 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001215
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001216 argv += optind;
1217 if (!argv[0])
1218 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001219
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001220 if (argv[1])
Denys Vlasenko2a816392011-05-13 17:28:09 +02001221 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001222
Denis Vlasenko5c759602006-10-28 12:37:16 +00001223 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001224 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001225 nfiles = 0;
1226 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001227 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001228 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001229 !((G.all_fmt & STYLE_MASK) == STYLE_LONG
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001230 || (option_mask32 & (OPT_s|OPT_F))
Denys Vlasenkoea684c62011-03-12 03:12:36 +01001231 )
Denys Vlasenko982aa262010-12-19 21:54:39 +01001232 /* ... or if -H: */
1233 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001234 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001235 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001236 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001237 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001238 continue;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001239 /*cur->fname_allocated = 0; - already is */
1240 cur->dn_next = dn;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001241 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001242 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001243 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001244
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001245 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1246 if (nfiles == 0)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001247 return G.exit_code;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001248
Eric Andersen11c65522000-09-07 17:24:47 +00001249 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001250 * allocate memory for an array to hold dnode pointers
1251 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001252 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001253 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1254 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001255 dn = dn->dn_next;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001256 if (!dn)
1257 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001258 }
1259
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001260 if (option_mask32 & OPT_d) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001261 sort_and_display_files(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001262 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001263 dnd = splitdnarray(dnp, SPLIT_DIR);
1264 dnf = splitdnarray(dnp, SPLIT_FILE);
1265 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001266 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001267 if (dnfiles > 0) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001268 sort_and_display_files(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001269 if (ENABLE_FEATURE_CLEAN_UP)
1270 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001271 }
1272 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001273 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001274 scan_and_display_dirs_recur(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001275 if (ENABLE_FEATURE_CLEAN_UP)
1276 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001277 }
1278 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001279
Rob Landley26314862006-05-02 19:46:52 +00001280 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001281 dfree(dnp);
Denys Vlasenko2a816392011-05-13 17:28:09 +02001282 return G.exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001283}