blob: 3052fda325b96d984720731012ec648e89fec202 [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 Vlasenko2f7d9e82010-12-19 07:06:44 +0100114//usage: "\n -C List by columns"
115//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 */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100201DISP_ROWS = 1 << 2, /* print across rows */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000202
Denys Vlasenko982aa262010-12-19 21:54:39 +0100203/* what is the overall style of the listing */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100204STYLE_COLUMNAR = 1 << 3, /* many records per line */
205STYLE_LONG = 2 << 3, /* one record per line, extended info */
206STYLE_SINGLE = 3 << 3, /* one record per line */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100207STYLE_MASK = STYLE_SINGLE,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000208};
Eric Andersencc8ed391999-10-05 16:24:54 +0000209
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100210/* -Cadi1l Std options, busybox always supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100211/* -gnsxA Std options, busybox always supports */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100212/* -Q GNU option, busybox always supports */
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100213/* -k Std option, busybox always supports (by ignoring) */
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100214/* It means "for -s, show sizes in kbytes" */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100215/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
216/* since otherwise -s shows kbytes anyway */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200217/* -LHRctur Std options, busybox optionally supports */
218/* -Fp Std options, busybox optionally supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100219/* -SXvhTw GNU options, busybox optionally supports */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200220/* -T WIDTH Ignored (we don't use tabs on output) */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100221/* -Z SELinux mandated option, busybox optionally supports */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000222static const char ls_options[] ALIGN1 =
Denys Vlasenko11540a82017-01-23 18:01:48 +0100223 "Cadi1lgnsxAk" /* 12 opts, total 12 */
224 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */
225 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */
226 IF_SELINUX("Z") /* 1, 16 */
227 "Q" /* 1, 17 */
228 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */
229 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100230 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */
231 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100232 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */
233;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000234enum {
235 //OPT_C = (1 << 0),
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100236 OPT_a = (1 << 1),
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100237 OPT_d = (1 << 2),
Denys Vlasenkob60686c2017-01-23 18:34:11 +0100238 OPT_i = (1 << 3),
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100239 //OPT_1 = (1 << 4),
240 OPT_l = (1 << 5),
Denis Vlasenko248ce912009-03-03 14:09:04 +0000241 OPT_g = (1 << 6),
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100242 OPT_n = (1 << 7),
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100243 OPT_s = (1 << 8),
Denis Vlasenko248ce912009-03-03 14:09:04 +0000244 //OPT_x = (1 << 9),
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100245 OPT_A = (1 << 10),
Denys Vlasenko11540a82017-01-23 18:01:48 +0100246 //OPT_k = (1 << 11),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100247
Denys Vlasenko11540a82017-01-23 18:01:48 +0100248 OPTBIT_F = 12,
249 OPTBIT_p, /* 13 */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100250 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100251 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
Denys Vlasenko11540a82017-01-23 18:01:48 +0100252 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
253 OPTBIT_c, /* 17 */
254 OPTBIT_t, /* 18 */
255 OPTBIT_u, /* 19 */
256 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
257 OPTBIT_X, /* 21 */
258 OPTBIT_r, /* 22 */
259 OPTBIT_v, /* 23 */
260 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100261 OPTBIT_H, /* 25 */
Denys Vlasenkocd387f22011-02-27 04:10:00 +0100262 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100263 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100264 OPTBIT_w, /* 28 */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100265 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
266 OPTBIT_dirs_first,
267 OPTBIT_color, /* 31 */
268 /* with long opts, we use all 32 bits */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100269
Denys Vlasenko11540a82017-01-23 18:01:48 +0100270 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
271 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
272 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
273 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
274 OPT_Q = (1 << OPTBIT_Q),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100275 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100276 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
277 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
278 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
279 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
280 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
281 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100282 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
283 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
284 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100285 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_LS_WIDTH,
286 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_LS_WIDTH,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100287 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100288 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
289 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000290};
291
Denis Vlasenko248ce912009-03-03 14:09:04 +0000292/* TODO: simple toggles may be stored as OPT_xxx bits instead */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100293static const uint8_t opt_flags[] = {
Denys Vlasenko69675782013-01-14 01:34:48 +0100294 STYLE_COLUMNAR, /* C */
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100295 0, /* a */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100296 0, /* d */
Denys Vlasenkob60686c2017-01-23 18:34:11 +0100297 0, /* i */
James Youngmana4bc10c2010-12-20 01:36:16 +0100298 STYLE_SINGLE, /* 1 */
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100299 LIST_LONG | STYLE_LONG, /* l - by keeping it after -1, "ls -l -1" ignores -1 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100300 LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100301 LIST_LONG | STYLE_LONG, /* n (numeris uid/gid) - handled via OPT_n. assumes l */
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100302 0, /* s */
James Youngmana4bc10c2010-12-20 01:36:16 +0100303 DISP_ROWS | STYLE_COLUMNAR, /* x */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100304 0xff
305 /* options after -x are not processed through opt_flags */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000306};
307
308
Eric Andersen11c65522000-09-07 17:24:47 +0000309/*
Denys Vlasenko2a816392011-05-13 17:28:09 +0200310 * a directory entry and its stat info
Eric Andersen11c65522000-09-07 17:24:47 +0000311 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200312struct dnode {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200313 const char *name; /* usually basename, but think "ls -l dir/file" */
314 const char *fullname; /* full name (usable for stat etc) */
315 struct dnode *dn_next; /* for linked list */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000316 IF_SELINUX(security_context_t sid;)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200317 smallint fname_allocated;
318
319 /* Used to avoid re-doing [l]stat at printout stage
320 * if we already collected needed data in scan stage:
321 */
322 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
323 mode_t dn_mode_stat; /* obtained with stat, or 0 */
324
325// struct stat dstat;
326// struct stat is huge. We don't need it in full.
327// At least we don't need st_dev and st_blksize,
328// but there are invisible fields as well
329// (such as nanosecond-resolution timespamps)
330// and padding, which we also don't want to store.
331// We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
Denys Vlasenko4029e212011-05-13 17:28:46 +0200332// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
Denys Vlasenko2a816392011-05-13 17:28:09 +0200333//
334 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
335 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
336 off_t dn_size;
337#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100338 time_t dn_time;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200339#endif
340 ino_t dn_ino;
341 blkcnt_t dn_blocks;
342 nlink_t dn_nlink;
343 uid_t dn_uid;
344 gid_t dn_gid;
345 int dn_rdev_maj;
346 int dn_rdev_min;
347// dev_t dn_dev;
348// blksize_t dn_blksize;
Eric Andersen11c65522000-09-07 17:24:47 +0000349};
Eric Andersen11c65522000-09-07 17:24:47 +0000350
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000351struct globals {
352#if ENABLE_FEATURE_LS_COLOR
353 smallint show_color;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200354# define G_show_color (G.show_color)
355#else
356# define G_show_color 0
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000357#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000358 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000359 unsigned all_fmt;
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100360#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenko2a816392011-05-13 17:28:09 +0200361 unsigned terminal_width;
362# define G_terminal_width (G.terminal_width)
363#else
364# define G_terminal_width TERMINAL_WIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000365#endif
366#if ENABLE_FEATURE_LS_TIMESTAMPS
367 /* Do time() just once. Saves one syscall per file for "ls -l" */
368 time_t current_time_t;
369#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100370} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200371#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000372#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200373 setup_common_bufsiz(); \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200374 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000375 memset(&G, 0, sizeof(G)); \
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100376 IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \
Denys Vlasenko2a816392011-05-13 17:28:09 +0200377 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000378} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000379
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000380
Denys Vlasenko4029e212011-05-13 17:28:46 +0200381/*** Output code ***/
Denys Vlasenko2a816392011-05-13 17:28:09 +0200382
Eric Andersen79565b62000-08-11 18:10:21 +0000383
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000384/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
385 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
386 * 3/7:multiplexed char/block device)
387 * and we use 0 for unknown and 15 for executables (see below) */
388#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200389/* un fi chr - dir - blk - file - link - sock - - exe */
390#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000391/* 036 black foreground 050 black background
392 037 red foreground 051 red background
393 040 green foreground 052 green background
394 041 brown foreground 053 brown background
395 042 blue foreground 054 blue background
396 043 magenta (purple) foreground 055 magenta background
397 044 cyan (light blue) foreground 056 cyan background
398 045 gray foreground 057 white background
399*/
400#define COLOR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200401 /*un fi chr - dir - blk - file - link - sock - - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000402 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
403 [TYPEINDEX(mode)])
404/* Select normal (0) [actually "reset all"] or bold (1)
405 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
406 * let's use 7 for "impossible" types, just for fun)
407 * Note: coreutils 6.9 uses inverted red for setuid binaries.
408 */
409#define ATTR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200410 /*un fi chr - dir - blk - file- link- sock- - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000411 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
412 [TYPEINDEX(mode)])
413
Denis Vlasenko5c759602006-10-28 12:37:16 +0000414#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000415/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000416static char fgcolor(mode_t mode)
417{
Rob Landley9947a242006-06-15 22:11:10 +0000418 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000419 return COLOR(0xF000); /* File is executable ... */
420 return COLOR(mode);
421}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000422static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000423{
Rob Landley9947a242006-06-15 22:11:10 +0000424 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000425 return ATTR(0xF000); /* File is executable ... */
426 return ATTR(mode);
427}
428#endif
429
Denys Vlasenko2a816392011-05-13 17:28:09 +0200430#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000431static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000432{
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100433 if (!(option_mask32 & (OPT_F|OPT_p)))
Eric Andersen11c65522000-09-07 17:24:47 +0000434 return '\0';
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100435
Rob Landley9947a242006-06-15 22:11:10 +0000436 if (S_ISDIR(mode))
437 return '/';
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100438 if (!(option_mask32 & OPT_F))
Rob Landley9947a242006-06-15 22:11:10 +0000439 return '\0';
440 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000441 return '*';
442 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000443}
444#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000445
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100446static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000447{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100448 unsigned len;
449 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000450
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100451 // TODO: quote tab as \t, etc, if -Q
452 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000453
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100454 if (!(option_mask32 & OPT_Q)) {
455 return uni_stat.unicode_width;
456 }
457
458 len = 2 + uni_stat.unicode_width;
459 while (*name) {
460 if (*name == '"' || *name == '\\') {
461 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000462 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100463 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000464 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100465 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000466}
467
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100468/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100469 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100470 * STYLE_SINGLE and STYLE_LONG don't care.
471 * coreutils 7.2 also supports:
472 * ls -b (--escape) = octal escapes (although it doesn't look like working)
473 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200474 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100475static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200476{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100477 unsigned len;
478 uni_stat_t uni_stat;
479
480 // TODO: quote tab as \t, etc, if -Q
481 name = printable_string(&uni_stat, name);
482
483 if (!(option_mask32 & OPT_Q)) {
484 fputs(name, stdout);
485 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200486 }
487
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100488 len = 2 + uni_stat.unicode_width;
489 putchar('"');
490 while (*name) {
491 if (*name == '"' || *name == '\\') {
492 putchar('\\');
493 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000494 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700495 putchar(*name);
496 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000497 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100498 putchar('"');
499 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000500}
501
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100502/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100503 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100504 * STYLE_SINGLE and STYLE_LONG don't care.
505 */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200506static NOINLINE unsigned display_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000507{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200508 unsigned column = 0;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200509 char *lpath;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000510#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200511 struct stat statbuf;
Eric Andersen11c65522000-09-07 17:24:47 +0000512 char append;
513#endif
514
Denis Vlasenko5c759602006-10-28 12:37:16 +0000515#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200516 append = append_char(dn->dn_mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000517#endif
518
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000519 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100520 * does not appear *inside* the "ls -l" line */
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200521 lpath = NULL;
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100522 if (G.all_fmt & LIST_LONG)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200523 if (S_ISLNK(dn->dn_mode))
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000524 lpath = xmalloc_readlink_or_warn(dn->fullname);
525
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100526 if (option_mask32 & OPT_i) /* show inode# */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200527 column += printf("%7llu ", (long long) dn->dn_ino);
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100528//TODO: -h should affect -s too:
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100529 if (option_mask32 & OPT_s) /* show allocated blocks */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200530 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100531 if (G.all_fmt & LIST_LONG) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100532 /* long listing: show mode */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200533 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100534 /* long listing: show number of links */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200535 column += printf("%4lu ", (long) dn->dn_nlink);
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100536 /* long listing: show user/group */
537 if (option_mask32 & OPT_n) {
538 if (option_mask32 & OPT_g)
539 column += printf("%-8u ", (int) dn->dn_gid);
540 else
541 column += printf("%-8u %-8u ",
542 (int) dn->dn_uid,
543 (int) dn->dn_gid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000544 }
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100545#if ENABLE_FEATURE_LS_USERNAME
546 else {
547 if (option_mask32 & OPT_g) {
548 column += printf("%-8.8s ",
549 get_cached_groupname(dn->dn_gid));
550 } else {
551 column += printf("%-8.8s %-8.8s ",
552 get_cached_username(dn->dn_uid),
553 get_cached_groupname(dn->dn_gid));
554 }
555 }
Eric Andersen11c65522000-09-07 17:24:47 +0000556#endif
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100557#if ENABLE_SELINUX
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100558 }
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100559 if (option_mask32 & OPT_Z) {
560 column += printf("%-32s ", dn->sid ? dn->sid : "?");
561 freecon(dn->sid);
562 }
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100563 if (G.all_fmt & LIST_LONG) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100564#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100565 /* long listing: show size */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200566 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000567 column += printf("%4u, %3u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200568 dn->dn_rdev_maj,
569 dn->dn_rdev_min);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000570 } else {
Denys Vlasenko982aa262010-12-19 21:54:39 +0100571 if (option_mask32 & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100572 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200573 /* print size, show one fractional, use suffixes */
574 make_human_readable_str(dn->dn_size, 1, 0)
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200575 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000576 } else {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200577 column += printf("%9"OFF_FMT"u ", dn->dn_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000578 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000579 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000580#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100581 /* long listing: show {m,c,a}time */
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100582 if (option_mask32 & OPT_full_time) { /* --full-time */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100583 /* coreutils 8.4 ls --full-time prints:
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100584 * 2009-07-13 17:49:27.000000000 +0200
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100585 * we don't show fractional seconds.
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100586 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100587 char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")];
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100588 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z",
589 localtime(&dn->dn_time));
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100590 column += printf("%s ", buf);
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100591 } else { /* ordinary time format */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100592 /* G.current_time_t is ~== time(NULL) */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100593 char *filetime = ctime(&dn->dn_time);
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100594 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100595 time_t age = G.current_time_t - dn->dn_time;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000596 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
Denys Vlasenko8e92df12015-02-16 15:36:25 +0100597 /* less than 6 months old */
598 /* "mmm dd hh:mm " */
599 printf("%.12s ", filetime + 4);
600 } else {
601 /* "mmm dd yyyy " */
602 /* "mmm dd yyyyy " after year 9999 :) */
603 strchr(filetime + 20, '\n')[0] = ' ';
604 printf("%.7s%6s", filetime + 4, filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000605 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000606 column += 13;
607 }
Eric Andersen11c65522000-09-07 17:24:47 +0000608#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100609 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100610
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000611#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200612 if (G_show_color) {
613 mode_t mode = dn->dn_mode_lstat;
614 if (!mode)
615 if (lstat(dn->fullname, &statbuf) == 0)
616 mode = statbuf.st_mode;
617 printf("\033[%u;%um", bold(mode), fgcolor(mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000618 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100619#endif
620 column += print_name(dn->name);
Denys Vlasenko2a816392011-05-13 17:28:09 +0200621 if (G_show_color) {
James Youngmana4bc10c2010-12-20 01:36:16 +0100622 printf("\033[0m");
623 }
624
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200625 if (lpath) {
626 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000627#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100628 if ((option_mask32 & (OPT_F|OPT_p))
629 || G_show_color
630 ) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200631 mode_t mode = dn->dn_mode_stat;
632 if (!mode)
633 if (stat(dn->fullname, &statbuf) == 0)
634 mode = statbuf.st_mode;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200635# if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200636 append = append_char(mode);
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200637# endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200638# if ENABLE_FEATURE_LS_COLOR
639 if (G_show_color) {
640 printf("\033[%u;%um", bold(mode), fgcolor(mode));
641 }
642# endif
Eric Andersen11c65522000-09-07 17:24:47 +0000643 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200644#endif
645 column += print_name(lpath) + 4;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200646 free(lpath);
647 if (G_show_color) {
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200648 printf("\033[0m");
649 }
Eric Andersen11c65522000-09-07 17:24:47 +0000650 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000651#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100652 if (option_mask32 & (OPT_F|OPT_p)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000653 if (append) {
654 putchar(append);
655 column++;
656 }
657 }
658#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000659
Glenn L McGrath4d001292003-01-06 01:11:50 +0000660 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000661}
662
Denys Vlasenko4029e212011-05-13 17:28:46 +0200663static void display_files(struct dnode **dn, unsigned nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100664{
665 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100666 unsigned column;
667 unsigned nexttab;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100668 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100669
Denys Vlasenko2a816392011-05-13 17:28:09 +0200670 if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100671 ncols = 1;
672 } else {
673 /* find the longest file name, use that as the column width */
674 for (i = 0; dn[i]; i++) {
675 int len = calc_name_len(dn[i]->name);
676 if (column_width < len)
677 column_width = len;
678 }
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100679 column_width += 2
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100680 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100681 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
682 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
683 ;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200684 ncols = (unsigned)G_terminal_width / column_width;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100685 }
686
687 if (ncols > 1) {
688 nrows = nfiles / ncols;
689 if (nrows * ncols < nfiles)
690 nrows++; /* round up fractionals */
691 } else {
692 nrows = nfiles;
693 ncols = 1;
694 }
695
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100696 column = 0;
697 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100698 for (row = 0; row < nrows; row++) {
699 for (nc = 0; nc < ncols; nc++) {
700 /* reach into the array based on the column and row */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200701 if (G.all_fmt & DISP_ROWS)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100702 i = (row * ncols) + nc; /* display across row */
703 else
704 i = (nc * nrows) + row; /* display by column */
705 if (i < nfiles) {
706 if (column > 0) {
707 nexttab -= column;
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100708 printf("%*s", nexttab, "");
709 column += nexttab;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100710 }
711 nexttab = column + column_width;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200712 column += display_single(dn[i]);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100713 }
714 }
715 putchar('\n');
716 column = 0;
717 }
718}
719
720
Denys Vlasenko4029e212011-05-13 17:28:46 +0200721/*** Dir scanning code ***/
722
723static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100724{
Denys Vlasenko4029e212011-05-13 17:28:46 +0200725 struct stat statbuf;
726 struct dnode *cur;
727
728 cur = xzalloc(sizeof(*cur));
729 cur->fullname = fullname;
730 cur->name = name;
731
732 if ((option_mask32 & OPT_L) || force_follow) {
733#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100734 if (option_mask32 & OPT_Z) {
Denys Vlasenko69675782013-01-14 01:34:48 +0100735 getfilecon(fullname, &cur->sid);
Denys Vlasenko4029e212011-05-13 17:28:46 +0200736 }
737#endif
738 if (stat(fullname, &statbuf)) {
739 bb_simple_perror_msg(fullname);
740 G.exit_code = EXIT_FAILURE;
741 free(cur);
742 return NULL;
743 }
744 cur->dn_mode_stat = statbuf.st_mode;
745 } else {
746#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100747 if (option_mask32 & OPT_Z) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200748 lgetfilecon(fullname, &cur->sid);
749 }
750#endif
751 if (lstat(fullname, &statbuf)) {
752 bb_simple_perror_msg(fullname);
753 G.exit_code = EXIT_FAILURE;
754 free(cur);
755 return NULL;
756 }
757 cur->dn_mode_lstat = statbuf.st_mode;
758 }
759
760 /* cur->dstat = statbuf: */
761 cur->dn_mode = statbuf.st_mode ;
762 cur->dn_size = statbuf.st_size ;
763#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100764 cur->dn_time = statbuf.st_mtime ;
765 if (option_mask32 & OPT_u)
766 cur->dn_time = statbuf.st_atime;
767 if (option_mask32 & OPT_c)
768 cur->dn_time = statbuf.st_ctime;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200769#endif
770 cur->dn_ino = statbuf.st_ino ;
771 cur->dn_blocks = statbuf.st_blocks;
772 cur->dn_nlink = statbuf.st_nlink ;
773 cur->dn_uid = statbuf.st_uid ;
774 cur->dn_gid = statbuf.st_gid ;
775 cur->dn_rdev_maj = major(statbuf.st_rdev);
776 cur->dn_rdev_min = minor(statbuf.st_rdev);
777
778 return cur;
779}
780
781static unsigned count_dirs(struct dnode **dn, int which)
782{
783 unsigned dirs, all;
784
785 if (!dn)
786 return 0;
787
788 dirs = all = 0;
789 for (; *dn; dn++) {
790 const char *name;
791
792 all++;
793 if (!S_ISDIR((*dn)->dn_mode))
794 continue;
795
796 name = (*dn)->name;
797 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
798 /* or if it's not . or .. */
799 || name[0] != '.'
800 || (name[1] && (name[1] != '.' || name[2]))
801 ) {
802 dirs++;
803 }
804 }
805 return which != SPLIT_FILE ? dirs : all - dirs;
806}
807
808/* get memory to hold an array of pointers */
809static struct dnode **dnalloc(unsigned num)
810{
811 if (num < 1)
812 return NULL;
813
814 num++; /* so that we have terminating NULL */
815 return xzalloc(num * sizeof(struct dnode *));
816}
817
818#if ENABLE_FEATURE_LS_RECURSIVE
819static void dfree(struct dnode **dnp)
820{
821 unsigned i;
822
823 if (dnp == NULL)
824 return;
825
826 for (i = 0; dnp[i]; i++) {
827 struct dnode *cur = dnp[i];
828 if (cur->fname_allocated)
829 free((char*)cur->fullname);
830 free(cur);
831 }
832 free(dnp);
833}
834#else
835#define dfree(...) ((void)0)
836#endif
837
838/* Returns NULL-terminated malloced vector of pointers (or NULL) */
839static struct dnode **splitdnarray(struct dnode **dn, int which)
840{
841 unsigned dncnt, d;
842 struct dnode **dnp;
843
844 if (dn == NULL)
845 return NULL;
846
847 /* count how many dirs or files there are */
848 dncnt = count_dirs(dn, which);
849
850 /* allocate a file array and a dir array */
851 dnp = dnalloc(dncnt);
852
853 /* copy the entrys into the file or dir array */
854 for (d = 0; *dn; dn++) {
855 if (S_ISDIR((*dn)->dn_mode)) {
856 const char *name;
857
858 if (which == SPLIT_FILE)
859 continue;
860
861 name = (*dn)->name;
862 if ((which & SPLIT_DIR) /* any dir... */
863 /* ... or not . or .. */
864 || name[0] != '.'
865 || (name[1] && (name[1] != '.' || name[2]))
866 ) {
867 dnp[d++] = *dn;
868 }
869 } else
870 if (which == SPLIT_FILE) {
871 dnp[d++] = *dn;
872 }
873 }
874 return dnp;
875}
876
877#if ENABLE_FEATURE_LS_SORTFILES
878static int sortcmp(const void *a, const void *b)
879{
880 struct dnode *d1 = *(struct dnode **)a;
881 struct dnode *d2 = *(struct dnode **)b;
Denys Vlasenko11540a82017-01-23 18:01:48 +0100882 unsigned opt = option_mask32;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200883 off_t dif;
884
Denys Vlasenko11540a82017-01-23 18:01:48 +0100885 dif = 0; /* assume sort by name */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200886 // TODO: use pre-initialized function pointer
887 // instead of branch forest
Denys Vlasenko11540a82017-01-23 18:01:48 +0100888 if (opt & OPT_dirs_first) {
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100889 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
890 if (dif != 0)
891 goto maybe_invert_and_ret;
892 }
893
Denys Vlasenko11540a82017-01-23 18:01:48 +0100894 if (opt & OPT_S) { /* sort by size */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200895 dif = (d2->dn_size - d1->dn_size);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200896 } else
Denys Vlasenko11540a82017-01-23 18:01:48 +0100897 if (opt & OPT_t) { /* sort by time */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100898 dif = (d2->dn_time - d1->dn_time);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200899 } else
Denys Vlasenko1e18a012011-06-21 17:12:52 +0200900#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
Denys Vlasenko11540a82017-01-23 18:01:48 +0100901 if (opt & OPT_v) { /* sort by version */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200902 dif = strverscmp(d1->name, d2->name);
903 } else
Denys Vlasenko561f9c82011-06-21 16:38:29 +0200904#endif
Denys Vlasenko11540a82017-01-23 18:01:48 +0100905 if (opt & OPT_X) { /* sort by extension */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200906 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
Denys Vlasenko4029e212011-05-13 17:28:46 +0200907 }
908 if (dif == 0) {
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200909 /* sort by name, use as tie breaker for other sorts */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200910 if (ENABLE_LOCALE_SUPPORT)
911 dif = strcoll(d1->name, d2->name);
912 else
913 dif = strcmp(d1->name, d2->name);
Denys Vlasenko11540a82017-01-23 18:01:48 +0100914 } else {
915 /* Make dif fit into an int */
916 if (sizeof(dif) > sizeof(int)) {
917 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
918 /* shift leaving only "int" worth of bits */
919 /* (this requires dif != 0, and here it is nonzero) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200920 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100921 }
922 }
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100923 maybe_invert_and_ret:
Denys Vlasenko11540a82017-01-23 18:01:48 +0100924 return (opt & OPT_r) ? -(int)dif : (int)dif;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100925}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200926
927static void dnsort(struct dnode **dn, int size)
928{
929 qsort(dn, size, sizeof(*dn), sortcmp);
930}
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200931
932static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
933{
934 dnsort(dn, nfiles);
935 display_files(dn, nfiles);
936}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200937#else
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200938# define dnsort(dn, size) ((void)0)
939# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100940#endif
941
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100942/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200943static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100944{
945 struct dnode *dn, *cur, **dnp;
946 struct dirent *entry;
947 DIR *dir;
948 unsigned i, nfiles;
949
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100950 *nfiles_p = 0;
951 dir = warn_opendir(path);
952 if (dir == NULL) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200953 G.exit_code = EXIT_FAILURE;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100954 return NULL; /* could not open the dir */
955 }
956 dn = NULL;
957 nfiles = 0;
958 while ((entry = readdir(dir)) != NULL) {
959 char *fullname;
960
961 /* are we going to list the file- it may be . or .. or a hidden file */
962 if (entry->d_name[0] == '.') {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100963 if (!(option_mask32 & (OPT_a|OPT_A)))
964 continue; /* skip all dotfiles if no -a/-A */
965 if (!(option_mask32 & OPT_a)
966 && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100967 ) {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100968 continue; /* if only -A, skip . and .. but show other dotfiles */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100969 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100970 }
971 fullname = concat_path_file(path, entry->d_name);
972 cur = my_stat(fullname, bb_basename(fullname), 0);
973 if (!cur) {
974 free(fullname);
975 continue;
976 }
977 cur->fname_allocated = 1;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200978 cur->dn_next = dn;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100979 dn = cur;
980 nfiles++;
981 }
982 closedir(dir);
983
984 if (dn == NULL)
985 return NULL;
986
987 /* now that we know how many files there are
988 * allocate memory for an array to hold dnode pointers
989 */
990 *nfiles_p = nfiles;
991 dnp = dnalloc(nfiles);
992 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
993 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200994 dn = dn->dn_next;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100995 if (!dn)
996 break;
997 }
998
999 return dnp;
1000}
1001
Denys Vlasenko4029e212011-05-13 17:28:46 +02001002#if ENABLE_DESKTOP
1003/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
1004 * If any of the -l, -n, -s options is specified, each list
1005 * of files within the directory shall be preceded by a
1006 * status line indicating the number of file system blocks
1007 * occupied by files in the directory in 512-byte units if
1008 * the -k option is not specified, or 1024-byte units if the
1009 * -k option is specified, rounded up to the next integral
1010 * number of units.
1011 */
1012/* by Jorgen Overgaard (jorgen AT antistaten.se) */
1013static off_t calculate_blocks(struct dnode **dn)
1014{
1015 uoff_t blocks = 1;
1016 if (dn) {
1017 while (*dn) {
1018 /* st_blocks is in 512 byte blocks */
1019 blocks += (*dn)->dn_blocks;
1020 dn++;
1021 }
1022 }
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001023
Denys Vlasenko4029e212011-05-13 17:28:46 +02001024 /* Even though standard says use 512 byte blocks, coreutils use 1k */
1025 /* Actually, we round up by calculating (blocks + 1) / 2,
1026 * "+ 1" was done when we initialized blocks to 1 */
1027 return blocks >> 1;
1028}
1029#endif
1030
1031static void scan_and_display_dirs_recur(struct dnode **dn, int first)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001032{
1033 unsigned nfiles;
1034 struct dnode **subdnp;
1035
1036 for (; *dn; dn++) {
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001037 if ((G.all_fmt & DISP_DIRNAME)
1038 || (option_mask32 & OPT_R)
1039 ) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001040 if (!first)
1041 bb_putchar('\n');
1042 first = 0;
1043 printf("%s:\n", (*dn)->fullname);
1044 }
Denys Vlasenko4029e212011-05-13 17:28:46 +02001045 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001046#if ENABLE_DESKTOP
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001047 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG
1048 || (option_mask32 & OPT_s)
1049 ) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001050 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001051 }
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001052#endif
1053 if (nfiles > 0) {
1054 /* list all files at this level */
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001055 sort_and_display_files(subdnp, nfiles);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001056
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001057 if (ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001058 && (option_mask32 & OPT_R)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001059 ) {
1060 struct dnode **dnd;
1061 unsigned dndirs;
1062 /* recursive - list the sub-dirs */
1063 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1064 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1065 if (dndirs > 0) {
1066 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001067 scan_and_display_dirs_recur(dnd, 0);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001068 /* free the array of dnode pointers to the dirs */
1069 free(dnd);
1070 }
1071 }
1072 /* free the dnodes and the fullname mem */
1073 dfree(subdnp);
1074 }
1075 }
1076}
1077
1078
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001079int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +00001080{
Glenn L McGrath792cae52004-01-18 05:15:16 +00001081 struct dnode **dnd;
1082 struct dnode **dnf;
1083 struct dnode **dnp;
1084 struct dnode *dn;
1085 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001086 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001087 unsigned nfiles;
1088 unsigned dnfiles;
1089 unsigned dndirs;
1090 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001091#if ENABLE_FEATURE_LS_COLOR
1092 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1093 /* coreutils 6.10:
1094 * # ls --color=BOGUS
1095 * ls: invalid argument 'BOGUS' for '--color'
1096 * Valid arguments are:
1097 * 'always', 'yes', 'force'
1098 * 'never', 'no', 'none'
1099 * 'auto', 'tty', 'if-tty'
1100 * (and substrings: "--color=alwa" work too)
1101 */
1102 static const char ls_longopts[] ALIGN1 =
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001103 "full-time\0" No_argument "\xff"
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001104 "group-directories-first\0" No_argument "\xfe"
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001105 "color\0" Optional_argument "\xfd"
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001106 ;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001107 static const char color_str[] ALIGN1 =
1108 "always\0""yes\0""force\0"
1109 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001110 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001111 const char *color_opt = color_str; /* "always" */
1112#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001113
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001114 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001115
Denys Vlasenko28055022010-01-04 20:49:58 +01001116 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001117
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001118#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001119 /* obtain the terminal width */
Denys Vlasenko641caae2015-10-23 01:44:22 +02001120 G_terminal_width = get_terminal_width(STDIN_FILENO);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001121 /* go one less... */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001122 G_terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001123#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001124
Eric Andersen11c65522000-09-07 17:24:47 +00001125 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001126 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001127 opt_complementary =
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001128 /* --full-time implies -l */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001129 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS("\xff""l"))
Denys Vlasenkof3137462010-12-19 05:05:34 +01001130 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1131 * in some pairs of opts, only last one takes effect:
1132 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001133 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001134 // ":m-l:l-m" - we don't have -m
1135 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001136 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1137 ":C-1:1-C" /* bycols/oneline */
1138 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
Denys Vlasenkob47b3ce2011-08-10 00:51:29 +02001139 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001140 /* -w NUM: */
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001141 IF_FEATURE_LS_WIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001142 opt = getopt32(argv, ls_options
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001143 IF_FEATURE_LS_WIDTH(, NULL, &G_terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001144 IF_FEATURE_LS_COLOR(, &color_opt)
1145 );
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001146#if 0 /* option bits debug */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001147 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 +01001148 if (opt & OPT_c ) bb_error_msg("-c");
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001149 if (opt & OPT_l ) bb_error_msg("-l");
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001150 if (opt & OPT_H ) bb_error_msg("-H");
1151 if (opt & OPT_color ) bb_error_msg("--color");
1152 if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
1153 if (opt & OPT_full_time ) bb_error_msg("--full-time");
1154 exit(0);
1155#endif
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001156 for (i = 0; opt_flags[i] != 0xff; i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001157 if (opt & (1 << i)) {
Denys Vlasenko982aa262010-12-19 21:54:39 +01001158 uint32_t flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001159
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001160 if (flags & STYLE_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001161 G.all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001162
Denys Vlasenko2a816392011-05-13 17:28:09 +02001163 G.all_fmt |= flags;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001164 }
Eric Andersen11c65522000-09-07 17:24:47 +00001165 }
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +01001166#if ENABLE_SELINUX
1167 if (opt & OPT_Z) {
1168 if (!is_selinux_enabled())
1169 option_mask32 &= ~OPT_Z;
1170 }
1171#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001172
Denis Vlasenko5c759602006-10-28 12:37:16 +00001173#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +02001174 /* set G_show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001175 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1176 char *p = getenv("LS_COLORS");
1177 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001178 if (!p || (p[0] && strcmp(p, "none") != 0))
Denys Vlasenko2a816392011-05-13 17:28:09 +02001179 G_show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001180 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001181 if (opt & OPT_color) {
1182 if (color_opt[0] == 'n')
Denys Vlasenko2a816392011-05-13 17:28:09 +02001183 G_show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001184 else switch (index_in_substrings(color_str, color_opt)) {
1185 case 3:
1186 case 4:
1187 case 5:
1188 if (isatty(STDOUT_FILENO)) {
1189 case 0:
1190 case 1:
1191 case 2:
Denys Vlasenko2a816392011-05-13 17:28:09 +02001192 G_show_color = 1;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001193 }
1194 }
Paul Fox156dc412005-08-01 19:33:30 +00001195 }
1196#endif
1197
Eric Andersen11c65522000-09-07 17:24:47 +00001198 /* sort out which command line options take precedence */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001199 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1200 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001201 if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) { /* not -l? */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001202 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1203 /* when to sort by time? -t[cu] sorts by time even with -l */
1204 /* (this is achieved by opt_flags[] element for -t) */
1205 /* without -l, bare -c or -u enable sort too */
1206 /* (with -l, bare -c or -u just select which time to show) */
1207 if (opt & (OPT_c|OPT_u)) {
Denys Vlasenko11540a82017-01-23 18:01:48 +01001208 option_mask32 |= OPT_t;
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001209 }
1210 }
1211 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001212
Denys Vlasenkof3137462010-12-19 05:05:34 +01001213 /* choose a display format if one was not already specified by an option */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001214 if (!(G.all_fmt & STYLE_MASK))
1215 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001216
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001217 argv += optind;
1218 if (!argv[0])
1219 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001220
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001221 if (argv[1])
Denys Vlasenko2a816392011-05-13 17:28:09 +02001222 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001223
Denis Vlasenko5c759602006-10-28 12:37:16 +00001224 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001225 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001226 nfiles = 0;
1227 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001228 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001229 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001230 !((G.all_fmt & STYLE_MASK) == STYLE_LONG
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001231 || (option_mask32 & (OPT_s|OPT_F))
Denys Vlasenkoea684c62011-03-12 03:12:36 +01001232 )
Denys Vlasenko982aa262010-12-19 21:54:39 +01001233 /* ... or if -H: */
1234 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001235 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001236 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001237 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001238 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001239 continue;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001240 /*cur->fname_allocated = 0; - already is */
1241 cur->dn_next = dn;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001242 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001243 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001244 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001245
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001246 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1247 if (nfiles == 0)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001248 return G.exit_code;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001249
Eric Andersen11c65522000-09-07 17:24:47 +00001250 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001251 * allocate memory for an array to hold dnode pointers
1252 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001253 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001254 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1255 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001256 dn = dn->dn_next;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001257 if (!dn)
1258 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001259 }
1260
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001261 if (option_mask32 & OPT_d) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001262 sort_and_display_files(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001263 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001264 dnd = splitdnarray(dnp, SPLIT_DIR);
1265 dnf = splitdnarray(dnp, SPLIT_FILE);
1266 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001267 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001268 if (dnfiles > 0) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001269 sort_and_display_files(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001270 if (ENABLE_FEATURE_CLEAN_UP)
1271 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001272 }
1273 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001274 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001275 scan_and_display_dirs_recur(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001276 if (ENABLE_FEATURE_CLEAN_UP)
1277 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001278 }
1279 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001280
Rob Landley26314862006-05-02 19:46:52 +00001281 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001282 dfree(dnp);
Denys Vlasenko2a816392011-05-13 17:28:09 +02001283 return G.exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001284}