bug-findutils
[Top][All Lists]
Advanced

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

PATCH: simplify printing of time stamps


From: Urs Thuermann
Subject: PATCH: simplify printing of time stamps
Date: 05 Jan 2013 02:07:25 +0100
User-agent: Gnus/5.0808 (Gnus v5.8.8) Emacs/20.7

This patch simplifies considerably the functions do_time_format() and
format_date() used for %A, %C, and %T in the -printf predicate.
Instead of formatting the time using strftime(), then searching for
the seconds and appending a dot and the nanoseconds, one could simply
use nstrftime() from the GNU lib (already distributed with GNU
findutils) and use %N to get the nanoseconds.

The format strings used with nstrftime() are

        %F+%T.%N0       for %T+
        %s.%N0          for %T@
        %S.%N0          for %TS
        %T.%N0          for %TT

Only for %TX no nanoseconds are printed anymore, since we don't know
how %X will be formatted by nstrftime().

This patch also makes nanoseconds availabe for -printf by using %TN.

I would also suggest reverting the change in findutils-4.3.3 that %TT
and %TS print nanosecond resolution.  This change makes it difficult
to only print the seconds in the minute without nanoseconds.  With
this patch the user could then achieve the same effect using %TT.%TN
or %TS.%TN, respecitvely.

Therefore, I suggest deleting the line

        #define PRINT_NANOSECONDS

from my patch.  This would IMHO make GNU find cleaner, because people
knowing strftime(3) get what they expect from %TT and %TS, and it
makes GNU find consistent with the behavior of ls(1) and date(1) from
GNU coreutils.

urs


diff -rup findutils-4.4.2.orig/find/pred.c findutils-4.4.2/find/pred.c
--- findutils-4.4.2.orig/find/pred.c    2009-05-16 17:17:01.000000000 +0200
+++ findutils-4.4.2/find/pred.c 2012-12-26 11:42:41.529912314 +0100
@@ -33,6 +33,7 @@
 #include <locale.h>
 #include <openat.h>
 #include <ctype.h>
+#include "strftime.h"
 #include "xalloc.h"
 #include "dirname.h"
 #include "human.h"
@@ -2021,57 +2022,12 @@ launch (const struct buildcmd_control *c
 }
 
 
-static boolean
-scan_for_digit_differences(const char *p, const char *q,
-                          size_t *first, size_t *n)
-{
-  bool seen = false;
-  size_t i;
-
-  for (i=0; p[i] && q[i]; i++)
-    {
-      if (p[i] != q[i])
-       {
-         if (!isdigit((unsigned char)q[i]) || !isdigit ((unsigned char)q[i]))
-           return false;
-
-         if (!seen)
-           {
-             *first = i;
-             *n = 1;
-             seen = 1;
-           }
-         else
-           {
-             if (i-*first == *n)
-               {
-                 /* Still in the first sequence of differing digits. */
-                 ++*n;
-               }
-             else
-               {
-                 /* More than one differing contiguous character sequence. */
-                 return false;
-               }
-           }
-       }
-    }
-  if (p[i] || q[i])
-    {
-      /* strings are different lengths. */
-      return false;
-    }
-  return true;
-}
-
-
 static char*
