[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
- [PATCH] ls: support --time=creation to show/sort birth time,
Pádraig Brady <=