[tz] [PROPOSED 2/2] strftime: conform better to POSIX+
Paul Eggert
eggert at cs.ucla.edu
Sat Aug 8 19:29:23 UTC 2020
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);
--
2.17.1
More information about the tz
mailing list