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