blob: 8d25b5d5fe5c7ad8797c6fe03ad4ebb032b43305 [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) */
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 */
Denis Vlasenko248ce912009-03-03 14:09:04 +0000205 ;
206enum {
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[] = {
James Youngmana4bc10c2010-12-20 01:36:16 +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 Vlasenkod87815d2010-12-19 05:43:44 +0100270 0, /* g (don't show owner) - handled via OPT_g */
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 Vlasenkod87815d2010-12-19 05:43:44 +0100290 LIST_FILETYPE | LIST_EXEC, /* F */
291 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/*
306 * a directory entry and its stat info are stored here
307 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200308struct dnode {
309 const char *name; /* the dir entry name */
310 const char *fullname; /* the dir entry name */
311 struct dnode *next; /* point at the next node */
312 smallint fname_allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000313 struct stat dstat; /* the file stat info */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000314 IF_SELINUX(security_context_t sid;)
Eric Andersen11c65522000-09-07 17:24:47 +0000315};
Eric Andersen11c65522000-09-07 17:24:47 +0000316
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000317struct globals {
318#if ENABLE_FEATURE_LS_COLOR
319 smallint show_color;
320#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000321 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000322 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000323#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000324 unsigned terminal_width; // = TERMINAL_WIDTH;
325#endif
326#if ENABLE_FEATURE_LS_TIMESTAMPS
327 /* Do time() just once. Saves one syscall per file for "ls -l" */
328 time_t current_time_t;
329#endif
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100330} FIX_ALIASING;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000331#define G (*(struct globals*)&bb_common_bufsiz1)
332#if ENABLE_FEATURE_LS_COLOR
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200333# define show_color (G.show_color )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000334#else
335enum { show_color = 0 };
336#endif
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200337#define exit_code (G.exit_code )
338#define all_fmt (G.all_fmt )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000339#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200340# define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000341#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000342enum {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000343 terminal_width = TERMINAL_WIDTH,
344};
Eric Andersen11c65522000-09-07 17:24:47 +0000345#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000346#define current_time_t (G.current_time_t)
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000347#define INIT_G() do { \
Denys Vlasenko1b34d4f2009-09-30 02:39:57 +0200348 /* we have to zero it out because of NOEXEC */ \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000349 memset(&G, 0, sizeof(G)); \
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000350 IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
351 IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000352} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000353
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000354
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000355static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000356{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000357 struct stat dstat;
358 struct dnode *cur;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000359 IF_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000360
Denys Vlasenko982aa262010-12-19 21:54:39 +0100361 if ((option_mask32 & OPT_L) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000362#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000363 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000364 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000365 }
Eric Andersen9e480452003-07-03 10:07:04 +0000366#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000367 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000368 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000369 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000370 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000371 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000372 } else {
373#if ENABLE_SELINUX
374 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000375 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000376 }
Eric Andersen9e480452003-07-03 10:07:04 +0000377#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000378 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000379 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000380 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000381 return 0;
382 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000383 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000384
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200385 cur = xmalloc(sizeof(*cur));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000386 cur->fullname = fullname;
387 cur->name = name;
388 cur->dstat = dstat;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000389 IF_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000390 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000391}
392
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000393/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
394 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
395 * 3/7:multiplexed char/block device)
396 * and we use 0 for unknown and 15 for executables (see below) */
397#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
398#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
399#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
400/* 036 black foreground 050 black background
401 037 red foreground 051 red background
402 040 green foreground 052 green background
403 041 brown foreground 053 brown background
404 042 blue foreground 054 blue background
405 043 magenta (purple) foreground 055 magenta background
406 044 cyan (light blue) foreground 056 cyan background
407 045 gray foreground 057 white background
408*/
409#define COLOR(mode) ( \
410 /*un fi chr dir blk file link sock exe */ \
411 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
412 [TYPEINDEX(mode)])
413/* Select normal (0) [actually "reset all"] or bold (1)
414 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
415 * let's use 7 for "impossible" types, just for fun)
416 * Note: coreutils 6.9 uses inverted red for setuid binaries.
417 */
418#define ATTR(mode) ( \
419 /*un fi chr dir blk file link sock exe */ \
420 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
421 [TYPEINDEX(mode)])
422
Denis Vlasenko5c759602006-10-28 12:37:16 +0000423#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000424/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000425static char fgcolor(mode_t mode)
426{
Rob Landley9947a242006-06-15 22:11:10 +0000427 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000428 return COLOR(0xF000); /* File is executable ... */
429 return COLOR(mode);
430}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000431static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000432{
Rob Landley9947a242006-06-15 22:11:10 +0000433 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000434 return ATTR(0xF000); /* File is executable ... */
435 return ATTR(mode);
436}
437#endif
438
Denis Vlasenko5c759602006-10-28 12:37:16 +0000439#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000440static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000441{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000442 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000443 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000444 if (S_ISDIR(mode))
445 return '/';
446 if (!(all_fmt & LIST_EXEC))
447 return '\0';
448 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000449 return '*';
450 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000451}
452#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000453
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200454static unsigned count_dirs(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000455{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200456 unsigned dirs, all;
Eric Andersen11c65522000-09-07 17:24:47 +0000457
Denis Vlasenko5c759602006-10-28 12:37:16 +0000458 if (!dn)
459 return 0;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200460
461 dirs = all = 0;
462 for (; *dn; dn++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000463 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200464
465 all++;
466 if (!S_ISDIR((*dn)->dstat.st_mode))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000467 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200468 name = (*dn)->name;
469 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
470 /* or if it's not . or .. */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200471 || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000472 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000473 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000474 }
Eric Andersen11c65522000-09-07 17:24:47 +0000475 }
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200476 return which != SPLIT_FILE ? dirs : all - dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000477}
478
Eric Andersen11c65522000-09-07 17:24:47 +0000479/* get memory to hold an array of pointers */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200480static struct dnode **dnalloc(unsigned num)
Eric Andersen11c65522000-09-07 17:24:47 +0000481{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000482 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000483 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000484
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200485 num++; /* so that we have terminating NULL */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000486 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000487}
488
Denis Vlasenko5c759602006-10-28 12:37:16 +0000489#if ENABLE_FEATURE_LS_RECURSIVE
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200490static void dfree(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000491{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200492 unsigned i;
Eric Andersen11c65522000-09-07 17:24:47 +0000493
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000494 if (dnp == NULL)
495 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000496
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200497 for (i = 0; dnp[i]; i++) {
Rob Landley26314862006-05-02 19:46:52 +0000498 struct dnode *cur = dnp[i];
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200499 if (cur->fname_allocated)
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200500 free((char*)cur->fullname);
501 free(cur);
Eric Andersen11c65522000-09-07 17:24:47 +0000502 }
Denys Vlasenkoffd47742009-10-03 11:42:33 +0200503 free(dnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000504}
Rob Landleyc44bc982006-05-28 01:19:06 +0000505#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000506#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000507#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000508
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200509/* Returns NULL-terminated malloced vector of pointers (or NULL) */
510static struct dnode **splitdnarray(struct dnode **dn, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000511{
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200512 unsigned dncnt, d;
Eric Andersen11c65522000-09-07 17:24:47 +0000513 struct dnode **dnp;
514
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200515 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000516 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000517
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200518 /* count how many dirs or files there are */
519 dncnt = count_dirs(dn, which);
Eric Andersen11c65522000-09-07 17:24:47 +0000520
521 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000522 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000523
524 /* copy the entrys into the file or dir array */
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200525 for (d = 0; *dn; dn++) {
526 if (S_ISDIR((*dn)->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000527 const char *name;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200528
Denis Vlasenko5c759602006-10-28 12:37:16 +0000529 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
530 continue;
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200531 name = (*dn)->name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000532 if ((which & SPLIT_DIR)
533 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
534 ) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200535 dnp[d++] = *dn;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000536 }
537 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
Denys Vlasenkocae409c2009-10-03 11:43:48 +0200538 dnp[d++] = *dn;
Eric Andersen11c65522000-09-07 17:24:47 +0000539 }
540 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000541 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000542}
543
Denis Vlasenko5c759602006-10-28 12:37:16 +0000544#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000545static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000546{
Rob Landley425e7582006-05-03 20:22:03 +0000547 struct dnode *d1 = *(struct dnode **)a;
548 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000549 unsigned sort_opts = all_fmt & SORT_MASK;
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100550 off_t dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000551
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000552 dif = 0; /* assume SORT_NAME */
553 // TODO: use pre-initialized function pointer
554 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000555 if (sort_opts == SORT_SIZE) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100556 dif = (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000557 } else if (sort_opts == SORT_ATIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100558 dif = (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000559 } else if (sort_opts == SORT_CTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100560 dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000561 } else if (sort_opts == SORT_MTIME) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100562 dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000563 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000564 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000565 /* } else if (sort_opts == SORT_VERSION) { */
566 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000567 }
Eric Andersen11c65522000-09-07 17:24:47 +0000568 if (dif == 0) {
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100569 /* sort by name, or tie_breaker for other sorts */
570 if (ENABLE_LOCALE_SUPPORT)
571 dif = strcoll(d1->name, d2->name);
572 else
573 dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000574 }
575
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100576 /* Make dif fit into an int */
577 if (sizeof(dif) > sizeof(int)) {
Denys Vlasenko6b01b712010-01-24 22:52:21 +0100578 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
579 /* shift leaving only "int" worth of bits */
580 if (dif != 0) {
581 dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100582 }
Eric Andersen11c65522000-09-07 17:24:47 +0000583 }
Denys Vlasenko7be97c52010-01-18 13:02:27 +0100584
585 return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000586}
587
Rob Landley425e7582006-05-03 20:22:03 +0000588static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000589{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000590 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000591}
Rob Landleyea224be2006-06-18 20:20:07 +0000592#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000593#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000594#endif
595
Rob Landleyea224be2006-06-18 20:20:07 +0000596
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100597static unsigned calc_name_len(const char *name)
Eric Andersen11c65522000-09-07 17:24:47 +0000598{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100599 unsigned len;
600 uni_stat_t uni_stat;
Eric Andersen11c65522000-09-07 17:24:47 +0000601
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100602 // TODO: quote tab as \t, etc, if -Q
603 name = printable_string(&uni_stat, name);
Eric Andersen11c65522000-09-07 17:24:47 +0000604
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100605 if (!(option_mask32 & OPT_Q)) {
606 return uni_stat.unicode_width;
607 }
608
609 len = 2 + uni_stat.unicode_width;
610 while (*name) {
611 if (*name == '"' || *name == '\\') {
612 len++;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000613 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100614 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000615 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100616 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000617}
618
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000619
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100620/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100621 * Note that only STYLE_COLUMNAR uses return value.
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100622 * STYLE_SINGLE and STYLE_LONG don't care.
623 * coreutils 7.2 also supports:
624 * ls -b (--escape) = octal escapes (although it doesn't look like working)
625 * ls -N (--literal) = not escape at all
Denys Vlasenko0683d4d2009-10-03 10:53:36 +0200626 */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100627static unsigned print_name(const char *name)
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200628{
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100629 unsigned len;
630 uni_stat_t uni_stat;
631
632 // TODO: quote tab as \t, etc, if -Q
633 name = printable_string(&uni_stat, name);
634
635 if (!(option_mask32 & OPT_Q)) {
636 fputs(name, stdout);
637 return uni_stat.unicode_width;
Denys Vlasenko87c150c2009-10-03 01:14:15 +0200638 }
639
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100640 len = 2 + uni_stat.unicode_width;
641 putchar('"');
642 while (*name) {
643 if (*name == '"' || *name == '\\') {
644 putchar('\\');
645 len++;
Eric Andersen11c65522000-09-07 17:24:47 +0000646 }
Dan Fandrich77d48722010-09-07 23:38:28 -0700647 putchar(*name);
648 name++;
Eric Andersen11c65522000-09-07 17:24:47 +0000649 }
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100650 putchar('"');
651 return len;
Eric Andersen11c65522000-09-07 17:24:47 +0000652}
653
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100654/* Return the number of used columns.
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100655 * Note that only STYLE_COLUMNAR uses return value,
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100656 * STYLE_SINGLE and STYLE_LONG don't care.
657 */
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200658static NOINLINE unsigned list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000659{
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200660 unsigned column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000661 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000662#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000663 struct stat info;
664 char append;
665#endif
666
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200667 /* Never happens:
Glenn L McGrath4d001292003-01-06 01:11:50 +0000668 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000669 return 0;
Denys Vlasenko76c7d952009-10-03 01:15:47 +0200670 */
Eric Andersen11c65522000-09-07 17:24:47 +0000671
Denis Vlasenko5c759602006-10-28 12:37:16 +0000672#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000673 append = append_char(dn->dstat.st_mode);
674#endif
675
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000676 /* Do readlink early, so that if it fails, error message
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100677 * does not appear *inside* the "ls -l" line */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000678 if (all_fmt & LIST_SYMLINK)
679 if (S_ISLNK(dn->dstat.st_mode))
680 lpath = xmalloc_readlink_or_warn(dn->fullname);
681
682 if (all_fmt & LIST_INO)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100683 column += printf("%7llu ", (long long) dn->dstat.st_ino);
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000684 if (all_fmt & LIST_BLOCKS)
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100685 column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000686 if (all_fmt & LIST_MODEBITS)
687 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
688 if (all_fmt & LIST_NLINKS)
689 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100690 if (all_fmt & LIST_ID_NUMERIC) {
691 if (option_mask32 & OPT_g)
692 column += printf("%-8u ", (int) dn->dstat.st_gid);
693 else
694 column += printf("%-8u %-8u ",
695 (int) dn->dstat.st_uid,
696 (int) dn->dstat.st_gid);
697 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000698#if ENABLE_FEATURE_LS_USERNAME
Denys Vlasenko9f368e32011-02-28 12:16:10 +0100699 else if (all_fmt & LIST_ID_NAME) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000700 if (option_mask32 & OPT_g) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100701 column += printf("%-8.8s ",
Denys Vlasenko4909fec2010-11-06 00:46:57 +0100702 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000703 } else {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100704 column += printf("%-8.8s %-8.8s ",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000705 get_cached_username(dn->dstat.st_uid),
706 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000707 }
708 }
Eric Andersen11c65522000-09-07 17:24:47 +0000709#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100710 if (all_fmt & LIST_SIZE) {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000711 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
712 column += printf("%4u, %3u ",
713 (int) major(dn->dstat.st_rdev),
714 (int) minor(dn->dstat.st_rdev));
715 } else {
Denys Vlasenko982aa262010-12-19 21:54:39 +0100716 if (option_mask32 & OPT_h) {
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100717 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
Denys Vlasenko0bf44d02009-10-13 01:25:09 +0200718 /* print st_size, show one fractional, use suffixes */
719 make_human_readable_str(dn->dstat.st_size, 1, 0)
720 );
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000721 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000722 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000723 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000724 }
725 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000726#if ENABLE_FEATURE_LS_TIMESTAMPS
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100727 if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
728 char *filetime;
729 time_t ttime = dn->dstat.st_mtime;
730 if (all_fmt & TIME_ACCESS)
731 ttime = dn->dstat.st_atime;
732 if (all_fmt & TIME_CHANGE)
733 ttime = dn->dstat.st_ctime;
734 filetime = ctime(&ttime);
735 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
736 if (all_fmt & LIST_FULLTIME)
737 column += printf("%.24s ", filetime);
738 else { /* LIST_DATE_TIME */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000739 /* current_time_t ~== time(NULL) */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100740 time_t age = current_time_t - ttime;
741 printf("%.6s ", filetime + 4); /* "Jun 30" */
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000742 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
743 /* hh:mm if less than 6 months old */
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100744 printf("%.5s ", filetime + 11);
745 } else { /* year. buggy if year > 9999 ;) */
746 printf(" %.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000747 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000748 column += 13;
749 }
Denys Vlasenko9c3b84a2010-01-18 01:55:00 +0100750 }
Eric Andersen11c65522000-09-07 17:24:47 +0000751#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000752#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000753 if (all_fmt & LIST_CONTEXT) {
754 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
755 freecon(dn->sid);
756 }
Eric Andersen9e480452003-07-03 10:07:04 +0000757#endif
James Youngmana4bc10c2010-12-20 01:36:16 +0100758
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000759#if ENABLE_FEATURE_LS_COLOR
James Youngmana4bc10c2010-12-20 01:36:16 +0100760 if (show_color) {
761 info.st_mode = 0; /* for fgcolor() */
762 lstat(dn->fullname, &info);
763 printf("\033[%u;%um", bold(info.st_mode),
764 fgcolor(info.st_mode));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000765 }
James Youngmana4bc10c2010-12-20 01:36:16 +0100766#endif
767 column += print_name(dn->name);
768 if (show_color) {
769 printf("\033[0m");
770 }
771
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000772 if (all_fmt & LIST_SYMLINK) {
773 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
774 printf(" -> ");
775#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
776#if ENABLE_FEATURE_LS_COLOR
777 info.st_mode = 0; /* for fgcolor() */
778#endif
779 if (stat(dn->fullname, &info) == 0) {
780 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000781 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000782#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000783#if ENABLE_FEATURE_LS_COLOR
784 if (show_color) {
785 printf("\033[%u;%um", bold(info.st_mode),
786 fgcolor(info.st_mode));
787 }
788#endif
789 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000790 if (show_color) {
791 printf("\033[0m");
792 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000793 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000794 }
795 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000796#if ENABLE_FEATURE_LS_FILETYPES
797 if (all_fmt & LIST_FILETYPE) {
798 if (append) {
799 putchar(append);
800 column++;
801 }
802 }
803#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000804
Glenn L McGrath4d001292003-01-06 01:11:50 +0000805 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000806}
807
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100808static void showfiles(struct dnode **dn, unsigned nfiles)
809{
810 unsigned i, ncols, nrows, row, nc;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100811 unsigned column;
812 unsigned nexttab;
Denys Vlasenkod87815d2010-12-19 05:43:44 +0100813 unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100814
815 if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
816 ncols = 1;
817 } else {
818 /* find the longest file name, use that as the column width */
819 for (i = 0; dn[i]; i++) {
820 int len = calc_name_len(dn[i]->name);
821 if (column_width < len)
822 column_width = len;
823 }
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100824 column_width += 1 +
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100825 IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
826 ((all_fmt & LIST_INO) ? 8 : 0) +
827 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
828 ncols = (int) (terminal_width / column_width);
829 }
830
831 if (ncols > 1) {
832 nrows = nfiles / ncols;
833 if (nrows * ncols < nfiles)
834 nrows++; /* round up fractionals */
835 } else {
836 nrows = nfiles;
837 ncols = 1;
838 }
839
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100840 column = 0;
841 nexttab = 0;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100842 for (row = 0; row < nrows; row++) {
843 for (nc = 0; nc < ncols; nc++) {
844 /* reach into the array based on the column and row */
845 if (all_fmt & DISP_ROWS)
846 i = (row * ncols) + nc; /* display across row */
847 else
848 i = (nc * nrows) + row; /* display by column */
849 if (i < nfiles) {
850 if (column > 0) {
851 nexttab -= column;
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +0100852 printf("%*s ", nexttab, "");
853 column += nexttab + 1;
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100854 }
855 nexttab = column + column_width;
856 column += list_single(dn[i]);
857 }
858 }
859 putchar('\n');
860 column = 0;
861 }
862}
863
864
865#if ENABLE_DESKTOP
866/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
867 * If any of the -l, -n, -s options is specified, each list
868 * of files within the directory shall be preceded by a
869 * status line indicating the number of file system blocks
870 * occupied by files in the directory in 512-byte units if
871 * the -k option is not specified, or 1024-byte units if the
872 * -k option is specified, rounded up to the next integral
873 * number of units.
874 */
875/* by Jorgen Overgaard (jorgen AT antistaten.se) */
876static off_t calculate_blocks(struct dnode **dn)
877{
878 uoff_t blocks = 1;
879 if (dn) {
880 while (*dn) {
881 /* st_blocks is in 512 byte blocks */
882 blocks += (*dn)->dstat.st_blocks;
883 dn++;
884 }
885 }
886
887 /* Even though standard says use 512 byte blocks, coreutils use 1k */
888 /* Actually, we round up by calculating (blocks + 1) / 2,
889 * "+ 1" was done when we initialized blocks to 1 */
890 return blocks >> 1;
891}
892#endif
893
894
895static struct dnode **list_dir(const char *, unsigned *);
896
897static void showdirs(struct dnode **dn, int first)
898{
899 unsigned nfiles;
900 unsigned dndirs;
901 struct dnode **subdnp;
902 struct dnode **dnd;
903
Denys Vlasenkod8528b82010-01-31 05:15:38 +0100904 for (; *dn; dn++) {
905 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
906 if (!first)
907 bb_putchar('\n');
908 first = 0;
909 printf("%s:\n", (*dn)->fullname);
910 }
911 subdnp = list_dir((*dn)->fullname, &nfiles);
912#if ENABLE_DESKTOP
913 if ((all_fmt & STYLE_MASK) == STYLE_LONG)
914 printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
915#endif
916 if (nfiles > 0) {
917 /* list all files at this level */
918 dnsort(subdnp, nfiles);
919 showfiles(subdnp, nfiles);
920 if (ENABLE_FEATURE_LS_RECURSIVE
921 && (all_fmt & DISP_RECURSIVE)
922 ) {
923 /* recursive - list the sub-dirs */
924 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
925 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
926 if (dndirs > 0) {
927 dnsort(dnd, dndirs);
928 showdirs(dnd, 0);
929 /* free the array of dnode pointers to the dirs */
930 free(dnd);
931 }
932 }
933 /* free the dnodes and the fullname mem */
934 dfree(subdnp);
935 }
936 }
937}
938
939
940/* Returns NULL-terminated malloced vector of pointers (or NULL) */
941static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
942{
943 struct dnode *dn, *cur, **dnp;
944 struct dirent *entry;
945 DIR *dir;
946 unsigned i, nfiles;
947
948 /* Never happens:
949 if (path == NULL)
950 return NULL;
951 */
952
953 *nfiles_p = 0;
954 dir = warn_opendir(path);
955 if (dir == NULL) {
956 exit_code = EXIT_FAILURE;
957 return NULL; /* could not open the dir */
958 }
959 dn = NULL;
960 nfiles = 0;
961 while ((entry = readdir(dir)) != NULL) {
962 char *fullname;
963
964 /* are we going to list the file- it may be . or .. or a hidden file */
965 if (entry->d_name[0] == '.') {
966 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
967 && !(all_fmt & DISP_DOT)
968 ) {
969 continue;
970 }
971 if (!(all_fmt & DISP_HIDDEN))
972 continue;
973 }
974 fullname = concat_path_file(path, entry->d_name);
975 cur = my_stat(fullname, bb_basename(fullname), 0);
976 if (!cur) {
977 free(fullname);
978 continue;
979 }
980 cur->fname_allocated = 1;
981 cur->next = dn;
982 dn = cur;
983 nfiles++;
984 }
985 closedir(dir);
986
987 if (dn == NULL)
988 return NULL;
989
990 /* now that we know how many files there are
991 * allocate memory for an array to hold dnode pointers
992 */
993 *nfiles_p = nfiles;
994 dnp = dnalloc(nfiles);
995 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
996 dnp[i] = dn; /* save pointer to node in array */
997 dn = dn->next;
998 if (!dn)
999 break;
1000 }
1001
1002 return dnp;
1003}
1004
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001005
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001006int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +00001007{
Glenn L McGrath792cae52004-01-18 05:15:16 +00001008 struct dnode **dnd;
1009 struct dnode **dnf;
1010 struct dnode **dnp;
1011 struct dnode *dn;
1012 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +00001013 unsigned opt;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001014 unsigned nfiles;
1015 unsigned dnfiles;
1016 unsigned dndirs;
1017 unsigned i;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001018#if ENABLE_FEATURE_LS_COLOR
1019 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1020 /* coreutils 6.10:
1021 * # ls --color=BOGUS
1022 * ls: invalid argument 'BOGUS' for '--color'
1023 * Valid arguments are:
1024 * 'always', 'yes', 'force'
1025 * 'never', 'no', 'none'
1026 * 'auto', 'tty', 'if-tty'
1027 * (and substrings: "--color=alwa" work too)
1028 */
1029 static const char ls_longopts[] ALIGN1 =
1030 "color\0" Optional_argument "\xff"; /* no short equivalent */
1031 static const char color_str[] ALIGN1 =
1032 "always\0""yes\0""force\0"
1033 "auto\0""tty\0""if-tty\0";
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001034 /* need to initialize since --color has _an optional_ argument */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001035 const char *color_opt = color_str; /* "always" */
1036#endif
Glenn L McGrath792cae52004-01-18 05:15:16 +00001037
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001038 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +00001039
Denys Vlasenko28055022010-01-04 20:49:58 +01001040 init_unicode();
Denys Vlasenko42a8fd02009-07-11 21:36:13 +02001041
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001042 if (ENABLE_FEATURE_LS_SORTFILES)
1043 all_fmt = SORT_NAME;
Glenn L McGrath792cae52004-01-18 05:15:16 +00001044
Denis Vlasenko5c759602006-10-28 12:37:16 +00001045#if ENABLE_FEATURE_AUTOWIDTH
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001046 /* obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +00001047 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001048 /* go one less... */
Eric Andersen8efe9672003-09-15 08:33:45 +00001049 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +00001050#endif
Eric Andersen11c65522000-09-07 17:24:47 +00001051
Eric Andersen11c65522000-09-07 17:24:47 +00001052 /* process options */
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001053 IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001054 opt_complementary =
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001055 /* -e implies -l */
1056 "el"
Denys Vlasenkof3137462010-12-19 05:05:34 +01001057 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1058 * in some pairs of opts, only last one takes effect:
1059 */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001060 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
Denys Vlasenko982aa262010-12-19 21:54:39 +01001061 // ":m-l:l-m" - we don't have -m
1062 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
Denys Vlasenkof3137462010-12-19 05:05:34 +01001063 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1064 ":C-1:1-C" /* bycols/oneline */
1065 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1066 ":c-u:u-c" /* mtime/atime */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001067 /* -w NUM: */
1068 IF_FEATURE_AUTOWIDTH(":w+");
Denys Vlasenkof3137462010-12-19 05:05:34 +01001069 opt = getopt32(argv, ls_options
Denys Vlasenko2f7d9e82010-12-19 07:06:44 +01001070 IF_FEATURE_AUTOWIDTH(, NULL, &terminal_width)
Denys Vlasenkof3137462010-12-19 05:05:34 +01001071 IF_FEATURE_LS_COLOR(, &color_opt)
1072 );
Denys Vlasenko982aa262010-12-19 21:54:39 +01001073 for (i = 0; opt_flags[i] != (1U << 31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +00001074 if (opt & (1 << i)) {
Denys Vlasenko982aa262010-12-19 21:54:39 +01001075 uint32_t flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001076
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001077 if (flags & STYLE_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001078 all_fmt &= ~STYLE_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001079 if (flags & SORT_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001080 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001081 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +00001082 all_fmt &= ~TIME_MASK;
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001083
Manuel Novoa III cad53642003-03-19 09:13:01 +00001084 all_fmt |= flags;
1085 }
Eric Andersen11c65522000-09-07 17:24:47 +00001086 }
1087
Denis Vlasenko5c759602006-10-28 12:37:16 +00001088#if ENABLE_FEATURE_LS_COLOR
1089 /* find color bit value - last position for short getopt */
1090 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1091 char *p = getenv("LS_COLORS");
1092 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +00001093 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001094 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +00001095 }
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001096 if (opt & OPT_color) {
1097 if (color_opt[0] == 'n')
Denis Vlasenko5c759602006-10-28 12:37:16 +00001098 show_color = 0;
Denys Vlasenkoae05dd42009-07-03 12:22:19 +02001099 else switch (index_in_substrings(color_str, color_opt)) {
1100 case 3:
1101 case 4:
1102 case 5:
1103 if (isatty(STDOUT_FILENO)) {
1104 case 0:
1105 case 1:
1106 case 2:
1107 show_color = 1;
1108 }
1109 }
Paul Fox156dc412005-08-01 19:33:30 +00001110 }
1111#endif
1112
Eric Andersen11c65522000-09-07 17:24:47 +00001113 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +00001114 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +00001115 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +00001116 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1117 if (all_fmt & TIME_CHANGE)
1118 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1119 if (all_fmt & TIME_ACCESS)
1120 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1121 }
Denys Vlasenko9f368e32011-02-28 12:16:10 +01001122 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
Denys Vlasenko407ab2a2010-12-19 16:29:08 +01001123 all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001124
Denys Vlasenkof3137462010-12-19 05:05:34 +01001125 /* choose a display format if one was not already specified by an option */
Rob Landleyea224be2006-06-18 20:20:07 +00001126 if (!(all_fmt & STYLE_MASK))
Denys Vlasenkod87815d2010-12-19 05:43:44 +01001127 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +00001128
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001129 argv += optind;
1130 if (!argv[0])
1131 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001132
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001133 if (argv[1])
1134 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001135
Denis Vlasenko5c759602006-10-28 12:37:16 +00001136 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001137 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001138 nfiles = 0;
1139 do {
Denys Vlasenko163d8642010-12-19 06:16:28 +01001140 cur = my_stat(*argv, *argv,
Denys Vlasenko982aa262010-12-19 21:54:39 +01001141 /* follow links on command line unless -l, -s or -F: */
Denys Vlasenko163d8642010-12-19 06:16:28 +01001142 !((all_fmt & (STYLE_LONG|LIST_BLOCKS)) || (option_mask32 & OPT_F))
Denys Vlasenko982aa262010-12-19 21:54:39 +01001143 /* ... or if -H: */
1144 || (option_mask32 & OPT_H)
Denys Vlasenko163d8642010-12-19 06:16:28 +01001145 );
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001146 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001147 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001148 continue;
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001149 cur->fname_allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001150 cur->next = dn;
1151 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001152 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001153 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001154
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001155 /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1156 if (nfiles == 0)
1157 return exit_code;
1158
Eric Andersen11c65522000-09-07 17:24:47 +00001159 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001160 * allocate memory for an array to hold dnode pointers
1161 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001162 dnp = dnalloc(nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001163 for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1164 dnp[i] = dn; /* save pointer to node in array */
1165 dn = dn->next;
1166 if (!dn)
1167 break;
Eric Andersen11c65522000-09-07 17:24:47 +00001168 }
1169
Manuel Novoa III cad53642003-03-19 09:13:01 +00001170 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001171 dnsort(dnp, nfiles);
Denys Vlasenko76c7d952009-10-03 01:15:47 +02001172 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001173 } else {
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001174 dnd = splitdnarray(dnp, SPLIT_DIR);
1175 dnf = splitdnarray(dnp, SPLIT_FILE);
1176 dndirs = count_dirs(dnp, SPLIT_DIR);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001177 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001178 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001179 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001180 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001181 if (ENABLE_FEATURE_CLEAN_UP)
1182 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001183 }
1184 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001185 dnsort(dnd, dndirs);
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001186 showdirs(dnd, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001187 if (ENABLE_FEATURE_CLEAN_UP)
1188 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001189 }
1190 }
Rob Landley26314862006-05-02 19:46:52 +00001191 if (ENABLE_FEATURE_CLEAN_UP)
Denys Vlasenkocae409c2009-10-03 11:43:48 +02001192 dfree(dnp);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001193 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001194}