From 2b4fcfdacf93c3dfd6ac15e31790a9e154374679 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Thu, 28 May 2015 23:08:12 -0400 Subject: [PATCH] fix failure of ungetc and ungetwc to work on files in eof status these functions were written to handle clearing eof status, but failed to account for the __toread function's handling of eof. with this patch applied, __toread still returns EOF when the file is in eof status, so that read operations will fail, but it also sets up valid buffer pointers for read mode, which are set to the end of the buffer rather than the beginning in order to make the whole buffer available to ungetc/ungetwc. minor changes to __uflow were needed since it's now possible to have non-zero buffer pointers while in eof status. as made, these changes remove a 'fast path' bypassing the function call to __toread, which could be reintroduced with slightly different logic, but since ordinary files have a syscall in f->read, optimizing the code path does not seem worthwhile. the __stdio_read function is also updated not to zero the read buffer pointers on eof/error. while not necessary for correctness, this change avoids the overhead of calling __toread in ungetc after reaching eof, and it also reduces code size and increases consistency with the fmemopen read operation which does not zero the pointers. --- src/stdio/__stdio_read.c | 1 - src/stdio/__toread.c | 8 ++++---- src/stdio/__uflow.c | 6 +++--- src/stdio/ungetc.c | 3 ++- src/stdio/ungetwc.c | 3 ++- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/stdio/__stdio_read.c b/src/stdio/__stdio_read.c index 6cd7b073..59473444 100644 --- a/src/stdio/__stdio_read.c +++ b/src/stdio/__stdio_read.c @@ -21,7 +21,6 @@ size_t __stdio_read(FILE *f, unsigned char *buf, size_t len) pthread_cleanup_pop(0); if (cnt <= 0) { f->flags |= F_EOF ^ ((F_ERR^F_EOF) & cnt); - f->rpos = f->rend = 0; return cnt; } if (cnt <= iov[0].iov_len) return cnt; diff --git a/src/stdio/__toread.c b/src/stdio/__toread.c index 52624f3d..b08f5bb4 100644 --- a/src/stdio/__toread.c +++ b/src/stdio/__toread.c @@ -5,12 +5,12 @@ 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; + if (f->flags & F_NORD) { + f->flags |= F_ERR; return EOF; } - f->rpos = f->rend = f->buf; - return 0; + f->rpos = f->rend = f->buf + f->buf_size; + return (f->flags & F_EOF) ? EOF : 0; } void __stdio_exit_needed(void); diff --git a/src/stdio/__uflow.c b/src/stdio/__uflow.c index e28922c2..2a88bca6 100644 --- a/src/stdio/__uflow.c +++ b/src/stdio/__uflow.c @@ -1,11 +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. */ +/* This function assumes it will never be called if there is already + * data buffered for reading. */ int __uflow(FILE *f) { unsigned char c; - if ((f->rend || !__toread(f)) && f->read(f, &c, 1)==1) return c; + if (!__toread(f) && f->read(f, &c, 1)==1) return c; return EOF; } diff --git a/src/stdio/ungetc.c b/src/stdio/ungetc.c index 7f56f8d5..180673a4 100644 --- a/src/stdio/ungetc.c +++ b/src/stdio/ungetc.c @@ -6,7 +6,8 @@ int ungetc(int c, FILE *f) FLOCK(f); - if ((!f->rend && __toread(f)) || f->rpos <= f->buf - UNGET) { + if (!f->rpos) __toread(f); + if (!f->rpos || f->rpos <= f->buf - UNGET) { FUNLOCK(f); return EOF; } diff --git a/src/stdio/ungetwc.c b/src/stdio/ungetwc.c index 8cc85a6b..913f7168 100644 --- a/src/stdio/ungetwc.c +++ b/src/stdio/ungetwc.c @@ -19,7 +19,8 @@ wint_t ungetwc(wint_t c, FILE *f) f->mode |= f->mode+1; - if ((!f->rend && __toread(f)) || f->rpos < f->buf - UNGET + l) { + if (!f->rpos) __toread(f); + if (!f->rpos || f->rpos < f->buf - UNGET + l) { FUNLOCK(f); return EOF; } -- 2.25.1