coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] ls: support --time=creation to show/sort birth time


From: Pádraig Brady
Subject: [PATCH] ls: support --time=creation to show/sort birth time
Date: Thu, 2 Jan 2020 17:48:00 +0000

* src/ls.c (usage): Reorganize help for --time,
and add description for --time=birth.
(do_statx): Store btime in mtime if available.
(get_stat_btime): A new function to read the creation time
from the appropriate stat structure member.
(cmp_btime): A new function to compare birth time.
(print_long_format): Output '?' when birth time unavailable.
* doc/coreutils.texi: Document --time={birth,creation}.
* tests/local.mk: Reference the new test.
* tests/ls/birthtime.sh: Add a new test.
* NEWS: Mention the new feature.
---
 NEWS                  |  3 ++
 doc/coreutils.texi    | 14 ++++++++-
 src/ls.c              | 81 ++++++++++++++++++++++++++++++++++++++++++---------
 tests/local.mk        |  1 +
 tests/ls/birthtime.sh | 27 +++++++++++++++++
 5 files changed, 112 insertions(+), 14 deletions(-)
 create mode 100755 tests/ls/birthtime.sh

diff --git a/NEWS b/NEWS
index 14ec876..5231b84 100644
--- a/NEWS
+++ b/NEWS
@@ -62,6 +62,9 @@ GNU coreutils NEWS                                    -*- 
outline -*-
 
 ** New Features
 
+  ls now supports the --time=birth option to display and sort by
+  file creation time, where available.
+
   od --skip-bytes now can use lseek even if the input is not a regular
   file, greatly improving performance in some cases.
 
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 96a9cee..cb238f0 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -7877,7 +7877,8 @@ Sort by file size, largest first.
 @opindex -t
 @opindex --sort
 @opindex modification timestamp@r{, sorting files by}
-Sort by modification timestamp (mtime), newest first.
+Sort by modification timestamp (mtime) by default, newest first.
+The timestamp to order by can be changed with the @option{--time} option.
 @xref{File timestamps}.
 
 @item -u
@@ -7895,6 +7896,17 @@ When explicitly sorting by time (@option{--sort=time} or 
@option{-t})
 or when not using a long listing format, sort according to the atime.
 @xref{File timestamps}.
 
+@item --time=birth
+@itemx --time=creation
+@opindex --time
+@opindex birth time@r{, printing or sorting files by}
+@opindex creation timestamp@r{, printing or sorting files by}
+If the long listing format (e.g., @option{--format=long}) is being used,
+print the file creation timestamp if available.
+When explicitly sorting by time (@option{--sort=time} or @option{-t})
+or when not using a long listing format, sort according to the birth time.
+@xref{File timestamps}.
+
 @item -U
 @itemx --sort=none
 @opindex -U
diff --git a/src/ls.c b/src/ls.c
index c3555f4..8389305 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -460,6 +460,7 @@ enum time_type
     time_mtime,                        /* default */
     time_ctime,                        /* -c */
     time_atime,                        /* -u */
+    time_btime,                 /* birth time */
     time_numtypes              /* the number of elements of this enum */
   };
 
@@ -912,11 +913,16 @@ ARGMATCH_VERIFY (sort_args, sort_types);
 
 static char const *const time_args[] =
 {
-  "atime", "access", "use", "ctime", "status", NULL
+  "atime", "access", "use",
+  "ctime", "status",
+  "birth", "creation",
+  NULL
 };
 static enum time_type const time_types[] =
 {
-  time_atime, time_atime, time_atime, time_ctime, time_ctime
+  time_atime, time_atime, time_atime,
+  time_ctime, time_ctime,
+  time_btime, time_btime,
 };
 ARGMATCH_VERIFY (time_args, time_types);
 
@@ -1065,6 +1071,23 @@ dired_dump_obstack (const char *prefix, struct obstack 
*os)
     }
 }
 
