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