inetd: comment tweak. no code changes
[oweals/busybox.git] / coreutils / ls.c
index 8007f2a2ed4bf22c8a86cef6d7b3856f23c6fbe0..827b3508930f0366f20ec4db072764088a7ad2d0 100644 (file)
  * [2009-03]
  * ls sorts listing now, and supports almost all options.
  */
-
 #include "libbb.h"
+#include "unicode.h"
 
-#if ENABLE_FEATURE_ASSUME_UNICODE
-#include <wchar.h>
-#endif
 
 /* This is a NOEXEC applet. Be very careful! */
 
  */
 # undef CONFIG_FEATURE_LS_TIMESTAMPS
 # undef ENABLE_FEATURE_LS_TIMESTAMPS
-# undef USE_FEATURE_LS_TIMESTAMPS
-# undef SKIP_FEATURE_LS_TIMESTAMPS
+# undef IF_FEATURE_LS_TIMESTAMPS
+# undef IF_NOT_FEATURE_LS_TIMESTAMPS
 # define CONFIG_FEATURE_LS_TIMESTAMPS 1
 # define ENABLE_FEATURE_LS_TIMESTAMPS 1
-# define USE_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
-# define SKIP_FEATURE_LS_TIMESTAMPS(...)
+# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
+# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
 #endif
 
 
@@ -76,7 +73,7 @@ LIST_ID_NAME    = 1 << 4,
 LIST_ID_NUMERIC = 1 << 5,
 LIST_CONTEXT    = 1 << 6,
 LIST_SIZE       = 1 << 7,
-LIST_DEV        = 1 << 8,
+//LIST_DEV        = 1 << 8, - unused, synonym to LIST_SIZE
 LIST_DATE_TIME  = 1 << 9,
 LIST_FULLTIME   = 1 << 10,
 LIST_FILENAME   = 1 << 11,
@@ -138,15 +135,14 @@ SPLIT_SUBDIR    = 2,
 /* "[-]e", I think we made this one up */
 static const char ls_options[] ALIGN1 =
        "Cadil1gnsxQAk" /* 13 opts, total 13 */
