'simple' error message functions by Loic Grenie <loic.grenie@gmail.com>.
[oweals/busybox.git] / coreutils / ls.c
index 6b507db69ff74bd49612526d7af52bb568016c51..92a9a289dc9dbae829b121e0143349938b1ed99e 100644 (file)
  * 1. requires lstat (BSD) - how do you do it without?
  */
 
-#include "busybox.h"
 #include <getopt.h>
+#include "libbb.h"
+
+/* This is a NOEXEC applet. Be very careful! */
+
 
 enum {
 
@@ -64,7 +67,7 @@ LIST_MASK       = (LIST_EXEC << 1) - 1,
 
 /* what files will be displayed */
 DISP_DIRNAME    = 1 << 15,      /* 2 or more items? label directories */
-DISP_HIDDEN     = 1 << 16,      /* show filenames starting with .  */
+DISP_HIDDEN     = 1 << 16,      /* show filenames starting with . */
 DISP_DOT        = 1 << 17,      /* show . and .. */
 DISP_NOLIST     = 1 << 18,      /* show directory as itself, not contents */
 DISP_RECURSIVE  = 1 << 19,      /* show directory and everything below it */
@@ -114,13 +117,12 @@ SPLIT_SUBDIR    = 2,
 
 /* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
 #if ENABLE_FEATURE_LS_COLOR
-static int show_color;
+static smallint 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 }
-};
+ * equivalent */
+static const char ls_color_opt[] ALIGN1 =
+       "color\0" Optional_argument "\xff" /* no short equivalent */
+       ;
 #else
 enum { show_color = 0 };
 #endif
@@ -129,8 +131,8 @@ enum { show_color = 0 };
  * 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;)
@@ -156,31 +158,31 @@ enum {
 
 static int status = EXIT_SUCCESS;
 
-static struct dnode *my_stat(char *fullname, char *name)
+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);
+                       bb_simple_perror_msg(fullname);
                        status = 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);
+                       bb_simple_perror_msg(fullname);
                        status = EXIT_FAILURE;
                        return 0;
                }
@@ -238,7 +240,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,13 +287,13 @@ 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 */
 }
 #else
-#define dfree(...) do {} while(0)
+#define dfree(...) ((void)0)
 #endif
 
 static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
@@ -317,7 +319,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;
@@ -359,7 +361,7 @@ static int sortcmp(const void *a, const void *b)
        }
 
        if (dif == 0) {
-               /* sort by name- may be a tie_breaker for time or size cmp */
+               /* sort by name - may be a tie_breaker for time or size cmp */
                if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name);
                else dif = strcmp(d1->name, d2->name);
        }
@@ -375,7 +377,7 @@ static void dnsort(struct dnode **dn, int size)
        qsort(dn, size, sizeof(*dn), sortcmp);
 }
 #else
-#define dnsort(dn, size) do {} while(0)
+#define dnsort(dn, size) ((void)0)
 #endif
 
 
@@ -449,7 +451,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);
                }
@@ -501,16 +503,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;
@@ -523,7 +525,7 @@ static struct dnode **list_dir(const char *path)
        closedir(dir);
 
        /* now that we know how many files there are
-          ** allocate memory for an array to hold dnode pointers
+        * allocate memory for an array to hold dnode pointers
         */
        if (dn == NULL)
                return NULL;
@@ -537,13 +539,16 @@ 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)
 {
        int i, column = 0;
 
-#if ENABLE_FEATURE_LS_USERNAME
-       char scratch[16];
-#endif
 #if ENABLE_FEATURE_LS_TIMESTAMPS
        char *filetime;
        time_t ttime, age;
@@ -574,7 +579,7 @@ static int list_single(struct dnode *dn)
                        column += printf("%7ld ", (long) dn->dstat.st_ino);
                        break;
                case LIST_BLOCKS:
-                       column += printf("%4"OFF_FMT" ", (off_t) dn->dstat.st_blocks >> 1);
+                       column += printf("%4"OFF_FMT"d ", (off_t) dn->dstat.st_blocks >> 1);
                        break;
                case LIST_MODEBITS:
                        column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
@@ -584,10 +589,9 @@ static int list_single(struct dnode *dn)
                        break;
                case LIST_ID_NAME:
 #if ENABLE_FEATURE_LS_USERNAME
-                       bb_getpwuid(scratch, dn->dstat.st_uid, sizeof(scratch));
-                       printf("%-8.8s ", scratch);
-                       bb_getgrgid(scratch, dn->dstat.st_gid, sizeof(scratch));
-                       printf("%-8.8s", scratch);
+                       printf("%-8.8s %-8.8s",
+                               get_cached_username(dn->dstat.st_uid),
+                               get_cached_groupname(dn->dstat.st_gid));
                        column += 17;
                        break;
 #endif
@@ -604,7 +608,7 @@ static int list_single(struct dnode *dn)
                                        column += printf("%9s ",
                                                make_human_readable_str(dn->dstat.st_size, 1, 0));
                                } else {
-                                       column += printf("%9"OFF_FMT" ", (off_t) dn->dstat.st_size);
+                                       column += printf("%9"OFF_FMT"d ", (off_t) dn->dstat.st_size);
                                }
                        }
                        break;