+/* Return the platform birthtime member of the stat structure,
+   or fallback to the mtime member, which we have populated
+   from the statx structure where supported.  */
+static struct timespec
+get_stat_btime (struct stat const *st)
+{
+  struct timespec btimespec;
+
+#if HAVE_STATX && defined STATX_INO
+  btimespec = get_stat_mtime (st);
+#else
+  btimespec = get_stat_birthtime (st);
+#endif
+
+  return btimespec;
+}
+
 #if HAVE_STATX && defined STATX_INO
 static unsigned int _GL_ATTRIBUTE_PURE
 time_type_to_statx (void)
@@ -1077,6 +1100,8 @@ time_type_to_statx (void)
       return STATX_MTIME;
     case time_atime:
       return STATX_ATIME;
+    case time_btime:
+      return STATX_BTIME;
     default:
       abort ();
     }
@@ -1127,9 +1152,19 @@ do_statx (int fd, const char *name, struct stat *st, int 
flags,
           unsigned int mask)
 {
   struct statx stx;
+  bool want_btime = mask & STATX_BTIME;
   int ret = statx (fd, name, flags, mask, &stx);
   if (ret >= 0)
-    statx_to_stat (&stx, st);
+    {
+      statx_to_stat (&stx, st);
+      /* Since we only need one timestamp type,
+         store birth time in st_mtim.  */
+      if (mask & STATX_BTIME)
+        st->st_mtim = statx_timestamp_to_timespec (stx.stx_btime);
+      else if (want_btime)
+        st->st_mtim.tv_sec = st->st_mtim.tv_nsec = -1;
+    }
+
   return ret;
 }
 
@@ -2298,7 +2333,8 @@ decode_switches (int argc, char **argv)
      -lu means show atime and sort by name, -lut means show atime and sort
      by atime.  */
 
