22 * Copyright (c) 1983, 1995, 1996 Eric P. Allman
33 * Copyright (c) 1988, 1993
44 * The Regents of the University of California. All rights reserved.
5+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
56 *
67 * Redistribution and use in source and binary forms, with or without
78 * modification, are permitted provided that the following conditions
4950 * SNPRINTF, VSNPRINTF and friends
5051 *
5152 * These versions have been grabbed off the net. They have been
52- * cleaned up to compile properly and support for most of the Single Unix
53- * Specification has been added. Remaining unimplemented features are:
53+ * cleaned up to compile properly and support for most of the C99
54+ * specification has been added. Remaining unimplemented features are:
5455 *
5556 * 1. No locale support: the radix character is always '.' and the '
5657 * (single quote) format flag is ignored.
6465 * 5. Space and '#' flags are not implemented.
6566 *
6667 *
67- * The result values of these functions are not the same across different
68- * platforms. This implementation is compatible with the Single Unix Spec :
68+ * Historically the result values of sprintf/snprintf varied across platforms.
69+ * This implementation now follows the C99 standard :
6970 *
70- * 1. -1 is returned only if processing is abandoned due to an invalid
71- * parameter, such as incorrect format string. (Although not required by
72- * the spec, this happens only when no characters have yet been transmitted
73- * to the destination.)
71+ * 1. -1 is returned if an error is detected in the format string, or if
72+ * a write to the target stream fails (as reported by fwrite). Note that
73+ * overrunning snprintf's target buffer is *not* an error.
7474 *
75- * 2. For snprintf and sprintf, 0 is returned if str == NULL or count == 0;
76- * no data has been stored .
75+ * 2. For successful writes to streams, the actual number of bytes written
76+ * to the stream is returned .
7777 *
78- * 3. Otherwise, the number of bytes actually transmitted to the destination
79- * is returned (excluding the trailing '\0' for snprintf and sprintf).
78+ * 3. For successful sprintf/snprintf, the number of bytes that would have
79+ * been written to an infinite-size buffer (excluding the trailing '\0')
80+ * is returned. snprintf will truncate its output to fit in the buffer
81+ * (ensuring a trailing '\0' unless count == 0), but this is not reflected
82+ * in the function result.
8083 *
81- * For snprintf with nonzero count, the result cannot be more than count-1
82- * (a trailing '\0' is always stored); it is not possible to distinguish
83- * buffer overrun from exact fit. This is unlike some implementations that
84- * return the number of bytes that would have been needed for the complete
85- * result string.
84+ * snprintf buffer overrun can be detected by checking for function result
85+ * greater than or equal to the supplied count.
8686 */
8787
8888/**************************************************************
101101#undef fprintf
102102#undef printf
103103
104- /* Info about where the formatted output is going */
104+ /*
105+ * Info about where the formatted output is going.
106+ *
107+ * dopr and subroutines will not write at/past bufend, but snprintf
108+ * reserves one byte, ensuring it may place the trailing '\0' there.
109+ *
110+ * In snprintf, we use nchars to count the number of bytes dropped on the
111+ * floor due to buffer overrun. The correct result of snprintf is thus
112+ * (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might
113+ * seem: nchars is the number of emitted bytes that are not in the buffer now,
114+ * either because we sent them to the stream or because we couldn't fit them
115+ * into the buffer to begin with.)
116+ */
105117typedef struct
106118{
107119 char * bufptr ; /* next buffer output position */
108120 char * bufstart ; /* first buffer element */
109- char * bufend ; /* last buffer element, or NULL */
121+ char * bufend ; /* last+1 buffer element, or NULL */
110122 /* bufend == NULL is for sprintf, where we assume buf is big enough */
111123 FILE * stream ; /* eventual output destination, or NULL */
112- int nchars ; /* # chars already sent to stream */
124+ int nchars ; /* # chars sent to stream, or dropped */
113125 bool failed ; /* call is a failure; errno is set */
114126} PrintfTarget ;
115127
@@ -147,17 +159,28 @@ int
147159pg_vsnprintf (char * str , size_t count , const char * fmt , va_list args )
148160{
149161 PrintfTarget target ;
162+ char onebyte [1 ];
150163
151- if (str == NULL || count == 0 )
152- return 0 ;
164+ /*
165+ * C99 allows the case str == NULL when count == 0. Rather than
166+ * special-casing this situation further down, we substitute a one-byte
167+ * local buffer. Callers cannot tell, since the function result doesn't
168+ * depend on count.
169+ */
170+ if (count == 0 )
171+ {
172+ str = onebyte ;
173+ count = 1 ;
174+ }
153175 target .bufstart = target .bufptr = str ;
154176 target .bufend = str + count - 1 ;
155177 target .stream = NULL ;
156- /* target.nchars is unused in this case */
178+ target .nchars = 0 ;
157179 target .failed = false;
158180 dopr (& target , fmt , args );
159181 * (target .bufptr ) = '\0' ;
160- return target .failed ? -1 : (target .bufptr - target .bufstart );
182+ return target .failed ? -1 : (target .bufptr - target .bufstart
183+ + target .nchars );
161184}
162185
163186int
@@ -177,16 +200,15 @@ pg_vsprintf(char *str, const char *fmt, va_list args)
177200{
178201 PrintfTarget target ;
179202
180- if (str == NULL )
181- return 0 ;
182203 target .bufstart = target .bufptr = str ;
183204 target .bufend = NULL ;
184205 target .stream = NULL ;
185- /* target.nchars is unused in this case */
206+ target .nchars = 0 ; /* not really used in this case */
186207 target .failed = false;
187208 dopr (& target , fmt , args );
188209 * (target .bufptr ) = '\0' ;
189- return target .failed ? -1 : (target .bufptr - target .bufstart );
210+ return target .failed ? -1 : (target .bufptr - target .bufstart
211+ + target .nchars );
190212}
191213
192214int
@@ -213,7 +235,7 @@ pg_vfprintf(FILE *stream, const char *fmt, va_list args)
213235 return -1 ;
214236 }
215237 target .bufstart = target .bufptr = buffer ;
216- target .bufend = buffer + sizeof (buffer ) - 1 ;
238+ target .bufend = buffer + sizeof (buffer ); /* use the whole buffer */
217239 target .stream = stream ;
218240 target .nchars = 0 ;
219241 target .failed = false;
@@ -256,6 +278,10 @@ flushbuffer(PrintfTarget *target)
256278{
257279 size_t nc = target -> bufptr - target -> bufstart ;
258280
281+ /*
282+ * Don't write anything if we already failed; this is to ensure we
283+ * preserve the original failure's errno.
284+ */
259285 if (!target -> failed && nc > 0 )
260286 {
261287 size_t written ;
@@ -1035,7 +1061,10 @@ dostr(const char *str, int slen, PrintfTarget *target)
10351061 {
10361062 /* buffer full, can we dump to stream? */
10371063 if (target -> stream == NULL )
1038- return ; /* no, lose the data */
1064+ {
1065+ target -> nchars += slen ; /* no, lose the data */
1066+ return ;
1067+ }
10391068 flushbuffer (target );
10401069 continue ;
10411070 }
@@ -1054,7 +1083,10 @@ dopr_outch(int c, PrintfTarget *target)
10541083 {
10551084 /* buffer full, can we dump to stream? */
10561085 if (target -> stream == NULL )
1057- return ; /* no, lose the data */
1086+ {
1087+ target -> nchars ++ ; /* no, lose the data */
1088+ return ;
1089+ }
10581090 flushbuffer (target );
10591091 }
10601092 * (target -> bufptr ++ ) = c ;
0 commit comments