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 theSingle Unix
56
- *Specification has been added. Remaining unimplemented features are:
56
+ * cleaned up to compile properly and support for most of theC99
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 ofthese functions are not the same acrossdifferent
71
- *platforms. This implementationis compatible with theSingle Unix Spec :
71
+ *Historically the result values ofsprintf/snprintf varied acrossplatforms.
72
+ * This implementationnow follows theC99 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. Forsnprintf and sprintf, 0 is returned if str == NULL or count == 0;
79
- *no data has been stored .
78
+ * 2. Forsuccessful 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 ;/* # charsalready 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.ncharsis 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.ncharsis 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 ;
@@ -1038,7 +1064,10 @@ dostr(const char *str, int slen, PrintfTarget *target)
1038
1064
{
1039
1065
/* buffer full, can we dump to stream? */
1040
1066
if (target -> stream == NULL )
1041
- return ;/* no, lose the data */
1067
+ {
1068
+ target -> nchars += slen ;/* no, lose the data */
1069
+ return ;
1070
+ }
1042
1071
flushbuffer (target );
1043
1072
continue ;
1044
1073
}
@@ -1057,7 +1086,10 @@ dopr_outch(int c, PrintfTarget *target)
1057
1086
{
1058
1087
/* buffer full, can we dump to stream? */
1059
1088
if (target -> stream == NULL )
1060
- return ;/* no, lose the data */
1089
+ {
1090
+ target -> nchars ++ ;/* no, lose the data */
1091
+ return ;
1092
+ }
1061
1093
flushbuffer (target );
1062
1094
}
1063
1095
* (target -> bufptr ++ )= c ;