]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
localtime: detect thread-safe alternatives and use them
authorViktor Szakats <commit@vsz.me>
Sat, 13 Dec 2025 03:27:41 +0000 (04:27 +0100)
committerViktor Szakats <commit@vsz.me>
Tue, 16 Dec 2025 13:30:06 +0000 (14:30 +0100)
- 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 54d9f060b4b0a8fb5fa006813e4db1ca5c1a07e8
Closes #19957

23 files changed:
CMake/unix-cache.cmake
CMake/win32-cache.cmake
CMakeLists.txt
configure.ac
lib/config-os400.h
lib/config-plan9.h
lib/curl_config.h.cmake
lib/curl_setup.h
m4/curl-functions.m4
projects/Windows/tmpl/curl.vcxproj
projects/generate.bat
src/Makefile.inc
src/tool_cb_dbg.c
src/toolx/tool_time.c [new file with mode: 0644]
src/toolx/tool_time.h [new file with mode: 0644]
tests/libtest/CMakeLists.txt
tests/libtest/Makefile.am
tests/libtest/Makefile.inc
tests/libtest/testtrace.c
tests/server/CMakeLists.txt
tests/server/Makefile.am
tests/server/Makefile.inc
tests/server/util.c

index 556e871a03cc0246e5bae5cf40a27598d15938b7..f3db9f0e974ce04f80249329424f6c10c77834a0 100644 (file)
@@ -169,6 +169,7 @@ else()
   set(HAVE_LINUX_TCP_H 0)
 endif()
 set(HAVE_LOCALE_H 1)
+set(HAVE_LOCALTIME_R 1)
 set(HAVE_LONGLONG 1)
 if(APPLE)
   set(HAVE_MACH_ABSOLUTE_TIME 1)
index 870acf80de190249c5208fa30db847a04314e845..e704d2195b6f6fd4d4877a1c5655c5bc4264d9cb 100644 (file)
@@ -123,6 +123,7 @@ set(HAVE_IOCTL_SIOCGIFADDR 0)
 set(HAVE_IO_H 1)
 set(HAVE_LINUX_TCP_H 0)
 set(HAVE_LOCALE_H 1)
+set(HAVE_LOCALTIME_R 0)
 set(HAVE_MEMRCHR 0)
 set(HAVE_MSG_NOSIGNAL 0)
 set(HAVE_NETDB_H 0)
index 9f447716f05f8bf02ee3f9d526b08b3c531ffc9f..3409264bd692214c55c5c6e5c0b4307aa1c29b34 100644 (file)
@@ -1570,6 +1570,7 @@ check_function_exists("getpwuid_r"    HAVE_GETPWUID_R)
 check_function_exists("geteuid"       HAVE_GETEUID)
 check_function_exists("utime"         HAVE_UTIME)
 check_symbol_exists("gmtime_r"        "stdlib.h;time.h" HAVE_GMTIME_R)
+check_symbol_exists("localtime_r"     "stdlib.h;time.h" HAVE_LOCALTIME_R)
 
 check_symbol_exists("gethostbyname_r" "netdb.h" HAVE_GETHOSTBYNAME_R)
 check_symbol_exists("gethostname"     "${CURL_INCLUDES}" HAVE_GETHOSTNAME)  # winsock2.h unistd.h proto/bsdsocket.h
index f0e439fa233abb27047679fa8ce8dc34205deb0e..2700158326349d376eceeb148802d31365daf21b 100644 (file)
@@ -4203,6 +4203,7 @@ CURL_CHECK_FUNC_GMTIME_R
 CURL_CHECK_FUNC_IOCTL
 CURL_CHECK_FUNC_IOCTLSOCKET
 CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL
+CURL_CHECK_FUNC_LOCALTIME_R
 CURL_CHECK_FUNC_MEMRCHR
 CURL_CHECK_FUNC_SIGACTION
 CURL_CHECK_FUNC_SIGINTERRUPT