@@ -615,7 +619,8 @@ static int list_single(struct dnode *dn)
                        break;
                case LIST_DATE_TIME:
                        if ((all_fmt & LIST_FULLTIME) == 0) {
-                               age = time(NULL) - ttime;
+                               /* 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 */
@@ -634,8 +639,8 @@ static int list_single(struct dnode *dn)
                                int len = 0;
 
                                if (dn->sid) {
-                                       /*  I assume sid initilized with NULL  */
-                                       len = strlen(dn->sid)+1;
+                                       /* I assume sid initilized with NULL */
+                                       len = strlen(dn->sid) + 1;
                                        safe_strncpy(context, dn->sid, len);
                                        freecon(dn->sid);
                                } else {
@@ -648,10 +653,12 @@ static int list_single(struct dnode *dn)
 #endif
                case LIST_FILENAME:
                        errno = 0;
+#if ENABLE_FEATURE_LS_COLOR
                        if (show_color && !lstat(dn->fullname, &info)) {
                                printf("\033[%d;%dm", bgcolor(info.st_mode),
                                                fgcolor(info.st_mode));
                        }
+#endif
                        column += printf("%s", dn->name);
                        if (show_color) {
                                printf("\033[0m");
@@ -659,7 +666,7 @@ static int list_single(struct dnode *dn)
                        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
@@ -667,11 +674,13 @@ static int list_single(struct dnode *dn)
                                        append = append_char(info.st_mode);
                                }
 #endif
+#if ENABLE_FEATURE_LS_COLOR
                                if (show_color) {
                                        errno = 0;
                                        printf("\033[%d;%dm", bgcolor(info.st_mode),
                                                   fgcolor(info.st_mode));
                                }
+#endif
                                column += printf("%s", lpath) + 4;
                                if (show_color) {
                                        printf("\033[0m");
@@ -701,7 +710,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")
@@ -709,7 +719,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,
@@ -760,12 +771,19 @@ static const unsigned opt_flags[] = {
        LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
 #endif
 #if ENABLE_FEATURE_AUTOWIDTH
-       0, 0,                    /* T, w - ignored */
+       0, 0,                       /* T, w - ignored */
+#endif
+#if ENABLE_SELINUX
+       LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */
 #endif
        (1U<<31)
 };
 
 
+/* THIS IS A "SAFE" APPLET, main() MAY BE CALLED INTERNALLY FROM SHELL */
+/* BE CAREFUL! */
+
+int ls_main(int argc, char **argv);
 int ls_main(int argc, char **argv)
 {
        struct dnode **dnd;
@@ -785,12 +803,16 @@ int ls_main(int argc, char **argv)
        USE_FEATURE_AUTOWIDTH(char *terminal_width_str = NULL;)
        USE_FEATURE_LS_COLOR(char *color_opt;)
 
+#if ENABLE_FEATURE_LS_TIMESTAMPS
+       time(&current_time_t);
+#endif
+
        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);
+       /* Obtain the terminal width */
+       get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL);
        /* Go one less... */
        terminal_width--;
 #endif
@@ -798,14 +820,14 @@ 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 = getopt32(argv, ls_options, &tabstops_str, &terminal_width_str
                                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)) {
@@ -823,8 +845,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;
                }
        }
@@ -890,7 +913,8 @@ int ls_main(int argc, char **argv)
        /* 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]);
+               /* ls w/o -l follows links on command line */
+               cur = my_stat(av[oi], av[oi], !(all_fmt & STYLE_LONG));
                if (!cur)
                        continue;
                cur->allocated = 0;
@@ -900,8 +924,8 @@ int ls_main(int argc, char **argv)
        }
 
        /* now that we know how many files there are
-       ** allocate memory for an array to hold dnode pointers
-       */
+        * allocate memory for an array to hold dnode pointers
+        */
        dnp = dnalloc(nfiles);
        for (i = 0, cur = dn; i < nfiles; i++) {
                dnp[i] = cur;   /* save pointer to node in array */