diff options
Diffstat (limited to 'src/stdio')
96 files changed, 2857 insertions, 0 deletions
| diff --git a/src/stdio/__fclose_ca.c b/src/stdio/__fclose_ca.c new file mode 100644 index 00000000..e0b12a15 --- /dev/null +++ b/src/stdio/__fclose_ca.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +int __fclose_ca(FILE *f) +{ +	return f->close(f); +} diff --git a/src/stdio/__fdopen.c b/src/stdio/__fdopen.c new file mode 100644 index 00000000..6ad7c57d --- /dev/null +++ b/src/stdio/__fdopen.c @@ -0,0 +1,52 @@ +#include "stdio_impl.h" + +FILE *__fdopen(int fd, const char *mode) +{ +	FILE *f; +	struct termios tio; +	int plus = !!strchr(mode, '+'); + +	/* Check for valid initial mode character */ +	if (!strchr("rwa", *mode)) return 0; + +	/* Allocate FILE+buffer or fail */ +	if (!(f=malloc(sizeof *f + UNGET + BUFSIZ))) return 0; + +	/* Zero-fill only the struct, not the buffer */ +	memset(f, 0, sizeof *f); + +	/* Impose mode restrictions */ +	if (!plus) f->flags = (*mode == 'r') ? F_NOWR : F_NORD; + +	/* Set append mode on fd if opened for append */ +	if (*mode == 'a') { +		int flags = __syscall_fcntl(fd, F_GETFL, 0); +		__syscall_fcntl(fd, F_SETFL, flags | O_APPEND); +	} + +	f->fd = fd; +	f->buf = (unsigned char *)f + sizeof *f + UNGET; +	f->buf_size = BUFSIZ; + +	/* Activate line buffered mode for terminals */ +	f->lbf = EOF; +	if (!(f->flags & F_NOWR) && !__syscall_ioctl(fd, TCGETS, &tio)) +		f->lbf = '\n'; + +	/* Initialize op ptrs. No problem if some are unneeded. */ +	f->read = __stdio_read; +	f->write = __stdio_write; +	f->seek = __stdio_seek; +	f->close = __stdio_close; + +	/* Add new FILE to open file list */ +	OFLLOCK(); +	f->next = ofl_head; +	if (ofl_head) ofl_head->prev = f; +	ofl_head = f; +	OFLUNLOCK(); + +	return f; +} + +weak_alias(__fdopen, fdopen); diff --git a/src/stdio/__fopen_rb_ca.c b/src/stdio/__fopen_rb_ca.c new file mode 100644 index 00000000..57d9b73c --- /dev/null +++ b/src/stdio/__fopen_rb_ca.c @@ -0,0 +1,18 @@ +#include "stdio_impl.h" + +FILE *__fopen_rb_ca(const char *filename, FILE *f, unsigned char *buf, size_t len) +{ +	memset(f, 0, sizeof *f); + +	f->fd = __syscall_open(filename, O_RDONLY, 0); +	if (f->fd < 0) return 0; + +	f->flags = F_NOWR | F_PERM; +	f->buf = buf + UNGET; +	f->buf_size = len - UNGET; +	f->read = __stdio_read; +	f->seek = __stdio_seek; +	f->close = __stdio_close; + +	return f; +} diff --git a/src/stdio/__fpending.c b/src/stdio/__fpending.c new file mode 100644 index 00000000..a4334e23 --- /dev/null +++ b/src/stdio/__fpending.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +size_t __fpending(FILE *f) +{ +	return f->wend ? f->wpos - f->wbase : 0; +} diff --git a/src/stdio/__ofl.c b/src/stdio/__ofl.c new file mode 100644 index 00000000..7d9652c8 --- /dev/null +++ b/src/stdio/__ofl.c @@ -0,0 +1,3 @@ +#include "stdio_impl.h" + +struct ofl __ofl; diff --git a/src/stdio/__overflow.c b/src/stdio/__overflow.c new file mode 100644 index 00000000..e35104de --- /dev/null +++ b/src/stdio/__overflow.c @@ -0,0 +1,52 @@ +#include "stdio_impl.h" + +static 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; + +	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/__scanf.c b/src/stdio/__scanf.c new file mode 100644 index 00000000..185615d3 --- /dev/null +++ b/src/stdio/__scanf.c @@ -0,0 +1,487 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <wchar.h> +#include <wctype.h> +#include <limits.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <float.h> + +#include "__scanf.h" + +static int read(rctx_t *r) +{ +	if (--r->w < 0) return r->w = -1; +	if (r->u) r->u = 0; +	else r->read(r); +	return r->c; +} + +static void unread(rctx_t *r) +{ +	//if (r->u || r->w < 0) return; +	if (r->w < 0) return; +	r->w++; +	r->u = 1; +} + +#define SIZE_hh -2 +#define SIZE_h  -1 +#define SIZE_def 0 +#define SIZE_l   1 +#define SIZE_ll  2 +#define SIZE_L   3 + +static void store_int(void *dest, int size, int neg, unsigned long long i) +{ +	if (!dest) return; +	if (neg) i = -i; +	switch (size) { +	case SIZE_hh: +		*(char *)dest = i; +		break; +	case SIZE_h: +		*(short *)dest = i; +		break; +	case SIZE_def: +		*(int *)dest = i; +		break; +	case SIZE_l: +		*(long *)dest = i; +		break; +	case SIZE_ll: +		*(long long *)dest = i; +		break; +	} +} + +static void *arg_n(va_list ap, unsigned int n) +{ +	void *p; +	unsigned int i; +	va_list ap2; +	va_copy(ap2, ap); +	for (i=n; i>1; i--) va_arg(ap2, void *); +	p = va_arg(ap2, void *); +	va_end(ap2); +	return p; +} + +int __scanf(rctx_t *r, const wchar_t *fmt, va_list ap) +{ +	int mode=0; +	int width; +	int size; +	const wchar_t *p, *z; +	int c, l, t, m; +	long long dummy; +	char *s; +	wchar_t *wcs; +	mbstate_t st; +	int wide = r->wide; +	void *dest=NULL; +	int invert; +	unsigned long long i=0; +	int neg=0; +	int matches=0; +	long double f; +	int (*is_space)(int) = r->is_space; + +	for (p=fmt; *p; ) { +		if (is_space(*p)) { +			do p++; while (is_space(*p)); +			do r->w=1; while (is_space(read(r))); +			unread(r); +			continue; +		} else if (*p != '%' || p[1] == '%') { +			if (*p == '%') p++; +			r->w = 1; +			if (*p++ != read(r)) +				goto match_fail; +			continue; +		} +		p++; +		if (mode != 1) { +			for (z=p; isdigit(*z); z++); +			if (*z != '$' && *z != '*') { +				if (mode == 0) mode = 1; +				else goto fmt_fail; +			} else if (*z != '*') { +				int pos = 0; +				mode = 2; +				for (; p<z; p++) { +					pos = 10*pos + *p - '0'; +				} +				p++; +				if (!pos) goto fmt_fail; +				dest = arg_n(ap, pos); +			} +		} +		if (*p == '*') { +			dest = NULL; +			p++; +		} else if (mode == 1) { +			dest = va_arg(ap, void *); +		} +		 +		if (!*p) goto fmt_fail; + +		width = 0; +		for (; isdigit(*p); p++) { +			width = 10*width + *p - '0'; +		} + +		size = 0; +		switch (*p++) { +		case 0: +			goto fmt_fail; +		case 'h': +			if (*p == 'h') p++, size = SIZE_hh; +			else size = SIZE_h; +			break; +		case 'l': +			if (*p == 'l') p++, size = SIZE_ll; +			else size = SIZE_l; +			break; +		case 'j': +			size = SIZE_ll; +			break; +		case 'z': +		case 't': +			size = SIZE_l; +			break; +		case 'L': +			size = SIZE_L; +			break; +		case 'd': case 'i': case 'o': case 'u': case 'x': +		case 'a': case 'e': case 'f': case 'g': +		case 'A': case 'E': case 'F': case 'G': case 'X': +		case 's': case 'c': case '[': +		case 'S': case 'C': +		case 'p': case 'n': +			p--; +			break; +		default: +			goto fmt_fail; +		} + +		t = *p++; + +		switch (t) { +		case 'C': +		case 'c': +			if (width < 1) width = 1; +		case 's': +			if (size == SIZE_l) t &= ~0x20; +		case 'd': case 'i': case 'o': case 'u': case 'x': +		case 'a': case 'e': case 'f': case 'g': +		case 'A': case 'E': case 'F': case 'G': case 'X': +		case '[': case 'S': +		case 'p': case 'n': +			if (width < 1) width = INT_MAX; +			break; +		default: +			goto fmt_fail; +		} + +		r->w = width; + +		if (t != 'n') { +			if (read(r) < 0) goto input_fail; +			unread(r); +		} + +		switch (t) { +		case 'n': +			store_int(dest, size, 0, r->l - r->u); +			/* do not increment match count, etc! */ +			continue; +		case 'C': +			wcs = dest ? dest : (void *)&dummy; +			st = (mbstate_t){ 0 }; +			while ((c=read(r)) >= 0) { +				if (wide) { +					if (dest) *wcs++ = c; +				} else { +					char ch = c; +					switch (mbrtowc(wcs, &ch, 1, &st)) { +					case -1: +						goto enc_fail; +					case -2: +						break; +					default: +						if (dest) wcs++; +					} +				} +			} +			if (r->w > 0) goto match_fail; +			break; +		case 'c': +			s = dest ? dest : (void *)&dummy; +			while ((c=read(r)) >= 0) { +				if (wide) { +					if ((l=wctomb(s, c)) < 0) +						goto enc_fail; +					if (dest) s += l; +				} else { +					if (dest) *s++ = c; +				} +			} +			if (r->w > 0) goto match_fail; +			break; +		case '[': +			wcs = dest ? dest : (void *)&dummy; +			s = dest ? dest : (void *)&dummy; +			if (!wide && size == SIZE_l) st = (mbstate_t){ 0 }; + +			if (*p == '^') p++, invert = 1; +			else invert = 0; + +			if (wide) { +				for (m=0; (c=read(r)) >= 0; m=1) { +					for (z=p; *z && *z != c && (*z != ']' || z==p); z++); +					if (!*z) goto fmt_fail; +					if (*z == c && (*z != ']' || z==p)) { +						if (invert) break; +					} else { +						if (!invert) break; +					} +					if (size == SIZE_l) { +						if (dest) *wcs++ = c; +					} else { +						if ((l=wctomb(s, c)) < 0) +							goto enc_fail; +						if (dest) s += l; +					} +				} +				for (p++; *p && *p != ']'; p++); +				p++; +			} else { +				unsigned char scanset[257]; +				memset(scanset, invert, sizeof scanset); +				scanset[0] = 0; +				for (z=p; *z && (*z != ']' || z==p); z++) +					scanset[1+*z] = 1-invert; +				if (!*z) goto fmt_fail; +				p=z+1; +				c=0; +				for (m=0; scanset[(c=read(r))+1]; m=1) { +					if (size == SIZE_l) { +						char ch = c; +						switch (mbrtowc(wcs, &ch, 1, &st)) { +						case -1: +							goto enc_fail; +						case -2: +							break; +						default: +							if (dest) wcs++; +						} +					} else { +						if (dest) *s++ = c; +					} +				} +			} +			if (!m) goto match_fail; +			if (dest) { +				if (size == SIZE_l) *wcs++ = 0; +				else *s++ = 0; +			} +			break; +		default: +			/* read unlimited number of spaces, then reset width */ +			do r->w = 1; while (is_space(c = read(r))); +			if (c < 0) goto input_fail; +			unread(r); +			r->w = width; +		} + +		switch (t) { +		case 'p': +		case 'X': +			t = 'x'; +		case 'd': +		case 'i': +		case 'o': +		case 'u': +		case 'x': +			i = m = neg = 0; +			if ((c=read(r)) == '-') neg=1; +			else if (c != '+') unread(r); +			switch (t) { +			case 'i': +			case 'x': +				if ((c=read(r)) != '0') { +					if (t == 'i') t = 'd'; +					unread(r); +					break; +				} +				if (((c=read(r))|0x20) != 'x') { +					if (t == 'i') { +						t = 'o'; +						/* lone 0 is valid octal */ +						if ((unsigned)(c-'0') >= 8) { +							m = 1; +							goto int_finish; +						} +					} +					unread(r); +					break; +				} +				t = 'x'; +			} +		} +		 +		switch (t) { +		case 'd': +		case 'u': +			for (m=0; isdigit(c=read(r)); m=1) +				i = 10*i + c-'0'; +			goto int_finish; +		case 'o': +			for (m=0; (unsigned)(c=read(r))-'0' < 8; m=1) +				i = (i<<3) + c-'0'; +			goto int_finish; +		case 'x': +			for (m=0; ; m=1) { +				if (isdigit(c=read(r))) { +					i = (i<<4) + c-'0'; +				} else if ((unsigned)(c|0x20)-'a' < 6) { +					i = (i<<4) + (c|0x20)-'a'+10; +				} else break; +			} +		int_finish: +			if (!m) goto match_fail; +			store_int(dest, size, neg, i); +			break; +		case 'a': +		case 'e': +		case 'f': +		case 'g': +			f = 0.0; +			neg = m = 0; +			if ((c=read(r)) == '-') neg=1; +			else if (c != '+') unread(r); +			/* FIXME: check for INF/NAN strings here */ +			if (read(r)=='0' && (m=1, (read(r)|0x20) == 'x')) +				goto hexfloat; +			else unread(r); +			for (; isdigit(c=read(r)); m=1) +				f = 10.0 * f + (c-'0'); +			if (c=='.') { +				double mag = 10.0; +				for (; isdigit(c=read(r)); mag*=10.0) +					f += (c-'0')/mag; +			} +			if ((c|0x20)=='e') { +				int ex=0, en=0; +				m = 0; +				if ((c=read(r))=='-') en=1; +				else if (c!='+') unread(r); +				for (; isdigit(c=read(r)); m=1) +					if (ex < LDBL_MAX_10_EXP) +						ex = 10 * ex + (c-'0'); +				if (ex > LDBL_MAX_10_EXP) +					f = en ? 0 : INFINITY; +				else { +					if (en) while (ex--) f/=10.0; +					else while (ex--) f*=10.0; +				} +			} +			goto writefloat; +hexfloat: +			m = 0; +			for (; isxdigit(c=read(r)); m=1) +				if (isdigit(c)) f = 16.0*f + (c-'0'); +				else f = 16.0*f + ((c|32)-'a'+10); +			if (c=='.') { +				double mag = 1/16.0; +				for (; isxdigit(c=read(r)); mag*=1/16.0) +					if (isdigit(c)) f += (c-'0')*mag; +					else f += ((c|32)-'a'+10)*mag; +			} +			if ((c|0x20)=='p') { +				int ex=0, en=0; +				m = 0; +				if ((c=read(r))=='-') en=1; +				else if (c!='+') unread(r); +				for (; isdigit(c=read(r)); m=1) +					if (ex < LDBL_MAX_EXP) +						ex = 10 * ex + (c-'0'); +				if (ex > LDBL_MAX_EXP) +					f = en ? 0 : INFINITY; +				else { +					if (en) while (ex--) f*=0.5; +					else while (ex--) f*=2.0; +				} +			} +writefloat: +			if (!m) goto match_fail; +			if (neg) f *= -1.0; +			if (dest) switch (size) { +			case SIZE_def: +				*(float *)dest = f; +				break; +			case SIZE_l: +				*(double *)dest = f; +				break; +			case SIZE_L: +				*(long double *)dest = f; +				break; +			} +			break; +		case 'S': +			wcs = dest ? dest : (void *)&dummy; +			st = (mbstate_t){ 0 }; +			while((c=read(r)) >= 0) { +				if (wide) { +					if (is_space(c)) break; +					if (dest) *wcs++ = c; +				} else { +					char ch = c; +					if (is_space(c)) break; +					switch (mbrtowc(wcs, &ch, 1, &st)) { +					case -1: +						goto enc_fail; +					case -2: +						break; +					default: +						if (dest) wcs++; +					} +				} +			} +			if (dest) *wcs++ = 0; +			break; +		case 's': +			s = dest ? dest : (void *)&dummy; +			while((c=read(r)) >= 0) { +				if (wide) { +					if (is_space(c)) break; +					if ((l=wctomb(s, c)) < 0) +						goto enc_fail; +					if (dest) s += l; +				} else { +					if (is_space(c)) break; +					if (dest) *s++ = c; +				} +			} +			if (dest) *s++ = 0; +			break; +		} + +		/* unread will do nothing if field width was exhausted */ +		unread(r); +		if (dest) matches++; +	} +	return matches; +enc_fail: +	errno = EILSEQ; +fmt_fail: +input_fail: +	if (!matches) matches--; +match_fail: +	unread(r); +	return matches; +} diff --git a/src/stdio/__scanf.h b/src/stdio/__scanf.h new file mode 100644 index 00000000..e549b979 --- /dev/null +++ b/src/stdio/__scanf.h @@ -0,0 +1,16 @@ +#include <wchar.h> + +typedef struct rctx +{ +	void (*read)(struct rctx *); +	void *opaque; +	int wide; +	int (*is_space)(); +	int l; +	int e; +	int c; +	int u; +	int w; +} rctx_t; + +int __scanf(rctx_t *, const wchar_t *, va_list); diff --git a/src/stdio/__stdio_close.c b/src/stdio/__stdio_close.c new file mode 100644 index 00000000..24fef33f --- /dev/null +++ b/src/stdio/__stdio_close.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +int __stdio_close(FILE *f) +{ +	return __syscall_close(f->fd); +} diff --git a/src/stdio/__stdio_read.c b/src/stdio/__stdio_read.c new file mode 100644 index 00000000..ee7e1258 --- /dev/null +++ b/src/stdio/__stdio_read.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +size_t __stdio_read(FILE *f, unsigned char *buf, size_t len) +{ +	return __syscall_read(f->fd, buf, len); +} diff --git a/src/stdio/__stdio_seek.c b/src/stdio/__stdio_seek.c new file mode 100644 index 00000000..fdb9fe7f --- /dev/null +++ b/src/stdio/__stdio_seek.c @@ -0,0 +1,15 @@ +#include "stdio_impl.h" + +static off_t retneg1(FILE *f, off_t off, int whence) +{ +	return -1; +} + +off_t __stdio_seek(FILE *f, off_t off, int whence) +{ +	off_t ret = __syscall_lseek(f->fd, off, whence); +	/* Detect unseekable files and optimize future failures out */ +	if (ret < 0 && off == 0 && whence == SEEK_CUR) +		f->seek = retneg1; +	return ret; +} diff --git a/src/stdio/__stdio_write.c b/src/stdio/__stdio_write.c new file mode 100644 index 00000000..78545626 --- /dev/null +++ b/src/stdio/__stdio_write.c @@ -0,0 +1,9 @@ +#include "stdio_impl.h" + +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_write(f->fd, buf, len))>0; buf+=cnt); +	return len-(stop-buf); +} diff --git a/src/stdio/__uflow.c b/src/stdio/__uflow.c new file mode 100644 index 00000000..5a51d610 --- /dev/null +++ b/src/stdio/__uflow.c @@ -0,0 +1,7 @@ +#include "stdio_impl.h" + +int __uflow(FILE *f) +{ +	if (__underflow(f) < 0) return EOF; +	else return *f->rpos++; +} diff --git a/src/stdio/__underflow.c b/src/stdio/__underflow.c new file mode 100644 index 00000000..b769f4e4 --- /dev/null +++ b/src/stdio/__underflow.c @@ -0,0 +1,38 @@ +#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/asprintf.c b/src/stdio/asprintf.c new file mode 100644 index 00000000..79e59c2d --- /dev/null +++ b/src/stdio/asprintf.c @@ -0,0 +1,14 @@ +#include <stdio.h> +#include <stdarg.h> + +int vasprintf(char **, const char *, va_list); + +int asprintf(char **s, const char *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vasprintf(s, fmt, ap); +	va_end(ap); +	return ret; +} diff --git a/src/stdio/clearerr.c b/src/stdio/clearerr.c new file mode 100644 index 00000000..3bf94d30 --- /dev/null +++ b/src/stdio/clearerr.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +void clearerr(FILE *f) +{ +	FLOCK(f); +	f->flags &= ~(F_EOF|F_ERR); +	FUNLOCK(f); +} + +weak_alias(clearerr, clearerr_unlocked); diff --git a/src/stdio/dprintf.c b/src/stdio/dprintf.c new file mode 100644 index 00000000..fa28322f --- /dev/null +++ b/src/stdio/dprintf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int dprintf(int fd, const char *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vdprintf(fd, fmt, ap); +	va_end(ap); +	return ret; +} diff --git a/src/stdio/fclose.c b/src/stdio/fclose.c new file mode 100644 index 00000000..26bc37e8 --- /dev/null +++ b/src/stdio/fclose.c @@ -0,0 +1,21 @@ +#include "stdio_impl.h" + +int fclose(FILE *f) +{ +	int r; +	int perm = f->flags & F_PERM; + +	if (!perm) { +		OFLLOCK(); +		if (f->prev) f->prev->next = f->next; +		if (f->next) f->next->prev = f->prev; +		if (ofl_head == f) ofl_head = f->next; +		OFLUNLOCK(); +	} + +	r = fflush(f) | f->close(f); + +	if (!perm) free(f); +	 +	return r; +} diff --git a/src/stdio/feof.c b/src/stdio/feof.c new file mode 100644 index 00000000..f2b739b5 --- /dev/null +++ b/src/stdio/feof.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +#undef feof + +int feof(FILE *f) +{ +	return !!(f->flags & F_EOF); +} + +weak_alias(feof, feof_unlocked); diff --git a/src/stdio/ferror.c b/src/stdio/ferror.c new file mode 100644 index 00000000..f535fbed --- /dev/null +++ b/src/stdio/ferror.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +#undef ferror + +int ferror(FILE *f) +{ +	return !!(f->flags & F_ERR); +} + +weak_alias(ferror, ferror_unlocked); diff --git a/src/stdio/fflush.c b/src/stdio/fflush.c new file mode 100644 index 00000000..cf3f5b0e --- /dev/null +++ b/src/stdio/fflush.c @@ -0,0 +1,50 @@ +#include "stdio_impl.h" + +static int __fflush_unlocked(FILE *f) +{ +	/* If writing, flush output. */ +	if (f->wpos > f->buf && __oflow(f)) return -1; + +	/* If reading, sync position, per POSIX */ +	if (f->rpos < f->rend) f->seek(f, f->rpos-f->rend, SEEK_CUR); +	f->rpos = f->rend; + +	/* Hook for special behavior on flush */ +	if (f->flush) f->flush(f); + +	return (f->flags & F_ERR) ? EOF : 0; +} + +/* stdout.c will override this if linked */ +static FILE *const __dummy = 0; +weak_alias(__dummy, __stdout_to_flush); + +int fflush(FILE *f) +{ +	int r; +	FILE *next; + +	if (f) { +		FLOCK(f); +		r = __fflush_unlocked(f); +		FUNLOCK(f); +		return r; +	} + +	r = __stdout_to_flush ? fflush(__stdout_to_flush) : 0; + +	OFLLOCK(); +	for (f=ofl_head; f; f=next) { +		FLOCK(f); +		OFLUNLOCK(); +		r |= __fflush_unlocked(f); +		OFLLOCK(); +		next = f->next; +		FUNLOCK(f); +	} +	OFLUNLOCK(); +	 +	return r; +} + +weak_alias(__fflush_unlocked, fflush_unlocked); diff --git a/src/stdio/fgetc.c b/src/stdio/fgetc.c new file mode 100644 index 00000000..3a7f1e30 --- /dev/null +++ b/src/stdio/fgetc.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +int fgetc(FILE *f) +{ +	int c; +	FLOCK(f); +	c = f->rpos < f->rstop ? *f->rpos++ : __uflow(f); +	FUNLOCK(f); +	return c; +} diff --git a/src/stdio/fgetpos.c b/src/stdio/fgetpos.c new file mode 100644 index 00000000..5b663d1e --- /dev/null +++ b/src/stdio/fgetpos.c @@ -0,0 +1,11 @@ +#include "stdio_impl.h" + +int fgetpos(FILE *f, fpos_t *pos) +{ +	off_t off = __ftello(f); +	if (off < 0) return -1; +	*(off_t *)pos = off; +	return 0; +} + +LFS64(fgetpos); diff --git a/src/stdio/fgets.c b/src/stdio/fgets.c new file mode 100644 index 00000000..7939303e --- /dev/null +++ b/src/stdio/fgets.c @@ -0,0 +1,34 @@ +#include "stdio_impl.h" + +#define MIN(a,b) ((a)<(b) ? (a) : (b)) + +char *fgets(char *s, int n, FILE *f) +{ +	char *p = s; +	unsigned char *z; +	size_t k; + +	if (!n--) return 0; + +	FLOCK(f); + +	while (n && !feof(f)) { +		z = memchr(f->rpos, '\n', f->rend - f->rpos); +		k = z ? z - f->rpos + 1 : f->rend - f->rpos; +		k = MIN(k, n); +		memcpy(p, f->rpos, k); +		f->rpos += k; +		p += k; +		n -= k; +		if (z) break; +		__underflow(f); +	} +	*p = 0; +	if (ferror(f)) p = s; + +	FUNLOCK(f); + +	return (p == s) ? 0 : s; +} + +weak_alias(fgets, fgets_unlocked); diff --git a/src/stdio/fgetwc.c b/src/stdio/fgetwc.c new file mode 100644 index 00000000..c990545f --- /dev/null +++ b/src/stdio/fgetwc.c @@ -0,0 +1,51 @@ +#include "stdio_impl.h" + +wint_t __fgetwc_unlocked(FILE *f) +{ +	mbstate_t st = { 0 }; +	wchar_t wc; +	int c; +	unsigned char b; +	size_t l; + +	f->mode |= f->mode+1; + +	/* Convert character from buffer if possible */ +	if (f->rpos < f->rend) { +		l = mbrtowc(&wc, f->rpos, f->rend - f->rpos, &st); +		if (l+2 >= 2) { +			f->rpos += l + !l; /* l==0 means 1 byte, null */ +			return wc; +		} +		if (l == -1) { +			f->rpos++; +			return WEOF; +		} +	} else l = -2; + +	/* Convert character byte-by-byte from __uflow */ +	while (l == -2) { +		b = c = __uflow(f); +		if (c < 0) { +			if (!mbsinit(&st)) errno = EILSEQ; +			return WEOF; +		} +		l = mbrtowc(&wc, &b, 1, &st); +		if (l == -1) return WEOF; +	} + +	FUNLOCK(f); +	return wc; +} + +wint_t fgetwc(FILE *f) +{ +	wint_t c; +	FLOCK(f); +	c = __fgetwc_unlocked(f); +	FUNLOCK(f); +	return c; +} + +weak_alias(__fgetwc_unlocked, fgetwc_unlocked); +weak_alias(__fgetwc_unlocked, getwc_unlocked); diff --git a/src/stdio/fgetws.c b/src/stdio/fgetws.c new file mode 100644 index 00000000..2e76b565 --- /dev/null +++ b/src/stdio/fgetws.c @@ -0,0 +1,27 @@ +#include "stdio_impl.h" + +wint_t __fgetwc_unlocked(FILE *); + +wchar_t *fgetws(wchar_t *s, int n, FILE *f) +{ +	wchar_t *p = s; + +	if (!n--) return s; + +	FLOCK(f); + +	for (; n; n--) { +		wint_t c = __fgetwc_unlocked(f); +		if (c == WEOF) break; +		*p++ = c; +		if (c == '\n') break; +	} +	*p = 0; +	if (ferror(f)) p = s; + +	FUNLOCK(f); + +	return (p == s) ? NULL : s; +} + +weak_alias(fgetws, fgetws_unlocked); diff --git a/src/stdio/fileno.c b/src/stdio/fileno.c new file mode 100644 index 00000000..9ffb26d5 --- /dev/null +++ b/src/stdio/fileno.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +int fileno(FILE *f) +{ +	return f->fd; +} + +weak_alias(fileno, fileno_unlocked); diff --git a/src/stdio/fmemopen.c b/src/stdio/fmemopen.c new file mode 100644 index 00000000..77a60746 --- /dev/null +++ b/src/stdio/fmemopen.c @@ -0,0 +1,16 @@ +#if 0 +#include "stdio_impl.h" + +static ssize_t mread(FILE *f, unsigned char *buf, size_t len) +{ +	memcpy(buf,  +} + +FILE *fmemopen(void *buf, size_t size, const char *mode) +{ +	FILE *f = calloc(sizeof(FILE), 1); +	if (!f) return 0; +	 +	// +} +#endif diff --git a/src/stdio/fopen.c b/src/stdio/fopen.c new file mode 100644 index 00000000..670cf5f3 --- /dev/null +++ b/src/stdio/fopen.c @@ -0,0 +1,34 @@ +#include "stdio_impl.h" + +FILE *fopen(const char *filename, const char *mode) +{ +	FILE *f; +	int fd; +	int flags; +	int plus = !!strchr(mode, '+'); + +	/* Check for valid initial mode character */ +	if (!strchr("rwa", *mode)) { +		errno = EINVAL; +		return 0; +	} + +	/* Compute the flags to pass to open() */ +	if (plus) flags = O_RDWR; +	else if (*mode == 'r') flags = O_RDONLY; +	else flags = O_WRONLY; +	if (*mode != 'r') flags |= O_CREAT; +	if (*mode == 'w') flags |= O_TRUNC; +	if (*mode == 'a') flags |= O_APPEND; + +	fd = __syscall_open(filename, flags, 0666); +	if (fd < 0) return 0; + +	f = __fdopen(fd, mode); +	if (f) return f; + +	__syscall_close(fd); +	return 0; +} + +LFS64(fopen); diff --git a/src/stdio/fprintf.c b/src/stdio/fprintf.c new file mode 100644 index 00000000..a220cc10 --- /dev/null +++ b/src/stdio/fprintf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int fprintf(FILE *f, const char *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vfprintf(f, fmt, ap); +	va_end(ap); +	return ret; +} diff --git a/src/stdio/fputc.c b/src/stdio/fputc.c new file mode 100644 index 00000000..ec859385 --- /dev/null +++ b/src/stdio/fputc.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +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); +	FUNLOCK(f); +	return c; +} diff --git a/src/stdio/fputs.c b/src/stdio/fputs.c new file mode 100644 index 00000000..e6bdb204 --- /dev/null +++ b/src/stdio/fputs.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +int fputs(const char *s, FILE *f) +{ +	size_t l = strlen(s); +	if (!l) return 0; +	return (int)fwrite(s, l, 1, f) - 1; +} + +weak_alias(fputs, fputs_unlocked); diff --git a/src/stdio/fputwc.c b/src/stdio/fputwc.c new file mode 100644 index 00000000..b48bb74d --- /dev/null +++ b/src/stdio/fputwc.c @@ -0,0 +1,33 @@ +#include "stdio_impl.h" + +wint_t __fputwc_unlocked(wchar_t c, FILE *f) +{ +	char mbc[MB_LEN_MAX]; +	int l; + +	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); +	} else if (f->wpos + MB_LEN_MAX < f->wend) { +		l = wctomb(f->wpos, c); +		if (l < 0) c = WEOF; +		else f->wpos += l; +	} else { +		l = wctomb(mbc, c); +		if (l < 0 || __fwritex(mbc, l, f) < l) c = WEOF; +	} +	return c; +} + +wint_t fputwc(wchar_t c, FILE *f) +{ +	FLOCK(f); +	c = __fputwc_unlocked(c, f); +	FUNLOCK(f); +	return 0; +} + +weak_alias(__fputwc_unlocked, fputwc_unlocked); +weak_alias(__fputwc_unlocked, putwc_unlocked); diff --git a/src/stdio/fputws.c b/src/stdio/fputws.c new file mode 100644 index 00000000..9057853b --- /dev/null +++ b/src/stdio/fputws.c @@ -0,0 +1,23 @@ +#include "stdio_impl.h" + +int fputws(const wchar_t *ws, FILE *f) +{ +	unsigned char buf[BUFSIZ]; +	size_t l=0; + +	FLOCK(f); + +	f->mode |= f->mode+1; + +	while (ws && (l = wcsrtombs(buf, (void*)&ws, sizeof buf, 0))+1 > 1) +		if (__fwritex(buf, l, f) < l) { +			FUNLOCK(f); +			return -1; +		} + +	FUNLOCK(f); + +	return l; /* 0 or -1 */ +} + +weak_alias(fputws, fputws_unlocked); diff --git a/src/stdio/fread.c b/src/stdio/fread.c new file mode 100644 index 00000000..0fa0b2aa --- /dev/null +++ b/src/stdio/fread.c @@ -0,0 +1,49 @@ +#include "stdio_impl.h" + +#define MIN(a,b) ((a)<(b) ? (a) : (b)) + +size_t fread(void *destv, size_t size, size_t nmemb, FILE *f) +{ +	unsigned char *dest = destv; +	size_t len = size*nmemb, l = len, k; + +	/* Never touch the file if length is zero.. */ +	if (!l) return 0; + +	FLOCK(f); + +	for (;;) { +		/* 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); +	} + +	/* 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 nmemb; +eof: +	FUNLOCK(f); +	return (len-l)/size; +} + +weak_alias(fread, fread_unlocked); diff --git a/src/stdio/freopen.c b/src/stdio/freopen.c new file mode 100644 index 00000000..8d3af9fc --- /dev/null +++ b/src/stdio/freopen.c @@ -0,0 +1,47 @@ +#include "stdio_impl.h" + +/* The basic idea of this implementation is to open a new FILE, + * hack the necessary parts of the new FILE into the old one, then + * close the new FILE. */ + +/* Locking is not necessary because, in the event of failure, the stream + * passed to freopen is invalid as soon as freopen is called. */ + +FILE *freopen(const char *filename, const char *mode, FILE *f) +{ +	int fl; +	FILE *f2; + +	fflush(f); + +	if (!filename) { +		f2 = fopen("/dev/null", mode); +		if (!f2) goto fail; +		fl = __syscall_fcntl(f2->fd, F_GETFL, 0); +		if (fl < 0 || __syscall_fcntl(f->fd, F_SETFL, fl) < 0) +			goto fail2; +	} else { +		f2 = fopen(filename, mode); +		if (!f2) goto fail; +		if (__syscall_dup2(f2->fd, f->fd) < 0) +			goto fail2; +	} + +	f->flags = (f->flags & F_PERM) | f2->flags; +	f->read = f2->read; +	f->write = f2->write; +	f->seek = f2->seek; +	f->close = f2->close; +	f->flush = f2->flush; + +	fclose(f2); +	return f; + +fail2: +	fclose(f2); +fail: +	fclose(f); +	return NULL; +} + +LFS64(freopen); diff --git a/src/stdio/fscanf.c b/src/stdio/fscanf.c new file mode 100644 index 00000000..51fc9b30 --- /dev/null +++ b/src/stdio/fscanf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int fscanf(FILE *f, const char *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vfscanf(f, fmt, ap); +	va_end(ap); +	return ret; +} diff --git a/src/stdio/fseek.c b/src/stdio/fseek.c new file mode 100644 index 00000000..bfaad375 --- /dev/null +++ b/src/stdio/fseek.c @@ -0,0 +1,38 @@ +#include "stdio_impl.h" + +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; + +	/* Perform the underlying seek operation. */ +	if (f->seek(f, off, whence) < 0) return -1; + +	/* If seek succeeded, file is seekable and we discard read buffer. */ +	f->rpos = f->rend = f->rstop = 0; +	f->flags &= ~F_EOF; +	 +	FUNLOCK(f);	 +	return 0; +} + +int __fseeko(FILE *f, off_t off, int whence) +{ +	int result; +	FLOCK(f); +	result = __fseeko_unlocked(f, off, whence); +	FUNLOCK(f); +	return result; +} + +int fseek(FILE *f, long off, int whence) +{ +	return __fseeko(f, off, whence); +} + +weak_alias(__fseeko, fseeko); + +LFS64(fseeko); diff --git a/src/stdio/fsetpos.c b/src/stdio/fsetpos.c new file mode 100644 index 00000000..5d76c8cd --- /dev/null +++ b/src/stdio/fsetpos.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +int fsetpos(FILE *f, const fpos_t *pos) +{ +	return __fseeko(f, *(const off_t *)pos, SEEK_SET); +} + +LFS64(fsetpos); diff --git a/src/stdio/ftell.c b/src/stdio/ftell.c new file mode 100644 index 00000000..aa1f5381 --- /dev/null +++ b/src/stdio/ftell.c @@ -0,0 +1,35 @@ +#include "stdio_impl.h" + +off_t __ftello_unlocked(FILE *f) +{ +	off_t pos = f->seek(f, 0, SEEK_CUR); +	if (pos < 0) { +		FUNLOCK(f); +		return pos; +	} +	/* Adjust for data in buffer. */ +	return pos - (f->rend - f->rpos) + (f->wpos - f->wbase); +} + +off_t __ftello(FILE *f) +{ +	off_t pos; +	FLOCK(f); +	pos = __ftello_unlocked(f); +	FUNLOCK(f); +	return pos; +} + +long ftell(FILE *f) +{ +	off_t pos = __ftello(f); +	if (pos > LONG_MAX) { +		errno = EOVERFLOW; +		return -1; +	} +	return pos; +} + +weak_alias(__ftello, ftello); + +LFS64(ftello); diff --git a/src/stdio/fwide.c b/src/stdio/fwide.c new file mode 100644 index 00000000..f4da47f6 --- /dev/null +++ b/src/stdio/fwide.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +#define SH (8*sizeof(int)-1) +#define NORMALIZE(x) ((x)>>SH | -((-(x))>>SH)) + +int fwide(FILE *f, int mode) +{ +	if (!f->mode) f->mode = NORMALIZE(mode); +	return f->mode; +} diff --git a/src/stdio/fwrite.c b/src/stdio/fwrite.c new file mode 100644 index 00000000..23974fe1 --- /dev/null +++ b/src/stdio/fwrite.c @@ -0,0 +1,51 @@ +#include "stdio_impl.h" + +size_t __fwritex(const unsigned char *s, size_t l, FILE *f) +{ +	size_t i = 0; +	size_t k = f->wend - f->wpos; + +	/* 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); +		} +	} + +	/* 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; +} + +size_t fwrite(const void *src, size_t size, size_t nmemb, FILE *f) +{ +	size_t l = size*nmemb; +	if (!l) return l; +	FLOCK(f); +	l = __fwritex(src, l, f); +	FUNLOCK(f); +	return l/size; +} + +weak_alias(fwrite, fwrite_unlocked); diff --git a/src/stdio/fwscanf.c b/src/stdio/fwscanf.c new file mode 100644 index 00000000..a6892cf6 --- /dev/null +++ b/src/stdio/fwscanf.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> + +int fwscanf(FILE *f, const wchar_t *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vfwscanf(f, fmt, ap); +	va_end(ap); +	return ret; +} diff --git a/src/stdio/getc.c b/src/stdio/getc.c new file mode 100644 index 00000000..b739b0a5 --- /dev/null +++ b/src/stdio/getc.c @@ -0,0 +1,6 @@ +#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 new file mode 100644 index 00000000..629223ea --- /dev/null +++ b/src/stdio/getc_unlocked.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +int getc_unlocked(FILE *f) +{ +	return f->rpos < f->rstop ? *f->rpos++ : __uflow(f); +} + +weak_alias (getc_unlocked, fgetc_unlocked); diff --git a/src/stdio/getchar.c b/src/stdio/getchar.c new file mode 100644 index 00000000..c1012658 --- /dev/null +++ b/src/stdio/getchar.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int getchar(void) +{ +	return fgetc(stdin); +} diff --git a/src/stdio/getchar_unlocked.c b/src/stdio/getchar_unlocked.c new file mode 100644 index 00000000..299cb958 --- /dev/null +++ b/src/stdio/getchar_unlocked.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +int getchar_unlocked(void) +{ +	return stdin->rpos < stdin->rstop ? *stdin->rpos++ : __uflow(stdin); +} diff --git a/src/stdio/getdelim.c b/src/stdio/getdelim.c new file mode 100644 index 00000000..f770d20b --- /dev/null +++ b/src/stdio/getdelim.c @@ -0,0 +1,59 @@ +#include "stdio_impl.h" + +#define MIN(a,b) ((a)<(b) ? (a) : (b)) + +ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) +{ +	char *tmp; +	unsigned char *z; +	size_t k; +	size_t i=0; + +	if (!n || !s) { +		errno = EINVAL; +		return -1; +	} + +	if (!*s) *n=0; + +	FLOCK(f); + +	while (!feof(f)) { +		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; +			tmp = realloc(*s, *n); +			if (!tmp) { +				*n = i+k+1; +				tmp = realloc(*s, *n); +				if (!tmp) goto oom; +			} +			*s = tmp; +		} +		memcpy(*s+i, f->rpos, k); +		f->rpos += k; +		i += k; +		if (z) break; +		__underflow(f); +	} +	(*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: +	errno = ENOMEM; +	return -1; +} diff --git a/src/stdio/getline.c b/src/stdio/getline.c new file mode 100644 index 00000000..a3a6651b --- /dev/null +++ b/src/stdio/getline.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +ssize_t getline(char **s, size_t *n, FILE *f) +{ +	return getdelim(s, n, '\n', f); +} diff --git a/src/stdio/gets.c b/src/stdio/gets.c new file mode 100644 index 00000000..24319eb2 --- /dev/null +++ b/src/stdio/gets.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +char *gets(char *s) +{ +	char *ret = fgets(s, INT_MAX, stdin); +	if (ret && s[strlen(s)-1] == '\n') s[strlen(s)-1] = 0; +	return ret; +} diff --git a/src/stdio/getw.c b/src/stdio/getw.c new file mode 100644 index 00000000..de9e985a --- /dev/null +++ b/src/stdio/getw.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int getw(FILE *f) +{ +	int x; +	return fread(&x, sizeof x, 1, f) ? x : EOF; +} diff --git a/src/stdio/getwc.c b/src/stdio/getwc.c new file mode 100644 index 00000000..a2818bc4 --- /dev/null +++ b/src/stdio/getwc.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +wint_t getwc(FILE *f) +{ +	return fgetwc(f); +} diff --git a/src/stdio/getwchar.c b/src/stdio/getwchar.c new file mode 100644 index 00000000..2295bd40 --- /dev/null +++ b/src/stdio/getwchar.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +wint_t getwchar(void) +{ +	return fgetwc(stdin); +} + +weak_alias(getwchar, getwchar_unlocked); diff --git a/src/stdio/pclose.c b/src/stdio/pclose.c new file mode 100644 index 00000000..c2fe7a24 --- /dev/null +++ b/src/stdio/pclose.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +int pclose(FILE *f) +{ +	int status; +	fclose(f); +	while (waitpid(f->pipe_pid, &status, 0) == -1) +		if (errno != EINTR) return -1; +	return status; +} diff --git a/src/stdio/perror.c b/src/stdio/perror.c new file mode 100644 index 00000000..e4637c8a --- /dev/null +++ b/src/stdio/perror.c @@ -0,0 +1,27 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include "stdio_impl.h" + +void perror(const char *msg) +{ +#if 1 +	if (msg) fprintf(stderr, "%s: %m\n", msg, strerror(errno)); +	else fprintf(stderr, "%m\n"); +#else +	FILE *f = stderr; +	char *errstr = strerror(errno); + +	FLOCK(f); +	 +	if (msg) { +		__fwritex(msg, strlen(msg), f); +		__putc_unlocked(':', f); +		__putc_unlocked(' ', f); +	} +	__fwritex(errstr, strlen(errstr), f); +	__putc_unlocked('\n', f); + +	FUNLOCK(f); +#endif +} diff --git a/src/stdio/popen.c b/src/stdio/popen.c new file mode 100644 index 00000000..1d33e9d6 --- /dev/null +++ b/src/stdio/popen.c @@ -0,0 +1,43 @@ +#include "stdio_impl.h" + +FILE *popen(const char *cmd, const char *mode) +{ +	int p[2]; +	int op; +	pid_t pid; +	FILE *f; +	const char *modes = "rw", *mi = strchr(modes, *mode); + +	if (mi) { +		op = mi-modes; +	} else { +		errno = EINVAL; +		return 0; +	} +	 +	if (pipe(p)) return NULL; +	f = fdopen(p[op], mode); +	if (!f) { +		close(p[0]); +		close(p[1]); +		return NULL; +	} +	 +	pid = fork(); +	switch (pid) { +	case -1: +		fclose(f); +		close(p[0]); +		close(p[1]); +		return NULL; +	case 0: +		dup2(p[1-op], 1-op); +		close(p[0]); +		close(p[1]); +		execl("/bin/sh", "sh", "-c", cmd, (char *)0); +		_exit(127); +	} +	close(p[1-op]); +	f->pipe_pid = pid; +	return f; +} diff --git a/src/stdio/printf.c b/src/stdio/printf.c new file mode 100644 index 00000000..efeb8b33 --- /dev/null +++ b/src/stdio/printf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int printf(const char *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vprintf(fmt, ap); +	va_end(ap); +	return ret; +} diff --git a/src/stdio/putc.c b/src/stdio/putc.c new file mode 100644 index 00000000..3c9dc11e --- /dev/null +++ b/src/stdio/putc.c @@ -0,0 +1,8 @@ +#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 new file mode 100644 index 00000000..f01da717 --- /dev/null +++ b/src/stdio/putc_unlocked.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +int putc_unlocked(int c, FILE *f) +{ +	return f->wpos < f->wstop ? (*f->wpos++ = c) : __overflow(f, c); +} + +weak_alias(putc_unlocked, fputc_unlocked); diff --git a/src/stdio/putchar.c b/src/stdio/putchar.c new file mode 100644 index 00000000..945636d5 --- /dev/null +++ b/src/stdio/putchar.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int putchar(int c) +{ +	return fputc(c, stdout); +} diff --git a/src/stdio/putchar_unlocked.c b/src/stdio/putchar_unlocked.c new file mode 100644 index 00000000..72d47d15 --- /dev/null +++ b/src/stdio/putchar_unlocked.c @@ -0,0 +1,7 @@ +#include "stdio_impl.h" + +int putchar_unlocked(int c) +{ +	return stdout->wpos < stdout->wstop ? +		(*stdout->wpos++ = c) : __overflow(stdout, c); +} diff --git a/src/stdio/puts.c b/src/stdio/puts.c new file mode 100644 index 00000000..eb70efdc --- /dev/null +++ b/src/stdio/puts.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +int puts(const char *s) +{ +	return -(fputs(s, stdout) < 0 || putchar('\n') < 0); +} diff --git a/src/stdio/putw.c b/src/stdio/putw.c new file mode 100644 index 00000000..137832ee --- /dev/null +++ b/src/stdio/putw.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int putw(int x, FILE *f) +{ +	return fwrite(&x, sizeof x, 1, f) ? x : EOF; +} diff --git a/src/stdio/putwc.c b/src/stdio/putwc.c new file mode 100644 index 00000000..80b54a47 --- /dev/null +++ b/src/stdio/putwc.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +wint_t putwc(wchar_t c, FILE *f) +{ +	return fputwc(c, f); +} diff --git a/src/stdio/putwchar.c b/src/stdio/putwchar.c new file mode 100644 index 00000000..3aacc1cf --- /dev/null +++ b/src/stdio/putwchar.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +wint_t putwchar(wchar_t c) +{ +	return fputwc(c, stdout); +} + +weak_alias(putwchar, putwchar_unlocked); diff --git a/src/stdio/remove.c b/src/stdio/remove.c new file mode 100644 index 00000000..8e338277 --- /dev/null +++ b/src/stdio/remove.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "syscall.h" + +int remove(const char *path) +{ +	return __syscall_unlink(path); +} diff --git a/src/stdio/rename.c b/src/stdio/rename.c new file mode 100644 index 00000000..4eced08a --- /dev/null +++ b/src/stdio/rename.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "syscall.h" + +int rename(const char *old, const char *new) +{ +	return syscall2(__NR_rename, (long)old, (long)new); +} diff --git a/src/stdio/rewind.c b/src/stdio/rewind.c new file mode 100644 index 00000000..7944b434 --- /dev/null +++ b/src/stdio/rewind.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void rewind(FILE *f) +{ +	fseek(f, 0, SEEK_SET); +} diff --git a/src/stdio/scanf.c b/src/stdio/scanf.c new file mode 100644 index 00000000..a04a4402 --- /dev/null +++ b/src/stdio/scanf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int scanf(const char *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vscanf(fmt, ap); +	va_end(ap); +	return ret; +} diff --git a/src/stdio/setbuf.c b/src/stdio/setbuf.c new file mode 100644 index 00000000..205afcfe --- /dev/null +++ b/src/stdio/setbuf.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +void setbuf(FILE *f, char *buf) +{ +	setvbuf(f, buf, buf ? _IOFBF : _IONBF, BUFSIZ); +} diff --git a/src/stdio/setvbuf.c b/src/stdio/setvbuf.c new file mode 100644 index 00000000..2985d3f1 --- /dev/null +++ b/src/stdio/setvbuf.c @@ -0,0 +1,22 @@ +#include "stdio_impl.h" + +/* This function makes no attempt to protect the user from his/her own + * stupidity. If called any time but when then ISO C standard specifically + * allows it, all hell can and will break loose, especially with threads! + * + * This implementation ignores all arguments except the buffering type, + * and uses the existing buffer allocated alongside the FILE object. + * In the case of stderr where the preexisting buffer is length 1, it + * is not possible to set line buffering or full buffering. */ + +int setvbuf(FILE *f, char *buf, int type, size_t size) +{ +	f->lbf = EOF; + +	if (type == _IONBF) +		f->buf_size = 1; +	else if (type == _IOLBF) +		f->lbf = '\n'; + +	return 0; +} diff --git a/src/stdio/snprintf.c b/src/stdio/snprintf.c new file mode 100644 index 00000000..4071c2f6 --- /dev/null +++ b/src/stdio/snprintf.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdarg.h> + +int snprintf(char *s, size_t n, const char *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vsnprintf(s, n, fmt, ap); +	va_end(ap); +	return ret; +} + diff --git a/src/stdio/sprintf.c b/src/stdio/sprintf.c new file mode 100644 index 00000000..6b225409 --- /dev/null +++ b/src/stdio/sprintf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int sprintf(char *s, const char *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vsprintf(s, fmt, ap); +	va_end(ap); +	return ret; +} diff --git a/src/stdio/sscanf.c b/src/stdio/sscanf.c new file mode 100644 index 00000000..a1cea699 --- /dev/null +++ b/src/stdio/sscanf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int sscanf(const char *s, const char *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vsscanf(s, fmt, ap); +	va_end(ap); +	return ret; +} diff --git a/src/stdio/stderr.c b/src/stdio/stderr.c new file mode 100644 index 00000000..4a79d4e7 --- /dev/null +++ b/src/stdio/stderr.c @@ -0,0 +1,13 @@ +#include "stdio_impl.h" + +static unsigned char buf[1+UNGET]; +static FILE f = { +	.buf = buf+UNGET, +	.buf_size = 1, +	.fd = 2, +	.flags = F_PERM | F_NORD, +	.write = __stdio_write, +	.seek = __stdio_seek, +	.close = __stdio_close, +}; +FILE *const stderr = &f; diff --git a/src/stdio/stdin.c b/src/stdio/stdin.c new file mode 100644 index 00000000..51c99232 --- /dev/null +++ b/src/stdio/stdin.c @@ -0,0 +1,13 @@ +#include "stdio_impl.h" + +static unsigned char buf[BUFSIZ+UNGET]; +static FILE f = { +	.buf = buf+UNGET, +	.buf_size = sizeof buf-UNGET, +	.fd = 0, +	.flags = F_PERM | F_NOWR, +	.read = __stdio_read, +	.seek = __stdio_seek, +	.close = __stdio_close, +}; +FILE *const stdin = &f; diff --git a/src/stdio/stdout.c b/src/stdio/stdout.c new file mode 100644 index 00000000..bf6eea6c --- /dev/null +++ b/src/stdio/stdout.c @@ -0,0 +1,17 @@ +#include "stdio_impl.h" + +static unsigned char buf[BUFSIZ+UNGET]; +static FILE f = { +	.buf = buf+UNGET, +	.buf_size = sizeof buf-UNGET, +	.fd = 1, +	.flags = F_PERM | F_NORD, +	.lbf = '\n', +	.write = __stdio_write, +	.seek = __stdio_seek, +	.close = __stdio_close, +}; +FILE *const stdout = &f; + +/* overrides symbol in fflush.c, used for flushing NULL */ +FILE *const __stdout_to_flush = &f; diff --git a/src/stdio/swscanf.c b/src/stdio/swscanf.c new file mode 100644 index 00000000..b66ad03e --- /dev/null +++ b/src/stdio/swscanf.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> + +int swscanf(const wchar_t *s, const wchar_t *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vswscanf(s, fmt, ap); +	va_end(ap); +	return ret; +} diff --git a/src/stdio/tempnam.c b/src/stdio/tempnam.c new file mode 100644 index 00000000..2cbcb864 --- /dev/null +++ b/src/stdio/tempnam.c @@ -0,0 +1,42 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include "libc.h" + +char *tempnam(const char *dir, const char *pfx) +{ +	static int lock; +	static int index; +	char *s; +	int pid = getpid(); +	int l; + +	if (!dir) dir = P_tmpdir; +	if (!pfx) pfx = "temp"; + +	if (access(dir, R_OK|W_OK|X_OK) != 0) +		return NULL; + +	l = strlen(dir) + 1 + strlen(pfx) + 2 + sizeof(int)*3*2 + 1; +	s = malloc(l); +	if (!s) { +		errno = ENOMEM; +		return NULL; +	} + +	LOCK(&lock); +	for (; index < TMP_MAX; index++) { +		snprintf(s, l, "%s/%s-%d-%d", dir, pfx, pid, index); +		if (access(s, F_OK) != 0) { +			UNLOCK(&lock); +			return s; +		} +	} +	UNLOCK(&lock); +	free(s); +	return NULL;	 +} diff --git a/src/stdio/tmpfile.c b/src/stdio/tmpfile.c new file mode 100644 index 00000000..185025f1 --- /dev/null +++ b/src/stdio/tmpfile.c @@ -0,0 +1,23 @@ +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include "stdio_impl.h" + +FILE *tmpfile(void) +{ +	char buf[L_tmpnam], *s; +	int fd; +	FILE *f; +	for (;;) { +		s = tmpnam(buf); +		if (!s) return NULL; +		fd = __syscall_open(s, O_RDWR | O_CREAT | O_EXCL, 0600); +		if (fd >= 0) { +			f = __fdopen(fd, "w+"); +			remove(s); +			return f; +		} +	} +} + +LFS64(tmpfile); diff --git a/src/stdio/tmpnam.c b/src/stdio/tmpnam.c new file mode 100644 index 00000000..14d59220 --- /dev/null +++ b/src/stdio/tmpnam.c @@ -0,0 +1,38 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <unistd.h> +#include "libc.h" + +char *tmpnam(char *s) +{ +	static int lock; +	static int index; +	static char *s2; +	int pid = getpid(); +	char *dir = getenv("TMPDIR"); + +	if (!s) { +		if (!s2) s2 = malloc(L_tmpnam); +		s = s2; +	} + +	/* this interface is insecure anyway but at least we can try.. */ +	if (!dir || strlen(dir) > L_tmpnam-32) +		dir = P_tmpdir; + +	if (access(dir, R_OK|W_OK|X_OK) != 0) +		return NULL; + +	LOCK(&lock); +	for (index++; index < TMP_MAX; index++) { +		snprintf(s, L_tmpnam, "%s/temp%d-%d", dir, pid, index); +		if (access(s, F_OK) != 0) { +			UNLOCK(&lock); +			return s; +		} +	} +	UNLOCK(&lock); +	return NULL; +} diff --git a/src/stdio/ungetc.c b/src/stdio/ungetc.c new file mode 100644 index 00000000..07181684 --- /dev/null +++ b/src/stdio/ungetc.c @@ -0,0 +1,33 @@ +#include "stdio_impl.h" + +int ungetc(int c, FILE *f) +{ +	if (c == EOF) return c; + +	FLOCK(f); + +	/* Fail if unreadable or writing and unable to flush */ +	if ((f->flags & (F_ERR|F_NORD)) || (f->wpos && __oflow(f))) { +		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; + +	FUNLOCK(f); +	return c; +} diff --git a/src/stdio/ungetwc.c b/src/stdio/ungetwc.c new file mode 100644 index 00000000..f7cde2e0 --- /dev/null +++ b/src/stdio/ungetwc.c @@ -0,0 +1,45 @@ +#include "stdio_impl.h" + +wint_t ungetwc(wint_t c, FILE *f) +{ +	unsigned char mbc[MB_LEN_MAX]; +	int l=1; + +	if (c == WEOF) return c; + +	/* Try conversion early so we can fail without locking if invalid */ +	if (!isascii(c) && (l = wctomb(mbc, c)) < 0) +		return WEOF; + +	FLOCK(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))) { +		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); +	return c; +} diff --git a/src/stdio/vasprintf.c b/src/stdio/vasprintf.c new file mode 100644 index 00000000..f2bbc7aa --- /dev/null +++ b/src/stdio/vasprintf.c @@ -0,0 +1,27 @@ +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +#define GUESS 240U + +int vasprintf(char **s, const char *fmt, va_list ap) +{ +	va_list ap2; +	char *a; +	int l=GUESS; + +	if (!(a=malloc(GUESS))) return -1; + +	va_copy(ap2, ap); +	l=vsnprintf(a, GUESS, fmt, ap2); +	va_end(ap2); + +	if (l<GUESS) { +		char *b = realloc(a, l+1U); +		*s = b ? b : a; +		return l; +	} +	free(a); +	if (l<0 || !(*s=malloc(l+1U))) return -1; +	return vsnprintf(*s, l+1U, fmt, ap); +} diff --git a/src/stdio/vdprintf.c b/src/stdio/vdprintf.c new file mode 100644 index 00000000..bfb1b0a9 --- /dev/null +++ b/src/stdio/vdprintf.c @@ -0,0 +1,14 @@ +#include "stdio_impl.h" + +int vdprintf(int fd, const char *fmt, va_list ap) +{ +	int r; +	char buf[BUFSIZ]; +	FILE f = { +		.fd = fd, .lbf = EOF, .write = __stdio_write, +		.buf = buf+UNGET, .buf_size = sizeof buf - UNGET +	}; +	r = vfprintf(&f, fmt, ap); +	__oflow(&f); +	return r; +} diff --git a/src/stdio/vfprintf.c b/src/stdio/vfprintf.c new file mode 100644 index 00000000..5e19acc5 --- /dev/null +++ b/src/stdio/vfprintf.c @@ -0,0 +1,640 @@ +#include "stdio_impl.h" + +/* Some useful macros */ + +#define MAX(a,b) ((a)>(b) ? (a) : (b)) +#define MIN(a,b) ((a)<(b) ? (a) : (b)) +#define CONCAT2(x,y) x ## y +#define CONCAT(x,y) CONCAT2(x,y) + +/* Convenient bit representation for modifier flags, which all fall + * within 31 codepoints of the space character. */ + +#define ALT_FORM   (1U<<'#'-' ') +#define ZERO_PAD   (1U<<'0'-' ') +#define LEFT_ADJ   (1U<<'-'-' ') +#define PAD_POS    (1U<<' '-' ') +#define MARK_POS   (1U<<'+'-' ') +#define GROUPED    (1U<<'\''-' ') + +#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED) + +#if UINT_MAX == ULONG_MAX +#define LONG_IS_INT +#endif + +#if SIZE_MAX != ULONG_MAX || UINTMAX_MAX != ULLONG_MAX +#define ODD_TYPES +#endif + +/* State machine to accept length modifiers + conversion specifiers. + * Result is 0 on failure, or an argument type to pop on success. */ + +enum { +	BARE, LPRE, LLPRE, HPRE, HHPRE, BIGLPRE, +	ZTPRE, JPRE, +	STOP, +	PTR, INT, UINT, ULLONG, +#ifndef LONG_IS_INT +	LONG, ULONG, +#else +#define LONG INT +#define ULONG UINT +#endif +	SHORT, USHORT, CHAR, UCHAR, +#ifdef ODD_TYPES +	LLONG, SIZET, IMAX, UMAX, PDIFF, UIPTR, +#else +#define LLONG ULLONG +#define SIZET ULONG +#define IMAX LLONG +#define UMAX ULLONG +#define PDIFF LONG +#define UIPTR ULONG +#endif +	DBL, LDBL, +	NOARG, +	MAXSTATE +}; + +#define S(x) [(x)-'A'] + +static const unsigned char states[]['z'-'A'+1] = { +	{ /* 0: bare types */ +		S('d') = INT, S('i') = INT, +		S('o') = UINT, S('u') = UINT, S('x') = UINT, S('X') = UINT, +		S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL, +		S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL, +		S('c') = CHAR, S('C') = INT, +		S('s') = PTR, S('S') = PTR, S('p') = UIPTR, S('n') = PTR, +		S('m') = NOARG, +		S('l') = LPRE, S('h') = HPRE, S('L') = BIGLPRE, +		S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE, +	}, { /* 1: l-prefixed */ +		S('d') = LONG, S('i') = LONG, +		S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG, +		S('c') = INT, S('s') = PTR, S('n') = PTR, +		S('l') = LLPRE, +	}, { /* 2: ll-prefixed */ +		S('d') = LLONG, S('i') = LLONG, +		S('o') = ULLONG, S('u') = ULLONG, +		S('x') = ULLONG, S('X') = ULLONG, +		S('n') = PTR, +	}, { /* 3: h-prefixed */ +		S('d') = SHORT, S('i') = SHORT, +		S('o') = USHORT, S('u') = USHORT, +		S('x') = USHORT, S('X') = USHORT, +		S('n') = PTR, +		S('h') = HHPRE, +	}, { /* 4: hh-prefixed */ +		S('d') = CHAR, S('i') = CHAR, +		S('o') = UCHAR, S('u') = UCHAR, +		S('x') = UCHAR, S('X') = UCHAR, +		S('n') = PTR, +	}, { /* 5: L-prefixed */ +		S('e') = LDBL, S('f') = LDBL, S('g') = LDBL, S('a') = LDBL, +		S('E') = LDBL, S('F') = LDBL, S('G') = LDBL, S('A') = LDBL, +		S('n') = PTR, +	}, { /* 6: z- or t-prefixed (assumed to be same size) */ +		S('d') = PDIFF, S('i') = PDIFF, +		S('o') = SIZET, S('u') = SIZET, +		S('x') = SIZET, S('X') = SIZET, +		S('n') = PTR, +	}, { /* 7: j-prefixed */ +		S('d') = IMAX, S('i') = IMAX, +		S('o') = UMAX, S('u') = UMAX, +		S('x') = UMAX, S('X') = UMAX, +		S('n') = PTR, +	} +}; + +#define OOB(x) ((unsigned)(x)-'A' > 'z'-'A') + +union arg +{ +	uintmax_t i; +	long double f; +	void *p; +}; + +static void pop_arg(union arg *arg, int type, va_list *ap) +{ +	/* Give the compiler a hint for optimizing the switch. */ +	if ((unsigned)type > MAXSTATE) return; +	switch (type) { +	       case PTR:	arg->p = va_arg(*ap, void *); +	break; case INT:	arg->i = va_arg(*ap, int); +	break; case UINT:	arg->i = va_arg(*ap, unsigned int); +#ifndef LONG_IS_INT +	break; case LONG:	arg->i = va_arg(*ap, long); +	break; case ULONG:	arg->i = va_arg(*ap, unsigned long); +#endif +	break; case ULLONG:	arg->i = va_arg(*ap, unsigned long long); +	break; case SHORT:	arg->i = (short)va_arg(*ap, int); +	break; case USHORT:	arg->i = (unsigned short)va_arg(*ap, int); +	break; case CHAR:	arg->i = (signed char)va_arg(*ap, int); +	break; case UCHAR:	arg->i = (unsigned char)va_arg(*ap, int); +#ifdef ODD_TYPES +	break; case LLONG:	arg->i = va_arg(*ap, long long); +	break; case SIZET:	arg->i = va_arg(*ap, size_t); +	break; case IMAX:	arg->i = va_arg(*ap, intmax_t); +	break; case UMAX:	arg->i = va_arg(*ap, uintmax_t); +	break; case PDIFF:	arg->i = va_arg(*ap, ptrdiff_t); +	break; case UIPTR:	arg->i = (uintptr_t)va_arg(*ap, void *); +#endif +	break; case DBL:	arg->f = va_arg(*ap, double); +	break; case LDBL:	arg->f = va_arg(*ap, long double); +	} +} + +static void out(FILE *f, const char *s, size_t l) +{ +	__fwritex(s, l, f); +} + +static void pad(FILE *f, char c, int w, int l, int fl) +{ +	char pad[256]; +	if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return; +	l = w - l; +	memset(pad, c, l>sizeof pad ? sizeof pad : l); +	for (; l >= sizeof pad; l -= sizeof pad) +		out(f, pad, sizeof pad); +	out(f, pad, l); +} + +static const char xdigits[16] = { +	"0123456789ABCDEF" +}; + +static char *fmt_x(uintmax_t x, char *s, int lower) +{ +	for (; x; x>>=4) *--s = xdigits[(x&15)]|lower; +	return s; +} + +static char *fmt_o(uintmax_t x, char *s) +{ +	for (; x; x>>=3) *--s = '0' + (x&7); +	return s; +} + +static char *fmt_u(uintmax_t x, char *s) +{ +	unsigned long y; +	for (   ; x>ULONG_MAX; x/=10) *--s = '0' + x%10; +	for (y=x;           y; y/=10) *--s = '0' + y%10; +	return s; +} + +static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) +{ +	uint32_t big[(LDBL_MAX_EXP+LDBL_MANT_DIG)/9+1]; +	uint32_t *a, *d, *r, *z; +	int e2=0, e, i, j, l; +	char buf[9+LDBL_MANT_DIG/4], *s; +	const char *prefix="-+ "; +	int pl; +	char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr; + +	pl=1; +	if (y<0 || 1/y<0) { +		y=-y; +	} else if (fl & MARK_POS) { +		prefix++; +	} else if (fl & PAD_POS) { +		prefix+=2; +	} else pl=0; + +	if (!isfinite(y)) { +		char *s = (t&32)?"inf":"INF"; +		if (y!=y) s=(t&32)?"nan":"NAN", pl=0; +		pad(f, ' ', w, 3+pl, fl&~ZERO_PAD); +		out(f, prefix, pl); +		out(f, s, 3); +		pad(f, ' ', w, 3+pl, fl^LEFT_ADJ); +		return MAX(w, 3+pl); +	} + +	y = frexpl(y, &e2) * 2; +	if (y) e2--; + +	if ((t|32)=='a') { +		long double round = 8.0; +		int re; + +		if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0; +		else re=LDBL_MANT_DIG/4-1-p; + +		if (re) { +			if (pl && *prefix=='-') y=-y; +			while (re--) round*=16; +			y+=round; +			y-=round; +			if (y<0) y=-y; +		} + +		estr=fmt_u(e2<0 ? -e2 : e2, ebuf); +		if (estr==ebuf) *--estr='0'; +		*--estr = (e2<0 ? '-' : '+'); +		*--estr = t+('p'-'a'); + +		s=buf; +		*s++='0'; +		*s++=t+('x'-'a'); +		do { +			int x=y; +			*s++=xdigits[x]|(t&32); +			y=16*(y-x); +			if (s-buf==3 && (y||p>0||(fl&ALT_FORM))) *s++='.'; +		} while (y); + +		if (p<0) p = s-buf-4; +		l = 1 + p + (p || (fl&ALT_FORM)) + ebuf-estr; + +		pad(f, ' ', w, pl+l, fl); +		out(f, prefix, pl); +		pad(f, '0', w, pl+l, fl^ZERO_PAD); +		out(f, buf, s-buf); +		pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0); +		out(f, estr, ebuf-estr); +		pad(f, '0', w, pl+l, fl^LEFT_ADJ); +		return MAX(w, pl+l); +	} +	if (p<0) p=6; + +	y *= 0x1p28; e2-=28; + +	if (e2<0) a=r=z=big; +	else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1; + +	do { +		*z = y; +		y = 1000000000*(y-*z++); +	} while (y); + +	while (e2>0) { +		uint32_t carry=0; +		int sh=MIN(29,e2); +		for (d=z-1; d>=a; d--) { +			uint64_t x = ((uint64_t)*d<<sh)+carry; +			*d = x % 1000000000; +			carry = x / 1000000000; +		} +		if (!z[-1] && z>a) z--; +		if (carry) *--a = carry; +		e2-=sh; +	} +	while (e2<0) { +		uint32_t carry=0, *z2; +		int sh=MIN(9,-e2); +		for (d=a; d<z; d++) { +			uint32_t rm = *d & (1<<sh)-1; +			*d = (*d>>sh) + carry; +			carry = (1000000000>>sh) * rm; +		} +		if (!*a) a++; +		if (carry) *z++ = carry; +		/* Avoid (slow!) computation past requested precision */ +		z2 = ((t|32)=='f' ? r : a) + 2 + p/9; +		z = MIN(z, z2); +		e2+=sh; +	} + +	if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++); +	else e=0; + +	/* Perform rounding: j is precision after the radix (possibly neg) */ +	j = p - ((t|32)!='f')*e - ((t|32)=='g'); +	if (j < 9*(z-r-1)) { +		uint32_t x; +		/* We avoid C's broken division of negative numbers */ +		d = r + 1 + (j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP; +		j += 9*LDBL_MAX_EXP; +		j %= 9; +		for (i=10, j++; j<9; i*=10, j++); +		x = *d % i; +		/* Are there any significant digits past j? */ +		if (x || d+1!=z) { +			long double round = CONCAT(0x1p,LDBL_MANT_DIG); +			long double small; +			if (x<i/2) small=0x01p-1; +			else if (i==i/2 && d+1==z) small=0x10p-1; +			else small=0x11p-1; +			if (pl && *prefix=='-') round*=-1, small*=-1; +			/* Decide whether to round by probing round+small */ +			if (round+small != round) { +				*d = *d - x + i; +				while (*d > 999999999) { +					*d--=0; +					(*d)++; +				} +				if (d<a) a=d; +				for (i=10, e=9*(r-a); *a>=i; i*=10, e++); +			} +		} +		for (; !z[-1] && z>a; z--); +	} +	 +	if ((t|32)=='g') { +		if (!p) p++; +		if (p>e && e>=-4) { +			t--; +			p-=e+1; +		} else { +			t-=2; +			p--; +		} +		if (!(fl&ALT_FORM)) { +			/* Count trailing zeros in last place */ +			if (z>a) for (i=10, j=0; z[-1]%i==0; i*=10, j++); +			else j=9; +			if ((t|32)=='f') +				p = MIN(p,MAX(0,9*(z-r-1)-j)); +			else +				p = MIN(p,MAX(0,9*(z-r-1)+e-j)); +		} +	} +	l = 1 + p + (p || (fl&ALT_FORM)); +	if ((t|32)=='f') { +		if (e>0) l+=e; +	} else { +		estr=fmt_u(e<0 ? -e : e, ebuf); +		while(ebuf-estr<2) *--estr='0'; +		*--estr = (e<0 ? '-' : '+'); +		*--estr = t; +		l += ebuf-estr; +	} + +	pad(f, ' ', w, pl+l, fl); +	out(f, prefix, pl); +	pad(f, '0', w, pl+l, fl^ZERO_PAD); + +	if ((t|32)=='f') { +		if (a>r) a=r; +		for (d=a; d<=r; d++) { +			char *s = fmt_u(*d, buf+9); +			if (d!=a) while (s>buf) *--s='0'; +			else if (s==buf+9) *--s='0'; +			out(f, s, buf+9-s); +		} +		if (p || (fl&ALT_FORM)) out(f, ".", 1); +		for (; d<z && p>0; d++, p-=9) { +			char *s = fmt_u(*d, buf+9); +			while (s>buf) *--s='0'; +			out(f, s, MIN(9,p)); +		} +		pad(f, '0', p+9, 9, 0); +	} else { +		if (z<=a) z=a+1; +		for (d=a; d<z && p>=0; d++) { +			char *s = fmt_u(*d, buf+9); +			if (s==buf+9) *--s='0'; +			if (d!=a) while (s>buf) *--s='0'; +			else { +				out(f, s++, 1); +				if (p>0||(fl&ALT_FORM)) out(f, ".", 1); +			} +			out(f, s, MIN(buf+9-s, p)); +			p -= buf+9-s; +		} +		pad(f, '0', p+18, 18, 0); +		out(f, estr, ebuf-estr); +	} + +	pad(f, ' ', w, pl+l, fl^LEFT_ADJ); + +	return MAX(w, pl+l); +} + +static int getint(char **s) { +	int i; +	for (i=0; isdigit(**s); (*s)++) +		i = 10*i + (**s-'0'); +	return i; +} + +static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, int *nl_type) +{ +	char *a, *z, *s=(char *)fmt; +	unsigned l10n=0, litpct, fl; +	int w, p; +	union arg arg; +	int argpos; +	unsigned st, ps; +	int cnt=0, l=0; +	int i; +	char buf[sizeof(uintmax_t)*3+3+LDBL_MANT_DIG/4]; +	const char *prefix; +	int t, pl; +	wchar_t wc[2], *ws; +	char mb[4]; + +	for (;;) { +		/* Update output count, end loop when fmt is exhausted */ +		if (cnt >= 0) { +			if (l > INT_MAX - cnt) { +				if (!ferror(f)) errno = EOVERFLOW; +				cnt = -1; +			} else cnt += l; +		} +		if (!*s) break; + +		/* Handle literal text and %% format specifiers */ +		for (a=s; *s && *s!='%'; s++); +		litpct = strspn(s, "%")/2; /* Optimize %%%% runs */ +		z = s+litpct; +		s += 2*litpct; +		l = z-a; +		if (f) out(f, a, l); +		if (l) continue; + +		if (isdigit(s[1]) && s[2]=='$') { +			l10n=1; +			argpos = s[1]-'0'; +			s+=3; +		} else { +			argpos = -1; +			s++; +		} + +		/* Read modifier flags */ +		for (fl=0; (unsigned)*s-' '<32 && (FLAGMASK&(1U<<*s-' ')); s++) +			fl |= 1U<<*s-' '; + +		/* Read field width */ +		if (*s=='*') { +			if (isdigit(s[1]) && s[2]=='$') { +				l10n=1; +				nl_type[s[1]-'0'] = INT; +				w = nl_arg[s[1]-'0'].i; +				s+=3; +			} else if (!l10n) { +				w = f ? va_arg(*ap, int) : 0; +				s++; +			} else return -1; +			if (w<0) fl|=LEFT_ADJ, w=-w; +		} else if ((w=getint(&s))<0) return -1; + +		/* Read precision */ +		if (*s=='.' && s[1]=='*') { +			if (isdigit(s[2]) && s[3]=='$') { +				nl_type[s[2]-'0'] = INT; +				p = nl_arg[s[2]-'0'].i; +				s+=4; +			} else if (!l10n) { +				p = f ? va_arg(*ap, int) : 0; +				s+=2; +			} else return -1; +		} else if (*s=='.') { +			s++; +			p = getint(&s); +		} else p = -1; + +		/* Format specifier state machine */ +		st=0; +		do { +			if (OOB(*s)) return -1; +			ps=st; +			st=states[st]S(*s++); +		} while (st-1<STOP); +		if (!st) return -1; + +		/* Check validity of argument type (nl/normal) */ +		if (st==NOARG) { +			if (argpos>=0) return -1; +			else if (!f) continue; +		} else { +			if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos]; +			else if (f) pop_arg(&arg, st, ap); +			else return 0; +		} + +		if (!f) continue; + +		z = buf + sizeof(buf); +		prefix = "-+   0X0x"; +		pl = 0; +		t = s[-1]; + +		/* Transform ls,lc -> S,C */ +		if (ps && (t&15)==3) t&=~32; + +		/* - and 0 flags are mutually exclusive */ +		if (fl & LEFT_ADJ) fl &= ~ZERO_PAD; + +		switch(t) { +		case 'n': +			switch(ps) { +			case BARE: *(int *)arg.p = l; +			case LPRE: *(long *)arg.p = l; +			case LLPRE: *(long long *)arg.p = l; +			case HPRE: *(unsigned short *)arg.p = l; +			case HHPRE: *(unsigned char *)arg.p = l; +			case ZTPRE: *(size_t *)arg.p = l; +			case JPRE: *(uintmax_t *)arg.p = l; +			} +			continue; +		case 'p': +			p = MAX(p, 2*sizeof(void*)); +			t = 'x'; +			fl |= ALT_FORM; +		case 'x': case 'X': +			a = fmt_x(arg.i, z, t&32); +			if (fl & ALT_FORM) prefix+=(t>>4), pl=2; +			if (0) { +		case 'o': +			a = fmt_o(arg.i, z); +			if ((fl&ALT_FORM) && arg.i) prefix+=5, pl=1; +			} if (0) { +		case 'd': case 'i': +			pl=1; +			if (arg.i>INTMAX_MAX) { +				arg.i=-arg.i; +			} else if (fl & MARK_POS) { +				prefix++; +			} else if (fl & PAD_POS) { +				prefix+=2; +			} else pl=0; +		case 'u': +			a = fmt_u(arg.i, z); +			} +			if (!arg.i && !p) continue; +			if (p>=0) fl &= ~ZERO_PAD; +			p = MAX(p, z-a + !arg.i); +			break; +		case 'c': +			*(a=z-(p=1))=arg.i; +			fl &= ~ZERO_PAD; +			break; +		case 'm': +			if (1) a = strerror(errno); else +		case 's': +			a = arg.p; +			z = memchr(a, 0, p); +			if (!z) z=a+p; +			else p=z-a; +			fl &= ~ZERO_PAD; +			break; +		case 'C': +			wc[0] = arg.i; +			wc[1] = 0; +			arg.p = wc; +			p = -1; +		case 'S': +			ws = arg.p; +			for (i=0; *ws && (l=wctomb(mb, *ws++))>=0 && l<=0U+p-i; i+=l); +			if (l<0) return -1; +			p = i; +			pad(f, ' ', w, p, fl); +			ws = arg.p; +			for (i=0; *ws && i+(l=wctomb(mb, *ws++))<=p; i+=l) +				out(f, mb, l); +			pad(f, ' ', w, p, fl^LEFT_ADJ); +			l = w>p ? w : p; +			continue; +		case 'e': case 'f': case 'g': case 'a': +		case 'E': case 'F': case 'G': case 'A': +			l = fmt_fp(f, arg.f, w, p, fl, t); +			continue; +		} + +		if (p < z-a) p = z-a; +		if (w < pl+p) w = pl+p; + +		pad(f, ' ', w, pl+p, fl); +		out(f, prefix, pl); +		pad(f, '0', w, pl+p, fl^ZERO_PAD); +		pad(f, '0', p, z-a, 0); +		out(f, a, z-a); +		pad(f, ' ', w, pl+p, fl^LEFT_ADJ); + +		l = w; +	} + +	if (f) return cnt; +	if (!l10n) return 0; + +	for (i=1; i<=NL_ARGMAX && nl_type[i]; i++) +		pop_arg(nl_arg+i, nl_type[i], ap); +	for (; i<=NL_ARGMAX && !nl_type[i]; i++); +	if (i<=NL_ARGMAX) return -1; +	return 1; +} + +int vfprintf(FILE *f, const char *fmt, va_list ap) +{ +	va_list ap2; +	int nl_type[NL_ARGMAX] = {0}; +	union arg nl_arg[NL_ARGMAX]; +	int ret; + +	va_copy(ap2, ap); +	if (printf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) return -1; + +	FLOCK(f); +	ret = printf_core(f, fmt, &ap2, nl_arg, nl_type); +	FUNLOCK(f); +	va_end(ap2); +	return ret; +} diff --git a/src/stdio/vfscanf.c b/src/stdio/vfscanf.c new file mode 100644 index 00000000..69f45081 --- /dev/null +++ b/src/stdio/vfscanf.c @@ -0,0 +1,43 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include "stdio_impl.h" +#include "__scanf.h" + +static void f_read(rctx_t *r) +{ +	FILE *f = r->opaque; +	if ((r->c = __uflow(f)) >= 0) r->l++; +} + +int vfscanf(FILE *f, const char *fmt, va_list ap) +{ +	size_t l = strlen(fmt), i, result; +	rctx_t r = { f_read, (void *)f, 0, isspace }; +	wchar_t fmt2[l+1]; + +	if (l > 0x100000) { +		errno = ENOMEM; +		return -1; +	} +	for (i=0; i<=l; i++) fmt2[i] = (unsigned char)fmt[i]; + +	FLOCK(f); + +	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--; +	} + +	FUNLOCK(f); +	return result; +} diff --git a/src/stdio/vfwscanf.c b/src/stdio/vfwscanf.c new file mode 100644 index 00000000..491c1403 --- /dev/null +++ b/src/stdio/vfwscanf.c @@ -0,0 +1,28 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <wchar.h> +#include <wctype.h> + +#include "stdio_impl.h" +#include "__scanf.h" + +static void f_read(rctx_t *r) +{ +	FILE *f = r->opaque; +	if ((r->c = fgetwc(f)) >= 0) r->l++; +} + +int vfwscanf(FILE *f, const wchar_t *fmt, va_list ap) +{ +	rctx_t r = { f_read, (void *)f, 1, iswspace }; +	int result; + +	result = __scanf(&r, fmt, ap); + +	if (r.u && r.c >= 0) { +		ungetwc(r.c, f); +	} + +	return result; +} diff --git a/src/stdio/vprintf.c b/src/stdio/vprintf.c new file mode 100644 index 00000000..67b38dac --- /dev/null +++ b/src/stdio/vprintf.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int vprintf(const char *fmt, va_list ap) +{ +	return vfprintf(stdout, fmt, ap); +} diff --git a/src/stdio/vscanf.c b/src/stdio/vscanf.c new file mode 100644 index 00000000..6f55b1c3 --- /dev/null +++ b/src/stdio/vscanf.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <stdarg.h> + +int vscanf(const char *fmt, va_list ap) +{ +	return vfscanf(stdin, fmt, ap); +} diff --git a/src/stdio/vsnprintf.c b/src/stdio/vsnprintf.c new file mode 100644 index 00000000..bda6b49b --- /dev/null +++ b/src/stdio/vsnprintf.c @@ -0,0 +1,33 @@ +#include "stdio_impl.h" + +static size_t sn_write(FILE *f, const unsigned char *s, size_t l) +{ +	/* pretend to succeed, but discard data */ +	return l; +} + +int vsnprintf(char *s, size_t n, const char *fmt, va_list ap) +{ +	int r; +	FILE f; +	unsigned char buf[1]; + +	memset(&f, 0, sizeof(FILE)); +	f.lbf = EOF; +	f.write = sn_write; +	f.buf_size = 1; +	f.buf = buf; +	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 = s; +		f.wbase = f.wend = s+n-1; +		f.wstop = f.wend - 1; +	} +	r = vfprintf(&f, fmt, ap); +	/* wpos points just after last byte written, or to s+n-1 (wbase) */ +	*f.wpos = 0; +	return r; +} diff --git a/src/stdio/vsprintf.c b/src/stdio/vsprintf.c new file mode 100644 index 00000000..7836ccb2 --- /dev/null +++ b/src/stdio/vsprintf.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <limits.h> + +int vsprintf(char *s, const char *fmt, va_list ap) +{ +	return vsnprintf(s, INT_MAX, fmt, ap); +} diff --git a/src/stdio/vsscanf.c b/src/stdio/vsscanf.c new file mode 100644 index 00000000..fd48f709 --- /dev/null +++ b/src/stdio/vsscanf.c @@ -0,0 +1,21 @@ +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "__scanf.h" + +static void s_read(rctx_t *r) +{ +	unsigned char *s = r->opaque; +	if (!s[r->l]) r->c = -1; +	else r->c = s[r->l++]; +} + +int vsscanf(const char *s, const char *fmt, va_list ap) +{ +	size_t l = strlen(fmt), i; +	wchar_t fmt2[l+1]; +	rctx_t r = { s_read, (void *)s, 0, isspace }; +	for (i=0; i<=l; i++) fmt2[i] = (unsigned char)fmt[i]; +	return __scanf(&r, fmt2, ap); +} diff --git a/src/stdio/vswscanf.c b/src/stdio/vswscanf.c new file mode 100644 index 00000000..2c4ffbe0 --- /dev/null +++ b/src/stdio/vswscanf.c @@ -0,0 +1,19 @@ +#include <stdio.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> + +#include "__scanf.h" + +static void s_read(rctx_t *r) +{ +	wchar_t *s = r->opaque; +	if (!s[r->l]) r->c = -1; +	else r->c = s[r->l++]; +} + +int vswscanf(const wchar_t *s, const wchar_t *fmt, va_list ap) +{ +	rctx_t r = { s_read, (void *)s, 1, iswspace }; +	return __scanf(&r, fmt, ap); +} diff --git a/src/stdio/vwscanf.c b/src/stdio/vwscanf.c new file mode 100644 index 00000000..86da0457 --- /dev/null +++ b/src/stdio/vwscanf.c @@ -0,0 +1,8 @@ +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> + +int vwscanf(const wchar_t *fmt, va_list ap) +{ +	return vfwscanf(stdin, fmt, ap); +} diff --git a/src/stdio/wscanf.c b/src/stdio/wscanf.c new file mode 100644 index 00000000..34b58846 --- /dev/null +++ b/src/stdio/wscanf.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> + +int wscanf(const wchar_t *fmt, ...) +{ +	int ret; +	va_list ap; +	va_start(ap, fmt); +	ret = vwscanf(fmt, ap); +	va_end(ap); +	return ret; +} | 
