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