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