diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 3c57e2f0314..bfc36ae03ac 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -106,6 +106,22 @@ extern DL_IMPORT(void) PyErr_SetInterrupt(void); extern DL_IMPORT(void) PyErr_SyntaxLocation(char *, int); extern DL_IMPORT(PyObject *) PyErr_ProgramText(char *, int); +/* 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 */ +#if defined(MS_WIN32) && !defined(HAVE_SNPRINTF) +# define HAVE_SNPRINTF +# define snprintf _snprintf +# define vsnprintf _vsnprintf +#endif +#ifndef HAVE_SNPRINTF +extern DL_IMPORT(int) PyOS_snprintf(char *str, size_t size, const char *format, ...); +extern DL_IMPORT(int) PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va); +#else +# define PyOS_vsnprintf vsnprintf +# define PyOS_snprintf snprintf +#endif #ifdef __cplusplus } diff --git a/Makefile.pre.in b/Makefile.pre.in index 3ca3397abd9..9fbf29d8f0c 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -214,6 +214,7 @@ PYTHON_OBJS= \ Python/marshal.o \ Python/modsupport.o \ Python/mystrtoul.o \ + Python/mysnprintf.o \ Python/pyfpe.o \ Python/pystate.o \ Python/pythonrun.o \ diff --git a/Python/modsupport.c b/Python/modsupport.c index 08685898da2..eb0818cc95c 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -26,8 +26,8 @@ char *_Py_PackageContext = NULL; */ static char api_version_warning[] = -"WARNING: Python C API version mismatch for module %s:\n\ - This Python has API version %d, module %s has version %d.\n"; +"Python C API version mismatch for module %.100s:\ + This Python has API version %d, module %.100s has version %d."; PyObject * Py_InitModule4(char *name, PyMethodDef *methods, char *doc, @@ -37,9 +37,15 @@ Py_InitModule4(char *name, PyMethodDef *methods, char *doc, PyMethodDef *ml; if (!Py_IsInitialized()) Py_FatalError("Interpreter not initialized (version mismatch?)"); - if (module_api_version != PYTHON_API_VERSION) - fprintf(stderr, api_version_warning, - name, PYTHON_API_VERSION, name, module_api_version); + if (module_api_version != PYTHON_API_VERSION) { + char message[512]; + PyOS_snprintf(message, sizeof(message), + api_version_warning, name, + PYTHON_API_VERSION, name, + module_api_version); + if (PyErr_Warn(PyExc_RuntimeWarning, message)) + return NULL; + } if (_Py_PackageContext != NULL) { char *p = strrchr(_Py_PackageContext, '.'); if (p != NULL && strcmp(name, p+1) == 0) { diff --git a/Python/mysnprintf.c b/Python/mysnprintf.c new file mode 100644 index 00000000000..e6c3fcedc66 --- /dev/null +++ b/Python/mysnprintf.c @@ -0,0 +1,96 @@ + +#include "Python.h" + +/* snprintf() emulation for platforms which don't have it (yet). + + Return value + + The number of characters printed (not including the trailing + `\0' used to end output to strings) or a negative number in + case of an error. + + PyOS_snprintf and PyOS_vsnprintf do not write more than size + bytes (including the trailing '\0'). + + If the output would have been truncated, they return the number + of characters (excluding the trailing '\0') which would have + been written to the final string if enough space had been + available. This is inline with the C99 standard. + +*/ + +#include + +#ifndef HAVE_SNPRINTF + +static +int myvsnprintf(char *str, size_t size, const char *format, va_list va) +{ + char *buffer = PyMem_Malloc(size + 512); + int len; + + if (buffer == NULL) + return -1; + len = vsprintf(buffer, format, va); + if (len < 0) { + PyMem_Free(buffer); + return len; + } + len++; + if (len > size + 512) + Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf"); + if (len > size) { + PyMem_Free(buffer); + return len - 1; + } + memcpy(str, buffer, len); + PyMem_Free(buffer); + return len - 1; +} + +int PyOS_snprintf(char *str, size_t size, const char *format, ...) +{ + int rc; + va_list va; + + va_start(va, format); + rc = myvsnprintf(str, size, format, va); + va_end(va); + return rc; +} + +int PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) +{ + return myvsnprintf(str, size, format, va); +} + +#else + +/* Make sure that a C API is included in the lib */ + +#ifdef PyOS_snprintf +# undef PyOS_snprintf +#endif + +int PyOS_snprintf(char *str, size_t size, const char *format, ...) +{ + int rc; + va_list va; + + va_start(va, format); + rc = vsnprintf(str, size, format, va); + va_end(va); + return rc; +} + +#ifdef PyOS_vsnprintf +# undef PyOS_vsnprintf +#endif + +int PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) +{ + return vsnprintf(str, size, format, va); +} + +#endif +