xref: /freebsd/contrib/sendmail/libsm/vfprintf.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
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>
16 SM_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 
36 static int	sm_bprintf __P((SM_FILE_T *, const char *, va_list));
37 static void	sm_find_arguments __P((const char *, va_list , va_list **));
38 static void	sm_grow_type_table_x __P((unsigned char **, int *));
39 static 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 
57 static int
58 sm_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 
97 static int
98 sm_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 
172 int
173 sm_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 
362 rflag:		ch = *fmt++;
363 reswitch:	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";
633 hex:			_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 */
640 nosign:			sign = '\0';
641 
642 			/*
643 			**  ``... diouXx conversions ... if a precision is
644 			**  specified, the 0 flag will be ignored.''
645 			**	-- ANSI X3J11
646 			*/
647 
648 number:			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 	}
772 done:
773 	FLUSH();
774 error:
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 
816 static void
817 sm_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 
887 rflag:		ch = *fmt++;
888 reswitch:	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 	}
1006 done:
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 
1085 static void
1086 sm_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