]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
nstrftime, fprintftime: Return -1, not 0, in case of failure.
authorBruno Haible <bruno@clisp.org>
Fri, 31 Oct 2025 22:36:27 +0000 (23:36 +0100)
committerBruno Haible <bruno@clisp.org>
Fri, 31 Oct 2025 22:36:27 +0000 (23:36 +0100)
* lib/strftime.h (nstrftime, c_nstrftime): Change return type to
ptrdiff_t.
* lib/strftime.c (retval_t): New type.
(FAILURE): New macro.
(width_add): Return FAILURE instead of 0.
(my_strftime): Change return type to retval_t.
(__strftime_internal): Likewise. Change some local variables to
retval_t. Handle failure of recursive __strftime_internal invocation.
Return FAILURE instead of 0.
* lib/fprintftime.h (fprintftime): Update specification.
* tests/test-nstrftime.h (FUNC_CHECKED): Change return type to
ptrdiff_t.
(posixtm_test, tzalloc_test, quarter_test, errno_test, locales_test):
Update.
* tests/test-nstrftime-DE.c (main): Update.
* tests/test-nstrftime-ET.c (main): Likewise.
* tests/test-nstrftime-IR.c (main): Likewise.
* tests/test-nstrftime-TH.c (main): Likewise.
* tests/test-nstrftime-w32utf8.c (main): Likewise.
* NEWS: Mention the changes.

ChangeLog
NEWS
lib/fprintftime.h
lib/strftime.c
lib/strftime.h
tests/test-nstrftime-DE.c
tests/test-nstrftime-ET.c
tests/test-nstrftime-IR.c
tests/test-nstrftime-TH.c
tests/test-nstrftime-w32utf8.c
tests/test-nstrftime.h

index 7136e3cb576d8d44dffccb6e293e27c2e5971fce..ed88885d7d0d14263e75aa2068501f2c065e38b1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2025-10-31  Bruno Haible  <bruno@clisp.org>
+
+       nstrftime, fprintftime: Return -1, not 0, in case of failure.
+       * lib/strftime.h (nstrftime, c_nstrftime): Change return type to
+       ptrdiff_t.
+       * lib/strftime.c (retval_t): New type.
+       (FAILURE): New macro.
+       (width_add): Return FAILURE instead of 0.
+       (my_strftime): Change return type to retval_t.
+       (__strftime_internal): Likewise. Change some local variables to
+       retval_t. Handle failure of recursive __strftime_internal invocation.
+       Return FAILURE instead of 0.
+       * lib/fprintftime.h (fprintftime): Update specification.
+       * tests/test-nstrftime.h (FUNC_CHECKED): Change return type to
+       ptrdiff_t.
+       (posixtm_test, tzalloc_test, quarter_test, errno_test, locales_test):
+       Update.
+       * tests/test-nstrftime-DE.c (main): Update.
+       * tests/test-nstrftime-ET.c (main): Likewise.
+       * tests/test-nstrftime-IR.c (main): Likewise.
+       * tests/test-nstrftime-TH.c (main): Likewise.
+       * tests/test-nstrftime-w32utf8.c (main): Likewise.
+       * NEWS: Mention the changes.
+
 2025-10-31  Bruno Haible  <bruno@clisp.org>
 
        Fix support for Mac OS X/PowerPC G5.
diff --git a/NEWS b/NEWS
index 55dfcd95b0d4ee25a4d9b234993fb09055919f61..0f226108cae06325d6b77319fb3b63bebd109983 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -78,6 +78,13 @@ User visible incompatible changes
 
 Date        Modules         Changes
 
+2025-10-31  nstrftime       The return type changed from size_t to ptrdiff_t.
+            c-nstrftime     The return value in case of failure changed from 0
+                            to -1.
+
+2025-10-31  fprintftime     The return value in case of failure changed from 0
+                            to -1.
+
 2025-10-30  fprintftime     The return value is changed from size_t to off64_t.
 
 2025-08-05  git-merge-changelog  This module is removed.  Use the package from
index a340b86117e5e7dc78f36d667809255e565ccc6d..df6eed66b3bf7ec8c5a644226d05150ab7faa444 100644 (file)
@@ -32,7 +32,8 @@ extern "C" {
    nstrftime format string, FMT) the time data, *TM, and the ZONE
    and NANOSECONDS values.
 
-   Return the number of bytes written to the stream (always >= 0).  */
+   Return the number of bytes written to the stream.
+   Upon failure, return -1 with errno set.  */
 off64_t fprintftime (FILE *fp, char const *fmt, struct tm const *tm,
                      timezone_t zone, int nanoseconds);
 
index e29cd32666a2d8500b7342247a5299e7d970b418..afb783778ee5d82d2ae2a8aaf3ecff102796b3d0 100644 (file)
@@ -205,7 +205,8 @@ enum pad_style
 #if FPRINTFTIME
 # define STREAM_OR_CHAR_T FILE
 # define STRFTIME_ARG(x) /* empty */
