blob: da370c74bacc5e357fe9a0320fa8d12adb61e50d [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 * tiny-ls.c version 0.1.0: A minimalist 'ls'
4 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
Eric Andersen11c65522000-09-07 17:24:47 +00005 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02006 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencc8ed391999-10-05 16:24:54 +00007 */
8
Denis Vlasenko5e4fda02009-03-08 23:46:48 +00009/* [date unknown. Perhaps before year 2000]
Eric Andersencc8ed391999-10-05 16:24:54 +000010 * To achieve a small memory footprint, this version of 'ls' doesn't do any
11 * file sorting, and only has the most essential command line switches
Eric Andersen77d92682001-05-23 20:32:09 +000012 * (i.e., the ones I couldn't live without :-) All features which involve
Eric Andersencc8ed391999-10-05 16:24:54 +000013 * linking in substantial chunks of libc can be disabled.
14 *
15 * Although I don't really want to add new features to this program to
16 * keep it small, I *am* interested to receive bug fixes and ways to make
17 * it more portable.
18 *
19 * KNOWN BUGS:
Denys Vlasenko87c150c2009-10-03 01:14:15 +020020 * 1. hidden files can make column width too large
Erik Andersen9ffdaa62000-02-11 21:55:04 +000021 *
Eric Andersencc8ed391999-10-05 16:24:54 +000022 * NON-OPTIMAL BEHAVIOUR:
23 * 1. autowidth reads directories twice
24 * 2. if you do a short directory listing without filetype characters
25 * appended, there's no need to stat each one
26 * PORTABILITY:
27 * 1. requires lstat (BSD) - how do you do it without?
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000028 *
29 * [2009-03]
30 * ls sorts listing now, and supports almost all options.
Eric Andersencc8ed391999-10-05 16:24:54 +000031 */
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010032
33//usage:#define ls_trivial_usage
34//usage: "[-1AaCxd"
Denys Vlasenko982aa262010-12-19 21:54:39 +010035//usage: IF_FEATURE_LS_FOLLOWLINKS("LH")
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010036//usage: IF_FEATURE_LS_RECURSIVE("R")
37//usage: IF_FEATURE_LS_FILETYPES("Fp") "lins"
38//usage: IF_FEATURE_LS_TIMESTAMPS("e")
39//usage: IF_FEATURE_HUMAN_READABLE("h")
40//usage: IF_FEATURE_LS_SORTFILES("rSXv")
41//usage: IF_FEATURE_LS_TIMESTAMPS("ctu")
42//usage: IF_SELINUX("kKZ") "]"
43//usage: IF_FEATURE_AUTOWIDTH(" -w WIDTH") " [FILE]..."
44//usage:#define ls_full_usage "\n\n"
45//usage: "List directory contents\n"
46//usage: "\nOptions:"
47//usage: "\n -1 List in a single column"
48//usage: "\n -A Don't list . and .."
49//usage: "\n -a Don't hide entries starting with ."
50//usage: "\n -C List by columns"
51//usage: "\n -x List by lines"
52//usage: "\n -d List directory entries instead of contents"
53//usage: IF_FEATURE_LS_FOLLOWLINKS(
Denys Vlasenko982aa262010-12-19 21:54:39 +010054//usage: "\n -L Follow symlinks"
55//usage: "\n -H Follow symlinks on command line only"
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +010056//usage: )
57//usage: IF_FEATURE_LS_RECURSIVE(
58//usage: "\n -R Recurse"
59//usage: )
60//usage: IF_FEATURE_LS_FILETYPES(
61//usage: "\n -F Append indicator (one of */=@|) to entries"
62//usage: "\n -p Append indicator (one of /=@|) to entries"
63//usage: )
64//usage: "\n -l Long listing format"
65//usage: "\n -i List inode numbers"
66//usage: "\n -n List numeric UIDs and GIDs instead of names"
67//usage: "\n -s List the size of each file, in blocks"
68//usage: IF_FEATURE_LS_TIMESTAMPS(
69//usage: "\n -e List full date and time"
70//usage: )
71//usage: IF_FEATURE_HUMAN_READABLE(
72//usage: "\n -h List sizes in human readable format (1K 243M 2G)"
73//usage: )
74//usage: IF_FEATURE_LS_SORTFILES(
75//usage: "\n -r Sort in reverse order"
76//usage: "\n -S Sort by file size"
77//usage: "\n -X Sort by extension"
78//usage: "\n -v Sort by version"
79//usage: )
80//usage: IF_FEATURE_LS_TIMESTAMPS(
81//usage: "\n -c With -l: sort by ctime"
82//usage: "\n -t With -l: sort by modification time"
83//usage: "\n -u With -l: sort by access time"
84//usage: )
85//usage: IF_SELINUX(
86//usage: "\n -k List security context"
87//usage: "\n -K List security context in long format"
88//usage: "\n -Z List security context and permission"
89//usage: )
90//usage: IF_FEATURE_AUTOWIDTH(
91//usage: "\n -w N Assume the terminal is N columns wide"
92//usage: )
93//usage: IF_FEATURE_LS_COLOR(
94//usage: "\n --color[={always,never,auto}] Control coloring"
95//usage: )
96
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000097#include "libbb.h"
Denys Vlasenko42a8fd02009-07-11 21:36:13 +020098#include "unicode.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +000099
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000100
Denis Vlasenko99912ca2007-04-10 15:43:37 +0000101/* This is a NOEXEC applet. Be very careful! */
102
Eric Andersenf1142c52001-02-20 06:16:29 +0000103
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000104#if ENABLE_FTPD
105/* ftpd uses ls, and without timestamps Mozilla won't understand
106 * ftpd's LIST output.
107 */
108# undef CONFIG_FEATURE_LS_TIMESTAMPS
109# undef ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000110# undef IF_FEATURE_LS_TIMESTAMPS
111# undef IF_NOT_FEATURE_LS_TIMESTAMPS
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000112# define CONFIG_FEATURE_LS_TIMESTAMPS 1
113# define ENABLE_FEATURE_LS_TIMESTAMPS 1
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000114# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
115# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
Denis Vlasenko245f91b2009-03-09 22:37:23 +0000116#endif
117
118
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000119enum {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000120TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000121
Denys Vlasenko982aa262010-12-19 21:54:39 +0100122SPLIT_DIR = 1,
123SPLIT_FILE = 0,
124SPLIT_SUBDIR = 2,
125
126/* Bits in all_fmt: */
Eric Andersencc8ed391999-10-05 16:24:54 +0000127
Eric Andersen11c65522000-09-07 17:24:47 +0000128/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
129/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000130LIST_INO = 1 << 0,
131LIST_BLOCKS = 1 << 1,
132LIST_MODEBITS = 1 << 2,
133LIST_NLINKS = 1 << 3,
134LIST_ID_NAME = 1 << 4,
135LIST_ID_NUMERIC = 1 << 5,
136LIST_CONTEXT = 1 << 6,
137LIST_SIZE = 1 << 7,
James Youngmana4bc10c2010-12-20 01:36:16 +0100138LIST_DATE_TIME = 1 << 8,
139LIST_FULLTIME = 1 << 9,
140LIST_SYMLINK = 1 << 10,
141LIST_FILETYPE = 1 << 11,
142LIST_EXEC = 1 << 12,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000143LIST_MASK = (LIST_EXEC << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +0000144
145/* what files will be displayed */
James Youngmana4bc10c2010-12-20 01:36:16 +0100146DISP_DIRNAME = 1 << 13, /* 2 or more items? label directories */
147DISP_HIDDEN = 1 << 14, /* show filenames starting with . */
148DISP_DOT = 1 << 15, /* show . and .. */
149DISP_NOLIST = 1 << 16, /* show directory as itself, not contents */
150DISP_RECURSIVE = 1 << 17, /* show directory and everything below it */
151DISP_ROWS = 1 << 18, /* print across rows */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000152DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +0000153
Denys Vlasenko982aa262010-12-19 21:54:39 +0100154/* what is the overall style of the listing */
James Youngmana4bc10c2010-12-20 01:36:16 +0100155STYLE_COLUMNAR = 1 << 19, /* many records per line */
156STYLE_LONG = 2 << 19, /* one record per line, extended info */
157STYLE_SINGLE = 3 << 19, /* one record per line */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100158STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000159
Eric Andersen11c65522000-09-07 17:24:47 +0000160/* which of the three times will be used */
James Youngmana4bc10c2010-12-20 01:36:16 +0100161TIME_CHANGE = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100162TIME_ACCESS = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
James Youngmana4bc10c2010-12-20 01:36:16 +0100163TIME_MASK = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000164
Denys Vlasenko982aa262010-12-19 21:54:39 +0100165/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
James Youngmana4bc10c2010-12-20 01:36:16 +0100166SORT_REVERSE = 1 << 23,
Eric Andersencc8ed391999-10-05 16:24:54 +0000167
Denys Vlasenko982aa262010-12-19 21:54:39 +0100168SORT_NAME = 0, /* sort by file name */
James Youngmana4bc10c2010-12-20 01:36:16 +0100169SORT_SIZE = 1 << 24, /* sort by file size */
170SORT_ATIME = 2 << 24, /* sort by last access time */
171SORT_CTIME = 3 << 24, /* sort by last change time */
172SORT_MTIME = 4 << 24, /* sort by last modification time */
173SORT_VERSION = 5 << 24, /* sort by version */
174SORT_EXT = 6 << 24, /* sort by file name extension */
175SORT_DIR = 7 << 24, /* sort by file or directory */
176SORT_MASK = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000177
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000178LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
James Youngmana4bc10c2010-12-20 01:36:16 +0100179 LIST_DATE_TIME | LIST_SYMLINK,
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000180};
Eric Andersencc8ed391999-10-05 16:24:54 +0000181
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100182/* -Cadil1 Std options, busybox always supports */
183/* -gnsxA Std options, busybox always supports */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100184/* -Q GNU option, busybox always supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100185/* -k SELinux option, busybox always supports (ignores if !SELinux) */
186/* Std has -k which means "show sizes in kbytes" */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100187/* -FLHRctur Std options, busybox optionally supports */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100188/* -p Std option, busybox optionally supports */
189/* Not fully compatible - we show not only '/' but other chars too */
190/* -SXvhTw GNU options, busybox optionally supports */
191/* -T TABWIDTH is ignored (we don't use tabs on output) */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100192/* -KZ SELinux mandated options, busybox optionally supports */
193/* (coreutils 8.4 has no -K, remove it?) */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100194/* -e I think we made this one up (looks similar to GNU --full-time) */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100195/* We already used up all 32 bits, if we need to add more, candidates for removal: */
196/* -K, -T, -e (add --full-time instead) */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000197static const char ls_options[] ALIGN1 =
Denys Vlasenko982aa262010-12-19 21:54:39 +0100198 "Cadil1gnsxQAk" /* 13 opts, total 13 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000199 IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
200 IF_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
201 IF_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100202 IF_FEATURE_LS_RECURSIVE("R") /* 1, 24 */
203 IF_SELINUX("KZ") /* 2, 26 */
204 IF_FEATURE_LS_FOLLOWLINKS("LH") /* 2, 28 */
205 IF_FEATURE_HUMAN_READABLE("h") /* 1, 29 */
206 IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 31 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100207 /* with --color, we use all 32 bits */;
Denis Vlasenko248ce912009-03-03 14:09:04 +0000208enum {
209 //OPT_C = (1 << 0),
210 //OPT_a = (1 << 1),
211 //OPT_d = (1 << 2),
212 //OPT_i = (1 << 3),
213 //OPT_l = (1 << 4),
214 //OPT_1 = (1 << 5),
215 OPT_g = (1 << 6),
216 //OPT_n = (1 << 7),
217 //OPT_s = (1 << 8),
218 //OPT_x = (1 << 9),
219 OPT_Q = (1 << 10),
220 //OPT_A = (1 << 11),
221 //OPT_k = (1 << 12),
Denys Vlasenko982aa262010-12-19 21:54:39 +0100222
223 OPTBIT_c = 13,
224 OPTBIT_e,
225 OPTBIT_t,
226 OPTBIT_u,
227 OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS,
228 OPTBIT_X, /* 18 */
229 OPTBIT_r,
230 OPTBIT_v,
231 OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
232 OPTBIT_p, /* 22 */
233 OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
234 OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
235 OPTBIT_Z, /* 25 */
236 OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX,
237 OPTBIT_H, /* 27 */
Denys Vlasenkocd387f22011-02-27 04:10:00 +0100238 OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100239 OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
Denys Vlasenko982aa262010-12-19 21:54:39 +0100240 OPTBIT_w, /* 30 */
241 OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH,
242
243 OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
244 OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS,
245 OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
246 OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
247 OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
248 OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
249 OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
250 OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
251 OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
252 OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
253 OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
254 OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX,
255 OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
256 OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
257 OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
258 OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
259 OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH,
260 OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH,
261 OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR,
Denis Vlasenko248ce912009-03-03 14:09:04 +0000262};
263
Denis Vlasenko248ce912009-03-03 14:09:04 +0000264/* TODO: simple toggles may be stored as OPT_xxx bits instead */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100265static const uint32_t opt_flags[] = {
James Youngmana4bc10c2010-12-20 01:36:16 +0100266 STYLE_COLUMNAR, /* C */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100267 DISP_HIDDEN | DISP_DOT, /* a */
268 DISP_NOLIST, /* d */
269 LIST_INO, /* i */
Denys Vlasenko982aa262010-12-19 21:54:39 +0100270 LIST_LONG | STYLE_LONG, /* l */
James Youngmana4bc10c2010-12-20 01:36:16 +0100271 STYLE_SINGLE, /* 1 */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100272 LIST_LONG | STYLE_LONG, /* g (don't show owner) - handled via OPT_g. assumes l */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100273 LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100274 LIST_BLOCKS, /* s */
James Youngmana4bc10c2010-12-20 01:36:16 +0100275 DISP_ROWS | STYLE_COLUMNAR, /* x */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100276 0, /* Q (quote filename) - handled via OPT_Q */
277 DISP_HIDDEN, /* A */
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100278 ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000279#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100280 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100281 LIST_FULLTIME, /* e */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +0100282 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
283 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000284#endif
285#if ENABLE_FEATURE_LS_SORTFILES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100286 SORT_SIZE, /* S */
287 SORT_EXT, /* X */
288 SORT_REVERSE, /* r */
289 SORT_VERSION, /* v */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000290#endif
291#if ENABLE_FEATURE_LS_FILETYPES
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100292 LIST_FILETYPE | LIST_EXEC, /* F */
293 LIST_FILETYPE, /* p */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000294#endif
Denis Vlasenko248ce912009-03-03 14:09:04 +0000295#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100296 DISP_RECURSIVE, /* R */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000297#endif
Denis Vlasenko248ce912009-03-03 14:09:04 +0000298#if ENABLE_SELINUX
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100299 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */
300 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000301#endif
Denys Vlasenko982aa262010-12-19 21:54:39 +0100302 (1U << 31)
303 /* options after Z are not processed through opt_flags */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000304};
305
306
Eric Andersen11c65522000-09-07 17:24:47 +0000307/*
308 * a directory entry and its stat info are stored here
309 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200310struct dnode {
311 const char *name; /* the dir entry name */
312 const char *fullname; /* the dir entry name */
313 struct dnode *next; /* point at the next node */
314 smallint fname_allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000315 struct stat dstat; /* the file stat info */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000316 IF_SELINUX(security_context_t sid;)
Eric Andersen11c65522000-09-07 17:24:47 +0000317};
Eric Andersen11c65522000-09-07 17:24:47 +0000318
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000319struct globals {
320#if ENABLE_FEATURE_LS_COLOR
321 smallint show_color;
322#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000323 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000324 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000325#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000326 unsigned terminal_width; // = TERMINAL_WIDTH;
327#endif
328#if ENABLE_FEATURE_LS_TIMESTAMPS
329 /* Do time() just once. Saves one syscall per file for "ls -l" */
330 time_t current_time_t;
331#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100332} FIX_ALIASING;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000333#define G (*(struct globals*)&bb_common_bufsiz1)
334#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200335# define show_color (G.show_color )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000336#else
337enum { show_color = 0 };
338#endif
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200339#define exit_code (G.exit_code )
340#define all_fmt (G.all_fmt )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000341#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200342# define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000343#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000344enum {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000345 terminal_width = TERMINAL_WIDTH,
346};
Eric Andersen11c65522000-09-07 17:24:47 +0000347#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000348#define current_time_t (G.current_time_t)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000349#define INIT_G() do { \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200350 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000351 memset(&G, 0, sizeof(G)); \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000352 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
353 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000354} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000355
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000356
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000357static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000358{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000359 struct stat dstat;
360 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000361 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000362
Denys Vlasenko982aa262010-12-19 21:54:39 +0100363 if ((option_mask32 & OPT_L) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000364#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000365 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000366 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000367 }
Eric Andersen9e480452003-07-03 10:07:04 +0000368#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000369 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000370 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000371 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000372 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000373 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000374 } else {
375#if ENABLE_SELINUX
376 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000377 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000378 }
Eric Andersen9e480452003-07-03 10:07:04 +0000379#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000380 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000381 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000382 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000383 return 0;
384 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000385 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000386
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200387 cur = xmalloc(sizeof(*cur));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000388 cur->fullname = fullname;
389 cur->name = name;
390 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000391 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000392 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000393}
394
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000395/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
396 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
397 * 3/7:multiplexed char/block device)
398 * and we use 0 for unknown and 15 for executables (see below) */
399#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
400#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
401#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
402/* 036 black foreground 050 black background
403 037 red foreground 051 red background
404 040 green foreground 052 green background
405 041 brown foreground 053 brown background
406 042 blue foreground 054 blue background
407 043 magenta (purple) foreground 055 magenta background
408 044 cyan (light blue) foreground 056 cyan background
409 045 gray foreground 057 white background
410*/
411#define COLOR(mode) ( \
412 /*un fi chr dir blk file link sock exe */ \
413 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
414 [TYPEINDEX(mode)])
415/* Select normal (0) [actually "reset all"] or bold (1)
416 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
417 * let's use 7 for "impossible" types, just for fun)
418 * Note: coreutils 6.9 uses inverted red for setuid binaries.
419 */
420#define ATTR(mode) ( \
421 /*un fi chr dir blk file link sock exe */ \
422 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
423 [TYPEINDEX(mode)])
424
Denis Vlasenko5c759602006-10-28 12:37:16 +0000425#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000426/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000427static char fgcolor(mode_t mode)
428{
Rob Landley9947a242006-06-15 22:11:10 +0000429 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000430 return COLOR(0xF000); /* File is executable ... */
431 return COLOR(mode);
432}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000433static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000434{
Rob Landley9947a242006-06-15 22:11:10 +0000435 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000436 return ATTR(0xF000); /* File is executable ... */
437 return ATTR(mode);
438}
439#endif
440
Denis Vlasenko5c759602006-10-28 12:37:16 +0000441#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000442static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000443{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000444 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000445 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000446 if (S_ISDIR(mode))
447 return '/';
448 if (!(all_fmt & LIST_EXEC))
449 return '\0';
450 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000451 return '*';
452 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000453}
454#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000455
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200456static unsigned count_dirs(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000457{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200458 unsigned dirs, all;
Eric Andersen11c65522000-09-07 17:24:47 +0000459
Denis Vlasenko5c759602006-10-28 12:37:16 +0000460 if (!dn)
461 return 0;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200462
463 dirs = all = 0;
464 for (; *dn; dn++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000465 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200466
467 all++;
468 if (!S_ISDIR((*dn)->dstat.st_mode))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000469 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200470 name = (*dn)->name;
471 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
472 /* or if it's not . or .. */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200473 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000474 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000475 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000476 }
Eric Andersen11c65522000-09-07 17:24:47 +0000477 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200478 return which != SPLIT_FILE ? dirs : all - dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000479}
480
Eric Andersen11c65522000-09-07 17:24:47 +0000481/* get memory to hold an array of pointers */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200482static struct dnode **dnalloc(unsigned num)
Eric Andersen11c65522000-09-07 17:24:47 +0000483{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000484 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000485 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000486
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200487 num++; /* so that we have terminating NULL */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000488 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000489}
490
Denis Vlasenko5c759602006-10-28 12:37:16 +0000491#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200492static void dfree(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000493{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200494 unsigned i;
Eric Andersen11c65522000-09-07 17:24:47 +0000495
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000496 if (dnp == NULL)
497 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000498
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200499 for (i = 0; dnp[i]; i++) {
Rob Landley26314862006-05-02 19:46:52 +0000500 struct dnode *cur = dnp[i];
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200501 if (cur->fname_allocated)
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200502 free((char*)cur->fullname);
503 free(cur);
Eric Andersen11c65522000-09-07 17:24:47 +0000504 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200505 free(dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000506}
Rob Landleyc44bc982006-05-28 01:19:06 +0000507#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000508#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000509#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000510
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200511/* Returns NULL-terminated malloced vector of pointers (or NULL) */
512static struct dnode **splitdnarray(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000513{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200514 unsigned dncnt, d;
Eric Andersen11c65522000-09-07 17:24:47 +0000515 struct dnode **dnp;
516
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200517 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000518 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000519
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200520 /* count how many dirs or files there are */
521 dncnt = count_dirs(dn, which);
Eric Andersen11c65522000-09-07 17:24:47 +0000522
523 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000524 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000525
526 /* copy the entrys into the file or dir array */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200527 for (d = 0; *dn; dn++) {
528 if (S_ISDIR((*dn)->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000529 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200530
Denis Vlasenko5c759602006-10-28 12:37:16 +0000531 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
532 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200533 name = (*dn)->name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000534 if ((which & SPLIT_DIR)
535 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
536 ) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200537 dnp[d++] = *dn;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000538 }
539 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200540 dnp[d++] = *dn;
Eric Andersen11c65522000-09-07 17:24:47 +0000541 }
542 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000543 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000544}
545
Denis Vlasenko5c759602006-10-28 12:37:16 +0000546#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000547static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000548{
Rob Landley425e7582006-05-03 20:22:03 +0000549 struct dnode *d1 = *(struct dnode **)a;
550 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000551 unsigned sort_opts = all_fmt & SORT_MASK;
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100552 off_t dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000553
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000554 dif = 0; /* assume SORT_NAME */
555 // TODO: use pre-initialized function pointer
556 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000557 if (sort_opts == SORT_SIZE) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100558 dif = (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000559 } else if (sort_opts == SORT_ATIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100560 dif = (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000561 } else if (sort_opts == SORT_CTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100562 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000563 } else if (sort_opts == SORT_MTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100564 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000565 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000566 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000567 /* } else if (sort_opts == SORT_VERSION) { */
568 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000569 }
Eric Andersen11c65522000-09-07 17:24:47 +0000570 if (dif == 0) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100571 /* sort by name, or tie_breaker for other sorts */
572 if (ENABLE_LOCALE_SUPPORT)
573 dif = strcoll(d1->name, d2->name);
574 else
575 dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000576 }
577
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100578 /* Make dif fit into an int */
579 if (sizeof(dif) > sizeof(int)) {
Denys Vlasenko6b01b712010-01-24 22:52:21 +0100580 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
581 /* shift leaving only "int" worth of bits */
582 if (dif != 0) {
583 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100584 }
Eric Andersen11c65522000-09-07 17:24:47 +0000585 }
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100586
587 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000588}
589
Rob Landley425e7582006-05-03 20:22:03 +0000590static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000591{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000592 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000593}
Rob Landleyea224be2006-06-18 20:20:07 +0000594#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000595#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000596#endif
597
Rob Landleyea224be2006-06-18 20:20:07 +0000598
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100599static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000600{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100601 unsigned len;
602 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000603
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100604 // TODO: quote tab as \t, etc, if -Q
605 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000606
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100607 if (!(option_mask32 & OPT_Q)) {
608 return uni_stat.unicode_width;
609 }
610
611 len = 2 + uni_stat.unicode_width;
612 while (*name) {
613 if (*name == '"' || *name == '\\') {
614 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000615 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100616 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000617 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100618 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000619}
620
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000621
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100622/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100623 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100624 * STYLE_SINGLE and STYLE_LONG don't care.
625 * coreutils 7.2 also supports:
626 * ls -b (--escape) = octal escapes (although it doesn't look like working)
627 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200628 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100629static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200630{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100631 unsigned len;
632 uni_stat_t uni_stat;
633
634 // TODO: quote tab as \t, etc, if -Q
635 name = printable_string(&uni_stat, name);
636
637 if (!(option_mask32 & OPT_Q)) {
638 fputs(name, stdout);
639 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200640 }
641
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100642 len = 2 + uni_stat.unicode_width;
643 putchar('"');
644 while (*name) {
645 if (*name == '"' || *name == '\\') {
646 putchar('\\');
647 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000648 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700649 putchar(*name);
650 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000651 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100652 putchar('"');
653 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000654}
655
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100656/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100657 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100658 * STYLE_SINGLE and STYLE_LONG don't care.
659 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200660static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000661{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200662 unsigned column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000663 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000664#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000665 struct stat info;
666 char append;
667#endif
668
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200669 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000670 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000671 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200672 */
Eric Andersen11c65522000-09-07 17:24:47 +0000673
Denis Vlasenko5c759602006-10-28 12:37:16 +0000674#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000675 append = append_char(dn->dstat.st_mode);
676#endif
677
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000678 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100679 * does not appear *inside* the "ls -l" line */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000680 if (all_fmt & LIST_SYMLINK)
681 if (S_ISLNK(dn->dstat.st_mode))
682 lpath = xmalloc_readlink_or_warn(dn->fullname);
683
684 if (all_fmt & LIST_INO)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100685 column += printf("%7llu ", (long long) dn->dstat.st_ino);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000686 if (all_fmt & LIST_BLOCKS)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100687 column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000688 if (all_fmt & LIST_MODEBITS)
689 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
690 if (all_fmt & LIST_NLINKS)
691 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100692 if (all_fmt & LIST_ID_NUMERIC) {
693 if (option_mask32 & OPT_g)
694 column += printf("%-8u ", (int) dn->dstat.st_gid);
695 else
696 column += printf("%-8u %-8u ",
697 (int) dn->dstat.st_uid,
698 (int) dn->dstat.st_gid);
699 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000700#if ENABLE_FEATURE_LS_USERNAME
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100701 else if (all_fmt & LIST_ID_NAME) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000702 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100703 column += printf("%-8.8s ",
Denys Vlasenko4909fec2010-11-06 00:46:57 +0100704 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000705 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100706 column += printf("%-8.8s %-8.8s ",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000707 get_cached_username(dn->dstat.st_uid),
708 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000709 }
710 }
Eric Andersen11c65522000-09-07 17:24:47 +0000711#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100712 if (all_fmt & LIST_SIZE) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000713 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
714 column += printf("%4u, %3u ",
715 (int) major(dn->dstat.st_rdev),
716 (int) minor(dn->dstat.st_rdev));
717 } else {
Denys Vlasenko982aa262010-12-19 21:54:39 +0100718 if (option_mask32 & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100719 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200720 /* print st_size, show one fractional, use suffixes */
721 make_human_readable_str(dn->dstat.st_size, 1, 0)
722 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000723 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000724 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000725 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000726 }
727 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000728#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100729 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
730 char *filetime;
731 time_t ttime = dn->dstat.st_mtime;
732 if (all_fmt & TIME_ACCESS)
733 ttime = dn->dstat.st_atime;
734 if (all_fmt & TIME_CHANGE)
735 ttime = dn->dstat.st_ctime;
736 filetime = ctime(&ttime);
737 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100738 if (all_fmt & LIST_FULLTIME) { /* -e */
739 /* Note: coreutils 8.4 ls --full-time prints:
740 * 2009-07-13 17:49:27.000000000 +0200
741 */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100742 column += printf("%.24s ", filetime);
Denys Vlasenko26d11b82011-02-28 12:38:08 +0100743 } else { /* LIST_DATE_TIME */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000744 /* current_time_t ~== time(NULL) */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100745 time_t age = current_time_t - ttime;
746 printf("%.6s ", filetime + 4); /* "Jun 30" */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000747 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
748 /* hh:mm if less than 6 months old */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100749 printf("%.5s ", filetime + 11);
750 } else { /* year. buggy if year > 9999 ;) */
751 printf(" %.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000752 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000753 column += 13;
754 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100755 }
Eric Andersen11c65522000-09-07 17:24:47 +0000756#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000757#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000758 if (all_fmt & LIST_CONTEXT) {
759 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
760 freecon(dn->sid);
761 }
Eric Andersen9e480452003-07-03 10:07:04 +0000762#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100763
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000764#if ENABLE_FEATURE_LS_COLOR
James Youngmana4bc10c2010-12-20 01:36:16 +0100765 if (show_color) {
766 info.st_mode = 0; /* for fgcolor() */
767 lstat(dn->fullname, &info);
768 printf("\033[%u;%um", bold(info.st_mode),
769 fgcolor(info.st_mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000770 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100771#endif
772 column += print_name(dn->name);
773 if (show_color) {
774 printf("\033[0m");
775 }
776
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000777 if (all_fmt & LIST_SYMLINK) {
778 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
779 printf(" -> ");
780#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
781#if ENABLE_FEATURE_LS_COLOR
782 info.st_mode = 0; /* for fgcolor() */
783#endif
784 if (stat(dn->fullname, &info) == 0) {
785 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000786 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000787#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000788#if ENABLE_FEATURE_LS_COLOR
789 if (show_color) {
790 printf("\033[%u;%um", bold(info.st_mode),
791 fgcolor(info.st_mode));
792 }
793#endif
794 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000795 if (show_color) {
796 printf("\033[0m");
797 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000798 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000799 }
800 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000801#if ENABLE_FEATURE_LS_FILETYPES
802 if (all_fmt & LIST_FILETYPE) {
803 if (append) {
804 putchar(append);
805 column++;
806 }
807 }
808#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000809
Glenn L McGrath4d001292003-01-06 01:11:50 +0000810 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000811}
812
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100813static void showfiles(struct dnode **dn, unsigned nfiles)
814{
815 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100816 unsigned column;
817 unsigned nexttab;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100818 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100819
820 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
821 ncols = 1;
822 } else {
823 /* find the longest file name, use that as the column width */
824 for (i = 0; dn[i]; i++) {
825 int len = calc_name_len(dn[i]->name);
826 if (column_width < len)
827 column_width = len;
828 }
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100829 column_width += 1 +
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100830 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
831 ((all_fmt & LIST_INO) ? 8 : 0) +
832 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
833 ncols = (int) (terminal_width / column_width);
834 }
835
836 if (ncols > 1) {
837 nrows = nfiles / ncols;
838 if (nrows * ncols < nfiles)
839 nrows++; /* round up fractionals */
840 } else {
841 nrows = nfiles;
842 ncols = 1;
843 }
844
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100845 column = 0;
846 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100847 for (row = 0; row < nrows; row++) {
848 for (nc = 0; nc < ncols; nc++) {
849 /* reach into the array based on the column and row */
850 if (all_fmt & DISP_ROWS)
851 i = (row * ncols) + nc; /* display across row */
852 else
853 i = (nc * nrows) + row; /* display by column */
854 if (i < nfiles) {
855 if (column > 0) {
856 nexttab -= column;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100857 printf("%*s ", nexttab, "");
858 column += nexttab + 1;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100859 }
860 nexttab = column + column_width;
861 column += list_single(dn[i]);
862 }
863 }
864 putchar('\n');
865 column = 0;
866 }
867}
868
869
870#if ENABLE_DESKTOP
871/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
872 * If any of the -l, -n, -s options is specified, each list
873 * of files within the directory shall be preceded by a
874 * status line indicating the number of file system blocks
875 * occupied by files in the directory in 512-byte units if
876 * the -k option is not specified, or 1024-byte units if the
877 * -k option is specified, rounded up to the next integral
878 * number of units.
879 */
880/* by Jorgen Overgaard (jorgen AT antistaten.se) */
881static off_t calculate_blocks(struct dnode **dn)
882{
883 uoff_t blocks = 1;
884 if (dn) {
885 while (*dn) {
886 /* st_blocks is in 512 byte blocks */
887 blocks += (*dn)->dstat.st_blocks;
888 dn++;
889 }
890 }
891
892 /* Even though standard says use 512 byte blocks, coreutils use 1k */
893 /* Actually, we round up by calculating (blocks + 1) / 2,
894 * "+ 1" was done when we initialized blocks to 1 */
895 return blocks >> 1;
896}
897#endif
898
899
900static struct dnode **list_dir(const char *, unsigned *);
901
902static void showdirs(struct dnode **dn, int first)
903{
904 unsigned nfiles;
905 unsigned dndirs;
906 struct dnode **subdnp;
907 struct dnode **dnd;
908
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100909 for (; *dn; dn++) {
910 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
911 if (!first)
912 bb_putchar('\n');
913 first = 0;
914 printf("%s:\n", (*dn)->fullname);
915 }
916 subdnp = list_dir((*dn)->fullname, &nfiles);
917#if ENABLE_DESKTOP
918 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
919 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
920#endif
921 if (nfiles > 0) {
922 /* list all files at this level */
923 dnsort(subdnp, nfiles);
924 showfiles(subdnp, nfiles);
925 if (ENABLE_FEATURE_LS_RECURSIVE
926 && (all_fmt & DISP_RECURSIVE)
927 ) {
928 /* recursive - list the sub-dirs */
929 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
930 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
931 if (dndirs > 0) {
932 dnsort(dnd, dndirs);
933 showdirs(dnd, 0);
934 /* free the array of dnode pointers to the dirs */
935 free(dnd);
936 }
937 }
938 /* free the dnodes and the fullname mem */
939 dfree(subdnp);
940 }
941 }
942}
943
944
945/* Returns NULL-terminated malloced vector of pointers (or NULL) */
946static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
947{
948 struct dnode *dn, *cur, **dnp;
949 struct dirent *entry;
950 DIR *dir;
951 unsigned i, nfiles;
952
953 /* Never happens:
954 if (path == NULL)
955 return NULL;
956 */
957
958 *nfiles_p = 0;
959 dir = warn_opendir(path);
960 if (dir == NULL) {
961 exit_code = EXIT_FAILURE;
962 return NULL; /* could not open the dir */
963 }
964 dn = NULL;
965 nfiles = 0;
966 while ((entry = readdir(dir)) != NULL) {
967 char *fullname;
968
969 /* are we going to list the file- it may be . or .. or a hidden file */
970 if (entry->d_name[0] == '.') {
971 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
972 && !(all_fmt & DISP_DOT)
973 ) {
974 continue;
975 }
976 if (!(all_fmt & DISP_HIDDEN))
977 continue;
978 }
979 fullname = concat_path_file(path, entry->d_name);
980 cur = my_stat(fullname, bb_basename(fullname), 0);
981 if (!cur) {
982 free(fullname);
983 continue;
984 }
985 cur->fname_allocated = 1;
986 cur->next = dn;
987 dn = cur;
988 nfiles++;
989 }
990 closedir(dir);
991
992 if (dn == NULL)
993 return NULL;
994
995 /* now that we know how many files there are
996 * allocate memory for an array to hold dnode pointers
997 */
998 *nfiles_p = nfiles;
999 dnp = dnalloc(nfiles);
1000 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1001 dnp[i] = dn; /* save pointer to node in array */
1002 dn = dn->next;
1003 if (!dn)
1004 break;
1005 }
1006
1007 return dnp;
1008}
1009
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001010
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001011int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +00001012{
Glenn L McGrath792cae52004-01-18 05:15:16 +00001013 struct dnode **dnd;
1014 struct dnode **dnf;
1015 struct dnode **dnp;
1016 struct dnode *dn;
1017 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001018 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001019 unsigned nfiles;
1020 unsigned dnfiles;
1021 unsigned dndirs;
1022 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001023#if ENABLE_FEATURE_LS_COLOR
1024 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1025 /* coreutils 6.10:
1026 * # ls --color=BOGUS
1027 * ls: invalid argument 'BOGUS' for '--color'
1028 * Valid arguments are:
1029 * 'always', 'yes', 'force'
1030 * 'never', 'no', 'none'
1031 * 'auto', 'tty', 'if-tty'
1032 * (and substrings: "--color=alwa" work too)
1033 */
1034 static const char ls_longopts[] ALIGN1 =
1035 "color\0" Optional_argument "\xff"; /* no short equivalent */
1036 static const char color_str[] ALIGN1 =
1037 "always\0""yes\0""force\0"
1038 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001039 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001040 const char *color_opt = color_str; /* "always" */
1041#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001042
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001043 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001044
Denys Vlasenko28055022010-01-04 20:49:58 +01001045 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001046
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001047 if (ENABLE_FEATURE_LS_SORTFILES)
1048 all_fmt = SORT_NAME;
Glenn L McGrath792cae52004-01-18 05:15:16 +00001049
Denis Vlasenko5c759602006-10-28 12:37:16 +00001050#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001051 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +00001052 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001053 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +00001054 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001055#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001056
Eric Andersen11c65522000-09-07 17:24:47 +00001057 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001058 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001059 opt_complementary =
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001060 /* -e implies -l */
1061 "el"
Denys Vlasenkof3137462010-12-19 05:05:34 +01001062 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1063 * in some pairs of opts, only last one takes effect:
1064 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001065 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001066 // ":m-l:l-m" - we don't have -m
1067 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001068 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1069 ":C-1:1-C" /* bycols/oneline */
1070 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1071 ":c-u:u-c" /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001072 /* -w NUM: */
1073 IF_FEATURE_AUTOWIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001074 opt = getopt32(argv, ls_options
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +01001075 IF_FEATURE_AUTOWIDTH(, NULL, &terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001076 IF_FEATURE_LS_COLOR(, &color_opt)
1077 );
Denys Vlasenko982aa262010-12-19 21:54:39 +01001078 for (i = 0; opt_flags[i] != (1U << 31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001079 if (opt & (1 << i)) {
Denys Vlasenko982aa262010-12-19 21:54:39 +01001080 uint32_t flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001081
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001082 if (flags & STYLE_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001083 all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001084 if (flags & SORT_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001085 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001086 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001087 all_fmt &= ~TIME_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001088
Manuel Novoa III cad53642003-03-19 09:13:01 +00001089 all_fmt |= flags;
1090 }
Eric Andersen11c65522000-09-07 17:24:47 +00001091 }
1092
Denis Vlasenko5c759602006-10-28 12:37:16 +00001093#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko26d11b82011-02-28 12:38:08 +01001094 /* set show_color = 1/0 */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001095 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1096 char *p = getenv("LS_COLORS");
1097 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001098 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001099 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001100 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001101 if (opt & OPT_color) {
1102 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +00001103 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001104 else switch (index_in_substrings(color_str, color_opt)) {
1105 case 3:
1106 case 4:
1107 case 5:
1108 if (isatty(STDOUT_FILENO)) {
1109 case 0:
1110 case 1:
1111 case 2:
1112 show_color = 1;
1113 }
1114 }
Paul Fox156dc412005-08-01 19:33:30 +00001115 }
1116#endif
1117
Eric Andersen11c65522000-09-07 17:24:47 +00001118 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001119 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001120 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001121 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1122 if (all_fmt & TIME_CHANGE)
1123 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1124 if (all_fmt & TIME_ACCESS)
1125 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1126 }
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001127 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001128 all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001129
Denys Vlasenkof3137462010-12-19 05:05:34 +01001130 /* choose a display format if one was not already specified by an option */
Rob Landleyea224be2006-06-18 20:20:07 +00001131 if (!(all_fmt & STYLE_MASK))
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001132 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001133
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001134 argv += optind;
1135 if (!argv[0])
1136 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001137
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001138 if (argv[1])
1139 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001140
Denis Vlasenko5c759602006-10-28 12:37:16 +00001141 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001142 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001143 nfiles = 0;
1144 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001145 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001146 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001147 !((all_fmt & (STYLE_LONG|LIST_BLOCKS)) || (option_mask32 & OPT_F))
Denys Vlasenko982aa262010-12-19 21:54:39 +01001148 /* ... or if -H: */
1149 || (option_mask32 & OPT_H)
Denys Vlasenko163d8642010-12-19 06:16:28 +01001150 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001151 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001152 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001153 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001154 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001155 cur->next = dn;
1156 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001157 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001158 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001159
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001160 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1161 if (nfiles == 0)
1162 return exit_code;
1163
Eric Andersen11c65522000-09-07 17:24:47 +00001164 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001165 * allocate memory for an array to hold dnode pointers
1166 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001167 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001168 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1169 dnp[i] = dn; /* save pointer to node in array */
1170 dn = dn->next;
1171 if (!dn)
1172 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001173 }
1174
Manuel Novoa III cad53642003-03-19 09:13:01 +00001175 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001176 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001177 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001178 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001179 dnd = splitdnarray(dnp, SPLIT_DIR);
1180 dnf = splitdnarray(dnp, SPLIT_FILE);
1181 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001182 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001183 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001184 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001185 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001186 if (ENABLE_FEATURE_CLEAN_UP)
1187 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001188 }
1189 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001190 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001191 showdirs(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001192 if (ENABLE_FEATURE_CLEAN_UP)
1193 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001194 }
1195 }
Rob Landley26314862006-05-02 19:46:52 +00001196 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001197 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001198 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001199}