diff options
Diffstat (limited to 'src/stdio')
35 files changed, 235 insertions, 268 deletions
diff --git a/src/stdio/__overflow.c b/src/stdio/__overflow.c index e35104de..3bb37923 100644 --- a/src/stdio/__overflow.c +++ b/src/stdio/__overflow.c @@ -1,52 +1,10 @@ #include "stdio_impl.h" -static int overflow(FILE *f, int c) +int __overflow(FILE *f, int _c) { - /* Initialize if we're not already writing */ - if (!f->wend) { - /* Fail if we're in error state or unwritable. */ - if (f->flags & (F_ERR|F_NOWR)) return EOF; - - /* Set byte orientation -1,0=>-1; 1=>1 */ - f->mode |= f->mode-1; - - /* Clear read buffer (easier than summoning nasal demons) */ - f->rpos = f->rend = f->rstop = 0; - - /* Activate write through the buffer */ - f->wpos = f->wbase = f->buf; - f->wend = f->buf + f->buf_size; - f->wstop = (f->lbf < 0) ? f->wend - 1 : 0; - } - - /* Buffer can always hold at least 1 byte... */ - if (c != EOF) { - *f->wpos++ = c; - if (f->wpos <= f->wstop && c != f->lbf) return c; - } - /* ...since if the next call fails, buffer is empty. */ - if (f->write(f, f->wbase, f->wpos - f->wbase) < 0) { - f->flags |= F_ERR; - f->wpos = f->wbase = f->wend = f->wstop = 0; - return EOF; - } - - /* Buffer is empty so reset position to beginning */ - f->wpos = f->wbase; - + unsigned char c = _c; + if (!f->wend && __towrite(f)) return EOF; + if (f->wpos < f->wend && c != f->lbf) return *f->wpos++ = c; + if (f->write(f, &c, 1)!=1) return EOF; return c; } - -int __overflow(FILE *f, int c) -{ - return overflow(f, c & 0xff); -} - -int __oflow(FILE *f) -{ - overflow(f, EOF); - return (f->flags & F_ERR) ? EOF : 0; -} - -/* Link flush-on-exit code iff any stdio write functions are linked. */ -int (*const __fflush_on_exit)(FILE *) = fflush; diff --git a/src/stdio/__stdio_read.c b/src/stdio/__stdio_read.c index d9bb3269..a2e4cd62 100644 --- a/src/stdio/__stdio_read.c +++ b/src/stdio/__stdio_read.c @@ -2,5 +2,21 @@ size_t __stdio_read(FILE *f, unsigned char *buf, size_t len) { - return syscall(SYS_read, f->fd, buf, len); + struct iovec iov[2] = { + { .iov_base = buf, .iov_len = len }, + { .iov_base = f->buf, .iov_len = f->buf_size } + }; + ssize_t cnt; + + cnt = syscall(SYS_readv, f->fd, iov, 2); + if (cnt <= 0) { + f->flags |= F_EOF ^ ((F_ERR^F_EOF) & cnt); + f->rpos = f->rend = 0; + return cnt; + } + if (cnt <= len) return cnt; + cnt -= len; + f->rpos = f->buf; + f->rend = f->buf + cnt; + return len; } diff --git a/src/stdio/__stdio_seek.c b/src/stdio/__stdio_seek.c index c205ab82..35ae788c 100644 --- a/src/stdio/__stdio_seek.c +++ b/src/stdio/__stdio_seek.c @@ -2,6 +2,7 @@ static off_t retneg1(FILE *f, off_t off, int whence) { + errno = ESPIPE; return -1; } @@ -15,7 +16,6 @@ off_t __stdio_seek(FILE *f, off_t off, int whence) ret = syscall(SYS_lseek, f->fd, off, whence); #endif /* Detect unseekable files and optimize future failures out */ - if (ret < 0 && off == 0 && whence == SEEK_CUR) - f->seek = retneg1; + if (ret < 0 && errno == ESPIPE) f->seek = retneg1; return ret; } diff --git a/src/stdio/__stdio_write.c b/src/stdio/__stdio_write.c index d4264eff..63d9c858 100644 --- a/src/stdio/__stdio_write.c +++ b/src/stdio/__stdio_write.c @@ -2,8 +2,29 @@ size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len) { - const unsigned char *stop = buf+len; - ssize_t cnt = 1; - for (; buf<stop && (cnt=syscall(SYS_write, f->fd, buf, len))>0; buf+=cnt); - return len-(stop-buf); + struct iovec iovs[2] = { + { .iov_base = f->wbase, .iov_len = f->wpos-f->wbase }, + { .iov_base = (void *)buf, .iov_len = len } + }; + struct iovec *iov = iovs; + size_t rem = iov[0].iov_len + iov[1].iov_len; + int iovcnt = 2; + ssize_t cnt; + f->wpos = f->wbase; + for (;;) { + cnt = syscall(SYS_writev, f->fd, iov, iovcnt); + if (cnt == rem) return len; + if (cnt < 0) { + f->wpos = f->wbase = f->wend = 0; + f->flags |= F_ERR; + return iovcnt == 2 ? 0 : len-iov[0].iov_len; + } + rem -= cnt; + if (cnt > iov[0].iov_len) { + cnt -= iov[0].iov_len; + iov++; iovcnt--; + } + iov[0].iov_base = (char *)iov[0].iov_base + cnt; + iov[0].iov_len -= cnt; + } } diff --git a/src/stdio/__stdout_write.c b/src/stdio/__stdout_write.c new file mode 100644 index 00000000..4683ffc3 --- /dev/null +++ b/src/stdio/__stdout_write.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +size_t __stdout_write(FILE *f, const unsigned char *buf, size_t len) +{ + struct termios tio; + f->write = __stdio_write; + if (!(f->flags & F_SVB) && syscall(SYS_ioctl, f->fd, TCGETS, &tio)) + f->lbf = -1; + return __stdio_write(f, buf, len); +} diff --git a/src/stdio/__toread.c b/src/stdio/__toread.c new file mode 100644 index 00000000..f00cc467 --- /dev/null +++ b/src/stdio/__toread.c @@ -0,0 +1,14 @@ +#include <stdio_impl.h> + +int __toread(FILE *f) +{ + f->mode |= f->mode-1; + if (f->wpos > f->buf) f->write(f, 0, 0); + f->wpos = f->wbase = f->wend = 0; + if (f->flags & (F_EOF|F_NORD)) { + if (f->flags & F_NORD) f->flags |= F_ERR; + return EOF; + } + f->rpos = f->rend = f->buf; + return 0; +} diff --git a/src/stdio/__towrite.c b/src/stdio/__towrite.c new file mode 100644 index 00000000..b4587419 --- /dev/null +++ b/src/stdio/__towrite.c @@ -0,0 +1,21 @@ +#include "stdio_impl.h" + +int __towrite(FILE *f) +{ + f->mode |= f->mode-1; + if (f->flags & (F_NOWR)) { + f->flags |= F_ERR; + return EOF; + } + /* Clear read buffer (easier than summoning nasal demons) */ + f->rpos = f->rend = 0; + + /* Activate write through the buffer. */ + f->wpos = f->wbase = f->buf; + f->wend = f->buf + f->buf_size; + + return 0; +} + +/* Link flush-on-exit code iff any stdio write functions are linked. */ +int (*const __fflush_on_exit)(FILE *) = fflush; diff --git a/src/stdio/__uflow.c b/src/stdio/__uflow.c index 5a51d610..544dda98 100644 --- a/src/stdio/__uflow.c +++ b/src/stdio/__uflow.c @@ -1,7 +1,11 @@ #include "stdio_impl.h" +/* This function will never be called if there is already data + * buffered for reading. Thus we can get by with very few branches. */ + int __uflow(FILE *f) { - if (__underflow(f) < 0) return EOF; - else return *f->rpos++; + unsigned char c = EOF; + if (f->rend || !__toread(f)) f->read(f, &c, 1); + return c; } diff --git a/src/stdio/__underflow.c b/src/stdio/__underflow.c deleted file mode 100644 index b769f4e4..00000000 --- a/src/stdio/__underflow.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "stdio_impl.h" - -int __underflow(FILE *f) -{ - ssize_t cnt; - - /* Read from buffer (Do we ever get called when this is true??) */ - if (f->rpos < f->rstop) return *f->rpos; - - /* Initialize if we're not already reading */ - if (!f->rstop) { - /* Fail immediately if unreadable, eof, or error state. */ - if (f->flags & (F_EOF|F_ERR|F_NORD)) return EOF; - - /* Set byte orientation -1,0=>-1; 1=>1 */ - f->mode |= f->mode-1; - - /* Flush any unwritten output; fail on error. */ - if (f->wpos > f->buf && __oflow(f)) return EOF; - - /* Disallow writes to buffer. */ - f->wstop = 0; - } - - /* Perform the underlying read operation */ - if ((cnt=f->read(f, f->buf, f->buf_size)) + 1 <= 1) { - /* Set flags and leave read mode */ - f->flags |= F_EOF | (cnt & F_ERR); - f->rpos = f->rend = f->rstop = 0; - return EOF; - } - - /* Setup buffer pointers for reading from buffer */ - f->rpos = f->buf; - f->rend = f->rstop = f->buf + cnt; - - return *f->rpos; -} diff --git a/src/stdio/fflush.c b/src/stdio/fflush.c index cf3f5b0e..cdbd39bc 100644 --- a/src/stdio/fflush.c +++ b/src/stdio/fflush.c @@ -2,17 +2,23 @@ static int __fflush_unlocked(FILE *f) { - /* If writing, flush output. */ - if (f->wpos > f->buf && __oflow(f)) return -1; + /* If writing, flush output */ + if (f->wpos > f->wbase) { + f->write(f, 0, 0); + if (!f->wpos) return EOF; + } /* If reading, sync position, per POSIX */ if (f->rpos < f->rend) f->seek(f, f->rpos-f->rend, SEEK_CUR); - f->rpos = f->rend; + + /* Clear read and write modes */ + f->wpos = f->wbase = f->wend = 0; + f->rpos = f->rend = 0; /* Hook for special behavior on flush */ if (f->flush) f->flush(f); - return (f->flags & F_ERR) ? EOF : 0; + return 0; } /* stdout.c will override this if linked */ @@ -36,9 +42,9 @@ int fflush(FILE *f) OFLLOCK(); for (f=ofl_head; f; f=next) { FLOCK(f); - OFLUNLOCK(); + //OFLUNLOCK(); r |= __fflush_unlocked(f); - OFLLOCK(); + //OFLLOCK(); next = f->next; FUNLOCK(f); } diff --git a/src/stdio/fgetc.c b/src/stdio/fgetc.c index 3a7f1e30..da638684 100644 --- a/src/stdio/fgetc.c +++ b/src/stdio/fgetc.c @@ -4,7 +4,9 @@ int fgetc(FILE *f) { int c; FLOCK(f); - c = f->rpos < f->rstop ? *f->rpos++ : __uflow(f); + c = getc_unlocked(f); FUNLOCK(f); return c; } + +weak_alias(fgetc, getc); diff --git a/src/stdio/fgets.c b/src/stdio/fgets.c index 7939303e..3135a69a 100644 --- a/src/stdio/fgets.c +++ b/src/stdio/fgets.c @@ -7,12 +7,17 @@ char *fgets(char *s, int n, FILE *f) char *p = s; unsigned char *z; size_t k; + int c; - if (!n--) return 0; + if (n--<=1) { + if (n) return 0; + *s = 0; + return s; + } FLOCK(f); - while (n && !feof(f)) { + while (n) { z = memchr(f->rpos, '\n', f->rend - f->rpos); k = z ? z - f->rpos + 1 : f->rend - f->rpos; k = MIN(k, n); @@ -20,15 +25,19 @@ char *fgets(char *s, int n, FILE *f) f->rpos += k; p += k; n -= k; - if (z) break; - __underflow(f); + if (z || !n) break; + if ((c = getc_unlocked(f)) < 0) { + if (p==s || !feof(f)) s = 0; + break; + } + n--; + if ((*p++ = c) == '\n') break; } *p = 0; - if (ferror(f)) p = s; FUNLOCK(f); - return (p == s) ? 0 : s; + return s; } weak_alias(fgets, fgets_unlocked); diff --git a/src/stdio/fgetwc.c b/src/stdio/fgetwc.c index 77b30fd1..5e420594 100644 --- a/src/stdio/fgetwc.c +++ b/src/stdio/fgetwc.c @@ -23,9 +23,9 @@ wint_t __fgetwc_unlocked(FILE *f) } } else l = -2; - /* Convert character byte-by-byte from __uflow */ + /* Convert character byte-by-byte */ while (l == -2) { - b = c = __uflow(f); + b = c = getc_unlocked(f); if (c < 0) { if (!mbsinit(&st)) errno = EILSEQ; return WEOF; diff --git a/src/stdio/fputc.c b/src/stdio/fputc.c index ec859385..98d0a20a 100644 --- a/src/stdio/fputc.c +++ b/src/stdio/fputc.c @@ -3,8 +3,9 @@ int fputc(int c, FILE *f) { FLOCK(f); - if (c != f->lbf && f->wpos + 1 < f->wend) *f->wpos++ = c; - else c = __overflow(f, c); + c = putc_unlocked(c, f); FUNLOCK(f); return c; } + +weak_alias(fputc, putc); diff --git a/src/stdio/fputwc.c b/src/stdio/fputwc.c index ec49b5c6..292a53fb 100644 --- a/src/stdio/fputwc.c +++ b/src/stdio/fputwc.c @@ -8,8 +8,7 @@ wint_t __fputwc_unlocked(wchar_t c, FILE *f) f->mode |= f->mode+1; if (isascii(c)) { - if (c != f->lbf && f->wpos + 1 < f->wend) *f->wpos++ = c; - else c = __overflow(f, c); + c = putc_unlocked(c, f); } else if (f->wpos + MB_LEN_MAX < f->wend) { l = wctomb((void *)f->wpos, c); if (l < 0) c = WEOF; diff --git a/src/stdio/fread.c b/src/stdio/fread.c index 0fa0b2aa..8105fe99 100644 --- a/src/stdio/fread.c +++ b/src/stdio/fread.c @@ -12,38 +12,31 @@ size_t fread(void *destv, size_t size, size_t nmemb, FILE *f) FLOCK(f); - for (;;) { + if (f->rend - f->rpos > 0) { /* First exhaust the buffer. */ k = MIN(f->rend - f->rpos, l); memcpy(dest, f->rpos, k); f->rpos += k; dest += k; l -= k; - - /* Stop on EOF or errors */ - if (f->flags & (F_EOF|F_ERR|F_NORD)) goto eof; - - /* Done? Or going unbuffered? */ - if (!l || l > f->buf_size/2) break; - - /* Otherwise, refill & read thru buffer. */ - __underflow(f); + } + + if (!l) { + FUNLOCK(f); + return nmemb; } /* Read the remainder directly */ for (; l; l-=k, dest+=k) { k = f->read(f, dest, l); if (k+1<=1) { - f->flags |= F_EOF | (F_ERR & k); - goto eof; + FUNLOCK(f); + return (len-l)/size; } } FUNLOCK(f); return nmemb; -eof: - FUNLOCK(f); - return (len-l)/size; } weak_alias(fread, fread_unlocked); diff --git a/src/stdio/fseek.c b/src/stdio/fseek.c index bfaad375..8d9da440 100644 --- a/src/stdio/fseek.c +++ b/src/stdio/fseek.c @@ -5,17 +5,25 @@ int __fseeko_unlocked(FILE *f, off_t off, int whence) /* Adjust relative offset for unread data in buffer, if any. */ if (whence == SEEK_CUR) off -= f->rend - f->rpos; - /* If writing, flush output. */ - if (f->wpos > f->buf && __oflow(f)) return -1; + /* Flush write buffer, and report error on failure. */ + if (f->wpos > f->wbase) { + f->write(f, 0, 0); + if (!f->wpos) return -1; + } - /* Perform the underlying seek operation. */ - if (f->seek(f, off, whence) < 0) return -1; + /* Leave writing mode */ + f->wpos = f->wbase = f->wend = 0; + + /* Perform the underlying seek. */ + if (f->seek(f, off, whence) < 0) { + f->flags |= F_ERR; + return -1; + } /* If seek succeeded, file is seekable and we discard read buffer. */ - f->rpos = f->rend = f->rstop = 0; + f->rpos = f->rend = 0; f->flags &= ~F_EOF; - FUNLOCK(f); return 0; } diff --git a/src/stdio/fwrite.c b/src/stdio/fwrite.c index 23974fe1..02908c4b 100644 --- a/src/stdio/fwrite.c +++ b/src/stdio/fwrite.c @@ -2,50 +2,36 @@ size_t __fwritex(const unsigned char *s, size_t l, FILE *f) { - size_t i = 0; - size_t k = f->wend - f->wpos; + size_t i=0; + + if (!f->wend && __towrite(f)) return 0; + + if (l > f->wend - f->wpos) return f->write(f, s, l); - /* Handle line-buffered mode by breaking into 2 parts */ if (f->lbf >= 0) { /* Match /^(.*\n|)/ */ for (i=l; i && s[i-1] != '\n'; i--); if (i) { - f->lbf = EOF; - __fwritex(s, i, f); - f->lbf = '\n'; - __oflow(f); - return ferror(f) ? 0 : i + __fwritex(s+i, l-i, f); + if (f->write(f, s, i) < i) + return i; + s += i; + l -= i; } } - /* Buffer initial segment */ - if (k > l) k = l; - memcpy(f->wpos, s, k); - f->wpos += k; - if (f->wpos < f->wend) return l; - - /* If there's work left to do, flush buffer */ - __oflow(f); - if (ferror(f)) return 0; - - /* If the remainder will not fit in buffer, write it directly */ - if (l - k >= f->wend - f->wpos) - return k + f->write(f, s+k, l-k); - - /* Otherwise, buffer the remainder */ - memcpy(f->wpos, s+k, l-k); - f->wpos += l-k; - return l; + memcpy(f->wpos, s, l); + f->wpos += l; + return l+i; } size_t fwrite(const void *src, size_t size, size_t nmemb, FILE *f) { - size_t l = size*nmemb; + size_t k, l = size*nmemb; if (!l) return l; FLOCK(f); - l = __fwritex(src, l, f); + k = __fwritex(src, l, f); FUNLOCK(f); - return l/size; + return k==l ? nmemb : l/size; } weak_alias(fwrite, fwrite_unlocked); diff --git a/src/stdio/getc.c b/src/stdio/getc.c deleted file mode 100644 index b739b0a5..00000000 --- a/src/stdio/getc.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "stdio_impl.h" - -int getc(FILE *f) -{ - return fgetc(f); -} diff --git a/src/stdio/getc_unlocked.c b/src/stdio/getc_unlocked.c index 629223ea..203a1081 100644 --- a/src/stdio/getc_unlocked.c +++ b/src/stdio/getc_unlocked.c @@ -1,8 +1,8 @@ #include "stdio_impl.h" -int getc_unlocked(FILE *f) +int (getc_unlocked)(FILE *f) { - return f->rpos < f->rstop ? *f->rpos++ : __uflow(f); + return getc_unlocked(f); } weak_alias (getc_unlocked, fgetc_unlocked); diff --git a/src/stdio/getchar_unlocked.c b/src/stdio/getchar_unlocked.c index 299cb958..355ac318 100644 --- a/src/stdio/getchar_unlocked.c +++ b/src/stdio/getchar_unlocked.c @@ -2,5 +2,5 @@ int getchar_unlocked(void) { - return stdin->rpos < stdin->rstop ? *stdin->rpos++ : __uflow(stdin); + return getc_unlocked(stdin); } diff --git a/src/stdio/getdelim.c b/src/stdio/getdelim.c index f770d20b..20d345d1 100644 --- a/src/stdio/getdelim.c +++ b/src/stdio/getdelim.c @@ -8,6 +8,7 @@ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) unsigned char *z; size_t k; size_t i=0; + int c; if (!n || !s) { errno = EINVAL; @@ -18,16 +19,16 @@ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) FLOCK(f); - while (!feof(f)) { + for (;;) { z = memchr(f->rpos, delim, f->rend - f->rpos); k = z ? z - f->rpos + 1 : f->rend - f->rpos; if (i+k >= *n) { - if (k >= SIZE_MAX-i) goto oom; - *n = i+k+1; - if (*n < SIZE_MAX/2) *n *= 2; + if (k >= SIZE_MAX/2-i) goto oom; + *n = i+k+2; + if (*n < SIZE_MAX/4) *n *= 2; tmp = realloc(*s, *n); if (!tmp) { - *n = i+k+1; + *n = i+k+2; tmp = realloc(*s, *n); if (!tmp) goto oom; } @@ -37,23 +38,22 @@ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) f->rpos += k; i += k; if (z) break; - __underflow(f); + if ((c = getc_unlocked(f)) == EOF) { + if (!i || !feof(f)) { + FUNLOCK(f); + return -1; + } + break; + } + if (((*s)[i++] = c) == delim) break; } (*s)[i] = 0; - if (feof(f) || ferror(f)) { - FUNLOCK(f); - return -1; - } FUNLOCK(f); - if (i > SSIZE_MAX) { - errno = EOVERFLOW; - return -1; - } - return i; oom: + FUNLOCK(f); errno = ENOMEM; return -1; } diff --git a/src/stdio/putc.c b/src/stdio/putc.c deleted file mode 100644 index 3c9dc11e..00000000 --- a/src/stdio/putc.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "stdio_impl.h" - -int putc(int c, FILE *f) -{ - return fputc(c, f); -} - -weak_alias(putc, _IO_putc); diff --git a/src/stdio/putc_unlocked.c b/src/stdio/putc_unlocked.c index f01da717..b47876c9 100644 --- a/src/stdio/putc_unlocked.c +++ b/src/stdio/putc_unlocked.c @@ -1,8 +1,8 @@ #include "stdio_impl.h" -int putc_unlocked(int c, FILE *f) +int (putc_unlocked)(int c, FILE *f) { - return f->wpos < f->wstop ? (*f->wpos++ = c) : __overflow(f, c); + return putc_unlocked(c, f); } weak_alias(putc_unlocked, fputc_unlocked); diff --git a/src/stdio/putchar_unlocked.c b/src/stdio/putchar_unlocked.c index 72d47d15..8b5d0603 100644 --- a/src/stdio/putchar_unlocked.c +++ b/src/stdio/putchar_unlocked.c @@ -2,6 +2,5 @@ int putchar_unlocked(int c) { - return stdout->wpos < stdout->wstop ? - (*stdout->wpos++ = c) : __overflow(stdout, c); + return putc_unlocked(c, stdout); } diff --git a/src/stdio/setvbuf.c b/src/stdio/setvbuf.c index 2985d3f1..6dea0ebf 100644 --- a/src/stdio/setvbuf.c +++ b/src/stdio/setvbuf.c @@ -14,9 +14,11 @@ int setvbuf(FILE *f, char *buf, int type, size_t size) f->lbf = EOF; if (type == _IONBF) - f->buf_size = 1; + f->buf_size = 0; else if (type == _IOLBF) f->lbf = '\n'; + f->flags |= F_SVB; + return 0; } diff --git a/src/stdio/stderr.c b/src/stdio/stderr.c index 4a79d4e7..3bdaffbc 100644 --- a/src/stdio/stderr.c +++ b/src/stdio/stderr.c @@ -1,9 +1,9 @@ #include "stdio_impl.h" -static unsigned char buf[1+UNGET]; +static unsigned char buf[UNGET]; static FILE f = { .buf = buf+UNGET, - .buf_size = 1, + .buf_size = 0, .fd = 2, .flags = F_PERM | F_NORD, .write = __stdio_write, diff --git a/src/stdio/stdout.c b/src/stdio/stdout.c index bf6eea6c..552d729e 100644 --- a/src/stdio/stdout.c +++ b/src/stdio/stdout.c @@ -7,7 +7,7 @@ static FILE f = { .fd = 1, .flags = F_PERM | F_NORD, .lbf = '\n', - .write = __stdio_write, + .write = __stdout_write, .seek = __stdio_seek, .close = __stdio_close, }; diff --git a/src/stdio/ungetc.c b/src/stdio/ungetc.c index 07181684..7f56f8d5 100644 --- a/src/stdio/ungetc.c +++ b/src/stdio/ungetc.c @@ -6,25 +6,11 @@ int ungetc(int c, FILE *f) FLOCK(f); - /* Fail if unreadable or writing and unable to flush */ - if ((f->flags & (F_ERR|F_NORD)) || (f->wpos && __oflow(f))) { + if ((!f->rend && __toread(f)) || f->rpos <= f->buf - UNGET) { FUNLOCK(f); return EOF; } - /* Clear write mode */ - f->wbase = f->wpos = f->wstop = f->wend = 0; - - /* Put the file in read mode */ - if (!f->rpos) f->rpos = f->rend = f->buf; - - /* If unget buffer is already full, fail. */ - if (f->rpos <= f->buf - UNGET) { - FUNLOCK(f); - return EOF; - } - - /* Put a byte back into the buffer */ *--f->rpos = c; f->flags &= ~F_EOF; diff --git a/src/stdio/ungetwc.c b/src/stdio/ungetwc.c index 6871d034..5282fee1 100644 --- a/src/stdio/ungetwc.c +++ b/src/stdio/ungetwc.c @@ -15,29 +15,14 @@ wint_t ungetwc(wint_t c, FILE *f) f->mode |= f->mode+1; - /* Fail if unreadable or writing and unable to flush */ - if ((f->flags & (F_ERR|F_NORD)) || (f->wpos && __oflow(f))) { + if ((!f->rend && __toread(f)) || f->rpos < f->buf - UNGET + l) { FUNLOCK(f); return EOF; } - /* Clear write mode */ - f->wpos = f->wstop = f->wend = 0; - - /* Put the file in read mode */ - if (!f->rpos) f->rpos = f->rend = f->buf; - - /* If unget buffer is nonempty, fail. */ - if (f->rpos < f->buf) { - FUNLOCK(f); - return WEOF; - } - - /* Put character back into the buffer */ if (isascii(c)) *--f->rpos = c; else memcpy(f->rpos -= l, mbc, l); - /* Clear EOF */ f->flags &= ~F_EOF; FUNLOCK(f); diff --git a/src/stdio/vdprintf.c b/src/stdio/vdprintf.c index 68562e05..faf9536a 100644 --- a/src/stdio/vdprintf.c +++ b/src/stdio/vdprintf.c @@ -11,9 +11,9 @@ int vdprintf(int fd, const char *fmt, va_list ap) unsigned char buf[BUFSIZ]; FILE f = { .fd = fd, .lbf = EOF, .write = wrap_write, - .buf = buf+UNGET, .buf_size = sizeof buf - UNGET + .buf = buf+UNGET, .buf_size = sizeof buf - UNGET, + .lock = -1 }; r = vfprintf(&f, fmt, ap); - __oflow(&f); - return r; + return fflush(&f) ? EOF : r; } diff --git a/src/stdio/vfprintf.c b/src/stdio/vfprintf.c index b6bb3bcf..57878c03 100644 --- a/src/stdio/vfprintf.c +++ b/src/stdio/vfprintf.c @@ -434,7 +434,7 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, /* Update output count, end loop when fmt is exhausted */ if (cnt >= 0) { if (l > INT_MAX - cnt) { - if (!ferror(f)) errno = EOVERFLOW; + errno = EOVERFLOW; cnt = -1; } else cnt += l; } diff --git a/src/stdio/vfscanf.c b/src/stdio/vfscanf.c index 69f45081..414c2a3d 100644 --- a/src/stdio/vfscanf.c +++ b/src/stdio/vfscanf.c @@ -9,7 +9,7 @@ static void f_read(rctx_t *r) { FILE *f = r->opaque; - if ((r->c = __uflow(f)) >= 0) r->l++; + if ((r->c = getc_unlocked(f)) >= 0) r->l++; } int vfscanf(FILE *f, const char *fmt, va_list ap) @@ -28,15 +28,8 @@ int vfscanf(FILE *f, const char *fmt, va_list ap) result = __scanf(&r, fmt2, ap); - if (r.u && r.c >= 0) { - /* This code takes care of the case where the caller performs - * a nonmatching scanf to leave a character in the unscan - * buffer, followed by an unget, followed by a scanf that - * matches zero characters. In this case the final 'unread' - * character must be returned to the unget buffer rather than - * the unscan buffer. */ - f->rpos--; - } + if (r.u && r.c >= 0) + ungetc(r.c, f); FUNLOCK(f); return result; 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; } diff --git a/src/stdio/vswprintf.c b/src/stdio/vswprintf.c index 2d9f2002..8e8f80ce 100644 --- a/src/stdio/vswprintf.c +++ b/src/stdio/vswprintf.c @@ -10,6 +10,8 @@ static size_t sw_write(FILE *f, const unsigned char *s, size_t l) size_t l0 = l; int i = 0; struct cookie *c = f->cookie; + if (s!=f->wbase && sw_write(f, f->wbase, f->wpos-f->wbase)==-1) + return -1; while (c->l && l && (i=mbtowc(c->ws, (void *)s, l))>=0) { s+=i; l-=i; @@ -41,6 +43,6 @@ int vswprintf(wchar_t *s, size_t n, const wchar_t *fmt, va_list ap) return -1; } r = vfwprintf(&f, fmt, ap); - __oflow(&f); + sw_write(&f, 0, 0); return r>=n ? -1 : r; } |