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