Changeset 3232 in MondoRescue for branches/3.2/mindi-busybox/coreutils/ls.c


Ignore:
Timestamp:
Jan 1, 2014, 12:47:38 AM (10 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.21.1
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/3.2/mindi-busybox/coreutils/ls.c

    r2725 r3232  
    11/* vi: set sw=4 ts=4: */
    22/*
    3  * tiny-ls.c version 0.1.0: A minimalist 'ls'
    43 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
    54 *
     
    3029 * ls sorts listing now, and supports almost all options.
    3130 */
     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
    3295#include "libbb.h"
    3396#include "unicode.h"
     
    54117enum {
    55118TERMINAL_WIDTH  = 80,           /* use 79 if terminal has linefold bug */
    56 COLUMN_GAP      = 2,            /* includes the file type char */
    57 
    58 /* what is the overall style of the listing */
    59 STYLE_COLUMNS   = 1 << 21,      /* fill columns */
    60 STYLE_LONG      = 2 << 21,      /* one record per line, extended info */
    61 STYLE_SINGLE    = 3 << 21,      /* one record per line */
    62 STYLE_MASK      = STYLE_SINGLE,
     119
     120SPLIT_FILE      = 0,
     121SPLIT_DIR       = 1,
     122SPLIT_SUBDIR    = 2,
     123
     124/* Bits in G.all_fmt: */
    63125
    64126/* 51306 lrwxrwxrwx  1 root     root         2 May 11 01:43 /bin/view -> vi* */
     
    72134LIST_CONTEXT    = 1 << 6,
    73135LIST_SIZE       = 1 << 7,
    74 //LIST_DEV        = 1 << 8, - unused, synonym to LIST_SIZE
    75 LIST_DATE_TIME  = 1 << 9,
    76 LIST_FULLTIME   = 1 << 10,
    77 LIST_FILENAME   = 1 << 11,
    78 LIST_SYMLINK    = 1 << 12,
    79 LIST_FILETYPE   = 1 << 13,
    80 LIST_EXEC       = 1 << 14,
    81 LIST_MASK       = (LIST_EXEC << 1) - 1,
     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,
    82142
    83143/* what files will be displayed */
    84 DISP_DIRNAME    = 1 << 15,      /* 2 or more items? label directories */
    85 DISP_HIDDEN     = 1 << 16,      /* show filenames starting with . */
    86 DISP_DOT        = 1 << 17,      /* show . and .. */
    87 DISP_NOLIST     = 1 << 18,      /* show directory as itself, not contents */
    88 DISP_RECURSIVE  = 1 << 19,      /* show directory and everything below it */
    89 DISP_ROWS       = 1 << 20,      /* print across rows */
     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 */
    90150DISP_MASK       = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1),
    91151
     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
    92163/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
    93 SORT_FORWARD    = 0,            /* sort in reverse order */
    94 SORT_REVERSE    = 1 << 27,      /* sort in reverse order */
     164SORT_REVERSE    = 1 << 23,
    95165
    96166SORT_NAME       = 0,            /* sort by file name */
    97 SORT_SIZE       = 1 << 28,      /* sort by file size */
    98 SORT_ATIME      = 2 << 28,      /* sort by last access time */
    99 SORT_CTIME      = 3 << 28,      /* sort by last change time */
    100 SORT_MTIME      = 4 << 28,      /* sort by last modification time */
    101 SORT_VERSION    = 5 << 28,      /* sort by version */
    102 SORT_EXT        = 6 << 28,      /* sort by file name extension */
    103 SORT_DIR        = 7 << 28,      /* sort by file or directory */
    104 SORT_MASK       = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES,
    105 
    106 /* which of the three times will be used */
    107 TIME_CHANGE     = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
    108 TIME_ACCESS     = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS,
    109 TIME_MASK       = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS,
    110 
    111 FOLLOW_LINKS    = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS,
    112 
    113 LS_DISP_HR      = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE,
    114 
    115 LIST_SHORT      = LIST_FILENAME,
     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
    116176LIST_LONG       = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
    117                   LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK,
    118 
    119 SPLIT_DIR       = 1,
    120 SPLIT_FILE      = 0,
    121 SPLIT_SUBDIR    = 2,
     177                  LIST_DATE_TIME | LIST_SYMLINK,
    122178};
    123179
    124 /* "[-]Cadil1", POSIX mandated options, busybox always supports */
    125 /* "[-]gnsx", POSIX non-mandated options, busybox always supports */
    126 /* "[-]Q" GNU option? busybox always supports */
    127 /* "[-]Ak" GNU options, busybox always supports */
    128 /* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
    129 /* "[-]p", POSIX non-mandated options, busybox optionally supports */
    130 /* "[-]SXvThw", GNU options, busybox optionally supports */
    131 /* "[-]K", SELinux mandated options, busybox optionally supports */
    132 /* "[-]e", I think we made this one up */
     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) */
    133194static const char ls_options[] ALIGN1 =
    134     "Cadil1gnsxQAk" /* 13 opts, total 13 */
     195    "Cadil1gnsxQAk"      /* 13 opts, total 13 */
    135196    IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
    136197    IF_FEATURE_LS_SORTFILES("SXrv")  /* 4, 21 */
    137198    IF_FEATURE_LS_FILETYPES("Fp")    /* 2, 23 */
    138     IF_FEATURE_LS_FOLLOWLINKS("L")   /* 1, 24 */
    139     IF_FEATURE_LS_RECURSIVE("R")     /* 1, 25 */
    140     IF_FEATURE_HUMAN_READABLE("h")   /* 1, 26 */
    141     IF_SELINUX("KZ") /* 2, 28 */
    142     IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
    143     ;
     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 */;
    144205enum {
    145206    //OPT_C = (1 << 0),
     
    156217    //OPT_A = (1 << 11),
    157218    //OPT_k = (1 << 12),
    158     OPTBIT_color = 13
    159         + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
    160         + 4 * ENABLE_FEATURE_LS_SORTFILES
    161         + 2 * ENABLE_FEATURE_LS_FILETYPES
    162         + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
    163         + 1 * ENABLE_FEATURE_LS_RECURSIVE
    164         + 1 * ENABLE_FEATURE_HUMAN_READABLE
    165         + 2 * ENABLE_SELINUX
    166         + 2 * ENABLE_FEATURE_AUTOWIDTH,
    167     OPT_color = 1 << OPTBIT_color,
     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,
    168259};
    169260
    170 enum {
    171     LIST_MASK_TRIGGER   = 0,
    172     STYLE_MASK_TRIGGER  = STYLE_MASK,
    173     DISP_MASK_TRIGGER   = DISP_ROWS,
    174     SORT_MASK_TRIGGER   = SORT_MASK,
     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 */
    175301};
    176302
    177 /* TODO: simple toggles may be stored as OPT_xxx bits instead */
    178 static const unsigned opt_flags[] = {
    179     LIST_SHORT | STYLE_COLUMNS, /* C */
    180     DISP_HIDDEN | DISP_DOT,     /* a */
    181     DISP_NOLIST,                /* d */
    182     LIST_INO,                   /* i */
    183     LIST_LONG | STYLE_LONG,     /* l - remember LS_DISP_HR in mask! */
    184     LIST_SHORT | STYLE_SINGLE,  /* 1 */
    185     0,                          /* g (don't show owner) - handled via OPT_g */
    186     LIST_ID_NUMERIC,            /* n */
    187     LIST_BLOCKS,                /* s */
    188     DISP_ROWS,                  /* x */
    189     0,                          /* Q (quote filename) - handled via OPT_Q */
    190     DISP_HIDDEN,                /* A */
    191     ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
    192 #if ENABLE_FEATURE_LS_TIMESTAMPS
    193     TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME),   /* c */
    194     LIST_FULLTIME,              /* e */
    195     ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME,   /* t */
    196     TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME),   /* u */
    197 #endif
    198 #if ENABLE_FEATURE_LS_SORTFILES
    199     SORT_SIZE,                  /* S */
    200     SORT_EXT,                   /* X */
    201     SORT_REVERSE,               /* r */
    202     SORT_VERSION,               /* v */
    203 #endif
    204 #if ENABLE_FEATURE_LS_FILETYPES
    205     LIST_FILETYPE | LIST_EXEC,  /* F */
    206     LIST_FILETYPE,              /* p */
    207 #endif
    208 #if ENABLE_FEATURE_LS_FOLLOWLINKS
    209     FOLLOW_LINKS,               /* L */
    210 #endif
    211 #if ENABLE_FEATURE_LS_RECURSIVE
    212     DISP_RECURSIVE,             /* R */
    213 #endif
    214 #if ENABLE_FEATURE_HUMAN_READABLE
    215     LS_DISP_HR,                 /* h */
    216 #endif
    217 #if ENABLE_SELINUX
    218     LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
    219 #endif
    220 #if ENABLE_SELINUX
    221     LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
    222 #endif
    223     (1U<<31)
    224     /* options after Z are not processed through opt_flags:
    225      * T, w - ignored
    226      */
    227 };
    228 
    229303
    230304/*
    231  * a directory entry and its stat info are stored here
     305 * a directory entry and its stat info
    232306 */
    233307struct dnode {
    234     const char *name;       /* the dir entry name */
    235     const char *fullname;   /* the dir entry name */
    236     struct dnode *next;     /* point at the next node */
     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;)
    237312    smallint fname_allocated;
    238     struct stat dstat;      /* the file stat info */
    239     IF_SELINUX(security_context_t sid;)
     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;
    240346};
    241347
     
    243349#if ENABLE_FEATURE_LS_COLOR
    244350    smallint show_color;
     351# define G_show_color (G.show_color)
     352#else
     353# define G_show_color 0
    245354#endif
    246355    smallint exit_code;
    247356    unsigned all_fmt;
    248357#if ENABLE_FEATURE_AUTOWIDTH
    249     unsigned tabstops; // = COLUMN_GAP;
    250     unsigned terminal_width; // = TERMINAL_WIDTH;
     358    unsigned terminal_width;
     359# define G_terminal_width (G.terminal_width)
     360#else
     361# define G_terminal_width TERMINAL_WIDTH
    251362#endif
    252363#if ENABLE_FEATURE_LS_TIMESTAMPS
     
    256367} FIX_ALIASING;
    257368#define G (*(struct globals*)&bb_common_bufsiz1)
    258 #if ENABLE_FEATURE_LS_COLOR
    259 # define show_color     (G.show_color    )
    260 #else
    261 enum { show_color = 0 };
    262 #endif
    263 #define exit_code       (G.exit_code     )
    264 #define all_fmt         (G.all_fmt       )
    265 #if ENABLE_FEATURE_AUTOWIDTH
    266 # define tabstops       (G.tabstops      )
    267 # define terminal_width (G.terminal_width)
    268 #else
    269 enum {
    270     tabstops = COLUMN_GAP,
    271     terminal_width = TERMINAL_WIDTH,
    272 };
    273 #endif
    274 #define current_time_t (G.current_time_t)
    275369#define INIT_G() do { \
    276370    /* we have to zero it out because of NOEXEC */ \
    277371    memset(&G, 0, sizeof(G)); \
    278     IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
    279     IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
    280     IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
     372    IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
     373    IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
    281374} while (0)
    282375
    283376
    284 static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
    285 {
    286     struct stat dstat;
    287     struct dnode *cur;
    288     IF_SELINUX(security_context_t sid = NULL;)
    289 
    290     if ((all_fmt & FOLLOW_LINKS) || force_follow) {
    291 #if ENABLE_SELINUX
    292         if (is_selinux_enabled())  {
    293              getfilecon(fullname, &sid);
    294         }
    295 #endif
    296         if (stat(fullname, &dstat)) {
    297             bb_simple_perror_msg(fullname);
    298             exit_code = EXIT_FAILURE;
    299             return 0;
    300         }
    301     } else {
    302 #if ENABLE_SELINUX
    303         if (is_selinux_enabled()) {
    304             lgetfilecon(fullname, &sid);
    305         }
    306 #endif
    307         if (lstat(fullname, &dstat)) {
    308             bb_simple_perror_msg(fullname);
    309             exit_code = EXIT_FAILURE;
    310             return 0;
    311         }
    312     }
    313 
    314     cur = xmalloc(sizeof(*cur));
    315     cur->fullname = fullname;
    316     cur->name = name;
    317     cur->dstat = dstat;
    318     IF_SELINUX(cur->sid = sid;)
    319     return cur;
    320 }
     377/*** Output code ***/
     378
    321379
    322380/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
     
    325383 * and we use 0 for unknown and 15 for executables (see below) */
    326384#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
    327 #define TYPECHAR(mode)  ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
    328 #define APPCHAR(mode)   ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
     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)])
    329387/* 036 black foreground              050 black background
    330388   037 red foreground                051 red background
     
    337395*/
    338396#define COLOR(mode) ( \
    339     /*un  fi  chr     dir     blk     file    link    sock        exe */ \
     397    /*un  fi  chr  -  dir  -  blk  -  file -  link -  sock -   -  exe */ \
    340398    "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
    341399    [TYPEINDEX(mode)])
     
    346404 */
    347405#define ATTR(mode) ( \
    348     /*un fi chr   dir   blk   file  link  sock     exe */ \
     406    /*un fi chr - dir - blk - file- link- sock- -  exe */ \
    349407    "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
    350408    [TYPEINDEX(mode)])
     
    366424#endif
    367425
    368 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
     426#if ENABLE_FEATURE_LS_FILETYPES
    369427static char append_char(mode_t mode)
    370428{
    371     if (!(all_fmt & LIST_FILETYPE))
     429    if (!(G.all_fmt & LIST_FILETYPE))
    372430        return '\0';
    373431    if (S_ISDIR(mode))
    374432        return '/';
    375     if (!(all_fmt & LIST_EXEC))
     433    if (!(G.all_fmt & LIST_CLASSIFY))
    376434        return '\0';
    377435    if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
     
    381439#endif
    382440
    383 static unsigned count_dirs(struct dnode **dn, int which)
    384 {
    385     unsigned dirs, all;
    386 
    387     if (!dn)
    388         return 0;
    389 
    390     dirs = all = 0;
    391     for (; *dn; dn++) {
    392         const char *name;
    393 
    394         all++;
    395         if (!S_ISDIR((*dn)->dstat.st_mode))
    396             continue;
    397         name = (*dn)->name;
    398         if (which != SPLIT_SUBDIR /* if not requested to skip . / .. */
    399          /* or if it's not . or .. */
    400          || name[0] != '.' || (name[1] && (name[1] != '.' || name[2]))
    401         ) {
    402             dirs++;
    403         }
    404     }
    405     return which != SPLIT_FILE ? dirs : all - dirs;
    406 }
    407 
    408 /* get memory to hold an array of pointers */
    409 static struct dnode **dnalloc(unsigned num)
    410 {
    411     if (num < 1)
    412         return NULL;
    413 
    414     num++; /* so that we have terminating NULL */
    415     return xzalloc(num * sizeof(struct dnode *));
    416 }
    417 
    418 #if ENABLE_FEATURE_LS_RECURSIVE
    419 static void dfree(struct dnode **dnp)
    420 {
    421     unsigned i;
    422 
    423     if (dnp == NULL)
    424         return;
    425 
    426     for (i = 0; dnp[i]; i++) {
    427         struct dnode *cur = dnp[i];
    428         if (cur->fname_allocated)
    429             free((char*)cur->fullname);
    430         free(cur);
    431     }
    432     free(dnp);
    433 }
    434 #else
    435 #define dfree(...) ((void)0)
    436 #endif
    437 
    438 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
    439 static struct dnode **splitdnarray(struct dnode **dn, int which)
    440 {
    441     unsigned dncnt, d;
    442     struct dnode **dnp;
    443 
    444     if (dn == NULL)
    445         return NULL;
    446 
    447     /* count how many dirs or files there are */
    448     dncnt = count_dirs(dn, which);
    449 
    450     /* allocate a file array and a dir array */
    451     dnp = dnalloc(dncnt);
    452 
    453     /* copy the entrys into the file or dir array */
    454     for (d = 0; *dn; dn++) {
    455         if (S_ISDIR((*dn)->dstat.st_mode)) {
    456             const char *name;
    457 
    458             if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
    459                 continue;
    460             name = (*dn)->name;
    461             if ((which & SPLIT_DIR)
    462              || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
    463             ) {
    464                 dnp[d++] = *dn;
    465             }
    466         } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
    467             dnp[d++] = *dn;
    468         }
    469     }
    470     return dnp;
    471 }
    472 
    473 #if ENABLE_FEATURE_LS_SORTFILES
    474 static int sortcmp(const void *a, const void *b)
    475 {
    476     struct dnode *d1 = *(struct dnode **)a;
    477     struct dnode *d2 = *(struct dnode **)b;
    478     unsigned sort_opts = all_fmt & SORT_MASK;
    479     off_t dif;
    480 
    481     dif = 0; /* assume SORT_NAME */
    482     // TODO: use pre-initialized function pointer
    483     // instead of branch forest
    484     if (sort_opts == SORT_SIZE) {
    485         dif = (d2->dstat.st_size - d1->dstat.st_size);
    486     } else if (sort_opts == SORT_ATIME) {
    487         dif = (d2->dstat.st_atime - d1->dstat.st_atime);
    488     } else if (sort_opts == SORT_CTIME) {
    489         dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
    490     } else if (sort_opts == SORT_MTIME) {
    491         dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
    492     } else if (sort_opts == SORT_DIR) {
    493         dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
    494         /* } else if (sort_opts == SORT_VERSION) { */
    495         /* } else if (sort_opts == SORT_EXT) { */
    496     }
    497     if (dif == 0) {
    498         /* sort by name, or tie_breaker for other sorts */
    499         if (ENABLE_LOCALE_SUPPORT)
    500             dif = strcoll(d1->name, d2->name);
    501         else
    502             dif = strcmp(d1->name, d2->name);
    503     }
    504 
    505     /* Make dif fit into an int */
    506     if (sizeof(dif) > sizeof(int)) {
    507         enum { BITS_TO_SHIFT = 8 * (sizeof(dif) - sizeof(int)) };
    508         /* shift leaving only "int" worth of bits */
    509         if (dif != 0) {
    510             dif = 1 | (int)((uoff_t)dif >> BITS_TO_SHIFT);
    511         }
    512     }
    513 
    514     return (all_fmt & SORT_REVERSE) ? -(int)dif : (int)dif;
    515 }
    516 
    517 static void dnsort(struct dnode **dn, int size)
    518 {
    519     qsort(dn, size, sizeof(*dn), sortcmp);
    520 }
    521 #else
    522 #define dnsort(dn, size) ((void)0)
    523 #endif
    524 
    525 
    526441static unsigned calc_name_len(const char *name)
    527442{
     
    546461}
    547462
    548 
    549463/* Return the number of used columns.
    550  * Note that only STYLE_COLUMNS uses return value.
     464 * Note that only STYLE_COLUMNAR uses return value.
    551465 * STYLE_SINGLE and STYLE_LONG don't care.
    552466 * coreutils 7.2 also supports:
     
    582496
    583497/* Return the number of used columns.
    584  * Note that only STYLE_COLUMNS uses return value,
     498 * Note that only STYLE_COLUMNAR uses return value,
    585499 * STYLE_SINGLE and STYLE_LONG don't care.
    586500 */
    587 static NOINLINE unsigned list_single(const struct dnode *dn)
     501static NOINLINE unsigned display_single(const struct dnode *dn)
    588502{
    589503    unsigned column = 0;
    590     char *lpath = lpath; /* for compiler */
     504    char *lpath;
    591505#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
    592     struct stat info;
     506    struct stat statbuf;
    593507    char append;
    594508#endif
    595509
    596     /* Never happens:
    597     if (dn->fullname == NULL)
    598         return 0;
    599     */
    600 
    601510#if ENABLE_FEATURE_LS_FILETYPES
    602     append = append_char(dn->dstat.st_mode);
     511    append = append_char(dn->dn_mode);
    603512#endif
    604513
    605514    /* Do readlink early, so that if it fails, error message
    606515     * does not appear *inside* the "ls -l" line */
    607     if (all_fmt & LIST_SYMLINK)
    608         if (S_ISLNK(dn->dstat.st_mode))
     516    lpath = NULL;
     517    if (G.all_fmt & LIST_SYMLINK)
     518        if (S_ISLNK(dn->dn_mode))
    609519            lpath = xmalloc_readlink_or_warn(dn->fullname);
    610520
    611     if (all_fmt & LIST_INO)
    612         column += printf("%7llu ", (long long) dn->dstat.st_ino);
    613     if (all_fmt & LIST_BLOCKS)
    614         column += printf("%4"OFF_FMT"u ", (off_t) (dn->dstat.st_blocks >> 1));
    615     if (all_fmt & LIST_MODEBITS)
    616         column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
    617     if (all_fmt & LIST_NLINKS)
    618         column += printf("%4lu ", (long) dn->dstat.st_nlink);
     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    }
    619538#if ENABLE_FEATURE_LS_USERNAME
    620     if (all_fmt & LIST_ID_NAME) {
     539    else if (G.all_fmt & LIST_ID_NAME) {
    621540        if (option_mask32 & OPT_g) {
    622541            column += printf("%-8.8s ",
    623                 get_cached_groupname(dn->dstat.st_gid));
     542                get_cached_groupname(dn->dn_gid));
    624543        } else {
    625544            column += printf("%-8.8s %-8.8s ",
    626                 get_cached_username(dn->dstat.st_uid),
    627                 get_cached_groupname(dn->dstat.st_gid));
    628         }
    629     }
    630 #endif
    631     if (all_fmt & LIST_ID_NUMERIC) {
    632         if (option_mask32 & OPT_g)
    633             column += printf("%-8u ", (int) dn->dstat.st_gid);
    634         else
    635             column += printf("%-8u %-8u ",
    636                     (int) dn->dstat.st_uid,
    637                     (int) dn->dstat.st_gid);
    638     }
    639     if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
    640         if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
     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)) {
    641552            column += printf("%4u, %3u ",
    642                     (int) major(dn->dstat.st_rdev),
    643                     (int) minor(dn->dstat.st_rdev));
     553                    dn->dn_rdev_maj,
     554                    dn->dn_rdev_min);
    644555        } else {
    645             if (all_fmt & LS_DISP_HR) {
     556            if (option_mask32 & OPT_h) {
    646557                column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
    647                     /* print st_size, show one fractional, use suffixes */
    648                     make_human_readable_str(dn->dstat.st_size, 1, 0)
     558                    /* print size, show one fractional, use suffixes */
     559                    make_human_readable_str(dn->dn_size, 1, 0)
    649560                );
    650561            } else {
    651                 column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
     562                column += printf("%9"OFF_FMT"u ", dn->dn_size);
    652563            }
    653564        }
    654565    }
    655566#if ENABLE_FEATURE_LS_TIMESTAMPS
    656     if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
     567    if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
    657568        char *filetime;
    658         time_t ttime = dn->dstat.st_mtime;
    659         if (all_fmt & TIME_ACCESS)
    660             ttime = dn->dstat.st_atime;
    661         if (all_fmt & TIME_CHANGE)
    662             ttime = dn->dstat.st_ctime;
     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;
    663574        filetime = ctime(&ttime);
    664575        /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
    665         if (all_fmt & LIST_FULLTIME)
     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             */
    666580            column += printf("%.24s ", filetime);
    667         else { /* LIST_DATE_TIME */
    668             /* current_time_t ~== time(NULL) */
    669             time_t age = current_time_t - ttime;
     581        } else { /* LIST_DATE_TIME */
     582            /* G.current_time_t ~== time(NULL) */
     583            time_t age = G.current_time_t - ttime;
    670584            printf("%.6s ", filetime + 4); /* "Jun 30" */
    671585            if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
     
    680594#endif
    681595#if ENABLE_SELINUX
    682     if (all_fmt & LIST_CONTEXT) {
     596    if (G.all_fmt & LIST_CONTEXT) {
    683597        column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
    684598        freecon(dn->sid);
    685599    }
    686600#endif
    687     if (all_fmt & LIST_FILENAME) {
     601
    688602#if ENABLE_FEATURE_LS_COLOR
    689         if (show_color) {
    690             info.st_mode = 0; /* for fgcolor() */
    691             lstat(dn->fullname, &info);
    692             printf("\033[%u;%um", bold(info.st_mode),
    693                     fgcolor(info.st_mode));
    694         }
    695 #endif
    696         column += print_name(dn->name);
    697         if (show_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) {
    698637            printf("\033[0m");
    699638        }
    700639    }
    701     if (all_fmt & LIST_SYMLINK) {
    702         if (S_ISLNK(dn->dstat.st_mode) && lpath) {
    703             printf(" -> ");
    704 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
    705 #if ENABLE_FEATURE_LS_COLOR
    706             info.st_mode = 0; /* for fgcolor() */
    707 #endif
    708             if (stat(dn->fullname, &info) == 0) {
    709                 append = append_char(info.st_mode);
    710             }
    711 #endif
    712 #if ENABLE_FEATURE_LS_COLOR
    713             if (show_color) {
    714                 printf("\033[%u;%um", bold(info.st_mode),
    715                        fgcolor(info.st_mode));
    716             }
    717 #endif
    718             column += print_name(lpath) + 4;
    719             if (show_color) {
    720                 printf("\033[0m");
    721             }
    722             free(lpath);
    723         }
    724     }
    725640#if ENABLE_FEATURE_LS_FILETYPES
    726     if (all_fmt & LIST_FILETYPE) {
     641    if (G.all_fmt & LIST_FILETYPE) {
    727642        if (append) {
    728643            putchar(append);
     
    735650}
    736651
    737 static void showfiles(struct dnode **dn, unsigned nfiles)
     652static void display_files(struct dnode **dn, unsigned nfiles)
    738653{
    739654    unsigned i, ncols, nrows, row, nc;
    740     unsigned column = 0;
    741     unsigned nexttab = 0;
    742     unsigned column_width = 0; /* used only by STYLE_COLUMNS */
    743 
    744     if (all_fmt & STYLE_LONG) { /* STYLE_LONG or STYLE_SINGLE */
     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 */
    745660        ncols = 1;
    746661    } else {
     
    751666                column_width = len;
    752667        }
    753         column_width += tabstops +
    754             IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
    755                 ((all_fmt & LIST_INO) ? 8 : 0) +
    756                 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
    757         ncols = (int) (terminal_width / column_width);
     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;
    758673    }
    759674
     
    767682    }
    768683
     684    column = 0;
     685    nexttab = 0;
    769686    for (row = 0; row < nrows; row++) {
    770687        for (nc = 0; nc < ncols; nc++) {
    771688            /* reach into the array based on the column and row */
    772             if (all_fmt & DISP_ROWS)
     689            if (G.all_fmt & DISP_ROWS)
    773690                i = (row * ncols) + nc; /* display across row */
    774691            else
     
    777694                if (column > 0) {
    778695                    nexttab -= column;
    779                     printf("%*s", nexttab, "");
    780                     column += nexttab;
     696                    printf("%*s ", nexttab, "");
     697                    column += nexttab + 1;
    781698                }
    782699                nexttab = column + column_width;
    783                 column += list_single(dn[i]);
     700                column += display_single(dn[i]);
    784701            }
    785702        }
     
    789706}
    790707
     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}
    791991
    792992#if ENABLE_DESKTOP
     
    8071007        while (*dn) {
    8081008            /* st_blocks is in 512 byte blocks */
    809             blocks += (*dn)->dstat.st_blocks;
     1009            blocks += (*dn)->dn_blocks;
    8101010            dn++;
    8111011        }
     
    8191019#endif
    8201020
    821 
    822 static struct dnode **list_dir(const char *, unsigned *);
    823 
    824 static void showdirs(struct dnode **dn, int first)
     1021static void scan_and_display_dirs_recur(struct dnode **dn, int first)
    8251022{
    8261023    unsigned nfiles;
    827     unsigned dndirs;
    8281024    struct dnode **subdnp;
    829     struct dnode **dnd;
    830 
    831     /* Never happens:
    832     if (dn == NULL || ndirs < 1) {
    833         return;
    834     }
    835     */
    8361025
    8371026    for (; *dn; dn++) {
    838         if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
     1027        if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
    8391028            if (!first)
    8401029                bb_putchar('\n');
     
    8421031            printf("%s:\n", (*dn)->fullname);
    8431032        }
    844         subdnp = list_dir((*dn)->fullname, &nfiles);
     1033        subdnp = scan_one_dir((*dn)->fullname, &nfiles);
    8451034#if ENABLE_DESKTOP
    846         if ((all_fmt & STYLE_MASK) == STYLE_LONG)
     1035        if ((G.all_fmt & STYLE_MASK) == STYLE_LONG)
    8471036            printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
    8481037#endif
    8491038        if (nfiles > 0) {
    8501039            /* list all files at this level */
    851             dnsort(subdnp, nfiles);
    852             showfiles(subdnp, nfiles);
     1040            sort_and_display_files(subdnp, nfiles);
     1041
    8531042            if (ENABLE_FEATURE_LS_RECURSIVE
    854              && (all_fmt & DISP_RECURSIVE)
     1043             && (G.all_fmt & DISP_RECURSIVE)
    8551044            ) {
     1045                struct dnode **dnd;
     1046                unsigned dndirs;
    8561047                /* recursive - list the sub-dirs */
    8571048                dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
     
    8591050                if (dndirs > 0) {
    8601051                    dnsort(dnd, dndirs);
    861                     showdirs(dnd, 0);
     1052                    scan_and_display_dirs_recur(dnd, 0);
    8621053                    /* free the array of dnode pointers to the dirs */
    8631054                    free(dnd);
     
    8681059        }
    8691060    }
    870 }
    871 
    872 
    873 /* Returns NULL-terminated malloced vector of pointers (or NULL) */
    874 static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
    875 {
    876     struct dnode *dn, *cur, **dnp;
    877     struct dirent *entry;
    878     DIR *dir;
    879     unsigned i, nfiles;
    880 
    881     /* Never happens:
    882     if (path == NULL)
    883         return NULL;
    884     */
    885 
    886     *nfiles_p = 0;
    887     dir = warn_opendir(path);
    888     if (dir == NULL) {
    889         exit_code = EXIT_FAILURE;
    890         return NULL;    /* could not open the dir */
    891     }
    892     dn = NULL;
    893     nfiles = 0;
    894     while ((entry = readdir(dir)) != NULL) {
    895         char *fullname;
    896 
    897         /* are we going to list the file- it may be . or .. or a hidden file */
    898         if (entry->d_name[0] == '.') {
    899             if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
    900              && !(all_fmt & DISP_DOT)
    901             ) {
    902                 continue;
    903             }
    904             if (!(all_fmt & DISP_HIDDEN))
    905                 continue;
    906         }
    907         fullname = concat_path_file(path, entry->d_name);
    908         cur = my_stat(fullname, bb_basename(fullname), 0);
    909         if (!cur) {
    910             free(fullname);
    911             continue;
    912         }
    913         cur->fname_allocated = 1;
    914         cur->next = dn;
    915         dn = cur;
    916         nfiles++;
    917     }
    918     closedir(dir);
    919 
    920     if (dn == NULL)
    921         return NULL;
    922 
    923     /* now that we know how many files there are
    924      * allocate memory for an array to hold dnode pointers
    925      */
    926     *nfiles_p = nfiles;
    927     dnp = dnalloc(nfiles);
    928     for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
    929         dnp[i] = dn;    /* save pointer to node in array */
    930         dn = dn->next;
    931         if (!dn)
    932             break;
    933     }
    934 
    935     return dnp;
    9361061}
    9371062
     
    9731098    init_unicode();
    9741099
    975     all_fmt = LIST_SHORT |
    976         (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
     1100    if (ENABLE_FEATURE_LS_SORTFILES)
     1101        G.all_fmt = SORT_NAME;
    9771102
    9781103#if ENABLE_FEATURE_AUTOWIDTH
    9791104    /* obtain the terminal width */
    980     get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
     1105    get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
    9811106    /* go one less... */
    982     terminal_width--;
     1107    G_terminal_width--;
    9831108#endif
    9841109
    9851110    /* process options */
    9861111    IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
    987 #if ENABLE_FEATURE_AUTOWIDTH
    988     opt_complementary = "T+:w+"; /* -T N, -w N */
    989     opt = getopt32(argv, ls_options, &tabstops, &terminal_width
    990                 IF_FEATURE_LS_COLOR(, &color_opt));
    991 #else
    992     opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
    993 #endif
    994     for (i = 0; opt_flags[i] != (1U<<31); i++) {
     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++) {
    9951132        if (opt & (1 << i)) {
    996             unsigned flags = opt_flags[i];
    997 
    998             if (flags & LIST_MASK_TRIGGER)
    999                 all_fmt &= ~LIST_MASK;
    1000             if (flags & STYLE_MASK_TRIGGER)
    1001                 all_fmt &= ~STYLE_MASK;
    1002             if (flags & SORT_MASK_TRIGGER)
    1003                 all_fmt &= ~SORT_MASK;
    1004             if (flags & DISP_MASK_TRIGGER)
    1005                 all_fmt &= ~DISP_MASK;
     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;
    10061139            if (flags & TIME_MASK)
    1007                 all_fmt &= ~TIME_MASK;
    1008             if (flags & LIST_CONTEXT)
    1009                 all_fmt |= STYLE_SINGLE;
    1010             /* huh?? opt cannot be 'l' */
    1011             //if (LS_DISP_HR && opt == 'l')
    1012             //  all_fmt &= ~LS_DISP_HR;
    1013             all_fmt |= flags;
     1140                G.all_fmt &= ~TIME_MASK;
     1141
     1142            G.all_fmt |= flags;
    10141143        }
    10151144    }
    10161145
    10171146#if ENABLE_FEATURE_LS_COLOR
    1018     /* find color bit value - last position for short getopt */
     1147    /* set G_show_color = 1/0 */
    10191148    if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
    10201149        char *p = getenv("LS_COLORS");
    10211150        /* LS_COLORS is unset, or (not empty && not "none") ? */
    10221151        if (!p || (p[0] && strcmp(p, "none") != 0))
    1023             show_color = 1;
     1152            G_show_color = 1;
    10241153    }
    10251154    if (opt & OPT_color) {
    10261155        if (color_opt[0] == 'n')
    1027             show_color = 0;
     1156            G_show_color = 0;
    10281157        else switch (index_in_substrings(color_str, color_opt)) {
    10291158        case 3:
     
    10341163        case 1:
    10351164        case 2:
    1036                 show_color = 1;
     1165                G_show_color = 1;
    10371166            }
    10381167        }
     
    10411170
    10421171    /* sort out which command line options take precedence */
    1043     if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST))
    1044         all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
     1172    if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
     1173        G.all_fmt &= ~DISP_RECURSIVE;   /* no recurse if listing only dir */
    10451174    if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
    1046         if (all_fmt & TIME_CHANGE)
    1047             all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
    1048         if (all_fmt & TIME_ACCESS)
    1049             all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
    1050     }
    1051     if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
    1052         all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
    1053     if (ENABLE_FEATURE_LS_USERNAME)
    1054         if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
    1055             all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
    1056 
    1057     /* choose a display format */
    1058     if (!(all_fmt & STYLE_MASK))
    1059         all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
     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);
    10601186
    10611187    argv += optind;
     
    10641190
    10651191    if (argv[1])
    1066         all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
     1192        G.all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
    10671193
    10681194    /* stuff the command line file names into a dnode array */
     
    10701196    nfiles = 0;
    10711197    do {
    1072         /* NB: follow links on command line unless -l or -s */
    1073         cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
     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        );
    10741208        argv++;
    10751209        if (!cur)
    10761210            continue;
    1077         cur->fname_allocated = 0;
    1078         cur->next = dn;
     1211        /*cur->fname_allocated = 0; - already is */
     1212        cur->dn_next = dn;
    10791213        dn = cur;
    10801214        nfiles++;
     
    10831217    /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
    10841218    if (nfiles == 0)
    1085         return exit_code;
     1219        return G.exit_code;
    10861220
    10871221    /* now that we know how many files there are
     
    10911225    for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
    10921226        dnp[i] = dn;    /* save pointer to node in array */
    1093         dn = dn->next;
     1227        dn = dn->dn_next;
    10941228        if (!dn)
    10951229            break;
    10961230    }
    10971231
    1098     if (all_fmt & DISP_NOLIST) {
    1099         dnsort(dnp, nfiles);
    1100         showfiles(dnp, nfiles);
     1232    if (G.all_fmt & DISP_NOLIST) {
     1233        sort_and_display_files(dnp, nfiles);
    11011234    } else {
    11021235        dnd = splitdnarray(dnp, SPLIT_DIR);
     
    11051238        dnfiles = nfiles - dndirs;
    11061239        if (dnfiles > 0) {
    1107             dnsort(dnf, dnfiles);
    1108             showfiles(dnf, dnfiles);
     1240            sort_and_display_files(dnf, dnfiles);
    11091241            if (ENABLE_FEATURE_CLEAN_UP)
    11101242                free(dnf);
     
    11121244        if (dndirs > 0) {
    11131245            dnsort(dnd, dndirs);
    1114             showdirs(dnd, dnfiles == 0);
     1246            scan_and_display_dirs_recur(dnd, dnfiles == 0);
    11151247            if (ENABLE_FEATURE_CLEAN_UP)
    11161248                free(dnd);
    11171249        }
    11181250    }
     1251
    11191252    if (ENABLE_FEATURE_CLEAN_UP)
    11201253        dfree(dnp);
    1121     return exit_code;
    1122 }
     1254    return G.exit_code;
     1255}
Note: See TracChangeset for help on using the changeset viewer.