diff --git a/src/port/snprintf.c b/src/port/snprintf.c index a184134..851e2ae 100644 *** a/src/port/snprintf.c --- b/src/port/snprintf.c *************** *** 2,7 **** --- 2,8 ---- * Copyright (c) 1983, 1995, 1996 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions *************** *** 49,56 **** * SNPRINTF, VSNPRINTF and friends * * These versions have been grabbed off the net. They have been ! * cleaned up to compile properly and support for most of the Single Unix ! * Specification has been added. Remaining unimplemented features are: * * 1. No locale support: the radix character is always '.' and the ' * (single quote) format flag is ignored. --- 50,57 ---- * SNPRINTF, VSNPRINTF and friends * * These versions have been grabbed off the net. They have been ! * cleaned up to compile properly and support for most of the C99 ! * specification has been added. Remaining unimplemented features are: * * 1. No locale support: the radix character is always '.' and the ' * (single quote) format flag is ignored. *************** *** 64,88 **** * 5. Space and '#' flags are not implemented. * * ! * The result values of these functions are not the same across different ! * platforms. This implementation is compatible with the Single Unix Spec: * ! * 1. -1 is returned only if processing is abandoned due to an invalid ! * parameter, such as incorrect format string. (Although not required by ! * the spec, this happens only when no characters have yet been transmitted ! * to the destination.) * ! * 2. For snprintf and sprintf, 0 is returned if str == NULL or count == 0; ! * no data has been stored. * ! * 3. Otherwise, the number of bytes actually transmitted to the destination ! * is returned (excluding the trailing '\0' for snprintf and sprintf). * ! * For snprintf with nonzero count, the result cannot be more than count-1 ! * (a trailing '\0' is always stored); it is not possible to distinguish ! * buffer overrun from exact fit. This is unlike some implementations that ! * return the number of bytes that would have been needed for the complete ! * result string. */ /************************************************************** --- 65,88 ---- * 5. Space and '#' flags are not implemented. * * ! * Historically the result values of sprintf/snprintf varied across platforms. ! * This implementation now follows the C99 standard: * ! * 1. -1 is returned if an error is detected in the format string, or if ! * a write to the target stream fails (as reported by fwrite). Note that ! * overrunning snprintf's target buffer is *not* an error. * ! * 2. For successful writes to streams, the actual number of bytes written ! * to the stream is returned. * ! * 3. For successful sprintf/snprintf, the number of bytes that would have ! * been written to an infinite-size buffer (excluding the trailing '\0') ! * is returned. snprintf will truncate its output to fit in the buffer ! * (ensuring a trailing '\0' unless count == 0), but this is not reflected ! * in the function result. * ! * snprintf buffer overrun can be detected by checking for function result ! * greater than or equal to the supplied count. */ /************************************************************** *************** *** 101,115 **** #undef fprintf #undef printf ! /* Info about where the formatted output is going */ typedef struct { char *bufptr; /* next buffer output position */ char *bufstart; /* first buffer element */ ! char *bufend; /* last buffer element, or NULL */ /* bufend == NULL is for sprintf, where we assume buf is big enough */ FILE *stream; /* eventual output destination, or NULL */ ! int nchars; /* # chars already sent to stream */ bool failed; /* call is a failure; errno is set */ } PrintfTarget; --- 101,127 ---- #undef fprintf #undef printf ! /* ! * Info about where the formatted output is going. ! * ! * dopr and subroutines will not write at/past bufend, but snprintf ! * reserves one byte, ensuring it may place the trailing '\0' there. ! * ! * In snprintf, we use nchars to count the number of bytes dropped on the ! * floor due to buffer overrun. The correct result of snprintf is thus ! * (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might ! * seem: nchars is the number of emitted bytes that are not in the buffer now, ! * either because we sent them to the stream or because we couldn't fit them ! * into the buffer to begin with.) ! */ typedef struct { char *bufptr; /* next buffer output position */ char *bufstart; /* first buffer element */ ! char *bufend; /* last+1 buffer element, or NULL */ /* bufend == NULL is for sprintf, where we assume buf is big enough */ FILE *stream; /* eventual output destination, or NULL */ ! int nchars; /* # chars sent to stream, or dropped */ bool failed; /* call is a failure; errno is set */ } PrintfTarget; *************** int *** 147,163 **** pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { PrintfTarget target; ! if (str == NULL || count == 0) ! return 0; target.bufstart = target.bufptr = str; target.bufend = str + count - 1; target.stream = NULL; ! /* target.nchars is unused in this case */ target.failed = false; dopr(&target, fmt, args); *(target.bufptr) = '\0'; ! return target.failed ? -1 : (target.bufptr - target.bufstart); } int --- 159,186 ---- pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { PrintfTarget target; + char onebyte[1]; ! /* ! * C99 allows the case str == NULL when count == 0. Rather than ! * special-casing this situation further down, we substitute a one-byte ! * local buffer. Callers cannot tell, since the function result doesn't ! * depend on count. ! */ ! if (count == 0) ! { ! str = onebyte; ! count = 1; ! } target.bufstart = target.bufptr = str; target.bufend = str + count - 1; target.stream = NULL; ! target.nchars = 0; target.failed = false; dopr(&target, fmt, args); *(target.bufptr) = '\0'; ! return target.failed ? -1 : (target.bufptr - target.bufstart ! + target.nchars); } int *************** pg_vsprintf(char *str, const char *fmt, *** 177,192 **** { PrintfTarget target; - if (str == NULL) - return 0; target.bufstart = target.bufptr = str; target.bufend = NULL; target.stream = NULL; ! /* target.nchars is unused in this case */ target.failed = false; dopr(&target, fmt, args); *(target.bufptr) = '\0'; ! return target.failed ? -1 : (target.bufptr - target.bufstart); } int --- 200,214 ---- { PrintfTarget target; target.bufstart = target.bufptr = str; target.bufend = NULL; target.stream = NULL; ! target.nchars = 0; /* not really used in this case */ target.failed = false; dopr(&target, fmt, args); *(target.bufptr) = '\0'; ! return target.failed ? -1 : (target.bufptr - target.bufstart ! + target.nchars); } int *************** pg_vfprintf(FILE *stream, const char *fm *** 213,219 **** return -1; } target.bufstart = target.bufptr = buffer; ! target.bufend = buffer + sizeof(buffer) - 1; target.stream = stream; target.nchars = 0; target.failed = false; --- 235,241 ---- return -1; } target.bufstart = target.bufptr = buffer; ! target.bufend = buffer + sizeof(buffer); /* use the whole buffer */ target.stream = stream; target.nchars = 0; target.failed = false; *************** flushbuffer(PrintfTarget *target) *** 256,261 **** --- 278,287 ---- { size_t nc = target->bufptr - target->bufstart; + /* + * Don't write anything if we already failed; this is to ensure we + * preserve the original failure's errno. + */ if (!target->failed && nc > 0) { size_t written; *************** dostr(const char *str, int slen, PrintfT *** 1035,1041 **** { /* buffer full, can we dump to stream? */ if (target->stream == NULL) ! return; /* no, lose the data */ flushbuffer(target); continue; } --- 1061,1070 ---- { /* buffer full, can we dump to stream? */ if (target->stream == NULL) ! { ! target->nchars += slen; /* no, lose the data */ ! return; ! } flushbuffer(target); continue; } *************** dopr_outch(int c, PrintfTarget *target) *** 1054,1060 **** { /* buffer full, can we dump to stream? */ if (target->stream == NULL) ! return; /* no, lose the data */ flushbuffer(target); } *(target->bufptr++) = c; --- 1083,1092 ---- { /* buffer full, can we dump to stream? */ if (target->stream == NULL) ! { ! target->nchars++; /* no, lose the data */ ! return; ! } flushbuffer(target); } *(target->bufptr++) = c;