/* 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();
#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;
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 *);
#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;
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;
}
static off_t retneg1(FILE *f, off_t off, int whence)
{
+ errno = ESPIPE;
return -1;
}
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;
}
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;
+ }
}
--- /dev/null
+#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);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
}
+++ /dev/null
-#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;
-}
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 */
OFLLOCK();
for (f=ofl_head; f; f=next) {
FLOCK(f);
- OFLUNLOCK();
+ //OFLUNLOCK();
r |= __fflush_unlocked(f);
- OFLLOCK();
+ //OFLLOCK();
next = f->next;
FUNLOCK(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);
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);
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);
}
} 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;
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);
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;
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);
/* 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;
}
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);
+++ /dev/null
-#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);
int getchar_unlocked(void)
{
- return stdin->rpos < stdin->rstop ? *stdin->rpos++ : __uflow(stdin);
+ return getc_unlocked(stdin);
}
unsigned char *z;
size_t k;
size_t i=0;
+ int c;
if (!n || !s) {
errno = EINVAL;
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;
}
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;
}
+++ /dev/null
-#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);
int putchar_unlocked(int c)
{
- return stdout->wpos < stdout->wstop ?
- (*stdout->wpos++ = c) : __overflow(stdout, c);
+ return putc_unlocked(c, stdout);
}
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,
.fd = 1,
.flags = F_PERM | F_NORD,
.lbf = '\n',
- .write = __stdio_write,
+ .write = __stdout_write,
.seek = __stdio_seek,
.close = __stdio_close,
};
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;
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);
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;
}
/* 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;
}
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)
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;
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;
}
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;
return -1;
}
r = vfwprintf(&f, fmt, ap);
- __oflow(&f);
+ sw_write(&f, 0, 0);
return r>=n ? -1 : r;
}