diff options
Diffstat (limited to 'src/stdio/vsnprintf.c')
-rw-r--r-- | src/stdio/vsnprintf.c | 42 |
1 files changed, 23 insertions, 19 deletions
diff --git a/src/stdio/vsnprintf.c b/src/stdio/vsnprintf.c index 1f316ca4..ba17bd7d 100644 --- a/src/stdio/vsnprintf.c +++ b/src/stdio/vsnprintf.c @@ -2,33 +2,37 @@ static size_t sn_write(FILE *f, const unsigned char *s, size_t l) { - /* pretend to succeed, but discard data */ + size_t k = f->wend - f->wpos; + if (k > l) k = l; + memcpy(f->wpos, s, k); + f->wpos += k; + /* pretend to succeed, but discard extra data */ return l; } int vsnprintf(char *s, size_t n, const char *fmt, va_list ap) { int r; - FILE f; - unsigned char buf[1]; + char b; + FILE f = { .lbf = EOF, .write = sn_write, .lock = -1 }; - memset(&f, 0, sizeof(FILE)); - f.lbf = EOF; - f.write = sn_write; - f.buf_size = 1; - f.buf = buf; - f.lock = -1; - if (n > INT_MAX) { - errno = EOVERFLOW; - return -1; - } else if (n > 0) { - if (n > (char *)0+SIZE_MAX-s) n = (char *)0+SIZE_MAX-s; - f.wpos = (void *)s; - f.wbase = f.wend = (void *)(s+n-1); - f.wstop = f.wend - 1; + if (n-1 > INT_MAX-1) { + if (n) { + errno = EOVERFLOW; + return -1; + } + s = &b; + n = 1; } + + /* Ensure pointers don't wrap if "infinite" n is passed in */ + if (n > (char *)0+SIZE_MAX-s-1) n = (char *)0+SIZE_MAX-s-1; + f.buf_size = n; + f.buf = f.wpos = (void *)s; + f.wbase = f.wend = (void *)(s+n); r = vfprintf(&f, fmt, ap); - /* wpos points just after last byte written, or to s+n-1 (wbase) */ - *f.wpos = 0; + + /* Null-terminate, overwriting last char if dest buffer is full */ + if (n) f.wpos[-(f.wpos == f.wend)] = 0; return r; } |