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