This aligns nstrftime better with draft POSIX 202x strftime.
* lib/nstrftime.c: Include errno.h.
(width_add, __strftime_internal): Set errno on failure,
and preserve it on success. Check for mktime_z failure.
* modules/nstrftime (Depends-on): Add errno.
* modules/nstrftime-tests (Depends-on): Add atoll, intprops.
* tests/test-nstrftime.c: Include intprops.h, limits.h.
(errno_test): New test function.
(main): Call it.
+2020-08-15 Paul Eggert <eggert@cs.ucla.edu>
+
+ nstrftime: be more predictable about errno
+ This aligns nstrftime better with draft POSIX 202x strftime.
+ * lib/nstrftime.c: Include errno.h.
+ (width_add, __strftime_internal): Set errno on failure,
+ and preserve it on success. Check for mktime_z failure.
+ * modules/nstrftime (Depends-on): Add errno.
+ * modules/nstrftime-tests (Depends-on): Add atoll, intprops.
+ * tests/test-nstrftime.c: Include intprops.h, limits.h.
+ (errno_test): New test function.
+ (main): Call it.
+
2020-08-15 Bruno Haible <bruno@clisp.org>
canonicalize: Fix a problem of the autoconf test on MSVC/clang.
#endif
#include <ctype.h>
+#include <errno.h>
#include <time.h>
#if HAVE_TZNAME && !HAVE_DECL_TZNAME
size_t _w = pad == L_('-') || width < 0 ? 0 : width; \
size_t _incr = _n < _w ? _w : _n; \
if (_incr >= maxsize - i) \
- return 0; \
+ { \
+ errno = ERANGE; \
+ return 0; \
+ } \
if (p) \
{ \
if (_n < _w) \
size_t maxsize = (size_t) -1;
#endif
+ int saved_errno = errno;
int hour12 = tp->tm_hour;
#ifdef _NL_CURRENT
/* We cannot make the following values variables since we must delay
time_t t;
ltm = *tp;
+ ltm.tm_yday = -1;
t = mktime_z (tz, <m);
+ if (ltm.tm_yday < 0)
+ {
+ errno = EOVERFLOW;
+ return 0;
+ }
/* Generate string value for T using time_t arithmetic;
this works even if sizeof (long) < sizeof (time_t). */
*p = L_('\0');
#endif
+ errno = saved_errno;
return i;
}
/* Just like strftime, but with two more arguments:
POSIX requires that strftime use the local timezone information.
Use the timezone __TZ instead. Use __NS as the number of
- nanoseconds in the %N directive. */
+ nanoseconds in the %N directive.
+
+ On error, set errno and return 0. Otherwise, return the number of
+ bytes generated (not counting the trailing NUL), preserving errno
+ if the number is 0. This errno behavior is in draft POSIX 202x
+ plus some requested changes to POSIX. */
size_t nstrftime (char *restrict, size_t, char const *, struct tm const *,
timezone_t __tz, int __ns);
Depends-on:
attribute
+errno
extensions
intprops
stdbool
tests/macros.h
Depends-on:
+atoll
+intprops
strerror
configure.ac:
#include "strftime.h"
+#include "intprops.h"
+
#include <errno.h>
+#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
return result;
}
+static int
+errno_test (void)
+{
+ int fail = 0;
+ struct tm tm = { .tm_year = 2020 - 1900, .tm_mday = 1 };
+ char buf[INT_BUFSIZE_BOUND (time_t)];
+ size_t n;
+ int bigyear = LLONG_MAX - 1900 < INT_MAX ? LLONG_MAX - 1900 : INT_MAX;
+
+ errno = 0;
+ n = nstrftime (buf, 0, "%m", &tm, 0, 0);
+ if (! (n == 0 && errno == ERANGE))
+ {
+ fail = 1;
+ printf ("nstrftime failed to set errno = ERANGE\n");
+ }
+
+ errno = 0;
+ n = nstrftime (buf, sizeof buf, "", &tm, 0, 0);
+ if (! (n == 0 && errno == 0))
+ {
+ fail = 1;
+ printf ("nstrftime failed to leave errno alone\n");
+ }
+
+
+ tm.tm_year = bigyear;
+ errno = 0;
+ n = nstrftime (buf, sizeof buf, "%s", &tm, 0, 0);
+ if (n == 0)
+ {
+ if (errno != EOVERFLOW)
+ {
+ fail = 1;
+ printf ("nstrftime failed to set errno = EOVERFLOW\n");
+ }
+
+ if (mktime_z (0, &tm) != (time_t) -1)
+ {
+ fail = 1;
+ printf ("nstrftime %%s failed but mktime_z worked for tm_year=%d\n",
+ bigyear);
+ }
+ }
+ else
+ {
+ long long int text_seconds = atoll (buf);
+ if (text_seconds <= (LLONG_MAX - 1 < TYPE_MAXIMUM (time_t)
+ ? LLONG_MAX - 1 : TYPE_MAXIMUM (time_t)))
+ {
+ time_t bigtime = text_seconds;
+ struct tm *tmp = gmtime (&bigtime);
+ if (!tmp)
+ {
+ fail = 1;
+ printf ("gmtime failed on nstrftime result\n");
+ }
+ else
+ {
+ char buf1[sizeof buf];
+ size_t n1 = nstrftime (buf1, sizeof buf1, "%s", tmp, 0, 0);
+ buf1[n1] = '\0';
+ if (! STREQ (buf, buf1))
+ {
+ fail = 1;
+ printf ("nstrftime %%s first returned '%s', then '%s'\n",
+ buf, buf1);
+ }
+ }
+ }
+ }
+
+ return fail;
+}
+
int
main (void)
{
fail |= posixtm_test ();
fail |= tzalloc_test ();
fail |= quarter_test ();
+ fail |= errno_test ();
return fail;
}