From 96cdb72eeaed9fbe6a3434eabcbf900fdfc1a6d5 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 6 Feb 2022 13:32:11 -0800 Subject: [PATCH 4/4] Use GNU ls algorithm for deciding timestamp format Problem reported by Dan Jacobson (Bug#50694). * gnulib.modules: Add gettime, timespec. * src/copyin.c: Include timespec.h. (current_time): Now struct timespec, not time_t. All uses changed. (long_format): When formatting a timestamp, use the same algorithm that GNU ls does to decide whether a file is recent. --- gnulib.modules | 2 ++ src/copyin.c | 30 ++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/gnulib.modules b/gnulib.modules index 15037a0..64cda0a 100644 --- a/gnulib.modules +++ b/gnulib.modules @@ -12,6 +12,7 @@ fnmatch-gnu full-write getline gettext-h +gettime gitlog-to-changelog hash inttostr @@ -24,6 +25,7 @@ stdbool stdint stpcpy strerror +timespec unlocked-io utimens xalloc diff --git a/src/copyin.c b/src/copyin.c index 0af23a0..28d0c33 100644 --- a/src/copyin.c +++ b/src/copyin.c @@ -31,6 +31,7 @@ #ifndef FNM_PATHNAME # include #endif +#include #ifndef HAVE_LCHOWN # define lchown(f,u,g) 0 @@ -719,7 +720,7 @@ copyin_file (struct cpio_file_stat *file_hdr, int in_file_des) /* Current time for verbose table. */ -static time_t current_time; +static struct timespec current_time; /* Print the file described by FILE_HDR in long format. @@ -761,10 +762,25 @@ long_format (struct cpio_file_stat *file_hdr, char const *link_name) or a year outside the range 1000-9999, since 0 <= WHEN < 2**33. */ time_t when = file_hdr->c_mtime; char *tbuf = ctime (&when); - int six_months = 6 * 30 * 24 * 60 * 60; - bool recent = ((current_time < six_months - || current_time - six_months <= when) - && when <= current_time); + + /* If the file appears to be in the future, update the current + time, in case the file happens to have been modified since + the last time we checked the clock. */ + struct timespec when_timespec = { .tv_sec = when }; + if (timespec_cmp (current_time, when_timespec) < 0) + current_time = current_timespec (); + + /* Consider a time to be recent if it is within the past six months. + Use the same algorithm that GNU 'ls' does, for consistency. + A Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds + on the average. Write this value as an integer constant to + avoid floating point hassles. */ + struct timespec six_months_ago = { + .tv_sec = current_time.tv_sec - 31556952 / 2, + .tv_nsec = current_time.tv_nsec + }; + bool recent = (timespec_cmp (six_months_ago, when_timespec) < 0 + & timespec_cmp (when_timespec, current_time) < 0); if (!recent) { /* The file is older than 6 months, or in the future. @@ -1264,9 +1280,7 @@ process_copy_in (void) /* Get date and time if needed for processing the table option. */ if (table_flag && verbose_flag) - { - time (¤t_time); - } + current_time = current_timespec (); /* Check whether the input file might be a tape. */ in_file_des = archive_des; -- 2.32.0