diff options
Diffstat (limited to 'src/port/snprintf.c')
-rw-r--r-- | src/port/snprintf.c | 94 |
1 files changed, 55 insertions, 39 deletions
diff --git a/src/port/snprintf.c b/src/port/snprintf.c index 0c779a601fc..91c97d487cd 100644 --- a/src/port/snprintf.c +++ b/src/port/snprintf.c @@ -114,6 +114,7 @@ typedef struct /* 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; /* @@ -143,7 +144,7 @@ typedef union static void flushbuffer(PrintfTarget *target); -static int dopr(PrintfTarget *target, const char *format, va_list args); +static void dopr(PrintfTarget *target, const char *format, va_list args); int @@ -157,14 +158,10 @@ pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args) target.bufend = str + count - 1; target.stream = NULL; /* target.nchars is unused in this case */ - if (dopr(&target, fmt, args)) - { - *(target.bufptr) = '\0'; - errno = EINVAL; /* bad format */ - return -1; - } + target.failed = false; + dopr(&target, fmt, args); *(target.bufptr) = '\0'; - return target.bufptr - target.bufstart; + return target.failed ? -1 : (target.bufptr - target.bufstart); } int @@ -190,14 +187,10 @@ pg_vsprintf(char *str, const char *fmt, va_list args) target.bufend = NULL; target.stream = NULL; /* target.nchars is unused in this case */ - if (dopr(&target, fmt, args)) - { - *(target.bufptr) = '\0'; - errno = EINVAL; /* bad format */ - return -1; - } + target.failed = false; + dopr(&target, fmt, args); *(target.bufptr) = '\0'; - return target.bufptr - target.bufstart; + return target.failed ? -1 : (target.bufptr - target.bufstart); } int @@ -227,14 +220,11 @@ pg_vfprintf(FILE *stream, const char *fmt, va_list args) target.bufend = buffer + sizeof(buffer) - 1; target.stream = stream; target.nchars = 0; - if (dopr(&target, fmt, args)) - { - errno = EINVAL; /* bad format */ - return -1; - } + target.failed = false; + dopr(&target, fmt, args); /* dump any remaining buffer contents */ flushbuffer(&target); - return target.nchars; + return target.failed ? -1 : target.nchars; } int @@ -261,14 +251,24 @@ pg_printf(const char *fmt,...) return len; } -/* call this only when stream is defined */ +/* + * Attempt to write the entire buffer to target->stream; discard the entire + * buffer in any case. Call this only when target->stream is defined. + */ static void flushbuffer(PrintfTarget *target) { size_t nc = target->bufptr - target->bufstart; - if (nc > 0) - target->nchars += fwrite(target->bufstart, 1, nc, target->stream); + if (!target->failed && nc > 0) + { + size_t written; + + written = fwrite(target->bufstart, 1, nc, target->stream); + target->nchars += written; + if (written != nc) + target->failed = true; + } target->bufptr = target->bufstart; } @@ -295,7 +295,7 @@ static void trailing_pad(int *padlen, PrintfTarget *target); /* * dopr(): poor man's version of doprintf */ -static int +static void dopr(PrintfTarget *target, const char *format, va_list args) { const char *format_start = format; @@ -372,12 +372,12 @@ nextch1: case '$': have_dollar = true; if (accum <= 0 || accum > NL_ARGMAX) - return -1; + goto bad_format; if (afterstar) { if (argtypes[accum] && argtypes[accum] != ATYPE_INT) - return -1; + goto bad_format; argtypes[accum] = ATYPE_INT; last_dollar = Max(last_dollar, accum); afterstar = false; @@ -427,7 +427,7 @@ nextch1: atype = ATYPE_INT; if (argtypes[fmtpos] && argtypes[fmtpos] != atype) - return -1; + goto bad_format; argtypes[fmtpos] = atype; last_dollar = Max(last_dollar, fmtpos); } @@ -439,7 +439,7 @@ nextch1: { if (argtypes[fmtpos] && argtypes[fmtpos] != ATYPE_INT) - return -1; + goto bad_format; argtypes[fmtpos] = ATYPE_INT; last_dollar = Max(last_dollar, fmtpos); } @@ -452,7 +452,7 @@ nextch1: { if (argtypes[fmtpos] && argtypes[fmtpos] != ATYPE_CHARPTR) - return -1; + goto bad_format; argtypes[fmtpos] = ATYPE_CHARPTR; last_dollar = Max(last_dollar, fmtpos); } @@ -468,7 +468,7 @@ nextch1: { if (argtypes[fmtpos] && argtypes[fmtpos] != ATYPE_DOUBLE) - return -1; + goto bad_format; argtypes[fmtpos] = ATYPE_DOUBLE; last_dollar = Max(last_dollar, fmtpos); } @@ -489,7 +489,7 @@ nextch1: /* Per spec, you use either all dollar or all not. */ if (have_dollar && have_non_dollar) - return -1; + goto bad_format; /* * In dollar mode, collect the arguments in physical order. @@ -499,7 +499,7 @@ nextch1: switch (argtypes[i]) { case ATYPE_NONE: - return -1; /* invalid format */ + goto bad_format; case ATYPE_INT: argvalues[i].i = va_arg(args, int); break; @@ -524,6 +524,9 @@ nextch1: format = format_start; while ((ch = *format++) != '\0') { + if (target->failed) + break; + if (ch != '%') { dopr_outch(ch, target); @@ -781,7 +784,11 @@ nextch2: } } - return 0; + return; + +bad_format: + errno = EINVAL; + target->failed = true; } static size_t @@ -831,8 +838,10 @@ fmtptr(void *value, PrintfTarget *target) /* we rely on regular C library's sprintf to do the basic conversion */ vallen = sprintf(convert, "%p", value); - - dostr(convert, vallen, target); + if (vallen < 0) + target->failed = true; + else + dostr(convert, vallen, target); } static void @@ -965,16 +974,19 @@ fmtfloat(double value, char type, int forcesign, int leftjust, if (pointflag) { - sprintf(fmt, "%%.%d%c", prec, type); + if (sprintf(fmt, "%%.%d%c", prec, type) < 0) + goto fail; zeropadlen = precision - prec; } - else - sprintf(fmt, "%%%c", type); + else if (sprintf(fmt, "%%%c", type) < 0) + goto fail; if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue)) value = -value; vallen = sprintf(convert, fmt, value); + if (vallen < 0) + goto fail; /* If it's infinity or NaN, forget about doing any zero-padding */ if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1])) @@ -1014,6 +1026,10 @@ fmtfloat(double value, char type, int forcesign, int leftjust, } trailing_pad(&padlen, target); + return; + +fail: + target->failed = true; } static void |