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 d0c1bfc1d5b68a08873a367549ad09d4b310ea91..bfdb3923fc98b6f515d8cab5cf90dcba7de1b4f6 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 b977df6829013714e1bb52e753ade4f9abadc12f..90a8214b10c57bfd75c6b9872e864ef28744006c 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 e35104deb001de5a0ea2b67d0531a36af4bdd0b4..3bb37923abb847787878cf868d86bce570fb267e 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 d9bb3269c41821031be8e5c8e1d95020fbe03218..a2e4cd62fe9f35dbae98d1ade1fec32d7fe4ebf1 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 c205ab8206a36341fada87615b0af7706ed26dfd..35ae788cd1aface51136b4d53ffa6e493e98a37d 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 d4264eff29b10a27adc1709e67dc3bd4bf763a79..63d9c858db371486dcffb75279dbca559c2ca5ce 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 5a51d6102a9a073f4cf31184eed5628e1b2728ba..544dda982dbf0abf4ae703825d91e84ffd2bd94a 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 cf3f5b0efcc11d25b232ae2d8c2076a252df0fb7..cdbd39bc3ab1ef9ef2ccaa6a9eb0c310a6f338b3 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 3a7f1e304850f46c5076577750126eb8fe20354d..da6386842f99fc869d244f3c0b9e71c3465910ef 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 7939303e60b17df093748d8d756765074f360561..3135a69a2b9ebfe7b8475af708295c9e7e626056 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 77b30fd18df076664895f8d6eff2182039cccea2..5e420594da00dbf9bce0105508f6f3ef11487f08 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 ec859385644cc9ed219c29dbb49f85cfb0cf667b..98d0a20aca814bddf897c8049aff06af9c4cf75e 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 ec49b5c6a5cbc813421e6cc065a47e17327b9abb..292a53fb8ed46b0ae14ce609862300f402c17f1a 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 0fa0b2aa1d24f528d1704b53e62cb45b54229c48..8105fe995d884ffeae8cf4b0492a8baef35e2905 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 bfaad375ddd55cc9ff3a13ad359da5ca017d78e2..8d9da4408fbfe22ed5cef60c7c05354e53e40adb 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 23974fe1f3fb5e19027bda0019b3360e352d0540..02908c4b9ad39176d4b5f6591a0b3d72518d20d5 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 629223eaa97a401354a8889b9d84861706939fdc..203a1081f9981a28668b44ffaee47a3bb13a74e6 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 299cb9585b354ca86bc4541ebe3114d16451c77a..355ac318d43d2371af6399bf7652d2baabf5d9b4 100644 (file)
@@ -2,5 +2,5 @@
 
 int getchar_unlocked(void)
 {
-       return stdin->rpos < stdin->rstop ? *stdin->rpos++ : __uflow(stdin);
+       return getc_unlocked(stdin);
 }
index f770d20bd975ca9663260ac351e2c2021945507c..20d345d1da604114a33640e94fbacffd65e91887 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 f01da7172644f6c6501b9b260220a7df00ef2640..b47876c9f7541580f13f081f97d59fe3625073a7 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 72d47d1539463781167fd8c06cee1bbdcb7861a3..8b5d06034162679e604e3e4666ccf2a423050d1a 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 2985d3f1b6b8bcfef43e8bc6308168ce5caa53d0..6dea0ebf558c4076b95798c449a8b79994109d2a 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 4a79d4e7b36a47f563d714e19e9b8273b7f22449..3bdaffbc13022cdf333e1fe65235c48b5dbdeade 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 bf6eea6c0b25408d4af99f10d9daf3dc33636bd1..552d729e48f399f3f3b6a28649a2c75691c0faf4 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 0718168427603259924da29e38e4fb8d30b49302..7f56f8d5f07b79dba384f9ef02fe2b8c160b6467 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 6871d0344a037314727235a99d49262fd77a4fbb..5282fee14577af9109768272e3265a806c58541f 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 68562e052bf28de8cd060b3d7a668d0d74b8532f..faf9536a7d529ee488c95d40c82b342b402c2a99 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 b6bb3bcf481936113e5453e9ab7d03d3ac7ae14d..57878c031f418d78d292eb53ea10ea2170b22b0a 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 69f45081bf83dda70b42157ac0bc2284a399904e..414c2a3d79e088e1e6919e99689012ef7ec6cdfa 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 1f316ca48207f986a6dd0617269e85f3e977fc06..ba17bd7d84079eb5bce14e5d7f341b267975dc17 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 2d9f2002bec26ac34db0b0e21a97c52ad7c009cf..8e8f80ce9cf5a18dd9e282e4ece80d307590ecef 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;
 }