blob: 61baa9a1109ebfb398914bd7ce0589916f2683ad [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 *
Bernhard Reutner-Fischercb448162006-04-12 07:35:12 +00006 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
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:
Erik Andersen9ffdaa62000-02-11 21:55:04 +000020 * 1. ls -l of a directory doesn't give "total <blocks>" header
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000021 * 2. hidden files can make column width too large
Erik Andersen9ffdaa62000-02-11 21:55:04 +000022 *
Eric Andersencc8ed391999-10-05 16:24:54 +000023 * NON-OPTIMAL BEHAVIOUR:
24 * 1. autowidth reads directories twice
25 * 2. if you do a short directory listing without filetype characters
26 * appended, there's no need to stat each one
27 * PORTABILITY:
28 * 1. requires lstat (BSD) - how do you do it without?
Denis Vlasenko5e4fda02009-03-08 23:46:48 +000029 *
30 * [2009-03]
31 * ls sorts listing now, and supports almost all options.
Eric Andersencc8ed391999-10-05 16:24:54 +000032 */
33
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000034#include "libbb.h"
Denis Vlasenko99912ca2007-04-10 15:43:37 +000035
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +000036#if ENABLE_FEATURE_ASSUME_UNICODE
37#include <wchar.h>
38#endif
39
Denis Vlasenko99912ca2007-04-10 15:43:37 +000040/* This is a NOEXEC applet. Be very careful! */
41
Eric Andersenf1142c52001-02-20 06:16:29 +000042
Denis Vlasenko245f91b2009-03-09 22:37:23 +000043#if ENABLE_FTPD
44/* ftpd uses ls, and without timestamps Mozilla won't understand
45 * ftpd's LIST output.
46 */
47# undef CONFIG_FEATURE_LS_TIMESTAMPS
48# undef ENABLE_FEATURE_LS_TIMESTAMPS
49# undef USE_FEATURE_LS_TIMESTAMPS
50# undef SKIP_FEATURE_LS_TIMESTAMPS
51# define CONFIG_FEATURE_LS_TIMESTAMPS 1
52# define ENABLE_FEATURE_LS_TIMESTAMPS 1
53# define USE_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
54# define SKIP_FEATURE_LS_TIMESTAMPS(...)
55#endif
56
57
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000058enum {
Manuel Novoa III cad53642003-03-19 09:13:01 +000059
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000060TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
61COLUMN_GAP = 2, /* includes the file type char */
62
63/* what is the overall style of the listing */
64STYLE_COLUMNS = 1 << 21, /* fill columns */
65STYLE_LONG = 2 << 21, /* one record per line, extended info */
66STYLE_SINGLE = 3 << 21, /* one record per line */
67STYLE_MASK = STYLE_SINGLE,
Eric Andersencc8ed391999-10-05 16:24:54 +000068
Eric Andersen11c65522000-09-07 17:24:47 +000069/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
70/* what file information will be listed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000071LIST_INO = 1 << 0,
72LIST_BLOCKS = 1 << 1,
73LIST_MODEBITS = 1 << 2,
74LIST_NLINKS = 1 << 3,
75LIST_ID_NAME = 1 << 4,
76LIST_ID_NUMERIC = 1 << 5,
77LIST_CONTEXT = 1 << 6,
78LIST_SIZE = 1 << 7,
Denis Vlasenko3a014b82009-03-21 19:11:23 +000079//LIST_DEV = 1 << 8, - unused, synonym to LIST_SIZE
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000080LIST_DATE_TIME = 1 << 9,
81LIST_FULLTIME = 1 << 10,
82LIST_FILENAME = 1 << 11,
83LIST_SYMLINK = 1 << 12,
84LIST_FILETYPE = 1 << 13,
85LIST_EXEC = 1 << 14,
86LIST_MASK = (LIST_EXEC << 1) - 1,
Eric Andersen11c65522000-09-07 17:24:47 +000087
88/* what files will be displayed */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000089DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */
Denis Vlasenko656f7462006-10-28 13:02:55 +000090DISP_HIDDEN = 1 << 16, /* show filenames starting with . */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000091DISP_DOT = 1 << 17, /* show . and .. */
92DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */
93DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */
94DISP_ROWS = 1 << 20, /* print across rows */
95DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
Manuel Novoa III cad53642003-03-19 09:13:01 +000096
Denis Vlasenko94cf69f2006-10-28 12:37:51 +000097/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
98SORT_FORWARD = 0, /* sort in reverse order */
99SORT_REVERSE = 1 << 27, /* sort in reverse order */
Eric Andersen11c65522000-09-07 17:24:47 +0000100
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000101SORT_NAME = 0, /* sort by file name */
102SORT_SIZE = 1 << 28, /* sort by file size */
103SORT_ATIME = 2 << 28, /* sort by last access time */
104SORT_CTIME = 3 << 28, /* sort by last change time */
105SORT_MTIME = 4 << 28, /* sort by last modification time */
106SORT_VERSION = 5 << 28, /* sort by version */
107SORT_EXT = 6 << 28, /* sort by file name extension */
108SORT_DIR = 7 << 28, /* sort by file or directory */
109SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
Eric Andersencc8ed391999-10-05 16:24:54 +0000110
Eric Andersen11c65522000-09-07 17:24:47 +0000111/* which of the three times will be used */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000112TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
113TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
114TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
Manuel Novoa III cad53642003-03-19 09:13:01 +0000115
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000116FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
Eric Andersencc8ed391999-10-05 16:24:54 +0000117
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000118LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
Eric Andersencc8ed391999-10-05 16:24:54 +0000119
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000120LIST_SHORT = LIST_FILENAME,
121LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
122 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
123
124SPLIT_DIR = 1,
125SPLIT_FILE = 0,
126SPLIT_SUBDIR = 2,
127
128};
Eric Andersencc8ed391999-10-05 16:24:54 +0000129
Denis Vlasenko248ce912009-03-03 14:09:04 +0000130/* "[-]Cadil1", POSIX mandated options, busybox always supports */
131/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
132/* "[-]Q" GNU option? busybox always supports */
133/* "[-]Ak" GNU options, busybox always supports */
134/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
135/* "[-]p", POSIX non-mandated options, busybox optionally supports */
136/* "[-]SXvThw", GNU options, busybox optionally supports */
137/* "[-]K", SELinux mandated options, busybox optionally supports */
138/* "[-]e", I think we made this one up */
139static const char ls_options[] ALIGN1 =
140 "Cadil1gnsxQAk" /* 13 opts, total 13 */
141 USE_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
142 USE_FEATURE_LS_SORTFILES("SXrv") /* 4, 21 */
143 USE_FEATURE_LS_FILETYPES("Fp") /* 2, 23 */
144 USE_FEATURE_LS_FOLLOWLINKS("L") /* 1, 24 */
145 USE_FEATURE_LS_RECURSIVE("R") /* 1, 25 */
146 USE_FEATURE_HUMAN_READABLE("h") /* 1, 26 */
147 USE_SELINUX("K") /* 1, 27 */
148 USE_SELINUX("Z") /* 1, 28 */
149 USE_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
150 ;
151enum {
152 //OPT_C = (1 << 0),
153 //OPT_a = (1 << 1),
154 //OPT_d = (1 << 2),
155 //OPT_i = (1 << 3),
156 //OPT_l = (1 << 4),
157 //OPT_1 = (1 << 5),
158 OPT_g = (1 << 6),
159 //OPT_n = (1 << 7),
160 //OPT_s = (1 << 8),
161 //OPT_x = (1 << 9),
162 OPT_Q = (1 << 10),
163 //OPT_A = (1 << 11),
164 //OPT_k = (1 << 12),
165};
166
167enum {
168 LIST_MASK_TRIGGER = 0,
169 STYLE_MASK_TRIGGER = STYLE_MASK,
170 DISP_MASK_TRIGGER = DISP_ROWS,
171 SORT_MASK_TRIGGER = SORT_MASK,
172};
173
174/* TODO: simple toggles may be stored as OPT_xxx bits instead */
175static const unsigned opt_flags[] = {
176 LIST_SHORT | STYLE_COLUMNS, /* C */
177 DISP_HIDDEN | DISP_DOT, /* a */
178 DISP_NOLIST, /* d */
179 LIST_INO, /* i */
180 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
181 LIST_SHORT | STYLE_SINGLE, /* 1 */
182 0, /* g (don't show group) - handled via OPT_g */
183 LIST_ID_NUMERIC, /* n */
184 LIST_BLOCKS, /* s */
185 DISP_ROWS, /* x */
186 0, /* Q (quote filename) - handled via OPT_Q */
187 DISP_HIDDEN, /* A */
188 ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
189#if ENABLE_FEATURE_LS_TIMESTAMPS
190 TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
191 LIST_FULLTIME, /* e */
192 ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
193 TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
194#endif
195#if ENABLE_FEATURE_LS_SORTFILES
196 SORT_SIZE, /* S */
197 SORT_EXT, /* X */
198 SORT_REVERSE, /* r */
199 SORT_VERSION, /* v */
200#endif
201#if ENABLE_FEATURE_LS_FILETYPES
202 LIST_FILETYPE | LIST_EXEC, /* F */
203 LIST_FILETYPE, /* p */
204#endif
205#if ENABLE_FEATURE_LS_FOLLOWLINKS
206 FOLLOW_LINKS, /* L */
207#endif
208#if ENABLE_FEATURE_LS_RECURSIVE
209 DISP_RECURSIVE, /* R */
210#endif
211#if ENABLE_FEATURE_HUMAN_READABLE
212 LS_DISP_HR, /* h */
213#endif
214#if ENABLE_SELINUX
215 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
216#endif
217#if ENABLE_SELINUX
218 LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
219#endif
220 (1U<<31)
221 /* options after Z are not processed through opt_flags:
222 * T, w - ignored
223 */
224};
225
226
Eric Andersen11c65522000-09-07 17:24:47 +0000227/*
228 * a directory entry and its stat info are stored here
229 */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000230struct dnode { /* the basic node */
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000231 const char *name; /* the dir entry name */
232 const char *fullname; /* the dir entry name */
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +0000233 int allocated;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000234 struct stat dstat; /* the file stat info */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000235 USE_SELINUX(security_context_t sid;)
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000236 struct dnode *next; /* point at the next node */
Eric Andersen11c65522000-09-07 17:24:47 +0000237};
Eric Andersen11c65522000-09-07 17:24:47 +0000238
Glenn L McGrath4d001292003-01-06 01:11:50 +0000239static struct dnode **list_dir(const char *);
Eric Andersen3e6ff902001-03-09 21:24:12 +0000240static struct dnode **dnalloc(int);
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000241static int list_single(const struct dnode *);
Eric Andersen11c65522000-09-07 17:24:47 +0000242
Eric Andersen11c65522000-09-07 17:24:47 +0000243
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000244struct globals {
245#if ENABLE_FEATURE_LS_COLOR
246 smallint show_color;
247#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000248 smallint exit_code;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000249 unsigned all_fmt;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000250#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000251 unsigned tabstops; // = COLUMN_GAP;
252 unsigned terminal_width; // = TERMINAL_WIDTH;
253#endif
254#if ENABLE_FEATURE_LS_TIMESTAMPS
255 /* Do time() just once. Saves one syscall per file for "ls -l" */
256 time_t current_time_t;
257#endif
258};
259#define G (*(struct globals*)&bb_common_bufsiz1)
260#if ENABLE_FEATURE_LS_COLOR
261#define show_color (G.show_color )
262#else
263enum { show_color = 0 };
264#endif
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000265#define exit_code (G.exit_code )
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000266#define all_fmt (G.all_fmt )
267#if ENABLE_FEATURE_AUTOWIDTH
268#define tabstops (G.tabstops )
269#define terminal_width (G.terminal_width)
Eric Andersenf5d5e772001-01-24 23:34:48 +0000270#else
Denis Vlasenko5c759602006-10-28 12:37:16 +0000271enum {
272 tabstops = COLUMN_GAP,
273 terminal_width = TERMINAL_WIDTH,
274};
Eric Andersen11c65522000-09-07 17:24:47 +0000275#endif
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000276#define current_time_t (G.current_time_t)
277/* memset: we have to zero it out because of NOEXEC */
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000278#define INIT_G() do { \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000279 memset(&G, 0, sizeof(G)); \
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +0000280 USE_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
281 USE_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000282 USE_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000283} while (0)
Eric Andersencc8ed391999-10-05 16:24:54 +0000284
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000285
286#if ENABLE_FEATURE_ASSUME_UNICODE
287/* libbb candidate */
288static size_t mbstrlen(const char *string)
289{
290 size_t width = mbsrtowcs(NULL /*dest*/, &string,
291 MAXINT(size_t) /*len*/, NULL /*state*/);
292 if (width == (size_t)-1)
293 return strlen(string);
294 return width;
295}
296#else
297#define mbstrlen(string) strlen(string)
298#endif
299
Matt Kraai33fdae52000-10-13 17:59:43 +0000300
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000301static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000302{
Glenn L McGrath4d001292003-01-06 01:11:50 +0000303 struct stat dstat;
304 struct dnode *cur;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000305 USE_SELINUX(security_context_t sid = NULL;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000306
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000307 if ((all_fmt & FOLLOW_LINKS) || force_follow) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000308#if ENABLE_SELINUX
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000309 if (is_selinux_enabled()) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000310 getfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000311 }
Eric Andersen9e480452003-07-03 10:07:04 +0000312#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000313 if (stat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000314 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000315 exit_code = EXIT_FAILURE;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000316 return 0;
Matt Kraai9a6e67c2000-10-13 18:03:21 +0000317 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000318 } else {
319#if ENABLE_SELINUX
320 if (is_selinux_enabled()) {
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000321 lgetfilecon(fullname, &sid);
Rob Landley60158cb2005-05-03 06:25:50 +0000322 }
Eric Andersen9e480452003-07-03 10:07:04 +0000323#endif
Bernhard Reutner-Fischerd2c306e2006-05-29 12:10:23 +0000324 if (lstat(fullname, &dstat)) {
Denis Vlasenko0c97c9d2007-10-01 11:58:38 +0000325 bb_simple_perror_msg(fullname);
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000326 exit_code = EXIT_FAILURE;
Eric Andersen9e480452003-07-03 10:07:04 +0000327 return 0;
328 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000329 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000330
Denis Vlasenko5c759602006-10-28 12:37:16 +0000331 cur = xmalloc(sizeof(struct dnode));
Glenn L McGrath4d001292003-01-06 01:11:50 +0000332 cur->fullname = fullname;
333 cur->name = name;
334 cur->dstat = dstat;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000335 USE_SELINUX(cur->sid = sid;)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000336 return cur;
Eric Andersen79565b62000-08-11 18:10:21 +0000337}
338
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000339
340/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
341 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
342 * 3/7:multiplexed char/block device)
343 * and we use 0 for unknown and 15 for executables (see below) */
344#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
345#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
346#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
347/* 036 black foreground 050 black background
348 037 red foreground 051 red background
349 040 green foreground 052 green background
350 041 brown foreground 053 brown background
351 042 blue foreground 054 blue background
352 043 magenta (purple) foreground 055 magenta background
353 044 cyan (light blue) foreground 056 cyan background
354 045 gray foreground 057 white background
355*/
356#define COLOR(mode) ( \
357 /*un fi chr dir blk file link sock exe */ \
358 "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
359 [TYPEINDEX(mode)])
360/* Select normal (0) [actually "reset all"] or bold (1)
361 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
362 * let's use 7 for "impossible" types, just for fun)
363 * Note: coreutils 6.9 uses inverted red for setuid binaries.
364 */
365#define ATTR(mode) ( \
366 /*un fi chr dir blk file link sock exe */ \
367 "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
368 [TYPEINDEX(mode)])
369
Denis Vlasenko5c759602006-10-28 12:37:16 +0000370#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000371/* mode of zero is interpreted as "unknown" (stat failed) */
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000372static char fgcolor(mode_t mode)
373{
Rob Landley9947a242006-06-15 22:11:10 +0000374 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000375 return COLOR(0xF000); /* File is executable ... */
376 return COLOR(mode);
377}
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000378static char bold(mode_t mode)
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000379{
Rob Landley9947a242006-06-15 22:11:10 +0000380 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Eric Andersen3ad0bd92002-03-20 09:13:48 +0000381 return ATTR(0xF000); /* File is executable ... */
382 return ATTR(mode);
383}
384#endif
385
Denis Vlasenko5c759602006-10-28 12:37:16 +0000386#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000387static char append_char(mode_t mode)
Eric Andersen79565b62000-08-11 18:10:21 +0000388{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000389 if (!(all_fmt & LIST_FILETYPE))
Eric Andersen11c65522000-09-07 17:24:47 +0000390 return '\0';
Rob Landley9947a242006-06-15 22:11:10 +0000391 if (S_ISDIR(mode))
392 return '/';
393 if (!(all_fmt & LIST_EXEC))
394 return '\0';
395 if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000396 return '*';
397 return APPCHAR(mode);
Eric Andersen11c65522000-09-07 17:24:47 +0000398}
399#endif
Eric Andersen79565b62000-08-11 18:10:21 +0000400
Denis Vlasenkoc1969f62009-03-18 22:39:34 +0000401
Denis Vlasenko5c759602006-10-28 12:37:16 +0000402#define countdirs(A, B) count_dirs((A), (B), 1)
403#define countsubdirs(A, B) count_dirs((A), (B), 0)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000404static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
Eric Andersen11c65522000-09-07 17:24:47 +0000405{
406 int i, dirs;
407
Denis Vlasenko5c759602006-10-28 12:37:16 +0000408 if (!dn)
409 return 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000410 dirs = 0;
411 for (i = 0; i < nfiles; i++) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000412 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000413 if (!S_ISDIR(dn[i]->dstat.st_mode))
414 continue;
415 name = dn[i]->name;
416 if (notsubdirs
417 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
418 ) {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000419 dirs++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000420 }
Eric Andersen11c65522000-09-07 17:24:47 +0000421 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000422 return dirs;
Eric Andersen11c65522000-09-07 17:24:47 +0000423}
424
Eric Andersen3e6ff902001-03-09 21:24:12 +0000425static int countfiles(struct dnode **dnp)
Eric Andersen11c65522000-09-07 17:24:47 +0000426{
427 int nfiles;
428 struct dnode *cur;
429
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000430 if (dnp == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000431 return 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000432 nfiles = 0;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000433 for (cur = dnp[0]; cur->next; cur = cur->next)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000434 nfiles++;
Eric Andersen11c65522000-09-07 17:24:47 +0000435 nfiles++;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000436 return nfiles;
Eric Andersen11c65522000-09-07 17:24:47 +0000437}
438
439/* get memory to hold an array of pointers */
Eric Andersen3e6ff902001-03-09 21:24:12 +0000440static struct dnode **dnalloc(int num)
Eric Andersen11c65522000-09-07 17:24:47 +0000441{
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000442 if (num < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000443 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000444
Denis Vlasenko5c759602006-10-28 12:37:16 +0000445 return xzalloc(num * sizeof(struct dnode *));
Eric Andersen11c65522000-09-07 17:24:47 +0000446}
447
Denis Vlasenko5c759602006-10-28 12:37:16 +0000448#if ENABLE_FEATURE_LS_RECURSIVE
Rob Landley26314862006-05-02 19:46:52 +0000449static void dfree(struct dnode **dnp, int nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000450{
Rob Landley26314862006-05-02 19:46:52 +0000451 int i;
Eric Andersen11c65522000-09-07 17:24:47 +0000452
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000453 if (dnp == NULL)
454 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000455
Rob Landley26314862006-05-02 19:46:52 +0000456 for (i = 0; i < nfiles; i++) {
457 struct dnode *cur = dnp[i];
Denis Vlasenko5c759602006-10-28 12:37:16 +0000458 if (cur->allocated)
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000459 free((char*)cur->fullname); /* free the filename */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000460 free(cur); /* free the dnode */
Eric Andersen11c65522000-09-07 17:24:47 +0000461 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000462 free(dnp); /* free the array holding the dnode pointers */
Eric Andersen11c65522000-09-07 17:24:47 +0000463}
Rob Landleyc44bc982006-05-28 01:19:06 +0000464#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000465#define dfree(...) ((void)0)
Eric Andersen20aab262001-07-19 22:28:02 +0000466#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000467
Eric Andersen3e6ff902001-03-09 21:24:12 +0000468static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
Eric Andersen11c65522000-09-07 17:24:47 +0000469{
470 int dncnt, i, d;
471 struct dnode **dnp;
472
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000473 if (dn == NULL || nfiles < 1)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000474 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000475
476 /* count how many dirs and regular files there are */
Eric Andersencf1189f2000-11-29 21:52:06 +0000477 if (which == SPLIT_SUBDIR)
478 dncnt = countsubdirs(dn, nfiles);
479 else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000480 dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
Eric Andersencf1189f2000-11-29 21:52:06 +0000481 if (which == SPLIT_FILE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000482 dncnt = nfiles - dncnt; /* looking for files */
Eric Andersencf1189f2000-11-29 21:52:06 +0000483 }
Eric Andersen11c65522000-09-07 17:24:47 +0000484
485 /* allocate a file array and a dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000486 dnp = dnalloc(dncnt);
Eric Andersen11c65522000-09-07 17:24:47 +0000487
488 /* copy the entrys into the file or dir array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000489 for (d = i = 0; i < nfiles; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000490 if (S_ISDIR(dn[i]->dstat.st_mode)) {
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000491 const char *name;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000492 if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
493 continue;
494 name = dn[i]->name;
495 if ((which & SPLIT_DIR)
496 || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
497 ) {
498 dnp[d++] = dn[i];
Manuel Novoa III cad53642003-03-19 09:13:01 +0000499 }
500 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
501 dnp[d++] = dn[i];
Eric Andersen11c65522000-09-07 17:24:47 +0000502 }
503 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000504 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000505}
506
Denis Vlasenko5c759602006-10-28 12:37:16 +0000507#if ENABLE_FEATURE_LS_SORTFILES
Rob Landley425e7582006-05-03 20:22:03 +0000508static int sortcmp(const void *a, const void *b)
Eric Andersen11c65522000-09-07 17:24:47 +0000509{
Rob Landley425e7582006-05-03 20:22:03 +0000510 struct dnode *d1 = *(struct dnode **)a;
511 struct dnode *d2 = *(struct dnode **)b;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000512 unsigned sort_opts = all_fmt & SORT_MASK;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000513 int dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000514
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000515 dif = 0; /* assume SORT_NAME */
516 // TODO: use pre-initialized function pointer
517 // instead of branch forest
Eric Andersen11c65522000-09-07 17:24:47 +0000518 if (sort_opts == SORT_SIZE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000519 dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
Eric Andersen11c65522000-09-07 17:24:47 +0000520 } else if (sort_opts == SORT_ATIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000521 dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
Eric Andersen11c65522000-09-07 17:24:47 +0000522 } else if (sort_opts == SORT_CTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000523 dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
Eric Andersen11c65522000-09-07 17:24:47 +0000524 } else if (sort_opts == SORT_MTIME) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000525 dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
Eric Andersen11c65522000-09-07 17:24:47 +0000526 } else if (sort_opts == SORT_DIR) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000527 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000528 /* } else if (sort_opts == SORT_VERSION) { */
529 /* } else if (sort_opts == SORT_EXT) { */
Eric Andersen11c65522000-09-07 17:24:47 +0000530 }
531
Eric Andersen11c65522000-09-07 17:24:47 +0000532 if (dif == 0) {
Denis Vlasenkofcdb00f2006-11-21 00:09:37 +0000533 /* sort by name - may be a tie_breaker for time or size cmp */
Rob Landleyea224be2006-06-18 20:20:07 +0000534 if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
535 else dif = strcmp(d1->name, d2->name);
Eric Andersen11c65522000-09-07 17:24:47 +0000536 }
537
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000538 if (all_fmt & SORT_REVERSE) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000539 dif = -dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000540 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000541 return dif;
Eric Andersen11c65522000-09-07 17:24:47 +0000542}
543
Rob Landley425e7582006-05-03 20:22:03 +0000544static void dnsort(struct dnode **dn, int size)
Eric Andersen11c65522000-09-07 17:24:47 +0000545{
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000546 qsort(dn, size, sizeof(*dn), sortcmp);
Eric Andersen11c65522000-09-07 17:24:47 +0000547}
Rob Landleyea224be2006-06-18 20:20:07 +0000548#else
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000549#define dnsort(dn, size) ((void)0)
Eric Andersen11c65522000-09-07 17:24:47 +0000550#endif
551
Rob Landleyea224be2006-06-18 20:20:07 +0000552
Eric Andersen3e6ff902001-03-09 21:24:12 +0000553static void showfiles(struct dnode **dn, int nfiles)
Eric Andersen11c65522000-09-07 17:24:47 +0000554{
555 int i, ncols, nrows, row, nc;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000556 int column = 0;
557 int nexttab = 0;
558 int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
Eric Andersen11c65522000-09-07 17:24:47 +0000559
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000560 if (dn == NULL || nfiles < 1)
561 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000562
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000563 if (all_fmt & STYLE_LONG) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000564 ncols = 1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000565 } else {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000566 /* find the longest file name, use that as the column width */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000567 for (i = 0; i < nfiles; i++) {
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000568 int len = mbstrlen(dn[i]->name);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000569 if (column_width < len)
570 column_width = len;
571 }
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000572 column_width += tabstops +
573 USE_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
574 ((all_fmt & LIST_INO) ? 8 : 0) +
575 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
Glenn L McGrath4d001292003-01-06 01:11:50 +0000576 ncols = (int) (terminal_width / column_width);
Eric Andersen11c65522000-09-07 17:24:47 +0000577 }
578
Eric Andersene57d54b2001-01-30 18:03:11 +0000579 if (ncols > 1) {
580 nrows = nfiles / ncols;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000581 if (nrows * ncols < nfiles)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000582 nrows++; /* round up fractionals */
Eric Andersene57d54b2001-01-30 18:03:11 +0000583 } else {
584 nrows = nfiles;
585 ncols = 1;
586 }
Eric Andersen11c65522000-09-07 17:24:47 +0000587
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000588 for (row = 0; row < nrows; row++) {
589 for (nc = 0; nc < ncols; nc++) {
Eric Andersen79565b62000-08-11 18:10:21 +0000590 /* reach into the array based on the column and row */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000591 i = (nc * nrows) + row; /* assume display by column */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000592 if (all_fmt & DISP_ROWS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000593 i = (row * ncols) + nc; /* display across row */
Eric Andersen11c65522000-09-07 17:24:47 +0000594 if (i < nfiles) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000595 if (column > 0) {
596 nexttab -= column;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000597 printf("%*s", nexttab, "");
598 column += nexttab;
599 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000600 nexttab = column + column_width;
601 column += list_single(dn[i]);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000602 }
Glenn L McGrath4d001292003-01-06 01:11:50 +0000603 }
604 putchar('\n');
605 column = 0;
Eric Andersena42982e2000-06-07 17:28:53 +0000606 }
Eric Andersen11c65522000-09-07 17:24:47 +0000607}
608
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000609
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000610static void showdirs(struct dnode **dn, int ndirs, int first)
Eric Andersen11c65522000-09-07 17:24:47 +0000611{
612 int i, nfiles;
613 struct dnode **subdnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000614 int dndirs;
615 struct dnode **dnd;
Eric Andersen11c65522000-09-07 17:24:47 +0000616
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000617 if (dn == NULL || ndirs < 1)
618 return;
Eric Andersen11c65522000-09-07 17:24:47 +0000619
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000620 for (i = 0; i < ndirs; i++) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000621 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000622 if (!first)
Denis Vlasenko4daad902007-09-27 10:20:47 +0000623 bb_putchar('\n');
Glenn L McGrathc4db0832004-03-06 09:12:55 +0000624 first = 0;
625 printf("%s:\n", dn[i]->fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000626 }
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000627 subdnp = list_dir(dn[i]->fullname);
628 nfiles = countfiles(subdnp);
Eric Andersen11c65522000-09-07 17:24:47 +0000629 if (nfiles > 0) {
630 /* list all files at this level */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000631 dnsort(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000632 showfiles(subdnp, nfiles);
Rob Landley2b8a05a2006-06-20 17:43:01 +0000633 if (ENABLE_FEATURE_LS_RECURSIVE) {
634 if (all_fmt & DISP_RECURSIVE) {
635 /* recursive- list the sub-dirs */
636 dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
637 dndirs = countsubdirs(subdnp, nfiles);
638 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000639 dnsort(dnd, dndirs);
Rob Landley2b8a05a2006-06-20 17:43:01 +0000640 showdirs(dnd, dndirs, 0);
641 /* free the array of dnode pointers to the dirs */
642 free(dnd);
643 }
Eric Andersen11c65522000-09-07 17:24:47 +0000644 }
Rob Landley2b8a05a2006-06-20 17:43:01 +0000645 /* free the dnodes and the fullname mem */
646 dfree(subdnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +0000647 }
Eric Andersen11c65522000-09-07 17:24:47 +0000648 }
649 }
650}
651
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000652
Glenn L McGrath4d001292003-01-06 01:11:50 +0000653static struct dnode **list_dir(const char *path)
Eric Andersen11c65522000-09-07 17:24:47 +0000654{
655 struct dnode *dn, *cur, **dnp;
656 struct dirent *entry;
657 DIR *dir;
Eric Andersen11c65522000-09-07 17:24:47 +0000658 int i, nfiles;
659
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000660 if (path == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000661 return NULL;
Eric Andersen11c65522000-09-07 17:24:47 +0000662
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000663 dn = NULL;
664 nfiles = 0;
Rob Landleyd921b2e2006-08-03 15:41:12 +0000665 dir = warn_opendir(path);
Eric Andersen11c65522000-09-07 17:24:47 +0000666 if (dir == NULL) {
Denis Vlasenko4a689e92008-06-18 16:38:22 +0000667 exit_code = EXIT_FAILURE;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000668 return NULL; /* could not open the dir */
Eric Andersen11c65522000-09-07 17:24:47 +0000669 }
670 while ((entry = readdir(dir)) != NULL) {
Glenn L McGrath4d001292003-01-06 01:11:50 +0000671 char *fullname;
672
Eric Andersen11c65522000-09-07 17:24:47 +0000673 /* are we going to list the file- it may be . or .. or a hidden file */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000674 if (entry->d_name[0] == '.') {
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000675 if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
676 && !(all_fmt & DISP_DOT)
677 ) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000678 continue;
Denis Vlasenko16abcd92007-04-13 23:59:52 +0000679 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000680 if (!(all_fmt & DISP_HIDDEN))
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000681 continue;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000682 }
683 fullname = concat_path_file(path, entry->d_name);
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000684 cur = my_stat(fullname, bb_basename(fullname), 0);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000685 if (!cur) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000686 free(fullname);
Eric Andersen11c65522000-09-07 17:24:47 +0000687 continue;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000688 }
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +0000689 cur->allocated = 1;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000690 cur->next = dn;
691 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +0000692 nfiles++;
693 }
694 closedir(dir);
695
696 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +0000697 * allocate memory for an array to hold dnode pointers
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000698 */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000699 if (dn == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000700 return NULL;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000701 dnp = dnalloc(nfiles);
702 for (i = 0, cur = dn; i < nfiles; i++) {
703 dnp[i] = cur; /* save pointer to node in array */
704 cur = cur->next;
Eric Andersen11c65522000-09-07 17:24:47 +0000705 }
706
Denis Vlasenko5c759602006-10-28 12:37:16 +0000707 return dnp;
Eric Andersen11c65522000-09-07 17:24:47 +0000708}
709
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000710
Denis Vlasenko248ce912009-03-03 14:09:04 +0000711static int print_name(const char *name)
712{
713 if (option_mask32 & OPT_Q) {
714#if ENABLE_FEATURE_ASSUME_UNICODE
715 int len = 2 + mbstrlen(name);
716#else
717 int len = 2;
718#endif
719 putchar('"');
720 while (*name) {
721 if (*name == '"') {
722 putchar('\\');
723 len++;
724 }
725 putchar(*name++);
726 if (!ENABLE_FEATURE_ASSUME_UNICODE)
727 len++;
728 }
729 putchar('"');
730 return len;
731 }
732 /* No -Q: */
733#if ENABLE_FEATURE_ASSUME_UNICODE
734 fputs(name, stdout);
735 return mbstrlen(name);
736#else
737 return printf("%s", name);
738#endif
739}
740
741
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000742static int list_single(const struct dnode *dn)
Eric Andersen11c65522000-09-07 17:24:47 +0000743{
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000744 int column = 0;
Denis Vlasenko4d3a8122009-03-27 17:22:00 +0000745 char *lpath = lpath; /* for compiler */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000746#if ENABLE_FEATURE_LS_TIMESTAMPS
Eric Andersen11c65522000-09-07 17:24:47 +0000747 char *filetime;
748 time_t ttime, age;
749#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000750#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
Eric Andersen11c65522000-09-07 17:24:47 +0000751 struct stat info;
752 char append;
753#endif
754
Glenn L McGrath4d001292003-01-06 01:11:50 +0000755 if (dn->fullname == NULL)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000756 return 0;
Eric Andersen11c65522000-09-07 17:24:47 +0000757
Denis Vlasenko5c759602006-10-28 12:37:16 +0000758#if ENABLE_FEATURE_LS_TIMESTAMPS
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000759 ttime = dn->dstat.st_mtime; /* the default time */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000760 if (all_fmt & TIME_ACCESS)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000761 ttime = dn->dstat.st_atime;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000762 if (all_fmt & TIME_CHANGE)
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000763 ttime = dn->dstat.st_ctime;
764 filetime = ctime(&ttime);
Eric Andersen11c65522000-09-07 17:24:47 +0000765#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000766#if ENABLE_FEATURE_LS_FILETYPES
Eric Andersen11c65522000-09-07 17:24:47 +0000767 append = append_char(dn->dstat.st_mode);
768#endif
769
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000770 /* Do readlink early, so that if it fails, error message
771 * does not appear *inside* of the "ls -l" line */
772 if (all_fmt & LIST_SYMLINK)
773 if (S_ISLNK(dn->dstat.st_mode))
774 lpath = xmalloc_readlink_or_warn(dn->fullname);
775
776 if (all_fmt & LIST_INO)
777 column += printf("%7lu ", (long) dn->dstat.st_ino);
778 if (all_fmt & LIST_BLOCKS)
779 column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1);
780 if (all_fmt & LIST_MODEBITS)
781 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
782 if (all_fmt & LIST_NLINKS)
783 column += printf("%4lu ", (long) dn->dstat.st_nlink);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000784#if ENABLE_FEATURE_LS_USERNAME
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000785 if (all_fmt & LIST_ID_NAME) {
786 if (option_mask32 & OPT_g) {
787 column += printf("%-8.8s",
788 get_cached_username(dn->dstat.st_uid));
789 } else {
790 column += printf("%-8.8s %-8.8s",
Denis Vlasenko2405ad62007-01-19 21:24:17 +0000791 get_cached_username(dn->dstat.st_uid),
792 get_cached_groupname(dn->dstat.st_gid));
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000793 }
794 }
Eric Andersen11c65522000-09-07 17:24:47 +0000795#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000796 if (all_fmt & LIST_ID_NUMERIC) {
797 if (option_mask32 & OPT_g)
798 column += printf("%-8u", (int) dn->dstat.st_uid);
799 else
800 column += printf("%-8u %-8u",
801 (int) dn->dstat.st_uid,
802 (int) dn->dstat.st_gid);
803 }
804 if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
805 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
806 column += printf("%4u, %3u ",
807 (int) major(dn->dstat.st_rdev),
808 (int) minor(dn->dstat.st_rdev));
809 } else {
810 if (all_fmt & LS_DISP_HR) {
811 column += printf("%9s ",
812 make_human_readable_str(dn->dstat.st_size, 1, 0));
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000813 } else {
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000814 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000815 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000816 }
817 }
Denis Vlasenko5c759602006-10-28 12:37:16 +0000818#if ENABLE_FEATURE_LS_TIMESTAMPS
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000819 if (all_fmt & LIST_FULLTIME)
820 column += printf("%24.24s ", filetime);
821 if (all_fmt & LIST_DATE_TIME)
822 if ((all_fmt & LIST_FULLTIME) == 0) {
823 /* current_time_t ~== time(NULL) */
824 age = current_time_t - ttime;
825 printf("%6.6s ", filetime + 4);
826 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
827 /* hh:mm if less than 6 months old */
828 printf("%5.5s ", filetime + 11);
829 } else {
830 printf(" %4.4s ", filetime + 20);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000831 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000832 column += 13;
833 }
Eric Andersen11c65522000-09-07 17:24:47 +0000834#endif
Denis Vlasenko5c759602006-10-28 12:37:16 +0000835#if ENABLE_SELINUX
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000836 if (all_fmt & LIST_CONTEXT) {
837 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
838 freecon(dn->sid);
839 }
Eric Andersen9e480452003-07-03 10:07:04 +0000840#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000841 if (all_fmt & LIST_FILENAME) {
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000842#if ENABLE_FEATURE_LS_COLOR
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000843 if (show_color) {
844 info.st_mode = 0; /* for fgcolor() */
845 lstat(dn->fullname, &info);
846 printf("\033[%u;%um", bold(info.st_mode),
847 fgcolor(info.st_mode));
848 }
849#endif
850 column += print_name(dn->name);
851 if (show_color) {
852 printf("\033[0m");
853 }
854 }
855 if (all_fmt & LIST_SYMLINK) {
856 if (S_ISLNK(dn->dstat.st_mode) && lpath) {
857 printf(" -> ");
858#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
859#if ENABLE_FEATURE_LS_COLOR
860 info.st_mode = 0; /* for fgcolor() */
861#endif
862 if (stat(dn->fullname, &info) == 0) {
863 append = append_char(info.st_mode);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000864 }
Denis Vlasenkoc61852a2006-11-29 11:09:43 +0000865#endif
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000866#if ENABLE_FEATURE_LS_COLOR
867 if (show_color) {
868 printf("\033[%u;%um", bold(info.st_mode),
869 fgcolor(info.st_mode));
870 }
871#endif
872 column += print_name(lpath) + 4;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +0000873 if (show_color) {
874 printf("\033[0m");
875 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000876 free(lpath);
Eric Andersen11c65522000-09-07 17:24:47 +0000877 }
878 }
Denis Vlasenko3a014b82009-03-21 19:11:23 +0000879#if ENABLE_FEATURE_LS_FILETYPES
880 if (all_fmt & LIST_FILETYPE) {
881 if (append) {
882 putchar(append);
883 column++;
884 }
885 }
886#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000887
Glenn L McGrath4d001292003-01-06 01:11:50 +0000888 return column;
Eric Andersen11c65522000-09-07 17:24:47 +0000889}
890
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000891
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000892/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
893#if ENABLE_FEATURE_LS_COLOR
894/* long option entry used only for --color, which has no short option
895 * equivalent */
896static const char ls_color_opt[] ALIGN1 =
897 "color\0" Optional_argument "\xff" /* no short equivalent */
898 ;
899#endif
900
Denis Vlasenko97fd6d82007-03-19 20:59:20 +0000901
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000902int ls_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen11c65522000-09-07 17:24:47 +0000903{
Glenn L McGrath792cae52004-01-18 05:15:16 +0000904 struct dnode **dnd;
905 struct dnode **dnf;
906 struct dnode **dnp;
907 struct dnode *dn;
908 struct dnode *cur;
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000909 unsigned opt;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000910 int nfiles;
Glenn L McGrath792cae52004-01-18 05:15:16 +0000911 int dnfiles;
912 int dndirs;
Glenn L McGrath792cae52004-01-18 05:15:16 +0000913 int i;
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000914 /* need to initialize since --color has _an optional_ argument */
915 USE_FEATURE_LS_COLOR(const char *color_opt = "always";)
Glenn L McGrath792cae52004-01-18 05:15:16 +0000916
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000917 INIT_G();
Denis Vlasenkoe0554432007-01-19 22:03:06 +0000918
Rob Landley2b8a05a2006-06-20 17:43:01 +0000919 all_fmt = LIST_SHORT |
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000920 (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000921
Denis Vlasenko5c759602006-10-28 12:37:16 +0000922#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko656f7462006-10-28 13:02:55 +0000923 /* Obtain the terminal width */
Denis Vlasenkof893da82007-08-09 08:27:24 +0000924 get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
Eric Andersen8efe9672003-09-15 08:33:45 +0000925 /* Go one less... */
926 terminal_width--;
Eric Andersen6d687812003-11-04 23:16:48 +0000927#endif
Eric Andersen11c65522000-09-07 17:24:47 +0000928
Eric Andersen11c65522000-09-07 17:24:47 +0000929 /* process options */
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000930 USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000931#if ENABLE_FEATURE_AUTOWIDTH
Denis Vlasenko1d426652008-03-17 09:09:09 +0000932 opt_complementary = "T+:w+"; /* -T N, -w N */
933 opt = getopt32(argv, ls_options, &tabstops, &terminal_width
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000934 USE_FEATURE_LS_COLOR(, &color_opt));
Glenn L McGrath792cae52004-01-18 05:15:16 +0000935#else
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000936 opt = getopt32(argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));
Manuel Novoa III cad53642003-03-19 09:13:01 +0000937#endif
Eric Andersend07cf592004-02-05 13:52:03 +0000938 for (i = 0; opt_flags[i] != (1U<<31); i++) {
Glenn L McGrath792cae52004-01-18 05:15:16 +0000939 if (opt & (1 << i)) {
Denis Vlasenko5c759602006-10-28 12:37:16 +0000940 unsigned flags = opt_flags[i];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000941
Denis Vlasenko5c759602006-10-28 12:37:16 +0000942 if (flags & LIST_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000943 all_fmt &= ~LIST_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000944 if (flags & STYLE_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000945 all_fmt &= ~STYLE_MASK;
Denis Vlasenko94cf69f2006-10-28 12:37:51 +0000946 if (flags & SORT_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000947 all_fmt &= ~SORT_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000948 if (flags & DISP_MASK_TRIGGER)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000949 all_fmt &= ~DISP_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000950 if (flags & TIME_MASK)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000951 all_fmt &= ~TIME_MASK;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000952 if (flags & LIST_CONTEXT)
Eric Andersen9e480452003-07-03 10:07:04 +0000953 all_fmt |= STYLE_SINGLE;
Denis Vlasenko2110aa92007-02-28 23:14:06 +0000954 /* huh?? opt cannot be 'l' */
955 //if (LS_DISP_HR && opt == 'l')
956 // all_fmt &= ~LS_DISP_HR;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000957 all_fmt |= flags;
958 }
Eric Andersen11c65522000-09-07 17:24:47 +0000959 }
960
Denis Vlasenko5c759602006-10-28 12:37:16 +0000961#if ENABLE_FEATURE_LS_COLOR
962 /* find color bit value - last position for short getopt */
963 if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
964 char *p = getenv("LS_COLORS");
965 /* LS_COLORS is unset, or (not empty && not "none") ? */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000966 if (!p || (p[0] && strcmp(p, "none") != 0))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000967 show_color = 1;
Denis Vlasenko5c759602006-10-28 12:37:16 +0000968 }
969 if (opt & (1 << i)) { /* next flag after short options */
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000970 if (strcmp("always", color_opt) == 0)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000971 show_color = 1;
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000972 else if (strcmp("never", color_opt) == 0)
Denis Vlasenko5c759602006-10-28 12:37:16 +0000973 show_color = 0;
Denis Vlasenko51f1b6c2008-07-15 05:21:47 +0000974 else if (strcmp("auto", color_opt) == 0 && isatty(STDOUT_FILENO))
Denis Vlasenko5c759602006-10-28 12:37:16 +0000975 show_color = 1;
Paul Fox156dc412005-08-01 19:33:30 +0000976 }
977#endif
978
Eric Andersen11c65522000-09-07 17:24:47 +0000979 /* sort out which command line options take precedence */
Denis Vlasenko5c759602006-10-28 12:37:16 +0000980 if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000981 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
Rob Landleyea224be2006-06-18 20:20:07 +0000982 if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
983 if (all_fmt & TIME_CHANGE)
984 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
985 if (all_fmt & TIME_ACCESS)
986 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
987 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000988 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
989 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
Denis Vlasenko5c759602006-10-28 12:37:16 +0000990 if (ENABLE_FEATURE_LS_USERNAME)
991 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
992 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000993
Eric Andersen11c65522000-09-07 17:24:47 +0000994 /* choose a display format */
Rob Landleyea224be2006-06-18 20:20:07 +0000995 if (!(all_fmt & STYLE_MASK))
Eric Andersen70060d22004-03-27 10:02:48 +0000996 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
Eric Andersen11c65522000-09-07 17:24:47 +0000997
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +0000998 argv += optind;
999 if (!argv[0])
1000 *--argv = (char*)".";
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001001
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001002 if (argv[1])
1003 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
Eric Andersen11c65522000-09-07 17:24:47 +00001004
Denis Vlasenko5c759602006-10-28 12:37:16 +00001005 /* stuff the command line file names into a dnode array */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001006 dn = NULL;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001007 nfiles = 0;
1008 do {
Denis Vlasenko11a6f9b2009-03-03 13:20:22 +00001009 /* NB: follow links on command line unless -l or -s */
1010 cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001011 argv++;
Glenn L McGrath4d001292003-01-06 01:11:50 +00001012 if (!cur)
Eric Andersen11c65522000-09-07 17:24:47 +00001013 continue;
"Vladimir N. Oleynik"a8c23aa2005-09-05 15:06:57 +00001014 cur->allocated = 0;
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001015 cur->next = dn;
1016 dn = cur;
Eric Andersen11c65522000-09-07 17:24:47 +00001017 nfiles++;
Denis Vlasenkoc7497ea2008-06-13 11:16:09 +00001018 } while (*argv);
Eric Andersen11c65522000-09-07 17:24:47 +00001019
1020 /* now that we know how many files there are
Denis Vlasenko656f7462006-10-28 13:02:55 +00001021 * allocate memory for an array to hold dnode pointers
1022 */
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001023 dnp = dnalloc(nfiles);
1024 for (i = 0, cur = dn; i < nfiles; i++) {
1025 dnp[i] = cur; /* save pointer to node in array */
1026 cur = cur->next;
Eric Andersen11c65522000-09-07 17:24:47 +00001027 }
1028
Manuel Novoa III cad53642003-03-19 09:13:01 +00001029 if (all_fmt & DISP_NOLIST) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001030 dnsort(dnp, nfiles);
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001031 if (nfiles > 0)
1032 showfiles(dnp, nfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001033 } else {
Glenn L McGrathe3906fc2002-08-22 18:13:54 +00001034 dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
1035 dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
1036 dndirs = countdirs(dnp, nfiles);
1037 dnfiles = nfiles - dndirs;
Eric Andersen11c65522000-09-07 17:24:47 +00001038 if (dnfiles > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001039 dnsort(dnf, dnfiles);
Eric Andersen11c65522000-09-07 17:24:47 +00001040 showfiles(dnf, dnfiles);
Rob Landley26314862006-05-02 19:46:52 +00001041 if (ENABLE_FEATURE_CLEAN_UP)
1042 free(dnf);
Eric Andersen11c65522000-09-07 17:24:47 +00001043 }
1044 if (dndirs > 0) {
Denis Vlasenko94cf69f2006-10-28 12:37:51 +00001045 dnsort(dnd, dndirs);
Glenn L McGrathc4db0832004-03-06 09:12:55 +00001046 showdirs(dnd, dndirs, dnfiles == 0);
Rob Landley26314862006-05-02 19:46:52 +00001047 if (ENABLE_FEATURE_CLEAN_UP)
1048 free(dnd);
Eric Andersen11c65522000-09-07 17:24:47 +00001049 }
1050 }
Rob Landley26314862006-05-02 19:46:52 +00001051 if (ENABLE_FEATURE_CLEAN_UP)
1052 dfree(dnp, nfiles);
Denis Vlasenko4a689e92008-06-18 16:38:22 +00001053 return exit_code;
Eric Andersencc8ed391999-10-05 16:24:54 +00001054}