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