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