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.25 2000/02/16 01:38:52 assar 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(prec != -1) 218 width -= prec; 219 else 220 width -= strlen((char *)arg); 221 if(!(flags & minus_flag)) 222 while(width-- > 0) 223 if((*state->append_char) (state, ' ')) 224 return 1; 225 if (prec != -1) { 226 while (*arg && prec--) 227 if ((*state->append_char) (state, *arg++)) 228 return 1; 229 } else { 230 while (*arg) 231 if ((*state->append_char) (state, *arg++)) 232 return 1; 233 } 234 if(flags & minus_flag) 235 while(width-- > 0) 236 if((*state->append_char) (state, ' ')) 237 return 1; 238 return 0; 239 } 240 241 static int 242 append_char(struct state *state, 243 unsigned char arg, 244 int width, 245 int flags) 246 { 247 while(!(flags & minus_flag) && --width > 0) 248 if((*state->append_char) (state, ' ')) 249 return 1; 250 251 if((*state->append_char) (state, arg)) 252 return 1; 253 while((flags & minus_flag) && --width > 0) 254 if((*state->append_char) (state, ' ')) 255 return 1; 256 257 return 0; 258 } 259 260 /* 261 * This can't be made into a function... 262 */ 263 264 #define PARSE_INT_FORMAT(res, arg, unsig) \ 265 if (long_flag) \ 266 res = (unsig long)va_arg(arg, unsig long); \ 267 else if (short_flag) \ 268 res = (unsig short)va_arg(arg, unsig int); \ 269 else \ 270 res = (unsig int)va_arg(arg, unsig int) 271 272 /* 273 * zyxprintf - return 0 or -1 274 */ 275 276 static int 277 xyzprintf (struct state *state, const char *char_format, va_list ap) 278 { 279 const unsigned char *format = (const unsigned char *)char_format; 280 unsigned char c; 281 282 while((c = *format++)) { 283 if (c == '%') { 284 int flags = 0; 285 int width = 0; 286 int prec = -1; 287 int long_flag = 0; 288 int short_flag = 0; 289 290 /* flags */ 291 while((c = *format++)){ 292 if(c == '-') 293 flags |= minus_flag; 294 else if(c == '+') 295 flags |= plus_flag; 296 else if(c == ' ') 297 flags |= space_flag; 298 else if(c == '#') 299 flags |= alternate_flag; 300 else if(c == '0') 301 flags |= zero_flag; 302 else 303 break; 304 } 305 306 if((flags & space_flag) && (flags & plus_flag)) 307 flags ^= space_flag; 308 309 if((flags & minus_flag) && (flags & zero_flag)) 310 flags ^= zero_flag; 311 312 /* width */ 313 if (isdigit(c)) 314 do { 315 width = width * 10 + c - '0'; 316 c = *format++; 317 } while(isdigit(c)); 318 else if(c == '*') { 319 width = va_arg(ap, int); 320 c = *format++; 321 } 322 323 /* precision */ 324 if (c == '.') { 325 prec = 0; 326 c = *format++; 327 if (isdigit(c)) 328 do { 329 prec = prec * 10 + c - '0'; 330 c = *format++; 331 } while(isdigit(c)); 332 else if (c == '*') { 333 prec = va_arg(ap, int); 334 c = *format++; 335 } 336 } 337 338 /* size */ 339 340 if (c == 'h') { 341 short_flag = 1; 342 c = *format++; 343 } else if (c == 'l') { 344 long_flag = 1; 345 c = *format++; 346 } 347 348 switch (c) { 349 case 'c' : 350 if(append_char(state, va_arg(ap, int), width, flags)) 351 return -1; 352 break; 353 case 's' : 354 if (append_string(state, 355 va_arg(ap, unsigned char*), 356 width, 357 prec, 358 flags)) 359 return -1; 360 break; 361 case 'd' : 362 case 'i' : { 363 long arg; 364 unsigned long num; 365 int minusp = 0; 366 367 PARSE_INT_FORMAT(arg, ap, signed); 368 369 if (arg < 0) { 370 minusp = 1; 371 num = -arg; 372 } else 373 num = arg; 374 375 if (append_number (state, num, 10, "0123456789", 376 width, prec, flags, minusp)) 377 return -1; 378 break; 379 } 380 case 'u' : { 381 unsigned long arg; 382 383 PARSE_INT_FORMAT(arg, ap, unsigned); 384 385 if (append_number (state, arg, 10, "0123456789", 386 width, prec, flags, 0)) 387 return -1; 388 break; 389 } 390 case 'o' : { 391 unsigned long arg; 392 393 PARSE_INT_FORMAT(arg, ap, unsigned); 394 395 if (append_number (state, arg, 010, "01234567", 396 width, prec, flags, 0)) 397 return -1; 398 break; 399 } 400 case 'x' : { 401 unsigned long arg; 402 403 PARSE_INT_FORMAT(arg, ap, unsigned); 404 405 if (append_number (state, arg, 0x10, "0123456789abcdef", 406 width, prec, flags, 0)) 407 return -1; 408 break; 409 } 410 case 'X' :{ 411 unsigned long arg; 412 413 PARSE_INT_FORMAT(arg, ap, unsigned); 414 415 if (append_number (state, arg, 0x10, "0123456789ABCDEF", 416 width, prec, flags, 0)) 417 return -1; 418 break; 419 } 420 case 'p' : { 421 unsigned long arg = (unsigned long)va_arg(ap, void*); 422 423 if (append_number (state, arg, 0x10, "0123456789ABCDEF", 424 width, prec, flags, 0)) 425 return -1; 426 break; 427 } 428 case 'n' : { 429 int *arg = va_arg(ap, int*); 430 *arg = state->s - state->str; 431 break; 432 } 433 case '\0' : 434 --format; 435 /* FALLTHROUGH */ 436 case '%' : 437 if ((*state->append_char)(state, c)) 438 return -1; 439 break; 440 default : 441 if ( (*state->append_char)(state, '%') 442 || (*state->append_char)(state, c)) 443 return -1; 444 break; 445 } 446 } else 447 if ((*state->append_char) (state, c)) 448 return -1; 449 } 450 return 0; 451 } 452 453 #ifndef HAVE_SNPRINTF 454 int 455 snprintf (char *str, size_t sz, const char *format, ...) 456 { 457 va_list args; 458 int ret; 459 460 va_start(args, format); 461 ret = vsnprintf (str, sz, format, args); 462 463 #ifdef PARANOIA 464 { 465 int ret2; 466 char *tmp; 467 468 tmp = malloc (sz); 469 if (tmp == NULL) 470 abort (); 471 472 ret2 = vsprintf (tmp, format, args); 473 if (ret != ret2 || strcmp(str, tmp)) 474 abort (); 475 free (tmp); 476 } 477 #endif 478 479 va_end(args); 480 return ret; 481 } 482 #endif 483 484 #ifndef HAVE_ASPRINTF 485 int 486 asprintf (char **ret, const char *format, ...) 487 { 488 va_list args; 489 int val; 490 491 va_start(args, format); 492 val = vasprintf (ret, format, args); 493 494 #ifdef PARANOIA 495 { 496 int ret2; 497 char *tmp; 498 tmp = malloc (val + 1); 499 if (tmp == NULL) 500 abort (); 501 502 ret2 = vsprintf (tmp, format, args); 503 if (val != ret2 || strcmp(*ret, tmp)) 504 abort (); 505 free (tmp); 506 } 507 #endif 508 509 va_end(args); 510 return val; 511 } 512 #endif 513 514 #ifndef HAVE_ASNPRINTF 515 int 516 asnprintf (char **ret, size_t max_sz, const char *format, ...) 517 { 518 va_list args; 519 int val; 520 521 va_start(args, format); 522 val = vasnprintf (ret, max_sz, format, args); 523 524 #ifdef PARANOIA 525 { 526 int ret2; 527 char *tmp; 528 tmp = malloc (val + 1); 529 if (tmp == NULL) 530 abort (); 531 532 ret2 = vsprintf (tmp, format, args); 533 if (val != ret2 || strcmp(*ret, tmp)) 534 abort (); 535 free (tmp); 536 } 537 #endif 538 539 va_end(args); 540 return val; 541 } 542 #endif 543 544 #ifndef HAVE_VASPRINTF 545 int 546 vasprintf (char **ret, const char *format, va_list args) 547 { 548 return vasnprintf (ret, 0, format, args); 549 } 550 #endif 551 552 553 #ifndef HAVE_VASNPRINTF 554 int 555 vasnprintf (char **ret, size_t max_sz, const char *format, va_list args) 556 { 557 int st; 558 size_t len; 559 struct state state; 560 561 state.max_sz = max_sz; 562 state.sz = 1; 563 state.str = malloc(state.sz); 564 if (state.str == NULL) { 565 *ret = NULL; 566 return -1; 567 } 568 state.s = state.str; 569 state.theend = state.s + state.sz - 1; 570 state.append_char = as_append_char; 571 state.reserve = as_reserve; 572 573 st = xyzprintf (&state, format, args); 574 if (st) { 575 free (state.str); 576 *ret = NULL; 577 return -1; 578 } else { 579 char *tmp; 580 581 *state.s = '\0'; 582 len = state.s - state.str; 583 tmp = realloc (state.str, len+1); 584 if (tmp == NULL) { 585 free (state.str); 586 *ret = NULL; 587 return -1; 588 } 589 *ret = tmp; 590 return len; 591 } 592 } 593 #endif 594 595 #ifndef HAVE_VSNPRINTF 596 int 597 vsnprintf (char *str, size_t sz, const char *format, va_list args) 598 { 599 struct state state; 600 int ret; 601 unsigned char *ustr = (unsigned char *)str; 602 603 state.max_sz = 0; 604 state.sz = sz; 605 state.str = ustr; 606 state.s = ustr; 607 state.theend = ustr + sz - 1; 608 state.append_char = sn_append_char; 609 state.reserve = sn_reserve; 610 611 ret = xyzprintf (&state, format, args); 612 *state.s = '\0'; 613 if (ret) 614 return sz; 615 else 616 return state.s - state.str; 617 } 618 #endif 619 620