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