-typedef off64_t byte_count_t, sbyte_count_t;
+typedef off64_t byte_count_t;
+typedef off64_t sbyte_count_t;
 # define SBYTE_COUNT_MAX 0x7fffffffffffffff
 #else
 # define STREAM_OR_CHAR_T CHAR_T
@@ -215,6 +216,18 @@ typedef ptrdiff_t sbyte_count_t;
 # define SBYTE_COUNT_MAX PTRDIFF_MAX
 #endif
 
+/* The functions strftime[_l], wcsftime[_l] defined by glibc have a return type
+   'size_t', for compatibility with POSIX, and return 0 upon failure.
+   The functions defined by Gnulib have a signed return type, and return -1
+   upon failure.  */
+#ifdef _LIBC
+typedef size_t retval_t;
+# define FAILURE 0
+#else
+typedef sbyte_count_t retval_t;
+# define FAILURE -1
+#endif
+
 #if FPRINTFTIME
 # define memset_byte(P, Len, Byte) \
    do \
@@ -249,7 +262,7 @@ typedef ptrdiff_t sbyte_count_t;
       if (_incr >= maxsize - i)                                               \
         {                                                                     \
           errno = ERANGE;                                                     \
-          return 0;                                                           \
+          return FAILURE;                                                     \
         }                                                                     \
       if (p)                                                                  \
         {                                                                     \
@@ -908,14 +921,14 @@ static CHAR_T const c_month_names[][sizeof "September"] =
 # define ns 0
 #endif
 
-static byte_count_t __strftime_internal (STREAM_OR_CHAR_T *,
-                                         STRFTIME_ARG (size_t)
-                                         const CHAR_T *, const struct tm *,
-                                         CAL_ARGS (const struct calendar *,
-                                                   struct calendar_date *)
-                                         bool, enum pad_style,
-                                         sbyte_count_t, bool *
-                                         extra_args_spec LOCALE_PARAM);
+static retval_t __strftime_internal (STREAM_OR_CHAR_T *,
+                                     STRFTIME_ARG (size_t)
+                                     const CHAR_T *, const struct tm *,
+                                     CAL_ARGS (const struct calendar *,
+                                               struct calendar_date *)
+                                     bool, enum pad_style,
+                                     sbyte_count_t, bool *
+                                     extra_args_spec LOCALE_PARAM);
 
 #if !defined _LIBC \
     && (!(HAVE_ONLY_C_LOCALE || (USE_C_LOCALE && !HAVE_STRFTIME_L)) \
@@ -1118,13 +1131,16 @@ get_tm_zone (timezone_t tz, char *ubuf, int ubufsize, int modifier,
 }
 
 /* Write information from TP into S according to the format
-   string FORMAT.  Return the humber of bytes written.
+   string FORMAT.  Return the number of bytes written.
+   Upon failure:
+     - return 0 for the functions defined by glibc,
+     - return -1 for the functions defined by Gnulib.
 
    If !FPRINTFTIME, write no more than MAXSIZE bytes (including the
    terminating '\0'), and if S is NULL do not write into S.
    To determine how many characters would be written, use NULL for S
    and (size_t) -1 for MAXSIZE.  */
-byte_count_t
+retval_t
 my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
              const CHAR_T *format,
              const struct tm *tp extra_args_spec LOCALE_PARAM)
@@ -1167,7 +1183,7 @@ libc_hidden_def (my_strftime)
    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 byte_count_t
+static retval_t
 __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
                      const CHAR_T *format,
                      const struct tm *tp,
@@ -1239,7 +1255,7 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
 # define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11))
 # define ap_len 2
 #endif
-  byte_count_t i = 0;
+  retval_t i = 0;
   STREAM_OR_CHAR_T *p = s;
   const CHAR_T *f;
 #if DO_MULTIBYTE && !defined COMPILE_WIDE
@@ -1603,13 +1619,15 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
           subwidth = -1;
         subformat_width:
           {
-            byte_count_t len =
+            retval_t len =
               __strftime_internal (NULL, STRFTIME_ARG ((size_t) -1)
                                    subfmt, tp,
                                    CAL_ARGS (cal, caldate)
                                    to_uppcase, pad, subwidth,
                                    tzset_called
                                    extra_args LOCALE_ARG);
+            if (FAILURE < 0 && len < 0)
+              return FAILURE; /* errno is set here */
             add (len, __strftime_internal (p,
                                            STRFTIME_ARG (maxsize - i)
                                            subfmt, tp,
@@ -1894,7 +1912,7 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
                     if (ckd_add (&i, i, padding) && FPRINTFTIME)
                       {
                         errno = ERANGE;
-                        return 0;
+                        return FAILURE;
                       }
                     width -= padding;
                   }
@@ -2057,7 +2075,7 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
             if (ltm.tm_yday < 0)
               {
                 errno = EOVERFLOW;
-                return 0;
+                return FAILURE;
               }
 
             /* Generate string value for T using time_t arithmetic;
@@ -2276,12 +2294,12 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
             mbstate_t st = {0};
             size_t len = __mbsrtowcs_l (p, &z, maxsize - i, &st, loc);
             if (len == (size_t) -1)
-              return 0;
+              return FAILURE;
             size_t incr = len < w ? w : len;
             if (incr >= maxsize - i)
               {
                 errno = ERANGE;
-                return 0;
+                return FAILURE;
               }
             if (p)
               {
index a76c98c9c8254af5c3240175b293724cd541a6ec..0ac129a499e6b7d1966ae83edbafa7cd868d5230 100644 (file)
@@ -72,22 +72,23 @@ extern "C" {
    Store the result, as a string with a trailing NUL character, at the
    beginning of the array __S[0..__MAXSIZE-1] and return the length of
    that string, not counting the trailing NUL, and without changing errno.
-   If unsuccessful, possibly change the array __S, set errno, and return 0;
+   If unsuccessful, possibly change the array __S, set errno, and return -1;
    errno == ERANGE means the string didn't fit.
 
    This function is like strftime, but with two more arguments:
      * __TZ instead of the local timezone information,
-     * __NS as the number of nanoseconds in the %N directive.
+     * __NS as the number of nanoseconds in the %N directive,
+   and with a failure return value of -1 instead of 0.
  */
-size_t nstrftime (char *restrict __s, size_t __maxsize,
-                  char const *__format,
-                  struct tm const *__tp, timezone_t __tz, int __ns);
+ptrdiff_t nstrftime (char *restrict __s, size_t __maxsize,
+                     char const *__format,
+                     struct tm const *__tp, timezone_t __tz, int __ns);
 
 /* Like nstrftime, except that it uses the "C" locale instead of the
    current locale.  */
-size_t c_nstrftime (char *restrict __s, size_t __maxsize,
-                    char const *__format,
-                    struct tm const *__tp, timezone_t __tz, int __ns);
+ptrdiff_t c_nstrftime (char *restrict __s, size_t __maxsize,
+                       char const *__format,
+                       struct tm const *__tp, timezone_t __tz, int __ns);
 
 #ifdef __cplusplus
 }
index e53d469f20df3fd02292264109375b1544615694..a3c8bf61f7418d860b6c386ee3c9314245dc8059 100644 (file)
@@ -68,7 +68,7 @@ main ()
 #else
 
   char buf[100];
-  size_t ret;
+  ptrdiff_t ret;
   /* Native Windows does not support dates before 1970-01-01.  */
 # if !(defined _WIN32 && !defined __CYGWIN__)
   {
index a4e0051c63227b52f1b953cafab163292719ffdb..cd9988fc1a0674f8a5ca5e97244dadfb9be84d6d 100644 (file)
@@ -71,7 +71,7 @@ main ()
 #else
 
   char buf[100];
-  size_t ret;
+  ptrdiff_t ret;
   /* Native Windows does not support dates before 1970-01-01.  */
 # if !(defined _WIN32 && !defined __CYGWIN__)
   {
index 5f0c5184899d1f09a524b2175ecdd7a825544aad..459dd73d04d633225550274b69bec8909e96ef62 100644 (file)
@@ -71,7 +71,7 @@ main ()
 #else
 
   char buf[100];
-  size_t ret;
+  ptrdiff_t ret;
   /* Native Windows does not support dates before 1970-01-01.  */
 # if !(defined _WIN32 && !defined __CYGWIN__)
   {
index b65cab4ed333080011b87d39017888c5b7fe0c29..9f87451d6e753a0943bc59ea837e0ff424d8a452 100644 (file)
@@ -65,7 +65,7 @@ main ()
 #else
 
   char buf[100];
-  size_t ret;
+  ptrdiff_t ret;
   /* Native Windows does not support dates before 1970-01-01.  */
 # if !(defined _WIN32 && !defined __CYGWIN__)
   {
index 433a983fd812845b5b6c5840b6ff7c362c4afba5..3218d5e58823eec68eeeb6946a213c6afd387f69 100644 (file)
@@ -47,7 +47,7 @@ main (int argc, char *argv[])
   if (strcmp (argv[1], "1") == 0)
     {
       /* Test a non-ASCII French month name.  */
-      size_t n = nstrftime (buf, sizeof (buf), "%B", tm, NULL, ns);
+      ptrdiff_t n = nstrftime (buf, sizeof (buf), "%B", tm, NULL, ns);
       ASSERT (n > 0);
       printf ("buf = |%s|\n", buf);
       fflush (stdout);
@@ -59,7 +59,7 @@ main (int argc, char *argv[])
   if (strcmp (argv[1], "2") == 0)
     {
       /* Test a non-ASCII Japanese weekday name.  */
-      size_t n = nstrftime (buf, sizeof (buf), "%A", tm, NULL, ns);
+      ptrdiff_t n = nstrftime (buf, sizeof (buf), "%A", tm, NULL, ns);
       ASSERT (n > 0);
       printf ("buf = |%s|\n", buf);
       fflush (stdout);
index d16255d906dfcb0e80ad84843b6faf4a77fb6ac0..387a6c3b0f9b691e0133ec9b354f16ea3d18aa7d 100644 (file)
 #define TZ_ANGLE_BRACKETS_SHOULD_WORK (200112 <= _POSIX_VERSION)
 
 /* A wrapper around FUNC that checks the return value.  */
-static size_t
+static ptrdiff_t
 FUNC_CHECKED (char *restrict s, size_t maxsize,
               char const *format,
               struct tm const *tp, timezone_t tz, int ns)
 {
-  size_t ret = FUNC (s, maxsize, format, tp, tz, ns);
+  ptrdiff_t ret = FUNC (s, maxsize, format, tp, tz, ns);
   if (ret > 0)
     {
       ASSERT (ret < maxsize);
@@ -67,12 +67,12 @@ posixtm_test (void)
       char buf[1000];
       time_t t = T[i].in;
       struct tm *tm = gmtime (&t);
-      size_t n;
+      ptrdiff_t n;
 
       ASSERT (tm);
 
       n = FUNC_CHECKED (buf, sizeof buf, T[i].fmt, tm, NULL, T[i].in_ns);
-      if (n == 0)
+      if (n == -1)
         {
           fail = 1;
           printf ("%s failed with format %s\n", FUNC_NAME, T[i].fmt);
@@ -206,7 +206,7 @@ tzalloc_test (void)
       static char const format[] = "%Y-%m-%d %H:%M:%S %z (%Z)";
       char buf[1000];
       struct tm tm;
-      size_t n;
+      ptrdiff_t n;
 
       if (!tz && tza->setting)
         {
@@ -233,7 +233,7 @@ tzalloc_test (void)
         }
 
       n = FUNC_CHECKED (buf, sizeof buf, format, &tm, tz, 0);
-      if (n == 0)
+      if (n == -1)
         {
           fail = 1;
           printf ("%s: %ld: %s failed\n", setting, lt, FUNC_NAME);
@@ -280,8 +280,8 @@ quarter_test (void)
       struct tm qtm = { .tm_mon = mon - 1 };
       char fmt[3] = {'%','q','\0'};
 
-      size_t r = FUNC_CHECKED (out, sizeof (out), fmt, &qtm, NULL, 0);
-      if (r == 0)
+      ptrdiff_t r = FUNC_CHECKED (out, sizeof (out), fmt, &qtm, NULL, 0);
+      if (r == -1)
         {
           printf ("%s(\"%%q\") failed\n", FUNC_NAME);
           fflush (stdout);
@@ -310,12 +310,12 @@ 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;
+  ptrdiff_t n;
   int bigyear = LLONG_MAX - 1900 < INT_MAX ? LLONG_MAX - 1900 : INT_MAX;
 
   errno = 0;
   n = FUNC_CHECKED (buf, 0, "%m", &tm, NULL, 0);
-  if (! (n == 0 && errno == ERANGE))
+  if (! (n == -1 && errno == ERANGE))
     {
       fail = 1;
       printf ("%s failed to set errno = ERANGE\n", FUNC_NAME);
@@ -335,7 +335,7 @@ errno_test (void)
   tm.tm_year = bigyear;
   errno = 0;
   n = FUNC_CHECKED (buf, sizeof buf, "%s", &tm, NULL, 0);
-  if (n == 0)
+  if (n == -1)
     {
       if (errno != EOVERFLOW)
         {
@@ -369,7 +369,8 @@ errno_test (void)
           else
             {
               char buf1[sizeof buf];
-              size_t n1 = FUNC_CHECKED (buf1, sizeof buf1, "%s", tmp, NULL, 0);
+              ptrdiff_t n1 =
+                FUNC_CHECKED (buf1, sizeof buf1, "%s", tmp, NULL, 0);
               buf1[n1] = '\0';
               if (! STREQ (buf, buf1))
                 {
@@ -400,7 +401,7 @@ locales_test (language_t language)
   struct tm *tm = gmtime (&t);
   int ns = 123456789;
   char buf[100];
-  size_t n;
+  ptrdiff_t n;
 
   n = FUNC_CHECKED (buf, sizeof buf, "%+4Y-%m-%d %H:%M:%S.%N", tm, NULL, ns);
   ASSERT (n > 0);