90795507726b862927676a22042fbbec8a95573e
[platform/upstream/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:     "\nOptions:"
46 //usage:     "\n        -1      One column output"
47 //usage:     "\n        -a      Include entries which start with ."
48 //usage:     "\n        -A      Like -a, but exclude . and .."
49 //usage:     "\n        -C      List by columns"
50 //usage:     "\n        -x      List by lines"
51 //usage:     "\n        -d      List directory entries instead of contents"
52 //usage:        IF_FEATURE_LS_FOLLOWLINKS(
53 //usage:     "\n        -L      Follow symlinks"
54 //usage:     "\n        -H      Follow symlinks on command line"
55 //usage:        )
56 //usage:        IF_FEATURE_LS_RECURSIVE(
57 //usage:     "\n        -R      Recurse"
58 //usage:        )
59 //usage:        IF_FEATURE_LS_FILETYPES(
60 //usage:     "\n        -p      Append / to dir entries"
61 //usage:     "\n        -F      Append indicator (one of */=@|) to entries"
62 //usage:        )
63 //usage:     "\n        -l      Long listing format"
64 //usage:     "\n        -i      List inode numbers"
65 //usage:     "\n        -n      List numeric UIDs and GIDs instead of names"
66 //usage:     "\n        -s      List allocated blocks"
67 //usage:        IF_FEATURE_LS_TIMESTAMPS(
68 //usage:     "\n        -e      List full date and time"
69 //usage:        )
70 //usage:        IF_FEATURE_HUMAN_READABLE(
71 //usage:     "\n        -h      List sizes in human readable format (1K 243M 2G)"
72 //usage:        )
73 //usage:        IF_FEATURE_LS_SORTFILES(
74 //usage:     "\n        -r      Sort in reverse order"
75 //usage:     "\n        -S      Sort by size"
76 //usage:     "\n        -X      Sort by extension"
77 //usage:     "\n        -v      Sort by version"
78 //usage:        )
79 //usage:        IF_FEATURE_LS_TIMESTAMPS(
80 //usage:     "\n        -c      With -l: sort by ctime"
81 //usage:     "\n        -t      With -l: sort by mtime"
82 //usage:     "\n        -u      With -l: sort by atime"
83 //usage:        )
84 //usage:        IF_SELINUX(
85 //usage:     "\n        -k      List security context"
86 //usage:     "\n        -K      List security context in long format"
87 //usage:     "\n        -Z      List security context and permission"
88 //usage:        )
89 //usage:        IF_FEATURE_AUTOWIDTH(
90 //usage:     "\n        -w N    Assume the terminal is N columns wide"
91 //usage:        )
92 //usage:        IF_FEATURE_LS_COLOR(
93 //usage:     "\n        --color[={always,never,auto}]   Control coloring"
94 //usage:        )
95
96 #include "libbb.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 /* -FLHRctur Std options, busybox optionally supports */
187 /* -p       Std option, busybox optionally supports */
188 /*          Not fully compatible - we show not only '/' but other chars too */
189 /* -SXvhTw  GNU options, busybox optionally supports */
190 /*          -T TABWIDTH is ignored (we don't use tabs on output) */
191 /* -KZ      SELinux mandated options, busybox optionally supports */
192 /*          (coreutils 8.4 has no -K, remove it?) */
193 /* -e       I think we made this one up (looks similar to GNU --full-time) */
194 /* We already used up all 32 bits, if we need to add more, candidates for removal: */
195 /* -K, -T, -e (add --full-time instead) */
196 static const char ls_options[] ALIGN1 =
197         "Cadil1gnsxQAk"      /* 13 opts, total 13 */
198         IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
199         IF_FEATURE_LS_SORTFILES("SXrv")  /* 4, 21 */
200         IF_FEATURE_LS_FILETYPES("Fp")    /* 2, 23 */
201         IF_FEATURE_LS_RECURSIVE("R")     /* 1, 24 */
202         IF_SELINUX("KZ")                 /* 2, 26 */
203         IF_FEATURE_LS_FOLLOWLINKS("LH")  /* 2, 28 */
204         IF_FEATURE_HUMAN_READABLE("h")   /* 1, 29 */
205         IF_FEATURE_AUTOWIDTH("T:w:")     /* 2, 31 */
206         /* with --color, we use all 32 bits */;
207 enum {
208         //OPT_C = (1 << 0),
209         //OPT_a = (1 << 1),
210         //OPT_d = (1 << 2),
211         //OPT_i = (1 << 3),
212         //OPT_l = (1 << 4),
213         //OPT_1 = (1 << 5),
214         OPT_g = (1 << 6),
215         //OPT_n = (1 << 7),
216         //OPT_s = (1 << 8),
217         //OPT_x = (1 << 9),
218         OPT_Q = (1 << 10),
219         //OPT_A = (1 << 11),
220         //OPT_k = (1 << 12),
221
222         OPTBIT_c = 13,
223         OPTBIT_e,
224         OPTBIT_t,
225         OPTBIT_u,
226         OPTBIT_S = OPTBIT_c + 4 * ENABLE_FEATURE_LS_TIMESTAMPS,
227         OPTBIT_X, /* 18 */
228         OPTBIT_r,
229         OPTBIT_v,
230         OPTBIT_F = OPTBIT_S + 4 * ENABLE_FEATURE_LS_SORTFILES,
231         OPTBIT_p, /* 22 */
232         OPTBIT_R = OPTBIT_F + 2 * ENABLE_FEATURE_LS_FILETYPES,
233         OPTBIT_K = OPTBIT_R + 1 * ENABLE_FEATURE_LS_RECURSIVE,
234         OPTBIT_Z, /* 25 */
235         OPTBIT_L = OPTBIT_K + 2 * ENABLE_SELINUX,
236         OPTBIT_H, /* 27 */
237         OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
238         OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
239         OPTBIT_w, /* 30 */
240         OPTBIT_color = OPTBIT_T + 2 * ENABLE_FEATURE_AUTOWIDTH,
241
242         OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
243         OPT_e = (1 << OPTBIT_e) * ENABLE_FEATURE_LS_TIMESTAMPS,
244         OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
245         OPT_u = (1 << OPTBIT_u) * ENABLE_FEATURE_LS_TIMESTAMPS,
246         OPT_S = (1 << OPTBIT_S) * ENABLE_FEATURE_LS_SORTFILES,
247         OPT_X = (1 << OPTBIT_X) * ENABLE_FEATURE_LS_SORTFILES,
248         OPT_r = (1 << OPTBIT_r) * ENABLE_FEATURE_LS_SORTFILES,
249         OPT_v = (1 << OPTBIT_v) * ENABLE_FEATURE_LS_SORTFILES,
250         OPT_F = (1 << OPTBIT_F) * ENABLE_FEATURE_LS_FILETYPES,
251         OPT_p = (1 << OPTBIT_p) * ENABLE_FEATURE_LS_FILETYPES,
252         OPT_R = (1 << OPTBIT_R) * ENABLE_FEATURE_LS_RECURSIVE,
253         OPT_K = (1 << OPTBIT_K) * ENABLE_SELINUX,
254         OPT_Z = (1 << OPTBIT_Z) * ENABLE_SELINUX,
255         OPT_L = (1 << OPTBIT_L) * ENABLE_FEATURE_LS_FOLLOWLINKS,
256         OPT_H = (1 << OPTBIT_H) * ENABLE_FEATURE_LS_FOLLOWLINKS,
257         OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
258         OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_AUTOWIDTH,
259         OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_AUTOWIDTH,
260         OPT_color = (1 << OPTBIT_color) * ENABLE_FEATURE_LS_COLOR,
261 };
262
263 /* TODO: simple toggles may be stored as OPT_xxx bits instead */
264 static const uint32_t opt_flags[] = {
265         STYLE_COLUMNAR,              /* C */
266         DISP_HIDDEN | DISP_DOT,      /* a */
267         DISP_NOLIST,                 /* d */
268         LIST_INO,                    /* i */
269         LIST_LONG | STYLE_LONG,      /* l */
270         STYLE_SINGLE,                /* 1 */
271         LIST_LONG | STYLE_LONG,      /* g (don't show owner) - handled via OPT_g. assumes l */
272         LIST_ID_NUMERIC | LIST_LONG | STYLE_LONG, /* n (assumes l) */
273         LIST_BLOCKS,                 /* s */
274         DISP_ROWS | STYLE_COLUMNAR,  /* x */
275         0,                           /* Q (quote filename) - handled via OPT_Q */
276         DISP_HIDDEN,                 /* A */
277         ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */
278 #if ENABLE_FEATURE_LS_TIMESTAMPS
279         TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
280         LIST_FULLTIME,               /* e */
281         ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
282         TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
283 #endif
284 #if ENABLE_FEATURE_LS_SORTFILES
285         SORT_SIZE,                   /* S */
286         SORT_EXT,                    /* X */
287         SORT_REVERSE,                /* r */
288         SORT_VERSION,                /* v */
289 #endif
290 #if ENABLE_FEATURE_LS_FILETYPES
291         LIST_FILETYPE | LIST_CLASSIFY, /* F */
292         LIST_FILETYPE,               /* p */
293 #endif
294 #if ENABLE_FEATURE_LS_RECURSIVE
295         DISP_RECURSIVE,              /* R */
296 #endif
297 #if ENABLE_SELINUX
298         LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME|STYLE_SINGLE, /* K */
299         LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT|STYLE_SINGLE, /* Z */
300 #endif
301         (1U << 31)
302         /* options after Z are not processed through opt_flags */
303 };
304
305
306 /*
307  * a directory entry and its stat info
308  */
309 struct dnode {
310         const char *name;       /* usually basename, but think "ls -l dir/file" */
311         const char *fullname;   /* full name (usable for stat etc) */
312         struct dnode *dn_next;  /* for linked list */
313         IF_SELINUX(security_context_t sid;)
314         smallint fname_allocated;
315
316         /* Used to avoid re-doing [l]stat at printout stage
317          * if we already collected needed data in scan stage:
318          */
319         mode_t    dn_mode_lstat;   /* obtained with lstat, or 0 */
320         mode_t    dn_mode_stat;    /* obtained with stat, or 0 */
321
322 //      struct stat dstat;
323 // struct stat is huge. We don't need it in full.
324 // At least we don't need st_dev and st_blksize,
325 // but there are invisible fields as well
326 // (such as nanosecond-resolution timespamps)
327 // and padding, which we also don't want to store.
328 // We also can pre-parse dev_t dn_rdev (in glibc, it's huge).
329 // On 32-bit uclibc: dnode size went from 112 to 84 bytes.
330 //
331         /* Same names as in struct stat, but with dn_ instead of st_ pfx: */
332         mode_t    dn_mode; /* obtained with lstat OR stat, depending on -L etc */
333         off_t     dn_size;
334 #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
335         time_t    dn_atime;
336         time_t    dn_mtime;
337         time_t    dn_ctime;
338 #endif
339         ino_t     dn_ino;
340         blkcnt_t  dn_blocks;
341         nlink_t   dn_nlink;
342         uid_t     dn_uid;
343         gid_t     dn_gid;
344         int       dn_rdev_maj;
345         int       dn_rdev_min;
346 //      dev_t     dn_dev;
347 //      blksize_t dn_blksize;
348 };
349
350 struct globals {
351 #if ENABLE_FEATURE_LS_COLOR
352         smallint show_color;
353 # define G_show_color (G.show_color)
354 #else
355 # define G_show_color 0
356 #endif
357         smallint exit_code;
358         unsigned all_fmt;
359 #if ENABLE_FEATURE_AUTOWIDTH
360         unsigned terminal_width;
361 # define G_terminal_width (G.terminal_width)
362 #else
363 # define G_terminal_width TERMINAL_WIDTH
364 #endif
365 #if ENABLE_FEATURE_LS_TIMESTAMPS
366         /* Do time() just once. Saves one syscall per file for "ls -l" */
367         time_t current_time_t;
368 #endif
369 } FIX_ALIASING;
370 #define G (*(struct globals*)&bb_common_bufsiz1)
371 #define INIT_G() do { \
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                 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                         printf("%.6s ", filetime + 4); /* "Jun 30" */
587                         if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
588                                 /* hh:mm if less than 6 months old */
589                                 printf("%.5s ", filetime + 11);
590                         } else { /* year. buggy if year > 9999 ;) */
591                                 printf(" %.4s ", filetime + 20);
592                         }
593                         column += 13;
594                 }
595         }
596 #endif
597 #if ENABLE_SELINUX
598         if (G.all_fmt & LIST_CONTEXT) {
599                 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
600                 freecon(dn->sid);
601         }
602 #endif
603
604 #if ENABLE_FEATURE_LS_COLOR
605         if (G_show_color) {
606                 mode_t mode = dn->dn_mode_lstat;
607                 if (!mode)
608                         if (lstat(dn->fullname, &statbuf) == 0)
609                                 mode = statbuf.st_mode;
610                 printf("\033[%u;%um", bold(mode), fgcolor(mode));
611         }
612 #endif
613         column += print_name(dn->name);
614         if (G_show_color) {
615                 printf("\033[0m");
616         }
617
618         if (lpath) {
619                 printf(" -> ");
620 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
621                 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
622                         mode_t mode = dn->dn_mode_stat;
623                         if (!mode)
624                                 if (stat(dn->fullname, &statbuf) == 0)
625                                         mode = statbuf.st_mode;
626 # if ENABLE_FEATURE_LS_FILETYPES
627                         append = append_char(mode);
628 # endif
629 # if ENABLE_FEATURE_LS_COLOR
630                         if (G_show_color) {
631                                 printf("\033[%u;%um", bold(mode), fgcolor(mode));
632                         }
633 # endif
634                 }
635 #endif
636                 column += print_name(lpath) + 4;
637                 free(lpath);
638                 if (G_show_color) {
639                         printf("\033[0m");
640                 }
641         }
642 #if ENABLE_FEATURE_LS_FILETYPES
643         if (G.all_fmt & LIST_FILETYPE) {
644                 if (append) {
645                         putchar(append);
646                         column++;
647                 }
648         }
649 #endif
650
651         return column;
652 }
653
654 static void display_files(struct dnode **dn, unsigned nfiles)
655 {
656         unsigned i, ncols, nrows, row, nc;
657         unsigned column;
658         unsigned nexttab;
659         unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
660
661         if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
662                 ncols = 1;
663         } else {
664                 /* find the longest file name, use that as the column width */
665                 for (i = 0; dn[i]; i++) {
666                         int len = calc_name_len(dn[i]->name);
667                         if (column_width < len)
668                                 column_width = len;
669                 }
670                 column_width += 1 +
671                         IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
672                                 ((G.all_fmt & LIST_INO) ? 8 : 0) +
673                                 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
674                 ncols = (unsigned)G_terminal_width / column_width;
675         }
676
677         if (ncols > 1) {
678                 nrows = nfiles / ncols;
679                 if (nrows * ncols < nfiles)
680                         nrows++;                /* round up fractionals */
681         } else {
682                 nrows = nfiles;
683                 ncols = 1;
684         }
685
686         column = 0;
687         nexttab = 0;
688         for (row = 0; row < nrows; row++) {
689                 for (nc = 0; nc < ncols; nc++) {
690                         /* reach into the array based on the column and row */
691                         if (G.all_fmt & DISP_ROWS)
692                                 i = (row * ncols) + nc; /* display across row */
693                         else
694                                 i = (nc * nrows) + row; /* display by column */
695                         if (i < nfiles) {
696                                 if (column > 0) {
697                                         nexttab -= column;
698                                         printf("%*s ", nexttab, "");
699                                         column += nexttab + 1;
700                                 }
701                                 nexttab = column + column_width;
702                                 column += display_single(dn[i]);
703                         }
704                 }
705                 putchar('\n');
706                 column = 0;
707         }
708 }
709
710
711 /*** Dir scanning code ***/
712
713 static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
714 {
715         struct stat statbuf;
716         struct dnode *cur;
717
718         cur = xzalloc(sizeof(*cur));
719         cur->fullname = fullname;
720         cur->name = name;
721
722         if ((option_mask32 & OPT_L) || force_follow) {
723 #if ENABLE_SELINUX
724                 if (is_selinux_enabled())  {
725                          getfilecon(fullname, &cur->sid);
726                 }
727 #endif
728                 if (stat(fullname, &statbuf)) {
729                         bb_simple_perror_msg(fullname);
730                         G.exit_code = EXIT_FAILURE;
731                         free(cur);
732                         return NULL;
733                 }
734                 cur->dn_mode_stat = statbuf.st_mode;
735         } else {
736 #if ENABLE_SELINUX
737                 if (is_selinux_enabled()) {
738                         lgetfilecon(fullname, &cur->sid);
739                 }
740 #endif
741                 if (lstat(fullname, &statbuf)) {
742                         bb_simple_perror_msg(fullname);
743                         G.exit_code = EXIT_FAILURE;
744                         free(cur);
745                         return NULL;
746                 }
747                 cur->dn_mode_lstat = statbuf.st_mode;
748         }
749
750         /* cur->dstat = statbuf: */
751         cur->dn_mode   = statbuf.st_mode  ;
752         cur->dn_size   = statbuf.st_size  ;
753 #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
754         cur->dn_atime  = statbuf.st_atime ;
755         cur->dn_mtime  = statbuf.st_mtime ;
756         cur->dn_ctime  = statbuf.st_ctime ;
757 #endif
758         cur->dn_ino    = statbuf.st_ino   ;
759         cur->dn_blocks = statbuf.st_blocks;
760         cur->dn_nlink  = statbuf.st_nlink ;
761         cur->dn_uid    = statbuf.st_uid   ;
762         cur->dn_gid    = statbuf.st_gid   ;
763         cur->dn_rdev_maj = major(statbuf.st_rdev);
764         cur->dn_rdev_min = minor(statbuf.st_rdev);
765
766         return cur;
767 }
768
769 static unsigned count_dirs(struct dnode **dn, int which)
770 {
771         unsigned dirs, all;
772
773         if (!dn)
774                 return 0;
775
776         dirs = all = 0;
777         for (; *dn; dn++) {
778                 const char *name;
779
780                 all++;
781                 if (!S_ISDIR((*dn)->dn_mode))
782                         continue;
783
784                 name = (*dn)->name;
785                 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
786                  /* or if it's not . or .. */
787                  || name[0] != '.'
788                  || (name[1] && (name[1] != '.' || name[2]))
789                 ) {
790                         dirs++;
791                 }
792         }
793         return which != SPLIT_FILE ? dirs : all - dirs;
794 }
795
796 /* get memory to hold an array of pointers */
797 static struct dnode **dnalloc(unsigned num)
798 {
799         if (num < 1)
800                 return NULL;
801
802         num++; /* so that we have terminating NULL */
803         return xzalloc(num * sizeof(struct dnode *));
804 }
805
806 #if ENABLE_FEATURE_LS_RECURSIVE
807 static void dfree(struct dnode **dnp)
808 {
809         unsigned i;
810
811         if (dnp == NULL)
812                 return;
813
814         for (i = 0; dnp[i]; i++) {
815                 struct dnode *cur = dnp[i];
816                 if (cur->fname_allocated)
817                         free((char*)cur->fullname);
818                 free(cur);
819         }
820         free(dnp);
821 }
822 #else
823 #define dfree(...) ((void)0)
824 #endif
825
826 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
827 static struct dnode **splitdnarray(struct dnode **dn, int which)
828 {
829         unsigned dncnt, d;
830         struct dnode **dnp;
831
832         if (dn == NULL)
833                 return NULL;
834
835         /* count how many dirs or files there are */
836         dncnt = count_dirs(dn, which);
837
838         /* allocate a file array and a dir array */
839         dnp = dnalloc(dncnt);
840
841         /* copy the entrys into the file or dir array */
842         for (d = 0; *dn; dn++) {
843                 if (S_ISDIR((*dn)->dn_mode)) {
844                         const char *name;
845
846                         if (which == SPLIT_FILE)
847                                 continue;
848
849                         name = (*dn)->name;
850                         if ((which & SPLIT_DIR) /* any dir... */
851                         /* ... or not . or .. */
852                          || name[0] != '.'
853                          || (name[1] && (name[1] != '.' || name[2]))
854                         ) {
855                                 dnp[d++] = *dn;
856                         }
857                 } else
858                 if (which == SPLIT_FILE) {
859                         dnp[d++] = *dn;
860                 }
861         }
862         return dnp;
863 }
864
865 #if ENABLE_FEATURE_LS_SORTFILES
866 static int sortcmp(const void *a, const void *b)
867 {
868         struct dnode *d1 = *(struct dnode **)a;
869         struct dnode *d2 = *(struct dnode **)b;
870         unsigned sort_opts = G.all_fmt & SORT_MASK;
871         off_t dif;
872
873         dif = 0; /* assume SORT_NAME */
874         // TODO: use pre-initialized function pointer
875         // instead of branch forest
876         if (sort_opts == SORT_SIZE) {
877                 dif = (d2->dn_size - d1->dn_size);
878         } else if (sort_opts == SORT_ATIME) {
879                 dif = (d2->dn_atime - d1->dn_atime);
880         } else if (sort_opts == SORT_CTIME) {
881                 dif = (d2->dn_ctime - d1->dn_ctime);
882         } else if (sort_opts == SORT_MTIME) {
883                 dif = (d2->dn_mtime - d1->dn_mtime);
884         } else if (sort_opts == SORT_DIR) {
885                 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
886                 /* } else if (sort_opts == SORT_VERSION) { */
887                 /* } else if (sort_opts == SORT_EXT) { */
888         }
889         if (dif == 0) {
890                 /* sort by name, or tie_breaker for other sorts */
891                 if (ENABLE_LOCALE_SUPPORT)
892                         dif = strcoll(d1->name, d2->name);
893                 else
894                         dif = strcmp(d1->name, d2->name);
895         }
896
897         /* Make dif fit into an int */
898         if (sizeof(dif) > sizeof(int)) {
899                 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
900                 /* shift leaving only "int" worth of bits */
901                 if (dif != 0) {
902                         dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
903                 }
904         }
905
906         return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
907 }
908
909 static void dnsort(struct dnode **dn, int size)
910 {
911         qsort(dn, size, sizeof(*dn), sortcmp);
912 }
913 #else
914 #define dnsort(dn, size) ((void)0)
915 #endif
916
917 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
918 static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
919 {
920         struct dnode *dn, *cur, **dnp;
921         struct dirent *entry;
922         DIR *dir;
923         unsigned i, nfiles;
924
925         *nfiles_p = 0;
926         dir = warn_opendir(path);
927         if (dir == NULL) {
928                 G.exit_code = EXIT_FAILURE;
929                 return NULL;    /* could not open the dir */
930         }
931         dn = NULL;
932         nfiles = 0;
933         while ((entry = readdir(dir)) != NULL) {
934                 char *fullname;
935
936                 /* are we going to list the file- it may be . or .. or a hidden file */
937                 if (entry->d_name[0] == '.') {
938                         if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
939                          && !(G.all_fmt & DISP_DOT)
940                         ) {
941                                 continue;
942                         }
943                         if (!(G.all_fmt & DISP_HIDDEN))
944                                 continue;
945                 }
946                 fullname = concat_path_file(path, entry->d_name);
947                 cur = my_stat(fullname, bb_basename(fullname), 0);
948                 if (!cur) {
949                         free(fullname);
950                         continue;
951                 }
952                 cur->fname_allocated = 1;
953                 cur->dn_next = dn;
954                 dn = cur;
955                 nfiles++;
956         }
957         closedir(dir);
958
959         if (dn == NULL)
960                 return NULL;
961
962         /* now that we know how many files there are
963          * allocate memory for an array to hold dnode pointers
964          */
965         *nfiles_p = nfiles;
966         dnp = dnalloc(nfiles);
967         for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
968                 dnp[i] = dn;    /* save pointer to node in array */
969                 dn = dn->dn_next;
970                 if (!dn)
971                         break;
972         }
973
974         return dnp;
975 }
976
977 #if ENABLE_DESKTOP
978 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
979  * If any of the -l, -n, -s options is specified, each list
980  * of files within the directory shall be preceded by a
981  * status line indicating the number of file system blocks
982  * occupied by files in the directory in 512-byte units if
983  * the -k option is not specified, or 1024-byte units if the
984  * -k option is specified, rounded up to the next integral
985  * number of units.
986  */
987 /* by Jorgen Overgaard (jorgen AT antistaten.se) */
988 static off_t calculate_blocks(struct dnode **dn)
989 {
990         uoff_t blocks = 1;
991         if (dn) {
992                 while (*dn) {
993                         /* st_blocks is in 512 byte blocks */
994                         blocks += (*dn)->dn_blocks;
995                         dn++;
996                 }
997         }
998
999         /* Even though standard says use 512 byte blocks, coreutils use 1k */
1000         /* Actually, we round up by calculating (blocks + 1) / 2,
1001          * "+ 1" was done when we initialized blocks to 1 */
1002         return blocks >> 1;
1003 }
1004 #endif
1005
1006 static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1007 {
1008         unsigned nfiles;
1009         struct dnode **subdnp;
1010
1011         for (; *dn; dn++) {
1012                 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
1013                         if (!first)
1014                                 bb_putchar('\n');
1015                         first = 0;
1016                         printf("%s:\n", (*dn)->fullname);
1017                 }
1018                 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
1019 #if ENABLE_DESKTOP
1020                 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG)
1021                         printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1022 #endif
1023                 if (nfiles > 0) {
1024                         /* list all files at this level */
1025                         dnsort(subdnp, nfiles);
1026                         display_files(subdnp, nfiles);
1027
1028                         if (ENABLE_FEATURE_LS_RECURSIVE
1029                          && (G.all_fmt & DISP_RECURSIVE)
1030                         ) {
1031                                 struct dnode **dnd;
1032                                 unsigned dndirs;
1033                                 /* recursive - list the sub-dirs */
1034                                 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1035                                 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1036                                 if (dndirs > 0) {
1037                                         dnsort(dnd, dndirs);
1038                                         scan_and_display_dirs_recur(dnd, 0);
1039                                         /* free the array of dnode pointers to the dirs */
1040                                         free(dnd);
1041                                 }
1042                         }
1043                         /* free the dnodes and the fullname mem */
1044                         dfree(subdnp);
1045                 }
1046         }
1047 }
1048
1049
1050 int ls_main(int argc UNUSED_PARAM, char **argv)
1051 {
1052         struct dnode **dnd;
1053         struct dnode **dnf;
1054         struct dnode **dnp;
1055         struct dnode *dn;
1056         struct dnode *cur;
1057         unsigned opt;
1058         unsigned nfiles;
1059         unsigned dnfiles;
1060         unsigned dndirs;
1061         unsigned i;
1062 #if ENABLE_FEATURE_LS_COLOR
1063         /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1064         /* coreutils 6.10:
1065          * # ls --color=BOGUS
1066          * ls: invalid argument 'BOGUS' for '--color'
1067          * Valid arguments are:
1068          * 'always', 'yes', 'force'
1069          * 'never', 'no', 'none'
1070          * 'auto', 'tty', 'if-tty'
1071          * (and substrings: "--color=alwa" work too)
1072          */
1073         static const char ls_longopts[] ALIGN1 =
1074                 "color\0" Optional_argument "\xff"; /* no short equivalent */
1075         static const char color_str[] ALIGN1 =
1076                 "always\0""yes\0""force\0"
1077                 "auto\0""tty\0""if-tty\0";
1078         /* need to initialize since --color has _an optional_ argument */
1079         const char *color_opt = color_str; /* "always" */
1080 #endif
1081
1082         INIT_G();
1083
1084         init_unicode();
1085
1086         if (ENABLE_FEATURE_LS_SORTFILES)
1087                 G.all_fmt = SORT_NAME;
1088
1089 #if ENABLE_FEATURE_AUTOWIDTH
1090         /* obtain the terminal width */
1091         get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
1092         /* go one less... */
1093         G_terminal_width--;
1094 #endif
1095
1096         /* process options */
1097         IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
1098         opt_complementary =
1099                 /* -e implies -l */
1100                 "el"
1101                 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1102                  * in some pairs of opts, only last one takes effect:
1103                  */
1104                 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
1105                 // ":m-l:l-m" - we don't have -m
1106                 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1107                 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1108                 ":C-1:1-C" /* bycols/oneline */
1109                 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1110                 ":c-u:u-c" /* mtime/atime */
1111                 /* -w NUM: */
1112                 IF_FEATURE_AUTOWIDTH(":w+");
1113         opt = getopt32(argv, ls_options
1114                 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
1115                 IF_FEATURE_LS_COLOR(, &color_opt)
1116         );
1117         for (i = 0; opt_flags[i] != (1U << 31); i++) {
1118                 if (opt & (1 << i)) {
1119                         uint32_t flags = opt_flags[i];
1120
1121                         if (flags & STYLE_MASK)
1122                                 G.all_fmt &= ~STYLE_MASK;
1123                         if (flags & SORT_MASK)
1124                                 G.all_fmt &= ~SORT_MASK;
1125                         if (flags & TIME_MASK)
1126                                 G.all_fmt &= ~TIME_MASK;
1127
1128                         G.all_fmt |= flags;
1129                 }
1130         }
1131
1132 #if ENABLE_FEATURE_LS_COLOR
1133         /* set G_show_color = 1/0 */
1134         if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1135                 char *p = getenv("LS_COLORS");
1136                 /* LS_COLORS is unset, or (not empty && not "none") ? */
1137                 if (!p || (p[0] && strcmp(p, "none") != 0))
1138                         G_show_color = 1;
1139         }
1140         if (opt & OPT_color) {
1141                 if (color_opt[0] == 'n')
1142                         G_show_color = 0;
1143                 else switch (index_in_substrings(color_str, color_opt)) {
1144                 case 3:
1145                 case 4:
1146                 case 5:
1147                         if (isatty(STDOUT_FILENO)) {
1148                 case 0:
1149                 case 1:
1150                 case 2:
1151                                 G_show_color = 1;
1152                         }
1153                 }
1154         }
1155 #endif
1156
1157         /* sort out which command line options take precedence */
1158         if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1159                 G.all_fmt &= ~DISP_RECURSIVE;   /* no recurse if listing only dir */
1160         if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1161                 if (G.all_fmt & TIME_CHANGE)
1162                         G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1163                 if (G.all_fmt & TIME_ACCESS)
1164                         G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
1165         }
1166         if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
1167                 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
1168
1169         /* choose a display format if one was not already specified by an option */
1170         if (!(G.all_fmt & STYLE_MASK))
1171                 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
1172
1173         argv += optind;
1174         if (!argv[0])
1175                 *--argv = (char*)".";
1176
1177         if (argv[1])
1178                 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
1179
1180         /* stuff the command line file names into a dnode array */
1181         dn = NULL;
1182         nfiles = 0;
1183         do {
1184                 cur = my_stat(*argv, *argv,
1185                         /* follow links on command line unless -l, -s or -F: */
1186                         !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1187                           || (G.all_fmt & LIST_BLOCKS)
1188                           || (option_mask32 & OPT_F)
1189                         )
1190                         /* ... or if -H: */
1191                         || (option_mask32 & OPT_H)
1192                         /* ... or if -L, but my_stat always follows links if -L */
1193                 );
1194                 argv++;
1195                 if (!cur)
1196                         continue;
1197                 /*cur->fname_allocated = 0; - already is */
1198                 cur->dn_next = dn;
1199                 dn = cur;
1200                 nfiles++;
1201         } while (*argv);
1202
1203         /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1204         if (nfiles == 0)
1205                 return G.exit_code;
1206
1207         /* now that we know how many files there are
1208          * allocate memory for an array to hold dnode pointers
1209          */
1210         dnp = dnalloc(nfiles);
1211         for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1212                 dnp[i] = dn;    /* save pointer to node in array */
1213                 dn = dn->dn_next;
1214                 if (!dn)
1215                         break;
1216         }
1217
1218         if (G.all_fmt & DISP_NOLIST) {
1219                 dnsort(dnp, nfiles);
1220                 display_files(dnp, nfiles);
1221         } else {
1222                 dnd = splitdnarray(dnp, SPLIT_DIR);
1223                 dnf = splitdnarray(dnp, SPLIT_FILE);
1224                 dndirs = count_dirs(dnp, SPLIT_DIR);
1225                 dnfiles = nfiles - dndirs;
1226                 if (dnfiles > 0) {
1227                         dnsort(dnf, dnfiles);
1228                         display_files(dnf, dnfiles);
1229                         if (ENABLE_FEATURE_CLEAN_UP)
1230                                 free(dnf);
1231                 }
1232                 if (dndirs > 0) {
1233                         dnsort(dnd, dndirs);
1234                         scan_and_display_dirs_recur(dnd, dnfiles == 0);
1235                         if (ENABLE_FEATURE_CLEAN_UP)
1236                                 free(dnd);
1237                 }
1238         }
1239
1240         if (ENABLE_FEATURE_CLEAN_UP)
1241                 dfree(dnp);
1242         return G.exit_code;
1243 }