blob: 344b4e61e5953fa1ff4b8ee1a7dbc9d3ce9304c3 [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 */
7
Denis Vlasenko5e4fda02009-03-08 23:46:48 +00008/* [date unknown. Perhaps before year 2000]
Eric Andersencc8ed391999-10-05 16:24:54 +00009 * To achieve a small memory footprint, this version of 'ls' doesn't do any
10 * file sorting, and only has the most essential command line switches
Eric Andersen77d92682001-05-23 20:32:09 +000011 * (i.e., the ones I couldn't live without :-) All features which involve
Eric Andersencc8ed391999-10-05 16:24:54 +000012 * linking in substantial chunks of libc can be disabled.
13 *
14 * Although I don't really want to add new features to this program to
15 * keep it small, I *am* interested to receive bug fixes and ways to make
16 * it more portable.
17 *
18 * KNOWN BUGS:
Denys Vlasenko87c150c2009-10-03 01:14:15 +020019 * 1. hidden files can make column width too large
Erik Andersen9ffdaa62000-02-11 21:55:04 +000020 *
Eric Andersencc8ed391999-10-05 16:24:54 +000021 * NON-OPTIMAL BEHAVIOUR:
22 * 1. autowidth reads directories twice
23 * 2. if you do a short directory listing without filetype characters
24 * appended, there's no need to stat each one
25 * PORTABILITY:
26 * 1. requires lstat (BSD) - how do you do it without?
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000027 *
28 * [2009-03]
29 * ls sorts listing now, and supports almost all options.
Eric Andersencc8ed391999-10-05 16:24:54 +000030 */
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010031
32//usage:#define ls_trivial_usage
33//usage: "[-1AaCxd"
Denys Vlasenko982aa262010-12-19 21:54:39 +010034//usage: IF_FEATURE_LS_FOLLOWLINKS("LH")
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010035//usage: IF_FEATURE_LS_RECURSIVE("R")
36//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins"
37//usage: IF_FEATURE_LS_TIMESTAMPS("e")
38//usage: IF_FEATURE_HUMAN_READABLE("h")
39//usage: IF_FEATURE_LS_SORTFILES("rSXv")
40//usage: IF_FEATURE_LS_TIMESTAMPS("ctu")
41//usage: IF_SELINUX("kKZ") "]"
Denys Vlasenko279a7ac2011-05-12 18:44:51 +020042//usage: IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..."
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010043//usage:#define ls_full_usage "\n\n"
44//usage: "List directory contents\n"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020045//usage: "\n -1 One column output"
46//usage: "\n -a Include entries which start with ."
47//usage: "\n -A Like -a, but exclude . and .."
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010048//usage: "\n -C List by columns"
49//usage: "\n -x List by lines"
50//usage: "\n -d List directory entries instead of contents"
51//usage: IF_FEATURE_LS_FOLLOWLINKS(
Denys Vlasenko982aa262010-12-19 21:54:39 +010052//usage: "\n -L Follow symlinks"
Denys Vlasenko4ad95e62011-05-12 18:40:59 +020053//usage: "\n -H Follow symlinks on command line"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010054//usage: )
55//usage: IF_FEATURE_LS_RECURSIVE(
56//usage: "\n -R Recurse"
57//usage: )
58//usage: IF_FEATURE_LS_FILETYPES(
Denys Vlasenko4ad95e62011-05-12 18:40:59 +020059//usage: "\n -p Append / to dir entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010060//usage: "\n -F Append indicator (one of */=@|) to entries"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010061//usage: )
62//usage: "\n -l Long listing format"
63//usage: "\n -i List inode numbers"
64//usage: "\n -n List numeric UIDs and GIDs instead of names"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020065//usage: "\n -s List allocated blocks"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010066//usage: IF_FEATURE_LS_TIMESTAMPS(
67//usage: "\n -e List full date and time"
68//usage: )
69//usage: IF_FEATURE_HUMAN_READABLE(
70//usage: "\n -h List sizes in human readable format (1K 243M 2G)"
71//usage: )
72//usage: IF_FEATURE_LS_SORTFILES(
73//usage: "\n -r Sort in reverse order"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020074//usage: "\n -S Sort by size"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010075//usage: "\n -X Sort by extension"
76//usage: "\n -v Sort by version"
77//usage: )
78//usage: IF_FEATURE_LS_TIMESTAMPS(
79//usage: "\n -c With -l: sort by ctime"
Denys Vlasenkofa9126e2011-04-03 01:27:49 +020080//usage: "\n -t With -l: sort by mtime"
81//usage: "\n -u With -l: sort by atime"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010082//usage: )
83//usage: IF_SELINUX(
84//usage: "\n -k List security context"
85//usage: "\n -K List security context in long format"
86//usage: "\n -Z List security context and permission"
87//usage: )
88//usage: IF_FEATURE_AUTOWIDTH(
89//usage: "\n -w N Assume the terminal is N columns wide"
90//usage: )
91//usage: IF_FEATURE_LS_COLOR(
92//usage: "\n --color[={always,never,auto}] Control coloring"
93//usage: )
94
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000095#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +020096#include "common_bufsiz.h"
Denys Vlasenko42a8fd02009-07-11 21:36:13 +020097#include "unicode.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +000098
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +000099
Denis Vlasenko99912ca2007-04-10 15:43:37 +0000100/* This is a NOEXEC applet. Be very careful! */
101
Eric Andersenf1142c52001-02-20 06:16:29 +0000102
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000103#if ENABLE_FTPD
104/* ftpd uses ls, and without timestamps Mozilla won't understand
105 * ftpd's LIST output.
106 */
107# undef CONFIG_FEATURE_LS_TIMESTAMPS
108# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000109# undef IF_FEATURE_LS_TIMESTAMPS
110# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000111# define CONFIG_FEATURE_LS_TIMESTAMPS 1
112# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000113# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
114# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000115#endif
116
117
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000118enum {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000119TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000120
Denys Vlasenko982aa262010-12-19 21:54:39 +0100121SPLIT_FILE = 0,
Denys Vlasenko2a816392011-05-13 17:28:09 +0200122SPLIT_DIR = 1,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100123SPLIT_SUBDIR = 2,
124
Denys Vlasenko2a816392011-05-13 17:28:09 +0200125/* Bits in G.all_fmt: */
Eric Andersencc8ed391999-10-05 16:24:54 +0000126
Eric Andersen11c65522000-09-07 17:24:47 +0000127/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
128/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000129LIST_INO = 1 << 0,
130LIST_BLOCKS = 1 << 1,
131LIST_MODEBITS = 1 << 2,
132LIST_NLINKS = 1 << 3,
133LIST_ID_NAME = 1 << 4,
134LIST_ID_NUMERIC = 1 << 5,
135LIST_CONTEXT = 1 << 6,
136LIST_SIZE = 1 << 7,
James Youngmana4bc10c2010-12-20 01:36:16 +0100137LIST_DATE_TIME = 1 << 8,
138LIST_FULLTIME = 1 << 9,
139LIST_SYMLINK = 1 << 10,
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200140LIST_FILETYPE = 1 << 11, /* show / suffix for dirs */
141LIST_CLASSIFY = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */
142LIST_MASK = (LIST_CLASSIFY << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +0000143
144/* what files will be displayed */
James Youngmana4bc10c2010-12-20 01:36:16 +0100145DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */
146DISP_HIDDEN = 1 << 14, /* show filenames starting with . */
147DISP_DOT = 1 << 15, /* show . and .. */
148DISP_NOLIST = 1 << 16, /* show directory as itself, not contents */
149DISP_RECURSIVE = 1 << 17, /* show directory and everything below it */
150DISP_ROWS = 1 << 18, /* print across rows */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000151DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +0000152
Denys Vlasenko982aa262010-12-19 21:54:39 +0100153/* what is the overall style of the listing */
James Youngmana4bc10c2010-12-20 01:36:16 +0100154STYLE_COLUMNAR = 1 << 19, /* many records per line */
155STYLE_LONG = 2 << 19, /* one record per line, extended info */
156STYLE_SINGLE = 3 << 19, /* one record per line */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100157STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000158
Eric Andersen11c65522000-09-07 17:24:47 +0000159/* which of the three times will be used */
James Youngmana4bc10c2010-12-20 01:36:16 +0100160TIME_CHANGE = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100161TIME_ACCESS = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
James Youngmana4bc10c2010-12-20 01:36:16 +0100162TIME_MASK = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000163
Denys Vlasenko982aa262010-12-19 21:54:39 +0100164/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
James Youngmana4bc10c2010-12-20 01:36:16 +0100165SORT_REVERSE = 1 << 23,
Eric Andersencc8ed391999-10-05 16:24:54 +0000166
Denys Vlasenko982aa262010-12-19 21:54:39 +0100167SORT_NAME = 0, /* sort by file name */
James Youngmana4bc10c2010-12-20 01:36:16 +0100168SORT_SIZE = 1 << 24, /* sort by file size */
169SORT_ATIME = 2 << 24, /* sort by last access time */
170SORT_CTIME = 3 << 24, /* sort by last change time */
171SORT_MTIME = 4 << 24, /* sort by last modification time */
172SORT_VERSION = 5 << 24, /* sort by version */
173SORT_EXT = 6 << 24, /* sort by file name extension */
174SORT_DIR = 7 << 24, /* sort by file or directory */
175SORT_MASK = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000176
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000177LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
James Youngmana4bc10c2010-12-20 01:36:16 +0100178 LIST_DATE_TIME | LIST_SYMLINK,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000179};
Eric Andersencc8ed391999-10-05 16:24:54 +0000180
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100181/* -Cadil1 Std options, busybox always supports */
182/* -gnsxA Std options, busybox always supports */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100183/* -Q GNU option, busybox always supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100184/* -k SELinux option, busybox always supports (ignores if !SELinux) */
185/* Std has -k which means "show sizes in kbytes" */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200186/* -LHRctur Std options, busybox optionally supports */
187/* -Fp Std options, busybox optionally supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100188/* -SXvhTw GNU options, busybox optionally supports */
Denys Vlasenko8ea683d2011-06-13 02:24:18 +0200189/* -T WIDTH Ignored (we don't use tabs on output) */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100190/* -KZ SELinux mandated options, busybox optionally supports */
191/* (coreutils 8.4 has no -K, remove it?) */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100192/* -e I think we made this one up (looks similar to GNU --full-time) */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100193/* We already used up all 32 bits, if we need to add more, candidates for removal: */
194/* -K, -T, -e (add --full-time instead) */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000195static const char ls_options[] ALIGN1 =
Denys Vlasenko982aa262010-12-19 21:54:39 +0100196 "Cadil1gnsxQAk" /* 13 opts, total 13 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000197 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
198 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
199 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100200 IF_FEATURE_LS_RECURSIVE("R") /* 1, 24 */
201 IF_SELINUX("KZ") /* 2, 26 */
202 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 28 */
203 IF_FEATURE_HUMAN_READABLE("h") /* 1, 29 */
204 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 31 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100205 /* with --color, we use all 32 bits */;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000206enum {
207 //OPT_C = (1 << 0),
208 //OPT_a = (1 << 1),
209 //OPT_d = (1 << 2),
210 //OPT_i = (1 << 3),
211 //OPT_l = (1 << 4),
212 //OPT_1 = (1 << 5),
213 OPT_g = (1 << 6),
214 //OPT_n = (1 << 7),
215 //OPT_s = (1 << 8),
216 //OPT_x = (1 << 9),
217 OPT_Q = (1 << 10),
218 //OPT_A = (1 << 11),
219 //OPT_k = (1 << 12),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100220
221 OPTBIT_c = 13,
222 OPTBIT_e,
223 OPTBIT_t,
224 OPTBIT_u,
225 OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS,
226 OPTBIT_X, /* 18 */
227 OPTBIT_r,
228 OPTBIT_v,
229 OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
230 OPTBIT_p, /* 22 */
231 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
232 OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
233 OPTBIT_Z, /* 25 */
234 OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX,
235 OPTBIT_H, /* 27 */
Denys Vlasenkocd387f22011-02-27 04:10:00 +0100236 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100237 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100238 OPTBIT_w, /* 30 */
239 OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH,
240
241 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
242 OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS,
243 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
244 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
245 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
246 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
247 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
248 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
249 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
250 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
251 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
252 OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX,
253 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
254 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
255 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
256 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
257 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH,
258 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH,
259 OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000260};
261
Denis Vlasenko248ce912009-03-03 14:09:04 +0000262/* TODO: simple toggles may be stored as OPT_xxx bits instead */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100263static const uint32_t opt_flags[] = {
Denys Vlasenko69675782013-01-14 01:34:48 +0100264 STYLE_COLUMNAR, /* C */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100265 DISP_HIDDEN | DISP_DOT, /* a */
266 DISP_NOLIST, /* d */
267 LIST_INO, /* i */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100268 LIST_LONG | STYLE_LONG, /* l */
James Youngmana4bc10c2010-12-20 01:36:16 +0100269 STYLE_SINGLE, /* 1 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100270 LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100271 LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100272 LIST_BLOCKS, /* s */
James Youngmana4bc10c2010-12-20 01:36:16 +0100273 DISP_ROWS | STYLE_COLUMNAR, /* x */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100274 0, /* Q (quote filename) - handled via OPT_Q */
275 DISP_HIDDEN, /* A */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100276 ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000277#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100278 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100279 LIST_FULLTIME, /* e */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100280 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
281 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000282#endif
283#if ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100284 SORT_SIZE, /* S */
285 SORT_EXT, /* X */
286 SORT_REVERSE, /* r */
287 SORT_VERSION, /* v */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000288#endif
289#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200290 LIST_FILETYPE | LIST_CLASSIFY, /* F */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100291 LIST_FILETYPE, /* p */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000292#endif
Denis Vlasenko248ce912009-03-03 14:09:04 +0000293#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100294 DISP_RECURSIVE, /* R */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000295#endif
Denis Vlasenko248ce912009-03-03 14:09:04 +0000296#if ENABLE_SELINUX
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100297 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */
298 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000299#endif
Denys Vlasenko982aa262010-12-19 21:54:39 +0100300 (1U << 31)
301 /* options after Z are not processed through opt_flags */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000302};
303
304
Eric Andersen11c65522000-09-07 17:24:47 +0000305/*
Denys Vlasenko2a816392011-05-13 17:28:09 +0200306 * a directory entry and its stat info
Eric Andersen11c65522000-09-07 17:24:47 +0000307 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200308struct dnode {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200309 const char *name; /* usually basename, but think "ls -l dir/file" */
310 const char *fullname; /* full name (usable for stat etc) */
311 struct dnode *dn_next; /* for linked list */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000312 IF_SELINUX(security_context_t sid;)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200313 smallint fname_allocated;
314
315 /* Used to avoid re-doing [l]stat at printout stage
316 * if we already collected needed data in scan stage:
317 */
318 mode_t dn_mode_lstat; /* obtained with lstat, or 0 */
319 mode_t dn_mode_stat; /* obtained with stat, or 0 */
320
321// struct stat dstat;
322// struct stat is huge. We don't need it in full.
323// At least we don't need st_dev and st_blksize,
324// but there are invisible fields as well
325// (such as nanosecond-resolution timespamps)
326// and padding, which we also don't want to store.
327// We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
Denys Vlasenko4029e212011-05-13 17:28:46 +0200328// On 32-bit uclibc: dnode size went from 112 to 84 bytes.
Denys Vlasenko2a816392011-05-13 17:28:09 +0200329//
330 /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
331 mode_t dn_mode; /* obtained with lstat OR stat, depending on -L etc */
332 off_t dn_size;
333#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
334 time_t dn_atime;
335 time_t dn_mtime;
336 time_t dn_ctime;
337#endif
338 ino_t dn_ino;
339 blkcnt_t dn_blocks;
340 nlink_t dn_nlink;
341 uid_t dn_uid;
342 gid_t dn_gid;
343 int dn_rdev_maj;
344 int dn_rdev_min;
345// dev_t dn_dev;
346// blksize_t dn_blksize;
Eric Andersen11c65522000-09-07 17:24:47 +0000347};
Eric Andersen11c65522000-09-07 17:24:47 +0000348
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000349struct globals {
350#if ENABLE_FEATURE_LS_COLOR
351 smallint show_color;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200352# define G_show_color (G.show_color)
353#else
354# define G_show_color 0
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000355#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000356 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000357 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000358#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko2a816392011-05-13 17:28:09 +0200359 unsigned terminal_width;
360# define G_terminal_width (G.terminal_width)
361#else
362# define G_terminal_width TERMINAL_WIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000363#endif
364#if ENABLE_FEATURE_LS_TIMESTAMPS
365 /* Do time() just once. Saves one syscall per file for "ls -l" */
366 time_t current_time_t;
367#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100368} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200369#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000370#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200371 setup_common_bufsiz(); \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200372 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000373 memset(&G, 0, sizeof(G)); \
Denys Vlasenko2a816392011-05-13 17:28:09 +0200374 IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
375 IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000376} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000377
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000378
Denys Vlasenko4029e212011-05-13 17:28:46 +0200379/*** Output code ***/
Denys Vlasenko2a816392011-05-13 17:28:09 +0200380
Eric Andersen79565b62000-08-11 18:10:21 +0000381
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000382/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
383 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
384 * 3/7:multiplexed char/block device)
385 * and we use 0 for unknown and 15 for executables (see below) */
386#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200387/* un fi chr - dir - blk - file - link - sock - - exe */
388#define APPCHAR(mode) ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000389/* 036 black foreground 050 black background
390 037 red foreground 051 red background
391 040 green foreground 052 green background
392 041 brown foreground 053 brown background
393 042 blue foreground 054 blue background
394 043 magenta (purple) foreground 055 magenta background
395 044 cyan (light blue) foreground 056 cyan background
396 045 gray foreground 057 white background
397*/
398#define COLOR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200399 /*un fi chr - dir - blk - file - link - sock - - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000400 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
401 [TYPEINDEX(mode)])
402/* Select normal (0) [actually "reset all"] or bold (1)
403 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
404 * let's use 7 for "impossible" types, just for fun)
405 * Note: coreutils 6.9 uses inverted red for setuid binaries.
406 */
407#define ATTR(mode) ( \
Denys Vlasenko4ad95e62011-05-12 18:40:59 +0200408 /*un fi chr - dir - blk - file- link- sock- - exe */ \
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000409 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
410 [TYPEINDEX(mode)])
411
Denis Vlasenko5c759602006-10-28 12:37:16 +0000412#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000413/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000414static char fgcolor(mode_t mode)
415{
Rob Landley9947a242006-06-15 22:11:10 +0000416 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000417 return COLOR(0xF000); /* File is executable ... */
418 return COLOR(mode);
419}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000420static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000421{
Rob Landley9947a242006-06-15 22:11:10 +0000422 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000423 return ATTR(0xF000); /* File is executable ... */
424 return ATTR(mode);
425}
426#endif
427
Denys Vlasenko2a816392011-05-13 17:28:09 +0200428#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000429static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000430{
Denys Vlasenko2a816392011-05-13 17:28:09 +0200431 if (!(G.all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000432 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000433 if (S_ISDIR(mode))
434 return '/';
Denys Vlasenko2a816392011-05-13 17:28:09 +0200435 if (!(G.all_fmt & LIST_CLASSIFY))
Rob Landley9947a242006-06-15 22:11:10 +0000436 return '\0';
437 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000438 return '*';
439 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000440}
441#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000442
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100443static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000444{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100445 unsigned len;
446 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000447
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100448 // TODO: quote tab as \t, etc, if -Q
449 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000450
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100451 if (!(option_mask32 & OPT_Q)) {
452 return uni_stat.unicode_width;
453 }
454
455 len = 2 + uni_stat.unicode_width;
456 while (*name) {
457 if (*name == '"' || *name == '\\') {
458 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000459 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100460 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000461 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100462 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000463}
464
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100465/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100466 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100467 * STYLE_SINGLE and STYLE_LONG don't care.
468 * coreutils 7.2 also supports:
469 * ls -b (--escape) = octal escapes (although it doesn't look like working)
470 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200471 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100472static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200473{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100474 unsigned len;
475 uni_stat_t uni_stat;
476
477 // TODO: quote tab as \t, etc, if -Q
478 name = printable_string(&uni_stat, name);
479
480 if (!(option_mask32 & OPT_Q)) {
481 fputs(name, stdout);
482 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200483 }
484
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100485 len = 2 + uni_stat.unicode_width;
486 putchar('"');
487 while (*name) {
488 if (*name == '"' || *name == '\\') {
489 putchar('\\');
490 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000491 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700492 putchar(*name);
493 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000494 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100495 putchar('"');
496 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000497}
498
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100499/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100500 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100501 * STYLE_SINGLE and STYLE_LONG don't care.
502 */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200503static NOINLINE unsigned display_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000504{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200505 unsigned column = 0;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200506 char *lpath;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000507#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200508 struct stat statbuf;
Eric Andersen11c65522000-09-07 17:24:47 +0000509 char append;
510#endif
511
Denis Vlasenko5c759602006-10-28 12:37:16 +0000512#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200513 append = append_char(dn->dn_mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000514#endif
515
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000516 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100517 * does not appear *inside* the "ls -l" line */
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200518 lpath = NULL;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200519 if (G.all_fmt & LIST_SYMLINK)
520 if (S_ISLNK(dn->dn_mode))
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000521 lpath = xmalloc_readlink_or_warn(dn->fullname);
522
Denys Vlasenko2a816392011-05-13 17:28:09 +0200523 if (G.all_fmt & LIST_INO)
524 column += printf("%7llu ", (long long) dn->dn_ino);
Denys Vlasenko3b28dae2011-03-01 05:37:41 +0100525//TODO: -h should affect -s too:
Denys Vlasenko2a816392011-05-13 17:28:09 +0200526 if (G.all_fmt & LIST_BLOCKS)
527 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
528 if (G.all_fmt & LIST_MODEBITS)
529 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
530 if (G.all_fmt & LIST_NLINKS)
531 column += printf("%4lu ", (long) dn->dn_nlink);
532 if (G.all_fmt & LIST_ID_NUMERIC) {
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100533 if (option_mask32 & OPT_g)
Denys Vlasenko2a816392011-05-13 17:28:09 +0200534 column += printf("%-8u ", (int) dn->dn_gid);
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100535 else
536 column += printf("%-8u %-8u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200537 (int) dn->dn_uid,
538 (int) dn->dn_gid);
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100539 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000540#if ENABLE_FEATURE_LS_USERNAME
Denys Vlasenko2a816392011-05-13 17:28:09 +0200541 else if (G.all_fmt & LIST_ID_NAME) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000542 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100543 column += printf("%-8.8s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200544 get_cached_groupname(dn->dn_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000545 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100546 column += printf("%-8.8s %-8.8s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200547 get_cached_username(dn->dn_uid),
548 get_cached_groupname(dn->dn_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000549 }
550 }
Eric Andersen11c65522000-09-07 17:24:47 +0000551#endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200552 if (G.all_fmt & LIST_SIZE) {
553 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000554 column += printf("%4u, %3u ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200555 dn->dn_rdev_maj,
556 dn->dn_rdev_min);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000557 } else {
Denys Vlasenko982aa262010-12-19 21:54:39 +0100558 if (option_mask32 & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100559 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko2a816392011-05-13 17:28:09 +0200560 /* print size, show one fractional, use suffixes */
561 make_human_readable_str(dn->dn_size, 1, 0)
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200562 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000563 } else {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200564 column += printf("%9"OFF_FMT"u ", dn->dn_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000565 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000566 }
567 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000568#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko2a816392011-05-13 17:28:09 +0200569 if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100570 char *filetime;
Denys Vlasenkoc5beaa02015-02-23 15:25:58 +0100571 const time_t *ttime = &dn->dn_mtime;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200572 if (G.all_fmt & TIME_ACCESS)
Denys Vlasenko0cb981c2015-02-18 11:29:07 +0100573 ttime = &dn->dn_atime;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200574 if (G.all_fmt & TIME_CHANGE)
Denys Vlasenko0cb981c2015-02-18 11:29:07 +0100575 ttime = &dn->dn_ctime;
576 filetime = ctime(ttime);
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100577 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200578 if (G.all_fmt & LIST_FULLTIME) { /* -e */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100579 /* Note: coreutils 8.4 ls --full-time prints:
580 * 2009-07-13 17:49:27.000000000 +0200
581 */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100582 column += printf("%.24s ", filetime);
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100583 } else { /* LIST_DATE_TIME */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200584 /* G.current_time_t ~== time(NULL) */
Denys Vlasenko0cb981c2015-02-18 11:29:07 +0100585 time_t age = G.current_time_t - *ttime;
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000586 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
Denys Vlasenko8e92df12015-02-16 15:36:25 +0100587 /* less than 6 months old */
588 /* "mmm dd hh:mm " */
589 printf("%.12s ", filetime + 4);
590 } else {
591 /* "mmm dd yyyy " */
592 /* "mmm dd yyyyy " after year 9999 :) */
593 strchr(filetime + 20, '\n')[0] = ' ';
594 printf("%.7s%6s", filetime + 4, filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000595 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000596 column += 13;
597 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100598 }
Eric Andersen11c65522000-09-07 17:24:47 +0000599#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000600#if ENABLE_SELINUX
Denys Vlasenko2a816392011-05-13 17:28:09 +0200601 if (G.all_fmt & LIST_CONTEXT) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000602 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
603 freecon(dn->sid);
604 }
Eric Andersen9e480452003-07-03 10:07:04 +0000605#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100606
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000607#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200608 if (G_show_color) {
609 mode_t mode = dn->dn_mode_lstat;
610 if (!mode)
611 if (lstat(dn->fullname, &statbuf) == 0)
612 mode = statbuf.st_mode;
613 printf("\033[%u;%um", bold(mode), fgcolor(mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000614 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100615#endif
616 column += print_name(dn->name);
Denys Vlasenko2a816392011-05-13 17:28:09 +0200617 if (G_show_color) {
James Youngmana4bc10c2010-12-20 01:36:16 +0100618 printf("\033[0m");
619 }
620
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200621 if (lpath) {
622 printf(" -> ");
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000623#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +0200624 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
625 mode_t mode = dn->dn_mode_stat;
626 if (!mode)
627 if (stat(dn->fullname, &statbuf) == 0)
628 mode = statbuf.st_mode;
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200629# if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200630 append = append_char(mode);
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200631# endif
Denys Vlasenko2a816392011-05-13 17:28:09 +0200632# if ENABLE_FEATURE_LS_COLOR
633 if (G_show_color) {
634 printf("\033[%u;%um", bold(mode), fgcolor(mode));
635 }
636# endif
Eric Andersen11c65522000-09-07 17:24:47 +0000637 }
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200638#endif
639 column += print_name(lpath) + 4;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200640 free(lpath);
641 if (G_show_color) {
Denys Vlasenkod27ac292011-05-13 17:27:15 +0200642 printf("\033[0m");
643 }
Eric Andersen11c65522000-09-07 17:24:47 +0000644 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000645#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenko2a816392011-05-13 17:28:09 +0200646 if (G.all_fmt & LIST_FILETYPE) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000647 if (append) {
648 putchar(append);
649 column++;
650 }
651 }
652#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000653
Glenn L McGrath4d001292003-01-06 01:11:50 +0000654 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000655}
656
Denys Vlasenko4029e212011-05-13 17:28:46 +0200657static void display_files(struct dnode **dn, unsigned nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100658{
659 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100660 unsigned column;
661 unsigned nexttab;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100662 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100663
Denys Vlasenko2a816392011-05-13 17:28:09 +0200664 if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100665 ncols = 1;
666 } else {
667 /* find the longest file name, use that as the column width */
668 for (i = 0; dn[i]; i++) {
669 int len = calc_name_len(dn[i]->name);
670 if (column_width < len)
671 column_width = len;
672 }
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100673 column_width += 2 +
Denys Vlasenko2a816392011-05-13 17:28:09 +0200674 IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
675 ((G.all_fmt & LIST_INO) ? 8 : 0) +
676 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
677 ncols = (unsigned)G_terminal_width / column_width;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100678 }
679
680 if (ncols > 1) {
681 nrows = nfiles / ncols;
682 if (nrows * ncols < nfiles)
683 nrows++; /* round up fractionals */
684 } else {
685 nrows = nfiles;
686 ncols = 1;
687 }
688
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100689 column = 0;
690 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100691 for (row = 0; row < nrows; row++) {
692 for (nc = 0; nc < ncols; nc++) {
693 /* reach into the array based on the column and row */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200694 if (G.all_fmt & DISP_ROWS)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100695 i = (row * ncols) + nc; /* display across row */
696 else
697 i = (nc * nrows) + row; /* display by column */
698 if (i < nfiles) {
699 if (column > 0) {
700 nexttab -= column;
Denys Vlasenkoea351b92016-03-06 17:53:11 +0100701 printf("%*s", nexttab, "");
702 column += nexttab;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100703 }
704 nexttab = column + column_width;
Denys Vlasenko4029e212011-05-13 17:28:46 +0200705 column += display_single(dn[i]);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100706 }
707 }
708 putchar('\n');
709 column = 0;
710 }
711}
712
713
Denys Vlasenko4029e212011-05-13 17:28:46 +0200714/*** Dir scanning code ***/
715
716static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100717{
Denys Vlasenko4029e212011-05-13 17:28:46 +0200718 struct stat statbuf;
719 struct dnode *cur;
720
721 cur = xzalloc(sizeof(*cur));
722 cur->fullname = fullname;
723 cur->name = name;
724
725 if ((option_mask32 & OPT_L) || force_follow) {
726#if ENABLE_SELINUX
727 if (is_selinux_enabled()) {
Denys Vlasenko69675782013-01-14 01:34:48 +0100728 getfilecon(fullname, &cur->sid);
Denys Vlasenko4029e212011-05-13 17:28:46 +0200729 }
730#endif
731 if (stat(fullname, &statbuf)) {
732 bb_simple_perror_msg(fullname);
733 G.exit_code = EXIT_FAILURE;
734 free(cur);
735 return NULL;
736 }
737 cur->dn_mode_stat = statbuf.st_mode;
738 } else {
739#if ENABLE_SELINUX
740 if (is_selinux_enabled()) {
741 lgetfilecon(fullname, &cur->sid);
742 }
743#endif
744 if (lstat(fullname, &statbuf)) {
745 bb_simple_perror_msg(fullname);
746 G.exit_code = EXIT_FAILURE;
747 free(cur);
748 return NULL;
749 }
750 cur->dn_mode_lstat = statbuf.st_mode;
751 }
752
753 /* cur->dstat = statbuf: */
754 cur->dn_mode = statbuf.st_mode ;
755 cur->dn_size = statbuf.st_size ;
756#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
757 cur->dn_atime = statbuf.st_atime ;
758 cur->dn_mtime = statbuf.st_mtime ;
759 cur->dn_ctime = statbuf.st_ctime ;
760#endif
761 cur->dn_ino = statbuf.st_ino ;
762 cur->dn_blocks = statbuf.st_blocks;
763 cur->dn_nlink = statbuf.st_nlink ;
764 cur->dn_uid = statbuf.st_uid ;
765 cur->dn_gid = statbuf.st_gid ;
766 cur->dn_rdev_maj = major(statbuf.st_rdev);
767 cur->dn_rdev_min = minor(statbuf.st_rdev);
768
769 return cur;
770}
771
772static unsigned count_dirs(struct dnode **dn, int which)
773{
774 unsigned dirs, all;
775
776 if (!dn)
777 return 0;
778
779 dirs = all = 0;
780 for (; *dn; dn++) {
781 const char *name;
782
783 all++;
784 if (!S_ISDIR((*dn)->dn_mode))
785 continue;
786
787 name = (*dn)->name;
788 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
789 /* or if it's not . or .. */
790 || name[0] != '.'
791 || (name[1] && (name[1] != '.' || name[2]))
792 ) {
793 dirs++;
794 }
795 }
796 return which != SPLIT_FILE ? dirs : all - dirs;
797}
798
799/* get memory to hold an array of pointers */
800static struct dnode **dnalloc(unsigned num)
801{
802 if (num < 1)
803 return NULL;
804
805 num++; /* so that we have terminating NULL */
806 return xzalloc(num * sizeof(struct dnode *));
807}
808
809#if ENABLE_FEATURE_LS_RECURSIVE
810static void dfree(struct dnode **dnp)
811{
812 unsigned i;
813
814 if (dnp == NULL)
815 return;
816
817 for (i = 0; dnp[i]; i++) {
818 struct dnode *cur = dnp[i];
819 if (cur->fname_allocated)
820 free((char*)cur->fullname);
821 free(cur);
822 }
823 free(dnp);
824}
825#else
826#define dfree(...) ((void)0)
827#endif
828
829/* Returns NULL-terminated malloced vector of pointers (or NULL) */
830static struct dnode **splitdnarray(struct dnode **dn, int which)
831{
832 unsigned dncnt, d;
833 struct dnode **dnp;
834
835 if (dn == NULL)
836 return NULL;
837
838 /* count how many dirs or files there are */
839 dncnt = count_dirs(dn, which);
840
841 /* allocate a file array and a dir array */
842 dnp = dnalloc(dncnt);
843
844 /* copy the entrys into the file or dir array */
845 for (d = 0; *dn; dn++) {
846 if (S_ISDIR((*dn)->dn_mode)) {
847 const char *name;
848
849 if (which == SPLIT_FILE)
850 continue;
851
852 name = (*dn)->name;
853 if ((which & SPLIT_DIR) /* any dir... */
854 /* ... or not . or .. */
855 || name[0] != '.'
856 || (name[1] && (name[1] != '.' || name[2]))
857 ) {
858 dnp[d++] = *dn;
859 }
860 } else
861 if (which == SPLIT_FILE) {
862 dnp[d++] = *dn;
863 }
864 }
865 return dnp;
866}
867
868#if ENABLE_FEATURE_LS_SORTFILES
869static int sortcmp(const void *a, const void *b)
870{
871 struct dnode *d1 = *(struct dnode **)a;
872 struct dnode *d2 = *(struct dnode **)b;
873 unsigned sort_opts = G.all_fmt & SORT_MASK;
874 off_t dif;
875
876 dif = 0; /* assume SORT_NAME */
877 // TODO: use pre-initialized function pointer
878 // instead of branch forest
879 if (sort_opts == SORT_SIZE) {
880 dif = (d2->dn_size - d1->dn_size);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200881 } else
882 if (sort_opts == SORT_ATIME) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200883 dif = (d2->dn_atime - d1->dn_atime);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200884 } else
885 if (sort_opts == SORT_CTIME) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200886 dif = (d2->dn_ctime - d1->dn_ctime);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200887 } else
888 if (sort_opts == SORT_MTIME) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200889 dif = (d2->dn_mtime - d1->dn_mtime);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200890 } else
891 if (sort_opts == SORT_DIR) {
Denys Vlasenko4029e212011-05-13 17:28:46 +0200892 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200893 } else
Denys Vlasenko1e18a012011-06-21 17:12:52 +0200894#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200895 if (sort_opts == SORT_VERSION) {
896 dif = strverscmp(d1->name, d2->name);
897 } else
Denys Vlasenko561f9c82011-06-21 16:38:29 +0200898#endif
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200899 if (sort_opts == SORT_EXT) {
900 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
Denys Vlasenko4029e212011-05-13 17:28:46 +0200901 }
902 if (dif == 0) {
Denys Vlasenkof194cc12011-06-13 02:13:42 +0200903 /* sort by name, use as tie breaker for other sorts */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200904 if (ENABLE_LOCALE_SUPPORT)
905 dif = strcoll(d1->name, d2->name);
906 else
907 dif = strcmp(d1->name, d2->name);
908 }
909
910 /* Make dif fit into an int */
911 if (sizeof(dif) > sizeof(int)) {
912 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
913 /* shift leaving only "int" worth of bits */
914 if (dif != 0) {
915 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100916 }
917 }
918
Denys Vlasenko4029e212011-05-13 17:28:46 +0200919 return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100920}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200921
922static void dnsort(struct dnode **dn, int size)
923{
924 qsort(dn, size, sizeof(*dn), sortcmp);
925}
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200926
927static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
928{
929 dnsort(dn, nfiles);
930 display_files(dn, nfiles);
931}
Denys Vlasenko4029e212011-05-13 17:28:46 +0200932#else
Denys Vlasenko8dd29da2011-05-13 17:55:08 +0200933# define dnsort(dn, size) ((void)0)
934# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100935#endif
936
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100937/* Returns NULL-terminated malloced vector of pointers (or NULL) */
Denys Vlasenko4029e212011-05-13 17:28:46 +0200938static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100939{
940 struct dnode *dn, *cur, **dnp;
941 struct dirent *entry;
942 DIR *dir;
943 unsigned i, nfiles;
944
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100945 *nfiles_p = 0;
946 dir = warn_opendir(path);
947 if (dir == NULL) {
Denys Vlasenko2a816392011-05-13 17:28:09 +0200948 G.exit_code = EXIT_FAILURE;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100949 return NULL; /* could not open the dir */
950 }
951 dn = NULL;
952 nfiles = 0;
953 while ((entry = readdir(dir)) != NULL) {
954 char *fullname;
955
956 /* are we going to list the file- it may be . or .. or a hidden file */
957 if (entry->d_name[0] == '.') {
958 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
Denys Vlasenko2a816392011-05-13 17:28:09 +0200959 && !(G.all_fmt & DISP_DOT)
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100960 ) {
961 continue;
962 }
Denys Vlasenko2a816392011-05-13 17:28:09 +0200963 if (!(G.all_fmt & DISP_HIDDEN))
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100964 continue;
965 }
966 fullname = concat_path_file(path, entry->d_name);
967 cur = my_stat(fullname, bb_basename(fullname), 0);
968 if (!cur) {
969 free(fullname);
970 continue;
971 }
972 cur->fname_allocated = 1;
Denys Vlasenko2a816392011-05-13 17:28:09 +0200973 cur->dn_next = dn;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100974 dn = cur;
975 nfiles++;
976 }
977 closedir(dir);
978
979 if (dn == NULL)
980 return NULL;
981
982 /* now that we know how many files there are
983 * allocate memory for an array to hold dnode pointers
984 */
985 *nfiles_p = nfiles;
986 dnp = dnalloc(nfiles);
987 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
988 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +0200989 dn = dn->dn_next;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100990 if (!dn)
991 break;
992 }
993
994 return dnp;
995}
996
Denys Vlasenko4029e212011-05-13 17:28:46 +0200997#if ENABLE_DESKTOP
998/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
999 * If any of the -l, -n, -s options is specified, each list
1000 * of files within the directory shall be preceded by a
1001 * status line indicating the number of file system blocks
1002 * occupied by files in the directory in 512-byte units if
1003 * the -k option is not specified, or 1024-byte units if the
1004 * -k option is specified, rounded up to the next integral
1005 * number of units.
1006 */
1007/* by Jorgen Overgaard (jorgen AT antistaten.se) */
1008static off_t calculate_blocks(struct dnode **dn)
1009{
1010 uoff_t blocks = 1;
1011 if (dn) {
1012 while (*dn) {
1013 /* st_blocks is in 512 byte blocks */
1014 blocks += (*dn)->dn_blocks;
1015 dn++;
1016 }
1017 }
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001018
Denys Vlasenko4029e212011-05-13 17:28:46 +02001019 /* Even though standard says use 512 byte blocks, coreutils use 1k */
1020 /* Actually, we round up by calculating (blocks + 1) / 2,
1021 * "+ 1" was done when we initialized blocks to 1 */
1022 return blocks >> 1;
1023}
1024#endif
1025
1026static void scan_and_display_dirs_recur(struct dnode **dn, int first)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001027{
1028 unsigned nfiles;
1029 struct dnode **subdnp;
1030
1031 for (; *dn; dn++) {
Denys Vlasenko2a816392011-05-13 17:28:09 +02001032 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001033 if (!first)
1034 bb_putchar('\n');
1035 first = 0;
1036 printf("%s:\n", (*dn)->fullname);
1037 }
Denys Vlasenko4029e212011-05-13 17:28:46 +02001038 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001039#if ENABLE_DESKTOP
Denys Vlasenkofca0ee52014-02-27 15:40:26 +01001040 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS))
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001041 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1042#endif
1043 if (nfiles > 0) {
1044 /* list all files at this level */
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001045 sort_and_display_files(subdnp, nfiles);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001046
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001047 if (ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenko2a816392011-05-13 17:28:09 +02001048 && (G.all_fmt & DISP_RECURSIVE)
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001049 ) {
1050 struct dnode **dnd;
1051 unsigned dndirs;
1052 /* recursive - list the sub-dirs */
1053 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1054 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1055 if (dndirs > 0) {
1056 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001057 scan_and_display_dirs_recur(dnd, 0);
Denys Vlasenko66ca2412011-05-13 17:27:36 +02001058 /* free the array of dnode pointers to the dirs */
1059 free(dnd);
1060 }
1061 }
1062 /* free the dnodes and the fullname mem */
1063 dfree(subdnp);
1064 }
1065 }
1066}
1067
1068
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001069int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +00001070{
Glenn L McGrath792cae52004-01-18 05:15:16 +00001071 struct dnode **dnd;
1072 struct dnode **dnf;
1073 struct dnode **dnp;
1074 struct dnode *dn;
1075 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001076 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001077 unsigned nfiles;
1078 unsigned dnfiles;
1079 unsigned dndirs;
1080 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001081#if ENABLE_FEATURE_LS_COLOR
1082 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1083 /* coreutils 6.10:
1084 * # ls --color=BOGUS
1085 * ls: invalid argument 'BOGUS' for '--color'
1086 * Valid arguments are:
1087 * 'always', 'yes', 'force'
1088 * 'never', 'no', 'none'
1089 * 'auto', 'tty', 'if-tty'
1090 * (and substrings: "--color=alwa" work too)
1091 */
1092 static const char ls_longopts[] ALIGN1 =
1093 "color\0" Optional_argument "\xff"; /* no short equivalent */
1094 static const char color_str[] ALIGN1 =
1095 "always\0""yes\0""force\0"
1096 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001097 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001098 const char *color_opt = color_str; /* "always" */
1099#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001100
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001101 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001102
Denys Vlasenko28055022010-01-04 20:49:58 +01001103 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001104
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001105 if (ENABLE_FEATURE_LS_SORTFILES)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001106 G.all_fmt = SORT_NAME;
Glenn L McGrath792cae52004-01-18 05:15:16 +00001107
Denis Vlasenko5c759602006-10-28 12:37:16 +00001108#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001109 /* obtain the terminal width */
Denys Vlasenko641caae2015-10-23 01:44:22 +02001110 G_terminal_width = get_terminal_width(STDIN_FILENO);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001111 /* go one less... */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001112 G_terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001113#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001114
Eric Andersen11c65522000-09-07 17:24:47 +00001115 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001116 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001117 opt_complementary =
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001118 /* -e implies -l */
Denys Vlasenkob47b3ce2011-08-10 00:51:29 +02001119 IF_FEATURE_LS_TIMESTAMPS("el")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001120 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1121 * in some pairs of opts, only last one takes effect:
1122 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001123 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001124 // ":m-l:l-m" - we don't have -m
1125 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001126 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1127 ":C-1:1-C" /* bycols/oneline */
1128 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
Denys Vlasenkob47b3ce2011-08-10 00:51:29 +02001129 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001130 /* -w NUM: */
1131 IF_FEATURE_AUTOWIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001132 opt = getopt32(argv, ls_options
Denys Vlasenko2a816392011-05-13 17:28:09 +02001133 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001134 IF_FEATURE_LS_COLOR(, &color_opt)
1135 );
Denys Vlasenko982aa262010-12-19 21:54:39 +01001136 for (i = 0; opt_flags[i] != (1U << 31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001137 if (opt & (1 << i)) {
Denys Vlasenko982aa262010-12-19 21:54:39 +01001138 uint32_t flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001139
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001140 if (flags & STYLE_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001141 G.all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001142 if (flags & SORT_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001143 G.all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001144 if (flags & TIME_MASK)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001145 G.all_fmt &= ~TIME_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001146
Denys Vlasenko2a816392011-05-13 17:28:09 +02001147 G.all_fmt |= flags;
Manuel Novoa III cad53642003-03-19 09:13:01 +00001148 }
Eric Andersen11c65522000-09-07 17:24:47 +00001149 }
1150
Denis Vlasenko5c759602006-10-28 12:37:16 +00001151#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko2a816392011-05-13 17:28:09 +02001152 /* set G_show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001153 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1154 char *p = getenv("LS_COLORS");
1155 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001156 if (!p || (p[0] && strcmp(p, "none") != 0))
Denys Vlasenko2a816392011-05-13 17:28:09 +02001157 G_show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001158 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001159 if (opt & OPT_color) {
1160 if (color_opt[0] == 'n')
Denys Vlasenko2a816392011-05-13 17:28:09 +02001161 G_show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001162 else switch (index_in_substrings(color_str, color_opt)) {
1163 case 3:
1164 case 4:
1165 case 5:
1166 if (isatty(STDOUT_FILENO)) {
1167 case 0:
1168 case 1:
1169 case 2:
Denys Vlasenko2a816392011-05-13 17:28:09 +02001170 G_show_color = 1;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001171 }
1172 }
Paul Fox156dc412005-08-01 19:33:30 +00001173 }
1174#endif
1175
Eric Andersen11c65522000-09-07 17:24:47 +00001176 /* sort out which command line options take precedence */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001177 if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1178 G.all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001179 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
Denys Vlasenko2a816392011-05-13 17:28:09 +02001180 if (G.all_fmt & TIME_CHANGE)
1181 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1182 if (G.all_fmt & TIME_ACCESS)
1183 G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
Rob Landleyea224be2006-06-18 20:20:07 +00001184 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001185 if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
1186 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001187
Denys Vlasenkof3137462010-12-19 05:05:34 +01001188 /* choose a display format if one was not already specified by an option */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001189 if (!(G.all_fmt & STYLE_MASK))
1190 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001191
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001192 argv += optind;
1193 if (!argv[0])
1194 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001195
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001196 if (argv[1])
Denys Vlasenko2a816392011-05-13 17:28:09 +02001197 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001198
Denis Vlasenko5c759602006-10-28 12:37:16 +00001199 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001200 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001201 nfiles = 0;
1202 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001203 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001204 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001205 !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1206 || (G.all_fmt & LIST_BLOCKS)
Denys Vlasenkoea684c62011-03-12 03:12:36 +01001207 || (option_mask32 & OPT_F)
1208 )
Denys Vlasenko982aa262010-12-19 21:54:39 +01001209 /* ... or if -H: */
1210 || (option_mask32 & OPT_H)
Denys Vlasenkod27ac292011-05-13 17:27:15 +02001211 /* ... or if -L, but my_stat always follows links if -L */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001212 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001213 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001214 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001215 continue;
Denys Vlasenko2a816392011-05-13 17:28:09 +02001216 /*cur->fname_allocated = 0; - already is */
1217 cur->dn_next = dn;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001218 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001219 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001220 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001221
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001222 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1223 if (nfiles == 0)
Denys Vlasenko2a816392011-05-13 17:28:09 +02001224 return G.exit_code;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001225
Eric Andersen11c65522000-09-07 17:24:47 +00001226 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001227 * allocate memory for an array to hold dnode pointers
1228 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001229 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001230 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1231 dnp[i] = dn; /* save pointer to node in array */
Denys Vlasenko2a816392011-05-13 17:28:09 +02001232 dn = dn->dn_next;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001233 if (!dn)
1234 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001235 }
1236
Denys Vlasenko2a816392011-05-13 17:28:09 +02001237 if (G.all_fmt & DISP_NOLIST) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001238 sort_and_display_files(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001239 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001240 dnd = splitdnarray(dnp, SPLIT_DIR);
1241 dnf = splitdnarray(dnp, SPLIT_FILE);
1242 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001243 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001244 if (dnfiles > 0) {
Denys Vlasenko8dd29da2011-05-13 17:55:08 +02001245 sort_and_display_files(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001246 if (ENABLE_FEATURE_CLEAN_UP)
1247 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001248 }
1249 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001250 dnsort(dnd, dndirs);
Denys Vlasenko4029e212011-05-13 17:28:46 +02001251 scan_and_display_dirs_recur(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001252 if (ENABLE_FEATURE_CLEAN_UP)
1253 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001254 }
1255 }
Denys Vlasenko2a816392011-05-13 17:28:09 +02001256
Rob Landley26314862006-05-02 19:46:52 +00001257 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001258 dfree(dnp);
Denys Vlasenko2a816392011-05-13 17:28:09 +02001259 return G.exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001260}