index 3a48406b2a62e3bd95a4f24148b3cf059ace7ba3..ab7f670d830087309eb65d30351a0a49cd2b274e 100644 (file)
 /* Define if you have the GNU gssapi libraries */
 #undef HAVE_GSSGNU
 
+/* Define if you have the `localtime_r' function. */
+#define HAVE_LOCALTIME_R
+
 /* Define if you have the <netdb.h> header file. */
 #define HAVE_NETDB_H
 
index 68d0cf7f569fac7d93dc8f23d33454c5ff819b1d..616b8e265846294c52887b09ded8ed5a4ebde67b 100644 (file)
@@ -86,6 +86,7 @@
 #define HAVE_LIBGEN_H 1
 #define HAVE_LIBZ 1
 #define HAVE_LOCALE_H 1
+#define HAVE_LOCALTIME_R 1
 #define HAVE_LONGLONG 1
 #define HAVE_NETDB_H 1
 #define HAVE_NETINET_IN_H 1
index eaac11ec00da91b69e31007021901c6a18fcb0a8..d8d9d7ecbe58d53952b908bd0f8640d742a83d94 100644 (file)
 /* Define to 1 if you have the <locale.h> header file. */
 #cmakedefine HAVE_LOCALE_H 1
 
+/* Define to 1 if you have a working localtime_r function. */
+#cmakedefine HAVE_LOCALTIME_R 1
+
 /* Define to 1 if the compiler supports the 'long long' data type. */
 #cmakedefine HAVE_LONGLONG 1
 
index 954df47120984f47b5b8a89a1630a038cb4f8df0..7ba32f069fa8ab97b649a7294389f4d3f9fd01a9 100644 (file)
@@ -94,8 +94,7 @@
 #define _CRT_NONSTDC_NO_DEPRECATE  /* for close(), fileno(), unlink(), etc. */
 #endif
 #ifndef _CRT_SECURE_NO_WARNINGS
-#define _CRT_SECURE_NO_WARNINGS  /* for getenv(), strcpy(),
-                                    in tests: localtime(), sscanf() */
+#define _CRT_SECURE_NO_WARNINGS  /* for getenv(), strcpy(), tests: sscanf() */
 #endif
 #endif /* _MSC_VER */
 
index e872c1c23c95a1a0ac0e29f859f23e9c54027329..932b1de2d9a2a7b409aebbb3f3f6ae2ecb01a530 100644 (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
index 940b106b74c0bed1139f5978bcf15254a6e5338f..d00e0b76ab372b6ad13cf45224663fb52d833bca 100644 (file)
   </ItemDefinitionGroup>
   <ItemGroup>
 CURL_LIB_CURLX_C_FILES
+CURL_SRC_TOOLX_C_FILES
 CURL_SRC_C_FILES
   </ItemGroup>
   <ItemGroup>
 CURL_LIB_CURLX_H_FILES
+CURL_SRC_TOOLX_H_FILES
 CURL_SRC_X_H_FILES
 CURL_SRC_H_FILES
   </ItemGroup>
index 3aebff183e9c622e235b120ce527f9ba9a526f4e..e66a34aba8b14a78a5c8c3c479ed0e0e6984b6f1 100644 (file)
@@ -226,6 +226,10 @@ rem
       for /f "delims=" %%c in ('dir /b ..\lib\vtls\*.c') do call :element lib\vtls "%%c" %3
     ) else if "!var!" == "CURL_LIB_VTLS_H_FILES" (
       for /f "delims=" %%h in ('dir /b ..\lib\vtls\*.h') do call :element lib\vtls "%%h" %3
+    ) else if "!var!" == "CURL_SRC_TOOLX_C_FILES" (
+      for /f "delims=" %%c in ('dir /b ..\src\toolx\*.c') do call :element src\toolx "%%c" %3
+    ) else if "!var!" == "CURL_SRC_TOOLX_H_FILES" (
+      for /f "delims=" %%h in ('dir /b ..\src\toolx\*.h') do call :element src\toolx "%%h" %3
     ) else (
       echo.!var!>> %3
     )
