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