update random config tester
[oweals/busybox.git] / coreutils / ls.c
index 067e463eea0c52038cbd28ef9dccc9b6f5e60d24..f4e71bc6a4d0305a4a02eeb6079e1d1cb32deffc 100644 (file)
  * 1. requires lstat (BSD) - how do you do it without?
  */
 
-#include "busybox.h"
-#include <getopt.h>
+#include "libbb.h"
+
+#if ENABLE_FEATURE_ASSUME_UNICODE
+#include <wchar.h>
+#endif
+
+/* This is a NOEXEC applet. Be very careful! */
+
 
 enum {
 
@@ -112,76 +118,106 @@ SPLIT_SUBDIR    = 2,
 #define ATTR(mode)     ("\00\00\01\00\01\00\01\00"\
                         "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)])
 
-/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
-#if ENABLE_FEATURE_LS_COLOR
-static int show_color;
-/* long option entry used only for --color, which has no short option
- * equivalent */
-static const struct option ls_color_opt[] = {
-       { "color", optional_argument, NULL, 1 },
-       { NULL, 0, NULL, 0 }
-};
-#else
-enum { show_color = 0 };
-#endif
-
 /*
  * a directory entry and its stat info are stored here
  */
 struct dnode {                  /* the basic node */
-       char *name;             /* the dir entry name */
-       char *fullname;         /* the dir entry name */
+       const char *name;             /* the dir entry name */
+       const char *fullname;         /* the dir entry name */
        int   allocated;
        struct stat dstat;      /* the file stat info */
        USE_SELINUX(security_context_t sid;)
        struct dnode *next;     /* point at the next node */
 };
-typedef struct dnode dnode_t;
 
 static struct dnode **list_dir(const char *);
 static struct dnode **dnalloc(int);
-static int list_single(struct dnode *);
+static int list_single(const struct dnode *);
 
-static unsigned all_fmt;
 
+struct globals {
+#if ENABLE_FEATURE_LS_COLOR
+       smallint show_color;
+#endif
+       smallint exit_code;
+       unsigned all_fmt;
 #if ENABLE_FEATURE_AUTOWIDTH
-static unsigned tabstops = COLUMN_GAP;
-static unsigned terminal_width = TERMINAL_WIDTH;
+       unsigned tabstops; // = COLUMN_GAP;
+       unsigned terminal_width; // = TERMINAL_WIDTH;
+#endif
+#if ENABLE_FEATURE_LS_TIMESTAMPS
+       /* Do time() just once. Saves one syscall per file for "ls -l" */
+       time_t current_time_t;
+#endif
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#if ENABLE_FEATURE_LS_COLOR
+#define show_color     (G.show_color    )
+#else
+enum { show_color = 0 };
+#endif
+#define exit_code      (G.exit_code     )
+#define all_fmt        (G.all_fmt       )
+#if ENABLE_FEATURE_AUTOWIDTH
+#define tabstops       (G.tabstops      )
+#define terminal_width (G.terminal_width)
 #else
 enum {
        tabstops = COLUMN_GAP,
        terminal_width = TERMINAL_WIDTH,
 };
 #endif
+#define current_time_t (G.current_time_t)
+/* memset: we have to zero it out because of NOEXEC */
+#define INIT_G() do { \
+       memset(&G, 0, sizeof(G)); \
+       USE_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
+       USE_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
+       USE_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
+} while (0)
 
-static int status = EXIT_SUCCESS;
 
-static struct dnode *my_stat(char *fullname, char *name)
+#if ENABLE_FEATURE_ASSUME_UNICODE
+/* libbb candidate */
+static size_t mbstrlen(const char *string)
+{
+       size_t width = mbsrtowcs(NULL /*dest*/, &string,
+                               MAXINT(size_t) /*len*/, NULL /*state*/);
+       if (width == (size_t)-1)
+               return strlen(string);
+       return width;
+}
+#else
+#define mbstrlen(string) strlen(string)
+#endif
+
+
+static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
 {
        struct stat dstat;
        struct dnode *cur;
        USE_SELINUX(security_context_t sid = NULL;)
 
-       if (all_fmt & FOLLOW_LINKS) {
+       if ((all_fmt & FOLLOW_LINKS) || force_follow) {
 #if ENABLE_SELINUX
                if (is_selinux_enabled())  {
                         getfilecon(fullname, &sid);
                }
 #endif
                if (stat(fullname, &dstat)) {
-                       bb_perror_msg("%s", fullname);
-                       status = EXIT_FAILURE;
+                       bb_simple_perror_msg(fullname);
+                       exit_code = EXIT_FAILURE;
                        return 0;
                }
        } else {
 #if ENABLE_SELINUX
                if (is_selinux_enabled()) {
-                       lgetfilecon(fullname,&sid);
+                       lgetfilecon(fullname, &sid);
                }
 #endif
                if (lstat(fullname, &dstat)) {
-                       bb_perror_msg("%s", fullname);
-                       status = EXIT_FAILURE;
+                       bb_simple_perror_msg(fullname);
+                       exit_code = EXIT_FAILURE;
                        return 0;
                }
        }
@@ -238,7 +274,7 @@ static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
                return 0;
        dirs = 0;
        for (i = 0; i < nfiles; i++) {
-               char *name;
+               const char *name;
                if (!S_ISDIR(dn[i]->dstat.st_mode))
                        continue;
                name = dn[i]->name;
@@ -285,7 +321,7 @@ static void dfree(struct dnode **dnp, int nfiles)
        for (i = 0; i < nfiles; i++) {
                struct dnode *cur = dnp[i];
                if (cur->allocated)
-                       free(cur->fullname);    /* free the filename */
+                       free((char*)cur->fullname);     /* free the filename */
                free(cur);              /* free the dnode */
        }
        free(dnp);                      /* free the array holding the dnode pointers */
@@ -317,7 +353,7 @@ static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
        /* copy the entrys into the file or dir array */
        for (d = i = 0; i < nfiles; i++) {
                if (S_ISDIR(dn[i]->dstat.st_mode)) {
-                       char *name;
+                       const char *name;
                        if (!(which & (SPLIT_DIR|SPLIT_SUBDIR)))
                                continue;
                        name = dn[i]->name;
@@ -394,7 +430,7 @@ static void showfiles(struct dnode **dn, int nfiles)
        } else {
                /* find the longest file name, use that as the column width */
                for (i = 0; i < nfiles; i++) {
-                       int len = strlen(dn[i]->name);
+                       int len = mbstrlen(dn[i]->name);
                        if (column_width < len)
                                column_width = len;
                }
@@ -449,7 +485,7 @@ static void showdirs(struct dnode **dn, int ndirs, int first)
        for (i = 0; i < ndirs; i++) {
                if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
                        if (!first)
-                               puts("");
+                               bb_putchar('\n');
                        first = 0;
                        printf("%s:\n", dn[i]->fullname);
                }
@@ -493,7 +529,7 @@ static struct dnode **list_dir(const char *path)
        nfiles = 0;
        dir = warn_opendir(path);
        if (dir == NULL) {
-               status = EXIT_FAILURE;
+               exit_code = EXIT_FAILURE;
                return NULL;    /* could not open the dir */
        }
        while ((entry = readdir(dir)) != NULL) {
@@ -501,16 +537,16 @@ static struct dnode **list_dir(const char *path)
 
                /* are we going to list the file- it may be . or .. or a hidden file */
                if (entry->d_name[0] == '.') {
-                       if ((entry->d_name[1] == 0 || (
-                               entry->d_name[1] == '.'
-                               && entry->d_name[2] == 0))
-                                       && !(all_fmt & DISP_DOT))
+                       if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))
+                        && !(all_fmt & DISP_DOT)
+                       ) {
                                continue;
+                       }
                        if (!(all_fmt & DISP_HIDDEN))
                                continue;
                }
                fullname = concat_path_file(path, entry->d_name);
-               cur = my_stat(fullname, strrchr(fullname, '/') + 1);
+               cur = my_stat(fullname, bb_basename(fullname), 0);
                if (!cur) {
                        free(fullname);
                        continue;
@@ -537,13 +573,7 @@ static struct dnode **list_dir(const char *path)
 }
 
 
-#if ENABLE_FEATURE_LS_TIMESTAMPS
-/* Do time() just once. Saves one syscall per file for "ls -l" */
-/* Initialized in main() */
-static time_t current_time_t;
-#endif
-
-static int list_single(struct dnode *dn)
+static int list_single(const struct dnode *dn)
 {
        int i, column = 0;
 
@@ -657,14 +687,19 @@ static int list_single(struct dnode *dn)
                                                fgcolor(info.st_mode));
                        }
 #endif
+#if ENABLE_FEATURE_ASSUME_UNICODE
+                       printf("%s", dn->name);
+                       column += mbstrlen(dn->name);
+#else
                        column += printf("%s", dn->name);
+#endif
                        if (show_color) {
                                printf("\033[0m");
                        }
                        break;
                case LIST_SYMLINK:
                        if (S_ISLNK(dn->dstat.st_mode)) {
-                               char *lpath = xreadlink(dn->fullname);
+                               char *lpath = xmalloc_readlink_or_warn(dn->fullname);
                                if (!lpath) break;
                                printf(" -> ");
 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
@@ -700,6 +735,7 @@ static int list_single(struct dnode *dn)
        return column;
 }
 
+
 /* "[-]Cadil1", POSIX mandated options, busybox always supports */
 /* "[-]gnsx", POSIX non-mandated options, busybox always supports */
 /* "[-]Ak" GNU options, busybox always supports */
@@ -708,7 +744,8 @@ static int list_single(struct dnode *dn)
 /* "[-]SXvThw", GNU options, busybox optionally supports */
 /* "[-]K", SELinux mandated options, busybox optionally supports */
 /* "[-]e", I think we made this one up */
-static const char ls_options[] = "Cadil1gnsxAk"
+static const char ls_options[] ALIGN1 =
+       "Cadil1gnsxAk"
        USE_FEATURE_LS_TIMESTAMPS("cetu")
        USE_FEATURE_LS_SORTFILES("SXrv")
        USE_FEATURE_LS_FILETYPES("Fp")
@@ -716,7 +753,8 @@ static const char ls_options[] = "Cadil1gnsxAk"
        USE_FEATURE_LS_RECURSIVE("R")
        USE_FEATURE_HUMAN_READABLE("h")
        USE_SELINUX("K")
-       USE_FEATURE_AUTOWIDTH("T:w:");
+       USE_FEATURE_AUTOWIDTH("T:w:")
+       USE_SELINUX("Z");
 
 enum {
        LIST_MASK_TRIGGER       = 0,
@@ -768,12 +806,26 @@ static const unsigned opt_flags[] = {
 #endif
 #if ENABLE_FEATURE_AUTOWIDTH
        0, 0,                       /* T, w - ignored */
+#endif
+#if ENABLE_SELINUX
+       LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
 #endif
        (1U<<31)
 };
 
 
-int ls_main(int argc, char **argv)
+/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
+#if ENABLE_FEATURE_LS_COLOR
+/* long option entry used only for --color, which has no short option
+ * equivalent */
+static const char ls_color_opt[] ALIGN1 =
+       "color\0" Optional_argument "\xff" /* no short equivalent */
+       ;
+#endif
+
+
+int ls_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ls_main(int argc UNUSED_PARAM, char **argv)
 {
        struct dnode **dnd;
        struct dnode **dnf;
@@ -781,29 +833,21 @@ int ls_main(int argc, char **argv)
        struct dnode *dn;
        struct dnode *cur;
        unsigned opt;
-       int nfiles = 0;
+       int nfiles;
        int dnfiles;
        int dndirs;
-       int oi;
-       int ac;
        int i;
-       char **av;
-       USE_FEATURE_AUTOWIDTH(char *tabstops_str = NULL;)
-       USE_FEATURE_AUTOWIDTH(char *terminal_width_str = NULL;)
-       USE_FEATURE_LS_COLOR(char *color_opt;)
-
-       setvbuf(stdout, bb_common_bufsiz1, _IOFBF, BUFSIZ);
+       /* need to initialize since --color has _an optional_ argument */
+       USE_FEATURE_LS_COLOR(const char *color_opt = "always";)
 
-#if ENABLE_FEATURE_LS_TIMESTAMPS
-       time(&current_time_t);
-#endif
+       INIT_G();
 
        all_fmt = LIST_SHORT |
                (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
 
 #if ENABLE_FEATURE_AUTOWIDTH
        /* Obtain the terminal width */
-       get_terminal_width_height(STDOUT_FILENO, &terminal_width, NULL);
+       get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
        /* Go one less... */
        terminal_width--;
 #endif
@@ -811,14 +855,11 @@ int ls_main(int argc, char **argv)
        /* process options */
        USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)
 #if ENABLE_FEATURE_AUTOWIDTH
-       opt = getopt32(argc, argv, ls_options, &tabstops_str, &terminal_width_str
+       opt_complementary = "T+:w+"; /* -T N, -w N */
+       opt = getopt32(argv, ls_options, &tabstops, &terminal_width
                                USE_FEATURE_LS_COLOR(, &color_opt));
-       if (tabstops_str)
-               tabstops = xatou(tabstops_str);
-       if (terminal_width_str)
-               terminal_width = xatou(terminal_width_str);
 #else
-       opt = getopt32(argc, argv, ls_options  USE_FEATURE_LS_COLOR(, &color_opt));
+       opt = getopt32(argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));
 #endif
        for (i = 0; opt_flags[i] != (1U<<31); i++) {
                if (opt & (1 << i)) {
@@ -836,8 +877,9 @@ int ls_main(int argc, char **argv)
                                all_fmt &= ~TIME_MASK;
                        if (flags & LIST_CONTEXT)
                                all_fmt |= STYLE_SINGLE;
-                       if (LS_DISP_HR && opt == 'l')
-                               all_fmt &= ~LS_DISP_HR;
+                       /* huh?? opt cannot be 'l' */
+                       //if (LS_DISP_HR && opt == 'l')
+                       //      all_fmt &= ~LS_DISP_HR;
                        all_fmt |= flags;
                }
        }
@@ -847,15 +889,15 @@ int ls_main(int argc, char **argv)
        if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) {
                char *p = getenv("LS_COLORS");
                /* LS_COLORS is unset, or (not empty && not "none") ? */
-               if (!p || (p[0] && strcmp(p, "none")))
+               if (!p || (p[0] && strcmp(p, "none") != 0))
                        show_color = 1;
        }
        if (opt & (1 << i)) {  /* next flag after short options */
-               if (!color_opt || !strcmp("always", color_opt))
+               if (strcmp("always", color_opt) == 0)
                        show_color = 1;
-               else if (color_opt && !strcmp("never", color_opt))
+               else if (strcmp("never", color_opt) == 0)
                        show_color = 0;
-               else if (color_opt && !strcmp("auto", color_opt) && isatty(STDOUT_FILENO))
+               else if (strcmp("auto", color_opt) == 0 && isatty(STDOUT_FILENO))
                        show_color = 1;
        }
 #endif
@@ -879,38 +921,27 @@ int ls_main(int argc, char **argv)
        if (!(all_fmt & STYLE_MASK))
                all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
 
-       /*
-        * when there are no cmd line args we have to supply a default "." arg.
-        * we will create a second argv array, "av" that will hold either
-        * our created "." arg, or the real cmd line args.  The av array
-        * just holds the pointers- we don't move the date the pointers
-        * point to.
-        */
-       ac = argc - optind;     /* how many cmd line args are left */
-       if (ac < 1) {
-               static const char *const dotdir[] = { "." };
+       argv += optind;
+       if (!argv[0])
+               *--argv = (char*)".";
 
-               av = (char **) dotdir;
-               ac = 1;
-       } else {
-               av = argv + optind;
-       }
-
-       /* now, everything is in the av array */
-       if (ac > 1)
-               all_fmt |= DISP_DIRNAME;        /* 2 or more items? label directories */
+       if (argv[1])
+               all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
 
        /* stuff the command line file names into a dnode array */
        dn = NULL;
-       for (oi = 0; oi < ac; oi++) {
-               cur = my_stat(av[oi], av[oi]);
+       nfiles = 0;
+       do {
+               /* ls w/o -l follows links on command line */
+               cur = my_stat(*argv, *argv, !(all_fmt & STYLE_LONG));
+               argv++;
                if (!cur)
                        continue;
                cur->allocated = 0;
                cur->next = dn;
                dn = cur;
                nfiles++;
-       }
+       } while (*argv);
 
        /* now that we know how many files there are
         * allocate memory for an array to hold dnode pointers
@@ -945,5 +976,5 @@ int ls_main(int argc, char **argv)
        }
        if (ENABLE_FEATURE_CLEAN_UP)
                dfree(dnp, nfiles);
-       return status;
+       return exit_code;
 }