On 2024-02-05 06:37, Bruno Haible wrote:

I disagree that the ctime module is "a maintenance hassle". In my view,
its maintenance takes much less effort than the average.

That depends on what we're averaging over. (Certainly over the past day it's been more than average. :-)


Since my environment for native Windows is based on Cygwin (since that's
generally more reliable than MSYS2) and the ctime problem with $TZ is a
known one, I want to never encounter it again — even if I'm testing or
debugging a package that happens to use ctime.

If you want to keep maintaining that TZ fix then let's keep the ctime module. Still, Gnulib should not encourage ctime's use.


Also, according to your doc changes (which I'm mostly committing in your name),
a program that makes sure to use ctime() only for dates between 1900 and
2100 is fine.

I think the range is [1000, 9999]; not sure where [1900, 2100] came from.


That comment was not about SunOS. It was my attempt at understanding
and documenting why ctime_r was still not adequate. I had written:

What you had written was a logical consequence of this earlier part of the Gnulib manual:

@code{ctime_r} takes a pre-allocated buffer and length of the buffer,
and returns @code{NULL} on errors.

That was wrong on two counts. First, POSIX ctime_r doesn't have a length argument (though traditional SunOS ctime_r does). Second, POSIX ctime_r has undefined behavior on errors. Since what you wrote depended on an incorrect characterization of ctime_r, there seems little point to keeping it; as far as I know the comment is useful only on Solaris when _POSIX_PTHREAD_SEMANTICS is not defined, something that is impractical if Gnulib's 'extensions' module is used (which it should be).


26 bytes is not enough. But 50 should be enough. Can we give the advice
that it's OK to invoke it with a buffer of size 50?

Heading in that direction would require testing/examination on various systems, some of which we lack source code for, and ctime_r is going away soon; surely it's not worth the effort.

If Gnulib-using code really needs a ctime substitute we should define one with a suitable API (e.g., c_nstrftime).

For now I installed the attached. Please feel free to resurrect the SunOS ctime_r commentary if you think it useful (but it should be labeled more clearly as being for Solaris).
From 92a981f18fb8eea357336bcd6638b37ac36aaa4d Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Mon, 5 Feb 2024 22:48:29 -0800
Subject: [PATCH] ctime: improve doc

* doc/posix-functions/asctime.texi (asctime):
* doc/posix-functions/asctime_r.texi (asctime_r):
* doc/posix-functions/ctime_r.texi (ctime_r):
* doc/posix-functions/ctime.texi (ctime):
Mention locale problem of strftime more consistently.  Improve
wording.  For ctime and ctime_r, mention that localtime_r can
fail.
* doc/posix-functions/ctime.texi (ctime): Move history section
to end and spiff up a bit.
* doc/posix-functions/ctime_r.texi (ctime_r): Omit commentary that
assumes traditional SunOS ctime_r API; it was confusing and not
useful for Gnulib apps, which assume the POSIX API.
---
 ChangeLog                          | 16 ++++++++++
 doc/posix-functions/asctime.texi   |  3 +-
 doc/posix-functions/asctime_r.texi |  3 +-
 doc/posix-functions/ctime.texi     | 50 +++++++++++++++++-------------
 doc/posix-functions/ctime_r.texi   | 14 +--------
 5 files changed, 50 insertions(+), 36 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2e774f6114..4813e18c7b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2024-02-03  Paul Eggert  <[email protected]>
+
+	ctime: improve doc
+	* doc/posix-functions/asctime.texi (asctime):
+	* doc/posix-functions/asctime_r.texi (asctime_r):
+	* doc/posix-functions/ctime_r.texi (ctime_r):
+	* doc/posix-functions/ctime.texi (ctime):
+	Mention locale problem of strftime more consistently.  Improve
+	wording.  For ctime and ctime_r, mention that localtime_r can
+	fail.
+	* doc/posix-functions/ctime.texi (ctime): Move history section
+	to end and spiff up a bit.
+	* doc/posix-functions/ctime_r.texi (ctime_r): Omit commentary that
+	assumes traditional SunOS ctime_r API; it was confusing and not
+	useful for Gnulib apps, which assume the POSIX API.
+
 2024-02-05  Bruno Haible  <[email protected]>
 
 	Further improve cross-compilation for midipix.
diff --git a/doc/posix-functions/asctime.texi b/doc/posix-functions/asctime.texi
index 7b19deaa22..13aae8385a 100644
--- a/doc/posix-functions/asctime.texi
+++ b/doc/posix-functions/asctime.texi
@@ -17,8 +17,9 @@ This function is deprecated in C23.
 Likewise, POSIX says this function is obsolescent and it is planned to be
 removed in a future version.
 Portable applications can use @code{strftime} (or even @code{sprintf}) instead.
+However, @code{strftime} is locale dependent.
 @item
 This function may overflow its internal buffer if its argument
-specifies a time before the year 1000 or after the year 9999.
+specifies a year before 1000 or after 9999.
 @xref{ctime}.
 @end itemize
diff --git a/doc/posix-functions/asctime_r.texi b/doc/posix-functions/asctime_r.texi
index ae527bd197..e948d3c47b 100644
--- a/doc/posix-functions/asctime_r.texi
+++ b/doc/posix-functions/asctime_r.texi
@@ -24,8 +24,9 @@ mingw, MSVC 14.
 POSIX says this function is obsolescent and it is planned to be
 removed in a future version.
 Use the function @code{strftime} (or even @code{sprintf}) instead.
+However, @code{strftime} is locale dependent.
 @item
 This function may overflow its output buffer if its argument
-specifies a time before the year 1000 or after the year 9999.
+specifies a year before 1000 or after 9999.
 @xref{ctime}.
 @end itemize
diff --git a/doc/posix-functions/ctime.texi b/doc/posix-functions/ctime.texi
index 76c6e0ffe0..3a1aa489f4 100644
--- a/doc/posix-functions/ctime.texi
+++ b/doc/posix-functions/ctime.texi
@@ -21,28 +21,10 @@ Likewise, POSIX says this function is obsolescent and it is planned to be
 removed in a future version.
 Portable applications can use @code{localtime_r} and @code{strftime}
 (or even @code{sprintf}) instead.
+However, @code{localtime_r} can fail and @code{strftime} is locale dependent.
 @item
 This function may overflow its internal buffer if its argument
 specifies a time before the year 1000 or after the year 9999.
-
-Here is some history about this.
-This function was safe to use decades ago when @code{time_t} was narrow
-and there was no @code{strftime} or internationalization.
-Code could call @code{ctime} and then select the parts needed.
-For example, in Unix 7th Edition @file{/usr/src/cmd/ls.c} (1979):
-
-@example
-cp = ctime(&p->lmtime);
-if(p->lmtime < year)
-        printf(" %-7.7s %-4.4s ", cp+4, cp+20); else
-        printf(" %-12.12s ", cp+4);
-@end example
-
-This has well-defined behavior if @code{time_t} is only 32 bits
-and so was OK for circa 1979 platforms.
-However, today's platforms have a @code{time_t} so wide
-that the year might not be in the range [1000, 9999],
-and ISO C says that in this case the behavior of @code{ctime} is undefined.
 @item
 The @code{ctime} function need not be reentrant, and consequently is
 not required to be thread safe.  Implementations of @code{ctime}
@@ -55,5 +37,31 @@ Native Windows platforms (mingw, MSVC) support only a subset of time
 zones supported by GNU or specified by POSIX@.  @xref{tzset}.
 @end itemize
 
-A more flexible function is @code{strftime}.  However, note that it is
-locale dependent.
+Although @code{ctime} and related functions @code{asctime}, @code{asctime_r}
+and @code{ctime_r} formerly were plausible to use,
+they are now unsafe in general, and should be avoided.
+
+Decades ago when @code{time_t} was narrow
+and there was no @code{strftime} or internationalization,
+code could call these functions and then select the parts needed.
+For example, in Unix 7th Edition @file{/usr/src/cmd/ls.c} (1979):
+
+@example
+cp = ctime(&p->lmtime);
+if(p->lmtime < year)
+        printf(" %-7.7s %-4.4s ", cp+4, cp+20); else
+        printf(" %-12.12s ", cp+4);
+@end example
+
+@noindent
+This had well-defined behavior when @code{time_t} was only 32 bits
+and so was OK for circa 1979 platforms.
+
+However, today's platforms have a @code{time_t} so wide
+that the year might not be in the range [1000, 9999].
+In this case the behavior of @code{ctime} is undefined
+and some platforms behave badly, overrunning a buffer;
+and even on platforms where no buffer overrun occurs,
+the 7th Edition code can generate wrong output for out-of-range years,
+because it incorrectly assumes that every year is represented by
+exactly four digits.
diff --git a/doc/posix-functions/ctime_r.texi b/doc/posix-functions/ctime_r.texi
index be157cb643..8b3fcb4ea7 100644
--- a/doc/posix-functions/ctime_r.texi
+++ b/doc/posix-functions/ctime_r.texi
@@ -25,21 +25,9 @@ POSIX says this function is obsolescent and it is planned to be
 removed in a future version.
 Use the functions @code{localtime_r} and @code{strftime}
 (or even @code{sprintf}) instead.
+However, @code{localtime_r} can fail and @code{strftime} is locale dependent.
 @item
 This function may overflow its output buffer if its argument
 specifies a time before the year 1000 or after the year 9999.
 @xref{ctime}.
 @end itemize
-
-@code{ctime_r} takes a pre-allocated buffer and length of the buffer,
-and returns @code{NULL} on errors.
-The input buffer should be at least 26 bytes in size.  The output
-string is locale-independent.  However, years can have more than 4
-digits if @code{time_t} is sufficiently wide, so the length of the
-required output buffer is not easy to determine.  Increasing the
-buffer size when @code{ctime_r} returns @code{NULL} is not necessarily
-sufficient.  The @code{NULL} return value could mean some other error
-condition, which will not go away by increasing the buffer size.
-
-A more flexible function is @code{strftime}.  However, note that it is
-locale dependent.
-- 
2.40.1

Reply via email to