fix failure of ungetc and ungetwc to work on files in eof status
authorRich Felker <dalias@aerifal.cx>
Fri, 29 May 2015 03:08:12 +0000 (23:08 -0400)
committerRich Felker <dalias@aerifal.cx>
Fri, 29 May 2015 04:04:36 +0000 (00:04 -0400)
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
src/stdio/__toread.c
src/stdio/__uflow.c
src/stdio/ungetc.c
src/stdio/ungetwc.c

index 6cd7b0733a85a3e83deb5c14ed1f6616888d3480..5947344438b30559206d0966210455c28a9c6615 100644 (file)
@@ -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;
index 52624f3d4f2370edbbd8d40a418ee48d06b4a98d..b08f5bb440345efefc5dd0f4d8679e0a56eb91e0 100644 (file)
@@ -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);
index e28922c2ff461a960c37501d6cbe298fe8ca68f4..2a88bca6b123cc2143911783a09a9cac0ce53773 100644 (file)
@@ -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;
 }
index 7f56f8d5f07b79dba384f9ef02fe2b8c160b6467..180673a47663ad57b11d3e6525c35cc23858939a 100644 (file)
@@ -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;
        }
index 8cc85a6bfd2a00b615ecccdc96fbfc519e4b77f5..913f7168ed4935870b89b4f023640843b7d5b25d 100644 (file)
@@ -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;
        }