-do_time_format (const char *fmt, const struct tm *p, const char *ns, size_t 
ns_size)
+do_time_format (const char *fmt, const struct tm *p, int ns)
 {
   static char *buf = NULL;
   static size_t buf_size;
   char *timefmt = NULL;
-  struct tm altered_time;
 
 
   /* If the format expands to nothing (%p in some locales, for
@@ -2083,15 +2039,6 @@ do_time_format (const char *fmt, const s
   timefmt = xmalloc (strlen(fmt) + 2u);
   sprintf (timefmt, "_%s", fmt);
 
-  /* altered_time is a similar time, but in which both
-   * digits of the seconds field are different.
-   */
-  altered_time = *p;
-  if (altered_time.tm_sec >= 11)
-    altered_time.tm_sec -= 11;
-  else
-    altered_time.tm_sec += 11;
-
   /* If we call strftime() with buf_size=0, the program will coredump
    * on Solaris, since it unconditionally writes the terminating null
    * character.
@@ -2104,46 +2051,10 @@ do_time_format (const char *fmt, const s
        * Therefore we do not check for (buf_used != 0) as the termination
        * condition.
        */
-      size_t buf_used = strftime (buf, buf_size, timefmt, p);
+           size_t buf_used = nstrftime (buf, buf_size, timefmt, p, 0, ns);
       if (buf_used             /* Conforming POSIX system */
          && (buf_used < buf_size)) /* Solaris workaround */
        {
-         char *altbuf;
-         size_t i = 0, n = 0;
-         size_t final_len = (buf_used
-                             + 1u /* for \0 */
-                             + ns_size);
-         buf = xrealloc (buf, final_len);
-         altbuf = xmalloc (final_len);
-         strftime (altbuf, buf_size, timefmt, &altered_time);
-
-         /* Find the seconds digits; they should be the only changed part.
-          * In theory the result of the two formatting operations could differ 
in
-          * more than just one sequence of decimal digits (for example %X might
-          * in theory return a spelled-out time like "thirty seconds past 
noon").
-          * When that happens, we just avoid inserting the nanoseconds field.
-          */
-         if (scan_for_digit_differences (buf, altbuf, &i, &n)
-             && (2==n) && !isdigit((unsigned char)buf[i+n]))
-           {
-             const size_t end_of_seconds = i + n;
-             const size_t suffix_len = buf_used-(end_of_seconds)+1;
-
-             /* Move the tail (including the \0).  Note that this
-              * is a move of an overlapping memory block, so we
-              * must use memmove instead of memcpy.  Then insert
-              * the nanoseconds (but not its trailing \0).
-              */
-             assert (end_of_seconds + ns_size + suffix_len == final_len);
-             memmove (buf+end_of_seconds+ns_size,
-                      buf+end_of_seconds,
-                      suffix_len);
-             memcpy (buf+i+n, ns, ns_size);
-           }
-         else
-           {
-             /* No seconds digits.  No need to insert anything. */
-           }
          /* The first character of buf is the underscore, which we actually
           * don't want.
           */
@@ -2169,90 +2080,33 @@ do_time_format (const char *fmt, const s
  * really runtime checks.  The assertions actually exist to verify
  * that the various buffers are correctly sized.
  */
+#define PRINT_NANOSECONDS
 static char *
 format_date (struct timespec ts, int kind)
 {
-  /* In theory, we use an extra 10 characters for 9 digits of
-   * nanoseconds and 1 for the decimal point.  However, the real
-   * world is more complex than that.
-   *
-   * For example, some systems return junk in the tv_nsec part of
-   * st_birthtime.  An example of this is the NetBSD-4.0-RELENG kernel
-   * (at Sat Mar 24 18:46:46 2007) running a NetBSD-3.1-RELEASE
-   * runtime and examining files on an msdos filesytem.  So for that
-   * reason we set NS_BUF_LEN to 32, which is simply "long enough" as
-   * opposed to "exactly the right size".  Note that the behaviour of
-   * NetBSD appears to be a result of the use of uninitialised data,
-   * as it's not 100% reproducible (more like 25%).
-   */
-  enum {
-    NS_BUF_LEN = 32,
-    DATE_LEN_PERCENT_APLUS=21  /* length of result of %A+ (it's longer than 
%c)*/
-  };
-  static char buf[128u+10u + MAX(DATE_LEN_PERCENT_APLUS,
-                           MAX (LONGEST_HUMAN_READABLE + 2, 
NS_BUF_LEN+64+200))];
-  char ns_buf[NS_BUF_LEN]; /* -.9999999990 (- sign can happen!)*/
-  int  charsprinted, need_ns_suffix;
   struct tm *tm;
-  char fmt[6];
-
-  /* human_readable() assumes we pass a buffer which is at least as
-   * long as LONGEST_HUMAN_READABLE.  We use an assertion here to
-   * ensure that no nasty unsigned overflow happend in our calculation
-   * of the size of buf.  Do the assertion here rather than in the
-   * code for %@ so that we find the problem quickly if it exists.  If
-   * you want to submit a patch to move this into the if statement, go
-   * ahead, I'll apply it.  But include performance timings
-   * demonstrating that the performance difference is actually
-   * measurable.
-   */
-  verify (sizeof(buf) >= LONGEST_HUMAN_READABLE);
-
-  charsprinted = 0;
-  need_ns_suffix = 0;
+  char fmt[10];
 
   /* Format the main part of the time. */
   if (kind == '+')
     {
-      strcpy (fmt, "%F+%T");
-      need_ns_suffix = 1;
+      strcpy (fmt, "%F+%T.%N0");
     }
-  else
+  else if (kind == '@')
     {
-      fmt[0] = '%';
-      fmt[1] = kind;
-      fmt[2] = '\0';
-
-      /* %a, %c, and %t are handled in ctime_format() */
-      switch (kind)
-       {
-       case 'S':
-       case 'T':
-       case 'X':
-       case '@':
-         need_ns_suffix = 1;
-         break;
-       default:
-         need_ns_suffix = 0;
-         break;
-       }
+      strcpy (fmt, "%s.%N0");
     }
-
-  if (need_ns_suffix)
+#ifdef PRINT_NANOSECONDS
+  else if (kind == 'S' || kind == 'T')
     {
-      /* Format the nanoseconds part.  Leave a trailing zero to
-       * discourage people from writing scripts which extract the
-       * fractional part of the timestamp by using column offsets.
-       * The reason for discouraging this is that in the future, the
-       * granularity may not be nanoseconds.
-       */
-      charsprinted = snprintf(ns_buf, NS_BUF_LEN, ".%09ld0", (long 
int)ts.tv_nsec);
-      assert (charsprinted < NS_BUF_LEN);
+      sprintf (fmt, "%%%c.%%N0", kind);
     }
+#endif
   else
     {
-      charsprinted = 0;
-      ns_buf[0] = 0;
+      fmt[0] = '%';
+      fmt[1] = kind;
+      fmt[2] = '\0';
     }
 
   if (kind != '@')
@@ -2260,17 +2114,50 @@ format_date (struct timespec ts, int kin
       tm = localtime (&ts.tv_sec);
       if (tm)
        {
-         char *s = do_time_format (fmt, tm, ns_buf, charsprinted);
+         char *s = do_time_format (fmt, tm, ts.tv_nsec);
          if (s)
            return s;
        }
     }
 
-  /* If we get to here, either the format was %@, or we have fallen back to it
-   * because strftime failed.
+  /* If we get to here, strftime has failed.
    */
   if (1)
     {
+      /* In theory, we use an extra 10 characters for 9 digits of
+       * nanoseconds and 1 for the decimal point.  However, the real
+       * world is more complex than that.
+       *
+       * For example, some systems return junk in the tv_nsec part of
+       * st_birthtime.  An example of this is the NetBSD-4.0-RELENG kernel
+       * (at Sat Mar 24 18:46:46 2007) running a NetBSD-3.1-RELEASE
+       * runtime and examining files on an msdos filesytem.  So for that
+       * reason we set NS_BUF_LEN to 32, which is simply "long enough" as
+       * opposed to "exactly the right size".  Note that the behaviour of
+       * NetBSD appears to be a result of the use of uninitialised data,
+       * as it's not 100% reproducible (more like 25%).
+       */
+      enum {
+       NS_BUF_LEN = 32,
+       DATE_LEN_PERCENT_APLUS=21    /* length of result of %A+ (it's longer 
than %c)*/
+      };
+      static char buf[128u+10u + MAX(DATE_LEN_PERCENT_APLUS,
+                                    MAX (LONGEST_HUMAN_READABLE + 2, 
NS_BUF_LEN+64+200))];
+      char ns_buf[NS_BUF_LEN]; /* -.9999999990 (- sign can happen!)*/
+      int  charsprinted;
+
+      /* human_readable() assumes we pass a buffer which is at least as
+       * long as LONGEST_HUMAN_READABLE.  We use an assertion here to
+       * ensure that no nasty unsigned overflow happend in our calculation
+       * of the size of buf.  Do the assertion here rather than in the
+       * code for %@ so that we find the problem quickly if it exists.  If
+       * you want to submit a patch to move this into the if statement, go
+       * ahead, I'll apply it.  But include performance timings
+       * demonstrating that the performance difference is actually
+       * measurable.
+       */
+      verify (sizeof(buf) >= LONGEST_HUMAN_READABLE);
+
       uintmax_t w = ts.tv_sec;
       size_t used, len, remaining;
 
@@ -2284,27 +2171,34 @@ format_date (struct timespec ts, int kin
       if (ts.tv_sec < 0)
        *--p = '-'; /* XXX: Ugh, relying on internal details of 
human_readable(). */
 
+      /* Format the nanoseconds part.  Leave a trailing zero to
+       * discourage people from writing scripts which extract the
+       * fractional part of the timestamp by using column offsets.
+       * The reason for discouraging this is that in the future, the
+       * granularity may not be nanoseconds.
+       */
+      charsprinted = snprintf(ns_buf, NS_BUF_LEN, ".%09ld0", (long 
int)ts.tv_nsec);
+      assert (charsprinted < NS_BUF_LEN);
+
       /* Add the nanoseconds part.  Because we cannot enforce a
        * particlar implementation of human_readable, we cannot assume
        * any particular value for (p-buf).  So we need to be careful
        * that there is enough space remaining in the buffer.
        */
-      if (need_ns_suffix)
-       {
-         len = strlen(p);
-         used = (p-buf) + len; /* Offset into buf of current end */
-         assert (sizeof buf > used); /* Ensure we can perform subtraction 
safely. */
-         remaining = sizeof buf - used - 1u; /* allow space for NUL */
-
-         if (strlen(ns_buf) >= remaining)
-           {
-             error(0, 0,
-                   "charsprinted=%ld but remaining=%lu: ns_buf=%s",
-                   (long)charsprinted, (unsigned long)remaining, ns_buf);
-           }
-         assert (strlen(ns_buf) < remaining);
-         strcat(p, ns_buf);
+      len = strlen(p);
+      used = (p-buf) + len;    /* Offset into buf of current end */
+      assert (sizeof buf > used); /* Ensure we can perform subtraction safely. 
*/
+      remaining = sizeof buf - used - 1u; /* allow space for NUL */
+
+      if (strlen(ns_buf) >= remaining)
+       {
+         error(0, 0,
+               "charsprinted=%ld but remaining=%lu: ns_buf=%s",
+               (long)charsprinted, (unsigned long)remaining, ns_buf);
        }
+      assert (strlen(ns_buf) < remaining);
+      strcat(p, ns_buf);
+
       return p;
     }
 }



reply via email to

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