-       USE_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
-       USE_FEATURE_LS_SORTFILES("SXrv")  /* 4, 21 */
-       USE_FEATURE_LS_FILETYPES("Fp")    /* 2, 23 */
-       USE_FEATURE_LS_FOLLOWLINKS("L")   /* 1, 24 */
-       USE_FEATURE_LS_RECURSIVE("R")     /* 1, 25 */
-       USE_FEATURE_HUMAN_READABLE("h")   /* 1, 26 */
-       USE_SELINUX("K") /* 1, 27 */
-       USE_SELINUX("Z") /* 1, 28 */
-       USE_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
+       IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
+       IF_FEATURE_LS_SORTFILES("SXrv")  /* 4, 21 */
+       IF_FEATURE_LS_FILETYPES("Fp")    /* 2, 23 */
+       IF_FEATURE_LS_FOLLOWLINKS("L")   /* 1, 24 */
+       IF_FEATURE_LS_RECURSIVE("R")     /* 1, 25 */
+       IF_FEATURE_HUMAN_READABLE("h")   /* 1, 26 */
+       IF_SELINUX("KZ") /* 2, 28 */
+       IF_FEATURE_AUTOWIDTH("T:w:") /* 2, 30 */
        ;
 enum {
        //OPT_C = (1 << 0),
@@ -162,6 +158,16 @@ enum {
        OPT_Q = (1 << 10),
        //OPT_A = (1 << 11),
        //OPT_k = (1 << 12),
+       OPTBIT_color = 13
+               + 4 * ENABLE_FEATURE_LS_TIMESTAMPS
+               + 4 * ENABLE_FEATURE_LS_SORTFILES
+               + 2 * ENABLE_FEATURE_LS_FILETYPES
+               + 1 * ENABLE_FEATURE_LS_FOLLOWLINKS
+               + 1 * ENABLE_FEATURE_LS_RECURSIVE
+               + 1 * ENABLE_FEATURE_HUMAN_READABLE
+               + 2 * ENABLE_SELINUX
+               + 2 * ENABLE_FEATURE_AUTOWIDTH,
+       OPT_color = 1 << OPTBIT_color,
 };
 
 enum {
@@ -232,7 +238,7 @@ struct dnode {                  /* the basic node */
        const char *fullname;         /* the dir entry name */
        int   allocated;
        struct stat dstat;      /* the file stat info */
-       USE_SELINUX(security_context_t sid;)
+       IF_SELINUX(security_context_t sid;)
        struct dnode *next;     /* point at the next node */
 };
 
@@ -277,32 +283,17 @@ enum {
 /* 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);) \
+       IF_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \
+       IF_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \
+       IF_FEATURE_LS_TIMESTAMPS(time(&current_time_t);) \
 } while (0)
 
 
-#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_SELINUX(security_context_t sid = NULL;)
 
        if ((all_fmt & FOLLOW_LINKS) || force_follow) {
 #if ENABLE_SELINUX
@@ -332,7 +323,7 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f
        cur->fullname = fullname;
        cur->name = name;
        cur->dstat = dstat;
-       USE_SELINUX(cur->sid = sid;)
+       IF_SELINUX(cur->sid = sid;)
        return cur;
 }
 
@@ -565,12 +556,12 @@ 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 = mbstrlen(dn[i]->name);
+                       int len = bb_mbstrlen(dn[i]->name);
                        if (column_width < len)
                                column_width = len;
                }
                column_width += tabstops +
-                       USE_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
+                       IF_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + )
                                     ((all_fmt & LIST_INO) ? 8 : 0) +
                                     ((all_fmt & LIST_BLOCKS) ? 5 : 0);
                ncols = (int) (terminal_width / column_width);
@@ -712,7 +703,7 @@ static int print_name(const char *name)
 {
        if (option_mask32 & OPT_Q) {
 #if ENABLE_FEATURE_ASSUME_UNICODE
-               int len = 2 + mbstrlen(name);
+               int len = 2 + bb_mbstrlen(name);
 #else
                int len = 2;
 #endif
@@ -732,7 +723,7 @@ static int print_name(const char *name)
        /* No -Q: */
 #if ENABLE_FEATURE_ASSUME_UNICODE
        fputs(name, stdout);
-       return mbstrlen(name);
+       return bb_mbstrlen(name);
 #else
        return printf("%s", name);
 #endif
@@ -741,8 +732,8 @@ static int print_name(const char *name)
 
 static int list_single(const struct dnode *dn)
 {
-       int i, column = 0;
-
+       int column = 0;
+       char *lpath = lpath; /* for compiler */
 #if ENABLE_FEATURE_LS_TIMESTAMPS
        char *filetime;
        time_t ttime, age;
@@ -767,142 +758,128 @@ static int list_single(const struct dnode *dn)
        append = append_char(dn->dstat.st_mode);
 #endif
 
-       for (i = 0; i <= 31; i++) {
-               switch (all_fmt & (1 << i)) {
-               case LIST_INO:
-                       column += printf("%7lu ", (long) dn->dstat.st_ino);
-                       break;
-               case LIST_BLOCKS:
-                       column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1);
-                       break;
-               case LIST_MODEBITS:
-                       column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
-                       break;
-               case LIST_NLINKS:
-                       column += printf("%4lu ", (long) dn->dstat.st_nlink);
-                       break;
-               case LIST_ID_NAME:
+       /* Do readlink early, so that if it fails, error message
+        * does not appear *inside* of the "ls -l" line */
+       if (all_fmt & LIST_SYMLINK)
+               if (S_ISLNK(dn->dstat.st_mode))
+                       lpath = xmalloc_readlink_or_warn(dn->fullname);
+
+       if (all_fmt & LIST_INO)
+               column += printf("%7lu ", (long) dn->dstat.st_ino);
+       if (all_fmt & LIST_BLOCKS)
+               column += printf("%4"OFF_FMT"u ", (off_t) dn->dstat.st_blocks >> 1);
+       if (all_fmt & LIST_MODEBITS)
+               column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
+       if (all_fmt & LIST_NLINKS)
+               column += printf("%4lu ", (long) dn->dstat.st_nlink);
 #if ENABLE_FEATURE_LS_USERNAME
-                       if (option_mask32 & OPT_g) {
-                               printf("%-8.8s",
-                                       get_cached_username(dn->dstat.st_uid));
-                               column += 9;
-                               break;
-                       }
-                       printf("%-8.8s %-8.8s",
+       if (all_fmt & LIST_ID_NAME) {
+               if (option_mask32 & OPT_g) {
+                       column += printf("%-8.8s",
+                               get_cached_username(dn->dstat.st_uid));
+               } else {
+                       column += printf("%-8.8s %-8.8s",
                                get_cached_username(dn->dstat.st_uid),
                                get_cached_groupname(dn->dstat.st_gid));
-                       column += 17;
-                       break;
+               }
+       }
 #endif
-               case LIST_ID_NUMERIC:
-                       column += printf("%-8u %-8u", dn->dstat.st_uid, dn->dstat.st_gid);
-                       break;
-               case LIST_SIZE:
-               case LIST_DEV:
-                       if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
-                               column += printf("%4u, %3u ", (int) major(dn->dstat.st_rdev),
-                                          (int) minor(dn->dstat.st_rdev));
+       if (all_fmt & LIST_ID_NUMERIC) {
+               if (option_mask32 & OPT_g)
+                       column += printf("%-8u", (int) dn->dstat.st_uid);
+               else
+                       column += printf("%-8u %-8u",
+                                       (int) dn->dstat.st_uid,
+                                       (int) dn->dstat.st_gid);
+       }
+       if (all_fmt & (LIST_SIZE /*|LIST_DEV*/ )) {
+               if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
+                       column += printf("%4u, %3u ",
+                                       (int) major(dn->dstat.st_rdev),
+                                       (int) minor(dn->dstat.st_rdev));
+               } else {
+                       if (all_fmt & LS_DISP_HR) {
+                               column += printf("%9s ",
+                                       make_human_readable_str(dn->dstat.st_size, 1, 0));
                        } else {
-                               if (all_fmt & LS_DISP_HR) {
-                                       column += printf("%9s ",
-                                               make_human_readable_str(dn->dstat.st_size, 1, 0));
-                               } else {
-                                       column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
-                               }
+                               column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size);
                        }
-                       break;
+               }
+       }
 #if ENABLE_FEATURE_LS_TIMESTAMPS
-               case LIST_FULLTIME:
-                       printf("%24.24s ", filetime);
-                       column += 25;
-                       break;
-               case LIST_DATE_TIME:
-                       if ((all_fmt & LIST_FULLTIME) == 0) {
-                               /* current_time_t ~== time(NULL) */
-                               age = current_time_t - ttime;
-                               printf("%6.6s ", filetime + 4);
-                               if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
-                                       /* hh:mm if less than 6 months old */
-                                       printf("%5.5s ", filetime + 11);
-                               } else {
-                                       printf(" %4.4s ", filetime + 20);
-                               }
-                               column += 13;
+       if (all_fmt & LIST_FULLTIME)
+               column += printf("%24.24s ", filetime);
+       if (all_fmt & LIST_DATE_TIME)
+               if ((all_fmt & LIST_FULLTIME) == 0) {
+                       /* current_time_t ~== time(NULL) */
+                       age = current_time_t - ttime;
+                       printf("%6.6s ", filetime + 4);
+                       if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
+                               /* hh:mm if less than 6 months old */
+                               printf("%5.5s ", filetime + 11);
+                       } else {
+                               printf(" %4.4s ", filetime + 20);
                        }
-                       break;
+                       column += 13;
+               }
 #endif
 #if ENABLE_SELINUX
-               case LIST_CONTEXT:
-                       column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
-                       freecon(dn->sid);
-                       break;
+       if (all_fmt & LIST_CONTEXT) {
+               column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
+               freecon(dn->sid);
+       }
 #endif
-               case LIST_FILENAME:
+       if (all_fmt & LIST_FILENAME) {
 #if ENABLE_FEATURE_LS_COLOR
-                       if (show_color) {
-                               info.st_mode = 0; /* for fgcolor() */
-                               lstat(dn->fullname, &info);
-                               printf("\033[%u;%um", bold(info.st_mode),
-                                               fgcolor(info.st_mode));
-                       }
+               if (show_color) {
+                       info.st_mode = 0; /* for fgcolor() */
+                       lstat(dn->fullname, &info);
+                       printf("\033[%u;%um", bold(info.st_mode),
+                                       fgcolor(info.st_mode));
+               }
 #endif
-                       column += print_name(dn->name);
-                       if (show_color) {
-                               printf("\033[0m");
-                       }
-                       break;
-               case LIST_SYMLINK:
-                       if (S_ISLNK(dn->dstat.st_mode)) {
-                               char *lpath = xmalloc_readlink_or_warn(dn->fullname);
-                               if (!lpath) break;
-                               printf(" -> ");
+               column += print_name(dn->name);
+               if (show_color) {
+                       printf("\033[0m");
+               }
+       }
+       if (all_fmt & LIST_SYMLINK) {
+               if (S_ISLNK(dn->dstat.st_mode) && lpath) {
+                       printf(" -> ");
 #if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
 #if ENABLE_FEATURE_LS_COLOR
-                               info.st_mode = 0; /* for fgcolor() */
+                       info.st_mode = 0; /* for fgcolor() */
 #endif
-                               if (stat(dn->fullname, &info) == 0) {
-                                       append = append_char(info.st_mode);
-                               }
+                       if (stat(dn->fullname, &info) == 0) {
+                               append = append_char(info.st_mode);
+                       }
 #endif
 #if ENABLE_FEATURE_LS_COLOR
-                               if (show_color) {
-                                       printf("\033[%u;%um", bold(info.st_mode),
-                                                  fgcolor(info.st_mode));
-                               }
+                       if (show_color) {
+                               printf("\033[%u;%um", bold(info.st_mode),
+                                          fgcolor(info.st_mode));
+                       }
 #endif
-                               column += print_name(lpath) + 4;
-                               if (show_color) {
-                                       printf("\033[0m");
-                               }
-                               free(lpath);
+                       column += print_name(lpath) + 4;
+                       if (show_color) {
+                               printf("\033[0m");
                        }
-                       break;
+                       free(lpath);
+               }
+       }
 #if ENABLE_FEATURE_LS_FILETYPES
-               case LIST_FILETYPE:
-                       if (append) {
-                               putchar(append);
-                               column++;
-                       }
-                       break;
-#endif
+       if (all_fmt & LIST_FILETYPE) {
+               if (append) {
+                       putchar(append);
+                       column++;
                }
        }
+#endif
 
        return column;
 }
 
 
-/* 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 UNUSED_PARAM, char **argv)
 {
        struct dnode **dnd;
@@ -915,29 +892,48 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
        int dnfiles;
        int dndirs;
        int i;
+#if ENABLE_FEATURE_LS_COLOR
+       /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
+       /* coreutils 6.10:
+        * # ls --color=BOGUS
+        * ls: invalid argument 'BOGUS' for '--color'
+        * Valid arguments are:
+        * 'always', 'yes', 'force'
+        * 'never', 'no', 'none'
+        * 'auto', 'tty', 'if-tty'
+        * (and substrings: "--color=alwa" work too)
+        */
+       static const char ls_longopts[] ALIGN1 =
+               "color\0" Optional_argument "\xff"; /* no short equivalent */
+       static const char color_str[] ALIGN1 =
+               "always\0""yes\0""force\0"
+               "auto\0""tty\0""if-tty\0";
        /* need to initialize since --color has _an optional_ argument */
-       USE_FEATURE_LS_COLOR(const char *color_opt = "always";)
+       const char *color_opt = color_str; /* "always" */
+#endif
 
        INIT_G();
 
+       check_unicode_in_env();
+
        all_fmt = LIST_SHORT |
                (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD));
 
 #if ENABLE_FEATURE_AUTOWIDTH
-       /* Obtain the terminal width */
+       /* obtain the terminal width */
        get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
-       /* Go one less... */
+       /* go one less... */
        terminal_width--;
 #endif
 
        /* process options */
-       USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;)
+       IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
 #if ENABLE_FEATURE_AUTOWIDTH
        opt_complementary = "T+:w+"; /* -T N, -w N */
        opt = getopt32(argv, ls_options, &tabstops, &terminal_width
-                               USE_FEATURE_LS_COLOR(, &color_opt));
+                               IF_FEATURE_LS_COLOR(, &color_opt));
 #else
-       opt = getopt32(argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt));
+       opt = getopt32(argv, ls_options IF_FEATURE_LS_COLOR(, &color_opt));
 #endif
        for (i = 0; opt_flags[i] != (1U<<31); i++) {
                if (opt & (1 << i)) {
@@ -970,13 +966,20 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
                if (!p || (p[0] && strcmp(p, "none") != 0))
                        show_color = 1;
        }
-       if (opt & (1 << i)) {  /* next flag after short options */
-               if (strcmp("always", color_opt) == 0)
-                       show_color = 1;
-               else if (strcmp("never", color_opt) == 0)
+       if (opt & OPT_color) {
+               if (color_opt[0] == 'n')
                        show_color = 0;
-               else if (strcmp("auto", color_opt) == 0 && isatty(STDOUT_FILENO))
-                       show_color = 1;
+               else switch (index_in_substrings(color_str, color_opt)) {
+               case 3:
+               case 4:
+               case 5:
+                       if (isatty(STDOUT_FILENO)) {
+               case 0:
+               case 1:
+               case 2:
+                               show_color = 1;
+                       }
+               }
        }
 #endif