bug-gnulib
[Top][All Lists]
Advanced

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

[PATCH] nstrftime: better width support for %N, %z


From: Paul Eggert
Subject: [PATCH] nstrftime: better width support for %N, %z
Date: Fri, 6 Dec 2019 14:51:31 -0800

* lib/nstrftime.c (width_add, width_add1, width_cpy):
New macros, which generalize ‘add’, ‘add1’, ‘cpy’ by adding
a new WIDTH parameter.
(add, add1, cpy): Use these macros.
(width_add): Do not treat digits == 0 as a special case,
do not pad if padding is ‘-’, and do not use a negative width.
(__strftime_internal): Redo formatting of nanoseconds and numeric
timezones to avoid buffer misuse in unusual cases, and so that
widths make more sense.  Add support for widths greater than 9 to
the %N format; they are zero filled on the right.
* tests/test-nstrftime.c (posixtm_test): Add a %12N test.
---
 ChangeLog              |  15 +++++
 lib/nstrftime.c        | 138 ++++++++++++++++++-----------------------
 tests/test-nstrftime.c |   1 +
 3 files changed, 75 insertions(+), 79 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index acfa55f56..493211d66 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2019-12-06  Paul Eggert  <address@hidden>
+
+       nstrftime: better width support for %N, %z
+       * lib/nstrftime.c (width_add, width_add1, width_cpy):
+       New macros, which generalize ‘add’, ‘add1’, ‘cpy’ by adding
+       a new WIDTH parameter.
+       (add, add1, cpy): Use these macros.
+       (width_add): Do not treat digits == 0 as a special case,
+       do not pad if padding is ‘-’, and do not use a negative width.
+       (__strftime_internal): Redo formatting of nanoseconds and numeric
+       timezones to avoid buffer misuse in unusual cases, and so that
+       widths make more sense.  Add support for widths greater than 9 to
+       the %N format; they are zero filled on the right.
+       * tests/test-nstrftime.c (posixtm_test): Add a %12N test.
+
 2019-12-05  Bruno Haible  <address@hidden>
 
        Fix compilation errors in C++ mode on Solaris 10 and Solaris 11.
diff --git a/lib/nstrftime.c b/lib/nstrftime.c
index 11ad00b10..ada767613 100644
--- a/lib/nstrftime.c
+++ b/lib/nstrftime.c
@@ -162,19 +162,20 @@ extern char *tzname[];
 # define advance(P, N) ((P) += (N))
 #endif
 
