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