1 /* snprintf - compatibility implementation of snprintf, vsnprintf 2 * 3 * Copyright (c) 2013, NLnet Labs. All rights reserved. 4 * 5 * This software is open source. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * Redistributions of source code must retain the above copyright notice, 12 * this list of conditions and the following disclaimer. 13 * 14 * Redistributions in binary form must reproduce the above copyright notice, 15 * this list of conditions and the following disclaimer in the documentation 16 * and/or other materials provided with the distribution. 17 * 18 * Neither the name of the NLNET LABS nor the names of its contributors may 19 * be used to endorse or promote products derived from this software without 20 * specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 28 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include "config.h" 36 #include <stdio.h> 37 #include <ctype.h> 38 #include <string.h> 39 #include <stdarg.h> 40 #include <stdlib.h> 41 #include <errno.h> 42 #ifdef HAVE_STDINT_H 43 #include <stdint.h> 44 #endif 45 #include <limits.h> 46 47 /* for test */ 48 /* #define SNPRINTF_TEST 1 */ 49 #ifdef SNPRINTF_TEST 50 #define snprintf my_snprintf 51 #define vsnprintf my_vsnprintf 52 #endif /* SNPRINTF_TEST */ 53 54 int snprintf(char* str, size_t size, const char* format, ...); 55 int vsnprintf(char* str, size_t size, const char* format, va_list arg); 56 57 /** 58 * Very portable snprintf implementation, limited in functionality, 59 * esp. for %[capital] %[nonportable] and so on. Reduced float functionality, 60 * mostly in formatting and range (e+-16), for %f and %g. 61 * 62 * %s, %d, %u, %i, %x, %c, %n and %% are fully supported. 63 * This includes width, precision, flags 0- +, and *(arg for wid,prec). 64 * %f, %g, %m, %p have reduced support, support for wid,prec,flags,*, but 65 * less floating point range, no %e formatting for %g. 66 */ 67 int snprintf(char* str, size_t size, const char* format, ...) 68 { 69 int r; 70 va_list args; 71 va_start(args, format); 72 r = vsnprintf(str, size, format, args); 73 va_end(args); 74 return r; 75 } 76 77 /** add padding to string */ 78 static void 79 print_pad(char** at, size_t* left, int* ret, char p, int num) 80 { 81 while(num--) { 82 if(*left > 1) { 83 *(*at)++ = p; 84 (*left)--; 85 } 86 (*ret)++; 87 } 88 } 89 90 /** get negative symbol, 0 if none */ 91 static char 92 get_negsign(int negative, int plus, int space) 93 { 94 if(negative) 95 return '-'; 96 if(plus) 97 return '+'; 98 if(space) 99 return ' '; 100 return 0; 101 } 102 103 #define PRINT_DEC_BUFSZ 32 /* 20 is enough for 64 bit decimals */ 104 /** print decimal into buffer, returns length */ 105 static int 106 print_dec(char* buf, int max, unsigned int value) 107 { 108 int i = 0; 109 if(value == 0) { 110 if(max > 0) { 111 buf[0] = '0'; 112 i = 1; 113 } 114 } else while(value && i < max) { 115 buf[i++] = '0' + value % 10; 116 value /= 10; 117 } 118 return i; 119 } 120 121 /** print long decimal into buffer, returns length */ 122 static int 123 print_dec_l(char* buf, int max, unsigned long value) 124 { 125 int i = 0; 126 if(value == 0) { 127 if(max > 0) { 128 buf[0] = '0'; 129 i = 1; 130 } 131 } else while(value && i < max) { 132 buf[i++] = '0' + value % 10; 133 value /= 10; 134 } 135 return i; 136 } 137 138 /** print long decimal into buffer, returns length */ 139 static int 140 print_dec_ll(char* buf, int max, unsigned long long value) 141 { 142 int i = 0; 143 if(value == 0) { 144 if(max > 0) { 145 buf[0] = '0'; 146 i = 1; 147 } 148 } else while(value && i < max) { 149 buf[i++] = '0' + value % 10; 150 value /= 10; 151 } 152 return i; 153 } 154 155 /** print hex into buffer, returns length */ 156 static int 157 print_hex(char* buf, int max, unsigned int value) 158 { 159 const char* h = "0123456789abcdef"; 160 int i = 0; 161 if(value == 0) { 162 if(max > 0) { 163 buf[0] = '0'; 164 i = 1; 165 } 166 } else while(value && i < max) { 167 buf[i++] = h[value & 0x0f]; 168 value >>= 4; 169 } 170 return i; 171 } 172 173 /** print long hex into buffer, returns length */ 174 static int 175 print_hex_l(char* buf, int max, unsigned long value) 176 { 177 const char* h = "0123456789abcdef"; 178 int i = 0; 179 if(value == 0) { 180 if(max > 0) { 181 buf[0] = '0'; 182 i = 1; 183 } 184 } else while(value && i < max) { 185 buf[i++] = h[value & 0x0f]; 186 value >>= 4; 187 } 188 return i; 189 } 190 191 /** print long long hex into buffer, returns length */ 192 static int 193 print_hex_ll(char* buf, int max, unsigned long long value) 194 { 195 const char* h = "0123456789abcdef"; 196 int i = 0; 197 if(value == 0) { 198 if(max > 0) { 199 buf[0] = '0'; 200 i = 1; 201 } 202 } else while(value && i < max) { 203 buf[i++] = h[value & 0x0f]; 204 value >>= 4; 205 } 206 return i; 207 } 208 209 /** copy string into result, reversed */ 210 static void 211 spool_str_rev(char** at, size_t* left, int* ret, const char* buf, int len) 212 { 213 int i = len; 214 while(i) { 215 if(*left > 1) { 216 *(*at)++ = buf[--i]; 217 (*left)--; 218 } else --i; 219 (*ret)++; 220 } 221 } 222 223 /** copy string into result */ 224 static void 225 spool_str(char** at, size_t* left, int* ret, const char* buf, int len) 226 { 227 int i; 228 for(i=0; i<len; i++) { 229 if(*left > 1) { 230 *(*at)++ = buf[i]; 231 (*left)--; 232 } 233 (*ret)++; 234 } 235 } 236 237 /** print number formatted */ 238 static void 239 print_num(char** at, size_t* left, int* ret, int minw, int precision, 240 int prgiven, int zeropad, int minus, int plus, int space, 241 int zero, int negative, char* buf, int len) 242 { 243 int w = len; /* excludes minus sign */ 244 char s = get_negsign(negative, plus, space); 245 if(minus) { 246 /* left adjust the number into the field, space padding */ 247 /* calc numw = [sign][zeroes][number] */ 248 int numw = w; 249 if(precision == 0 && zero) numw = 0; 250 if(numw < precision) numw = precision; 251 if(s) numw++; 252 253 /* sign */ 254 if(s) print_pad(at, left, ret, s, 1); 255 256 /* number */ 257 if(precision == 0 && zero) { 258 /* "" for the number */ 259 } else { 260 if(w < precision) 261 print_pad(at, left, ret, '0', precision - w); 262 spool_str_rev(at, left, ret, buf, len); 263 } 264 /* spaces */ 265 if(numw < minw) 266 print_pad(at, left, ret, ' ', minw - numw); 267 } else { 268 /* pad on the left of the number */ 269 /* calculate numw has width of [sign][zeroes][number] */ 270 int numw = w; 271 if(precision == 0 && zero) numw = 0; 272 if(numw < precision) numw = precision; 273 if(!prgiven && zeropad && numw < minw) numw = minw; 274 else if(s) numw++; 275 276 /* pad with spaces */ 277 if(numw < minw) 278 print_pad(at, left, ret, ' ', minw - numw); 279 /* print sign (and one less zeropad if so) */ 280 if(s) { 281 print_pad(at, left, ret, s, 1); 282 numw--; 283 } 284 /* pad with zeroes */ 285 if(w < numw) 286 print_pad(at, left, ret, '0', numw - w); 287 if(precision == 0 && zero) 288 return; 289 /* print the characters for the value */ 290 spool_str_rev(at, left, ret, buf, len); 291 } 292 } 293 294 /** print %d and %i */ 295 static void 296 print_num_d(char** at, size_t* left, int* ret, int value, 297 int minw, int precision, int prgiven, int zeropad, int minus, 298 int plus, int space) 299 { 300 char buf[PRINT_DEC_BUFSZ]; 301 int negative = (value < 0); 302 int zero = (value == 0); 303 int len = print_dec(buf, (int)sizeof(buf), 304 (unsigned int)(negative?-value:value)); 305 print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 306 plus, space, zero, negative, buf, len); 307 } 308 309 /** print %ld and %li */ 310 static void 311 print_num_ld(char** at, size_t* left, int* ret, long value, 312 int minw, int precision, int prgiven, int zeropad, int minus, 313 int plus, int space) 314 { 315 char buf[PRINT_DEC_BUFSZ]; 316 int negative = (value < 0); 317 int zero = (value == 0); 318 int len = print_dec_l(buf, (int)sizeof(buf), 319 (unsigned long)(negative?-value:value)); 320 print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 321 plus, space, zero, negative, buf, len); 322 } 323 324 /** print %lld and %lli */ 325 static void 326 print_num_lld(char** at, size_t* left, int* ret, long long value, 327 int minw, int precision, int prgiven, int zeropad, int minus, 328 int plus, int space) 329 { 330 char buf[PRINT_DEC_BUFSZ]; 331 int negative = (value < 0); 332 int zero = (value == 0); 333 int len = print_dec_ll(buf, (int)sizeof(buf), 334 (unsigned long long)(negative?-value:value)); 335 print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 336 plus, space, zero, negative, buf, len); 337 } 338 339 /** print %u */ 340 static void 341 print_num_u(char** at, size_t* left, int* ret, unsigned int value, 342 int minw, int precision, int prgiven, int zeropad, int minus, 343 int plus, int space) 344 { 345 char buf[PRINT_DEC_BUFSZ]; 346 int negative = 0; 347 int zero = (value == 0); 348 int len = print_dec(buf, (int)sizeof(buf), value); 349 print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 350 plus, space, zero, negative, buf, len); 351 } 352 353 /** print %lu */ 354 static void 355 print_num_lu(char** at, size_t* left, int* ret, unsigned long value, 356 int minw, int precision, int prgiven, int zeropad, int minus, 357 int plus, int space) 358 { 359 char buf[PRINT_DEC_BUFSZ]; 360 int negative = 0; 361 int zero = (value == 0); 362 int len = print_dec_l(buf, (int)sizeof(buf), value); 363 print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 364 plus, space, zero, negative, buf, len); 365 } 366 367 /** print %llu */ 368 static void 369 print_num_llu(char** at, size_t* left, int* ret, unsigned long long value, 370 int minw, int precision, int prgiven, int zeropad, int minus, 371 int plus, int space) 372 { 373 char buf[PRINT_DEC_BUFSZ]; 374 int negative = 0; 375 int zero = (value == 0); 376 int len = print_dec_ll(buf, (int)sizeof(buf), value); 377 print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 378 plus, space, zero, negative, buf, len); 379 } 380 381 /** print %x */ 382 static void 383 print_num_x(char** at, size_t* left, int* ret, unsigned int value, 384 int minw, int precision, int prgiven, int zeropad, int minus, 385 int plus, int space) 386 { 387 char buf[PRINT_DEC_BUFSZ]; 388 int negative = 0; 389 int zero = (value == 0); 390 int len = print_hex(buf, (int)sizeof(buf), value); 391 print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 392 plus, space, zero, negative, buf, len); 393 } 394 395 /** print %lx */ 396 static void 397 print_num_lx(char** at, size_t* left, int* ret, unsigned long value, 398 int minw, int precision, int prgiven, int zeropad, int minus, 399 int plus, int space) 400 { 401 char buf[PRINT_DEC_BUFSZ]; 402 int negative = 0; 403 int zero = (value == 0); 404 int len = print_hex_l(buf, (int)sizeof(buf), value); 405 print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 406 plus, space, zero, negative, buf, len); 407 } 408 409 /** print %llx */ 410 static void 411 print_num_llx(char** at, size_t* left, int* ret, unsigned long long value, 412 int minw, int precision, int prgiven, int zeropad, int minus, 413 int plus, int space) 414 { 415 char buf[PRINT_DEC_BUFSZ]; 416 int negative = 0; 417 int zero = (value == 0); 418 int len = print_hex_ll(buf, (int)sizeof(buf), value); 419 print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 420 plus, space, zero, negative, buf, len); 421 } 422 423 /** print %llp */ 424 static void 425 print_num_llp(char** at, size_t* left, int* ret, void* value, 426 int minw, int precision, int prgiven, int zeropad, int minus, 427 int plus, int space) 428 { 429 char buf[PRINT_DEC_BUFSZ]; 430 int negative = 0; 431 int zero = (value == 0); 432 #if defined(SIZE_MAX) && defined(UINT32_MAX) && (UINT32_MAX == SIZE_MAX || INT32_MAX == SIZE_MAX) 433 /* avoid warning about upcast on 32bit systems */ 434 unsigned long long llvalue = (unsigned long)value; 435 #else 436 unsigned long long llvalue = (unsigned long long)value; 437 #endif 438 int len = print_hex_ll(buf, (int)sizeof(buf), llvalue); 439 if(zero) { 440 buf[0]=')'; 441 buf[1]='l'; 442 buf[2]='i'; 443 buf[3]='n'; 444 buf[4]='('; 445 len = 5; 446 } else { 447 /* put '0x' in front of the (reversed) buffer result */ 448 if(len < PRINT_DEC_BUFSZ) 449 buf[len++] = 'x'; 450 if(len < PRINT_DEC_BUFSZ) 451 buf[len++] = '0'; 452 } 453 print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 454 plus, space, zero, negative, buf, len); 455 } 456 457 #define PRINT_FLOAT_BUFSZ 64 /* xx.yy with 20.20 about the max */ 458 /** spool remainder after the decimal point to buffer, in reverse */ 459 static int 460 print_remainder(char* buf, int max, double r, int prec) 461 { 462 unsigned long long cap = 1; 463 unsigned long long value; 464 int len, i; 465 if(prec > 19) prec = 19; /* max we can do */ 466 if(max < prec) return 0; 467 for(i=0; i<prec; i++) { 468 cap *= 10; 469 } 470 r *= (double)cap; 471 value = (unsigned long long)r; 472 /* see if we need to round up */ 473 if(((unsigned long long)((r - (double)value)*10.0)) >= 5) { 474 value++; 475 /* that might carry to numbers before the comma, if so, 476 * just ignore that rounding. failure because 64bitprintout */ 477 if(value >= cap) 478 value = cap-1; 479 } 480 len = print_dec_ll(buf, max, value); 481 while(len < prec) { /* pad with zeroes, e.g. if 0.0012 */ 482 buf[len++] = '0'; 483 } 484 if(len < max) 485 buf[len++] = '.'; 486 return len; 487 } 488 489 /** spool floating point to buffer */ 490 static int 491 print_float(char* buf, int max, double value, int prec) 492 { 493 /* as xxx.xxx if prec==0, no '.', with prec decimals after . */ 494 /* no conversion for NAN and INF, because we do not want to require 495 linking with -lm. */ 496 /* Thus, the conversions use 64bit integers to convert the numbers, 497 * which makes 19 digits before and after the decimal point the max */ 498 unsigned long long whole = (unsigned long long)value; 499 double remain = value - (double)whole; 500 int len = 0; 501 if(prec != 0) 502 len = print_remainder(buf, max, remain, prec); 503 len += print_dec_ll(buf+len, max-len, whole); 504 return len; 505 } 506 507 /** print %f */ 508 static void 509 print_num_f(char** at, size_t* left, int* ret, double value, 510 int minw, int precision, int prgiven, int zeropad, int minus, 511 int plus, int space) 512 { 513 char buf[PRINT_FLOAT_BUFSZ]; 514 int negative = (value < 0); 515 int zero = 0; 516 int len; 517 if(!prgiven) precision = 6; 518 len = print_float(buf, (int)sizeof(buf), negative?-value:value, 519 precision); 520 print_num(at, left, ret, minw, 1, 0, zeropad, minus, 521 plus, space, zero, negative, buf, len); 522 } 523 524 /* rudimentary %g support */ 525 static int 526 print_float_g(char* buf, int max, double value, int prec) 527 { 528 unsigned long long whole = (unsigned long long)value; 529 double remain = value - (double)whole; 530 int before = 0; 531 int len = 0; 532 533 /* number of digits before the decimal point */ 534 while(whole > 0) { 535 before++; 536 whole /= 10; 537 } 538 whole = (unsigned long long)value; 539 540 if(prec > before && remain != 0.0) { 541 /* see if the last decimals are zero, if so, skip them */ 542 len = print_remainder(buf, max, remain, prec-before); 543 while(len > 0 && buf[0]=='0') { 544 memmove(buf, buf+1, --len); 545 } 546 } 547 len += print_dec_ll(buf+len, max-len, whole); 548 return len; 549 } 550 551 552 /** print %g */ 553 static void 554 print_num_g(char** at, size_t* left, int* ret, double value, 555 int minw, int precision, int prgiven, int zeropad, int minus, 556 int plus, int space) 557 { 558 char buf[PRINT_FLOAT_BUFSZ]; 559 int negative = (value < 0); 560 int zero = 0; 561 int len; 562 if(!prgiven) precision = 6; 563 if(precision == 0) precision = 1; 564 len = print_float_g(buf, (int)sizeof(buf), negative?-value:value, 565 precision); 566 print_num(at, left, ret, minw, 1, 0, zeropad, minus, 567 plus, space, zero, negative, buf, len); 568 } 569 570 571 /** strnlen (compat implementation) */ 572 static int 573 my_strnlen(const char* s, int max) 574 { 575 int i; 576 for(i=0; i<max; i++) 577 if(s[i]==0) 578 return i; 579 return max; 580 } 581 582 /** print %s */ 583 static void 584 print_str(char** at, size_t* left, int* ret, char* s, 585 int minw, int precision, int prgiven, int minus) 586 { 587 int w; 588 /* with prec: no more than x characters from this string, stop at 0 */ 589 if(prgiven) 590 w = my_strnlen(s, precision); 591 else w = (int)strlen(s); /* up to the nul */ 592 if(w < minw && !minus) 593 print_pad(at, left, ret, ' ', minw - w); 594 spool_str(at, left, ret, s, w); 595 if(w < minw && minus) 596 print_pad(at, left, ret, ' ', minw - w); 597 } 598 599 /** print %c */ 600 static void 601 print_char(char** at, size_t* left, int* ret, int c, 602 int minw, int minus) 603 { 604 if(1 < minw && !minus) 605 print_pad(at, left, ret, ' ', minw - 1); 606 print_pad(at, left, ret, c, 1); 607 if(1 < minw && minus) 608 print_pad(at, left, ret, ' ', minw - 1); 609 } 610 611 612 /** 613 * Print to string. 614 * str: string buffer for result. result will be null terminated. 615 * size: size of the buffer. null is put inside buffer. 616 * format: printf format string. 617 * arg: '...' arguments to print. 618 * returns number of characters. a null is printed after this. 619 * return number of bytes that would have been written 620 * if the buffer had been large enough. 621 * 622 * supported format specifiers: 623 * %s, %u, %d, %x, %i, %f, %g, %c, %p, %n. 624 * length: l, ll (for d, u, x). 625 * precision: 6.6d (for d, u, x) 626 * %f, %g precisions, 0.3f 627 * %20s, '.*s' 628 * and %%. 629 */ 630 int vsnprintf(char* str, size_t size, const char* format, va_list arg) 631 { 632 char* at = str; 633 size_t left = size; 634 int ret = 0; 635 const char* fmt = format; 636 int conv, minw, precision, prgiven, zeropad, minus, plus, space, length; 637 while(*fmt) { 638 /* copy string before % */ 639 while(*fmt && *fmt!='%') { 640 if(left > 1) { 641 *at++ = *fmt++; 642 left--; 643 } else fmt++; 644 ret++; 645 } 646 647 /* see if we are at end */ 648 if(!*fmt) break; 649 650 /* fetch next argument % designation from format string */ 651 fmt++; /* skip the '%' */ 652 653 /********************************/ 654 /* get the argument designation */ 655 /********************************/ 656 /* we must do this vararg stuff inside this function for 657 * portability. Hence, get_designation, and print_designation 658 * are not their own functions. */ 659 660 /* printout designation: 661 * conversion specifier: x, d, u, s, c, m, p 662 * flags: # not supported 663 * 0 zeropad (on the left) 664 * - left adjust (right by default) 665 * ' ' printspace for positive number (in - position). 666 * + alwayssign 667 * fieldwidth: [1-9][0-9]* minimum field width. 668 * if this is * then type int next argument specifies the minwidth. 669 * if this is negative, the - flag is set (with positive width). 670 * precision: period[digits]*, %.2x. 671 * if this is * then type int next argument specifies the precision. 672 * just '.' or negative value means precision=0. 673 * this is mindigits to print for d, i, u, x 674 * this is aftercomma digits for f 675 * this is max number significant digits for g 676 * maxnumber characters to be printed for s 677 * length: 0-none (int), 1-l (long), 2-ll (long long) 678 * notsupported: hh (char), h (short), L (long double), q, j, z, t 679 * Does not support %m$ and *m$ argument designation as array indices. 680 * Does not support %#x 681 * 682 */ 683 minw = 0; 684 precision = 1; 685 prgiven = 0; 686 zeropad = 0; 687 minus = 0; 688 plus = 0; 689 space = 0; 690 length = 0; 691 692 /* get flags in any order */ 693 for(;;) { 694 if(*fmt == '0') 695 zeropad = 1; 696 else if(*fmt == '-') 697 minus = 1; 698 else if(*fmt == '+') 699 plus = 1; 700 else if(*fmt == ' ') 701 space = 1; 702 else break; 703 fmt++; 704 } 705 706 /* field width */ 707 if(*fmt == '*') { 708 fmt++; /* skip char */ 709 minw = va_arg(arg, int); 710 if(minw < 0) { 711 minus = 1; 712 minw = -minw; 713 } 714 } else while(*fmt >= '0' && *fmt <= '9') { 715 minw = minw*10 + (*fmt++)-'0'; 716 } 717 718 /* precision */ 719 if(*fmt == '.') { 720 fmt++; /* skip period */ 721 prgiven = 1; 722 precision = 0; 723 if(*fmt == '*') { 724 fmt++; /* skip char */ 725 precision = va_arg(arg, int); 726 if(precision < 0) 727 precision = 0; 728 } else while(*fmt >= '0' && *fmt <= '9') { 729 precision = precision*10 + (*fmt++)-'0'; 730 } 731 } 732 733 /* length */ 734 if(*fmt == 'l') { 735 fmt++; /* skip char */ 736 length = 1; 737 if(*fmt == 'l') { 738 fmt++; /* skip char */ 739 length = 2; 740 } 741 } 742 743 /* get the conversion */ 744 if(!*fmt) conv = 0; 745 else conv = *fmt++; 746 747 /***********************************/ 748 /* print that argument designation */ 749 /***********************************/ 750 switch(conv) { 751 case 'i': 752 case 'd': 753 if(length == 0) 754 print_num_d(&at, &left, &ret, va_arg(arg, int), 755 minw, precision, prgiven, zeropad, minus, plus, space); 756 else if(length == 1) 757 print_num_ld(&at, &left, &ret, va_arg(arg, long), 758 minw, precision, prgiven, zeropad, minus, plus, space); 759 else if(length == 2) 760 print_num_lld(&at, &left, &ret, 761 va_arg(arg, long long), 762 minw, precision, prgiven, zeropad, minus, plus, space); 763 break; 764 case 'u': 765 if(length == 0) 766 print_num_u(&at, &left, &ret, 767 va_arg(arg, unsigned int), 768 minw, precision, prgiven, zeropad, minus, plus, space); 769 else if(length == 1) 770 print_num_lu(&at, &left, &ret, 771 va_arg(arg, unsigned long), 772 minw, precision, prgiven, zeropad, minus, plus, space); 773 else if(length == 2) 774 print_num_llu(&at, &left, &ret, 775 va_arg(arg, unsigned long long), 776 minw, precision, prgiven, zeropad, minus, plus, space); 777 break; 778 case 'x': 779 if(length == 0) 780 print_num_x(&at, &left, &ret, 781 va_arg(arg, unsigned int), 782 minw, precision, prgiven, zeropad, minus, plus, space); 783 else if(length == 1) 784 print_num_lx(&at, &left, &ret, 785 va_arg(arg, unsigned long), 786 minw, precision, prgiven, zeropad, minus, plus, space); 787 else if(length == 2) 788 print_num_llx(&at, &left, &ret, 789 va_arg(arg, unsigned long long), 790 minw, precision, prgiven, zeropad, minus, plus, space); 791 break; 792 case 's': 793 print_str(&at, &left, &ret, va_arg(arg, char*), 794 minw, precision, prgiven, minus); 795 break; 796 case 'c': 797 print_char(&at, &left, &ret, va_arg(arg, int), 798 minw, minus); 799 break; 800 case 'n': 801 /* unsupported to harden against format string 802 * exploitation, 803 * handled like an unknown format specifier. */ 804 /* *va_arg(arg, int*) = ret; */ 805 break; 806 case 'm': 807 print_str(&at, &left, &ret, strerror(errno), 808 minw, precision, prgiven, minus); 809 break; 810 case 'p': 811 print_num_llp(&at, &left, &ret, va_arg(arg, void*), 812 minw, precision, prgiven, zeropad, minus, plus, space); 813 break; 814 case '%': 815 print_pad(&at, &left, &ret, '%', 1); 816 break; 817 case 'f': 818 print_num_f(&at, &left, &ret, va_arg(arg, double), 819 minw, precision, prgiven, zeropad, minus, plus, space); 820 break; 821 case 'g': 822 print_num_g(&at, &left, &ret, va_arg(arg, double), 823 minw, precision, prgiven, zeropad, minus, plus, space); 824 break; 825 /* unknown */ 826 default: 827 case 0: break; 828 } 829 } 830 831 /* zero terminate */ 832 if(left > 0) 833 *at = 0; 834 return ret; 835 } 836 837 #ifdef SNPRINTF_TEST 838 839 /** do tests */ 840 #undef snprintf 841 #define DOTEST(bufsz, result, retval, ...) do { \ 842 char buf[bufsz]; \ 843 printf("now test %s\n", #__VA_ARGS__); \ 844 int r=my_snprintf(buf, sizeof(buf), __VA_ARGS__); \ 845 if(r != retval || strcmp(buf, result) != 0) { \ 846 printf("error test(%s) was \"%s\":%d\n", \ 847 ""#bufsz", "#result", "#retval", "#__VA_ARGS__, \ 848 buf, r); \ 849 exit(1); \ 850 } \ 851 r=snprintf(buf, sizeof(buf), __VA_ARGS__); \ 852 if(r != retval || strcmp(buf, result) != 0) { \ 853 printf("error test(%s) differs with system, \"%s\":%d\n", \ 854 ""#bufsz", "#result", "#retval", "#__VA_ARGS__, \ 855 buf, r); \ 856 exit(1); \ 857 } \ 858 printf("test(\"%s\":%d) passed\n", buf, r); \ 859 } while(0); 860 861 /** test program */ 862 int main(void) 863 { 864 int x = 0; 865 866 /* bufsize, expectedstring, expectedretval, snprintf arguments */ 867 DOTEST(1024, "hello", 5, "hello"); 868 DOTEST(1024, "h", 1, "h"); 869 /* warning from gcc for format string, but it does work 870 * DOTEST(1024, "", 0, ""); */ 871 872 DOTEST(3, "he", 5, "hello"); 873 DOTEST(1, "", 7, "%d", 7823089); 874 875 /* test positive numbers */ 876 DOTEST(1024, "0", 1, "%d", 0); 877 DOTEST(1024, "1", 1, "%d", 1); 878 DOTEST(1024, "9", 1, "%d", 9); 879 DOTEST(1024, "15", 2, "%d", 15); 880 DOTEST(1024, "ab15cd", 6, "ab%dcd", 15); 881 DOTEST(1024, "167", 3, "%d", 167); 882 DOTEST(1024, "7823089", 7, "%d", 7823089); 883 DOTEST(1024, " 12", 3, "%3d", 12); 884 DOTEST(1024, "012", 3, "%.3d", 12); 885 DOTEST(1024, "012", 3, "%3.3d", 12); 886 DOTEST(1024, "012", 3, "%03d", 12); 887 DOTEST(1024, " 012", 4, "%4.3d", 12); 888 DOTEST(1024, "", 0, "%.0d", 0); 889 890 /* test negative numbers */ 891 DOTEST(1024, "-1", 2, "%d", -1); 892 DOTEST(1024, "-12", 3, "%3d", -12); 893 DOTEST(1024, " -2", 3, "%3d", -2); 894 DOTEST(1024, "-012", 4, "%.3d", -12); 895 DOTEST(1024, "-012", 4, "%3.3d", -12); 896 DOTEST(1024, "-012", 4, "%4.3d", -12); 897 DOTEST(1024, " -012", 5, "%5.3d", -12); 898 DOTEST(1024, "-12", 3, "%03d", -12); 899 DOTEST(1024, "-02", 3, "%03d", -2); 900 DOTEST(1024, "-15", 3, "%d", -15); 901 DOTEST(1024, "-7307", 5, "%d", -7307); 902 DOTEST(1024, "-12 ", 5, "%-5d", -12); 903 DOTEST(1024, "-00012", 6, "%-.5d", -12); 904 905 /* test + and space flags */ 906 DOTEST(1024, "+12", 3, "%+d", 12); 907 DOTEST(1024, " 12", 3, "% d", 12); 908 909 /* test %u */ 910 DOTEST(1024, "12", 2, "%u", 12); 911 DOTEST(1024, "0", 1, "%u", 0); 912 DOTEST(1024, "4294967295", 10, "%u", 0xffffffff); 913 914 /* test %x */ 915 DOTEST(1024, "0", 1, "%x", 0); 916 DOTEST(1024, "c", 1, "%x", 12); 917 DOTEST(1024, "12ab34cd", 8, "%x", 0x12ab34cd); 918 919 /* test %llu, %lld */ 920 DOTEST(1024, "18446744073709551615", 20, "%llu", 921 (long long)0xffffffffffffffff); 922 DOTEST(1024, "-9223372036854775808", 20, "%lld", 923 (long long)0x8000000000000000); 924 DOTEST(1024, "9223372036854775808", 19, "%llu", 925 (long long)0x8000000000000000); 926 927 /* test %s */ 928 DOTEST(1024, "hello", 5, "%s", "hello"); 929 DOTEST(1024, " hello", 10, "%10s", "hello"); 930 DOTEST(1024, "hello ", 10, "%-10s", "hello"); 931 DOTEST(1024, "he", 2, "%.2s", "hello"); 932 DOTEST(1024, " he", 4, "%4.2s", "hello"); 933 DOTEST(1024, " h", 4, "%4.2s", "h"); 934 935 /* test %c */ 936 DOTEST(1024, "a", 1, "%c", 'a'); 937 /* warning from gcc for format string, but it does work 938 DOTEST(1024, " a", 5, "%5c", 'a'); 939 DOTEST(1024, "a", 1, "%.0c", 'a'); */ 940 941 /* test %n */ 942 DOTEST(1024, "hello", 5, "hello%n", &x); 943 if(x != 5) { printf("the %%n failed\n"); exit(1); } 944 945 /* test %m */ 946 errno = 0; 947 DOTEST(1024, "Success", 7, "%m"); 948 949 /* test %p */ 950 DOTEST(1024, "0x10", 4, "%p", (void*)0x10); 951 DOTEST(1024, "(nil)", 5, "%p", (void*)0x0); 952 953 /* test %% */ 954 DOTEST(1024, "%", 1, "%%"); 955 956 /* test %f */ 957 DOTEST(1024, "0.000000", 8, "%f", 0.0); 958 DOTEST(1024, "0.00", 4, "%.2f", 0.0); 959 /* differs, "-0.00" DOTEST(1024, "0.00", 4, "%.2f", -0.0); */ 960 DOTEST(1024, "234.00", 6, "%.2f", 234.005); 961 DOTEST(1024, "8973497.1246", 12, "%.4f", 8973497.12456); 962 DOTEST(1024, "-12.000000", 10, "%f", -12.0); 963 DOTEST(1024, "6", 1, "%.0f", 6.0); 964 965 DOTEST(1024, "6", 1, "%g", 6.0); 966 DOTEST(1024, "6.1", 3, "%g", 6.1); 967 DOTEST(1024, "6.15", 4, "%g", 6.15); 968 969 /* These format strings are from the code of NSD, Unbound, ldns */ 970 971 DOTEST(1024, "abcdef", 6, "%s", "abcdef"); 972 DOTEST(1024, "005", 3, "%03u", 5); 973 DOTEST(1024, "12345", 5, "%03u", 12345); 974 DOTEST(1024, "5", 1, "%d", 5); 975 DOTEST(1024, "(nil)", 5, "%p", NULL); 976 DOTEST(1024, "12345", 5, "%ld", (long)12345); 977 DOTEST(1024, "12345", 5, "%lu", (long)12345); 978 DOTEST(1024, " 12345", 12, "%12u", (unsigned)12345); 979 DOTEST(1024, "12345", 5, "%u", (unsigned)12345); 980 DOTEST(1024, "12345", 5, "%llu", (unsigned long long)12345); 981 DOTEST(1024, "12345", 5, "%x", 0x12345); 982 DOTEST(1024, "12345", 5, "%llx", (long long)0x12345); 983 DOTEST(1024, "012345", 6, "%6.6d", 12345); 984 DOTEST(1024, "012345", 6, "%6.6u", 12345); 985 DOTEST(1024, "1234.54", 7, "%g", 1234.54); 986 DOTEST(1024, "123456789.54", 12, "%.12g", 123456789.54); 987 DOTEST(1024, "3456789123456.54", 16, "%.16g", 3456789123456.54); 988 /* %24g does not work with 24 digits, not enough accuracy, 989 * the first 16 digits are correct */ 990 DOTEST(1024, "12345", 5, "%3.3d", 12345); 991 DOTEST(1024, "000", 3, "%3.3d", 0); 992 DOTEST(1024, "001", 3, "%3.3d", 1); 993 DOTEST(1024, "012", 3, "%3.3d", 12); 994 DOTEST(1024, "-012", 4, "%3.3d", -12); 995 DOTEST(1024, "he", 2, "%.2s", "hello"); 996 DOTEST(1024, "helloworld", 10, "%s%s", "hello", "world"); 997 DOTEST(1024, "he", 2, "%.*s", 2, "hello"); 998 DOTEST(1024, " hello", 7, "%*s", 7, "hello"); 999 DOTEST(1024, "hello ", 7, "%*s", -7, "hello"); 1000 DOTEST(1024, "0", 1, "%c", '0'); 1001 DOTEST(1024, "A", 1, "%c", 'A'); 1002 DOTEST(1024, "", 1, "%c", 0); 1003 DOTEST(1024, "\010", 1, "%c", 8); 1004 DOTEST(1024, "%", 1, "%%"); 1005 DOTEST(1024, "0a", 2, "%02x", 0x0a); 1006 DOTEST(1024, "bd", 2, "%02x", 0xbd); 1007 DOTEST(1024, "12", 2, "%02ld", (long)12); 1008 DOTEST(1024, "02", 2, "%02ld", (long)2); 1009 DOTEST(1024, "02", 2, "%02u", (unsigned)2); 1010 DOTEST(1024, "765432", 6, "%05u", (unsigned)765432); 1011 DOTEST(1024, "10.234", 6, "%0.3f", 10.23421); 1012 DOTEST(1024, "123456.234", 10, "%0.3f", 123456.23421); 1013 DOTEST(1024, "123456789.234", 13, "%0.3f", 123456789.23421); 1014 DOTEST(1024, "123456.23", 9, "%.2f", 123456.23421); 1015 DOTEST(1024, "123456", 6, "%.0f", 123456.23421); 1016 DOTEST(1024, "0123", 4, "%.4x", 0x0123); 1017 DOTEST(1024, "00000123", 8, "%.8x", 0x0123); 1018 DOTEST(1024, "ffeb0cde", 8, "%.8x", 0xffeb0cde); 1019 DOTEST(1024, " 987654321", 10, "%10lu", (unsigned long)987654321); 1020 DOTEST(1024, " 987654321", 12, "%12lu", (unsigned long)987654321); 1021 DOTEST(1024, "987654321", 9, "%i", 987654321); 1022 DOTEST(1024, "-87654321", 9, "%i", -87654321); 1023 DOTEST(1024, "hello ", 16, "%-16s", "hello"); 1024 DOTEST(1024, " ", 16, "%-16s", ""); 1025 DOTEST(1024, "a ", 16, "%-16s", "a"); 1026 DOTEST(1024, "foobarfoobar ", 16, "%-16s", "foobarfoobar"); 1027 DOTEST(1024, "foobarfoobarfoobar", 18, "%-16s", "foobarfoobarfoobar"); 1028 1029 /* combined expressions */ 1030 DOTEST(1024, "foo 1.0 size 512 edns", 21, 1031 "foo %s size %d %s%s", "1.0", 512, "", "edns"); 1032 DOTEST(15, "foo 1.0 size 5", 21, 1033 "foo %s size %d %s%s", "1.0", 512, "", "edns"); 1034 DOTEST(1024, "packet 1203ceff id", 18, 1035 "packet %2.2x%2.2x%2.2x%2.2x id", 0x12, 0x03, 0xce, 0xff); 1036 DOTEST(1024, "/tmp/testbound_123abcd.tmp", 26, "/tmp/testbound_%u%s%s.tmp", 123, "ab", "cd"); 1037 1038 return 0; 1039 } 1040 #endif /* SNPRINTF_TEST */ 1041