blob: f90784a1a624df58894474f1015006a9898ee8e4 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersen9d3aba71999-10-06 09:04:55 +00002/*
Eric Andersencc8ed391999-10-05 16:24:54 +00003 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
Eric Andersen11c65522000-09-07 17:24:47 +00004 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02005 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencc8ed391999-10-05 16:24:54 +00006 */
Denis Vlasenko5e4fda02009-03-08 23:46:48 +00007/* [date unknown. Perhaps before year 2000]
Eric Andersencc8ed391999-10-05 16:24:54 +00008 * To achieve a small memory footprint, this version of 'ls' doesn't do any
9 * file sorting, and only has the most essential command line switches
Eric Andersen77d92682001-05-23 20:32:09 +000010 * (i.e., the ones I couldn't live without :-) All features which involve
Eric Andersencc8ed391999-10-05 16:24:54 +000011 * linking in substantial chunks of libc can be disabled.
12 *
13 * Although I don't really want to add new features to this program to
14 * keep it small, I *am* interested to receive bug fixes and ways to make
15 * it more portable.
16 *
17 * KNOWN BUGS:
Denys Vlasenko87c150c2009-10-03 01:14:15 +020018 * 1. hidden files can make column width too large
Erik Andersen9ffdaa62000-02-11 21:55:04 +000019 *
Eric Andersencc8ed391999-10-05 16:24:54 +000020 * NON-OPTIMAL BEHAVIOUR:
21 * 1. autowidth reads directories twice
22 * 2. if you do a short directory listing without filetype characters
23 * appended, there's no need to stat each one
24 * PORTABILITY:
25 * 1. requires lstat (BSD) - how do you do it without?
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000026 *
27 * [2009-03]
28 * ls sorts listing now, and supports almost all options.
Eric Andersencc8ed391999-10-05 16:24:54 +000029 */
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010030//config:config LS
31//config: bool "ls"
32//config: default y
33//config: help
34//config: ls is used to list the contents of directories.
35//config:
36//config:config FEATURE_LS_FILETYPES
37//config: bool "Enable filetyping options (-p and -F)"
38//config: default y
39//config: depends on LS
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010040//config:
41//config:config FEATURE_LS_FOLLOWLINKS
42//config: bool "Enable symlinks dereferencing (-L)"
43//config: default y
44//config: depends on LS
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010045//config:
46//config:config FEATURE_LS_RECURSIVE
47//config: bool "Enable recursion (-R)"
48//config: default y
49//config: depends on LS
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010050//config:
Denys Vlasenkoed15dde2017-01-11 16:35:52 +010051//config:config FEATURE_LS_WIDTH
52//config: bool "Enable -w WIDTH and window size autodetection"
53//config: default y
54//config: depends on LS
55//config:
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010056//config:config FEATURE_LS_SORTFILES
57//config: bool "Sort the file names"
58//config: default y
59//config: depends on LS
60//config: help
61//config: Allow ls to sort file names alphabetically.
62//config:
63//config:config FEATURE_LS_TIMESTAMPS
64//config: bool "Show file timestamps"
65//config: default y
66//config: depends on LS
67//config: help
68//config: Allow ls to display timestamps for files.
69//config:
70//config:config FEATURE_LS_USERNAME
71//config: bool "Show username/groupnames"
72//config: default y
73//config: depends on LS
74//config: help
75//config: Allow ls to display username/groupname for files.
76//config:
77//config:config FEATURE_LS_COLOR
78//config: bool "Allow use of color to identify file types"
79//config: default y
80//config: depends on LS && LONG_OPTS
81//config: help
82//config: This enables the --color option to ls.
83//config:
84//config:config FEATURE_LS_COLOR_IS_DEFAULT
85//config: bool "Produce colored ls output by default"
86//config: default y
87//config: depends on FEATURE_LS_COLOR
88//config: help
89//config: Saying yes here will turn coloring on by default,
90//config: even if no "--color" option is given to the ls command.
91//config: This is not recommended, since the colors are not
92//config: configurable, and the output may not be legible on
93//config: many output screens.
94
95//applet:IF_LS(APPLET_NOEXEC(ls, ls, BB_DIR_BIN, BB_SUID_DROP, ls))
96
97//kbuild:lib-$(CONFIG_LS) += ls.o
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010098
99//usage:#define ls_trivial_usage
100//usage: "[-1AaCxd"
Denys Vlasenko982aa262010-12-19 21:54:39 +0100101//usage: IF_FEATURE_LS_FOLLOWLINKS("LH")
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100102//usage: IF_FEATURE_LS_RECURSIVE("R")
103//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100104//usage: IF_FEATURE_HUMAN_READABLE("h")
105//usage: IF_FEATURE_LS_SORTFILES("rSXv")
106//usage: IF_FEATURE_LS_TIMESTAMPS("ctu")
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100107//usage: IF_SELINUX("kZ") "]"
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100108//usage: IF_FEATURE_LS_WIDTH(" [-w WIDTH]") " [FILE]..."
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100109//usage:#define ls_full_usage "\n\n"
110//usage: "List directory contents\n"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +0200111//usage: "\n -1 One column output"
112//usage: "\n -a Include entries which start with ."
113//usage: "\n -A Like -a, but exclude . and .."
Denys 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,
195
Denys Vlasenko2a816392011-05-13 17:28:09 +0200196/* Bits in G.all_fmt: */
Eric Andersen11c65522000-09-07 17:24:47 +0000197/* what files will be displayed */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100198DISP_DIRNAME = 1 << 9, /* 2 or more items? label directories */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000199};
Eric Andersencc8ed391999-10-05 16:24:54 +0000200
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100201/* -Cadi1l Std options, busybox always supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100202/* -gnsxA Std options, busybox always supports */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100203/* -Q GNU option, busybox always supports */
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100204/* -k Std option, busybox always supports (by ignoring) */
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100205/* It means "for -s, show sizes in kbytes" */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100206/* Seems to only affect "POSIXLY_CORRECT=1 ls -sk" */
207/* since otherwise -s shows kbytes anyway */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200208/* -LHRctur Std options, busybox optionally supports */
209/* -Fp Std options, busybox optionally supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100210/* -SXvhTw GNU options, busybox optionally supports */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200211/* -T WIDTH Ignored (we don't use tabs on output) */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100212/* -Z SELinux mandated option, busybox optionally supports */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000213static const char ls_options[] ALIGN1 =
Denys Vlasenko11540a82017-01-23 18:01:48 +0100214 "Cadi1lgnsxAk" /* 12 opts, total 12 */
215 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 14 */
216 IF_FEATURE_LS_RECURSIVE("R") /* 1, 15 */
217 IF_SELINUX("Z") /* 1, 16 */
218 "Q" /* 1, 17 */
219 IF_FEATURE_LS_TIMESTAMPS("ctu") /* 3, 20 */
220 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 24 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100221 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 26 */
222 IF_FEATURE_HUMAN_READABLE("h") /* 1, 27 */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100223 IF_FEATURE_LS_WIDTH("T:w:") /* 2, 29 */
224;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000225enum {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100226 OPT_C = (1 << 0),
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100227 OPT_a = (1 << 1),
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +0100228 OPT_d = (1 << 2),
Denys Vlasenkob60686c2017-01-23 18:34:11 +0100229 OPT_i = (1 << 3),
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100230 OPT_1 = (1 << 4),
Denys Vlasenkoe1f90d12017-01-22 22:02:19 +0100231 OPT_l = (1 << 5),
Denis Vlasenko248ce912009-03-03 14:09:04 +0000232 OPT_g = (1 << 6),
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100233 OPT_n = (1 << 7),
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100234 OPT_s = (1 << 8),
Denys Vlasenko92c68982017-01-23 20:21:14 +0100235 OPT_x = (1 << 9),
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100236 OPT_A = (1 << 10),
Denys Vlasenko11540a82017-01-23 18:01:48 +0100237 //OPT_k = (1 << 11),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100238
Denys Vlasenko11540a82017-01-23 18:01:48 +0100239 OPTBIT_F = 12,
240 OPTBIT_p, /* 13 */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100241 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100242 OPTBIT_Z = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
Denys Vlasenko11540a82017-01-23 18:01:48 +0100243 OPTBIT_Q = OPTBIT_Z + 1 * ENABLE_SELINUX,
244 OPTBIT_c, /* 17 */
245 OPTBIT_t, /* 18 */
246 OPTBIT_u, /* 19 */
247 OPTBIT_S = OPTBIT_c + 3 * ENABLE_FEATURE_LS_TIMESTAMPS,
248 OPTBIT_X, /* 21 */
249 OPTBIT_r, /* 22 */
250 OPTBIT_v, /* 23 */
251 OPTBIT_L = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100252 OPTBIT_H, /* 25 */
Denys Vlasenkocd387f22011-02-27 04:10:00 +0100253 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100254 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100255 OPTBIT_w, /* 28 */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100256 OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
257 OPTBIT_dirs_first,
258 OPTBIT_color, /* 31 */
259 /* with long opts, we use all 32 bits */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100260
Denys Vlasenko11540a82017-01-23 18:01:48 +0100261 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
262 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
263 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
264 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
265 OPT_Q = (1 << OPTBIT_Q),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100266 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100267 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
268 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
269 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
270 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
271 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
272 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100273 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
274 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
275 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100276 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_LS_WIDTH,
277 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_LS_WIDTH,
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100278 OPT_full_time = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100279 OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
280 OPT_color = (1 << OPTBIT_color ) * ENABLE_FEATURE_LS_COLOR,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000281};
282
Eric Andersen11c65522000-09-07 17:24:47 +0000283/*
Denys Vlasenko2a816392011-05-13 17:28:09 +0200284 * a directory entry and its stat info
Eric Andersen11c65522000-09-07 17:24:47 +0000285 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200286struct dnode {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200287 const char *name; /* usually basename, but think "ls -l dir/file" */
288 const char *fullname; /* full name (usable for stat etc) */
289 struct dnode *dn_next; /* for linked list */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000290 IF_SELINUX(security_context_t sid;)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200291 smallint fname_allocated;
292
293 /* Used to avoid re-doing [l]stat at printout stage
294 * if we already collected needed data in scan stage:
295 */
296 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
297 mode_t dn_mode_stat; /* obtained with stat, or 0 */
298
299// struct stat dstat;
300// struct stat is huge. We don't need it in full.
301// At least we don't need st_dev and st_blksize,
302// but there are invisible fields as well
303// (such as nanosecond-resolution timespamps)
304// and padding, which we also don't want to store.
305// We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
Denys Vlasenko4029e212011-05-13 17:28:46 +0200306// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
Denys Vlasenko2a816392011-05-13 17:28:09 +0200307//
308 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
309 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
310 off_t dn_size;
311#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100312 time_t dn_time;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200313#endif
314 ino_t dn_ino;
315 blkcnt_t dn_blocks;
316 nlink_t dn_nlink;
317 uid_t dn_uid;
318 gid_t dn_gid;
319 int dn_rdev_maj;
320 int dn_rdev_min;
321// dev_t dn_dev;
322// blksize_t dn_blksize;
Eric Andersen11c65522000-09-07 17:24:47 +0000323};
Eric Andersen11c65522000-09-07 17:24:47 +0000324
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000325struct globals {
326#if ENABLE_FEATURE_LS_COLOR
327 smallint show_color;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200328# define G_show_color (G.show_color)
329#else
330# define G_show_color 0
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000331#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000332 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000333 unsigned all_fmt;
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100334#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenko2a816392011-05-13 17:28:09 +0200335 unsigned terminal_width;
336# define G_terminal_width (G.terminal_width)
337#else
338# define G_terminal_width TERMINAL_WIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000339#endif
340#if ENABLE_FEATURE_LS_TIMESTAMPS
341 /* Do time() just once. Saves one syscall per file for "ls -l" */
342 time_t current_time_t;
343#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100344} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200345#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000346#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200347 setup_common_bufsiz(); \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200348 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000349 memset(&G, 0, sizeof(G)); \
Denys Vlasenkoed15dde2017-01-11 16:35:52 +0100350 IF_FEATURE_LS_WIDTH(G_terminal_width = TERMINAL_WIDTH;) \
Denys Vlasenko2a816392011-05-13 17:28:09 +0200351 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000352} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000353
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000354
Denys Vlasenko4029e212011-05-13 17:28:46 +0200355/*** Output code ***/
Denys Vlasenko2a816392011-05-13 17:28:09 +0200356
Eric Andersen79565b62000-08-11 18:10:21 +0000357
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000358/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
359 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
360 * 3/7:multiplexed char/block device)
361 * and we use 0 for unknown and 15 for executables (see below) */
362#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200363/* un fi chr - dir - blk - file - link - sock - - exe */
364#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000365/* 036 black foreground 050 black background
366 037 red foreground 051 red background
367 040 green foreground 052 green background
368 041 brown foreground 053 brown background
369 042 blue foreground 054 blue background
370 043 magenta (purple) foreground 055 magenta background
371 044 cyan (light blue) foreground 056 cyan background
372 045 gray foreground 057 white background
373*/
374#define COLOR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200375 /*un fi chr - dir - blk - file - link - sock - - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000376 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
377 [TYPEINDEX(mode)])
378/* Select normal (0) [actually "reset all"] or bold (1)
379 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
380 * let's use 7 for "impossible" types, just for fun)
381 * Note: coreutils 6.9 uses inverted red for setuid binaries.
382 */
383#define ATTR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200384 /*un fi chr - dir - blk - file- link- sock- - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000385 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
386 [TYPEINDEX(mode)])
387
Denis Vlasenko5c759602006-10-28 12:37:16 +0000388#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000389/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000390static char fgcolor(mode_t mode)
391{
Rob Landley9947a242006-06-15 22:11:10 +0000392 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000393 return COLOR(0xF000); /* File is executable ... */
394 return COLOR(mode);
395}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000396static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000397{
Rob Landley9947a242006-06-15 22:11:10 +0000398 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000399 return ATTR(0xF000); /* File is executable ... */
400 return ATTR(mode);
401}
402#endif
403
Denys Vlasenko2a816392011-05-13 17:28:09 +0200404#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000405static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000406{
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100407 if (!(option_mask32 & (OPT_F|OPT_p)))
Eric Andersen11c65522000-09-07 17:24:47 +0000408 return '\0';
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100409
Rob Landley9947a242006-06-15 22:11:10 +0000410 if (S_ISDIR(mode))
411 return '/';
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100412 if (!(option_mask32 & OPT_F))
Rob Landley9947a242006-06-15 22:11:10 +0000413 return '\0';
414 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000415 return '*';
416 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000417}
418#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000419
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100420static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000421{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100422 unsigned len;
423 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000424
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100425 // TODO: quote tab as \t, etc, if -Q
426 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000427
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100428 if (!(option_mask32 & OPT_Q)) {
429 return uni_stat.unicode_width;
430 }
431
432 len = 2 + uni_stat.unicode_width;
433 while (*name) {
434 if (*name == '"' || *name == '\\') {
435 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000436 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100437 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000438 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100439 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000440}
441
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100442/* Return the number of used columns.
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100443 * Note that only columnar output uses return value.
444 * -l and -1 modes don't care.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100445 * coreutils 7.2 also supports:
446 * ls -b (--escape) = octal escapes (although it doesn't look like working)
447 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200448 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100449static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200450{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100451 unsigned len;
452 uni_stat_t uni_stat;
453
454 // TODO: quote tab as \t, etc, if -Q
455 name = printable_string(&uni_stat, name);
456
457 if (!(option_mask32 & OPT_Q)) {
458 fputs(name, stdout);
459 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200460 }
461
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100462 len = 2 + uni_stat.unicode_width;
463 putchar('"');
464 while (*name) {
465 if (*name == '"' || *name == '\\') {
466 putchar('\\');
467 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000468 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700469 putchar(*name);
470 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000471 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100472 putchar('"');
473 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000474}
475
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100476/* Return the number of used columns.
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100477 * Note that only columnar output uses return value,
478 * -l and -1 modes don't care.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100479 */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200480static NOINLINE unsigned display_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000481{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200482 unsigned column = 0;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200483 char *lpath;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100484 int opt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000485#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200486 struct stat statbuf;
Eric Andersen11c65522000-09-07 17:24:47 +0000487 char append;
488#endif
489
Denis Vlasenko5c759602006-10-28 12:37:16 +0000490#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200491 append = append_char(dn->dn_mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000492#endif
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100493 opt = option_mask32;
Eric Andersen11c65522000-09-07 17:24:47 +0000494
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000495 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100496 * does not appear *inside* the "ls -l" line */
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200497 lpath = NULL;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100498 if (opt & OPT_l)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200499 if (S_ISLNK(dn->dn_mode))
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000500 lpath = xmalloc_readlink_or_warn(dn->fullname);
501
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100502 if (opt & OPT_i) /* show inode# */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200503 column += printf("%7llu ", (long long) dn->dn_ino);
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100504//TODO: -h should affect -s too:
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100505 if (opt & OPT_s) /* show allocated blocks */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200506 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100507 if (opt & OPT_l) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100508 /* long listing: show mode */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200509 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100510 /* long listing: show number of links */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200511 column += printf("%4lu ", (long) dn->dn_nlink);
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100512 /* long listing: show user/group */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100513 if (opt & OPT_n) {
514 if (opt & OPT_g)
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100515 column += printf("%-8u ", (int) dn->dn_gid);
516 else
517 column += printf("%-8u %-8u ",
518 (int) dn->dn_uid,
519 (int) dn->dn_gid);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000520 }
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100521#if ENABLE_FEATURE_LS_USERNAME
522 else {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100523 if (opt & OPT_g) {
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100524 column += printf("%-8.8s ",
525 get_cached_groupname(dn->dn_gid));
526 } else {
527 column += printf("%-8.8s %-8.8s ",
528 get_cached_username(dn->dn_uid),
529 get_cached_groupname(dn->dn_gid));
530 }
531 }
Eric Andersen11c65522000-09-07 17:24:47 +0000532#endif
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100533#if ENABLE_SELINUX
Denys Vlasenko51b01fd2017-01-23 19:42:12 +0100534 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100535 if (opt & OPT_Z) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100536 column += printf("%-32s ", dn->sid ? dn->sid : "?");
537 freecon(dn->sid);
538 }
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100539 if (opt & OPT_l) {
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100540#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100541 /* long listing: show size */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200542 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000543 column += printf("%4u, %3u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200544 dn->dn_rdev_maj,
545 dn->dn_rdev_min);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000546 } else {
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100547 if (opt & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100548 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200549 /* print size, show one fractional, use suffixes */
550 make_human_readable_str(dn->dn_size, 1, 0)
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200551 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000552 } else {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200553 column += printf("%9"OFF_FMT"u ", dn->dn_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000554 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000555 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000556#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100557 /* long listing: show {m,c,a}time */
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100558 if (opt & OPT_full_time) { /* --full-time */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100559 /* coreutils 8.4 ls --full-time prints:
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100560 * 2009-07-13 17:49:27.000000000 +0200
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100561 * we don't show fractional seconds.
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100562 */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100563 char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")];
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100564 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z",
565 localtime(&dn->dn_time));
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100566 column += printf("%s ", buf);
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100567 } else { /* ordinary time format */
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100568 /* G.current_time_t is ~== time(NULL) */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100569 char *filetime = ctime(&dn->dn_time);
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100570 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100571 time_t age = G.current_time_t - dn->dn_time;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000572 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
Denys Vlasenko8e92df12015-02-16 15:36:25 +0100573 /* less than 6 months old */
574 /* "mmm dd hh:mm " */
575 printf("%.12s ", filetime + 4);
576 } else {
577 /* "mmm dd yyyy " */
578 /* "mmm dd yyyyy " after year 9999 :) */
579 strchr(filetime + 20, '\n')[0] = ' ';
580 printf("%.7s%6s", filetime + 4, filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000581 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000582 column += 13;
583 }
Eric Andersen11c65522000-09-07 17:24:47 +0000584#endif
Denys Vlasenko2c3131d2017-01-23 19:05:11 +0100585 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100586
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000587#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200588 if (G_show_color) {
589 mode_t mode = dn->dn_mode_lstat;
590 if (!mode)
591 if (lstat(dn->fullname, &statbuf) == 0)
592 mode = statbuf.st_mode;
593 printf("\033[%u;%um", bold(mode), fgcolor(mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000594 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100595#endif
596 column += print_name(dn->name);
Denys Vlasenko2a816392011-05-13 17:28:09 +0200597 if (G_show_color) {
James Youngmana4bc10c2010-12-20 01:36:16 +0100598 printf("\033[0m");
599 }
600
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200601 if (lpath) {
602 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000603#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100604 if ((opt & (OPT_F|OPT_p))
Denys Vlasenko96d9c5b2017-01-23 19:56:13 +0100605 || G_show_color
606 ) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200607 mode_t mode = dn->dn_mode_stat;
608 if (!mode)
609 if (stat(dn->fullname, &statbuf) == 0)
610 mode = statbuf.st_mode;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200611# if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200612 append = append_char(mode);
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200613# endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200614# if ENABLE_FEATURE_LS_COLOR
615 if (G_show_color) {
616 printf("\033[%u;%um", bold(mode), fgcolor(mode));
617 }
618# endif
Eric Andersen11c65522000-09-07 17:24:47 +0000619 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200620#endif
621 column += print_name(lpath) + 4;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200622 free(lpath);
623 if (G_show_color) {
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200624 printf("\033[0m");
625 }
Eric Andersen11c65522000-09-07 17:24:47 +0000626 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000627#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100628 if (opt & (OPT_F|OPT_p)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000629 if (append) {
630 putchar(append);
631 column++;
632 }
633 }
634#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000635
Glenn L McGrath4d001292003-01-06 01:11:50 +0000636 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000637}
638
Denys Vlasenko4029e212011-05-13 17:28:46 +0200639static void display_files(struct dnode **dn, unsigned nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100640{
641 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100642 unsigned column;
643 unsigned nexttab;
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100644 unsigned column_width = 0; /* used only by coulmnal output */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100645
Denys Vlasenkoccc16992017-01-23 20:43:06 +0100646 if (option_mask32 & (OPT_l|OPT_1)) {
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100647 ncols = 1;
648 } else {
649 /* find the longest file name, use that as the column width */
650 for (i = 0; dn[i]; i++) {
651 int len = calc_name_len(dn[i]->name);
652 if (column_width < len)
653 column_width = len;
654 }
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100655 column_width += 2
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100656 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +0100657 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
658 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
659 ;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200660 ncols = (unsigned)G_terminal_width / column_width;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100661 }
662
663 if (ncols > 1) {
664 nrows = nfiles / ncols;
665 if (nrows * ncols < nfiles)
666 nrows++; /* round up fractionals */
667 } else {
668 nrows = nfiles;
669 ncols = 1;
670 }
671
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100672 column = 0;
673 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100674 for (row = 0; row < nrows; row++) {
675 for (nc = 0; nc < ncols; nc++) {
676 /* reach into the array based on the column and row */
Denys Vlasenko92c68982017-01-23 20:21:14 +0100677 if (option_mask32 & OPT_x)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100678 i = (row * ncols) + nc; /* display across row */
679 else
680 i = (nc * nrows) + row; /* display by column */
681 if (i < nfiles) {
682 if (column > 0) {
683 nexttab -= column;
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100684 printf("%*s", nexttab, "");
685 column += nexttab;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100686 }
687 nexttab = column + column_width;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200688 column += display_single(dn[i]);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100689 }
690 }
691 putchar('\n');
692 column = 0;
693 }
694}
695
696
Denys Vlasenko4029e212011-05-13 17:28:46 +0200697/*** Dir scanning code ***/
698
699static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100700{
Denys Vlasenko4029e212011-05-13 17:28:46 +0200701 struct stat statbuf;
702 struct dnode *cur;
703
704 cur = xzalloc(sizeof(*cur));
705 cur->fullname = fullname;
706 cur->name = name;
707
708 if ((option_mask32 & OPT_L) || force_follow) {
709#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100710 if (option_mask32 & OPT_Z) {
Denys Vlasenko69675782013-01-14 01:34:48 +0100711 getfilecon(fullname, &cur->sid);
Denys Vlasenko4029e212011-05-13 17:28:46 +0200712 }
713#endif
714 if (stat(fullname, &statbuf)) {
715 bb_simple_perror_msg(fullname);
716 G.exit_code = EXIT_FAILURE;
717 free(cur);
718 return NULL;
719 }
720 cur->dn_mode_stat = statbuf.st_mode;
721 } else {
722#if ENABLE_SELINUX
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +0100723 if (option_mask32 & OPT_Z) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200724 lgetfilecon(fullname, &cur->sid);
725 }
726#endif
727 if (lstat(fullname, &statbuf)) {
728 bb_simple_perror_msg(fullname);
729 G.exit_code = EXIT_FAILURE;
730 free(cur);
731 return NULL;
732 }
733 cur->dn_mode_lstat = statbuf.st_mode;
734 }
735
736 /* cur->dstat = statbuf: */
737 cur->dn_mode = statbuf.st_mode ;
738 cur->dn_size = statbuf.st_size ;
739#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100740 cur->dn_time = statbuf.st_mtime ;
741 if (option_mask32 & OPT_u)
742 cur->dn_time = statbuf.st_atime;
743 if (option_mask32 & OPT_c)
744 cur->dn_time = statbuf.st_ctime;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200745#endif
746 cur->dn_ino = statbuf.st_ino ;
747 cur->dn_blocks = statbuf.st_blocks;
748 cur->dn_nlink = statbuf.st_nlink ;
749 cur->dn_uid = statbuf.st_uid ;
750 cur->dn_gid = statbuf.st_gid ;
751 cur->dn_rdev_maj = major(statbuf.st_rdev);
752 cur->dn_rdev_min = minor(statbuf.st_rdev);
753
754 return cur;
755}
756
757static unsigned count_dirs(struct dnode **dn, int which)
758{
759 unsigned dirs, all;
760
761 if (!dn)
762 return 0;
763
764 dirs = all = 0;
765 for (; *dn; dn++) {
766 const char *name;
767
768 all++;
769 if (!S_ISDIR((*dn)->dn_mode))
770 continue;
771
772 name = (*dn)->name;
773 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
774 /* or if it's not . or .. */
775 || name[0] != '.'
776 || (name[1] && (name[1] != '.' || name[2]))
777 ) {
778 dirs++;
779 }
780 }
781 return which != SPLIT_FILE ? dirs : all - dirs;
782}
783
784/* get memory to hold an array of pointers */
785static struct dnode **dnalloc(unsigned num)
786{
787 if (num < 1)
788 return NULL;
789
790 num++; /* so that we have terminating NULL */
791 return xzalloc(num * sizeof(struct dnode *));
792}
793
794#if ENABLE_FEATURE_LS_RECURSIVE
795static void dfree(struct dnode **dnp)
796{
797 unsigned i;
798
799 if (dnp == NULL)
800 return;
801
802 for (i = 0; dnp[i]; i++) {
803 struct dnode *cur = dnp[i];
804 if (cur->fname_allocated)
805 free((char*)cur->fullname);
806 free(cur);
807 }
808 free(dnp);
809}
810#else
811#define dfree(...) ((void)0)
812#endif
813
814/* Returns NULL-terminated malloced vector of pointers (or NULL) */
815static struct dnode **splitdnarray(struct dnode **dn, int which)
816{
817 unsigned dncnt, d;
818 struct dnode **dnp;
819
820 if (dn == NULL)
821 return NULL;
822
823 /* count how many dirs or files there are */
824 dncnt = count_dirs(dn, which);
825
826 /* allocate a file array and a dir array */
827 dnp = dnalloc(dncnt);
828
829 /* copy the entrys into the file or dir array */
830 for (d = 0; *dn; dn++) {
831 if (S_ISDIR((*dn)->dn_mode)) {
832 const char *name;
833
834 if (which == SPLIT_FILE)
835 continue;
836
837 name = (*dn)->name;
838 if ((which & SPLIT_DIR) /* any dir... */
839 /* ... or not . or .. */
840 || name[0] != '.'
841 || (name[1] && (name[1] != '.' || name[2]))
842 ) {
843 dnp[d++] = *dn;
844 }
845 } else
846 if (which == SPLIT_FILE) {
847 dnp[d++] = *dn;
848 }
849 }
850 return dnp;
851}
852
853#if ENABLE_FEATURE_LS_SORTFILES
854static int sortcmp(const void *a, const void *b)
855{
856 struct dnode *d1 = *(struct dnode **)a;
857 struct dnode *d2 = *(struct dnode **)b;
Denys Vlasenko11540a82017-01-23 18:01:48 +0100858 unsigned opt = option_mask32;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200859 off_t dif;
860
Denys Vlasenko11540a82017-01-23 18:01:48 +0100861 dif = 0; /* assume sort by name */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200862 // TODO: use pre-initialized function pointer
863 // instead of branch forest
Denys Vlasenko11540a82017-01-23 18:01:48 +0100864 if (opt & OPT_dirs_first) {
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100865 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
866 if (dif != 0)
867 goto maybe_invert_and_ret;
868 }
869
Denys Vlasenko11540a82017-01-23 18:01:48 +0100870 if (opt & OPT_S) { /* sort by size */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200871 dif = (d2->dn_size - d1->dn_size);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200872 } else
Denys Vlasenko11540a82017-01-23 18:01:48 +0100873 if (opt & OPT_t) { /* sort by time */
Denys Vlasenkof580baf2017-01-22 19:02:57 +0100874 dif = (d2->dn_time - d1->dn_time);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200875 } else
Denys Vlasenko1e18a012011-06-21 17:12:52 +0200876#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
Denys Vlasenko11540a82017-01-23 18:01:48 +0100877 if (opt & OPT_v) { /* sort by version */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200878 dif = strverscmp(d1->name, d2->name);
879 } else
Denys Vlasenko561f9c82011-06-21 16:38:29 +0200880#endif
Denys Vlasenko11540a82017-01-23 18:01:48 +0100881 if (opt & OPT_X) { /* sort by extension */
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200882 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
Denys Vlasenko4029e212011-05-13 17:28:46 +0200883 }
884 if (dif == 0) {
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200885 /* sort by name, use as tie breaker for other sorts */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200886 if (ENABLE_LOCALE_SUPPORT)
887 dif = strcoll(d1->name, d2->name);
888 else
889 dif = strcmp(d1->name, d2->name);
Denys Vlasenko11540a82017-01-23 18:01:48 +0100890 } else {
891 /* Make dif fit into an int */
892 if (sizeof(dif) > sizeof(int)) {
893 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
894 /* shift leaving only "int" worth of bits */
895 /* (this requires dif != 0, and here it is nonzero) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200896 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100897 }
898 }
Denys Vlasenko194b2eb2017-01-22 17:32:20 +0100899 maybe_invert_and_ret:
Denys Vlasenko11540a82017-01-23 18:01:48 +0100900 return (opt & OPT_r) ? -(int)dif : (int)dif;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100901}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200902
903static void dnsort(struct dnode **dn, int size)
904{
905 qsort(dn, size, sizeof(*dn), sortcmp);
906}
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200907
908static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
909{
910 dnsort(dn, nfiles);
911 display_files(dn, nfiles);
912}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200913#else
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200914# define dnsort(dn, size) ((void)0)
915# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100916#endif
917
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100918/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200919static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100920{
921 struct dnode *dn, *cur, **dnp;
922 struct dirent *entry;
923 DIR *dir;
924 unsigned i, nfiles;
925
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100926 *nfiles_p = 0;
927 dir = warn_opendir(path);
928 if (dir == NULL) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200929 G.exit_code = EXIT_FAILURE;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100930 return NULL; /* could not open the dir */
931 }
932 dn = NULL;
933 nfiles = 0;
934 while ((entry = readdir(dir)) != NULL) {
935 char *fullname;
936
937 /* are we going to list the file- it may be . or .. or a hidden file */
938 if (entry->d_name[0] == '.') {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100939 if (!(option_mask32 & (OPT_a|OPT_A)))
940 continue; /* skip all dotfiles if no -a/-A */
941 if (!(option_mask32 & OPT_a)
942 && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100943 ) {
Denys Vlasenkof5bd6f62017-01-23 18:23:19 +0100944 continue; /* if only -A, skip . and .. but show other dotfiles */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100945 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100946 }
947 fullname = concat_path_file(path, entry->d_name);
948 cur = my_stat(fullname, bb_basename(fullname), 0);
949 if (!cur) {
950 free(fullname);
951 continue;
952 }
953 cur->fname_allocated = 1;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200954 cur->dn_next = dn;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100955 dn = cur;
956 nfiles++;
957 }
958 closedir(dir);
959
960 if (dn == NULL)
961 return NULL;
962
963 /* now that we know how many files there are
964 * allocate memory for an array to hold dnode pointers
965 */
966 *nfiles_p = nfiles;
967 dnp = dnalloc(nfiles);
968 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
969 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200970 dn = dn->dn_next;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100971 if (!dn)
972 break;
973 }
974
975 return dnp;
976}
977
Denys Vlasenko4029e212011-05-13 17:28:46 +0200978#if ENABLE_DESKTOP
979/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
980 * If any of the -l, -n, -s options is specified, each list
981 * of files within the directory shall be preceded by a
982 * status line indicating the number of file system blocks
983 * occupied by files in the directory in 512-byte units if
984 * the -k option is not specified, or 1024-byte units if the
985 * -k option is specified, rounded up to the next integral
986 * number of units.
987 */
988/* by Jorgen Overgaard (jorgen AT antistaten.se) */
989static off_t calculate_blocks(struct dnode **dn)
990{
991 uoff_t blocks = 1;
992 if (dn) {
993 while (*dn) {
994 /* st_blocks is in 512 byte blocks */
995 blocks += (*dn)->dn_blocks;
996 dn++;
997 }
998 }
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000999
Denys Vlasenko4029e212011-05-13 17:28:46 +02001000 /* Even though standard says use 512 byte blocks, coreutils use 1k */
1001 /* Actually, we round up by calculating (blocks + 1) / 2,
1002 * "+ 1" was done when we initialized blocks to 1 */
1003 return blocks >> 1;
1004}
1005#endif
1006
1007static void scan_and_display_dirs_recur(struct dnode **dn, int first)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001008{
1009 unsigned nfiles;
1010 struct dnode **subdnp;
1011
1012 for (; *dn; dn++) {
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001013 if ((G.all_fmt & DISP_DIRNAME)
1014 || (option_mask32 & OPT_R)
1015 ) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001016 if (!first)
1017 bb_putchar('\n');
1018 first = 0;
1019 printf("%s:\n", (*dn)->fullname);
1020 }
Denys Vlasenko4029e212011-05-13 17:28:46 +02001021 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001022#if ENABLE_DESKTOP
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001023 if (option_mask32 & (OPT_s|OPT_l)) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001024 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
Denys Vlasenko5d43ddc2017-01-23 18:43:43 +01001025 }
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001026#endif
1027 if (nfiles > 0) {
1028 /* list all files at this level */
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001029 sort_and_display_files(subdnp, nfiles);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001030
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001031 if (ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001032 && (option_mask32 & OPT_R)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001033 ) {
1034 struct dnode **dnd;
1035 unsigned dndirs;
1036 /* recursive - list the sub-dirs */
1037 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1038 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1039 if (dndirs > 0) {
1040 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001041 scan_and_display_dirs_recur(dnd, 0);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001042 /* free the array of dnode pointers to the dirs */
1043 free(dnd);
1044 }
1045 }
1046 /* free the dnodes and the fullname mem */
1047 dfree(subdnp);
1048 }
1049 }
1050}
1051
1052
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001053int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +00001054{
Glenn L McGrath792cae52004-01-18 05:15:16 +00001055 struct dnode **dnd;
1056 struct dnode **dnf;
1057 struct dnode **dnp;
1058 struct dnode *dn;
1059 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001060 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001061 unsigned nfiles;
1062 unsigned dnfiles;
1063 unsigned dndirs;
1064 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001065#if ENABLE_FEATURE_LS_COLOR
1066 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1067 /* coreutils 6.10:
1068 * # ls --color=BOGUS
1069 * ls: invalid argument 'BOGUS' for '--color'
1070 * Valid arguments are:
1071 * 'always', 'yes', 'force'
1072 * 'never', 'no', 'none'
1073 * 'auto', 'tty', 'if-tty'
1074 * (and substrings: "--color=alwa" work too)
1075 */
1076 static const char ls_longopts[] ALIGN1 =
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001077 "full-time\0" No_argument "\xff"
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001078 "group-directories-first\0" No_argument "\xfe"
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001079 "color\0" Optional_argument "\xfd"
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001080 ;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001081 static const char color_str[] ALIGN1 =
1082 "always\0""yes\0""force\0"
1083 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001084 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001085 const char *color_opt = color_str; /* "always" */
1086#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001087
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001088 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001089
Denys Vlasenko28055022010-01-04 20:49:58 +01001090 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001091
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001092#if ENABLE_FEATURE_LS_WIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001093 /* obtain the terminal width */
Denys Vlasenko641caae2015-10-23 01:44:22 +02001094 G_terminal_width = get_terminal_width(STDIN_FILENO);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001095 /* go one less... */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001096 G_terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001097#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001098
Eric Andersen11c65522000-09-07 17:24:47 +00001099 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001100 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001101 opt_complementary =
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001102 /* -n and -g imply -l */
1103 "nl:gl"
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001104 /* --full-time implies -l */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001105 IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(":\xff""l"))
Denys Vlasenkof3137462010-12-19 05:05:34 +01001106 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1107 * in some pairs of opts, only last one takes effect:
1108 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001109 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001110 // ":m-l:l-m" - we don't have -m
1111 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001112 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1113 ":C-1:1-C" /* bycols/oneline */
1114 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
Denys Vlasenkob47b3ce2011-08-10 00:51:29 +02001115 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001116 /* -w NUM: */
Denys Vlasenkoed15dde2017-01-11 16:35:52 +01001117 IF_FEATURE_LS_WIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001118 opt = getopt32(argv, ls_options
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001119 IF_FEATURE_LS_WIDTH(, /*-T*/ NULL, /*-w*/ &G_terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001120 IF_FEATURE_LS_COLOR(, &color_opt)
1121 );
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001122#if 0 /* option bits debug */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001123 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 +01001124 if (opt & OPT_c ) bb_error_msg("-c");
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001125 if (opt & OPT_l ) bb_error_msg("-l");
Denys Vlasenko194b2eb2017-01-22 17:32:20 +01001126 if (opt & OPT_H ) bb_error_msg("-H");
1127 if (opt & OPT_color ) bb_error_msg("--color");
1128 if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
1129 if (opt & OPT_full_time ) bb_error_msg("--full-time");
1130 exit(0);
1131#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001132
Denys Vlasenkoa1cbaca2017-01-23 19:30:14 +01001133#if ENABLE_SELINUX
1134 if (opt & OPT_Z) {
1135 if (!is_selinux_enabled())
1136 option_mask32 &= ~OPT_Z;
1137 }
1138#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001139
Denis Vlasenko5c759602006-10-28 12:37:16 +00001140#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +02001141 /* set G_show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001142 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1143 char *p = getenv("LS_COLORS");
1144 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001145 if (!p || (p[0] && strcmp(p, "none") != 0))
Denys Vlasenko2a816392011-05-13 17:28:09 +02001146 G_show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001147 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001148 if (opt & OPT_color) {
1149 if (color_opt[0] == 'n')
Denys Vlasenko2a816392011-05-13 17:28:09 +02001150 G_show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001151 else switch (index_in_substrings(color_str, color_opt)) {
1152 case 3:
1153 case 4:
1154 case 5:
1155 if (isatty(STDOUT_FILENO)) {
1156 case 0:
1157 case 1:
1158 case 2:
Denys Vlasenko2a816392011-05-13 17:28:09 +02001159 G_show_color = 1;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001160 }
1161 }
Paul Fox156dc412005-08-01 19:33:30 +00001162 }
1163#endif
1164
Eric Andersen11c65522000-09-07 17:24:47 +00001165 /* sort out which command line options take precedence */
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001166 if (ENABLE_FEATURE_LS_RECURSIVE && (opt & OPT_d))
1167 option_mask32 &= ~OPT_R; /* no recurse if listing only dir */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001168 if (!(opt & OPT_l)) { /* not -l? */
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001169 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1170 /* when to sort by time? -t[cu] sorts by time even with -l */
1171 /* (this is achieved by opt_flags[] element for -t) */
1172 /* without -l, bare -c or -u enable sort too */
1173 /* (with -l, bare -c or -u just select which time to show) */
1174 if (opt & (OPT_c|OPT_u)) {
Denys Vlasenko11540a82017-01-23 18:01:48 +01001175 option_mask32 |= OPT_t;
Denys Vlasenkof580baf2017-01-22 19:02:57 +01001176 }
1177 }
1178 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001179
Denys Vlasenkof3137462010-12-19 05:05:34 +01001180 /* choose a display format if one was not already specified by an option */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001181 if (!(option_mask32 & (OPT_l|OPT_1|OPT_x|OPT_C)))
1182 option_mask32 |= (isatty(STDOUT_FILENO) ? OPT_C : OPT_1);
Eric Andersen11c65522000-09-07 17:24:47 +00001183
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001184 argv += optind;
1185 if (!argv[0])
1186 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001187
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001188 if (argv[1])
Denys Vlasenko2a816392011-05-13 17:28:09 +02001189 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001190
Denis Vlasenko5c759602006-10-28 12:37:16 +00001191 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001192 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001193 nfiles = 0;
1194 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001195 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001196 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenkoccc16992017-01-23 20:43:06 +01001197 !(option_mask32 & (OPT_l|OPT_s|OPT_F))
Denys Vlasenko982aa262010-12-19 21:54:39 +01001198 /* ... or if -H: */
1199 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001200 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001201 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001202 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001203 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001204 continue;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001205 /*cur->fname_allocated = 0; - already is */
1206 cur->dn_next = dn;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001207 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001208 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001209 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001210
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001211 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1212 if (nfiles == 0)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001213 return G.exit_code;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001214
Eric Andersen11c65522000-09-07 17:24:47 +00001215 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001216 * allocate memory for an array to hold dnode pointers
1217 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001218 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001219 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1220 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001221 dn = dn->dn_next;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001222 if (!dn)
1223 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001224 }
1225
Denys Vlasenko4cde4cc2017-01-23 20:08:22 +01001226 if (option_mask32 & OPT_d) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001227 sort_and_display_files(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001228 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001229 dnd = splitdnarray(dnp, SPLIT_DIR);
1230 dnf = splitdnarray(dnp, SPLIT_FILE);
1231 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001232 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001233 if (dnfiles > 0) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001234 sort_and_display_files(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001235 if (ENABLE_FEATURE_CLEAN_UP)
1236 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001237 }
1238 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001239 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001240 scan_and_display_dirs_recur(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001241 if (ENABLE_FEATURE_CLEAN_UP)
1242 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001243 }
1244 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001245
Rob Landley26314862006-05-02 19:46:52 +00001246 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001247 dfree(dnp);
Denys Vlasenko2a816392011-05-13 17:28:09 +02001248 return G.exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001249}