Support POSIX.1-2024 by defining asctime_r and ctime_r only on platforms conforming to POSIX.1-2017 and earlier. * Makefile, NEWS, localtime.c, newctime.3: Document this. * asctime.c (asctime_static): New macro. (asctime_r, ctime_r): Now private static functions unless supporting older POSIX. * private.h (HAVE_DECL_ASCTIME_R): Default to 1 only if SUPPORT_POSIX2008. (SUPPORT_POSIX2008): New macro. (asctime_r, ctime_r): Declare only if SUPPORT_POSIX2008. --- Makefile | 16 ++++++++++------ NEWS | 7 +++++++ asctime.c | 19 ++++++++++++++++--- localtime.c | 4 ++-- newctime.3 | 9 +++++++++ private.h | 45 +++++++++++++++++++++++++++++++++++---------- 6 files changed, 79 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index d60aacc9..6b093013 100644 --- a/Makefile +++ b/Makefile @@ -10,10 +10,10 @@ # To affect how this Makefile works, you can run a shell script like this: # # #!/bin/sh -# make CC='gcc -std=gnu11' "$@" +# make CC='gcc -std=gnu23' "$@" # -# This example script is appropriate for a pre-2017 GNU/Linux system -# where a non-default setting is needed to support this package's use of C99. +# This example script is appropriate for a circa 2024 GNU/Linux system +# where a non-default setting enables this package's optional use of C23. # # Alternatively, you can simply edit this Makefile to tailor the following # macro definitions. @@ -53,7 +53,7 @@ DATAFORM= main LOCALTIME= Factory -# The POSIXRULES macro controls interpretation of POSIX-2017.1-like TZ +# The POSIXRULES macro controls interpretation of POSIX-like TZ # settings like TZ='EET-2EEST' that lack DST transition rules. # If POSIXRULES is '-', no template is installed; this is the default. # Any other value for POSIXRULES is obsolete and should not be relied on, as: @@ -219,6 +219,7 @@ LDLIBS= # than what POSIX specifies, assuming local time is UT. # For example, N is 252460800 on AmigaOS. # -DHAVE_DECL_ASCTIME_R=0 if <time.h> does not declare asctime_r +# on POSIX platforms predating POSIX.1-2024 # -DHAVE_DECL_ENVIRON if <unistd.h> declares 'environ' # -DHAVE_DECL_TIMEGM=0 if <time.h> does not declare timegm # -DHAVE_DIRECT_H if mkdir needs <direct.h> (MS-Windows) @@ -229,7 +230,7 @@ LDLIBS= # where LDLIBS also needs to contain -lintl on some hosts; # -DHAVE_GETTEXT=0 to avoid using gettext # -DHAVE_INCOMPATIBLE_CTIME_R if your system's time.h declares -# ctime_r and asctime_r incompatibly with the POSIX standard +# ctime_r and asctime_r incompatibly with POSIX.1-2017 # (Solaris when _POSIX_PTHREAD_SEMANTICS is not defined). # -DHAVE_INTTYPES_H=0 if <inttypes.h> does not work*+ # -DHAVE_LINK=0 if your system lacks a link function @@ -263,6 +264,8 @@ LDLIBS= # -Dssize_t=long on hosts like MS-Windows that lack ssize_t # -DSUPPORT_C89 if the tzcode library should support C89 callers+ # However, this might trigger latent bugs in C99-or-later callers. +# -DSUPPORT_POSIX2008 if the library should support older POSIX callers+ +# However, this might cause problems in POSIX.1-2024-or-later callers. # -DSUPPRESS_TZDIR to not prepend TZDIR to file names; this has # security implications and is not recommended for general use # -DTHREAD_SAFE to make localtime.c thread-safe, as POSIX requires; @@ -302,7 +305,8 @@ LDLIBS= # # * Options marked "*" can be omitted if your compiler is C23 compatible. # * Options marked "+" are obsolescent and are planned to be removed -# once the code assumes C99 or later, say in the year 2029. +# once the code assumes C99 or later (say in the year 2029) +# and POSIX.1-2024 or later (say in the year 2034). # # Select instrumentation via "make GCC_INSTRUMENT='whatever'". GCC_INSTRUMENT = \ diff --git a/NEWS b/NEWS index 2b34f78c..596a67ab 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ Unreleased, experimental changes System V names are now obsolescent. The main data form now uses %z. The code now conforms to RFC 8536 for early timestamps. + Support POSIX.1-2024, which removes asctime_r and ctime_r. Improve historical data for Portugal and possessions. Changes to data @@ -52,6 +53,12 @@ Unreleased, experimental changes does not affect behavior when reading TZif files generated by zic 2018f and later. + POSIX.1-2024 removes asctime_r and ctime_r and does not let + libraries define them, so remove them except when needed to + conform to earlier POSIX. These functions are dangerous as they + can overrun user buffers. If you still need them, add + -DSUPPORT_POSIX2008 to CFLAGS. + Release 2024a - 2024-02-01 09:28:56 -0800 diff --git a/asctime.c b/asctime.c index a40661f2..f75ec868 100644 --- a/asctime.c +++ b/asctime.c @@ -1,4 +1,4 @@ -/* asctime and asctime_r a la POSIX and ISO C, except pad years before 1000. */ +/* asctime a la ISO C. */ /* ** This file is in the public domain, so clarified as of @@ -25,8 +25,8 @@ ** leading zeroes to get the newline in the traditional place. ** The -4 ensures that we get four characters of output even if ** we call a strftime variant that produces fewer characters for some years. -** The ISO C and POSIX standards prohibit padding the year, -** but many implementations pad anyway; most likely the standards are buggy. +** This conforms to recent ISO C and POSIX standards, which say behavior +** is undefined when the year is less than 1000 or greater than 9999. */ static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n"; /* @@ -60,6 +60,18 @@ static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1]; static char buf_ctime[sizeof buf_asctime]; #endif +/* Publish asctime_r and ctime_r only when supporting older POSIX. */ +#if SUPPORT_POSIX2008 +# define asctime_static +#else +# define asctime_static static +# undef asctime_r +# undef ctime_r +# define asctime_r static_asctime_r +# define ctime_r static_ctime_r +#endif + +asctime_static char * asctime_r(struct tm const *restrict timeptr, char *restrict buf) { @@ -116,6 +128,7 @@ asctime(register const struct tm *timeptr) return asctime_r(timeptr, buf_asctime); } +asctime_static char * ctime_r(const time_t *timep, char *buf) { diff --git a/localtime.c b/localtime.c index 6d8030cd..83466575 100644 --- a/localtime.c +++ b/localtime.c @@ -1408,8 +1408,8 @@ tzfree(timezone_t sp) } /* -** NetBSD 6.1.4 has ctime_rz, but omit it because POSIX says ctime and -** ctime_r are obsolescent and have potential security problems that +** NetBSD 6.1.4 has ctime_rz, but omit it because C23 deprecates ctime and +** POSIX.1-2024 removes ctime_r. Both have potential security problems that ** ctime_rz would share. Callers can instead use localtime_rz + strftime. ** ** NetBSD 6.1.4 has tzgetname, but omit it because it doesn't work diff --git a/newctime.3 b/newctime.3 index d6abd61f..ccc19748 100644 --- a/newctime.3 +++ b/newctime.3 @@ -13,12 +13,14 @@ asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time .PP .B [[deprecated]] char *ctime(time_t const *clock); .PP +/* Only in POSIX.1-2017 and earlier. */ .B char *ctime_r(time_t const *clock, char *buf); .PP .B double difftime(time_t time1, time_t time0); .PP .B [[deprecated]] char *asctime(struct tm const *tm); .PP +/* Only in POSIX.1-2017 and earlier. */ .B "char *asctime_r(struct tm const *restrict tm," .B " char *restrict result);" .PP @@ -223,6 +225,13 @@ and functions are like their unsuffixed counterparts, except that they accept an additional argument specifying where to store the result if successful. +The +.B ctime_r +and +.B asctime_r +functions are present only on systems supporting POSIX.1-2017 and earlier, +as they are removed in POSIX.1-2024 and user code can define these +functions with other meanings. .PP The .B localtime_rz diff --git a/private.h b/private.h index 0dac6af4..67061fbb 100644 --- a/private.h +++ b/private.h @@ -69,10 +69,6 @@ ** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'. */ -#ifndef HAVE_DECL_ASCTIME_R -# define HAVE_DECL_ASCTIME_R 1 -#endif - #if !defined HAVE__GENERIC && defined __has_extension # if !__has_extension(c_generic_selections) # define HAVE__GENERIC 0 @@ -236,6 +232,31 @@ # include <unistd.h> /* for R_OK, and other POSIX goodness */ #endif /* HAVE_UNISTD_H */ +/* SUPPORT_POSIX2008 means the tzcode library should support + POSIX.1-2017-and-earlier callers in addition to the usual support for + POSIX.1-2024-and-later callers; however, this can be + incompatible with POSIX.1-2024-and-later callers. + This macro is obsolescent, and the plan is to remove it + along with any code needed only when it is nonzero. + A good time to do that might be in the year 2034. + This macro's name is SUPPORT_POSIX2008 because _POSIX_VERSION == 200809 + in POSIX.1-2017, a minor revision of POSIX.1-2008. */ +#ifndef SUPPORT_POSIX2008 +# if defined _POSIX_VERSION && _POSIX_VERSION <= 200809 +# define SUPPORT_POSIX2008 1 +# else +# define SUPPORT_POSIX2008 0 +# endif +#endif + +#ifndef HAVE_DECL_ASCTIME_R +# if SUPPORT_POSIX2008 +# define HAVE_DECL_ASCTIME_R 1 +# else +# define HAVE_DECL_ASCTIME_R 0 +# endif +#endif + #ifndef HAVE_STRFTIME_L # if _POSIX_VERSION < 200809 # define HAVE_STRFTIME_L 0 @@ -604,12 +625,8 @@ typedef time_tz tz_time_t; # undef asctime # define asctime tz_asctime -# undef asctime_r -# define asctime_r tz_asctime_r # undef ctime # define ctime tz_ctime -# undef ctime_r -# define ctime_r tz_ctime_r # undef difftime # define difftime tz_difftime # undef gmtime @@ -654,6 +671,12 @@ typedef time_tz tz_time_t; # define tzfree tz_tzfree # undef tzset # define tzset tz_tzset +# if SUPPORT_POSIX2008 +# undef asctime_r +# define asctime_r tz_asctime_r +# undef ctime_r +# define ctime_r tz_ctime_r +# endif # if HAVE_STRFTIME_L # undef strftime_l # define strftime_l tz_strftime_l @@ -679,9 +702,11 @@ typedef time_tz tz_time_t; # define DEPRECATED_IN_C23 ATTRIBUTE_DEPRECATED # endif DEPRECATED_IN_C23 char *asctime(struct tm const *); -char *asctime_r(struct tm const *restrict, char *restrict); DEPRECATED_IN_C23 char *ctime(time_t const *); +#if SUPPORT_POSIX2008 +char *asctime_r(struct tm const *restrict, char *restrict); char *ctime_r(time_t const *, char *); +#endif ATTRIBUTE_UNSEQUENCED double difftime(time_t, time_t); size_t strftime(char *restrict, size_t, char const *restrict, struct tm const *restrict); @@ -713,7 +738,7 @@ void tzset(void); time_t timegm(struct tm *); #endif -#if !HAVE_DECL_ASCTIME_R && !defined asctime_r +#if !HAVE_DECL_ASCTIME_R && !defined asctime_r && SUPPORT_POSIX2008 extern char *asctime_r(struct tm const *restrict, char *restrict); #endif -- 2.45.1