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>
16 SM_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
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
sm_print(fp,timeout,uio)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 formatting 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 formatting
88 **
89 ** Results:
90 ** Failure: SM_IO_EOF and errno set
91 ** Success: number of data units used in the formatting
92 **
93 ** Side effects:
94 ** formatted o/p can be SM_IO_BUFSIZ length maximum
95 */
96
97 static int
sm_bprintf(fp,fmt,ap)98 sm_bprintf(fp, fmt, ap)
99 SM_FILE_T *fp;
100 const char *fmt;
101 va_list ap;
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 formatting for o/p
160 **
161 ** Parameters:
162 ** fp -- file pointer for o/p
163 ** timeout -- time to complete the print
164 ** fmt0 -- formatting directives
165 ** ap -- vectors with data units for formatting
166 **
167 ** Results:
168 ** Success: number of data units used for formatting
169 ** Failure: SM_IO_EOF and sets errno
170 */
171
172 int
sm_io_vfprintf(fp,timeout,fmt0,ap)173 sm_io_vfprintf(fp, timeout, fmt0, ap)
174 SM_FILE_T *fp;
175 int timeout;
176 const char *fmt0;
177 va_list ap;
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 #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";
643 hex: _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 */
650 nosign: sign = '\0';
651
652 /*
653 ** ``... diouXx conversions ... if a precision is
654 ** specified, the 0 flag will be ignored.''
655 ** -- ANSI X3J11
656 */
657
658 number: 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 }
782 done:
783 FLUSH();
784 error:
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 -- formatting directives
820 ** ap -- vector list of data unit for formatting consumption
821 ** argtable -- an indexable table (returned) of 'ap'
822 **
823 ** Results:
824 ** none.
825 */
826
827 static void
sm_find_arguments(fmt0,ap,argtable)828 sm_find_arguments(fmt0, ap, argtable)
829 const char *fmt0;
830 va_list ap;
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
898 rflag: ch = *fmt++;
899 reswitch: 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 }
1017 done:
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 SM_VA_END_COPY((*argtable)[n]);
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
sm_grow_type_table_x(typetable,tablesize)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