1/* This file is NOT part of Wget, but is used by Wget on the systems 2 where vsnprintf() is not defined. It has been written by Patrick 3 Powell and modified by other people. All the copyright and other 4 notices have been left intact. 5 6 My changes are documented at the bottom, along with other changes. 7 I hereby place my modifications to this file under the public 8 domain. */ 9 10/* 11 * Copyright Patrick Powell 1995 12 * This code is based on code written by Patrick Powell (papowell@astart.com) 13 * It may be used for any purpose as long as this notice remains intact 14 * on all source code distributions 15 */ 16 17/************************************************************** 18 * Original: 19 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 20 * A bombproof version of doprnt (dopr) included. 21 * Sigh. This sort of thing is always nasty do deal with. Note that 22 * the version here does not include floating point... 23 * 24 * snprintf() is used instead of sprintf() as it does limit checks 25 * for string length. This covers a nasty loophole. 26 * 27 * The other functions are there to prevent NULL pointers from 28 * causing nast effects. 29 * 30 * More Recently: 31 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 32 * This was ugly. It is still ugly. I opted out of floating point 33 * numbers, but the formatter understands just about everything 34 * from the normal C string format, at least as far as I can tell from 35 * the Solaris 2.5 printf(3S) man page. 36 * 37 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 38 * Ok, added some minimal floating point support, which means this 39 * probably requires libm on most operating systems. Don't yet 40 * support the exponent (e,E) and sigfig (g,G). Also, fmtint() 41 * was pretty badly broken, it just wasn't being exercised in ways 42 * which showed it, so that's been fixed. Also, formated the code 43 * to mutt conventions, and removed dead code left over from the 44 * original. Also, there is now a builtin-test, just compile with: 45 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm 46 * and run snprintf for results. 47 * 48 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i 49 * The PGP code was using unsigned hexadecimal formats. 50 * Unfortunately, unsigned formats simply didn't work. 51 * 52 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8 53 * The original code assumed that both snprintf() and vsnprintf() were 54 * missing. Some systems only have snprintf() but not vsnprintf(), so 55 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. 56 * 57 * Andrew Tridgell (tridge@samba.org) Oct 1998 58 * fixed handling of %.0f 59 * added test for HAVE_LONG_DOUBLE 60 * 61 * Russ Allbery <rra@stanford.edu> 2000-08-26 62 * fixed return value to comply with C99 63 * fixed handling of snprintf(NULL, ...) 64 * 65 * Hrvoje Niksic <hniksic@xemacs.org> 2000-11-04 66 * include <config.h> instead of "config.h". 67 * moved TEST_SNPRINTF stuff out of HAVE_SNPRINTF ifdef. 68 * include <stdio.h> for NULL. 69 * added support and test cases for long long. 70 * don't declare argument types to (v)snprintf if stdarg is not used. 71 * use int instead of short int as 2nd arg to va_arg. 72 * 73 * alexk (INN) 2002-08-21 74 * use LLONG in fmtfp to handle more characters during floating 75 * point conversion. 76 * 77 * herb (Samba) 2002-12-19 78 * actually print args for %g and %e 79 * 80 * Hrvoje Niksic <hniksic@xemacs.org> 2005-04-15 81 * write function definitions in the ansi2knr-friendly way. 82 * if string precision is specified, don't read VALUE past it. 83 * fix bug in fmtfp that caused 0.01 to be printed as 0.1. 84 * don't include <ctype.h> because none of it is used. 85 * interpret precision as number of significant digits with %g 86 * omit trailing decimal zeros with %g 87 * 88 **************************************************************/ 89 90#include "wget.h" 91 92/* For testing purposes, always compile in the code. */ 93#ifdef TEST_SNPRINTF 94# undef HAVE_SNPRINTF 95# undef HAVE_VSNPRINTF 96# ifndef SIZEOF_LONG_LONG 97# ifdef __GNUC__ 98# define SIZEOF_LONG_LONG 8 99# endif 100# endif 101#endif 102 103#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) 104 105#include <string.h> 106#include <sys/types.h> 107#include <stdio.h> /* for NULL */ 108 109#include <stdarg.h> 110 111#ifdef HAVE_LONG_DOUBLE 112#define LDOUBLE long double 113#else 114#define LDOUBLE double 115#endif 116 117#if SIZEOF_LONG_LONG != 0 118# define LLONG long long 119#else 120# define LLONG long 121#endif 122 123/* If we're running the test suite, rename snprintf and vsnprintf to 124 avoid conflicts with the system version. */ 125#ifdef TEST_SNPRINTF 126# define snprintf test_snprintf 127# define vsnprintf test_vsnprintf 128#endif 129 130int snprintf (char *str, size_t count, const char *fmt, ...); 131int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); 132 133static int dopr (char *buffer, size_t maxlen, const char *format, 134 va_list args); 135static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, 136 const char *value, int flags, int min, int max); 137static int fmtint (char *buffer, size_t *currlen, size_t maxlen, 138 LLONG value, int base, int min, int max, int flags); 139static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, 140 LDOUBLE fvalue, int min, int max, int flags); 141static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c); 142 143/* 144 * dopr(): poor man's version of doprintf 145 */ 146 147/* format read states */ 148#define DP_S_DEFAULT 0 149#define DP_S_FLAGS 1 150#define DP_S_MIN 2 151#define DP_S_DOT 3 152#define DP_S_MAX 4 153#define DP_S_MOD 5 154#define DP_S_MOD_L 6 155#define DP_S_CONV 7 156#define DP_S_DONE 8 157 158/* format flags - Bits */ 159#define DP_F_MINUS (1 << 0) 160#define DP_F_PLUS (1 << 1) 161#define DP_F_SPACE (1 << 2) 162#define DP_F_NUM (1 << 3) 163#define DP_F_ZERO (1 << 4) 164#define DP_F_UP (1 << 5) 165#define DP_F_UNSIGNED (1 << 6) 166#define DP_F_FP_G (1 << 7) 167 168/* Conversion Flags */ 169#define DP_C_SHORT 1 170#define DP_C_LONG 2 171#define DP_C_LLONG 3 172#define DP_C_LDOUBLE 4 173 174#define char_to_int(p) (p - '0') 175#define MAX(p,q) ((p >= q) ? p : q) 176#define MIN(p,q) ((p <= q) ? p : q) 177 178static int dopr (char *buffer, size_t maxlen, const char *format, va_list args) 179{ 180 char ch; 181 LLONG value; 182 LDOUBLE fvalue; 183 char *strvalue; 184 int min; 185 int max; 186 int state; 187 int flags; 188 int cflags; 189 int total; 190 size_t currlen; 191 192 state = DP_S_DEFAULT; 193 currlen = flags = cflags = min = 0; 194 max = -1; 195 ch = *format++; 196 total = 0; 197 198 while (state != DP_S_DONE) 199 { 200 if (ch == '\0') 201 state = DP_S_DONE; 202 203 switch(state) 204 { 205 case DP_S_DEFAULT: 206 if (ch == '%') 207 state = DP_S_FLAGS; 208 else 209 total += dopr_outch (buffer, &currlen, maxlen, ch); 210 ch = *format++; 211 break; 212 case DP_S_FLAGS: 213 switch (ch) 214 { 215 case '-': 216 flags |= DP_F_MINUS; 217 ch = *format++; 218 break; 219 case '+': 220 flags |= DP_F_PLUS; 221 ch = *format++; 222 break; 223 case ' ': 224 flags |= DP_F_SPACE; 225 ch = *format++; 226 break; 227 case '#': 228 flags |= DP_F_NUM; 229 ch = *format++; 230 break; 231 case '0': 232 flags |= DP_F_ZERO; 233 ch = *format++; 234 break; 235 default: 236 state = DP_S_MIN; 237 break; 238 } 239 break; 240 case DP_S_MIN: 241 if ('0' <= ch && ch <= '9') 242 { 243 min = 10*min + char_to_int (ch); 244 ch = *format++; 245 } 246 else if (ch == '*') 247 { 248 min = va_arg (args, int); 249 ch = *format++; 250 state = DP_S_DOT; 251 } 252 else 253 state = DP_S_DOT; 254 break; 255 case DP_S_DOT: 256 if (ch == '.') 257 { 258 state = DP_S_MAX; 259 ch = *format++; 260 } 261 else 262 state = DP_S_MOD; 263 break; 264 case DP_S_MAX: 265 if ('0' <= ch && ch <= '9') 266 { 267 if (max < 0) 268 max = 0; 269 max = 10*max + char_to_int (ch); 270 ch = *format++; 271 } 272 else if (ch == '*') 273 { 274 max = va_arg (args, int); 275 ch = *format++; 276 state = DP_S_MOD; 277 } 278 else 279 state = DP_S_MOD; 280 break; 281 case DP_S_MOD: 282 switch (ch) 283 { 284 case 'h': 285 cflags = DP_C_SHORT; 286 ch = *format++; 287 break; 288 case 'l': 289 cflags = DP_C_LONG; 290 ch = *format++; 291 break; 292 case 'L': 293 cflags = DP_C_LDOUBLE; 294 ch = *format++; 295 break; 296 default: 297 break; 298 } 299 if (cflags != DP_C_LONG) 300 state = DP_S_CONV; 301 else 302 state = DP_S_MOD_L; 303 break; 304 case DP_S_MOD_L: 305 switch (ch) 306 { 307 case 'l': 308 cflags = DP_C_LLONG; 309 ch = *format++; 310 break; 311 default: 312 break; 313 } 314 state = DP_S_CONV; 315 break; 316 case DP_S_CONV: 317 switch (ch) 318 { 319 case 'd': 320 case 'i': 321 if (cflags == DP_C_SHORT) 322 value = (short int) va_arg (args, int); 323 else if (cflags == DP_C_LONG) 324 value = va_arg (args, long int); 325 else if (cflags == DP_C_LLONG) 326 value = va_arg (args, LLONG); 327 else 328 value = va_arg (args, int); 329 total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); 330 break; 331 case 'o': 332 flags |= DP_F_UNSIGNED; 333 if (cflags == DP_C_SHORT) 334 value = (unsigned short int) va_arg (args, unsigned int); 335 else if (cflags == DP_C_LONG) 336 value = va_arg (args, unsigned long int); 337 else if (cflags == DP_C_LLONG) 338 value = va_arg (args, unsigned LLONG); 339 else 340 value = va_arg (args, unsigned int); 341 total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); 342 break; 343 case 'u': 344 flags |= DP_F_UNSIGNED; 345 if (cflags == DP_C_SHORT) 346 value = (unsigned short int) va_arg (args, unsigned int); 347 else if (cflags == DP_C_LONG) 348 value = va_arg (args, unsigned long int); 349 else if (cflags == DP_C_LLONG) 350 value = va_arg (args, unsigned LLONG); 351 else 352 value = va_arg (args, unsigned int); 353 total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); 354 break; 355 case 'X': 356 flags |= DP_F_UP; 357 case 'x': 358 flags |= DP_F_UNSIGNED; 359 if (cflags == DP_C_SHORT) 360 value = (unsigned short int) va_arg (args, unsigned int); 361 else if (cflags == DP_C_LONG) 362 value = va_arg (args, unsigned long int); 363 else if (cflags == DP_C_LLONG) 364 value = va_arg (args, unsigned LLONG); 365 else 366 value = va_arg (args, unsigned int); 367 total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); 368 break; 369 case 'f': 370 if (cflags == DP_C_LDOUBLE) 371 fvalue = va_arg (args, LDOUBLE); 372 else 373 fvalue = va_arg (args, double); 374 total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); 375 break; 376 case 'E': 377 flags |= DP_F_UP; 378 case 'e': 379 if (cflags == DP_C_LDOUBLE) 380 fvalue = va_arg (args, LDOUBLE); 381 else 382 fvalue = va_arg (args, double); 383 total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); 384 break; 385 case 'G': 386 flags |= DP_F_UP; 387 case 'g': 388 flags |= DP_F_FP_G; 389 if (cflags == DP_C_LDOUBLE) 390 fvalue = va_arg (args, LDOUBLE); 391 else 392 fvalue = va_arg (args, double); 393 if (max == 0) 394 /* C99 says: if precision [for %g] is zero, it is taken as one */ 395 max = 1; 396 total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); 397 break; 398 case 'c': 399 total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); 400 break; 401 case 's': 402 strvalue = va_arg (args, char *); 403 total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); 404 break; 405 case 'p': 406 strvalue = va_arg (args, void *); 407 total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, 408 max, flags); 409 break; 410 case 'n': 411 if (cflags == DP_C_SHORT) 412 { 413 short int *num; 414 num = va_arg (args, short int *); 415 *num = currlen; 416 } 417 else if (cflags == DP_C_LONG) 418 { 419 long int *num; 420 num = va_arg (args, long int *); 421 *num = currlen; 422 } 423 else if (cflags == DP_C_LLONG) 424 { 425 LLONG *num; 426 num = va_arg (args, LLONG *); 427 *num = currlen; 428 } 429 else 430 { 431 int *num; 432 num = va_arg (args, int *); 433 *num = currlen; 434 } 435 break; 436 case '%': 437 total += dopr_outch (buffer, &currlen, maxlen, ch); 438 break; 439 case 'w': 440 /* not supported yet, treat as next char */ 441 ch = *format++; 442 break; 443 default: 444 /* Unknown, skip */ 445 break; 446 } 447 ch = *format++; 448 state = DP_S_DEFAULT; 449 flags = cflags = min = 0; 450 max = -1; 451 break; 452 case DP_S_DONE: 453 break; 454 default: 455 /* hmm? */ 456 break; /* some picky compilers need this */ 457 } 458 } 459 if (buffer != NULL) 460 { 461 if (currlen < maxlen - 1) 462 buffer[currlen] = '\0'; 463 else 464 buffer[maxlen - 1] = '\0'; 465 } 466 return total; 467} 468 469static int fmtstr (char *buffer, size_t *currlen, size_t maxlen, 470 const char *value, int flags, int min, int max) 471{ 472 int padlen, strln; /* amount to pad */ 473 int cnt = 0; 474 int total = 0; 475 476 if (value == 0) 477 { 478 value = "(null)"; 479 } 480 481 if (max < 0) 482 strln = strlen (value); 483 else 484 /* When precision is specified, don't read VALUE past precision. */ 485 /*strln = strnlen (value, max);*/ 486 for (strln = 0; strln < max && value[strln]; ++strln) 487 ; 488 padlen = min - strln; 489 if (padlen < 0) 490 padlen = 0; 491 if (flags & DP_F_MINUS) 492 padlen = -padlen; /* Left Justify */ 493 494 while (padlen > 0) 495 { 496 total += dopr_outch (buffer, currlen, maxlen, ' '); 497 --padlen; 498 } 499 while (*value && ((max < 0) || (cnt < max))) 500 { 501 total += dopr_outch (buffer, currlen, maxlen, *value++); 502 ++cnt; 503 } 504 while (padlen < 0) 505 { 506 total += dopr_outch (buffer, currlen, maxlen, ' '); 507 ++padlen; 508 } 509 return total; 510} 511 512/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ 513 514static int fmtint (char *buffer, size_t *currlen, size_t maxlen, 515 LLONG value, int base, int min, int max, int flags) 516{ 517 int signvalue = 0; 518 unsigned LLONG uvalue; 519 char convert[24]; 520 int place = 0; 521 int spadlen = 0; /* amount to space pad */ 522 int zpadlen = 0; /* amount to zero pad */ 523 const char *digits; 524 int total = 0; 525 526 if (max < 0) 527 max = 0; 528 529 uvalue = value; 530 531 if(!(flags & DP_F_UNSIGNED)) 532 { 533 if( value < 0 ) { 534 signvalue = '-'; 535 uvalue = -value; 536 } 537 else 538 if (flags & DP_F_PLUS) /* Do a sign (+/i) */ 539 signvalue = '+'; 540 else 541 if (flags & DP_F_SPACE) 542 signvalue = ' '; 543 } 544 545 if (flags & DP_F_UP) 546 /* Should characters be upper case? */ 547 digits = "0123456789ABCDEF"; 548 else 549 digits = "0123456789abcdef"; 550 551 do { 552 convert[place++] = digits[uvalue % (unsigned)base]; 553 uvalue = (uvalue / (unsigned)base ); 554 } while(uvalue && (place < sizeof (convert))); 555 if (place == sizeof (convert)) place--; 556 convert[place] = 0; 557 558 zpadlen = max - place; 559 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); 560 if (zpadlen < 0) zpadlen = 0; 561 if (spadlen < 0) spadlen = 0; 562 if (flags & DP_F_ZERO) 563 { 564 zpadlen = MAX(zpadlen, spadlen); 565 spadlen = 0; 566 } 567 if (flags & DP_F_MINUS) 568 spadlen = -spadlen; /* Left Justifty */ 569 570#ifdef DEBUG_SNPRINTF 571 dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", 572 zpadlen, spadlen, min, max, place)); 573#endif 574 575 /* Spaces */ 576 while (spadlen > 0) 577 { 578 total += dopr_outch (buffer, currlen, maxlen, ' '); 579 --spadlen; 580 } 581 582 /* Sign */ 583 if (signvalue) 584 total += dopr_outch (buffer, currlen, maxlen, signvalue); 585 586 /* Zeros */ 587 if (zpadlen > 0) 588 { 589 while (zpadlen > 0) 590 { 591 total += dopr_outch (buffer, currlen, maxlen, '0'); 592 --zpadlen; 593 } 594 } 595 596 /* Digits */ 597 while (place > 0) 598 total += dopr_outch (buffer, currlen, maxlen, convert[--place]); 599 600 /* Left Justified spaces */ 601 while (spadlen < 0) { 602 total += dopr_outch (buffer, currlen, maxlen, ' '); 603 ++spadlen; 604 } 605 606 return total; 607} 608 609static LDOUBLE abs_val (LDOUBLE value) 610{ 611 LDOUBLE result = value; 612 613 if (value < 0) 614 result = -value; 615 616 return result; 617} 618 619static LDOUBLE pow10_int (int exp) 620{ 621 LDOUBLE result = 1; 622 623 while (exp) 624 { 625 result *= 10; 626 exp--; 627 } 628 629 return result; 630} 631 632static LLONG round_int (LDOUBLE value) 633{ 634 LLONG intpart; 635 636 intpart = value; 637 value = value - intpart; 638 if (value >= 0.5) 639 intpart++; 640 641 return intpart; 642} 643 644static int fmtfp (char *buffer, size_t *currlen, size_t maxlen, 645 LDOUBLE fvalue, int min, int max, int flags) 646{ 647 int signvalue = 0; 648 LDOUBLE ufvalue; 649 char iconvert[24]; 650 char fconvert[24]; 651 int iplace = 0; 652 int fplace = 0; 653 int padlen = 0; /* amount to pad */ 654 int zpadlen = 0; 655 int total = 0; 656 LLONG intpart; 657 LLONG fracpart; 658 LLONG mask10; 659 int leadingfrac0s = 0; /* zeros at the start of fractional part */ 660 int omitzeros = 0; 661 int omitcount = 0; 662 663 /* 664 * AIX manpage says the default is 0, but Solaris says the default 665 * is 6, and sprintf on AIX defaults to 6 666 */ 667 if (max < 0) 668 max = 6; 669 670 ufvalue = abs_val (fvalue); 671 672 if (fvalue < 0) 673 signvalue = '-'; 674 else 675 if (flags & DP_F_PLUS) /* Do a sign (+/i) */ 676 signvalue = '+'; 677 else 678 if (flags & DP_F_SPACE) 679 signvalue = ' '; 680 681#if 0 682 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ 683#endif 684 685 intpart = ufvalue; 686 687 /* With %g precision is the number of significant digits, which 688 includes the digits in intpart. */ 689 if (flags & DP_F_FP_G) 690 { 691 if (intpart != 0) 692 { 693 /* For each digit of INTPART, print one less fractional digit. */ 694 LLONG temp = intpart; 695 for (temp = intpart; temp != 0; temp /= 10) 696 --max; 697 if (max < 0) 698 max = 0; 699 } 700 else 701 { 702 /* For each leading 0 in fractional part, print one more 703 fractional digit. */ 704 LDOUBLE temp; 705 if (ufvalue != 0) 706 for (temp = ufvalue; temp < 0.1; temp *= 10) 707 ++max; 708 } 709 } 710 711 /* C99: trailing zeros are removed from the fractional portion of the 712 result unless the # flag is specified */ 713 if ((flags & DP_F_FP_G) && !(flags & DP_F_NUM)) 714 omitzeros = 1; 715 716#if SIZEOF_LONG_LONG > 0 717# define MAX_DIGITS 18 /* grok more digits with long long */ 718#else 719# define MAX_DIGITS 9 /* just long */ 720#endif 721 722 /* 723 * Sorry, we only support several digits past the decimal because of 724 * our conversion method 725 */ 726 if (max > MAX_DIGITS) 727 max = MAX_DIGITS; 728 729 /* Factor of 10 with the needed number of digits, e.g. 1000 for max==3 */ 730 mask10 = pow10_int (max); 731 732 /* We "cheat" by converting the fractional part to integer by 733 * multiplying by a factor of 10 734 */ 735 fracpart = round_int (mask10 * (ufvalue - intpart)); 736 737 if (fracpart >= mask10) 738 { 739 intpart++; 740 fracpart -= mask10; 741 } 742 else if (fracpart != 0) 743 /* If fracpart has less digits than the 10* mask, we need to 744 manually insert leading 0s. For example 2.01's fractional part 745 requires one leading zero to distinguish it from 2.1. */ 746 while (fracpart < mask10 / 10) 747 { 748 ++leadingfrac0s; 749 mask10 /= 10; 750 } 751 752#ifdef DEBUG_SNPRINTF 753 dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart)); 754#endif 755 756 /* Convert integer part */ 757 do { 758 iconvert[iplace++] = '0' + intpart % 10; 759 intpart = (intpart / 10); 760 } while(intpart && (iplace < sizeof(iconvert))); 761 if (iplace == sizeof(iconvert)) iplace--; 762 iconvert[iplace] = 0; 763 764 /* Convert fractional part */ 765 do { 766 fconvert[fplace++] = '0' + fracpart % 10; 767 fracpart = (fracpart / 10); 768 } while(fracpart && (fplace < sizeof(fconvert))); 769 while (leadingfrac0s-- > 0 && fplace < sizeof(fconvert)) 770 fconvert[fplace++] = '0'; 771 if (fplace == sizeof(fconvert)) fplace--; 772 fconvert[fplace] = 0; 773 if (omitzeros) 774 while (omitcount < fplace && fconvert[omitcount] == '0') 775 ++omitcount; 776 777 /* -1 for decimal point, another -1 if we are printing a sign */ 778 padlen = min - iplace - (max - omitcount) - 1 - ((signvalue) ? 1 : 0); 779 if (!omitzeros) 780 zpadlen = max - fplace; 781 if (zpadlen < 0) 782 zpadlen = 0; 783 if (padlen < 0) 784 padlen = 0; 785 if (flags & DP_F_MINUS) 786 padlen = -padlen; /* Left Justifty */ 787 788 if ((flags & DP_F_ZERO) && (padlen > 0)) 789 { 790 if (signvalue) 791 { 792 total += dopr_outch (buffer, currlen, maxlen, signvalue); 793 --padlen; 794 signvalue = 0; 795 } 796 while (padlen > 0) 797 { 798 total += dopr_outch (buffer, currlen, maxlen, '0'); 799 --padlen; 800 } 801 } 802 while (padlen > 0) 803 { 804 total += dopr_outch (buffer, currlen, maxlen, ' '); 805 --padlen; 806 } 807 if (signvalue) 808 total += dopr_outch (buffer, currlen, maxlen, signvalue); 809 810 while (iplace > 0) 811 total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); 812 813 /* 814 * Decimal point. This should probably use locale to find the correct 815 * char to print out. 816 */ 817 if (max > 0 && (fplace > omitcount || zpadlen > 0)) 818 { 819 total += dopr_outch (buffer, currlen, maxlen, '.'); 820 821 while (fplace > omitcount) 822 total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); 823 } 824 825 while (zpadlen > 0) 826 { 827 total += dopr_outch (buffer, currlen, maxlen, '0'); 828 --zpadlen; 829 } 830 831 while (padlen < 0) 832 { 833 total += dopr_outch (buffer, currlen, maxlen, ' '); 834 ++padlen; 835 } 836 837 return total; 838} 839 840static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) 841{ 842 if (*currlen + 1 < maxlen) 843 buffer[(*currlen)++] = c; 844 return 1; 845} 846 847#ifndef HAVE_VSNPRINTF 848int vsnprintf (char *str, size_t count, const char *fmt, va_list args) 849{ 850 if (str != NULL) 851 str[0] = 0; 852 return dopr(str, count, fmt, args); 853} 854#endif /* !HAVE_VSNPRINTF */ 855 856#ifndef HAVE_SNPRINTF 857int snprintf (char *str, size_t count, const char *fmt,...) 858{ 859 va_list ap; 860 int total; 861 862 va_start (ap, fmt); 863 total = vsnprintf (str, count, fmt, ap); 864 va_end (ap); 865 return total; 866} 867#endif /* !HAVE_SNPRINTF */ 868#endif /* !HAVE_SNPRINTF || !HAVE_VSNPRINTF */ 869 870#ifdef TEST_SNPRINTF 871 872# ifndef LONG_STRING 873# define LONG_STRING 1024 874# endif 875 876int main (void) 877{ 878 char buf1[LONG_STRING]; 879 char buf2[LONG_STRING]; 880 char *fp_fmt[] = { 881 /* %f formats */ 882 "%f", 883 "%-1.5f", 884 "%1.5f", 885 "%123.9f", 886 "%10.5f", 887 "% 10.5f", 888 "%+22.9f", 889 "%+4.9f", 890 "%01.3f", 891 "%4f", 892 "%3.1f", 893 "%3.2f", 894 "%.0f", 895 "%.1f", 896 "%#10.1f", 897#if SIZEOF_LONG_LONG != 0 898 "%.16f", 899 "%18.16f", 900 "%-16.16f", 901#endif 902 /* %g formats */ 903 "%g", 904 "%1.5g", 905 "%-1.5g", 906 "%.9g", 907 "%123.9g", 908 "%#123.9g", 909#if SIZEOF_LONG_LONG != 0 910 "%.16g", 911 "%20.16g", 912#endif 913 NULL 914 }; 915 double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 916 0.9996, 1.996, 4.136, 0.00205, 0.0001, 321.000009, 917 0}; 918 char *int_fmt[] = { 919 "%-1.5d", 920 "%1.5d", 921 "%123.9d", 922 "%5.5d", 923 "%10.5d", 924 "% 10.5d", 925 "%+22.33d", 926 "%01.3d", 927 "%4d", 928 NULL 929 }; 930 long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; 931#if SIZEOF_LONG_LONG != 0 932 char *llong_fmt[] = { 933 "%lld", "%llu", 934 "%-1.5lld", "%-1.5llu", 935 "%1.5lld", "%1.5llu", 936 "%123.9lld", "%123.9llu", 937 "%5.5lld", "%5.5llu", 938 "%10.5lld", "%10.5llu", 939 "% 10.5lld", "% 10.5llu", 940 "%+22.33lld", "%+22.33llu", 941 "%01.3lld", "%01.3llu", 942 "%4lld", "%4llu", 943 NULL 944 }; 945 long long llong_nums[] = { 946 ~(long long)0, /* all-1 bit pattern */ 947 (~(unsigned long long)0) >> 1, /* largest signed long long */ 948 /* random... */ 949 -150, 134, 91340, 341, 950 0 951 }; 952#endif 953 int x, y; 954 int fail = 0; 955 int num = 0; 956 957 printf ("Testing snprintf format codes against system sprintf...\n"); 958 959 for (x = 0; fp_fmt[x] != NULL ; x++) 960 for (y = 0; fp_nums[y] != 0 ; y++) 961 { 962 snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]); 963 sprintf (buf2, fp_fmt[x], fp_nums[y]); 964 if (strcmp (buf1, buf2)) 965 { 966 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", 967 fp_fmt[x], buf1, buf2); 968 fail++; 969 } 970 num++; 971 } 972 973 for (x = 0; int_fmt[x] != NULL ; x++) 974 for (y = 0; int_nums[y] != 0 ; y++) 975 { 976 snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]); 977 sprintf (buf2, int_fmt[x], int_nums[y]); 978 if (strcmp (buf1, buf2)) 979 { 980 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", 981 int_fmt[x], buf1, buf2); 982 fail++; 983 } 984 num++; 985 } 986 987#if SIZEOF_LONG_LONG != 0 988 for (x = 0; llong_fmt[x] != NULL ; x++) 989 for (y = 0; llong_nums[y] != 0 ; y++) 990 { 991 snprintf (buf1, sizeof (buf1), llong_fmt[x], llong_nums[y]); 992 sprintf (buf2, llong_fmt[x], llong_nums[y]); 993 if (strcmp (buf1, buf2)) 994 { 995 printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", 996 llong_fmt[x], buf1, buf2); 997 fail++; 998 } 999 num++; 1000 } 1001#endif 1002 1003 printf ("%d tests failed out of %d.\n", fail, num); 1004 return 0; 1005} 1006#endif /* TEST_SNPRINTF */ 1007