From 32d18dcf47a8ac9f0e21b787bc154f8ca72dbdd8 Mon Sep 17 00:00:00 2001 From: Guillem Jover Date: Fri, 9 Feb 2024 04:32:12 +0100 Subject: [PATCH] Add explicit time32 and time64 support Handle the three potential system scenarios: - system time_t is time64 - system time_t is time32 and supports time64 - system time_t is time32 and does not support time64 Add the explicit time32 and time64 functions when necessary and map them accordingly for each of these cases. --- configure.ac | 1 + include/Makefile.am | 11 +++ include/bsd/sys/cdefs.h | 4 + include/bsd/timeconv.h | 13 +++ m4/libbsd-large.m4 | 51 ++++++++++ src/libbsd.map.in | 11 +++ src/timeconv.c | 208 ++++++++++++++++++++++++++++++++++++---- test/.gitignore | 2 + test/Makefile.am | 8 ++ test/timeconv32.c | 43 +++++++++ test/timeconv64.c | 40 ++++++++ 11 files changed, 371 insertions(+), 21 deletions(-) create mode 100644 m4/libbsd-large.m4 create mode 100644 test/timeconv32.c create mode 100644 test/timeconv64.c diff --git a/configure.ac b/configure.ac index bedc615..e516f93 100644 --- a/configure.ac +++ b/configure.ac @@ -29,6 +29,7 @@ user_CFLAGS=${CFLAGS-unset} # Checks for operating system services and capabilities. AC_USE_SYSTEM_EXTENSIONS AC_SYS_LARGEFILE +LIBBSD_SYS_TIME64 AM_PROG_AR LT_INIT diff --git a/include/Makefile.am b/include/Makefile.am index 9a75b97..52b78be 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -75,3 +75,14 @@ nobase_include_HEADERS += \ bsd/vis.h \ # EOL endif + +do_header_subst = $(AM_V_GEN) $(SED) \ + -e 's:^//\(.define LIBBSD_SYS_TIME_BITS\).*:\1 $(LIBBSD_SYS_TIME_BITS):' \ + -e 's:^//\(.define LIBBSD_SYS_HAS_TIME64\).*:\1 $(LIBBSD_SYS_HAS_TIME64):' \ + # EOL + +install-data-hook: + $(do_header_subst) <$(DESTDIR)$(includedir)/bsd/sys/cdefs.h \ + >$(DESTDIR)$(includedir)/bsd/sys/cdefs.h.new + mv $(DESTDIR)$(includedir)/bsd/sys/cdefs.h.new \ + $(DESTDIR)$(includedir)/bsd/sys/cdefs.h diff --git a/include/bsd/sys/cdefs.h b/include/bsd/sys/cdefs.h index 126f427..d2627c4 100644 --- a/include/bsd/sys/cdefs.h +++ b/include/bsd/sys/cdefs.h @@ -86,6 +86,10 @@ #define _SYS_CDEFS_H #endif +/* Define the ABI for the current system. */ +//#define LIBBSD_SYS_TIME_BITS 0 +//#define LIBBSD_SYS_HAS_TIME64 0 + #define LIBBSD_CONCAT(x, y) x ## y #define LIBBSD_STRING(x) #x diff --git a/include/bsd/timeconv.h b/include/bsd/timeconv.h index a426bd3..755c89c 100644 --- a/include/bsd/timeconv.h +++ b/include/bsd/timeconv.h @@ -58,4 +58,17 @@ time_t _long_to_time(long tlong); int _time_to_int(time_t t); time_t _int_to_time(int tint); +#if LIBBSD_SYS_TIME_BITS == 32 && LIBBSD_SYS_HAS_TIME64 +#if defined _TIME_BITS && _TIME_BITS == 64 +time_t LIBBSD_REDIRECT(_time32_to_time, (int32_t t32), _time32_to_time_time64); +int32_t LIBBSD_REDIRECT(_time_to_time32, (time_t t), _time_to_time32_time64); +time_t LIBBSD_REDIRECT(_time64_to_time, (int64_t t64), _time64_to_time_time64); +int64_t LIBBSD_REDIRECT(_time_to_time64, (time_t t), _time_to_time64_time64); +long LIBBSD_REDIRECT(_time_to_long, (time_t t), _time_to_long_time64); +time_t LIBBSD_REDIRECT(_long_to_time, (long tlong), _long_to_time_time64); +int LIBBSD_REDIRECT(_time_to_int, (time_t t), _time_to_int_time64); +time_t LIBBSD_REDIRECT(_int_to_time, (int tint), _int_to_time_time64); +#endif +#endif + #endif /* LIBBSD_TIMECONV_H */ diff --git a/m4/libbsd-large.m4 b/m4/libbsd-large.m4 new file mode 100644 index 0000000..d279111 --- /dev/null +++ b/m4/libbsd-large.m4 @@ -0,0 +1,51 @@ +# Copyright © 2024 Guillem Jover + +# LIBBSD_SYS_TIME64 +# ----------------- +# Check for availability of time64 support. +AC_DEFUN([LIBBSD_SYS_TIME64], [ + # Check the default time_t size. + AC_CHECK_SIZEOF([time_t], [], [[ +#undef _TIME_BITS +#include +]]) + AS_IF([test $ac_cv_sizeof_time_t = 8], [ + libbsd_sys_time_bits=64 + ], [ + libbsd_sys_time_bits=32 + ]) + AC_DEFINE_UNQUOTED([LIBBSD_SYS_TIME_BITS], [$libbsd_sys_time_bits], + [The number of bits for the default system time_t ABI]) + AC_SUBST([LIBBSD_SYS_TIME_BITS], [$libbsd_sys_time_bits]) + AS_UNSET([ac_cv_sizeof_time_t]) + AM_CONDITIONAL([LIBBSD_SYS_IS_TIME32], [test "$libbsd_sys_time_bits" -eq 32]) + + # Check the whether the system supports 64-bit time_t. + AC_CHECK_SIZEOF([time_t], [], [[ +#define _FILE_OFFSET_BITS 64 +#define _TIME_BITS 64 +#include +]]) + AS_IF([test $ac_cv_sizeof_time_t = 8], [ + libbsd_sys_has_time64=1 + ], [ + libbsd_sys_has_time64=0 + ]) + AC_DEFINE_UNQUOTED([LIBBSD_SYS_HAS_TIME64], [$libbsd_sys_has_time64], + [Enable if the system supports 64-bit time_t]) + AC_SUBST([LIBBSD_SYS_HAS_TIME64], [$libbsd_sys_has_time64]) + AM_CONDITIONAL([LIBBSD_SYS_HAS_TIME64], [test "$libbsd_sys_has_time64" -eq 1]) + AS_IF([test "$libbsd_sys_time_bits" = 32 && \ + test "$libbsd_sys_has_time64" -eq 1], [ + abi_time64=yes + ], [ + abi_time64=no + ]) + LIBBSD_SELECT_ABI([time64], [explicit time64 time_t support]) + + AS_IF([test $ac_cv_sys_file_offset_bits = 64 && \ + test $libbsd_sys_time_bits = 32 && \ + test $ac_cv_sizeof_time_t = 8], [ + AC_DEFINE([_TIME_BITS], [64], [Enable 64-bit time_t support]) + ]) +]) diff --git a/src/libbsd.map.in b/src/libbsd.map.in index 9670d4c..d7047dc 100644 --- a/src/libbsd.map.in +++ b/src/libbsd.map.in @@ -305,4 +305,15 @@ LIBBSD_0.12.0 { vasprintf; asprintf; #endif + +#if LIBBSD_ABI_TIMECONV && LIBBSD_ABI_TIME64 + _time32_to_time_time64; + _time_to_time32_time64; + _time64_to_time_time64; + _time_to_time64_time64; + _time_to_long_time64; + _long_to_time_time64; + _time_to_int_time64; + _int_to_time_time64; +#endif } LIBBSD_0.11.0; diff --git a/src/timeconv.c b/src/timeconv.c index 11f926e..0c122e4 100644 --- a/src/timeconv.c +++ b/src/timeconv.c @@ -32,17 +32,55 @@ #include #include -#include + +#include "local-link.h" + +#if LIBBSD_SYS_TIME_BITS == 64 +/* + * - enable time64 functions + * - symver libbsd_time64 -> 0.7 + */ +#define time32_symbol(name) +#define time64_symbol(name) \ + libbsd_strong_alias(libbsd ## name ## _time64, name) +#elif LIBBSD_SYS_TIME_BITS == 32 && LIBBSD_SYS_HAS_TIME64 +/* + * - enable time32 functions + * - enable time64 functions + * - symver libbsd_time32 -> 0.7 + * - map libbsd_time64 _time64 0.12 + */ +#define time32_symbol(name) \ + libbsd_strong_alias(libbsd ## name ## _time32, name) +#define time64_symbol(name) \ + libbsd_strong_alias(libbsd ## name ## _time64, name ## _time64) +#elif LIBBSD_SYS_TIME_BITS == 32 && !LIBBSD_SYS_HAS_TIME64 +/* + * - enable time32 functions + * - symver libbsd_time32 -> + */ +#define time32_symbol(name) \ + libbsd_strong_alias(libbsd ## name ## _time32, name) +#define time64_symbol(name) +#else +#error "Unknown time_t support" +#endif + +#if LIBBSD_SYS_HAS_TIME64 +typedef int64_t libbsd_time64_t; /* * Convert a 32 bit representation of time_t into time_t. XXX needs to * implement the 50-year rule to handle post-2038 conversions. */ -time_t -_time32_to_time(int32_t t32) +libbsd_time64_t +libbsd_time32_to_time_time64(int32_t t32); +libbsd_time64_t +libbsd_time32_to_time_time64(int32_t t32) { - return((time_t)t32); + return((libbsd_time64_t)t32); } +time64_symbol(_time32_to_time); /* * Convert time_t to a 32 bit representation. If time_t is 64 bits we can @@ -50,68 +88,196 @@ _time32_to_time(int32_t t32) * converted back to a temporally local 64 bit time_t using time32_to_time. */ int32_t -_time_to_time32(time_t t) +libbsd_time_to_time32_time64(libbsd_time64_t t); +int32_t +libbsd_time_to_time32_time64(libbsd_time64_t t) { return((int32_t)t); } +time64_symbol(_time_to_time32); /* * Convert a 64 bit representation of time_t into time_t. If time_t is * represented as 32 bits we can simply chop it and not support times * past 2038. */ -time_t -_time64_to_time(int64_t t64) +libbsd_time64_t +libbsd_time64_to_time_time64(int64_t t64); +libbsd_time64_t +libbsd_time64_to_time_time64(int64_t t64) { - return((time_t)t64); + return((libbsd_time64_t)t64); } +time64_symbol(_time64_to_time); /* * Convert time_t to a 64 bit representation. If time_t is represented * as 32 bits we simply sign-extend and do not support times past 2038. */ int64_t -_time_to_time64(time_t t) +libbsd_time_to_time64_time64(libbsd_time64_t t); +int64_t +libbsd_time_to_time64_time64(libbsd_time64_t t) { return((int64_t)t); } +time64_symbol(_time_to_time64); /* * Convert to/from 'long'. Depending on the sizeof(long) this may or * may not require using the 50-year rule. */ long -_time_to_long(time_t t) +libbsd_time_to_long_time64(libbsd_time64_t t); +long +libbsd_time_to_long_time64(libbsd_time64_t t) { if (sizeof(long) == sizeof(int64_t)) - return(_time_to_time64(t)); + return(libbsd_time_to_time64_time64(t)); return((long)t); } +time64_symbol(_time_to_long); -time_t -_long_to_time(long tlong) +libbsd_time64_t +libbsd_long_to_time_time64(long tlong); +libbsd_time64_t +libbsd_long_to_time_time64(long tlong) { if (sizeof(long) == sizeof(int32_t)) - return(_time32_to_time(tlong)); - return((time_t)tlong); + return(libbsd_time32_to_time_time64(tlong)); + return((libbsd_time64_t)tlong); } +time64_symbol(_long_to_time); /* * Convert to/from 'int'. Depending on the sizeof(int) this may or * may not require using the 50-year rule. */ int -_time_to_int(time_t t) +libbsd_time_to_int_time64(time_t t); +int +libbsd_time_to_int_time64(time_t t) { if (sizeof(int) == sizeof(int64_t)) - return(_time_to_time64(t)); + return(libbsd_time_to_time64_time64(t)); return((int)t); } +time64_symbol(_time_to_int); -time_t -_int_to_time(int tint) +libbsd_time64_t +libbsd_int_to_time_time64(int tint); +libbsd_time64_t +libbsd_int_to_time_time64(int tint) { if (sizeof(int) == sizeof(int32_t)) - return(_time32_to_time(tint)); - return((time_t)tint); + return(libbsd_time32_to_time_time64(tint)); + return((libbsd_time64_t)tint); } +time64_symbol(_int_to_time); +#endif + +#if LIBBSD_SYS_TIME_BITS == 32 +typedef int32_t libbsd_time32_t; + +libbsd_time32_t +libbsd_time32_to_time_time32(int32_t t32); +libbsd_time32_t +libbsd_time32_to_time_time32(int32_t t32) +{ + return((libbsd_time32_t)t32); +} +time32_symbol(_time32_to_time); + +/* + * Convert time_t to a 32 bit representation. If time_t is 64 bits we can + * simply chop it down. The resulting 32 bit representation can be + * converted back to a temporally local 64 bit time_t using time32_to_time. + */ +int32_t +libbsd_time_to_time32_time32(libbsd_time32_t t); +int32_t +libbsd_time_to_time32_time32(libbsd_time32_t t) +{ + return((int32_t)t); +} +time32_symbol(_time_to_time32); + +/* + * Convert a 64 bit representation of time_t into time_t. If time_t is + * represented as 32 bits we can simply chop it and not support times + * past 2038. + */ +libbsd_time32_t +libbsd_time64_to_time_time32(int64_t t64); +libbsd_time32_t +libbsd_time64_to_time_time32(int64_t t64) +{ + return((libbsd_time32_t)t64); +} +time32_symbol(_time64_to_time); + +/* + * Convert time_t to a 64 bit representation. If time_t is represented + * as 32 bits we simply sign-extend and do not support times past 2038. + */ +int64_t +libbsd_time_to_time64_time32(libbsd_time32_t t); +int64_t +libbsd_time_to_time64_time32(libbsd_time32_t t) +{ + return((int64_t)t); +} +time32_symbol(_time_to_time64); + +/* + * Convert to/from 'long'. Depending on the sizeof(long) this may or + * may not require using the 50-year rule. + */ +long +libbsd_time_to_long_time32(libbsd_time32_t t); +long +libbsd_time_to_long_time32(libbsd_time32_t t) +{ + if (sizeof(long) == sizeof(int64_t)) + return(libbsd_time_to_time64_time32(t)); + return((long)t); +} +time32_symbol(_time_to_long); + +libbsd_time32_t +libbsd_long_to_time_time32(long tlong); +libbsd_time32_t +libbsd_long_to_time_time32(long tlong) +{ + if (sizeof(long) == sizeof(int32_t)) + return(libbsd_time32_to_time_time32(tlong)); + return((libbsd_time32_t)tlong); +} +time32_symbol(_long_to_time); + +/* + * Convert to/from 'int'. Depending on the sizeof(int) this may or + * may not require using the 50-year rule. + */ +int +libbsd_time_to_int_time32(libbsd_time32_t t); +int +libbsd_time_to_int_time32(libbsd_time32_t t) +{ + if (sizeof(int) == sizeof(int64_t)) + return(libbsd_time_to_time64_time32(t)); + return((int)t); +} +time32_symbol(_time_to_int); + +libbsd_time32_t +libbsd_int_to_time_time32(int tint); +libbsd_time32_t +libbsd_int_to_time_time32(int tint) +{ + if (sizeof(int) == sizeof(int32_t)) + return(libbsd_time32_to_time_time32(tint)); + return((libbsd_time32_t)tint); +} +time32_symbol(_int_to_time); +#endif diff --git a/test/.gitignore b/test/.gitignore index a80f854..bfd5572 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -22,5 +22,7 @@ strl strmode strnstr strtonum +timeconv32 +timeconv64 vis vis-openbsd diff --git a/test/Makefile.am b/test/Makefile.am index 1c64abb..13c3309 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -182,4 +182,12 @@ check_PROGRAMS += \ # EOL endif +if LIBBSD_SYS_IS_TIME32 +check_PROGRAMS += timeconv32 +endif + +if LIBBSD_SYS_HAS_TIME64 +check_PROGRAMS += timeconv64 +endif + TESTS = $(check_SCRIPTS) $(check_PROGRAMS) diff --git a/test/timeconv32.c b/test/timeconv32.c new file mode 100644 index 0000000..549e612 --- /dev/null +++ b/test/timeconv32.c @@ -0,0 +1,43 @@ +/* + * Copyright © 2024 Guillem Jover + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef _TIME_BITS + +#include +#include +#include +#include + +int +main(int argc, char **argv) +{ + time_t t; + + t = _time64_to_time(INT64_MAX); + assert(t < INT64_MAX); + + return 0; +} diff --git a/test/timeconv64.c b/test/timeconv64.c new file mode 100644 index 0000000..d175ed2 --- /dev/null +++ b/test/timeconv64.c @@ -0,0 +1,40 @@ +/* + * Copyright © 2024 Guillem Jover + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +int +main(int argc, char **argv) +{ + time_t t; + + t = _time64_to_time(INT64_MAX); + assert(t == INT64_MAX); + + return 0; +}