major stdio overhaul, using readv/writev, plus other changes
authorRich Felker <dalias@aerifal.cx>
Mon, 28 Mar 2011 05:14:44 +0000 (01:14 -0400)
committerRich Felker <dalias@aerifal.cx>
Mon, 28 Mar 2011 05:14:44 +0000 (01:14 -0400)
the biggest change in this commit is that stdio now uses readv to fill
the caller's buffer and the FILE buffer with a single syscall, and
likewise writev to flush the FILE buffer and write out the caller's
buffer in a single syscall.

making this change required fundamental architectural changes to
stdio, so i also made a number of other improvements in the process:

- the implementation no longer assumes that further io will fail
  following errors, and no longer blocks io when the error flag is set
  (though the latter could easily be changed back if desired)

- unbuffered mode is no longer implemented as a one-byte buffer. as a
  consequence, scanf unreading has to use ungetc, to the unget buffer
  has been enlarged to hold at least 2 wide characters.

- the FILE structure has been rearranged to maintain the locations of
  the fields that might be used in glibc getc/putc type macros, while
  shrinking the structure to save some space.

- error cases for fflush, fseek, etc. should be more correct.

- library-internal macros are used for getc_unlocked and putc_unlocked
  now, eliminating some ugly code duplication. __uflow and __overflow
  are no longer used anywhere but these macros. switch to read or
  write mode is also separated so the code can be better shared, e.g.
  with ungetc.

- lots of other small things.

37 files changed:
src/exit/exit.c
src/internal/stdio_impl.h
src/stdio/__overflow.c
src/stdio/__stdio_read.c
src/stdio/__stdio_seek.c
src/stdio/__stdio_write.c
src/stdio/__stdout_write.c [new file with mode: 0644]
src/stdio/__toread.c [new file with mode: 0644]
src/stdio/__towrite.c [new file with mode: 0644]
src/stdio/__uflow.c
src/stdio/__underflow.c [deleted file]
src/stdio/fflush.c
src/stdio/fgetc.c
src/stdio/fgets.c
src/stdio/fgetwc.c
src/stdio/fputc.c
src/stdio/fputwc.c
src/stdio/fread.c
src/stdio/fseek.c
src/stdio/fwrite.c
src/stdio/getc.c [deleted file]
src/stdio/getc_unlocked.c
src/stdio/getchar_unlocked.c
src/stdio/getdelim.c
src/stdio/putc.c [deleted file]
src/stdio/putc_unlocked.c
src/stdio/putchar_unlocked.c
src/stdio/setvbuf.c
src/stdio/stderr.c
src/stdio/stdout.c
src/stdio/ungetc.c
src/stdio/ungetwc.c
src/stdio/vdprintf.c
src/stdio/vfprintf.c
src/stdio/vfscanf.c
src/stdio/vsnprintf.c
src/stdio/vswprintf.c

index d0c1bfc..bfdb392 100644 (file)
@@ -17,7 +17,7 @@ void exit(int code)
 
        /* Only do atexit & stdio flush if they were actually used */
        if (__funcs_on_exit) __funcs_on_exit();
-       if (__fflush_on_exit) __fflush_on_exit(0);
+       if (__fflush_on_exit) __fflush_on_exit((void *)0);
 
        /* Destructor s**t is kept separate from atexit to avoid bloat */
        if (libc.fini) libc.fini();
index b977df6..90a8214 100644 (file)
 #include <sys/wait.h>
 #include <math.h>
 #include <float.h>
+#include <sys/uio.h>
 #include "syscall.h"
 #include "libc.h"
 
-#define UNGET 4
+#define UNGET 8
 
 #define FLOCK(f) ((libc.lockfile && (f)->lock>=0) ? (libc.lockfile((f)),0) : 0)
 #define FUNLOCK(f) ((f)->lockcount && (--(f)->lockcount || ((f)->lock=0)))
 #define F_NOWR 8
 #define F_EOF 16
 #define F_ERR 32
