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