diff options
Diffstat (limited to 'lib/libc/stdio/printf-pos.c')
| -rw-r--r-- | lib/libc/stdio/printf-pos.c | 788 | 
1 files changed, 788 insertions, 0 deletions
diff --git a/lib/libc/stdio/printf-pos.c b/lib/libc/stdio/printf-pos.c new file mode 100644 index 0000000000000..c9b439b3d473c --- /dev/null +++ b/lib/libc/stdio/printf-pos.c @@ -0,0 +1,788 @@ +/*- + * 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. + * 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)vfprintf.c	8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * This is the code responsible for handling positional arguments + * (%m$ and %m$.n$) for vfprintf() and vfwprintf(). + */ + +#include "namespace.h" +#include <sys/types.h> + +#include <ctype.h> +#include <limits.h> +#include <locale.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <printf.h> + +#include <stdarg.h> +#include "un-namespace.h" + +#include "libc_private.h" +#include "local.h" +#include "fvwrite.h" +#include "printflocal.h" + +/* + * 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 +}; + +/* An expandable array of types. */ +struct typetable { +	enum typeid *table; /* table of types */ +	enum typeid stattable[STATIC_ARG_TBL_SIZE]; +	int tablesize;		/* current size of type table */ +	int tablemax;		/* largest used index in table */ +	int nextarg;		/* 1-based argument index */ +}; + +static void	__grow_type_table(struct typetable *); + +/* + * Initialize a struct typetable. + */ +static inline void +inittypes(struct typetable *types) +{ +	int n; + +	types->table = types->stattable; +	types->tablesize = STATIC_ARG_TBL_SIZE; +	types->tablemax = 0;  +	types->nextarg = 1; +	for (n = 0; n < STATIC_ARG_TBL_SIZE; n++) +		types->table[n] = T_UNUSED; +} + +/* + * struct typetable destructor. + */  +static inline void +freetypes(struct typetable *types) +{ + +	if (types->table != types->stattable) +		free (types->table); +} + +/* + * Add an argument type to the table, expanding if necessary. + */ +static inline void +addtype(struct typetable *types, enum typeid type) +{ + +	if (types->nextarg >= types->tablesize) +		__grow_type_table(types); +	if (types->nextarg > types->tablemax) +		types->tablemax = types->nextarg; +	types->table[types->nextarg++] = type; +} + +static inline void +addsarg(struct typetable *types, int flags) +{ + +	if (flags & INTMAXT) +		addtype(types, T_INTMAXT); +	else if (flags & SIZET) +		addtype(types, T_SIZET); +	else if (flags & PTRDIFFT) +		addtype(types, T_PTRDIFFT); +	else if (flags & LLONGINT) +		addtype(types, T_LLONG); +	else if (flags & LONGINT) +		addtype(types, T_LONG); +	else +		addtype(types, T_INT); +} + +static inline void +adduarg(struct typetable *types, int flags) +{ + +	if (flags & INTMAXT) +		addtype(types, T_UINTMAXT); +	else if (flags & SIZET) +		addtype(types, T_SIZET); +	else if (flags & PTRDIFFT) +		addtype(types, T_PTRDIFFT); +	else if (flags & LLONGINT) +		addtype(types, T_U_LLONG); +	else if (flags & LONGINT) +		addtype(types, T_U_LONG); +	else +		addtype(types, T_U_INT); +} + +/* + * Add * arguments to the type array. + */ +static inline void +addaster(struct typetable *types, char **fmtp) +{ +	char *cp; +	int n2; + +	n2 = 0; +	cp = *fmtp; +	while (is_digit(*cp)) { +		n2 = 10 * n2 + to_digit(*cp); +		cp++; +	} +	if (*cp == '$') { +		int hold = types->nextarg; +		types->nextarg = n2; +		addtype(types, T_INT); +		types->nextarg = hold; +		*fmtp = ++cp; +	} else { +		addtype(types, T_INT); +	} +} + +static inline void +addwaster(struct typetable *types, wchar_t **fmtp) +{ +	wchar_t *cp; +	int n2; + +	n2 = 0; +	cp = *fmtp; +	while (is_digit(*cp)) { +		n2 = 10 * n2 + to_digit(*cp); +		cp++; +	} +	if (*cp == '$') { +		int hold = types->nextarg; +		types->nextarg = n2; +		addtype(types, T_INT); +		types->nextarg = hold; +		*fmtp = ++cp; +	} else { +		addtype(types, T_INT); +	} +} + +/* + * 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. + */  +void +__find_arguments (const char *fmt0, va_list ap, union arg **argtable) +{ +	char *fmt;		/* format string */ +	int ch;			/* character from fmt */ +	int n;			/* handy integer (short term usage) */ +	int flags;		/* flags as above */ +	int width;		/* width from format (%8d), or 0 */ +	struct typetable types;	/* table of types */ + +	fmt = (char *)fmt0; +	inittypes(&types); + +	/* +	 * Scan the format for conversions (`%' character). +	 */ +	for (;;) { +		while ((ch = *fmt) != '\0' && ch != '%') +			fmt++; +		if (ch == '\0') +			goto done; +		fmt++;		/* skip over '%' */ + +		flags = 0; +		width = 0; + +rflag:		ch = *fmt++; +reswitch:	switch (ch) { +		case ' ': +		case '#': +			goto rflag; +		case '*': +			addaster(&types, &fmt); +			goto rflag; +		case '-': +		case '+': +		case '\'': +			goto rflag; +		case '.': +			if ((ch = *fmt++) == '*') { +				addaster(&types, &fmt); +				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 == '$') { +				types.nextarg = n; +				goto rflag; +			} +			width = n; +			goto reswitch; +#ifndef NO_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': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'c': +			if (flags & LONGINT) +				addtype(&types, T_WINT); +			else +				addtype(&types, T_INT); +			break; +		case 'D': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'd': +		case 'i': +			addsarg(&types, flags); +			break; +#ifndef NO_FLOATING_POINT +		case 'a': +		case 'A': +		case 'e': +		case 'E': +		case 'f': +		case 'g': +		case 'G': +			if (flags & LONGDBL) +				addtype(&types, T_LONG_DOUBLE); +			else +				addtype(&types, T_DOUBLE); +			break; +#endif /* !NO_FLOATING_POINT */ +		case 'n': +			if (flags & INTMAXT) +				addtype(&types, TP_INTMAXT); +			else if (flags & PTRDIFFT) +				addtype(&types, TP_PTRDIFFT); +			else if (flags & SIZET) +				addtype(&types, TP_SIZET); +			else if (flags & LLONGINT) +				addtype(&types, TP_LLONG); +			else if (flags & LONGINT) +				addtype(&types, TP_LONG); +			else if (flags & SHORTINT) +				addtype(&types, TP_SHORT); +			else if (flags & CHARINT) +				addtype(&types, TP_SCHAR); +			else +				addtype(&types, TP_INT); +			continue;	/* no output */ +		case 'O': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'o': +			adduarg(&types, flags); +			break; +		case 'p': +			addtype(&types, TP_VOID); +			break; +		case 'S': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 's': +			if (flags & LONGINT) +				addtype(&types, TP_WCHAR); +			else +				addtype(&types, TP_CHAR); +			break; +		case 'U': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'u': +		case 'X': +		case 'x': +			adduarg(&types, flags); +			break; +		default:	/* "%?" prints ?, unless ? is NUL */ +			if (ch == '\0') +				goto done; +			break; +		} +	} +done: +	/* +	 * Build the argument table. +	 */ +	if (types.tablemax >= STATIC_ARG_TBL_SIZE) { +		*argtable = (union arg *) +		    malloc (sizeof (union arg) * (types.tablemax + 1)); +	} + +	(*argtable) [0].intarg = 0; +	for (n = 1; n <= types.tablemax; n++) { +		switch (types.table[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, size_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; +		    case T_DOUBLE: +#ifndef NO_FLOATING_POINT +			(*argtable) [n].doublearg = va_arg (ap, double); +#endif +			break; +		    case T_LONG_DOUBLE: +#ifndef NO_FLOATING_POINT +			(*argtable) [n].longdoublearg = va_arg (ap, long double); +#endif +			break; +		    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; +		} +	} + +	freetypes(&types); +} + +/* wchar version of __find_arguments. */ +void +__find_warguments (const wchar_t *fmt0, va_list ap, union arg **argtable) +{ +	wchar_t *fmt;		/* format string */ +	wchar_t ch;		/* character from fmt */ +	int n;			/* handy integer (short term usage) */ +	int flags;		/* flags as above */ +	int width;		/* width from format (%8d), or 0 */ +	struct typetable types;	/* table of types */ + +	fmt = (wchar_t *)fmt0; +	inittypes(&types); + +	/* +	 * Scan the format for conversions (`%' character). +	 */ +	for (;;) { +		while ((ch = *fmt) != '\0' && ch != '%') +			fmt++; +		if (ch == '\0') +			goto done; +		fmt++;		/* skip over '%' */ + +		flags = 0; +		width = 0; + +rflag:		ch = *fmt++; +reswitch:	switch (ch) { +		case ' ': +		case '#': +			goto rflag; +		case '*': +			addwaster(&types, &fmt); +			goto rflag; +		case '-': +		case '+': +		case '\'': +			goto rflag; +		case '.': +			if ((ch = *fmt++) == '*') { +				addwaster(&types, &fmt); +				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 == '$') { +				types.nextarg = n; +				goto rflag; +			} +			width = n; +			goto reswitch; +#ifndef NO_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': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'c': +			if (flags & LONGINT) +				addtype(&types, T_WINT); +			else +				addtype(&types, T_INT); +			break; +		case 'D': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'd': +		case 'i': +			addsarg(&types, flags); +			break; +#ifndef NO_FLOATING_POINT +		case 'a': +		case 'A': +		case 'e': +		case 'E': +		case 'f': +		case 'g': +		case 'G': +			if (flags & LONGDBL) +				addtype(&types, T_LONG_DOUBLE); +			else +				addtype(&types, T_DOUBLE); +			break; +#endif /* !NO_FLOATING_POINT */ +		case 'n': +			if (flags & INTMAXT) +				addtype(&types, TP_INTMAXT); +			else if (flags & PTRDIFFT) +				addtype(&types, TP_PTRDIFFT); +			else if (flags & SIZET) +				addtype(&types, TP_SIZET); +			else if (flags & LLONGINT) +				addtype(&types, TP_LLONG); +			else if (flags & LONGINT) +				addtype(&types, TP_LONG); +			else if (flags & SHORTINT) +				addtype(&types, TP_SHORT); +			else if (flags & CHARINT) +				addtype(&types, TP_SCHAR); +			else +				addtype(&types, TP_INT); +			continue;	/* no output */ +		case 'O': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'o': +			adduarg(&types, flags); +			break; +		case 'p': +			addtype(&types, TP_VOID); +			break; +		case 'S': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 's': +			if (flags & LONGINT) +				addtype(&types, TP_WCHAR); +			else +				addtype(&types, TP_CHAR); +			break; +		case 'U': +			flags |= LONGINT; +			/*FALLTHROUGH*/ +		case 'u': +		case 'X': +		case 'x': +			adduarg(&types, flags); +			break; +		default:	/* "%?" prints ?, unless ? is NUL */ +			if (ch == '\0') +				goto done; +			break; +		} +	} +done: +	/* +	 * Build the argument table. +	 */ +	if (types.tablemax >= STATIC_ARG_TBL_SIZE) { +		*argtable = (union arg *) +		    malloc (sizeof (union arg) * (types.tablemax + 1)); +	} + +	(*argtable) [0].intarg = 0; +	for (n = 1; n <= types.tablemax; n++) { +		switch (types.table[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, size_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; +		    case T_DOUBLE: +#ifndef NO_FLOATING_POINT +			(*argtable) [n].doublearg = va_arg (ap, double); +#endif +			break; +		    case T_LONG_DOUBLE: +#ifndef NO_FLOATING_POINT +			(*argtable) [n].longdoublearg = va_arg (ap, long double); +#endif +			break; +		    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; +		} +	} + +	freetypes(&types); +} + +/* + * Increase the size of the type table. + */ +static void +__grow_type_table(struct typetable *types) +{ +	enum typeid *const oldtable = types->table; +	const int oldsize = types->tablesize; +	enum typeid *newtable; +	int n, newsize = oldsize * 2; + +	if (newsize < types->nextarg + 1) +		newsize = types->nextarg + 1; +	if (oldsize == STATIC_ARG_TBL_SIZE) { +		if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL) +			abort();			/* XXX handle better */ +		bcopy(oldtable, newtable, oldsize * sizeof(enum typeid)); +	} else { +		newtable = reallocf(oldtable, newsize * sizeof(enum typeid)); +		if (newtable == NULL) +			abort();			/* XXX handle better */ +	} +	for (n = oldsize; n < newsize; n++) +		newtable[n] = T_UNUSED; + +	types->table = newtable; +	types->tablesize = newsize; +}  | 
