blob: 0fe0345b317a02857fb07eb07d7989b9ba94c80a [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 */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000209static const char ls_options[] ALIGN1 =
Denys Vlasenko11540a82017-01-23 18:01:48 +0100210 "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 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100217 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 */
220;
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
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000350
Denys Vlasenko4029e212011-05-13 17:28:46 +0200351/*** Output code ***/
Denys Vlasenko2a816392011-05-13 17:28:09 +0200352
Eric Andersen79565b62000-08-11 18:10:21 +0000353
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000354/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
355 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
356 * 3/7:multiplexed char/block device)
357 * and we use 0 for unknown and 15 for executables (see below) */
358#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200359/* un fi chr - dir - blk - file - link - sock - - exe */
360#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000361/* 036 black foreground 050 black background
362 037 red foreground 051 red background
363 040 green foreground 052 green background
364 041 brown foreground 053 brown background
365 042 blue foreground 054 blue background
366 043 magenta (purple) foreground 055 magenta background
367 044 cyan (light blue) foreground 056 cyan background
368 045 gray foreground 057 white background
369*/
370#define COLOR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200371 /*un fi chr - dir - blk - file - link - sock - - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000372 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
373 [TYPEINDEX(mode)])
374/* Select normal (0) [actually "reset all"] or bold (1)
375 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
376 * let's use 7 for "impossible" types, just for fun)
377 * Note: coreutils 6.9 uses inverted red for setuid binaries.
378 */
379#define ATTR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200380 /*un fi chr - dir - blk - file- link- sock- - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000381 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
382 [TYPEINDEX(mode)])
383
Denis Vlasenko5c759602006-10-28 12:37:16 +0000384#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000385/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000386static char fgcolor(mode_t mode)
387{
Rob Landley9947a242006-06-15 22:11:10 +0000388 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000389 return COLOR(0xF000); /* File is executable ... */
390 return COLOR(mode);
391}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000392static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000393{
Rob Landley9947a242006-06-15 22:11:10 +0000394 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000395 return ATTR(0xF000); /* File is executable ... */
396 return ATTR(mode);
397}
398#endif
399
Denys Vlasenko2a816392011-05-13 17:28:09 +0200400#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000401static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000402{
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100403 if (!(option_mask32 & (OPT_F|OPT_p)))
Eric Andersen11c65522000-09-07 17:24:47 +0000404 return '\0';
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100405
Rob Landley9947a242006-06-15 22:11:10 +0000406 if (S_ISDIR(mode))
407 return '/';
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100408 if (!(option_mask32 & OPT_F))
Rob Landley9947a242006-06-15 22:11:10 +0000409 return '\0';
410 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000411 return '*';
412 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000413}
414#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000415
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100416static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000417{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100418 unsigned len;
419 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000420
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100421 // TODO: quote tab as \t, etc, if -Q
422 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000423
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100424 if (!(option_mask32 & OPT_Q)) {
425 return uni_stat.unicode_width;
426 }
427
428 len = 2 + uni_stat.unicode_width;
429 while (*name) {
430 if (*name == '"' || *name == '\\') {
431 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000432 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100433 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000434 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100435 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000436}
437
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100438/* Return the number of used columns.
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100439 * Note that only columnar output uses return value.
440 * -l and -1 modes don't care.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100441 * coreutils 7.2 also supports:
442 * ls -b (--escape) = octal escapes (although it doesn't look like working)
443 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200444 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100445static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200446{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100447 unsigned len;
448 uni_stat_t uni_stat;
449
450 // TODO: quote tab as \t, etc, if -Q
451 name = printable_string(&uni_stat, name);
452
453 if (!(option_mask32 & OPT_Q)) {
454 fputs(name, stdout);
455 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200456 }
457
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100458 len = 2 + uni_stat.unicode_width;
459 putchar('"');
460 while (*name) {
461 if (*name == '"' || *name == '\\') {
462 putchar('\\');
463 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000464 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700465 putchar(*name);
466 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000467 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100468 putchar('"');
469 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000470}
471
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100472/* Return the number of used columns.
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100473 * Note that only columnar output uses return value,
474 * -l and -1 modes don't care.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100475 */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200476static NOINLINE unsigned display_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000477{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200478 unsigned column = 0;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200479 char *lpath;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100480 int opt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000481#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200482 struct stat statbuf;
Eric Andersen11c65522000-09-07 17:24:47 +0000483 char append;
484#endif
485
Denis Vlasenko5c759602006-10-28 12:37:16 +0000486#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200487 append = append_char(dn->dn_mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000488#endif
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100489 opt = option_mask32;
Eric Andersen11c65522000-09-07 17:24:47 +0000490
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000491 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100492 * does not appear *inside* the "ls -l" line */
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200493 lpath = NULL;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100494 if (opt & OPT_l)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200495 if (S_ISLNK(dn->dn_mode))
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000496 lpath = xmalloc_readlink_or_warn(dn->fullname);
497
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100498 if (opt & OPT_i) /* show inode# */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200499 column += printf("%7llu ", (long long) dn->dn_ino);
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100500//TODO: -h should affect -s too:
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100501 if (opt & OPT_s) /* show allocated blocks */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200502 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100503 if (opt & OPT_l) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100504 /* long listing: show mode */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200505 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100506 /* long listing: show number of links */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200507 column += printf("%4lu ", (long) dn->dn_nlink);
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100508 /* long listing: show user/group */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100509 if (opt & OPT_n) {
510 if (opt & OPT_g)
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100511 column += printf("%-8u ", (int) dn->dn_gid);
512 else
513 column += printf("%-8u %-8u ",
514 (int) dn->dn_uid,
515 (int) dn->dn_gid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000516 }
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100517#if ENABLE_FEATURE_LS_USERNAME
518 else {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100519 if (opt & OPT_g) {
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100520 column += printf("%-8.8s ",
521 get_cached_groupname(dn->dn_gid));
522 } else {
523 column += printf("%-8.8s %-8.8s ",
524 get_cached_username(dn->dn_uid),
525 get_cached_groupname(dn->dn_gid));
526 }
527 }
Eric Andersen11c65522000-09-07 17:24:47 +0000528#endif
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100529#if ENABLE_SELINUX
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100530 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100531 if (opt & OPT_Z) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100532 column += printf("%-32s ", dn->sid ? dn->sid : "?");
533 freecon(dn->sid);
534 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100535 if (opt & OPT_l) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100536#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100537 /* long listing: show size */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200538 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000539 column += printf("%4u, %3u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200540 dn->dn_rdev_maj,
541 dn->dn_rdev_min);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000542 } else {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100543 if (opt & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100544 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200545 /* print size, show one fractional, use suffixes */
546 make_human_readable_str(dn->dn_size, 1, 0)
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200547 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000548 } else {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200549 column += printf("%9"OFF_FMT"u ", dn->dn_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000550 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000551 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000552#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100553 /* long listing: show {m,c,a}time */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100554 if (opt & OPT_full_time) { /* --full-time */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100555 /* coreutils 8.4 ls --full-time prints:
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100556 * 2009-07-13 17:49:27.000000000 +0200
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100557 * we don't show fractional seconds.
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100558 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100559 char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")];
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100560 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z",
561 localtime(&dn->dn_time));
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100562 column += printf("%s ", buf);
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100563 } else { /* ordinary time format */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100564 /* G.current_time_t is ~== time(NULL) */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100565 char *filetime = ctime(&dn->dn_time);
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100566 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100567 time_t age = G.current_time_t - dn->dn_time;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000568 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
Denys Vlasenko8e92df12015-02-16 15:36:25 +0100569 /* less than 6 months old */
570 /* "mmm dd hh:mm " */
571 printf("%.12s ", filetime + 4);
572 } else {
573 /* "mmm dd yyyy " */
574 /* "mmm dd yyyyy " after year 9999 :) */
575 strchr(filetime + 20, '\n')[0] = ' ';
576 printf("%.7s%6s", filetime + 4, filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000577 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000578 column += 13;
579 }
Eric Andersen11c65522000-09-07 17:24:47 +0000580#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100581 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100582
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000583#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200584 if (G_show_color) {
585 mode_t mode = dn->dn_mode_lstat;
586 if (!mode)
587 if (lstat(dn->fullname, &statbuf) == 0)
588 mode = statbuf.st_mode;
589 printf("\033[%u;%um", bold(mode), fgcolor(mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000590 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100591#endif
592 column += print_name(dn->name);
Denys Vlasenko2a816392011-05-13 17:28:09 +0200593 if (G_show_color) {
James Youngmana4bc10c2010-12-20 01:36:16 +0100594 printf("\033[0m");
595 }
596
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200597 if (lpath) {
598 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000599#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100600 if ((opt & (OPT_F|OPT_p))
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100601 || G_show_color
602 ) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200603 mode_t mode = dn->dn_mode_stat;
604 if (!mode)
605 if (stat(dn->fullname, &statbuf) == 0)
606 mode = statbuf.st_mode;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200607# if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200608 append = append_char(mode);
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200609# endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200610# if ENABLE_FEATURE_LS_COLOR
611 if (G_show_color) {
612 printf("\033[%u;%um", bold(mode), fgcolor(mode));
613 }
614# endif
Eric Andersen11c65522000-09-07 17:24:47 +0000615 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200616#endif
617 column += print_name(lpath) + 4;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200618 free(lpath);
619 if (G_show_color) {
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200620 printf("\033[0m");
621 }
Eric Andersen11c65522000-09-07 17:24:47 +0000622 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000623#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100624 if (opt & (OPT_F|OPT_p)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000625 if (append) {
626 putchar(append);
627 column++;
628 }
629 }
630#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000631
Glenn L McGrath4d001292003-01-06 01:11:50 +0000632 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000633}
634
Denys Vlasenko4029e212011-05-13 17:28:46 +0200635static void display_files(struct dnode **dn, unsigned nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100636{
637 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100638 unsigned column;
639 unsigned nexttab;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100640 unsigned column_width = 0; /* used only by coulmnal output */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100641
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100642 if (option_mask32 & (OPT_l|OPT_1)) {
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100643 ncols = 1;
644 } else {
645 /* find the longest file name, use that as the column width */
646 for (i = 0; dn[i]; i++) {
647 int len = calc_name_len(dn[i]->name);
648 if (column_width < len)
649 column_width = len;
650 }
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100651 column_width += 2
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100652 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100653 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
654 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
655 ;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200656 ncols = (unsigned)G_terminal_width / column_width;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100657 }
658
659 if (ncols > 1) {
660 nrows = nfiles / ncols;
661 if (nrows * ncols < nfiles)
662 nrows++; /* round up fractionals */
663 } else {
664 nrows = nfiles;
665 ncols = 1;
666 }
667
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100668 column = 0;
669 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100670 for (row = 0; row < nrows; row++) {
671 for (nc = 0; nc < ncols; nc++) {
672 /* reach into the array based on the column and row */
Denys Vlasenko92c68982017-01-23 20:21:14 +0100673 if (option_mask32 & OPT_x)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100674 i = (row * ncols) + nc; /* display across row */
675 else
676 i = (nc * nrows) + row; /* display by column */
677 if (i < nfiles) {
678 if (column > 0) {
679 nexttab -= column;
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100680 printf("%*s", nexttab, "");
681 column += nexttab;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100682 }
683 nexttab = column + column_width;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200684 column += display_single(dn[i]);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100685 }
686 }
687 putchar('\n');
688 column = 0;
689 }
690}
691
692
Denys Vlasenko4029e212011-05-13 17:28:46 +0200693/*** Dir scanning code ***/
694
695static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100696{
Denys Vlasenko4029e212011-05-13 17:28:46 +0200697 struct stat statbuf;
698 struct dnode *cur;
699
700 cur = xzalloc(sizeof(*cur));
701 cur->fullname = fullname;
702 cur->name = name;
703
704 if ((option_mask32 & OPT_L) || force_follow) {
705#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100706 if (option_mask32 & OPT_Z) {
Denys Vlasenko69675782013-01-14 01:34:48 +0100707 getfilecon(fullname, &cur->sid);
Denys Vlasenko4029e212011-05-13 17:28:46 +0200708 }
709#endif
710 if (stat(fullname, &statbuf)) {
711 bb_simple_perror_msg(fullname);
712 G.exit_code = EXIT_FAILURE;
713 free(cur);
714 return NULL;
715 }
716 cur->dn_mode_stat = statbuf.st_mode;
717 } else {
718#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100719 if (option_mask32 & OPT_Z) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200720 lgetfilecon(fullname, &cur->sid);
721 }
722#endif
723 if (lstat(fullname, &statbuf)) {
724 bb_simple_perror_msg(fullname);
725 G.exit_code = EXIT_FAILURE;
726 free(cur);
727 return NULL;
728 }
729 cur->dn_mode_lstat = statbuf.st_mode;
730 }
731
732 /* cur->dstat = statbuf: */
733 cur->dn_mode = statbuf.st_mode ;
734 cur->dn_size = statbuf.st_size ;
735#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100736 cur->dn_time = statbuf.st_mtime ;
737 if (option_mask32 & OPT_u)
738 cur->dn_time = statbuf.st_atime;
739 if (option_mask32 & OPT_c)
740 cur->dn_time = statbuf.st_ctime;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200741#endif
742 cur->dn_ino = statbuf.st_ino ;
743 cur->dn_blocks = statbuf.st_blocks;
744 cur->dn_nlink = statbuf.st_nlink ;
745 cur->dn_uid = statbuf.st_uid ;
746 cur->dn_gid = statbuf.st_gid ;
747 cur->dn_rdev_maj = major(statbuf.st_rdev);
748 cur->dn_rdev_min = minor(statbuf.st_rdev);
749
750 return cur;
751}
752
753static unsigned count_dirs(struct dnode **dn, int which)
754{
755 unsigned dirs, all;
756
757 if (!dn)
758 return 0;
759
760 dirs = all = 0;
761 for (; *dn; dn++) {
762 const char *name;
763
764 all++;
765 if (!S_ISDIR((*dn)->dn_mode))
766 continue;
767
768 name = (*dn)->name;
769 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
770 /* or if it's not . or .. */
771 || name[0] != '.'
772 || (name[1] && (name[1] != '.' || name[2]))
773 ) {
774 dirs++;
775 }
776 }
777 return which != SPLIT_FILE ? dirs : all - dirs;
778}
779
780/* get memory to hold an array of pointers */
781static struct dnode **dnalloc(unsigned num)
782{
783 if (num < 1)
784 return NULL;
785
786 num++; /* so that we have terminating NULL */
787 return xzalloc(num * sizeof(struct dnode *));
788}
789
790#if ENABLE_FEATURE_LS_RECURSIVE
791static void dfree(struct dnode **dnp)
792{
793 unsigned i;
794
795 if (dnp == NULL)
796 return;
797
798 for (i = 0; dnp[i]; i++) {
799 struct dnode *cur = dnp[i];
800 if (cur->fname_allocated)
801 free((char*)cur->fullname);
802 free(cur);
803 }
804 free(dnp);
805}
806#else
807#define dfree(...) ((void)0)
808#endif
809
810/* Returns NULL-terminated malloced vector of pointers (or NULL) */
811static struct dnode **splitdnarray(struct dnode **dn, int which)
812{
813 unsigned dncnt, d;
814 struct dnode **dnp;
815
816 if (dn == NULL)
817 return NULL;
818
819 /* count how many dirs or files there are */
820 dncnt = count_dirs(dn, which);
821
822 /* allocate a file array and a dir array */
823 dnp = dnalloc(dncnt);
824
825 /* copy the entrys into the file or dir array */
826 for (d = 0; *dn; dn++) {
827 if (S_ISDIR((*dn)->dn_mode)) {
828 const char *name;
829
830 if (which == SPLIT_FILE)
831 continue;
832
833 name = (*dn)->name;
834 if ((which & SPLIT_DIR) /* any dir... */
835 /* ... or not . or .. */
836 || name[0] != '.'
837 || (name[1] && (name[1] != '.' || name[2]))
838 ) {
839 dnp[d++] = *dn;
840 }
841 } else
842 if (which == SPLIT_FILE) {
843 dnp[d++] = *dn;
844 }
845 }
846 return dnp;
847}
848
849#if ENABLE_FEATURE_LS_SORTFILES
850static int sortcmp(const void *a, const void *b)
851{
852 struct dnode *d1 = *(struct dnode **)a;
853 struct dnode *d2 = *(struct dnode **)b;
Denys Vlasenko11540a82017-01-23 18:01:48 +0100854 unsigned opt = option_mask32;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200855 off_t dif;
856
Denys Vlasenko11540a82017-01-23 18:01:48 +0100857 dif = 0; /* assume sort by name */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200858 // TODO: use pre-initialized function pointer
859 // instead of branch forest
Denys Vlasenko11540a82017-01-23 18:01:48 +0100860 if (opt & OPT_dirs_first) {
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100861 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
862 if (dif != 0)
863 goto maybe_invert_and_ret;
864 }
865
Denys Vlasenko11540a82017-01-23 18:01:48 +0100866 if (opt & OPT_S) { /* sort by size */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200867 dif = (d2->dn_size - d1->dn_size);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200868 } else
Denys Vlasenko11540a82017-01-23 18:01:48 +0100869 if (opt & OPT_t) { /* sort by time */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100870 dif = (d2->dn_time - d1->dn_time);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200871 } else
Denys Vlasenko1e18a012011-06-21 17:12:52 +0200872#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
Denys Vlasenko11540a82017-01-23 18:01:48 +0100873 if (opt & OPT_v) { /* sort by version */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200874 dif = strverscmp(d1->name, d2->name);
875 } else
Denys Vlasenko561f9c82011-06-21 16:38:29 +0200876#endif
Denys Vlasenko11540a82017-01-23 18:01:48 +0100877 if (opt & OPT_X) { /* sort by extension */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200878 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
Denys Vlasenko4029e212011-05-13 17:28:46 +0200879 }
880 if (dif == 0) {
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200881 /* sort by name, use as tie breaker for other sorts */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200882 if (ENABLE_LOCALE_SUPPORT)
883 dif = strcoll(d1->name, d2->name);
884 else
885 dif = strcmp(d1->name, d2->name);
Denys Vlasenko11540a82017-01-23 18:01:48 +0100886 } else {
887 /* Make dif fit into an int */
888 if (sizeof(dif) > sizeof(int)) {
889 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
890 /* shift leaving only "int" worth of bits */
891 /* (this requires dif != 0, and here it is nonzero) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200892 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100893 }
894 }
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100895 maybe_invert_and_ret:
Denys Vlasenko11540a82017-01-23 18:01:48 +0100896 return (opt & OPT_r) ? -(int)dif : (int)dif;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100897}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200898
899static void dnsort(struct dnode **dn, int size)
900{
901 qsort(dn, size, sizeof(*dn), sortcmp);
902}
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200903
904static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
905{
906 dnsort(dn, nfiles);
907 display_files(dn, nfiles);
908}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200909#else
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200910# define dnsort(dn, size) ((void)0)
911# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100912#endif
913
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100914/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200915static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100916{
917 struct dnode *dn, *cur, **dnp;
918 struct dirent *entry;
919 DIR *dir;
920 unsigned i, nfiles;
921
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100922 *nfiles_p = 0;
923 dir = warn_opendir(path);
924 if (dir == NULL) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200925 G.exit_code = EXIT_FAILURE;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100926 return NULL; /* could not open the dir */
927 }
928 dn = NULL;
929 nfiles = 0;
930 while ((entry = readdir(dir)) != NULL) {
931 char *fullname;
932
933 /* are we going to list the file- it may be . or .. or a hidden file */
934 if (entry->d_name[0] == '.') {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100935 if (!(option_mask32 & (OPT_a|OPT_A)))
936 continue; /* skip all dotfiles if no -a/-A */
937 if (!(option_mask32 & OPT_a)
938 && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100939 ) {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100940 continue; /* if only -A, skip . and .. but show other dotfiles */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100941 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100942 }
943 fullname = concat_path_file(path, entry->d_name);
944 cur = my_stat(fullname, bb_basename(fullname), 0);
945 if (!cur) {
946 free(fullname);
947 continue;
948 }
949 cur->fname_allocated = 1;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200950 cur->dn_next = dn;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100951 dn = cur;
952 nfiles++;
953 }
954 closedir(dir);
955
956 if (dn == NULL)
957 return NULL;
958
959 /* now that we know how many files there are
960 * allocate memory for an array to hold dnode pointers
961 */
962 *nfiles_p = nfiles;
963 dnp = dnalloc(nfiles);
964 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
965 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200966 dn = dn->dn_next;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100967 if (!dn)
968 break;
969 }
970
971 return dnp;
972}
973
Denys Vlasenko4029e212011-05-13 17:28:46 +0200974#if ENABLE_DESKTOP
975/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
976 * If any of the -l, -n, -s options is specified, each list
977 * of files within the directory shall be preceded by a
978 * status line indicating the number of file system blocks
979 * occupied by files in the directory in 512-byte units if
980 * the -k option is not specified, or 1024-byte units if the
981 * -k option is specified, rounded up to the next integral
982 * number of units.
983 */
984/* by Jorgen Overgaard (jorgen AT antistaten.se) */
985static off_t calculate_blocks(struct dnode **dn)
986{
987 uoff_t blocks = 1;
988 if (dn) {
989 while (*dn) {
990 /* st_blocks is in 512 byte blocks */
991 blocks += (*dn)->dn_blocks;
992 dn++;
993 }
994 }
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000995
Denys Vlasenko4029e212011-05-13 17:28:46 +0200996 /* Even though standard says use 512 byte blocks, coreutils use 1k */
997 /* Actually, we round up by calculating (blocks + 1) / 2,
998 * "+ 1" was done when we initialized blocks to 1 */
999 return blocks >> 1;
1000}
1001#endif
1002
1003static void scan_and_display_dirs_recur(struct dnode **dn, int first)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001004{
1005 unsigned nfiles;
1006 struct dnode **subdnp;
1007
1008 for (; *dn; dn++) {
Denys Vlasenko9a64c332017-01-23 20:46:12 +01001009 if (G.show_dirname || (option_mask32 & OPT_R)) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001010 if (!first)
1011 bb_putchar('\n');
1012 first = 0;
1013 printf("%s:\n", (*dn)->fullname);
1014 }
Denys Vlasenko4029e212011-05-13 17:28:46 +02001015 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001016#if ENABLE_DESKTOP
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001017 if (option_mask32 & (OPT_s|OPT_l)) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001018 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001019 }
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001020#endif
1021 if (nfiles > 0) {
1022 /* list all files at this level */
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001023 sort_and_display_files(subdnp, nfiles);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001024
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001025 if (ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001026 && (option_mask32 & OPT_R)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001027 ) {
1028 struct dnode **dnd;
1029 unsigned dndirs;
1030 /* recursive - list the sub-dirs */
1031 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1032 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1033 if (dndirs > 0) {
1034 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001035 scan_and_display_dirs_recur(dnd, 0);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001036 /* free the array of dnode pointers to the dirs */
1037 free(dnd);
1038 }
1039 }
1040 /* free the dnodes and the fullname mem */
1041 dfree(subdnp);
1042 }
1043 }
1044}
1045
1046
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001047int ls_main(int argc UNUSED_PARAM, char **argv)
Denys Vlasenkob13b6182017-01-25 04:52:45 +01001048{ /* ^^^^^^^^^^^^^^^^^ note: if FTPD, argc can be wrong, see ftpd.c */
Glenn L McGrath792cae52004-01-18 05:15:16 +00001049 struct dnode **dnd;
1050 struct dnode **dnf;
1051 struct dnode **dnp;
1052 struct dnode *dn;
1053 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001054 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001055 unsigned nfiles;
1056 unsigned dnfiles;
1057 unsigned dndirs;
1058 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001059#if ENABLE_FEATURE_LS_COLOR
1060 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1061 /* coreutils 6.10:
1062 * # ls --color=BOGUS
1063 * ls: invalid argument 'BOGUS' for '--color'
1064 * Valid arguments are:
1065 * 'always', 'yes', 'force'
1066 * 'never', 'no', 'none'
1067 * 'auto', 'tty', 'if-tty'
1068 * (and substrings: "--color=alwa" work too)
1069 */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001070 static const char color_str[] ALIGN1 =
1071 "always\0""yes\0""force\0"
1072 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001073 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001074 const char *color_opt = color_str; /* "always" */
1075#endif
Laurent Bercot25999372017-05-26 16:50:53 +02001076#if ENABLE_LONG_OPTS
1077 static const char ls_longopts[] ALIGN1 =
1078 "full-time\0" No_argument "\xff"
1079 "group-directories-first\0" No_argument "\xfe"
1080 "color\0" Optional_argument "\xfd"
1081 ;
1082#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001083
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001084 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001085
Denys Vlasenko28055022010-01-04 20:49:58 +01001086 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001087
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001088#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001089 /* obtain the terminal width */
Denys Vlasenko641caae2015-10-23 01:44:22 +02001090 G_terminal_width = get_terminal_width(STDIN_FILENO);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001091 /* go one less... */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001092 G_terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001093#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001094
Eric Andersen11c65522000-09-07 17:24:47 +00001095 /* process options */
Laurent Bercot25999372017-05-26 16:50:53 +02001096 IF_LONG_OPTS(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001097 opt_complementary =
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001098 /* -n and -g imply -l */
1099 "nl:gl"
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001100 /* --full-time implies -l */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001101 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l"))
Denys Vlasenkof3137462010-12-19 05:05:34 +01001102 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1103 * in some pairs of opts, only last one takes effect:
1104 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001105 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001106 // ":m-l:l-m" - we don't have -m
1107 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001108 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1109 ":C-1:1-C" /* bycols/oneline */
1110 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
Denys Vlasenkob47b3ce2011-08-10 00:51:29 +02001111 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001112 /* -w NUM: */
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001113 IF_FEATURE_LS_WIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001114 opt = getopt32(argv, ls_options
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001115 IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001116 IF_FEATURE_LS_COLOR(, &color_opt)
1117 );
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001118#if 0 /* option bits debug */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001119 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 +01001120 if (opt & OPT_c ) bb_error_msg("-c");
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001121 if (opt & OPT_l ) bb_error_msg("-l");
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001122 if (opt & OPT_H ) bb_error_msg("-H");
1123 if (opt & OPT_color ) bb_error_msg("--color");
1124 if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
1125 if (opt & OPT_full_time ) bb_error_msg("--full-time");
1126 exit(0);
1127#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001128
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +01001129#if ENABLE_SELINUX
1130 if (opt & OPT_Z) {
1131 if (!is_selinux_enabled())
1132 option_mask32 &= ~OPT_Z;
1133 }
1134#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001135
Denis Vlasenko5c759602006-10-28 12:37:16 +00001136#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +02001137 /* set G_show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001138 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1139 char *p = getenv("LS_COLORS");
1140 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001141 if (!p || (p[0] && strcmp(p, "none") != 0))
Denys Vlasenko2a816392011-05-13 17:28:09 +02001142 G_show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001143 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001144 if (opt & OPT_color) {
1145 if (color_opt[0] == 'n')
Denys Vlasenko2a816392011-05-13 17:28:09 +02001146 G_show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001147 else switch (index_in_substrings(color_str, color_opt)) {
1148 case 3:
1149 case 4:
1150 case 5:
1151 if (isatty(STDOUT_FILENO)) {
1152 case 0:
1153 case 1:
1154 case 2:
Denys Vlasenko2a816392011-05-13 17:28:09 +02001155 G_show_color = 1;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001156 }
1157 }
Paul Fox156dc412005-08-01 19:33:30 +00001158 }
1159#endif
1160
Eric Andersen11c65522000-09-07 17:24:47 +00001161 /* sort out which command line options take precedence */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001162 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1163 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001164 if (!(opt & OPT_l)) { /* not -l? */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001165 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1166 /* when to sort by time? -t[cu] sorts by time even with -l */
1167 /* (this is achieved by opt_flags[] element for -t) */
1168 /* without -l, bare -c or -u enable sort too */
1169 /* (with -l, bare -c or -u just select which time to show) */
1170 if (opt & (OPT_c|OPT_u)) {
Denys Vlasenko11540a82017-01-23 18:01:48 +01001171 option_mask32 |= OPT_t;
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001172 }
1173 }
1174 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001175
Denys Vlasenkof3137462010-12-19 05:05:34 +01001176 /* choose a display format if one was not already specified by an option */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001177 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C)))
1178 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1);
Eric Andersen11c65522000-09-07 17:24:47 +00001179
Denys Vlasenkob13b6182017-01-25 04:52:45 +01001180 if (ENABLE_FTPD && applet_name[0] == 'f') {
1181 /* ftpd secret backdoor. dirs first are much nicer */
1182 option_mask32 |= OPT_dirs_first;
1183 }
1184
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001185 argv += optind;
1186 if (!argv[0])
1187 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001188
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001189 if (argv[1])
Denys Vlasenko9a64c332017-01-23 20:46:12 +01001190 G.show_dirname = 1; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001191
Denis Vlasenko5c759602006-10-28 12:37:16 +00001192 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001193 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001194 nfiles = 0;
1195 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001196 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001197 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001198 !(option_mask32 & (OPT_l|OPT_s|OPT_F))
Denys Vlasenko982aa262010-12-19 21:54:39 +01001199 /* ... or if -H: */
1200 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001201 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001202 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001203 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001204 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001205 continue;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001206 /*cur->fname_allocated = 0; - already is */
1207 cur->dn_next = dn;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001208 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001209 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001210 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001211
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001212 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1213 if (nfiles == 0)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001214 return G.exit_code;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001215
Eric Andersen11c65522000-09-07 17:24:47 +00001216 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001217 * allocate memory for an array to hold dnode pointers
1218 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001219 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001220 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1221 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001222 dn = dn->dn_next;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001223 if (!dn)
1224 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001225 }
1226
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001227 if (option_mask32 & OPT_d) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001228 sort_and_display_files(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001229 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001230 dnd = splitdnarray(dnp, SPLIT_DIR);
1231 dnf = splitdnarray(dnp, SPLIT_FILE);
1232 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001233 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001234 if (dnfiles > 0) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001235 sort_and_display_files(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001236 if (ENABLE_FEATURE_CLEAN_UP)
1237 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001238 }
1239 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001240 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001241 scan_and_display_dirs_recur(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001242 if (ENABLE_FEATURE_CLEAN_UP)
1243 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001244 }
1245 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001246
Rob Landley26314862006-05-02 19:46:52 +00001247 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001248 dfree(dnp);
Denys Vlasenko2a816392011-05-13 17:28:09 +02001249 return G.exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001250}