Home | History | Annotate | Line # | Download | only in roken
snprintf.c revision 1.1.1.2
      1 /*	$NetBSD: snprintf.c,v 1.1.1.2 2014/04/24 12:45:52 pettai 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 
    123 
    124 static size_t
    125 pad(struct snprintf_state *state, int width, char c)
    126 {
    127     size_t len = 0;
    128     while(width-- > 0){
    129 	(*state->append_char)(state,  c);
    130 	++len;
    131     }
    132     return len;
    133 }
    134 
    135 /* return true if we should use alternatve hex form */
    136 static int
    137 use_alternative (int flags, u_longest num, unsigned base)
    138 {
    139     return (flags & alternate_flag) && base == 16 && num != 0;
    140 }
    141 
    142 static int
    143 append_number(struct snprintf_state *state,
    144 	      u_longest num, unsigned base, const char *rep,
    145 	      int width, int prec, int flags, int minusp)
    146 {
    147     int len = 0;
    148     u_longest n = num;
    149     char nstr[64]; /* enough for <192 bit octal integers */
    150     int nstart, nlen;
    151     char signchar;
    152 
    153     /* given precision, ignore zero flag */
    154     if(prec != -1)
    155 	flags &= ~zero_flag;
    156     else
    157 	prec = 1;
    158 
    159     /* format number as string */
    160     nstart = sizeof(nstr);
    161     nlen = 0;
    162     nstr[--nstart] = '\0';
    163     do {
    164 	assert(nstart > 0);
    165 	nstr[--nstart] = rep[n % base];
    166 	++nlen;
    167 	n /= base;
    168     } while(n);
    169 
    170     /* zero value with zero precision should produce no digits */
    171     if(prec == 0 && num == 0) {
    172 	nlen--;
    173 	nstart++;
    174     }
    175 
    176     /* figure out what char to use for sign */
    177     if(minusp)
    178 	signchar = '-';
    179     else if((flags & plus_flag))
    180 	signchar = '+';
    181     else if((flags & space_flag))
    182 	signchar = ' ';
    183     else
    184 	signchar = '\0';
    185 
    186     if((flags & alternate_flag) && base == 8) {
    187 	/* if necessary, increase the precision to
    188 	   make first digit a zero */
    189 
    190 	/* XXX C99 claims (regarding # and %o) that "if the value and
    191            precision are both 0, a single 0 is printed", but there is
    192            no such wording for %x. This would mean that %#.o would
    193            output "0", but %#.x "". This does not make sense, and is
    194            also not what other printf implementations are doing. */
    195 
    196 	if(prec <= nlen && nstr[nstart] != '0' && nstr[nstart] != '\0')
    197 	    prec = nlen + 1;
    198     }
    199 
    200     /* possible formats:
    201        pad | sign | alt | zero | digits
    202        sign | alt | zero | digits | pad   minus_flag
    203        sign | alt | zero | digits zero_flag */
    204 
    205     /* if not right justifying or padding with zeros, we need to
    206        compute the length of the rest of the string, and then pad with
    207        spaces */
    208     if(!(flags & (minus_flag | zero_flag))) {
    209 	if(prec > nlen)
    210 	    width -= prec;
    211 	else
    212 	    width -= nlen;
    213 
    214 	if(use_alternative(flags, num, base))
    215 	    width -= 2;
    216 
    217 	if(signchar != '\0')
    218 	    width--;
    219 
    220 	/* pad to width */
    221 	len += pad(state, width, ' ');
    222     }
    223     if(signchar != '\0') {
    224 	(*state->append_char)(state, signchar);
    225 	++len;
    226     }
    227     if(use_alternative(flags, num, base)) {
    228 	(*state->append_char)(state, '0');
    229 	(*state->append_char)(state, rep[10] + 23); /* XXX */
    230 	len += 2;
    231     }
    232     if(flags & zero_flag) {
    233 	/* pad to width with zeros */
    234 	if(prec - nlen > width - len - nlen)
    235 	    len += pad(state, prec - nlen, '0');
    236 	else
    237 	    len += pad(state, width - len - nlen, '0');
    238     } else
    239 	/* pad to prec with zeros */
    240 	len += pad(state, prec - nlen, '0');
    241 
    242     while(nstr[nstart] != '\0') {
    243 	(*state->append_char)(state, nstr[nstart++]);
    244 	++len;
    245     }
    246 
    247     if(flags & minus_flag)
    248 	len += pad(state, width - len, ' ');
    249 
    250     return len;
    251 }
    252 
    253 /*
    254  * return length
    255  */
    256 
    257 static size_t
    258 append_string (struct snprintf_state *state,
    259 	       const unsigned char *arg,
    260 	       int width,
    261 	       int prec,
    262 	       int flags)
    263 {
    264     size_t len = 0;
    265 
    266     if(arg == NULL)
    267 	arg = (const unsigned char*)"(null)";
    268 
    269     if(prec != -1)
    270 	width -= prec;
    271     else
    272 	width -= strlen((const char *)arg);
    273     if(!(flags & minus_flag))
    274 	len += pad(state, width, ' ');
    275 
    276     if (prec != -1) {
    277 	while (*arg && prec--) {
    278 	    (*state->append_char) (state, *arg++);
    279 	    ++len;
    280 	}
    281     } else {
    282 	while (*arg) {
    283 	    (*state->append_char) (state, *arg++);
    284 	    ++len;
    285 	}
    286     }
    287     if(flags & minus_flag)
    288 	len += pad(state, width, ' ');
    289     return len;
    290 }
    291 
    292 static int
    293 append_char(struct snprintf_state *state,
    294 	    unsigned char arg,
    295 	    int width,
    296 	    int flags)
    297 {
    298     int len = 0;
    299 
    300     while(!(flags & minus_flag) && --width > 0) {
    301 	(*state->append_char) (state, ' ')    ;
    302 	++len;
    303     }
    304     (*state->append_char) (state, arg);
    305     ++len;
    306     while((flags & minus_flag) && --width > 0) {
    307 	(*state->append_char) (state, ' ');
    308 	++len;
    309     }
    310     return 0;
    311 }
    312 
    313 /*
    314  * This can't be made into a function...
    315  */
    316 
    317 #ifdef HAVE_LONG_LONG
    318 
    319 #define PARSE_INT_FORMAT(res, arg, unsig) \
    320 if (long_long_flag) \
    321      res = (unsig long long)va_arg(arg, unsig long long); \
    322 else if (long_flag) \
    323      res = (unsig long)va_arg(arg, unsig long); \
    324 else if (size_t_flag) \
    325      res = (unsig long)va_arg(arg, size_t); \
    326 else if (short_flag) \
    327      res = (unsig short)va_arg(arg, unsig int); \
    328 else \
    329      res = (unsig int)va_arg(arg, unsig int)
    330 
    331 #else
    332 
    333 #define PARSE_INT_FORMAT(res, arg, unsig) \
    334 if (long_flag) \
    335      res = (unsig long)va_arg(arg, unsig long); \
    336 else if (size_t_flag) \
    337      res = (unsig long)va_arg(arg, size_t); \
    338 else if (short_flag) \
    339      res = (unsig short)va_arg(arg, unsig int); \
    340 else \
    341      res = (unsig int)va_arg(arg, unsig int)
    342 
    343 #endif
    344 
    345 /*
    346  * zyxprintf - return length, as snprintf
    347  */
    348 
    349 static size_t
    350 xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap)
    351 {
    352     const unsigned char *format = (const unsigned char *)char_format;
    353     unsigned char c;
    354     size_t len = 0;
    355 
    356     while((c = *format++)) {
    357 	if (c == '%') {
    358 	    int flags          = 0;
    359 	    int width          = 0;
    360 	    int prec           = -1;
    361 	    int size_t_flag    = 0;
    362 	    int long_long_flag = 0;
    363 	    int long_flag      = 0;
    364 	    int short_flag     = 0;
    365 
    366 	    /* flags */
    367 	    while((c = *format++)){
    368 		if(c == '-')
    369 		    flags |= minus_flag;
    370 		else if(c == '+')
    371 		    flags |= plus_flag;
    372 		else if(c == ' ')
    373 		    flags |= space_flag;
    374 		else if(c == '#')
    375 		    flags |= alternate_flag;
    376 		else if(c == '0')
    377 		    flags |= zero_flag;
    378 		else if(c == '\'')
    379 		    ; /* just ignore */
    380 		else
    381 		    break;
    382 	    }
    383 
    384 	    if((flags & space_flag) && (flags & plus_flag))
    385 		flags ^= space_flag;
    386 
    387 	    if((flags & minus_flag) && (flags & zero_flag))
    388 		flags ^= zero_flag;
    389 
    390 	    /* width */
    391 	    if (isdigit(c))
    392 		do {
    393 		    width = width * 10 + c - '0';
    394 		    c = *format++;
    395 		} while(isdigit(c));
    396 	    else if(c == '*') {
    397 		width = va_arg(ap, int);
    398 		c = *format++;
    399 	    }
    400 
    401 	    /* precision */
    402 	    if (c == '.') {
    403 		prec = 0;
    404 		c = *format++;
    405 		if (isdigit(c))
    406 		    do {
    407 			prec = prec * 10 + c - '0';
    408 			c = *format++;
    409 		    } while(isdigit(c));
    410 		else if (c == '*') {
    411 		    prec = va_arg(ap, int);
    412 		    c = *format++;
    413 		}
    414 	    }
    415 
    416 	    /* size */
    417 
    418 	    if (c == 'h') {
    419 		short_flag = 1;
    420 		c = *format++;
    421 	    } else if (c == 'z') {
    422 		size_t_flag = 1;
    423 		c = *format++;
    424 	    } else if (c == 'l') {
    425 		long_flag = 1;
    426 		c = *format++;
    427 		if (c == 'l') {
    428 		    long_long_flag = 1;
    429 		    c = *format++;
    430 		}
    431 	    }
    432 
    433 	    if(c != 'd' && c != 'i')
    434 		flags &= ~(plus_flag | space_flag);
    435 
    436 	    switch (c) {
    437 	    case 'c' :
    438 		append_char(state, va_arg(ap, int), width, flags);
    439 		++len;
    440 		break;
    441 	    case 's' :
    442 		len += append_string(state,
    443 				     va_arg(ap, unsigned char*),
    444 				     width,
    445 				     prec,
    446 				     flags);
    447 		break;
    448 	    case 'd' :
    449 	    case 'i' : {
    450 		longest arg;
    451 		u_longest num;
    452 		int minusp = 0;
    453 
    454 		PARSE_INT_FORMAT(arg, ap, signed);
    455 
    456 		if (arg < 0) {
    457 		    minusp = 1;
    458 		    num = -arg;
    459 		} else
    460 		    num = arg;
    461 
    462 		len += append_number (state, num, 10, "0123456789",
    463 				      width, prec, flags, minusp);
    464 		break;
    465 	    }
    466 	    case 'u' : {
    467 		u_longest arg;
    468 
    469 		PARSE_INT_FORMAT(arg, ap, unsigned);
    470 
    471 		len += append_number (state, arg, 10, "0123456789",
    472 				      width, prec, flags, 0);
    473 		break;
    474 	    }
    475 	    case 'o' : {
    476 		u_longest arg;
    477 
    478 		PARSE_INT_FORMAT(arg, ap, unsigned);
    479 
    480 		len += append_number (state, arg, 010, "01234567",
    481 				      width, prec, flags, 0);
    482 		break;
    483 	    }
    484 	    case 'x' : {
    485 		u_longest arg;
    486 
    487 		PARSE_INT_FORMAT(arg, ap, unsigned);
    488 
    489 		len += append_number (state, arg, 0x10, "0123456789abcdef",
    490 				      width, prec, flags, 0);
    491 		break;
    492 	    }
    493 	    case 'X' :{
    494 		u_longest arg;
    495 
    496 		PARSE_INT_FORMAT(arg, ap, unsigned);
    497 
    498 		len += append_number (state, arg, 0x10, "0123456789ABCDEF",
    499 				      width, prec, flags, 0);
    500 		break;
    501 	    }
    502 	    case 'p' : {
    503 		u_longest arg = (u_longest)va_arg(ap, void*);
    504 
    505 		len += append_number (state, arg, 0x10, "0123456789ABCDEF",
    506 				      width, prec, flags, 0);
    507 		break;
    508 	    }
    509 	    case 'n' : {
    510 		int *arg = va_arg(ap, int*);
    511 		*arg = state->s - state->str;
    512 		break;
    513 	    }
    514 	    case '\0' :
    515 		--format;
    516 		/* FALLTHROUGH */
    517 	    case '%' :
    518 		(*state->append_char)(state, c);
    519 		++len;
    520 		break;
    521 	    default :
    522 		(*state->append_char)(state, '%');
    523 		(*state->append_char)(state, c);
    524 		len += 2;
    525 		break;
    526 	    }
    527 	} else {
    528 	    (*state->append_char) (state, c);
    529 	    ++len;
    530 	}
    531     }
    532     return len;
    533 }
    534 
    535 #if !defined(HAVE_SNPRINTF) || defined(TEST_SNPRINTF)
    536 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    537 rk_snprintf (char *str, size_t sz, const char *format, ...)
    538 {
    539     va_list args;
    540     int ret;
    541 
    542     va_start(args, format);
    543     ret = vsnprintf (str, sz, format, args);
    544     va_end(args);
    545 
    546 #ifdef PARANOIA
    547     {
    548 	int ret2;
    549 	char *tmp;
    550 
    551 	tmp = malloc (sz);
    552 	if (tmp == NULL)
    553 	    abort ();
    554 
    555 	va_start(args, format);
    556 	ret2 = vsprintf (tmp, format, args);
    557 	va_end(args);
    558 	if (ret != ret2 || strcmp(str, tmp))
    559 	    abort ();
    560 	free (tmp);
    561     }
    562 #endif
    563 
    564     return ret;
    565 }
    566 #endif
    567 
    568 #if !defined(HAVE_ASPRINTF) || defined(TEST_SNPRINTF)
    569 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    570 rk_asprintf (char **ret, const char *format, ...)
    571 {
    572     va_list args;
    573     int val;
    574 
    575     va_start(args, format);
    576     val = vasprintf (ret, format, args);
    577     va_end(args);
    578 
    579 #ifdef PARANOIA
    580     {
    581 	int ret2;
    582 	char *tmp;
    583 	tmp = malloc (val + 1);
    584 	if (tmp == NULL)
    585 	    abort ();
    586 
    587 	va_start(args, format);
    588 	ret2 = vsprintf (tmp, format, args);
    589 	va_end(args);
    590 	if (val != ret2 || strcmp(*ret, tmp))
    591 	    abort ();
    592 	free (tmp);
    593     }
    594 #endif
    595 
    596     return val;
    597 }
    598 #endif
    599 
    600 #if !defined(HAVE_ASNPRINTF) || defined(TEST_SNPRINTF)
    601 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    602 rk_asnprintf (char **ret, size_t max_sz, const char *format, ...)
    603 {
    604     va_list args;
    605     int val;
    606 
    607     va_start(args, format);
    608     val = vasnprintf (ret, max_sz, format, args);
    609 
    610 #ifdef PARANOIA
    611     {
    612 	int ret2;
    613 	char *tmp;
    614 	tmp = malloc (val + 1);
    615 	if (tmp == NULL)
    616 	    abort ();
    617 
    618 	ret2 = vsprintf (tmp, format, args);
    619 	if (val != ret2 || strcmp(*ret, tmp))
    620 	    abort ();
    621 	free (tmp);
    622     }
    623 #endif
    624 
    625     va_end(args);
    626     return val;
    627 }
    628 #endif
    629 
    630 #if !defined(HAVE_VASPRINTF) || defined(TEST_SNPRINTF)
    631 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    632 rk_vasprintf (char **ret, const char *format, va_list args)
    633 {
    634     return vasnprintf (ret, 0, format, args);
    635 }
    636 #endif
    637 
    638 
    639 #if !defined(HAVE_VASNPRINTF) || defined(TEST_SNPRINTF)
    640 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    641 rk_vasnprintf (char **ret, size_t max_sz, const char *format, va_list args)
    642 {
    643     size_t st;
    644     struct snprintf_state state;
    645 
    646     state.max_sz = max_sz;
    647     state.sz     = 1;
    648     state.str    = malloc(state.sz);
    649     if (state.str == NULL) {
    650 	*ret = NULL;
    651 	return -1;
    652     }
    653     state.s = state.str;
    654     state.theend = state.s + state.sz - 1;
    655     state.append_char = as_append_char;
    656 
    657     st = xyzprintf (&state, format, args);
    658     if (st > state.sz) {
    659 	free (state.str);
    660 	*ret = NULL;
    661 	return -1;
    662     } else {
    663 	char *tmp;
    664 
    665 	*state.s = '\0';
    666 	tmp = realloc (state.str, st+1);
    667 	if (tmp == NULL) {
    668 	    free (state.str);
    669 	    *ret = NULL;
    670 	    return -1;
    671 	}
    672 	*ret = tmp;
    673 	return st;
    674     }
    675 }
    676 #endif
    677 
    678 #if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF)
    679 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
    680 rk_vsnprintf (char *str, size_t sz, const char *format, va_list args)
    681 {
    682     struct snprintf_state state;
    683     int ret;
    684     unsigned char *ustr = (unsigned char *)str;
    685 
    686     state.max_sz = 0;
    687     state.sz     = sz;
    688     state.str    = ustr;
    689     state.s      = ustr;
    690     state.theend = ustr + sz - (sz > 0);
    691     state.append_char = sn_append_char;
    692 
    693     ret = xyzprintf (&state, format, args);
    694     if (state.s != NULL && sz != 0)
    695 	*state.s = '\0';
    696     return ret;
    697 }
    698 #endif
    699