blob: db3ddb9446d521d7df6b2f4dd19da6de63a59166 [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"
112//usage: "\n -a Include entries which start with ."
113//usage: "\n -A Like -a, but exclude . and .."
Denys Vlasenko92c68982017-01-23 20:21:14 +0100114////usage: "\n -C List by columns" - don't show, this is a default anyway
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100115//usage: "\n -x List by lines"
116//usage: "\n -d List directory entries instead of contents"
117//usage: IF_FEATURE_LS_FOLLOWLINKS(
Denys Vlasenko982aa262010-12-19 21:54:39 +0100118//usage: "\n -L Follow symlinks"
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200119//usage: "\n -H Follow symlinks on command line"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100120//usage: )
121//usage: IF_FEATURE_LS_RECURSIVE(
122//usage: "\n -R Recurse"
123//usage: )
124//usage: IF_FEATURE_LS_FILETYPES(
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200125//usage: "\n -p Append / to dir entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100126//usage: "\n -F Append indicator (one of */=@|) to entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100127//usage: )
128//usage: "\n -l Long listing format"
129//usage: "\n -i List inode numbers"
130//usage: "\n -n List numeric UIDs and GIDs instead of names"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200131//usage: "\n -s List allocated blocks"
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100132//usage: IF_FEATURE_LS_TIMESTAMPS(
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100133//usage: "\n -lc List ctime"
134//usage: "\n -lu List atime"
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100135//usage: )
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100136//usage: IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(
137//usage: "\n --full-time List full date and time"
138//usage: ))
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100139//usage: IF_FEATURE_HUMAN_READABLE(
Denys Vlasenko11540a82017-01-23 18:01:48 +0100140//usage: "\n -h Human readable sizes (1K 243M 2G)"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100141//usage: )
142//usage: IF_FEATURE_LS_SORTFILES(
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100143//usage: IF_LONG_OPTS(
144//usage: "\n --group-directories-first"
145//usage: )
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200146//usage: "\n -S Sort by size"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100147//usage: "\n -X Sort by extension"
148//usage: "\n -v Sort by version"
149//usage: )
150//usage: IF_FEATURE_LS_TIMESTAMPS(
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100151//usage: "\n -t Sort by mtime"
152//usage: "\n -tc Sort by ctime"
153//usage: "\n -tu Sort by atime"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100154//usage: )
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100155//usage: "\n -r Reverse sort order"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100156//usage: IF_SELINUX(
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100157//usage: "\n -Z List security context and permission"
158//usage: )
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100159//usage: IF_FEATURE_LS_WIDTH(
Denys Vlasenko11540a82017-01-23 18:01:48 +0100160//usage: "\n -w N Format N columns wide"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100161//usage: )
162//usage: IF_FEATURE_LS_COLOR(
163//usage: "\n --color[={always,never,auto}] Control coloring"
164//usage: )
165
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000166#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200167#include "common_bufsiz.h"
Denys Vlasenko42a8fd02009-07-11 21:36:13 +0200168#include "unicode.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +0000169
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000170
Denis Vlasenko99912ca2007-04-10 15:43:37 +0000171/* This is a NOEXEC applet. Be very careful! */
172
Eric Andersenf1142c52001-02-20 06:16:29 +0000173
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000174#if ENABLE_FTPD
175/* ftpd uses ls, and without timestamps Mozilla won't understand
176 * ftpd's LIST output.
177 */
178# undef CONFIG_FEATURE_LS_TIMESTAMPS
179# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000180# undef IF_FEATURE_LS_TIMESTAMPS
181# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000182# define CONFIG_FEATURE_LS_TIMESTAMPS 1
183# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000184# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
185# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000186#endif
187
188
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000189enum {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000190TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000191
Denys Vlasenko982aa262010-12-19 21:54:39 +0100192SPLIT_FILE = 0,
Denys Vlasenko2a816392011-05-13 17:28:09 +0200193SPLIT_DIR = 1,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100194SPLIT_SUBDIR = 2,
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.
301// We also can 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)) {
456 fputs(name, stdout);
457 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 Vlasenko2a816392011-05-13 17:28:09 +0200506 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100507 /* long listing: show number of links */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200508 column += printf("%4lu ", (long) dn->dn_nlink);
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100509 /* long listing: show user/group */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100510 if (opt & OPT_n) {
511 if (opt & OPT_g)
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100512 column += printf("%-8u ", (int) dn->dn_gid);
513 else
514 column += printf("%-8u %-8u ",
515 (int) dn->dn_uid,
516 (int) dn->dn_gid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000517 }
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100518#if ENABLE_FEATURE_LS_USERNAME
519 else {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100520 if (opt & OPT_g) {
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100521 column += printf("%-8.8s ",
522 get_cached_groupname(dn->dn_gid));
523 } else {
524 column += printf("%-8.8s %-8.8s ",
525 get_cached_username(dn->dn_uid),
526 get_cached_groupname(dn->dn_gid));
527 }
528 }
Eric Andersen11c65522000-09-07 17:24:47 +0000529#endif
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100530#if ENABLE_SELINUX
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100531 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100532 if (opt & OPT_Z) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100533 column += printf("%-32s ", dn->sid ? dn->sid : "?");
534 freecon(dn->sid);
535 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100536 if (opt & OPT_l) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100537#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100538 /* long listing: show size */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200539 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000540 column += printf("%4u, %3u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200541 dn->dn_rdev_maj,
542 dn->dn_rdev_min);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000543 } else {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100544 if (opt & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100545 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200546 /* print size, show one fractional, use suffixes */
547 make_human_readable_str(dn->dn_size, 1, 0)
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200548 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000549 } else {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200550 column += printf("%9"OFF_FMT"u ", dn->dn_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000551 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000552 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000553#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100554 /* long listing: show {m,c,a}time */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100555 if (opt & OPT_full_time) { /* --full-time */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100556 /* coreutils 8.4 ls --full-time prints:
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100557 * 2009-07-13 17:49:27.000000000 +0200
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100558 * we don't show fractional seconds.
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100559 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100560 char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")];
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100561 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z",
562 localtime(&dn->dn_time));
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100563 column += printf("%s ", buf);
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100564 } else { /* ordinary time format */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100565 /* G.current_time_t is ~== time(NULL) */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100566 char *filetime = ctime(&dn->dn_time);
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100567 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100568 time_t age = G.current_time_t - dn->dn_time;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000569 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
Denys Vlasenko8e92df12015-02-16 15:36:25 +0100570 /* less than 6 months old */
571 /* "mmm dd hh:mm " */
572 printf("%.12s ", filetime + 4);
573 } else {
574 /* "mmm dd yyyy " */
575 /* "mmm dd yyyyy " after year 9999 :) */
576 strchr(filetime + 20, '\n')[0] = ' ';
577 printf("%.7s%6s", filetime + 4, filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000578 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000579 column += 13;
580 }
Eric Andersen11c65522000-09-07 17:24:47 +0000581#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100582 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100583
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000584#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200585 if (G_show_color) {
586 mode_t mode = dn->dn_mode_lstat;
587 if (!mode)
588 if (lstat(dn->fullname, &statbuf) == 0)
589 mode = statbuf.st_mode;
Denys Vlasenko8187e012017-09-13 22:48:30 +0200590 printf(ESC"[%u;%um", bold(mode), fgcolor(mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000591 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100592#endif
593 column += print_name(dn->name);
Denys Vlasenko2a816392011-05-13 17:28:09 +0200594 if (G_show_color) {
Denys Vlasenko8187e012017-09-13 22:48:30 +0200595 printf(ESC"[m");
James Youngmana4bc10c2010-12-20 01:36:16 +0100596 }
597
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200598 if (lpath) {
599 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000600#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100601 if ((opt & (OPT_F|OPT_p))
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100602 || G_show_color
603 ) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200604 mode_t mode = dn->dn_mode_stat;
605 if (!mode)
606 if (stat(dn->fullname, &statbuf) == 0)
607 mode = statbuf.st_mode;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200608# if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200609 append = append_char(mode);
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200610# endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200611# if ENABLE_FEATURE_LS_COLOR
612 if (G_show_color) {
Denys Vlasenko8187e012017-09-13 22:48:30 +0200613 printf(ESC"[%u;%um", bold(mode), fgcolor(mode));
Denys Vlasenko2a816392011-05-13 17:28:09 +0200614 }
615# endif
Eric Andersen11c65522000-09-07 17:24:47 +0000616 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200617#endif
618 column += print_name(lpath) + 4;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200619 free(lpath);
620 if (G_show_color) {
Denys Vlasenko8187e012017-09-13 22:48:30 +0200621 printf(ESC"[m");
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200622 }
Eric Andersen11c65522000-09-07 17:24:47 +0000623 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000624#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100625 if (opt & (OPT_F|OPT_p)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000626 if (append) {
627 putchar(append);
628 column++;
629 }
630 }
631#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000632
Glenn L McGrath4d001292003-01-06 01:11:50 +0000633 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000634}
635
Denys Vlasenko4029e212011-05-13 17:28:46 +0200636static void display_files(struct dnode **dn, unsigned nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100637{
638 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100639 unsigned column;
640 unsigned nexttab;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100641 unsigned column_width = 0; /* used only by coulmnal output */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100642
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100643 if (option_mask32 & (OPT_l|OPT_1)) {
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100644 ncols = 1;
645 } else {
646 /* find the longest file name, use that as the column width */
647 for (i = 0; dn[i]; i++) {
648 int len = calc_name_len(dn[i]->name);
649 if (column_width < len)
650 column_width = len;
651 }
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100652 column_width += 2
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100653 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100654 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
655 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
656 ;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200657 ncols = (unsigned)G_terminal_width / column_width;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100658 }
659
660 if (ncols > 1) {
661 nrows = nfiles / ncols;
662 if (nrows * ncols < nfiles)
663 nrows++; /* round up fractionals */
664 } else {
665 nrows = nfiles;
666 ncols = 1;
667 }
668
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100669 column = 0;
670 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100671 for (row = 0; row < nrows; row++) {
672 for (nc = 0; nc < ncols; nc++) {
673 /* reach into the array based on the column and row */
Denys Vlasenko92c68982017-01-23 20:21:14 +0100674 if (option_mask32 & OPT_x)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100675 i = (row * ncols) + nc; /* display across row */
676 else
677 i = (nc * nrows) + row; /* display by column */
678 if (i < nfiles) {
679 if (column > 0) {
680 nexttab -= column;
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100681 printf("%*s", nexttab, "");
682 column += nexttab;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100683 }
684 nexttab = column + column_width;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200685 column += display_single(dn[i]);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100686 }
687 }
688 putchar('\n');
689 column = 0;
690 }
691}
692
693
Denys Vlasenko4029e212011-05-13 17:28:46 +0200694/*** Dir scanning code ***/
695
696static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100697{
Denys Vlasenko4029e212011-05-13 17:28:46 +0200698 struct stat statbuf;
699 struct dnode *cur;
700
701 cur = xzalloc(sizeof(*cur));
702 cur->fullname = fullname;
703 cur->name = name;
704
705 if ((option_mask32 & OPT_L) || force_follow) {
706#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100707 if (option_mask32 & OPT_Z) {
Denys Vlasenko69675782013-01-14 01:34:48 +0100708 getfilecon(fullname, &cur->sid);
Denys Vlasenko4029e212011-05-13 17:28:46 +0200709 }
710#endif
711 if (stat(fullname, &statbuf)) {
712 bb_simple_perror_msg(fullname);
713 G.exit_code = EXIT_FAILURE;
714 free(cur);
715 return NULL;
716 }
717 cur->dn_mode_stat = statbuf.st_mode;
718 } else {
719#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100720 if (option_mask32 & OPT_Z) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200721 lgetfilecon(fullname, &cur->sid);
722 }
723#endif
724 if (lstat(fullname, &statbuf)) {
725 bb_simple_perror_msg(fullname);
726 G.exit_code = EXIT_FAILURE;
727 free(cur);
728 return NULL;
729 }
730 cur->dn_mode_lstat = statbuf.st_mode;
731 }
732
733 /* cur->dstat = statbuf: */
734 cur->dn_mode = statbuf.st_mode ;
735 cur->dn_size = statbuf.st_size ;
736#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100737 cur->dn_time = statbuf.st_mtime ;
738 if (option_mask32 & OPT_u)
739 cur->dn_time = statbuf.st_atime;
740 if (option_mask32 & OPT_c)
741 cur->dn_time = statbuf.st_ctime;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200742#endif
743 cur->dn_ino = statbuf.st_ino ;
744 cur->dn_blocks = statbuf.st_blocks;
745 cur->dn_nlink = statbuf.st_nlink ;
746 cur->dn_uid = statbuf.st_uid ;
747 cur->dn_gid = statbuf.st_gid ;
748 cur->dn_rdev_maj = major(statbuf.st_rdev);
749 cur->dn_rdev_min = minor(statbuf.st_rdev);
750
751 return cur;
752}
753
754static unsigned count_dirs(struct dnode **dn, int which)
755{
756 unsigned dirs, all;
757
758 if (!dn)
759 return 0;
760
761 dirs = all = 0;
762 for (; *dn; dn++) {
763 const char *name;
764
765 all++;
766 if (!S_ISDIR((*dn)->dn_mode))
767 continue;
768
769 name = (*dn)->name;
770 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
771 /* or if it's not . or .. */
772 || name[0] != '.'
773 || (name[1] && (name[1] != '.' || name[2]))
774 ) {
775 dirs++;
776 }
777 }
778 return which != SPLIT_FILE ? dirs : all - dirs;
779}
780
781/* get memory to hold an array of pointers */
782static struct dnode **dnalloc(unsigned num)
783{
784 if (num < 1)
785 return NULL;
786
787 num++; /* so that we have terminating NULL */
788 return xzalloc(num * sizeof(struct dnode *));
789}
790
791#if ENABLE_FEATURE_LS_RECURSIVE
792static void dfree(struct dnode **dnp)
793{
794 unsigned i;
795
796 if (dnp == NULL)
797 return;
798
799 for (i = 0; dnp[i]; i++) {
800 struct dnode *cur = dnp[i];
801 if (cur->fname_allocated)
802 free((char*)cur->fullname);
803 free(cur);
804 }
805 free(dnp);
806}
807#else
808#define dfree(...) ((void)0)
809#endif
810
811/* Returns NULL-terminated malloced vector of pointers (or NULL) */
812static struct dnode **splitdnarray(struct dnode **dn, int which)
813{
814 unsigned dncnt, d;
815 struct dnode **dnp;
816
817 if (dn == NULL)
818 return NULL;
819
820 /* count how many dirs or files there are */
821 dncnt = count_dirs(dn, which);
822
823 /* allocate a file array and a dir array */
824 dnp = dnalloc(dncnt);
825
826 /* copy the entrys into the file or dir array */
827 for (d = 0; *dn; dn++) {
828 if (S_ISDIR((*dn)->dn_mode)) {
829 const char *name;
830
831 if (which == SPLIT_FILE)
832 continue;
833
834 name = (*dn)->name;
835 if ((which & SPLIT_DIR) /* any dir... */
836 /* ... or not . or .. */
837 || name[0] != '.'
838 || (name[1] && (name[1] != '.' || name[2]))
839 ) {
840 dnp[d++] = *dn;
841 }
842 } else
843 if (which == SPLIT_FILE) {
844 dnp[d++] = *dn;
845 }
846 }
847 return dnp;
848}
849
850#if ENABLE_FEATURE_LS_SORTFILES
851static int sortcmp(const void *a, const void *b)
852{
853 struct dnode *d1 = *(struct dnode **)a;
854 struct dnode *d2 = *(struct dnode **)b;
Denys Vlasenko11540a82017-01-23 18:01:48 +0100855 unsigned opt = option_mask32;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200856 off_t dif;
857
Denys Vlasenko11540a82017-01-23 18:01:48 +0100858 dif = 0; /* assume sort by name */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200859 // TODO: use pre-initialized function pointer
860 // instead of branch forest
Denys Vlasenko11540a82017-01-23 18:01:48 +0100861 if (opt & OPT_dirs_first) {
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100862 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
863 if (dif != 0)
864 goto maybe_invert_and_ret;
865 }
866
Denys Vlasenko11540a82017-01-23 18:01:48 +0100867 if (opt & OPT_S) { /* sort by size */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200868 dif = (d2->dn_size - d1->dn_size);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200869 } else
Denys Vlasenko11540a82017-01-23 18:01:48 +0100870 if (opt & OPT_t) { /* sort by time */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100871 dif = (d2->dn_time - d1->dn_time);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200872 } else
Denys Vlasenko1e18a012011-06-21 17:12:52 +0200873#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
Denys Vlasenko11540a82017-01-23 18:01:48 +0100874 if (opt & OPT_v) { /* sort by version */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200875 dif = strverscmp(d1->name, d2->name);
876 } else
Denys Vlasenko561f9c82011-06-21 16:38:29 +0200877#endif
Denys Vlasenko11540a82017-01-23 18:01:48 +0100878 if (opt & OPT_X) { /* sort by extension */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200879 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
Denys Vlasenko4029e212011-05-13 17:28:46 +0200880 }
881 if (dif == 0) {
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200882 /* sort by name, use as tie breaker for other sorts */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200883 if (ENABLE_LOCALE_SUPPORT)
884 dif = strcoll(d1->name, d2->name);
885 else
886 dif = strcmp(d1->name, d2->name);
Denys Vlasenko11540a82017-01-23 18:01:48 +0100887 } else {
888 /* Make dif fit into an int */
889 if (sizeof(dif) > sizeof(int)) {
890 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
891 /* shift leaving only "int" worth of bits */
892 /* (this requires dif != 0, and here it is nonzero) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200893 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100894 }
895 }
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100896 maybe_invert_and_ret:
Denys Vlasenko11540a82017-01-23 18:01:48 +0100897 return (opt & OPT_r) ? -(int)dif : (int)dif;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100898}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200899
900static void dnsort(struct dnode **dn, int size)
901{
902 qsort(dn, size, sizeof(*dn), sortcmp);
903}
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200904
905static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
906{
907 dnsort(dn, nfiles);
908 display_files(dn, nfiles);
909}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200910#else
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200911# define dnsort(dn, size) ((void)0)
912# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100913#endif
914
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100915/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200916static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100917{
918 struct dnode *dn, *cur, **dnp;
919 struct dirent *entry;
920 DIR *dir;
921 unsigned i, nfiles;
922
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100923 *nfiles_p = 0;
924 dir = warn_opendir(path);
925 if (dir == NULL) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200926 G.exit_code = EXIT_FAILURE;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100927 return NULL; /* could not open the dir */
928 }
929 dn = NULL;
930 nfiles = 0;
931 while ((entry = readdir(dir)) != NULL) {
932 char *fullname;
933
934 /* are we going to list the file- it may be . or .. or a hidden file */
935 if (entry->d_name[0] == '.') {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100936 if (!(option_mask32 & (OPT_a|OPT_A)))
937 continue; /* skip all dotfiles if no -a/-A */
938 if (!(option_mask32 & OPT_a)
939 && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100940 ) {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100941 continue; /* if only -A, skip . and .. but show other dotfiles */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100942 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100943 }
944 fullname = concat_path_file(path, entry->d_name);
945 cur = my_stat(fullname, bb_basename(fullname), 0);
946 if (!cur) {
947 free(fullname);
948 continue;
949 }
950 cur->fname_allocated = 1;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200951 cur->dn_next = dn;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100952 dn = cur;
953 nfiles++;
954 }
955 closedir(dir);
956
957 if (dn == NULL)
958 return NULL;
959
960 /* now that we know how many files there are
961 * allocate memory for an array to hold dnode pointers
962 */
963 *nfiles_p = nfiles;
964 dnp = dnalloc(nfiles);
965 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
966 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200967 dn = dn->dn_next;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100968 if (!dn)
969 break;
970 }
971
972 return dnp;
973}
974
Denys Vlasenko4029e212011-05-13 17:28:46 +0200975#if ENABLE_DESKTOP
976/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
977 * If any of the -l, -n, -s options is specified, each list
978 * of files within the directory shall be preceded by a
979 * status line indicating the number of file system blocks
980 * occupied by files in the directory in 512-byte units if
981 * the -k option is not specified, or 1024-byte units if the
982 * -k option is specified, rounded up to the next integral
983 * number of units.
984 */
985/* by Jorgen Overgaard (jorgen AT antistaten.se) */
986static off_t calculate_blocks(struct dnode **dn)
987{
988 uoff_t blocks = 1;
989 if (dn) {
990 while (*dn) {
991 /* st_blocks is in 512 byte blocks */
992 blocks += (*dn)->dn_blocks;
993 dn++;
994 }
995 }
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000996
Denys Vlasenko4029e212011-05-13 17:28:46 +0200997 /* Even though standard says use 512 byte blocks, coreutils use 1k */
998 /* Actually, we round up by calculating (blocks + 1) / 2,
999 * "+ 1" was done when we initialized blocks to 1 */
1000 return blocks >> 1;
1001}
1002#endif
1003
1004static void scan_and_display_dirs_recur(struct dnode **dn, int first)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001005{
1006 unsigned nfiles;
1007 struct dnode **subdnp;
1008
1009 for (; *dn; dn++) {
Denys Vlasenko9a64c332017-01-23 20:46:12 +01001010 if (G.show_dirname || (option_mask32 & OPT_R)) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001011 if (!first)
1012 bb_putchar('\n');
1013 first = 0;
1014 printf("%s:\n", (*dn)->fullname);
1015 }
Denys Vlasenko4029e212011-05-13 17:28:46 +02001016 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001017#if ENABLE_DESKTOP
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001018 if (option_mask32 & (OPT_s|OPT_l)) {
Lauri Kasanena380aac2018-08-01 19:51:17 +03001019 if (option_mask32 & OPT_h) {
1020 printf("total %-"HUMAN_READABLE_MAX_WIDTH_STR"s\n",
1021 /* print size, no fractions, use suffixes */
1022 make_human_readable_str(calculate_blocks(subdnp) * 1024,
1023 0, 0)
1024 );
1025 } else {
1026 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1027 }
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001028 }
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001029#endif
1030 if (nfiles > 0) {
1031 /* list all files at this level */
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001032 sort_and_display_files(subdnp, nfiles);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001033
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001034 if (ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001035 && (option_mask32 & OPT_R)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001036 ) {
1037 struct dnode **dnd;
1038 unsigned dndirs;
1039 /* recursive - list the sub-dirs */
1040 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1041 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1042 if (dndirs > 0) {
1043 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001044 scan_and_display_dirs_recur(dnd, 0);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001045 /* free the array of dnode pointers to the dirs */
1046 free(dnd);
1047 }
1048 }
1049 /* free the dnodes and the fullname mem */
1050 dfree(subdnp);
1051 }
1052 }
1053}
1054
1055
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001056int ls_main(int argc UNUSED_PARAM, char **argv)
Denys Vlasenkob13b6182017-01-25 04:52:45 +01001057{ /* ^^^^^^^^^^^^^^^^^ note: if FTPD, argc can be wrong, see ftpd.c */
Glenn L McGrath792cae52004-01-18 05:15:16 +00001058 struct dnode **dnd;
1059 struct dnode **dnf;
1060 struct dnode **dnp;
1061 struct dnode *dn;
1062 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001063 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001064 unsigned nfiles;
1065 unsigned dnfiles;
1066 unsigned dndirs;
1067 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001068#if ENABLE_FEATURE_LS_COLOR
1069 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1070 /* coreutils 6.10:
1071 * # ls --color=BOGUS
1072 * ls: invalid argument 'BOGUS' for '--color'
1073 * Valid arguments are:
1074 * 'always', 'yes', 'force'
1075 * 'never', 'no', 'none'
1076 * 'auto', 'tty', 'if-tty'
1077 * (and substrings: "--color=alwa" work too)
1078 */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001079 static const char color_str[] ALIGN1 =
1080 "always\0""yes\0""force\0"
1081 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001082 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001083 const char *color_opt = color_str; /* "always" */
1084#endif
Laurent Bercot25999372017-05-26 16:50:53 +02001085#if ENABLE_LONG_OPTS
1086 static const char ls_longopts[] ALIGN1 =
1087 "full-time\0" No_argument "\xff"
1088 "group-directories-first\0" No_argument "\xfe"
1089 "color\0" Optional_argument "\xfd"
1090 ;
1091#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001092
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001093 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001094
Denys Vlasenko28055022010-01-04 20:49:58 +01001095 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001096
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001097#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001098 /* obtain the terminal width */
Denys Vlasenko641caae2015-10-23 01:44:22 +02001099 G_terminal_width = get_terminal_width(STDIN_FILENO);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001100 /* go one less... */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001101 G_terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001102#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001103
Eric Andersen11c65522000-09-07 17:24:47 +00001104 /* process options */
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001105 opt = getopt32long(argv, "^"
1106 ls_options
1107 "\0"
1108 /* -n and -g imply -l */
1109 "nl:gl"
1110 /* --full-time implies -l */
1111 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l"))
1112 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1113 * in some pairs of opts, only last one takes effect:
1114 */
1115 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
1116 // ":m-l:l-m" - we don't have -m
1117 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1118 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1119 ":C-1:1-C" /* bycols/oneline */
1120 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1121 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
1122 /* -w NUM: */
1123 IF_FEATURE_LS_WIDTH(":w+")
1124 , ls_longopts
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001125 IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001126 IF_FEATURE_LS_COLOR(, &color_opt)
1127 );
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001128#if 0 /* option bits debug */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001129 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 +01001130 if (opt & OPT_c ) bb_error_msg("-c");
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001131 if (opt & OPT_l ) bb_error_msg("-l");
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001132 if (opt & OPT_H ) bb_error_msg("-H");
1133 if (opt & OPT_color ) bb_error_msg("--color");
1134 if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
1135 if (opt & OPT_full_time ) bb_error_msg("--full-time");
1136 exit(0);
1137#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001138
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +01001139#if ENABLE_SELINUX
1140 if (opt & OPT_Z) {
1141 if (!is_selinux_enabled())
1142 option_mask32 &= ~OPT_Z;
1143 }
1144#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001145
Denis Vlasenko5c759602006-10-28 12:37:16 +00001146#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +02001147 /* set G_show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001148 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1149 char *p = getenv("LS_COLORS");
1150 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001151 if (!p || (p[0] && strcmp(p, "none") != 0))
Denys Vlasenko2a816392011-05-13 17:28:09 +02001152 G_show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001153 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001154 if (opt & OPT_color) {
1155 if (color_opt[0] == 'n')
Denys Vlasenko2a816392011-05-13 17:28:09 +02001156 G_show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001157 else switch (index_in_substrings(color_str, color_opt)) {
1158 case 3:
1159 case 4:
1160 case 5:
1161 if (isatty(STDOUT_FILENO)) {
1162 case 0:
1163 case 1:
1164 case 2:
Denys Vlasenko2a816392011-05-13 17:28:09 +02001165 G_show_color = 1;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001166 }
1167 }
Paul Fox156dc412005-08-01 19:33:30 +00001168 }
1169#endif
1170
Eric Andersen11c65522000-09-07 17:24:47 +00001171 /* sort out which command line options take precedence */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001172 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1173 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001174 if (!(opt & OPT_l)) { /* not -l? */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001175 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1176 /* when to sort by time? -t[cu] sorts by time even with -l */
1177 /* (this is achieved by opt_flags[] element for -t) */
1178 /* without -l, bare -c or -u enable sort too */
1179 /* (with -l, bare -c or -u just select which time to show) */
1180 if (opt & (OPT_c|OPT_u)) {
Denys Vlasenko11540a82017-01-23 18:01:48 +01001181 option_mask32 |= OPT_t;
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001182 }
1183 }
1184 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001185
Denys Vlasenkof3137462010-12-19 05:05:34 +01001186 /* choose a display format if one was not already specified by an option */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001187 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C)))
1188 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1);
Eric Andersen11c65522000-09-07 17:24:47 +00001189
Denys Vlasenkob13b6182017-01-25 04:52:45 +01001190 if (ENABLE_FTPD && applet_name[0] == 'f') {
1191 /* ftpd secret backdoor. dirs first are much nicer */
1192 option_mask32 |= OPT_dirs_first;
1193 }
1194
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001195 argv += optind;
1196 if (!argv[0])
1197 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001198
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001199 if (argv[1])
Denys Vlasenko9a64c332017-01-23 20:46:12 +01001200 G.show_dirname = 1; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001201
Denis Vlasenko5c759602006-10-28 12:37:16 +00001202 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001203 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001204 nfiles = 0;
1205 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001206 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001207 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001208 !(option_mask32 & (OPT_l|OPT_s|OPT_F))
Denys Vlasenko982aa262010-12-19 21:54:39 +01001209 /* ... or if -H: */
1210 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001211 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001212 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001213 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001214 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001215 continue;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001216 /*cur->fname_allocated = 0; - already is */
1217 cur->dn_next = dn;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001218 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001219 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001220 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001221
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001222 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1223 if (nfiles == 0)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001224 return G.exit_code;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001225
Eric Andersen11c65522000-09-07 17:24:47 +00001226 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001227 * allocate memory for an array to hold dnode pointers
1228 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001229 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001230 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1231 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001232 dn = dn->dn_next;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001233 if (!dn)
1234 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001235 }
1236
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001237 if (option_mask32 & OPT_d) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001238 sort_and_display_files(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001239 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001240 dnd = splitdnarray(dnp, SPLIT_DIR);
1241 dnf = splitdnarray(dnp, SPLIT_FILE);
1242 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001243 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001244 if (dnfiles > 0) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001245 sort_and_display_files(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001246 if (ENABLE_FEATURE_CLEAN_UP)
1247 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001248 }
1249 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001250 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001251 scan_and_display_dirs_recur(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001252 if (ENABLE_FEATURE_CLEAN_UP)
1253 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001254 }
1255 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001256
Rob Landley26314862006-05-02 19:46:52 +00001257 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001258 dfree(dnp);
Denys Vlasenko2a816392011-05-13 17:28:09 +02001259 return G.exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001260}