1 /* $NetBSD: snprintf.c,v 1.1.1.1 2011/04/13 18:15:43 elric Exp $ */ 2 3 /* 4 * Copyright (c) 1995-2003 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <config.h> 37 #include <stdio.h> 38 #include <stdarg.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <ctype.h> 42 #include <krb5/roken.h> 43 #include <assert.h> 44 45 enum format_flags { 46 minus_flag = 1, 47 plus_flag = 2, 48 space_flag = 4, 49 alternate_flag = 8, 50 zero_flag = 16 51 }; 52 53 /* 54 * Common state 55 */ 56 57 struct snprintf_state { 58 unsigned char *str; 59 unsigned char *s; 60 unsigned char *theend; 61 size_t sz; 62 size_t max_sz; 63 void (*append_char)(struct snprintf_state *, unsigned char); 64 /* XXX - methods */ 65 }; 66 67 #if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF) 68 static int 69 sn_reserve (struct snprintf_state *state, size_t n) 70 { 71 return state->s + n > state->theend; 72 } 73 74 static void 75 sn_append_char (struct snprintf_state *state, unsigned char c) 76 { 77 if (!sn_reserve (state, 1)) 78 *state->s++ = c; 79 } 80 #endif 81 82 static int 83 as_reserve (struct snprintf_state *state, size_t n) 84 { 85 if (state->s + n > state->theend) { 86 int off = state->s - state->str; 87 unsigned char *tmp; 88 89 if (state->max_sz && state->sz >= state->max_sz) 90 return 1; 91 92 state->sz = max(state->sz * 2, state->sz + n); 93 if (state->max_sz) 94 state->sz = min(state->sz, state->max_sz); 95 tmp = realloc (state->str, state->sz); 96 if (tmp == NULL) 97 return 1; 98 state->str = tmp; 99 state->s = state->str + off; 100 state->theend = state->str + state->sz - 1; 101 } 102 return 0; 103 } 104 105 static void 106 as_append_char (struct snprintf_state *state, unsigned char c) 107 { 108 if(!as_reserve (state, 1)) 109 *state->s++ = c; 110 } 111 112 /* longest integer types */ 113 114 #ifdef HAVE_LONG_LONG 115 typedef unsigned long long u_longest; 116 typedef long long longest; 117 #else 118 typedef unsigned long u_longest; 119 typedef long longest; 120 #endif 121 122 123 124 static size_t 125 pad(struct snprintf_state *state, int width, char c) 126 { 127 size_t len = 0; 128 while(width-- > 0){ 129 (*state->append_char)(state, c); 130 ++len; 131 } 132 return len; 133 } 134 135 /* return true if we should use alternatve hex form */ 136 static int 137 use_alternative (int flags, u_longest num, unsigned base) 138 { 139 return (flags & alternate_flag) && base == 16 && num != 0; 140 } 141 142 static int 143 append_number(struct snprintf_state *state, 144 u_longest num, unsigned base, const char *rep, 145 int width, int prec, int flags, int minusp) 146 { 147 int len = 0; 148 u_longest n = num; 149 char nstr[64]; /* enough for <192 bit octal integers */ 150 int nstart, nlen; 151 char signchar; 152 153 /* given precision, ignore zero flag */ 154 if(prec != -1) 155 flags &= ~zero_flag; 156 else 157 prec = 1; 158 159 /* format number as string */ 160 nstart = sizeof(nstr); 161 nlen = 0; 162 nstr[--nstart] = '\0'; 163 do { 164 assert(nstart > 0); 165 nstr[--nstart] = rep[n % base]; 166 ++nlen; 167 n /= base; 168 } while(n); 169 170 /* zero value with zero precision should produce no digits */ 171 if(prec == 0 && num == 0) { 172 nlen--; 173 nstart++; 174 } 175 176 /* figure out what char to use for sign */ 177 if(minusp) 178 signchar = '-'; 179 else if((flags & plus_flag)) 180 signchar = '+'; 181 else if((flags & space_flag)) 182 signchar = ' '; 183 else 184 signchar = '\0'; 185 186 if((flags & alternate_flag) && base == 8) { 187 /* if necessary, increase the precision to 188 make first digit a zero */ 189 190 /* XXX C99 claims (regarding # and %o) that "if the value and 191 precision are both 0, a single 0 is printed", but there is 192 no such wording for %x. This would mean that %#.o would 193 output "0", but %#.x "". This does not make sense, and is 194 also not what other printf implementations are doing. */ 195 196 if(prec <= nlen && nstr[nstart] != '0' && nstr[nstart] != '\0') 197 prec = nlen + 1; 198 } 199 200 /* possible formats: 201 pad | sign | alt | zero | digits 202 sign | alt | zero | digits | pad minus_flag 203 sign | alt | zero | digits zero_flag */ 204 205 /* if not right justifying or padding with zeros, we need to 206 compute the length of the rest of the string, and then pad with 207 spaces */ 208 if(!(flags & (minus_flag | zero_flag))) { 209 if(prec > nlen) 210 width -= prec; 211 else 212 width -= nlen; 213 214 if(use_alternative(flags, num, base)) 215 width -= 2; 216 217 if(signchar != '\0') 218 width--; 219 220 /* pad to width */ 221 len += pad(state, width, ' '); 222 } 223 if(signchar != '\0') { 224 (*state->append_char)(state, signchar); 225 ++len; 226 } 227 if(use_alternative(flags, num, base)) { 228 (*state->append_char)(state, '0'); 229 (*state->append_char)(state, rep[10] + 23); /* XXX */ 230 len += 2; 231 } 232 if(flags & zero_flag) { 233 /* pad to width with zeros */ 234 if(prec - nlen > width - len - nlen) 235 len += pad(state, prec - nlen, '0'); 236 else 237 len += pad(state, width - len - nlen, '0'); 238 } else 239 /* pad to prec with zeros */ 240 len += pad(state, prec - nlen, '0'); 241 242 while(nstr[nstart] != '\0') { 243 (*state->append_char)(state, nstr[nstart++]); 244 ++len; 245 } 246 247 if(flags & minus_flag) 248 len += pad(state, width - len, ' '); 249 250 return len; 251 } 252 253 /* 254 * return length 255 */ 256 257 static size_t 258 append_string (struct snprintf_state *state, 259 const unsigned char *arg, 260 int width, 261 int prec, 262 int flags) 263 { 264 size_t len = 0; 265 266 if(arg == NULL) 267 arg = (const unsigned char*)"(null)"; 268 269 if(prec != -1) 270 width -= prec; 271 else 272 width -= strlen((const char *)arg); 273 if(!(flags & minus_flag)) 274 len += pad(state, width, ' '); 275 276 if (prec != -1) { 277 while (*arg && prec--) { 278 (*state->append_char) (state, *arg++); 279 ++len; 280 } 281 } else { 282 while (*arg) { 283 (*state->append_char) (state, *arg++); 284 ++len; 285 } 286 } 287 if(flags & minus_flag) 288 len += pad(state, width, ' '); 289 return len; 290 } 291 292 static int 293 append_char(struct snprintf_state *state, 294 unsigned char arg, 295 int width, 296 int flags) 297 { 298 int len = 0; 299 300 while(!(flags & minus_flag) && --width > 0) { 301 (*state->append_char) (state, ' ') ; 302 ++len; 303 } 304 (*state->append_char) (state, arg); 305 ++len; 306 while((flags & minus_flag) && --width > 0) { 307 (*state->append_char) (state, ' '); 308 ++len; 309 } 310 return 0; 311 } 312 313 /* 314 * This can't be made into a function... 315 */ 316 317 #ifdef HAVE_LONG_LONG 318 319 #define PARSE_INT_FORMAT(res, arg, unsig) \ 320 if (long_long_flag) \ 321 res = (unsig long long)va_arg(arg, unsig long long); \ 322 else if (long_flag) \ 323 res = (unsig long)va_arg(arg, unsig long); \ 324 else if (size_t_flag) \ 325 res = (unsig long)va_arg(arg, size_t); \ 326 else if (short_flag) \ 327 res = (unsig short)va_arg(arg, unsig int); \ 328 else \ 329 res = (unsig int)va_arg(arg, unsig int) 330 331 #else 332 333 #define PARSE_INT_FORMAT(res, arg, unsig) \ 334 if (long_flag) \ 335 res = (unsig long)va_arg(arg, unsig long); \ 336 else if (size_t_flag) \ 337 res = (unsig long)va_arg(arg, size_t); \ 338 else if (short_flag) \ 339 res = (unsig short)va_arg(arg, unsig int); \ 340 else \ 341 res = (unsig int)va_arg(arg, unsig int) 342 343 #endif 344 345 /* 346 * zyxprintf - return length, as snprintf 347 */ 348 349 static size_t 350 xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap) 351 { 352 const unsigned char *format = (const unsigned char *)char_format; 353 unsigned char c; 354 size_t len = 0; 355 356 while((c = *format++)) { 357 if (c == '%') { 358 int flags = 0; 359 int width = 0; 360 int prec = -1; 361 int size_t_flag = 0; 362 int long_long_flag = 0; 363 int long_flag = 0; 364 int short_flag = 0; 365 366 /* flags */ 367 while((c = *format++)){ 368 if(c == '-') 369 flags |= minus_flag; 370 else if(c == '+') 371 flags |= plus_flag; 372 else if(c == ' ') 373 flags |= space_flag; 374 else if(c == '#') 375 flags |= alternate_flag; 376 else if(c == '0') 377 flags |= zero_flag; 378 else if(c == '\'') 379 ; /* just ignore */ 380 else 381 break; 382 } 383 384 if((flags & space_flag) && (flags & plus_flag)) 385 flags ^= space_flag; 386 387 if((flags & minus_flag) && (flags & zero_flag)) 388 flags ^= zero_flag; 389 390 /* width */ 391 if (isdigit(c)) 392 do { 393 width = width * 10 + c - '0'; 394 c = *format++; 395 } while(isdigit(c)); 396 else if(c == '*') { 397 width = va_arg(ap, int); 398 c = *format++; 399 } 400 401 /* precision */ 402 if (c == '.') { 403 prec = 0; 404 c = *format++; 405 if (isdigit(c)) 406 do { 407 prec = prec * 10 + c - '0'; 408 c = *format++; 409 } while(isdigit(c)); 410 else if (c == '*') { 411 prec = va_arg(ap, int); 412 c = *format++; 413 } 414 } 415 416 /* size */ 417 418 if (c == 'h') { 419 short_flag = 1; 420 c = *format++; 421 } else if (c == 'z') { 422 size_t_flag = 1; 423 c = *format++; 424 } else if (c == 'l') { 425 long_flag = 1; 426 c = *format++; 427 if (c == 'l') { 428 long_long_flag = 1; 429 c = *format++; 430 } 431 } 432 433 if(c != 'd' && c != 'i') 434 flags &= ~(plus_flag | space_flag); 435 436 switch (c) { 437 case 'c' : 438 append_char(state, va_arg(ap, int), width, flags); 439 ++len; 440 break; 441 case 's' : 442 len += append_string(state, 443 va_arg(ap, unsigned char*), 444 width, 445 prec, 446 flags); 447 break; 448 case 'd' : 449 case 'i' : { 450 longest arg; 451 u_longest num; 452 int minusp = 0; 453 454 PARSE_INT_FORMAT(arg, ap, signed); 455 456 if (arg < 0) { 457 minusp = 1; 458 num = -arg; 459 } else 460 num = arg; 461 462 len += append_number (state, num, 10, "0123456789", 463 width, prec, flags, minusp); 464 break; 465 } 466 case 'u' : { 467 u_longest arg; 468 469 PARSE_INT_FORMAT(arg, ap, unsigned); 470 471 len += append_number (state, arg, 10, "0123456789", 472 width, prec, flags, 0); 473 break; 474 } 475 case 'o' : { 476 u_longest arg; 477 478 PARSE_INT_FORMAT(arg, ap, unsigned); 479 480 len += append_number (state, arg, 010, "01234567", 481 width, prec, flags, 0); 482 break; 483 } 484 case 'x' : { 485 u_longest arg; 486 487 PARSE_INT_FORMAT(arg, ap, unsigned); 488 489 len += append_number (state, arg, 0x10, "0123456789abcdef", 490 width, prec, flags, 0); 491 break; 492 } 493 case 'X' :{ 494 u_longest arg; 495 496 PARSE_INT_FORMAT(arg, ap, unsigned); 497 498 len += append_number (state, arg, 0x10, "0123456789ABCDEF", 499 width, prec, flags, 0); 500 break; 501 } 502 case 'p' : { 503 u_longest arg = (u_longest)va_arg(ap, void*); 504 505 len += append_number (state, arg, 0x10, "0123456789ABCDEF", 506 width, prec, flags, 0); 507 break; 508 } 509 case 'n' : { 510 int *arg = va_arg(ap, int*); 511 *arg = state->s - state->str; 512 break; 513 } 514 case '\0' : 515 --format; 516 /* FALLTHROUGH */ 517 case '%' : 518 (*state->append_char)(state, c); 519 ++len; 520 break; 521 default : 522 (*state->append_char)(state, '%'); 523 (*state->append_char)(state, c); 524 len += 2; 525 break; 526 } 527 } else { 528 (*state->append_char) (state, c); 529 ++len; 530 } 531 } 532 return len; 533 } 534 535 #if !defined(HAVE_SNPRINTF) || defined(TEST_SNPRINTF) 536 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 537 rk_snprintf (char *str, size_t sz, const char *format, ...) 538 { 539 va_list args; 540 int ret; 541 542 va_start(args, format); 543 ret = vsnprintf (str, sz, format, args); 544 va_end(args); 545 546 #ifdef PARANOIA 547 { 548 int ret2; 549 char *tmp; 550 551 tmp = malloc (sz); 552 if (tmp == NULL) 553 abort (); 554 555 va_start(args, format); 556 ret2 = vsprintf (tmp, format, args); 557 va_end(args); 558 if (ret != ret2 || strcmp(str, tmp)) 559 abort (); 560 free (tmp); 561 } 562 #endif 563 564 return ret; 565 } 566 #endif 567 568 #if !defined(HAVE_ASPRINTF) || defined(TEST_SNPRINTF) 569 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 570 rk_asprintf (char **ret, const char *format, ...) 571 { 572 va_list args; 573 int val; 574 575 va_start(args, format); 576 val = vasprintf (ret, format, args); 577 va_end(args); 578 579 #ifdef PARANOIA 580 { 581 int ret2; 582 char *tmp; 583 tmp = malloc (val + 1); 584 if (tmp == NULL) 585 abort (); 586 587 va_start(args, format); 588 ret2 = vsprintf (tmp, format, args); 589 va_end(args); 590 if (val != ret2 || strcmp(*ret, tmp)) 591 abort (); 592 free (tmp); 593 } 594 #endif 595 596 return val; 597 } 598 #endif 599 600 #if !defined(HAVE_ASNPRINTF) || defined(TEST_SNPRINTF) 601 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 602 rk_asnprintf (char **ret, size_t max_sz, const char *format, ...) 603 { 604 va_list args; 605 int val; 606 607 va_start(args, format); 608 val = vasnprintf (ret, max_sz, format, args); 609 610 #ifdef PARANOIA 611 { 612 int ret2; 613 char *tmp; 614 tmp = malloc (val + 1); 615 if (tmp == NULL) 616 abort (); 617 618 ret2 = vsprintf (tmp, format, args); 619 if (val != ret2 || strcmp(*ret, tmp)) 620 abort (); 621 free (tmp); 622 } 623 #endif 624 625 va_end(args); 626 return val; 627 } 628 #endif 629 630 #if !defined(HAVE_VASPRINTF) || defined(TEST_SNPRINTF) 631 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 632 rk_vasprintf (char **ret, const char *format, va_list args) 633 { 634 return vasnprintf (ret, 0, format, args); 635 } 636 #endif 637 638 639 #if !defined(HAVE_VASNPRINTF) || defined(TEST_SNPRINTF) 640 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 641 rk_vasnprintf (char **ret, size_t max_sz, const char *format, va_list args) 642 { 643 size_t st; 644 struct snprintf_state state; 645 646 state.max_sz = max_sz; 647 state.sz = 1; 648 state.str = malloc(state.sz); 649 if (state.str == NULL) { 650 *ret = NULL; 651 return -1; 652 } 653 state.s = state.str; 654 state.theend = state.s + state.sz - 1; 655 state.append_char = as_append_char; 656 657 st = xyzprintf (&state, format, args); 658 if (st > state.sz) { 659 free (state.str); 660 *ret = NULL; 661 return -1; 662 } else { 663 char *tmp; 664 665 *state.s = '\0'; 666 tmp = realloc (state.str, st+1); 667 if (tmp == NULL) { 668 free (state.str); 669 *ret = NULL; 670 return -1; 671 } 672 *ret = tmp; 673 return st; 674 } 675 } 676 #endif 677 678 #if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF) 679 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 680 rk_vsnprintf (char *str, size_t sz, const char *format, va_list args) 681 { 682 struct snprintf_state state; 683 int ret; 684 unsigned char *ustr = (unsigned char *)str; 685 686 state.max_sz = 0; 687 state.sz = sz; 688 state.str = ustr; 689 state.s = ustr; 690 state.theend = ustr + sz - (sz > 0); 691 state.append_char = sn_append_char; 692 693 ret = xyzprintf (&state, format, args); 694 if (state.s != NULL && sz != 0) 695 *state.s = '\0'; 696 return ret; 697 } 698 #endif 699