From 4cdf785cd313c3272d04c2ef7458a35d44533d8b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 7 Mar 2023 14:06:35 +0000 Subject: [PATCH] snprintf: unify fallback logic Before this patch, the `snprintf()` fallback logic for envs not supporting this function (i.e. Visual Studio 2013 and older) varied depending on build tool, and used different techniques in examples, tests and libssh2 itself. This patch aims to apply a common logic to libssh2 and examples/tests. - libssh2: use local `snprintf()` fallback with all build tools. We already had a local implementation, but only with CMake. Move that to the library as `_libssh2_snprintf()`, and map `snprintf()` to it when `HAVE_SNPRINTF` is not set. Also change the length type from `int` to `size_t`, and fix formatting. - set or detect `HAVE_SNPRINTF` in non-CMake builds. Detect in autotools. Keep existing logic in `win32/libssh2_config.h`. Always set for OS/400, NetWare and VMS, keeping existing behaviour. (OS/400 builds use a different local implementation) - examples/tests: drop the CMake-specific fallback logic and map `snprintf()` to `_snprintf()` for old MSVC versions, like we did before with other build tools. This is unsafe, but should be fine for these uses. - `win32/libssh2_config.h`: make it easier to read. Closes #812 --- configure.ac | 2 +- example/CMakeLists.txt | 2 -- example/libssh2_config_cmake.h.in | 2 -- example/subsystem_netconf.c | 6 ++--- nw/GNUmakefile | 1 + os400/libssh2_config.h | 4 +++ src/libssh2_config_cmake.h.in | 26 ------------------ src/libssh2_priv.h | 6 +++++ src/misc.c | 24 +++++++++++++++++ tests/libssh2_config_cmake.h.in | 27 ------------------- tests/session_fixture.h | 4 +++ vms/libssh2_config.h | 1 + win32/libssh2_config.h | 44 +++++++++++++++---------------- 13 files changed, 64 insertions(+), 85 deletions(-) diff --git a/configure.ac b/configure.ac index 24b851e4..489bddb2 100644 --- a/configure.ac +++ b/configure.ac @@ -341,7 +341,7 @@ case $host in ;; esac -AC_CHECK_FUNCS(gettimeofday select strtoll memset_s) +AC_CHECK_FUNCS(gettimeofday select strtoll memset_s snprintf) dnl Check for select() into ws2_32 for Msys/Mingw if test "$ac_cv_func_select" != "yes"; then diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 3dc115ef..3198c4d6 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -90,8 +90,6 @@ check_include_files(winsock2.h HAVE_WINSOCK2_H) check_symbol_exists(strcasecmp strings.h HAVE_STRCASECMP) check_symbol_exists(_stricmp string.h HAVE__STRICMP) -check_symbol_exists(snprintf stdio.h HAVE_SNPRINTF) -check_symbol_exists(_snprintf stdio.h HAVE__SNPRINTF) check_symbol_exists(__func__ "" HAVE___FUNC__) check_symbol_exists(__FUNCTION__ "" HAVE___FUNCTION__) diff --git a/example/libssh2_config_cmake.h.in b/example/libssh2_config_cmake.h.in index 12264f7f..bdcbe849 100644 --- a/example/libssh2_config_cmake.h.in +++ b/example/libssh2_config_cmake.h.in @@ -48,8 +48,6 @@ /* Functions */ #cmakedefine HAVE_STRCASECMP #cmakedefine HAVE__STRICMP -#cmakedefine HAVE_SNPRINTF -#cmakedefine HAVE__SNPRINTF /* Workaround for platforms without POSIX strcasecmp (e.g. Windows) */ #ifndef HAVE_STRCASECMP diff --git a/example/subsystem_netconf.c b/example/subsystem_netconf.c index e53cfa5f..1b17bf67 100644 --- a/example/subsystem_netconf.c +++ b/example/subsystem_netconf.c @@ -37,10 +37,8 @@ #define INADDR_NONE (in_addr_t)~0 #endif -#ifndef HAVE_SNPRINTF -# ifdef HAVE__SNPRINTF -# define snprintf _snprintf -# endif +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf #endif const char *keyfile1 = "/home/username/.ssh/id_rsa.pub"; diff --git a/nw/GNUmakefile b/nw/GNUmakefile index 8899fdd8..4098b51f 100644 --- a/nw/GNUmakefile +++ b/nw/GNUmakefile @@ -424,6 +424,7 @@ ifdef ENABLE_IPV6 @echo $(DL)#define ENABLE_IPV6 1$(DL) >> $@ endif endif + @echo $(DL)#define HAVE_SNPRINTF 1$(DL) >> $@ @echo $(DL)#define HAVE_ARPA_INET_H 1$(DL) >> $@ @echo $(DL)#define HAVE_ASSERT_H 1$(DL) >> $@ @echo $(DL)#define HAVE_CTYPE_H 1$(DL) >> $@ diff --git a/os400/libssh2_config.h b/os400/libssh2_config.h index 6ec04da1..5aa8e20b 100644 --- a/os400/libssh2_config.h +++ b/os400/libssh2_config.h @@ -212,6 +212,10 @@ /* Use OS/400 Qc3 */ #define LIBSSH2_OS400QC3 +/* Use our platform-specific local implementation: + _libssh2_os400_snprintf */ +#define HAVE_SNPRINTF 1 + /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" diff --git a/src/libssh2_config_cmake.h.in b/src/libssh2_config_cmake.h.in index 40047924..4a915815 100644 --- a/src/libssh2_config_cmake.h.in +++ b/src/libssh2_config_cmake.h.in @@ -76,29 +76,3 @@ #cmakedefine HAVE_IOCTLSOCKET_CASE #cmakedefine HAVE_SO_NONBLOCK #cmakedefine HAVE_DISABLED_NONBLOCKING - -/* snprintf not in Visual Studio CRT and _snprintf dangerously incompatible. - We provide a safe wrapper if snprintf not found */ -#ifndef HAVE_SNPRINTF -#include -#include -/* Want safe, 'n += snprintf(b + n ...)' like function. If cp_max_len is 1 -* then assume cp is pointing to a null char and do nothing. Returns number -* number of chars placed in cp excluding the trailing null char. So for -* cp_max_len > 0 the return value is always < cp_max_len; for cp_max_len -* <= 0 the return value is 0 (and no chars are written to cp). */ -static int snprintf(char * cp, int cp_max_len, const char * fmt, ...) -{ - va_list args; - int n; - - if (cp_max_len < 2) - return 0; - va_start(args, fmt); - n = vsnprintf(cp, cp_max_len, fmt, args); - va_end(args); - return (n < cp_max_len) ? n : (cp_max_len - 1); -} - -#define HAVE_SNPRINTF -#endif diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index 4bf17f9f..44bfb12e 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -126,6 +126,12 @@ #define TRUE 1 #endif +/* Use local implementation when not available */ +#if !defined(HAVE_SNPRINTF) +#define LIBSSH2_SNPRINTF +#define snprintf _libssh2_snprintf +#endif + #ifdef _MSC_VER /* "inline" keyword is valid only with C++ engine! */ #define inline __inline diff --git a/src/misc.c b/src/misc.c index 933a2041..686401c1 100644 --- a/src/misc.c +++ b/src/misc.c @@ -60,6 +60,30 @@ #include #include +/* snprintf not in Visual Studio CRT and _snprintf dangerously incompatible. + We provide a safe wrapper if snprintf not found */ +#ifdef LIBSSH2_SNPRINTF +#include + +/* Want safe, 'n += snprintf(b + n ...)' like function. If cp_max_len is 1 +* then assume cp is pointing to a null char and do nothing. Returns number +* number of chars placed in cp excluding the trailing null char. So for +* cp_max_len > 0 the return value is always < cp_max_len; for cp_max_len +* <= 0 the return value is 0 (and no chars are written to cp). */ +int _libssh2_snprintf(char *cp, size_t cp_max_len, const char *fmt, ...) +{ + va_list args; + int n; + + if(cp_max_len < 2) + return 0; + va_start(args, fmt); + n = vsnprintf(cp, cp_max_len, fmt, args); + va_end(args); + return (n < cp_max_len) ? n : (cp_max_len - 1); +} +#endif + int _libssh2_error_flags(LIBSSH2_SESSION* session, int errcode, const char *errmsg, int errflags) { diff --git a/tests/libssh2_config_cmake.h.in b/tests/libssh2_config_cmake.h.in index f75711a3..cbcdec23 100644 --- a/tests/libssh2_config_cmake.h.in +++ b/tests/libssh2_config_cmake.h.in @@ -42,30 +42,3 @@ #cmakedefine HAVE_ARPA_INET_H #cmakedefine HAVE_NETINET_IN_H #cmakedefine HAVE_WINSOCK2_H -#cmakedefine HAVE_SNPRINTF - -/* snprintf not in Visual Studio CRT and _snprintf dangerously incompatible. - We provide a safe wrapper if snprintf not found */ -#ifndef HAVE_SNPRINTF -#include -#include -/* Want safe, 'n += snprintf(b + n ...)' like function. If cp_max_len is 1 -* then assume cp is pointing to a null char and do nothing. Returns number -* number of chars placed in cp excluding the trailing null char. So for -* cp_max_len > 0 the return value is always < cp_max_len; for cp_max_len -* <= 0 the return value is 0 (and no chars are written to cp). */ -static int snprintf(char *cp, int cp_max_len, const char *fmt, ...) -{ - va_list args; - int n; - - if (cp_max_len < 2) - return 0; - va_start(args, fmt); - n = vsnprintf(cp, cp_max_len, fmt, args); - va_end(args); - return (n < cp_max_len) ? n : (cp_max_len - 1); -} - -#define HAVE_SNPRINTF -#endif diff --git a/tests/session_fixture.h b/tests/session_fixture.h index f42d1dc8..3820a5a9 100644 --- a/tests/session_fixture.h +++ b/tests/session_fixture.h @@ -40,6 +40,10 @@ #include +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + LIBSSH2_SESSION *start_session_fixture(void); void stop_session_fixture(void); void print_last_session_error(const char *function); diff --git a/vms/libssh2_config.h b/vms/libssh2_config.h index b8f73e27..45a49b5a 100644 --- a/vms/libssh2_config.h +++ b/vms/libssh2_config.h @@ -25,6 +25,7 @@ typedef unsigned int socklen_t; /* missing in headers on VMS */ #define HAVE_ARPA_INET_H #define HAVE_GETTIMEOFDAY 1 +#define HAVE_SNPRINTF 1 #define POSIX_C_SOURCE diff --git a/win32/libssh2_config.h b/win32/libssh2_config.h index 0da35c48..a497eab5 100644 --- a/win32/libssh2_config.h +++ b/win32/libssh2_config.h @@ -4,38 +4,36 @@ #ifndef WIN32 #define WIN32 #endif + #ifndef _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_DEPRECATE 1 -#endif /* _CRT_SECURE_NO_DEPRECATE */ - -#ifdef __MINGW32__ -#define HAVE_UNISTD_H -#define HAVE_INTTYPES_H -#define HAVE_SYS_TIME_H -#define HAVE_GETTIMEOFDAY -#endif /* __MINGW32__ */ +#endif #define HAVE_LIBCRYPT32 #define HAVE_WINSOCK2_H #define HAVE_IOCTLSOCKET #define HAVE_SELECT +#define HAVE_SNPRINTF -#ifdef _MSC_VER -#if _MSC_VER < 1900 -#define snprintf _snprintf -#if _MSC_VER < 1500 -#define vsnprintf _vsnprintf -#endif -#define strdup _strdup -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#endif +#ifdef __MINGW32__ +# define HAVE_UNISTD_H +# define HAVE_INTTYPES_H +# define HAVE_SYS_TIME_H +# define HAVE_GETTIMEOFDAY +#elif defined(_MSC_VER) +# if _MSC_VER < 1900 +# undef HAVE_SNPRINTF +# if _MSC_VER < 1500 +# define vsnprintf _vsnprintf +# endif +# define strdup _strdup +# define strncasecmp _strnicmp +# define strcasecmp _stricmp +# endif #else -#ifndef __MINGW32__ -#define strncasecmp strnicmp -#define strcasecmp stricmp -#endif /* __MINGW32__ */ -#endif /* _MSC_VER */ +# define strncasecmp strnicmp +# define strcasecmp stricmp +#endif /* Enable newer diffie-hellman-group-exchange-sha1 syntax */ #define LIBSSH2_DH_GEX_NEW 1