+#define F_SVB 64
 
 struct __FILE_s {
        unsigned flags;
-       unsigned char *rpos, *rstop;
-       unsigned char *rend, *wend;
-       unsigned char *wpos, *wstop;
+       unsigned char *rpos, *rend;
+       int (*close)(FILE *);
+       unsigned char *wend, *wpos;
+       unsigned char *mustbezero_1;
        unsigned char *wbase;
-       unsigned char *dummy01[3];
+       size_t (*read)(FILE *, unsigned char *, size_t);
+       size_t (*write)(FILE *, const unsigned char *, size_t);
+       off_t (*seek)(FILE *, off_t, int);
        unsigned char *buf;
        size_t buf_size;
        FILE *prev, *next;
@@ -46,26 +51,25 @@ struct __FILE_s {
        int pipe_pid;
        long dummy2;
        short dummy3;
-       char dummy4;
+       signed char mode;
        signed char lbf;
        int lock;
        int lockcount;
        void *cookie;
        off_t off;
        int (*flush)(FILE *);
-       void **wide_data; /* must be NULL */
-       size_t (*read)(FILE *, unsigned char *, size_t);
-       size_t (*write)(FILE *, const unsigned char *, size_t);
-       off_t (*seek)(FILE *, off_t, int);
-       int mode;
-       int (*close)(FILE *);
+       void *mustbezero_2;
 };
 
 size_t __stdio_read(FILE *, unsigned char *, size_t);
 size_t __stdio_write(FILE *, const unsigned char *, size_t);
+size_t __stdout_write(FILE *, const unsigned char *, size_t);
 off_t __stdio_seek(FILE *, off_t, int);
 int __stdio_close(FILE *);
 
+int __toread(FILE *);
+int __towrite(FILE *);
+
 int __overflow(FILE *, int);
 int __oflow(FILE *);
 int __uflow(FILE *);
@@ -87,6 +91,12 @@ FILE *__fdopen(int, const char *);
 #define feof(f) ((f)->flags & F_EOF)
 #define ferror(f) ((f)->flags & F_ERR)
 
+#define getc_unlocked(f) \
+       ( ((f)->rpos < (f)->rend) ? *(f)->rpos++ : __uflow((f)) )
+
+#define putc_unlocked(c, f) ( ((c)!=(f)->lbf && (f)->wpos<(f)->wend) \
+       ? *(f)->wpos++ = (c) : __overflow((f),(c)) )
+
 /* Caller-allocated FILE * operations */
 FILE *__fopen_rb_ca(const char *, FILE *, unsigned char *, size_t);
 int __fclose_ca(FILE *);
index e35104d..3bb3792 100644 (file)
@@ -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;
index d9bb326..a2e4cd6 100644 (file)
@@ -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;
 }
index c205ab8..35ae788 100644 (file)
@@ -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;
 }
index d4264ef..63d9c85 100644 (file)
@@ -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 (file)
index 0000000..4683ffc
--- /dev/null
@@ -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 (file)
index 0000000..f00cc46
--- /dev/null
@@ -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 (file)
index 0000000..b458741
--- /dev/null
@@ -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;
index 5a51d61..544dda9 100644 (file)
@@ -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 (file)
index b769f4e..0000000
+++ /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;
-}
index cf3f5b0..cdbd39b 100644 (file)
@@ -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);
        }
index 3a7f1e3..da63868 100644 (file)
@@ -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);
index 7939303..3135a69 100644 (file)
@@ -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);
index 77b30fd..5e42059 100644 (file)
@@ -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;
index ec85938..98d0a20 100644 (file)
@@ -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);
index ec49b5c..292a53f 100644 (file)
@@ -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;
index 0fa0b2a..8105fe9 100644 (file)
@@ -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);
index bfaad37..8d9da44 100644 (file)
@@ -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;
 }
 
index 23974fe..02908c4 100644 (file)
@@ -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 (file)
index b739b0a..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "stdio_impl.h"
-
-int getc(FILE *f)
-{
-       return fgetc(f);
-}
index 629223e..203a108 100644 (file)
@@ -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);
index 299cb95..355ac31 100644 (file)
@@ -2,5 +2,5 @@
 
 int getchar_unlocked(void)
 {
-       return stdin->rpos < stdin->rstop ? *stdin->rpos++ : __uflow(stdin);
+       return getc_unlocked(stdin);
 }
index f770d20..20d345d 100644 (file)
@@ -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 (file)
index 3c9dc11..0000000
+++ /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);
index f01da71..b47876c 100644 (file)
@@ -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);
index 72d47d1..8b5d060 100644 (file)
@@ -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);
 }
index 2985d3f..6dea0eb 100644 (file)
@@ -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;
 }
index 4a79d4e..3bdaffb 100644 (file)
@@ -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,
index bf6eea6..552d729 100644 (file)
@@ -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,
 };
index 0718168..7f56f8d 100644 (file)
@@ -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;
 
index 6871d03..5282fee 100644 (file)
@@ -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);
index 68562e0..faf9536 100644 (file)
@@ -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;
 }
index b6bb3bc..57878c0 100644 (file)
@@ -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;
                }
index 69f4508..414c2a3 100644 (file)
@@ -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;
index 1f316ca..ba17bd7 100644 (file)
@@ -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;
 }
index 2d9f200..8e8f80c 100644 (file)
@@ -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;
 }