From: Bruno Haible Date: Fri, 31 Oct 2025 22:36:27 +0000 (+0100) Subject: nstrftime, fprintftime: Return -1, not 0, in case of failure. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5af90e586a2c7ce1d335d4e27492d694240c95bd;p=thirdparty%2Fgnulib.git 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. --- diff --git a/ChangeLog b/ChangeLog index 7136e3cb57..ed88885d7d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2025-10-31 Bruno Haible + + 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 Fix support for Mac OS X/PowerPC G5. diff --git a/NEWS b/NEWS index 55dfcd95b0..0f226108ca 100644 --- 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 diff --git a/lib/fprintftime.h b/lib/fprintftime.h index a340b86117..df6eed66b3 100644 --- a/lib/fprintftime.h +++ b/lib/fprintftime.h @@ -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); diff --git a/lib/strftime.c b/lib/strftime.c index e29cd32666..afb783778e 100644 --- a/lib/strftime.c +++ b/lib/strftime.c @@ -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) { diff --git a/lib/strftime.h b/lib/strftime.h index a76c98c9c8..0ac129a499 100644 --- a/lib/strftime.h +++ b/lib/strftime.h @@ -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 } diff --git a/tests/test-nstrftime-DE.c b/tests/test-nstrftime-DE.c index e53d469f20..a3c8bf61f7 100644 --- a/tests/test-nstrftime-DE.c +++ b/tests/test-nstrftime-DE.c @@ -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__) { diff --git a/tests/test-nstrftime-ET.c b/tests/test-nstrftime-ET.c index a4e0051c63..cd9988fc1a 100644 --- a/tests/test-nstrftime-ET.c +++ b/tests/test-nstrftime-ET.c @@ -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__) { diff --git a/tests/test-nstrftime-IR.c b/tests/test-nstrftime-IR.c index 5f0c518489..459dd73d04 100644 --- a/tests/test-nstrftime-IR.c +++ b/tests/test-nstrftime-IR.c @@ -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__) { diff --git a/tests/test-nstrftime-TH.c b/tests/test-nstrftime-TH.c index b65cab4ed3..9f87451d6e 100644 --- a/tests/test-nstrftime-TH.c +++ b/tests/test-nstrftime-TH.c @@ -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__) { diff --git a/tests/test-nstrftime-w32utf8.c b/tests/test-nstrftime-w32utf8.c index 433a983fd8..3218d5e588 100644 --- a/tests/test-nstrftime-w32utf8.c +++ b/tests/test-nstrftime-w32utf8.c @@ -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); diff --git a/tests/test-nstrftime.h b/tests/test-nstrftime.h index d16255d906..387a6c3b0f 100644 --- a/tests/test-nstrftime.h +++ b/tests/test-nstrftime.h @@ -23,12 +23,12 @@ #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);