vfprintf.c revision 363466
1/*
2 * Copyright (c) 2000-2001, 2004 Proofpoint, 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.55 2013-11-22 20:51:44 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_VFPRINTF -- 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#if HASSNPRINTF
539					snprintf(out, sizeof(out), fmt, width,
540						prec, val);
541#else
542					sprintf(out, fmt, width, prec, val);
543#endif
544				else
545#if HASSNPRINTF
546					snprintf(out, sizeof(out), fmt, width,
547						val);
548#else
549					sprintf(out, fmt, width, val);
550#endif
551				len = strlen(out);
552				PRINT(out, len);
553				FLUSH();
554				continue;
555			}
556		case 'n':
557			if (flags & QUADINT)
558				*GETARG(LONGLONG_T *) = ret;
559			else if (flags & LONGINT)
560				*GETARG(long *) = ret;
561			else if (flags & SHORTINT)
562				*GETARG(short *) = ret;
563			else
564				*GETARG(int *) = ret;
565			continue;	/* no output */
566		  case 'O':
567			flags |= LONGINT;
568			/*FALLTHROUGH*/
569		  case 'o':
570			_uquad = UARG();
571			base = OCT;
572			goto nosign;
573		  case 'p':
574
575			/*
576			**  ``The argument shall be a pointer to void.  The
577			**  value of the pointer is converted to a sequence
578			**  of printable characters, in an implementation-
579			**  defined manner.''
580			**	-- ANSI X3J11
581			*/
582
583			/* NOSTRICT */
584			{
585				union
586				{
587					void *p;
588					ULONGLONG_T ll;
589					unsigned long l;
590					unsigned i;
591				} u;
592				u.p = GETARG(void *);
593				if (sizeof(void *) == sizeof(ULONGLONG_T))
594					_uquad = u.ll;
595				else if (sizeof(void *) == sizeof(long))
596					_uquad = u.l;
597				else
598					_uquad = u.i;
599			}
600			base = HEX;
601			xdigs = "0123456789abcdef";
602			flags |= HEXPREFIX;
603			ch = 'x';
604			goto nosign;
605		  case 's':
606			if ((cp = GETARG(char *)) == NULL)
607				cp = "(null)";
608			if (prec >= 0)
609			{
610				/*
611				**  can't use strlen; can only look for the
612				**  NUL in the first `prec' characters, and
613				**  strlen() will go further.
614				*/
615
616				char *p = memchr(cp, 0, prec);
617
618				if (p != NULL)
619				{
620					size = p - cp;
621					if (size > prec)
622						size = prec;
623				}
624				else
625					size = prec;
626			}
627			else
628				size = strlen(cp);
629			sign = '\0';
630			break;
631		  case 'U':
632			flags |= LONGINT;
633			/*FALLTHROUGH*/
634		  case 'u':
635			_uquad = UARG();
636			base = DEC;
637			goto nosign;
638		  case 'X':
639			xdigs = "0123456789ABCDEF";
640			goto hex;
641		  case 'x':
642			xdigs = "0123456789abcdef";
643hex:			_uquad = UARG();
644			base = HEX;
645			/* leading 0x/X only if non-zero */
646			if (flags & ALT && _uquad != 0)
647				flags |= HEXPREFIX;
648
649			/* unsigned conversions */
650nosign:			sign = '\0';
651
652			/*
653			**  ``... diouXx conversions ... if a precision is
654			**  specified, the 0 flag will be ignored.''
655			**	-- ANSI X3J11
656			*/
657
658number:			if ((dprec = prec) >= 0)
659				flags &= ~ZEROPAD;
660
661			/*
662			**  ``The result of converting a zero value with an
663			**  explicit precision of zero is no characters.''
664			**	-- ANSI X3J11
665			*/
666
667			cp = buf + BUF;
668			if (_uquad != 0 || prec != 0)
669			{
670				/*
671				**  Unsigned mod is hard, and unsigned mod
672				**  by a constant is easier than that by
673				**  a variable; hence this switch.
674				*/
675
676				switch (base)
677				{
678				  case OCT:
679					do
680					{
681						*--cp = to_char(_uquad & 7);
682						_uquad >>= 3;
683					} while (_uquad);
684					/* handle octal leading 0 */
685					if (flags & ALT && *cp != '0')
686						*--cp = '0';
687					break;
688
689				  case DEC:
690					/* many numbers are 1 digit */
691					while (_uquad >= 10)
692					{
693						*--cp = to_char(_uquad % 10);
694						_uquad /= 10;
695					}
696					*--cp = to_char(_uquad);
697					break;
698
699				  case HEX:
700					do
701					{
702						*--cp = xdigs[_uquad & 15];
703						_uquad >>= 4;
704					} while (_uquad);
705					break;
706
707				  default:
708					cp = "bug in sm_io_vfprintf: bad base";
709					size = strlen(cp);
710					goto skipsize;
711				}
712			}
713			size = buf + BUF - cp;
714		  skipsize:
715			break;
716		  default:	/* "%?" prints ?, unless ? is NUL */
717			if (ch == '\0')
718				goto done;
719			/* pretend it was %c with argument ch */
720			cp = buf;
721			*cp = ch;
722			size = 1;
723			sign = '\0';
724			break;
725		}
726
727		/*
728		**  All reasonable formats wind up here.  At this point, `cp'
729		**  points to a string which (if not flags&LADJUST) should be
730		**  padded out to `width' places.  If flags&ZEROPAD, it should
731		**  first be prefixed by any sign or other prefix; otherwise,
732		**  it should be blank padded before the prefix is emitted.
733		**  After any left-hand padding and prefixing, emit zeroes
734		**  required by a decimal [diouxX] precision, then print the
735		**  string proper, then emit zeroes required by any leftover
736		**  floating precision; finally, if LADJUST, pad with blanks.
737		**
738		**  Compute actual size, so we know how much to pad.
739		**  size excludes decimal prec; realsz includes it.
740		*/
741
742		realsz = dprec > size ? dprec : size;
743		if (sign)
744			realsz++;
745		else if (flags & HEXPREFIX)
746			realsz+= 2;
747
748		/* right-adjusting blank padding */
749		if ((flags & (LADJUST|ZEROPAD)) == 0)
750			PAD(width - realsz, blanks);
751
752		/* prefix */
753		if (sign)
754		{
755			PRINT(&sign, 1);
756		}
757		else if (flags & HEXPREFIX)
758		{
759			ox[0] = '0';
760			ox[1] = ch;
761			PRINT(ox, 2);
762		}
763
764		/* right-adjusting zero padding */
765		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
766			PAD(width - realsz, zeroes);
767
768		/* leading zeroes from decimal precision */
769		PAD(dprec - size, zeroes);
770
771		/* the string or number proper */
772		PRINT(cp, size);
773		/* left-adjusting padding (always blank) */
774		if (flags & LADJUST)
775			PAD(width - realsz, blanks);
776
777		/* finally, adjust ret */
778		ret += width > realsz ? width : realsz;
779
780		FLUSH();	/* copy out the I/O vectors */
781	}
782done:
783	FLUSH();
784error:
785	SM_VA_END_COPY(orgap);
786	if ((argtable != NULL) && (argtable != statargtable))
787		sm_free(argtable);
788	return sm_error(fp) ? SM_IO_EOF : ret;
789	/* NOTREACHED */
790}
791
792/* Type ids for argument type table. */
793#define T_UNUSED	0
794#define T_SHORT		1
795#define T_U_SHORT	2
796#define TP_SHORT	3
797#define T_INT		4
798#define T_U_INT		5
799#define TP_INT		6
800#define T_LONG		7
801#define T_U_LONG	8
802#define TP_LONG		9
803#define T_QUAD		10
804#define T_U_QUAD	11
805#define TP_QUAD		12
806#define T_DOUBLE	13
807#define TP_CHAR		15
808#define TP_VOID		16
809
810/*
811**  SM_FIND_ARGUMENTS -- find all args when a positional parameter is found.
812**
813**  Find all arguments when a positional parameter is encountered.  Returns a
814**  table, indexed by argument number, of pointers to each arguments.  The
815**  initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
816**  It will be replaced with a malloc-ed one if it overflows.
817**
818**	Parameters:
819**		fmt0 -- formating directives
820**		ap -- vector list of data unit for formating consumption
821**		argtable -- an indexable table (returned) of 'ap'
822**
823**	Results:
824**		none.
825*/
826
827static void
828sm_find_arguments(fmt0, ap, argtable)
829	const char *fmt0;
830	SM_VA_LOCAL_DECL
831	va_list **argtable;
832{
833	register char *fmt;	/* format string */
834	register int ch;	/* character from fmt */
835	register int n, n2;	/* handy integer (short term usage) */
836	register char *cp;	/* handy char pointer (short term usage) */
837	register int flags;	/* flags as above */
838	unsigned char *typetable; /* table of types */
839	unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
840	int tablesize;		/* current size of type table */
841	int tablemax;		/* largest used index in table */
842	int nextarg;		/* 1-based argument index */
843
844	/* Add an argument type to the table, expanding if necessary. */
845#define ADDTYPE(type) \
846	((nextarg >= tablesize) ? \
847		(sm_grow_type_table_x(&typetable, &tablesize), 0) : 0, \
848	typetable[nextarg++] = type, \
849	(nextarg > tablemax) ? tablemax = nextarg : 0)
850
851#define ADDSARG() \
852	((flags & LONGINT) ? ADDTYPE(T_LONG) : \
853		((flags & SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
854
855#define ADDUARG() \
856	((flags & LONGINT) ? ADDTYPE(T_U_LONG) : \
857		((flags & SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
858
859	/* Add * arguments to the type array. */
860#define ADDASTER() \
861	n2 = 0; \
862	cp = fmt; \
863	while (is_digit(*cp)) \
864	{ \
865		n2 = 10 * n2 + to_digit(*cp); \
866		cp++; \
867	} \
868	if (*cp == '$') \
869	{ \
870		int hold = nextarg; \
871		nextarg = n2; \
872		ADDTYPE (T_INT); \
873		nextarg = hold; \
874		fmt = ++cp; \
875	} \
876	else \
877	{ \
878		ADDTYPE (T_INT); \
879	}
880	fmt = (char *) fmt0;
881	typetable = stattypetable;
882	tablesize = STATIC_ARG_TBL_SIZE;
883	tablemax = 0;
884	nextarg = 1;
885	(void) memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
886
887	/* Scan the format for conversions (`%' character). */
888	for (;;)
889	{
890		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
891			/* void */;
892		if (ch == '\0')
893			goto done;
894		fmt++;		/* skip over '%' */
895
896		flags = 0;
897
898rflag:		ch = *fmt++;
899reswitch:	switch (ch)
900		{
901		  case ' ':
902		  case '#':
903			goto rflag;
904		  case '*':
905			ADDASTER();
906			goto rflag;
907		  case '-':
908		  case '+':
909			goto rflag;
910		  case '.':
911			if ((ch = *fmt++) == '*')
912			{
913				ADDASTER();
914				goto rflag;
915			}
916			while (is_digit(ch))
917			{
918				ch = *fmt++;
919			}
920			goto reswitch;
921		  case '0':
922			goto rflag;
923		  case '1': case '2': case '3': case '4':
924		  case '5': case '6': case '7': case '8': case '9':
925			n = 0;
926			do
927			{
928				n = 10 * n + to_digit(ch);
929				ch = *fmt++;
930			} while (is_digit(ch));
931			if (ch == '$')
932			{
933				nextarg = n;
934				goto rflag;
935			}
936			goto reswitch;
937		  case 'h':
938			flags |= SHORTINT;
939			goto rflag;
940		  case 'l':
941			flags |= LONGINT;
942			goto rflag;
943		  case 'q':
944			flags |= QUADINT;
945			goto rflag;
946		  case 'c':
947			ADDTYPE(T_INT);
948			break;
949		  case 'D':
950			flags |= LONGINT;
951			/*FALLTHROUGH*/
952		  case 'd':
953		  case 'i':
954			if (flags & QUADINT)
955			{
956				ADDTYPE(T_QUAD);
957			}
958			else
959			{
960				ADDSARG();
961			}
962			break;
963		  case 'e':
964		  case 'E':
965		  case 'f':
966		  case 'g':
967		  case 'G':
968			ADDTYPE(T_DOUBLE);
969			break;
970		  case 'n':
971			if (flags & QUADINT)
972				ADDTYPE(TP_QUAD);
973			else if (flags & LONGINT)
974				ADDTYPE(TP_LONG);
975			else if (flags & SHORTINT)
976				ADDTYPE(TP_SHORT);
977			else
978				ADDTYPE(TP_INT);
979			continue;	/* no output */
980		  case 'O':
981			flags |= LONGINT;
982			/*FALLTHROUGH*/
983		  case 'o':
984			if (flags & QUADINT)
985				ADDTYPE(T_U_QUAD);
986			else
987				ADDUARG();
988			break;
989		  case 'p':
990			ADDTYPE(TP_VOID);
991			break;
992		  case 's':
993			ADDTYPE(TP_CHAR);
994			break;
995		  case 'U':
996			flags |= LONGINT;
997			/*FALLTHROUGH*/
998		  case 'u':
999			if (flags & QUADINT)
1000				ADDTYPE(T_U_QUAD);
1001			else
1002				ADDUARG();
1003			break;
1004		  case 'X':
1005		  case 'x':
1006			if (flags & QUADINT)
1007				ADDTYPE(T_U_QUAD);
1008			else
1009				ADDUARG();
1010			break;
1011		  default:	/* "%?" prints ?, unless ? is NUL */
1012			if (ch == '\0')
1013				goto done;
1014			break;
1015		}
1016	}
1017done:
1018	/* Build the argument table. */
1019	if (tablemax >= STATIC_ARG_TBL_SIZE)
1020	{
1021		*argtable = (va_list *)
1022		    sm_malloc(sizeof(va_list) * (tablemax + 1));
1023	}
1024
1025	for (n = 1; n <= tablemax; n++)
1026	{
1027		SM_VA_COPY((*argtable)[n], ap);
1028		switch (typetable [n])
1029		{
1030		  case T_UNUSED:
1031			(void) SM_VA_ARG(ap, int);
1032			break;
1033		  case T_SHORT:
1034			(void) SM_VA_ARG(ap, int);
1035			break;
1036		  case T_U_SHORT:
1037			(void) SM_VA_ARG(ap, int);
1038			break;
1039		  case TP_SHORT:
1040			(void) SM_VA_ARG(ap, short *);
1041			break;
1042		  case T_INT:
1043			(void) SM_VA_ARG(ap, int);
1044			break;
1045		  case T_U_INT:
1046			(void) SM_VA_ARG(ap, unsigned int);
1047			break;
1048		  case TP_INT:
1049			(void) SM_VA_ARG(ap, int *);
1050			break;
1051		  case T_LONG:
1052			(void) SM_VA_ARG(ap, long);
1053			break;
1054		  case T_U_LONG:
1055			(void) SM_VA_ARG(ap, unsigned long);
1056			break;
1057		  case TP_LONG:
1058			(void) SM_VA_ARG(ap, long *);
1059			break;
1060		  case T_QUAD:
1061			(void) SM_VA_ARG(ap, LONGLONG_T);
1062			break;
1063		  case T_U_QUAD:
1064			(void) SM_VA_ARG(ap, ULONGLONG_T);
1065			break;
1066		  case TP_QUAD:
1067			(void) SM_VA_ARG(ap, LONGLONG_T *);
1068			break;
1069		  case T_DOUBLE:
1070			(void) SM_VA_ARG(ap, double);
1071			break;
1072		  case TP_CHAR:
1073			(void) SM_VA_ARG(ap, char *);
1074			break;
1075		  case TP_VOID:
1076			(void) SM_VA_ARG(ap, void *);
1077			break;
1078		}
1079	}
1080
1081	if ((typetable != NULL) && (typetable != stattypetable))
1082		sm_free(typetable);
1083}
1084
1085/*
1086**  SM_GROW_TYPE_TABLE -- Increase the size of the type table.
1087**
1088**	Parameters:
1089**		tabletype -- type of table to grow
1090**		tablesize -- requested new table size
1091**
1092**	Results:
1093**		Raises an exception if can't allocate memory.
1094*/
1095
1096static void
1097sm_grow_type_table_x(typetable, tablesize)
1098	unsigned char **typetable;
1099	int *tablesize;
1100{
1101	unsigned char *oldtable = *typetable;
1102	int newsize = *tablesize * 2;
1103
1104	if (*tablesize == STATIC_ARG_TBL_SIZE)
1105	{
1106		*typetable = (unsigned char *) sm_malloc_x(sizeof(unsigned char)
1107							   * newsize);
1108		(void) memmove(*typetable, oldtable, *tablesize);
1109	}
1110	else
1111	{
1112		*typetable = (unsigned char *) sm_realloc_x(typetable,
1113					sizeof(unsigned char) * newsize);
1114	}
1115	(void) memset(&typetable [*tablesize], T_UNUSED,
1116		       (newsize - *tablesize));
1117
1118	*tablesize = newsize;
1119}
1120