findutils-patches
[Top][All Lists]
Advanced

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

[Findutils-patches] [PATCH] Fix Savannah bug #22662 (nanoseconds appende


From: James Youngman
Subject: [Findutils-patches] [PATCH] Fix Savannah bug #22662 (nanoseconds appended after "PM" for find -printf %AX in locale en_US.UTF-8).
Date: Fri, 16 May 2008 23:36:07 +0100

2008-05-16  James Youngman  <address@hidden>

        Fix Savannah bug #22662 (nanoseconds appended after "PM" for
        find -printf %AX in locale en_US.UTF-8).
        * find/pred.c (format_date): Use helper function do_time_format
        which can insert the nanoseconds field into the appropriate place
        within the formatted time.  This fixes Savannah bug #22662.
        (do_time_format): New function.  Formats the date, inserting the
        nanoseconds field after the seconds field, if there is a seconds
        field.
        (scan_for_digit_differences): New function.  Locates the seconds
        field.

Signed-off-by: James Youngman <address@hidden>
---
 NEWS        |    7 +++
 find/pred.c |  158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 152 insertions(+), 13 deletions(-)

diff --git a/NEWS b/NEWS
index b4b8fdc..c0682b7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,12 @@
 GNU findutils NEWS - User visible changes.     -*- outline -*- (allout)
 
+* Major changes in release 4.4.1, YYYY-MM-DD
+
+** Bug Fixes
+
+#22662: nanoseconds wrongly appended after "PM" for find -printf %AX
+in locale en_US.UTF-8.  
+
 * Major changes in release 4.4.0, 2008-03-15
 
 The 4.4.0 release of findutils is a stable release, succeeding the
diff --git a/find/pred.c b/find/pred.c
index d758276..d5ada2e 100644
--- a/find/pred.c
+++ b/find/pred.c
@@ -32,6 +32,7 @@
 #include <fcntl.h>
 #include <locale.h>
 #include <openat.h>
+#include <ctype.h>
 #include "xalloc.h"
 #include "dirname.h"
 #include "human.h"
@@ -2018,6 +2019,133 @@ launch (const struct buildcmd_control *ctl,
 }
 
 
+static boolean
+scan_for_digit_differences(const char *p, const char *q,
+                          size_t *first, size_t *n)
+{
+  bool ok = true;
+  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;
+           }
+         else
+           {
+             if (*first - i == *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)
+{
+  static char *buf = NULL;
+  static size_t buf_size = 0u;
+  char *timefmt = NULL;
+  boolean done = false;
+  struct tm altered_time;
+
+  
+  /* If the format expands to nothing (%p in some locales, for
+   * example), strftime can return 0.  We actually want to distinguish
+   * the error case where the buffer is too short, so we just prepend
+   * an otherwise uninteresting character to prevent the no-output
+   * case.
+   */
+  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;
+
+  while (!done)
+    {
+      const size_t buf_used = strftime (buf, buf_size, timefmt, p);
+      if (0 != buf_used)
+       {
+         char *altbuf;
+         size_t i, n;
+         size_t final_len = (buf_used 
+                             + 1u /* for \0 */
+                             - 1u /* because we don't need the initial 
underscore */
+                             + 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) && buf[i+n] && !isdigit((unsigned char)buf[i+n+1]))
+           {
+             const size_t end_of_seconds = i + n;
+
+             /* 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).
+              */
+             memmove (buf+end_of_seconds+ns_size,
+                      buf+end_of_seconds,
+                      buf_used-(end_of_seconds)+1);
+             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. 
+          */
+         free (timefmt);
+         return buf+1;
+       }
+      else
+       {
+         buf = x2nrealloc (buf, &buf_size, 2u);
+       }
+    }
+}
+
+          
+
 /* Return a static string formatting the time WHEN according to the
  * strftime format character KIND.
  *
@@ -2105,26 +2233,30 @@ format_date (struct timespec ts, int kind)
        * The reason for discouraging this is that in the future, the
        * granularity may not be nanoseconds.
        */
-      ns_buf[0] = 0;
       charsprinted = snprintf(ns_buf, NS_BUF_LEN, ".%09ld0", (long 
int)ts.tv_nsec);
       assert (charsprinted < NS_BUF_LEN);
     }
-
-  if (kind != '@'
-      && (tm = localtime (&ts.tv_sec))
-      && strftime (buf, sizeof buf, fmt, tm))
+  else
     {
-      /* For %AS, %CS, %TS, add the fractional part of the seconds
-       * information.
-       */
-      if (need_ns_suffix)
+      charsprinted = 0;
+      ns_buf[0] = 0;
+    }
+  
+  if (kind != '@')
+    {
+      tm = localtime (&ts.tv_sec);
+      if (tm) 
        {
-         assert ((sizeof buf - strlen(buf)) > strlen(ns_buf));
-         strcat(buf, ns_buf);
+         char *s = do_time_format (fmt, tm, ns_buf, charsprinted);
+         if (s)
+           return s;
        }
-      return buf;
     }
-  else
+
+  /* If we get to here, either the format was %@, or we have fallen back to it
+   * because strftime failed.
+   */
+  if (1)
     {
       uintmax_t w = ts.tv_sec;
       size_t used, len, remaining;
-- 
1.5.5.1





reply via email to

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