ls: more correct handling of -c, -u
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 22 Jan 2017 18:02:57 +0000 (19:02 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 22 Jan 2017 18:02:57 +0000 (19:02 +0100)
function                                             old     new   delta
my_stat                                              302     318     +16
packed_usage                                       30977   30969      -8
display_single                                       928     910     -18
sortcmp                                              258     228     -30
ls_main                                              776     732     -44
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/4 up/down: 16/-100)           Total: -84 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
coreutils/ls.c

index 0774a9a45561c4b2f52d2a83abb4703e45f634a0..b9d74a2cb9324afb1a1aa4dd8b4b2b9955466848 100644 (file)
 //usage:       IF_FEATURE_LS_FOLLOWLINKS("LH")
 //usage:       IF_FEATURE_LS_RECURSIVE("R")
 //usage:       IF_FEATURE_LS_FILETYPES("Fp") "lins"
-//usage:       IF_FEATURE_LS_TIMESTAMPS("e")
 //usage:       IF_FEATURE_HUMAN_READABLE("h")
 //usage:       IF_FEATURE_LS_SORTFILES("rSXv")
 //usage:       IF_FEATURE_LS_TIMESTAMPS("ctu")
-//usage:       IF_SELINUX("kKZ") "]"
+//usage:       IF_SELINUX("kZ") "]"
 //usage:       IF_FEATURE_LS_WIDTH(" [-w WIDTH]") " [FILE]..."
 //usage:#define ls_full_usage "\n\n"
 //usage:       "List directory contents\n"
 //usage:     "\n       -i      List inode numbers"
 //usage:     "\n       -n      List numeric UIDs and GIDs instead of names"
 //usage:     "\n       -s      List allocated blocks"
+//usage:       IF_FEATURE_LS_TIMESTAMPS(
+//usage:     "\n       -c      List ctime"
+//usage:     "\n       -u      List atime"
+//usage:       )
 //usage:       IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS(
 //usage:     "\n       --full-time     List full date and time"
 //usage:       ))
 //usage:     "\n       -S      Sort by size"
 //usage:     "\n       -X      Sort by extension"
 //usage:     "\n       -v      Sort by version"
-//usage:     "\n       -r      Reverse sort order"
 //usage:       )
 //usage:       IF_FEATURE_LS_TIMESTAMPS(
-//usage:     "\n       -c      With -l: sort by ctime"
-//usage:     "\n       -t      With -l: sort by mtime"
-//usage:     "\n       -u      With -l: sort by atime"
+//usage:     "\n       -t      Sort by mtime"
+//usage:     "\n       -tc     Sort by ctime"
+//usage:     "\n       -tu     Sort by atime"
 //usage:       )
+//usage:     "\n       -r      Reverse sort order"
 //usage:       IF_SELINUX(
 //usage:     "\n       -k      List security context"
 //usage:     "\n       -Z      List security context and permission"
@@ -225,22 +228,15 @@ STYLE_LONG      = 2 << 19,      /* one record per line, extended info */
 STYLE_SINGLE    = 3 << 19,      /* one record per line */
 STYLE_MASK      = STYLE_SINGLE,
 
