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