dd: move suffix struct to xatonum.c
[oweals/busybox.git] / coreutils / ls.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
4  *
5  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
6  */
7
8 /* [date unknown. Perhaps before year 2000]
9  * To achieve a small memory footprint, this version of 'ls' doesn't do any
10  * file sorting, and only has the most essential command line switches
11  * (i.e., the ones I couldn't live without :-) All features which involve
12  * linking in substantial chunks of libc can be disabled.
13  *
14  * Although I don't really want to add new features to this program to
15  * keep it small, I *am* interested to receive bug fixes and ways to make
16  * it more portable.
17  *
18  * KNOWN BUGS:
19  * 1. hidden files can make column width too large
20  *
21  * NON-OPTIMAL BEHAVIOUR:
22  * 1. autowidth reads directories twice
23  * 2. if you do a short directory listing without filetype characters
24  *    appended, there's no need to stat each one
25  * PORTABILITY:
26  * 1. requires lstat (BSD) - how do you do it without?
27  *
28  * [2009-03]
29  * ls sorts listing now, and supports almost all options.
30  */
31
32 //usage:#define ls_trivial_usage
33 //usage:        "[-1AaCxd"
34 //usage:        IF_FEATURE_LS_FOLLOWLINKS("LH")
35 //usage:        IF_FEATURE_LS_RECURSIVE("R")
36 //usage:        IF_FEATURE_LS_FILETYPES("Fp") "lins"
37 //usage:        IF_FEATURE_LS_TIMESTAMPS("e")
38 //usage:        IF_FEATURE_HUMAN_READABLE("h")
39 //usage:        IF_FEATURE_LS_SORTFILES("rSXv")
40 //usage:        IF_FEATURE_LS_TIMESTAMPS("ctu")
41 //usage:        IF_SELINUX("kKZ") "]"
42 //usage:        IF_FEATURE_AUTOWIDTH(" [-w WIDTH]") " [FILE]..."
43 //usage:#define ls_full_usage "\n\n"
44 //usage:       "List directory contents\n"
45 //usage:     "\n        -1      One column output"
46 //usage:     "\n        -a      Include entries which start with ."
47 //usage:     "\n        -A      Like -a, but exclude . and .."
48 //usage:     "\n        -C      List by columns"
49 //usage:     "\n        -x      List by lines"
50 //usage:     "\n        -d      List directory entries instead of contents"
51 //usage:        IF_FEATURE_LS_FOLLOWLINKS(
52 //usage:     "\n        -L      Follow symlinks"
53 //usage:     "\n        -H      Follow symlinks on command line"
54 //usage:        )
55 //usage:        IF_FEATURE_LS_RECURSIVE(
56 //usage:     "\n        -R      Recurse"
57 //usage:        )
58 //usage:        IF_FEATURE_LS_FILETYPES(
59 //usage:     "\n        -p      Append / to dir entries"
60 //usage:     "\n        -F      Append indicator (one of */=@|) to entries"
61 //usage:        )
62 //usage:     "\n        -l      Long listing format"
63 //usage:     "\n        -i      List inode numbers"
64 //usage:     "\n        -n      List numeric UIDs and GIDs instead of names"
65 //usage:     "\n        -s      List allocated blocks"
66 //usage:        IF_FEATURE_LS_TIMESTAMPS(
67 //usage:     "\n        -e      List full date and time"
68 //usage:        )
69 //usage:        IF_FEATURE_HUMAN_READABLE(
70 //usage:     "\n        -h      List sizes in human readable format (1K 243M 2G)"
71 //usage:        )
72 //usage:        IF_FEATURE_LS_SORTFILES(
73 //usage:     "\n        -r      Sort in reverse order"
74 //usage:     "\n        -S      Sort by size"
75 //usage:     "\n        -X      Sort by extension"
76 //usage:     "\n        -v      Sort by version"
77 //usage:        )
78 //usage:        IF_FEATURE_LS_TIMESTAMPS(
79 //usage:     "\n        -c      With -l: sort by ctime"
80 //usage:     "\n        -t      With -l: sort by mtime"
81 //usage:     "\n        -u      With -l: sort by atime"
82 //usage:        )
83 //usage:        IF_SELINUX(
84 //usage:     "\n        -k      List security context"
85 //usage:     "\n        -K      List security context in long format"
86 //usage:     "\n        -Z      List security context and permission"
87 //usage:        )
88 //usage:        IF_FEATURE_AUTOWIDTH(
89 //usage:     "\n        -w N    Assume the terminal is N columns wide"
90 //usage:        )
91 //usage:        IF_FEATURE_LS_COLOR(
92 //usage:     "\n        --color[={always,never,auto}]   Control coloring"
93 //usage:        )
94
95 #include "libbb.h"
96 #include "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                 const 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                         if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
585                                 /* less than 6 months old */
586                                 /* "mmm dd hh:mm " */
587                                 printf("%.12s ", filetime + 4);
588                         } else {
589                                 /* "mmm dd  yyyy " */
590                                 /* "mmm dd yyyyy " after year 9999 :) */
591                                 strchr(filetime + 20, '\n')[0] = ' ';
592                                 printf("%.7s%6s", filetime + 4, filetime + 20);
593                         }
594                         column += 13;
595                 }
596         }
597 #endif
598 #if ENABLE_SELINUX
599         if (G.all_fmt & LIST_CONTEXT) {
600                 column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
601                 freecon(dn->sid);
602         }
603 #endif
604
605 #if ENABLE_FEATURE_LS_COLOR
606         if (G_show_color) {
607                 mode_t mode = dn->dn_mode_lstat;
608                 if (!mode)
609                         if (lstat(dn->fullname, &statbuf) == 0)
610                                 mode = statbuf.st_mode;
611                 printf("\033[%u;%um", bold(mode), fgcolor(mode));
612         }
613 #endif
614         column += print_name(dn->name);
615         if (G_show_color) {
616                 printf("\033[0m");
617         }
618
619         if (lpath) {
620                 printf(" -> ");
621 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
622                 if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
623                         mode_t mode = dn->dn_mode_stat;
624                         if (!mode)
625                                 if (stat(dn->fullname, &statbuf) == 0)
626                                         mode = statbuf.st_mode;
627 # if ENABLE_FEATURE_LS_FILETYPES
628                         append = append_char(mode);
629 # endif
630 # if ENABLE_FEATURE_LS_COLOR
631                         if (G_show_color) {
632                                 printf("\033[%u;%um", bold(mode), fgcolor(mode));
633                         }
634 # endif
635                 }
636 #endif
637                 column += print_name(lpath) + 4;
638                 free(lpath);
639                 if (G_show_color) {
640                         printf("\033[0m");
641                 }
642         }
643 #if ENABLE_FEATURE_LS_FILETYPES
644         if (G.all_fmt & LIST_FILETYPE) {
645                 if (append) {
646                         putchar(append);
647                         column++;
648                 }
649         }
650 #endif
651
652         return column;
653 }
654
655 static void display_files(struct dnode **dn, unsigned nfiles)
656 {
657         unsigned i, ncols, nrows, row, nc;
658         unsigned column;
659         unsigned nexttab;
660         unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
661
662         if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
663                 ncols = 1;
664         } else {
665                 /* find the longest file name, use that as the column width */
666                 for (i = 0; dn[i]; i++) {
667                         int len = calc_name_len(dn[i]->name);
668                         if (column_width < len)
669                                 column_width = len;
670                 }
671                 column_width += 1 +
672                         IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
673                                 ((G.all_fmt & LIST_INO) ? 8 : 0) +
674                                 ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
675                 ncols = (unsigned)G_terminal_width / column_width;
676         }
677
678         if (ncols > 1) {
679                 nrows = nfiles / ncols;
680                 if (nrows * ncols < nfiles)
681                         nrows++;                /* round up fractionals */
682         } else {
683                 nrows = nfiles;
684                 ncols = 1;
685         }
686
687         column = 0;
688         nexttab = 0;
689         for (row = 0; row < nrows; row++) {
690                 for (nc = 0; nc < ncols; nc++) {
691                         /* reach into the array based on the column and row */
692                         if (G.all_fmt & DISP_ROWS)
693                                 i = (row * ncols) + nc; /* display across row */
694                         else
695                                 i = (nc * nrows) + row; /* display by column */
696                         if (i < nfiles) {
697                                 if (column > 0) {
698                                         nexttab -= column;
699                                         printf("%*s ", nexttab, "");
700                                         column += nexttab + 1;
701                                 }
702                                 nexttab = column + column_width;
703                                 column += display_single(dn[i]);
704                         }
705                 }
706                 putchar('\n');
707                 column = 0;
708         }
709 }
710
711
712 /*** Dir scanning code ***/
713
714 static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
715 {
716         struct stat statbuf;
717         struct dnode *cur;
718
719         cur = xzalloc(sizeof(*cur));
720         cur->fullname = fullname;
721         cur->name = name;
722
723         if ((option_mask32 & OPT_L) || force_follow) {
724 #if ENABLE_SELINUX
725                 if (is_selinux_enabled())  {
726                         getfilecon(fullname, &cur->sid);
727                 }
728 #endif
729                 if (stat(fullname, &statbuf)) {
730                         bb_simple_perror_msg(fullname);
731                         G.exit_code = EXIT_FAILURE;
732                         free(cur);
733                         return NULL;
734                 }
735                 cur->dn_mode_stat = statbuf.st_mode;
736         } else {
737 #if ENABLE_SELINUX
738                 if (is_selinux_enabled()) {
739                         lgetfilecon(fullname, &cur->sid);
740                 }
741 #endif
742                 if (lstat(fullname, &statbuf)) {
743                         bb_simple_perror_msg(fullname);
744                         G.exit_code = EXIT_FAILURE;
745                         free(cur);
746                         return NULL;
747                 }
748                 cur->dn_mode_lstat = statbuf.st_mode;
749         }
750
751         /* cur->dstat = statbuf: */
752         cur->dn_mode   = statbuf.st_mode  ;
753         cur->dn_size   = statbuf.st_size  ;
754 #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
755         cur->dn_atime  = statbuf.st_atime ;
756         cur->dn_mtime  = statbuf.st_mtime ;
757         cur->dn_ctime  = statbuf.st_ctime ;
758 #endif
759         cur->dn_ino    = statbuf.st_ino   ;
760         cur->dn_blocks = statbuf.st_blocks;
761         cur->dn_nlink  = statbuf.st_nlink ;
762         cur->dn_uid    = statbuf.st_uid   ;
763         cur->dn_gid    = statbuf.st_gid   ;
764         cur->dn_rdev_maj = major(statbuf.st_rdev);
765         cur->dn_rdev_min = minor(statbuf.st_rdev);
766
767         return cur;
768 }
769
770 static unsigned count_dirs(struct dnode **dn, int which)
771 {
772         unsigned dirs, all;
773
774         if (!dn)
775                 return 0;
776
777         dirs = all = 0;
778         for (; *dn; dn++) {
779                 const char *name;
780
781                 all++;
782                 if (!S_ISDIR((*dn)->dn_mode))
783                         continue;
784
785                 name = (*dn)->name;
786                 if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
787                  /* or if it's not . or .. */
788                  || name[0] != '.'
789                  || (name[1] && (name[1] != '.' || name[2]))
790                 ) {
791                         dirs++;
792                 }
793         }
794         return which != SPLIT_FILE ? dirs : all - dirs;
795 }
796
797 /* get memory to hold an array of pointers */
798 static struct dnode **dnalloc(unsigned num)
799 {
800         if (num < 1)
801                 return NULL;
802
803         num++; /* so that we have terminating NULL */
804         return xzalloc(num * sizeof(struct dnode *));
805 }
806
807 #if ENABLE_FEATURE_LS_RECURSIVE
808 static void dfree(struct dnode **dnp)
809 {
810         unsigned i;
811
812         if (dnp == NULL)
813                 return;
814
815         for (i = 0; dnp[i]; i++) {
816                 struct dnode *cur = dnp[i];
817                 if (cur->fname_allocated)
818                         free((char*)cur->fullname);
819                 free(cur);
820         }
821         free(dnp);
822 }
823 #else
824 #define dfree(...) ((void)0)
825 #endif
826
827 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
828 static struct dnode **splitdnarray(struct dnode **dn, int which)
829 {
830         unsigned dncnt, d;
831         struct dnode **dnp;
832
833         if (dn == NULL)
834                 return NULL;
835
836         /* count how many dirs or files there are */
837         dncnt = count_dirs(dn, which);
838
839         /* allocate a file array and a dir array */
840         dnp = dnalloc(dncnt);
841
842         /* copy the entrys into the file or dir array */
843         for (d = 0; *dn; dn++) {
844                 if (S_ISDIR((*dn)->dn_mode)) {
845                         const char *name;
846
847                         if (which == SPLIT_FILE)
848                                 continue;
849
850                         name = (*dn)->name;
851                         if ((which & SPLIT_DIR) /* any dir... */
852                         /* ... or not . or .. */
853                          || name[0] != '.'
854                          || (name[1] && (name[1] != '.' || name[2]))
855                         ) {
856                                 dnp[d++] = *dn;
857                         }
858                 } else
859                 if (which == SPLIT_FILE) {
860                         dnp[d++] = *dn;
861                 }
862         }
863         return dnp;
864 }
865
866 #if ENABLE_FEATURE_LS_SORTFILES
867 static int sortcmp(const void *a, const void *b)
868 {
869         struct dnode *d1 = *(struct dnode **)a;
870         struct dnode *d2 = *(struct dnode **)b;
871         unsigned sort_opts = G.all_fmt & SORT_MASK;
872         off_t dif;
873
874         dif = 0; /* assume SORT_NAME */
875         // TODO: use pre-initialized function pointer
876         // instead of branch forest
877         if (sort_opts == SORT_SIZE) {
878                 dif = (d2->dn_size - d1->dn_size);
879         } else
880         if (sort_opts == SORT_ATIME) {
881                 dif = (d2->dn_atime - d1->dn_atime);
882         } else
883         if (sort_opts == SORT_CTIME) {
884                 dif = (d2->dn_ctime - d1->dn_ctime);
885         } else
886         if (sort_opts == SORT_MTIME) {
887                 dif = (d2->dn_mtime - d1->dn_mtime);
888         } else
889         if (sort_opts == SORT_DIR) {
890                 dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
891         } else
892 #if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
893         if (sort_opts == SORT_VERSION) {
894                 dif = strverscmp(d1->name, d2->name);
895         } else
896 #endif
897         if (sort_opts == SORT_EXT) {
898                 dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
899         }
900         if (dif == 0) {
901                 /* sort by name, use as tie breaker for other sorts */
902                 if (ENABLE_LOCALE_SUPPORT)
903                         dif = strcoll(d1->name, d2->name);
904                 else
905                         dif = strcmp(d1->name, d2->name);
906         }
907
908         /* Make dif fit into an int */
909         if (sizeof(dif) > sizeof(int)) {
910                 enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
911                 /* shift leaving only "int" worth of bits */
912                 if (dif != 0) {
913                         dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
914                 }
915         }
916
917         return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
918 }
919
920 static void dnsort(struct dnode **dn, int size)
921 {
922         qsort(dn, size, sizeof(*dn), sortcmp);
923 }
924
925 static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
926 {
927         dnsort(dn, nfiles);
928         display_files(dn, nfiles);
929 }
930 #else
931 # define dnsort(dn, size) ((void)0)
932 # define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
933 #endif
934
935 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
936 static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
937 {
938         struct dnode *dn, *cur, **dnp;
939         struct dirent *entry;
940         DIR *dir;
941         unsigned i, nfiles;
942
943         *nfiles_p = 0;
944         dir = warn_opendir(path);
945         if (dir == NULL) {
946                 G.exit_code = EXIT_FAILURE;
947                 return NULL;    /* could not open the dir */
948         }
949         dn = NULL;
950         nfiles = 0;
951         while ((entry = readdir(dir)) != NULL) {
952                 char *fullname;
953
954                 /* are we going to list the file- it may be . or .. or a hidden file */
955                 if (entry->d_name[0] == '.') {
956                         if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
957                          && !(G.all_fmt & DISP_DOT)
958                         ) {
959                                 continue;
960                         }
961                         if (!(G.all_fmt & DISP_HIDDEN))
962                                 continue;
963                 }
964                 fullname = concat_path_file(path, entry->d_name);
965                 cur = my_stat(fullname, bb_basename(fullname), 0);
966                 if (!cur) {
967                         free(fullname);
968                         continue;
969                 }
970                 cur->fname_allocated = 1;
971                 cur->dn_next = dn;
972                 dn = cur;
973                 nfiles++;
974         }
975         closedir(dir);
976
977         if (dn == NULL)
978                 return NULL;
979
980         /* now that we know how many files there are
981          * allocate memory for an array to hold dnode pointers
982          */
983         *nfiles_p = nfiles;
984         dnp = dnalloc(nfiles);
985         for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
986                 dnp[i] = dn;    /* save pointer to node in array */
987                 dn = dn->dn_next;
988                 if (!dn)
989                         break;
990         }
991
992         return dnp;
993 }
994
995 #if ENABLE_DESKTOP
996 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
997  * If any of the -l, -n, -s options is specified, each list
998  * of files within the directory shall be preceded by a
999  * status line indicating the number of file system blocks
1000  * occupied by files in the directory in 512-byte units if
1001  * the -k option is not specified, or 1024-byte units if the
1002  * -k option is specified, rounded up to the next integral
1003  * number of units.
1004  */
1005 /* by Jorgen Overgaard (jorgen AT antistaten.se) */
1006 static off_t calculate_blocks(struct dnode **dn)
1007 {
1008         uoff_t blocks = 1;
1009         if (dn) {
1010                 while (*dn) {
1011                         /* st_blocks is in 512 byte blocks */
1012                         blocks += (*dn)->dn_blocks;
1013                         dn++;
1014                 }
1015         }
1016
1017         /* Even though standard says use 512 byte blocks, coreutils use 1k */
1018         /* Actually, we round up by calculating (blocks + 1) / 2,
1019          * "+ 1" was done when we initialized blocks to 1 */
1020         return blocks >> 1;
1021 }
1022 #endif
1023
1024 static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1025 {
1026         unsigned nfiles;
1027         struct dnode **subdnp;
1028
1029         for (; *dn; dn++) {
1030                 if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
1031                         if (!first)
1032                                 bb_putchar('\n');
1033                         first = 0;
1034                         printf("%s:\n", (*dn)->fullname);
1035                 }
1036                 subdnp = scan_one_dir((*dn)->fullname, &nfiles);
1037 #if ENABLE_DESKTOP
1038                 if ((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS))
1039                         printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1040 #endif
1041                 if (nfiles > 0) {
1042                         /* list all files at this level */
1043                         sort_and_display_files(subdnp, nfiles);
1044
1045                         if (ENABLE_FEATURE_LS_RECURSIVE
1046                          && (G.all_fmt & DISP_RECURSIVE)
1047                         ) {
1048                                 struct dnode **dnd;
1049                                 unsigned dndirs;
1050                                 /* recursive - list the sub-dirs */
1051                                 dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1052                                 dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1053                                 if (dndirs > 0) {
1054                                         dnsort(dnd, dndirs);
1055                                         scan_and_display_dirs_recur(dnd, 0);
1056                                         /* free the array of dnode pointers to the dirs */
1057                                         free(dnd);
1058                                 }
1059                         }
1060                         /* free the dnodes and the fullname mem */
1061                         dfree(subdnp);
1062                 }
1063         }
1064 }
1065
1066
1067 int ls_main(int argc UNUSED_PARAM, char **argv)
1068 {
1069         struct dnode **dnd;
1070         struct dnode **dnf;
1071         struct dnode **dnp;
1072         struct dnode *dn;
1073         struct dnode *cur;
1074         unsigned opt;
1075         unsigned nfiles;
1076         unsigned dnfiles;
1077         unsigned dndirs;
1078         unsigned i;
1079 #if ENABLE_FEATURE_LS_COLOR
1080         /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1081         /* coreutils 6.10:
1082          * # ls --color=BOGUS
1083          * ls: invalid argument 'BOGUS' for '--color'
1084          * Valid arguments are:
1085          * 'always', 'yes', 'force'
1086          * 'never', 'no', 'none'
1087          * 'auto', 'tty', 'if-tty'
1088          * (and substrings: "--color=alwa" work too)
1089          */
1090         static const char ls_longopts[] ALIGN1 =
1091                 "color\0" Optional_argument "\xff"; /* no short equivalent */
1092         static const char color_str[] ALIGN1 =
1093                 "always\0""yes\0""force\0"
1094                 "auto\0""tty\0""if-tty\0";
1095         /* need to initialize since --color has _an optional_ argument */
1096         const char *color_opt = color_str; /* "always" */
1097 #endif
1098
1099         INIT_G();
1100
1101         init_unicode();
1102
1103         if (ENABLE_FEATURE_LS_SORTFILES)
1104                 G.all_fmt = SORT_NAME;
1105
1106 #if ENABLE_FEATURE_AUTOWIDTH
1107         /* obtain the terminal width */
1108         get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
1109         /* go one less... */
1110         G_terminal_width--;
1111 #endif
1112
1113         /* process options */
1114         IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
1115         opt_complementary =
1116                 /* -e implies -l */
1117                 IF_FEATURE_LS_TIMESTAMPS("el")
1118                 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1119                  * in some pairs of opts, only last one takes effect:
1120                  */
1121                 IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
1122                 // ":m-l:l-m" - we don't have -m
1123                 IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1124                 ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1125                 ":C-1:1-C" /* bycols/oneline */
1126                 ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1127                 IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
1128                 /* -w NUM: */
1129                 IF_FEATURE_AUTOWIDTH(":w+");
1130         opt = getopt32(argv, ls_options
1131                 IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
1132                 IF_FEATURE_LS_COLOR(, &color_opt)
1133         );
1134         for (i = 0; opt_flags[i] != (1U << 31); i++) {
1135                 if (opt & (1 << i)) {
1136                         uint32_t flags = opt_flags[i];
1137
1138                         if (flags & STYLE_MASK)
1139                                 G.all_fmt &= ~STYLE_MASK;
1140                         if (flags & SORT_MASK)
1141                                 G.all_fmt &= ~SORT_MASK;
1142                         if (flags & TIME_MASK)
1143                                 G.all_fmt &= ~TIME_MASK;
1144
1145                         G.all_fmt |= flags;
1146                 }
1147         }
1148
1149 #if ENABLE_FEATURE_LS_COLOR
1150         /* set G_show_color = 1/0 */
1151         if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1152                 char *p = getenv("LS_COLORS");
1153                 /* LS_COLORS is unset, or (not empty && not "none") ? */
1154                 if (!p || (p[0] && strcmp(p, "none") != 0))
1155                         G_show_color = 1;
1156         }
1157         if (opt & OPT_color) {
1158                 if (color_opt[0] == 'n')
1159                         G_show_color = 0;
1160                 else switch (index_in_substrings(color_str, color_opt)) {
1161                 case 3:
1162                 case 4:
1163                 case 5:
1164                         if (isatty(STDOUT_FILENO)) {
1165                 case 0:
1166                 case 1:
1167                 case 2:
1168                                 G_show_color = 1;
1169                         }
1170                 }
1171         }
1172 #endif
1173
1174         /* sort out which command line options take precedence */
1175         if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1176                 G.all_fmt &= ~DISP_RECURSIVE;   /* no recurse if listing only dir */
1177         if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1178                 if (G.all_fmt & TIME_CHANGE)
1179                         G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1180                 if (G.all_fmt & TIME_ACCESS)
1181                         G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
1182         }
1183         if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
1184                 G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
1185
1186         /* choose a display format if one was not already specified by an option */
1187         if (!(G.all_fmt & STYLE_MASK))
1188                 G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
1189
1190         argv += optind;
1191         if (!argv[0])
1192                 *--argv = (char*)".";
1193
1194         if (argv[1])
1195                 G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
1196
1197         /* stuff the command line file names into a dnode array */
1198         dn = NULL;
1199         nfiles = 0;
1200         do {
1201                 cur = my_stat(*argv, *argv,
1202                         /* follow links on command line unless -l, -s or -F: */
1203                         !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1204                           || (G.all_fmt & LIST_BLOCKS)
1205                           || (option_mask32 & OPT_F)
1206                         )
1207                         /* ... or if -H: */
1208                         || (option_mask32 & OPT_H)
1209                         /* ... or if -L, but my_stat always follows links if -L */
1210                 );
1211                 argv++;
1212                 if (!cur)
1213                         continue;
1214                 /*cur->fname_allocated = 0; - already is */
1215                 cur->dn_next = dn;
1216                 dn = cur;
1217                 nfiles++;
1218         } while (*argv);
1219
1220         /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1221         if (nfiles == 0)
1222                 return G.exit_code;
1223
1224         /* now that we know how many files there are
1225          * allocate memory for an array to hold dnode pointers
1226          */
1227         dnp = dnalloc(nfiles);
1228         for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1229                 dnp[i] = dn;    /* save pointer to node in array */
1230                 dn = dn->dn_next;
1231                 if (!dn)
1232                         break;
1233         }
1234
1235         if (G.all_fmt & DISP_NOLIST) {
1236                 sort_and_display_files(dnp, nfiles);
1237         } else {
1238                 dnd = splitdnarray(dnp, SPLIT_DIR);
1239                 dnf = splitdnarray(dnp, SPLIT_FILE);
1240                 dndirs = count_dirs(dnp, SPLIT_DIR);
1241                 dnfiles = nfiles - dndirs;
1242                 if (dnfiles > 0) {
1243                         sort_and_display_files(dnf, dnfiles);
1244                         if (ENABLE_FEATURE_CLEAN_UP)
1245                                 free(dnf);
1246                 }
1247                 if (dndirs > 0) {
1248                         dnsort(dnd, dndirs);
1249                         scan_and_display_dirs_recur(dnd, dnfiles == 0);
1250                         if (ENABLE_FEATURE_CLEAN_UP)
1251                                 free(dnd);
1252                 }
1253         }
1254
1255         if (ENABLE_FEATURE_CLEAN_UP)
1256                 dfree(dnp);
1257         return G.exit_code;
1258 }