*: add most of the required setup_common_bufsiz() calls
[oweals/busybox.git] / coreutils / ls.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
4  *
5  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
6  */
7
8 /* [date unknown. Perhaps before year 2000]
9  * To achieve a small memory footprint, this version of 'ls' doesn't do any
10  * file sorting, and only has the most essential command line switches
11  * (i.e., the ones I couldn't live without :-) All features which involve
12  * linking in substantial chunks of libc can be disabled.
13  *
14  * Although I don't really want to add new features to this program to
15  * keep it small, I *am* interested to receive bug fixes and ways to make
16  * it more portable.
17  *
18  * KNOWN BUGS:
19  * 1. hidden files can make column width too large
20  *
21  * NON-OPTIMAL BEHAVIOUR:
22  * 1. autowidth reads directories twice
23  * 2. if you do a short directory listing without filetype characters
24  *    appended, there's no need to stat each one
25  * PORTABILITY:
26  * 1. requires lstat (BSD) - how do you do it without?
27  *
28  * [2009-03]
29  * ls sorts listing now, and supports almost all options.
30  */
31
32 //usage:#define ls_trivial_usage
33 //usage:        "[-1AaCxd"
34 //usage:        IF_FEATURE_LS_FOLLOWLINKS("LH")
35 //usage:        IF_FEATURE_LS_RECURSIVE("R")
36 //usage:        IF_FEATURE_LS_FILETYPES("Fp") "lins"
37 //usage:        IF_FEATURE_LS_TIMESTAMPS("e")
38 //usage:        IF_FEATURE_HUMAN_READABLE("h")
39 //usage:        IF_FEATURE_LS_SORTFILES("rSXv")
40 //usage:        IF_FEATURE_LS_TIMESTAMPS("ctu")
41 //usage:        IF_SELINUX("kKZ") "]"
42 //usage:        IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..."
43 //usage:#define ls_full_usage "\n\n"
44 //usage:       "List directory contents\n"
45 //usage:     "\n        -1      One column output"
46 //usage:     "\n        -a      Include entries which start with ."
47 //usage:     "\n        -A      Like -a, but exclude . and .."
48 //usage:     "\n        -C      List by columns"
49 //usage:     "\n        -x      List by lines"
50 //usage:     "\n        -d      List directory entries instead of contents"
51 //usage:        IF_FEATURE_LS_FOLLOWLINKS(
52 //usage:     "\n        -L      Follow symlinks"
53 //usage:     "\n        -H      Follow symlinks on command line"
54 //usage:        )
55 //usage:        IF_FEATURE_LS_RECURSIVE(
56 //usage:     "\n        -R      Recurse"
57 //usage:        )
58 //usage:        IF_FEATURE_LS_FILETYPES(
59 //usage:     "\n        -p      Append / to dir entries"
60 //usage:     "\n        -F      Append indicator (one of */=@|) to entries"
61 //usage:        )
62 //usage:     "\n        -l      Long listing format"
63 //usage:     "\n        -i      List inode numbers"
64 //usage:     "\n        -n      List numeric UIDs and GIDs instead of names"
65 //usage:     "\n        -s      List allocated blocks"
66 //usage:        IF_FEATURE_LS_TIMESTAMPS(
67 //usage:     "\n        -e      List full date and time"
68 //usage:        )
69 //usage:        IF_FEATURE_HUMAN_READABLE(
70 //usage:     "\n        -h      List sizes in human readable format (1K 243M 2G)"
71 //usage:        )
72 //usage:        IF_FEATURE_LS_SORTFILES(
73 //usage:     "\n        -r      Sort in reverse order"
74 //usage:     "\n        -S      Sort by size"
75 //usage:     "\n        -X      Sort by extension"
76 //usage:     "\n        -v      Sort by version"
77 //usage:        )
78 //usage:        IF_FEATURE_LS_TIMESTAMPS(
79 //usage:     "\n        -c      With -l: sort by ctime"
80 //usage:     "\n        -t      With -l: sort by mtime"
81 //usage:     "\n        -u      With -l: sort by atime"
82 //usage:        )
83 //usage:        IF_SELINUX(
84 //usage:     "\n        -k      List security context"
85 //usage:     "\n        -K      List security context in long format"
86 //usage:     "\n        -Z      List security context and permission"
87 //usage:        )
88 //usage:        IF_FEATURE_AUTOWIDTH(
89 //usage:     "\n        -w N    Assume the terminal is N columns wide"
90 //usage:        )
91 //usage:        IF_FEATURE_LS_COLOR(
92 //usage:     "\n        --color[={always,never,auto}]   Control coloring"
93 //usage:        )
94
95 #include "libbb.h"
96 #include "common_bufsiz.h"
97 #include "unicode.h"
98
99
100 /* This is a NOEXEC applet. Be very careful! */
101
102
103 #if ENABLE_FTPD
104 /* ftpd uses ls, and without timestamps Mozilla won't understand
105  * ftpd's LIST output.
106  */
107 # undef CONFIG_FEATURE_LS_TIMESTAMPS
108 # undef ENABLE_FEATURE_LS_TIMESTAMPS
109 # undef IF_FEATURE_LS_TIMESTAMPS
110 # undef IF_NOT_FEATURE_LS_TIMESTAMPS
111 # define CONFIG_FEATURE_LS_TIMESTAMPS 1
112 # define ENABLE_FEATURE_LS_TIMESTAMPS 1
113 # define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
114 # define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
115 #endif
116
117
118 enum {
119 TERMINAL_WIDTH  = 80,           /* use 79 if terminal has linefold bug */
120
121 SPLIT_FILE      = 0,
122 SPLIT_DIR       = 1,
123 SPLIT_SUBDIR    = 2,
124
125 /* Bits in G.all_fmt: */
126
127 /* 51306 lrwxrwxrwx  1 root     root         2 May 11 01:43 /bin/view -> vi* */
128 /* what file information will be listed */
129 LIST_INO        = 1 << 0,
130 LIST_BLOCKS     = 1 << 1,
131 LIST_MODEBITS   = 1 << 2,
132 LIST_NLINKS     = 1 << 3,
133 LIST_ID_NAME    = 1 << 4,
134 LIST_ID_NUMERIC = 1 << 5,
135 LIST_CONTEXT    = 1 << 6,
136 LIST_SIZE       = 1 << 7,
137 LIST_DATE_TIME  = 1 << 8,
138 LIST_FULLTIME   = 1 << 9,
139 LIST_SYMLINK    = 1 << 10,
140 LIST_FILETYPE   = 1 << 11, /* show / suffix for dirs */
141 LIST_CLASSIFY   = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */
142 LIST_MASK       = (LIST_CLASSIFY << 1) - 1,
143
144 /* what files will be displayed */
145 DISP_DIRNAME    = 1 << 13,      /* 2 or more items? label directories */
146 DISP_HIDDEN     = 1 << 14,      /* show filenames starting with . */
147 DISP_DOT        = 1 << 15,      /* show . and .. */
148 DISP_NOLIST     = 1 << 16,      /* show directory as itself, not contents */
149 DISP_RECURSIVE  = 1 << 17,      /* show directory and everything below it */
150 DISP_ROWS       = 1 << 18,      /* print across rows */
151 DISP_MASK       = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
152
153 /* what is the overall style of the listing */
154 STYLE_COLUMNAR  = 1 << 19,      /* many records per line */
155 STYLE_LONG      = 2 << 19,      /* one record per line, extended info */
156 STYLE_SINGLE    = 3 << 19,      /* one record per line */
157 STYLE_MASK      = STYLE_SINGLE,
158
159 /* which of the three times will be used */
160 TIME_CHANGE     = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
161 TIME_ACCESS     = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
162 TIME_MASK       = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
163
164 /* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
165 SORT_REVERSE    = 1 << 23,
166
167 SORT_NAME       = 0,            /* sort by file name */
168 SORT_SIZE       = 1 << 24,      /* sort by file size */
169 SORT_ATIME      = 2 << 24,      /* sort by last access time */
170 SORT_CTIME      = 3 << 24,      /* sort by last change time */
171 SORT_MTIME      = 4 << 24,      /* sort by last modification time */
172 SORT_VERSION    = 5 << 24,      /* sort by version */
173 SORT_EXT        = 6 << 24,      /* sort by file name extension */
174 SORT_DIR        = 7 << 24,      /* sort by file or directory */
175 SORT_MASK       = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES,
176
177 LIST_LONG       = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
178                   LIST_DATE_TIME | LIST_SYMLINK,
179 };
180
181 /* -Cadil1  Std options, busybox always supports */
182 /* -gnsxA   Std options, busybox always supports */
183 /* -Q       GNU option, busybox always supports */
184 /* -k       SELinux option, busybox always supports (ignores if !SELinux) */
185 /*          Std has -k which means "show sizes in kbytes" */
186 /* -LHRctur Std options, busybox optionally supports */
187 /* -Fp      Std options, busybox optionally supports */
188 /* -SXvhTw  GNU options, busybox optionally supports */
189 /* -T WIDTH Ignored (we don't use tabs on output) */
190 /* -KZ      SELinux mandated options, busybox optionally supports */
191 /*          (coreutils 8.4 has no -K, remove it?) */
192 /* -e       I think we made this one up (looks similar to GNU --full-time) */
193 /* We already used up all 32 bits, if we need to add more, candidates for removal: */
194 /* -K, -T, -e (add --full-time instead) */
195 static const char ls_options[] ALIGN1 =
196         "Cadil1gnsxQAk"      /* 13 opts, total 13 */
197         IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
198         IF_FEATURE_LS_SORTFILES("SXrv")  /* 4, 21 */
199         IF_FEATURE_LS_FILETYPES("Fp")    /* 2, 23 */
200         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 */
205         /* with --color, we use all 32 bits */;
206 enum {
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),
220
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 */
236         OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
237         OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
238         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,
260 };
261
262 /* TODO: simple toggles may be stored as OPT_xxx bits instead */
263 static const uint32_t opt_flags[] = {
264         STYLE_COLUMNAR,              /* C */
265         DISP_HIDDEN | DISP_DOT,      /* a */
266         DISP_NOLIST,                 /* d */
267         LIST_INO,                    /* i */
268         LIST_LONG | STYLE_LONG,      /* l */
269         STYLE_SINGLE,                /* 1 */
270         LIST_LONG | STYLE_LONG,      /* g (don't show owner) - handled via OPT_g. assumes l */
271         LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */
272         LIST_BLOCKS,                 /* s */
273         DISP_ROWS | STYLE_COLUMNAR,  /* x */
274         0,                           /* Q (quote filename) - handled via OPT_Q */
275         DISP_HIDDEN,                 /* A */
276         ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */
277 #if ENABLE_FEATURE_LS_TIMESTAMPS
278         TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
279         LIST_FULLTIME,               /* e */
280         ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
281         TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
282 #endif
283 #if ENABLE_FEATURE_LS_SORTFILES
284         SORT_SIZE,                   /* S */
285         SORT_EXT,                    /* X */
286         SORT_REVERSE,                /* r */
287         SORT_VERSION,                /* v */
288 #endif
289 #if ENABLE_FEATURE_LS_FILETYPES
290         LIST_FILETYPE | LIST_CLASSIFY, /* F */
291         LIST_FILETYPE,               /* p */
292 #endif
293 #if ENABLE_FEATURE_LS_RECURSIVE
294         DISP_RECURSIVE,              /* R */
295 #endif
296 #if ENABLE_SELINUX
297         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 */
299 #endif
300         (1U << 31)
301         /* options after Z are not processed through opt_flags */
302 };
303
304
305 /*
306  * a directory entry and its stat info
307  */
308 struct dnode {
309         const char *name;       /* usually basename, but think "ls -l dir/file" */
310         const char *fullname;   /* full name (usable for stat etc) */
311         struct dnode *dn_next;  /* for linked list */
312         IF_SELINUX(security_context_t sid;)
313         smallint fname_allocated;
314
315         /* Used to avoid re-doing [l]stat at printout stage
316          * if we already collected needed data in scan stage:
317          */
318         mode_t    dn_mode_lstat;   /* obtained with lstat, or 0 */
319         mode_t    dn_mode_stat;    /* obtained with stat, or 0 */
320
321 //      struct stat dstat;
322 // struct stat is huge. We don't need it in full.
323 // At least we don't need st_dev and st_blksize,
324 // but there are invisible fields as well
325 // (such as nanosecond-resolution timespamps)
326 // and padding, which we also don't want to store.
327 // We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
328 // On 32-bit uclibc: dnode size went from 112 to 84 bytes.
329 //
330         /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
331         mode_t    dn_mode; /* obtained with lstat OR stat, depending on -L etc */
332         off_t     dn_size;
333 #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
334         time_t    dn_atime;
335         time_t    dn_mtime;
336         time_t    dn_ctime;
337 #endif
338         ino_t     dn_ino;
339         blkcnt_t  dn_blocks;
340         nlink_t   dn_nlink;
341         uid_t     dn_uid;
342         gid_t     dn_gid;
343         int       dn_rdev_maj;
344         int       dn_rdev_min;
345 //      dev_t     dn_dev;
346 //      blksize_t dn_blksize;
347 };
348
349 struct globals {
350 #if ENABLE_FEATURE_LS_COLOR
351         smallint show_color;
352 # define G_show_color (G.show_color)
353 #else
354 # define G_show_color 0
355 #endif
356         smallint exit_code;
357         unsigned all_fmt;
358 #if ENABLE_FEATURE_AUTOWIDTH
359         unsigned terminal_width;
360 # define G_terminal_width (G.terminal_width)
361 #else
362 # define G_terminal_width TERMINAL_WIDTH
363 #endif
364 #if ENABLE_FEATURE_LS_TIMESTAMPS
365         /* Do time() just once. Saves one syscall per file for "ls -l" */
366         time_t current_time_t;
367 #endif
368 } FIX_ALIASING;
369 #define G (*(struct globals*)bb_common_bufsiz1)
370 #define INIT_G() do { \
371         setup_common_bufsiz(); \
372         /* we have to zero it out because of NOEXEC */ \
373         memset(&G, 0, sizeof(G)); \
374         IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
375         IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
376 } while (0)
377
378
379 /*** Output code ***/
380
381
382 /* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
383  * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
384  *  3/7:multiplexed char/block device)
385  * and we use 0 for unknown and 15 for executables (see below) */
386 #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
387 /*                       un  fi chr -   dir -  blk  -  file -  link - sock -   - exe */
388 #define APPCHAR(mode)   ("\0""|""\0""\0""/""\0""\0""\0""\0""\0""@""\0""=""\0""\0""\0" [TYPEINDEX(mode)])
389 /* 036 black foreground              050 black background
390    037 red foreground                051 red background
391    040 green foreground              052 green background
392    041 brown foreground              053 brown background
393    042 blue foreground               054 blue background
394    043 magenta (purple) foreground   055 magenta background
395    044 cyan (light blue) foreground  056 cyan background
396    045 gray foreground               057 white background
397 */
398 #define COLOR(mode) ( \
399         /*un  fi  chr  -  dir  -  blk  -  file -  link -  sock -   -  exe */ \
400         "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
401         [TYPEINDEX(mode)])
402 /* Select normal (0) [actually "reset all"] or bold (1)
403  * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
404  *  let's use 7 for "impossible" types, just for fun)
405  * Note: coreutils 6.9 uses inverted red for setuid binaries.
406  */
407 #define ATTR(mode) ( \
408         /*un fi chr - dir - blk - file- link- sock- -  exe */ \
409         "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
410         [TYPEINDEX(mode)])
411
412 #if ENABLE_FEATURE_LS_COLOR
413 /* mode of zero is interpreted as "unknown" (stat failed) */
414 static char fgcolor(mode_t mode)
415 {
416         if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
417                 return COLOR(0xF000);   /* File is executable ... */
418         return COLOR(mode);
419 }
420 static char bold(mode_t mode)
421 {
422         if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
423                 return ATTR(0xF000);    /* File is executable ... */
424         return ATTR(mode);
425 }
426 #endif
427
428 #if ENABLE_FEATURE_LS_FILETYPES
429 static char append_char(mode_t mode)
430 {
431         if (!(G.all_fmt & LIST_FILETYPE))
432                 return '\0';
433         if (S_ISDIR(mode))
434                 return '/';
435         if (!(G.all_fmt & LIST_CLASSIFY))
436                 return '\0';
437         if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
438                 return '*';
439         return APPCHAR(mode);
440 }
441 #endif
442
443 static unsigned calc_name_len(const char *name)
444 {
445         unsigned len;
446         uni_stat_t uni_stat;
447
448         // TODO: quote tab as \t, etc, if -Q
449         name = printable_string(&uni_stat, name);
450
451         if (!(option_mask32 & OPT_Q)) {
452                 return uni_stat.unicode_width;
453         }
454
455         len = 2 + uni_stat.unicode_width;
456         while (*name) {
457                 if (*name == '"' || *name == '\\') {
458                         len++;
459                 }
460                 name++;
461         }
462         return len;
463 }
464
465 /* Return the number of used columns.
466  * Note that only STYLE_COLUMNAR uses return value.
467  * STYLE_SINGLE and STYLE_LONG don't care.
468  * coreutils 7.2 also supports:
469  * ls -b (--escape) = octal escapes (although it doesn't look like working)
470  * ls -N (--literal) = not escape at all
471  */
472 static unsigned print_name(const char *name)
473 {
474         unsigned len;
475         uni_stat_t uni_stat;
476
477         // TODO: quote tab as \t, etc, if -Q
478         name = printable_string(&uni_stat, name);
479
480         if (!(option_mask32 & OPT_Q)) {
481                 fputs(name, stdout);
482                 return uni_stat.unicode_width;
483         }
484
485         len = 2 + uni_stat.unicode_width;
486         putchar('"');
487         while (*name) {
488                 if (*name == '"' || *name == '\\') {
489                         putchar('\\');
490                         len++;
491                 }
492                 putchar(*name);
493                 name++;
494         }
495         putchar('"');
496         return len;
497 }
498
499 /* Return the number of used columns.
500  * Note that only STYLE_COLUMNAR uses return value,
501  * STYLE_SINGLE and STYLE_LONG don't care.
502  */
503 static NOINLINE unsigned display_single(const struct dnode *dn)
504 {
505         unsigned column = 0;
506         char *lpath;
507 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
508         struct stat statbuf;
509         char append;
510 #endif
511
512 #if ENABLE_FEATURE_LS_FILETYPES
513         append = append_char(dn->dn_mode);
514 #endif
515
516         /* Do readlink early, so that if it fails, error message
517          * does not appear *inside* the "ls -l" line */
518         lpath = NULL;
519         if (G.all_fmt & LIST_SYMLINK)
520                 if (S_ISLNK(dn->dn_mode))
521                         lpath = xmalloc_readlink_or_warn(dn->fullname);
522
523         if (G.all_fmt & LIST_INO)
524                 column += printf("%7llu ", (long long) dn->dn_ino);
525 //TODO: -h should affect -s too:
526         if (G.all_fmt & LIST_BLOCKS)
527                 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
528         if (G.all_fmt & LIST_MODEBITS)
529                 column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
530         if (G.all_fmt & LIST_NLINKS)
531                 column += printf("%4lu ", (long) dn->dn_nlink);
532         if (G.all_fmt & LIST_ID_NUMERIC) {
533                 if (option_mask32 & OPT_g)
534                         column += printf("%-8u ", (int) dn->dn_gid);
535                 else
536                         column += printf("%-8u %-8u ",
537                                         (int) dn->dn_uid,
538                                         (int) dn->dn_gid);
539         }
540 #if ENABLE_FEATURE_LS_USERNAME
541         else if (G.all_fmt & LIST_ID_NAME) {
542                 if (option_mask32 & OPT_g) {
543                         column += printf("%-8.8s ",
544                                 get_cached_groupname(dn->dn_gid));
545                 } else {
546                         column += printf("%-8.8s %-8.8s ",
547                                 get_cached_username(dn->dn_uid),
548                                 get_cached_groupname(dn->dn_gid));
549                 }
550         }
551 #endif
552         if (G.all_fmt & LIST_SIZE) {
553                 if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
554                         column += printf("%4u, %3u ",
555                                         dn->dn_rdev_maj,
556                                         dn->dn_rdev_min);
557                 } else {
558                         if (option_mask32 & OPT_h) {
559                                 column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
560                                         /* print size, show one fractional, use suffixes */
561                                         make_human_readable_str(dn->dn_size, 1, 0)
562                                 );
563                         } else {
564                                 column += printf("%9"OFF_FMT"u ", dn->dn_size);
565                         }
566                 }
567         }
568 #if ENABLE_FEATURE_LS_TIMESTAMPS
569         if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
570                 char *filetime;
571                 const time_t *ttime = &dn->dn_mtime;
572                 if (G.all_fmt & TIME_ACCESS)
573                         ttime = &dn->dn_atime;
574                 if (G.all_fmt & TIME_CHANGE)
575                         ttime = &dn->dn_ctime;
576                 filetime = ctime(ttime);
577                 /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
578                 if (G.all_fmt & LIST_FULLTIME) { /* -e */
579                         /* Note: coreutils 8.4 ls --full-time prints:
580                          * 2009-07-13 17:49:27.000000000 +0200
581                          */
582                         column += printf("%.24s ", filetime);
583                 } else { /* LIST_DATE_TIME */
584                         /* G.current_time_t ~== time(NULL) */
585                         time_t age = G.current_time_t - *ttime;
586                         if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
587                                 /* less than 6 months old */
588                                 /* "mmm dd hh:mm " */
589                                 printf("%.12s ", filetime + 4);
590                         } else {
591                                 /* "mmm dd  yyyy " */
592                                 /* "mmm dd yyyyy " after year 9999 :) */
593                                 strchr(filetime + 20, '\n')[0] = ' ';
594                                 printf("%.7s%6s", filetime + 4, filetime + 20);
595                         }
596                         column += 13;
597                 }
598         }
599 #endif
600 #if ENABLE_SELINUX
601         if (G.all_fmt & LIST_CONTEXT) {
602                 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
603                 freecon(dn->sid);
604         }
605 #endif
606
607 #if ENABLE_FEATURE_LS_COLOR
608         if (G_show_color) {
609                 mode_t mode = dn->dn_mode_lstat;
610                 if (!mode)
611                         if (lstat(dn->fullname, &statbuf) == 0)
612                                 mode = statbuf.st_mode;
613                 printf("\033[%u;%um", bold(mode), fgcolor(mode));
614         }
615 #endif
616         column += print_name(dn->name);
617         if (G_show_color) {
618                 printf("\033[0m");
619         }
620
621         if (lpath) {
622                 printf(" -> ");
623 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
624                 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
625                         mode_t mode = dn->dn_mode_stat;
626                         if (!mode)
627                                 if (stat(dn->fullname, &statbuf) == 0)
628                                         mode = statbuf.st_mode;
629 # if ENABLE_FEATURE_LS_FILETYPES
630                         append = append_char(mode);
631 # endif
632 # if ENABLE_FEATURE_LS_COLOR
633                         if (G_show_color) {
634                                 printf("\033[%u;%um", bold(mode), fgcolor(mode));
635                         }
636 # endif
637                 }
638 #endif
639                 column += print_name(lpath) + 4;
640                 free(lpath);
641                 if (G_show_color) {
642                         printf("\033[0m");
643                 }
644         }
645 #if ENABLE_FEATURE_LS_FILETYPES
646         if (G.all_fmt & LIST_FILETYPE) {
647                 if (append) {
648                         putchar(append);
649                         column++;
650                 }
651         }
652 #endif
653
654         return column;
655 }
656
657 static void display_files(struct dnode **dn, unsigned nfiles)
658 {
659         unsigned i, ncols, nrows, row, nc;
660         unsigned column;
661         unsigned nexttab;
662         unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
663
664         if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
665                 ncols = 1;
666         } else {
667                 /* find the longest file name, use that as the column width */
668                 for (i = 0; dn[i]; i++) {
669                         int len = calc_name_len(dn[i]->name);
670                         if (column_width < len)
671                                 column_width = len;
672                 }
673                 column_width += 2 +
674                         IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
675                                 ((G.all_fmt & LIST_INO) ? 8 : 0) +
676                                 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
677                 ncols = (unsigned)G_terminal_width / column_width;
678         }
679
680         if (ncols > 1) {
681                 nrows = nfiles / ncols;
682                 if (nrows * ncols < nfiles)
683                         nrows++;                /* round up fractionals */
684         } else {
685                 nrows = nfiles;
686                 ncols = 1;
687         }
688
689         column = 0;
690         nexttab = 0;
691         for (row = 0; row < nrows; row++) {
692                 for (nc = 0; nc < ncols; nc++) {
693                         /* reach into the array based on the column and row */
694                         if (G.all_fmt & DISP_ROWS)
695                                 i = (row * ncols) + nc; /* display across row */
696                         else
697                                 i = (nc * nrows) + row; /* display by column */
698                         if (i < nfiles) {
699                                 if (column > 0) {
700                                         nexttab -= column;
701                                         printf("%*s", nexttab, "");
702                                         column += nexttab;
703                                 }
704                                 nexttab = column + column_width;
705                                 column += display_single(dn[i]);
706                         }
707                 }
708                 putchar('\n');
709                 column = 0;
710         }
711 }
712
713
714 /*** Dir scanning code ***/
715
716 static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
717 {
718         struct stat statbuf;
719         struct dnode *cur;
720
721         cur = xzalloc(sizeof(*cur));
722         cur->fullname = fullname;
723         cur->name = name;
724
725         if ((option_mask32 & OPT_L) || force_follow) {
726 #if ENABLE_SELINUX
727                 if (is_selinux_enabled())  {
728                         getfilecon(fullname, &cur->sid);
729                 }
730 #endif
731                 if (stat(fullname, &statbuf)) {
732                         bb_simple_perror_msg(fullname);
733                         G.exit_code = EXIT_FAILURE;
734                         free(cur);
735                         return NULL;
736                 }
737                 cur->dn_mode_stat = statbuf.st_mode;
738         } else {
739 #if ENABLE_SELINUX
740                 if (is_selinux_enabled()) {
741                         lgetfilecon(fullname, &cur->sid);
742                 }
743 #endif
744                 if (lstat(fullname, &statbuf)) {
745                         bb_simple_perror_msg(fullname);
746                         G.exit_code = EXIT_FAILURE;
747                         free(cur);
748                         return NULL;
749                 }
750                 cur->dn_mode_lstat = statbuf.st_mode;
751         }
752
753         /* cur->dstat = statbuf: */
754         cur->dn_mode   = statbuf.st_mode  ;
755         cur->dn_size   = statbuf.st_size  ;
756 #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
757         cur->dn_atime  = statbuf.st_atime ;
758         cur->dn_mtime  = statbuf.st_mtime ;
759         cur->dn_ctime  = statbuf.st_ctime ;
760 #endif
761         cur->dn_ino    = statbuf.st_ino   ;
762         cur->dn_blocks = statbuf.st_blocks;
763         cur->dn_nlink  = statbuf.st_nlink ;
764         cur->dn_uid    = statbuf.st_uid   ;
765         cur->dn_gid    = statbuf.st_gid   ;
766         cur->dn_rdev_maj = major(statbuf.st_rdev);
767         cur->dn_rdev_min = minor(statbuf.st_rdev);
768
769         return cur;
770 }
771
772 static unsigned count_dirs(struct dnode **dn, int which)
773 {
774         unsigned dirs, all;
775
776         if (!dn)
777                 return 0;
778
779         dirs = all = 0;
780         for (; *dn; dn++) {
781                 const char *name;
782
783                 all++;
784                 if (!S_ISDIR((*dn)->dn_mode))
785                         continue;
786
787                 name = (*dn)->name;
788                 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
789                  /* or if it's not . or .. */
790                  || name[0] != '.'
791                  || (name[1] && (name[1] != '.' || name[2]))
792                 ) {
793                         dirs++;
794                 }
795         }
796         return which != SPLIT_FILE ? dirs : all - dirs;
797 }
798
799 /* get memory to hold an array of pointers */
800 static struct dnode **dnalloc(unsigned num)
801 {
802         if (num < 1)
803                 return NULL;
804
805         num++; /* so that we have terminating NULL */
806         return xzalloc(num * sizeof(struct dnode *));
807 }
808
809 #if ENABLE_FEATURE_LS_RECURSIVE
810 static void dfree(struct dnode **dnp)
811 {
812         unsigned i;
813
814         if (dnp == NULL)
815                 return;
816
817         for (i = 0; dnp[i]; i++) {
818                 struct dnode *cur = dnp[i];
819                 if (cur->fname_allocated)
820                         free((char*)cur->fullname);
821                 free(cur);
822         }
823         free(dnp);
824 }
825 #else
826 #define dfree(...) ((void)0)
827 #endif
828
829 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
830 static struct dnode **splitdnarray(struct dnode **dn, int which)
831 {
832         unsigned dncnt, d;
833         struct dnode **dnp;
834
835         if (dn == NULL)
836                 return NULL;
837
838         /* count how many dirs or files there are */
839         dncnt = count_dirs(dn, which);
840
841         /* allocate a file array and a dir array */
842         dnp = dnalloc(dncnt);
843
844         /* copy the entrys into the file or dir array */
845         for (d = 0; *dn; dn++) {
846                 if (S_ISDIR((*dn)->dn_mode)) {
847                         const char *name;
848
849                         if (which == SPLIT_FILE)
850                                 continue;
851
852                         name = (*dn)->name;
853                         if ((which & SPLIT_DIR) /* any dir... */
854                         /* ... or not . or .. */
855                          || name[0] != '.'
856                          || (name[1] && (name[1] != '.' || name[2]))
857                         ) {
858                                 dnp[d++] = *dn;
859                         }
860                 } else
861                 if (which == SPLIT_FILE) {
862                         dnp[d++] = *dn;
863                 }
864         }
865         return dnp;
866 }
867
868 #if ENABLE_FEATURE_LS_SORTFILES
869 static int sortcmp(const void *a, const void *b)
870 {
871         struct dnode *d1 = *(struct dnode **)a;
872         struct dnode *d2 = *(struct dnode **)b;
873         unsigned sort_opts = G.all_fmt & SORT_MASK;
874         off_t dif;
875
876         dif = 0; /* assume SORT_NAME */
877         // TODO: use pre-initialized function pointer
878         // instead of branch forest
879         if (sort_opts == SORT_SIZE) {
880                 dif = (d2->dn_size - d1->dn_size);
881         } else
882         if (sort_opts == SORT_ATIME) {
883                 dif = (d2->dn_atime - d1->dn_atime);
884         } else
885         if (sort_opts == SORT_CTIME) {
886                 dif = (d2->dn_ctime - d1->dn_ctime);
887         } else
888         if (sort_opts == SORT_MTIME) {
889                 dif = (d2->dn_mtime - d1->dn_mtime);
890         } else
891         if (sort_opts == SORT_DIR) {
892                 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
893         } else
894 #if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
895         if (sort_opts == SORT_VERSION) {
896                 dif = strverscmp(d1->name, d2->name);
897         } else
898 #endif
899         if (sort_opts == SORT_EXT) {
900                 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
901         }
902         if (dif == 0) {
903                 /* sort by name, use as tie breaker for other sorts */
904                 if (ENABLE_LOCALE_SUPPORT)
905                         dif = strcoll(d1->name, d2->name);
906                 else
907                         dif = strcmp(d1->name, d2->name);
908         }
909
910         /* Make dif fit into an int */
911         if (sizeof(dif) > sizeof(int)) {
912                 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
913                 /* shift leaving only "int" worth of bits */
914                 if (dif != 0) {
915                         dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
916                 }
917         }
918
919         return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
920 }
921
922 static void dnsort(struct dnode **dn, int size)
923 {
924         qsort(dn, size, sizeof(*dn), sortcmp);
925 }
926
927 static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
928 {
929         dnsort(dn, nfiles);
930         display_files(dn, nfiles);
931 }
932 #else
933 # define dnsort(dn, size) ((void)0)
934 # define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
935 #endif
936
937 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
938 static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
939 {
940         struct dnode *dn, *cur, **dnp;
941         struct dirent *entry;
942         DIR *dir;
943         unsigned i, nfiles;
944
945         *nfiles_p = 0;
946         dir = warn_opendir(path);
947         if (dir == NULL) {
948                 G.exit_code = EXIT_FAILURE;
949                 return NULL;    /* could not open the dir */
950         }
951         dn = NULL;
952         nfiles = 0;
953         while ((entry = readdir(dir)) != NULL) {
954                 char *fullname;
955
956                 /* are we going to list the file- it may be . or .. or a hidden file */
957                 if (entry->d_name[0] == '.') {
958                         if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
959                          && !(G.all_fmt & DISP_DOT)
960                         ) {
961                                 continue;
962                         }
963                         if (!(G.all_fmt & DISP_HIDDEN))
964                                 continue;
965                 }
966                 fullname = concat_path_file(path, entry->d_name);
967                 cur = my_stat(fullname, bb_basename(fullname), 0);
968                 if (!cur) {
969                         free(fullname);
970                         continue;
971                 }
972                 cur->fname_allocated = 1;
973                 cur->dn_next = dn;
974                 dn = cur;
975                 nfiles++;
976         }
977         closedir(dir);
978
979         if (dn == NULL)
980                 return NULL;
981
982         /* now that we know how many files there are
983          * allocate memory for an array to hold dnode pointers
984          */
985         *nfiles_p = nfiles;
986         dnp = dnalloc(nfiles);
987         for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
988                 dnp[i] = dn;    /* save pointer to node in array */
989                 dn = dn->dn_next;
990                 if (!dn)
991                         break;
992         }
993
994         return dnp;
995 }
996
997 #if ENABLE_DESKTOP
998 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
999  * If any of the -l, -n, -s options is specified, each list
1000  * of files within the directory shall be preceded by a
1001  * status line indicating the number of file system blocks
1002  * occupied by files in the directory in 512-byte units if
1003  * the -k option is not specified, or 1024-byte units if the
1004  * -k option is specified, rounded up to the next integral
1005  * number of units.
1006  */
1007 /* by Jorgen Overgaard (jorgen AT antistaten.se) */
1008 static off_t calculate_blocks(struct dnode **dn)
1009 {
1010         uoff_t blocks = 1;
1011         if (dn) {
1012                 while (*dn) {
1013                         /* st_blocks is in 512 byte blocks */
1014                         blocks += (*dn)->dn_blocks;
1015                         dn++;
1016                 }
1017         }
1018
1019         /* Even though standard says use 512 byte blocks, coreutils use 1k */
1020         /* Actually, we round up by calculating (blocks + 1) / 2,
1021          * "+ 1" was done when we initialized blocks to 1 */
1022         return blocks >> 1;
1023 }
1024 #endif
1025
1026 static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1027 {
1028         unsigned nfiles;
1029         struct dnode **subdnp;
1030
1031         for (; *dn; dn++) {
1032                 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
1033                         if (!first)
1034                                 bb_putchar('\n');
1035                         first = 0;
1036                         printf("%s:\n", (*dn)->fullname);
1037                 }
1038                 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
1039 #if ENABLE_DESKTOP
1040                 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS))
1041                         printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1042 #endif
1043                 if (nfiles > 0) {
1044                         /* list all files at this level */
1045                         sort_and_display_files(subdnp, nfiles);
1046
1047                         if (ENABLE_FEATURE_LS_RECURSIVE
1048                          && (G.all_fmt & DISP_RECURSIVE)
1049                         ) {
1050                                 struct dnode **dnd;
1051                                 unsigned dndirs;
1052                                 /* recursive - list the sub-dirs */
1053                                 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1054                                 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1055                                 if (dndirs > 0) {
1056                                         dnsort(dnd, dndirs);
1057                                         scan_and_display_dirs_recur(dnd, 0);
1058                                         /* free the array of dnode pointers to the dirs */
1059                                         free(dnd);
1060                                 }
1061                         }
1062                         /* free the dnodes and the fullname mem */
1063                         dfree(subdnp);
1064                 }
1065         }
1066 }
1067
1068
1069 int ls_main(int argc UNUSED_PARAM, char **argv)
1070 {
1071         struct dnode **dnd;
1072         struct dnode **dnf;
1073         struct dnode **dnp;
1074         struct dnode *dn;
1075         struct dnode *cur;
1076         unsigned opt;
1077         unsigned nfiles;
1078         unsigned dnfiles;
1079         unsigned dndirs;
1080         unsigned i;
1081 #if ENABLE_FEATURE_LS_COLOR
1082         /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1083         /* coreutils 6.10:
1084          * # ls --color=BOGUS
1085          * ls: invalid argument 'BOGUS' for '--color'
1086          * Valid arguments are:
1087          * 'always', 'yes', 'force'
1088          * 'never', 'no', 'none'
1089          * 'auto', 'tty', 'if-tty'
1090          * (and substrings: "--color=alwa" work too)
1091          */
1092         static const char ls_longopts[] ALIGN1 =
1093                 "color\0" Optional_argument "\xff"; /* no short equivalent */
1094         static const char color_str[] ALIGN1 =
1095                 "always\0""yes\0""force\0"
1096                 "auto\0""tty\0""if-tty\0";
1097         /* need to initialize since --color has _an optional_ argument */
1098         const char *color_opt = color_str; /* "always" */
1099 #endif
1100
1101         INIT_G();
1102
1103         init_unicode();
1104
1105         if (ENABLE_FEATURE_LS_SORTFILES)
1106                 G.all_fmt = SORT_NAME;
1107
1108 #if ENABLE_FEATURE_AUTOWIDTH
1109         /* obtain the terminal width */
1110         G_terminal_width = get_terminal_width(STDIN_FILENO);
1111         /* go one less... */
1112         G_terminal_width--;
1113 #endif
1114
1115         /* process options */
1116         IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
1117         opt_complementary =
1118                 /* -e implies -l */
1119                 IF_FEATURE_LS_TIMESTAMPS("el")
1120                 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1121                  * in some pairs of opts, only last one takes effect:
1122                  */
1123                 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
1124                 // ":m-l:l-m" - we don't have -m
1125                 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1126                 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1127                 ":C-1:1-C" /* bycols/oneline */
1128                 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1129                 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
1130                 /* -w NUM: */
1131                 IF_FEATURE_AUTOWIDTH(":w+");
1132         opt = getopt32(argv, ls_options
1133                 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
1134                 IF_FEATURE_LS_COLOR(, &color_opt)
1135         );
1136         for (i = 0; opt_flags[i] != (1U << 31); i++) {
1137                 if (opt & (1 << i)) {
1138                         uint32_t flags = opt_flags[i];
1139
1140                         if (flags & STYLE_MASK)
1141                                 G.all_fmt &= ~STYLE_MASK;
1142                         if (flags & SORT_MASK)
1143                                 G.all_fmt &= ~SORT_MASK;
1144                         if (flags & TIME_MASK)
1145                                 G.all_fmt &= ~TIME_MASK;
1146
1147                         G.all_fmt |= flags;
1148                 }
1149         }
1150
1151 #if ENABLE_FEATURE_LS_COLOR
1152         /* set G_show_color = 1/0 */
1153         if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1154                 char *p = getenv("LS_COLORS");
1155                 /* LS_COLORS is unset, or (not empty && not "none") ? */
1156                 if (!p || (p[0] && strcmp(p, "none") != 0))
1157                         G_show_color = 1;
1158         }
1159         if (opt & OPT_color) {
1160                 if (color_opt[0] == 'n')
1161                         G_show_color = 0;
1162                 else switch (index_in_substrings(color_str, color_opt)) {
1163                 case 3:
1164                 case 4:
1165                 case 5:
1166                         if (isatty(STDOUT_FILENO)) {
1167                 case 0:
1168                 case 1:
1169                 case 2:
1170                                 G_show_color = 1;
1171                         }
1172                 }
1173         }
1174 #endif
1175
1176         /* sort out which command line options take precedence */
1177         if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1178                 G.all_fmt &= ~DISP_RECURSIVE;   /* no recurse if listing only dir */
1179         if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1180                 if (G.all_fmt & TIME_CHANGE)
1181                         G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1182                 if (G.all_fmt & TIME_ACCESS)
1183                         G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
1184         }
1185         if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
1186                 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
1187
1188         /* choose a display format if one was not already specified by an option */
1189         if (!(G.all_fmt & STYLE_MASK))
1190                 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
1191
1192         argv += optind;
1193         if (!argv[0])
1194                 *--argv = (char*)".";
1195
1196         if (argv[1])
1197                 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
1198
1199         /* stuff the command line file names into a dnode array */
1200         dn = NULL;
1201         nfiles = 0;
1202         do {
1203                 cur = my_stat(*argv, *argv,
1204                         /* follow links on command line unless -l, -s or -F: */
1205                         !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1206                           || (G.all_fmt & LIST_BLOCKS)
1207                           || (option_mask32 & OPT_F)
1208                         )
1209                         /* ... or if -H: */
1210                         || (option_mask32 & OPT_H)
1211                         /* ... or if -L, but my_stat always follows links if -L */
1212                 );
1213                 argv++;
1214                 if (!cur)
1215                         continue;
1216                 /*cur->fname_allocated = 0; - already is */
1217                 cur->dn_next = dn;
1218                 dn = cur;
1219                 nfiles++;
1220         } while (*argv);
1221
1222         /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1223         if (nfiles == 0)
1224                 return G.exit_code;
1225
1226         /* now that we know how many files there are
1227          * allocate memory for an array to hold dnode pointers
1228          */
1229         dnp = dnalloc(nfiles);
1230         for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1231                 dnp[i] = dn;    /* save pointer to node in array */
1232                 dn = dn->dn_next;
1233                 if (!dn)
1234                         break;
1235         }
1236
1237         if (G.all_fmt & DISP_NOLIST) {
1238                 sort_and_display_files(dnp, nfiles);
1239         } else {
1240                 dnd = splitdnarray(dnp, SPLIT_DIR);
1241                 dnf = splitdnarray(dnp, SPLIT_FILE);
1242                 dndirs = count_dirs(dnp, SPLIT_DIR);
1243                 dnfiles = nfiles - dndirs;
1244                 if (dnfiles > 0) {
1245                         sort_and_display_files(dnf, dnfiles);
1246                         if (ENABLE_FEATURE_CLEAN_UP)
1247                                 free(dnf);
1248                 }
1249                 if (dndirs > 0) {
1250                         dnsort(dnd, dndirs);
1251                         scan_and_display_dirs_recur(dnd, dnfiles == 0);
1252                         if (ENABLE_FEATURE_CLEAN_UP)
1253                                 free(dnd);
1254                 }
1255         }
1256
1257         if (ENABLE_FEATURE_CLEAN_UP)
1258                 dfree(dnp);
1259         return G.exit_code;
1260 }