From c7375c236ca5fa23661388e9f9c41e8312eb0cce Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Fri, 5 Nov 2010 19:35:12 -0700 Subject: [PATCH] stat: use e.g. %.3X instead of %X.%3:X for sub-second precision * NEWS: Document this. * doc/coreutils.texi (stat invocation): Likewise. * gl/lib/fstimeprec.c, gl/lib/fstimeprec.h, gl/modules/fstimeprec: * gl/modules/fstimeprec-tests, gl/tests/test-fstimeprec.c: New files. * bootstrap.conf (gnulib_modules): Add fstimeprec. * src/stat.c: Include fstimeprec.h. Don't include xstrtol.h. (decimal_point, decimal_point_len): New static vars. (main): Initialize them. (epoch_sec, out_ns): Remove. (out_int, out_uint): Now returns whatever printf returned. (out_minus_zero, out_epoch_secs): New functions. (print_stat): Use out_epoch_sec instead of out_ns and epoch_sec. (print_stat, print_it, usage): Remove the %:X-style formats. * tests/misc/stat-nanoseconds: Set TZ=UTC0 to avoid problems with weird time zones. Use a time stamp near the Epoch so that we don't have to worry about leap seconds. Redo test cases to match new behavior. * tests/touch/60-seconds: Change %Y.%:Y to %.9Y, to adjust to new behavior. --- NEWS | 9 +- bootstrap.conf | 1 + doc/coreutils.texi | 37 ++++---- gl/lib/fstimeprec.c | 178 +++++++++++++++++++++++++++++++++++ gl/lib/fstimeprec.h | 22 +++++ gl/modules/fstimeprec | 24 +++++ gl/modules/fstimeprec-tests | 10 ++ gl/tests/test-fstimeprec.c | 74 +++++++++++++++ src/stat.c | 220 +++++++++++++++++++++++++++----------------- tests/misc/stat-nanoseconds | 27 ++++-- tests/touch/60-seconds | 2 +- 11 files changed, 484 insertions(+), 120 deletions(-) create mode 100644 gl/lib/fstimeprec.c create mode 100644 gl/lib/fstimeprec.h create mode 100644 gl/modules/fstimeprec create mode 100644 gl/modules/fstimeprec-tests create mode 100644 gl/tests/test-fstimeprec.c diff --git a/NEWS b/NEWS index 3b9bea2..0cd6153 100644 --- a/NEWS +++ b/NEWS @@ -20,15 +20,12 @@ GNU coreutils NEWS -*- outline -*- stat's %X, %Y, and %Z directives once again print only the integer part of seconds since the epoch. This reverts a change from coreutils-8.6, that was deemed unnecessarily disruptive. To obtain - the nanoseconds portion corresponding to %X, you may now use %:X. - I.e., to print the floating point number of seconds using maximum - precision, use this format string: %X.%:X. Likewise for %Y, %Z and %W. + a full resolution time stamp for %X, use %.X; if you want (say) just + 3 fractional digits, use %.3X. Likewise for %Y and %Z. stat's new %W format directive would print floating point seconds. However, with the above change to %X, %Y and %Z, we've made %W work - the same way: %W now expands to seconds since the epoch (or 0 when - not supported), and %:W expands to the nanoseconds portion, or to - 0 if not supported. + the same way as the others. * Noteworthy changes in release 8.6 (2010-10-15) [stable] diff --git a/bootstrap.conf b/bootstrap.conf index 18ef914..5a6ebae 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -85,6 +85,7 @@ gnulib_modules=" freopen freopen-safer fseeko + fstimeprec fsusage fsync ftello diff --git a/doc/coreutils.texi b/doc/coreutils.texi index be5999f..8dfb069 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -10707,37 +10707,36 @@ The valid @var{format} directives for files with @option{--format} and @item %U - User name of owner @item %w - Time of file birth, or @samp{-} if unknown @item %W - Time of file birth as seconds since Epoch, or @samp{0} -@item %:W - Time of file birth: nanoseconds remainder, or @samp{0} @item %x - Time of last access @item %X - Time of last access as seconds since Epoch -@item %:X - Time of last access: nanoseconds remainder @item %y - Time of last modification @item %Y - Time of last modification as seconds since Epoch -@item %:Y - Time of last modification: nanoseconds remainder @item %z - Time of last change @item %Z - Time of last change as seconds since Epoch -@item %:Z - Time of last change: nanoseconds remainder @end itemize -Note that each of @samp{%:W}, @samp{%:X}, @samp{%:Y} and @samp{%:Z} -prints its zero-padded number of nanoseconds on a field of width 9. -However, if you specify anything between the @samp{%} and @samp{:}, -e.g., @samp{%10:X}, your modifier replaces the default of @samp{09}. - -Here are some examples: +The @samp{%W}, @samp{%X}, @samp{%Y}, and @samp{%Z} formats accept a +precision preceded by a period to specify the number of digits to +print after the decimal point. For example, @samp{%.9X} outputs the +last access time to nanosecond precision. If a period is given but no +precision, @command{stat} uses the estimated precision of the file +system. When discarding excess precision, time stamps are truncated +toward minus infinity. @example zero pad: - $ stat -c '[%015:Y]' /usr - [000000031367045] + $ stat -c '[%015Y]' /usr + [000001288929712] space align: - $ stat -c '[%15:Y]' /usr - [ 31367045] - $ stat -c '[%-15:Y]' /usr - [31367045 ] -truncate: - $ stat -c '[%.3:Y]' /usr - [031] + $ stat -c '[%15Y]' /usr + [ 1288929712] + $ stat -c '[%-15Y]' /usr + [1288929712 ] +precision: + $ stat -c '[%.3Y]' /usr + [1288929712.114] + $ stat -c '[%.Y]' /usr + [1288929712.114951] @end example The mount point printed by @samp{%m} is similar to that output diff --git a/gl/lib/fstimeprec.c b/gl/lib/fstimeprec.c new file mode 100644 index 0000000..fd63054 --- /dev/null +++ b/gl/lib/fstimeprec.c @@ -0,0 +1,178 @@ +/* Determine a file system's time stamp precision. + + Copyright 2010 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 . */ + +/* written by Paul Eggert */ + +#include +#include "fstimeprec.h" + +#include "hash.h" +#include "stat-time.h" +#include +#include + +/* A hash table that maps device numbers to precisions. */ +struct fstimeprec +{ + /* Map device numbers to precisions. */ + struct hash_table *map; + + /* Cache of the most recently allocated and otherwise-unused storage + for probing this table. */ + struct ent *probe; +}; + +/* A pair that maps a device number to a precision. */ +struct ent +{ + dev_t dev; + int prec; +}; + +/* Hash an entry. */ +static size_t +ent_hash (void const *x, size_t table_size) +{ + struct ent const *p = x; + dev_t dev = p->dev; + + /* FIXME: This code is duplicated from di-set.c. */ + /* When DEV is wider than size_t, exclusive-OR the words of DEV into H. + This avoids loss of info, without applying % to the wider type, + which could be quite slow on some systems. */ + size_t h = dev; + unsigned int i; + unsigned int n_words = sizeof dev / sizeof h + (sizeof dev % sizeof h != 0); + for (i = 1; i < n_words; i++) + h ^= dev >> CHAR_BIT * sizeof h * i; + + return h % table_size; +} + +/* Return true if two entries are the same. */ +static bool +ent_compare (void const *x, void const *y) +{ + struct ent const *a = x; + struct ent const *b = y; + return a->dev == b->dev; +} + +/* Using the SET table, map a device to an entry that represents + the file system precision. Return NULL on error. */ +static struct ent * +map_device (struct fstimeprec *tab, dev_t dev) +{ + /* Find space for the probe, reusing the cache if available. */ + struct ent *ent; + struct ent *probe = tab->probe; + if (probe) + { + /* If repeating a recent query, return the cached result. */ + if (probe->dev == dev) + return probe; + } + else + { + tab->probe = probe = malloc (sizeof *probe); + if (! probe) + return NULL; + probe->prec = 0; + } + + /* Probe for the device. */ + probe->dev = dev; + ent = hash_insert (tab->map, probe); + if (ent == probe) + tab->probe = NULL; + return ent; +} + +/* Return a newly allocated table of file system time stamp + resolutions, or NULL if out of memory. */ +struct fstimeprec * +fstimeprec_alloc (void) +{ + struct fstimeprec *tab = malloc (sizeof *tab); + if (tab) + { + enum { INITIAL_DEV_MAP_SIZE = 11 }; + tab->map = hash_initialize (INITIAL_DEV_MAP_SIZE, NULL, + ent_hash, ent_compare, free); + if (! tab->map) + { + free (tab); + return NULL; + } + tab->probe = NULL; + } + return tab; +} + +/* Free TAB. */ +void +fstimeprec_free (struct fstimeprec *tab) +{ + hash_free (tab->map); + free (tab->probe); + free (tab); +} + +/* Using the cached information in TAB, return the estimated precision + of time stamps for the file system containing the file whose status + info is in ST. The returned value counts the number of decimal + fraction digits. Return 0 on failure. + + If TAB is null, the guess is based purely on ST. */ +int +fstimeprec (struct fstimeprec *tab, struct stat const *st) +{ + /* Map the device number to an entry. */ + struct ent *ent = (tab ? map_device (tab, st->st_dev) : NULL); + + /* Guess the precision based on what has been seen so far. */ + int prec = (ent ? ent->prec : 0); + + /* If the current file's timestamp resolution is higher than the + guess, increase the guess. */ + if (prec < 9) + { + struct timespec ats = get_stat_atime (st); + struct timespec mts = get_stat_mtime (st); + struct timespec cts = get_stat_ctime (st); + struct timespec bts = get_stat_birthtime (st); + unsigned int ans = ats.tv_nsec; + unsigned int mns = mts.tv_nsec; + unsigned int cns = cts.tv_nsec; + unsigned int bns = bts.tv_nsec < 0 ? 0 : bts.tv_nsec; + unsigned int power = 10; + int p; + for (p = prec + 1; p < 9; p++) + power *= 10; + + while (ans % power | mns % power | cns % power | bns % power) + { + power /= 10; + prec++; + } + + if (ent) + ent->prec = prec; + } + + return prec; +} diff --git a/gl/lib/fstimeprec.h b/gl/lib/fstimeprec.h new file mode 100644 index 0000000..8ec24df --- /dev/null +++ b/gl/lib/fstimeprec.h @@ -0,0 +1,22 @@ +#include + +#ifndef ATTRIBUTE_MALLOC +# if __GNUC__ >= 3 +# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) +# else +# define ATTRIBUTE_MALLOC +# endif +#endif + +#ifndef _GL_ARG_NONNULL +# if (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) || __GNUC__ > 3 +# define _GL_ARG_NONNULL(params) __attribute__ ((__nonnull__ params)) +# else +# define _GL_ARG_NONNULL(params) +# endif +#endif + +struct fstimeprec *fstimeprec_alloc (void) ATTRIBUTE_MALLOC; +void fstimeprec_free (struct fstimeprec *) _GL_ARG_NONNULL ((1)); +int fstimeprec (struct fstimeprec *, struct stat const *) + _GL_ARG_NONNULL ((2)); diff --git a/gl/modules/fstimeprec b/gl/modules/fstimeprec new file mode 100644 index 0000000..787c2e7 --- /dev/null +++ b/gl/modules/fstimeprec @@ -0,0 +1,24 @@ +Description: +estimate precision of time stamps in file systems + +Files: +lib/fstimeprec.c +lib/fstimeprec.h + +Depends-on: +hash +stat-time + +configure.ac: + +Makefile.am: +lib_SOURCES += fstimeprec.c fstimeprec.h + +Include: +"fstimeprec.h" + +License +GPL + +Maintainer: +Paul Eggert and Jim Meyering diff --git a/gl/modules/fstimeprec-tests b/gl/modules/fstimeprec-tests new file mode 100644 index 0000000..bf6b49e --- /dev/null +++ b/gl/modules/fstimeprec-tests @@ -0,0 +1,10 @@ +Files: +tests/test-fstimeprec.c + +Depends-on: + +configure.ac: + +Makefile.am: +TESTS += test-fstimeprec +check_PROGRAMS += test-fstimeprec diff --git a/gl/tests/test-fstimeprec.c b/gl/tests/test-fstimeprec.c new file mode 100644 index 0000000..f699139 --- /dev/null +++ b/gl/tests/test-fstimeprec.c @@ -0,0 +1,74 @@ +/* Test the fstimeprec module. + Copyright (C) 2010 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 . */ + +/* written by Paul Eggert */ + +#include +#include +#include +#include + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +#include "fstimeprec.h" + +static int +fstimeprec_file (struct fstimeprec *tab, char const *file) +{ + struct stat st; + if (stat (file, &st) != 0) + return 0; + return fstimeprec (tab, &st); +} + +int +main (void) +{ + struct fstimeprec *tab = fstimeprec_alloc (); + ASSERT (tab); + + int m1 = fstimeprec_file (tab, "/"); + int m2 = fstimeprec_file (tab, "."); + int m3 = fstimeprec_file (tab, ".."); + ASSERT (0 <= m1 && m1 <= 9); + ASSERT (0 <= m2 && m2 <= 9); + ASSERT (0 <= m3 && m3 <= 9); + + int n1 = fstimeprec_file (tab, "/"); + int n2 = fstimeprec_file (tab, "."); + int n3 = fstimeprec_file (tab, ".."); + ASSERT (0 <= n1 && n1 <= 9); + ASSERT (0 <= n2 && n2 <= 9); + ASSERT (0 <= n3 && n3 <= 9); + + ASSERT (m1 <= n1); + ASSERT (m2 <= n2); + ASSERT (m3 <= n3); + + fstimeprec_free (tab); + + return 0; +} diff --git a/src/stat.c b/src/stat.c index d05a93b..99f115b 100644 --- a/src/stat.c +++ b/src/stat.c @@ -63,6 +63,7 @@ #include "file-type.h" #include "filemode.h" #include "fs.h" +#include "fstimeprec.h" #include "getopt.h" #include "mountlist.h" #include "quote.h" @@ -70,7 +71,6 @@ #include "stat-time.h" #include "strftime.h" #include "find-mount-point.h" -#include "xstrtol.h" #include "xvasprintf.h" #if USE_STATVFS @@ -183,6 +183,10 @@ static bool interpret_backslash_escapes; "" for --printf=FMT, "\n" for --format=FMT (-c). */ static char const *trailing_delim = ""; +/* The representation of the decimal point in the current locale. */ +static char const *decimal_point; +static size_t decimal_point_len; + /* Return the type of the specified file system. Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris). Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0). @@ -463,69 +467,23 @@ human_time (struct timespec t) return str; } -/* Return a string representation (in static storage) - of the number of seconds in T since the epoch. */ -static char * ATTRIBUTE_WARN_UNUSED_RESULT -epoch_sec (struct timespec t) -{ - static char str[INT_BUFSIZE_BOUND (time_t)]; - return timetostr (t.tv_sec, str); -} - -/* Output the number of nanoseconds, ARG.tv_nsec, honoring a - WIDTH.PRECISION format modifier, where PRECISION specifies - how many leading digits(on a field of 9) to print. */ -static void -out_ns (char *pformat, size_t prefix_len, struct timespec arg) -{ - /* If no format modifier is specified, i.e., nothing between the - "%" and ":" of "%:X", then use the default of zero-padding and - a width of 9. Otherwise, use the specified modifier(s). - This is to avoid the mistake of omitting the zero padding on - a number with fewer digits than the field width: when printing - nanoseconds after a decimal place, the resulting floating point - fraction would be off by a factor of 10 or more. - - If a precision/max width is specified, i.e., a '.' is present - in the modifier, then then treat the modifier as operating - on the default representation, i.e., a zero padded number - of width 9. */ - unsigned long int ns = arg.tv_nsec; - - if (memchr (pformat, '.', prefix_len)) /* precision specified. */ - { - char tmp[INT_BUFSIZE_BOUND (uintmax_t)]; - snprintf (tmp, sizeof tmp, "%09lu", ns); - strcpy (pformat + prefix_len, "s"); - printf (pformat, tmp); - } - else - { - char const *fmt = (prefix_len == 1) ? "09lu" : "lu"; - /* Note that pformat is big enough, as %:X -> %09lu - and two extra bytes are already allocated. */ - strcpy (pformat + prefix_len, fmt); - printf (pformat, ns); - } -} - static void out_string (char *pformat, size_t prefix_len, char const *arg) { strcpy (pformat + prefix_len, "s"); printf (pformat, arg); } -static void +static int out_int (char *pformat, size_t prefix_len, intmax_t arg) { strcpy (pformat + prefix_len, PRIdMAX); - printf (pformat, arg); + return printf (pformat, arg); } -static void +static int out_uint (char *pformat, size_t prefix_len, uintmax_t arg) { strcpy (pformat + prefix_len, PRIuMAX); - printf (pformat, arg); + return printf (pformat, arg); } static void out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg) @@ -539,6 +497,122 @@ out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg) strcpy (pformat + prefix_len, PRIxMAX); printf (pformat, arg); } +static int +out_minus_zero (char *pformat, size_t prefix_len) +{ + strcpy (pformat + prefix_len, ".0f"); + return printf (pformat, -0.25); +} + +/* Output the number of seconds since the Epoch, using a format that + acts like printf's %f format. */ +static void +out_epoch_sec (char *pformat, size_t prefix_len, struct stat const *statbuf, + struct timespec arg) +{ + char *dot = memchr (pformat, '.', prefix_len); + size_t sec_prefix_len = prefix_len; + int width = 0; + int precision = 0; + bool frac_left_adjust = false; + + if (dot) + { + sec_prefix_len = dot - pformat; + pformat[prefix_len] = '\0'; + + if (ISDIGIT (dot[1])) + { + long int lprec = strtol (dot + 1, NULL, 10); + precision = (lprec <= INT_MAX ? lprec : INT_MAX); + } + else + { + static struct fstimeprec *tab; + if (! tab) + tab = fstimeprec_alloc (); + precision = fstimeprec (tab, statbuf); + } + + if (precision && ISDIGIT (dot[-1])) + { + /* If a nontrivial width is given, subtract the width of the + decimal point and PRECISION digits that will be output + later. */ + char *p = dot; + *dot = '\0'; + + do + --p; + while (ISDIGIT (p[-1])); + + long int lwidth = strtol (p, NULL, 10); + width = (lwidth <= INT_MAX ? lwidth : INT_MAX); + if (1 < width) + { + p += (*p == '0'); + sec_prefix_len = p - pformat; + int w_d = (decimal_point_len < width + ? width - decimal_point_len + : 0); + if (1 < w_d) + { + int w = w_d - precision; + if (1 < w) + { + char *dst = pformat; + for (char const *src = dst; src < p; src++) + { + if (*src == '-') + frac_left_adjust = true; + else + *dst++ = *src; + } + sec_prefix_len = + (dst - pformat + + (frac_left_adjust ? 0 : sprintf (dst, "%d", w))); + } + } + } + } + } + + int divisor = 1; + for (int i = precision; i < 9; i++) + divisor *= 10; + int frac_sec = arg.tv_nsec / divisor; + int int_len; + + if (TYPE_SIGNED (time_t)) + { + bool minus_zero = false; + if (arg.tv_sec < 0 && arg.tv_nsec != 0) + { + int frac_sec_modulus = 1000000000 / divisor; + frac_sec = (frac_sec_modulus - frac_sec + - (arg.tv_nsec % divisor != 0)); + arg.tv_sec += (frac_sec != 0); + minus_zero = (arg.tv_sec == 0); + } + int_len = (minus_zero + ? out_minus_zero (pformat, sec_prefix_len) + : out_int (pformat, sec_prefix_len, arg.tv_sec)); + } + else + int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec); + + if (precision) + { + int prec = (precision < 9 ? precision : 9); + int trailing_prec = precision - prec; + int ilen = (int_len < 0 ? 0 : int_len); + int trailing_width = (ilen < width && decimal_point_len < width - ilen + ? width - ilen - decimal_point_len - prec + : 0); + printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec, + trailing_width, trailing_prec, 0); + } +} /* Print the context information of FILENAME, and return true iff the context could not be obtained. */ @@ -853,38 +927,26 @@ print_stat (char *pformat, size_t prefix_len, unsigned int m, } break; case 'W': - out_string (pformat, prefix_len, - epoch_sec (neg_to_zero (get_stat_birthtime (statbuf)))); - break; - case 'W' + 256: - out_ns (pformat, prefix_len, neg_to_zero (get_stat_birthtime (statbuf))); + out_epoch_sec (pformat, prefix_len, statbuf, + neg_to_zero (get_stat_birthtime (statbuf))); break; case 'x': out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf))); break; case 'X': - out_string (pformat, prefix_len, epoch_sec (get_stat_atime (statbuf))); - break; - case 'X' + 256: - out_ns (pformat, prefix_len, get_stat_atime (statbuf)); + out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf)); break; case 'y': out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf))); break; case 'Y': - out_string (pformat, prefix_len, epoch_sec (get_stat_mtime (statbuf))); - break; - case 'Y' + 256: - out_ns (pformat, prefix_len, get_stat_mtime (statbuf)); + out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf)); break; case 'z': out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf))); break; case 'Z': - out_string (pformat, prefix_len, epoch_sec (get_stat_ctime (statbuf))); - break; - case 'Z' + 256: - out_ns (pformat, prefix_len, get_stat_ctime (statbuf)); + out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf)); break; case 'C': fail |= out_file_context (pformat, prefix_len, filename); @@ -968,21 +1030,9 @@ print_it (char const *format, char const *filename, { size_t len = strspn (b + 1, "#-+.I 0123456789"); char const *fmt_char = b + len + 1; - unsigned int fmt_code; + unsigned int fmt_code = *fmt_char; memcpy (dest, b, len + 1); - /* The ":" modifier just before the letter in %W, %X, %Y, %Z - tells stat to print the nanoseconds portion of the date. */ - if (*fmt_char == ':' && strchr ("WXYZ", fmt_char[1])) - { - fmt_code = fmt_char[1] + 256; - ++fmt_char; - } - else - { - fmt_code = fmt_char[0]; - } - b = fmt_char; switch (fmt_code) { @@ -1276,16 +1326,12 @@ The valid format sequences for files (without --file-system):\n\ %U User name of owner\n\ %w Time of file birth, human-readable; - if unknown\n\ %W Time of file birth, seconds since Epoch; 0 if unknown\n\ - %:W Time of file birth, nanoseconds remainder; 0 if unknown\n\ %x Time of last access, human-readable\n\ %X Time of last access, seconds since Epoch\n\ - %:X Time of last access, nanoseconds remainder\n\ %y Time of last modification, human-readable\n\ %Y Time of last modification, seconds since Epoch\n\ - %:Y Time of last modification, nanoseconds remainder\n\ %z Time of last change, human-readable\n\ %Z Time of last change, seconds since Epoch\n\ - %:Z Time of last change, nanoseconds remainder\n\ \n\ "), stdout); @@ -1330,6 +1376,10 @@ main (int argc, char *argv[]) bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); + struct lconv const *locale = localeconv (); + decimal_point = (locale->decimal_point[0] ? locale->decimal_point : "."); + decimal_point_len = strlen (decimal_point); + atexit (close_stdout); while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1) diff --git a/tests/misc/stat-nanoseconds b/tests/misc/stat-nanoseconds index 314f43e..0f41eb0 100755 --- a/tests/misc/stat-nanoseconds +++ b/tests/misc/stat-nanoseconds @@ -19,18 +19,27 @@ test "$VERBOSE" = yes && stat --version . "${srcdir=.}/init.sh"; path_prepend_ ../src -touch -d '2010-10-21 18:43:33.023456789' k || framework_failure_ +# Set this to avoid problems with weird time zones. +TZ=UTC0 +export TZ + +# Use a time stamp near the Epoch to avoid trouble with leap seconds. +touch -d '1970-01-01 18:43:33.023456789' k || framework_failure_ ls --full-time | grep 18:43:33.023456789 \ || skip_ this file system does not support sub-second time stamps -test "$(stat -c %:X k)" = 023456789 || fail=1 -test "$(stat -c %3:X k)" = 23456789 || fail=1 -test "$(stat -c %3.3:X k)" = 023 || fail=1 -test "$(stat -c %.3:X k)" = 023 || fail=1 -test "$(stat -c %03.3:X k)" = 023 || fail=1 -test "$(stat -c %-5.3:X k)" = '023 ' || fail=1 -test "$(stat -c %05.3:X k)" = ' 023' || fail=1 -test "$(stat -c %010.3:X k)" = ' 023' || fail=1 +test "$(stat -c %X k)" = 67413 || fail=1 +test "$(stat -c %.X k)" = 67413.023456789 || fail=1 +test "$(stat -c %.1X k)" = 67413.0 || fail=1 +test "$(stat -c %.3X k)" = 67413.023 || fail=1 +test "$(stat -c %.6X k)" = 67413.023456 || fail=1 +test "$(stat -c %.9X k)" = 67413.023456789 || fail=1 +test "$(stat -c %13.6X k)" = ' 67413.023456' || fail=1 +test "$(stat -c %013.6X k)" = 067413.023456 || fail=1 +test "$(stat -c %-13.6X k)" = '67413.023456 ' || fail=1 +test "$(stat -c %18.10X k)" = ' 67413.0234567890' || fail=1 +test "$(stat -c %018.10X k)" = 0067413.0234567890 || fail=1 +test "$(stat -c %-18.10X k)" = '67413.0234567890 ' || fail=1 Exit $fail diff --git a/tests/touch/60-seconds b/tests/touch/60-seconds index d008296..e06c770 100755 --- a/tests/touch/60-seconds +++ b/tests/touch/60-seconds @@ -30,7 +30,7 @@ echo 60.000000000 > exp || framework_failure # an `invalid date format'. Specifying 60 seconds *is* valid. TZ=UTC0 touch -t 197001010000.60 f || fail=1 -stat --p='%Y.%:Y\n' f > out || fail=1 +stat --p='%.9Y\n' f > out || fail=1 compare out exp || fail=1 -- 2.7.4