Line data Source code
1 : #include "Python.h"
2 :
3 : /* snprintf() wrappers. If the platform has vsnprintf, we use it, else we
4 : emulate it in a half-hearted way. Even if the platform has it, we wrap
5 : it because platforms differ in what vsnprintf does in case the buffer
6 : is too small: C99 behavior is to return the number of characters that
7 : would have been written had the buffer not been too small, and to set
8 : the last byte of the buffer to \0. At least MS _vsnprintf returns a
9 : negative value instead, and fills the entire buffer with non-\0 data.
10 :
11 : The wrappers ensure that str[size-1] is always \0 upon return.
12 :
13 : PyOS_snprintf and PyOS_vsnprintf never write more than size bytes
14 : (including the trailing '\0') into str.
15 :
16 : If the platform doesn't have vsnprintf, and the buffer size needed to
17 : avoid truncation exceeds size by more than 512, Python aborts with a
18 : Py_FatalError.
19 :
20 : Return value (rv):
21 :
22 : When 0 <= rv < size, the output conversion was unexceptional, and
23 : rv characters were written to str (excluding a trailing \0 byte at
24 : str[rv]).
25 :
26 : When rv >= size, output conversion was truncated, and a buffer of
27 : size rv+1 would have been needed to avoid truncation. str[size-1]
28 : is \0 in this case.
29 :
30 : When rv < 0, "something bad happened". str[size-1] is \0 in this
31 : case too, but the rest of str is unreliable. It could be that
32 : an error in format codes was detected by libc, or on platforms
33 : with a non-C99 vsnprintf simply that the buffer wasn't big enough
34 : to avoid truncation, or on platforms without any vsnprintf that
35 : PyMem_Malloc couldn't obtain space for a temp buffer.
36 :
37 : CAUTION: Unlike C99, str != NULL and size > 0 are required.
38 : */
39 :
40 : int
41 6 : PyOS_snprintf(char *str, size_t size, const char *format, ...)
42 : {
43 : int rc;
44 : va_list va;
45 :
46 6 : va_start(va, format);
47 6 : rc = PyOS_vsnprintf(str, size, format, va);
48 6 : va_end(va);
49 6 : return rc;
50 : }
51 :
52 : int
53 6 : PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va)
54 : {
55 : int len; /* # bytes written, excluding \0 */
56 : #ifdef HAVE_SNPRINTF
57 : #define _PyOS_vsnprintf_EXTRA_SPACE 1
58 : #else
59 : #define _PyOS_vsnprintf_EXTRA_SPACE 512
60 : char *buffer;
61 : #endif
62 : assert(str != NULL);
63 : assert(size > 0);
64 : assert(format != NULL);
65 : /* We take a size_t as input but return an int. Sanity check
66 : * our input so that it won't cause an overflow in the
67 : * vsnprintf return value or the buffer malloc size. */
68 6 : if (size > INT_MAX - _PyOS_vsnprintf_EXTRA_SPACE) {
69 0 : len = -666;
70 0 : goto Done;
71 : }
72 :
73 : #ifdef HAVE_SNPRINTF
74 6 : len = vsnprintf(str, size, format, va);
75 : #else
76 : /* Emulate it. */
77 : buffer = PyMem_MALLOC(size + _PyOS_vsnprintf_EXTRA_SPACE);
78 : if (buffer == NULL) {
79 : len = -666;
80 : goto Done;
81 : }
82 :
83 : len = vsprintf(buffer, format, va);
84 : if (len < 0)
85 : /* ignore the error */;
86 :
87 : else if ((size_t)len >= size + _PyOS_vsnprintf_EXTRA_SPACE)
88 : Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf");
89 :
90 : else {
91 : const size_t to_copy = (size_t)len < size ?
92 : (size_t)len : size - 1;
93 : assert(to_copy < size);
94 : memcpy(str, buffer, to_copy);
95 : str[to_copy] = '\0';
96 : }
97 : PyMem_FREE(buffer);
98 : #endif
99 : Done:
100 6 : if (size > 0)
101 6 : str[size-1] = '\0';
102 6 : return len;
103 : #undef _PyOS_vsnprintf_EXTRA_SPACE
104 : }
|