diff options
37 files changed, 258 insertions, 281 deletions
| diff --git a/src/exit/exit.c b/src/exit/exit.c index d0c1bfc1..bfdb3923 100644 --- a/src/exit/exit.c +++ b/src/exit/exit.c @@ -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(); diff --git a/src/internal/stdio_impl.h b/src/internal/stdio_impl.h index b977df68..90a8214b 100644 --- a/src/internal/stdio_impl.h +++ b/src/internal/stdio_impl.h @@ -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 *); diff --git a/src/stdio/__overflow.c b/src/stdio/__overflow.c index e35104de..3bb37923 100644 --- a/src/stdio/__overflow.c +++ b/src/stdio/__overflow.c @@ -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; diff --git a/src/stdio/__stdio_read.c b/src/stdio/__stdio_read.c index d9bb3269..a2e4cd62 100644 --- a/src/stdio/__stdio_read.c +++ b/src/stdio/__stdio_read.c @@ -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;  } diff --git a/src/stdio/__stdio_seek.c b/src/stdio/__stdio_seek.c index c205ab82..35ae788c 100644 --- a/src/stdio/__stdio_seek.c +++ b/src/stdio/__stdio_seek.c @@ -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;  } diff --git a/src/stdio/__stdio_write.c b/src/stdio/__stdio_write.c index d4264eff..63d9c858 100644 --- a/src/stdio/__stdio_write.c +++ b/src/stdio/__stdio_write.c @@ -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 index 00000000..4683ffc3 --- /dev/null +++ b/src/stdio/__stdout_write.c @@ -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 index 00000000..f00cc467 --- /dev/null +++ b/src/stdio/__toread.c @@ -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 index 00000000..b4587419 --- /dev/null +++ b/src/stdio/__towrite.c @@ -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; diff --git a/src/stdio/__uflow.c b/src/stdio/__uflow.c index 5a51d610..544dda98 100644 --- a/src/stdio/__uflow.c +++ b/src/stdio/__uflow.c @@ -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 index b769f4e4..00000000 --- a/src/stdio/__underflow.c +++ /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; -} diff --git a/src/stdio/fflush.c b/src/stdio/fflush.c index cf3f5b0e..cdbd39bc 100644 --- a/src/stdio/fflush.c +++ b/src/stdio/fflush.c @@ -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);  	} diff --git a/src/stdio/fgetc.c b/src/stdio/fgetc.c index 3a7f1e30..da638684 100644 --- a/src/stdio/fgetc.c +++ b/src/stdio/fgetc.c @@ -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); diff --git a/src/stdio/fgets.c b/src/stdio/fgets.c index 7939303e..3135a69a 100644 --- a/src/stdio/fgets.c +++ b/src/stdio/fgets.c @@ -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); diff --git a/src/stdio/fgetwc.c b/src/stdio/fgetwc.c index 77b30fd1..5e420594 100644 --- a/src/stdio/fgetwc.c +++ b/src/stdio/fgetwc.c @@ -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; diff --git a/src/stdio/fputc.c b/src/stdio/fputc.c index ec859385..98d0a20a 100644 --- a/src/stdio/fputc.c +++ b/src/stdio/fputc.c @@ -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); diff --git a/src/stdio/fputwc.c b/src/stdio/fputwc.c index ec49b5c6..292a53fb 100644 --- a/src/stdio/fputwc.c +++ b/src/stdio/fputwc.c @@ -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; diff --git a/src/stdio/fread.c b/src/stdio/fread.c index 0fa0b2aa..8105fe99 100644 --- a/src/stdio/fread.c +++ b/src/stdio/fread.c @@ -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); diff --git a/src/stdio/fseek.c b/src/stdio/fseek.c index bfaad375..8d9da440 100644 --- a/src/stdio/fseek.c +++ b/src/stdio/fseek.c @@ -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;  } diff --git a/src/stdio/fwrite.c b/src/stdio/fwrite.c index 23974fe1..02908c4b 100644 --- a/src/stdio/fwrite.c +++ b/src/stdio/fwrite.c @@ -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 index b739b0a5..00000000 --- a/src/stdio/getc.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "stdio_impl.h" - -int getc(FILE *f) -{ -	return fgetc(f); -} diff --git a/src/stdio/getc_unlocked.c b/src/stdio/getc_unlocked.c index 629223ea..203a1081 100644 --- a/src/stdio/getc_unlocked.c +++ b/src/stdio/getc_unlocked.c @@ -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); diff --git a/src/stdio/getchar_unlocked.c b/src/stdio/getchar_unlocked.c index 299cb958..355ac318 100644 --- a/src/stdio/getchar_unlocked.c +++ b/src/stdio/getchar_unlocked.c @@ -2,5 +2,5 @@  int getchar_unlocked(void)  { -	return stdin->rpos < stdin->rstop ? *stdin->rpos++ : __uflow(stdin); +	return getc_unlocked(stdin);  } diff --git a/src/stdio/getdelim.c b/src/stdio/getdelim.c index f770d20b..20d345d1 100644 --- a/src/stdio/getdelim.c +++ b/src/stdio/getdelim.c @@ -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 index 3c9dc11e..00000000 --- a/src/stdio/putc.c +++ /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); diff --git a/src/stdio/putc_unlocked.c b/src/stdio/putc_unlocked.c index f01da717..b47876c9 100644 --- a/src/stdio/putc_unlocked.c +++ b/src/stdio/putc_unlocked.c @@ -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); diff --git a/src/stdio/putchar_unlocked.c b/src/stdio/putchar_unlocked.c index 72d47d15..8b5d0603 100644 --- a/src/stdio/putchar_unlocked.c +++ b/src/stdio/putchar_unlocked.c @@ -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);  } diff --git a/src/stdio/setvbuf.c b/src/stdio/setvbuf.c index 2985d3f1..6dea0ebf 100644 --- a/src/stdio/setvbuf.c +++ b/src/stdio/setvbuf.c @@ -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;  } diff --git a/src/stdio/stderr.c b/src/stdio/stderr.c index 4a79d4e7..3bdaffbc 100644 --- a/src/stdio/stderr.c +++ b/src/stdio/stderr.c @@ -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, diff --git a/src/stdio/stdout.c b/src/stdio/stdout.c index bf6eea6c..552d729e 100644 --- a/src/stdio/stdout.c +++ b/src/stdio/stdout.c @@ -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,  }; diff --git a/src/stdio/ungetc.c b/src/stdio/ungetc.c index 07181684..7f56f8d5 100644 --- a/src/stdio/ungetc.c +++ b/src/stdio/ungetc.c @@ -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; diff --git a/src/stdio/ungetwc.c b/src/stdio/ungetwc.c index 6871d034..5282fee1 100644 --- a/src/stdio/ungetwc.c +++ b/src/stdio/ungetwc.c @@ -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); diff --git a/src/stdio/vdprintf.c b/src/stdio/vdprintf.c index 68562e05..faf9536a 100644 --- a/src/stdio/vdprintf.c +++ b/src/stdio/vdprintf.c @@ -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;  } diff --git a/src/stdio/vfprintf.c b/src/stdio/vfprintf.c index b6bb3bcf..57878c03 100644 --- a/src/stdio/vfprintf.c +++ b/src/stdio/vfprintf.c @@ -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;  		} diff --git a/src/stdio/vfscanf.c b/src/stdio/vfscanf.c index 69f45081..414c2a3d 100644 --- a/src/stdio/vfscanf.c +++ b/src/stdio/vfscanf.c @@ -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; diff --git a/src/stdio/vsnprintf.c b/src/stdio/vsnprintf.c index 1f316ca4..ba17bd7d 100644 --- a/src/stdio/vsnprintf.c +++ b/src/stdio/vsnprintf.c @@ -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;  } diff --git a/src/stdio/vswprintf.c b/src/stdio/vswprintf.c index 2d9f2002..8e8f80ce 100644 --- a/src/stdio/vswprintf.c +++ b/src/stdio/vswprintf.c @@ -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;  } | 
