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
sm_print(fp,timeout,uio)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
sm_bprintf(fp,fmt,ap)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
sm_io_vfprintf(fp,timeout,fmt0,ap)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
sm_find_arguments(fmt0,ap,argtable)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
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