bug-gnulib
[Top][All Lists]
Advanced

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

[PATCH] nstrftime: be more predictable about errno


From: Paul Eggert
Subject: [PATCH] nstrftime: be more predictable about errno
Date: Sat, 15 Aug 2020 16:06:45 -0700

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.
---
 ChangeLog               | 13 +++++++
 lib/nstrftime.c         | 14 +++++++-
 lib/strftime.h          |  7 +++-
 modules/nstrftime       |  1 +
 modules/nstrftime-tests |  2 ++
 tests/test-nstrftime.c  | 79 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 114 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 86b512991..eb80aba80 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+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.
diff --git a/lib/nstrftime.c b/lib/nstrftime.c
index 35b65bbbd..28bc42fd6 100644
--- a/lib/nstrftime.c
+++ b/lib/nstrftime.c
@@ -33,6 +33,7 @@
 #endif
 
 #include <ctype.h>
+#include <errno.h>
 #include <time.h>
 
 #if HAVE_TZNAME && !HAVE_DECL_TZNAME
@@ -162,7 +163,10 @@ extern char *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)                                                        \
@@ -447,6 +451,7 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG 
(size_t maxsize)
   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
@@ -1186,7 +1191,13 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG 
(size_t maxsize)
             time_t t;
 
             ltm = *tp;
+            ltm.tm_yday = -1;
             t = mktime_z (tz, &ltm);
+            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).  */
@@ -1484,5 +1495,6 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG 
(size_t maxsize)
     *p = L_('\0');
 #endif
 
+  errno = saved_errno;
   return i;
 }
diff --git a/lib/strftime.h b/lib/strftime.h
index e85016315..fe0c4195a 100644
--- a/lib/strftime.h
+++ b/lib/strftime.h
@@ -24,7 +24,12 @@ extern "C" {
 /* 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);
 
diff --git a/modules/nstrftime b/modules/nstrftime
index 7ff82896b..9db0c7521 100644
--- a/modules/nstrftime
+++ b/modules/nstrftime
@@ -9,6 +9,7 @@ m4/nstrftime.m4
 
 Depends-on:
 attribute
+errno
 extensions
 intprops
 stdbool
diff --git a/modules/nstrftime-tests b/modules/nstrftime-tests
index 708b510d2..4089771e0 100644
--- a/modules/nstrftime-tests
+++ b/modules/nstrftime-tests
@@ -3,6 +3,8 @@ tests/test-nstrftime.c
 tests/macros.h
 
 Depends-on:
+atoll
+intprops
 strerror
 
 configure.ac:
diff --git a/tests/test-nstrftime.c b/tests/test-nstrftime.c
index cb9e2d6ad..63864fc83 100644
--- a/tests/test-nstrftime.c
+++ b/tests/test-nstrftime.c
@@ -20,7 +20,10 @@
 
 #include "strftime.h"
 
+#include "intprops.h"
+
 #include <errno.h>
+#include <limits.h>
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
@@ -247,6 +250,81 @@ quarter_test (void)
   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)
 {
@@ -254,6 +332,7 @@ main (void)
   fail |= posixtm_test ();
   fail |= tzalloc_test ();
   fail |= quarter_test ();
+  fail |= errno_test ();
   return fail;
 }
 
-- 
2.17.1




reply via email to

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