提交 e3cd6c5c 编写于 作者: R Rich Felker

major stdio overhaul, using readv/writev, plus other changes

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.
上级 ea343364
......@@ -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();
......
......@@ -18,10 +18,11 @@
#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)))
......@@ -31,14 +32,18 @@
#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 *);
......
#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;
......@@ -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;
}
......@@ -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;
}
......@@ -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;
}
}
#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);
}
#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;
}
#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;
#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;
}
#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;
}
......@@ -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);
}
......
......@@ -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);
......@@ -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);
......@@ -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;
......
......@@ -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);
......@@ -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;
......
......@@ -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);
......@@ -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;
}
......
......@@ -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);
#include "stdio_impl.h"
int getc(FILE *f)
{
return fgetc(f);
}
#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);
......@@ -2,5 +2,5 @@
int getchar_unlocked(void)
{
return stdin->rpos < stdin->rstop ? *stdin->rpos++ : __uflow(stdin);
return getc_unlocked(stdin);
}
......@@ -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;
}
#include "stdio_impl.h"
int putc(int c, FILE *f)
{
return fputc(c, f);
}
weak_alias(putc, _IO_putc);
#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);
......@@ -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);
}
......@@ -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;
}
#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,
......
......@@ -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,
};
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
}
......@@ -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;
}
......
......@@ -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;
......
......@@ -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;
}
......@@ -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;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册