2008-05-16 James Youngman <[EMAIL PROTECTED]> 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 <[EMAIL PROTECTED]> --- 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 _______________________________________________ Findutils-patches mailing list Findutils-patches@gnu.org http://lists.gnu.org/mailman/listinfo/findutils-patches