blob: b69b8046005bb855bfb44c88292460d71db7141c [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 Vlasenkoff8fda82022-01-08 22:59:49 +0100501 if (opt & OPT_s) { /* show allocated blocks */
502 if (opt & OPT_h) {
503 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
504 /* print size, show one fractional, use suffixes */
505 make_human_readable_str((off_t)dn->dn_blocks << 9, 1, 0)
506 );
507 } else {
508 column += printf("%6"OFF_FMT"u ", (off_t)(dn->dn_blocks >> 1));
509 }
510 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100511 if (opt & OPT_l) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100512 /* long listing: show mode */
Denys Vlasenko59ac4672021-09-17 01:13:58 +0200513 char modestr[12];
Denys Vlasenko6279aec2021-09-17 17:10:38 +0200514 column += printf("%-10s ", bb_mode_string(modestr, dn->dn_mode));
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100515 /* long listing: show number of links */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200516 column += printf("%4lu ", (long) dn->dn_nlink);
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100517 /* long listing: show user/group */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100518 if (opt & OPT_n) {
519 if (opt & OPT_g)
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100520 column += printf("%-8u ", (int) dn->dn_gid);
521 else
522 column += printf("%-8u %-8u ",
523 (int) dn->dn_uid,
524 (int) dn->dn_gid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000525 }
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100526#if ENABLE_FEATURE_LS_USERNAME
527 else {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100528 if (opt & OPT_g) {
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100529 column += printf("%-8.8s ",
530 get_cached_groupname(dn->dn_gid));
531 } else {
532 column += printf("%-8.8s %-8.8s ",
533 get_cached_username(dn->dn_uid),
534 get_cached_groupname(dn->dn_gid));
535 }
536 }
Eric Andersen11c65522000-09-07 17:24:47 +0000537#endif
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100538#if ENABLE_SELINUX
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100539 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100540 if (opt & OPT_Z) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100541 column += printf("%-32s ", dn->sid ? dn->sid : "?");
542 freecon(dn->sid);
543 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100544 if (opt & OPT_l) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100545#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100546 /* long listing: show size */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200547 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000548 column += printf("%4u, %3u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200549 dn->dn_rdev_maj,
550 dn->dn_rdev_min);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000551 } else {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100552 if (opt & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100553 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200554 /* print size, show one fractional, use suffixes */
555 make_human_readable_str(dn->dn_size, 1, 0)
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200556 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000557 } else {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200558 column += printf("%9"OFF_FMT"u ", dn->dn_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000559 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000560 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000561#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100562 /* long listing: show {m,c,a}time */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100563 if (opt & OPT_full_time) { /* --full-time */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100564 /* coreutils 8.4 ls --full-time prints:
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100565 * 2009-07-13 17:49:27.000000000 +0200
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100566 * we don't show fractional seconds.
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100567 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100568 char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")];
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100569 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z",
570 localtime(&dn->dn_time));
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100571 column += printf("%s ", buf);
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100572 } else { /* ordinary time format */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100573 /* G.current_time_t is ~== time(NULL) */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100574 char *filetime = ctime(&dn->dn_time);
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100575 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100576 time_t age = G.current_time_t - dn->dn_time;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000577 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
Denys Vlasenko8e92df12015-02-16 15:36:25 +0100578 /* less than 6 months old */
579 /* "mmm dd hh:mm " */
580 printf("%.12s ", filetime + 4);
581 } else {
582 /* "mmm dd yyyy " */
583 /* "mmm dd yyyyy " after year 9999 :) */
584 strchr(filetime + 20, '\n')[0] = ' ';
585 printf("%.7s%6s", filetime + 4, filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000586 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000587 column += 13;
588 }
Eric Andersen11c65522000-09-07 17:24:47 +0000589#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100590 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100591
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000592#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200593 if (G_show_color) {
594 mode_t mode = dn->dn_mode_lstat;
595 if (!mode)
596 if (lstat(dn->fullname, &statbuf) == 0)
597 mode = statbuf.st_mode;
Denys Vlasenko8187e012017-09-13 22:48:30 +0200598 printf(ESC"[%u;%um", bold(mode), fgcolor(mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000599 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100600#endif
601 column += print_name(dn->name);
Denys Vlasenko2a816392011-05-13 17:28:09 +0200602 if (G_show_color) {
Denys Vlasenko8187e012017-09-13 22:48:30 +0200603 printf(ESC"[m");
James Youngmana4bc10c2010-12-20 01:36:16 +0100604 }
605
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200606 if (lpath) {
607 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000608#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100609 if ((opt & (OPT_F|OPT_p))
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100610 || G_show_color
611 ) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200612 mode_t mode = dn->dn_mode_stat;
613 if (!mode)
614 if (stat(dn->fullname, &statbuf) == 0)
615 mode = statbuf.st_mode;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200616# if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200617 append = append_char(mode);
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200618# endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200619# if ENABLE_FEATURE_LS_COLOR
620 if (G_show_color) {
Denys Vlasenko8187e012017-09-13 22:48:30 +0200621 printf(ESC"[%u;%um", bold(mode), fgcolor(mode));
Denys Vlasenko2a816392011-05-13 17:28:09 +0200622 }
623# endif
Eric Andersen11c65522000-09-07 17:24:47 +0000624 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200625#endif
626 column += print_name(lpath) + 4;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200627 free(lpath);
628 if (G_show_color) {
Denys Vlasenko8187e012017-09-13 22:48:30 +0200629 printf(ESC"[m");
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200630 }
Eric Andersen11c65522000-09-07 17:24:47 +0000631 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000632#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100633 if (opt & (OPT_F|OPT_p)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000634 if (append) {
635 putchar(append);
636 column++;
637 }
638 }
639#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000640
Glenn L McGrath4d001292003-01-06 01:11:50 +0000641 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000642}
643
Denys Vlasenko4029e212011-05-13 17:28:46 +0200644static void display_files(struct dnode **dn, unsigned nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100645{
646 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100647 unsigned column;
648 unsigned nexttab;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100649 unsigned column_width = 0; /* used only by coulmnal output */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100650
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100651 if (option_mask32 & (OPT_l|OPT_1)) {
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100652 ncols = 1;
653 } else {
654 /* find the longest file name, use that as the column width */
655 for (i = 0; dn[i]; i++) {
656 int len = calc_name_len(dn[i]->name);
657 if (column_width < len)
658 column_width = len;
659 }
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100660 column_width += 2
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100661 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100662 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
663 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
664 ;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200665 ncols = (unsigned)G_terminal_width / column_width;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100666 }
667
668 if (ncols > 1) {
669 nrows = nfiles / ncols;
670 if (nrows * ncols < nfiles)
671 nrows++; /* round up fractionals */
672 } else {
673 nrows = nfiles;
674 ncols = 1;
675 }
676
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100677 column = 0;
678 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100679 for (row = 0; row < nrows; row++) {
680 for (nc = 0; nc < ncols; nc++) {
681 /* reach into the array based on the column and row */
Denys Vlasenko92c68982017-01-23 20:21:14 +0100682 if (option_mask32 & OPT_x)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100683 i = (row * ncols) + nc; /* display across row */
684 else
685 i = (nc * nrows) + row; /* display by column */
686 if (i < nfiles) {
687 if (column > 0) {
688 nexttab -= column;
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100689 printf("%*s", nexttab, "");
690 column += nexttab;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100691 }
692 nexttab = column + column_width;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200693 column += display_single(dn[i]);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100694 }
695 }
696 putchar('\n');
697 column = 0;
698 }
699}
700
701
Denys Vlasenko4029e212011-05-13 17:28:46 +0200702/*** Dir scanning code ***/
703
704static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100705{
Denys Vlasenko4029e212011-05-13 17:28:46 +0200706 struct stat statbuf;
707 struct dnode *cur;
708
709 cur = xzalloc(sizeof(*cur));
710 cur->fullname = fullname;
711 cur->name = name;
712
713 if ((option_mask32 & OPT_L) || force_follow) {
714#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100715 if (option_mask32 & OPT_Z) {
Denys Vlasenko69675782013-01-14 01:34:48 +0100716 getfilecon(fullname, &cur->sid);
Denys Vlasenko4029e212011-05-13 17:28:46 +0200717 }
718#endif
719 if (stat(fullname, &statbuf)) {
720 bb_simple_perror_msg(fullname);
721 G.exit_code = EXIT_FAILURE;
722 free(cur);
723 return NULL;
724 }
725 cur->dn_mode_stat = statbuf.st_mode;
726 } else {
727#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100728 if (option_mask32 & OPT_Z) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200729 lgetfilecon(fullname, &cur->sid);
730 }
731#endif
732 if (lstat(fullname, &statbuf)) {
733 bb_simple_perror_msg(fullname);
734 G.exit_code = EXIT_FAILURE;
735 free(cur);
736 return NULL;
737 }
738 cur->dn_mode_lstat = statbuf.st_mode;
739 }
740
741 /* cur->dstat = statbuf: */
742 cur->dn_mode = statbuf.st_mode ;
743 cur->dn_size = statbuf.st_size ;
744#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100745 cur->dn_time = statbuf.st_mtime ;
746 if (option_mask32 & OPT_u)
747 cur->dn_time = statbuf.st_atime;
748 if (option_mask32 & OPT_c)
749 cur->dn_time = statbuf.st_ctime;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200750#endif
751 cur->dn_ino = statbuf.st_ino ;
752 cur->dn_blocks = statbuf.st_blocks;
753 cur->dn_nlink = statbuf.st_nlink ;
754 cur->dn_uid = statbuf.st_uid ;
755 cur->dn_gid = statbuf.st_gid ;
756 cur->dn_rdev_maj = major(statbuf.st_rdev);
757 cur->dn_rdev_min = minor(statbuf.st_rdev);
758
759 return cur;
760}
761
762static unsigned count_dirs(struct dnode **dn, int which)
763{
764 unsigned dirs, all;
765
766 if (!dn)
767 return 0;
768
769 dirs = all = 0;
770 for (; *dn; dn++) {
771 const char *name;
772
773 all++;
774 if (!S_ISDIR((*dn)->dn_mode))
775 continue;
776
777 name = (*dn)->name;
778 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
779 /* or if it's not . or .. */
780 || name[0] != '.'
781 || (name[1] && (name[1] != '.' || name[2]))
782 ) {
783 dirs++;
784 }
785 }
786 return which != SPLIT_FILE ? dirs : all - dirs;
787}
788
789/* get memory to hold an array of pointers */
790static struct dnode **dnalloc(unsigned num)
791{
792 if (num < 1)
793 return NULL;
794
795 num++; /* so that we have terminating NULL */
796 return xzalloc(num * sizeof(struct dnode *));
797}
798
799#if ENABLE_FEATURE_LS_RECURSIVE
800static void dfree(struct dnode **dnp)
801{
802 unsigned i;
803
804 if (dnp == NULL)
805 return;
806
807 for (i = 0; dnp[i]; i++) {
808 struct dnode *cur = dnp[i];
809 if (cur->fname_allocated)
810 free((char*)cur->fullname);
811 free(cur);
812 }
813 free(dnp);
814}
815#else
816#define dfree(...) ((void)0)
817#endif
818
819/* Returns NULL-terminated malloced vector of pointers (or NULL) */
820static struct dnode **splitdnarray(struct dnode **dn, int which)
821{
822 unsigned dncnt, d;
823 struct dnode **dnp;
824
825 if (dn == NULL)
826 return NULL;
827
828 /* count how many dirs or files there are */
829 dncnt = count_dirs(dn, which);
830
831 /* allocate a file array and a dir array */
832 dnp = dnalloc(dncnt);
833
834 /* copy the entrys into the file or dir array */
835 for (d = 0; *dn; dn++) {
836 if (S_ISDIR((*dn)->dn_mode)) {
837 const char *name;
838
839 if (which == SPLIT_FILE)
840 continue;
841
842 name = (*dn)->name;
843 if ((which & SPLIT_DIR) /* any dir... */
844 /* ... or not . or .. */
845 || name[0] != '.'
846 || (name[1] && (name[1] != '.' || name[2]))
847 ) {
848 dnp[d++] = *dn;
849 }
850 } else
851 if (which == SPLIT_FILE) {
852 dnp[d++] = *dn;
853 }
854 }
855 return dnp;
856}
857
858#if ENABLE_FEATURE_LS_SORTFILES
859static int sortcmp(const void *a, const void *b)
860{
861 struct dnode *d1 = *(struct dnode **)a;
862 struct dnode *d2 = *(struct dnode **)b;
Denys Vlasenko11540a82017-01-23 18:01:48 +0100863 unsigned opt = option_mask32;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200864 off_t dif;
865
Denys Vlasenko11540a82017-01-23 18:01:48 +0100866 dif = 0; /* assume sort by name */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200867 // TODO: use pre-initialized function pointer
868 // instead of branch forest
Denys Vlasenko11540a82017-01-23 18:01:48 +0100869 if (opt & OPT_dirs_first) {
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100870 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
871 if (dif != 0)
872 goto maybe_invert_and_ret;
873 }
874
Denys Vlasenko11540a82017-01-23 18:01:48 +0100875 if (opt & OPT_S) { /* sort by size */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200876 dif = (d2->dn_size - d1->dn_size);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200877 } else
Denys Vlasenko11540a82017-01-23 18:01:48 +0100878 if (opt & OPT_t) { /* sort by time */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100879 dif = (d2->dn_time - d1->dn_time);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200880 } else
Denys Vlasenko1e18a012011-06-21 17:12:52 +0200881#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
Denys Vlasenko11540a82017-01-23 18:01:48 +0100882 if (opt & OPT_v) { /* sort by version */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200883 dif = strverscmp(d1->name, d2->name);
884 } else
Denys Vlasenko561f9c82011-06-21 16:38:29 +0200885#endif
Denys Vlasenko11540a82017-01-23 18:01:48 +0100886 if (opt & OPT_X) { /* sort by extension */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200887 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
Denys Vlasenko4029e212011-05-13 17:28:46 +0200888 }
889 if (dif == 0) {
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200890 /* sort by name, use as tie breaker for other sorts */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200891 if (ENABLE_LOCALE_SUPPORT)
892 dif = strcoll(d1->name, d2->name);
893 else
894 dif = strcmp(d1->name, d2->name);
Denys Vlasenko11540a82017-01-23 18:01:48 +0100895 } else {
896 /* Make dif fit into an int */
897 if (sizeof(dif) > sizeof(int)) {
898 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
899 /* shift leaving only "int" worth of bits */
900 /* (this requires dif != 0, and here it is nonzero) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200901 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100902 }
903 }
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100904 maybe_invert_and_ret:
Denys Vlasenko11540a82017-01-23 18:01:48 +0100905 return (opt & OPT_r) ? -(int)dif : (int)dif;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100906}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200907
908static void dnsort(struct dnode **dn, int size)
909{
910 qsort(dn, size, sizeof(*dn), sortcmp);
911}
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200912
913static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
914{
915 dnsort(dn, nfiles);
916 display_files(dn, nfiles);
917}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200918#else
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200919# define dnsort(dn, size) ((void)0)
920# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100921#endif
922
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100923/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200924static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100925{
926 struct dnode *dn, *cur, **dnp;
927 struct dirent *entry;
928 DIR *dir;
929 unsigned i, nfiles;
930
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100931 *nfiles_p = 0;
932 dir = warn_opendir(path);
933 if (dir == NULL) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200934 G.exit_code = EXIT_FAILURE;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100935 return NULL; /* could not open the dir */
936 }
937 dn = NULL;
938 nfiles = 0;
939 while ((entry = readdir(dir)) != NULL) {
940 char *fullname;
941
942 /* are we going to list the file- it may be . or .. or a hidden file */
943 if (entry->d_name[0] == '.') {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100944 if (!(option_mask32 & (OPT_a|OPT_A)))
945 continue; /* skip all dotfiles if no -a/-A */
946 if (!(option_mask32 & OPT_a)
947 && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100948 ) {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100949 continue; /* if only -A, skip . and .. but show other dotfiles */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100950 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100951 }
952 fullname = concat_path_file(path, entry->d_name);
Denys Vlasenko0231e342021-06-25 19:48:34 +0200953 cur = my_stat(fullname, bb_basename(fullname), 0);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100954 if (!cur) {
955 free(fullname);
956 continue;
957 }
958 cur->fname_allocated = 1;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200959 cur->dn_next = dn;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100960 dn = cur;
961 nfiles++;
962 }
963 closedir(dir);
964
965 if (dn == NULL)
966 return NULL;
967
968 /* now that we know how many files there are
969 * allocate memory for an array to hold dnode pointers
970 */
971 *nfiles_p = nfiles;
972 dnp = dnalloc(nfiles);
973 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
974 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200975 dn = dn->dn_next;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100976 if (!dn)
977 break;
978 }
979
980 return dnp;
981}
982
Denys Vlasenko4029e212011-05-13 17:28:46 +0200983#if ENABLE_DESKTOP
984/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
985 * If any of the -l, -n, -s options is specified, each list
986 * of files within the directory shall be preceded by a
987 * status line indicating the number of file system blocks
988 * occupied by files in the directory in 512-byte units if
989 * the -k option is not specified, or 1024-byte units if the
990 * -k option is specified, rounded up to the next integral
991 * number of units.
992 */
993/* by Jorgen Overgaard (jorgen AT antistaten.se) */
994static off_t calculate_blocks(struct dnode **dn)
995{
996 uoff_t blocks = 1;
997 if (dn) {
998 while (*dn) {
999 /* st_blocks is in 512 byte blocks */
1000 blocks += (*dn)->dn_blocks;
1001 dn++;
1002 }
1003 }
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001004
Denys Vlasenko4029e212011-05-13 17:28:46 +02001005 /* Even though standard says use 512 byte blocks, coreutils use 1k */
1006 /* Actually, we round up by calculating (blocks + 1) / 2,
1007 * "+ 1" was done when we initialized blocks to 1 */
1008 return blocks >> 1;
1009}
1010#endif
1011
1012static void scan_and_display_dirs_recur(struct dnode **dn, int first)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001013{
1014 unsigned nfiles;
1015 struct dnode **subdnp;
1016
1017 for (; *dn; dn++) {
Denys Vlasenko9a64c332017-01-23 20:46:12 +01001018 if (G.show_dirname || (option_mask32 & OPT_R)) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001019 if (!first)
1020 bb_putchar('\n');
1021 first = 0;
1022 printf("%s:\n", (*dn)->fullname);
1023 }
Denys Vlasenko4029e212011-05-13 17:28:46 +02001024 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001025#if ENABLE_DESKTOP
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001026 if (option_mask32 & (OPT_s|OPT_l)) {
Lauri Kasanena380aac2018-08-01 19:51:17 +03001027 if (option_mask32 & OPT_h) {
1028 printf("total %-"HUMAN_READABLE_MAX_WIDTH_STR"s\n",
1029 /* print size, no fractions, use suffixes */
1030 make_human_readable_str(calculate_blocks(subdnp) * 1024,
1031 0, 0)
1032 );
1033 } else {
1034 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1035 }
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001036 }
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001037#endif
1038 if (nfiles > 0) {
1039 /* list all files at this level */
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001040 sort_and_display_files(subdnp, nfiles);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001041
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001042 if (ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001043 && (option_mask32 & OPT_R)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001044 ) {
1045 struct dnode **dnd;
1046 unsigned dndirs;
1047 /* recursive - list the sub-dirs */
1048 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1049 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1050 if (dndirs > 0) {
1051 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001052 scan_and_display_dirs_recur(dnd, 0);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001053 /* free the array of dnode pointers to the dirs */
1054 free(dnd);
1055 }
1056 }
1057 /* free the dnodes and the fullname mem */
1058 dfree(subdnp);
1059 }
1060 }
1061}
1062
1063
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001064int ls_main(int argc UNUSED_PARAM, char **argv)
Denys Vlasenkob13b6182017-01-25 04:52:45 +01001065{ /* ^^^^^^^^^^^^^^^^^ note: if FTPD, argc can be wrong, see ftpd.c */
Glenn L McGrath792cae52004-01-18 05:15:16 +00001066 struct dnode **dnd;
1067 struct dnode **dnf;
1068 struct dnode **dnp;
1069 struct dnode *dn;
1070 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001071 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001072 unsigned nfiles;
1073 unsigned dnfiles;
1074 unsigned dndirs;
1075 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001076#if ENABLE_FEATURE_LS_COLOR
1077 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1078 /* coreutils 6.10:
1079 * # ls --color=BOGUS
1080 * ls: invalid argument 'BOGUS' for '--color'
1081 * Valid arguments are:
1082 * 'always', 'yes', 'force'
1083 * 'never', 'no', 'none'
1084 * 'auto', 'tty', 'if-tty'
1085 * (and substrings: "--color=alwa" work too)
1086 */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001087 static const char color_str[] ALIGN1 =
1088 "always\0""yes\0""force\0"
1089 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001090 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001091 const char *color_opt = color_str; /* "always" */
1092#endif
Laurent Bercot25999372017-05-26 16:50:53 +02001093#if ENABLE_LONG_OPTS
1094 static const char ls_longopts[] ALIGN1 =
1095 "full-time\0" No_argument "\xff"
1096 "group-directories-first\0" No_argument "\xfe"
Denys Vlasenko9501bc72019-05-26 13:53:41 +02001097 IF_FEATURE_LS_COLOR("color\0" Optional_argument "\xfd")
Laurent Bercot25999372017-05-26 16:50:53 +02001098 ;
1099#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001100
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001101 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001102
Denys Vlasenko28055022010-01-04 20:49:58 +01001103 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001104
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001105#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001106 /* obtain the terminal width */
Denys Vlasenko641caae2015-10-23 01:44:22 +02001107 G_terminal_width = get_terminal_width(STDIN_FILENO);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001108 /* go one less... */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001109 G_terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001110#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001111
Eric Andersen11c65522000-09-07 17:24:47 +00001112 /* process options */
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001113 opt = getopt32long(argv, "^"
1114 ls_options
1115 "\0"
1116 /* -n and -g imply -l */
1117 "nl:gl"
1118 /* --full-time implies -l */
1119 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l"))
1120 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1121 * in some pairs of opts, only last one takes effect:
1122 */
1123 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
1124 // ":m-l:l-m" - we don't have -m
1125 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1126 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1127 ":C-1:1-C" /* bycols/oneline */
1128 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1129 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
1130 /* -w NUM: */
1131 IF_FEATURE_LS_WIDTH(":w+")
1132 , ls_longopts
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001133 IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001134 IF_FEATURE_LS_COLOR(, &color_opt)
1135 );
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001136#if 0 /* option bits debug */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001137 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 +01001138 if (opt & OPT_c ) bb_error_msg("-c");
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001139 if (opt & OPT_l ) bb_error_msg("-l");
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001140 if (opt & OPT_H ) bb_error_msg("-H");
1141 if (opt & OPT_color ) bb_error_msg("--color");
1142 if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
1143 if (opt & OPT_full_time ) bb_error_msg("--full-time");
1144 exit(0);
1145#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001146
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +01001147#if ENABLE_SELINUX
1148 if (opt & OPT_Z) {
1149 if (!is_selinux_enabled())
1150 option_mask32 &= ~OPT_Z;
1151 }
1152#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001153
Denis Vlasenko5c759602006-10-28 12:37:16 +00001154#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +02001155 /* set G_show_color = 1/0 */
Sören Tempel3d9c6492021-05-23 14:14:10 +02001156 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && !is_TERM_dumb()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +00001157 char *p = getenv("LS_COLORS");
1158 /* LS_COLORS is unset, or (not empty && not "none") ? */
Sören Tempel3d9c6492021-05-23 14:14:10 +02001159 if (!p || (p[0] && strcmp(p, "none") != 0)) {
1160 if (isatty(STDOUT_FILENO)) {
1161 /* check isatty() last because it's expensive (syscall) */
1162 G_show_color = 1;
1163 }
1164 }
Denis Vlasenko5c759602006-10-28 12:37:16 +00001165 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001166 if (opt & OPT_color) {
1167 if (color_opt[0] == 'n')
Denys Vlasenko2a816392011-05-13 17:28:09 +02001168 G_show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001169 else switch (index_in_substrings(color_str, color_opt)) {
1170 case 3:
1171 case 4:
1172 case 5:
Sören Tempel3d9c6492021-05-23 14:14:10 +02001173 if (!is_TERM_dumb() && isatty(STDOUT_FILENO)) {
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001174 case 0:
1175 case 1:
1176 case 2:
Denys Vlasenko2a816392011-05-13 17:28:09 +02001177 G_show_color = 1;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001178 }
1179 }
Paul Fox156dc412005-08-01 19:33:30 +00001180 }
1181#endif
1182
Eric Andersen11c65522000-09-07 17:24:47 +00001183 /* sort out which command line options take precedence */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001184 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1185 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001186 if (!(opt & OPT_l)) { /* not -l? */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001187 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1188 /* when to sort by time? -t[cu] sorts by time even with -l */
1189 /* (this is achieved by opt_flags[] element for -t) */
1190 /* without -l, bare -c or -u enable sort too */
1191 /* (with -l, bare -c or -u just select which time to show) */
1192 if (opt & (OPT_c|OPT_u)) {
Denys Vlasenko11540a82017-01-23 18:01:48 +01001193 option_mask32 |= OPT_t;
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001194 }
1195 }
1196 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001197
Denys Vlasenkof3137462010-12-19 05:05:34 +01001198 /* choose a display format if one was not already specified by an option */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001199 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C)))
1200 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1);
Eric Andersen11c65522000-09-07 17:24:47 +00001201
Denys Vlasenkob13b6182017-01-25 04:52:45 +01001202 if (ENABLE_FTPD && applet_name[0] == 'f') {
1203 /* ftpd secret backdoor. dirs first are much nicer */
1204 option_mask32 |= OPT_dirs_first;
1205 }
1206
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001207 argv += optind;
1208 if (!argv[0])
1209 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001210
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001211 if (argv[1])
Denys Vlasenko9a64c332017-01-23 20:46:12 +01001212 G.show_dirname = 1; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001213
Denis Vlasenko5c759602006-10-28 12:37:16 +00001214 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001215 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001216 nfiles = 0;
1217 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001218 cur = my_stat(*argv, *argv,
Martijn Dekkerf83292c2019-01-04 18:54:52 +01001219 /* follow links on command line unless -l, -i, -s or -F: */
1220 !(option_mask32 & (OPT_l|OPT_i|OPT_s|OPT_F))
Denys Vlasenko982aa262010-12-19 21:54:39 +01001221 /* ... or if -H: */
1222 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001223 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001224 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001225 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001226 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001227 continue;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001228 /*cur->fname_allocated = 0; - already is */
1229 cur->dn_next = dn;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001230 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001231 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001232 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001233
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001234 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1235 if (nfiles == 0)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001236 return G.exit_code;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001237
Eric Andersen11c65522000-09-07 17:24:47 +00001238 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001239 * allocate memory for an array to hold dnode pointers
1240 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001241 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001242 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1243 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001244 dn = dn->dn_next;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001245 if (!dn)
1246 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001247 }
1248
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001249 if (option_mask32 & OPT_d) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001250 sort_and_display_files(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001251 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001252 dnd = splitdnarray(dnp, SPLIT_DIR);
1253 dnf = splitdnarray(dnp, SPLIT_FILE);
1254 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001255 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001256 if (dnfiles > 0) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001257 sort_and_display_files(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001258 if (ENABLE_FEATURE_CLEAN_UP)
1259 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001260 }
1261 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001262 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001263 scan_and_display_dirs_recur(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001264 if (ENABLE_FEATURE_CLEAN_UP)
1265 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001266 }
1267 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001268
Rob Landley26314862006-05-02 19:46:52 +00001269 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001270 dfree(dnp);
Denys Vlasenko2a816392011-05-13 17:28:09 +02001271 return G.exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001272}