summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2019-12-06 14:48:04 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2019-12-06 14:51:13 -0800
commit1c740444c9224c81b92c4375babc73efe57e1dc9 (patch)
tree44dfa4035dd0de1bf0c7f6c37c14b5c2d54f4a7a
parent0a13f6771d98fd03688a6e202f1e623010e49c6e (diff)
downloadgnulib-1c740444c9224c81b92c4375babc73efe57e1dc9.tar.gz
gnulib-1c740444c9224c81b92c4375babc73efe57e1dc9.tar.bz2
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.
-rw-r--r--ChangeLog15
-rw-r--r--lib/nstrftime.c138
-rw-r--r--tests/test-nstrftime.c1
3 files changed, 75 insertions, 79 deletions
diff --git a/ChangeLog b/ChangeLog
index acfa55f567..493211d667 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2019-12-06 Paul Eggert <eggert@cs.ucla.edu>
+
+ 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 <bruno@clisp.org>
Fix compilation errors in C++ mode on Solaris 10 and Solaris 11.
diff --git a/lib/nstrftime.c b/lib/nstrftime.c
index 11ad00b10d..ada767613f 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 33d92487c9..7a7f22a76e 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 }
};

Return to:

Send suggestions and report system problems to the System administrator.