source: branches/3.2/mindi-busybox/coreutils/ls.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
File size: 35.3 KB
Line 
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
117enum {
118TERMINAL_WIDTH  = 80,           /* use 79 if terminal has linefold bug */
119
120SPLIT_FILE      = 0,
121SPLIT_DIR       = 1,
122SPLIT_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 */
128LIST_INO        = 1 << 0,
129LIST_BLOCKS     = 1 << 1,
130LIST_MODEBITS   = 1 << 2,
131LIST_NLINKS     = 1 << 3,
132LIST_ID_NAME    = 1 << 4,
133LIST_ID_NUMERIC = 1 << 5,
134LIST_CONTEXT    = 1 << 6,
135LIST_SIZE       = 1 << 7,
136LIST_DATE_TIME  = 1 << 8,
137LIST_FULLTIME   = 1 << 9,
138LIST_SYMLINK    = 1 << 10,
139LIST_FILETYPE   = 1 << 11, /* show / suffix for dirs */
140LIST_CLASSIFY   = 1 << 12, /* requires LIST_FILETYPE, also show *,|,@,= suffixes */
141LIST_MASK       = (LIST_CLASSIFY << 1) - 1,
142
143/* what files will be displayed */
144DISP_DIRNAME    = 1 << 13,      /* 2 or more items? label directories */
145DISP_HIDDEN     = 1 << 14,      /* show filenames starting with . */
146DISP_DOT        = 1 << 15,      /* show . and .. */
147DISP_NOLIST     = 1 << 16,      /* show directory as itself, not contents */
148DISP_RECURSIVE  = 1 << 17,      /* show directory and everything below it */
149DISP_ROWS       = 1 << 18,      /* print across rows */
150DISP_MASK       = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
151
152/* what is the overall style of the listing */
153STYLE_COLUMNAR  = 1 << 19,      /* many records per line */
154STYLE_LONG      = 2 << 19,      /* one record per line, extended info */
155STYLE_SINGLE    = 3 << 19,      /* one record per line */
156STYLE_MASK      = STYLE_SINGLE,
157
158/* which of the three times will be used */
159TIME_CHANGE     = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
160TIME_ACCESS     = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
161TIME_MASK       = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
162
163/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
164SORT_REVERSE    = 1 << 23,
165
166SORT_NAME       = 0,            /* sort by file name */
167SORT_SIZE       = 1 << 24,      /* sort by file size */
168SORT_ATIME      = 2 << 24,      /* sort by last access time */
169SORT_CTIME      = 3 << 24,      /* sort by last change time */
170SORT_MTIME      = 4 << 24,      /* sort by last modification time */
171SORT_VERSION    = 5 << 24,      /* sort by version */
172SORT_EXT        = 6 << 24,      /* sort by file name extension */
173SORT_DIR        = 7 << 24,      /* sort by file or directory */
174SORT_MASK       = (7 << 24) * ENABLE_FEATURE_LS_SORTFILES,
175
176LIST_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) */
194static 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 */;
205enum {
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 */
262static 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 */
307struct 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
348struct 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) */
412static 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}
418static 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
427static 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
441static 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 */
470static 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 */
501static NOINLINE unsigned display_single(const struct dnode *dn)
502{
503    unsigned column = 0;
504    char *lpath;
505#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
506    struct stat statbuf;
507    char append;
508#endif
509
510#if ENABLE_FEATURE_LS_FILETYPES
511    append = append_char(dn->dn_mode);
512#endif
513
514    /* Do readlink early, so that if it fails, error message
515     * does not appear *inside* the "ls -l" line */
516    lpath = NULL;
517    if (G.all_fmt & LIST_SYMLINK)
518        if (S_ISLNK(dn->dn_mode))
519            lpath = xmalloc_readlink_or_warn(dn->fullname);
520
521    if (G.all_fmt & LIST_INO)
522        column += printf("%7llu ", (long long) dn->dn_ino);
523//TODO: -h should affect -s too:
524    if (G.all_fmt & LIST_BLOCKS)
525        column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
526    if (G.all_fmt & LIST_MODEBITS)
527        column += printf("%-10s ", (char *) bb_mode_string(dn->dn_mode));
528    if (G.all_fmt & LIST_NLINKS)
529        column += printf("%4lu ", (long) dn->dn_nlink);
530    if (G.all_fmt & LIST_ID_NUMERIC) {
531        if (option_mask32 & OPT_g)
532            column += printf("%-8u ", (int) dn->dn_gid);
533        else
534            column += printf("%-8u %-8u ",
535                    (int) dn->dn_uid,
536                    (int) dn->dn_gid);
537    }
538#if ENABLE_FEATURE_LS_USERNAME
539    else if (G.all_fmt & LIST_ID_NAME) {
540        if (option_mask32 & OPT_g) {
541            column += printf("%-8.8s ",
542                get_cached_groupname(dn->dn_gid));
543        } else {
544            column += printf("%-8.8s %-8.8s ",
545                get_cached_username(dn->dn_uid),
546                get_cached_groupname(dn->dn_gid));
547        }
548    }
549#endif
550    if (G.all_fmt & LIST_SIZE) {
551        if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
552            column += printf("%4u, %3u ",
553                    dn->dn_rdev_maj,
554                    dn->dn_rdev_min);
555        } else {
556            if (option_mask32 & OPT_h) {
557                column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
558                    /* print size, show one fractional, use suffixes */
559                    make_human_readable_str(dn->dn_size, 1, 0)
560                );
561            } else {
562                column += printf("%9"OFF_FMT"u ", dn->dn_size);
563            }
564        }
565    }
566#if ENABLE_FEATURE_LS_TIMESTAMPS
567    if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
568        char *filetime;
569        time_t ttime = dn->dn_mtime;
570        if (G.all_fmt & TIME_ACCESS)
571            ttime = dn->dn_atime;
572        if (G.all_fmt & TIME_CHANGE)
573            ttime = dn->dn_ctime;
574        filetime = ctime(&ttime);
575        /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
576        if (G.all_fmt & LIST_FULLTIME) { /* -e */
577            /* Note: coreutils 8.4 ls --full-time prints:
578             * 2009-07-13 17:49:27.000000000 +0200
579             */
580            column += printf("%.24s ", filetime);
581        } else { /* LIST_DATE_TIME */
582            /* G.current_time_t ~== time(NULL) */
583            time_t age = G.current_time_t - ttime;
584            printf("%.6s ", filetime + 4); /* "Jun 30" */
585            if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
586                /* hh:mm if less than 6 months old */
587                printf("%.5s ", filetime + 11);
588            } else { /* year. buggy if year > 9999 ;) */
589                printf(" %.4s ", filetime + 20);
590            }
591            column += 13;
592        }
593    }
594#endif
595#if ENABLE_SELINUX
596    if (G.all_fmt & LIST_CONTEXT) {
597        column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
598        freecon(dn->sid);
599    }
600#endif
601
602#if ENABLE_FEATURE_LS_COLOR
603    if (G_show_color) {
604        mode_t mode = dn->dn_mode_lstat;
605        if (!mode)
606            if (lstat(dn->fullname, &statbuf) == 0)
607                mode = statbuf.st_mode;
608        printf("\033[%u;%um", bold(mode), fgcolor(mode));
609    }
610#endif
611    column += print_name(dn->name);
612    if (G_show_color) {
613        printf("\033[0m");
614    }
615
616    if (lpath) {
617        printf(" -> ");
618#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
619        if ((G.all_fmt & LIST_FILETYPE) || G_show_color) {
620            mode_t mode = dn->dn_mode_stat;
621            if (!mode)
622                if (stat(dn->fullname, &statbuf) == 0)
623                    mode = statbuf.st_mode;
624# if ENABLE_FEATURE_LS_FILETYPES
625            append = append_char(mode);
626# endif
627# if ENABLE_FEATURE_LS_COLOR
628            if (G_show_color) {
629                printf("\033[%u;%um", bold(mode), fgcolor(mode));
630            }
631# endif
632        }
633#endif
634        column += print_name(lpath) + 4;
635        free(lpath);
636        if (G_show_color) {
637            printf("\033[0m");
638        }
639    }
640#if ENABLE_FEATURE_LS_FILETYPES
641    if (G.all_fmt & LIST_FILETYPE) {
642        if (append) {
643            putchar(append);
644            column++;
645        }
646    }
647#endif
648
649    return column;
650}
651
652static void display_files(struct dnode **dn, unsigned nfiles)
653{
654    unsigned i, ncols, nrows, row, nc;
655    unsigned column;
656    unsigned nexttab;
657    unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
658
659    if (G.all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
660        ncols = 1;
661    } else {
662        /* find the longest file name, use that as the column width */
663        for (i = 0; dn[i]; i++) {
664            int len = calc_name_len(dn[i]->name);
665            if (column_width < len)
666                column_width = len;
667        }
668        column_width += 1 +
669            IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
670                ((G.all_fmt & LIST_INO) ? 8 : 0) +
671                ((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
672        ncols = (unsigned)G_terminal_width / column_width;
673    }
674
675    if (ncols > 1) {
676        nrows = nfiles / ncols;
677        if (nrows * ncols < nfiles)
678            nrows++;                /* round up fractionals */
679    } else {
680        nrows = nfiles;
681        ncols = 1;
682    }
683
684    column = 0;
685    nexttab = 0;
686    for (row = 0; row < nrows; row++) {
687        for (nc = 0; nc < ncols; nc++) {
688            /* reach into the array based on the column and row */
689            if (G.all_fmt & DISP_ROWS)
690                i = (row * ncols) + nc; /* display across row */
691            else
692                i = (nc * nrows) + row; /* display by column */
693            if (i < nfiles) {
694                if (column > 0) {
695                    nexttab -= column;
696                    printf("%*s ", nexttab, "");
697                    column += nexttab + 1;
698                }
699                nexttab = column + column_width;
700                column += display_single(dn[i]);
701            }
702        }
703        putchar('\n');
704        column = 0;
705    }
706}
707
708
709/*** Dir scanning code ***/
710
711static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
712{
713    struct stat statbuf;
714    struct dnode *cur;
715
716    cur = xzalloc(sizeof(*cur));
717    cur->fullname = fullname;
718    cur->name = name;
719
720    if ((option_mask32 & OPT_L) || force_follow) {
721#if ENABLE_SELINUX
722        if (is_selinux_enabled())  {
723            getfilecon(fullname, &cur->sid);
724        }
725#endif
726        if (stat(fullname, &statbuf)) {
727            bb_simple_perror_msg(fullname);
728            G.exit_code = EXIT_FAILURE;
729            free(cur);
730            return NULL;
731        }
732        cur->dn_mode_stat = statbuf.st_mode;
733    } else {
734#if ENABLE_SELINUX
735        if (is_selinux_enabled()) {
736            lgetfilecon(fullname, &cur->sid);
737        }
738#endif
739        if (lstat(fullname, &statbuf)) {
740            bb_simple_perror_msg(fullname);
741            G.exit_code = EXIT_FAILURE;
742            free(cur);
743            return NULL;
744        }
745        cur->dn_mode_lstat = statbuf.st_mode;
746    }
747
748    /* cur->dstat = statbuf: */
749    cur->dn_mode   = statbuf.st_mode  ;
750    cur->dn_size   = statbuf.st_size  ;
751#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
752    cur->dn_atime  = statbuf.st_atime ;
753    cur->dn_mtime  = statbuf.st_mtime ;
754    cur->dn_ctime  = statbuf.st_ctime ;
755#endif
756    cur->dn_ino    = statbuf.st_ino   ;
757    cur->dn_blocks = statbuf.st_blocks;
758    cur->dn_nlink  = statbuf.st_nlink ;
759    cur->dn_uid    = statbuf.st_uid   ;
760    cur->dn_gid    = statbuf.st_gid   ;
761    cur->dn_rdev_maj = major(statbuf.st_rdev);
762    cur->dn_rdev_min = minor(statbuf.st_rdev);
763
764    return cur;
765}
766
767static unsigned count_dirs(struct dnode **dn, int which)
768{
769    unsigned dirs, all;
770
771    if (!dn)
772        return 0;
773
774    dirs = all = 0;
775    for (; *dn; dn++) {
776        const char *name;
777
778        all++;
779        if (!S_ISDIR((*dn)->dn_mode))
780            continue;
781
782        name = (*dn)->name;
783        if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
784         /* or if it's not . or .. */
785         || name[0] != '.'
786         || (name[1] && (name[1] != '.' || name[2]))
787        ) {
788            dirs++;
789        }
790    }
791    return which != SPLIT_FILE ? dirs : all - dirs;
792}
793
794/* get memory to hold an array of pointers */
795static struct dnode **dnalloc(unsigned num)
796{
797    if (num < 1)
798        return NULL;
799
800    num++; /* so that we have terminating NULL */
801    return xzalloc(num * sizeof(struct dnode *));
802}
803
804#if ENABLE_FEATURE_LS_RECURSIVE
805static void dfree(struct dnode **dnp)
806{
807    unsigned i;
808
809    if (dnp == NULL)
810        return;
811
812    for (i = 0; dnp[i]; i++) {
813        struct dnode *cur = dnp[i];
814        if (cur->fname_allocated)
815            free((char*)cur->fullname);
816        free(cur);
817    }
818    free(dnp);
819}
820#else
821#define dfree(...) ((void)0)
822#endif
823
824/* Returns NULL-terminated malloced vector of pointers (or NULL) */
825static struct dnode **splitdnarray(struct dnode **dn, int which)
826{
827    unsigned dncnt, d;
828    struct dnode **dnp;
829
830    if (dn == NULL)
831        return NULL;
832
833    /* count how many dirs or files there are */
834    dncnt = count_dirs(dn, which);
835
836    /* allocate a file array and a dir array */
837    dnp = dnalloc(dncnt);
838
839    /* copy the entrys into the file or dir array */
840    for (d = 0; *dn; dn++) {
841        if (S_ISDIR((*dn)->dn_mode)) {
842            const char *name;
843
844            if (which == SPLIT_FILE)
845                continue;
846
847            name = (*dn)->name;
848            if ((which & SPLIT_DIR) /* any dir... */
849            /* ... or not . or .. */
850             || name[0] != '.'
851             || (name[1] && (name[1] != '.' || name[2]))
852            ) {
853                dnp[d++] = *dn;
854            }
855        } else
856        if (which == SPLIT_FILE) {
857            dnp[d++] = *dn;
858        }
859    }
860    return dnp;
861}
862
863#if ENABLE_FEATURE_LS_SORTFILES
864static int sortcmp(const void *a, const void *b)
865{
866    struct dnode *d1 = *(struct dnode **)a;
867    struct dnode *d2 = *(struct dnode **)b;
868    unsigned sort_opts = G.all_fmt & SORT_MASK;
869    off_t dif;
870
871    dif = 0; /* assume SORT_NAME */
872    // TODO: use pre-initialized function pointer
873    // instead of branch forest
874    if (sort_opts == SORT_SIZE) {
875        dif = (d2->dn_size - d1->dn_size);
876    } else
877    if (sort_opts == SORT_ATIME) {
878        dif = (d2->dn_atime - d1->dn_atime);
879    } else
880    if (sort_opts == SORT_CTIME) {
881        dif = (d2->dn_ctime - d1->dn_ctime);
882    } else
883    if (sort_opts == SORT_MTIME) {
884        dif = (d2->dn_mtime - d1->dn_mtime);
885    } else
886    if (sort_opts == SORT_DIR) {
887        dif = S_ISDIR(d2->dn_mode) - S_ISDIR(d1->dn_mode);
888    } else
889#if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
890    if (sort_opts == SORT_VERSION) {
891        dif = strverscmp(d1->name, d2->name);
892    } else
893#endif
894    if (sort_opts == SORT_EXT) {
895        dif = strcmp(strchrnul(d1->name, '.'), strchrnul(d2->name, '.'));
896    }
897    if (dif == 0) {
898        /* sort by name, use as tie breaker for other sorts */
899        if (ENABLE_LOCALE_SUPPORT)
900            dif = strcoll(d1->name, d2->name);
901        else
902            dif = strcmp(d1->name, d2->name);
903    }
904
905    /* Make dif fit into an int */
906    if (sizeof(dif) > sizeof(int)) {
907        enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
908        /* shift leaving only "int" worth of bits */
909        if (dif != 0) {
910            dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
911        }
912    }
913
914    return (G.all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
915}
916
917static void dnsort(struct dnode **dn, int size)
918{
919    qsort(dn, size, sizeof(*dn), sortcmp);
920}
921
922static void sort_and_display_files(struct dnode **dn, unsigned nfiles)
923{
924    dnsort(dn, nfiles);
925    display_files(dn, nfiles);
926}
927#else
928# define dnsort(dn, size) ((void)0)
929# define sort_and_display_files(dn, nfiles) display_files(dn, nfiles)
930#endif
931
932/* Returns NULL-terminated malloced vector of pointers (or NULL) */
933static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
934{
935    struct dnode *dn, *cur, **dnp;
936    struct dirent *entry;
937    DIR *dir;
938    unsigned i, nfiles;
939
940    *nfiles_p = 0;
941    dir = warn_opendir(path);
942    if (dir == NULL) {
943        G.exit_code = EXIT_FAILURE;
944        return NULL;    /* could not open the dir */
945    }
946    dn = NULL;
947    nfiles = 0;
948    while ((entry = readdir(dir)) != NULL) {
949        char *fullname;
950
951        /* are we going to list the file- it may be . or .. or a hidden file */
952        if (entry->d_name[0] == '.') {
953            if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
954             && !(G.all_fmt & DISP_DOT)
955            ) {
956                continue;
957            }
958            if (!(G.all_fmt & DISP_HIDDEN))
959                continue;
960        }
961        fullname = concat_path_file(path, entry->d_name);
962        cur = my_stat(fullname, bb_basename(fullname), 0);
963        if (!cur) {
964            free(fullname);
965            continue;
966        }
967        cur->fname_allocated = 1;
968        cur->dn_next = dn;
969        dn = cur;
970        nfiles++;
971    }
972    closedir(dir);
973
974    if (dn == NULL)
975        return NULL;
976
977    /* now that we know how many files there are
978     * allocate memory for an array to hold dnode pointers
979     */
980    *nfiles_p = nfiles;
981    dnp = dnalloc(nfiles);
982    for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
983        dnp[i] = dn;    /* save pointer to node in array */
984        dn = dn->dn_next;
985        if (!dn)
986            break;
987    }
988
989    return dnp;
990}
991
992#if ENABLE_DESKTOP
993/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
994 * If any of the -l, -n, -s options is specified, each list
995 * of files within the directory shall be preceded by a
996 * status line indicating the number of file system blocks
997 * occupied by files in the directory in 512-byte units if
998 * the -k option is not specified, or 1024-byte units if the
999 * -k option is specified, rounded up to the next integral
1000 * number of units.
1001 */
1002/* by Jorgen Overgaard (jorgen AT antistaten.se) */
1003static off_t calculate_blocks(struct dnode **dn)
1004{
1005    uoff_t blocks = 1;
1006    if (dn) {
1007        while (*dn) {
1008            /* st_blocks is in 512 byte blocks */
1009            blocks += (*dn)->dn_blocks;
1010            dn++;
1011        }
1012    }
1013
1014    /* Even though standard says use 512 byte blocks, coreutils use 1k */
1015    /* Actually, we round up by calculating (blocks + 1) / 2,
1016     * "+ 1" was done when we initialized blocks to 1 */
1017    return blocks >> 1;
1018}
1019#endif
1020
1021static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1022{
1023    unsigned nfiles;
1024    struct dnode **subdnp;
1025
1026    for (; *dn; dn++) {
1027        if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
1028            if (!first)
1029                bb_putchar('\n');
1030            first = 0;
1031            printf("%s:\n", (*dn)->fullname);
1032        }
1033        subdnp = scan_one_dir((*dn)->fullname, &nfiles);
1034#if ENABLE_DESKTOP
1035        if ((G.all_fmt & STYLE_MASK) == STYLE_LONG)
1036            printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
1037#endif
1038        if (nfiles > 0) {
1039            /* list all files at this level */
1040            sort_and_display_files(subdnp, nfiles);
1041
1042            if (ENABLE_FEATURE_LS_RECURSIVE
1043             && (G.all_fmt & DISP_RECURSIVE)
1044            ) {
1045                struct dnode **dnd;
1046                unsigned dndirs;
1047                /* recursive - list the sub-dirs */
1048                dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
1049                dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
1050                if (dndirs > 0) {
1051                    dnsort(dnd, dndirs);
1052                    scan_and_display_dirs_recur(dnd, 0);
1053                    /* free the array of dnode pointers to the dirs */
1054                    free(dnd);
1055                }
1056            }
1057            /* free the dnodes and the fullname mem */
1058            dfree(subdnp);
1059        }
1060    }
1061}
1062
1063
1064int ls_main(int argc UNUSED_PARAM, char **argv)
1065{
1066    struct dnode **dnd;
1067    struct dnode **dnf;
1068    struct dnode **dnp;
1069    struct dnode *dn;
1070    struct dnode *cur;
1071    unsigned opt;
1072    unsigned nfiles;
1073    unsigned dnfiles;
1074    unsigned dndirs;
1075    unsigned i;
1076#if ENABLE_FEATURE_LS_COLOR
1077    /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
1078    /* coreutils 6.10:
1079     * # ls --color=BOGUS
1080     * ls: invalid argument 'BOGUS' for '--color'
1081     * Valid arguments are:
1082     * 'always', 'yes', 'force'
1083     * 'never', 'no', 'none'
1084     * 'auto', 'tty', 'if-tty'
1085     * (and substrings: "--color=alwa" work too)
1086     */
1087    static const char ls_longopts[] ALIGN1 =
1088        "color\0" Optional_argument "\xff"; /* no short equivalent */
1089    static const char color_str[] ALIGN1 =
1090        "always\0""yes\0""force\0"
1091        "auto\0""tty\0""if-tty\0";
1092    /* need to initialize since --color has _an optional_ argument */
1093    const char *color_opt = color_str; /* "always" */
1094#endif
1095
1096    INIT_G();
1097
1098    init_unicode();
1099
1100    if (ENABLE_FEATURE_LS_SORTFILES)
1101        G.all_fmt = SORT_NAME;
1102
1103#if ENABLE_FEATURE_AUTOWIDTH
1104    /* obtain the terminal width */
1105    get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
1106    /* go one less... */
1107    G_terminal_width--;
1108#endif
1109
1110    /* process options */
1111    IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
1112    opt_complementary =
1113        /* -e implies -l */
1114        IF_FEATURE_LS_TIMESTAMPS("el")
1115        /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
1116         * in some pairs of opts, only last one takes effect:
1117         */
1118        IF_FEATURE_LS_TIMESTAMPS(IF_FEATURE_LS_SORTFILES(":t-S:S-t")) /* time/size */
1119        // ":m-l:l-m" - we don't have -m
1120        IF_FEATURE_LS_FOLLOWLINKS(":H-L:L-H")
1121        ":C-xl:x-Cl:l-xC" /* bycols/bylines/long */
1122        ":C-1:1-C" /* bycols/oneline */
1123        ":x-1:1-x" /* bylines/oneline (not in SuS, but in GNU coreutils 8.4) */
1124        IF_FEATURE_LS_TIMESTAMPS(":c-u:u-c") /* mtime/atime */
1125        /* -w NUM: */
1126        IF_FEATURE_AUTOWIDTH(":w+");
1127    opt = getopt32(argv, ls_options
1128        IF_FEATURE_AUTOWIDTH(, NULL, &G_terminal_width)
1129        IF_FEATURE_LS_COLOR(, &color_opt)
1130    );
1131    for (i = 0; opt_flags[i] != (1U << 31); i++) {
1132        if (opt & (1 << i)) {
1133            uint32_t flags = opt_flags[i];
1134
1135            if (flags & STYLE_MASK)
1136                G.all_fmt &= ~STYLE_MASK;
1137            if (flags & SORT_MASK)
1138                G.all_fmt &= ~SORT_MASK;
1139            if (flags & TIME_MASK)
1140                G.all_fmt &= ~TIME_MASK;
1141
1142            G.all_fmt |= flags;
1143        }
1144    }
1145
1146#if ENABLE_FEATURE_LS_COLOR
1147    /* set G_show_color = 1/0 */
1148    if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
1149        char *p = getenv("LS_COLORS");
1150        /* LS_COLORS is unset, or (not empty && not "none") ? */
1151        if (!p || (p[0] && strcmp(p, "none") != 0))
1152            G_show_color = 1;
1153    }
1154    if (opt & OPT_color) {
1155        if (color_opt[0] == 'n')
1156            G_show_color = 0;
1157        else switch (index_in_substrings(color_str, color_opt)) {
1158        case 3:
1159        case 4:
1160        case 5:
1161            if (isatty(STDOUT_FILENO)) {
1162        case 0:
1163        case 1:
1164        case 2:
1165                G_show_color = 1;
1166            }
1167        }
1168    }
1169#endif
1170
1171    /* sort out which command line options take precedence */
1172    if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
1173        G.all_fmt &= ~DISP_RECURSIVE;   /* no recurse if listing only dir */
1174    if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
1175        if (G.all_fmt & TIME_CHANGE)
1176            G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
1177        if (G.all_fmt & TIME_ACCESS)
1178            G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
1179    }
1180    if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
1181        G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
1182
1183    /* choose a display format if one was not already specified by an option */
1184    if (!(G.all_fmt & STYLE_MASK))
1185        G.all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNAR : STYLE_SINGLE);
1186
1187    argv += optind;
1188    if (!argv[0])
1189        *--argv = (char*)".";
1190
1191    if (argv[1])
1192        G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
1193
1194    /* stuff the command line file names into a dnode array */
1195    dn = NULL;
1196    nfiles = 0;
1197    do {
1198        cur = my_stat(*argv, *argv,
1199            /* follow links on command line unless -l, -s or -F: */
1200            !((G.all_fmt & STYLE_MASK) == STYLE_LONG
1201              || (G.all_fmt & LIST_BLOCKS)
1202              || (option_mask32 & OPT_F)
1203            )
1204            /* ... or if -H: */
1205            || (option_mask32 & OPT_H)
1206            /* ... or if -L, but my_stat always follows links if -L */
1207        );
1208        argv++;
1209        if (!cur)
1210            continue;
1211        /*cur->fname_allocated = 0; - already is */
1212        cur->dn_next = dn;
1213        dn = cur;
1214        nfiles++;
1215    } while (*argv);
1216
1217    /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
1218    if (nfiles == 0)
1219        return G.exit_code;
1220
1221    /* now that we know how many files there are
1222     * allocate memory for an array to hold dnode pointers
1223     */
1224    dnp = dnalloc(nfiles);
1225    for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
1226        dnp[i] = dn;    /* save pointer to node in array */
1227        dn = dn->dn_next;
1228        if (!dn)
1229            break;
1230    }
1231
1232    if (G.all_fmt & DISP_NOLIST) {
1233        sort_and_display_files(dnp, nfiles);
1234    } else {
1235        dnd = splitdnarray(dnp, SPLIT_DIR);
1236        dnf = splitdnarray(dnp, SPLIT_FILE);
1237        dndirs = count_dirs(dnp, SPLIT_DIR);
1238        dnfiles = nfiles - dndirs;
1239        if (dnfiles > 0) {
1240            sort_and_display_files(dnf, dnfiles);
1241            if (ENABLE_FEATURE_CLEAN_UP)
1242                free(dnf);
1243        }
1244        if (dndirs > 0) {
1245            dnsort(dnd, dndirs);
1246            scan_and_display_dirs_recur(dnd, dnfiles == 0);
1247            if (ENABLE_FEATURE_CLEAN_UP)
1248                free(dnd);
1249        }
1250    }
1251
1252    if (ENABLE_FEATURE_CLEAN_UP)
1253        dfree(dnp);
1254    return G.exit_code;
1255}
Note: See TracBrowser for help on using the repository browser.