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