summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2001-12-03 00:43:33 (GMT)
committerTim Peters <tim.peters@gmail.com>2001-12-03 00:43:33 (GMT)
commitfaad5ad59005d16080aa79b593fede25c6a7457c (patch)
treea65ad8b3d7ec365c2bd363763eae7a1a9955b843
parent17d015409765d29e199d4828cb136acd3196efe6 (diff)
downloadcpython-faad5ad59005d16080aa79b593fede25c6a7457c.zip
cpython-faad5ad59005d16080aa79b593fede25c6a7457c.tar.gz
cpython-faad5ad59005d16080aa79b593fede25c6a7457c.tar.bz2
mysnprintf.c: Massive rewrite of PyOS_snprintf and PyOS_vsnprintf, to
use wrappers on all platforms, to make this as consistent as possible x- platform (in particular, make sure there's at least one \0 byte in the output buffer). Also document more of the truth about what these do. getargs.c, seterror(): Three computations of remaining buffer size were backwards, thus telling PyOS_snprintf the buffer is larger than it actually is. This matters a lot now that PyOS_snprintf ensures there's a trailing \0 byte (because it didn't get the truth about the buffer size, it was storing \0 beyond the true end of the buffer). sysmodule.c, mywrite(): Simplify, now that PyOS_vsnprintf guarantees to produce a \0 byte.
-rw-r--r--Include/pyerrors.h5
-rw-r--r--Python/getargs.c6
-rw-r--r--Python/mysnprintf.c152
-rw-r--r--Python/sysmodule.c9
4 files changed, 78 insertions, 94 deletions
diff --git a/Include/pyerrors.h b/Include/pyerrors.h
index a1dce1c..e2e2629 100644
--- a/Include/pyerrors.h
+++ b/Include/pyerrors.h
@@ -123,16 +123,11 @@ extern DL_IMPORT(PyObject *) PyErr_ProgramText(char *, int);
# define vsnprintf _vsnprintf
#endif
-#ifndef HAVE_SNPRINTF
#include <stdarg.h>
extern DL_IMPORT(int) PyOS_snprintf(char *str, size_t size, const char *format, ...)
__attribute__((format(printf, 3, 4)));
extern DL_IMPORT(int) PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va)
__attribute__((format(printf, 3, 0)));
-#else
-# define PyOS_vsnprintf vsnprintf
-# define PyOS_snprintf snprintf
-#endif
#ifdef __cplusplus
}
diff --git a/Python/getargs.c b/Python/getargs.c
index a58816f..9df2a2e 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -231,7 +231,7 @@ seterror(int iarg, char *msg, int *levels, char *fname, char *message)
p += strlen(p);
}
if (iarg != 0) {
- PyOS_snprintf(p, sizeof(buf) - (buf - p),
+ PyOS_snprintf(p, sizeof(buf) - (p - buf),
"argument %d", iarg);
i = 0;
p += strlen(p);
@@ -243,10 +243,10 @@ seterror(int iarg, char *msg, int *levels, char *fname, char *message)
}
}
else {
- PyOS_snprintf(p, sizeof(buf) - (buf - p), "argument");
+ PyOS_snprintf(p, sizeof(buf) - (p - buf), "argument");
p += strlen(p);
}
- PyOS_snprintf(p, sizeof(buf) - (buf - p), " %.256s", msg);
+ PyOS_snprintf(p, sizeof(buf) - (p - buf), " %.256s", msg);
message = buf;
}
PyErr_SetString(PyExc_TypeError, message);
diff --git a/Python/mysnprintf.c b/Python/mysnprintf.c
index 02f9291..e3b72de 100644
--- a/Python/mysnprintf.c
+++ b/Python/mysnprintf.c
@@ -1,97 +1,93 @@
-
#include "Python.h"
+#include <ctype.h>
-/* snprintf() emulation for platforms which don't have it (yet).
-
- Return value
+/* snprintf() 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
+ would have been written had the buffer not been too small, and to set
+ the last byte of the buffer to \0. At least MS _vsnprintf returns a
+ negative value instead, and fills the entire buffer with non-\0 data.
- 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.
+ The wrappers ensure that str[size-1] is always \0 upon return.
- PyOS_snprintf and PyOS_vsnprintf do not write more than size
- bytes (including the trailing '\0').
+ PyOS_snprintf and PyOS_vsnprintf never write more than size bytes
+ (including the trailing '\0') into str.
- 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.
+ If the platform doesn't have vsnprintf, and the buffer size needed to
+ avoid truncation exceeds size by more than 512, Python aborts with a
+ Py_FatalError.
-*/
+ Return value (rv):
-#include <ctype.h>
+ When 0 <= rv < size, the output conversion was unexceptional, and
+ rv characters were written to str (excluding a trailing \0 byte at
+ str[rv]).
-#ifndef HAVE_SNPRINTF
+ When rv >= size, output conversion was truncated, and a buffer of
+ size rv+1 would have been needed to avoid truncation. str[size-1]
+ is \0 in this case.
-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++;
- assert(len >= 0);
- if ((size_t)len > size + 512)
- Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf");
- if ((size_t)len > size)
- buffer[size-1] = '\0';
- else
- size = len;
- memcpy(str, buffer, size);
- PyMem_Free(buffer);
- return len - 1;
-}
+ When rv < 0, "something bad happened". str[size-1] is \0 in this
+ case too, but the rest of str is unreliable. It could be that
+ an error in format codes was detected by libc, or on platforms
+ with a non-C99 vsnprintf simply that the buffer wasn't big enough
+ to avoid truncation, or on platforms without any vsnprintf that
+ PyMem_Malloc couldn't obtain space for a temp buffer.
+
+ CAUTION: Unlike C99, str != NULL and size > 0 are required.
+*/
-int PyOS_snprintf(char *str, size_t size, const char *format, ...)
+int
+PyOS_snprintf(char *str, size_t size, const char *format, ...)
{
- int rc;
- va_list va;
+ int rc;
+ va_list va;
- va_start(va, format);
- rc = myvsnprintf(str, size, format, va);
- va_end(va);
- return rc;
+ va_start(va, format);
+ rc = PyOS_vsnprintf(str, size, format, va);
+ va_end(va);
+ return rc;
}
-int PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va)
+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
+ int len; /* # bytes written, excluding \0 */
+#ifndef HAVE_SNPRINTF
+ char *buffer;
#endif
+ assert(str != NULL);
+ assert(size > 0);
+ assert(format != NULL);
-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
+#ifdef HAVE_SNPRINTF
+ len = vsnprintf(str, size, format, va);
+#else
+ /* Emulate it. */
+ buffer = PyMem_Malloc(size + 512);
+ if (buffer == NULL) {
+ len = -666;
+ goto Done;
+ }
+
+ len = vsprintf(buffer, format, va);
+ if (len < 0)
+ /* ignore the error */;
+
+ else if ((size_t)len >= size + 512)
+ Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf");
+
+ else {
+ const size_t to_copy = (size_t)len < size ?
+ (size_t)len : size - 1;
+ assert(to_copy < size);
+ memcpy(str, buffer, to_copy);
+ str[to_copy] = '\0';
+ }
+ PyMem_Free(buffer);
+Done:
#endif
-
-int PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va)
-{
- return vsnprintf(str, size, format, va);
+ str[size-1] = '\0';
+ return len;
}
-
-#endif
-
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index faa63ab..ff49adc 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1025,18 +1025,11 @@ mywrite(char *name, FILE *fp, const char *format, va_list va)
char buffer[1001];
const int written = PyOS_vsnprintf(buffer, sizeof(buffer),
format, va);
- const int trouble = written < 0 || written >= sizeof(buffer);
- if (trouble) {
- /* Ensure there's a trailing null byte -- MS
- vsnprintf fills the buffer to the very end
- if it's not big enough. */
- buffer[sizeof(buffer) - 1] = '\0';
- }
if (PyFile_WriteString(buffer, file) != 0) {
PyErr_Clear();
fputs(buffer, fp);
}
- if (trouble) {
+ if (written < 0 || written >= sizeof(buffer)) {
const char *truncated = "... truncated";
if (PyFile_WriteString(truncated, file) != 0) {
PyErr_Clear();