-  if ((time_type == time_ctime || time_type == time_atime)
+  if ((time_type == time_ctime || time_type == time_atime
+       || time_type == time_btime)
       && !sort_type_specified && format != long_format)
     {
       sort_type = sort_time;
@@ -3786,6 +3822,15 @@ cmp_atime (struct fileinfo const *a, struct fileinfo 
const *b,
 }
 
 static inline int
+cmp_btime (struct fileinfo const *a, struct fileinfo const *b,
+           int (*cmp) (char const *, char const *))
+{
+  int diff = timespec_cmp (get_stat_btime (&b->stat),
+                           get_stat_btime (&a->stat));
+  return diff ? diff : cmp (a->name, b->name);
+}
+
+static inline int
 cmp_size (struct fileinfo const *a, struct fileinfo const *b,
           int (*cmp) (char const *, char const *))
 {
@@ -3816,6 +3861,7 @@ cmp_extension (struct fileinfo const *a, struct fileinfo 
const *b,
 DEFINE_SORT_FUNCTIONS (ctime, cmp_ctime)
 DEFINE_SORT_FUNCTIONS (mtime, cmp_mtime)
 DEFINE_SORT_FUNCTIONS (atime, cmp_atime)
+DEFINE_SORT_FUNCTIONS (btime, cmp_btime)
 DEFINE_SORT_FUNCTIONS (size, cmp_size)
 DEFINE_SORT_FUNCTIONS (name, cmp_name)
 DEFINE_SORT_FUNCTIONS (extension, cmp_extension)
@@ -3892,7 +3938,8 @@ static qsortFunc const sort_functions[][2][2][2] =
     /* last are time sort functions */
     LIST_SORTFUNCTION_VARIANTS (mtime),
     LIST_SORTFUNCTION_VARIANTS (ctime),
-    LIST_SORTFUNCTION_VARIANTS (atime)
+    LIST_SORTFUNCTION_VARIANTS (atime),
+    LIST_SORTFUNCTION_VARIANTS (btime)
   };
 
 /* The number of sort keys is calculated as the sum of
@@ -4162,6 +4209,7 @@ print_long_format (const struct fileinfo *f)
   char *p;
   struct timespec when_timespec;
   struct tm when_local;
+  bool btime_ok = true;
 
   /* Compute the mode string, except remove the trailing space if no
      file in this directory has an ACL or security context.  */
@@ -4191,6 +4239,11 @@ print_long_format (const struct fileinfo *f)
     case time_atime:
       when_timespec = get_stat_atime (&f->stat);
       break;
+    case time_btime:
+      when_timespec = get_stat_btime (&f->stat);
+      if (when_timespec.tv_sec == -1 && when_timespec.tv_nsec == -1)
+        btime_ok = false;
+      break;
     default:
       abort ();
     }
@@ -4291,7 +4344,8 @@ print_long_format (const struct fileinfo *f)
   s = 0;
   *p = '\1';
 
-  if (f->stat_ok && localtime_rz (localtz, &when_timespec.tv_sec, &when_local))
+  if (f->stat_ok && btime_ok
+      && localtime_rz (localtz, &when_timespec.tv_sec, &when_local))
     {
       struct timespec six_months_ago;
       bool recent;
@@ -4332,7 +4386,7 @@ print_long_format (const struct fileinfo *f)
          print it as a huge integer number of seconds.  */
       char hbuf[INT_BUFSIZE_BOUND (intmax_t)];
       sprintf (p, "%*s ", long_time_expected_width (),
-               (! f->stat_ok
+               (! f->stat_ok || ! btime_ok
                 ? "?"
                 : timetostr (when_timespec.tv_sec, hbuf)));
       /* FIXME: (maybe) We discarded when_timespec.tv_nsec. */
@@ -5380,17 +5434,18 @@ Sort entries alphabetically if none of -cftuvSUX nor 
--sort is specified.\n\
       --sort=WORD            sort by WORD instead of name: none (-U), size 
(-S)\
 ,\n\
                                time (-t), version (-v), extension (-X)\n\
-      --time=WORD            with -l, show time as WORD instead of default\n\
-                               modification time: atime or access or use (-u);\
-\n\
-                               ctime or status (-c); also use specified time\n\
-                               as sort key if --sort=time (newest first)\n\
+      --time=WORD            change the default of using modification times;\n\
+                               access time (-u): atime, access, use;\n\
+                               change time (-c): ctime, status;\n\
+                               birth time: birth, creation;\n\
+                             with -l, WORD determines which time to show;\n\
+                             with --sort=time, sort by WORD (newest first)\n\
 "), stdout);
       fputs (_("\
       --time-style=TIME_STYLE  time/date format with -l; see TIME_STYLE 
below\n\
 "), stdout);
       fputs (_("\
-  -t                         sort by modification time, newest first\n\
+  -t                         sort by time, newest first; see --time\n\
   -T, --tabsize=COLS         assume tab stops at each COLS instead of 8\n\
 "), stdout);
       fputs (_("\
diff --git a/tests/local.mk b/tests/local.mk
index 5285f3a..bbcb9d4 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -589,6 +589,7 @@ all_tests =                                 \
   tests/ln/target-1.sh                         \
   tests/ls/a-option.sh                         \
   tests/ls/abmon-align.sh                      \
+  tests/ls/birthtime.sh                                \
   tests/ls/block-size.sh                       \
   tests/ls/color-clear-to-eol.sh               \
   tests/ls/color-dtype-dir.sh                  \
diff --git a/tests/ls/birthtime.sh b/tests/ls/birthtime.sh
new file mode 100755
index 0000000..1bda033
--- /dev/null
+++ b/tests/ls/birthtime.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+# ensure that ls attempts birthtime access
+
+# Copyright (C) 2020 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ ls
+
+# ls should not fail, even if birth time not available
+touch a || framework_failure_
+ls --time=birth -l a || fail=1
+ls --time=creation -t a || fail=1
+
+Exit $fail
-- 
2.9.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]