mirror of https://github.com/python/cpython
105 lines
3.3 KiB
C
105 lines
3.3 KiB
C
#include "Python.h"
|
|
|
|
/* 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 wrappers ensure that str[size-1] is always \0 upon return.
|
|
|
|
PyOS_snprintf and PyOS_vsnprintf never write more than size bytes
|
|
(including the trailing '\0') into str.
|
|
|
|
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):
|
|
|
|
When 0 <= rv < size, the output conversion was unexceptional, and
|
|
rv characters were written to str (excluding a trailing \0 byte at
|
|
str[rv]).
|
|
|
|
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.
|
|
|
|
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 rc;
|
|
va_list va;
|
|
|
|
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 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);
|
|
/* 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. */
|
|
if (size > INT_MAX - _PyOS_vsnprintf_EXTRA_SPACE) {
|
|
len = -666;
|
|
goto Done;
|
|
}
|
|
|
|
#ifdef HAVE_SNPRINTF
|
|
len = vsnprintf(str, size, format, va);
|
|
#else
|
|
/* Emulate it. */
|
|
buffer = PyMem_MALLOC(size + _PyOS_vsnprintf_EXTRA_SPACE);
|
|
if (buffer == NULL) {
|
|
len = -666;
|
|
goto Done;
|
|
}
|
|
|
|
len = vsprintf(buffer, format, va);
|
|
if (len < 0)
|
|
/* ignore the error */;
|
|
|
|
else if ((size_t)len >= size + _PyOS_vsnprintf_EXTRA_SPACE)
|
|
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);
|
|
#endif
|
|
Done:
|
|
if (size > 0)
|
|
str[size-1] = '\0';
|
|
return len;
|
|
#undef _PyOS_vsnprintf_EXTRA_SPACE
|
|
}
|