1/* $NetBSD: snprintf.c,v 1.3 2023/06/19 21:41:45 christos Exp $ */ 2 3/* 4 * Copyright (c) 1995-2003 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include <config.h> 37#include <stdio.h> 38#include <stdarg.h> 39#include <stdlib.h> 40#include <string.h> 41#include <ctype.h> 42#include <krb5/roken.h> 43#include <assert.h> 44 45enum 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 57struct snprintf_state { 58 unsigned char *str; 59 unsigned char *s; 60 unsigned char *theend; 61 size_t sz; 62 size_t max_sz; 63 void (*append_char)(struct snprintf_state *, unsigned char); 64 /* XXX - methods */ 65}; 66 67#if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF) 68static int 69sn_reserve (struct snprintf_state *state, size_t n) 70{ 71 return state->s + n > state->theend; 72} 73 74static void 75sn_append_char (struct snprintf_state *state, unsigned char c) 76{ 77 if (!sn_reserve (state, 1)) 78 *state->s++ = c; 79} 80#endif 81 82static int 83as_reserve (struct snprintf_state *state, size_t n) 84{ 85 if (state->s + n > state->theend) { 86 int off = state->s - state->str; 87 unsigned char *tmp; 88 89 if (state->max_sz && state->sz >= state->max_sz) 90 return 1; 91 92 state->sz = max(state->sz * 2, state->sz + n); 93 if (state->max_sz) 94 state->sz = min(state->sz, state->max_sz); 95 tmp = realloc (state->str, state->sz); 96 if (tmp == NULL) 97 return 1; 98 state->str = tmp; 99 state->s = state->str + off; 100 state->theend = state->str + state->sz - 1; 101 } 102 return 0; 103} 104 105static void 106as_append_char (struct snprintf_state *state, unsigned char c) 107{ 108 if(!as_reserve (state, 1)) 109 *state->s++ = c; 110} 111 112/* longest integer types */ 113 114#ifdef HAVE_LONG_LONG 115typedef unsigned long long u_longest; 116typedef long long longest; 117#else 118typedef unsigned long u_longest; 119typedef long longest; 120#endif 121 122#ifndef HAVE_UINTPTR_T 123typedef u_longest uintptr_t; 124#endif 125 126 127 128static size_t 129pad(struct snprintf_state *state, int width, char c) 130{ 131 size_t len = 0; 132 while(width-- > 0){ 133 (*state->append_char)(state, c); 134 ++len; 135 } 136 return len; 137} 138 139/* return true if we should use alternatve hex form */ 140static int 141use_alternative (int flags, u_longest num, unsigned base) 142{ 143 return (flags & alternate_flag) && base == 16 && num != 0; 144} 145 146static int 147append_number(struct snprintf_state *state, 148 u_longest num, unsigned base, const char *rep, 149 int width, int prec, int flags, int minusp) 150{ 151 int len = 0; 152 u_longest n = num; 153 char nstr[64]; /* enough for <192 bit octal integers */ 154 int nstart, nlen; 155 char signchar; 156 157 /* given precision, ignore zero flag */ 158 if(prec != -1) 159 flags &= ~zero_flag; 160 else 161 prec = 1; 162 163 /* format number as string */ 164 nstart = sizeof(nstr); 165 nlen = 0; 166 nstr[--nstart] = '\0'; 167 do { 168 assert(nstart > 0); 169 nstr[--nstart] = rep[n % base]; 170 ++nlen; 171 n /= base; 172 } while(n); 173 174 /* zero value with zero precision should produce no digits */ 175 if(prec == 0 && num == 0) { 176 nlen--; 177 nstart++; 178 } 179 180 /* figure out what char to use for sign */ 181 if(minusp) 182 signchar = '-'; 183 else if((flags & plus_flag)) 184 signchar = '+'; 185 else if((flags & space_flag)) 186 signchar = ' '; 187 else 188 signchar = '\0'; 189 190 if((flags & alternate_flag) && base == 8) { 191 /* if necessary, increase the precision to 192 make first digit a zero */ 193 194 /* XXX C99 claims (regarding # and %o) that "if the value and 195 precision are both 0, a single 0 is printed", but there is 196 no such wording for %x. This would mean that %#.o would 197 output "0", but %#.x "". This does not make sense, and is 198 also not what other printf implementations are doing. */ 199 200 if(prec <= nlen && nstr[nstart] != '0' && nstr[nstart] != '\0') 201 prec = nlen + 1; 202 } 203 204 /* possible formats: 205 pad | sign | alt | zero | digits 206 sign | alt | zero | digits | pad minus_flag 207 sign | alt | zero | digits zero_flag */ 208 209 /* if not right justifying or padding with zeros, we need to 210 compute the length of the rest of the string, and then pad with 211 spaces */ 212 if(!(flags & (minus_flag | zero_flag))) { 213 if(prec > nlen) 214 width -= prec; 215 else 216 width -= nlen; 217 218 if(use_alternative(flags, num, base)) 219 width -= 2; 220 221 if(signchar != '\0') 222 width--; 223 224 /* pad to width */ 225 len += pad(state, width, ' '); 226 } 227 if(signchar != '\0') { 228 (*state->append_char)(state, signchar); 229 ++len; 230 } 231 if(use_alternative(flags, num, base)) { 232 (*state->append_char)(state, '0'); 233 (*state->append_char)(state, rep[10] + 23); /* XXX */ 234 len += 2; 235 } 236 if(flags & zero_flag) { 237 /* pad to width with zeros */ 238 if(prec - nlen > width - len - nlen) 239 len += pad(state, prec - nlen, '0'); 240 else 241 len += pad(state, width - len - nlen, '0'); 242 } else 243 /* pad to prec with zeros */ 244 len += pad(state, prec - nlen, '0'); 245 246 while(nstr[nstart] != '\0') { 247 (*state->append_char)(state, nstr[nstart++]); 248 ++len; 249 } 250 251 if(flags & minus_flag) 252 len += pad(state, width - len, ' '); 253 254 return len; 255} 256 257/* 258 * return length 259 */ 260 261static size_t 262append_string (struct snprintf_state *state, 263 const unsigned char *arg, 264 int width, 265 int prec, 266 int flags) 267{ 268 size_t len = 0; 269 270 if(arg == NULL) 271 arg = (const unsigned char*)"(null)"; 272 273 if(prec != -1) 274 width -= prec; 275 else 276 width -= strlen((const char *)arg); 277 if(!(flags & minus_flag)) 278 len += pad(state, width, ' '); 279 280 if (prec != -1) { 281 while (prec-- && *arg) { 282 (*state->append_char) (state, *arg++); 283 ++len; 284 } 285 } else { 286 while (*arg) { 287 (*state->append_char) (state, *arg++); 288 ++len; 289 } 290 } 291 if(flags & minus_flag) 292 len += pad(state, width, ' '); 293 return len; 294} 295 296static int 297append_char(struct snprintf_state *state, 298 unsigned char arg, 299 int width, 300 int flags) 301{ 302 int len = 0; 303 304 while(!(flags & minus_flag) && --width > 0) { 305 (*state->append_char) (state, ' ') ; 306 ++len; 307 } 308 (*state->append_char) (state, arg); 309 ++len; 310 while((flags & minus_flag) && --width > 0) { 311 (*state->append_char) (state, ' '); 312 ++len; 313 } 314 return 0; 315} 316 317/* 318 * This can't be made into a function... 319 */ 320 321#ifdef HAVE_LONG_LONG 322 323#define PARSE_INT_FORMAT(res, arg, unsig) \ 324if (long_long_flag) \ 325 res = (unsig long long)va_arg(arg, unsig long long); \ 326else if (long_flag) \ 327 res = (unsig long)va_arg(arg, unsig long); \ 328else if (size_t_flag) \ 329 res = (unsig long)va_arg(arg, size_t); \ 330else if (short_flag) \ 331 res = (unsig short)va_arg(arg, unsig int); \ 332else \ 333 res = (unsig int)va_arg(arg, unsig int) 334 335#else 336 337#define PARSE_INT_FORMAT(res, arg, unsig) \ 338if (long_flag) \ 339 res = (unsig long)va_arg(arg, unsig long); \ 340else if (size_t_flag) \ 341 res = (unsig long)va_arg(arg, size_t); \ 342else if (short_flag) \ 343 res = (unsig short)va_arg(arg, unsig int); \ 344else \ 345 res = (unsig int)va_arg(arg, unsig int) 346 347#endif 348 349/* 350 * zyxprintf - return length, as snprintf 351 */ 352 353static size_t 354xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap) 355{ 356 const unsigned char *format = (const unsigned char *)char_format; 357 unsigned char c; 358 size_t len = 0; 359 360 while((c = *format++)) { 361 if (c == '%') { 362 int flags = 0; 363 int width = 0; 364 int prec = -1; 365 int size_t_flag = 0; 366 int long_long_flag = 0; 367 int long_flag = 0; 368 int short_flag = 0; 369 370 /* flags */ 371 while((c = *format++)){ 372 if(c == '-') 373 flags |= minus_flag; 374 else if(c == '+') 375 flags |= plus_flag; 376 else if(c == ' ') 377 flags |= space_flag; 378 else if(c == '#') 379 flags |= alternate_flag; 380 else if(c == '0') 381 flags |= zero_flag; 382 else if(c == '\'') 383 ; /* just ignore */ 384 else 385 break; 386 } 387 388 if((flags & space_flag) && (flags & plus_flag)) 389 flags ^= space_flag; 390 391 if((flags & minus_flag) && (flags & zero_flag)) 392 flags ^= zero_flag; 393 394 /* width */ 395 if (isdigit(c)) 396 do { 397 width = width * 10 + c - '0'; 398 c = *format++; 399 } while(isdigit(c)); 400 else if(c == '*') { 401 width = va_arg(ap, int); 402 c = *format++; 403 } 404 405 /* precision */ 406 if (c == '.') { 407 prec = 0; 408 c = *format++; 409 if (isdigit(c)) 410 do { 411 prec = prec * 10 + c - '0'; 412 c = *format++; 413 } while(isdigit(c)); 414 else if (c == '*') { 415 prec = va_arg(ap, int); 416 c = *format++; 417 } 418 } 419 420 /* size */ 421 422 if (c == 'h') { 423 short_flag = 1; 424 c = *format++; 425 } else if (c == 'z') { 426 size_t_flag = 1; 427 c = *format++; 428 } else if (c == 'l') { 429 long_flag = 1; 430 c = *format++; 431 if (c == 'l') { 432 long_long_flag = 1; 433 c = *format++; 434 } 435 } 436 437 if(c != 'd' && c != 'i') 438 flags &= ~(plus_flag | space_flag); 439 440 switch (c) { 441 case 'c' : 442 append_char(state, va_arg(ap, int), width, flags); 443 ++len; 444 break; 445 case 's' : 446 len += append_string(state, 447 va_arg(ap, unsigned char*), 448 width, 449 prec, 450 flags); 451 break; 452 case 'd' : 453 case 'i' : { 454 int64_t arg; 455 uint64_t num; 456 int minusp = 0; 457 458 PARSE_INT_FORMAT(arg, ap, signed); 459 460 if (arg == INT64_MIN) { 461 minusp = 1; 462 num = (uint64_t)INT64_MAX + 1; 463 } else if (arg < 0) { 464 minusp = 1; 465 num = -arg; 466 } else 467 num = arg; 468 469 len += append_number (state, num, 10, "0123456789", 470 width, prec, flags, minusp); 471 break; 472 } 473 case 'u' : { 474 u_longest arg; 475 476 PARSE_INT_FORMAT(arg, ap, unsigned); 477 478 len += append_number (state, arg, 10, "0123456789", 479 width, prec, flags, 0); 480 break; 481 } 482 case 'o' : { 483 u_longest arg; 484 485 PARSE_INT_FORMAT(arg, ap, unsigned); 486 487 len += append_number (state, arg, 010, "01234567", 488 width, prec, flags, 0); 489 break; 490 } 491 case 'x' : { 492 u_longest arg; 493 494 PARSE_INT_FORMAT(arg, ap, unsigned); 495 496 len += append_number (state, arg, 0x10, "0123456789abcdef", 497 width, prec, flags, 0); 498 break; 499 } 500 case 'X' :{ 501 u_longest arg; 502 503 PARSE_INT_FORMAT(arg, ap, unsigned); 504 505 len += append_number (state, arg, 0x10, "0123456789ABCDEF", 506 width, prec, flags, 0); 507 break; 508 } 509 case 'p' : { 510 uintptr_t arg = (uintptr_t)va_arg(ap, void*); 511 512 len += append_number (state, arg, 0x10, "0123456789ABCDEF", 513 width, prec, flags, 0); 514 break; 515 } 516 case 'n' : { 517 int *arg = va_arg(ap, int*); 518 *arg = state->s - state->str; 519 break; 520 } 521 case '\0' : 522 --format; 523 /* FALLTHROUGH */ 524 case '%' : 525 (*state->append_char)(state, c); 526 ++len; 527 break; 528 default : 529 (*state->append_char)(state, '%'); 530 (*state->append_char)(state, c); 531 len += 2; 532 break; 533 } 534 } else { 535 (*state->append_char) (state, c); 536 ++len; 537 } 538 } 539 return len; 540} 541 542#if !defined(HAVE_SNPRINTF) || defined(TEST_SNPRINTF) 543ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 544rk_snprintf (char *str, size_t sz, const char *format, ...) 545{ 546 va_list args; 547 int ret; 548 549 va_start(args, format); 550 ret = vsnprintf (str, sz, format, args); 551 va_end(args); 552 553#ifdef PARANOIA 554 { 555 int ret2; 556 char *tmp; 557 558 tmp = malloc (sz); 559 if (tmp == NULL) 560 abort (); 561 562 va_start(args, format); 563 ret2 = vsprintf (tmp, format, args); 564 va_end(args); 565 if (ret != ret2 || strcmp(str, tmp)) 566 abort (); 567 free (tmp); 568 } 569#endif 570 571 return ret; 572} 573#endif 574 575#if !defined(HAVE_ASPRINTF) || defined(TEST_SNPRINTF) 576ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 577rk_asprintf (char **ret, const char *format, ...) 578{ 579 va_list args; 580 int val; 581 582 va_start(args, format); 583 val = vasprintf (ret, format, args); 584 va_end(args); 585 586#ifdef PARANOIA 587 { 588 int ret2; 589 char *tmp; 590 tmp = malloc (val + 1); 591 if (tmp == NULL) 592 abort (); 593 594 va_start(args, format); 595 ret2 = vsprintf (tmp, format, args); 596 va_end(args); 597 if (val != ret2 || strcmp(*ret, tmp)) 598 abort (); 599 free (tmp); 600 } 601#endif 602 603 return val; 604} 605#endif 606 607#if !defined(HAVE_ASNPRINTF) || defined(TEST_SNPRINTF) 608ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 609rk_asnprintf (char **ret, size_t max_sz, const char *format, ...) 610{ 611 va_list args; 612 int val; 613 614 va_start(args, format); 615 val = vasnprintf (ret, max_sz, format, args); 616 617#ifdef PARANOIA 618 { 619 int ret2; 620 char *tmp; 621 tmp = malloc (val + 1); 622 if (tmp == NULL) 623 abort (); 624 625 ret2 = vsprintf (tmp, format, args); 626 if (val != ret2 || strcmp(*ret, tmp)) 627 abort (); 628 free (tmp); 629 } 630#endif 631 632 va_end(args); 633 return val; 634} 635#endif 636 637#if !defined(HAVE_VASPRINTF) || defined(TEST_SNPRINTF) 638ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 639rk_vasprintf (char **ret, const char *format, va_list args) 640{ 641 return vasnprintf (ret, 0, format, args); 642} 643#endif 644 645 646#if !defined(HAVE_VASNPRINTF) || defined(TEST_SNPRINTF) 647ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 648rk_vasnprintf (char **ret, size_t max_sz, const char *format, va_list args) 649{ 650 size_t st; 651 struct snprintf_state state; 652 653 state.max_sz = max_sz; 654 state.sz = 1; 655 state.str = malloc(state.sz); 656 if (state.str == NULL) { 657 *ret = NULL; 658 return -1; 659 } 660 state.s = state.str; 661 state.theend = state.s + state.sz - 1; 662 state.append_char = as_append_char; 663 664 st = xyzprintf (&state, format, args); 665 if (st > state.sz) { 666 free (state.str); 667 *ret = NULL; 668 return -1; 669 } else { 670 char *tmp; 671 672 *state.s = '\0'; 673 tmp = realloc (state.str, st+1); 674 if (tmp == NULL) { 675 free (state.str); 676 *ret = NULL; 677 return -1; 678 } 679 *ret = tmp; 680 return st; 681 } 682} 683#endif 684 685#if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF) 686ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 687rk_vsnprintf (char *str, size_t sz, const char *format, va_list args) 688{ 689 struct snprintf_state state; 690 int ret; 691 unsigned char *ustr = (unsigned char *)str; 692 693 state.max_sz = 0; 694 state.sz = sz; 695 state.str = ustr; 696 state.s = ustr; 697 state.theend = ustr + sz - (sz > 0); 698 state.append_char = sn_append_char; 699 700 ret = xyzprintf (&state, format, args); 701 if (state.s != NULL && sz != 0) 702 *state.s = '\0'; 703 return ret; 704} 705#endif 706