Home | History | Annotate | Line # | Download | only in roken
      1 /*	$NetBSD: snprintf.c,v 1.3 2023/06/19 21:41:45 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995-2003 Kungliga Tekniska Hgskolan
      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 
     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 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)
     68 static int
     69 sn_reserve (struct snprintf_state *state, size_t n)
     70 {
     71     return state->s + n > state->theend;
     72 }
     73 
     74 static void
     75 sn_append_char (struct snprintf_state *state, unsigned char c)
     76 {
     77     if (!sn_reserve (state, 1))
     78 	*state->s++ = c;
     79 }
     80 #endif
     81 
     82 static int
     83 as_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 
    105 static void
    106 as_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
    115 typedef unsigned long long u_longest;
    116 typedef long long longest;
    117 #else
    118 typedef unsigned long u_longest;
    119 typedef long longest;
    120 #endif
    121 
    122 #ifndef HAVE_UINTPTR_T
    123 typedef u_longest uintptr_t;
    124 #endif
    125 
    126 
    127 
    128 static size_t
    129 pad(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 */
    140 static int
    141 use_alternative (int flags, u_longest num, unsigned base)
    142 {
    143     return (flags & alternate_flag) && base == 16 && num != 0;
    144 }
    145 
    146 static int
    147 append_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 
    261 static size_t
    262 append_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 
    296 static int
    297 append_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) \
    324 if (long_long_flag) \
    325      res = (unsig long long)va_arg(arg, unsig long long); \
    326 else if (long_flag) \
    327      res = (unsig long)va_arg(arg, unsig long); \
    328 else if (size_t_flag) \
    329      res = (unsig long)va_arg(arg, size_t); \
    330 else if (short_flag) \
    331      res = (unsig short)va_arg(arg, unsig int); \
    332 else \
    333      res = (unsig int)va_arg(arg, unsig int)
    334 
    335 #else
    336 
    337 #define PARSE_INT_FORMAT(res, arg, unsig) \
    338 if (long_flag) \
    339      res = (unsig long)va_arg(arg, unsig long); \
    340 else if (size_t_flag) \
    341      res = (unsig long)va_arg(arg, size_t); \
    342 else if (short_flag) \
    343      res = (unsig short)va_arg(arg, unsig int); \
    344 else \
    345      res = (unsig int)va_arg(arg, unsig int)
    346 
    347 #endif
    348 
    349 /*
    350  * zyxprintf - return length, as snprintf
    351  */
    352 
    353 static size_t
    354 xyzprintf (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)
    543 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    544 rk_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)
    576 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    577 rk_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)
    608 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    609 rk_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)
    638 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    639 rk_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)
    647 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    648 rk_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)
    686 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    687 rk_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