index 6e5e31f806e9a231e8e07e2c79b09420f32d7efd..82c3817ce9da9a4c48c0e8cd91d0511806b7e022 100644 (file)
@@ -64,6 +64,12 @@ CURLX_HFILES = \
   ../lib/curlx/warnless.h \
   ../lib/curlx/winapi.h
 
+TOOLX_CFILES = \
+  toolx/tool_time.c
+
+TOOLX_HFILES = \
+  toolx/tool_time.h
+
 CURL_CFILES = \
   config2setopts.c \
   slist_wc.c \
@@ -107,7 +113,8 @@ CURL_CFILES = \
   tool_writeout.c \
   tool_writeout_json.c \
   tool_xattr.c \
-  var.c
+  var.c \
+  $(TOOLX_CFILES)
 
 CURL_HFILES = \
   config2setopts.h \
@@ -154,6 +161,7 @@ CURL_HFILES = \
   tool_writeout.h \
   tool_writeout_json.h \
   tool_xattr.h \
-  var.h
+  var.h \
+  $(TOOLX_HFILES)
 
 CURL_RCFILES = curl.rc
index 2301bd67f57c150385c9470eb231a81e936d578d..6a566c30b097593bad9b1b1582780ef31743890a 100644 (file)
@@ -27,6 +27,7 @@
 #include "tool_msgs.h"
 #include "tool_cb_dbg.h"
 #include "tool_util.h"
