From e822e37946f27c09953bb5733acf3b07c2db690f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2020 21:59:47 +0200 Subject: [PATCH] bpo-36020: Remove snprintf macro in pyerrors.h (GH-20889) On Windows, #include "pyerrors.h" no longer defines "snprintf" and "vsnprintf" macros. PyOS_snprintf() and PyOS_vsnprintf() should be used to get portable behavior. Replace snprintf() calls with PyOS_snprintf() and replace vsnprintf() calls with PyOS_vsnprintf(). --- Include/pyerrors.h | 17 ++--------- .../2020-06-15-16-46-01.bpo-36020.djI6jw.rst | 2 ++ Modules/_ctypes/callbacks.c | 2 +- Modules/socketmodule.c | 7 ++--- Parser/tokenizer.c | 2 +- Python/mysnprintf.c | 29 ++++++++++++------- 6 files changed, 27 insertions(+), 32 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 399bb7c3a6f..979a26ba68a 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -4,6 +4,8 @@ extern "C" { #endif +#include // va_list + /* Error handling definitions */ PyAPI_FUNC(void) PyErr_SetNone(PyObject *); @@ -307,21 +309,6 @@ PyAPI_FUNC(int) PyUnicodeTranslateError_SetReason( const char *reason /* UTF-8 encoded string */ ); -/* These APIs aren't really part of the error implementation, but - often needed to format error messages; the native C lib APIs are - not available on all platforms, which is why we provide emulations - for those platforms in Python/mysnprintf.c, - WARNING: The return value of snprintf varies across platforms; do - not rely on any particular behavior; eventually the C99 defn may - be reliable. -*/ -#if defined(MS_WIN32) && !defined(HAVE_SNPRINTF) -# define HAVE_SNPRINTF -# define snprintf _snprintf -# define vsnprintf _vsnprintf -#endif - -#include PyAPI_FUNC(int) PyOS_snprintf(char *str, size_t size, const char *format, ...) Py_GCC_ATTRIBUTE((format(printf, 3, 4))); PyAPI_FUNC(int) PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) diff --git a/Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst b/Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst new file mode 100644 index 00000000000..1f91dce4608 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-06-15-16-46-01.bpo-36020.djI6jw.rst @@ -0,0 +1,2 @@ +On Windows, ``#include "pyerrors.h"`` no longer defines ``snprintf`` and +``vsnprintf`` macros. diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 29e8fac8c94..2abfa67cdc0 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -84,7 +84,7 @@ PrintError(const char *msg, ...) va_list marker; va_start(marker, msg); - vsnprintf(buf, sizeof(buf), msg, marker); + PyOS_vsnprintf(buf, sizeof(buf), msg, marker); va_end(marker); if (f != NULL && f != Py_None) PyFile_WriteString(buf, f); diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index f60a27ebe40..db0eeaafeec 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -436,13 +436,12 @@ remove_unusable_flags(PyObject *m) #endif #ifdef MS_WIN32 -#undef EAFNOSUPPORT -#define EAFNOSUPPORT WSAEAFNOSUPPORT -#define snprintf _snprintf +# undef EAFNOSUPPORT +# define EAFNOSUPPORT WSAEAFNOSUPPORT #endif #ifndef SOCKETCLOSE -#define SOCKETCLOSE close +# define SOCKETCLOSE close #endif #if (defined(HAVE_BLUETOOTH_H) || defined(HAVE_BLUETOOTH_BLUETOOTH_H)) && !defined(__NetBSD__) && !defined(__DragonFly__) diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index d461e4e24e7..f3c1d9b20ad 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1133,7 +1133,7 @@ verify_identifier(struct tok_state *tok) Py_DECREF(s); // PyUnicode_FromFormatV() does not support %X char hex[9]; - snprintf(hex, sizeof(hex), "%04X", ch); + (void)PyOS_snprintf(hex, sizeof(hex), "%04X", ch); if (Py_UNICODE_ISPRINTABLE(ch)) { syntaxerror(tok, "invalid character '%c' (U+%s)", ch, hex); } diff --git a/Python/mysnprintf.c b/Python/mysnprintf.c index 945a81abb01..458ca14d5c6 100644 --- a/Python/mysnprintf.c +++ b/Python/mysnprintf.c @@ -1,6 +1,8 @@ #include "Python.h" -/* snprintf() wrappers. If the platform has vsnprintf, we use it, else we +/* snprintf() and vsnprintf() wrappers. + + If the platform has vsnprintf, we use it, else we emulate it in a half-hearted way. Even if the platform has it, we wrap it because platforms differ in what vsnprintf does in case the buffer is too small: C99 behavior is to return the number of characters that @@ -52,16 +54,17 @@ PyOS_snprintf(char *str, size_t size, const char *format, ...) int PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) { - int len; /* # bytes written, excluding \0 */ -#ifdef HAVE_SNPRINTF -#define _PyOS_vsnprintf_EXTRA_SPACE 1 -#else -#define _PyOS_vsnprintf_EXTRA_SPACE 512 - char *buffer; -#endif assert(str != NULL); assert(size > 0); assert(format != NULL); + + int len; /* # bytes written, excluding \0 */ +#if defined(_MSC_VER) || defined(HAVE_SNPRINTF) +# define _PyOS_vsnprintf_EXTRA_SPACE 1 +#else +# define _PyOS_vsnprintf_EXTRA_SPACE 512 + char *buffer; +#endif /* We take a size_t as input but return an int. Sanity check * our input so that it won't cause an overflow in the * vsnprintf return value or the buffer malloc size. */ @@ -70,10 +73,12 @@ PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) goto Done; } -#ifdef HAVE_SNPRINTF +#if defined(_MSC_VER) + len = _vsnprintf(str, size, format, va); +#elif defined(HAVE_SNPRINTF) len = vsnprintf(str, size, format, va); #else - /* Emulate it. */ + /* Emulate vsnprintf(). */ buffer = PyMem_MALLOC(size + _PyOS_vsnprintf_EXTRA_SPACE); if (buffer == NULL) { len = -666; @@ -96,9 +101,11 @@ PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) } PyMem_FREE(buffer); #endif + Done: - if (size > 0) + if (size > 0) { str[size-1] = '\0'; + } return len; #undef _PyOS_vsnprintf_EXTRA_SPACE }