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