1 #include <ldns/config.h> 2 3 #ifndef HAVE_SNPRINTF 4 5 #include <ctype.h> 6 #include <sys/types.h> 7 8 /* Define this as a fall through, HAVE_STDARG_H is probably already set */ 9 10 #define HAVE_VARARGS_H 11 12 /************************************************************** 13 * Original: 14 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 15 * A bombproof version of doprnt (dopr) included. 16 * Sigh. This sort of thing is always nasty do deal with. Note that 17 * the version here does not include floating point... 18 * 19 * snprintf() is used instead of sprintf() as it does limit checks 20 * for string length. This covers a nasty loophole. 21 * 22 * The other functions are there to prevent NULL pointers from 23 * causing nast effects. 24 * 25 * More Recently: 26 * Brandon Long (blong@fiction.net) 9/15/96 for mutt 0.43 27 * This was ugly. It is still ugly. I opted out of floating point 28 * numbers, but the formatter understands just about everything 29 * from the normal C string format, at least as far as I can tell from 30 * the Solaris 2.5 printf(3S) man page. 31 * 32 * Brandon Long (blong@fiction.net) 10/22/97 for mutt 0.87.1 33 * Ok, added some minimal floating point support, which means this 34 * probably requires libm on most operating systems. Don't yet 35 * support the exponent (e,E) and sigfig (g,G). Also, fmtint() 36 * was pretty badly broken, it just wasn't being exercised in ways 37 * which showed it, so that's been fixed. Also, formated the code 38 * to mutt conventions, and removed dead code left over from the 39 * original. Also, there is now a builtin-test, just compile with: 40 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm 41 * and run snprintf for results. 42 * 43 **************************************************************/ 44 45 46 /* varargs declarations: */ 47 48 #if defined(HAVE_STDARG_H) 49 # include <stdarg.h> 50 # define HAVE_STDARGS /* let's hope that works everywhere (mj) */ 51 # define VA_LOCAL_DECL va_list ap 52 # define VA_START(f) va_start(ap, f) 53 # define VA_SHIFT(v,t) ; /* no-op for ANSI */ 54 # define VA_END va_end(ap) 55 #else 56 # if defined(HAVE_VARARGS_H) 57 # include <varargs.h> 58 # undef HAVE_STDARGS 59 # define VA_LOCAL_DECL va_list ap 60 # define VA_START(f) va_start(ap) /* f is ignored! */ 61 # define VA_SHIFT(v,t) v = va_arg(ap,t) 62 # define VA_END va_end(ap) 63 # else 64 /*XX ** NO VARARGS ** XX*/ 65 # endif 66 #endif 67 68 int snprintf (char *str, size_t count, const char *fmt, ...); 69 int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); 70 71 static void dopr (char *buffer, size_t maxlen, const char *format, 72 va_list args); 73 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, 74 char *value, int flags, int min, int max); 75 static void fmtint (char *buffer, size_t *currlen, size_t maxlen, 76 long value, int base, int min, int max, int flags); 77 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, 78 long double fvalue, int min, int max, int flags); 79 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); 80 81 int vsnprintf (char *str, size_t count, const char *fmt, va_list args) 82 { 83 str[0] = 0; 84 dopr(str, count, fmt, args); 85 return(strlen(str)); 86 } 87 88 /* VARARGS3 */ 89 #ifdef HAVE_STDARGS 90 int snprintf (char *str,size_t count,const char *fmt,...) 91 #else 92 int snprintf (va_alist) va_dcl 93 #endif 94 { 95 #ifndef HAVE_STDARGS 96 char *str; 97 size_t count; 98 char *fmt; 99 #endif 100 VA_LOCAL_DECL; 101 102 VA_START (fmt); 103 VA_SHIFT (str, char *); 104 VA_SHIFT (count, size_t ); 105 VA_SHIFT (fmt, char *); 106 (void) vsnprintf(str, count, fmt, ap); 107 VA_END; 108 return(strlen(str)); 109 } 110 111 /* 112 * dopr(): poor man's version of doprintf 113 */ 114 115 /* format read states */ 116 #define DP_S_DEFAULT 0 117 #define DP_S_FLAGS 1 118 #define DP_S_MIN 2 119 #define DP_S_DOT 3 120 #define DP_S_MAX 4 121 #define DP_S_MOD 5 122 #define DP_S_CONV 6 123 #define DP_S_DONE 7 124 125 /* format flags - Bits */ 126 #define DP_F_MINUS 1 127 #define DP_F_PLUS 2 128 #define DP_F_SPACE 4 129 #define DP_F_NUM 8 130 #define DP_F_ZERO 16 131 #define DP_F_UP 32 132 133 /* Conversion Flags */ 134 #define DP_C_SHORT 1 135 #define DP_C_LONG 2 136 #define DP_C_LDOUBLE 3 137 138 #define char_to_int(p) (p - '0') 139 #define MAX(p,q) ((p >= q) ? p : q) 140 141 static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) 142 { 143 char ch; 144 long value; 145 long double fvalue; 146 char *strvalue; 147 int min; 148 int max; 149 int state; 150 int flags; 151 int cflags; 152 size_t currlen; 153 154 state = DP_S_DEFAULT; 155 currlen = flags = cflags = min = 0; 156 max = -1; 157 ch = *format++; 158 159 while (state != DP_S_DONE) 160 { 161 if ((ch == '\0') || (currlen >= maxlen)) 162 state = DP_S_DONE; 163 164 switch(state) 165 { 166 case DP_S_DEFAULT: 167 if (ch == '%') 168 state = DP_S_FLAGS; 169 else 170 dopr_outch (buffer, &currlen, maxlen, ch); 171 ch = *format++; 172 break; 173 case DP_S_FLAGS: 174 switch (ch) 175 { 176 case '-': 177 flags |= DP_F_MINUS; 178 ch = *format++; 179 break; 180 case '+': 181 flags |= DP_F_PLUS; 182 ch = *format++; 183 break; 184 case ' ': 185 flags |= DP_F_SPACE; 186 ch = *format++; 187 break; 188 case '#': 189 flags |= DP_F_NUM; 190 ch = *format++; 191 break; 192 case '0': 193 flags |= DP_F_ZERO; 194 ch = *format++; 195 break; 196 default: 197 state = DP_S_MIN; 198 break; 199 } 200 break; 201 case DP_S_MIN: 202 if (isdigit((int) ch)) 203 { 204 min = 10*min + char_to_int (ch); 205 ch = *format++; 206 } 207 else if (ch == '*') 208 { 209 min = va_arg (args, int); 210 ch = *format++; 211 state = DP_S_DOT; 212 } 213 else 214 state = DP_S_DOT; 215 break; 216 case DP_S_DOT: 217 if (ch == '.') 218 { 219 state = DP_S_MAX; 220 ch = *format++; 221 } 222 else 223 state = DP_S_MOD; 224 break; 225 case DP_S_MAX: 226 if (isdigit((int) ch)) 227 { 228 if (max < 0) 229 max = 0; 230 max = 10*max + char_to_int (ch); 231 ch = *format++; 232 } 233 else if (ch == '*') 234 { 235 max = va_arg (args, int); 236 ch = *format++; 237 state = DP_S_MOD; 238 } 239 else 240 state = DP_S_MOD; 241 break; 242 case DP_S_MOD: 243 /* Currently, we don't support Long Long, bummer */ 244 switch (ch) 245 { 246 case 'h': 247 cflags = DP_C_SHORT; 248 ch = *format++; 249 break; 250 case 'l': 251 cflags = DP_C_LONG; 252 ch = *format++; 253 break; 254 case 'L': 255 cflags = DP_C_LDOUBLE; 256 ch = *format++; 257 break; 258 default: 259 break; 260 } 261 state = DP_S_CONV; 262 break; 263 case DP_S_CONV: 264 switch (ch) 265 { 266 case 'd': 267 case 'i': 268 if (cflags == DP_C_SHORT) 269 value = va_arg (args, int); 270 else if (cflags == DP_C_LONG) 271 value = va_arg (args, long int); 272 else 273 value = va_arg (args, int); 274 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); 275 break; 276 case 'o': 277 flags &= ~DP_F_PLUS; 278 if (cflags == DP_C_SHORT) 279 value = va_arg (args, unsigned int); 280 else if (cflags == DP_C_LONG) 281 value = va_arg (args, unsigned long int); 282 else 283 value = va_arg (args, unsigned int); 284 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); 285 break; 286 case 'u': 287 flags &= ~DP_F_PLUS; 288 if (cflags == DP_C_SHORT) 289 value = va_arg (args, unsigned int); 290 else if (cflags == DP_C_LONG) 291 value = va_arg (args, unsigned long int); 292 else 293 value = va_arg (args, unsigned int); 294 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); 295 break; 296 case 'X': 297 flags |= DP_F_UP; 298 case 'x': 299 flags &= ~DP_F_PLUS; 300 if (cflags == DP_C_SHORT) 301 value = va_arg (args, unsigned int); 302 else if (cflags == DP_C_LONG) 303 value = va_arg (args, unsigned long int); 304 else 305 value = va_arg (args, unsigned int); 306 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); 307 break; 308 case 'f': 309 if (cflags == DP_C_LDOUBLE) 310 fvalue = va_arg (args, long double); 311 else 312 fvalue = va_arg (args, double); 313 /* um, floating point? */ 314 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); 315 break; 316 case 'E': 317 flags |= DP_F_UP; 318 case 'e': 319 if (cflags == DP_C_LDOUBLE) 320 fvalue = va_arg (args, long double); 321 else 322 fvalue = va_arg (args, double); 323 break; 324 case 'G': 325 flags |= DP_F_UP; 326 case 'g': 327 if (cflags == DP_C_LDOUBLE) 328 fvalue = va_arg (args, long double); 329 else 330 fvalue = va_arg (args, double); 331 break; 332 case 'c': 333 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); 334 break; 335 case 's': 336 strvalue = va_arg (args, char *); 337 if (max < 0) 338 max = maxlen; /* ie, no max */ 339 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); 340 break; 341 case 'p': 342 strvalue = va_arg (args, void *); 343 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); 344 break; 345 case 'n': 346 if (cflags == DP_C_SHORT) 347 { 348 short int *num; 349 num = va_arg (args, short int *); 350 *num = currlen; 351 } 352 else if (cflags == DP_C_LONG) 353 { 354 long int *num; 355 num = va_arg (args, long int *); 356 *num = currlen; 357 } 358 else 359 { 360 int *num; 361 num = va_arg (args, int *); 362 *num = currlen; 363 } 364 break; 365 case '%': 366 dopr_outch (buffer, &currlen, maxlen, ch); 367 break; 368 case 'w': 369 /* not supported yet, treat as next char */ 370 ch = *format++; 371 break; 372 default: 373 /* Unknown, skip */ 374 break; 375 } 376 ch = *format++; 377 state = DP_S_DEFAULT; 378 flags = cflags = min = 0; 379 max = -1; 380 break; 381 case DP_S_DONE: 382 break; 383 default: 384 /* hmm? */ 385 break; /* some picky compilers need this */ 386 } 387 } 388 if (currlen < maxlen - 1) 389 buffer[currlen] = '\0'; 390 else 391 buffer[maxlen - 1] = '\0'; 392 } 393 394 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, 395 char *value, int flags, int min, int max) 396 { 397 int padlen, strln; /* amount to pad */ 398 int cnt = 0; 399 400 if (value == 0) 401 { 402 value = (char *) "<NULL>"; 403 } 404 405 for (strln = 0; value[strln]; ++strln); /* strlen */ 406 padlen = min - strln; 407 if (padlen < 0) 408 padlen = 0; 409 if (flags & DP_F_MINUS) 410 padlen = -padlen; /* Left Justify */ 411 412 while ((padlen > 0) && (cnt < max)) 413 { 414 dopr_outch (buffer, currlen, maxlen, ' '); 415 --padlen; 416 ++cnt; 417 } 418 while (*value && (cnt < max)) 419 { 420 dopr_outch (buffer, currlen, maxlen, *value++); 421 ++cnt; 422 } 423 while ((padlen < 0) && (cnt < max)) 424 { 425 dopr_outch (buffer, currlen, maxlen, ' '); 426 ++padlen; 427 ++cnt; 428 } 429 } 430 431 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ 432 433 static void fmtint (char *buffer, size_t *currlen, size_t maxlen, 434 long value, int base, int min, int max, int flags) 435 { 436 int signvalue = 0; 437 unsigned long uvalue; 438 char convert[20]; 439 int place = 0; 440 int spadlen = 0; /* amount to space pad */ 441 int zpadlen = 0; /* amount to zero pad */ 442 int caps = 0; 443 444 if (max < 0) 445 max = 0; 446 447 uvalue = value; 448 if( value < 0 ) { 449 signvalue = '-'; 450 uvalue = -value; 451 } 452 else 453 if (flags & DP_F_PLUS) /* Do a sign (+/i) */ 454 signvalue = '+'; 455 else 456 if (flags & DP_F_SPACE) 457 signvalue = ' '; 458 459 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ 460 461 do { 462 convert[place++] = 463 (caps? "0123456789ABCDEF":"0123456789abcdef") 464 [uvalue % (unsigned)base ]; 465 uvalue = (uvalue / (unsigned)base ); 466 } while(uvalue && (place < 20)); 467 if (place == 20) place--; 468 convert[place] = 0; 469 470 zpadlen = max - place; 471 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); 472 if (zpadlen < 0) zpadlen = 0; 473 if (spadlen < 0) spadlen = 0; 474 if (flags & DP_F_ZERO) 475 { 476 zpadlen = MAX(zpadlen, spadlen); 477 spadlen = 0; 478 } 479 if (flags & DP_F_MINUS) 480 spadlen = -spadlen; /* Left Justifty */ 481 482 #ifdef DEBUG_SNPRINTF 483 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", 484 zpadlen, spadlen, min, max, place)); 485 #endif 486 487 /* Spaces */ 488 while (spadlen > 0) 489 { 490 dopr_outch (buffer, currlen, maxlen, ' '); 491 --spadlen; 492 } 493 494 /* Sign */ 495 if (signvalue) 496 dopr_outch (buffer, currlen, maxlen, signvalue); 497 498 /* Zeros */ 499 if (zpadlen > 0) 500 { 501 while (zpadlen > 0) 502 { 503 dopr_outch (buffer, currlen, maxlen, '0'); 504 --zpadlen; 505 } 506 } 507 508 /* Digits */ 509 while (place > 0) 510 dopr_outch (buffer, currlen, maxlen, convert[--place]); 511 512 /* Left Justified spaces */ 513 while (spadlen < 0) { 514 dopr_outch (buffer, currlen, maxlen, ' '); 515 ++spadlen; 516 } 517 } 518 519 static long double abs_val (long double value) 520 { 521 long double result = value; 522 523 if (value < 0) 524 result = -value; 525 526 return result; 527 } 528 529 static double pow10 (double exp) 530 { 531 long double result = 1; 532 533 while (exp) 534 { 535 result *= 10; 536 exp--; 537 } 538 539 return result; 540 } 541 542 static double round (double value) 543 { 544 long intpart; 545 546 intpart = value; 547 value = value - intpart; 548 if (value >= 0.5) 549 intpart++; 550 551 return intpart; 552 } 553 554 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, 555 long double fvalue, int min, int max, int flags) 556 { 557 int signvalue = 0; 558 long double ufvalue; 559 char iconvert[20]; 560 char fconvert[20]; 561 int iplace = 0; 562 int fplace = 0; 563 int padlen = 0; /* amount to pad */ 564 int zpadlen = 0; 565 int caps = 0; 566 long intpart; 567 long fracpart; 568 569 /* 570 * AIX manpage says the default is 0, but Solaris says the default 571 * is 6, and sprintf on AIX defaults to 6 572 */ 573 if (max < 0) 574 max = 6; 575 576 ufvalue = abs_val (fvalue); 577 578 if (fvalue < 0) 579 signvalue = '-'; 580 else 581 if (flags & DP_F_PLUS) /* Do a sign (+/i) */ 582 signvalue = '+'; 583 else 584 if (flags & DP_F_SPACE) 585 signvalue = ' '; 586 587 #if 0 588 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ 589 #endif 590 591 intpart = ufvalue; 592 593 /* 594 * Sorry, we only support 9 digits past the decimal because of our 595 * conversion method 596 */ 597 if (max > 9) 598 max = 9; 599 600 /* We "cheat" by converting the fractional part to integer by 601 * multiplying by a factor of 10 602 */ 603 fracpart = round ((pow10 (max)) * (ufvalue - intpart)); 604 605 if (fracpart >= pow10 (max)) 606 { 607 intpart++; 608 fracpart -= pow10 (max); 609 } 610 611 #ifdef DEBUG_SNPRINTF 612 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); 613 #endif 614 615 /* Convert integer part */ 616 do { 617 iconvert[iplace++] = 618 (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10]; 619 intpart = (intpart / 10); 620 } while(intpart && (iplace < 20)); 621 if (iplace == 20) iplace--; 622 iconvert[iplace] = 0; 623 624 /* Convert fractional part */ 625 do { 626 fconvert[fplace++] = 627 (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10]; 628 fracpart = (fracpart / 10); 629 } while(fracpart && (fplace < 20)); 630 if (fplace == 20) fplace--; 631 fconvert[fplace] = 0; 632 633 /* -1 for decimal point, another -1 if we are printing a sign */ 634 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 635 zpadlen = max - fplace; 636 if (zpadlen < 0) 637 zpadlen = 0; 638 if (padlen < 0) 639 padlen = 0; 640 if (flags & DP_F_MINUS) 641 padlen = -padlen; /* Left Justifty */ 642 643 if ((flags & DP_F_ZERO) && (padlen > 0)) 644 { 645 if (signvalue) 646 { 647 dopr_outch (buffer, currlen, maxlen, signvalue); 648 --padlen; 649 signvalue = 0; 650 } 651 while (padlen > 0) 652 { 653 dopr_outch (buffer, currlen, maxlen, '0'); 654 --padlen; 655 } 656 } 657 while (padlen > 0) 658 { 659 dopr_outch (buffer, currlen, maxlen, ' '); 660 --padlen; 661 } 662 if (signvalue) 663 dopr_outch (buffer, currlen, maxlen, signvalue); 664 665 while (iplace > 0) 666 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); 667 668 /* 669 * Decimal point. This should probably use locale to find the correct 670 * char to print out. 671 */ 672 dopr_outch (buffer, currlen, maxlen, '.'); 673 674 while (zpadlen > 0) 675 { 676 dopr_outch (buffer, currlen, maxlen, '0'); 677 --zpadlen; 678 } 679 680 while (fplace > 0) 681 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); 682 683 while (padlen < 0) 684 { 685 dopr_outch (buffer, currlen, maxlen, ' '); 686 ++padlen; 687 } 688 } 689 690 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) 691 { 692 if (*currlen < maxlen) 693 buffer[(*currlen)++] = c; 694 } 695 696 #ifdef TEST_SNPRINTF 697 #ifndef LONG_STRING 698 #define LONG_STRING 1024 699 #endif 700 int main (void) 701 { 702 char buf1[LONG_STRING]; 703 char buf2[LONG_STRING]; 704 char *fp_fmt[] = { 705 "%-1.5f", 706 "%1.5f", 707 "%123.9f", 708 "%10.5f", 709 "% 10.5f", 710 "%+22.9f", 711 "%+4.9f", 712 "%01.3f", 713 "%4f", 714 "%3.1f", 715 "%3.2f", 716 NULL 717 }; 718 double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 719 0.9996, 1.996, 4.136, 0}; 720 char *int_fmt[] = { 721 "%-1.5d", 722 "%1.5d", 723 "%123.9d", 724 "%5.5d", 725 "%10.5d", 726 "% 10.5d", 727 "%+22.33d", 728 "%01.3d", 729 "%4d", 730 NULL 731 }; 732 long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; 733 int x, y; 734 int fail = 0; 735 int num = 0; 736 737 printf ("Testing snprintf format codes against system sprintf...\n"); 738 739 for (x = 0; fp_fmt[x] != NULL ; x++) 740 for (y = 0; fp_nums[y] != 0 ; y++) 741 { 742 snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]); 743 sprintf (buf2, fp_fmt[x], fp_nums[y]); 744 if (strcmp (buf1, buf2)) 745 { 746 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", 747 fp_fmt[x], buf1, buf2); 748 fail++; 749 } 750 num++; 751 } 752 753 for (x = 0; int_fmt[x] != NULL ; x++) 754 for (y = 0; int_nums[y] != 0 ; y++) 755 { 756 snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]); 757 sprintf (buf2, int_fmt[x], int_nums[y]); 758 if (strcmp (buf1, buf2)) 759 { 760 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", 761 int_fmt[x], buf1, buf2); 762 fail++; 763 } 764 num++; 765 } 766 printf ("%d tests failed out of %d.\n", fail, num); 767 } 768 #endif /* SNPRINTF_TEST */ 769 770 #endif /* !HAVE_SNPRINTF */ 771