1/*-
2 * Copyright (c) 1990, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Chris Torek.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#if defined(LIBC_SCCS) && !defined(lint)
34static char sccsid[] = "@(#)vfprintf.c	8.1 (Berkeley) 6/4/93";
35#endif /* LIBC_SCCS and not lint */
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD$");
38
39/*
40 * This is the code responsible for handling positional arguments
41 * (%m$ and %m$.n$) for vfprintf() and vfwprintf().
42 */
43
44#include "namespace.h"
45#include <sys/types.h>
46
47#include <stdarg.h>
48#include <stddef.h>
49#include <stdint.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <wchar.h>
54
55#include "un-namespace.h"
56#include "printflocal.h"
57
58/*
59 * Type ids for argument type table.
60 */
61enum typeid {
62	T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT,
63	T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG,
64	T_PTRDIFFT, TP_PTRDIFFT, T_SSIZET, T_SIZET, TP_SSIZET,
65	T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR,
66	T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR
67};
68
69/* An expandable array of types. */
70struct typetable {
71	enum typeid *table; /* table of types */
72	enum typeid stattable[STATIC_ARG_TBL_SIZE];
73	int tablesize;		/* current size of type table */
74	int tablemax;		/* largest used index in table */
75	int nextarg;		/* 1-based argument index */
76};
77
78static int	__grow_type_table(struct typetable *);
79static void	build_arg_table (struct typetable *, va_list, union arg **);
80
81/*
82 * Initialize a struct typetable.
83 */
84static inline void
85inittypes(struct typetable *types)
86{
87	int n;
88
89	types->table = types->stattable;
90	types->tablesize = STATIC_ARG_TBL_SIZE;
91	types->tablemax = 0;
92	types->nextarg = 1;
93	for (n = 0; n < STATIC_ARG_TBL_SIZE; n++)
94		types->table[n] = T_UNUSED;
95}
96
97/*
98 * struct typetable destructor.
99 */
100static inline void
101freetypes(struct typetable *types)
102{
103
104	if (types->table != types->stattable)
105		free (types->table);
106}
107
108/*
109 * Ensure that there is space to add a new argument type to the type table.
110 * Expand the table if necessary. Returns 0 on success.
111 */
112static inline int
113_ensurespace(struct typetable *types)
114{
115
116	if (types->nextarg >= types->tablesize) {
117		if (__grow_type_table(types))
118			return (-1);
119	}
120	if (types->nextarg > types->tablemax)
121		types->tablemax = types->nextarg;
122	return (0);
123}
124
125/*
126 * Add an argument type to the table, expanding if necessary.
127 * Returns 0 on success.
128 */
129static inline int
130addtype(struct typetable *types, enum typeid type)
131{
132
133	if (_ensurespace(types))
134		return (-1);
135	types->table[types->nextarg++] = type;
136	return (0);
137}
138
139static inline int
140addsarg(struct typetable *types, int flags)
141{
142
143	if (_ensurespace(types))
144		return (-1);
145	if (flags & INTMAXT)
146		types->table[types->nextarg++] = T_INTMAXT;
147	else if (flags & SIZET)
148		types->table[types->nextarg++] = T_SSIZET;
149	else if (flags & PTRDIFFT)
150		types->table[types->nextarg++] = T_PTRDIFFT;
151	else if (flags & LLONGINT)
152		types->table[types->nextarg++] = T_LLONG;
153	else if (flags & LONGINT)
154		types->table[types->nextarg++] = T_LONG;
155	else
156		types->table[types->nextarg++] = T_INT;
157	return (0);
158}
159
160static inline int
161adduarg(struct typetable *types, int flags)
162{
163
164	if (_ensurespace(types))
165		return (-1);
166	if (flags & INTMAXT)
167		types->table[types->nextarg++] = T_UINTMAXT;
168	else if (flags & SIZET)
169		types->table[types->nextarg++] = T_SIZET;
170	else if (flags & PTRDIFFT)
171		types->table[types->nextarg++] = T_SIZET;
172	else if (flags & LLONGINT)
173		types->table[types->nextarg++] = T_U_LLONG;
174	else if (flags & LONGINT)
175		types->table[types->nextarg++] = T_U_LONG;
176	else
177		types->table[types->nextarg++] = T_U_INT;
178	return (0);
179}
180
181/*
182 * Add * arguments to the type array.
183 */
184static inline int
185addaster(struct typetable *types, char **fmtp)
186{
187	char *cp;
188	int n2;
189
190	n2 = 0;
191	cp = *fmtp;
192	while (is_digit(*cp)) {
193		n2 = 10 * n2 + to_digit(*cp);
194		cp++;
195	}
196	if (*cp == '$') {
197		int hold = types->nextarg;
198		types->nextarg = n2;
199		if (addtype(types, T_INT))
200			return (-1);
201		types->nextarg = hold;
202		*fmtp = ++cp;
203	} else {
204		if (addtype(types, T_INT))
205			return (-1);
206	}
207	return (0);
208}
209
210static inline int
211addwaster(struct typetable *types, wchar_t **fmtp)
212{
213	wchar_t *cp;
214	int n2;
215
216	n2 = 0;
217	cp = *fmtp;
218	while (is_digit(*cp)) {
219		n2 = 10 * n2 + to_digit(*cp);
220		cp++;
221	}
222	if (*cp == '$') {
223		int hold = types->nextarg;
224		types->nextarg = n2;
225		if (addtype(types, T_INT))
226			return (-1);
227		types->nextarg = hold;
228		*fmtp = ++cp;
229	} else {
230		if (addtype(types, T_INT))
231			return (-1);
232	}
233	return (0);
234}
235
236/*
237 * Find all arguments when a positional parameter is encountered.  Returns a
238 * table, indexed by argument number, of pointers to each arguments.  The
239 * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
240 * It will be replaces with a malloc-ed one if it overflows.
241 * Returns 0 on success. On failure, returns nonzero and sets errno.
242 */
243int
244__find_arguments (const char *fmt0, va_list ap, union arg **argtable)
245{
246	char *fmt;		/* format string */
247	int ch;			/* character from fmt */
248	int n;			/* handy integer (short term usage) */
249	int error;
250	int flags;		/* flags as above */
251	struct typetable types;	/* table of types */
252
253	fmt = (char *)fmt0;
254	inittypes(&types);
255	error = 0;
256
257	/*
258	 * Scan the format for conversions (`%' character).
259	 */
260	for (;;) {
261		while ((ch = *fmt) != '\0' && ch != '%')
262			fmt++;
263		if (ch == '\0')
264			goto done;
265		fmt++;		/* skip over '%' */
266
267		flags = 0;
268
269rflag:		ch = *fmt++;
270reswitch:	switch (ch) {
271		case ' ':
272		case '#':
273			goto rflag;
274		case '*':
275			if ((error = addaster(&types, &fmt)))
276				goto error;
277			goto rflag;
278		case '-':
279		case '+':
280		case '\'':
281			goto rflag;
282		case '.':
283			if ((ch = *fmt++) == '*') {
284				if ((error = addaster(&types, &fmt)))
285					goto error;
286				goto rflag;
287			}
288			while (is_digit(ch)) {
289				ch = *fmt++;
290			}
291			goto reswitch;
292		case '0':
293			goto rflag;
294		case '1': case '2': case '3': case '4':
295		case '5': case '6': case '7': case '8': case '9':
296			n = 0;
297			do {
298				n = 10 * n + to_digit(ch);
299				ch = *fmt++;
300			} while (is_digit(ch));
301			if (ch == '$') {
302				types.nextarg = n;
303				goto rflag;
304			}
305			goto reswitch;
306#ifndef NO_FLOATING_POINT
307		case 'L':
308			flags |= LONGDBL;
309			goto rflag;
310#endif
311		case 'h':
312			if (flags & SHORTINT) {
313				flags &= ~SHORTINT;
314				flags |= CHARINT;
315			} else
316				flags |= SHORTINT;
317			goto rflag;
318		case 'j':
319			flags |= INTMAXT;
320			goto rflag;
321		case 'l':
322			if (flags & LONGINT) {
323				flags &= ~LONGINT;
324				flags |= LLONGINT;
325			} else
326				flags |= LONGINT;
327			goto rflag;
328		case 'q':
329			flags |= LLONGINT;	/* not necessarily */
330			goto rflag;
331		case 't':
332			flags |= PTRDIFFT;
333			goto rflag;
334		case 'z':
335			flags |= SIZET;
336			goto rflag;
337		case 'C':
338			flags |= LONGINT;
339			/*FALLTHROUGH*/
340		case 'c':
341			error = addtype(&types,
342					(flags & LONGINT) ? T_WINT : T_INT);
343			if (error)
344				goto error;
345			break;
346		case 'D':
347			flags |= LONGINT;
348			/*FALLTHROUGH*/
349		case 'd':
350		case 'i':
351			if ((error = addsarg(&types, flags)))
352				goto error;
353			break;
354#ifndef NO_FLOATING_POINT
355		case 'a':
356		case 'A':
357		case 'e':
358		case 'E':
359		case 'f':
360		case 'g':
361		case 'G':
362			error = addtype(&types,
363			    (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE);
364			if (error)
365				goto error;
366			break;
367#endif /* !NO_FLOATING_POINT */
368		case 'n':
369			if (flags & INTMAXT)
370				error = addtype(&types, TP_INTMAXT);
371			else if (flags & PTRDIFFT)
372				error = addtype(&types, TP_PTRDIFFT);
373			else if (flags & SIZET)
374				error = addtype(&types, TP_SSIZET);
375			else if (flags & LLONGINT)
376				error = addtype(&types, TP_LLONG);
377			else if (flags & LONGINT)
378				error = addtype(&types, TP_LONG);
379			else if (flags & SHORTINT)
380				error = addtype(&types, TP_SHORT);
381			else if (flags & CHARINT)
382				error = addtype(&types, TP_SCHAR);
383			else
384				error = addtype(&types, TP_INT);
385			if (error)
386				goto error;
387			continue;	/* no output */
388		case 'O':
389			flags |= LONGINT;
390			/*FALLTHROUGH*/
391		case 'o':
392			if ((error = adduarg(&types, flags)))
393				goto error;
394			break;
395		case 'p':
396			if ((error = addtype(&types, TP_VOID)))
397				goto error;
398			break;
399		case 'S':
400			flags |= LONGINT;
401			/*FALLTHROUGH*/
402		case 's':
403			error = addtype(&types,
404					(flags & LONGINT) ? TP_WCHAR : TP_CHAR);
405			if (error)
406				goto error;
407			break;
408		case 'U':
409			flags |= LONGINT;
410			/*FALLTHROUGH*/
411		case 'u':
412		case 'X':
413		case 'x':
414			if ((error = adduarg(&types, flags)))
415				goto error;
416			break;
417		default:	/* "%?" prints ?, unless ? is NUL */
418			if (ch == '\0')
419				goto done;
420			break;
421		}
422	}
423done:
424	build_arg_table(&types, ap, argtable);
425error:
426	freetypes(&types);
427	return (error || *argtable == NULL);
428}
429
430/* wchar version of __find_arguments. */
431int
432__find_warguments (const wchar_t *fmt0, va_list ap, union arg **argtable)
433{
434	wchar_t *fmt;		/* format string */
435	wchar_t ch;		/* character from fmt */
436	int n;			/* handy integer (short term usage) */
437	int error;
438	int flags;		/* flags as above */
439	struct typetable types;	/* table of types */
440
441	fmt = (wchar_t *)fmt0;
442	inittypes(&types);
443	error = 0;
444
445	/*
446	 * Scan the format for conversions (`%' character).
447	 */
448	for (;;) {
449		while ((ch = *fmt) != '\0' && ch != '%')
450			fmt++;
451		if (ch == '\0')
452			goto done;
453		fmt++;		/* skip over '%' */
454
455		flags = 0;
456
457rflag:		ch = *fmt++;
458reswitch:	switch (ch) {
459		case ' ':
460		case '#':
461			goto rflag;
462		case '*':
463			if ((error = addwaster(&types, &fmt)))
464				goto error;
465			goto rflag;
466		case '-':
467		case '+':
468		case '\'':
469			goto rflag;
470		case '.':
471			if ((ch = *fmt++) == '*') {
472				if ((error = addwaster(&types, &fmt)))
473					goto error;
474				goto rflag;
475			}
476			while (is_digit(ch)) {
477				ch = *fmt++;
478			}
479			goto reswitch;
480		case '0':
481			goto rflag;
482		case '1': case '2': case '3': case '4':
483		case '5': case '6': case '7': case '8': case '9':
484			n = 0;
485			do {
486				n = 10 * n + to_digit(ch);
487				ch = *fmt++;
488			} while (is_digit(ch));
489			if (ch == '$') {
490				types.nextarg = n;
491				goto rflag;
492			}
493			goto reswitch;
494#ifndef NO_FLOATING_POINT
495		case 'L':
496			flags |= LONGDBL;
497			goto rflag;
498#endif
499		case 'h':
500			if (flags & SHORTINT) {
501				flags &= ~SHORTINT;
502				flags |= CHARINT;
503			} else
504				flags |= SHORTINT;
505			goto rflag;
506		case 'j':
507			flags |= INTMAXT;
508			goto rflag;
509		case 'l':
510			if (flags & LONGINT) {
511				flags &= ~LONGINT;
512				flags |= LLONGINT;
513			} else
514				flags |= LONGINT;
515			goto rflag;
516		case 'q':
517			flags |= LLONGINT;	/* not necessarily */
518			goto rflag;
519		case 't':
520			flags |= PTRDIFFT;
521			goto rflag;
522		case 'z':
523			flags |= SIZET;
524			goto rflag;
525		case 'C':
526			flags |= LONGINT;
527			/*FALLTHROUGH*/
528		case 'c':
529			error = addtype(&types,
530					(flags & LONGINT) ? T_WINT : T_INT);
531			if (error)
532				goto error;
533			break;
534		case 'D':
535			flags |= LONGINT;
536			/*FALLTHROUGH*/
537		case 'd':
538		case 'i':
539			if ((error = addsarg(&types, flags)))
540				goto error;
541			break;
542#ifndef NO_FLOATING_POINT
543		case 'a':
544		case 'A':
545		case 'e':
546		case 'E':
547		case 'f':
548		case 'g':
549		case 'G':
550			error = addtype(&types,
551			    (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE);
552			if (error)
553				goto error;
554			break;
555#endif /* !NO_FLOATING_POINT */
556		case 'n':
557			if (flags & INTMAXT)
558				error = addtype(&types, TP_INTMAXT);
559			else if (flags & PTRDIFFT)
560				error = addtype(&types, TP_PTRDIFFT);
561			else if (flags & SIZET)
562				error = addtype(&types, TP_SSIZET);
563			else if (flags & LLONGINT)
564				error = addtype(&types, TP_LLONG);
565			else if (flags & LONGINT)
566				error = addtype(&types, TP_LONG);
567			else if (flags & SHORTINT)
568				error = addtype(&types, TP_SHORT);
569			else if (flags & CHARINT)
570				error = addtype(&types, TP_SCHAR);
571			else
572				error = addtype(&types, TP_INT);
573			if (error)
574				goto error;
575			continue;	/* no output */
576		case 'O':
577			flags |= LONGINT;
578			/*FALLTHROUGH*/
579		case 'o':
580			if ((error = adduarg(&types, flags)))
581				goto error;
582			break;
583		case 'p':
584			if ((error = addtype(&types, TP_VOID)))
585				goto error;
586			break;
587		case 'S':
588			flags |= LONGINT;
589			/*FALLTHROUGH*/
590		case 's':
591			error = addtype(&types,
592			    (flags & LONGINT) ? TP_WCHAR : TP_CHAR);
593			if (error)
594				goto error;
595			break;
596		case 'U':
597			flags |= LONGINT;
598			/*FALLTHROUGH*/
599		case 'u':
600		case 'X':
601		case 'x':
602			if ((error = adduarg(&types, flags)))
603				goto error;
604			break;
605		default:	/* "%?" prints ?, unless ? is NUL */
606			if (ch == '\0')
607				goto done;
608			break;
609		}
610	}
611done:
612	build_arg_table(&types, ap, argtable);
613error:
614	freetypes(&types);
615	return (error || *argtable == NULL);
616}
617
618/*
619 * Increase the size of the type table. Returns 0 on success.
620 */
621static int
622__grow_type_table(struct typetable *types)
623{
624	enum typeid *const oldtable = types->table;
625	const int oldsize = types->tablesize;
626	enum typeid *newtable;
627	int n, newsize = oldsize * 2;
628
629	if (newsize < types->nextarg + 1)
630		newsize = types->nextarg + 1;
631	if (oldsize == STATIC_ARG_TBL_SIZE) {
632		if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL)
633			return (-1);
634		bcopy(oldtable, newtable, oldsize * sizeof(enum typeid));
635	} else {
636		newtable = realloc(oldtable, newsize * sizeof(enum typeid));
637		if (newtable == NULL)
638			return (-1);
639	}
640	for (n = oldsize; n < newsize; n++)
641		newtable[n] = T_UNUSED;
642
643	types->table = newtable;
644	types->tablesize = newsize;
645
646	return (0);
647}
648
649/*
650 * Build the argument table from the completed type table.
651 * On malloc failure, *argtable is set to NULL.
652 */
653static void
654build_arg_table(struct typetable *types, va_list ap, union arg **argtable)
655{
656	int n;
657
658	if (types->tablemax >= STATIC_ARG_TBL_SIZE) {
659		*argtable = (union arg *)
660		    malloc (sizeof (union arg) * (types->tablemax + 1));
661		if (*argtable == NULL)
662			return;
663	}
664
665	(*argtable) [0].intarg = 0;
666	for (n = 1; n <= types->tablemax; n++) {
667		switch (types->table[n]) {
668		    case T_UNUSED: /* whoops! */
669			(*argtable) [n].intarg = va_arg (ap, int);
670			break;
671		    case TP_SCHAR:
672			(*argtable) [n].pschararg = va_arg (ap, signed char *);
673			break;
674		    case TP_SHORT:
675			(*argtable) [n].pshortarg = va_arg (ap, short *);
676			break;
677		    case T_INT:
678			(*argtable) [n].intarg = va_arg (ap, int);
679			break;
680		    case T_U_INT:
681			(*argtable) [n].uintarg = va_arg (ap, unsigned int);
682			break;
683		    case TP_INT:
684			(*argtable) [n].pintarg = va_arg (ap, int *);
685			break;
686		    case T_LONG:
687			(*argtable) [n].longarg = va_arg (ap, long);
688			break;
689		    case T_U_LONG:
690			(*argtable) [n].ulongarg = va_arg (ap, unsigned long);
691			break;
692		    case TP_LONG:
693			(*argtable) [n].plongarg = va_arg (ap, long *);
694			break;
695		    case T_LLONG:
696			(*argtable) [n].longlongarg = va_arg (ap, long long);
697			break;
698		    case T_U_LLONG:
699			(*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long);
700			break;
701		    case TP_LLONG:
702			(*argtable) [n].plonglongarg = va_arg (ap, long long *);
703			break;
704		    case T_PTRDIFFT:
705			(*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t);
706			break;
707		    case TP_PTRDIFFT:
708			(*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *);
709			break;
710		    case T_SIZET:
711			(*argtable) [n].sizearg = va_arg (ap, size_t);
712			break;
713		    case T_SSIZET:
714			(*argtable) [n].sizearg = va_arg (ap, ssize_t);
715			break;
716		    case TP_SSIZET:
717			(*argtable) [n].pssizearg = va_arg (ap, ssize_t *);
718			break;
719		    case T_INTMAXT:
720			(*argtable) [n].intmaxarg = va_arg (ap, intmax_t);
721			break;
722		    case T_UINTMAXT:
723			(*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t);
724			break;
725		    case TP_INTMAXT:
726			(*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *);
727			break;
728		    case T_DOUBLE:
729#ifndef NO_FLOATING_POINT
730			(*argtable) [n].doublearg = va_arg (ap, double);
731#endif
732			break;
733		    case T_LONG_DOUBLE:
734#ifndef NO_FLOATING_POINT
735			(*argtable) [n].longdoublearg = va_arg (ap, long double);
736#endif
737			break;
738		    case TP_CHAR:
739			(*argtable) [n].pchararg = va_arg (ap, char *);
740			break;
741		    case TP_VOID:
742			(*argtable) [n].pvoidarg = va_arg (ap, void *);
743			break;
744		    case T_WINT:
745			(*argtable) [n].wintarg = va_arg (ap, wint_t);
746			break;
747		    case TP_WCHAR:
748			(*argtable) [n].pwchararg = va_arg (ap, wchar_t *);
749			break;
750		}
751	}
752}
753