-/* which of the three times will be used */
-TIME_CHANGE     = (1 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
-TIME_ACCESS     = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
-TIME_MASK       = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
-
 /* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
 SORT_REVERSE    = 1 << 23,
 SORT_DIRS_FIRST = 1 << 24,
 
 SORT_NAME       = 0,            /* sort by file name */
 SORT_SIZE       = 1 << 25,      /* sort by file size */
-SORT_ATIME      = 2 << 25,      /* sort by last access time */
-SORT_CTIME      = 3 << 25,      /* sort by last change time */
-SORT_MTIME      = 4 << 25,      /* sort by last modification time */
-SORT_VERSION    = 5 << 25,      /* sort by version */
-SORT_EXT        = 6 << 25,      /* sort by file name extension */
+SORT_TIME       = 2 << 25,      /* sort by {a,m,c}time */
+SORT_VERSION    = 3 << 25,      /* sort by version */
+SORT_EXT        = 4 << 25,      /* sort by file name extension */
 SORT_MASK       = (7 << 25) * ENABLE_FEATURE_LS_SORTFILES,
 
 LIST_LONG       = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
@@ -268,14 +264,14 @@ static const char ls_options[] ALIGN1 =
        IF_SELINUX("Z")                  /* 1, 24 */
        IF_FEATURE_LS_FOLLOWLINKS("LH")  /* 2, 26 */
        IF_FEATURE_HUMAN_READABLE("h")   /* 1, 27 */
-       IF_FEATURE_LS_WIDTH("T:w:")     /* 2, 29 */
-       /* with --color, we use all 32 bits */;
+       IF_FEATURE_LS_WIDTH("T:w:")      /* 2, 29 */
+;
 enum {
        //OPT_C = (1 << 0),
        //OPT_a = (1 << 1),
        //OPT_d = (1 << 2),
        //OPT_i = (1 << 3),
-       //OPT_l = (1 << 4),
+       OPT_l = (1 << 4),
        //OPT_1 = (1 << 5),
        OPT_g = (1 << 6),
        //OPT_n = (1 << 7),
@@ -301,9 +297,10 @@ enum {
        OPTBIT_h = OPTBIT_L + 2 * ENABLE_FEATURE_LS_FOLLOWLINKS,
        OPTBIT_T = OPTBIT_h + 1 * ENABLE_FEATURE_HUMAN_READABLE,
        OPTBIT_w, /* 28 */
-       OPTBIT_color      = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
-       OPTBIT_dirs_first = OPTBIT_color + 1 * ENABLE_FEATURE_LS_COLOR,
-       OPTBIT_full_time,
+       OPTBIT_full_time = OPTBIT_T + 2 * ENABLE_FEATURE_LS_WIDTH,
+       OPTBIT_dirs_first,
+       OPTBIT_color, /* 31 */
+       /* with long opts, we use all 32 bits */
 
        OPT_c = (1 << OPTBIT_c) * ENABLE_FEATURE_LS_TIMESTAMPS,
        OPT_t = (1 << OPTBIT_t) * ENABLE_FEATURE_LS_TIMESTAMPS,
@@ -321,9 +318,9 @@ enum {
        OPT_h = (1 << OPTBIT_h) * ENABLE_FEATURE_HUMAN_READABLE,
        OPT_T = (1 << OPTBIT_T) * ENABLE_FEATURE_LS_WIDTH,
        OPT_w = (1 << OPTBIT_w) * ENABLE_FEATURE_LS_WIDTH,
-       OPT_color      = (1 << OPTBIT_color     ) * ENABLE_FEATURE_LS_COLOR,
-       OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
        OPT_full_time  = (1 << OPTBIT_full_time ) * ENABLE_LONG_OPTS,
+       OPT_dirs_first = (1 << OPTBIT_dirs_first) * ENABLE_LONG_OPTS,
+       OPT_color      = (1 << OPTBIT_color     ) * ENABLE_FEATURE_LS_COLOR,
 };
 
 /* TODO: simple toggles may be stored as OPT_xxx bits instead */
@@ -342,9 +339,9 @@ static const uint32_t opt_flags[] = {
        DISP_HIDDEN,                 /* A */
        ENABLE_SELINUX * (LIST_CONTEXT|STYLE_SINGLE), /* k (ignored if !SELINUX) */
 #if ENABLE_FEATURE_LS_TIMESTAMPS
-       TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */
-       ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */
-       TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */
+       0,                           /* c - handled via OPT_c */
+       (ENABLE_FEATURE_LS_SORTFILES * SORT_TIME), /* t */
+       0,                           /* u - handled via OPT_u */
 #endif
 #if ENABLE_FEATURE_LS_SORTFILES
        SORT_SIZE,                   /* S */
@@ -396,9 +393,7 @@ struct dnode {
        mode_t    dn_mode; /* obtained with lstat OR stat, depending on -L etc */
        off_t     dn_size;
 #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
-       time_t    dn_atime;
-       time_t    dn_mtime;
-       time_t    dn_ctime;
+       time_t    dn_time;
 #endif
        ino_t     dn_ino;
        blkcnt_t  dn_blocks;
@@ -632,14 +627,11 @@ static NOINLINE unsigned display_single(const struct dnode *dn)
        }
 #if ENABLE_FEATURE_LS_TIMESTAMPS
        if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
-               const time_t *ttime = &dn->dn_mtime;
-               if (G.all_fmt & TIME_ACCESS)
-                       ttime = &dn->dn_atime;
-               if (G.all_fmt & TIME_CHANGE)
-                       ttime = &dn->dn_ctime;
+               const time_t *ttime = &dn->dn_time;
                if (G.all_fmt & LIST_FULLTIME) { /* --full-time */
                        /* coreutils 8.4 ls --full-time prints:
                         * 2009-07-13 17:49:27.000000000 +0200
+                        * we don't show fractional seconds.
                         */
                        char buf[sizeof("YYYY-mm-dd HH:MM:SS TIMEZONE")];
                        strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z", localtime(ttime));
@@ -820,9 +812,11 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f
        cur->dn_mode   = statbuf.st_mode  ;
        cur->dn_size   = statbuf.st_size  ;
 #if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
-       cur->dn_atime  = statbuf.st_atime ;
-       cur->dn_mtime  = statbuf.st_mtime ;
-       cur->dn_ctime  = statbuf.st_ctime ;
+       cur->dn_time   = statbuf.st_mtime ;
+       if (option_mask32 & OPT_u)
+               cur->dn_time = statbuf.st_atime;
+       if (option_mask32 & OPT_c)
+               cur->dn_time = statbuf.st_ctime;
 #endif
        cur->dn_ino    = statbuf.st_ino   ;
        cur->dn_blocks = statbuf.st_blocks;
@@ -951,14 +945,8 @@ static int sortcmp(const void *a, const void *b)
        if (sort_opts == SORT_SIZE) {
                dif = (d2->dn_size - d1->dn_size);
        } else
-       if (sort_opts == SORT_ATIME) {
-               dif = (d2->dn_atime - d1->dn_atime);
-       } else
-       if (sort_opts == SORT_CTIME) {
-               dif = (d2->dn_ctime - d1->dn_ctime);
-       } else
-       if (sort_opts == SORT_MTIME) {
-               dif = (d2->dn_mtime - d1->dn_mtime);
+       if (sort_opts == SORT_TIME) {
+               dif = (d2->dn_time - d1->dn_time);
        } else
 #if defined(HAVE_STRVERSCMP) && HAVE_STRVERSCMP == 1
        if (sort_opts == SORT_VERSION) {
@@ -1159,9 +1147,9 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
         * (and substrings: "--color=alwa" work too)
         */
        static const char ls_longopts[] ALIGN1 =
-               "color\0" Optional_argument "\xff" /* no short equivalent */
+               "full-time\0" No_argument "\xff"
                "group-directories-first\0" No_argument "\xfe"
-               "full-time\0" No_argument "\xfd"
+               "color\0" Optional_argument "\xfd"
        ;
        static const char color_str[] ALIGN1 =
                "always\0""yes\0""force\0"
@@ -1188,7 +1176,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
        IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
        opt_complementary =
                /* --full-time implies -l */
-               IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS("\xfd""l"))
+               IF_FEATURE_LS_TIMESTAMPS(IF_LONG_OPTS("\xff""l"))
                /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html:
                 * in some pairs of opts, only last one takes effect:
                 */
@@ -1206,8 +1194,9 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
                IF_FEATURE_LS_COLOR(, &color_opt)
        );
 #if 0 /* option bits debug */
-       bb_error_msg("opt:0x%08x H:%x color:%x dirs:%x", opt, OPT_H, OPT_color, OPT_dirs_first);
+       bb_error_msg("opt:0x%08x l:%x H:%x color:%x dirs:%x", opt, OPT_l, OPT_H, OPT_color, OPT_dirs_first);
        if (opt & OPT_c         ) bb_error_msg("-c");
+       if (opt & OPT_l         ) bb_error_msg("-l");
        if (opt & OPT_H         ) bb_error_msg("-H");
        if (opt & OPT_color     ) bb_error_msg("--color");
        if (opt & OPT_dirs_first) bb_error_msg("--group-directories-first");
@@ -1222,8 +1211,6 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
                                G.all_fmt &= ~STYLE_MASK;
                        if (flags & SORT_MASK)
                                G.all_fmt &= ~SORT_MASK;
-                       if (flags & TIME_MASK)
-                               G.all_fmt &= ~TIME_MASK;
 
                        G.all_fmt |= flags;
                }
@@ -1261,14 +1248,18 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
        /* sort out which command line options take precedence */
        if (ENABLE_FEATURE_LS_RECURSIVE && (G.all_fmt & DISP_NOLIST))
                G.all_fmt &= ~DISP_RECURSIVE;   /* no recurse if listing only dir */
-       if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
-               if (G.all_fmt & TIME_CHANGE)
-                       G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_CTIME;
-               if (G.all_fmt & TIME_ACCESS)
-                       G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_ATIME;
-       }
-       if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) /* not -l? */
+       if ((G.all_fmt & STYLE_MASK) != STYLE_LONG) { /* not -l? */
                G.all_fmt &= ~(LIST_ID_NUMERIC|LIST_ID_NAME|LIST_FULLTIME);
+               if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) {
+                       /* when to sort by time? -t[cu] sorts by time even with -l */
+                       /* (this is achieved by opt_flags[] element for -t) */
+                       /* without -l, bare -c or -u enable sort too */
+                       /* (with -l, bare -c or -u just select which time to show) */
+                       if (opt & (OPT_c|OPT_u)) {
+                               G.all_fmt = (G.all_fmt & ~SORT_MASK) | SORT_TIME;
+                       }
+               }
+       }
 
        /* choose a display format if one was not already specified by an option */
        if (!(G.all_fmt & STYLE_MASK))