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