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
5253 *SNPRINTF, VSNPRINTF and friends
5354 *
5455 * 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:
5758 *
5859 * 1. No locale support: the radix character is always '.' and the '
5960 * (single quote) format flag is ignored.
6768 * 5. Space and '#' flags are not implemented.
6869 *
6970 *
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 :
7273 *
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.
7777 *
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 .
8080 *
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.
8386 *
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.
8989 */
9090
9191/**************************************************************
104104#undef fprintf
105105#undef printf
106106
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+ */
108120typedef struct
109121{
110122char * bufptr ;/* next buffer output position */
111123char * bufstart ;/* first buffer element */
112- char * bufend ;/* last buffer element, or NULL */
124+ char * bufend ;/* last+1 buffer element, or NULL */
113125/* bufend == NULL is for sprintf, where we assume buf is big enough */
114126FILE * stream ;/* eventual output destination, or NULL */
115- int nchars ;/* # charsalready sent to stream */
127+ int nchars ;/* # chars sent to stream, or dropped */
116128bool failed ;/* call is a failure; errno is set */
117129}PrintfTarget ;
118130
@@ -150,17 +162,28 @@ int
150162pg_vsnprintf (char * str ,size_t count ,const char * fmt ,va_list args )
151163{
152164PrintfTarget target ;
165+ char onebyte [1 ];
153166
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+ }
156178target .bufstart = target .bufptr = str ;
157179target .bufend = str + count - 1 ;
158180target .stream = NULL ;
159- /* target.ncharsis unused in this case */
181+ target .nchars = 0 ;
160182target .failed = false;
161183dopr (& target ,fmt ,args );
162184* (target .bufptr )= '\0' ;
163- return target .failed ?-1 : (target .bufptr - target .bufstart );
185+ return target .failed ?-1 : (target .bufptr - target .bufstart
186+ + target .nchars );
164187}
165188
166189int
@@ -180,16 +203,15 @@ pg_vsprintf(char *str, const char *fmt, va_list args)
180203{
181204PrintfTarget target ;
182205
183- if (str == NULL )
184- return 0 ;
185206target .bufstart = target .bufptr = str ;
186207target .bufend = NULL ;
187208target .stream = NULL ;
188- /* target.ncharsis unused in this case */
209+ target .nchars = 0 ; /* not really used in this case */
189210target .failed = false;
190211dopr (& target ,fmt ,args );
191212* (target .bufptr )= '\0' ;
192- return target .failed ?-1 : (target .bufptr - target .bufstart );
213+ return target .failed ?-1 : (target .bufptr - target .bufstart
214+ + target .nchars );
193215}
194216
195217int
@@ -216,7 +238,7 @@ pg_vfprintf(FILE *stream, const char *fmt, va_list args)
216238return -1 ;
217239}
218240target .bufstart = target .bufptr = buffer ;
219- target .bufend = buffer + sizeof (buffer )- 1 ;
241+ target .bufend = buffer + sizeof (buffer ); /* use the whole buffer */
220242target .stream = stream ;
221243target .nchars = 0 ;
222244target .failed = false;
@@ -259,6 +281,10 @@ flushbuffer(PrintfTarget *target)
259281{
260282size_t nc = target -> bufptr - target -> bufstart ;
261283
284+ /*
285+ * Don't write anything if we already failed; this is to ensure we
286+ * preserve the original failure's errno.
287+ */
262288if (!target -> failed && nc > 0 )
263289{
264290size_t written ;
@@ -1046,7 +1072,10 @@ dostr(const char *str, int slen, PrintfTarget *target)
10461072{
10471073/* buffer full, can we dump to stream? */
10481074if (target -> stream == NULL )
1049- return ;/* no, lose the data */
1075+ {
1076+ target -> nchars += slen ;/* no, lose the data */
1077+ return ;
1078+ }
10501079flushbuffer (target );
10511080continue ;
10521081}
@@ -1065,7 +1094,10 @@ dopr_outch(int c, PrintfTarget *target)
10651094{
10661095/* buffer full, can we dump to stream? */
10671096if (target -> stream == NULL )
1068- return ;/* no, lose the data */
1097+ {
1098+ target -> nchars ++ ;/* no, lose the data */
1099+ return ;
1100+ }
10691101flushbuffer (target );
10701102}
10711103* (target -> bufptr ++ )= c ;