2
2
* Copyright (c) 1983, 1995, 1996 Eric P. Allman
3
3
* Copyright (c) 1988, 1993
4
4
* The Regents of the University of California. All rights reserved.
5
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
5
6
*
6
7
* Redistribution and use in source and binary forms, with or without
7
8
* modification, are permitted provided that the following conditions
52
53
* SNPRINTF, VSNPRINTF and friends
53
54
*
54
55
* These versions have been grabbed off the net. They have been
55
- * cleaned up to compile properly and support for most of the Single Unix
56
- * Specification has been added. Remaining unimplemented features are:
56
+ * cleaned up to compile properly and support for most of the C99
57
+ * specification has been added. Remaining unimplemented features are:
57
58
*
58
59
* 1. No locale support: the radix character is always '.' and the '
59
60
* (single quote) format flag is ignored.
67
68
* 5. Space and '#' flags are not implemented.
68
69
*
69
70
*
70
- * The result values of these functions are not the same across different
71
- * platforms. This implementation is compatible with the Single Unix Spec :
71
+ * Historically the result values of sprintf/snprintf varied across platforms.
72
+ * This implementation now follows the C99 standard :
72
73
*
73
- * 1. -1 is returned only if processing is abandoned due to an invalid
74
- * parameter, such as incorrect format string. (Although not required by
75
- * the spec, this happens only when no characters have yet been transmitted
76
- * to the destination.)
74
+ * 1. -1 is returned if an error is detected in the format string, or if
75
+ * a write to the target stream fails (as reported by fwrite). Note that
76
+ * overrunning snprintf's target buffer is *not* an error.
77
77
*
78
- * 2. For snprintf and sprintf, 0 is returned if str == NULL or count == 0;
79
- * no data has been stored .
78
+ * 2. For successful writes to streams, the actual number of bytes written
79
+ * to the stream is returned .
80
80
*
81
- * 3. Otherwise, the number of bytes actually transmitted to the destination
82
- * is returned (excluding the trailing '\0' for snprintf and sprintf).
81
+ * 3. For successful sprintf/snprintf, the number of bytes that would have
82
+ * been written to an infinite-size buffer (excluding the trailing '\0')
83
+ * is returned. snprintf will truncate its output to fit in the buffer
84
+ * (ensuring a trailing '\0' unless count == 0), but this is not reflected
85
+ * in the function result.
83
86
*
84
- * For snprintf with nonzero count, the result cannot be more than count-1
85
- * (a trailing '\0' is always stored); it is not possible to distinguish
86
- * buffer overrun from exact fit. This is unlike some implementations that
87
- * return the number of bytes that would have been needed for the complete
88
- * result string.
87
+ * snprintf buffer overrun can be detected by checking for function result
88
+ * greater than or equal to the supplied count.
89
89
*/
90
90
91
91
/**************************************************************
104
104
#undef fprintf
105
105
#undef printf
106
106
107
- /* Info about where the formatted output is going */
107
+ /*
108
+ * Info about where the formatted output is going.
109
+ *
110
+ * dopr and subroutines will not write at/past bufend, but snprintf
111
+ * reserves one byte, ensuring it may place the trailing '\0' there.
112
+ *
113
+ * In snprintf, we use nchars to count the number of bytes dropped on the
114
+ * floor due to buffer overrun. The correct result of snprintf is thus
115
+ * (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might
116
+ * seem: nchars is the number of emitted bytes that are not in the buffer now,
117
+ * either because we sent them to the stream or because we couldn't fit them
118
+ * into the buffer to begin with.)
119
+ */
108
120
typedef struct
109
121
{
110
122
char * bufptr ; /* next buffer output position */
111
123
char * bufstart ; /* first buffer element */
112
- char * bufend ; /* last buffer element, or NULL */
124
+ char * bufend ; /* last+1 buffer element, or NULL */
113
125
/* bufend == NULL is for sprintf, where we assume buf is big enough */
114
126
FILE * stream ; /* eventual output destination, or NULL */
115
- int nchars ; /* # chars already sent to stream */
127
+ int nchars ; /* # chars sent to stream, or dropped */
116
128
bool failed ; /* call is a failure; errno is set */
117
129
} PrintfTarget ;
118
130
@@ -150,17 +162,28 @@ int
150
162
pg_vsnprintf (char * str , size_t count , const char * fmt , va_list args )
151
163
{
152
164
PrintfTarget target ;
165
+ char onebyte [1 ];
153
166
154
- if (str == NULL || count == 0 )
155
- return 0 ;
167
+ /*
168
+ * C99 allows the case str == NULL when count == 0. Rather than
169
+ * special-casing this situation further down, we substitute a one-byte
170
+ * local buffer. Callers cannot tell, since the function result doesn't
171
+ * depend on count.
172
+ */
173
+ if (count == 0 )
174
+ {
175
+ str = onebyte ;
176
+ count = 1 ;
177
+ }
156
178
target .bufstart = target .bufptr = str ;
157
179
target .bufend = str + count - 1 ;
158
180
target .stream = NULL ;
159
- /* target.nchars is unused in this case */
181
+ target .nchars = 0 ;
160
182
target .failed = false;
161
183
dopr (& target , fmt , args );
162
184
* (target .bufptr ) = '\0' ;
163
- return target .failed ? -1 : (target .bufptr - target .bufstart );
185
+ return target .failed ? -1 : (target .bufptr - target .bufstart
186
+ + target .nchars );
164
187
}
165
188
166
189
int
@@ -180,16 +203,15 @@ pg_vsprintf(char *str, const char *fmt, va_list args)
180
203
{
181
204
PrintfTarget target ;
182
205
183
- if (str == NULL )
184
- return 0 ;
185
206
target .bufstart = target .bufptr = str ;
186
207
target .bufend = NULL ;
187
208
target .stream = NULL ;
188
- /* target.nchars is unused in this case */
209
+ target .nchars = 0 ; /* not really used in this case */
189
210
target .failed = false;
190
211
dopr (& target , fmt , args );
191
212
* (target .bufptr ) = '\0' ;
192
- return target .failed ? -1 : (target .bufptr - target .bufstart );
213
+ return target .failed ? -1 : (target .bufptr - target .bufstart
214
+ + target .nchars );
193
215
}
194
216
195
217
int
@@ -216,7 +238,7 @@ pg_vfprintf(FILE *stream, const char *fmt, va_list args)
216
238
return -1 ;
217
239
}
218
240
target .bufstart = target .bufptr = buffer ;
219
- target .bufend = buffer + sizeof (buffer ) - 1 ;
241
+ target .bufend = buffer + sizeof (buffer ); /* use the whole buffer */
220
242
target .stream = stream ;
221
243
target .nchars = 0 ;
222
244
target .failed = false;
@@ -259,6 +281,10 @@ flushbuffer(PrintfTarget *target)
259
281
{
260
282
size_t nc = target -> bufptr - target -> bufstart ;
261
283
284
+ /*
285
+ * Don't write anything if we already failed; this is to ensure we
286
+ * preserve the original failure's errno.
287
+ */
262
288
if (!target -> failed && nc > 0 )
263
289
{
264
290
size_t written ;
@@ -1020,7 +1046,10 @@ dostr(const char *str, int slen, PrintfTarget *target)
1020
1046
{
1021
1047
/* buffer full, can we dump to stream? */
1022
1048
if (target -> stream == NULL )
1023
- return ; /* no, lose the data */
1049
+ {
1050
+ target -> nchars += slen ; /* no, lose the data */
1051
+ return ;
1052
+ }
1024
1053
flushbuffer (target );
1025
1054
continue ;
1026
1055
}
@@ -1039,7 +1068,10 @@ dopr_outch(int c, PrintfTarget *target)
1039
1068
{
1040
1069
/* buffer full, can we dump to stream? */
1041
1070
if (target -> stream == NULL )
1042
- return ; /* no, lose the data */
1071
+ {
1072
+ target -> nchars ++ ; /* no, lose the data */
1073
+ return ;
1074
+ }
1043
1075
flushbuffer (target );
1044
1076
}
1045
1077
* (target -> bufptr ++ ) = c ;
0 commit comments