190792Sgshapiro/*
2261194Sgshapiro * Copyright (c) 2000-2001, 2004 Proofpoint, Inc. and its suppliers.
390792Sgshapiro *      All rights reserved.
490792Sgshapiro * Copyright (c) 1990
590792Sgshapiro *	The Regents of the University of California.  All rights reserved.
690792Sgshapiro *
790792Sgshapiro * This code is derived from software contributed to Berkeley by
890792Sgshapiro * Chris Torek.
990792Sgshapiro *
1090792Sgshapiro * By using this file, you agree to the terms and conditions set
1190792Sgshapiro * forth in the LICENSE file which can be found at the top level of
1290792Sgshapiro * the sendmail distribution.
1390792Sgshapiro */
1490792Sgshapiro
1590792Sgshapiro#include <sm/gen.h>
16266527SgshapiroSM_IDSTR(id, "@(#)$Id: vfprintf.c,v 1.55 2013-11-22 20:51:44 ca Exp $")
1790792Sgshapiro
1890792Sgshapiro/*
1990792Sgshapiro**  Overall:
2090792Sgshapiro**  Actual printing innards.
2190792Sgshapiro**  This code is large and complicated...
2290792Sgshapiro*/
2390792Sgshapiro
2490792Sgshapiro#include <sys/types.h>
2590792Sgshapiro#include <stdlib.h>
2690792Sgshapiro#include <string.h>
2790792Sgshapiro#include <errno.h>
2890792Sgshapiro#include <sm/config.h>
2990792Sgshapiro#include <sm/varargs.h>
3090792Sgshapiro#include <sm/io.h>
3190792Sgshapiro#include <sm/heap.h>
3290792Sgshapiro#include <sm/conf.h>
3390792Sgshapiro#include "local.h"
3490792Sgshapiro#include "fvwrite.h"
3590792Sgshapiro
36141858Sgshapirostatic int	sm_bprintf __P((SM_FILE_T *, const char *, va_list));
3790792Sgshapirostatic void	sm_find_arguments __P((const char *, va_list , va_list **));
3890792Sgshapirostatic void	sm_grow_type_table_x __P((unsigned char **, int *));
3990792Sgshapirostatic int	sm_print __P((SM_FILE_T *, int, struct sm_uio *));
4090792Sgshapiro
4190792Sgshapiro/*
4290792Sgshapiro**  SM_PRINT -- print/flush to the file
4390792Sgshapiro**
4490792Sgshapiro**  Flush out all the vectors defined by the given uio,
4590792Sgshapiro**  then reset it so that it can be reused.
4690792Sgshapiro**
4790792Sgshapiro**	Parameters:
4890792Sgshapiro**		fp -- file pointer
4990792Sgshapiro**		timeout -- time to complete operation (milliseconds)
5090792Sgshapiro**		uio -- vector list of memory locations of data for printing
5190792Sgshapiro**
5290792Sgshapiro**	Results:
5390792Sgshapiro**		Success: 0 (zero)
5490792Sgshapiro**		Failure:
5590792Sgshapiro*/
5690792Sgshapiro
5790792Sgshapirostatic int
5890792Sgshapirosm_print(fp, timeout, uio)
5990792Sgshapiro	SM_FILE_T *fp;
6090792Sgshapiro	int timeout;
6190792Sgshapiro	register struct sm_uio *uio;
6290792Sgshapiro{
6390792Sgshapiro	register int err;
6490792Sgshapiro
6590792Sgshapiro	if (uio->uio_resid == 0)
6690792Sgshapiro	{
6790792Sgshapiro		uio->uio_iovcnt = 0;
6890792Sgshapiro		return 0;
6990792Sgshapiro	}
7090792Sgshapiro	err = sm_fvwrite(fp, timeout, uio);
7190792Sgshapiro	uio->uio_resid = 0;
7290792Sgshapiro	uio->uio_iovcnt = 0;
7390792Sgshapiro	return err;
7490792Sgshapiro}
7590792Sgshapiro
7690792Sgshapiro/*
7790792Sgshapiro**  SM_BPRINTF -- allow formating to an unbuffered file.
7890792Sgshapiro**
7990792Sgshapiro**  Helper function for `fprintf to unbuffered unix file': creates a
8090792Sgshapiro**  temporary buffer (via a "fake" file pointer).
8190792Sgshapiro**  We only work on write-only files; this avoids
8290792Sgshapiro**  worries about ungetc buffers and so forth.
8390792Sgshapiro**
8490792Sgshapiro**	Parameters:
8590792Sgshapiro**		fp -- the file to send the o/p to
8690792Sgshapiro**		fmt -- format instructions for the o/p
8790792Sgshapiro**		ap -- vectors of data units used for formating
8890792Sgshapiro**
8990792Sgshapiro**	Results:
9090792Sgshapiro**		Failure: SM_IO_EOF and errno set
9190792Sgshapiro**		Success: number of data units used in the formating
9290792Sgshapiro**
9390792Sgshapiro**	Side effects:
9490792Sgshapiro**		formatted o/p can be SM_IO_BUFSIZ length maximum
9590792Sgshapiro*/
9690792Sgshapiro
9790792Sgshapirostatic int
9890792Sgshapirosm_bprintf(fp, fmt, ap)
99141858Sgshapiro	SM_FILE_T *fp;
10090792Sgshapiro	const char *fmt;
10190792Sgshapiro	SM_VA_LOCAL_DECL
10290792Sgshapiro{
10390792Sgshapiro	int ret;
10490792Sgshapiro	SM_FILE_T fake;
10590792Sgshapiro	unsigned char buf[SM_IO_BUFSIZ];
10690792Sgshapiro	extern const char SmFileMagic[];
10790792Sgshapiro
10890792Sgshapiro	/* copy the important variables */
10990792Sgshapiro	fake.sm_magic = SmFileMagic;
11090792Sgshapiro	fake.f_timeout = SM_TIME_FOREVER;
11190792Sgshapiro	fake.f_timeoutstate = SM_TIME_BLOCK;
11290792Sgshapiro	fake.f_flags = fp->f_flags & ~SMNBF;
11390792Sgshapiro	fake.f_file = fp->f_file;
11490792Sgshapiro	fake.f_cookie = fp->f_cookie;
11590792Sgshapiro	fake.f_write = fp->f_write;
11690792Sgshapiro	fake.f_close = NULL;
11790792Sgshapiro	fake.f_open = NULL;
11890792Sgshapiro	fake.f_read = NULL;
11990792Sgshapiro	fake.f_seek = NULL;
12090792Sgshapiro	fake.f_setinfo = fake.f_getinfo = NULL;
12190792Sgshapiro	fake.f_type = "sm_bprintf:fake";
12290792Sgshapiro
12390792Sgshapiro	/* set up the buffer */
12490792Sgshapiro	fake.f_bf.smb_base = fake.f_p = buf;
12590792Sgshapiro	fake.f_bf.smb_size = fake.f_w = sizeof(buf);
12690792Sgshapiro	fake.f_lbfsize = 0;	/* not actually used, but Just In Case */
12790792Sgshapiro
12890792Sgshapiro	/* do the work, then copy any error status */
12990792Sgshapiro	ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap);
13090792Sgshapiro	if (ret >= 0 && sm_io_flush(&fake, SM_TIME_FOREVER))
13190792Sgshapiro		ret = SM_IO_EOF;	/* errno set by sm_io_flush */
13290792Sgshapiro	if (fake.f_flags & SMERR)
13390792Sgshapiro		fp->f_flags |= SMERR;
13490792Sgshapiro	return ret;
13590792Sgshapiro}
13690792Sgshapiro
13790792Sgshapiro
13890792Sgshapiro#define BUF		40
13990792Sgshapiro
14090792Sgshapiro#define STATIC_ARG_TBL_SIZE 8	/* Size of static argument table. */
14190792Sgshapiro
14290792Sgshapiro
14390792Sgshapiro/* Macros for converting digits to letters and vice versa */
14490792Sgshapiro#define to_digit(c)	((c) - '0')
14590792Sgshapiro#define is_digit(c)	((unsigned) to_digit(c) <= 9)
14690792Sgshapiro#define to_char(n)	((char) (n) + '0')
14790792Sgshapiro
14890792Sgshapiro/* Flags used during conversion. */
14990792Sgshapiro#define ALT		0x001		/* alternate form */
15090792Sgshapiro#define HEXPREFIX	0x002		/* add 0x or 0X prefix */
15190792Sgshapiro#define LADJUST		0x004		/* left adjustment */
15290792Sgshapiro#define LONGINT		0x010		/* long integer */
15390792Sgshapiro#define QUADINT		0x020		/* quad integer */
15490792Sgshapiro#define SHORTINT	0x040		/* short integer */
15590792Sgshapiro#define ZEROPAD		0x080		/* zero (as opposed to blank) pad */
15690792Sgshapiro#define FPT		0x100		/* Floating point number */
15790792Sgshapiro
15890792Sgshapiro/*
159285229Sgshapiro**  SM_IO_VFPRINTF -- performs actual formating for o/p
16090792Sgshapiro**
16190792Sgshapiro**	Parameters:
16290792Sgshapiro**		fp -- file pointer for o/p
16390792Sgshapiro**		timeout -- time to complete the print
16490792Sgshapiro**		fmt0 -- formating directives
16590792Sgshapiro**		ap -- vectors with data units for formating
16690792Sgshapiro**
16790792Sgshapiro**	Results:
16890792Sgshapiro**		Success: number of data units used for formatting
16990792Sgshapiro**		Failure: SM_IO_EOF and sets errno
17090792Sgshapiro*/
17190792Sgshapiro
17290792Sgshapiroint
17390792Sgshapirosm_io_vfprintf(fp, timeout, fmt0, ap)
17490792Sgshapiro	SM_FILE_T *fp;
17590792Sgshapiro	int timeout;
17690792Sgshapiro	const char *fmt0;
17790792Sgshapiro	SM_VA_LOCAL_DECL
17890792Sgshapiro{
17990792Sgshapiro	register char *fmt;	/* format string */
18090792Sgshapiro	register int ch;	/* character from fmt */
18190792Sgshapiro	register int n, m, n2;	/* handy integers (short term usage) */
18290792Sgshapiro	register char *cp;	/* handy char pointer (short term usage) */
18390792Sgshapiro	register struct sm_iov *iovp;/* for PRINT macro */
18490792Sgshapiro	register int flags;	/* flags as above */
18590792Sgshapiro	int ret;		/* return value accumulator */
18690792Sgshapiro	int width;		/* width from format (%8d), or 0 */
18790792Sgshapiro	int prec;		/* precision from format (%.3d), or -1 */
18890792Sgshapiro	char sign;		/* sign prefix (' ', '+', '-', or \0) */
18990792Sgshapiro	wchar_t wc;
19090792Sgshapiro	ULONGLONG_T _uquad;	/* integer arguments %[diouxX] */
19190792Sgshapiro	enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
19290792Sgshapiro	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
19390792Sgshapiro	int realsz;		/* field size expanded by dprec */
19490792Sgshapiro	int size;		/* size of converted field or string */
19590792Sgshapiro	char *xdigs="0123456789abcdef"; /* digits for [xX] conversion */
19690792Sgshapiro#define NIOV 8
19790792Sgshapiro	struct sm_uio uio;	/* output information: summary */
19890792Sgshapiro	struct sm_iov iov[NIOV];/* ... and individual io vectors */
19990792Sgshapiro	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
20090792Sgshapiro	char ox[2];		/* space for 0x hex-prefix */
20190792Sgshapiro	va_list *argtable;	/* args, built due to positional arg */
20290792Sgshapiro	va_list statargtable[STATIC_ARG_TBL_SIZE];
20390792Sgshapiro	int nextarg;		/* 1-based argument index */
20490792Sgshapiro	va_list orgap;		/* original argument pointer */
20590792Sgshapiro
20690792Sgshapiro	/*
20790792Sgshapiro	**  Choose PADSIZE to trade efficiency vs. size.  If larger printf
20890792Sgshapiro	**  fields occur frequently, increase PADSIZE and make the initialisers
20990792Sgshapiro	**  below longer.
21090792Sgshapiro	*/
21190792Sgshapiro#define PADSIZE	16		/* pad chunk size */
21290792Sgshapiro	static char blanks[PADSIZE] =
21390792Sgshapiro	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
21490792Sgshapiro	static char zeroes[PADSIZE] =
21590792Sgshapiro	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
21690792Sgshapiro
21790792Sgshapiro	/*
21890792Sgshapiro	**  BEWARE, these `goto error' on error, and PAD uses `n'.
21990792Sgshapiro	*/
22090792Sgshapiro#define PRINT(ptr, len) do { \
22190792Sgshapiro	iovp->iov_base = (ptr); \
22290792Sgshapiro	iovp->iov_len = (len); \
22390792Sgshapiro	uio.uio_resid += (len); \
22490792Sgshapiro	iovp++; \
22590792Sgshapiro	if (++uio.uio_iovcnt >= NIOV) \
22690792Sgshapiro	{ \
22790792Sgshapiro		if (sm_print(fp, timeout, &uio)) \
22890792Sgshapiro			goto error; \
22990792Sgshapiro		iovp = iov; \
23090792Sgshapiro	} \
23190792Sgshapiro} while (0)
23290792Sgshapiro#define PAD(howmany, with) do \
23390792Sgshapiro{ \
23490792Sgshapiro	if ((n = (howmany)) > 0) \
23590792Sgshapiro	{ \
23690792Sgshapiro		while (n > PADSIZE) { \
23790792Sgshapiro			PRINT(with, PADSIZE); \
23890792Sgshapiro			n -= PADSIZE; \
23990792Sgshapiro		} \
24090792Sgshapiro		PRINT(with, n); \
24190792Sgshapiro	} \
24290792Sgshapiro} while (0)
24390792Sgshapiro#define FLUSH() do \
24490792Sgshapiro{ \
24590792Sgshapiro	if (uio.uio_resid && sm_print(fp, timeout, &uio)) \
24690792Sgshapiro		goto error; \
24790792Sgshapiro	uio.uio_iovcnt = 0; \
24890792Sgshapiro	iovp = iov; \
24990792Sgshapiro} while (0)
25090792Sgshapiro
25190792Sgshapiro	/*
25290792Sgshapiro	**  To extend shorts properly, we need both signed and unsigned
25390792Sgshapiro	**  argument extraction methods.
25490792Sgshapiro	*/
25590792Sgshapiro#define SARG() \
25690792Sgshapiro	(flags&QUADINT ? SM_VA_ARG(ap, LONGLONG_T) : \
25790792Sgshapiro	    flags&LONGINT ? GETARG(long) : \
25890792Sgshapiro	    flags&SHORTINT ? (long) (short) GETARG(int) : \
25990792Sgshapiro	    (long) GETARG(int))
26090792Sgshapiro#define UARG() \
26190792Sgshapiro	(flags&QUADINT ? SM_VA_ARG(ap, ULONGLONG_T) : \
26290792Sgshapiro	    flags&LONGINT ? GETARG(unsigned long) : \
26390792Sgshapiro	    flags&SHORTINT ? (unsigned long) (unsigned short) GETARG(int) : \
26490792Sgshapiro	    (unsigned long) GETARG(unsigned int))
26590792Sgshapiro
26690792Sgshapiro	/*
26790792Sgshapiro	**  Get * arguments, including the form *nn$.  Preserve the nextarg
26890792Sgshapiro	**  that the argument can be gotten once the type is determined.
26990792Sgshapiro	*/
27090792Sgshapiro#define GETASTER(val) \
27190792Sgshapiro	n2 = 0; \
27290792Sgshapiro	cp = fmt; \
27390792Sgshapiro	while (is_digit(*cp)) \
27490792Sgshapiro	{ \
27590792Sgshapiro		n2 = 10 * n2 + to_digit(*cp); \
27690792Sgshapiro		cp++; \
27790792Sgshapiro	} \
27890792Sgshapiro	if (*cp == '$') \
27990792Sgshapiro	{ \
28090792Sgshapiro		int hold = nextarg; \
28190792Sgshapiro		if (argtable == NULL) \
28290792Sgshapiro		{ \
28390792Sgshapiro			argtable = statargtable; \
28490792Sgshapiro			sm_find_arguments(fmt0, orgap, &argtable); \
28590792Sgshapiro		} \
28690792Sgshapiro		nextarg = n2; \
28790792Sgshapiro		val = GETARG(int); \
28890792Sgshapiro		nextarg = hold; \
28990792Sgshapiro		fmt = ++cp; \
29090792Sgshapiro	} \
29190792Sgshapiro	else \
29290792Sgshapiro	{ \
29390792Sgshapiro		val = GETARG(int); \
29490792Sgshapiro	}
29590792Sgshapiro
29690792Sgshapiro/*
29790792Sgshapiro**  Get the argument indexed by nextarg.   If the argument table is
29890792Sgshapiro**  built, use it to get the argument.  If its not, get the next
29990792Sgshapiro**  argument (and arguments must be gotten sequentially).
30090792Sgshapiro*/
30190792Sgshapiro
30290792Sgshapiro#if SM_VA_STD
30390792Sgshapiro# define GETARG(type) \
30490792Sgshapiro	(((argtable != NULL) ? (void) (ap = argtable[nextarg]) : (void) 0), \
30590792Sgshapiro	 nextarg++, SM_VA_ARG(ap, type))
30690792Sgshapiro#else /* SM_VA_STD */
30790792Sgshapiro# define GETARG(type) \
30890792Sgshapiro	((argtable != NULL) ? (*((type*)(argtable[nextarg++]))) : \
30990792Sgshapiro			      (nextarg++, SM_VA_ARG(ap, type)))
31090792Sgshapiro#endif /* SM_VA_STD */
31190792Sgshapiro
31290792Sgshapiro	/* sorry, fprintf(read_only_file, "") returns SM_IO_EOF, not 0 */
31390792Sgshapiro	if (cantwrite(fp))
31490792Sgshapiro	{
31590792Sgshapiro		errno = EBADF;
31690792Sgshapiro		return SM_IO_EOF;
31790792Sgshapiro	}
31890792Sgshapiro
31990792Sgshapiro	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
32090792Sgshapiro	if ((fp->f_flags & (SMNBF|SMWR|SMRW)) == (SMNBF|SMWR) &&
32190792Sgshapiro	    fp->f_file >= 0)
32290792Sgshapiro		return sm_bprintf(fp, fmt0, ap);
32390792Sgshapiro
32490792Sgshapiro	fmt = (char *) fmt0;
32590792Sgshapiro	argtable = NULL;
32690792Sgshapiro	nextarg = 1;
32790792Sgshapiro	SM_VA_COPY(orgap, ap);
32890792Sgshapiro	uio.uio_iov = iovp = iov;
32990792Sgshapiro	uio.uio_resid = 0;
33090792Sgshapiro	uio.uio_iovcnt = 0;
33190792Sgshapiro	ret = 0;
33290792Sgshapiro
33390792Sgshapiro	/* Scan the format for conversions (`%' character). */
33490792Sgshapiro	for (;;)
33590792Sgshapiro	{
33690792Sgshapiro		cp = fmt;
33790792Sgshapiro		n = 0;
33890792Sgshapiro		while ((wc = *fmt) != '\0')
33990792Sgshapiro		{
34090792Sgshapiro			if (wc == '%')
34190792Sgshapiro			{
34290792Sgshapiro				n = 1;
34390792Sgshapiro				break;
34490792Sgshapiro			}
34590792Sgshapiro			fmt++;
34690792Sgshapiro		}
34790792Sgshapiro		if ((m = fmt - cp) != 0)
34890792Sgshapiro		{
34990792Sgshapiro			PRINT(cp, m);
35090792Sgshapiro			ret += m;
35190792Sgshapiro		}
35290792Sgshapiro		if (n <= 0)
35390792Sgshapiro			goto done;
35490792Sgshapiro		fmt++;		/* skip over '%' */
35590792Sgshapiro
35690792Sgshapiro		flags = 0;
35790792Sgshapiro		dprec = 0;
35890792Sgshapiro		width = 0;
35990792Sgshapiro		prec = -1;
36090792Sgshapiro		sign = '\0';
36190792Sgshapiro
36290792Sgshapirorflag:		ch = *fmt++;
36390792Sgshapiroreswitch:	switch (ch)
36490792Sgshapiro		{
36590792Sgshapiro		  case ' ':
36690792Sgshapiro
36790792Sgshapiro			/*
36890792Sgshapiro			**  ``If the space and + flags both appear, the space
36990792Sgshapiro			**  flag will be ignored.''
37090792Sgshapiro			**	-- ANSI X3J11
37190792Sgshapiro			*/
37290792Sgshapiro
37390792Sgshapiro			if (!sign)
37490792Sgshapiro				sign = ' ';
37590792Sgshapiro			goto rflag;
37690792Sgshapiro		  case '#':
37790792Sgshapiro			flags |= ALT;
37890792Sgshapiro			goto rflag;
37990792Sgshapiro		  case '*':
38090792Sgshapiro
38190792Sgshapiro			/*
38290792Sgshapiro			**  ``A negative field width argument is taken as a
38390792Sgshapiro			**  - flag followed by a positive field width.''
38490792Sgshapiro			**	-- ANSI X3J11
38590792Sgshapiro			**  They don't exclude field widths read from args.
38690792Sgshapiro			*/
38790792Sgshapiro
38890792Sgshapiro			GETASTER(width);
38990792Sgshapiro			if (width >= 0)
39090792Sgshapiro				goto rflag;
39190792Sgshapiro			width = -width;
39290792Sgshapiro			/* FALLTHROUGH */
39390792Sgshapiro		  case '-':
39490792Sgshapiro			flags |= LADJUST;
39590792Sgshapiro			goto rflag;
39690792Sgshapiro		  case '+':
39790792Sgshapiro			sign = '+';
39890792Sgshapiro			goto rflag;
39990792Sgshapiro		  case '.':
40090792Sgshapiro			if ((ch = *fmt++) == '*')
40190792Sgshapiro			{
40290792Sgshapiro				GETASTER(n);
40390792Sgshapiro				prec = n < 0 ? -1 : n;
40490792Sgshapiro				goto rflag;
40590792Sgshapiro			}
40690792Sgshapiro			n = 0;
40790792Sgshapiro			while (is_digit(ch))
40890792Sgshapiro			{
40990792Sgshapiro				n = 10 * n + to_digit(ch);
41090792Sgshapiro				ch = *fmt++;
41190792Sgshapiro			}
41290792Sgshapiro			if (ch == '$')
41390792Sgshapiro			{
41490792Sgshapiro				nextarg = n;
41590792Sgshapiro				if (argtable == NULL)
41690792Sgshapiro				{
41790792Sgshapiro					argtable = statargtable;
41890792Sgshapiro					sm_find_arguments(fmt0, orgap,
41990792Sgshapiro					    &argtable);
42090792Sgshapiro				}
42190792Sgshapiro				goto rflag;
42290792Sgshapiro			}
42390792Sgshapiro			prec = n < 0 ? -1 : n;
42490792Sgshapiro			goto reswitch;
42590792Sgshapiro		  case '0':
42690792Sgshapiro
42790792Sgshapiro			/*
42890792Sgshapiro			**  ``Note that 0 is taken as a flag, not as the
42990792Sgshapiro			**  beginning of a field width.''
43090792Sgshapiro			**	-- ANSI X3J11
43190792Sgshapiro			*/
43290792Sgshapiro
43390792Sgshapiro			flags |= ZEROPAD;
43490792Sgshapiro			goto rflag;
43590792Sgshapiro		  case '1': case '2': case '3': case '4':
43690792Sgshapiro		  case '5': case '6': case '7': case '8': case '9':
43790792Sgshapiro			n = 0;
43890792Sgshapiro			do
43990792Sgshapiro			{
44090792Sgshapiro				n = 10 * n + to_digit(ch);
44190792Sgshapiro				ch = *fmt++;
44290792Sgshapiro			} while (is_digit(ch));
44390792Sgshapiro			if (ch == '$')
44490792Sgshapiro			{
44590792Sgshapiro				nextarg = n;
44690792Sgshapiro				if (argtable == NULL)
44790792Sgshapiro				{
44890792Sgshapiro					argtable = statargtable;
44990792Sgshapiro					sm_find_arguments(fmt0, orgap,
45090792Sgshapiro					    &argtable);
45190792Sgshapiro				}
45290792Sgshapiro				goto rflag;
45390792Sgshapiro			}
45490792Sgshapiro			width = n;
45590792Sgshapiro			goto reswitch;
45690792Sgshapiro		  case 'h':
45790792Sgshapiro			flags |= SHORTINT;
45890792Sgshapiro			goto rflag;
45990792Sgshapiro		  case 'l':
46090792Sgshapiro			if (*fmt == 'l')
46190792Sgshapiro			{
46290792Sgshapiro				fmt++;
46390792Sgshapiro				flags |= QUADINT;
46490792Sgshapiro			}
46590792Sgshapiro			else
46690792Sgshapiro			{
46790792Sgshapiro				flags |= LONGINT;
46890792Sgshapiro			}
46990792Sgshapiro			goto rflag;
47090792Sgshapiro		  case 'q':
47190792Sgshapiro			flags |= QUADINT;
47290792Sgshapiro			goto rflag;
47390792Sgshapiro		  case 'c':
47490792Sgshapiro			*(cp = buf) = GETARG(int);
47590792Sgshapiro			size = 1;
47690792Sgshapiro			sign = '\0';
47790792Sgshapiro			break;
47890792Sgshapiro		  case 'D':
47990792Sgshapiro			flags |= LONGINT;
48090792Sgshapiro			/*FALLTHROUGH*/
48190792Sgshapiro		  case 'd':
48290792Sgshapiro		  case 'i':
48390792Sgshapiro			_uquad = SARG();
48490792Sgshapiro			if ((LONGLONG_T) _uquad < 0)
48590792Sgshapiro			{
48690792Sgshapiro				_uquad = -(LONGLONG_T) _uquad;
48790792Sgshapiro				sign = '-';
48890792Sgshapiro			}
48990792Sgshapiro			base = DEC;
49090792Sgshapiro			goto number;
49190792Sgshapiro		  case 'e':
49290792Sgshapiro		  case 'E':
49390792Sgshapiro		  case 'f':
49490792Sgshapiro		  case 'g':
49590792Sgshapiro		  case 'G':
49690792Sgshapiro			{
49790792Sgshapiro				double val;
49890792Sgshapiro				char *p;
49990792Sgshapiro				char fmt[16];
50090792Sgshapiro				char out[150];
50190792Sgshapiro				size_t len;
50290792Sgshapiro
50390792Sgshapiro				/*
50490792Sgshapiro				**  This code implements floating point output
50590792Sgshapiro				**  in the most portable manner possible,
50690792Sgshapiro				**  relying only on 'sprintf' as defined by
50790792Sgshapiro				**  the 1989 ANSI C standard.
50890792Sgshapiro				**  We silently cap width and precision
50990792Sgshapiro				**  at 120, to avoid buffer overflow.
51090792Sgshapiro				*/
51190792Sgshapiro
51290792Sgshapiro				val = GETARG(double);
51390792Sgshapiro
51490792Sgshapiro				p = fmt;
51590792Sgshapiro				*p++ = '%';
51690792Sgshapiro				if (sign)
51790792Sgshapiro					*p++ = sign;
51890792Sgshapiro				if (flags & ALT)
51990792Sgshapiro					*p++ = '#';
52090792Sgshapiro				if (flags & LADJUST)
52190792Sgshapiro					*p++ = '-';
52290792Sgshapiro				if (flags & ZEROPAD)
52390792Sgshapiro					*p++ = '0';
52490792Sgshapiro				*p++ = '*';
52590792Sgshapiro				if (prec >= 0)
52690792Sgshapiro				{
52790792Sgshapiro					*p++ = '.';
52890792Sgshapiro					*p++ = '*';
52990792Sgshapiro				}
53090792Sgshapiro				*p++ = ch;
53190792Sgshapiro				*p = '\0';
53290792Sgshapiro
53390792Sgshapiro				if (width > 120)
53490792Sgshapiro					width = 120;
53590792Sgshapiro				if (prec > 120)
53690792Sgshapiro					prec = 120;
53790792Sgshapiro				if (prec >= 0)
538157001Sgshapiro#if HASSNPRINTF
539157001Sgshapiro					snprintf(out, sizeof(out), fmt, width,
540157001Sgshapiro						prec, val);
541157001Sgshapiro#else /* HASSNPRINTF */
54290792Sgshapiro					sprintf(out, fmt, width, prec, val);
543157001Sgshapiro#endif /* HASSNPRINTF */
54490792Sgshapiro				else
545157001Sgshapiro#if HASSNPRINTF
546157001Sgshapiro					snprintf(out, sizeof(out), fmt, width,
547157001Sgshapiro						val);
548157001Sgshapiro#else /* HASSNPRINTF */
54990792Sgshapiro					sprintf(out, fmt, width, val);
550157001Sgshapiro#endif /* HASSNPRINTF */
55190792Sgshapiro				len = strlen(out);
55290792Sgshapiro				PRINT(out, len);
55390792Sgshapiro				FLUSH();
55490792Sgshapiro				continue;
55590792Sgshapiro			}
55690792Sgshapiro		case 'n':
55790792Sgshapiro			if (flags & QUADINT)
55890792Sgshapiro				*GETARG(LONGLONG_T *) = ret;
55990792Sgshapiro			else if (flags & LONGINT)
56090792Sgshapiro				*GETARG(long *) = ret;
56190792Sgshapiro			else if (flags & SHORTINT)
56290792Sgshapiro				*GETARG(short *) = ret;
56390792Sgshapiro			else
56490792Sgshapiro				*GETARG(int *) = ret;
56590792Sgshapiro			continue;	/* no output */
56690792Sgshapiro		  case 'O':
56790792Sgshapiro			flags |= LONGINT;
56890792Sgshapiro			/*FALLTHROUGH*/
56990792Sgshapiro		  case 'o':
57090792Sgshapiro			_uquad = UARG();
57190792Sgshapiro			base = OCT;
57290792Sgshapiro			goto nosign;
57390792Sgshapiro		  case 'p':
57490792Sgshapiro
57590792Sgshapiro			/*
57690792Sgshapiro			**  ``The argument shall be a pointer to void.  The
57790792Sgshapiro			**  value of the pointer is converted to a sequence
57890792Sgshapiro			**  of printable characters, in an implementation-
57990792Sgshapiro			**  defined manner.''
58090792Sgshapiro			**	-- ANSI X3J11
58190792Sgshapiro			*/
58290792Sgshapiro
58390792Sgshapiro			/* NOSTRICT */
58490792Sgshapiro			{
58590792Sgshapiro				union
58690792Sgshapiro				{
58790792Sgshapiro					void *p;
58890792Sgshapiro					ULONGLONG_T ll;
58990792Sgshapiro					unsigned long l;
59090792Sgshapiro					unsigned i;
59190792Sgshapiro				} u;
59290792Sgshapiro				u.p = GETARG(void *);
59390792Sgshapiro				if (sizeof(void *) == sizeof(ULONGLONG_T))
59490792Sgshapiro					_uquad = u.ll;
59590792Sgshapiro				else if (sizeof(void *) == sizeof(long))
59690792Sgshapiro					_uquad = u.l;
59790792Sgshapiro				else
59890792Sgshapiro					_uquad = u.i;
59990792Sgshapiro			}
60090792Sgshapiro			base = HEX;
60190792Sgshapiro			xdigs = "0123456789abcdef";
60290792Sgshapiro			flags |= HEXPREFIX;
60390792Sgshapiro			ch = 'x';
60490792Sgshapiro			goto nosign;
60590792Sgshapiro		  case 's':
60690792Sgshapiro			if ((cp = GETARG(char *)) == NULL)
60790792Sgshapiro				cp = "(null)";
60890792Sgshapiro			if (prec >= 0)
60990792Sgshapiro			{
61090792Sgshapiro				/*
61190792Sgshapiro				**  can't use strlen; can only look for the
61290792Sgshapiro				**  NUL in the first `prec' characters, and
61390792Sgshapiro				**  strlen() will go further.
61490792Sgshapiro				*/
61590792Sgshapiro
61690792Sgshapiro				char *p = memchr(cp, 0, prec);
61790792Sgshapiro
61890792Sgshapiro				if (p != NULL)
61990792Sgshapiro				{
62090792Sgshapiro					size = p - cp;
62190792Sgshapiro					if (size > prec)
62290792Sgshapiro						size = prec;
62390792Sgshapiro				}
62490792Sgshapiro				else
62590792Sgshapiro					size = prec;
62690792Sgshapiro			}
62790792Sgshapiro			else
62890792Sgshapiro				size = strlen(cp);
62990792Sgshapiro			sign = '\0';
63090792Sgshapiro			break;
63190792Sgshapiro		  case 'U':
63290792Sgshapiro			flags |= LONGINT;
63390792Sgshapiro			/*FALLTHROUGH*/
63490792Sgshapiro		  case 'u':
63590792Sgshapiro			_uquad = UARG();
63690792Sgshapiro			base = DEC;
63790792Sgshapiro			goto nosign;
63890792Sgshapiro		  case 'X':
63990792Sgshapiro			xdigs = "0123456789ABCDEF";
64090792Sgshapiro			goto hex;
64190792Sgshapiro		  case 'x':
64290792Sgshapiro			xdigs = "0123456789abcdef";
64390792Sgshapirohex:			_uquad = UARG();
64490792Sgshapiro			base = HEX;
64590792Sgshapiro			/* leading 0x/X only if non-zero */
64690792Sgshapiro			if (flags & ALT && _uquad != 0)
64790792Sgshapiro				flags |= HEXPREFIX;
64890792Sgshapiro
64990792Sgshapiro			/* unsigned conversions */
65090792Sgshapironosign:			sign = '\0';
65190792Sgshapiro
65290792Sgshapiro			/*
65390792Sgshapiro			**  ``... diouXx conversions ... if a precision is
65490792Sgshapiro			**  specified, the 0 flag will be ignored.''
65590792Sgshapiro			**	-- ANSI X3J11
65690792Sgshapiro			*/
65790792Sgshapiro
65890792Sgshapironumber:			if ((dprec = prec) >= 0)
65990792Sgshapiro				flags &= ~ZEROPAD;
66090792Sgshapiro
66190792Sgshapiro			/*
66290792Sgshapiro			**  ``The result of converting a zero value with an
66390792Sgshapiro			**  explicit precision of zero is no characters.''
66490792Sgshapiro			**	-- ANSI X3J11
66590792Sgshapiro			*/
66690792Sgshapiro
66790792Sgshapiro			cp = buf + BUF;
66890792Sgshapiro			if (_uquad != 0 || prec != 0)
66990792Sgshapiro			{
67090792Sgshapiro				/*
67190792Sgshapiro				**  Unsigned mod is hard, and unsigned mod
67290792Sgshapiro				**  by a constant is easier than that by
67390792Sgshapiro				**  a variable; hence this switch.
67490792Sgshapiro				*/
67590792Sgshapiro
67690792Sgshapiro				switch (base)
67790792Sgshapiro				{
67890792Sgshapiro				  case OCT:
67990792Sgshapiro					do
68090792Sgshapiro					{
68190792Sgshapiro						*--cp = to_char(_uquad & 7);
68290792Sgshapiro						_uquad >>= 3;
68390792Sgshapiro					} while (_uquad);
68490792Sgshapiro					/* handle octal leading 0 */
68590792Sgshapiro					if (flags & ALT && *cp != '0')
68690792Sgshapiro						*--cp = '0';
68790792Sgshapiro					break;
68890792Sgshapiro
68990792Sgshapiro				  case DEC:
69090792Sgshapiro					/* many numbers are 1 digit */
69190792Sgshapiro					while (_uquad >= 10)
69290792Sgshapiro					{
69390792Sgshapiro						*--cp = to_char(_uquad % 10);
69490792Sgshapiro						_uquad /= 10;
69590792Sgshapiro					}
69690792Sgshapiro					*--cp = to_char(_uquad);
69790792Sgshapiro					break;
69890792Sgshapiro
69990792Sgshapiro				  case HEX:
70090792Sgshapiro					do
70190792Sgshapiro					{
70290792Sgshapiro						*--cp = xdigs[_uquad & 15];
70390792Sgshapiro						_uquad >>= 4;
70490792Sgshapiro					} while (_uquad);
70590792Sgshapiro					break;
70690792Sgshapiro
70790792Sgshapiro				  default:
70890792Sgshapiro					cp = "bug in sm_io_vfprintf: bad base";
70990792Sgshapiro					size = strlen(cp);
71090792Sgshapiro					goto skipsize;
71190792Sgshapiro				}
71290792Sgshapiro			}
71390792Sgshapiro			size = buf + BUF - cp;
71490792Sgshapiro		  skipsize:
71590792Sgshapiro			break;
71690792Sgshapiro		  default:	/* "%?" prints ?, unless ? is NUL */
71790792Sgshapiro			if (ch == '\0')
71890792Sgshapiro				goto done;
71990792Sgshapiro			/* pretend it was %c with argument ch */
72090792Sgshapiro			cp = buf;
72190792Sgshapiro			*cp = ch;
72290792Sgshapiro			size = 1;
72390792Sgshapiro			sign = '\0';
72490792Sgshapiro			break;
72590792Sgshapiro		}
72690792Sgshapiro
72790792Sgshapiro		/*
72890792Sgshapiro		**  All reasonable formats wind up here.  At this point, `cp'
72990792Sgshapiro		**  points to a string which (if not flags&LADJUST) should be
73090792Sgshapiro		**  padded out to `width' places.  If flags&ZEROPAD, it should
73190792Sgshapiro		**  first be prefixed by any sign or other prefix; otherwise,
73290792Sgshapiro		**  it should be blank padded before the prefix is emitted.
73390792Sgshapiro		**  After any left-hand padding and prefixing, emit zeroes
73490792Sgshapiro		**  required by a decimal [diouxX] precision, then print the
73590792Sgshapiro		**  string proper, then emit zeroes required by any leftover
73690792Sgshapiro		**  floating precision; finally, if LADJUST, pad with blanks.
73790792Sgshapiro		**
73890792Sgshapiro		**  Compute actual size, so we know how much to pad.
73990792Sgshapiro		**  size excludes decimal prec; realsz includes it.
74090792Sgshapiro		*/
74190792Sgshapiro
74290792Sgshapiro		realsz = dprec > size ? dprec : size;
74390792Sgshapiro		if (sign)
74490792Sgshapiro			realsz++;
74590792Sgshapiro		else if (flags & HEXPREFIX)
74690792Sgshapiro			realsz+= 2;
74790792Sgshapiro
74890792Sgshapiro		/* right-adjusting blank padding */
74990792Sgshapiro		if ((flags & (LADJUST|ZEROPAD)) == 0)
75090792Sgshapiro			PAD(width - realsz, blanks);
75190792Sgshapiro
75290792Sgshapiro		/* prefix */
75390792Sgshapiro		if (sign)
75490792Sgshapiro		{
75590792Sgshapiro			PRINT(&sign, 1);
75690792Sgshapiro		}
75790792Sgshapiro		else if (flags & HEXPREFIX)
75890792Sgshapiro		{
75990792Sgshapiro			ox[0] = '0';
76090792Sgshapiro			ox[1] = ch;
76190792Sgshapiro			PRINT(ox, 2);
76290792Sgshapiro		}
76390792Sgshapiro
76490792Sgshapiro		/* right-adjusting zero padding */
76590792Sgshapiro		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
76690792Sgshapiro			PAD(width - realsz, zeroes);
76790792Sgshapiro
76890792Sgshapiro		/* leading zeroes from decimal precision */
76990792Sgshapiro		PAD(dprec - size, zeroes);
77090792Sgshapiro
77190792Sgshapiro		/* the string or number proper */
77290792Sgshapiro		PRINT(cp, size);
77390792Sgshapiro		/* left-adjusting padding (always blank) */
77490792Sgshapiro		if (flags & LADJUST)
77590792Sgshapiro			PAD(width - realsz, blanks);
77690792Sgshapiro
77790792Sgshapiro		/* finally, adjust ret */
77890792Sgshapiro		ret += width > realsz ? width : realsz;
77990792Sgshapiro
78090792Sgshapiro		FLUSH();	/* copy out the I/O vectors */
78190792Sgshapiro	}
78290792Sgshapirodone:
78390792Sgshapiro	FLUSH();
78490792Sgshapiroerror:
78590792Sgshapiro	if ((argtable != NULL) && (argtable != statargtable))
78690792Sgshapiro		sm_free(argtable);
78790792Sgshapiro	return sm_error(fp) ? SM_IO_EOF : ret;
78890792Sgshapiro	/* NOTREACHED */
78990792Sgshapiro}
79090792Sgshapiro
79190792Sgshapiro/* Type ids for argument type table. */
79290792Sgshapiro#define T_UNUSED	0
79390792Sgshapiro#define T_SHORT		1
79490792Sgshapiro#define T_U_SHORT	2
79590792Sgshapiro#define TP_SHORT	3
79690792Sgshapiro#define T_INT		4
79790792Sgshapiro#define T_U_INT		5
79890792Sgshapiro#define TP_INT		6
79990792Sgshapiro#define T_LONG		7
80090792Sgshapiro#define T_U_LONG	8
80190792Sgshapiro#define TP_LONG		9
80290792Sgshapiro#define T_QUAD		10
80390792Sgshapiro#define T_U_QUAD	11
80490792Sgshapiro#define TP_QUAD		12
80590792Sgshapiro#define T_DOUBLE	13
80690792Sgshapiro#define TP_CHAR		15
80790792Sgshapiro#define TP_VOID		16
80890792Sgshapiro
80990792Sgshapiro/*
81090792Sgshapiro**  SM_FIND_ARGUMENTS -- find all args when a positional parameter is found.
81190792Sgshapiro**
81290792Sgshapiro**  Find all arguments when a positional parameter is encountered.  Returns a
81390792Sgshapiro**  table, indexed by argument number, of pointers to each arguments.  The
81490792Sgshapiro**  initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
81590792Sgshapiro**  It will be replaced with a malloc-ed one if it overflows.
81690792Sgshapiro**
81790792Sgshapiro**	Parameters:
81890792Sgshapiro**		fmt0 -- formating directives
81990792Sgshapiro**		ap -- vector list of data unit for formating consumption
82090792Sgshapiro**		argtable -- an indexable table (returned) of 'ap'
82190792Sgshapiro**
82290792Sgshapiro**	Results:
82390792Sgshapiro**		none.
82490792Sgshapiro*/
82590792Sgshapiro
82690792Sgshapirostatic void
82790792Sgshapirosm_find_arguments(fmt0, ap, argtable)
82890792Sgshapiro	const char *fmt0;
82990792Sgshapiro	SM_VA_LOCAL_DECL
83090792Sgshapiro	va_list **argtable;
83190792Sgshapiro{
83290792Sgshapiro	register char *fmt;	/* format string */
83390792Sgshapiro	register int ch;	/* character from fmt */
83490792Sgshapiro	register int n, n2;	/* handy integer (short term usage) */
83590792Sgshapiro	register char *cp;	/* handy char pointer (short term usage) */
83690792Sgshapiro	register int flags;	/* flags as above */
83790792Sgshapiro	unsigned char *typetable; /* table of types */
83890792Sgshapiro	unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
83990792Sgshapiro	int tablesize;		/* current size of type table */
84090792Sgshapiro	int tablemax;		/* largest used index in table */
84190792Sgshapiro	int nextarg;		/* 1-based argument index */
84290792Sgshapiro
84390792Sgshapiro	/* Add an argument type to the table, expanding if necessary. */
84490792Sgshapiro#define ADDTYPE(type) \
84590792Sgshapiro	((nextarg >= tablesize) ? \
84690792Sgshapiro		(sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \
84790792Sgshapiro	typetable[nextarg++] = type, \
84890792Sgshapiro	(nextarg > tablemax) ? tablemax = nextarg : 0)
84990792Sgshapiro
85090792Sgshapiro#define ADDSARG() \
85190792Sgshapiro	((flags & LONGINT) ? ADDTYPE(T_LONG) : \
85290792Sgshapiro		((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
85390792Sgshapiro
85490792Sgshapiro#define ADDUARG() \
85590792Sgshapiro	((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \
85690792Sgshapiro		((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
85790792Sgshapiro
85890792Sgshapiro	/* Add * arguments to the type array. */
85990792Sgshapiro#define ADDASTER() \
86090792Sgshapiro	n2 = 0; \
86190792Sgshapiro	cp = fmt; \
86290792Sgshapiro	while (is_digit(*cp)) \
86390792Sgshapiro	{ \
86490792Sgshapiro		n2 = 10 * n2 + to_digit(*cp); \
86590792Sgshapiro		cp++; \
86690792Sgshapiro	} \
86790792Sgshapiro	if (*cp == '$') \
86890792Sgshapiro	{ \
86990792Sgshapiro		int hold = nextarg; \
87090792Sgshapiro		nextarg = n2; \
87190792Sgshapiro		ADDTYPE (T_INT); \
87290792Sgshapiro		nextarg = hold; \
87390792Sgshapiro		fmt = ++cp; \
87490792Sgshapiro	} \
87590792Sgshapiro	else \
87690792Sgshapiro	{ \
87790792Sgshapiro		ADDTYPE (T_INT); \
87890792Sgshapiro	}
87990792Sgshapiro	fmt = (char *) fmt0;
88090792Sgshapiro	typetable = stattypetable;
88190792Sgshapiro	tablesize = STATIC_ARG_TBL_SIZE;
88290792Sgshapiro	tablemax = 0;
88390792Sgshapiro	nextarg = 1;
88490792Sgshapiro	(void) memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
88590792Sgshapiro
88690792Sgshapiro	/* Scan the format for conversions (`%' character). */
88790792Sgshapiro	for (;;)
88890792Sgshapiro	{
88990792Sgshapiro		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
89090792Sgshapiro			/* void */;
89190792Sgshapiro		if (ch == '\0')
89290792Sgshapiro			goto done;
89390792Sgshapiro		fmt++;		/* skip over '%' */
89490792Sgshapiro
89590792Sgshapiro		flags = 0;
89690792Sgshapiro
89790792Sgshapirorflag:		ch = *fmt++;
89890792Sgshapiroreswitch:	switch (ch)
89990792Sgshapiro		{
90090792Sgshapiro		  case ' ':
90190792Sgshapiro		  case '#':
90290792Sgshapiro			goto rflag;
90390792Sgshapiro		  case '*':
90490792Sgshapiro			ADDASTER();
90590792Sgshapiro			goto rflag;
90690792Sgshapiro		  case '-':
90790792Sgshapiro		  case '+':
90890792Sgshapiro			goto rflag;
90990792Sgshapiro		  case '.':
91090792Sgshapiro			if ((ch = *fmt++) == '*')
91190792Sgshapiro			{
91290792Sgshapiro				ADDASTER();
91390792Sgshapiro				goto rflag;
91490792Sgshapiro			}
91590792Sgshapiro			while (is_digit(ch))
91690792Sgshapiro			{
91790792Sgshapiro				ch = *fmt++;
91890792Sgshapiro			}
91990792Sgshapiro			goto reswitch;
92090792Sgshapiro		  case '0':
92190792Sgshapiro			goto rflag;
92290792Sgshapiro		  case '1': case '2': case '3': case '4':
92390792Sgshapiro		  case '5': case '6': case '7': case '8': case '9':
92490792Sgshapiro			n = 0;
92590792Sgshapiro			do
92690792Sgshapiro			{
92790792Sgshapiro				n = 10 * n + to_digit(ch);
92890792Sgshapiro				ch = *fmt++;
92990792Sgshapiro			} while (is_digit(ch));
93090792Sgshapiro			if (ch == '$')
93190792Sgshapiro			{
93290792Sgshapiro				nextarg = n;
93390792Sgshapiro				goto rflag;
93490792Sgshapiro			}
93590792Sgshapiro			goto reswitch;
93690792Sgshapiro		  case 'h':
93790792Sgshapiro			flags |= SHORTINT;
93890792Sgshapiro			goto rflag;
93990792Sgshapiro		  case 'l':
94090792Sgshapiro			flags |= LONGINT;
94190792Sgshapiro			goto rflag;
94290792Sgshapiro		  case 'q':
94390792Sgshapiro			flags |= QUADINT;
94490792Sgshapiro			goto rflag;
94590792Sgshapiro		  case 'c':
94690792Sgshapiro			ADDTYPE(T_INT);
94790792Sgshapiro			break;
94890792Sgshapiro		  case 'D':
94990792Sgshapiro			flags |= LONGINT;
95090792Sgshapiro			/*FALLTHROUGH*/
95190792Sgshapiro		  case 'd':
95290792Sgshapiro		  case 'i':
95390792Sgshapiro			if (flags & QUADINT)
95490792Sgshapiro			{
95590792Sgshapiro				ADDTYPE(T_QUAD);
95690792Sgshapiro			}
95790792Sgshapiro			else
95890792Sgshapiro			{
95990792Sgshapiro				ADDSARG();
96090792Sgshapiro			}
96190792Sgshapiro			break;
96290792Sgshapiro		  case 'e':
96390792Sgshapiro		  case 'E':
96490792Sgshapiro		  case 'f':
96590792Sgshapiro		  case 'g':
96690792Sgshapiro		  case 'G':
96790792Sgshapiro			ADDTYPE(T_DOUBLE);
96890792Sgshapiro			break;
96990792Sgshapiro		  case 'n':
97090792Sgshapiro			if (flags & QUADINT)
97190792Sgshapiro				ADDTYPE(TP_QUAD);
97290792Sgshapiro			else if (flags & LONGINT)
97390792Sgshapiro				ADDTYPE(TP_LONG);
97490792Sgshapiro			else if (flags & SHORTINT)
97590792Sgshapiro				ADDTYPE(TP_SHORT);
97690792Sgshapiro			else
97790792Sgshapiro				ADDTYPE(TP_INT);
97890792Sgshapiro			continue;	/* no output */
97990792Sgshapiro		  case 'O':
98090792Sgshapiro			flags |= LONGINT;
98190792Sgshapiro			/*FALLTHROUGH*/
98290792Sgshapiro		  case 'o':
98390792Sgshapiro			if (flags & QUADINT)
98490792Sgshapiro				ADDTYPE(T_U_QUAD);
98590792Sgshapiro			else
98690792Sgshapiro				ADDUARG();
98790792Sgshapiro			break;
98890792Sgshapiro		  case 'p':
98990792Sgshapiro			ADDTYPE(TP_VOID);
99090792Sgshapiro			break;
99190792Sgshapiro		  case 's':
99290792Sgshapiro			ADDTYPE(TP_CHAR);
99390792Sgshapiro			break;
99490792Sgshapiro		  case 'U':
99590792Sgshapiro			flags |= LONGINT;
99690792Sgshapiro			/*FALLTHROUGH*/
99790792Sgshapiro		  case 'u':
99890792Sgshapiro			if (flags & QUADINT)
99990792Sgshapiro				ADDTYPE(T_U_QUAD);
100090792Sgshapiro			else
100190792Sgshapiro				ADDUARG();
100290792Sgshapiro			break;
100390792Sgshapiro		  case 'X':
100490792Sgshapiro		  case 'x':
100590792Sgshapiro			if (flags & QUADINT)
100690792Sgshapiro				ADDTYPE(T_U_QUAD);
100790792Sgshapiro			else
100890792Sgshapiro				ADDUARG();
100990792Sgshapiro			break;
101090792Sgshapiro		  default:	/* "%?" prints ?, unless ? is NUL */
101190792Sgshapiro			if (ch == '\0')
101290792Sgshapiro				goto done;
101390792Sgshapiro			break;
101490792Sgshapiro		}
101590792Sgshapiro	}
101690792Sgshapirodone:
101790792Sgshapiro	/* Build the argument table. */
101890792Sgshapiro	if (tablemax >= STATIC_ARG_TBL_SIZE)
101990792Sgshapiro	{
102090792Sgshapiro		*argtable = (va_list *)
102190792Sgshapiro		    sm_malloc(sizeof(va_list) * (tablemax + 1));
102290792Sgshapiro	}
102390792Sgshapiro
102490792Sgshapiro	for (n = 1; n <= tablemax; n++)
102590792Sgshapiro	{
102690792Sgshapiro		SM_VA_COPY((*argtable)[n], ap);
102790792Sgshapiro		switch (typetable [n])
102890792Sgshapiro		{
102990792Sgshapiro		  case T_UNUSED:
103090792Sgshapiro			(void) SM_VA_ARG(ap, int);
103190792Sgshapiro			break;
103290792Sgshapiro		  case T_SHORT:
103390792Sgshapiro			(void) SM_VA_ARG(ap, int);
103490792Sgshapiro			break;
103590792Sgshapiro		  case T_U_SHORT:
103690792Sgshapiro			(void) SM_VA_ARG(ap, int);
103790792Sgshapiro			break;
103890792Sgshapiro		  case TP_SHORT:
103990792Sgshapiro			(void) SM_VA_ARG(ap, short *);
104090792Sgshapiro			break;
104190792Sgshapiro		  case T_INT:
104290792Sgshapiro			(void) SM_VA_ARG(ap, int);
104390792Sgshapiro			break;
104490792Sgshapiro		  case T_U_INT:
104590792Sgshapiro			(void) SM_VA_ARG(ap, unsigned int);
104690792Sgshapiro			break;
104790792Sgshapiro		  case TP_INT:
104890792Sgshapiro			(void) SM_VA_ARG(ap, int *);
104990792Sgshapiro			break;
105090792Sgshapiro		  case T_LONG:
105190792Sgshapiro			(void) SM_VA_ARG(ap, long);
105290792Sgshapiro			break;
105390792Sgshapiro		  case T_U_LONG:
105490792Sgshapiro			(void) SM_VA_ARG(ap, unsigned long);
105590792Sgshapiro			break;
105690792Sgshapiro		  case TP_LONG:
105790792Sgshapiro			(void) SM_VA_ARG(ap, long *);
105890792Sgshapiro			break;
105990792Sgshapiro		  case T_QUAD:
106090792Sgshapiro			(void) SM_VA_ARG(ap, LONGLONG_T);
106190792Sgshapiro			break;
106290792Sgshapiro		  case T_U_QUAD:
106390792Sgshapiro			(void) SM_VA_ARG(ap, ULONGLONG_T);
106490792Sgshapiro			break;
106590792Sgshapiro		  case TP_QUAD:
106690792Sgshapiro			(void) SM_VA_ARG(ap, LONGLONG_T *);
106790792Sgshapiro			break;
106890792Sgshapiro		  case T_DOUBLE:
106990792Sgshapiro			(void) SM_VA_ARG(ap, double);
107090792Sgshapiro			break;
107190792Sgshapiro		  case TP_CHAR:
107290792Sgshapiro			(void) SM_VA_ARG(ap, char *);
107390792Sgshapiro			break;
107490792Sgshapiro		  case TP_VOID:
107590792Sgshapiro			(void) SM_VA_ARG(ap, void *);
107690792Sgshapiro			break;
107790792Sgshapiro		}
107890792Sgshapiro	}
107990792Sgshapiro
108090792Sgshapiro	if ((typetable != NULL) && (typetable != stattypetable))
108190792Sgshapiro		sm_free(typetable);
108290792Sgshapiro}
108390792Sgshapiro
108490792Sgshapiro/*
108590792Sgshapiro**  SM_GROW_TYPE_TABLE -- Increase the size of the type table.
108690792Sgshapiro**
108790792Sgshapiro**	Parameters:
108890792Sgshapiro**		tabletype -- type of table to grow
108990792Sgshapiro**		tablesize -- requested new table size
109090792Sgshapiro**
109190792Sgshapiro**	Results:
109290792Sgshapiro**		Raises an exception if can't allocate memory.
109390792Sgshapiro*/
109490792Sgshapiro
109590792Sgshapirostatic void
109690792Sgshapirosm_grow_type_table_x(typetable, tablesize)
109790792Sgshapiro	unsigned char **typetable;
109890792Sgshapiro	int *tablesize;
109990792Sgshapiro{
110090792Sgshapiro	unsigned char *oldtable = *typetable;
110190792Sgshapiro	int newsize = *tablesize * 2;
110290792Sgshapiro
110390792Sgshapiro	if (*tablesize == STATIC_ARG_TBL_SIZE)
110490792Sgshapiro	{
110590792Sgshapiro		*typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char)
110690792Sgshapiro							   * newsize);
110790792Sgshapiro		(void) memmove(*typetable, oldtable, *tablesize);
110890792Sgshapiro	}
110990792Sgshapiro	else
111090792Sgshapiro	{
111190792Sgshapiro		*typetable = (unsigned char *) sm_realloc_x(typetable,
111290792Sgshapiro					sizeof(unsigned char) * newsize);
111390792Sgshapiro	}
111490792Sgshapiro	(void) memset(&typetable [*tablesize], T_UNUSED,
111590792Sgshapiro		       (newsize - *tablesize));
111690792Sgshapiro
111790792Sgshapiro	*tablesize = newsize;
111890792Sgshapiro}
1119