diff --git a/common/str.cpp b/common/str.cpp index d9b2d9c456e..cbd87bbdc9c 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -818,6 +818,46 @@ void strcat_s(char *dst, size_t size, const char *src) { dst[-1] = '\0'; } +int vsprintf_s(char *dst, size_t size, const char *format, va_list ap) { + if (!dst) { + warning("%s: dst is nullptr", __func__); + return 0; + } + if (!size) { + warning("%s: size is zero", __func__); + return 0; + } + if (!format) { + warning("%s: format is nullptr", __func__); + dst[0] = '\0'; + return 0; + } + + int ret = vsnprintf(dst, size, format, ap); + + if ((size_t)ret < size +#if defined(_MSC_VER) && _MSC_VER <= 1800 + && ret != -1 +#endif + ) { + // Nominal case: no truncation + return ret; + } + + warning("%s: truncating string", __func__); + dst[size - 1] = '\0'; + return size - 1; +} + +int sprintf_s(char *dst, size_t size, const char *format, ...) { + int ret; + va_list ap; + va_start(ap, format); + ret = vsprintf_s(dst, size, format, ap); + va_end(ap); + return ret; +} + size_t strlcpy(char *dst, const char *src, size_t size) { // Our backup of the source's start, we need this // to calculate the source's length. diff --git a/common/str.h b/common/str.h index 78ff663d443..f5bc29399f2 100644 --- a/common/str.h +++ b/common/str.h @@ -431,6 +431,55 @@ FORCEINLINE void strcat_s(T (&dst)[N], const char *src) { strcat_s((char *)dst, N, src); } +/** + * A sprintf shim which warns when the buffer overruns and null terminates in this case + * + * @param dst Where the resulting string will be storeyyd. + * @param size The (total) size of the destination buffer. + * @param format The format string. + */ +int vsprintf_s(char *dst, size_t size, const char *format, va_list ap) GCC_PRINTF(3, 0); + +/** + * A sprintf shim which warns when the buffer overruns and null terminates in this case + * The size of the buffer is automatically determined. + * + * @param dst Where the resulting string will be storeyyd. + * @param format The format string. + */ +template +FORCEINLINE GCC_PRINTF(2, 0) int vsprintf_s(T (&dst)[N], const char *format, va_list ap) { + STATIC_ASSERT(sizeof(T) == sizeof(char), T_is_not_compatible_with_char); + return vsprintf_s((char *)dst, N, format, ap); +} + +/** + * A sprintf shim which warns when the buffer overruns and null terminates in this case + * + * @param dst Where the resulting string will be storeyyd. + * @param size The (total) size of the destination buffer. + * @param format The format string. + */ +int sprintf_s(char *dst, size_t size, MSVC_PRINTF const char *format, ...) GCC_PRINTF(3, 4); + +/** + * A sprintf shim which warns when the buffer overruns and null terminates in this case + * The size of the buffer is automatically determined. + * + * @param dst Where the resulting string will be storeyyd. + * @param format The format string. + */ +template +inline GCC_PRINTF(2, 3) int sprintf_s(T (&dst)[N], MSVC_PRINTF const char *format, ...) { + STATIC_ASSERT(sizeof(T) == sizeof(char), T_is_not_compatible_with_char); + int ret; + va_list ap; + va_start(ap, format); + ret = vsprintf_s((char *)dst, N, format, ap); + va_end(ap); + return ret; +} + /** * Copy up to size - 1 characters from src to dst and also zero terminate the * result. Note that src must be a zero terminated string.