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