+#include "toolx/tool_time.h"
 
 static void dump(const char *timebuf, const char *idsbuf, const char *text,
                  FILE *stream, const unsigned char *ptr, size_t size,
@@ -34,7 +35,6 @@ static void dump(const char *timebuf, const char *idsbuf, const char *text,
 
 /*
  * Return the formatted HH:MM:SS for the tv_sec given.
- * NOT thread safe.
  */
 static const char *hms_for_sec(time_t tv_sec)
 {
@@ -42,10 +42,12 @@ static const char *hms_for_sec(time_t tv_sec)
   static char hms_buf[12];
 
   if(tv_sec != cached_tv_sec) {
-    /* !checksrc! disable BANNEDFUNC 1 */
-    struct tm *now = localtime(&tv_sec);  /* not thread safe either */
+    struct tm now;
+    CURLcode result = toolx_localtime(tv_sec, &now);
+    if(result)
+      memset(&now, 0, sizeof(now));
     curl_msnprintf(hms_buf, sizeof(hms_buf), "%02d:%02d:%02d",
-                   now->tm_hour, now->tm_min, now->tm_sec);
+                   now.tm_hour, now.tm_min, now.tm_sec);
     cached_tv_sec = tv_sec;
   }
   return hms_buf;
diff --git a/src/toolx/tool_time.c b/src/toolx/tool_time.c
new file mode 100644 (file)
index 0000000..affcd7a
--- /dev/null
@@ -0,0 +1,61 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "tool_time.h"
+
+#if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 3)
+#include <sec_api/time_s.h>  /* for _localtime32_s(), _localtime64_s() */
+#ifdef _USE_32BIT_TIME_T
+#define localtime_s _localtime32_s
+#else
+#define localtime_s _localtime64_s
+#endif
+#endif
+
+/*
+ * toolx_localtime() is a localtime() replacement for portability. Do not use
+ * the localtime_s(), localtime_r() or localtime() functions anywhere else but
+ * here.
+ */
+CURLcode toolx_localtime(time_t intime, struct tm *store)
+{
+#ifdef _WIN32
+  if(localtime_s(store, &intime)) /* thread-safe */
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+#elif defined(HAVE_LOCALTIME_R)
+  const struct tm *tm;
+  tm = localtime_r(&intime, store); /* thread-safe */
+  if(!tm)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+#else
+  const struct tm *tm;
+  /* !checksrc! disable BANNEDFUNC 1 */
+  tm = localtime(&intime); /* not thread-safe */
+  if(tm)
+    *store = *tm; /* copy the pointed struct to the local copy */
+  else
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+#endif
+
+  return CURLE_OK;
+}
diff --git a/src/toolx/tool_time.h b/src/toolx/tool_time.h
new file mode 100644 (file)
index 0000000..1544f67
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef HEADER_TOOLX_TOOL_TIME_H
+#define HEADER_TOOLX_TOOL_TIME_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+CURLcode toolx_localtime(time_t intime, struct tm *store);
+
+#endif /* HEADER_TOOLX_TOOL_TIME_H */
index 277d86c1880bd7d7e41346f6311b2307b4e8eeb6..6f27b42dfe7e9820f2f4b063b80316ff06ffc6b1 100644 (file)
@@ -22,7 +22,7 @@
 #
 ###########################################################################
 
-# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TESTS_C variables
+# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TOOLX_C, TESTS_C variables
 curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
 include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
 
@@ -41,10 +41,10 @@ list(APPEND TESTS_C "lib1521.c")
 
 add_custom_command(OUTPUT "${BUNDLE}.c"
   COMMAND ${PERL_EXECUTABLE} "${PROJECT_SOURCE_DIR}/scripts/mk-unity.pl"
-    --include ${UTILS_C} ${CURLX_C} --test ${TESTS_C} > "${BUNDLE}.c"
+    --include ${UTILS_C} ${CURLX_C} ${TOOLX_C} --test ${TESTS_C} > "${BUNDLE}.c"
   DEPENDS
     "${PROJECT_SOURCE_DIR}/scripts/mk-unity.pl" "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.inc"
-    ${FIRST_C} ${UTILS_C} ${CURLX_C} ${TESTS_C}
+    ${FIRST_C} ${UTILS_C} ${CURLX_C} ${TOOLX_C} ${TESTS_C}
   VERBATIM)
 
 add_executable(${BUNDLE} EXCLUDE_FROM_ALL "${BUNDLE}.c")
@@ -53,6 +53,7 @@ target_link_libraries(${BUNDLE} ${LIB_SELECTED} ${CURL_LIBS})
 target_include_directories(${BUNDLE} PRIVATE
   "${PROJECT_BINARY_DIR}/lib"            # for "curl_config.h"
   "${PROJECT_SOURCE_DIR}/lib"            # for "curl_setup.h", curlx
+  "${PROJECT_SOURCE_DIR}/src"            # for toolx
   "${CMAKE_CURRENT_SOURCE_DIR}"          # for the generated bundle source to find included test sources
 )
 target_compile_definitions(${BUNDLE} PRIVATE ${CURL_DEBUG_MACROS})
index b62a359eabefbeceb28d73174c274e3f1c11e80e..7b99ba68e7a60b4172a00596e282188cb7618861 100644 (file)
@@ -31,14 +31,16 @@ AUTOMAKE_OPTIONS = foreign nostdinc
 # $(top_srcdir)/include is for libcurl's external include files
 # $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file
 # $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files
+# $(top_srcdir)/src for toolx header files
 # $(srcdir) for the generated bundle source to find included test sources
 
 AM_CPPFLAGS = -I$(top_srcdir)/include        \
               -I$(top_builddir)/lib          \
               -I$(top_srcdir)/lib            \
+              -I$(top_srcdir)/src            \
               -I$(srcdir)
 
-# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TESTS_C variables
+# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TOOLX_C, TESTS_C variables
 include Makefile.inc
 
 EXTRA_DIST = CMakeLists.txt $(FIRST_C) $(FIRST_H) $(UTILS_C) $(UTILS_H) $(TESTS_C) \
@@ -65,8 +67,8 @@ else
 # These are part of the libcurl static lib. Add them here when linking shared.
 curlx_c_lib = $(CURLX_C)
 endif
-$(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(UTILS_C) $(curlx_c_lib) $(TESTS_C) lib1521.c
-       @PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(UTILS_C) $(curlx_c_lib) --test $(TESTS_C) lib1521.c > $(BUNDLE).c
+$(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(UTILS_C) $(curlx_c_lib) $(TOOLX_C) $(TESTS_C) lib1521.c
+       @PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(UTILS_C) $(curlx_c_lib) $(TOOLX_C) --test $(TESTS_C) lib1521.c > $(BUNDLE).c
 
 noinst_PROGRAMS = $(BUNDLE)
 LDADD = $(top_builddir)/lib/libcurl.la @LIBCURL_PC_LIBS_PRIVATE@
index a0d78f96c381a5c3214e51ef640473a4ab773b7a..ab616ca67a116a4af315844a1041f86fe0d42189 100644 (file)
@@ -46,6 +46,9 @@ CURLX_C = \
   ../../lib/curlx/warnless.c \
   ../../lib/curlx/winapi.c
 
+TOOLX_C = \
+  ../../src/toolx/tool_time.c
+
 # All libtest programs
 TESTS_C = \
   cli_ftp_upload.c \
index a541d4e55c184ae306714977afe3f1659ea56b97..7734b7d52d1241456503383f84fbde8df99e06a0 100644 (file)
@@ -23,6 +23,8 @@
  ***************************************************************************/
 #include "testtrace.h"
 
+#include <toolx/tool_time.h>
+
 struct libtest_trace_cfg debug_config;
 
 static time_t epoch_offset; /* for test time tracing */
@@ -93,7 +95,8 @@ int libtest_debug_cb(CURL *curl, curl_infotype type,
   timestr = &timebuf[0];
 
   if(trace_cfg->tracetime) {
-    struct tm *now;
+    CURLcode result;
+    struct tm now;
     struct curltime tv;
     time_t secs;
     tv = curlx_now();
@@ -102,10 +105,11 @@ int libtest_debug_cb(CURL *curl, curl_infotype type,
       known_offset = 1;
     }
     secs = epoch_offset + tv.tv_sec;
-    /* !checksrc! disable BANNEDFUNC 1 */
-    now = localtime(&secs);  /* not thread safe but we do not care */
+    result = toolx_localtime(secs, &now);
+    if(result)
+      memset(&now, 0, sizeof(now));
     curl_msnprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld ",
-                   now->tm_hour, now->tm_min, now->tm_sec, (long)tv.tv_usec);
+                   now.tm_hour, now.tm_min, now.tm_sec, (long)tv.tv_usec);
   }
 
   switch(type) {
index 15a5e975dc3d170974af308a497c1aef52cf9b76..feb63ed96ad0db131825b7b911eb7b78f17813ea 100644 (file)
 #
 ###########################################################################
 
-# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TESTS_C variables
+# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TOOLX_C, TESTS_C variables
 curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
 include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
 
 add_custom_command(OUTPUT "${BUNDLE}.c"
   COMMAND ${PERL_EXECUTABLE} "${PROJECT_SOURCE_DIR}/scripts/mk-unity.pl"
-    --include ${UTILS_C} ${CURLX_C} --test ${TESTS_C} > "${BUNDLE}.c"
+    --include ${UTILS_C} ${CURLX_C} ${TOOLX_C} --test ${TESTS_C} > "${BUNDLE}.c"
   DEPENDS
     "${PROJECT_SOURCE_DIR}/scripts/mk-unity.pl" "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.inc"
-    ${FIRST_C} ${UTILS_C} ${CURLX_C} ${TESTS_C}
+    ${FIRST_C} ${UTILS_C} ${CURLX_C} ${TOOLX_C} ${TESTS_C}
   VERBATIM)
 
 add_executable(${BUNDLE} EXCLUDE_FROM_ALL "${BUNDLE}.c")
@@ -40,6 +40,7 @@ target_link_libraries(${BUNDLE} ${CURL_NETWORK_AND_TIME_LIBS})
 target_include_directories(${BUNDLE} PRIVATE
   "${PROJECT_BINARY_DIR}/lib"            # for "curl_config.h"
   "${PROJECT_SOURCE_DIR}/lib"            # for "curl_setup.h", curlx
+  "${PROJECT_SOURCE_DIR}/src"            # for toolx
   "${CMAKE_CURRENT_SOURCE_DIR}"          # for the generated bundle source to find included test sources
 )
 set_target_properties(${BUNDLE} PROPERTIES OUTPUT_NAME "${BUNDLE}" PROJECT_LABEL "Test ${BUNDLE}" UNITY_BUILD OFF C_CLANG_TIDY "")
index 0751903a61d122f9e9729301f44c024bc80a399a..fe0a15eaec5354250f3ee341bde88eda54be25b7 100644 (file)
@@ -31,14 +31,16 @@ AUTOMAKE_OPTIONS = foreign nostdinc
 # $(top_srcdir)/include is for libcurl's external include files
 # $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file
 # $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "borrowed" files
+# $(top_srcdir)/src for toolx header files
 # $(srcdir) for the generated bundle source to find included test sources
 
 AM_CPPFLAGS = -I$(top_srcdir)/include        \
               -I$(top_builddir)/lib          \
               -I$(top_srcdir)/lib            \
+              -I$(top_srcdir)/src            \
               -I$(srcdir)
 
-# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TESTS_C variables
+# Get BUNDLE, FIRST_C, FIRST_H, UTILS_C, UTILS_H, CURLX_C, TOOLX_C, TESTS_C variables
 include Makefile.inc
 
 EXTRA_DIST = CMakeLists.txt .checksrc $(FIRST_C) $(FIRST_H) $(UTILS_C) $(UTILS_H) $(TESTS_C)
@@ -48,8 +50,8 @@ CFLAGS += @CURL_CFLAG_EXTRAS@
 # Prevent LIBS from being used for all link targets
 LIBS = $(BLANK_AT_MAKETIME)
 
-$(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(UTILS_C) $(CURLX_C) $(TESTS_C)
-       @PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(UTILS_C) $(CURLX_C) --test $(TESTS_C) > $(BUNDLE).c
+$(BUNDLE).c: $(top_srcdir)/scripts/mk-unity.pl Makefile.inc $(FIRST_C) $(UTILS_C) $(CURLX_C) $(TOOLX_C) $(TESTS_C)
+       @PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(UTILS_C) $(CURLX_C) $(TOOLX_C) --test $(TESTS_C) > $(BUNDLE).c
 
 noinst_PROGRAMS = $(BUNDLE)
 LDADD = @CURL_NETWORK_AND_TIME_LIBS@
index c335e885b255be6516a2a3967c0a0d065ddfa9ff..eb81a3494998c7c6da3ba04364421021cc13b879 100644 (file)
@@ -49,6 +49,9 @@ CURLX_C = \
   ../../lib/curlx/warnless.c \
   ../../lib/curlx/winapi.c
 
+TOOLX_C = \
+  ../../src/toolx/tool_time.c
+
 # All test servers
 TESTS_C = \
   dnsd.c \
index b021aa43dc91b7b09afcdf93112f6e6d6897ed47..043de037ae32673c320ce0c758270ec7b9466f2f 100644 (file)
@@ -23,6 +23,8 @@
  ***************************************************************************/
 #include "first.h"
 
+#include <toolx/tool_time.h>
+
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
@@ -83,8 +85,9 @@ void logmsg(const char *msg, ...)
   char buffer[2048 + 1];
   FILE *logfp;
   struct curltime tv;
+  CURLcode result;
   time_t sec;
-  struct tm *now;
+  struct tm now;
   char timebuf[50];
   static time_t epoch_offset;
   static int    known_offset;
@@ -100,12 +103,12 @@ void logmsg(const char *msg, ...)
     known_offset = 1;
   }
   sec = epoch_offset + tv.tv_sec;
-  /* !checksrc! disable BANNEDFUNC 1 */
-  now = localtime(&sec); /* not thread safe but we do not care */
+  result = toolx_localtime(sec, &now);
+  if(result)
+    memset(&now, 0, sizeof(now));
 
   snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld",
-           (int)now->tm_hour, (int)now->tm_min, (int)now->tm_sec,
-           (long)tv.tv_usec);
+           now.tm_hour, now.tm_min, now.tm_sec, (long)tv.tv_usec);
 
   va_start(ap, msg);
 #ifdef __clang__