blob: 48f5eb482c49bb622d7f464e32834763907e1ca1 [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
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020031//config: bool "ls (14 kb)"
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010032//config: default y
33//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020034//config: ls is used to list the contents of directories.
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010035//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
Denys Vlasenko72089cf2017-07-21 09:50:55 +020061//config: Allow ls to sort file names alphabetically.
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010062//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
Denys Vlasenko72089cf2017-07-21 09:50:55 +020068//config: Allow ls to display timestamps for files.
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010069//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
Denys Vlasenko72089cf2017-07-21 09:50:55 +020075//config: Allow ls to display username/groupname for files.
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010076//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
Denys Vlasenko72089cf2017-07-21 09:50:55 +020082//config: This enables the --color option to ls.
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010083//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
Denys Vlasenko72089cf2017-07-21 09:50:55 +020089//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.
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010094
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"
Denys Vlasenko08ea7be2021-06-05 10:36:17 +0200112//usage: "\n -a Include names starting with ."
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200113//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"
Denys Vlasenko08ea7be2021-06-05 10:36:17 +0200116//usage: "\n -d List directory names, not contents"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100117//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 Vlasenko08ea7be2021-06-05 10:36:17 +0200125//usage: "\n -p Append / to directory names"
126//usage: "\n -F Append indicator (one of */=@|) to names"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100127//usage: )
Denys Vlasenko08ea7be2021-06-05 10:36:17 +0200128//usage: "\n -l Long format"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100129//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(
Denys Vlasenko08ea7be2021-06-05 10:36:17 +0200137//usage: "\n --full-time List full date/time"
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100138//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(
Denys Vlasenko08ea7be2021-06-05 10:36:17 +0200163//usage: "\n --color[={always,never,auto}]"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100164//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 {
Denys Vlasenko08ea7be2021-06-05 10:36:17 +0200190TERMINAL_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,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000195};
Eric Andersencc8ed391999-10-05 16:24:54 +0000196
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100197/* -Cadi1l Std options, busybox always supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100198/* -gnsxA Std options, busybox always supports */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100199/* -Q GNU option, busybox always supports */
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100200/* -k Std option, busybox always supports (by ignoring) */
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100201/* It means "for -s, show sizes in kbytes" */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100202/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
203/* since otherwise -s shows kbytes anyway */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200204/* -LHRctur Std options, busybox optionally supports */
205/* -Fp Std options, busybox optionally supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100206/* -SXvhTw GNU options, busybox optionally supports */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200207/* -T WIDTH Ignored (we don't use tabs on output) */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100208/* -Z SELinux mandated option, busybox optionally supports */
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200209#define ls_options \
210 "Cadi1lgnsxAk" /* 12 opts, total 12 */ \
211 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */ \
212 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */ \
213 IF_SELINUX("Z") /* 1, 16 */ \
214 "Q" /* 1, 17 */ \
215 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */ \
216 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */ \
217 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */ \
218 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */ \
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100219 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200220
Denis Vlasenko248ce912009-03-03 14:09:04 +0000221enum {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100222 OPT_C = (1 << 0),
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100223 OPT_a = (1 << 1),
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100224 OPT_d = (1 << 2),
Denys Vlasenkob60686c2017-01-23 18:34:11 +0100225 OPT_i = (1 << 3),
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100226 OPT_1 = (1 << 4),
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100227 OPT_l = (1 << 5),
Denis Vlasenko248ce912009-03-03 14:09:04 +0000228 OPT_g = (1 << 6),
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100229 OPT_n = (1 << 7),
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100230 OPT_s = (1 << 8),
Denys Vlasenko92c68982017-01-23 20:21:14 +0100231 OPT_x = (1 << 9),
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100232 OPT_A = (1 << 10),
Denys Vlasenko11540a82017-01-23 18:01:48 +0100233 //OPT_k = (1 << 11),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100234
Denys Vlasenko11540a82017-01-23 18:01:48 +0100235 OPTBIT_F = 12,
236 OPTBIT_p, /* 13 */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100237 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100238 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
Denys Vlasenko11540a82017-01-23 18:01:48 +0100239 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
240 OPTBIT_c, /* 17 */
241 OPTBIT_t, /* 18 */
242 OPTBIT_u, /* 19 */
243 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
244 OPTBIT_X, /* 21 */
245 OPTBIT_r, /* 22 */
246 OPTBIT_v, /* 23 */
247 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100248 OPTBIT_H, /* 25 */
Denys Vlasenkocd387f22011-02-27 04:10:00 +0100249 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100250 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100251 OPTBIT_w, /* 28 */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100252 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
253 OPTBIT_dirs_first,
254 OPTBIT_color, /* 31 */
255 /* with long opts, we use all 32 bits */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100256
Denys Vlasenko11540a82017-01-23 18:01:48 +0100257 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
258 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
259 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
260 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
261 OPT_Q = (1 << OPTBIT_Q),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100262 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100263 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
264 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
265 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
266 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
267 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
268 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100269 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
270 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
271 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100272 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_LS_WIDTH,
273 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_LS_WIDTH,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100274 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100275 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
276 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000277};
278
Eric Andersen11c65522000-09-07 17:24:47 +0000279/*
Denys Vlasenko2a816392011-05-13 17:28:09 +0200280 * a directory entry and its stat info
Eric Andersen11c65522000-09-07 17:24:47 +0000281 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200282struct dnode {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200283 const char *name; /* usually basename, but think "ls -l dir/file" */
284 const char *fullname; /* full name (usable for stat etc) */
285 struct dnode *dn_next; /* for linked list */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000286 IF_SELINUX(security_context_t sid;)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200287 smallint fname_allocated;
288
289 /* Used to avoid re-doing [l]stat at printout stage
290 * if we already collected needed data in scan stage:
291 */
292 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
293 mode_t dn_mode_stat; /* obtained with stat, or 0 */
294
295// struct stat dstat;
296// struct stat is huge. We don't need it in full.
297// At least we don't need st_dev and st_blksize,
298// but there are invisible fields as well
299// (such as nanosecond-resolution timespamps)
300// and padding, which we also don't want to store.
Denys Vlasenko08ea7be2021-06-05 10:36:17 +0200301// We also pre-parse dev_t dn_rdev (in glibc, it's huge).
Denys Vlasenko4029e212011-05-13 17:28:46 +0200302// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
Denys Vlasenko2a816392011-05-13 17:28:09 +0200303//
304 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
305 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
306 off_t dn_size;
307#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100308 time_t dn_time;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200309#endif
310 ino_t dn_ino;
311 blkcnt_t dn_blocks;
312 nlink_t dn_nlink;
313 uid_t dn_uid;
314 gid_t dn_gid;
315 int dn_rdev_maj;
316 int dn_rdev_min;
317// dev_t dn_dev;
318// blksize_t dn_blksize;
Eric Andersen11c65522000-09-07 17:24:47 +0000319};
Eric Andersen11c65522000-09-07 17:24:47 +0000320
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000321struct globals {
322#if ENABLE_FEATURE_LS_COLOR
323 smallint show_color;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200324# define G_show_color (G.show_color)
325#else
326# define G_show_color 0
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000327#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000328 smallint exit_code;
Denys Vlasenko9a64c332017-01-23 20:46:12 +0100329 smallint show_dirname;
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100330#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenko2a816392011-05-13 17:28:09 +0200331 unsigned terminal_width;
332# define G_terminal_width (G.terminal_width)
333#else
334# define G_terminal_width TERMINAL_WIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000335#endif
336#if ENABLE_FEATURE_LS_TIMESTAMPS
337 /* Do time() just once. Saves one syscall per file for "ls -l" */
338 time_t current_time_t;
339#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100340} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200341#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000342#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200343 setup_common_bufsiz(); \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200344 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000345 memset(&G, 0, sizeof(G)); \
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100346 IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \
Denys Vlasenko2a816392011-05-13 17:28:09 +0200347 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000348} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000349
Denys Vlasenko8187e012017-09-13 22:48:30 +0200350#define ESC "\033"
351
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000352
Denys Vlasenko4029e212011-05-13 17:28:46 +0200353/*** Output code ***/
Denys Vlasenko2a816392011-05-13 17:28:09 +0200354
Eric Andersen79565b62000-08-11 18:10:21 +0000355
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000356/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
357 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
358 * 3/7:multiplexed char/block device)
359 * and we use 0 for unknown and 15 for executables (see below) */
360#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200361/* un fi chr - dir - blk - file - link - sock - - exe */
362#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000363/* 036 black foreground 050 black background
364 037 red foreground 051 red background
365 040 green foreground 052 green background
366 041 brown foreground 053 brown background
367 042 blue foreground 054 blue background
368 043 magenta (purple) foreground 055 magenta background
369 044 cyan (light blue) foreground 056 cyan background
370 045 gray foreground 057 white background
371*/
372#define COLOR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200373 /*un fi chr - dir - blk - file - link - sock - - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000374 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
375 [TYPEINDEX(mode)])
376/* Select normal (0) [actually "reset all"] or bold (1)
377 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
378 * let's use 7 for "impossible" types, just for fun)
379 * Note: coreutils 6.9 uses inverted red for setuid binaries.
380 */
381#define ATTR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200382 /*un fi chr - dir - blk - file- link- sock- - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000383 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
384 [TYPEINDEX(mode)])
385
Denis Vlasenko5c759602006-10-28 12:37:16 +0000386#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000387/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000388static char fgcolor(mode_t mode)
389{
Rob Landley9947a242006-06-15 22:11:10 +0000390 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000391 return COLOR(0xF000); /* File is executable ... */
392 return COLOR(mode);
393}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000394static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000395{
Rob Landley9947a242006-06-15 22:11:10 +0000396 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000397 return ATTR(0xF000); /* File is executable ... */
398 return ATTR(mode);
399}
400#endif
401
Denys Vlasenko2a816392011-05-13 17:28:09 +0200402#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000403static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000404{
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100405 if (!(option_mask32 & (OPT_F|OPT_p)))
Eric Andersen11c65522000-09-07 17:24:47 +0000406 return '\0';
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100407
Rob Landley9947a242006-06-15 22:11:10 +0000408 if (S_ISDIR(mode))
409 return '/';
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100410 if (!(option_mask32 & OPT_F))
Rob Landley9947a242006-06-15 22:11:10 +0000411 return '\0';
412 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000413 return '*';
414 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000415}
416#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000417
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100418static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000419{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100420 unsigned len;
421 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000422
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100423 // TODO: quote tab as \t, etc, if -Q
Denys Vlasenko349d72c2018-09-30 16:56:56 +0200424 name = printable_string2(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000425
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100426 if (!(option_mask32 & OPT_Q)) {
427 return uni_stat.unicode_width;
428 }
429
430 len = 2 + uni_stat.unicode_width;
431 while (*name) {
432 if (*name == '"' || *name == '\\') {
433 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000434 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100435 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000436 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100437 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000438}
439
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100440/* Return the number of used columns.
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100441 * Note that only columnar output uses return value.
442 * -l and -1 modes don't care.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100443 * coreutils 7.2 also supports:
444 * ls -b (--escape) = octal escapes (although it doesn't look like working)
445 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200446 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100447static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200448{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100449 unsigned len;
450 uni_stat_t uni_stat;
451
452 // TODO: quote tab as \t, etc, if -Q
Denys Vlasenko349d72c2018-09-30 16:56:56 +0200453 name = printable_string2(&uni_stat, name);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100454
455 if (!(option_mask32 & OPT_Q)) {
Ron Yorstoncad3fc72021-02-03 20:47:14 +0100456 fputs_stdout(name);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100457 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200458 }
459
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100460 len = 2 + uni_stat.unicode_width;
461 putchar('"');
462 while (*name) {
463 if (*name == '"' || *name == '\\') {
464 putchar('\\');
465 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000466 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700467 putchar(*name);
468 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000469 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100470 putchar('"');
471 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000472}
473
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100474/* Return the number of used columns.
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100475 * Note that only columnar output uses return value,
476 * -l and -1 modes don't care.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100477 */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200478static NOINLINE unsigned display_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000479{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200480 unsigned column = 0;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200481 char *lpath;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100482 int opt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000483#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200484 struct stat statbuf;
Denys Vlasenko82d1c1f2017-12-31 17:30:02 +0100485#endif
486#if ENABLE_FEATURE_LS_FILETYPES
487 char append = append_char(dn->dn_mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000488#endif
489
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100490 opt = option_mask32;
Eric Andersen11c65522000-09-07 17:24:47 +0000491
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000492 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100493 * does not appear *inside* the "ls -l" line */
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200494 lpath = NULL;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100495 if (opt & OPT_l)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200496 if (S_ISLNK(dn->dn_mode))
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000497 lpath = xmalloc_readlink_or_warn(dn->fullname);
498
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100499 if (opt & OPT_i) /* show inode# */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200500 column += printf("%7llu ", (long long) dn->dn_ino);
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100501//TODO: -h should affect -s too:
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100502 if (opt & OPT_s) /* show allocated blocks */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200503 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100504 if (opt & OPT_l) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100505 /* long listing: show mode */
Denys Vlasenko59ac4672021-09-17 01:13:58 +0200506 char modestr[12];
Denys Vlasenko6279aec2021-09-17 17:10:38 +0200507 column += printf("%-10s ", bb_mode_string(modestr, dn->dn_mode));
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100508 /* long listing: show number of links */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200509 column += printf("%4lu ", (long) dn->dn_nlink);
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100510 /* long listing: show user/group */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100511 if (opt & OPT_n) {
512 if (opt & OPT_g)
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100513 column += printf("%-8u ", (int) dn->dn_gid);
514 else
515 column += printf("%-8u %-8u ",
516 (int) dn->dn_uid,
517 (int) dn->dn_gid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000518 }
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100519#if ENABLE_FEATURE_LS_USERNAME
520 else {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100521 if (opt & OPT_g) {
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100522 column += printf("%-8.8s ",
523 get_cached_groupname(dn->dn_gid));
524 } else {
525 column += printf("%-8.8s %-8.8s ",
526 get_cached_username(dn->dn_uid),
527 get_cached_groupname(dn->dn_gid));
528 }
529 }
Eric Andersen11c65522000-09-07 17:24:47 +0000530#endif
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100531#if ENABLE_SELINUX
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100532 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100533 if (opt & OPT_Z) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100534 column += printf("%-32s ", dn->sid ? dn->sid : "?");
535 freecon(dn->sid);
536 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100537 if (opt & OPT_l) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100538#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100539 /* long listing: show size */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200540 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000541 column += printf("%4u, %3u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200542 dn->dn_rdev_maj,
543 dn->dn_rdev_min);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000544 } else {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100545 if (opt & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100546 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200547 /* print size, show one fractional, use suffixes */
548 make_human_readable_str(dn->dn_size, 1, 0)
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200549 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000550 } else {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200551 column += printf("%9"OFF_FMT"u ", dn->dn_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000552 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000553 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000554#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100555 /* long listing: show {m,c,a}time */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100556 if (opt & OPT_full_time) { /* --full-time */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100557 /* coreutils 8.4 ls --full-time prints:
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100558 * 2009-07-13 17:49:27.000000000 +0200
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100559 * we don't show fractional seconds.
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100560 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100561 char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")];
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100562 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z",
563 localtime(&dn->dn_time));
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100564 column += printf("%s ", buf);
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100565 } else { /* ordinary time format */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100566 /* G.current_time_t is ~== time(NULL) */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100567 char *filetime = ctime(&dn->dn_time);
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100568 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100569 time_t age = G.current_time_t - dn->dn_time;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000570 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
Denys Vlasenko8e92df12015-02-16 15:36:25 +0100571 /* less than 6 months old */
572 /* "mmm dd hh:mm " */
573 printf("%.12s ", filetime + 4);
574 } else {
575 /* "mmm dd yyyy " */
576 /* "mmm dd yyyyy " after year 9999 :) */
577 strchr(filetime + 20, '\n')[0] = ' ';
578 printf("%.7s%6s", filetime + 4, filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000579 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000580 column += 13;
581 }
Eric Andersen11c65522000-09-07 17:24:47 +0000582#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100583 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100584
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000585#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200586 if (G_show_color) {
587 mode_t mode = dn->dn_mode_lstat;
588 if (!mode)
589 if (lstat(dn->fullname, &statbuf) == 0)
590 mode = statbuf.st_mode;
Denys Vlasenko8187e012017-09-13 22:48:30 +0200591 printf(ESC"[%u;%um", bold(mode), fgcolor(mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000592 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100593#endif
594 column += print_name(dn->name);
Denys Vlasenko2a816392011-05-13 17:28:09 +0200595 if (G_show_color) {
Denys Vlasenko8187e012017-09-13 22:48:30 +0200596 printf(ESC"[m");
James Youngmana4bc10c2010-12-20 01:36:16 +0100597 }
598
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200599 if (lpath) {
600 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000601#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100602 if ((opt & (OPT_F|OPT_p))
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100603 || G_show_color
604 ) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200605 mode_t mode = dn->dn_mode_stat;
606 if (!mode)
607 if (stat(dn->fullname, &statbuf) == 0)
608 mode = statbuf.st_mode;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200609# if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200610 append = append_char(mode);
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200611# endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200612# if ENABLE_FEATURE_LS_COLOR
613 if (G_show_color) {
Denys Vlasenko8187e012017-09-13 22:48:30 +0200614 printf(ESC"[%u;%um", bold(mode), fgcolor(mode));
Denys Vlasenko2a816392011-05-13 17:28:09 +0200615 }
616# endif
Eric Andersen11c65522000-09-07 17:24:47 +0000617 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200618#endif
619 column += print_name(lpath) + 4;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200620 free(lpath);
621 if (G_show_color) {
Denys Vlasenko8187e012017-09-13 22:48:30 +0200622 printf(ESC"[m");
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200623 }
Eric Andersen11c65522000-09-07 17:24:47 +0000624 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000625#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100626 if (opt & (OPT_F|OPT_p)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000627 if (append) {
628 putchar(append);
629 column++;
630 }
631 }
632#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000633
Glenn L McGrath4d001292003-01-06 01:11:50 +0000634 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000635}
636
Denys Vlasenko4029e212011-05-13 17:28:46 +0200637static void display_files(struct dnode **dn, unsigned nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100638{
639 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100640 unsigned column;
641 unsigned nexttab;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100642 unsigned column_width = 0; /* used only by coulmnal output */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100643
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100644 if (option_mask32 & (OPT_l|OPT_1)) {
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100645 ncols = 1;
646 } else {
647 /* find the longest file name, use that as the column width */
648 for (i = 0; dn[i]; i++) {
649 int len = calc_name_len(dn[i]->name);
650 if (column_width < len)
651 column_width = len;
652 }
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100653 column_width += 2
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100654 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100655 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
656 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
657 ;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200658 ncols = (unsigned)G_terminal_width / column_width;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100659 }
660
661 if (ncols > 1) {
662 nrows = nfiles / ncols;
663 if (nrows * ncols < nfiles)
664 nrows++; /* round up fractionals */
665 } else {
666 nrows = nfiles;
667 ncols = 1;
668 }
669
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100670 column = 0;
671 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100672 for (row = 0; row < nrows; row++) {
673 for (nc = 0; nc < ncols; nc++) {
674 /* reach into the array based on the column and row */
Denys Vlasenko92c68982017-01-23 20:21:14 +0100675 if (option_mask32 & OPT_x)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100676 i = (row * ncols) + nc; /* display across row */
677 else
678 i = (nc * nrows) + row; /* display by column */
679 if (i < nfiles) {
680 if (column > 0) {
681 nexttab -= column;
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100682 printf("%*s", nexttab, "");
683 column += nexttab;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100684 }
685 nexttab = column + column_width;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200686 column += display_single(dn[i]);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100687 }
688 }
689 putchar('\n');
690 column = 0;
691 }
692}
693
694
Denys Vlasenko4029e212011-05-13 17:28:46 +0200695/*** Dir scanning code ***/
696
697static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100698{
Denys Vlasenko4029e212011-05-13 17:28:46 +0200699 struct stat statbuf;
700 struct dnode *cur;
701
702 cur = xzalloc(sizeof(*cur));
703 cur->fullname = fullname;
704 cur->name = name;
705
706 if ((option_mask32 & OPT_L) || force_follow) {
707#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100708 if (option_mask32 & OPT_Z) {
Denys Vlasenko69675782013-01-14 01:34:48 +0100709 getfilecon(fullname, &cur->sid);
Denys Vlasenko4029e212011-05-13 17:28:46 +0200710 }
711#endif
712 if (stat(fullname, &statbuf)) {
713 bb_simple_perror_msg(fullname);
714 G.exit_code = EXIT_FAILURE;
715 free(cur);
716 return NULL;
717 }
718 cur->dn_mode_stat = statbuf.st_mode;
719 } else {
720#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100721 if (option_mask32 & OPT_Z) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200722 lgetfilecon(fullname, &cur->sid);
723 }
724#endif
725 if (lstat(fullname, &statbuf)) {
726 bb_simple_perror_msg(fullname);
727 G.exit_code = EXIT_FAILURE;
728 free(cur);
729 return NULL;
730 }
731 cur->dn_mode_lstat = statbuf.st_mode;
732 }
733
734 /* cur->dstat = statbuf: */
735 cur->dn_mode = statbuf.st_mode ;
736 cur->dn_size = statbuf.st_size ;
737#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100738 cur->dn_time = statbuf.st_mtime ;
739 if (option_mask32 & OPT_u)
740 cur->dn_time = statbuf.st_atime;
741 if (option_mask32 & OPT_c)
742 cur->dn_time = statbuf.st_ctime;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200743#endif
744 cur->dn_ino = statbuf.st_ino ;
745 cur->dn_blocks = statbuf.st_blocks;
746 cur->dn_nlink = statbuf.st_nlink ;
747 cur->dn_uid = statbuf.st_uid ;
748 cur->dn_gid = statbuf.st_gid ;
749 cur->dn_rdev_maj = major(statbuf.st_rdev);
750 cur->dn_rdev_min = minor(statbuf.st_rdev);
751
752 return cur;
753}
754
755static unsigned count_dirs(struct dnode **dn, int which)
756{
757 unsigned dirs, all;
758
759 if (!dn)
760 return 0;
761
762 dirs = all = 0;
763 for (; *dn; dn++) {
764 const char *name;
765
766 all++;
767 if (!S_ISDIR((*dn)->dn_mode))
768 continue;
769
770 name = (*dn)->name;
771 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
772 /* or if it's not . or .. */
773 || name[0] != '.'
774 || (name[1] && (name[1] != '.' || name[2]))
775 ) {
776 dirs++;
777 }
778 }
779 return which != SPLIT_FILE ? dirs : all - dirs;
780}
781
782/* get memory to hold an array of pointers */
783static struct dnode **dnalloc(unsigned num)
784{
785 if (num < 1)
786 return NULL;
787
788 num++; /* so that we have terminating NULL */
789 return xzalloc(num * sizeof(struct dnode *));
790}
791
792#if ENABLE_FEATURE_LS_RECURSIVE
793static void dfree(struct dnode **dnp)
794{
795 unsigned i;
796
797 if (dnp == NULL)
798 return;
799
800 for (i = 0; dnp[i]; i++) {
801 struct dnode *cur = dnp[i];
802 if (cur->fname_allocated)
803 free((char*)cur->fullname);
804 free(cur);
805 }
806 free(dnp);
807}
808#else
809#define dfree(...) ((void)0)
810#endif
811
812/* Returns NULL-terminated malloced vector of pointers (or NULL) */
813static struct dnode **splitdnarray(struct dnode **dn, int which)
814{
815 unsigned dncnt, d;
816 struct dnode **dnp;
817
818 if (dn == NULL)
819 return NULL;
820
821 /* count how many dirs or files there are */
822 dncnt = count_dirs(dn, which);
823
824 /* allocate a file array and a dir array */
825 dnp = dnalloc(dncnt);
826
827 /* copy the entrys into the file or dir array */
828 for (d = 0; *dn; dn++) {
829 if (S_ISDIR((*dn)->dn_mode)) {
830 const char *name;
831
832 if (which == SPLIT_FILE)
833 continue;
834
835 name = (*dn)->name;
836 if ((which & SPLIT_DIR) /* any dir... */
837 /* ... or not . or .. */
838 || name[0] != '.'
839 || (name[1] && (name[1] != '.' || name[2]))
840 ) {
841 dnp[d++] = *dn;
842 }
843 } else
844 if (which == SPLIT_FILE) {
845 dnp[d++] = *dn;
846 }
847 }
848 return dnp;
849}
850
851#if ENABLE_FEATURE_LS_SORTFILES
852static int sortcmp(const void *a, const void *b)
853{
854 struct dnode *d1 = *(struct dnode **)a;
855 struct dnode *d2 = *(struct dnode **)b;
Denys Vlasenko11540a82017-01-23 18:01:48 +0100856 unsigned opt = option_mask32;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200857 off_t dif;
858
Denys Vlasenko11540a82017-01-23 18:01:48 +0100859 dif = 0; /* assume sort by name */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200860 // TODO: use pre-initialized function pointer
861 // instead of branch forest
Denys Vlasenko11540a82017-01-23 18:01:48 +0100862 if (opt & OPT_dirs_first) {
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100863 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
864 if (dif != 0)
865 goto maybe_invert_and_ret;
866 }
867
Denys Vlasenko11540a82017-01-23 18:01:48 +0100868 if (opt & OPT_S) { /* sort by size */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200869 dif = (d2->dn_size - d1->dn_size);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200870 } else
Denys Vlasenko11540a82017-01-23 18:01:48 +0100871 if (opt & OPT_t) { /* sort by time */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100872 dif = (d2->dn_time - d1->dn_time);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200873 } else
Denys Vlasenko1e18a012011-06-21 17:12:52 +0200874#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
Denys Vlasenko11540a82017-01-23 18:01:48 +0100875 if (opt & OPT_v) { /* sort by version */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200876 dif = strverscmp(d1->name, d2->name);
877 } else
Denys Vlasenko561f9c82011-06-21 16:38:29 +0200878#endif
Denys Vlasenko11540a82017-01-23 18:01:48 +0100879 if (opt & OPT_X) { /* sort by extension */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200880 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
Denys Vlasenko4029e212011-05-13 17:28:46 +0200881 }
882 if (dif == 0) {
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200883 /* sort by name, use as tie breaker for other sorts */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200884 if (ENABLE_LOCALE_SUPPORT)
885 dif = strcoll(d1->name, d2->name);
886 else
887 dif = strcmp(d1->name, d2->name);
Denys Vlasenko11540a82017-01-23 18:01:48 +0100888 } else {
889 /* Make dif fit into an int */
890 if (sizeof(dif) > sizeof(int)) {
891 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
892 /* shift leaving only "int" worth of bits */
893 /* (this requires dif != 0, and here it is nonzero) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200894 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100895 }
896 }
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100897 maybe_invert_and_ret:
Denys Vlasenko11540a82017-01-23 18:01:48 +0100898 return (opt & OPT_r) ? -(int)dif : (int)dif;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100899}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200900
901static void dnsort(struct dnode **dn, int size)
902{
903 qsort(dn, size, sizeof(*dn), sortcmp);
904}
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200905
906static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
907{
908 dnsort(dn, nfiles);
909 display_files(dn, nfiles);
910}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200911#else
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200912# define dnsort(dn, size) ((void)0)
913# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100914#endif
915
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100916/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200917static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100918{
919 struct dnode *dn, *cur, **dnp;
920 struct dirent *entry;
921 DIR *dir;
922 unsigned i, nfiles;
923
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100924 *nfiles_p = 0;
925 dir = warn_opendir(path);
926 if (dir == NULL) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200927 G.exit_code = EXIT_FAILURE;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100928 return NULL; /* could not open the dir */
929 }
930 dn = NULL;
931 nfiles = 0;
932 while ((entry = readdir(dir)) != NULL) {
933 char *fullname;
934
935 /* are we going to list the file- it may be . or .. or a hidden file */
936 if (entry->d_name[0] == '.') {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100937 if (!(option_mask32 & (OPT_a|OPT_A)))
938 continue; /* skip all dotfiles if no -a/-A */
939 if (!(option_mask32 & OPT_a)
940 && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100941 ) {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100942 continue; /* if only -A, skip . and .. but show other dotfiles */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100943 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100944 }
945 fullname = concat_path_file(path, entry->d_name);
Denys Vlasenko0231e342021-06-25 19:48:34 +0200946 cur = my_stat(fullname, bb_basename(fullname), 0);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100947 if (!cur) {
948 free(fullname);
949 continue;
950 }
951 cur->fname_allocated = 1;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200952 cur->dn_next = dn;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100953 dn = cur;
954 nfiles++;
955 }
956 closedir(dir);
957
958 if (dn == NULL)
959 return NULL;
960
961 /* now that we know how many files there are
962 * allocate memory for an array to hold dnode pointers
963 */
964 *nfiles_p = nfiles;
965 dnp = dnalloc(nfiles);
966 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
967 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200968 dn = dn->dn_next;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100969 if (!dn)
970 break;
971 }
972
973 return dnp;
974}
975
Denys Vlasenko4029e212011-05-13 17:28:46 +0200976#if ENABLE_DESKTOP
977/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
978 * If any of the -l, -n, -s options is specified, each list
979 * of files within the directory shall be preceded by a
980 * status line indicating the number of file system blocks
981 * occupied by files in the directory in 512-byte units if
982 * the -k option is not specified, or 1024-byte units if the
983 * -k option is specified, rounded up to the next integral
984 * number of units.
985 */
986/* by Jorgen Overgaard (jorgen AT antistaten.se) */
987static off_t calculate_blocks(struct dnode **dn)
988{
989 uoff_t blocks = 1;
990 if (dn) {
991 while (*dn) {
992 /* st_blocks is in 512 byte blocks */
993 blocks += (*dn)->dn_blocks;
994 dn++;
995 }
996 }
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000997
Denys Vlasenko4029e212011-05-13 17:28:46 +0200998 /* Even though standard says use 512 byte blocks, coreutils use 1k */
999 /* Actually, we round up by calculating (blocks + 1) / 2,
1000 * "+ 1" was done when we initialized blocks to 1 */
1001 return blocks >> 1;
1002}
1003#endif
1004
1005static void scan_and_display_dirs_recur(struct dnode **dn, int first)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001006{
1007 unsigned nfiles;
1008 struct dnode **subdnp;
1009
1010 for (; *dn; dn++) {
Denys Vlasenko9a64c332017-01-23 20:46:12 +01001011 if (G.show_dirname || (option_mask32 & OPT_R)) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001012 if (!first)
1013 bb_putchar('\n');
1014 first = 0;
1015 printf("%s:\n", (*dn)->fullname);
1016 }
Denys Vlasenko4029e212011-05-13 17:28:46 +02001017 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001018#if ENABLE_DESKTOP
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001019 if (option_mask32 & (OPT_s|OPT_l)) {
Lauri Kasanena380aac2018-08-01 19:51:17 +03001020 if (option_mask32 & OPT_h) {
1021 printf("total %-"HUMAN_READABLE_MAX_WIDTH_STR"s\n",
1022 /* print size, no fractions, use suffixes */
1023 make_human_readable_str(calculate_blocks(subdnp) * 1024,
1024 0, 0)
1025 );
1026 } else {
1027 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1028 }
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001029 }
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001030#endif
1031 if (nfiles > 0) {
1032 /* list all files at this level */
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001033 sort_and_display_files(subdnp, nfiles);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001034
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001035 if (ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001036 && (option_mask32 & OPT_R)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001037 ) {
1038 struct dnode **dnd;
1039 unsigned dndirs;
1040 /* recursive - list the sub-dirs */
1041 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1042 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1043 if (dndirs > 0) {
1044 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001045 scan_and_display_dirs_recur(dnd, 0);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001046 /* free the array of dnode pointers to the dirs */
1047 free(dnd);
1048 }
1049 }
1050 /* free the dnodes and the fullname mem */
1051 dfree(subdnp);
1052 }
1053 }
1054}
1055
1056
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001057int ls_main(int argc UNUSED_PARAM, char **argv)
Denys Vlasenkob13b6182017-01-25 04:52:45 +01001058{ /* ^^^^^^^^^^^^^^^^^ note: if FTPD, argc can be wrong, see ftpd.c */
Glenn L McGrath792cae52004-01-18 05:15:16 +00001059 struct dnode **dnd;
1060 struct dnode **dnf;
1061 struct dnode **dnp;
1062 struct dnode *dn;
1063 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001064 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001065 unsigned nfiles;
1066 unsigned dnfiles;
1067 unsigned dndirs;
1068 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001069#if ENABLE_FEATURE_LS_COLOR
1070 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1071 /* coreutils 6.10:
1072 * # ls --color=BOGUS
1073 * ls: invalid argument 'BOGUS' for '--color'
1074 * Valid arguments are:
1075 * 'always', 'yes', 'force'
1076 * 'never', 'no', 'none'
1077 * 'auto', 'tty', 'if-tty'
1078 * (and substrings: "--color=alwa" work too)
1079 */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001080 static const char color_str[] ALIGN1 =
1081 "always\0""yes\0""force\0"
1082 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001083 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001084 const char *color_opt = color_str; /* "always" */
1085#endif
Laurent Bercot25999372017-05-26 16:50:53 +02001086#if ENABLE_LONG_OPTS
1087 static const char ls_longopts[] ALIGN1 =
1088 "full-time\0" No_argument "\xff"
1089 "group-directories-first\0" No_argument "\xfe"
Denys Vlasenko9501bc72019-05-26 13:53:41 +02001090 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
Laurent Bercot25999372017-05-26 16:50:53 +02001091 ;
1092#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001093
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001094 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001095
Denys Vlasenko28055022010-01-04 20:49:58 +01001096 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001097
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001098#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001099 /* obtain the terminal width */
Denys Vlasenko641caae2015-10-23 01:44:22 +02001100 G_terminal_width = get_terminal_width(STDIN_FILENO);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001101 /* go one less... */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001102 G_terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001103#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001104
Eric Andersen11c65522000-09-07 17:24:47 +00001105 /* process options */
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001106 opt = getopt32long(argv, "^"
1107 ls_options
1108 "\0"
1109 /* -n and -g imply -l */
1110 "nl:gl"
1111 /* --full-time implies -l */
1112 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l"))
1113 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1114 * in some pairs of opts, only last one takes effect:
1115 */
1116 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
1117 // ":m-l:l-m" - we don't have -m
1118 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1119 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1120 ":C-1:1-C" /* bycols/oneline */
1121 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1122 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
1123 /* -w NUM: */
1124 IF_FEATURE_LS_WIDTH(":w+")
1125 , ls_longopts
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001126 IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001127 IF_FEATURE_LS_COLOR(, &color_opt)
1128 );
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001129#if 0 /* option bits debug */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001130 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 +01001131 if (opt & OPT_c ) bb_error_msg("-c");
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001132 if (opt & OPT_l ) bb_error_msg("-l");
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001133 if (opt & OPT_H ) bb_error_msg("-H");
1134 if (opt & OPT_color ) bb_error_msg("--color");
1135 if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
1136 if (opt & OPT_full_time ) bb_error_msg("--full-time");
1137 exit(0);
1138#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001139
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +01001140#if ENABLE_SELINUX
1141 if (opt & OPT_Z) {
1142 if (!is_selinux_enabled())
1143 option_mask32 &= ~OPT_Z;
1144 }
1145#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001146
Denis Vlasenko5c759602006-10-28 12:37:16 +00001147#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +02001148 /* set G_show_color = 1/0 */
Sören Tempel3d9c6492021-05-23 14:14:10 +02001149 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && !is_TERM_dumb()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +00001150 char *p = getenv("LS_COLORS");
1151 /* LS_COLORS is unset, or (not empty && not "none") ? */
Sören Tempel3d9c6492021-05-23 14:14:10 +02001152 if (!p || (p[0] && strcmp(p, "none") != 0)) {
1153 if (isatty(STDOUT_FILENO)) {
1154 /* check isatty() last because it's expensive (syscall) */
1155 G_show_color = 1;
1156 }
1157 }
Denis Vlasenko5c759602006-10-28 12:37:16 +00001158 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001159 if (opt & OPT_color) {
1160 if (color_opt[0] == 'n')
Denys Vlasenko2a816392011-05-13 17:28:09 +02001161 G_show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001162 else switch (index_in_substrings(color_str, color_opt)) {
1163 case 3:
1164 case 4:
1165 case 5:
Sören Tempel3d9c6492021-05-23 14:14:10 +02001166 if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) {
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001167 case 0:
1168 case 1:
1169 case 2:
Denys Vlasenko2a816392011-05-13 17:28:09 +02001170 G_show_color = 1;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001171 }
1172 }
Paul Fox156dc412005-08-01 19:33:30 +00001173 }
1174#endif
1175
Eric Andersen11c65522000-09-07 17:24:47 +00001176 /* sort out which command line options take precedence */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001177 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1178 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001179 if (!(opt & OPT_l)) { /* not -l? */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001180 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1181 /* when to sort by time? -t[cu] sorts by time even with -l */
1182 /* (this is achieved by opt_flags[] element for -t) */
1183 /* without -l, bare -c or -u enable sort too */
1184 /* (with -l, bare -c or -u just select which time to show) */
1185 if (opt & (OPT_c|OPT_u)) {
Denys Vlasenko11540a82017-01-23 18:01:48 +01001186 option_mask32 |= OPT_t;
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001187 }
1188 }
1189 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001190
Denys Vlasenkof3137462010-12-19 05:05:34 +01001191 /* choose a display format if one was not already specified by an option */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001192 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C)))
1193 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1);
Eric Andersen11c65522000-09-07 17:24:47 +00001194
Denys Vlasenkob13b6182017-01-25 04:52:45 +01001195 if (ENABLE_FTPD && applet_name[0] == 'f') {
1196 /* ftpd secret backdoor. dirs first are much nicer */
1197 option_mask32 |= OPT_dirs_first;
1198 }
1199
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001200 argv += optind;
1201 if (!argv[0])
1202 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001203
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001204 if (argv[1])
Denys Vlasenko9a64c332017-01-23 20:46:12 +01001205 G.show_dirname = 1; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001206
Denis Vlasenko5c759602006-10-28 12:37:16 +00001207 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001208 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001209 nfiles = 0;
1210 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001211 cur = my_stat(*argv, *argv,
Martijn Dekkerf83292c2019-01-04 18:54:52 +01001212 /* follow links on command line unless -l, -i, -s or -F: */
1213 !(option_mask32 & (OPT_l|OPT_i|OPT_s|OPT_F))
Denys Vlasenko982aa262010-12-19 21:54:39 +01001214 /* ... or if -H: */
1215 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001216 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001217 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001218 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001219 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001220 continue;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001221 /*cur->fname_allocated = 0; - already is */
1222 cur->dn_next = dn;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001223 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001224 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001225 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001226
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001227 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1228 if (nfiles == 0)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001229 return G.exit_code;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001230
Eric Andersen11c65522000-09-07 17:24:47 +00001231 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001232 * allocate memory for an array to hold dnode pointers
1233 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001234 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001235 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1236 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001237 dn = dn->dn_next;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001238 if (!dn)
1239 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001240 }
1241
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001242 if (option_mask32 & OPT_d) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001243 sort_and_display_files(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001244 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001245 dnd = splitdnarray(dnp, SPLIT_DIR);
1246 dnf = splitdnarray(dnp, SPLIT_FILE);
1247 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001248 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001249 if (dnfiles > 0) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001250 sort_and_display_files(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001251 if (ENABLE_FEATURE_CLEAN_UP)
1252 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001253 }
1254 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001255 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001256 scan_and_display_dirs_recur(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001257 if (ENABLE_FEATURE_CLEAN_UP)
1258 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001259 }
1260 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001261
Rob Landley26314862006-05-02 19:46:52 +00001262 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001263 dfree(dnp);
Denys Vlasenko2a816392011-05-13 17:28:09 +02001264 return G.exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001265}