xref: /illumos-gate/usr/src/cmd/sendmail/libsm/vfprintf.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
3*7c478bd9Sstevel@tonic-gate  *      All rights reserved.
4*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1990
5*7c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
6*7c478bd9Sstevel@tonic-gate  *
7*7c478bd9Sstevel@tonic-gate  * This code is derived from software contributed to Berkeley by
8*7c478bd9Sstevel@tonic-gate  * Chris Torek.
9*7c478bd9Sstevel@tonic-gate  *
10*7c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
11*7c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
12*7c478bd9Sstevel@tonic-gate  * the sendmail distribution.
13*7c478bd9Sstevel@tonic-gate  */
14*7c478bd9Sstevel@tonic-gate 
15*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
16*7c478bd9Sstevel@tonic-gate 
17*7c478bd9Sstevel@tonic-gate #include <sm/gen.h>
18*7c478bd9Sstevel@tonic-gate SM_IDSTR(id, "@(#)$Id: vfprintf.c,v 1.53 2004/08/03 20:54:49 ca Exp $")
19*7c478bd9Sstevel@tonic-gate 
20*7c478bd9Sstevel@tonic-gate /*
21*7c478bd9Sstevel@tonic-gate **  Overall:
22*7c478bd9Sstevel@tonic-gate **  Actual printing innards.
23*7c478bd9Sstevel@tonic-gate **  This code is large and complicated...
24*7c478bd9Sstevel@tonic-gate */
25*7c478bd9Sstevel@tonic-gate 
26*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
27*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
28*7c478bd9Sstevel@tonic-gate #include <string.h>
29*7c478bd9Sstevel@tonic-gate #include <errno.h>
30*7c478bd9Sstevel@tonic-gate #include <sm/config.h>
31*7c478bd9Sstevel@tonic-gate #include <sm/varargs.h>
32*7c478bd9Sstevel@tonic-gate #include <sm/io.h>
33*7c478bd9Sstevel@tonic-gate #include <sm/heap.h>
34*7c478bd9Sstevel@tonic-gate #include <sm/conf.h>
35*7c478bd9Sstevel@tonic-gate #include "local.h"
36*7c478bd9Sstevel@tonic-gate #include "fvwrite.h"
37*7c478bd9Sstevel@tonic-gate 
38*7c478bd9Sstevel@tonic-gate static int	sm_bprintf __P((SM_FILE_T *, const char *, va_list));
39*7c478bd9Sstevel@tonic-gate static void	sm_find_arguments __P((const char *, va_list , va_list **));
40*7c478bd9Sstevel@tonic-gate static void	sm_grow_type_table_x __P((unsigned char **, int *));
41*7c478bd9Sstevel@tonic-gate static int	sm_print __P((SM_FILE_T *, int, struct sm_uio *));
42*7c478bd9Sstevel@tonic-gate 
43*7c478bd9Sstevel@tonic-gate /*
44*7c478bd9Sstevel@tonic-gate **  SM_PRINT -- print/flush to the file
45*7c478bd9Sstevel@tonic-gate **
46*7c478bd9Sstevel@tonic-gate **  Flush out all the vectors defined by the given uio,
47*7c478bd9Sstevel@tonic-gate **  then reset it so that it can be reused.
48*7c478bd9Sstevel@tonic-gate **
49*7c478bd9Sstevel@tonic-gate **	Parameters:
50*7c478bd9Sstevel@tonic-gate **		fp -- file pointer
51*7c478bd9Sstevel@tonic-gate **		timeout -- time to complete operation (milliseconds)
52*7c478bd9Sstevel@tonic-gate **		uio -- vector list of memory locations of data for printing
53*7c478bd9Sstevel@tonic-gate **
54*7c478bd9Sstevel@tonic-gate **	Results:
55*7c478bd9Sstevel@tonic-gate **		Success: 0 (zero)
56*7c478bd9Sstevel@tonic-gate **		Failure:
57*7c478bd9Sstevel@tonic-gate */
58*7c478bd9Sstevel@tonic-gate 
59*7c478bd9Sstevel@tonic-gate static int
60*7c478bd9Sstevel@tonic-gate sm_print(fp, timeout, uio)
61*7c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
62*7c478bd9Sstevel@tonic-gate 	int timeout;
63*7c478bd9Sstevel@tonic-gate 	register struct sm_uio *uio;
64*7c478bd9Sstevel@tonic-gate {
65*7c478bd9Sstevel@tonic-gate 	register int err;
66*7c478bd9Sstevel@tonic-gate 
67*7c478bd9Sstevel@tonic-gate 	if (uio->uio_resid == 0)
68*7c478bd9Sstevel@tonic-gate 	{
69*7c478bd9Sstevel@tonic-gate 		uio->uio_iovcnt = 0;
70*7c478bd9Sstevel@tonic-gate 		return 0;
71*7c478bd9Sstevel@tonic-gate 	}
72*7c478bd9Sstevel@tonic-gate 	err = sm_fvwrite(fp, timeout, uio);
73*7c478bd9Sstevel@tonic-gate 	uio->uio_resid = 0;
74*7c478bd9Sstevel@tonic-gate 	uio->uio_iovcnt = 0;
75*7c478bd9Sstevel@tonic-gate 	return err;
76*7c478bd9Sstevel@tonic-gate }
77*7c478bd9Sstevel@tonic-gate 
78*7c478bd9Sstevel@tonic-gate /*
79*7c478bd9Sstevel@tonic-gate **  SM_BPRINTF -- allow formating to an unbuffered file.
80*7c478bd9Sstevel@tonic-gate **
81*7c478bd9Sstevel@tonic-gate **  Helper function for `fprintf to unbuffered unix file': creates a
82*7c478bd9Sstevel@tonic-gate **  temporary buffer (via a "fake" file pointer).
83*7c478bd9Sstevel@tonic-gate **  We only work on write-only files; this avoids
84*7c478bd9Sstevel@tonic-gate **  worries about ungetc buffers and so forth.
85*7c478bd9Sstevel@tonic-gate **
86*7c478bd9Sstevel@tonic-gate **	Parameters:
87*7c478bd9Sstevel@tonic-gate **		fp -- the file to send the o/p to
88*7c478bd9Sstevel@tonic-gate **		fmt -- format instructions for the o/p
89*7c478bd9Sstevel@tonic-gate **		ap -- vectors of data units used for formating
90*7c478bd9Sstevel@tonic-gate **
91*7c478bd9Sstevel@tonic-gate **	Results:
92*7c478bd9Sstevel@tonic-gate **		Failure: SM_IO_EOF and errno set
93*7c478bd9Sstevel@tonic-gate **		Success: number of data units used in the formating
94*7c478bd9Sstevel@tonic-gate **
95*7c478bd9Sstevel@tonic-gate **	Side effects:
96*7c478bd9Sstevel@tonic-gate **		formatted o/p can be SM_IO_BUFSIZ length maximum
97*7c478bd9Sstevel@tonic-gate */
98*7c478bd9Sstevel@tonic-gate 
99*7c478bd9Sstevel@tonic-gate static int
100*7c478bd9Sstevel@tonic-gate sm_bprintf(fp, fmt, ap)
101*7c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
102*7c478bd9Sstevel@tonic-gate 	const char *fmt;
103*7c478bd9Sstevel@tonic-gate 	SM_VA_LOCAL_DECL
104*7c478bd9Sstevel@tonic-gate {
105*7c478bd9Sstevel@tonic-gate 	int ret;
106*7c478bd9Sstevel@tonic-gate 	SM_FILE_T fake;
107*7c478bd9Sstevel@tonic-gate 	unsigned char buf[SM_IO_BUFSIZ];
108*7c478bd9Sstevel@tonic-gate 	extern const char SmFileMagic[];
109*7c478bd9Sstevel@tonic-gate 
110*7c478bd9Sstevel@tonic-gate 	/* copy the important variables */
111*7c478bd9Sstevel@tonic-gate 	fake.sm_magic = SmFileMagic;
112*7c478bd9Sstevel@tonic-gate 	fake.f_timeout = SM_TIME_FOREVER;
113*7c478bd9Sstevel@tonic-gate 	fake.f_timeoutstate = SM_TIME_BLOCK;
114*7c478bd9Sstevel@tonic-gate 	fake.f_flags = fp->f_flags & ~SMNBF;
115*7c478bd9Sstevel@tonic-gate 	fake.f_file = fp->f_file;
116*7c478bd9Sstevel@tonic-gate 	fake.f_cookie = fp->f_cookie;
117*7c478bd9Sstevel@tonic-gate 	fake.f_write = fp->f_write;
118*7c478bd9Sstevel@tonic-gate 	fake.f_close = NULL;
119*7c478bd9Sstevel@tonic-gate 	fake.f_open = NULL;
120*7c478bd9Sstevel@tonic-gate 	fake.f_read = NULL;
121*7c478bd9Sstevel@tonic-gate 	fake.f_seek = NULL;
122*7c478bd9Sstevel@tonic-gate 	fake.f_setinfo = fake.f_getinfo = NULL;
123*7c478bd9Sstevel@tonic-gate 	fake.f_type = "sm_bprintf:fake";
124*7c478bd9Sstevel@tonic-gate 
125*7c478bd9Sstevel@tonic-gate 	/* set up the buffer */
126*7c478bd9Sstevel@tonic-gate 	fake.f_bf.smb_base = fake.f_p = buf;
127*7c478bd9Sstevel@tonic-gate 	fake.f_bf.smb_size = fake.f_w = sizeof(buf);
128*7c478bd9Sstevel@tonic-gate 	fake.f_lbfsize = 0;	/* not actually used, but Just In Case */
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate 	/* do the work, then copy any error status */
131*7c478bd9Sstevel@tonic-gate 	ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap);
132*7c478bd9Sstevel@tonic-gate 	if (ret >= 0 && sm_io_flush(&fake, SM_TIME_FOREVER))
133*7c478bd9Sstevel@tonic-gate 		ret = SM_IO_EOF;	/* errno set by sm_io_flush */
134*7c478bd9Sstevel@tonic-gate 	if (fake.f_flags & SMERR)
135*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= SMERR;
136*7c478bd9Sstevel@tonic-gate 	return ret;
137*7c478bd9Sstevel@tonic-gate }
138*7c478bd9Sstevel@tonic-gate 
139*7c478bd9Sstevel@tonic-gate 
140*7c478bd9Sstevel@tonic-gate #define BUF		40
141*7c478bd9Sstevel@tonic-gate 
142*7c478bd9Sstevel@tonic-gate #define STATIC_ARG_TBL_SIZE 8	/* Size of static argument table. */
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate 
145*7c478bd9Sstevel@tonic-gate /* Macros for converting digits to letters and vice versa */
146*7c478bd9Sstevel@tonic-gate #define to_digit(c)	((c) - '0')
147*7c478bd9Sstevel@tonic-gate #define is_digit(c)	((unsigned) to_digit(c) <= 9)
148*7c478bd9Sstevel@tonic-gate #define to_char(n)	((char) (n) + '0')
149*7c478bd9Sstevel@tonic-gate 
150*7c478bd9Sstevel@tonic-gate /* Flags used during conversion. */
151*7c478bd9Sstevel@tonic-gate #define ALT		0x001		/* alternate form */
152*7c478bd9Sstevel@tonic-gate #define HEXPREFIX	0x002		/* add 0x or 0X prefix */
153*7c478bd9Sstevel@tonic-gate #define LADJUST		0x004		/* left adjustment */
154*7c478bd9Sstevel@tonic-gate #define LONGINT		0x010		/* long integer */
155*7c478bd9Sstevel@tonic-gate #define QUADINT		0x020		/* quad integer */
156*7c478bd9Sstevel@tonic-gate #define SHORTINT	0x040		/* short integer */
157*7c478bd9Sstevel@tonic-gate #define ZEROPAD		0x080		/* zero (as opposed to blank) pad */
158*7c478bd9Sstevel@tonic-gate #define FPT		0x100		/* Floating point number */
159*7c478bd9Sstevel@tonic-gate 
160*7c478bd9Sstevel@tonic-gate /*
161*7c478bd9Sstevel@tonic-gate **  SM_IO_VPRINTF -- performs actual formating for o/p
162*7c478bd9Sstevel@tonic-gate **
163*7c478bd9Sstevel@tonic-gate **	Parameters:
164*7c478bd9Sstevel@tonic-gate **		fp -- file pointer for o/p
165*7c478bd9Sstevel@tonic-gate **		timeout -- time to complete the print
166*7c478bd9Sstevel@tonic-gate **		fmt0 -- formating directives
167*7c478bd9Sstevel@tonic-gate **		ap -- vectors with data units for formating
168*7c478bd9Sstevel@tonic-gate **
169*7c478bd9Sstevel@tonic-gate **	Results:
170*7c478bd9Sstevel@tonic-gate **		Success: number of data units used for formatting
171*7c478bd9Sstevel@tonic-gate **		Failure: SM_IO_EOF and sets errno
172*7c478bd9Sstevel@tonic-gate */
173*7c478bd9Sstevel@tonic-gate 
174*7c478bd9Sstevel@tonic-gate int
175*7c478bd9Sstevel@tonic-gate sm_io_vfprintf(fp, timeout, fmt0, ap)
176*7c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
177*7c478bd9Sstevel@tonic-gate 	int timeout;
178*7c478bd9Sstevel@tonic-gate 	const char *fmt0;
179*7c478bd9Sstevel@tonic-gate 	SM_VA_LOCAL_DECL
180*7c478bd9Sstevel@tonic-gate {
181*7c478bd9Sstevel@tonic-gate 	register char *fmt;	/* format string */
182*7c478bd9Sstevel@tonic-gate 	register int ch;	/* character from fmt */
183*7c478bd9Sstevel@tonic-gate 	register int n, m, n2;	/* handy integers (short term usage) */
184*7c478bd9Sstevel@tonic-gate 	register char *cp;	/* handy char pointer (short term usage) */
185*7c478bd9Sstevel@tonic-gate 	register struct sm_iov *iovp;/* for PRINT macro */
186*7c478bd9Sstevel@tonic-gate 	register int flags;	/* flags as above */
187*7c478bd9Sstevel@tonic-gate 	int ret;		/* return value accumulator */
188*7c478bd9Sstevel@tonic-gate 	int width;		/* width from format (%8d), or 0 */
189*7c478bd9Sstevel@tonic-gate 	int prec;		/* precision from format (%.3d), or -1 */
190*7c478bd9Sstevel@tonic-gate 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
191*7c478bd9Sstevel@tonic-gate 	wchar_t wc;
192*7c478bd9Sstevel@tonic-gate 	ULONGLONG_T _uquad;	/* integer arguments %[diouxX] */
193*7c478bd9Sstevel@tonic-gate 	enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
194*7c478bd9Sstevel@tonic-gate 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
195*7c478bd9Sstevel@tonic-gate 	int realsz;		/* field size expanded by dprec */
196*7c478bd9Sstevel@tonic-gate 	int size;		/* size of converted field or string */
197*7c478bd9Sstevel@tonic-gate 	char *xdigs="0123456789abcdef"; /* digits for [xX] conversion */
198*7c478bd9Sstevel@tonic-gate #define NIOV 8
199*7c478bd9Sstevel@tonic-gate 	struct sm_uio uio;	/* output information: summary */
200*7c478bd9Sstevel@tonic-gate 	struct sm_iov iov[NIOV];/* ... and individual io vectors */
201*7c478bd9Sstevel@tonic-gate 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
202*7c478bd9Sstevel@tonic-gate 	char ox[2];		/* space for 0x hex-prefix */
203*7c478bd9Sstevel@tonic-gate 	va_list *argtable;	/* args, built due to positional arg */
204*7c478bd9Sstevel@tonic-gate 	va_list statargtable[STATIC_ARG_TBL_SIZE];
205*7c478bd9Sstevel@tonic-gate 	int nextarg;		/* 1-based argument index */
206*7c478bd9Sstevel@tonic-gate 	va_list orgap;		/* original argument pointer */
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate 	/*
209*7c478bd9Sstevel@tonic-gate 	**  Choose PADSIZE to trade efficiency vs. size.  If larger printf
210*7c478bd9Sstevel@tonic-gate 	**  fields occur frequently, increase PADSIZE and make the initialisers
211*7c478bd9Sstevel@tonic-gate 	**  below longer.
212*7c478bd9Sstevel@tonic-gate 	*/
213*7c478bd9Sstevel@tonic-gate #define PADSIZE	16		/* pad chunk size */
214*7c478bd9Sstevel@tonic-gate 	static char blanks[PADSIZE] =
215*7c478bd9Sstevel@tonic-gate 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
216*7c478bd9Sstevel@tonic-gate 	static char zeroes[PADSIZE] =
217*7c478bd9Sstevel@tonic-gate 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
218*7c478bd9Sstevel@tonic-gate 
219*7c478bd9Sstevel@tonic-gate 	/*
220*7c478bd9Sstevel@tonic-gate 	**  BEWARE, these `goto error' on error, and PAD uses `n'.
221*7c478bd9Sstevel@tonic-gate 	*/
222*7c478bd9Sstevel@tonic-gate #define PRINT(ptr, len) do { \
223*7c478bd9Sstevel@tonic-gate 	iovp->iov_base = (ptr); \
224*7c478bd9Sstevel@tonic-gate 	iovp->iov_len = (len); \
225*7c478bd9Sstevel@tonic-gate 	uio.uio_resid += (len); \
226*7c478bd9Sstevel@tonic-gate 	iovp++; \
227*7c478bd9Sstevel@tonic-gate 	if (++uio.uio_iovcnt >= NIOV) \
228*7c478bd9Sstevel@tonic-gate 	{ \
229*7c478bd9Sstevel@tonic-gate 		if (sm_print(fp, timeout, &uio)) \
230*7c478bd9Sstevel@tonic-gate 			goto error; \
231*7c478bd9Sstevel@tonic-gate 		iovp = iov; \
232*7c478bd9Sstevel@tonic-gate 	} \
233*7c478bd9Sstevel@tonic-gate } while (0)
234*7c478bd9Sstevel@tonic-gate #define PAD(howmany, with) do \
235*7c478bd9Sstevel@tonic-gate { \
236*7c478bd9Sstevel@tonic-gate 	if ((n = (howmany)) > 0) \
237*7c478bd9Sstevel@tonic-gate 	{ \
238*7c478bd9Sstevel@tonic-gate 		while (n > PADSIZE) { \
239*7c478bd9Sstevel@tonic-gate 			PRINT(with, PADSIZE); \
240*7c478bd9Sstevel@tonic-gate 			n -= PADSIZE; \
241*7c478bd9Sstevel@tonic-gate 		} \
242*7c478bd9Sstevel@tonic-gate 		PRINT(with, n); \
243*7c478bd9Sstevel@tonic-gate 	} \
244*7c478bd9Sstevel@tonic-gate } while (0)
245*7c478bd9Sstevel@tonic-gate #define FLUSH() do \
246*7c478bd9Sstevel@tonic-gate { \
247*7c478bd9Sstevel@tonic-gate 	if (uio.uio_resid && sm_print(fp, timeout, &uio)) \
248*7c478bd9Sstevel@tonic-gate 		goto error; \
249*7c478bd9Sstevel@tonic-gate 	uio.uio_iovcnt = 0; \
250*7c478bd9Sstevel@tonic-gate 	iovp = iov; \
251*7c478bd9Sstevel@tonic-gate } while (0)
252*7c478bd9Sstevel@tonic-gate 
253*7c478bd9Sstevel@tonic-gate 	/*
254*7c478bd9Sstevel@tonic-gate 	**  To extend shorts properly, we need both signed and unsigned
255*7c478bd9Sstevel@tonic-gate 	**  argument extraction methods.
256*7c478bd9Sstevel@tonic-gate 	*/
257*7c478bd9Sstevel@tonic-gate #define SARG() \
258*7c478bd9Sstevel@tonic-gate 	(flags&QUADINT ? SM_VA_ARG(ap, LONGLONG_T) : \
259*7c478bd9Sstevel@tonic-gate 	    flags&LONGINT ? GETARG(long) : \
260*7c478bd9Sstevel@tonic-gate 	    flags&SHORTINT ? (long) (short) GETARG(int) : \
261*7c478bd9Sstevel@tonic-gate 	    (long) GETARG(int))
262*7c478bd9Sstevel@tonic-gate #define UARG() \
263*7c478bd9Sstevel@tonic-gate 	(flags&QUADINT ? SM_VA_ARG(ap, ULONGLONG_T) : \
264*7c478bd9Sstevel@tonic-gate 	    flags&LONGINT ? GETARG(unsigned long) : \
265*7c478bd9Sstevel@tonic-gate 	    flags&SHORTINT ? (unsigned long) (unsigned short) GETARG(int) : \
266*7c478bd9Sstevel@tonic-gate 	    (unsigned long) GETARG(unsigned int))
267*7c478bd9Sstevel@tonic-gate 
268*7c478bd9Sstevel@tonic-gate 	/*
269*7c478bd9Sstevel@tonic-gate 	**  Get * arguments, including the form *nn$.  Preserve the nextarg
270*7c478bd9Sstevel@tonic-gate 	**  that the argument can be gotten once the type is determined.
271*7c478bd9Sstevel@tonic-gate 	*/
272*7c478bd9Sstevel@tonic-gate #define GETASTER(val) \
273*7c478bd9Sstevel@tonic-gate 	n2 = 0; \
274*7c478bd9Sstevel@tonic-gate 	cp = fmt; \
275*7c478bd9Sstevel@tonic-gate 	while (is_digit(*cp)) \
276*7c478bd9Sstevel@tonic-gate 	{ \
277*7c478bd9Sstevel@tonic-gate 		n2 = 10 * n2 + to_digit(*cp); \
278*7c478bd9Sstevel@tonic-gate 		cp++; \
279*7c478bd9Sstevel@tonic-gate 	} \
280*7c478bd9Sstevel@tonic-gate 	if (*cp == '$') \
281*7c478bd9Sstevel@tonic-gate 	{ \
282*7c478bd9Sstevel@tonic-gate 		int hold = nextarg; \
283*7c478bd9Sstevel@tonic-gate 		if (argtable == NULL) \
284*7c478bd9Sstevel@tonic-gate 		{ \
285*7c478bd9Sstevel@tonic-gate 			argtable = statargtable; \
286*7c478bd9Sstevel@tonic-gate 			sm_find_arguments(fmt0, orgap, &argtable); \
287*7c478bd9Sstevel@tonic-gate 		} \
288*7c478bd9Sstevel@tonic-gate 		nextarg = n2; \
289*7c478bd9Sstevel@tonic-gate 		val = GETARG(int); \
290*7c478bd9Sstevel@tonic-gate 		nextarg = hold; \
291*7c478bd9Sstevel@tonic-gate 		fmt = ++cp; \
292*7c478bd9Sstevel@tonic-gate 	} \
293*7c478bd9Sstevel@tonic-gate 	else \
294*7c478bd9Sstevel@tonic-gate 	{ \
295*7c478bd9Sstevel@tonic-gate 		val = GETARG(int); \
296*7c478bd9Sstevel@tonic-gate 	}
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate /*
299*7c478bd9Sstevel@tonic-gate **  Get the argument indexed by nextarg.   If the argument table is
300*7c478bd9Sstevel@tonic-gate **  built, use it to get the argument.  If its not, get the next
301*7c478bd9Sstevel@tonic-gate **  argument (and arguments must be gotten sequentially).
302*7c478bd9Sstevel@tonic-gate */
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate #if SM_VA_STD
305*7c478bd9Sstevel@tonic-gate # define GETARG(type) \
306*7c478bd9Sstevel@tonic-gate 	(((argtable != NULL) ? (void) (ap = argtable[nextarg]) : (void) 0), \
307*7c478bd9Sstevel@tonic-gate 	 nextarg++, SM_VA_ARG(ap, type))
308*7c478bd9Sstevel@tonic-gate #else /* SM_VA_STD */
309*7c478bd9Sstevel@tonic-gate # define GETARG(type) \
310*7c478bd9Sstevel@tonic-gate 	((argtable != NULL) ? (*((type*)(argtable[nextarg++]))) : \
311*7c478bd9Sstevel@tonic-gate 			      (nextarg++, SM_VA_ARG(ap, type)))
312*7c478bd9Sstevel@tonic-gate #endif /* SM_VA_STD */
313*7c478bd9Sstevel@tonic-gate 
314*7c478bd9Sstevel@tonic-gate 	/* sorry, fprintf(read_only_file, "") returns SM_IO_EOF, not 0 */
315*7c478bd9Sstevel@tonic-gate 	if (cantwrite(fp))
316*7c478bd9Sstevel@tonic-gate 	{
317*7c478bd9Sstevel@tonic-gate 		errno = EBADF;
318*7c478bd9Sstevel@tonic-gate 		return SM_IO_EOF;
319*7c478bd9Sstevel@tonic-gate 	}
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
322*7c478bd9Sstevel@tonic-gate 	if ((fp->f_flags & (SMNBF|SMWR|SMRW)) == (SMNBF|SMWR) &&
323*7c478bd9Sstevel@tonic-gate 	    fp->f_file >= 0)
324*7c478bd9Sstevel@tonic-gate 		return sm_bprintf(fp, fmt0, ap);
325*7c478bd9Sstevel@tonic-gate 
326*7c478bd9Sstevel@tonic-gate 	fmt = (char *) fmt0;
327*7c478bd9Sstevel@tonic-gate 	argtable = NULL;
328*7c478bd9Sstevel@tonic-gate 	nextarg = 1;
329*7c478bd9Sstevel@tonic-gate 	SM_VA_COPY(orgap, ap);
330*7c478bd9Sstevel@tonic-gate 	uio.uio_iov = iovp = iov;
331*7c478bd9Sstevel@tonic-gate 	uio.uio_resid = 0;
332*7c478bd9Sstevel@tonic-gate 	uio.uio_iovcnt = 0;
333*7c478bd9Sstevel@tonic-gate 	ret = 0;
334*7c478bd9Sstevel@tonic-gate 
335*7c478bd9Sstevel@tonic-gate 	/* Scan the format for conversions (`%' character). */
336*7c478bd9Sstevel@tonic-gate 	for (;;)
337*7c478bd9Sstevel@tonic-gate 	{
338*7c478bd9Sstevel@tonic-gate 		cp = fmt;
339*7c478bd9Sstevel@tonic-gate 		n = 0;
340*7c478bd9Sstevel@tonic-gate 		while ((wc = *fmt) != '\0')
341*7c478bd9Sstevel@tonic-gate 		{
342*7c478bd9Sstevel@tonic-gate 			if (wc == '%')
343*7c478bd9Sstevel@tonic-gate 			{
344*7c478bd9Sstevel@tonic-gate 				n = 1;
345*7c478bd9Sstevel@tonic-gate 				break;
346*7c478bd9Sstevel@tonic-gate 			}
347*7c478bd9Sstevel@tonic-gate 			fmt++;
348*7c478bd9Sstevel@tonic-gate 		}
349*7c478bd9Sstevel@tonic-gate 		if ((m = fmt - cp) != 0)
350*7c478bd9Sstevel@tonic-gate 		{
351*7c478bd9Sstevel@tonic-gate 			PRINT(cp, m);
352*7c478bd9Sstevel@tonic-gate 			ret += m;
353*7c478bd9Sstevel@tonic-gate 		}
354*7c478bd9Sstevel@tonic-gate 		if (n <= 0)
355*7c478bd9Sstevel@tonic-gate 			goto done;
356*7c478bd9Sstevel@tonic-gate 		fmt++;		/* skip over '%' */
357*7c478bd9Sstevel@tonic-gate 
358*7c478bd9Sstevel@tonic-gate 		flags = 0;
359*7c478bd9Sstevel@tonic-gate 		dprec = 0;
360*7c478bd9Sstevel@tonic-gate 		width = 0;
361*7c478bd9Sstevel@tonic-gate 		prec = -1;
362*7c478bd9Sstevel@tonic-gate 		sign = '\0';
363*7c478bd9Sstevel@tonic-gate 
364*7c478bd9Sstevel@tonic-gate rflag:		ch = *fmt++;
365*7c478bd9Sstevel@tonic-gate reswitch:	switch (ch)
366*7c478bd9Sstevel@tonic-gate 		{
367*7c478bd9Sstevel@tonic-gate 		  case ' ':
368*7c478bd9Sstevel@tonic-gate 
369*7c478bd9Sstevel@tonic-gate 			/*
370*7c478bd9Sstevel@tonic-gate 			**  ``If the space and + flags both appear, the space
371*7c478bd9Sstevel@tonic-gate 			**  flag will be ignored.''
372*7c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
373*7c478bd9Sstevel@tonic-gate 			*/
374*7c478bd9Sstevel@tonic-gate 
375*7c478bd9Sstevel@tonic-gate 			if (!sign)
376*7c478bd9Sstevel@tonic-gate 				sign = ' ';
377*7c478bd9Sstevel@tonic-gate 			goto rflag;
378*7c478bd9Sstevel@tonic-gate 		  case '#':
379*7c478bd9Sstevel@tonic-gate 			flags |= ALT;
380*7c478bd9Sstevel@tonic-gate 			goto rflag;
381*7c478bd9Sstevel@tonic-gate 		  case '*':
382*7c478bd9Sstevel@tonic-gate 
383*7c478bd9Sstevel@tonic-gate 			/*
384*7c478bd9Sstevel@tonic-gate 			**  ``A negative field width argument is taken as a
385*7c478bd9Sstevel@tonic-gate 			**  - flag followed by a positive field width.''
386*7c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
387*7c478bd9Sstevel@tonic-gate 			**  They don't exclude field widths read from args.
388*7c478bd9Sstevel@tonic-gate 			*/
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate 			GETASTER(width);
391*7c478bd9Sstevel@tonic-gate 			if (width >= 0)
392*7c478bd9Sstevel@tonic-gate 				goto rflag;
393*7c478bd9Sstevel@tonic-gate 			width = -width;
394*7c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
395*7c478bd9Sstevel@tonic-gate 		  case '-':
396*7c478bd9Sstevel@tonic-gate 			flags |= LADJUST;
397*7c478bd9Sstevel@tonic-gate 			goto rflag;
398*7c478bd9Sstevel@tonic-gate 		  case '+':
399*7c478bd9Sstevel@tonic-gate 			sign = '+';
400*7c478bd9Sstevel@tonic-gate 			goto rflag;
401*7c478bd9Sstevel@tonic-gate 		  case '.':
402*7c478bd9Sstevel@tonic-gate 			if ((ch = *fmt++) == '*')
403*7c478bd9Sstevel@tonic-gate 			{
404*7c478bd9Sstevel@tonic-gate 				GETASTER(n);
405*7c478bd9Sstevel@tonic-gate 				prec = n < 0 ? -1 : n;
406*7c478bd9Sstevel@tonic-gate 				goto rflag;
407*7c478bd9Sstevel@tonic-gate 			}
408*7c478bd9Sstevel@tonic-gate 			n = 0;
409*7c478bd9Sstevel@tonic-gate 			while (is_digit(ch))
410*7c478bd9Sstevel@tonic-gate 			{
411*7c478bd9Sstevel@tonic-gate 				n = 10 * n + to_digit(ch);
412*7c478bd9Sstevel@tonic-gate 				ch = *fmt++;
413*7c478bd9Sstevel@tonic-gate 			}
414*7c478bd9Sstevel@tonic-gate 			if (ch == '$')
415*7c478bd9Sstevel@tonic-gate 			{
416*7c478bd9Sstevel@tonic-gate 				nextarg = n;
417*7c478bd9Sstevel@tonic-gate 				if (argtable == NULL)
418*7c478bd9Sstevel@tonic-gate 				{
419*7c478bd9Sstevel@tonic-gate 					argtable = statargtable;
420*7c478bd9Sstevel@tonic-gate 					sm_find_arguments(fmt0, orgap,
421*7c478bd9Sstevel@tonic-gate 					    &argtable);
422*7c478bd9Sstevel@tonic-gate 				}
423*7c478bd9Sstevel@tonic-gate 				goto rflag;
424*7c478bd9Sstevel@tonic-gate 			}
425*7c478bd9Sstevel@tonic-gate 			prec = n < 0 ? -1 : n;
426*7c478bd9Sstevel@tonic-gate 			goto reswitch;
427*7c478bd9Sstevel@tonic-gate 		  case '0':
428*7c478bd9Sstevel@tonic-gate 
429*7c478bd9Sstevel@tonic-gate 			/*
430*7c478bd9Sstevel@tonic-gate 			**  ``Note that 0 is taken as a flag, not as the
431*7c478bd9Sstevel@tonic-gate 			**  beginning of a field width.''
432*7c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
433*7c478bd9Sstevel@tonic-gate 			*/
434*7c478bd9Sstevel@tonic-gate 
435*7c478bd9Sstevel@tonic-gate 			flags |= ZEROPAD;
436*7c478bd9Sstevel@tonic-gate 			goto rflag;
437*7c478bd9Sstevel@tonic-gate 		  case '1': case '2': case '3': case '4':
438*7c478bd9Sstevel@tonic-gate 		  case '5': case '6': case '7': case '8': case '9':
439*7c478bd9Sstevel@tonic-gate 			n = 0;
440*7c478bd9Sstevel@tonic-gate 			do
441*7c478bd9Sstevel@tonic-gate 			{
442*7c478bd9Sstevel@tonic-gate 				n = 10 * n + to_digit(ch);
443*7c478bd9Sstevel@tonic-gate 				ch = *fmt++;
444*7c478bd9Sstevel@tonic-gate 			} while (is_digit(ch));
445*7c478bd9Sstevel@tonic-gate 			if (ch == '$')
446*7c478bd9Sstevel@tonic-gate 			{
447*7c478bd9Sstevel@tonic-gate 				nextarg = n;
448*7c478bd9Sstevel@tonic-gate 				if (argtable == NULL)
449*7c478bd9Sstevel@tonic-gate 				{
450*7c478bd9Sstevel@tonic-gate 					argtable = statargtable;
451*7c478bd9Sstevel@tonic-gate 					sm_find_arguments(fmt0, orgap,
452*7c478bd9Sstevel@tonic-gate 					    &argtable);
453*7c478bd9Sstevel@tonic-gate 				}
454*7c478bd9Sstevel@tonic-gate 				goto rflag;
455*7c478bd9Sstevel@tonic-gate 			}
456*7c478bd9Sstevel@tonic-gate 			width = n;
457*7c478bd9Sstevel@tonic-gate 			goto reswitch;
458*7c478bd9Sstevel@tonic-gate 		  case 'h':
459*7c478bd9Sstevel@tonic-gate 			flags |= SHORTINT;
460*7c478bd9Sstevel@tonic-gate 			goto rflag;
461*7c478bd9Sstevel@tonic-gate 		  case 'l':
462*7c478bd9Sstevel@tonic-gate 			if (*fmt == 'l')
463*7c478bd9Sstevel@tonic-gate 			{
464*7c478bd9Sstevel@tonic-gate 				fmt++;
465*7c478bd9Sstevel@tonic-gate 				flags |= QUADINT;
466*7c478bd9Sstevel@tonic-gate 			}
467*7c478bd9Sstevel@tonic-gate 			else
468*7c478bd9Sstevel@tonic-gate 			{
469*7c478bd9Sstevel@tonic-gate 				flags |= LONGINT;
470*7c478bd9Sstevel@tonic-gate 			}
471*7c478bd9Sstevel@tonic-gate 			goto rflag;
472*7c478bd9Sstevel@tonic-gate 		  case 'q':
473*7c478bd9Sstevel@tonic-gate 			flags |= QUADINT;
474*7c478bd9Sstevel@tonic-gate 			goto rflag;
475*7c478bd9Sstevel@tonic-gate 		  case 'c':
476*7c478bd9Sstevel@tonic-gate 			*(cp = buf) = GETARG(int);
477*7c478bd9Sstevel@tonic-gate 			size = 1;
478*7c478bd9Sstevel@tonic-gate 			sign = '\0';
479*7c478bd9Sstevel@tonic-gate 			break;
480*7c478bd9Sstevel@tonic-gate 		  case 'D':
481*7c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
482*7c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
483*7c478bd9Sstevel@tonic-gate 		  case 'd':
484*7c478bd9Sstevel@tonic-gate 		  case 'i':
485*7c478bd9Sstevel@tonic-gate 			_uquad = SARG();
486*7c478bd9Sstevel@tonic-gate 			if ((LONGLONG_T) _uquad < 0)
487*7c478bd9Sstevel@tonic-gate 			{
488*7c478bd9Sstevel@tonic-gate 				_uquad = -(LONGLONG_T) _uquad;
489*7c478bd9Sstevel@tonic-gate 				sign = '-';
490*7c478bd9Sstevel@tonic-gate 			}
491*7c478bd9Sstevel@tonic-gate 			base = DEC;
492*7c478bd9Sstevel@tonic-gate 			goto number;
493*7c478bd9Sstevel@tonic-gate 		  case 'e':
494*7c478bd9Sstevel@tonic-gate 		  case 'E':
495*7c478bd9Sstevel@tonic-gate 		  case 'f':
496*7c478bd9Sstevel@tonic-gate 		  case 'g':
497*7c478bd9Sstevel@tonic-gate 		  case 'G':
498*7c478bd9Sstevel@tonic-gate 			{
499*7c478bd9Sstevel@tonic-gate 				double val;
500*7c478bd9Sstevel@tonic-gate 				char *p;
501*7c478bd9Sstevel@tonic-gate 				char fmt[16];
502*7c478bd9Sstevel@tonic-gate 				char out[150];
503*7c478bd9Sstevel@tonic-gate 				size_t len;
504*7c478bd9Sstevel@tonic-gate 
505*7c478bd9Sstevel@tonic-gate 				/*
506*7c478bd9Sstevel@tonic-gate 				**  This code implements floating point output
507*7c478bd9Sstevel@tonic-gate 				**  in the most portable manner possible,
508*7c478bd9Sstevel@tonic-gate 				**  relying only on 'sprintf' as defined by
509*7c478bd9Sstevel@tonic-gate 				**  the 1989 ANSI C standard.
510*7c478bd9Sstevel@tonic-gate 				**  We silently cap width and precision
511*7c478bd9Sstevel@tonic-gate 				**  at 120, to avoid buffer overflow.
512*7c478bd9Sstevel@tonic-gate 				*/
513*7c478bd9Sstevel@tonic-gate 
514*7c478bd9Sstevel@tonic-gate 				val = GETARG(double);
515*7c478bd9Sstevel@tonic-gate 
516*7c478bd9Sstevel@tonic-gate 				p = fmt;
517*7c478bd9Sstevel@tonic-gate 				*p++ = '%';
518*7c478bd9Sstevel@tonic-gate 				if (sign)
519*7c478bd9Sstevel@tonic-gate 					*p++ = sign;
520*7c478bd9Sstevel@tonic-gate 				if (flags & ALT)
521*7c478bd9Sstevel@tonic-gate 					*p++ = '#';
522*7c478bd9Sstevel@tonic-gate 				if (flags & LADJUST)
523*7c478bd9Sstevel@tonic-gate 					*p++ = '-';
524*7c478bd9Sstevel@tonic-gate 				if (flags & ZEROPAD)
525*7c478bd9Sstevel@tonic-gate 					*p++ = '0';
526*7c478bd9Sstevel@tonic-gate 				*p++ = '*';
527*7c478bd9Sstevel@tonic-gate 				if (prec >= 0)
528*7c478bd9Sstevel@tonic-gate 				{
529*7c478bd9Sstevel@tonic-gate 					*p++ = '.';
530*7c478bd9Sstevel@tonic-gate 					*p++ = '*';
531*7c478bd9Sstevel@tonic-gate 				}
532*7c478bd9Sstevel@tonic-gate 				*p++ = ch;
533*7c478bd9Sstevel@tonic-gate 				*p = '\0';
534*7c478bd9Sstevel@tonic-gate 
535*7c478bd9Sstevel@tonic-gate 				if (width > 120)
536*7c478bd9Sstevel@tonic-gate 					width = 120;
537*7c478bd9Sstevel@tonic-gate 				if (prec > 120)
538*7c478bd9Sstevel@tonic-gate 					prec = 120;
539*7c478bd9Sstevel@tonic-gate 				if (prec >= 0)
540*7c478bd9Sstevel@tonic-gate 					sprintf(out, fmt, width, prec, val);
541*7c478bd9Sstevel@tonic-gate 				else
542*7c478bd9Sstevel@tonic-gate 					sprintf(out, fmt, width, val);
543*7c478bd9Sstevel@tonic-gate 				len = strlen(out);
544*7c478bd9Sstevel@tonic-gate 				PRINT(out, len);
545*7c478bd9Sstevel@tonic-gate 				FLUSH();
546*7c478bd9Sstevel@tonic-gate 				continue;
547*7c478bd9Sstevel@tonic-gate 			}
548*7c478bd9Sstevel@tonic-gate 		case 'n':
549*7c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
550*7c478bd9Sstevel@tonic-gate 				*GETARG(LONGLONG_T *) = ret;
551*7c478bd9Sstevel@tonic-gate 			else if (flags & LONGINT)
552*7c478bd9Sstevel@tonic-gate 				*GETARG(long *) = ret;
553*7c478bd9Sstevel@tonic-gate 			else if (flags & SHORTINT)
554*7c478bd9Sstevel@tonic-gate 				*GETARG(short *) = ret;
555*7c478bd9Sstevel@tonic-gate 			else
556*7c478bd9Sstevel@tonic-gate 				*GETARG(int *) = ret;
557*7c478bd9Sstevel@tonic-gate 			continue;	/* no output */
558*7c478bd9Sstevel@tonic-gate 		  case 'O':
559*7c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
560*7c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
561*7c478bd9Sstevel@tonic-gate 		  case 'o':
562*7c478bd9Sstevel@tonic-gate 			_uquad = UARG();
563*7c478bd9Sstevel@tonic-gate 			base = OCT;
564*7c478bd9Sstevel@tonic-gate 			goto nosign;
565*7c478bd9Sstevel@tonic-gate 		  case 'p':
566*7c478bd9Sstevel@tonic-gate 
567*7c478bd9Sstevel@tonic-gate 			/*
568*7c478bd9Sstevel@tonic-gate 			**  ``The argument shall be a pointer to void.  The
569*7c478bd9Sstevel@tonic-gate 			**  value of the pointer is converted to a sequence
570*7c478bd9Sstevel@tonic-gate 			**  of printable characters, in an implementation-
571*7c478bd9Sstevel@tonic-gate 			**  defined manner.''
572*7c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
573*7c478bd9Sstevel@tonic-gate 			*/
574*7c478bd9Sstevel@tonic-gate 
575*7c478bd9Sstevel@tonic-gate 			/* NOSTRICT */
576*7c478bd9Sstevel@tonic-gate 			{
577*7c478bd9Sstevel@tonic-gate 				union
578*7c478bd9Sstevel@tonic-gate 				{
579*7c478bd9Sstevel@tonic-gate 					void *p;
580*7c478bd9Sstevel@tonic-gate 					ULONGLONG_T ll;
581*7c478bd9Sstevel@tonic-gate 					unsigned long l;
582*7c478bd9Sstevel@tonic-gate 					unsigned i;
583*7c478bd9Sstevel@tonic-gate 				} u;
584*7c478bd9Sstevel@tonic-gate 				u.p = GETARG(void *);
585*7c478bd9Sstevel@tonic-gate 				if (sizeof(void *) == sizeof(ULONGLONG_T))
586*7c478bd9Sstevel@tonic-gate 					_uquad = u.ll;
587*7c478bd9Sstevel@tonic-gate 				else if (sizeof(void *) == sizeof(long))
588*7c478bd9Sstevel@tonic-gate 					_uquad = u.l;
589*7c478bd9Sstevel@tonic-gate 				else
590*7c478bd9Sstevel@tonic-gate 					_uquad = u.i;
591*7c478bd9Sstevel@tonic-gate 			}
592*7c478bd9Sstevel@tonic-gate 			base = HEX;
593*7c478bd9Sstevel@tonic-gate 			xdigs = "0123456789abcdef";
594*7c478bd9Sstevel@tonic-gate 			flags |= HEXPREFIX;
595*7c478bd9Sstevel@tonic-gate 			ch = 'x';
596*7c478bd9Sstevel@tonic-gate 			goto nosign;
597*7c478bd9Sstevel@tonic-gate 		  case 's':
598*7c478bd9Sstevel@tonic-gate 			if ((cp = GETARG(char *)) == NULL)
599*7c478bd9Sstevel@tonic-gate 				cp = "(null)";
600*7c478bd9Sstevel@tonic-gate 			if (prec >= 0)
601*7c478bd9Sstevel@tonic-gate 			{
602*7c478bd9Sstevel@tonic-gate 				/*
603*7c478bd9Sstevel@tonic-gate 				**  can't use strlen; can only look for the
604*7c478bd9Sstevel@tonic-gate 				**  NUL in the first `prec' characters, and
605*7c478bd9Sstevel@tonic-gate 				**  strlen() will go further.
606*7c478bd9Sstevel@tonic-gate 				*/
607*7c478bd9Sstevel@tonic-gate 
608*7c478bd9Sstevel@tonic-gate 				char *p = memchr(cp, 0, prec);
609*7c478bd9Sstevel@tonic-gate 
610*7c478bd9Sstevel@tonic-gate 				if (p != NULL)
611*7c478bd9Sstevel@tonic-gate 				{
612*7c478bd9Sstevel@tonic-gate 					size = p - cp;
613*7c478bd9Sstevel@tonic-gate 					if (size > prec)
614*7c478bd9Sstevel@tonic-gate 						size = prec;
615*7c478bd9Sstevel@tonic-gate 				}
616*7c478bd9Sstevel@tonic-gate 				else
617*7c478bd9Sstevel@tonic-gate 					size = prec;
618*7c478bd9Sstevel@tonic-gate 			}
619*7c478bd9Sstevel@tonic-gate 			else
620*7c478bd9Sstevel@tonic-gate 				size = strlen(cp);
621*7c478bd9Sstevel@tonic-gate 			sign = '\0';
622*7c478bd9Sstevel@tonic-gate 			break;
623*7c478bd9Sstevel@tonic-gate 		  case 'U':
624*7c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
625*7c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
626*7c478bd9Sstevel@tonic-gate 		  case 'u':
627*7c478bd9Sstevel@tonic-gate 			_uquad = UARG();
628*7c478bd9Sstevel@tonic-gate 			base = DEC;
629*7c478bd9Sstevel@tonic-gate 			goto nosign;
630*7c478bd9Sstevel@tonic-gate 		  case 'X':
631*7c478bd9Sstevel@tonic-gate 			xdigs = "0123456789ABCDEF";
632*7c478bd9Sstevel@tonic-gate 			goto hex;
633*7c478bd9Sstevel@tonic-gate 		  case 'x':
634*7c478bd9Sstevel@tonic-gate 			xdigs = "0123456789abcdef";
635*7c478bd9Sstevel@tonic-gate hex:			_uquad = UARG();
636*7c478bd9Sstevel@tonic-gate 			base = HEX;
637*7c478bd9Sstevel@tonic-gate 			/* leading 0x/X only if non-zero */
638*7c478bd9Sstevel@tonic-gate 			if (flags & ALT && _uquad != 0)
639*7c478bd9Sstevel@tonic-gate 				flags |= HEXPREFIX;
640*7c478bd9Sstevel@tonic-gate 
641*7c478bd9Sstevel@tonic-gate 			/* unsigned conversions */
642*7c478bd9Sstevel@tonic-gate nosign:			sign = '\0';
643*7c478bd9Sstevel@tonic-gate 
644*7c478bd9Sstevel@tonic-gate 			/*
645*7c478bd9Sstevel@tonic-gate 			**  ``... diouXx conversions ... if a precision is
646*7c478bd9Sstevel@tonic-gate 			**  specified, the 0 flag will be ignored.''
647*7c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
648*7c478bd9Sstevel@tonic-gate 			*/
649*7c478bd9Sstevel@tonic-gate 
650*7c478bd9Sstevel@tonic-gate number:			if ((dprec = prec) >= 0)
651*7c478bd9Sstevel@tonic-gate 				flags &= ~ZEROPAD;
652*7c478bd9Sstevel@tonic-gate 
653*7c478bd9Sstevel@tonic-gate 			/*
654*7c478bd9Sstevel@tonic-gate 			**  ``The result of converting a zero value with an
655*7c478bd9Sstevel@tonic-gate 			**  explicit precision of zero is no characters.''
656*7c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
657*7c478bd9Sstevel@tonic-gate 			*/
658*7c478bd9Sstevel@tonic-gate 
659*7c478bd9Sstevel@tonic-gate 			cp = buf + BUF;
660*7c478bd9Sstevel@tonic-gate 			if (_uquad != 0 || prec != 0)
661*7c478bd9Sstevel@tonic-gate 			{
662*7c478bd9Sstevel@tonic-gate 				/*
663*7c478bd9Sstevel@tonic-gate 				**  Unsigned mod is hard, and unsigned mod
664*7c478bd9Sstevel@tonic-gate 				**  by a constant is easier than that by
665*7c478bd9Sstevel@tonic-gate 				**  a variable; hence this switch.
666*7c478bd9Sstevel@tonic-gate 				*/
667*7c478bd9Sstevel@tonic-gate 
668*7c478bd9Sstevel@tonic-gate 				switch (base)
669*7c478bd9Sstevel@tonic-gate 				{
670*7c478bd9Sstevel@tonic-gate 				  case OCT:
671*7c478bd9Sstevel@tonic-gate 					do
672*7c478bd9Sstevel@tonic-gate 					{
673*7c478bd9Sstevel@tonic-gate 						*--cp = to_char(_uquad & 7);
674*7c478bd9Sstevel@tonic-gate 						_uquad >>= 3;
675*7c478bd9Sstevel@tonic-gate 					} while (_uquad);
676*7c478bd9Sstevel@tonic-gate 					/* handle octal leading 0 */
677*7c478bd9Sstevel@tonic-gate 					if (flags & ALT && *cp != '0')
678*7c478bd9Sstevel@tonic-gate 						*--cp = '0';
679*7c478bd9Sstevel@tonic-gate 					break;
680*7c478bd9Sstevel@tonic-gate 
681*7c478bd9Sstevel@tonic-gate 				  case DEC:
682*7c478bd9Sstevel@tonic-gate 					/* many numbers are 1 digit */
683*7c478bd9Sstevel@tonic-gate 					while (_uquad >= 10)
684*7c478bd9Sstevel@tonic-gate 					{
685*7c478bd9Sstevel@tonic-gate 						*--cp = to_char(_uquad % 10);
686*7c478bd9Sstevel@tonic-gate 						_uquad /= 10;
687*7c478bd9Sstevel@tonic-gate 					}
688*7c478bd9Sstevel@tonic-gate 					*--cp = to_char(_uquad);
689*7c478bd9Sstevel@tonic-gate 					break;
690*7c478bd9Sstevel@tonic-gate 
691*7c478bd9Sstevel@tonic-gate 				  case HEX:
692*7c478bd9Sstevel@tonic-gate 					do
693*7c478bd9Sstevel@tonic-gate 					{
694*7c478bd9Sstevel@tonic-gate 						*--cp = xdigs[_uquad & 15];
695*7c478bd9Sstevel@tonic-gate 						_uquad >>= 4;
696*7c478bd9Sstevel@tonic-gate 					} while (_uquad);
697*7c478bd9Sstevel@tonic-gate 					break;
698*7c478bd9Sstevel@tonic-gate 
699*7c478bd9Sstevel@tonic-gate 				  default:
700*7c478bd9Sstevel@tonic-gate 					cp = "bug in sm_io_vfprintf: bad base";
701*7c478bd9Sstevel@tonic-gate 					size = strlen(cp);
702*7c478bd9Sstevel@tonic-gate 					goto skipsize;
703*7c478bd9Sstevel@tonic-gate 				}
704*7c478bd9Sstevel@tonic-gate 			}
705*7c478bd9Sstevel@tonic-gate 			size = buf + BUF - cp;
706*7c478bd9Sstevel@tonic-gate 		  skipsize:
707*7c478bd9Sstevel@tonic-gate 			break;
708*7c478bd9Sstevel@tonic-gate 		  default:	/* "%?" prints ?, unless ? is NUL */
709*7c478bd9Sstevel@tonic-gate 			if (ch == '\0')
710*7c478bd9Sstevel@tonic-gate 				goto done;
711*7c478bd9Sstevel@tonic-gate 			/* pretend it was %c with argument ch */
712*7c478bd9Sstevel@tonic-gate 			cp = buf;
713*7c478bd9Sstevel@tonic-gate 			*cp = ch;
714*7c478bd9Sstevel@tonic-gate 			size = 1;
715*7c478bd9Sstevel@tonic-gate 			sign = '\0';
716*7c478bd9Sstevel@tonic-gate 			break;
717*7c478bd9Sstevel@tonic-gate 		}
718*7c478bd9Sstevel@tonic-gate 
719*7c478bd9Sstevel@tonic-gate 		/*
720*7c478bd9Sstevel@tonic-gate 		**  All reasonable formats wind up here.  At this point, `cp'
721*7c478bd9Sstevel@tonic-gate 		**  points to a string which (if not flags&LADJUST) should be
722*7c478bd9Sstevel@tonic-gate 		**  padded out to `width' places.  If flags&ZEROPAD, it should
723*7c478bd9Sstevel@tonic-gate 		**  first be prefixed by any sign or other prefix; otherwise,
724*7c478bd9Sstevel@tonic-gate 		**  it should be blank padded before the prefix is emitted.
725*7c478bd9Sstevel@tonic-gate 		**  After any left-hand padding and prefixing, emit zeroes
726*7c478bd9Sstevel@tonic-gate 		**  required by a decimal [diouxX] precision, then print the
727*7c478bd9Sstevel@tonic-gate 		**  string proper, then emit zeroes required by any leftover
728*7c478bd9Sstevel@tonic-gate 		**  floating precision; finally, if LADJUST, pad with blanks.
729*7c478bd9Sstevel@tonic-gate 		**
730*7c478bd9Sstevel@tonic-gate 		**  Compute actual size, so we know how much to pad.
731*7c478bd9Sstevel@tonic-gate 		**  size excludes decimal prec; realsz includes it.
732*7c478bd9Sstevel@tonic-gate 		*/
733*7c478bd9Sstevel@tonic-gate 
734*7c478bd9Sstevel@tonic-gate 		realsz = dprec > size ? dprec : size;
735*7c478bd9Sstevel@tonic-gate 		if (sign)
736*7c478bd9Sstevel@tonic-gate 			realsz++;
737*7c478bd9Sstevel@tonic-gate 		else if (flags & HEXPREFIX)
738*7c478bd9Sstevel@tonic-gate 			realsz+= 2;
739*7c478bd9Sstevel@tonic-gate 
740*7c478bd9Sstevel@tonic-gate 		/* right-adjusting blank padding */
741*7c478bd9Sstevel@tonic-gate 		if ((flags & (LADJUST|ZEROPAD)) == 0)
742*7c478bd9Sstevel@tonic-gate 			PAD(width - realsz, blanks);
743*7c478bd9Sstevel@tonic-gate 
744*7c478bd9Sstevel@tonic-gate 		/* prefix */
745*7c478bd9Sstevel@tonic-gate 		if (sign)
746*7c478bd9Sstevel@tonic-gate 		{
747*7c478bd9Sstevel@tonic-gate 			PRINT(&sign, 1);
748*7c478bd9Sstevel@tonic-gate 		}
749*7c478bd9Sstevel@tonic-gate 		else if (flags & HEXPREFIX)
750*7c478bd9Sstevel@tonic-gate 		{
751*7c478bd9Sstevel@tonic-gate 			ox[0] = '0';
752*7c478bd9Sstevel@tonic-gate 			ox[1] = ch;
753*7c478bd9Sstevel@tonic-gate 			PRINT(ox, 2);
754*7c478bd9Sstevel@tonic-gate 		}
755*7c478bd9Sstevel@tonic-gate 
756*7c478bd9Sstevel@tonic-gate 		/* right-adjusting zero padding */
757*7c478bd9Sstevel@tonic-gate 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
758*7c478bd9Sstevel@tonic-gate 			PAD(width - realsz, zeroes);
759*7c478bd9Sstevel@tonic-gate 
760*7c478bd9Sstevel@tonic-gate 		/* leading zeroes from decimal precision */
761*7c478bd9Sstevel@tonic-gate 		PAD(dprec - size, zeroes);
762*7c478bd9Sstevel@tonic-gate 
763*7c478bd9Sstevel@tonic-gate 		/* the string or number proper */
764*7c478bd9Sstevel@tonic-gate 		PRINT(cp, size);
765*7c478bd9Sstevel@tonic-gate 		/* left-adjusting padding (always blank) */
766*7c478bd9Sstevel@tonic-gate 		if (flags & LADJUST)
767*7c478bd9Sstevel@tonic-gate 			PAD(width - realsz, blanks);
768*7c478bd9Sstevel@tonic-gate 
769*7c478bd9Sstevel@tonic-gate 		/* finally, adjust ret */
770*7c478bd9Sstevel@tonic-gate 		ret += width > realsz ? width : realsz;
771*7c478bd9Sstevel@tonic-gate 
772*7c478bd9Sstevel@tonic-gate 		FLUSH();	/* copy out the I/O vectors */
773*7c478bd9Sstevel@tonic-gate 	}
774*7c478bd9Sstevel@tonic-gate done:
775*7c478bd9Sstevel@tonic-gate 	FLUSH();
776*7c478bd9Sstevel@tonic-gate error:
777*7c478bd9Sstevel@tonic-gate 	if ((argtable != NULL) && (argtable != statargtable))
778*7c478bd9Sstevel@tonic-gate 		sm_free(argtable);
779*7c478bd9Sstevel@tonic-gate 	return sm_error(fp) ? SM_IO_EOF : ret;
780*7c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
781*7c478bd9Sstevel@tonic-gate }
782*7c478bd9Sstevel@tonic-gate 
783*7c478bd9Sstevel@tonic-gate /* Type ids for argument type table. */
784*7c478bd9Sstevel@tonic-gate #define T_UNUSED	0
785*7c478bd9Sstevel@tonic-gate #define T_SHORT		1
786*7c478bd9Sstevel@tonic-gate #define T_U_SHORT	2
787*7c478bd9Sstevel@tonic-gate #define TP_SHORT	3
788*7c478bd9Sstevel@tonic-gate #define T_INT		4
789*7c478bd9Sstevel@tonic-gate #define T_U_INT		5
790*7c478bd9Sstevel@tonic-gate #define TP_INT		6
791*7c478bd9Sstevel@tonic-gate #define T_LONG		7
792*7c478bd9Sstevel@tonic-gate #define T_U_LONG	8
793*7c478bd9Sstevel@tonic-gate #define TP_LONG		9
794*7c478bd9Sstevel@tonic-gate #define T_QUAD		10
795*7c478bd9Sstevel@tonic-gate #define T_U_QUAD	11
796*7c478bd9Sstevel@tonic-gate #define TP_QUAD		12
797*7c478bd9Sstevel@tonic-gate #define T_DOUBLE	13
798*7c478bd9Sstevel@tonic-gate #define TP_CHAR		15
799*7c478bd9Sstevel@tonic-gate #define TP_VOID		16
800*7c478bd9Sstevel@tonic-gate 
801*7c478bd9Sstevel@tonic-gate /*
802*7c478bd9Sstevel@tonic-gate **  SM_FIND_ARGUMENTS -- find all args when a positional parameter is found.
803*7c478bd9Sstevel@tonic-gate **
804*7c478bd9Sstevel@tonic-gate **  Find all arguments when a positional parameter is encountered.  Returns a
805*7c478bd9Sstevel@tonic-gate **  table, indexed by argument number, of pointers to each arguments.  The
806*7c478bd9Sstevel@tonic-gate **  initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
807*7c478bd9Sstevel@tonic-gate **  It will be replaced with a malloc-ed one if it overflows.
808*7c478bd9Sstevel@tonic-gate **
809*7c478bd9Sstevel@tonic-gate **	Parameters:
810*7c478bd9Sstevel@tonic-gate **		fmt0 -- formating directives
811*7c478bd9Sstevel@tonic-gate **		ap -- vector list of data unit for formating consumption
812*7c478bd9Sstevel@tonic-gate **		argtable -- an indexable table (returned) of 'ap'
813*7c478bd9Sstevel@tonic-gate **
814*7c478bd9Sstevel@tonic-gate **	Results:
815*7c478bd9Sstevel@tonic-gate **		none.
816*7c478bd9Sstevel@tonic-gate */
817*7c478bd9Sstevel@tonic-gate 
818*7c478bd9Sstevel@tonic-gate static void
819*7c478bd9Sstevel@tonic-gate sm_find_arguments(fmt0, ap, argtable)
820*7c478bd9Sstevel@tonic-gate 	const char *fmt0;
821*7c478bd9Sstevel@tonic-gate 	SM_VA_LOCAL_DECL
822*7c478bd9Sstevel@tonic-gate 	va_list **argtable;
823*7c478bd9Sstevel@tonic-gate {
824*7c478bd9Sstevel@tonic-gate 	register char *fmt;	/* format string */
825*7c478bd9Sstevel@tonic-gate 	register int ch;	/* character from fmt */
826*7c478bd9Sstevel@tonic-gate 	register int n, n2;	/* handy integer (short term usage) */
827*7c478bd9Sstevel@tonic-gate 	register char *cp;	/* handy char pointer (short term usage) */
828*7c478bd9Sstevel@tonic-gate 	register int flags;	/* flags as above */
829*7c478bd9Sstevel@tonic-gate 	unsigned char *typetable; /* table of types */
830*7c478bd9Sstevel@tonic-gate 	unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
831*7c478bd9Sstevel@tonic-gate 	int tablesize;		/* current size of type table */
832*7c478bd9Sstevel@tonic-gate 	int tablemax;		/* largest used index in table */
833*7c478bd9Sstevel@tonic-gate 	int nextarg;		/* 1-based argument index */
834*7c478bd9Sstevel@tonic-gate 
835*7c478bd9Sstevel@tonic-gate 	/* Add an argument type to the table, expanding if necessary. */
836*7c478bd9Sstevel@tonic-gate #define ADDTYPE(type) \
837*7c478bd9Sstevel@tonic-gate 	((nextarg >= tablesize) ? \
838*7c478bd9Sstevel@tonic-gate 		(sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \
839*7c478bd9Sstevel@tonic-gate 	typetable[nextarg++] = type, \
840*7c478bd9Sstevel@tonic-gate 	(nextarg > tablemax) ? tablemax = nextarg : 0)
841*7c478bd9Sstevel@tonic-gate 
842*7c478bd9Sstevel@tonic-gate #define ADDSARG() \
843*7c478bd9Sstevel@tonic-gate 	((flags & LONGINT) ? ADDTYPE(T_LONG) : \
844*7c478bd9Sstevel@tonic-gate 		((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
845*7c478bd9Sstevel@tonic-gate 
846*7c478bd9Sstevel@tonic-gate #define ADDUARG() \
847*7c478bd9Sstevel@tonic-gate 	((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \
848*7c478bd9Sstevel@tonic-gate 		((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
849*7c478bd9Sstevel@tonic-gate 
850*7c478bd9Sstevel@tonic-gate 	/* Add * arguments to the type array. */
851*7c478bd9Sstevel@tonic-gate #define ADDASTER() \
852*7c478bd9Sstevel@tonic-gate 	n2 = 0; \
853*7c478bd9Sstevel@tonic-gate 	cp = fmt; \
854*7c478bd9Sstevel@tonic-gate 	while (is_digit(*cp)) \
855*7c478bd9Sstevel@tonic-gate 	{ \
856*7c478bd9Sstevel@tonic-gate 		n2 = 10 * n2 + to_digit(*cp); \
857*7c478bd9Sstevel@tonic-gate 		cp++; \
858*7c478bd9Sstevel@tonic-gate 	} \
859*7c478bd9Sstevel@tonic-gate 	if (*cp == '$') \
860*7c478bd9Sstevel@tonic-gate 	{ \
861*7c478bd9Sstevel@tonic-gate 		int hold = nextarg; \
862*7c478bd9Sstevel@tonic-gate 		nextarg = n2; \
863*7c478bd9Sstevel@tonic-gate 		ADDTYPE (T_INT); \
864*7c478bd9Sstevel@tonic-gate 		nextarg = hold; \
865*7c478bd9Sstevel@tonic-gate 		fmt = ++cp; \
866*7c478bd9Sstevel@tonic-gate 	} \
867*7c478bd9Sstevel@tonic-gate 	else \
868*7c478bd9Sstevel@tonic-gate 	{ \
869*7c478bd9Sstevel@tonic-gate 		ADDTYPE (T_INT); \
870*7c478bd9Sstevel@tonic-gate 	}
871*7c478bd9Sstevel@tonic-gate 	fmt = (char *) fmt0;
872*7c478bd9Sstevel@tonic-gate 	typetable = stattypetable;
873*7c478bd9Sstevel@tonic-gate 	tablesize = STATIC_ARG_TBL_SIZE;
874*7c478bd9Sstevel@tonic-gate 	tablemax = 0;
875*7c478bd9Sstevel@tonic-gate 	nextarg = 1;
876*7c478bd9Sstevel@tonic-gate 	(void) memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
877*7c478bd9Sstevel@tonic-gate 
878*7c478bd9Sstevel@tonic-gate 	/* Scan the format for conversions (`%' character). */
879*7c478bd9Sstevel@tonic-gate 	for (;;)
880*7c478bd9Sstevel@tonic-gate 	{
881*7c478bd9Sstevel@tonic-gate 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
882*7c478bd9Sstevel@tonic-gate 			/* void */;
883*7c478bd9Sstevel@tonic-gate 		if (ch == '\0')
884*7c478bd9Sstevel@tonic-gate 			goto done;
885*7c478bd9Sstevel@tonic-gate 		fmt++;		/* skip over '%' */
886*7c478bd9Sstevel@tonic-gate 
887*7c478bd9Sstevel@tonic-gate 		flags = 0;
888*7c478bd9Sstevel@tonic-gate 
889*7c478bd9Sstevel@tonic-gate rflag:		ch = *fmt++;
890*7c478bd9Sstevel@tonic-gate reswitch:	switch (ch)
891*7c478bd9Sstevel@tonic-gate 		{
892*7c478bd9Sstevel@tonic-gate 		  case ' ':
893*7c478bd9Sstevel@tonic-gate 		  case '#':
894*7c478bd9Sstevel@tonic-gate 			goto rflag;
895*7c478bd9Sstevel@tonic-gate 		  case '*':
896*7c478bd9Sstevel@tonic-gate 			ADDASTER();
897*7c478bd9Sstevel@tonic-gate 			goto rflag;
898*7c478bd9Sstevel@tonic-gate 		  case '-':
899*7c478bd9Sstevel@tonic-gate 		  case '+':
900*7c478bd9Sstevel@tonic-gate 			goto rflag;
901*7c478bd9Sstevel@tonic-gate 		  case '.':
902*7c478bd9Sstevel@tonic-gate 			if ((ch = *fmt++) == '*')
903*7c478bd9Sstevel@tonic-gate 			{
904*7c478bd9Sstevel@tonic-gate 				ADDASTER();
905*7c478bd9Sstevel@tonic-gate 				goto rflag;
906*7c478bd9Sstevel@tonic-gate 			}
907*7c478bd9Sstevel@tonic-gate 			while (is_digit(ch))
908*7c478bd9Sstevel@tonic-gate 			{
909*7c478bd9Sstevel@tonic-gate 				ch = *fmt++;
910*7c478bd9Sstevel@tonic-gate 			}
911*7c478bd9Sstevel@tonic-gate 			goto reswitch;
912*7c478bd9Sstevel@tonic-gate 		  case '0':
913*7c478bd9Sstevel@tonic-gate 			goto rflag;
914*7c478bd9Sstevel@tonic-gate 		  case '1': case '2': case '3': case '4':
915*7c478bd9Sstevel@tonic-gate 		  case '5': case '6': case '7': case '8': case '9':
916*7c478bd9Sstevel@tonic-gate 			n = 0;
917*7c478bd9Sstevel@tonic-gate 			do
918*7c478bd9Sstevel@tonic-gate 			{
919*7c478bd9Sstevel@tonic-gate 				n = 10 * n + to_digit(ch);
920*7c478bd9Sstevel@tonic-gate 				ch = *fmt++;
921*7c478bd9Sstevel@tonic-gate 			} while (is_digit(ch));
922*7c478bd9Sstevel@tonic-gate 			if (ch == '$')
923*7c478bd9Sstevel@tonic-gate 			{
924*7c478bd9Sstevel@tonic-gate 				nextarg = n;
925*7c478bd9Sstevel@tonic-gate 				goto rflag;
926*7c478bd9Sstevel@tonic-gate 			}
927*7c478bd9Sstevel@tonic-gate 			goto reswitch;
928*7c478bd9Sstevel@tonic-gate 		  case 'h':
929*7c478bd9Sstevel@tonic-gate 			flags |= SHORTINT;
930*7c478bd9Sstevel@tonic-gate 			goto rflag;
931*7c478bd9Sstevel@tonic-gate 		  case 'l':
932*7c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
933*7c478bd9Sstevel@tonic-gate 			goto rflag;
934*7c478bd9Sstevel@tonic-gate 		  case 'q':
935*7c478bd9Sstevel@tonic-gate 			flags |= QUADINT;
936*7c478bd9Sstevel@tonic-gate 			goto rflag;
937*7c478bd9Sstevel@tonic-gate 		  case 'c':
938*7c478bd9Sstevel@tonic-gate 			ADDTYPE(T_INT);
939*7c478bd9Sstevel@tonic-gate 			break;
940*7c478bd9Sstevel@tonic-gate 		  case 'D':
941*7c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
942*7c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
943*7c478bd9Sstevel@tonic-gate 		  case 'd':
944*7c478bd9Sstevel@tonic-gate 		  case 'i':
945*7c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
946*7c478bd9Sstevel@tonic-gate 			{
947*7c478bd9Sstevel@tonic-gate 				ADDTYPE(T_QUAD);
948*7c478bd9Sstevel@tonic-gate 			}
949*7c478bd9Sstevel@tonic-gate 			else
950*7c478bd9Sstevel@tonic-gate 			{
951*7c478bd9Sstevel@tonic-gate 				ADDSARG();
952*7c478bd9Sstevel@tonic-gate 			}
953*7c478bd9Sstevel@tonic-gate 			break;
954*7c478bd9Sstevel@tonic-gate 		  case 'e':
955*7c478bd9Sstevel@tonic-gate 		  case 'E':
956*7c478bd9Sstevel@tonic-gate 		  case 'f':
957*7c478bd9Sstevel@tonic-gate 		  case 'g':
958*7c478bd9Sstevel@tonic-gate 		  case 'G':
959*7c478bd9Sstevel@tonic-gate 			ADDTYPE(T_DOUBLE);
960*7c478bd9Sstevel@tonic-gate 			break;
961*7c478bd9Sstevel@tonic-gate 		  case 'n':
962*7c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
963*7c478bd9Sstevel@tonic-gate 				ADDTYPE(TP_QUAD);
964*7c478bd9Sstevel@tonic-gate 			else if (flags & LONGINT)
965*7c478bd9Sstevel@tonic-gate 				ADDTYPE(TP_LONG);
966*7c478bd9Sstevel@tonic-gate 			else if (flags & SHORTINT)
967*7c478bd9Sstevel@tonic-gate 				ADDTYPE(TP_SHORT);
968*7c478bd9Sstevel@tonic-gate 			else
969*7c478bd9Sstevel@tonic-gate 				ADDTYPE(TP_INT);
970*7c478bd9Sstevel@tonic-gate 			continue;	/* no output */
971*7c478bd9Sstevel@tonic-gate 		  case 'O':
972*7c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
973*7c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
974*7c478bd9Sstevel@tonic-gate 		  case 'o':
975*7c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
976*7c478bd9Sstevel@tonic-gate 				ADDTYPE(T_U_QUAD);
977*7c478bd9Sstevel@tonic-gate 			else
978*7c478bd9Sstevel@tonic-gate 				ADDUARG();
979*7c478bd9Sstevel@tonic-gate 			break;
980*7c478bd9Sstevel@tonic-gate 		  case 'p':
981*7c478bd9Sstevel@tonic-gate 			ADDTYPE(TP_VOID);
982*7c478bd9Sstevel@tonic-gate 			break;
983*7c478bd9Sstevel@tonic-gate 		  case 's':
984*7c478bd9Sstevel@tonic-gate 			ADDTYPE(TP_CHAR);
985*7c478bd9Sstevel@tonic-gate 			break;
986*7c478bd9Sstevel@tonic-gate 		  case 'U':
987*7c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
988*7c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
989*7c478bd9Sstevel@tonic-gate 		  case 'u':
990*7c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
991*7c478bd9Sstevel@tonic-gate 				ADDTYPE(T_U_QUAD);
992*7c478bd9Sstevel@tonic-gate 			else
993*7c478bd9Sstevel@tonic-gate 				ADDUARG();
994*7c478bd9Sstevel@tonic-gate 			break;
995*7c478bd9Sstevel@tonic-gate 		  case 'X':
996*7c478bd9Sstevel@tonic-gate 		  case 'x':
997*7c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
998*7c478bd9Sstevel@tonic-gate 				ADDTYPE(T_U_QUAD);
999*7c478bd9Sstevel@tonic-gate 			else
1000*7c478bd9Sstevel@tonic-gate 				ADDUARG();
1001*7c478bd9Sstevel@tonic-gate 			break;
1002*7c478bd9Sstevel@tonic-gate 		  default:	/* "%?" prints ?, unless ? is NUL */
1003*7c478bd9Sstevel@tonic-gate 			if (ch == '\0')
1004*7c478bd9Sstevel@tonic-gate 				goto done;
1005*7c478bd9Sstevel@tonic-gate 			break;
1006*7c478bd9Sstevel@tonic-gate 		}
1007*7c478bd9Sstevel@tonic-gate 	}
1008*7c478bd9Sstevel@tonic-gate done:
1009*7c478bd9Sstevel@tonic-gate 	/* Build the argument table. */
1010*7c478bd9Sstevel@tonic-gate 	if (tablemax >= STATIC_ARG_TBL_SIZE)
1011*7c478bd9Sstevel@tonic-gate 	{
1012*7c478bd9Sstevel@tonic-gate 		*argtable = (va_list *)
1013*7c478bd9Sstevel@tonic-gate 		    sm_malloc(sizeof(va_list) * (tablemax + 1));
1014*7c478bd9Sstevel@tonic-gate 	}
1015*7c478bd9Sstevel@tonic-gate 
1016*7c478bd9Sstevel@tonic-gate 	for (n = 1; n <= tablemax; n++)
1017*7c478bd9Sstevel@tonic-gate 	{
1018*7c478bd9Sstevel@tonic-gate 		SM_VA_COPY((*argtable)[n], ap);
1019*7c478bd9Sstevel@tonic-gate 		switch (typetable [n])
1020*7c478bd9Sstevel@tonic-gate 		{
1021*7c478bd9Sstevel@tonic-gate 		  case T_UNUSED:
1022*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, int);
1023*7c478bd9Sstevel@tonic-gate 			break;
1024*7c478bd9Sstevel@tonic-gate 		  case T_SHORT:
1025*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, int);
1026*7c478bd9Sstevel@tonic-gate 			break;
1027*7c478bd9Sstevel@tonic-gate 		  case T_U_SHORT:
1028*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, int);
1029*7c478bd9Sstevel@tonic-gate 			break;
1030*7c478bd9Sstevel@tonic-gate 		  case TP_SHORT:
1031*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, short *);
1032*7c478bd9Sstevel@tonic-gate 			break;
1033*7c478bd9Sstevel@tonic-gate 		  case T_INT:
1034*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, int);
1035*7c478bd9Sstevel@tonic-gate 			break;
1036*7c478bd9Sstevel@tonic-gate 		  case T_U_INT:
1037*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, unsigned int);
1038*7c478bd9Sstevel@tonic-gate 			break;
1039*7c478bd9Sstevel@tonic-gate 		  case TP_INT:
1040*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, int *);
1041*7c478bd9Sstevel@tonic-gate 			break;
1042*7c478bd9Sstevel@tonic-gate 		  case T_LONG:
1043*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, long);
1044*7c478bd9Sstevel@tonic-gate 			break;
1045*7c478bd9Sstevel@tonic-gate 		  case T_U_LONG:
1046*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, unsigned long);
1047*7c478bd9Sstevel@tonic-gate 			break;
1048*7c478bd9Sstevel@tonic-gate 		  case TP_LONG:
1049*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, long *);
1050*7c478bd9Sstevel@tonic-gate 			break;
1051*7c478bd9Sstevel@tonic-gate 		  case T_QUAD:
1052*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, LONGLONG_T);
1053*7c478bd9Sstevel@tonic-gate 			break;
1054*7c478bd9Sstevel@tonic-gate 		  case T_U_QUAD:
1055*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, ULONGLONG_T);
1056*7c478bd9Sstevel@tonic-gate 			break;
1057*7c478bd9Sstevel@tonic-gate 		  case TP_QUAD:
1058*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, LONGLONG_T *);
1059*7c478bd9Sstevel@tonic-gate 			break;
1060*7c478bd9Sstevel@tonic-gate 		  case T_DOUBLE:
1061*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, double);
1062*7c478bd9Sstevel@tonic-gate 			break;
1063*7c478bd9Sstevel@tonic-gate 		  case TP_CHAR:
1064*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, char *);
1065*7c478bd9Sstevel@tonic-gate 			break;
1066*7c478bd9Sstevel@tonic-gate 		  case TP_VOID:
1067*7c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, void *);
1068*7c478bd9Sstevel@tonic-gate 			break;
1069*7c478bd9Sstevel@tonic-gate 		}
1070*7c478bd9Sstevel@tonic-gate 	}
1071*7c478bd9Sstevel@tonic-gate 
1072*7c478bd9Sstevel@tonic-gate 	if ((typetable != NULL) && (typetable != stattypetable))
1073*7c478bd9Sstevel@tonic-gate 		sm_free(typetable);
1074*7c478bd9Sstevel@tonic-gate }
1075*7c478bd9Sstevel@tonic-gate 
1076*7c478bd9Sstevel@tonic-gate /*
1077*7c478bd9Sstevel@tonic-gate **  SM_GROW_TYPE_TABLE -- Increase the size of the type table.
1078*7c478bd9Sstevel@tonic-gate **
1079*7c478bd9Sstevel@tonic-gate **	Parameters:
1080*7c478bd9Sstevel@tonic-gate **		tabletype -- type of table to grow
1081*7c478bd9Sstevel@tonic-gate **		tablesize -- requested new table size
1082*7c478bd9Sstevel@tonic-gate **
1083*7c478bd9Sstevel@tonic-gate **	Results:
1084*7c478bd9Sstevel@tonic-gate **		Raises an exception if can't allocate memory.
1085*7c478bd9Sstevel@tonic-gate */
1086*7c478bd9Sstevel@tonic-gate 
1087*7c478bd9Sstevel@tonic-gate static void
1088*7c478bd9Sstevel@tonic-gate sm_grow_type_table_x(typetable, tablesize)
1089*7c478bd9Sstevel@tonic-gate 	unsigned char **typetable;
1090*7c478bd9Sstevel@tonic-gate 	int *tablesize;
1091*7c478bd9Sstevel@tonic-gate {
1092*7c478bd9Sstevel@tonic-gate 	unsigned char *oldtable = *typetable;
1093*7c478bd9Sstevel@tonic-gate 	int newsize = *tablesize * 2;
1094*7c478bd9Sstevel@tonic-gate 
1095*7c478bd9Sstevel@tonic-gate 	if (*tablesize == STATIC_ARG_TBL_SIZE)
1096*7c478bd9Sstevel@tonic-gate 	{
1097*7c478bd9Sstevel@tonic-gate 		*typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char)
1098*7c478bd9Sstevel@tonic-gate 							   * newsize);
1099*7c478bd9Sstevel@tonic-gate 		(void) memmove(*typetable, oldtable, *tablesize);
1100*7c478bd9Sstevel@tonic-gate 	}
1101*7c478bd9Sstevel@tonic-gate 	else
1102*7c478bd9Sstevel@tonic-gate 	{
1103*7c478bd9Sstevel@tonic-gate 		*typetable = (unsigned char *) sm_realloc_x(typetable,
1104*7c478bd9Sstevel@tonic-gate 					sizeof(unsigned char) * newsize);
1105*7c478bd9Sstevel@tonic-gate 	}
1106*7c478bd9Sstevel@tonic-gate 	(void) memset(&typetable [*tablesize], T_UNUSED,
1107*7c478bd9Sstevel@tonic-gate 		       (newsize - *tablesize));
1108*7c478bd9Sstevel@tonic-gate 
1109*7c478bd9Sstevel@tonic-gate 	*tablesize = newsize;
1110*7c478bd9Sstevel@tonic-gate }
1111