Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/coreutils/ls.c


Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

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

    r1765 r2725  
    44 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
    55 *
    6  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    77 */
    88
    9 /*
     9/* [date unknown. Perhaps before year 2000]
    1010 * To achieve a small memory footprint, this version of 'ls' doesn't do any
    1111 * file sorting, and only has the most essential command line switches
     
    1818 *
    1919 * KNOWN BUGS:
    20  * 1. ls -l of a directory doesn't give "total <blocks>" header
    21  * 2. ls of a symlink to a directory doesn't list directory contents
    22  * 3. hidden files can make column width too large
     20 * 1. hidden files can make column width too large
    2321 *
    2422 * NON-OPTIMAL BEHAVIOUR:
     
    2826 * PORTABILITY:
    2927 * 1. requires lstat (BSD) - how do you do it without?
     28 *
     29 * [2009-03]
     30 * ls sorts listing now, and supports almost all options.
    3031 */
    31 
    32 #include <getopt.h>
    3332#include "libbb.h"
     33#include "unicode.h"
     34
    3435
    3536/* This is a NOEXEC applet. Be very careful! */
    3637
    3738
     39#if ENABLE_FTPD
     40/* ftpd uses ls, and without timestamps Mozilla won't understand
     41 * ftpd's LIST output.
     42 */
     43# undef CONFIG_FEATURE_LS_TIMESTAMPS
     44# undef ENABLE_FEATURE_LS_TIMESTAMPS
     45# undef IF_FEATURE_LS_TIMESTAMPS
     46# undef IF_NOT_FEATURE_LS_TIMESTAMPS
     47# define CONFIG_FEATURE_LS_TIMESTAMPS 1
     48# define ENABLE_FEATURE_LS_TIMESTAMPS 1
     49# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
     50# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
     51#endif
     52
     53
    3854enum {
    39 
    4055TERMINAL_WIDTH  = 80,           /* use 79 if terminal has linefold bug */
    4156COLUMN_GAP      = 2,            /* includes the file type char */
     
    5772LIST_CONTEXT    = 1 << 6,
    5873LIST_SIZE       = 1 << 7,
    59 LIST_DEV        = 1 << 8,
     74//LIST_DEV        = 1 << 8, - unused, synonym to LIST_SIZE
    6075LIST_DATE_TIME  = 1 << 9,
    6176LIST_FULLTIME   = 1 << 10,
     
    105120SPLIT_FILE      = 0,
    106121SPLIT_SUBDIR    = 2,
    107 
    108122};
    109123
    110 #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
    111 #define TYPECHAR(mode)  ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
    112 #define APPCHAR(mode)   ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
    113 #define COLOR(mode) ("\000\043\043\043\042\000\043\043"\
    114              "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)])
    115 #define ATTR(mode)  ("\00\00\01\00\01\00\01\00"\
    116              "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)])
    117 
    118 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
    119 #if ENABLE_FEATURE_LS_COLOR
    120 static smallint show_color;
    121 /* long option entry used only for --color, which has no short option
    122  * equivalent */
    123 static const char ls_color_opt[] ALIGN1 =
    124     "color\0" Optional_argument "\xff" /* no short equivalent */
     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 */
     133static const char ls_options[] ALIGN1 =
     134    "Cadil1gnsxQAk" /* 13 opts, total 13 */
     135    IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
     136    IF_FEATURE_LS_SORTFILES("SXrv")  /* 4, 21 */
     137    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 */
    125143    ;
    126 #else
    127 enum { show_color = 0 };
    128 #endif
     144enum {
     145    //OPT_C = (1 << 0),
     146    //OPT_a = (1 << 1),
     147    //OPT_d = (1 << 2),
     148    //OPT_i = (1 << 3),
     149    //OPT_l = (1 << 4),
     150    //OPT_1 = (1 << 5),
     151    OPT_g = (1 << 6),
     152    //OPT_n = (1 << 7),
     153    //OPT_s = (1 << 8),
     154    //OPT_x = (1 << 9),
     155    OPT_Q = (1 << 10),
     156    //OPT_A = (1 << 11),
     157    //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,
     168};
     169
     170enum {
     171    LIST_MASK_TRIGGER   = 0,
     172    STYLE_MASK_TRIGGER  = STYLE_MASK,
     173    DISP_MASK_TRIGGER   = DISP_ROWS,
     174    SORT_MASK_TRIGGER   = SORT_MASK,
     175};
     176
     177/* TODO: simple toggles may be stored as OPT_xxx bits instead */
     178static 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
    129229
    130230/*
    131231 * a directory entry and its stat info are stored here
    132232 */
    133 struct dnode {                  /* the basic node */
    134     const char *name;             /* the dir entry name */
    135     const char *fullname;         /* the dir entry name */
    136     int   allocated;
     233struct 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 */
     237    smallint fname_allocated;
    137238    struct stat dstat;      /* the file stat info */
    138     USE_SELINUX(security_context_t sid;)
    139     struct dnode *next;     /* point at the next node */
     239    IF_SELINUX(security_context_t sid;)
    140240};
    141 typedef struct dnode dnode_t;
    142 
    143 static struct dnode **list_dir(const char *);
    144 static struct dnode **dnalloc(int);
    145 static int list_single(struct dnode *);
    146 
    147 static unsigned all_fmt;
    148 
     241
     242struct globals {
     243#if ENABLE_FEATURE_LS_COLOR
     244    smallint show_color;
     245#endif
     246    smallint exit_code;
     247    unsigned all_fmt;
    149248#if ENABLE_FEATURE_AUTOWIDTH
    150 static unsigned tabstops = COLUMN_GAP;
    151 static unsigned terminal_width = TERMINAL_WIDTH;
     249    unsigned tabstops; // = COLUMN_GAP;
     250    unsigned terminal_width; // = TERMINAL_WIDTH;
     251#endif
     252#if ENABLE_FEATURE_LS_TIMESTAMPS
     253    /* Do time() just once. Saves one syscall per file for "ls -l" */
     254    time_t current_time_t;
     255#endif
     256} FIX_ALIASING;
     257#define G (*(struct globals*)&bb_common_bufsiz1)
     258#if ENABLE_FEATURE_LS_COLOR
     259# define show_color     (G.show_color    )
     260#else
     261enum { 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)
    152268#else
    153269enum {
     
    156272};
    157273#endif
    158 
    159 static int status = EXIT_SUCCESS;
     274#define current_time_t (G.current_time_t)
     275#define INIT_G() do { \
     276    /* we have to zero it out because of NOEXEC */ \
     277    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);) \
     281} while (0)
     282
    160283
    161284static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
     
    163286    struct stat dstat;
    164287    struct dnode *cur;
    165     USE_SELINUX(security_context_t sid = NULL;)
     288    IF_SELINUX(security_context_t sid = NULL;)
    166289
    167290    if ((all_fmt & FOLLOW_LINKS) || force_follow) {
     
    172295#endif
    173296        if (stat(fullname, &dstat)) {
    174             bb_perror_msg("%s", fullname);
    175             status = EXIT_FAILURE;
     297            bb_simple_perror_msg(fullname);
     298            exit_code = EXIT_FAILURE;
    176299            return 0;
    177300        }
     
    183306#endif
    184307        if (lstat(fullname, &dstat)) {
    185             bb_perror_msg("%s", fullname);
    186             status = EXIT_FAILURE;
     308            bb_simple_perror_msg(fullname);
     309            exit_code = EXIT_FAILURE;
    187310            return 0;
    188311        }
    189312    }
    190313
    191     cur = xmalloc(sizeof(struct dnode));
     314    cur = xmalloc(sizeof(*cur));
    192315    cur->fullname = fullname;
    193316    cur->name = name;
    194317    cur->dstat = dstat;
    195     USE_SELINUX(cur->sid = sid;)
     318    IF_SELINUX(cur->sid = sid;)
    196319    return cur;
    197320}
    198321
     322/* FYI type values: 1:fifo 2:char 4:dir 6:blk 8:file 10:link 12:socket
     323 * (various wacky OSes: 13:Sun door 14:BSD whiteout 5:XENIX named file
     324 *  3/7:multiplexed char/block device)
     325 * and we use 0 for unknown and 15 for executables (see below) */
     326#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)])
     329/* 036 black foreground              050 black background
     330   037 red foreground                051 red background
     331   040 green foreground              052 green background
     332   041 brown foreground              053 brown background
     333   042 blue foreground               054 blue background
     334   043 magenta (purple) foreground   055 magenta background
     335   044 cyan (light blue) foreground  056 cyan background
     336   045 gray foreground               057 white background
     337*/
     338#define COLOR(mode) ( \
     339    /*un  fi  chr     dir     blk     file    link    sock        exe */ \
     340    "\037\043\043\045\042\045\043\043\000\045\044\045\043\045\045\040" \
     341    [TYPEINDEX(mode)])
     342/* Select normal (0) [actually "reset all"] or bold (1)
     343 * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
     344 *  let's use 7 for "impossible" types, just for fun)
     345 * Note: coreutils 6.9 uses inverted red for setuid binaries.
     346 */
     347#define ATTR(mode) ( \
     348    /*un fi chr   dir   blk   file  link  sock     exe */ \
     349    "\01\00\01\07\01\07\01\07\00\07\01\07\01\07\07\01" \
     350    [TYPEINDEX(mode)])
     351
    199352#if ENABLE_FEATURE_LS_COLOR
     353/* mode of zero is interpreted as "unknown" (stat failed) */
    200354static char fgcolor(mode_t mode)
    201355{
    202     /* Check wheter the file is existing (if so, color it red!) */
    203     if (errno == ENOENT)
    204         return '\037';
    205356    if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
    206357        return COLOR(0xF000);   /* File is executable ... */
    207358    return COLOR(mode);
    208359}
    209 
    210 static char bgcolor(mode_t mode)
     360static char bold(mode_t mode)
    211361{
    212362    if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
     
    231381#endif
    232382
    233 #define countdirs(A, B) count_dirs((A), (B), 1)
    234 #define countsubdirs(A, B) count_dirs((A), (B), 0)
    235 static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
    236 {
    237     int i, dirs;
     383static unsigned count_dirs(struct dnode **dn, int which)
     384{
     385    unsigned dirs, all;
    238386
    239387    if (!dn)
    240388        return 0;
    241     dirs = 0;
    242     for (i = 0; i < nfiles; i++) {
     389
     390    dirs = all = 0;
     391    for (; *dn; dn++) {
    243392        const char *name;
    244         if (!S_ISDIR(dn[i]->dstat.st_mode))
     393
     394        all++;
     395        if (!S_ISDIR((*dn)->dstat.st_mode))
    245396            continue;
    246         name = dn[i]->name;
    247         if (notsubdirs
    248          || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
     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]))
    249401        ) {
    250402            dirs++;
    251403        }
    252404    }
    253     return dirs;
    254 }
    255 
    256 static int countfiles(struct dnode **dnp)
    257 {
    258     int nfiles;
    259     struct dnode *cur;
    260 
    261     if (dnp == NULL)
    262         return 0;
    263     nfiles = 0;
    264     for (cur = dnp[0]; cur->next; cur = cur->next)
    265         nfiles++;
    266     nfiles++;
    267     return nfiles;
     405    return which != SPLIT_FILE ? dirs : all - dirs;
    268406}
    269407
    270408/* get memory to hold an array of pointers */
    271 static struct dnode **dnalloc(int num)
     409static struct dnode **dnalloc(unsigned num)
    272410{
    273411    if (num < 1)
    274412        return NULL;
    275413
     414    num++; /* so that we have terminating NULL */
    276415    return xzalloc(num * sizeof(struct dnode *));
    277416}
    278417
    279418#if ENABLE_FEATURE_LS_RECURSIVE
    280 static void dfree(struct dnode **dnp, int nfiles)
    281 {
    282     int i;
     419static void dfree(struct dnode **dnp)
     420{
     421    unsigned i;
    283422
    284423    if (dnp == NULL)
    285424        return;
    286425
    287     for (i = 0; i < nfiles; i++) {
     426    for (i = 0; dnp[i]; i++) {
    288427        struct dnode *cur = dnp[i];
    289         if (cur->allocated)
    290             free((char*)cur->fullname); /* free the filename */
    291         free(cur);      /* free the dnode */
    292     }
    293     free(dnp);          /* free the array holding the dnode pointers */
     428        if (cur->fname_allocated)
     429            free((char*)cur->fullname);
     430        free(cur);
     431    }
     432    free(dnp);
    294433}
    295434#else
     
    297436#endif
    298437
    299 static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
    300 {
    301     int dncnt, i, d;
     438/* Returns NULL-terminated malloced vector of pointers (or NULL) */
     439static struct dnode **splitdnarray(struct dnode **dn, int which)
     440{
     441    unsigned dncnt, d;
    302442    struct dnode **dnp;
    303443
    304     if (dn == NULL || nfiles < 1)
     444    if (dn == NULL)
    305445        return NULL;
    306446
    307     /* count how many dirs and regular files there are */
    308     if (which == SPLIT_SUBDIR)
    309         dncnt = countsubdirs(dn, nfiles);
    310     else {
    311         dncnt = countdirs(dn, nfiles);  /* assume we are looking for dirs */
    312         if (which == SPLIT_FILE)
    313             dncnt = nfiles - dncnt; /* looking for files */
    314     }
     447    /* count how many dirs or files there are */
     448    dncnt = count_dirs(dn, which);
    315449
    316450    /* allocate a file array and a dir array */
     
    318452
    319453    /* copy the entrys into the file or dir array */
    320     for (d = i = 0; i < nfiles; i++) {
    321         if (S_ISDIR(dn[i]->dstat.st_mode)) {
     454    for (d = 0; *dn; dn++) {
     455        if (S_ISDIR((*dn)->dstat.st_mode)) {
    322456            const char *name;
     457
    323458            if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
    324459                continue;
    325             name = dn[i]->name;
     460            name = (*dn)->name;
    326461            if ((which & SPLIT_DIR)
    327462             || name[0]!='.' || (name[1] && (name[1]!='.' || name[2]))
    328463            ) {
    329                 dnp[d++] = dn[i];
     464                dnp[d++] = *dn;
    330465            }
    331466        } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
    332             dnp[d++] = dn[i];
     467            dnp[d++] = *dn;
    333468        }
    334469    }
     
    342477    struct dnode *d2 = *(struct dnode **)b;
    343478    unsigned sort_opts = all_fmt & SORT_MASK;
    344     int dif;
     479    off_t dif;
    345480
    346481    dif = 0; /* assume SORT_NAME */
     
    348483    // instead of branch forest
    349484    if (sort_opts == SORT_SIZE) {
    350         dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
     485        dif = (d2->dstat.st_size - d1->dstat.st_size);
    351486    } else if (sort_opts == SORT_ATIME) {
    352         dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
     487        dif = (d2->dstat.st_atime - d1->dstat.st_atime);
    353488    } else if (sort_opts == SORT_CTIME) {
    354         dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
     489        dif = (d2->dstat.st_ctime - d1->dstat.st_ctime);
    355490    } else if (sort_opts == SORT_MTIME) {
    356         dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
     491        dif = (d2->dstat.st_mtime - d1->dstat.st_mtime);
    357492    } else if (sort_opts == SORT_DIR) {
    358493        dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
     
    360495        /* } else if (sort_opts == SORT_EXT) { */
    361496    }
    362 
    363497    if (dif == 0) {
    364         /* sort by name - may be a tie_breaker for time or size cmp */
    365         if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
    366         else dif = strcmp(d1->name, d2->name);
    367     }
    368 
    369     if (all_fmt & SORT_REVERSE) {
    370         dif = -dif;
    371     }
    372     return dif;
     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;
    373515}
    374516
     
    382524
    383525
    384 static void showfiles(struct dnode **dn, int nfiles)
    385 {
    386     int i, ncols, nrows, row, nc;
    387     int column = 0;
    388     int nexttab = 0;
    389     int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
    390 
    391     if (dn == NULL || nfiles < 1)
    392         return;
    393 
    394     if (all_fmt & STYLE_LONG) {
     526static unsigned calc_name_len(const char *name)
     527{
     528    unsigned len;
     529    uni_stat_t uni_stat;
     530
     531    // TODO: quote tab as \t, etc, if -Q
     532    name = printable_string(&uni_stat, name);
     533
     534    if (!(option_mask32 & OPT_Q)) {
     535        return uni_stat.unicode_width;
     536    }
     537
     538    len = 2 + uni_stat.unicode_width;
     539    while (*name) {
     540        if (*name == '"' || *name == '\\') {
     541            len++;
     542        }
     543        name++;
     544    }
     545    return len;
     546}
     547
     548
     549/* Return the number of used columns.
     550 * Note that only STYLE_COLUMNS uses return value.
     551 * STYLE_SINGLE and STYLE_LONG don't care.
     552 * coreutils 7.2 also supports:
     553 * ls -b (--escape) = octal escapes (although it doesn't look like working)
     554 * ls -N (--literal) = not escape at all
     555 */
     556static unsigned print_name(const char *name)
     557{
     558    unsigned len;
     559    uni_stat_t uni_stat;
     560
     561    // TODO: quote tab as \t, etc, if -Q
     562    name = printable_string(&uni_stat, name);
     563
     564    if (!(option_mask32 & OPT_Q)) {
     565        fputs(name, stdout);
     566        return uni_stat.unicode_width;
     567    }
     568
     569    len = 2 + uni_stat.unicode_width;
     570    putchar('"');
     571    while (*name) {
     572        if (*name == '"' || *name == '\\') {
     573            putchar('\\');
     574            len++;
     575        }
     576        putchar(*name);
     577        name++;
     578    }
     579    putchar('"');
     580    return len;
     581}
     582
     583/* Return the number of used columns.
     584 * Note that only STYLE_COLUMNS uses return value,
     585 * STYLE_SINGLE and STYLE_LONG don't care.
     586 */
     587static NOINLINE unsigned list_single(const struct dnode *dn)
     588{
     589    unsigned column = 0;
     590    char *lpath = lpath; /* for compiler */
     591#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
     592    struct stat info;
     593    char append;
     594#endif
     595
     596    /* Never happens:
     597    if (dn->fullname == NULL)
     598        return 0;
     599    */
     600
     601#if ENABLE_FEATURE_LS_FILETYPES
     602    append = append_char(dn->dstat.st_mode);
     603#endif
     604
     605    /* Do readlink early, so that if it fails, error message
     606     * does not appear *inside* the "ls -l" line */
     607    if (all_fmt & LIST_SYMLINK)
     608        if (S_ISLNK(dn->dstat.st_mode))
     609            lpath = xmalloc_readlink_or_warn(dn->fullname);
     610
     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);
     619#if ENABLE_FEATURE_LS_USERNAME
     620    if (all_fmt & LIST_ID_NAME) {
     621        if (option_mask32 & OPT_g) {
     622            column += printf("%-8.8s ",
     623                get_cached_groupname(dn->dstat.st_gid));
     624        } else {
     625            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)) {
     641            column += printf("%4u, %3u ",
     642                    (int) major(dn->dstat.st_rdev),
     643                    (int) minor(dn->dstat.st_rdev));
     644        } else {
     645            if (all_fmt & LS_DISP_HR) {
     646                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)
     649                );
     650            } else {
     651                column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
     652            }
     653        }
     654    }
     655#if ENABLE_FEATURE_LS_TIMESTAMPS
     656    if (all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
     657        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;
     663        filetime = ctime(&ttime);
     664        /* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
     665        if (all_fmt & LIST_FULLTIME)
     666            column += printf("%.24s ", filetime);
     667        else { /* LIST_DATE_TIME */
     668            /* current_time_t ~== time(NULL) */
     669            time_t age = current_time_t - ttime;
     670            printf("%.6s ", filetime + 4); /* "Jun 30" */
     671            if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
     672                /* hh:mm if less than 6 months old */
     673                printf("%.5s ", filetime + 11);
     674            } else { /* year. buggy if year > 9999 ;) */
     675                printf(" %.4s ", filetime + 20);
     676            }
     677            column += 13;
     678        }
     679    }
     680#endif
     681#if ENABLE_SELINUX
     682    if (all_fmt & LIST_CONTEXT) {
     683        column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
     684        freecon(dn->sid);
     685    }
     686#endif
     687    if (all_fmt & LIST_FILENAME) {
     688#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) {
     698            printf("\033[0m");
     699        }
     700    }
     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    }
     725#if ENABLE_FEATURE_LS_FILETYPES
     726    if (all_fmt & LIST_FILETYPE) {
     727        if (append) {
     728            putchar(append);
     729            column++;
     730        }
     731    }
     732#endif
     733
     734    return column;
     735}
     736
     737static void showfiles(struct dnode **dn, unsigned nfiles)
     738{
     739    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 */
    395745        ncols = 1;
    396746    } else {
    397747        /* find the longest file name, use that as the column width */
    398         for (i = 0; i < nfiles; i++) {
    399             int len = strlen(dn[i]->name);
     748        for (i = 0; dn[i]; i++) {
     749            int len = calc_name_len(dn[i]->name);
    400750            if (column_width < len)
    401751                column_width = len;
    402752        }
    403753        column_width += tabstops +
    404             USE_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
    405                          ((all_fmt & LIST_INO) ? 8 : 0) +
    406                          ((all_fmt & LIST_BLOCKS) ? 5 : 0);
     754            IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
     755                ((all_fmt & LIST_INO) ? 8 : 0) +
     756                ((all_fmt & LIST_BLOCKS) ? 5 : 0);
    407757        ncols = (int) (terminal_width / column_width);
    408758    }
     
    420770        for (nc = 0; nc < ncols; nc++) {
    421771            /* reach into the array based on the column and row */
    422             i = (nc * nrows) + row; /* assume display by column */
    423772            if (all_fmt & DISP_ROWS)
    424773                i = (row * ncols) + nc; /* display across row */
     774            else
     775                i = (nc * nrows) + row; /* display by column */
    425776            if (i < nfiles) {
    426777                if (column > 0) {
     
    439790
    440791
    441 static void showdirs(struct dnode **dn, int ndirs, int first)
    442 {
    443     int i, nfiles;
     792#if ENABLE_DESKTOP
     793/* http://www.opengroup.org/onlinepubs/9699919799/utilities/ls.html
     794 * If any of the -l, -n, -s options is specified, each list
     795 * of files within the directory shall be preceded by a
     796 * status line indicating the number of file system blocks
     797 * occupied by files in the directory in 512-byte units if
     798 * the -k option is not specified, or 1024-byte units if the
     799 * -k option is specified, rounded up to the next integral
     800 * number of units.
     801 */
     802/* by Jorgen Overgaard (jorgen AT antistaten.se) */
     803static off_t calculate_blocks(struct dnode **dn)
     804{
     805    uoff_t blocks = 1;
     806    if (dn) {
     807        while (*dn) {
     808            /* st_blocks is in 512 byte blocks */
     809            blocks += (*dn)->dstat.st_blocks;
     810            dn++;
     811        }
     812    }
     813
     814    /* Even though standard says use 512 byte blocks, coreutils use 1k */
     815    /* Actually, we round up by calculating (blocks + 1) / 2,
     816     * "+ 1" was done when we initialized blocks to 1 */
     817    return blocks >> 1;
     818}
     819#endif
     820
     821
     822static struct dnode **list_dir(const char *, unsigned *);
     823
     824static void showdirs(struct dnode **dn, int first)
     825{
     826    unsigned nfiles;
     827    unsigned dndirs;
    444828    struct dnode **subdnp;
    445     int dndirs;
    446829    struct dnode **dnd;
    447830
    448     if (dn == NULL || ndirs < 1)
     831    /* Never happens:
     832    if (dn == NULL || ndirs < 1) {
    449833        return;
    450 
    451     for (i = 0; i < ndirs; i++) {
     834    }
     835    */
     836
     837    for (; *dn; dn++) {
    452838        if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
    453839            if (!first)
    454                 puts("");
     840                bb_putchar('\n');
    455841            first = 0;
    456             printf("%s:\n", dn[i]->fullname);
    457         }
    458         subdnp = list_dir(dn[i]->fullname);
    459         nfiles = countfiles(subdnp);
     842            printf("%s:\n", (*dn)->fullname);
     843        }
     844        subdnp = list_dir((*dn)->fullname, &nfiles);
     845#if ENABLE_DESKTOP
     846        if ((all_fmt & STYLE_MASK) == STYLE_LONG)
     847            printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
     848#endif
    460849        if (nfiles > 0) {
    461850            /* list all files at this level */
    462851            dnsort(subdnp, nfiles);
    463852            showfiles(subdnp, nfiles);
    464             if (ENABLE_FEATURE_LS_RECURSIVE) {
    465                 if (all_fmt & DISP_RECURSIVE) {
    466                     /* recursive- list the sub-dirs */
    467                     dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
    468                     dndirs = countsubdirs(subdnp, nfiles);
    469                     if (dndirs > 0) {
    470                         dnsort(dnd, dndirs);
    471                         showdirs(dnd, dndirs, 0);
    472                         /* free the array of dnode pointers to the dirs */
    473                         free(dnd);
    474                     }
     853            if (ENABLE_FEATURE_LS_RECURSIVE
     854             && (all_fmt & DISP_RECURSIVE)
     855            ) {
     856                /* recursive - list the sub-dirs */
     857                dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
     858                dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
     859                if (dndirs > 0) {
     860                    dnsort(dnd, dndirs);
     861                    showdirs(dnd, 0);
     862                    /* free the array of dnode pointers to the dirs */
     863                    free(dnd);
    475864                }
    476                 /* free the dnodes and the fullname mem */
    477                 dfree(subdnp, nfiles);
    478865            }
    479         }
    480     }
    481 }
    482 
    483 
    484 static struct dnode **list_dir(const char *path)
     866            /* free the dnodes and the fullname mem */
     867            dfree(subdnp);
     868        }
     869    }
     870}
     871
     872
     873/* Returns NULL-terminated malloced vector of pointers (or NULL) */
     874static struct dnode **list_dir(const char *path, unsigned *nfiles_p)
    485875{
    486876    struct dnode *dn, *cur, **dnp;
    487877    struct dirent *entry;
    488878    DIR *dir;
    489     int i, nfiles;
    490 
     879    unsigned i, nfiles;
     880
     881    /* Never happens:
    491882    if (path == NULL)
    492883        return NULL;
    493 
     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    }
    494892    dn = NULL;
    495893    nfiles = 0;
    496     dir = warn_opendir(path);
    497     if (dir == NULL) {
    498         status = EXIT_FAILURE;
    499         return NULL;    /* could not open the dir */
    500     }
    501894    while ((entry = readdir(dir)) != NULL) {
    502895        char *fullname;
     
    518911            continue;
    519912        }
    520         cur->allocated = 1;
     913        cur->fname_allocated = 1;
    521914        cur->next = dn;
    522915        dn = cur;
     
    525918    closedir(dir);
    526919
     920    if (dn == NULL)
     921        return NULL;
     922
    527923    /* now that we know how many files there are
    528924     * allocate memory for an array to hold dnode pointers
    529925     */
    530     if (dn == NULL)
    531         return NULL;
     926    *nfiles_p = nfiles;
    532927    dnp = dnalloc(nfiles);
    533     for (i = 0, cur = dn; i < nfiles; i++) {
    534         dnp[i] = cur;   /* save pointer to node in array */
    535         cur = cur->next;
     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;
    536933    }
    537934
     
    540937
    541938
    542 #if ENABLE_FEATURE_LS_TIMESTAMPS
    543 /* Do time() just once. Saves one syscall per file for "ls -l" */
    544 /* Initialized in main() */
    545 static time_t current_time_t;
    546 #endif
    547 
    548 static int list_single(struct dnode *dn)
    549 {
    550     int i, column = 0;
    551 
    552 #if ENABLE_FEATURE_LS_TIMESTAMPS
    553     char *filetime;
    554     time_t ttime, age;
    555 #endif
    556 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
    557     struct stat info;
    558     char append;
    559 #endif
    560 
    561     if (dn->fullname == NULL)
    562         return 0;
    563 
    564 #if ENABLE_FEATURE_LS_TIMESTAMPS
    565     ttime = dn->dstat.st_mtime; /* the default time */
    566     if (all_fmt & TIME_ACCESS)
    567         ttime = dn->dstat.st_atime;
    568     if (all_fmt & TIME_CHANGE)
    569         ttime = dn->dstat.st_ctime;
    570     filetime = ctime(&ttime);
    571 #endif
    572 #if ENABLE_FEATURE_LS_FILETYPES
    573     append = append_char(dn->dstat.st_mode);
    574 #endif
    575 
    576     for (i = 0; i <= 31; i++) {
    577         switch (all_fmt & (1 << i)) {
    578         case LIST_INO:
    579             column += printf("%7ld ", (long) dn->dstat.st_ino);
    580             break;
    581         case LIST_BLOCKS:
    582             column += printf("%4"OFF_FMT"d ", (off_t) dn->dstat.st_blocks >> 1);
    583             break;
    584         case LIST_MODEBITS:
    585             column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
    586             break;
    587         case LIST_NLINKS:
    588             column += printf("%4ld ", (long) dn->dstat.st_nlink);
    589             break;
    590         case LIST_ID_NAME:
    591 #if ENABLE_FEATURE_LS_USERNAME
    592             printf("%-8.8s %-8.8s",
    593                 get_cached_username(dn->dstat.st_uid),
    594                 get_cached_groupname(dn->dstat.st_gid));
    595             column += 17;
    596             break;
    597 #endif
    598         case LIST_ID_NUMERIC:
    599             column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid);
    600             break;
    601         case LIST_SIZE:
    602         case LIST_DEV:
    603             if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
    604                 column += printf("%4d, %3d ", (int) major(dn->dstat.st_rdev),
    605                        (int) minor(dn->dstat.st_rdev));
    606             } else {
    607                 if (all_fmt & LS_DISP_HR) {
    608                     column += printf("%9s ",
    609                         make_human_readable_str(dn->dstat.st_size, 1, 0));
    610                 } else {
    611                     column += printf("%9"OFF_FMT"d ", (off_t) dn->dstat.st_size);
    612                 }
    613             }
    614             break;
    615 #if ENABLE_FEATURE_LS_TIMESTAMPS
    616         case LIST_FULLTIME:
    617             printf("%24.24s ", filetime);
    618             column += 25;
    619             break;
    620         case LIST_DATE_TIME:
    621             if ((all_fmt & LIST_FULLTIME) == 0) {
    622                 /* current_time_t ~== time(NULL) */
    623                 age = current_time_t - ttime;
    624                 printf("%6.6s ", filetime + 4);
    625                 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
    626                     /* hh:mm if less than 6 months old */
    627                     printf("%5.5s ", filetime + 11);
    628                 } else {
    629                     printf(" %4.4s ", filetime + 20);
    630                 }
    631                 column += 13;
    632             }
    633             break;
    634 #endif
    635 #if ENABLE_SELINUX
    636         case LIST_CONTEXT:
    637             {
    638                 char context[80];
    639                 int len = 0;
    640 
    641                 if (dn->sid) {
    642                     /* I assume sid initilized with NULL */
    643                     len = strlen(dn->sid) + 1;
    644                     safe_strncpy(context, dn->sid, len);
    645                     freecon(dn->sid);
    646                 } else {
    647                     safe_strncpy(context, "unknown", 8);
    648                 }
    649                 printf("%-32s ", context);
    650                 column += MAX(33, len);
    651             }
    652             break;
    653 #endif
    654         case LIST_FILENAME:
    655             errno = 0;
    656 #if ENABLE_FEATURE_LS_COLOR
    657             if (show_color && !lstat(dn->fullname, &info)) {
    658                 printf("\033[%d;%dm", bgcolor(info.st_mode),
    659                         fgcolor(info.st_mode));
    660             }
    661 #endif
    662             column += printf("%s", dn->name);
    663             if (show_color) {
    664                 printf("\033[0m");
    665             }
    666             break;
    667         case LIST_SYMLINK:
    668             if (S_ISLNK(dn->dstat.st_mode)) {
    669                 char *lpath = xmalloc_readlink_or_warn(dn->fullname);
    670                 if (!lpath) break;
    671                 printf(" -> ");
    672 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
    673                 if (!stat(dn->fullname, &info)) {
    674                     append = append_char(info.st_mode);
    675                 }
    676 #endif
    677 #if ENABLE_FEATURE_LS_COLOR
    678                 if (show_color) {
    679                     errno = 0;
    680                     printf("\033[%d;%dm", bgcolor(info.st_mode),
    681                            fgcolor(info.st_mode));
    682                 }
    683 #endif
    684                 column += printf("%s", lpath) + 4;
    685                 if (show_color) {
    686                     printf("\033[0m");
    687                 }
    688                 free(lpath);
    689             }
    690             break;
    691 #if ENABLE_FEATURE_LS_FILETYPES
    692         case LIST_FILETYPE:
    693             if (append) {
    694                 putchar(append);
    695                 column++;
    696             }
    697             break;
    698 #endif
    699         }
    700     }
    701 
    702     return column;
    703 }
    704 
    705 /* "[-]Cadil1", POSIX mandated options, busybox always supports */
    706 /* "[-]gnsx", POSIX non-mandated options, busybox always supports */
    707 /* "[-]Ak" GNU options, busybox always supports */
    708 /* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
    709 /* "[-]p", POSIX non-mandated options, busybox optionally supports */
    710 /* "[-]SXvThw", GNU options, busybox optionally supports */
    711 /* "[-]K", SELinux mandated options, busybox optionally supports */
    712 /* "[-]e", I think we made this one up */
    713 static const char ls_options[] ALIGN1 =
    714     "Cadil1gnsxAk"
    715     USE_FEATURE_LS_TIMESTAMPS("cetu")
    716     USE_FEATURE_LS_SORTFILES("SXrv")
    717     USE_FEATURE_LS_FILETYPES("Fp")
    718     USE_FEATURE_LS_FOLLOWLINKS("L")
    719     USE_FEATURE_LS_RECURSIVE("R")
    720     USE_FEATURE_HUMAN_READABLE("h")
    721     USE_SELINUX("K")
    722     USE_FEATURE_AUTOWIDTH("T:w:")
    723     USE_SELINUX("Z");
    724 
    725 enum {
    726     LIST_MASK_TRIGGER   = 0,
    727     STYLE_MASK_TRIGGER  = STYLE_MASK,
    728     DISP_MASK_TRIGGER   = DISP_ROWS,
    729     SORT_MASK_TRIGGER   = SORT_MASK,
    730 };
    731 
    732 static const unsigned opt_flags[] = {
    733     LIST_SHORT | STYLE_COLUMNS, /* C */
    734     DISP_HIDDEN | DISP_DOT,     /* a */
    735     DISP_NOLIST,                /* d */
    736     LIST_INO,                   /* i */
    737     LIST_LONG | STYLE_LONG,     /* l - remember LS_DISP_HR in mask! */
    738     LIST_SHORT | STYLE_SINGLE,  /* 1 */
    739     0,                          /* g - ingored */
    740     LIST_ID_NUMERIC,            /* n */
    741     LIST_BLOCKS,                /* s */
    742     DISP_ROWS,                  /* x */
    743     DISP_HIDDEN,                /* A */
    744     ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */
    745 #if ENABLE_FEATURE_LS_TIMESTAMPS
    746     TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME),   /* c */
    747     LIST_FULLTIME,              /* e */
    748     ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME,   /* t */
    749     TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME),   /* u */
    750 #endif
    751 #if ENABLE_FEATURE_LS_SORTFILES
    752     SORT_SIZE,                  /* S */
    753     SORT_EXT,                   /* X */
    754     SORT_REVERSE,               /* r */
    755     SORT_VERSION,               /* v */
    756 #endif
    757 #if ENABLE_FEATURE_LS_FILETYPES
    758     LIST_FILETYPE | LIST_EXEC,  /* F */
    759     LIST_FILETYPE,              /* p */
    760 #endif
    761 #if ENABLE_FEATURE_LS_FOLLOWLINKS
    762     FOLLOW_LINKS,               /* L */
    763 #endif
    764 #if ENABLE_FEATURE_LS_RECURSIVE
    765     DISP_RECURSIVE,             /* R */
    766 #endif
    767 #if ENABLE_FEATURE_HUMAN_READABLE
    768     LS_DISP_HR,                 /* h */
    769 #endif
    770 #if ENABLE_SELINUX
    771     LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
    772 #endif
    773 #if ENABLE_FEATURE_AUTOWIDTH
    774     0, 0,                       /* T, w - ignored */
    775 #endif
    776 #if ENABLE_SELINUX
    777     LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
    778 #endif
    779     (1U<<31)
    780 };
    781 
    782 
    783 /* THIS IS A "SAFE" APPLET, main() MAY BE CALLED INTERNALLY FROM SHELL */
    784 /* BE CAREFUL! */
    785 
    786 int ls_main(int argc, char **argv);
    787 int ls_main(int argc, char **argv)
     939int ls_main(int argc UNUSED_PARAM, char **argv)
    788940{
    789941    struct dnode **dnd;
     
    793945    struct dnode *cur;
    794946    unsigned opt;
    795     int nfiles = 0;
    796     int dnfiles;
    797     int dndirs;
    798     int oi;
    799     int ac;
    800     int i;
    801     char **av;
    802     USE_FEATURE_AUTOWIDTH(char *tabstops_str = NULL;)
    803     USE_FEATURE_AUTOWIDTH(char *terminal_width_str = NULL;)
    804     USE_FEATURE_LS_COLOR(char *color_opt;)
    805 
    806 #if ENABLE_FEATURE_LS_TIMESTAMPS
    807     time(&current_time_t);
    808 #endif
     947    unsigned nfiles;
     948    unsigned dnfiles;
     949    unsigned dndirs;
     950    unsigned i;
     951#if ENABLE_FEATURE_LS_COLOR
     952    /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
     953    /* coreutils 6.10:
     954     * # ls --color=BOGUS
     955     * ls: invalid argument 'BOGUS' for '--color'
     956     * Valid arguments are:
     957     * 'always', 'yes', 'force'
     958     * 'never', 'no', 'none'
     959     * 'auto', 'tty', 'if-tty'
     960     * (and substrings: "--color=alwa" work too)
     961     */
     962    static const char ls_longopts[] ALIGN1 =
     963        "color\0" Optional_argument "\xff"; /* no short equivalent */
     964    static const char color_str[] ALIGN1 =
     965        "always\0""yes\0""force\0"
     966        "auto\0""tty\0""if-tty\0";
     967    /* need to initialize since --color has _an optional_ argument */
     968    const char *color_opt = color_str; /* "always" */
     969#endif
     970
     971    INIT_G();
     972
     973    init_unicode();
    809974
    810975    all_fmt = LIST_SHORT |
     
    812977
    813978#if ENABLE_FEATURE_AUTOWIDTH
    814     /* Obtain the terminal width */
     979    /* obtain the terminal width */
    815980    get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
    816     /* Go one less... */
     981    /* go one less... */
    817982    terminal_width--;
    818983#endif
    819984
    820985    /* process options */
    821     USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)
     986    IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
    822987#if ENABLE_FEATURE_AUTOWIDTH
    823     opt = getopt32(argv, ls_options, &tabstops_str, &terminal_width_str
    824                 USE_FEATURE_LS_COLOR(, &color_opt));
    825     if (tabstops_str)
    826         tabstops = xatou(tabstops_str);
    827     if (terminal_width_str)
    828         terminal_width = xatou(terminal_width_str);
     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));
    829991#else
    830     opt = getopt32(argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));
     992    opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
    831993#endif
    832994    for (i = 0; opt_flags[i] != (1U<<31); i++) {
     
    8581020        char *p = getenv("LS_COLORS");
    8591021        /* LS_COLORS is unset, or (not empty && not "none") ? */
    860         if (!p || (p[0] && strcmp(p, "none")))
     1022        if (!p || (p[0] && strcmp(p, "none") != 0))
    8611023            show_color = 1;
    8621024    }
    863     if (opt & (1 << i)) {  /* next flag after short options */
    864         if (!color_opt || !strcmp("always", color_opt))
    865             show_color = 1;
    866         else if (color_opt && !strcmp("never", color_opt))
     1025    if (opt & OPT_color) {
     1026        if (color_opt[0] == 'n')
    8671027            show_color = 0;
    868         else if (color_opt && !strcmp("auto", color_opt) && isatty(STDOUT_FILENO))
    869             show_color = 1;
     1028        else switch (index_in_substrings(color_str, color_opt)) {
     1029        case 3:
     1030        case 4:
     1031        case 5:
     1032            if (isatty(STDOUT_FILENO)) {
     1033        case 0:
     1034        case 1:
     1035        case 2:
     1036                show_color = 1;
     1037            }
     1038        }
    8701039    }
    8711040#endif
     
    8901059        all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
    8911060
    892     /*
    893      * when there are no cmd line args we have to supply a default "." arg.
    894      * we will create a second argv array, "av" that will hold either
    895      * our created "." arg, or the real cmd line args.  The av array
    896      * just holds the pointers- we don't move the date the pointers
    897      * point to.
    898      */
    899     ac = argc - optind; /* how many cmd line args are left */
    900     if (ac < 1) {
    901         static const char *const dotdir[] = { "." };
    902 
    903         av = (char **) dotdir;
    904         ac = 1;
    905     } else {
    906         av = argv + optind;
    907     }
    908 
    909     /* now, everything is in the av array */
    910     if (ac > 1)
    911         all_fmt |= DISP_DIRNAME;    /* 2 or more items? label directories */
     1061    argv += optind;
     1062    if (!argv[0])
     1063        *--argv = (char*)".";
     1064
     1065    if (argv[1])
     1066        all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
    9121067
    9131068    /* stuff the command line file names into a dnode array */
    9141069    dn = NULL;
    915     for (oi = 0; oi < ac; oi++) {
    916         /* ls w/o -l follows links on command line */
    917         cur = my_stat(av[oi], av[oi], !(all_fmt & STYLE_LONG));
     1070    nfiles = 0;
     1071    do {
     1072        /* NB: follow links on command line unless -l or -s */
     1073        cur = my_stat(*argv, *argv, !(all_fmt & (STYLE_LONG|LIST_BLOCKS)));
     1074        argv++;
    9181075        if (!cur)
    9191076            continue;
    920         cur->allocated = 0;
     1077        cur->fname_allocated = 0;
    9211078        cur->next = dn;
    9221079        dn = cur;
    9231080        nfiles++;
    924     }
     1081    } while (*argv);
     1082
     1083    /* nfiles _may_ be 0 here - try "ls doesnt_exist" */
     1084    if (nfiles == 0)
     1085        return exit_code;
    9251086
    9261087    /* now that we know how many files there are
     
    9281089     */
    9291090    dnp = dnalloc(nfiles);
    930     for (i = 0, cur = dn; i < nfiles; i++) {
    931         dnp[i] = cur;   /* save pointer to node in array */
    932         cur = cur->next;
     1091    for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
     1092        dnp[i] = dn;    /* save pointer to node in array */
     1093        dn = dn->next;
     1094        if (!dn)
     1095            break;
    9331096    }
    9341097
    9351098    if (all_fmt & DISP_NOLIST) {
    9361099        dnsort(dnp, nfiles);
    937         if (nfiles > 0)
    938             showfiles(dnp, nfiles);
     1100        showfiles(dnp, nfiles);
    9391101    } else {
    940         dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
    941         dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
    942         dndirs = countdirs(dnp, nfiles);
     1102        dnd = splitdnarray(dnp, SPLIT_DIR);
     1103        dnf = splitdnarray(dnp, SPLIT_FILE);
     1104        dndirs = count_dirs(dnp, SPLIT_DIR);
    9431105        dnfiles = nfiles - dndirs;
    9441106        if (dnfiles > 0) {
     
    9501112        if (dndirs > 0) {
    9511113            dnsort(dnd, dndirs);
    952             showdirs(dnd, dndirs, dnfiles == 0);
     1114            showdirs(dnd, dnfiles == 0);
    9531115            if (ENABLE_FEATURE_CLEAN_UP)
    9541116                free(dnd);
     
    9561118    }
    9571119    if (ENABLE_FEATURE_CLEAN_UP)
    958         dfree(dnp, nfiles);
    959     return status;
    960 }
     1120        dfree(dnp);
     1121    return exit_code;
     1122}
Note: See TracChangeset for help on using the changeset viewer.