From 8b238ec54c09556eb2aa405c1741eedfd12c4a87 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 8 Aug 2020 12:19:28 -0700 Subject: [PATCH 24/47] strftime: conform better to POSIX+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The latest POSIX draft specifies errno values for some strftime errors. Implement those, plus one other one: a reliable way to determine whether 0 represents failure or buffer exhaustion (I’ll propose this to POSIX). * newstrftime.3 (RETURN VALUE): Document this. * strftime.c (strftime): Set errno according to current draft of POSIX. Also, set errno to ERANGE on overflow, and preserve errno if there is no error. (_fmt): Return NULL if %s would be out of range. Callers changed. --- newstrftime.3 | 34 +++++++++++++++++++++++++++------- strftime.c | 24 +++++++++++++++++++++++- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/newstrftime.3 b/newstrftime.3 index 63842c7..887aba5 100644 --- a/newstrftime.3 +++ b/newstrftime.3 @@ -82,13 +82,6 @@ and one other character. No more than .I maxsize bytes are placed into the array. -If the total number of resulting bytes, including the terminating -NUL character, is not more than -.IR maxsize , -.B strftime -returns the number of bytes placed into the array, not counting the -terminating NUL. -Otherwise, zero is returned and the array contents are unspecified. .PP Each conversion specification is replaced by the characters as follows which are then copied into the array. @@ -259,6 +252,33 @@ is replaced by a single %. is replaced by the locale's date and time in .BR date (1) format. +.SH "RETURN VALUE" +If the conversion is successful, +.B strftime +returns the number of bytes placed into the array, not counting the +terminating NUL; +.B errno +is unchanged if the returned value is zero. +Otherwise, +.B errno +is set to indicate the error, zero is returned, +and the array contents are unspecified. +.SH ERRORS +This function fails if: +.TP +[ERANGE] +The total number of resulting bytes, including the terminating +NUL character, is more than +.IR maxsize . +.PP +This function may fail if: +.TP +[EOVERFLOW] +The format includes an +.c %s +conversion and the number of seconds since the Epoch cannot be represented +in a +.c time_t . .SH SEE ALSO date(1), getenv(3), diff --git a/strftime.c b/strftime.c index 14cbc9a..4f871cd 100644 --- a/strftime.c +++ b/strftime.c @@ -130,10 +130,15 @@ size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *t) { char * p; + int saved_errno = errno; enum warn warn = IN_NONE; tzset(); p = _fmt(format, t, s, s + maxsize, &warn); + if (!p) { + errno = EOVERFLOW; + return 0; + } if (DEPRECATE_TWO_DIGIT_YEARS && warn != IN_NONE && getenv(YEAR_2000_NAME)) { fprintf(stderr, "\n"); @@ -146,9 +151,12 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *t) else fprintf(stderr, "all locales"); fprintf(stderr, "\n"); } - if (p == s + maxsize) + if (p == s + maxsize) { + errno = ERANGE; return 0; + } *p = '\0'; + errno = saved_errno; return p - s; } @@ -312,7 +320,21 @@ label: time_t mkt; tm = *t; + tm.tm_yday = -1; mkt = mktime(&tm); + if (mkt == (time_t) -1) { + /* Fail unless this -1 represents + a valid time. */ + struct tm tm_1; + if (!localtime_r(&mkt, &tm_1)) + return NULL; + if (!(tm.tm_year == tm_1.tm_year + && tm.tm_yday == tm_1.tm_yday + && tm.tm_hour == tm_1.tm_hour + && tm.tm_min == tm_1.tm_min + && tm.tm_sec == tm_1.tm_sec)) + return NULL; + } if (TYPE_SIGNED(time_t)) sprintf(buf, "%"PRIdMAX, (intmax_t) mkt); -- 1.8.3.1