diff options
| author | Tim J. Robbins <tjr@FreeBSD.org> | 2002-09-21 13:00:30 +0000 | 
|---|---|---|
| committer | Tim J. Robbins <tjr@FreeBSD.org> | 2002-09-21 13:00:30 +0000 | 
| commit | c5604d0a50cc18e7c5546e9c77a8fdcb69cb5e3c (patch) | |
| tree | 81419641bed2078ca1975a619324e3a8c863309f /lib/libc | |
| parent | a5dd613ce64ef196a7fb028817ea9e84c2cc9385 (diff) | |
Notes
Diffstat (limited to 'lib/libc')
| -rw-r--r-- | lib/libc/stdio/Makefile.inc | 12 | ||||
| -rw-r--r-- | lib/libc/stdio/fwprintf.c | 45 | ||||
| -rw-r--r-- | lib/libc/stdio/local.h | 1 | ||||
| -rw-r--r-- | lib/libc/stdio/stdio.3 | 6 | ||||
| -rw-r--r-- | lib/libc/stdio/swprintf.c | 45 | ||||
| -rw-r--r-- | lib/libc/stdio/vfwprintf.c | 1538 | ||||
| -rw-r--r-- | lib/libc/stdio/vswprintf.c | 89 | ||||
| -rw-r--r-- | lib/libc/stdio/vwprintf.c | 39 | ||||
| -rw-r--r-- | lib/libc/stdio/wprintf.3 | 618 | ||||
| -rw-r--r-- | lib/libc/stdio/wprintf.c | 45 | 
10 files changed, 2434 insertions, 4 deletions
| diff --git a/lib/libc/stdio/Makefile.inc b/lib/libc/stdio/Makefile.inc index 00da45af4080..d4a7a435e3e8 100644 --- a/lib/libc/stdio/Makefile.inc +++ b/lib/libc/stdio/Makefile.inc @@ -8,19 +8,21 @@ SRCS+=	_flock_stub.c asprintf.c clrerr.c fclose.c fdopen.c feof.c ferror.c \  	fflush.c fgetc.c fgetln.c fgetpos.c fgets.c fgetwc.c fgetws.c \  	fileno.c findfp.c flags.c fopen.c fprintf.c fpurge.c fputc.c fputs.c \  	fputwc.c fputws.c fread.c freopen.c fscanf.c fseek.c fsetpos.c \ -	ftell.c funopen.c fvwrite.c fwalk.c fwide.c fwrite.c getc.c \ +	ftell.c funopen.c fvwrite.c fwalk.c fwide.c fwprintf.c fwrite.c getc.c \  	getchar.c gets.c getw.c getwc.c getwchar.c makebuf.c mktemp.c \  	perror.c printf.c putc.c putchar.c puts.c putw.c putwc.c putwchar.c \  	refill.c remove.c rewind.c rget.c scanf.c setbuf.c setbuffer.c \ -	setvbuf.c snprintf.c sprintf.c sscanf.c stdio.c tempnam.c tmpfile.c \ +	setvbuf.c snprintf.c sprintf.c sscanf.c stdio.c swprintf.c tempnam.c \ +	tmpfile.c \  	tmpnam.c ungetc.c ungetwc.c vasprintf.c vfprintf.c vfscanf.c \ -	vprintf.c vscanf.c vsnprintf.c vsprintf.c vsscanf.c wbuf.c wsetup.c +	vfwprintf.c vprintf.c vscanf.c vsnprintf.c vsprintf.c vsscanf.c \ +	vswprintf.c vwprintf.c wbuf.c wprintf.c wsetup.c  .if ${LIB} == "c"  MAN+=	fclose.3 ferror.3 fflush.3 fgetln.3 fgets.3 fgetws.3 fopen.3 fputs.3 \  	fputws.3 fread.3 fseek.3 funopen.3 fwide.3 getc.3 getwc.3 mktemp.3 \  	printf.3 putc.3 putwc.3 remove.3 scanf.3 setbuf.3 stdio.3 tmpnam.3 \ -	ungetc.3 ungetwc.3 +	ungetc.3 ungetwc.3 wprintf.3  MLINKS+=ferror.3 clearerr.3 ferror.3 feof.3 ferror.3 fileno.3  MLINKS+=fflush.3 fpurge.3 @@ -45,4 +47,6 @@ MLINKS+=scanf.3 fscanf.3 scanf.3 sscanf.3 scanf.3 vfscanf.3 scanf.3 vscanf.3 \  	scanf.3 vsscanf.3  MLINKS+=setbuf.3 setbuffer.3 setbuf.3 setlinebuf.3 setbuf.3 setvbuf.3  MLINKS+=tmpnam.3 tempnam.3 tmpnam.3 tmpfile.3 +MLINKS+=wprintf.3 fwprintf.3 wprintf.3 swprintf.3 \ +	wprintf.3 vwprintf.3 wprintf.3 vfwprintf.3 wprintf.3 vswprintf.3  .endif diff --git a/lib/libc/stdio/fwprintf.c b/lib/libc/stdio/fwprintf.c new file mode 100644 index 000000000000..a22edaeb55f0 --- /dev/null +++ b/lib/libc/stdio/fwprintf.c @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2002 Tim J. Robbins + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdarg.h> +#include <stdio.h> +#include <wchar.h> + +int +fwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt, ...) +{ +	int ret; +	va_list ap; + +	va_start(ap, fmt); +	ret = vfwprintf(fp, fmt, ap); +	va_end(ap); + +	return (ret); +} diff --git a/lib/libc/stdio/local.h b/lib/libc/stdio/local.h index 6c860cd87595..309cc9c06e88 100644 --- a/lib/libc/stdio/local.h +++ b/lib/libc/stdio/local.h @@ -72,6 +72,7 @@ extern int	__swsetup(FILE *);  extern int	__sflags(const char *, int *);  extern int	__ungetc(int, FILE *);  extern int	__vfprintf(FILE *, const char *, __va_list); +extern int	__vfwprintf(FILE *, const wchar_t *, __va_list);  extern int	__sdidinit; diff --git a/lib/libc/stdio/stdio.3 b/lib/libc/stdio/stdio.3 index 4561a980360c..d1e53c34afcc 100644 --- a/lib/libc/stdio/stdio.3 +++ b/lib/libc/stdio/stdio.3 @@ -268,6 +268,7 @@ library conforms to  .It "funopen	open a stream"  .It "fwide	set/get orientation of stream"  .It "fwopen	open a stream" +.It "fwprintf	formatted wide character output conversion"  .It "fwrite	binary stream input/output"  .It "getc	get next character or word from input stream"  .It "getchar	get next character or word from input stream" @@ -297,6 +298,7 @@ library conforms to  .It "sprintf	formatted output conversion"  .It "sscanf	input format conversion"  .It "strerror	system error messages" +.It "swprintf	formatted wide character output conversion"  .It "sys_errlist	system error messages"  .It "sys_nerr	system error messages"  .It "tempnam	temporary file routines" @@ -307,9 +309,13 @@ library conforms to  .It "vasprintf	formatted output conversion"  .It "vfprintf	formatted output conversion"  .It "vfscanf	input format conversion" +.It "vfwprintf	formatted wide character output conversion"  .It "vprintf	formatted output conversion"  .It "vscanf	input format conversion"  .It "vsnprintf	formatted output conversion"  .It "vsprintf	formatted output conversion"  .It "vsscanf	input format conversion" +.It "vswprintf	formatted wide character output conversion" +.It "vwprintf	formatted wide character output conversion" +.It "wprintf	formatted wide character output conversion"  .El diff --git a/lib/libc/stdio/swprintf.c b/lib/libc/stdio/swprintf.c new file mode 100644 index 000000000000..d66531820d30 --- /dev/null +++ b/lib/libc/stdio/swprintf.c @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2002 Tim J. Robbins + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdarg.h> +#include <stdio.h> +#include <wchar.h> + +int +swprintf(wchar_t * __restrict s, size_t n, const wchar_t * __restrict fmt, ...) +{ +	int ret; +	va_list ap; + +	va_start(ap, fmt); +	ret = vswprintf(s, n, fmt, ap); +	va_end(ap); + +	return (ret); +} diff --git a/lib/libc/stdio/vfwprintf.c b/lib/libc/stdio/vfwprintf.c new file mode 100644 index 000000000000..7c8e47788e79 --- /dev/null +++ b/lib/libc/stdio/vfwprintf.c @@ -0,0 +1,1538 @@ +/*- + * Copyright (c) 1990, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#if 0 +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)vfprintf.c	8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +__FBSDID("FreeBSD: src/lib/libc/stdio/vfprintf.c,v 1.45 2002/09/06 11:23:55 tjr Exp "); +#endif +__FBSDID("$FreeBSD$"); + +/* + * Actual wprintf innards. + * + * Avoid making gratuitous changes to this source file; it should be kept + * as close as possible to vfprintf.c for ease of maintenance. + */ + +#include "namespace.h" +#include <sys/types.h> + +#include <ctype.h> +#include <limits.h> +#include <locale.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> +#include "un-namespace.h" + +#include "libc_private.h" +#include "local.h" +#include "fvwrite.h" + +/* Define FLOATING_POINT to get floating point. */ +#define	FLOATING_POINT + +union arg { +	int	intarg; +	u_int	uintarg; +	long	longarg; +	u_long	ulongarg; +	long long longlongarg; +	unsigned long long ulonglongarg; +	ptrdiff_t ptrdiffarg; +	size_t	sizearg; +	intmax_t intmaxarg; +	uintmax_t uintmaxarg; +	void	*pvoidarg; +	char	*pchararg; +	signed char *pschararg; +	short	*pshortarg; +	int	*pintarg; +	long	*plongarg; +	long long *plonglongarg; +	ptrdiff_t *pptrdiffarg; +	size_t	*psizearg; +	intmax_t *pintmaxarg; +#ifdef FLOATING_POINT +	double	doublearg; +	long double longdoublearg; +#endif +	wint_t	wintarg; +	wchar_t	*pwchararg; +}; + +/* + * Type ids for argument type table. + */ +enum typeid { +	T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT, +	T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG, +	T_PTRDIFFT, TP_PTRDIFFT, T_SIZET, TP_SIZET, +	T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR, +	T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR +}; + +static int	__sbprintf(FILE *, const wchar_t *, va_list); +static wchar_t	*__ujtoa(uintmax_t, wchar_t *, int, int, wchar_t *, int, char, +		    const char *); +static wchar_t	*__ultoa(u_long, wchar_t *, int, int, wchar_t *, int, char, +		    const char *); +static wchar_t	*__mbsconv(char *, int); +static void	__find_arguments(const wchar_t *, va_list, union arg **); +static void	__grow_type_table(int, enum typeid **, int *); + +/* + * Helper function for `fprintf to unbuffered unix file': creates a + * temporary buffer.  We only work on write-only files; this avoids + * worries about ungetc buffers and so forth. + */ +static int +__sbprintf(FILE *fp, const wchar_t *fmt, va_list ap) +{ +	int ret; +	FILE fake; +	unsigned char buf[BUFSIZ]; + +	/* copy the important variables */ +	fake._flags = fp->_flags & ~__SNBF; +	fake._file = fp->_file; +	fake._cookie = fp->_cookie; +	fake._write = fp->_write; +	fake._extra = fp->_extra; + +	/* set up the buffer */ +	fake._bf._base = fake._p = buf; +	fake._bf._size = fake._w = sizeof(buf); +	fake._lbfsize = 0;	/* not actually used, but Just In Case */ + +	/* do the work, then copy any error status */ +	ret = __vfwprintf(&fake, fmt, ap); +	if (ret >= 0 && __fflush(&fake)) +		ret = WEOF; +	if (fake._flags & __SERR) +		fp->_flags |= __SERR; +	return (ret); +} + +/* + * Macros for converting digits to letters and vice versa + */ +#define	to_digit(c)	((c) - '0') +#define is_digit(c)	((unsigned)to_digit(c) <= 9) +#define	to_char(n)	((n) + '0') + +/* + * Convert an unsigned long to ASCII for printf purposes, returning + * a pointer to the first character of the string representation. + * Octal numbers can be forced to have a leading zero; hex numbers + * use the given digits. + */ +static wchar_t * +__ultoa(u_long val, wchar_t *endp, int base, int octzero, wchar_t *xdigs, +	int needgrp, char thousep, const char *grp) +{ +	wchar_t *cp = endp; +	long sval; +	int ndig; + +	/* +	 * Handle the three cases separately, in the hope of getting +	 * better/faster code. +	 */ +	switch (base) { +	case 10: +		if (val < 10) {	/* many numbers are 1 digit */ +			*--cp = to_char(val); +			return (cp); +		} +		ndig = 0; +		/* +		 * On many machines, unsigned arithmetic is harder than +		 * signed arithmetic, so we do at most one unsigned mod and +		 * divide; this is sufficient to reduce the range of +		 * the incoming value to where signed arithmetic works. +		 */ +		if (val > LONG_MAX) { +			*--cp = to_char(val % 10); +			ndig++; +			sval = val / 10; +		} else +			sval = val; +		do { +			*--cp = to_char(sval % 10); +			ndig++; +			/* +			 * If (*grp == CHAR_MAX) then no more grouping +			 * should be performed. +			 */ +			if (needgrp && ndig == *grp && *grp != CHAR_MAX +					&& sval > 9) { +				*--cp = thousep; +				ndig = 0; +				/* +				 * If (*(grp+1) == '\0') then we have to +				 * use *grp character (last grouping rule) +				 * for all next cases +				 */ +				if (*(grp+1) != '\0') +					grp++; +			} +			sval /= 10; +		} while (sval != 0); +		break; + +	case 8: +		do { +			*--cp = to_char(val & 7); +			val >>= 3; +		} while (val); +		if (octzero && *cp != '0') +			*--cp = '0'; +		break; + +	case 16: +		do { +			*--cp = xdigs[val & 15]; +			val >>= 4; +		} while (val); +		break; + +	default:			/* oops */ +		abort(); +	} +	return (cp); +} + +/* Identical to __ultoa, but for intmax_t. */ +static wchar_t * +__ujtoa(uintmax_t val, wchar_t *endp, int base, int octzero, wchar_t *xdigs,  +	int needgrp, char thousep, const char *grp) +{ +	wchar_t *cp = endp; +	intmax_t sval; +	int ndig; + +	/* quick test for small values; __ultoa is typically much faster */ +	/* (perhaps instead we should run until small, then call __ultoa?) */ +	if (val <= ULONG_MAX) +		return (__ultoa((u_long)val, endp, base, octzero, xdigs, +		    needgrp, thousep, grp)); +	switch (base) { +	case 10: +		if (val < 10) { +			*--cp = to_char(val % 10); +			return (cp); +		} +		ndig = 0; +		if (val > INTMAX_MAX) { +			*--cp = to_char(val % 10); +			ndig++; +			sval = val / 10; +		} else +			sval = val; +		do { +			*--cp = to_char(sval % 10); +			ndig++; +			/* +			 * If (*grp == CHAR_MAX) then no more grouping +			 * should be performed. +			 */ +			if (needgrp && *grp != CHAR_MAX && ndig == *grp +					&& sval > 9) { +				*--cp = thousep; +				ndig = 0; +				/* +				 * If (*(grp+1) == '\0') then we have to +				 * use *grp character (last grouping rule) +				 * for all next cases +				 */ +				if (*(grp+1) != '\0') +					grp++; +			} +			sval /= 10; +		} while (sval != 0); +		break; + +	case 8: +		do { +			*--cp = to_char(val & 7); +			val >>= 3; +		} while (val); +		if (octzero && *cp != '0') +			*--cp = '0'; +		break; + +	case 16: +		do { +			*--cp = xdigs[val & 15]; +			val >>= 4; +		} while (val); +		break; + +	default: +		abort(); +	} +	return (cp); +} + +/* + * Convert a multibyte character string argument for the %s format to a wide + * string representation. ``prec'' specifies the maximum number of bytes + * to output. If ``prec'' is greater than or equal to zero, we can't assume + * that the multibyte char. string ends in a null character. + */ +static wchar_t * +__mbsconv(char *mbsarg, int prec) +{ +	wchar_t *convbuf, *wcp; +	const char *p; +	size_t insize, nchars, nconv; +	mbstate_t mbs; + +	/* +	 * Supplied argument is a multibyte string; convert it to wide +	 * characters first. +	 */ +	if (prec >= 0) { +		/* +		 * String is not guaranteed to be NUL-terminated. Find the +		 * number of characters to print. +		 */ +		memset(&mbs, 0, sizeof(mbs)); +		p = mbsarg; +		insize = nchars = 0; +		while (nchars != (size_t)prec) { +			nconv = mbrlen(p, MB_CUR_MAX, &mbs); +			if (nconv == 0 || nconv == (size_t)-1 || +			    nconv == (size_t)-2) +				break; +			p += nconv; +			nchars++; +			insize += nconv; +		} +		if (nconv == (size_t)-1 || nconv == (size_t)-2) +			return (NULL); +	} else +		insize = strlen(mbsarg); + +	/* +	 * Allocate buffer for the result and perform the conversion, +	 * converting at most `size' bytes of the input multibyte string to +	 * wide characters for printing. +	 */ +	convbuf = malloc((insize + 1) * sizeof(*convbuf)); +	if (convbuf == NULL) +		return (NULL); +	wcp = convbuf; +	p = mbsarg; +	memset(&mbs, 0, sizeof(mbs)); +	while (insize != 0) { +		nconv = mbrtowc(wcp, p, insize, &mbs); +		if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) +			break; +		wcp++; +		p += nconv; +		insize -= nconv; +	} +	if (nconv == (size_t)-1 || nconv == (size_t)-2) { +		free(convbuf); +		return (NULL); +	} +	*wcp = L'\0'; + +	return (convbuf); +} + +/* + * MT-safe version + */ +int +vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, va_list ap) + +{ +	int ret; + +	FLOCKFILE(fp); +	ret = __vfwprintf(fp, fmt0, ap); +	FUNLOCKFILE(fp); +	return (ret); +} + +#ifdef FLOATING_POINT +#include <math.h> +#include "floatio.h" + +#define	BUF		((MAXEXP*2)+MAXFRACT+1)		/* + decimal point */ +#define	DEFPREC		6 + +static wchar_t *cvt(double, int, int, char *, int *, wchar_t, int *, +	    wchar_t **); +static int exponent(wchar_t *, int, wchar_t); + +#else /* no FLOATING_POINT */ + +#define	BUF		136 + +#endif /* FLOATING_POINT */ + +#define STATIC_ARG_TBL_SIZE 8           /* Size of static argument table. */ + +/* + * Flags used during conversion. + */ +#define	ALT		0x001		/* alternate form */ +#define	HEXPREFIX	0x002		/* add 0x or 0X prefix */ +#define	LADJUST		0x004		/* left adjustment */ +#define	LONGDBL		0x008		/* long double */ +#define	LONGINT		0x010		/* long integer */ +#define	LLONGINT	0x020		/* long long integer */ +#define	SHORTINT	0x040		/* short integer */ +#define	ZEROPAD		0x080		/* zero (as opposed to blank) pad */ +#define	FPT		0x100		/* Floating point number */ +#define	GROUPING	0x200		/* use grouping ("'" flag) */ +					/* C99 additional size modifiers: */ +#define	SIZET		0x400		/* size_t */ +#define	PTRDIFFT	0x800		/* ptrdiff_t */ +#define	INTMAXT		0x1000		/* intmax_t */ +#define	CHARINT		0x2000		/* print char using int format */ + +/* + * Non-MT-safe version + */ +int +__vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap) +{ +	wchar_t *fmt;		/* format string */ +	wchar_t ch;		/* character from fmt */ +	int n, n2;		/* handy integer (short term usage) */ +	wchar_t *cp;		/* handy char pointer (short term usage) */ +	int flags;		/* flags as above */ +	int ret;		/* return value accumulator */ +	int width;		/* width from format (%8d), or 0 */ +	int prec;		/* precision from format (%.3d), or -1 */ +	wchar_t sign;		/* sign prefix (' ', '+', '-', or \0) */ +	char thousands_sep;	/* locale specific thousands separator */ +	const char *grouping;	/* locale specific numeric grouping rules */ +#ifdef FLOATING_POINT +	char *decimal_point;	/* locale specific decimal point */ +	char softsign;		/* temporary negative sign for floats */ +	double _double;		/* double precision arguments %[eEfgG] */ +	int expt;		/* integer value of exponent */ +	int expsize;		/* character count for expstr */ +	int ndig;		/* actual number of digits returned by cvt */ +	wchar_t expstr[7];	/* buffer for exponent string */ +	wchar_t *dtoaresult;	/* buffer allocated by dtoa */ +#endif +	u_long	ulval;		/* integer arguments %[diouxX] */ +	uintmax_t ujval;	/* %j, %ll, %q, %t, %z integers */ +	int base;		/* base for [diouxX] conversion */ +	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */ +	int realsz;		/* field size expanded by dprec, sign, etc */ +	int size;		/* size of converted field or string */ +	int prsize;             /* max size of printed field */ +	wchar_t *xdigs;		/* digits for [xX] conversion */ +	wchar_t buf[BUF];	/* space for %c, %[diouxX], %[eEfFgG] */ +	wchar_t ox[2];		/* space for 0x hex-prefix */ +	union arg *argtable;	/* args, built due to positional arg */ +	union arg statargtable [STATIC_ARG_TBL_SIZE]; +	int nextarg;		/* 1-based argument index */ +	va_list orgap;		/* original argument pointer */ +	wchar_t *convbuf;	/* multibyte to wide conversion result */ + +	/* +	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf +	 * fields occur frequently, increase PADSIZE and make the initialisers +	 * below longer. +	 */ +#define	PADSIZE	16		/* pad chunk size */ +	static wchar_t blanks[PADSIZE] = +	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; +	static wchar_t zeroes[PADSIZE] = +	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + +	/* +	 * BEWARE, these `goto error' on error, PRINT uses `n2' and +	 * PAD uses `n'. +	 */ +#define	PRINT(ptr, len)	do {			\ +	for (n2 = 0; n2 < (len); n2++)		\ +		__fputwc((ptr)[n2], fp);	\ +} while (0) +#define	PAD(howmany, with)	do {		\ +	if ((n = (howmany)) > 0) {		\ +		while (n > PADSIZE) {		\ +			PRINT(with, PADSIZE);	\ +			n -= PADSIZE;		\ +		}				\ +		PRINT(with, n);			\ +	}					\ +} while (0) + +	/* +	 * Get the argument indexed by nextarg.   If the argument table is +	 * built, use it to get the argument.  If its not, get the next +	 * argument (and arguments must be gotten sequentially). +	 */ +#define GETARG(type) \ +	((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ +	    (nextarg++, va_arg(ap, type))) + +	/* +	 * To extend shorts properly, we need both signed and unsigned +	 * argument extraction methods. +	 */ +#define	SARG() \ +	(flags&LONGINT ? GETARG(long) : \ +	    flags&SHORTINT ? (long)(short)GETARG(int) : \ +	    flags&CHARINT ? (long)(signed char)GETARG(int) : \ +	    (long)GETARG(int)) +#define	UARG() \ +	(flags&LONGINT ? GETARG(u_long) : \ +	    flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ +	    flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ +	    (u_long)GETARG(u_int)) +#define	INTMAX_SIZE	(INTMAXT|SIZET|PTRDIFFT|LLONGINT) +#define SJARG() \ +	(flags&INTMAXT ? GETARG(intmax_t) : \ +	    flags&SIZET ? (intmax_t)GETARG(size_t) : \ +	    flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ +	    (intmax_t)GETARG(long long)) +#define	UJARG() \ +	(flags&INTMAXT ? GETARG(uintmax_t) : \ +	    flags&SIZET ? (uintmax_t)GETARG(size_t) : \ +	    flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ +	    (uintmax_t)GETARG(unsigned long long)) + +	/* +	 * Get * arguments, including the form *nn$.  Preserve the nextarg +	 * that the argument can be gotten once the type is determined. +	 */ +#define GETASTER(val) \ +	n2 = 0; \ +	cp = fmt; \ +	while (is_digit(*cp)) { \ +		n2 = 10 * n2 + to_digit(*cp); \ +		cp++; \ +	} \ +	if (*cp == '$') { \ +		int hold = nextarg; \ +		if (argtable == NULL) { \ +			argtable = statargtable; \ +			__find_arguments (fmt0, orgap, &argtable); \ +		} \ +		nextarg = n2; \ +		val = GETARG (int); \ +		nextarg = hold; \ +		fmt = ++cp; \ +	} else { \ +		val = GETARG (int); \ +	} + + +	thousands_sep = '\0'; +	grouping = NULL; +#ifdef FLOATING_POINT +	dtoaresult = NULL; +	decimal_point = localeconv()->decimal_point; +#endif +	convbuf = NULL; +	/* sorry, fwprintf(read_only_file, L"") returns WEOF, not 0 */ +	if (cantwrite(fp)) +		return (EOF); + +	/* optimise fprintf(stderr) (and other unbuffered Unix files) */ +	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && +	    fp->_file >= 0) +		return (__sbprintf(fp, fmt0, ap)); + +	fmt = (wchar_t *)fmt0; +	argtable = NULL; +	nextarg = 1; +	orgap = ap; +	ret = 0; + +	/* +	 * Scan the format for conversions (`%' character). +	 */ +	for (;;) { +		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) +			/* void */; +		if ((n = fmt - cp) != 0) { +			if ((unsigned)ret + n > INT_MAX) { +				ret = EOF; +				goto error; +			} +			PRINT(cp, n); +			ret += n; +		} +		if (ch == '\0') +			goto done; +		fmt++;		/* skip over '%' */ + +		flags = 0; +		dprec = 0; +		width = 0; +		prec = -1; +		sign = '\0'; + +rflag:		ch = *fmt++; +reswitch:	switch (ch) { +		case ' ': +			/*- +			 * ``If the space and + flags both appear, the space +			 * flag will be ignored.'' +			 *	-- ANSI X3J11 +			 */ +			if (!sign) +				sign = ' '; +			goto rflag; +		case '#': +			flags |= ALT; +			goto rflag; +		case '*': +			/*- +			 * ``A negative field width argument is taken as a +			 * - flag followed by a positive field width.'' +			 *	-- ANSI X3J11 +			 * They don't exclude field widths read from args. +			 */ +			GETASTER (width); +			if (width >= 0) +				goto rflag; +			width = -width; +			/* FALLTHROUGH */ +		case '-': +			flags |= LADJUST; +			goto rflag; +		case '+': +			sign = '+'; +			goto rflag; +		case '\'': +			flags |= GROUPING; +			thousands_sep = *(localeconv()->thousands_sep); +			grouping = localeconv()->grouping; +			goto rflag; +		case '.': +			if ((ch = *fmt++) == '*') { +				GETASTER (n); +				prec = n < 0 ? -1 : n; +				goto rflag; +			} +			n = 0; +			while (is_digit(ch)) { +				n = 10 * n + to_digit(ch); +				ch = *fmt++; +			} +			prec = n < 0 ? -1 : n; +			goto reswitch; +		case '0': +			/*- +			 * ``Note that 0 is taken as a flag, not as the +			 * beginning of a field width.'' +			 *	-- ANSI X3J11 +			 */ +			flags |= ZEROPAD; +			goto rflag; +		case '1': case '2': case '3': case '4': +		case '5': case '6': case '7': case '8': case '9': +			n = 0; +			do { +				n = 10 * n + to_digit(ch); +				ch = *fmt++; +			} while (is_digit(ch)); +			if (ch == '$') { +				nextarg = n; +				if (argtable == NULL) { +					argtable = statargtable; +					__find_arguments (fmt0, orgap, +					    &argtable); +				} +				goto rflag; +			} +			width = n; +			goto reswitch; +#ifdef FLOATING_POINT +		case 'L': +			flags |= LONGDBL; +			goto rflag; +#endif +		case 'h': +			if (flags & SHORTINT) { +				flags &= ~SHORTINT; +				flags |= CHARINT; +			} else +				flags |= SHORTINT; +			goto rflag; +		case 'j': +			flags |= INTMAXT; +			goto rflag; +		case 'l': +			if (flags & LONGINT) { +				flags &= ~LONGINT; +				flags |= LLONGINT; +			} else +				flags |= LONGINT; +			goto rflag; +		case 'q': +			flags |= LLONGINT;	/* not necessarily */ +			goto rflag; +		case 't': +			flags |= PTRDIFFT; +			goto rflag; +		case 'z': +			flags |= SIZET; +			goto rflag; +		case 'c': +			if (flags & LONGINT) +				*(cp = buf) = (wchar_t)GETARG(wint_t); +			else +				*(cp = buf) = (wchar_t)btowc(GETARG(int)); +			size = 1; +			sign = '\0'; +			break; +		case 'D': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'd': +		case 'i': +			if (flags & INTMAX_SIZE) { +				ujval = SJARG(); +				if ((intmax_t)ujval < 0) { +					ujval = -ujval; +					sign = '-'; +				} +			} else { +				ulval = SARG(); +				if ((long)ulval < 0) { +					ulval = -ulval; +					sign = '-'; +				} +			} +			base = 10; +			goto number; +#ifdef FLOATING_POINT +#ifdef HEXFLOAT +		case 'a': +		case 'A': +#endif +		case 'e': +		case 'E': +			/*- +			 * Grouping apply to %i, %d, %u, %f, %F, %g, %G +			 * conversion specifiers only. For other conversions +			 * behavior is undefined. +			 *	-- POSIX +			 */ +			flags &= ~GROUPING; +			/*FALLTHROUGH*/ +		case 'f': +		case 'F': +			goto fp_begin; +		case 'g': +		case 'G': +			if (prec == 0) +				prec = 1; +fp_begin:		if (prec == -1) +				prec = DEFPREC; +			if (flags & LONGDBL) +				/* XXX this loses precision. */ +				_double = (double)GETARG(long double); +			else +				_double = GETARG(double); +			/* do this before tricky precision changes */ +			if (isinf(_double)) { +				if (_double < 0) +					sign = '-'; +				if (iswupper(ch)) +					cp = L"INF"; +				else +					cp = L"inf"; +				size = 3; +				break; +			} +			if (isnan(_double)) { +				if (iswupper(ch)) +					cp = L"NAN"; +				else +					cp = L"nan"; +				size = 3; +				break; +			} +			flags |= FPT; +			if (dtoaresult != NULL) { +				free(dtoaresult); +				dtoaresult = NULL; +			} +			cp = cvt(_double, prec, flags, &softsign, +				&expt, ch, &ndig, &dtoaresult); +			if (ch == 'g' || ch == 'G') { +				if (expt <= -4 || expt > prec) +					ch = (ch == 'g') ? 'e' : 'E'; +				else +					ch = 'g'; +			} +			if (ch == 'e' || ch == 'E') { +				--expt; +				expsize = exponent(expstr, expt, ch); +				size = expsize + ndig; +				if (ndig > 1 || flags & ALT) +					++size; +			} else if (ch == 'f' || ch == 'F') { +				if (expt > 0) { +					size = expt; +					if (prec || flags & ALT) +						size += prec + 1; +				} else	/* "0.X" */ +					size = prec + 2; +			} else if (expt >= ndig) {	/* fixed g fmt */ +				size = expt; +				if (flags & ALT) +					++size; +			} else +				size = ndig + (expt > 0 ? +					1 : 2 - expt); + +			if (softsign) +				sign = '-'; +			break; +#endif /* FLOATING_POINT */ +		case 'n': +			/* +			 * Assignment-like behavior is specified if the +			 * value overflows or is otherwise unrepresentable. +			 * C99 says to use `signed char' for %hhn conversions. +			 */ +			if (flags & LLONGINT) +				*GETARG(long long *) = ret; +			else if (flags & SIZET) +				*GETARG(ssize_t *) = (ssize_t)ret; +			else if (flags & PTRDIFFT) +				*GETARG(ptrdiff_t *) = ret; +			else if (flags & INTMAXT) +				*GETARG(intmax_t *) = ret; +			else if (flags & LONGINT) +				*GETARG(long *) = ret; +			else if (flags & SHORTINT) +				*GETARG(short *) = ret; +			else if (flags & CHARINT) +				*GETARG(signed char *) = ret; +			else +				*GETARG(int *) = ret; +			continue;	/* no output */ +		case 'O': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'o': +			if (flags & INTMAX_SIZE) +				ujval = UJARG(); +			else +				ulval = UARG(); +			base = 8; +			goto nosign; +		case 'p': +			/*- +			 * ``The argument shall be a pointer to void.  The +			 * value of the pointer is converted to a sequence +			 * of printable characters, in an implementation- +			 * defined manner.'' +			 *	-- ANSI X3J11 +			 */ +			ujval = (uintmax_t)(uintptr_t)GETARG(void *); +			base = 16; +			xdigs = L"0123456789abcdef"; +			flags = flags | INTMAXT | HEXPREFIX; +			ch = 'x'; +			goto nosign; +		case 's': +			if (flags & LONGINT) { +				if ((cp = GETARG(wchar_t *)) == NULL) +					cp = L"(null)"; +			} else { +				char *mbp; + +				if (convbuf != NULL) +					free(convbuf); +				if ((mbp = GETARG(char *)) == NULL) +					cp = L"(null)"; +				else { +					convbuf = __mbsconv(mbp, prec); +					if (convbuf == NULL) +						goto error; +					cp = convbuf; +				} +			} + +			if (prec >= 0) { +				/* +				 * can't use wcslen; can only look for the +				 * NUL in the first `prec' characters, and +				 * wcslen() will go further. +				 */ +				wchar_t *p = wmemchr(cp, 0, (size_t)prec); + +				if (p != NULL) { +					size = p - cp; +					if (size > prec) +						size = prec; +				} else +					size = prec; +			} else +				size = wcslen(cp); +			sign = '\0'; +			break; +		case 'U': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'u': +			if (flags & INTMAX_SIZE) +				ujval = UJARG(); +			else +				ulval = UARG(); +			base = 10; +			goto nosign; +		case 'X': +			xdigs = L"0123456789ABCDEF"; +			goto hex; +		case 'x': +			xdigs = L"0123456789abcdef"; +hex: +			if (flags & INTMAX_SIZE) +				ujval = UJARG(); +			else +				ulval = UARG(); +			base = 16; +			/* leading 0x/X only if non-zero */ +			if (flags & ALT && +			    (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) +				flags |= HEXPREFIX; + +			flags &= ~GROUPING; +			/* unsigned conversions */ +nosign:			sign = '\0'; +			/*- +			 * ``... diouXx conversions ... if a precision is +			 * specified, the 0 flag will be ignored.'' +			 *	-- ANSI X3J11 +			 */ +number:			if ((dprec = prec) >= 0) +				flags &= ~ZEROPAD; + +			/*- +			 * ``The result of converting a zero value with an +			 * explicit precision of zero is no characters.'' +			 *	-- ANSI X3J11 +			 */ +			cp = buf + BUF; +			if (flags & INTMAX_SIZE) { +				if (ujval != 0 || prec != 0) +					cp = __ujtoa(ujval, cp, base, +					    flags & ALT, xdigs, +					    flags & GROUPING, thousands_sep, +					    grouping); +			} else { +				if (ulval != 0 || prec != 0) +					cp = __ultoa(ulval, cp, base, +					    flags & ALT, xdigs, +					    flags & GROUPING, thousands_sep, +					    grouping); +			} +			size = buf + BUF - cp; +			break; +		default:	/* "%?" prints ?, unless ? is NUL */ +			if (ch == '\0') +				goto done; +			/* pretend it was %c with argument ch */ +			cp = buf; +			*cp = ch; +			size = 1; +			sign = '\0'; +			break; +		} + +		/* +		 * All reasonable formats wind up here.  At this point, `cp' +		 * points to a string which (if not flags&LADJUST) should be +		 * padded out to `width' places.  If flags&ZEROPAD, it should +		 * first be prefixed by any sign or other prefix; otherwise, +		 * it should be blank padded before the prefix is emitted. +		 * After any left-hand padding and prefixing, emit zeroes +		 * required by a decimal [diouxX] precision, then print the +		 * string proper, then emit zeroes required by any leftover +		 * floating precision; finally, if LADJUST, pad with blanks. +		 * +		 * Compute actual size, so we know how much to pad. +		 * size excludes decimal prec; realsz includes it. +		 */ +		realsz = dprec > size ? dprec : size; +		if (sign) +			realsz++; +		else if (flags & HEXPREFIX) +			realsz += 2; + +		prsize = width > realsz ? width : realsz; +		if ((unsigned)ret + prsize > INT_MAX) { +			ret = EOF; +			goto error; +		} + +		/* right-adjusting blank padding */ +		if ((flags & (LADJUST|ZEROPAD)) == 0) +			PAD(width - realsz, blanks); + +		/* prefix */ +		if (sign) { +			PRINT(&sign, 1); +		} else if (flags & HEXPREFIX) { +			ox[0] = '0'; +			ox[1] = ch; +			PRINT(ox, 2); +		} + +		/* right-adjusting zero padding */ +		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) +			PAD(width - realsz, zeroes); + +		/* leading zeroes from decimal precision */ +		PAD(dprec - size, zeroes); + +		/* the string or number proper */ +#ifdef FLOATING_POINT +		if ((flags & FPT) == 0) { +			PRINT(cp, size); +		} else {	/* glue together f_p fragments */ +			if (ch >= 'f') {	/* 'f' or 'g' */ +				if (_double == 0) { +					/* kludge for __dtoa irregularity */ +					PRINT(L"0", 1); +					if (expt < ndig || (flags & ALT) != 0) { +						PRINT(decimal_point, 1); +						PAD(ndig - 1, zeroes); +					} +				} else if (expt <= 0) { +					PRINT(L"0", 1); +					PRINT(decimal_point, 1); +					PAD(-expt, zeroes); +					PRINT(cp, ndig); +				} else if (expt >= ndig) { +					PRINT(cp, ndig); +					PAD(expt - ndig, zeroes); +					if (flags & ALT) +						PRINT(decimal_point, 1); +				} else { +					PRINT(cp, expt); +					cp += expt; +					PRINT(decimal_point, 1); +					PRINT(cp, ndig-expt); +				} +			} else {	/* 'e' or 'E' */ +				if (ndig > 1 || flags & ALT) { +					ox[0] = *cp++; +					ox[1] = *decimal_point; +					PRINT(ox, 2); +					if (_double) { +						PRINT(cp, ndig-1); +					} else	/* 0.[0..] */ +						/* __dtoa irregularity */ +						PAD(ndig - 1, zeroes); +				} else	/* XeYYY */ +					PRINT(cp, 1); +				PRINT(expstr, expsize); +			} +		} +#else +		PRINT(cp, size); +#endif +		/* left-adjusting padding (always blank) */ +		if (flags & LADJUST) +			PAD(width - realsz, blanks); + +		/* finally, adjust ret */ +		ret += prsize; +	} +done: +error: +#ifdef FLOATING_POINT +	if (dtoaresult != NULL) +		free(dtoaresult); +#endif +	if (convbuf != NULL) +		free(convbuf); +	if (__sferror(fp)) +		ret = EOF; +	if ((argtable != NULL) && (argtable != statargtable)) +		free (argtable); +	return (ret); +	/* NOTREACHED */ +} + +/* + * Find all arguments when a positional parameter is encountered.  Returns a + * table, indexed by argument number, of pointers to each arguments.  The + * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. + * It will be replaces with a malloc-ed one if it overflows. + */  +static void +__find_arguments (const wchar_t *fmt0, va_list ap, union arg **argtable) +{ +	wchar_t *fmt;		/* format string */ +	wchar_t ch;		/* character from fmt */ +	int n, n2;		/* handy integer (short term usage) */ +	wchar_t *cp;		/* handy char pointer (short term usage) */ +	int flags;		/* flags as above */ +	int width;		/* width from format (%8d), or 0 */ +	enum typeid *typetable; /* table of types */ +	enum typeid stattypetable [STATIC_ARG_TBL_SIZE]; +	int tablesize;		/* current size of type table */ +	int tablemax;		/* largest used index in table */ +	int nextarg;		/* 1-based argument index */ + +	/* +	 * Add an argument type to the table, expanding if necessary. +	 */ +#define ADDTYPE(type) \ +	((nextarg >= tablesize) ? \ +		__grow_type_table(nextarg, &typetable, &tablesize) : 0, \ +	(nextarg > tablemax) ? tablemax = nextarg : 0, \ +	typetable[nextarg++] = type) + +#define	ADDSARG() \ +	((flags&INTMAXT) ? ADDTYPE(T_INTMAXT) : \ +		((flags&SIZET) ? ADDTYPE(T_SIZET) : \ +		((flags&PTRDIFFT) ? ADDTYPE(T_PTRDIFFT) : \ +		((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \ +		((flags&LONGINT) ? ADDTYPE(T_LONG) : ADDTYPE(T_INT)))))) + +#define	ADDUARG() \ +	((flags&INTMAXT) ? ADDTYPE(T_UINTMAXT) : \ +		((flags&SIZET) ? ADDTYPE(T_SIZET) : \ +		((flags&PTRDIFFT) ? ADDTYPE(T_PTRDIFFT) : \ +		((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \ +		((flags&LONGINT) ? ADDTYPE(T_U_LONG) : ADDTYPE(T_U_INT)))))) + +	/* +	 * Add * arguments to the type array. +	 */ +#define ADDASTER() \ +	n2 = 0; \ +	cp = fmt; \ +	while (is_digit(*cp)) { \ +		n2 = 10 * n2 + to_digit(*cp); \ +		cp++; \ +	} \ +	if (*cp == '$') { \ +		int hold = nextarg; \ +		nextarg = n2; \ +		ADDTYPE (T_INT); \ +		nextarg = hold; \ +		fmt = ++cp; \ +	} else { \ +		ADDTYPE (T_INT); \ +	} +	fmt = (wchar_t *)fmt0; +	typetable = stattypetable; +	tablesize = STATIC_ARG_TBL_SIZE; +	tablemax = 0;  +	nextarg = 1; +	memset (typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); + +	/* +	 * Scan the format for conversions (`%' character). +	 */ +	for (;;) { +		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) +			/* void */; +		if (ch == '\0') +			goto done; +		fmt++;		/* skip over '%' */ + +		flags = 0; +		width = 0; + +rflag:		ch = *fmt++; +reswitch:	switch (ch) { +		case ' ': +		case '#': +			goto rflag; +		case '*': +			ADDASTER (); +			goto rflag; +		case '-': +		case '+': +		case '\'': +			goto rflag; +		case '.': +			if ((ch = *fmt++) == '*') { +				ADDASTER (); +				goto rflag; +			} +			while (is_digit(ch)) { +				ch = *fmt++; +			} +			goto reswitch; +		case '0': +			goto rflag; +		case '1': case '2': case '3': case '4': +		case '5': case '6': case '7': case '8': case '9': +			n = 0; +			do { +				n = 10 * n + to_digit(ch); +				ch = *fmt++; +			} while (is_digit(ch)); +			if (ch == '$') { +				nextarg = n; +				goto rflag; +			} +			width = n; +			goto reswitch; +#ifdef FLOATING_POINT +		case 'L': +			flags |= LONGDBL; +			goto rflag; +#endif +		case 'h': +			if (flags & SHORTINT) { +				flags &= ~SHORTINT; +				flags |= CHARINT; +			} else +				flags |= SHORTINT; +			goto rflag; +		case 'j': +			flags |= INTMAXT; +			goto rflag; +		case 'l': +			if (flags & LONGINT) { +				flags &= ~LONGINT; +				flags |= LLONGINT; +			} else +				flags |= LONGINT; +			goto rflag; +		case 'q': +			flags |= LLONGINT;	/* not necessarily */ +			goto rflag; +		case 't': +			flags |= PTRDIFFT; +			goto rflag; +		case 'z': +			flags |= SIZET; +			goto rflag; +		case 'c': +			if (flags & LONGINT) +				ADDTYPE(T_WINT); +			else +				ADDTYPE(T_INT); +			break; +		case 'D': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'd': +		case 'i': +			ADDSARG(); +			break; +#ifdef FLOATING_POINT +#ifdef HEXFLOAT +		case 'a': +		case 'A': +#endif +		case 'e': +		case 'E': +		case 'f': +		case 'g': +		case 'G': +			if (flags & LONGDBL) +				ADDTYPE(T_LONG_DOUBLE); +			else +				ADDTYPE(T_DOUBLE); +			break; +#endif /* FLOATING_POINT */ +		case 'n': +			if (flags & INTMAXT) +				ADDTYPE(TP_INTMAXT); +			else if (flags & PTRDIFFT) +				ADDTYPE(TP_PTRDIFFT); +			else if (flags & SIZET) +				ADDTYPE(TP_SIZET); +			else if (flags & LLONGINT) +				ADDTYPE(TP_LLONG); +			else if (flags & LONGINT) +				ADDTYPE(TP_LONG); +			else if (flags & SHORTINT) +				ADDTYPE(TP_SHORT); +			else if (flags & CHARINT) +				ADDTYPE(TP_SCHAR); +			else +				ADDTYPE(TP_INT); +			continue;	/* no output */ +		case 'O': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'o': +			ADDUARG(); +			break; +		case 'p': +			ADDTYPE(TP_VOID); +			break; +		case 's': +			if (flags & LONGINT) +				ADDTYPE(TP_WCHAR); +			else +				ADDTYPE(TP_CHAR); +			break; +		case 'U': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'u': +		case 'X': +		case 'x': +			ADDUARG(); +			break; +		default:	/* "%?" prints ?, unless ? is NUL */ +			if (ch == '\0') +				goto done; +			break; +		} +	} +done: +	/* +	 * Build the argument table. +	 */ +	if (tablemax >= STATIC_ARG_TBL_SIZE) { +		*argtable = (union arg *) +		    malloc (sizeof (union arg) * (tablemax + 1)); +	} + +	(*argtable) [0].intarg = 0; +	for (n = 1; n <= tablemax; n++) { +		switch (typetable [n]) { +		    case T_UNUSED: /* whoops! */ +			(*argtable) [n].intarg = va_arg (ap, int); +			break; +		    case TP_SCHAR: +			(*argtable) [n].pschararg = va_arg (ap, signed char *); +			break; +		    case TP_SHORT: +			(*argtable) [n].pshortarg = va_arg (ap, short *); +			break; +		    case T_INT: +			(*argtable) [n].intarg = va_arg (ap, int); +			break; +		    case T_U_INT: +			(*argtable) [n].uintarg = va_arg (ap, unsigned int); +			break; +		    case TP_INT: +			(*argtable) [n].pintarg = va_arg (ap, int *); +			break; +		    case T_LONG: +			(*argtable) [n].longarg = va_arg (ap, long); +			break; +		    case T_U_LONG: +			(*argtable) [n].ulongarg = va_arg (ap, unsigned long); +			break; +		    case TP_LONG: +			(*argtable) [n].plongarg = va_arg (ap, long *); +			break; +		    case T_LLONG: +			(*argtable) [n].longlongarg = va_arg (ap, long long); +			break; +		    case T_U_LLONG: +			(*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long); +			break; +		    case TP_LLONG: +			(*argtable) [n].plonglongarg = va_arg (ap, long long *); +			break; +		    case T_PTRDIFFT: +			(*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t); +			break; +		    case TP_PTRDIFFT: +			(*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *); +			break; +		    case T_SIZET: +			(*argtable) [n].sizearg = va_arg (ap, size_t); +			break; +		    case TP_SIZET: +			(*argtable) [n].psizearg = va_arg (ap, ssize_t *); +			break; +		    case T_INTMAXT: +			(*argtable) [n].intmaxarg = va_arg (ap, intmax_t); +			break; +		    case T_UINTMAXT: +			(*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t); +			break; +		    case TP_INTMAXT: +			(*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *); +			break; +#ifdef FLOATING_POINT +		    case T_DOUBLE: +			(*argtable) [n].doublearg = va_arg (ap, double); +			break; +		    case T_LONG_DOUBLE: +			(*argtable) [n].longdoublearg = va_arg (ap, long double); +			break; +#endif +		    case TP_CHAR: +			(*argtable) [n].pchararg = va_arg (ap, char *); +			break; +		    case TP_VOID: +			(*argtable) [n].pvoidarg = va_arg (ap, void *); +			break; +		    case T_WINT: +			(*argtable) [n].wintarg = va_arg (ap, wint_t); +			break; +		    case TP_WCHAR: +			(*argtable) [n].pwchararg = va_arg (ap, wchar_t *); +			break; +		} +	} + +	if ((typetable != NULL) && (typetable != stattypetable)) +		free (typetable); +} + +/* + * Increase the size of the type table. + */ +static void +__grow_type_table (int nextarg, enum typeid **typetable, int *tablesize) +{ +	enum typeid *const oldtable = *typetable; +	const int oldsize = *tablesize; +	enum typeid *newtable; +	int newsize = oldsize * 2; + +	if (newsize < nextarg + 1) +		newsize = nextarg + 1; +	if (oldsize == STATIC_ARG_TBL_SIZE) { +		if ((newtable = malloc(newsize)) == NULL) +			abort();			/* XXX handle better */ +		bcopy(oldtable, newtable, oldsize); +	} else { +		if ((newtable = reallocf(oldtable, newsize)) == NULL) +			abort();			/* XXX handle better */ +	} +	memset(&newtable[oldsize], T_UNUSED, newsize - oldsize); + +	*typetable = newtable; +	*tablesize = newsize; +} + + +#ifdef FLOATING_POINT + +extern char *__dtoa(double, int, int, int *, int *, char **, char **); + +static wchar_t * +cvt(double value, int ndigits, int flags, char *sign, int *decpt, +    wchar_t ch, int *length, wchar_t **dtoaresultp) +{ +	int i, mode, dsgn; +	wchar_t *digits, *bp, *result, *rve; +	char *tresult, *trve; + +	if (ch == 'f') +		mode = 3;		/* ndigits after the decimal point */ +	else { +		/* +		 * To obtain ndigits after the decimal point for the 'e' +		 * and 'E' formats, round to ndigits + 1 significant +		 * figures. +		 */ +		if (ch == 'e' || ch == 'E') +			ndigits++; +		mode = 2;		/* ndigits significant digits */ +	} +	__dtoa(value, mode, ndigits, decpt, &dsgn, &trve, &tresult); +	if ((result = malloc((trve - tresult + 1) * sizeof(*result))) == NULL) +		abort();		/* XXX handle better */ +	for (i = 0; i < trve - tresult + 1; i++) +		result[i] = (wchar_t)(unsigned char)tresult[i]; +	rve = result + (trve - tresult); +	free(tresult); +	*dtoaresultp = result; +	digits = result; +	*sign = dsgn != 0; +	if ((ch != 'g' && ch != 'G') || flags & ALT) { +		/* print trailing zeros */ +		bp = digits + ndigits; +		if (ch == 'f') { +			if (*digits == '0' && value) +				*decpt = -ndigits + 1; +			bp += *decpt; +		} +		if (value == 0)	/* kludge for __dtoa irregularity */ +			rve = bp; +		while (rve < bp) +			*rve++ = '0'; +	} +	*length = rve - digits; +	return (digits); +} + +static int +exponent(wchar_t *p0, int exp, wchar_t fmtch) +{ +	wchar_t *p, *t; +	wchar_t expbuf[MAXEXP]; + +	p = p0; +	*p++ = fmtch; +	if (exp < 0) { +		exp = -exp; +		*p++ = '-'; +	} +	else +		*p++ = '+'; +	t = expbuf + MAXEXP; +	if (exp > 9) { +		do { +			*--t = to_char(exp % 10); +		} while ((exp /= 10) > 9); +		*--t = to_char(exp); +		for (; t < expbuf + MAXEXP; *p++ = *t++); +	} +	else { +		*p++ = '0'; +		*p++ = to_char(exp); +	} +	return (p - p0); +} +#endif /* FLOATING_POINT */ diff --git a/lib/libc/stdio/vswprintf.c b/lib/libc/stdio/vswprintf.c new file mode 100644 index 000000000000..da5c4c25f7a3 --- /dev/null +++ b/lib/libc/stdio/vswprintf.c @@ -0,0 +1,89 @@ +/*	$OpenBSD: vasprintf.c,v 1.4 1998/06/21 22:13:47 millert Exp $	*/ + +/* + * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + *    derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#if 0 +__FBSDID("FreeBSD: src/lib/libc/stdio/vasprintf.c,v 1.16 2002/08/21 16:19:57 mike Exp "); +#endif +__FBSDID("$FreeBSD$"); + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <wchar.h> +#include "local.h" + +int +vswprintf(wchar_t * __restrict s, size_t n, const wchar_t * __restrict fmt, +    __va_list ap) +{ +	FILE f; +	struct __sFILEX ext; +	mbstate_t mbs; +	char *mbp; +	size_t mbresult; +	int ret; + +	if (n == 0) { +		errno = EINVAL; +		return (-1); +	} + +	f._file = -1; +	f._flags = __SWR | __SSTR | __SALC; +	f._bf._base = f._p = (unsigned char *)malloc(128); +	if (f._bf._base == NULL) { +		errno = ENOMEM; +		return (-1); +	} +	f._bf._size = f._w = 127;		/* Leave room for the NUL */ +	f._extra = &ext; +	INITEXTRA(&f); +	ret = __vfwprintf(&f, fmt, ap); +	*f._p = '\0'; +	mbp = f._bf._base; +	memset(&mbs, 0, sizeof(mbs)); +	/* +	 * XXX Undo the conversion from wide characters to multibyte that +	 * fputwc() did in __vfwprintf(). +	 */ +	if (mbsrtowcs(s, (const char **)&mbp, n, &mbs) == (size_t)-1) { +		free(f._bf._base); +		errno = EILSEQ; +		return (-1); +	} +	free(f._bf._base); +	if (s[n - 1] != L'\0') { +		s[n - 1] = L'\0'; +		errno = EOVERFLOW; +		return (-1); +	} + +	return (ret); +} diff --git a/lib/libc/stdio/vwprintf.c b/lib/libc/stdio/vwprintf.c new file mode 100644 index 000000000000..91212a8bf3d0 --- /dev/null +++ b/lib/libc/stdio/vwprintf.c @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2002 Tim J. Robbins + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdarg.h> +#include <stdio.h> +#include <wchar.h> + +int +vwprintf(const wchar_t * __restrict fmt, va_list ap) +{ + +	return (vfwprintf(stdout, fmt, ap)); +} diff --git a/lib/libc/stdio/wprintf.3 b/lib/libc/stdio/wprintf.3 new file mode 100644 index 000000000000..17dd79fc1024 --- /dev/null +++ b/lib/libc/stdio/wprintf.3 @@ -0,0 +1,618 @@ +.\" Copyright (c) 1990, 1991, 1993 +.\"	The Regents of the University of California.  All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Chris Torek and the American National Standards Committee X3, +.\" on Information Processing Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\"    must display the following acknowledgement: +.\"	This product includes software developed by the University of +.\"	California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\"    may be used to endorse or promote products derived from this software +.\"    without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\"     @(#)printf.3	8.1 (Berkeley) 6/4/93 +.\" FreeBSD: src/lib/libc/stdio/printf.3,v 1.47 2002/09/06 11:23:55 tjr Exp  +.\" $FreeBSD$ +.\" +.Dd September 18, 2002 +.Dt WPRINTF 3 +.Os +.Sh NAME +.Nm wprintf , fwprintf , swprintf , +.Nm vwprintf , vfwprintf , vswprintf +.Nd formatted wide character output conversion +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In stdio.h +.In wchar.h +.Ft int +.Fn fwprintf "FILE * restrict stream" "const wchar_t * restrict format" ... +.Ft int +.Fn swprintf "wchar_t * restrict ws" "size_t n" "const wchar_t * restrict format" ... +.Ft int +.Fn wprintf "const wchar_t * restrict format" ... +.In stdarg.h +.Ft int +.Fn vfwprintf "FILE * restrict stream" "const wchar_t * restrict" "va_list ap" +.Ft int +.Fn vswprintf "wchar_t * restrict ws" "size_t n" "const wchar_t *restrict format" "va_list ap" +.Ft int +.Fn vwprintf "const wchar_t * restrict format" "va_list ap" +.Sh DESCRIPTION +The +.Fn wprintf +family of functions produces output according to a +.Fa format +as described below. +The +.Fn wprintf +and +.Fn vwprintf +functions +write output to +.Pa stdout , +the standard output stream; +.Fn fwprintf +and +.Fn vfwprintf +write output to the given output +.Fa stream ; +.Fn swprintf , +.Fn vswprintf , +and +.Fn vsnprintf +write to the wide character string +.Fa ws . +.Pp +These functions write the output under the control of a +.Fa format +string that specifies how subsequent arguments +(or arguments accessed via the variable-length argument facilities of +.Xr stdarg 3 ) +are converted for output. +.Pp +These functions return the number of characters printed +(not including the trailing +.Ql \e0 +used to end output to strings). +.Pp +The +.Fn swprintf +and +.Fn vswprintf +functions will fail if +.Fa n +or more wide characters were requested to be written, +.Pp +The format string is composed of zero or more directives: +ordinary +characters (not +.Cm % ) , +which are copied unchanged to the output stream; +and conversion specifications, each of which results +in fetching zero or more subsequent arguments. +Each conversion specification is introduced by +the +.Cm % +character. +The arguments must correspond properly (after type promotion) +with the conversion specifier. +After the +.Cm % , +the following appear in sequence: +.Bl -bullet +.It +An optional field, consisting of a decimal digit string followed by a +.Cm $ , +specifying the next argument to access. +If this field is not provided, the argument following the last +argument accessed will be used. +Arguments are numbered starting at +.Cm 1 . +If unaccessed arguments in the format string are interspersed with ones that +are accessed the results will be indeterminate. +.It +Zero or more of the following flags: +.Bl -tag -width ".So \  Sc (space)" +.It Sq Cm # +The value should be converted to an +.Dq alternate form . +For +.Cm c , d , i , n , p , s , +and +.Cm u +conversions, this option has no effect. +For +.Cm o +conversions, the precision of the number is increased to force the first +character of the output string to a zero (except if a zero value is printed +with an explicit precision of zero). +For +.Cm x +and +.Cm X +conversions, a non-zero result has the string +.Ql 0x +(or +.Ql 0X +for +.Cm X +conversions) prepended to it. +For +.Cm a , A , e , E , f , F , g , +and +.Cm G +conversions, the result will always contain a decimal point, even if no +digits follow it (normally, a decimal point appears in the results of +those conversions only if a digit follows). +For +.Cm g +and +.Cm G +conversions, trailing zeros are not removed from the result as they +would otherwise be. +.It So Cm 0 Sc (zero) +Zero padding. +For all conversions except +.Cm n , +the converted value is padded on the left with zeros rather than blanks. +If a precision is given with a numeric conversion +.Cm ( d , i , o , u , i , x , +and +.Cm X ) , +the +.Cm 0 +flag is ignored. +.It Sq Cm \- +A negative field width flag; +the converted value is to be left adjusted on the field boundary. +Except for +.Cm n +conversions, the converted value is padded on the right with blanks, +rather than on the left with blanks or zeros. +A +.Cm \- +overrides a +.Cm 0 +if both are given. +.It So "\ " Sc (space) +A blank should be left before a positive number +produced by a signed conversion +.Cm ( a , A , d , e , E , f , F , g , G , +or +.Cm i ) . +.It Sq Cm + +A sign must always be placed before a +number produced by a signed conversion. +A +.Cm + +overrides a space if both are used. +.It Sq Cm ' +Decimal conversions +.Cm ( d , u , +or +.Cm i ) +or the integral portion of a floating point conversion +.Cm ( f +or +.Cm F ) +should be grouped and separated by thousands using +the non-monetary separator returned by +.Xr localeconv 3 . +.El +.It +An optional decimal digit string specifying a minimum field width. +If the converted value has fewer characters than the field width, it will +be padded with spaces on the left (or right, if the left-adjustment +flag has been given) to fill out +the field width. +.It +An optional precision, in the form of a period +.Cm \&. +followed by an +optional digit string. +If the digit string is omitted, the precision is taken as zero. +This gives the minimum number of digits to appear for +.Cm d , i , o , u , x , +and +.Cm X +conversions, the number of digits to appear after the decimal-point for +.Cm a , A , e , E , f , +and +.Cm F +conversions, the maximum number of significant digits for +.Cm g +and +.Cm G +conversions, or the maximum number of characters to be printed from a +string for +.Cm s +conversions. +.It +An optional length modifier, that specifies the size of the argument. +The following length modifiers are valid for the +.Cm d , i , n , o , u , x , +or +.Cm X +conversion: +.Bl -column ".Cm q Em (deprecated)" ".Vt signed char" ".Vt unsigned long long" ".Vt long long *" +.It Sy Modifier Ta Cm d , i Ta Cm o , u , x , X Ta Cm n +.It Cm hh Ta Vt "signed char" Ta Vt "unsigned char" Ta Vt "signed char *" +.It Cm h Ta Vt short Ta Vt "unsigned short" Ta Vt "short *" +.It Cm l No (ell) Ta Vt long Ta Vt "unsigned long" Ta Vt "long *" +.It Cm ll No (ell ell) Ta Vt "long long" Ta Vt "unsigned long long" Ta Vt "long long *" +.It Cm j Ta Vt intmax_t Ta Vt uintmax_t Ta Vt "intmax_t *" +.It Cm t Ta Vt ptrdiff_t Ta (see note) Ta Vt "ptrdiff_t *" +.It Cm z Ta (see note) Ta Vt size_t Ta (see note) +.It Cm q Em (deprecated) Ta Vt quad_t Ta Vt u_quad_t Ta Vt "quad_t *" +.El +.Pp +Note: +the +.Cm t +modifier, when applied to a +.Cm o , u , x , +or +.Cm X +conversion, indicates that the argument is of an unsigned type +equivalent in size to a +.Vt ptrdiff_t . +The +.Cm z +modifier, when applied to a +.Cm d +or +.Cm i +conversion, indicates that the argument is of a signed type equivalent in +size to a +.Vt size_t . +Similarly, when applied to an +.Cm n +conversion, it indicates that the argument is a pointer to a signed type +equivalent in size to a +.Vt size_t . +.Pp +The following length modifier is valid for the +.Cm a , A , e , E , f , F , g , +or +.Cm G +conversion: +.Bl -column ".Sy Modifier" ".Cm a , A , e , E , f , F , g , G" +.It Sy Modifier Ta Cm a , A , e , E , f , F , g , G +.It Cm L Ta Vt "long double" +.El +.Pp +The following length modifier is valid for the +.Cm c +or +.Cm s +conversion: +.Bl -column ".Sy Modifier" ".Vt wint_t" ".Vt wchar_t *" +.It Sy Modifier Ta Cm c Ta Cm s +.It Cm l No (ell) Ta Vt wint_t Ta Vt "wchar_t *" +.El +.It +A character that specifies the type of conversion to be applied. +.El +.Pp +A field width or precision, or both, may be indicated by +an asterisk +.Ql * +or an asterisk followed by one or more decimal digits and a +.Ql $ +instead of a +digit string. +In this case, an +.Vt int +argument supplies the field width or precision. +A negative field width is treated as a left adjustment flag followed by a +positive field width; a negative precision is treated as though it were +missing. +If a single format directive mixes positional +.Pq Li nn$ +and non-positional arguments, the results are undefined. +.Pp +The conversion specifiers and their meanings are: +.Bl -tag -width ".Cm diouxX" +.It Cm diouxX +The +.Vt int +(or appropriate variant) argument is converted to signed decimal +.Cm ( d +and +.Cm i ) , +unsigned octal +.Pq Cm o , +unsigned decimal +.Pq Cm u , +or unsigned hexadecimal +.Cm ( x +and +.Cm X ) +notation. +The letters +.Dq Li abcdef +are used for +.Cm x +conversions; the letters +.Dq Li ABCDEF +are used for +.Cm X +conversions. +The precision, if any, gives the minimum number of digits that must +appear; if the converted value requires fewer digits, it is padded on +the left with zeros. +.It Cm DOU +The +.Vt "long int" +argument is converted to signed decimal, unsigned octal, or unsigned +decimal, as if the format had been +.Cm ld , lo , +or +.Cm lu +respectively. +These conversion characters are deprecated, and will eventually disappear. +.It Cm eE +The +.Vt double +argument is rounded and converted in the style +.Sm off +.Oo \- Oc Ar d Li \&. Ar ddd Li e \\*[Pm] Ar dd +.Sm on +where there is one digit before the +decimal-point character +and the number of digits after it is equal to the precision; +if the precision is missing, +it is taken as 6; if the precision is +zero, no decimal-point character appears. +An +.Cm E +conversion uses the letter +.Ql E +(rather than +.Ql e ) +to introduce the exponent. +The exponent always contains at least two digits; if the value is zero, +the exponent is 00. +.Pp +For +.Cm a , A , e , E , f , F , g , +and +.Cm G +conversions, positive and negative infinity are represented as +.Li inf +and +.Li -inf +respectively when using the lowercase conversion character, and +.Li INF +and +.Li -INF +respectively when using the uppercase conversion character. +Similarly, NaN is represented as +.Li nan +when using the lowercase conversion, and +.Li NAN +when using the uppercase conversion. +.It Cm fF +The +.Vt double +argument is rounded and converted to decimal notation in the style +.Sm off +.Oo \- Oc Ar ddd Li \&. Ar ddd , +.Sm on +where the number of digits after the decimal-point character +is equal to the precision specification. +If the precision is missing, it is taken as 6; if the precision is +explicitly zero, no decimal-point character appears. +If a decimal point appears, at least one digit appears before it. +.It Cm gG +The +.Vt double +argument is converted in style +.Cm f +or +.Cm e +(or +.Cm F +or +.Cm E +for +.Cm G +conversions). +The precision specifies the number of significant digits. +If the precision is missing, 6 digits are given; if the precision is zero, +it is treated as 1. +Style +.Cm e +is used if the exponent from its conversion is less than \-4 or greater than +or equal to the precision. +Trailing zeros are removed from the fractional part of the result; a +decimal point appears only if it is followed by at least one digit. +.It Cm aA +The +.Vt double +argument is converted to hexadecimal notation in the style +.Sm off +.Oo \- Oc Li 0x Ar h Li \&. Ar hhhp Oo \\*[Pm] Oc Ar d , +.Sm on +where the number of digits after the hexadecimal-point character +is equal to the precision specification. +If the precision is missing, it is taken as enough to exactly +represent the floating-point number; if the precision is +explicitly zero, no hexadecimal-point character appears. +This is an exact conversion of the mantissa+exponent internal +floating point representation; the +.Sm off +.Oo \- Oc Li 0x Ar h Li \&. Ar hhh +.Sm on +portion represents exactly the mantissa; only denormalized +mantissas have a zero value to the left of the hexadecimal +point. +The +.Cm p +is a literal character +.Ql p ; +the exponent is preceded by a positive or negative sign +and is represented in decimal, using only enough characters +to represent the exponent. +The +.Cm A +conversion uses the prefix +.Dq Li 0X +(rather than +.Dq Li 0x ) , +the letters +.Dq Li ABCDEF +(rather than +.Dq Li abcdef ) +to represent the hex digits, and the letter +.Ql P +(rather than +.Ql p ) +to separate the mantissa and exponent. +.It Cm C +Treated as +.Cm c +with the +.Cm l +(ell) modifier. +.It Cm c +The +.Vt int +argument is converted to an +.Vt "unsigned char" , +then to a +.Vt wchar_t +as if by +.Xr btowc 3 , +and the resulting character is written. +.Pp +If the +.Cm l +(ell) modifier is used, the +.Vt wint_t +argument is converted to a +.Vt wchar_t +and written. +.It Cm S +Treated as +.Cm s +with the +.Cm l +(ell) modifier. +.It Cm s +The +.Vt "char *" +argument is expected to be a pointer to an array of character type (pointer +to a string) containing a multibyte sequence. +Characters from the array are converted to wide characters and written up to +(but not including) +a terminating +.Dv NUL +character; +if a precision is specified, no more than the number specified are +written. +If a precision is given, no null character +need be present; if the precision is not specified, or is greater than +the size of the array, the array must contain a terminating +.Dv NUL +character. +.Pp +If the +.Cm l +(ell) modifier is used, the +.Vt "wchar_t *" +argument is expected to be a pointer to an array of wide characters +(pointer to a wide string). +Each wide character in the string +is written. +Wide characters from the array are written up to (but not including) +a terminating wide +.Dv NUL +character; +if a precision is specified, no more than the number specified are +written (including shift sequences). +If a precision is given, no null character +need be present; if the precision is not specified, or is greater than +the number of characters in +the string, the array must contain a terminating wide +.Dv NUL +character. +.It Cm p +The +.Vt "void *" +pointer argument is printed in hexadecimal (as if by +.Ql %#x +or +.Ql %#lx ) . +.It Cm n +The number of characters written so far is stored into the +integer indicated by the +.Vt "int *" +(or variant) pointer argument. +No argument is converted. +.It Cm % +A +.Ql % +is written. +No argument is converted. +The complete conversion specification +is +.Ql %% . +.El +.Pp +The decimal point +character is defined in the program's locale (category +.Dv LC_NUMERIC ) . +.Pp +In no case does a non-existent or small field width cause truncation of +a numeric field; if the result of a conversion is wider than the field +width, the +field is expanded to contain the conversion result. +.Sh SECURITY CONSIDERATIONS +Refer to +.Xr printf 3 . +.Sh SEE ALSO +.Xr printf 3 , +.Xr setlocale 3 +.Sh STANDARDS +Subject to the caveats noted in the +.Sx BUGS +section +of +.Xr printf 3 , +the +.Fn wprintf , +.Fn fwprintf , +.Fn swprintf , +.Fn vwprintf , +.Fn vfwprintf +and +.Fn vswprintf +functions +conform to +.St -isoC-99 . diff --git a/lib/libc/stdio/wprintf.c b/lib/libc/stdio/wprintf.c new file mode 100644 index 000000000000..92426f6c207b --- /dev/null +++ b/lib/libc/stdio/wprintf.c @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2002 Tim J. Robbins + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdarg.h> +#include <stdio.h> +#include <wchar.h> + +int +wprintf(const wchar_t * __restrict fmt, ...) +{ +	int ret; +	va_list ap; + +	va_start(ap, fmt); +	ret = vfwprintf(stdout, fmt, ap); +	va_end(ap); + +	return (ret); +} | 