-#define add(n, f)                                                             \
+#define add(n, f) width_add (width, n, f)
+#define width_add(width, n, f)                                                \
   do                                                                          \
     {                                                                         \
       size_t _n = (n);                                                        \
-      size_t _w = (width < 0 ? 0 : width);                                    \
+      size_t _w = pad == L_('-') || width < 0 ? 0 : width;                    \
       size_t _incr = _n < _w ? _w : _n;                                       \
       if (_incr >= maxsize - i)                                               \
         return 0;                                                             \
       if (p)                                                                  \
         {                                                                     \
-          if (digits == 0 && _n < _w)                                         \
+          if (_n < _w)                                                        \
             {                                                                 \
-              size_t _delta = width - _n;                                     \
+              size_t _delta = _w - _n;                                        \
               if (pad == L_('0') || pad == L_('+'))                           \
                 memset_zero (p, _delta);                                      \
               else                                                            \
@@ -186,15 +187,17 @@ extern char *tzname[];
       i += _incr;                                                             \
     } while (0)
 
+#define add1(c) width_add1 (width, c)
 #if FPRINTFTIME
-# define add1(C) add (1, fputc (C, p))
+# define width_add1(width, c) width_add (width, 1, fputc (c, p))
 #else
-# define add1(C) add (1, *p = C)
+# define width_add1(width, c) width_add (width, 1, *p = c)
 #endif
 
+#define cpy(n, s) width_cpy (width, n, s)
 #if FPRINTFTIME
-# define cpy(n, s) \
-    add ((n),                                                                 \
+# define width_cpy(width, n, s)                                               \
+    width_add (width, n,                                                      \
      do                                                                       \
        {                                                                      \
          if (to_lowcase)                                                      \
@@ -214,8 +217,8 @@ extern char *tzname[];
      while (0)                                                                \
     )
 #else
-# define cpy(n, s)                                                            \
-    add ((n),                                                                 \
+# define width_cpy(width, n, s)                                               \
+    width_add (width, n,                                                      \
          if (to_lowcase)                                                      \
            memcpy_lowcase (p, (s), _n LOCALE_ARG);                            \
          else if (to_uppcase)                                                 \
@@ -435,9 +438,10 @@ my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t 
maxsize)
 libc_hidden_def (my_strftime)
 #endif
 
-/* Just like my_strftime, above, but with two more parameters.
-   UPCASE indicate that the result should be converted to upper case,
-   and *TZSET_CALLED indicates whether tzset has been called here.  */
+/* Just like my_strftime, above, but with more parameters.
+   UPCASE indicates that the result should be converted to upper case.
+   YR_SPEC and WIDTH specify the padding and width for the year.
+   *TZSET_CALLED indicates whether tzset has been called here.  */
 static size_t
 __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
                      const CHAR_T *format,
@@ -556,7 +560,7 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG 
(size_t maxsize)
 
   for (f = format; *f != '\0'; width = -1, f++)
     {
-      int pad = 0;              /* Padding for number ('-', '_', or 0).  */
+      int pad = 0;  /* Padding for number ('_', '-', '+', '0', or 0).  */
       int modifier;             /* Field modifier ('E', 'O', or 0).  */
       int digits = 0;           /* Max digits for numeric format.  */
       int number_value;         /* Numeric value to be printed.  */
@@ -565,7 +569,6 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG 
(size_t maxsize)
       bool always_output_a_sign; /* +/- should always be output.  */
       int tz_colon_mask;        /* Bitmask of where ':' should appear.  */
       const CHAR_T *subfmt;
-      CHAR_T sign_char;
       CHAR_T *bufp;
       CHAR_T buf[1
                  + 2 /* for the two colons in a %::z or %:::z time zone */
@@ -1035,59 +1038,34 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG 
(size_t maxsize)
           while (u_number_value != 0 || tz_colon_mask != 0);
 
         do_number_sign_and_padding:
-          if (digits < width)
-            digits = width;
-
-          sign_char = (negative_number ? L_('-')
-                       : always_output_a_sign ? L_('+')
-                       : 0);
-
-          if (pad == L_('-'))
-            {
-              if (sign_char)
-                add1 (sign_char);
-            }
-          else
-            {
-              int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0]))
-                                      - bufp) - !!sign_char;
-
-              if (padding > 0)
-                {
-                  if (pad == L_('_'))
-                    {
-                      if ((size_t) padding >= maxsize - i)
-                        return 0;
-
-                      if (p)
-                        memset_space (p, padding);
-                      i += padding;
-                      width = width > padding ? width - padding : 0;
-                      if (sign_char)
-                        add1 (sign_char);
-                    }
-                  else
-                    {
-                      if ((size_t) digits >= maxsize - i)
-                        return 0;
-
-                      if (sign_char)
-                        add1 (sign_char);
+          if (pad == 0)
+            pad = L_('0');
+          if (width < 0)
+            width = digits;
 
-                      if (p)
-                        memset_zero (p, padding);
-                      i += padding;
-                      width = 0;
-                    }
-                }
-              else
-                {
-                  if (sign_char)
-                    add1 (sign_char);
-                }
-            }
+          {
+            CHAR_T sign_char = (negative_number ? L_('-')
+                                : always_output_a_sign ? L_('+')
+                                : 0);
+            int numlen = buf + sizeof buf / sizeof buf[0] - bufp;
+            int shortage = width - !!sign_char - numlen;
+            int padding = pad == L_('-') || shortage <= 0 ? 0 : shortage;
+
+            if (sign_char)
+              {
+                if (pad == L_('_'))
+                  {
+                    if (p)
+                      memset_space (p, padding);
+                    i += padding;
+                    width -= padding;
+                  }
+                width_add1 (0, sign_char);
+                width--;
+              }
 
-          cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp);
+            cpy (numlen, bufp);
+          }
           break;
 
         case L_('F'):
@@ -1153,19 +1131,21 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG 
(size_t maxsize)
         case L_('N'):           /* GNU extension.  */
           if (modifier == L_('E'))
             goto bad_format;
-
-          number_value = ns;
-          if (width == -1)
-            width = 9;
-          else
-            {
-              /* Take an explicit width less than 9 as a precision.  */
-              int j;
-              for (j = width; j < 9; j++)
-                number_value /= 10;
-            }
-
-          DO_NUMBER (width, number_value);
+          {
+            int n = ns, ns_digits = 9;
+            if (width <= 0)
+              width = ns_digits;
+            int ndigs = ns_digits;
+            while (width < ndigs || (1 < ndigs && n % 10 == 0))
+              ndigs--, n /= 10;
+            for (int i = ndigs; 0 < i; i--)
+              buf[i - 1] = n % 10 + L_('0'), n /= 10;
+            if (!pad)
+              pad = L_('0');
+            width_cpy (0, ndigs, buf);
+            width_add (width - ndigs, 0, (void) 0);
+          }
+          break;
 #endif
 
         case L_('n'):
diff --git a/tests/test-nstrftime.c b/tests/test-nstrftime.c
index 33d92487c..7a7f22a76 100644
--- a/tests/test-nstrftime.c
+++ b/tests/test-nstrftime.c
@@ -44,6 +44,7 @@ static struct posixtm_test const T[] =
   {
     { 1300000000, 0,            "%F", "2011-03-13" },
     { 0,          10,           "%T.%N", "00:00:00.000000010" },
+    { 56,         123456789,    "%T.%12N", "00:00:56.123456789000" },
     { 0,          0,            NULL, NULL }
   };
 
-- 
2.23.0




reply via email to

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