17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright (c) 2000-2001, 2004 Sendmail, Inc. and its suppliers.
37c478bd9Sstevel@tonic-gate  *      All rights reserved.
47c478bd9Sstevel@tonic-gate  * Copyright (c) 1990
57c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
67c478bd9Sstevel@tonic-gate  *
77c478bd9Sstevel@tonic-gate  * This code is derived from software contributed to Berkeley by
87c478bd9Sstevel@tonic-gate  * Chris Torek.
97c478bd9Sstevel@tonic-gate  *
107c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
117c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
127c478bd9Sstevel@tonic-gate  * the sendmail distribution.
137c478bd9Sstevel@tonic-gate  */
147c478bd9Sstevel@tonic-gate 
157c478bd9Sstevel@tonic-gate #include <sm/gen.h>
16*49218d4fSjbeck SM_IDSTR(id, "@(#)$Id: vfprintf.c,v 1.54 2005/05/16 03:52:00 ca Exp $")
177c478bd9Sstevel@tonic-gate 
187c478bd9Sstevel@tonic-gate /*
197c478bd9Sstevel@tonic-gate **  Overall:
207c478bd9Sstevel@tonic-gate **  Actual printing innards.
217c478bd9Sstevel@tonic-gate **  This code is large and complicated...
227c478bd9Sstevel@tonic-gate */
237c478bd9Sstevel@tonic-gate 
247c478bd9Sstevel@tonic-gate #include <sys/types.h>
257c478bd9Sstevel@tonic-gate #include <stdlib.h>
267c478bd9Sstevel@tonic-gate #include <string.h>
277c478bd9Sstevel@tonic-gate #include <errno.h>
287c478bd9Sstevel@tonic-gate #include <sm/config.h>
297c478bd9Sstevel@tonic-gate #include <sm/varargs.h>
307c478bd9Sstevel@tonic-gate #include <sm/io.h>
317c478bd9Sstevel@tonic-gate #include <sm/heap.h>
327c478bd9Sstevel@tonic-gate #include <sm/conf.h>
337c478bd9Sstevel@tonic-gate #include "local.h"
347c478bd9Sstevel@tonic-gate #include "fvwrite.h"
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate static int	sm_bprintf __P((SM_FILE_T *, const char *, va_list));
377c478bd9Sstevel@tonic-gate static void	sm_find_arguments __P((const char *, va_list , va_list **));
387c478bd9Sstevel@tonic-gate static void	sm_grow_type_table_x __P((unsigned char **, int *));
397c478bd9Sstevel@tonic-gate static int	sm_print __P((SM_FILE_T *, int, struct sm_uio *));
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate /*
427c478bd9Sstevel@tonic-gate **  SM_PRINT -- print/flush to the file
437c478bd9Sstevel@tonic-gate **
447c478bd9Sstevel@tonic-gate **  Flush out all the vectors defined by the given uio,
457c478bd9Sstevel@tonic-gate **  then reset it so that it can be reused.
467c478bd9Sstevel@tonic-gate **
477c478bd9Sstevel@tonic-gate **	Parameters:
487c478bd9Sstevel@tonic-gate **		fp -- file pointer
497c478bd9Sstevel@tonic-gate **		timeout -- time to complete operation (milliseconds)
507c478bd9Sstevel@tonic-gate **		uio -- vector list of memory locations of data for printing
517c478bd9Sstevel@tonic-gate **
527c478bd9Sstevel@tonic-gate **	Results:
537c478bd9Sstevel@tonic-gate **		Success: 0 (zero)
547c478bd9Sstevel@tonic-gate **		Failure:
557c478bd9Sstevel@tonic-gate */
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate static int
sm_print(fp,timeout,uio)587c478bd9Sstevel@tonic-gate sm_print(fp, timeout, uio)
597c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
607c478bd9Sstevel@tonic-gate 	int timeout;
617c478bd9Sstevel@tonic-gate 	register struct sm_uio *uio;
627c478bd9Sstevel@tonic-gate {
637c478bd9Sstevel@tonic-gate 	register int err;
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate 	if (uio->uio_resid == 0)
667c478bd9Sstevel@tonic-gate 	{
677c478bd9Sstevel@tonic-gate 		uio->uio_iovcnt = 0;
687c478bd9Sstevel@tonic-gate 		return 0;
697c478bd9Sstevel@tonic-gate 	}
707c478bd9Sstevel@tonic-gate 	err = sm_fvwrite(fp, timeout, uio);
717c478bd9Sstevel@tonic-gate 	uio->uio_resid = 0;
727c478bd9Sstevel@tonic-gate 	uio->uio_iovcnt = 0;
737c478bd9Sstevel@tonic-gate 	return err;
747c478bd9Sstevel@tonic-gate }
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate /*
777c478bd9Sstevel@tonic-gate **  SM_BPRINTF -- allow formating to an unbuffered file.
787c478bd9Sstevel@tonic-gate **
797c478bd9Sstevel@tonic-gate **  Helper function for `fprintf to unbuffered unix file': creates a
807c478bd9Sstevel@tonic-gate **  temporary buffer (via a "fake" file pointer).
817c478bd9Sstevel@tonic-gate **  We only work on write-only files; this avoids
827c478bd9Sstevel@tonic-gate **  worries about ungetc buffers and so forth.
837c478bd9Sstevel@tonic-gate **
847c478bd9Sstevel@tonic-gate **	Parameters:
857c478bd9Sstevel@tonic-gate **		fp -- the file to send the o/p to
867c478bd9Sstevel@tonic-gate **		fmt -- format instructions for the o/p
877c478bd9Sstevel@tonic-gate **		ap -- vectors of data units used for formating
887c478bd9Sstevel@tonic-gate **
897c478bd9Sstevel@tonic-gate **	Results:
907c478bd9Sstevel@tonic-gate **		Failure: SM_IO_EOF and errno set
917c478bd9Sstevel@tonic-gate **		Success: number of data units used in the formating
927c478bd9Sstevel@tonic-gate **
937c478bd9Sstevel@tonic-gate **	Side effects:
947c478bd9Sstevel@tonic-gate **		formatted o/p can be SM_IO_BUFSIZ length maximum
957c478bd9Sstevel@tonic-gate */
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate static int
sm_bprintf(fp,fmt,ap)987c478bd9Sstevel@tonic-gate sm_bprintf(fp, fmt, ap)
997c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
1007c478bd9Sstevel@tonic-gate 	const char *fmt;
1017c478bd9Sstevel@tonic-gate 	SM_VA_LOCAL_DECL
1027c478bd9Sstevel@tonic-gate {
1037c478bd9Sstevel@tonic-gate 	int ret;
1047c478bd9Sstevel@tonic-gate 	SM_FILE_T fake;
1057c478bd9Sstevel@tonic-gate 	unsigned char buf[SM_IO_BUFSIZ];
1067c478bd9Sstevel@tonic-gate 	extern const char SmFileMagic[];
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate 	/* copy the important variables */
1097c478bd9Sstevel@tonic-gate 	fake.sm_magic = SmFileMagic;
1107c478bd9Sstevel@tonic-gate 	fake.f_timeout = SM_TIME_FOREVER;
1117c478bd9Sstevel@tonic-gate 	fake.f_timeoutstate = SM_TIME_BLOCK;
1127c478bd9Sstevel@tonic-gate 	fake.f_flags = fp->f_flags & ~SMNBF;
1137c478bd9Sstevel@tonic-gate 	fake.f_file = fp->f_file;
1147c478bd9Sstevel@tonic-gate 	fake.f_cookie = fp->f_cookie;
1157c478bd9Sstevel@tonic-gate 	fake.f_write = fp->f_write;
1167c478bd9Sstevel@tonic-gate 	fake.f_close = NULL;
1177c478bd9Sstevel@tonic-gate 	fake.f_open = NULL;
1187c478bd9Sstevel@tonic-gate 	fake.f_read = NULL;
1197c478bd9Sstevel@tonic-gate 	fake.f_seek = NULL;
1207c478bd9Sstevel@tonic-gate 	fake.f_setinfo = fake.f_getinfo = NULL;
1217c478bd9Sstevel@tonic-gate 	fake.f_type = "sm_bprintf:fake";
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 	/* set up the buffer */
1247c478bd9Sstevel@tonic-gate 	fake.f_bf.smb_base = fake.f_p = buf;
1257c478bd9Sstevel@tonic-gate 	fake.f_bf.smb_size = fake.f_w = sizeof(buf);
1267c478bd9Sstevel@tonic-gate 	fake.f_lbfsize = 0;	/* not actually used, but Just In Case */
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 	/* do the work, then copy any error status */
1297c478bd9Sstevel@tonic-gate 	ret = sm_io_vfprintf(&fake, SM_TIME_FOREVER, fmt, ap);
1307c478bd9Sstevel@tonic-gate 	if (ret >= 0 && sm_io_flush(&fake, SM_TIME_FOREVER))
1317c478bd9Sstevel@tonic-gate 		ret = SM_IO_EOF;	/* errno set by sm_io_flush */
1327c478bd9Sstevel@tonic-gate 	if (fake.f_flags & SMERR)
1337c478bd9Sstevel@tonic-gate 		fp->f_flags |= SMERR;
1347c478bd9Sstevel@tonic-gate 	return ret;
1357c478bd9Sstevel@tonic-gate }
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate #define BUF		40
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate #define STATIC_ARG_TBL_SIZE 8	/* Size of static argument table. */
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate /* Macros for converting digits to letters and vice versa */
1447c478bd9Sstevel@tonic-gate #define to_digit(c)	((c) - '0')
1457c478bd9Sstevel@tonic-gate #define is_digit(c)	((unsigned) to_digit(c) <= 9)
1467c478bd9Sstevel@tonic-gate #define to_char(n)	((char) (n) + '0')
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate /* Flags used during conversion. */
1497c478bd9Sstevel@tonic-gate #define ALT		0x001		/* alternate form */
1507c478bd9Sstevel@tonic-gate #define HEXPREFIX	0x002		/* add 0x or 0X prefix */
1517c478bd9Sstevel@tonic-gate #define LADJUST		0x004		/* left adjustment */
1527c478bd9Sstevel@tonic-gate #define LONGINT		0x010		/* long integer */
1537c478bd9Sstevel@tonic-gate #define QUADINT		0x020		/* quad integer */
1547c478bd9Sstevel@tonic-gate #define SHORTINT	0x040		/* short integer */
1557c478bd9Sstevel@tonic-gate #define ZEROPAD		0x080		/* zero (as opposed to blank) pad */
1567c478bd9Sstevel@tonic-gate #define FPT		0x100		/* Floating point number */
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate /*
1597c478bd9Sstevel@tonic-gate **  SM_IO_VPRINTF -- performs actual formating for o/p
1607c478bd9Sstevel@tonic-gate **
1617c478bd9Sstevel@tonic-gate **	Parameters:
1627c478bd9Sstevel@tonic-gate **		fp -- file pointer for o/p
1637c478bd9Sstevel@tonic-gate **		timeout -- time to complete the print
1647c478bd9Sstevel@tonic-gate **		fmt0 -- formating directives
1657c478bd9Sstevel@tonic-gate **		ap -- vectors with data units for formating
1667c478bd9Sstevel@tonic-gate **
1677c478bd9Sstevel@tonic-gate **	Results:
1687c478bd9Sstevel@tonic-gate **		Success: number of data units used for formatting
1697c478bd9Sstevel@tonic-gate **		Failure: SM_IO_EOF and sets errno
1707c478bd9Sstevel@tonic-gate */
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate int
sm_io_vfprintf(fp,timeout,fmt0,ap)1737c478bd9Sstevel@tonic-gate sm_io_vfprintf(fp, timeout, fmt0, ap)
1747c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
1757c478bd9Sstevel@tonic-gate 	int timeout;
1767c478bd9Sstevel@tonic-gate 	const char *fmt0;
1777c478bd9Sstevel@tonic-gate 	SM_VA_LOCAL_DECL
1787c478bd9Sstevel@tonic-gate {
1797c478bd9Sstevel@tonic-gate 	register char *fmt;	/* format string */
1807c478bd9Sstevel@tonic-gate 	register int ch;	/* character from fmt */
1817c478bd9Sstevel@tonic-gate 	register int n, m, n2;	/* handy integers (short term usage) */
1827c478bd9Sstevel@tonic-gate 	register char *cp;	/* handy char pointer (short term usage) */
1837c478bd9Sstevel@tonic-gate 	register struct sm_iov *iovp;/* for PRINT macro */
1847c478bd9Sstevel@tonic-gate 	register int flags;	/* flags as above */
1857c478bd9Sstevel@tonic-gate 	int ret;		/* return value accumulator */
1867c478bd9Sstevel@tonic-gate 	int width;		/* width from format (%8d), or 0 */
1877c478bd9Sstevel@tonic-gate 	int prec;		/* precision from format (%.3d), or -1 */
1887c478bd9Sstevel@tonic-gate 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
1897c478bd9Sstevel@tonic-gate 	wchar_t wc;
1907c478bd9Sstevel@tonic-gate 	ULONGLONG_T _uquad;	/* integer arguments %[diouxX] */
1917c478bd9Sstevel@tonic-gate 	enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
1927c478bd9Sstevel@tonic-gate 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
1937c478bd9Sstevel@tonic-gate 	int realsz;		/* field size expanded by dprec */
1947c478bd9Sstevel@tonic-gate 	int size;		/* size of converted field or string */
1957c478bd9Sstevel@tonic-gate 	char *xdigs="0123456789abcdef"; /* digits for [xX] conversion */
1967c478bd9Sstevel@tonic-gate #define NIOV 8
1977c478bd9Sstevel@tonic-gate 	struct sm_uio uio;	/* output information: summary */
1987c478bd9Sstevel@tonic-gate 	struct sm_iov iov[NIOV];/* ... and individual io vectors */
1997c478bd9Sstevel@tonic-gate 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
2007c478bd9Sstevel@tonic-gate 	char ox[2];		/* space for 0x hex-prefix */
2017c478bd9Sstevel@tonic-gate 	va_list *argtable;	/* args, built due to positional arg */
2027c478bd9Sstevel@tonic-gate 	va_list statargtable[STATIC_ARG_TBL_SIZE];
2037c478bd9Sstevel@tonic-gate 	int nextarg;		/* 1-based argument index */
2047c478bd9Sstevel@tonic-gate 	va_list orgap;		/* original argument pointer */
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	/*
2077c478bd9Sstevel@tonic-gate 	**  Choose PADSIZE to trade efficiency vs. size.  If larger printf
2087c478bd9Sstevel@tonic-gate 	**  fields occur frequently, increase PADSIZE and make the initialisers
2097c478bd9Sstevel@tonic-gate 	**  below longer.
2107c478bd9Sstevel@tonic-gate 	*/
2117c478bd9Sstevel@tonic-gate #define PADSIZE	16		/* pad chunk size */
2127c478bd9Sstevel@tonic-gate 	static char blanks[PADSIZE] =
2137c478bd9Sstevel@tonic-gate 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
2147c478bd9Sstevel@tonic-gate 	static char zeroes[PADSIZE] =
2157c478bd9Sstevel@tonic-gate 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	/*
2187c478bd9Sstevel@tonic-gate 	**  BEWARE, these `goto error' on error, and PAD uses `n'.
2197c478bd9Sstevel@tonic-gate 	*/
2207c478bd9Sstevel@tonic-gate #define PRINT(ptr, len) do { \
2217c478bd9Sstevel@tonic-gate 	iovp->iov_base = (ptr); \
2227c478bd9Sstevel@tonic-gate 	iovp->iov_len = (len); \
2237c478bd9Sstevel@tonic-gate 	uio.uio_resid += (len); \
2247c478bd9Sstevel@tonic-gate 	iovp++; \
2257c478bd9Sstevel@tonic-gate 	if (++uio.uio_iovcnt >= NIOV) \
2267c478bd9Sstevel@tonic-gate 	{ \
2277c478bd9Sstevel@tonic-gate 		if (sm_print(fp, timeout, &uio)) \
2287c478bd9Sstevel@tonic-gate 			goto error; \
2297c478bd9Sstevel@tonic-gate 		iovp = iov; \
2307c478bd9Sstevel@tonic-gate 	} \
2317c478bd9Sstevel@tonic-gate } while (0)
2327c478bd9Sstevel@tonic-gate #define PAD(howmany, with) do \
2337c478bd9Sstevel@tonic-gate { \
2347c478bd9Sstevel@tonic-gate 	if ((n = (howmany)) > 0) \
2357c478bd9Sstevel@tonic-gate 	{ \
2367c478bd9Sstevel@tonic-gate 		while (n > PADSIZE) { \
2377c478bd9Sstevel@tonic-gate 			PRINT(with, PADSIZE); \
2387c478bd9Sstevel@tonic-gate 			n -= PADSIZE; \
2397c478bd9Sstevel@tonic-gate 		} \
2407c478bd9Sstevel@tonic-gate 		PRINT(with, n); \
2417c478bd9Sstevel@tonic-gate 	} \
2427c478bd9Sstevel@tonic-gate } while (0)
2437c478bd9Sstevel@tonic-gate #define FLUSH() do \
2447c478bd9Sstevel@tonic-gate { \
2457c478bd9Sstevel@tonic-gate 	if (uio.uio_resid && sm_print(fp, timeout, &uio)) \
2467c478bd9Sstevel@tonic-gate 		goto error; \
2477c478bd9Sstevel@tonic-gate 	uio.uio_iovcnt = 0; \
2487c478bd9Sstevel@tonic-gate 	iovp = iov; \
2497c478bd9Sstevel@tonic-gate } while (0)
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	/*
2527c478bd9Sstevel@tonic-gate 	**  To extend shorts properly, we need both signed and unsigned
2537c478bd9Sstevel@tonic-gate 	**  argument extraction methods.
2547c478bd9Sstevel@tonic-gate 	*/
2557c478bd9Sstevel@tonic-gate #define SARG() \
2567c478bd9Sstevel@tonic-gate 	(flags&QUADINT ? SM_VA_ARG(ap, LONGLONG_T) : \
2577c478bd9Sstevel@tonic-gate 	    flags&LONGINT ? GETARG(long) : \
2587c478bd9Sstevel@tonic-gate 	    flags&SHORTINT ? (long) (short) GETARG(int) : \
2597c478bd9Sstevel@tonic-gate 	    (long) GETARG(int))
2607c478bd9Sstevel@tonic-gate #define UARG() \
2617c478bd9Sstevel@tonic-gate 	(flags&QUADINT ? SM_VA_ARG(ap, ULONGLONG_T) : \
2627c478bd9Sstevel@tonic-gate 	    flags&LONGINT ? GETARG(unsigned long) : \
2637c478bd9Sstevel@tonic-gate 	    flags&SHORTINT ? (unsigned long) (unsigned short) GETARG(int) : \
2647c478bd9Sstevel@tonic-gate 	    (unsigned long) GETARG(unsigned int))
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	/*
2677c478bd9Sstevel@tonic-gate 	**  Get * arguments, including the form *nn$.  Preserve the nextarg
2687c478bd9Sstevel@tonic-gate 	**  that the argument can be gotten once the type is determined.
2697c478bd9Sstevel@tonic-gate 	*/
2707c478bd9Sstevel@tonic-gate #define GETASTER(val) \
2717c478bd9Sstevel@tonic-gate 	n2 = 0; \
2727c478bd9Sstevel@tonic-gate 	cp = fmt; \
2737c478bd9Sstevel@tonic-gate 	while (is_digit(*cp)) \
2747c478bd9Sstevel@tonic-gate 	{ \
2757c478bd9Sstevel@tonic-gate 		n2 = 10 * n2 + to_digit(*cp); \
2767c478bd9Sstevel@tonic-gate 		cp++; \
2777c478bd9Sstevel@tonic-gate 	} \
2787c478bd9Sstevel@tonic-gate 	if (*cp == '$') \
2797c478bd9Sstevel@tonic-gate 	{ \
2807c478bd9Sstevel@tonic-gate 		int hold = nextarg; \
2817c478bd9Sstevel@tonic-gate 		if (argtable == NULL) \
2827c478bd9Sstevel@tonic-gate 		{ \
2837c478bd9Sstevel@tonic-gate 			argtable = statargtable; \
2847c478bd9Sstevel@tonic-gate 			sm_find_arguments(fmt0, orgap, &argtable); \
2857c478bd9Sstevel@tonic-gate 		} \
2867c478bd9Sstevel@tonic-gate 		nextarg = n2; \
2877c478bd9Sstevel@tonic-gate 		val = GETARG(int); \
2887c478bd9Sstevel@tonic-gate 		nextarg = hold; \
2897c478bd9Sstevel@tonic-gate 		fmt = ++cp; \
2907c478bd9Sstevel@tonic-gate 	} \
2917c478bd9Sstevel@tonic-gate 	else \
2927c478bd9Sstevel@tonic-gate 	{ \
2937c478bd9Sstevel@tonic-gate 		val = GETARG(int); \
2947c478bd9Sstevel@tonic-gate 	}
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate /*
2977c478bd9Sstevel@tonic-gate **  Get the argument indexed by nextarg.   If the argument table is
2987c478bd9Sstevel@tonic-gate **  built, use it to get the argument.  If its not, get the next
2997c478bd9Sstevel@tonic-gate **  argument (and arguments must be gotten sequentially).
3007c478bd9Sstevel@tonic-gate */
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate #if SM_VA_STD
3037c478bd9Sstevel@tonic-gate # define GETARG(type) \
3047c478bd9Sstevel@tonic-gate 	(((argtable != NULL) ? (void) (ap = argtable[nextarg]) : (void) 0), \
3057c478bd9Sstevel@tonic-gate 	 nextarg++, SM_VA_ARG(ap, type))
3067c478bd9Sstevel@tonic-gate #else /* SM_VA_STD */
3077c478bd9Sstevel@tonic-gate # define GETARG(type) \
3087c478bd9Sstevel@tonic-gate 	((argtable != NULL) ? (*((type*)(argtable[nextarg++]))) : \
3097c478bd9Sstevel@tonic-gate 			      (nextarg++, SM_VA_ARG(ap, type)))
3107c478bd9Sstevel@tonic-gate #endif /* SM_VA_STD */
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	/* sorry, fprintf(read_only_file, "") returns SM_IO_EOF, not 0 */
3137c478bd9Sstevel@tonic-gate 	if (cantwrite(fp))
3147c478bd9Sstevel@tonic-gate 	{
3157c478bd9Sstevel@tonic-gate 		errno = EBADF;
3167c478bd9Sstevel@tonic-gate 		return SM_IO_EOF;
3177c478bd9Sstevel@tonic-gate 	}
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
3207c478bd9Sstevel@tonic-gate 	if ((fp->f_flags & (SMNBF|SMWR|SMRW)) == (SMNBF|SMWR) &&
3217c478bd9Sstevel@tonic-gate 	    fp->f_file >= 0)
3227c478bd9Sstevel@tonic-gate 		return sm_bprintf(fp, fmt0, ap);
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 	fmt = (char *) fmt0;
3257c478bd9Sstevel@tonic-gate 	argtable = NULL;
3267c478bd9Sstevel@tonic-gate 	nextarg = 1;
3277c478bd9Sstevel@tonic-gate 	SM_VA_COPY(orgap, ap);
3287c478bd9Sstevel@tonic-gate 	uio.uio_iov = iovp = iov;
3297c478bd9Sstevel@tonic-gate 	uio.uio_resid = 0;
3307c478bd9Sstevel@tonic-gate 	uio.uio_iovcnt = 0;
3317c478bd9Sstevel@tonic-gate 	ret = 0;
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 	/* Scan the format for conversions (`%' character). */
3347c478bd9Sstevel@tonic-gate 	for (;;)
3357c478bd9Sstevel@tonic-gate 	{
3367c478bd9Sstevel@tonic-gate 		cp = fmt;
3377c478bd9Sstevel@tonic-gate 		n = 0;
3387c478bd9Sstevel@tonic-gate 		while ((wc = *fmt) != '\0')
3397c478bd9Sstevel@tonic-gate 		{
3407c478bd9Sstevel@tonic-gate 			if (wc == '%')
3417c478bd9Sstevel@tonic-gate 			{
3427c478bd9Sstevel@tonic-gate 				n = 1;
3437c478bd9Sstevel@tonic-gate 				break;
3447c478bd9Sstevel@tonic-gate 			}
3457c478bd9Sstevel@tonic-gate 			fmt++;
3467c478bd9Sstevel@tonic-gate 		}
3477c478bd9Sstevel@tonic-gate 		if ((m = fmt - cp) != 0)
3487c478bd9Sstevel@tonic-gate 		{
3497c478bd9Sstevel@tonic-gate 			PRINT(cp, m);
3507c478bd9Sstevel@tonic-gate 			ret += m;
3517c478bd9Sstevel@tonic-gate 		}
3527c478bd9Sstevel@tonic-gate 		if (n <= 0)
3537c478bd9Sstevel@tonic-gate 			goto done;
3547c478bd9Sstevel@tonic-gate 		fmt++;		/* skip over '%' */
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 		flags = 0;
3577c478bd9Sstevel@tonic-gate 		dprec = 0;
3587c478bd9Sstevel@tonic-gate 		width = 0;
3597c478bd9Sstevel@tonic-gate 		prec = -1;
3607c478bd9Sstevel@tonic-gate 		sign = '\0';
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate rflag:		ch = *fmt++;
3637c478bd9Sstevel@tonic-gate reswitch:	switch (ch)
3647c478bd9Sstevel@tonic-gate 		{
3657c478bd9Sstevel@tonic-gate 		  case ' ':
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 			/*
3687c478bd9Sstevel@tonic-gate 			**  ``If the space and + flags both appear, the space
3697c478bd9Sstevel@tonic-gate 			**  flag will be ignored.''
3707c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
3717c478bd9Sstevel@tonic-gate 			*/
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 			if (!sign)
3747c478bd9Sstevel@tonic-gate 				sign = ' ';
3757c478bd9Sstevel@tonic-gate 			goto rflag;
3767c478bd9Sstevel@tonic-gate 		  case '#':
3777c478bd9Sstevel@tonic-gate 			flags |= ALT;
3787c478bd9Sstevel@tonic-gate 			goto rflag;
3797c478bd9Sstevel@tonic-gate 		  case '*':
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 			/*
3827c478bd9Sstevel@tonic-gate 			**  ``A negative field width argument is taken as a
3837c478bd9Sstevel@tonic-gate 			**  - flag followed by a positive field width.''
3847c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
3857c478bd9Sstevel@tonic-gate 			**  They don't exclude field widths read from args.
3867c478bd9Sstevel@tonic-gate 			*/
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 			GETASTER(width);
3897c478bd9Sstevel@tonic-gate 			if (width >= 0)
3907c478bd9Sstevel@tonic-gate 				goto rflag;
3917c478bd9Sstevel@tonic-gate 			width = -width;
3927c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
3937c478bd9Sstevel@tonic-gate 		  case '-':
3947c478bd9Sstevel@tonic-gate 			flags |= LADJUST;
3957c478bd9Sstevel@tonic-gate 			goto rflag;
3967c478bd9Sstevel@tonic-gate 		  case '+':
3977c478bd9Sstevel@tonic-gate 			sign = '+';
3987c478bd9Sstevel@tonic-gate 			goto rflag;
3997c478bd9Sstevel@tonic-gate 		  case '.':
4007c478bd9Sstevel@tonic-gate 			if ((ch = *fmt++) == '*')
4017c478bd9Sstevel@tonic-gate 			{
4027c478bd9Sstevel@tonic-gate 				GETASTER(n);
4037c478bd9Sstevel@tonic-gate 				prec = n < 0 ? -1 : n;
4047c478bd9Sstevel@tonic-gate 				goto rflag;
4057c478bd9Sstevel@tonic-gate 			}
4067c478bd9Sstevel@tonic-gate 			n = 0;
4077c478bd9Sstevel@tonic-gate 			while (is_digit(ch))
4087c478bd9Sstevel@tonic-gate 			{
4097c478bd9Sstevel@tonic-gate 				n = 10 * n + to_digit(ch);
4107c478bd9Sstevel@tonic-gate 				ch = *fmt++;
4117c478bd9Sstevel@tonic-gate 			}
4127c478bd9Sstevel@tonic-gate 			if (ch == '$')
4137c478bd9Sstevel@tonic-gate 			{
4147c478bd9Sstevel@tonic-gate 				nextarg = n;
4157c478bd9Sstevel@tonic-gate 				if (argtable == NULL)
4167c478bd9Sstevel@tonic-gate 				{
4177c478bd9Sstevel@tonic-gate 					argtable = statargtable;
4187c478bd9Sstevel@tonic-gate 					sm_find_arguments(fmt0, orgap,
4197c478bd9Sstevel@tonic-gate 					    &argtable);
4207c478bd9Sstevel@tonic-gate 				}
4217c478bd9Sstevel@tonic-gate 				goto rflag;
4227c478bd9Sstevel@tonic-gate 			}
4237c478bd9Sstevel@tonic-gate 			prec = n < 0 ? -1 : n;
4247c478bd9Sstevel@tonic-gate 			goto reswitch;
4257c478bd9Sstevel@tonic-gate 		  case '0':
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 			/*
4287c478bd9Sstevel@tonic-gate 			**  ``Note that 0 is taken as a flag, not as the
4297c478bd9Sstevel@tonic-gate 			**  beginning of a field width.''
4307c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
4317c478bd9Sstevel@tonic-gate 			*/
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 			flags |= ZEROPAD;
4347c478bd9Sstevel@tonic-gate 			goto rflag;
4357c478bd9Sstevel@tonic-gate 		  case '1': case '2': case '3': case '4':
4367c478bd9Sstevel@tonic-gate 		  case '5': case '6': case '7': case '8': case '9':
4377c478bd9Sstevel@tonic-gate 			n = 0;
4387c478bd9Sstevel@tonic-gate 			do
4397c478bd9Sstevel@tonic-gate 			{
4407c478bd9Sstevel@tonic-gate 				n = 10 * n + to_digit(ch);
4417c478bd9Sstevel@tonic-gate 				ch = *fmt++;
4427c478bd9Sstevel@tonic-gate 			} while (is_digit(ch));
4437c478bd9Sstevel@tonic-gate 			if (ch == '$')
4447c478bd9Sstevel@tonic-gate 			{
4457c478bd9Sstevel@tonic-gate 				nextarg = n;
4467c478bd9Sstevel@tonic-gate 				if (argtable == NULL)
4477c478bd9Sstevel@tonic-gate 				{
4487c478bd9Sstevel@tonic-gate 					argtable = statargtable;
4497c478bd9Sstevel@tonic-gate 					sm_find_arguments(fmt0, orgap,
4507c478bd9Sstevel@tonic-gate 					    &argtable);
4517c478bd9Sstevel@tonic-gate 				}
4527c478bd9Sstevel@tonic-gate 				goto rflag;
4537c478bd9Sstevel@tonic-gate 			}
4547c478bd9Sstevel@tonic-gate 			width = n;
4557c478bd9Sstevel@tonic-gate 			goto reswitch;
4567c478bd9Sstevel@tonic-gate 		  case 'h':
4577c478bd9Sstevel@tonic-gate 			flags |= SHORTINT;
4587c478bd9Sstevel@tonic-gate 			goto rflag;
4597c478bd9Sstevel@tonic-gate 		  case 'l':
4607c478bd9Sstevel@tonic-gate 			if (*fmt == 'l')
4617c478bd9Sstevel@tonic-gate 			{
4627c478bd9Sstevel@tonic-gate 				fmt++;
4637c478bd9Sstevel@tonic-gate 				flags |= QUADINT;
4647c478bd9Sstevel@tonic-gate 			}
4657c478bd9Sstevel@tonic-gate 			else
4667c478bd9Sstevel@tonic-gate 			{
4677c478bd9Sstevel@tonic-gate 				flags |= LONGINT;
4687c478bd9Sstevel@tonic-gate 			}
4697c478bd9Sstevel@tonic-gate 			goto rflag;
4707c478bd9Sstevel@tonic-gate 		  case 'q':
4717c478bd9Sstevel@tonic-gate 			flags |= QUADINT;
4727c478bd9Sstevel@tonic-gate 			goto rflag;
4737c478bd9Sstevel@tonic-gate 		  case 'c':
4747c478bd9Sstevel@tonic-gate 			*(cp = buf) = GETARG(int);
4757c478bd9Sstevel@tonic-gate 			size = 1;
4767c478bd9Sstevel@tonic-gate 			sign = '\0';
4777c478bd9Sstevel@tonic-gate 			break;
4787c478bd9Sstevel@tonic-gate 		  case 'D':
4797c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
4807c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
4817c478bd9Sstevel@tonic-gate 		  case 'd':
4827c478bd9Sstevel@tonic-gate 		  case 'i':
4837c478bd9Sstevel@tonic-gate 			_uquad = SARG();
4847c478bd9Sstevel@tonic-gate 			if ((LONGLONG_T) _uquad < 0)
4857c478bd9Sstevel@tonic-gate 			{
4867c478bd9Sstevel@tonic-gate 				_uquad = -(LONGLONG_T) _uquad;
4877c478bd9Sstevel@tonic-gate 				sign = '-';
4887c478bd9Sstevel@tonic-gate 			}
4897c478bd9Sstevel@tonic-gate 			base = DEC;
4907c478bd9Sstevel@tonic-gate 			goto number;
4917c478bd9Sstevel@tonic-gate 		  case 'e':
4927c478bd9Sstevel@tonic-gate 		  case 'E':
4937c478bd9Sstevel@tonic-gate 		  case 'f':
4947c478bd9Sstevel@tonic-gate 		  case 'g':
4957c478bd9Sstevel@tonic-gate 		  case 'G':
4967c478bd9Sstevel@tonic-gate 			{
4977c478bd9Sstevel@tonic-gate 				double val;
4987c478bd9Sstevel@tonic-gate 				char *p;
4997c478bd9Sstevel@tonic-gate 				char fmt[16];
5007c478bd9Sstevel@tonic-gate 				char out[150];
5017c478bd9Sstevel@tonic-gate 				size_t len;
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 				/*
5047c478bd9Sstevel@tonic-gate 				**  This code implements floating point output
5057c478bd9Sstevel@tonic-gate 				**  in the most portable manner possible,
5067c478bd9Sstevel@tonic-gate 				**  relying only on 'sprintf' as defined by
5077c478bd9Sstevel@tonic-gate 				**  the 1989 ANSI C standard.
5087c478bd9Sstevel@tonic-gate 				**  We silently cap width and precision
5097c478bd9Sstevel@tonic-gate 				**  at 120, to avoid buffer overflow.
5107c478bd9Sstevel@tonic-gate 				*/
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 				val = GETARG(double);
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 				p = fmt;
5157c478bd9Sstevel@tonic-gate 				*p++ = '%';
5167c478bd9Sstevel@tonic-gate 				if (sign)
5177c478bd9Sstevel@tonic-gate 					*p++ = sign;
5187c478bd9Sstevel@tonic-gate 				if (flags & ALT)
5197c478bd9Sstevel@tonic-gate 					*p++ = '#';
5207c478bd9Sstevel@tonic-gate 				if (flags & LADJUST)
5217c478bd9Sstevel@tonic-gate 					*p++ = '-';
5227c478bd9Sstevel@tonic-gate 				if (flags & ZEROPAD)
5237c478bd9Sstevel@tonic-gate 					*p++ = '0';
5247c478bd9Sstevel@tonic-gate 				*p++ = '*';
5257c478bd9Sstevel@tonic-gate 				if (prec >= 0)
5267c478bd9Sstevel@tonic-gate 				{
5277c478bd9Sstevel@tonic-gate 					*p++ = '.';
5287c478bd9Sstevel@tonic-gate 					*p++ = '*';
5297c478bd9Sstevel@tonic-gate 				}
5307c478bd9Sstevel@tonic-gate 				*p++ = ch;
5317c478bd9Sstevel@tonic-gate 				*p = '\0';
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 				if (width > 120)
5347c478bd9Sstevel@tonic-gate 					width = 120;
5357c478bd9Sstevel@tonic-gate 				if (prec > 120)
5367c478bd9Sstevel@tonic-gate 					prec = 120;
5377c478bd9Sstevel@tonic-gate 				if (prec >= 0)
538*49218d4fSjbeck #if HASSNPRINTF
539*49218d4fSjbeck 					snprintf(out, sizeof(out), fmt, width,
540*49218d4fSjbeck 						prec, val);
541*49218d4fSjbeck #else /* HASSNPRINTF */
5427c478bd9Sstevel@tonic-gate 					sprintf(out, fmt, width, prec, val);
543*49218d4fSjbeck #endif /* HASSNPRINTF */
5447c478bd9Sstevel@tonic-gate 				else
545*49218d4fSjbeck #if HASSNPRINTF
546*49218d4fSjbeck 					snprintf(out, sizeof(out), fmt, width,
547*49218d4fSjbeck 						val);
548*49218d4fSjbeck #else /* HASSNPRINTF */
5497c478bd9Sstevel@tonic-gate 					sprintf(out, fmt, width, val);
550*49218d4fSjbeck #endif /* HASSNPRINTF */
5517c478bd9Sstevel@tonic-gate 				len = strlen(out);
5527c478bd9Sstevel@tonic-gate 				PRINT(out, len);
5537c478bd9Sstevel@tonic-gate 				FLUSH();
5547c478bd9Sstevel@tonic-gate 				continue;
5557c478bd9Sstevel@tonic-gate 			}
5567c478bd9Sstevel@tonic-gate 		case 'n':
5577c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
5587c478bd9Sstevel@tonic-gate 				*GETARG(LONGLONG_T *) = ret;
5597c478bd9Sstevel@tonic-gate 			else if (flags & LONGINT)
5607c478bd9Sstevel@tonic-gate 				*GETARG(long *) = ret;
5617c478bd9Sstevel@tonic-gate 			else if (flags & SHORTINT)
5627c478bd9Sstevel@tonic-gate 				*GETARG(short *) = ret;
5637c478bd9Sstevel@tonic-gate 			else
5647c478bd9Sstevel@tonic-gate 				*GETARG(int *) = ret;
5657c478bd9Sstevel@tonic-gate 			continue;	/* no output */
5667c478bd9Sstevel@tonic-gate 		  case 'O':
5677c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
5687c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
5697c478bd9Sstevel@tonic-gate 		  case 'o':
5707c478bd9Sstevel@tonic-gate 			_uquad = UARG();
5717c478bd9Sstevel@tonic-gate 			base = OCT;
5727c478bd9Sstevel@tonic-gate 			goto nosign;
5737c478bd9Sstevel@tonic-gate 		  case 'p':
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 			/*
5767c478bd9Sstevel@tonic-gate 			**  ``The argument shall be a pointer to void.  The
5777c478bd9Sstevel@tonic-gate 			**  value of the pointer is converted to a sequence
5787c478bd9Sstevel@tonic-gate 			**  of printable characters, in an implementation-
5797c478bd9Sstevel@tonic-gate 			**  defined manner.''
5807c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
5817c478bd9Sstevel@tonic-gate 			*/
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 			/* NOSTRICT */
5847c478bd9Sstevel@tonic-gate 			{
5857c478bd9Sstevel@tonic-gate 				union
5867c478bd9Sstevel@tonic-gate 				{
5877c478bd9Sstevel@tonic-gate 					void *p;
5887c478bd9Sstevel@tonic-gate 					ULONGLONG_T ll;
5897c478bd9Sstevel@tonic-gate 					unsigned long l;
5907c478bd9Sstevel@tonic-gate 					unsigned i;
5917c478bd9Sstevel@tonic-gate 				} u;
5927c478bd9Sstevel@tonic-gate 				u.p = GETARG(void *);
5937c478bd9Sstevel@tonic-gate 				if (sizeof(void *) == sizeof(ULONGLONG_T))
5947c478bd9Sstevel@tonic-gate 					_uquad = u.ll;
5957c478bd9Sstevel@tonic-gate 				else if (sizeof(void *) == sizeof(long))
5967c478bd9Sstevel@tonic-gate 					_uquad = u.l;
5977c478bd9Sstevel@tonic-gate 				else
5987c478bd9Sstevel@tonic-gate 					_uquad = u.i;
5997c478bd9Sstevel@tonic-gate 			}
6007c478bd9Sstevel@tonic-gate 			base = HEX;
6017c478bd9Sstevel@tonic-gate 			xdigs = "0123456789abcdef";
6027c478bd9Sstevel@tonic-gate 			flags |= HEXPREFIX;
6037c478bd9Sstevel@tonic-gate 			ch = 'x';
6047c478bd9Sstevel@tonic-gate 			goto nosign;
6057c478bd9Sstevel@tonic-gate 		  case 's':
6067c478bd9Sstevel@tonic-gate 			if ((cp = GETARG(char *)) == NULL)
6077c478bd9Sstevel@tonic-gate 				cp = "(null)";
6087c478bd9Sstevel@tonic-gate 			if (prec >= 0)
6097c478bd9Sstevel@tonic-gate 			{
6107c478bd9Sstevel@tonic-gate 				/*
6117c478bd9Sstevel@tonic-gate 				**  can't use strlen; can only look for the
6127c478bd9Sstevel@tonic-gate 				**  NUL in the first `prec' characters, and
6137c478bd9Sstevel@tonic-gate 				**  strlen() will go further.
6147c478bd9Sstevel@tonic-gate 				*/
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 				char *p = memchr(cp, 0, prec);
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 				if (p != NULL)
6197c478bd9Sstevel@tonic-gate 				{
6207c478bd9Sstevel@tonic-gate 					size = p - cp;
6217c478bd9Sstevel@tonic-gate 					if (size > prec)
6227c478bd9Sstevel@tonic-gate 						size = prec;
6237c478bd9Sstevel@tonic-gate 				}
6247c478bd9Sstevel@tonic-gate 				else
6257c478bd9Sstevel@tonic-gate 					size = prec;
6267c478bd9Sstevel@tonic-gate 			}
6277c478bd9Sstevel@tonic-gate 			else
6287c478bd9Sstevel@tonic-gate 				size = strlen(cp);
6297c478bd9Sstevel@tonic-gate 			sign = '\0';
6307c478bd9Sstevel@tonic-gate 			break;
6317c478bd9Sstevel@tonic-gate 		  case 'U':
6327c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
6337c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
6347c478bd9Sstevel@tonic-gate 		  case 'u':
6357c478bd9Sstevel@tonic-gate 			_uquad = UARG();
6367c478bd9Sstevel@tonic-gate 			base = DEC;
6377c478bd9Sstevel@tonic-gate 			goto nosign;
6387c478bd9Sstevel@tonic-gate 		  case 'X':
6397c478bd9Sstevel@tonic-gate 			xdigs = "0123456789ABCDEF";
6407c478bd9Sstevel@tonic-gate 			goto hex;
6417c478bd9Sstevel@tonic-gate 		  case 'x':
6427c478bd9Sstevel@tonic-gate 			xdigs = "0123456789abcdef";
6437c478bd9Sstevel@tonic-gate hex:			_uquad = UARG();
6447c478bd9Sstevel@tonic-gate 			base = HEX;
6457c478bd9Sstevel@tonic-gate 			/* leading 0x/X only if non-zero */
6467c478bd9Sstevel@tonic-gate 			if (flags & ALT && _uquad != 0)
6477c478bd9Sstevel@tonic-gate 				flags |= HEXPREFIX;
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 			/* unsigned conversions */
6507c478bd9Sstevel@tonic-gate nosign:			sign = '\0';
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 			/*
6537c478bd9Sstevel@tonic-gate 			**  ``... diouXx conversions ... if a precision is
6547c478bd9Sstevel@tonic-gate 			**  specified, the 0 flag will be ignored.''
6557c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
6567c478bd9Sstevel@tonic-gate 			*/
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate number:			if ((dprec = prec) >= 0)
6597c478bd9Sstevel@tonic-gate 				flags &= ~ZEROPAD;
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 			/*
6627c478bd9Sstevel@tonic-gate 			**  ``The result of converting a zero value with an
6637c478bd9Sstevel@tonic-gate 			**  explicit precision of zero is no characters.''
6647c478bd9Sstevel@tonic-gate 			**	-- ANSI X3J11
6657c478bd9Sstevel@tonic-gate 			*/
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 			cp = buf + BUF;
6687c478bd9Sstevel@tonic-gate 			if (_uquad != 0 || prec != 0)
6697c478bd9Sstevel@tonic-gate 			{
6707c478bd9Sstevel@tonic-gate 				/*
6717c478bd9Sstevel@tonic-gate 				**  Unsigned mod is hard, and unsigned mod
6727c478bd9Sstevel@tonic-gate 				**  by a constant is easier than that by
6737c478bd9Sstevel@tonic-gate 				**  a variable; hence this switch.
6747c478bd9Sstevel@tonic-gate 				*/
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 				switch (base)
6777c478bd9Sstevel@tonic-gate 				{
6787c478bd9Sstevel@tonic-gate 				  case OCT:
6797c478bd9Sstevel@tonic-gate 					do
6807c478bd9Sstevel@tonic-gate 					{
6817c478bd9Sstevel@tonic-gate 						*--cp = to_char(_uquad & 7);
6827c478bd9Sstevel@tonic-gate 						_uquad >>= 3;
6837c478bd9Sstevel@tonic-gate 					} while (_uquad);
6847c478bd9Sstevel@tonic-gate 					/* handle octal leading 0 */
6857c478bd9Sstevel@tonic-gate 					if (flags & ALT && *cp != '0')
6867c478bd9Sstevel@tonic-gate 						*--cp = '0';
6877c478bd9Sstevel@tonic-gate 					break;
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 				  case DEC:
6907c478bd9Sstevel@tonic-gate 					/* many numbers are 1 digit */
6917c478bd9Sstevel@tonic-gate 					while (_uquad >= 10)
6927c478bd9Sstevel@tonic-gate 					{
6937c478bd9Sstevel@tonic-gate 						*--cp = to_char(_uquad % 10);
6947c478bd9Sstevel@tonic-gate 						_uquad /= 10;
6957c478bd9Sstevel@tonic-gate 					}
6967c478bd9Sstevel@tonic-gate 					*--cp = to_char(_uquad);
6977c478bd9Sstevel@tonic-gate 					break;
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 				  case HEX:
7007c478bd9Sstevel@tonic-gate 					do
7017c478bd9Sstevel@tonic-gate 					{
7027c478bd9Sstevel@tonic-gate 						*--cp = xdigs[_uquad & 15];
7037c478bd9Sstevel@tonic-gate 						_uquad >>= 4;
7047c478bd9Sstevel@tonic-gate 					} while (_uquad);
7057c478bd9Sstevel@tonic-gate 					break;
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 				  default:
7087c478bd9Sstevel@tonic-gate 					cp = "bug in sm_io_vfprintf: bad base";
7097c478bd9Sstevel@tonic-gate 					size = strlen(cp);
7107c478bd9Sstevel@tonic-gate 					goto skipsize;
7117c478bd9Sstevel@tonic-gate 				}
7127c478bd9Sstevel@tonic-gate 			}
7137c478bd9Sstevel@tonic-gate 			size = buf + BUF - cp;
7147c478bd9Sstevel@tonic-gate 		  skipsize:
7157c478bd9Sstevel@tonic-gate 			break;
7167c478bd9Sstevel@tonic-gate 		  default:	/* "%?" prints ?, unless ? is NUL */
7177c478bd9Sstevel@tonic-gate 			if (ch == '\0')
7187c478bd9Sstevel@tonic-gate 				goto done;
7197c478bd9Sstevel@tonic-gate 			/* pretend it was %c with argument ch */
7207c478bd9Sstevel@tonic-gate 			cp = buf;
7217c478bd9Sstevel@tonic-gate 			*cp = ch;
7227c478bd9Sstevel@tonic-gate 			size = 1;
7237c478bd9Sstevel@tonic-gate 			sign = '\0';
7247c478bd9Sstevel@tonic-gate 			break;
7257c478bd9Sstevel@tonic-gate 		}
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 		/*
7287c478bd9Sstevel@tonic-gate 		**  All reasonable formats wind up here.  At this point, `cp'
7297c478bd9Sstevel@tonic-gate 		**  points to a string which (if not flags&LADJUST) should be
7307c478bd9Sstevel@tonic-gate 		**  padded out to `width' places.  If flags&ZEROPAD, it should
7317c478bd9Sstevel@tonic-gate 		**  first be prefixed by any sign or other prefix; otherwise,
7327c478bd9Sstevel@tonic-gate 		**  it should be blank padded before the prefix is emitted.
7337c478bd9Sstevel@tonic-gate 		**  After any left-hand padding and prefixing, emit zeroes
7347c478bd9Sstevel@tonic-gate 		**  required by a decimal [diouxX] precision, then print the
7357c478bd9Sstevel@tonic-gate 		**  string proper, then emit zeroes required by any leftover
7367c478bd9Sstevel@tonic-gate 		**  floating precision; finally, if LADJUST, pad with blanks.
7377c478bd9Sstevel@tonic-gate 		**
7387c478bd9Sstevel@tonic-gate 		**  Compute actual size, so we know how much to pad.
7397c478bd9Sstevel@tonic-gate 		**  size excludes decimal prec; realsz includes it.
7407c478bd9Sstevel@tonic-gate 		*/
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 		realsz = dprec > size ? dprec : size;
7437c478bd9Sstevel@tonic-gate 		if (sign)
7447c478bd9Sstevel@tonic-gate 			realsz++;
7457c478bd9Sstevel@tonic-gate 		else if (flags & HEXPREFIX)
7467c478bd9Sstevel@tonic-gate 			realsz+= 2;
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 		/* right-adjusting blank padding */
7497c478bd9Sstevel@tonic-gate 		if ((flags & (LADJUST|ZEROPAD)) == 0)
7507c478bd9Sstevel@tonic-gate 			PAD(width - realsz, blanks);
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 		/* prefix */
7537c478bd9Sstevel@tonic-gate 		if (sign)
7547c478bd9Sstevel@tonic-gate 		{
7557c478bd9Sstevel@tonic-gate 			PRINT(&sign, 1);
7567c478bd9Sstevel@tonic-gate 		}
7577c478bd9Sstevel@tonic-gate 		else if (flags & HEXPREFIX)
7587c478bd9Sstevel@tonic-gate 		{
7597c478bd9Sstevel@tonic-gate 			ox[0] = '0';
7607c478bd9Sstevel@tonic-gate 			ox[1] = ch;
7617c478bd9Sstevel@tonic-gate 			PRINT(ox, 2);
7627c478bd9Sstevel@tonic-gate 		}
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 		/* right-adjusting zero padding */
7657c478bd9Sstevel@tonic-gate 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
7667c478bd9Sstevel@tonic-gate 			PAD(width - realsz, zeroes);
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 		/* leading zeroes from decimal precision */
7697c478bd9Sstevel@tonic-gate 		PAD(dprec - size, zeroes);
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 		/* the string or number proper */
7727c478bd9Sstevel@tonic-gate 		PRINT(cp, size);
7737c478bd9Sstevel@tonic-gate 		/* left-adjusting padding (always blank) */
7747c478bd9Sstevel@tonic-gate 		if (flags & LADJUST)
7757c478bd9Sstevel@tonic-gate 			PAD(width - realsz, blanks);
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 		/* finally, adjust ret */
7787c478bd9Sstevel@tonic-gate 		ret += width > realsz ? width : realsz;
7797c478bd9Sstevel@tonic-gate 
7807c478bd9Sstevel@tonic-gate 		FLUSH();	/* copy out the I/O vectors */
7817c478bd9Sstevel@tonic-gate 	}
7827c478bd9Sstevel@tonic-gate done:
7837c478bd9Sstevel@tonic-gate 	FLUSH();
7847c478bd9Sstevel@tonic-gate error:
7857c478bd9Sstevel@tonic-gate 	if ((argtable != NULL) && (argtable != statargtable))
7867c478bd9Sstevel@tonic-gate 		sm_free(argtable);
7877c478bd9Sstevel@tonic-gate 	return sm_error(fp) ? SM_IO_EOF : ret;
7887c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
7897c478bd9Sstevel@tonic-gate }
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate /* Type ids for argument type table. */
7927c478bd9Sstevel@tonic-gate #define T_UNUSED	0
7937c478bd9Sstevel@tonic-gate #define T_SHORT		1
7947c478bd9Sstevel@tonic-gate #define T_U_SHORT	2
7957c478bd9Sstevel@tonic-gate #define TP_SHORT	3
7967c478bd9Sstevel@tonic-gate #define T_INT		4
7977c478bd9Sstevel@tonic-gate #define T_U_INT		5
7987c478bd9Sstevel@tonic-gate #define TP_INT		6
7997c478bd9Sstevel@tonic-gate #define T_LONG		7
8007c478bd9Sstevel@tonic-gate #define T_U_LONG	8
8017c478bd9Sstevel@tonic-gate #define TP_LONG		9
8027c478bd9Sstevel@tonic-gate #define T_QUAD		10
8037c478bd9Sstevel@tonic-gate #define T_U_QUAD	11
8047c478bd9Sstevel@tonic-gate #define TP_QUAD		12
8057c478bd9Sstevel@tonic-gate #define T_DOUBLE	13
8067c478bd9Sstevel@tonic-gate #define TP_CHAR		15
8077c478bd9Sstevel@tonic-gate #define TP_VOID		16
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate /*
8107c478bd9Sstevel@tonic-gate **  SM_FIND_ARGUMENTS -- find all args when a positional parameter is found.
8117c478bd9Sstevel@tonic-gate **
8127c478bd9Sstevel@tonic-gate **  Find all arguments when a positional parameter is encountered.  Returns a
8137c478bd9Sstevel@tonic-gate **  table, indexed by argument number, of pointers to each arguments.  The
8147c478bd9Sstevel@tonic-gate **  initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
8157c478bd9Sstevel@tonic-gate **  It will be replaced with a malloc-ed one if it overflows.
8167c478bd9Sstevel@tonic-gate **
8177c478bd9Sstevel@tonic-gate **	Parameters:
8187c478bd9Sstevel@tonic-gate **		fmt0 -- formating directives
8197c478bd9Sstevel@tonic-gate **		ap -- vector list of data unit for formating consumption
8207c478bd9Sstevel@tonic-gate **		argtable -- an indexable table (returned) of 'ap'
8217c478bd9Sstevel@tonic-gate **
8227c478bd9Sstevel@tonic-gate **	Results:
8237c478bd9Sstevel@tonic-gate **		none.
8247c478bd9Sstevel@tonic-gate */
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate static void
sm_find_arguments(fmt0,ap,argtable)8277c478bd9Sstevel@tonic-gate sm_find_arguments(fmt0, ap, argtable)
8287c478bd9Sstevel@tonic-gate 	const char *fmt0;
8297c478bd9Sstevel@tonic-gate 	SM_VA_LOCAL_DECL
8307c478bd9Sstevel@tonic-gate 	va_list **argtable;
8317c478bd9Sstevel@tonic-gate {
8327c478bd9Sstevel@tonic-gate 	register char *fmt;	/* format string */
8337c478bd9Sstevel@tonic-gate 	register int ch;	/* character from fmt */
8347c478bd9Sstevel@tonic-gate 	register int n, n2;	/* handy integer (short term usage) */
8357c478bd9Sstevel@tonic-gate 	register char *cp;	/* handy char pointer (short term usage) */
8367c478bd9Sstevel@tonic-gate 	register int flags;	/* flags as above */
8377c478bd9Sstevel@tonic-gate 	unsigned char *typetable; /* table of types */
8387c478bd9Sstevel@tonic-gate 	unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
8397c478bd9Sstevel@tonic-gate 	int tablesize;		/* current size of type table */
8407c478bd9Sstevel@tonic-gate 	int tablemax;		/* largest used index in table */
8417c478bd9Sstevel@tonic-gate 	int nextarg;		/* 1-based argument index */
8427c478bd9Sstevel@tonic-gate 
8437c478bd9Sstevel@tonic-gate 	/* Add an argument type to the table, expanding if necessary. */
8447c478bd9Sstevel@tonic-gate #define ADDTYPE(type) \
8457c478bd9Sstevel@tonic-gate 	((nextarg >= tablesize) ? \
8467c478bd9Sstevel@tonic-gate 		(sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \
8477c478bd9Sstevel@tonic-gate 	typetable[nextarg++] = type, \
8487c478bd9Sstevel@tonic-gate 	(nextarg > tablemax) ? tablemax = nextarg : 0)
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate #define ADDSARG() \
8517c478bd9Sstevel@tonic-gate 	((flags & LONGINT) ? ADDTYPE(T_LONG) : \
8527c478bd9Sstevel@tonic-gate 		((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
8537c478bd9Sstevel@tonic-gate 
8547c478bd9Sstevel@tonic-gate #define ADDUARG() \
8557c478bd9Sstevel@tonic-gate 	((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \
8567c478bd9Sstevel@tonic-gate 		((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	/* Add * arguments to the type array. */
8597c478bd9Sstevel@tonic-gate #define ADDASTER() \
8607c478bd9Sstevel@tonic-gate 	n2 = 0; \
8617c478bd9Sstevel@tonic-gate 	cp = fmt; \
8627c478bd9Sstevel@tonic-gate 	while (is_digit(*cp)) \
8637c478bd9Sstevel@tonic-gate 	{ \
8647c478bd9Sstevel@tonic-gate 		n2 = 10 * n2 + to_digit(*cp); \
8657c478bd9Sstevel@tonic-gate 		cp++; \
8667c478bd9Sstevel@tonic-gate 	} \
8677c478bd9Sstevel@tonic-gate 	if (*cp == '$') \
8687c478bd9Sstevel@tonic-gate 	{ \
8697c478bd9Sstevel@tonic-gate 		int hold = nextarg; \
8707c478bd9Sstevel@tonic-gate 		nextarg = n2; \
8717c478bd9Sstevel@tonic-gate 		ADDTYPE (T_INT); \
8727c478bd9Sstevel@tonic-gate 		nextarg = hold; \
8737c478bd9Sstevel@tonic-gate 		fmt = ++cp; \
8747c478bd9Sstevel@tonic-gate 	} \
8757c478bd9Sstevel@tonic-gate 	else \
8767c478bd9Sstevel@tonic-gate 	{ \
8777c478bd9Sstevel@tonic-gate 		ADDTYPE (T_INT); \
8787c478bd9Sstevel@tonic-gate 	}
8797c478bd9Sstevel@tonic-gate 	fmt = (char *) fmt0;
8807c478bd9Sstevel@tonic-gate 	typetable = stattypetable;
8817c478bd9Sstevel@tonic-gate 	tablesize = STATIC_ARG_TBL_SIZE;
8827c478bd9Sstevel@tonic-gate 	tablemax = 0;
8837c478bd9Sstevel@tonic-gate 	nextarg = 1;
8847c478bd9Sstevel@tonic-gate 	(void) memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 	/* Scan the format for conversions (`%' character). */
8877c478bd9Sstevel@tonic-gate 	for (;;)
8887c478bd9Sstevel@tonic-gate 	{
8897c478bd9Sstevel@tonic-gate 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
8907c478bd9Sstevel@tonic-gate 			/* void */;
8917c478bd9Sstevel@tonic-gate 		if (ch == '\0')
8927c478bd9Sstevel@tonic-gate 			goto done;
8937c478bd9Sstevel@tonic-gate 		fmt++;		/* skip over '%' */
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 		flags = 0;
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate rflag:		ch = *fmt++;
8987c478bd9Sstevel@tonic-gate reswitch:	switch (ch)
8997c478bd9Sstevel@tonic-gate 		{
9007c478bd9Sstevel@tonic-gate 		  case ' ':
9017c478bd9Sstevel@tonic-gate 		  case '#':
9027c478bd9Sstevel@tonic-gate 			goto rflag;
9037c478bd9Sstevel@tonic-gate 		  case '*':
9047c478bd9Sstevel@tonic-gate 			ADDASTER();
9057c478bd9Sstevel@tonic-gate 			goto rflag;
9067c478bd9Sstevel@tonic-gate 		  case '-':
9077c478bd9Sstevel@tonic-gate 		  case '+':
9087c478bd9Sstevel@tonic-gate 			goto rflag;
9097c478bd9Sstevel@tonic-gate 		  case '.':
9107c478bd9Sstevel@tonic-gate 			if ((ch = *fmt++) == '*')
9117c478bd9Sstevel@tonic-gate 			{
9127c478bd9Sstevel@tonic-gate 				ADDASTER();
9137c478bd9Sstevel@tonic-gate 				goto rflag;
9147c478bd9Sstevel@tonic-gate 			}
9157c478bd9Sstevel@tonic-gate 			while (is_digit(ch))
9167c478bd9Sstevel@tonic-gate 			{
9177c478bd9Sstevel@tonic-gate 				ch = *fmt++;
9187c478bd9Sstevel@tonic-gate 			}
9197c478bd9Sstevel@tonic-gate 			goto reswitch;
9207c478bd9Sstevel@tonic-gate 		  case '0':
9217c478bd9Sstevel@tonic-gate 			goto rflag;
9227c478bd9Sstevel@tonic-gate 		  case '1': case '2': case '3': case '4':
9237c478bd9Sstevel@tonic-gate 		  case '5': case '6': case '7': case '8': case '9':
9247c478bd9Sstevel@tonic-gate 			n = 0;
9257c478bd9Sstevel@tonic-gate 			do
9267c478bd9Sstevel@tonic-gate 			{
9277c478bd9Sstevel@tonic-gate 				n = 10 * n + to_digit(ch);
9287c478bd9Sstevel@tonic-gate 				ch = *fmt++;
9297c478bd9Sstevel@tonic-gate 			} while (is_digit(ch));
9307c478bd9Sstevel@tonic-gate 			if (ch == '$')
9317c478bd9Sstevel@tonic-gate 			{
9327c478bd9Sstevel@tonic-gate 				nextarg = n;
9337c478bd9Sstevel@tonic-gate 				goto rflag;
9347c478bd9Sstevel@tonic-gate 			}
9357c478bd9Sstevel@tonic-gate 			goto reswitch;
9367c478bd9Sstevel@tonic-gate 		  case 'h':
9377c478bd9Sstevel@tonic-gate 			flags |= SHORTINT;
9387c478bd9Sstevel@tonic-gate 			goto rflag;
9397c478bd9Sstevel@tonic-gate 		  case 'l':
9407c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
9417c478bd9Sstevel@tonic-gate 			goto rflag;
9427c478bd9Sstevel@tonic-gate 		  case 'q':
9437c478bd9Sstevel@tonic-gate 			flags |= QUADINT;
9447c478bd9Sstevel@tonic-gate 			goto rflag;
9457c478bd9Sstevel@tonic-gate 		  case 'c':
9467c478bd9Sstevel@tonic-gate 			ADDTYPE(T_INT);
9477c478bd9Sstevel@tonic-gate 			break;
9487c478bd9Sstevel@tonic-gate 		  case 'D':
9497c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
9507c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
9517c478bd9Sstevel@tonic-gate 		  case 'd':
9527c478bd9Sstevel@tonic-gate 		  case 'i':
9537c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
9547c478bd9Sstevel@tonic-gate 			{
9557c478bd9Sstevel@tonic-gate 				ADDTYPE(T_QUAD);
9567c478bd9Sstevel@tonic-gate 			}
9577c478bd9Sstevel@tonic-gate 			else
9587c478bd9Sstevel@tonic-gate 			{
9597c478bd9Sstevel@tonic-gate 				ADDSARG();
9607c478bd9Sstevel@tonic-gate 			}
9617c478bd9Sstevel@tonic-gate 			break;
9627c478bd9Sstevel@tonic-gate 		  case 'e':
9637c478bd9Sstevel@tonic-gate 		  case 'E':
9647c478bd9Sstevel@tonic-gate 		  case 'f':
9657c478bd9Sstevel@tonic-gate 		  case 'g':
9667c478bd9Sstevel@tonic-gate 		  case 'G':
9677c478bd9Sstevel@tonic-gate 			ADDTYPE(T_DOUBLE);
9687c478bd9Sstevel@tonic-gate 			break;
9697c478bd9Sstevel@tonic-gate 		  case 'n':
9707c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
9717c478bd9Sstevel@tonic-gate 				ADDTYPE(TP_QUAD);
9727c478bd9Sstevel@tonic-gate 			else if (flags & LONGINT)
9737c478bd9Sstevel@tonic-gate 				ADDTYPE(TP_LONG);
9747c478bd9Sstevel@tonic-gate 			else if (flags & SHORTINT)
9757c478bd9Sstevel@tonic-gate 				ADDTYPE(TP_SHORT);
9767c478bd9Sstevel@tonic-gate 			else
9777c478bd9Sstevel@tonic-gate 				ADDTYPE(TP_INT);
9787c478bd9Sstevel@tonic-gate 			continue;	/* no output */
9797c478bd9Sstevel@tonic-gate 		  case 'O':
9807c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
9817c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
9827c478bd9Sstevel@tonic-gate 		  case 'o':
9837c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
9847c478bd9Sstevel@tonic-gate 				ADDTYPE(T_U_QUAD);
9857c478bd9Sstevel@tonic-gate 			else
9867c478bd9Sstevel@tonic-gate 				ADDUARG();
9877c478bd9Sstevel@tonic-gate 			break;
9887c478bd9Sstevel@tonic-gate 		  case 'p':
9897c478bd9Sstevel@tonic-gate 			ADDTYPE(TP_VOID);
9907c478bd9Sstevel@tonic-gate 			break;
9917c478bd9Sstevel@tonic-gate 		  case 's':
9927c478bd9Sstevel@tonic-gate 			ADDTYPE(TP_CHAR);
9937c478bd9Sstevel@tonic-gate 			break;
9947c478bd9Sstevel@tonic-gate 		  case 'U':
9957c478bd9Sstevel@tonic-gate 			flags |= LONGINT;
9967c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
9977c478bd9Sstevel@tonic-gate 		  case 'u':
9987c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
9997c478bd9Sstevel@tonic-gate 				ADDTYPE(T_U_QUAD);
10007c478bd9Sstevel@tonic-gate 			else
10017c478bd9Sstevel@tonic-gate 				ADDUARG();
10027c478bd9Sstevel@tonic-gate 			break;
10037c478bd9Sstevel@tonic-gate 		  case 'X':
10047c478bd9Sstevel@tonic-gate 		  case 'x':
10057c478bd9Sstevel@tonic-gate 			if (flags & QUADINT)
10067c478bd9Sstevel@tonic-gate 				ADDTYPE(T_U_QUAD);
10077c478bd9Sstevel@tonic-gate 			else
10087c478bd9Sstevel@tonic-gate 				ADDUARG();
10097c478bd9Sstevel@tonic-gate 			break;
10107c478bd9Sstevel@tonic-gate 		  default:	/* "%?" prints ?, unless ? is NUL */
10117c478bd9Sstevel@tonic-gate 			if (ch == '\0')
10127c478bd9Sstevel@tonic-gate 				goto done;
10137c478bd9Sstevel@tonic-gate 			break;
10147c478bd9Sstevel@tonic-gate 		}
10157c478bd9Sstevel@tonic-gate 	}
10167c478bd9Sstevel@tonic-gate done:
10177c478bd9Sstevel@tonic-gate 	/* Build the argument table. */
10187c478bd9Sstevel@tonic-gate 	if (tablemax >= STATIC_ARG_TBL_SIZE)
10197c478bd9Sstevel@tonic-gate 	{
10207c478bd9Sstevel@tonic-gate 		*argtable = (va_list *)
10217c478bd9Sstevel@tonic-gate 		    sm_malloc(sizeof(va_list) * (tablemax + 1));
10227c478bd9Sstevel@tonic-gate 	}
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	for (n = 1; n <= tablemax; n++)
10257c478bd9Sstevel@tonic-gate 	{
10267c478bd9Sstevel@tonic-gate 		SM_VA_COPY((*argtable)[n], ap);
10277c478bd9Sstevel@tonic-gate 		switch (typetable [n])
10287c478bd9Sstevel@tonic-gate 		{
10297c478bd9Sstevel@tonic-gate 		  case T_UNUSED:
10307c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, int);
10317c478bd9Sstevel@tonic-gate 			break;
10327c478bd9Sstevel@tonic-gate 		  case T_SHORT:
10337c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, int);
10347c478bd9Sstevel@tonic-gate 			break;
10357c478bd9Sstevel@tonic-gate 		  case T_U_SHORT:
10367c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, int);
10377c478bd9Sstevel@tonic-gate 			break;
10387c478bd9Sstevel@tonic-gate 		  case TP_SHORT:
10397c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, short *);
10407c478bd9Sstevel@tonic-gate 			break;
10417c478bd9Sstevel@tonic-gate 		  case T_INT:
10427c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, int);
10437c478bd9Sstevel@tonic-gate 			break;
10447c478bd9Sstevel@tonic-gate 		  case T_U_INT:
10457c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, unsigned int);
10467c478bd9Sstevel@tonic-gate 			break;
10477c478bd9Sstevel@tonic-gate 		  case TP_INT:
10487c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, int *);
10497c478bd9Sstevel@tonic-gate 			break;
10507c478bd9Sstevel@tonic-gate 		  case T_LONG:
10517c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, long);
10527c478bd9Sstevel@tonic-gate 			break;
10537c478bd9Sstevel@tonic-gate 		  case T_U_LONG:
10547c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, unsigned long);
10557c478bd9Sstevel@tonic-gate 			break;
10567c478bd9Sstevel@tonic-gate 		  case TP_LONG:
10577c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, long *);
10587c478bd9Sstevel@tonic-gate 			break;
10597c478bd9Sstevel@tonic-gate 		  case T_QUAD:
10607c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, LONGLONG_T);
10617c478bd9Sstevel@tonic-gate 			break;
10627c478bd9Sstevel@tonic-gate 		  case T_U_QUAD:
10637c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, ULONGLONG_T);
10647c478bd9Sstevel@tonic-gate 			break;
10657c478bd9Sstevel@tonic-gate 		  case TP_QUAD:
10667c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, LONGLONG_T *);
10677c478bd9Sstevel@tonic-gate 			break;
10687c478bd9Sstevel@tonic-gate 		  case T_DOUBLE:
10697c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, double);
10707c478bd9Sstevel@tonic-gate 			break;
10717c478bd9Sstevel@tonic-gate 		  case TP_CHAR:
10727c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, char *);
10737c478bd9Sstevel@tonic-gate 			break;
10747c478bd9Sstevel@tonic-gate 		  case TP_VOID:
10757c478bd9Sstevel@tonic-gate 			(void) SM_VA_ARG(ap, void *);
10767c478bd9Sstevel@tonic-gate 			break;
10777c478bd9Sstevel@tonic-gate 		}
10787c478bd9Sstevel@tonic-gate 	}
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate 	if ((typetable != NULL) && (typetable != stattypetable))
10817c478bd9Sstevel@tonic-gate 		sm_free(typetable);
10827c478bd9Sstevel@tonic-gate }
10837c478bd9Sstevel@tonic-gate 
10847c478bd9Sstevel@tonic-gate /*
10857c478bd9Sstevel@tonic-gate **  SM_GROW_TYPE_TABLE -- Increase the size of the type table.
10867c478bd9Sstevel@tonic-gate **
10877c478bd9Sstevel@tonic-gate **	Parameters:
10887c478bd9Sstevel@tonic-gate **		tabletype -- type of table to grow
10897c478bd9Sstevel@tonic-gate **		tablesize -- requested new table size
10907c478bd9Sstevel@tonic-gate **
10917c478bd9Sstevel@tonic-gate **	Results:
10927c478bd9Sstevel@tonic-gate **		Raises an exception if can't allocate memory.
10937c478bd9Sstevel@tonic-gate */
10947c478bd9Sstevel@tonic-gate 
10957c478bd9Sstevel@tonic-gate static void
sm_grow_type_table_x(typetable,tablesize)10967c478bd9Sstevel@tonic-gate sm_grow_type_table_x(typetable, tablesize)
10977c478bd9Sstevel@tonic-gate 	unsigned char **typetable;
10987c478bd9Sstevel@tonic-gate 	int *tablesize;
10997c478bd9Sstevel@tonic-gate {
11007c478bd9Sstevel@tonic-gate 	unsigned char *oldtable = *typetable;
11017c478bd9Sstevel@tonic-gate 	int newsize = *tablesize * 2;
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	if (*tablesize == STATIC_ARG_TBL_SIZE)
11047c478bd9Sstevel@tonic-gate 	{
11057c478bd9Sstevel@tonic-gate 		*typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char)
11067c478bd9Sstevel@tonic-gate 							   * newsize);
11077c478bd9Sstevel@tonic-gate 		(void) memmove(*typetable, oldtable, *tablesize);
11087c478bd9Sstevel@tonic-gate 	}
11097c478bd9Sstevel@tonic-gate 	else
11107c478bd9Sstevel@tonic-gate 	{
11117c478bd9Sstevel@tonic-gate 		*typetable = (unsigned char *) sm_realloc_x(typetable,
11127c478bd9Sstevel@tonic-gate 					sizeof(unsigned char) * newsize);
11137c478bd9Sstevel@tonic-gate 	}
11147c478bd9Sstevel@tonic-gate 	(void) memset(&typetable [*tablesize], T_UNUSED,
11157c478bd9Sstevel@tonic-gate 		       (newsize - *tablesize));
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 	*tablesize = newsize;
11187c478bd9Sstevel@tonic-gate }
1119