localtime: detect thread-safe alternatives and use them

- add local API `toolx_localtime()` to wrap the banned function
  `localtime()`. Used from libcurl, libtests and test servers.
- auto-detect and use `localtime_r()` where available (e.g. Linux).
  Also to support multi-threading.
- use `localtime_s()` on Windows. It requires MSVC or mingw-w64 v4+.
  Also to support multi-threading.
  Use local workaround to also support mingw-w64 v3.
- add `src/toolx` to keep internal APIs used by the curl tool and tests,
  but not by libcurl. `toolx_localtime()` is the first API in it.
- replace `localtime()` calls with `toolx_localtime()`.
  Except in examples.
- note Windows XP's default `msvcrt.dll` doesn't offer secure CRT APIs.
  XP likely needs a newer version of this DLL, or may not run.
- note that `localtime()` mirrors `gmtime()`, with the difference that
  `gmtime()`'s internal wrapper lives in curlx.

Also:
- drop redundant `int` casts.

Refs:
https://learn.microsoft.com/cpp/c-runtime-library/reference/localtime-localtime32-localtime64
https://learn.microsoft.com/cpp/c-runtime-library/reference/localtime-s-localtime32-s-localtime64-s
https://pubs.opengroup.org/onlinepubs/9799919799/functions/localtime.html
https://linux.die.net/man/3/localtime_r

Ref: #19955 (for `gmtime_r()`)
Follow-up to 54d9f060b4
Closes #19957
This commit is contained in:
Viktor Szakats 2025-12-13 04:27:41 +01:00
parent c6988f9131
commit 32454b954a
No known key found for this signature in database
GPG key ID: B5ABD165E2AEF201
23 changed files with 285 additions and 29 deletions

View file

@ -2204,6 +2204,126 @@ AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [
])
dnl CURL_CHECK_FUNC_LOCALTIME_R
dnl -------------------------------------------------
dnl Verify if localtime_r is available, prototyped, can
dnl be compiled and seems to work. If all of these are
dnl true, and usage has not been previously disallowed
dnl with shell variable curl_disallow_localtime_r, then
dnl HAVE_LOCALTIME_R will be defined.
AC_DEFUN([CURL_CHECK_FUNC_LOCALTIME_R], [
AC_REQUIRE([CURL_INCLUDES_STDLIB])dnl
AC_REQUIRE([CURL_INCLUDES_TIME])dnl
#
tst_links_localtime_r="unknown"
tst_proto_localtime_r="unknown"
tst_compi_localtime_r="unknown"
tst_works_localtime_r="unknown"
tst_allow_localtime_r="unknown"
#
AC_MSG_CHECKING([if localtime_r can be linked])
AC_LINK_IFELSE([
AC_LANG_FUNC_LINK_TRY([localtime_r])
],[
AC_MSG_RESULT([yes])
tst_links_localtime_r="yes"
],[
AC_MSG_RESULT([no])
tst_links_localtime_r="no"
])
#
if test "$tst_links_localtime_r" = "yes"; then
AC_MSG_CHECKING([if localtime_r is prototyped])
AC_EGREP_CPP([localtime_r],[
$curl_includes_time
],[
AC_MSG_RESULT([yes])
tst_proto_localtime_r="yes"
],[
AC_MSG_RESULT([no])
tst_proto_localtime_r="no"
])
fi
#
if test "$tst_proto_localtime_r" = "yes"; then
AC_MSG_CHECKING([if localtime_r is compilable])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([[
$curl_includes_time
]],[[
time_t clock = 1170352587;
struct tm result;
if(localtime_r(&clock, &result) != 0)
return 1;
(void)result;
]])
],[
AC_MSG_RESULT([yes])
tst_compi_localtime_r="yes"
],[
AC_MSG_RESULT([no])
tst_compi_localtime_r="no"
])
fi
#
dnl only do runtime verification when not cross-compiling
if test "$cross_compiling" != "yes" &&
test "$tst_compi_localtime_r" = "yes"; then
AC_MSG_CHECKING([if localtime_r seems to work])
CURL_RUN_IFELSE([
AC_LANG_PROGRAM([[
$curl_includes_stdlib
$curl_includes_time
]],[[
time_t clock = 1170352587;
struct tm *tmp = 0;
struct tm result;
tmp = localtime_r(&clock, &result);
(void)result;
if(tmp)
return 0;
else
return 1;
]])
],[
AC_MSG_RESULT([yes])
tst_works_localtime_r="yes"
],[
AC_MSG_RESULT([no])
tst_works_localtime_r="no"
])
fi
#
if test "$tst_compi_localtime_r" = "yes" &&
test "$tst_works_localtime_r" != "no"; then
AC_MSG_CHECKING([if localtime_r usage allowed])
if test "x$curl_disallow_localtime_r" != "xyes"; then
AC_MSG_RESULT([yes])
tst_allow_localtime_r="yes"
else
AC_MSG_RESULT([no])
tst_allow_localtime_r="no"
fi
fi
#
AC_MSG_CHECKING([if localtime_r might be used])
if test "$tst_links_localtime_r" = "yes" &&
test "$tst_proto_localtime_r" = "yes" &&
test "$tst_compi_localtime_r" = "yes" &&
test "$tst_allow_localtime_r" = "yes" &&
test "$tst_works_localtime_r" != "no"; then
AC_MSG_RESULT([yes])
AC_DEFINE_UNQUOTED(HAVE_LOCALTIME_R, 1,
[Define to 1 if you have a working localtime_r function.])
curl_cv_func_localtime_r="yes"
else
AC_MSG_RESULT([no])
curl_cv_func_localtime_r="no"
fi
])
dnl CURL_CHECK_FUNC_INET_NTOP
dnl -------------------------------------------------
dnl Verify if inet_ntop is available, prototyped, can