Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright Patrick Powell 1995
      3  * This code is based on code written by Patrick Powell (papowell (at) astart.com)
      4  * It may be used for any purpose as long as this notice remains intact
      5  * on all source code distributions
      6  */
      7 
      8 /**************************************************************
      9  * Original:
     10  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
     11  * A bombproof version of doprnt (dopr) included.
     12  * Sigh.  This sort of thing is always nasty do deal with.  Note that
     13  * the version here does not include floating point...
     14  *
     15  * snprintf() is used instead of sprintf() as it does limit checks
     16  * for string length.  This covers a nasty loophole.
     17  *
     18  * The other functions are there to prevent NULL pointers from
     19  * causing nast effects.
     20  *
     21  * More Recently:
     22  *  Brandon Long <blong (at) fiction.net> 9/15/96 for mutt 0.43
     23  *  This was ugly.  It is still ugly.  I opted out of floating point
     24  *  numbers, but the formatter understands just about everything
     25  *  from the normal C string format, at least as far as I can tell from
     26  *  the Solaris 2.5 printf(3S) man page.
     27  *
     28  *  Brandon Long <blong (at) fiction.net> 10/22/97 for mutt 0.87.1
     29  *    Ok, added some minimal floating point support, which means this
     30  *    probably requires libm on most operating systems.  Don't yet
     31  *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
     32  *    was pretty badly broken, it just wasn't being exercised in ways
     33  *    which showed it, so that's been fixed.  Also, formated the code
     34  *    to mutt conventions, and removed dead code left over from the
     35  *    original.  Also, there is now a builtin-test, just compile with:
     36  *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
     37  *    and run snprintf for results.
     38  *
     39  *  Thomas Roessler <roessler (at) guug.de> 01/27/98 for mutt 0.89i
     40  *    The PGP code was using unsigned hexadecimal formats.
     41  *    Unfortunately, unsigned formats simply didn't work.
     42  *
     43  *  Michael Elkins <me (at) cs.hmc.edu> 03/05/98 for mutt 0.90.8
     44  *    The original code assumed that both snprintf() and vsnprintf() were
     45  *    missing.  Some systems only have snprintf() but not vsnprintf(), so
     46  *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
     47  *
     48  *  Andrew Tridgell (tridge (at) samba.org) Oct 1998
     49  *    fixed handling of %.0f
     50  *    added test for HAVE_LONG_DOUBLE
     51  *
     52  *  Russ Allbery <rra (at) stanford.edu> 2000-08-26
     53  *    fixed return value to comply with C99
     54  *    fixed handling of snprintf(NULL, ...)
     55  *
     56  **************************************************************/
     57 
     58 #include "cs_config.h"
     59 
     60 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
     61 
     62 #include <string.h>
     63 #include <ctype.h>
     64 #include <sys/types.h>
     65 
     66 /* Define this as a fall through, HAVE_STDARG_H is probably already set */
     67 
     68 #define HAVE_VARARGS_H
     69 
     70 
     71 /* varargs declarations: */
     72 
     73 #if defined(HAVE_STDARG_H)
     74 # include <stdarg.h>
     75 # define HAVE_STDARGS    /* let's hope that works everywhere (mj) */
     76 # define VA_LOCAL_DECL   va_list ap
     77 # define VA_START(f)     va_start(ap, f)
     78 # define VA_SHIFT(v,t)  ;   /* no-op for ANSI */
     79 # define VA_END          va_end(ap)
     80 #else
     81 # if defined(HAVE_VARARGS_H)
     82 #  include <varargs.h>
     83 #  undef HAVE_STDARGS
     84 #  define VA_LOCAL_DECL   va_list ap
     85 #  define VA_START(f)     va_start(ap)      /* f is ignored! */
     86 #  define VA_SHIFT(v,t) v = va_arg(ap,t)
     87 #  define VA_END        va_end(ap)
     88 # else
     89 /*XX ** NO VARARGS ** XX*/
     90 # endif
     91 #endif
     92 
     93 #ifdef HAVE_LONG_DOUBLE
     94 #define LDOUBLE long double
     95 #else
     96 #define LDOUBLE double
     97 #endif
     98 
     99 int snprintf (char *str, size_t count, const char *fmt, ...);
    100 int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
    101 
    102 static int dopr (char *buffer, size_t maxlen, const char *format,
    103                  va_list args);
    104 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
    105 		   char *value, int flags, int min, int max);
    106 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
    107 		   long value, int base, int min, int max, int flags);
    108 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
    109 		  LDOUBLE fvalue, int min, int max, int flags);
    110 static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
    111 
    112 /*
    113  * dopr(): poor man's version of doprintf
    114  */
    115 
    116 /* format read states */
    117 #define DP_S_DEFAULT 0
    118 #define DP_S_FLAGS   1
    119 #define DP_S_MIN     2
    120 #define DP_S_DOT     3
    121 #define DP_S_MAX     4
    122 #define DP_S_MOD     5
    123 #define DP_S_CONV    6
    124 #define DP_S_DONE    7
    125 
    126 /* format flags - Bits */
    127 #define DP_F_MINUS 	(1 << 0)
    128 #define DP_F_PLUS  	(1 << 1)
    129 #define DP_F_SPACE 	(1 << 2)
    130 #define DP_F_NUM   	(1 << 3)
    131 #define DP_F_ZERO  	(1 << 4)
    132 #define DP_F_UP    	(1 << 5)
    133 #define DP_F_UNSIGNED 	(1 << 6)
    134 
    135 /* Conversion Flags */
    136 #define DP_C_SHORT   1
    137 #define DP_C_LONG    2
    138 #define DP_C_LDOUBLE 3
    139 
    140 #define char_to_int(p) (p - '0')
    141 #define MAX(p,q) ((p >= q) ? p : q)
    142 #define MIN(p,q) ((p <= q) ? p : q)
    143 
    144 static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
    145 {
    146   char ch;
    147   long value;
    148   LDOUBLE fvalue;
    149   char *strvalue;
    150   int min;
    151   int max;
    152   int state;
    153   int flags;
    154   int cflags;
    155   int total;
    156   size_t currlen;
    157 
    158   state = DP_S_DEFAULT;
    159   currlen = flags = cflags = min = 0;
    160   max = -1;
    161   ch = *format++;
    162   total = 0;
    163 
    164   while (state != DP_S_DONE)
    165   {
    166     if (ch == '\0')
    167       state = DP_S_DONE;
    168 
    169     switch(state)
    170     {
    171     case DP_S_DEFAULT:
    172       if (ch == '%')
    173 	state = DP_S_FLAGS;
    174       else
    175 	total += dopr_outch (buffer, &currlen, maxlen, ch);
    176       ch = *format++;
    177       break;
    178     case DP_S_FLAGS:
    179       switch (ch)
    180       {
    181       case '-':
    182 	flags |= DP_F_MINUS;
    183         ch = *format++;
    184 	break;
    185       case '+':
    186 	flags |= DP_F_PLUS;
    187         ch = *format++;
    188 	break;
    189       case ' ':
    190 	flags |= DP_F_SPACE;
    191         ch = *format++;
    192 	break;
    193       case '#':
    194 	flags |= DP_F_NUM;
    195         ch = *format++;
    196 	break;
    197       case '0':
    198 	flags |= DP_F_ZERO;
    199         ch = *format++;
    200 	break;
    201       default:
    202 	state = DP_S_MIN;
    203 	break;
    204       }
    205       break;
    206     case DP_S_MIN:
    207       if (isdigit(ch))
    208       {
    209 	min = 10*min + char_to_int (ch);
    210 	ch = *format++;
    211       }
    212       else if (ch == '*')
    213       {
    214 	min = va_arg (args, int);
    215 	ch = *format++;
    216 	state = DP_S_DOT;
    217       }
    218       else
    219 	state = DP_S_DOT;
    220       break;
    221     case DP_S_DOT:
    222       if (ch == '.')
    223       {
    224 	state = DP_S_MAX;
    225 	ch = *format++;
    226       }
    227       else
    228 	state = DP_S_MOD;
    229       break;
    230     case DP_S_MAX:
    231       if (isdigit(ch))
    232       {
    233 	if (max < 0)
    234 	  max = 0;
    235 	max = 10*max + char_to_int (ch);
    236 	ch = *format++;
    237       }
    238       else if (ch == '*')
    239       {
    240 	max = va_arg (args, int);
    241 	ch = *format++;
    242 	state = DP_S_MOD;
    243       }
    244       else
    245 	state = DP_S_MOD;
    246       break;
    247     case DP_S_MOD:
    248       /* Currently, we don't support Long Long, bummer */
    249       switch (ch)
    250       {
    251       case 'h':
    252 	cflags = DP_C_SHORT;
    253 	ch = *format++;
    254 	break;
    255       case 'l':
    256 	cflags = DP_C_LONG;
    257 	ch = *format++;
    258 	break;
    259       case 'L':
    260 	cflags = DP_C_LDOUBLE;
    261 	ch = *format++;
    262 	break;
    263       default:
    264 	break;
    265       }
    266       state = DP_S_CONV;
    267       break;
    268     case DP_S_CONV:
    269       switch (ch)
    270       {
    271       case 'd':
    272       case 'i':
    273 	if (cflags == DP_C_SHORT)
    274 	  value = va_arg (args, short int);
    275 	else if (cflags == DP_C_LONG)
    276 	  value = va_arg (args, long int);
    277 	else
    278 	  value = va_arg (args, int);
    279 	total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
    280 	break;
    281       case 'o':
    282 	flags |= DP_F_UNSIGNED;
    283 	if (cflags == DP_C_SHORT)
    284 	  value = va_arg (args, unsigned short int);
    285 	else if (cflags == DP_C_LONG)
    286 	  value = va_arg (args, unsigned long int);
    287 	else
    288 	  value = va_arg (args, unsigned int);
    289 	total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
    290 	break;
    291       case 'u':
    292 	flags |= DP_F_UNSIGNED;
    293 	if (cflags == DP_C_SHORT)
    294 	  value = va_arg (args, unsigned short int);
    295 	else if (cflags == DP_C_LONG)
    296 	  value = va_arg (args, unsigned long int);
    297 	else
    298 	  value = va_arg (args, unsigned int);
    299 	total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
    300 	break;
    301       case 'X':
    302 	flags |= DP_F_UP;
    303       case 'x':
    304 	flags |= DP_F_UNSIGNED;
    305 	if (cflags == DP_C_SHORT)
    306 	  value = va_arg (args, unsigned short int);
    307 	else if (cflags == DP_C_LONG)
    308 	  value = va_arg (args, unsigned long int);
    309 	else
    310 	  value = va_arg (args, unsigned int);
    311 	total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
    312 	break;
    313       case 'f':
    314 	if (cflags == DP_C_LDOUBLE)
    315 	  fvalue = va_arg (args, LDOUBLE);
    316 	else
    317 	  fvalue = va_arg (args, double);
    318 	/* um, floating point? */
    319 	total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
    320 	break;
    321       case 'E':
    322 	flags |= DP_F_UP;
    323       case 'e':
    324 	if (cflags == DP_C_LDOUBLE)
    325 	  fvalue = va_arg (args, LDOUBLE);
    326 	else
    327 	  fvalue = va_arg (args, double);
    328 	break;
    329       case 'G':
    330 	flags |= DP_F_UP;
    331       case 'g':
    332 	if (cflags == DP_C_LDOUBLE)
    333 	  fvalue = va_arg (args, LDOUBLE);
    334 	else
    335 	  fvalue = va_arg (args, double);
    336 	break;
    337       case 'c':
    338 	total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
    339 	break;
    340       case 's':
    341 	strvalue = va_arg (args, char *);
    342 	total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
    343 	break;
    344       case 'p':
    345 	strvalue = va_arg (args, void *);
    346 	total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
    347                          max, flags);
    348 	break;
    349       case 'n':
    350 	if (cflags == DP_C_SHORT)
    351 	{
    352 	  short int *num;
    353 	  num = va_arg (args, short int *);
    354 	  *num = currlen;
    355         }
    356 	else if (cflags == DP_C_LONG)
    357 	{
    358 	  long int *num;
    359 	  num = va_arg (args, long int *);
    360 	  *num = currlen;
    361         }
    362 	else
    363 	{
    364 	  int *num;
    365 	  num = va_arg (args, int *);
    366 	  *num = currlen;
    367         }
    368 	break;
    369       case '%':
    370 	total += dopr_outch (buffer, &currlen, maxlen, ch);
    371 	break;
    372       case 'w':
    373 	/* not supported yet, treat as next char */
    374 	ch = *format++;
    375 	break;
    376       default:
    377 	/* Unknown, skip */
    378 	break;
    379       }
    380       ch = *format++;
    381       state = DP_S_DEFAULT;
    382       flags = cflags = min = 0;
    383       max = -1;
    384       break;
    385     case DP_S_DONE:
    386       break;
    387     default:
    388       /* hmm? */
    389       break; /* some picky compilers need this */
    390     }
    391   }
    392   if (buffer != NULL)
    393   {
    394     if (currlen < maxlen - 1)
    395       buffer[currlen] = '\0';
    396     else
    397       buffer[maxlen - 1] = '\0';
    398   }
    399   return total;
    400 }
    401 
    402 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
    403                    char *value, int flags, int min, int max)
    404 {
    405   int padlen, strln;     /* amount to pad */
    406   int cnt = 0;
    407   int total = 0;
    408 
    409   if (value == 0)
    410   {
    411     value = "<NULL>";
    412   }
    413 
    414   for (strln = 0; value[strln]; ++strln); /* strlen */
    415   if (max >= 0 && max < strln)
    416     strln = max;
    417   padlen = min - strln;
    418   if (padlen < 0)
    419     padlen = 0;
    420   if (flags & DP_F_MINUS)
    421     padlen = -padlen; /* Left Justify */
    422 
    423   while (padlen > 0)
    424   {
    425     total += dopr_outch (buffer, currlen, maxlen, ' ');
    426     --padlen;
    427   }
    428   while (*value && ((max < 0) || (cnt < max)))
    429   {
    430     total += dopr_outch (buffer, currlen, maxlen, *value++);
    431     ++cnt;
    432   }
    433   while (padlen < 0)
    434   {
    435     total += dopr_outch (buffer, currlen, maxlen, ' ');
    436     ++padlen;
    437   }
    438   return total;
    439 }
    440 
    441 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
    442 
    443 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
    444 		   long value, int base, int min, int max, int flags)
    445 {
    446   int signvalue = 0;
    447   unsigned long uvalue;
    448   char convert[20];
    449   int place = 0;
    450   int spadlen = 0; /* amount to space pad */
    451   int zpadlen = 0; /* amount to zero pad */
    452   int caps = 0;
    453   int total = 0;
    454 
    455   if (max < 0)
    456     max = 0;
    457 
    458   uvalue = value;
    459 
    460   if(!(flags & DP_F_UNSIGNED))
    461   {
    462     if( value < 0 ) {
    463       signvalue = '-';
    464       uvalue = -value;
    465     }
    466     else
    467       if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
    468 	signvalue = '+';
    469     else
    470       if (flags & DP_F_SPACE)
    471 	signvalue = ' ';
    472   }
    473 
    474   if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
    475 
    476   do {
    477     convert[place++] =
    478       (caps? "0123456789ABCDEF":"0123456789abcdef")
    479       [uvalue % (unsigned)base  ];
    480     uvalue = (uvalue / (unsigned)base );
    481   } while(uvalue && (place < 20));
    482   if (place == 20) place--;
    483   convert[place] = 0;
    484 
    485   zpadlen = max - place;
    486   spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
    487   if (zpadlen < 0) zpadlen = 0;
    488   if (spadlen < 0) spadlen = 0;
    489   if (flags & DP_F_ZERO)
    490   {
    491     zpadlen = MAX(zpadlen, spadlen);
    492     spadlen = 0;
    493   }
    494   if (flags & DP_F_MINUS)
    495     spadlen = -spadlen; /* Left Justifty */
    496 
    497 #ifdef DEBUG_SNPRINTF
    498   dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
    499       zpadlen, spadlen, min, max, place));
    500 #endif
    501 
    502   /* Spaces */
    503   while (spadlen > 0)
    504   {
    505     total += dopr_outch (buffer, currlen, maxlen, ' ');
    506     --spadlen;
    507   }
    508 
    509   /* Sign */
    510   if (signvalue)
    511     total += dopr_outch (buffer, currlen, maxlen, signvalue);
    512 
    513   /* Zeros */
    514   if (zpadlen > 0)
    515   {
    516     while (zpadlen > 0)
    517     {
    518       total += dopr_outch (buffer, currlen, maxlen, '0');
    519       --zpadlen;
    520     }
    521   }
    522 
    523   /* Digits */
    524   while (place > 0)
    525     total += dopr_outch (buffer, currlen, maxlen, convert[--place]);
    526 
    527   /* Left Justified spaces */
    528   while (spadlen < 0) {
    529     total += dopr_outch (buffer, currlen, maxlen, ' ');
    530     ++spadlen;
    531   }
    532 
    533   return total;
    534 }
    535 
    536 static LDOUBLE abs_val (LDOUBLE value)
    537 {
    538   LDOUBLE result = value;
    539 
    540   if (value < 0)
    541     result = -value;
    542 
    543   return result;
    544 }
    545 
    546 static LDOUBLE pow10 (int exp)
    547 {
    548   LDOUBLE result = 1;
    549 
    550   while (exp)
    551   {
    552     result *= 10;
    553     exp--;
    554   }
    555 
    556   return result;
    557 }
    558 
    559 static long round (LDOUBLE value)
    560 {
    561   long intpart;
    562 
    563   intpart = value;
    564   value = value - intpart;
    565   if (value >= 0.5)
    566     intpart++;
    567 
    568   return intpart;
    569 }
    570 
    571 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
    572 		  LDOUBLE fvalue, int min, int max, int flags)
    573 {
    574   int signvalue = 0;
    575   LDOUBLE ufvalue;
    576   char iconvert[20];
    577   char fconvert[20];
    578   int iplace = 0;
    579   int fplace = 0;
    580   int padlen = 0; /* amount to pad */
    581   int zpadlen = 0;
    582   int caps = 0;
    583   int total = 0;
    584   long intpart;
    585   long fracpart;
    586 
    587   /*
    588    * AIX manpage says the default is 0, but Solaris says the default
    589    * is 6, and sprintf on AIX defaults to 6
    590    */
    591   if (max < 0)
    592     max = 6;
    593 
    594   ufvalue = abs_val (fvalue);
    595 
    596   if (fvalue < 0)
    597     signvalue = '-';
    598   else
    599     if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
    600       signvalue = '+';
    601     else
    602       if (flags & DP_F_SPACE)
    603 	signvalue = ' ';
    604 
    605 #if 0
    606   if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
    607 #endif
    608 
    609   intpart = ufvalue;
    610 
    611   /*
    612    * Sorry, we only support 9 digits past the decimal because of our
    613    * conversion method
    614    */
    615   if (max > 9)
    616     max = 9;
    617 
    618   /* We "cheat" by converting the fractional part to integer by
    619    * multiplying by a factor of 10
    620    */
    621   fracpart = round ((pow10 (max)) * (ufvalue - intpart));
    622 
    623   if (fracpart >= pow10 (max))
    624   {
    625     intpart++;
    626     fracpart -= pow10 (max);
    627   }
    628 
    629 #ifdef DEBUG_SNPRINTF
    630   dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
    631 #endif
    632 
    633   /* Convert integer part */
    634   do {
    635     iconvert[iplace++] =
    636       (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
    637     intpart = (intpart / 10);
    638   } while(intpart && (iplace < 20));
    639   if (iplace == 20) iplace--;
    640   iconvert[iplace] = 0;
    641 
    642   /* Convert fractional part */
    643   do {
    644     fconvert[fplace++] =
    645       (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
    646     fracpart = (fracpart / 10);
    647   } while(fracpart && (fplace < 20));
    648   if (fplace == 20) fplace--;
    649   fconvert[fplace] = 0;
    650 
    651   /* -1 for decimal point, another -1 if we are printing a sign */
    652   padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
    653   zpadlen = max - fplace;
    654   if (zpadlen < 0)
    655     zpadlen = 0;
    656   if (padlen < 0)
    657     padlen = 0;
    658   if (flags & DP_F_MINUS)
    659     padlen = -padlen; /* Left Justifty */
    660 
    661   if ((flags & DP_F_ZERO) && (padlen > 0))
    662   {
    663     if (signvalue)
    664     {
    665       total += dopr_outch (buffer, currlen, maxlen, signvalue);
    666       --padlen;
    667       signvalue = 0;
    668     }
    669     while (padlen > 0)
    670     {
    671       total += dopr_outch (buffer, currlen, maxlen, '0');
    672       --padlen;
    673     }
    674   }
    675   while (padlen > 0)
    676   {
    677     total += dopr_outch (buffer, currlen, maxlen, ' ');
    678     --padlen;
    679   }
    680   if (signvalue)
    681     total += dopr_outch (buffer, currlen, maxlen, signvalue);
    682 
    683   while (iplace > 0)
    684     total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
    685 
    686   /*
    687    * Decimal point.  This should probably use locale to find the correct
    688    * char to print out.
    689    */
    690   if (max > 0)
    691   {
    692     total += dopr_outch (buffer, currlen, maxlen, '.');
    693 
    694     while (fplace > 0)
    695       total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
    696   }
    697 
    698   while (zpadlen > 0)
    699   {
    700     total += dopr_outch (buffer, currlen, maxlen, '0');
    701     --zpadlen;
    702   }
    703 
    704   while (padlen < 0)
    705   {
    706     total += dopr_outch (buffer, currlen, maxlen, ' ');
    707     ++padlen;
    708   }
    709 
    710   return total;
    711 }
    712 
    713 static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
    714 {
    715   if (*currlen + 1 < maxlen)
    716     buffer[(*currlen)++] = c;
    717   return 1;
    718 }
    719 
    720 #ifndef HAVE_VSNPRINTF
    721 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
    722 {
    723   if (str != NULL)
    724     str[0] = 0;
    725   return dopr(str, count, fmt, args);
    726 }
    727 #endif /* !HAVE_VSNPRINTF */
    728 
    729 #ifndef HAVE_SNPRINTF
    730 /* VARARGS3 */
    731 #ifdef HAVE_STDARGS
    732 int snprintf (char *str,size_t count,const char *fmt,...)
    733 #else
    734 int snprintf (va_alist) va_dcl
    735 #endif
    736 {
    737 #ifndef HAVE_STDARGS
    738   char *str;
    739   size_t count;
    740   char *fmt;
    741 #endif
    742   VA_LOCAL_DECL;
    743   int total;
    744 
    745   VA_START (fmt);
    746   VA_SHIFT (str, char *);
    747   VA_SHIFT (count, size_t );
    748   VA_SHIFT (fmt, char *);
    749   total = vsnprintf(str, count, fmt, ap);
    750   VA_END;
    751   return total;
    752 }
    753 #endif /* !HAVE_SNPRINTF */
    754 
    755 #ifdef TEST_SNPRINTF
    756 #ifndef LONG_STRING
    757 #define LONG_STRING 1024
    758 #endif
    759 int main (void)
    760 {
    761   char buf1[LONG_STRING];
    762   char buf2[LONG_STRING];
    763   char *fp_fmt[] = {
    764     "%-1.5f",
    765     "%1.5f",
    766     "%123.9f",
    767     "%10.5f",
    768     "% 10.5f",
    769     "%+22.9f",
    770     "%+4.9f",
    771     "%01.3f",
    772     "%4f",
    773     "%3.1f",
    774     "%3.2f",
    775     "%.0f",
    776     "%.1f",
    777     NULL
    778   };
    779   double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
    780     0.9996, 1.996, 4.136, 0};
    781   char *int_fmt[] = {
    782     "%-1.5d",
    783     "%1.5d",
    784     "%123.9d",
    785     "%5.5d",
    786     "%10.5d",
    787     "% 10.5d",
    788     "%+22.33d",
    789     "%01.3d",
    790     "%4d",
    791     NULL
    792   };
    793   long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
    794   int x, y;
    795   int fail = 0;
    796   int num = 0;
    797 
    798   printf ("Testing snprintf format codes against system sprintf...\n");
    799 
    800   for (x = 0; fp_fmt[x] != NULL ; x++)
    801     for (y = 0; fp_nums[y] != 0 ; y++)
    802     {
    803       snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
    804       sprintf (buf2, fp_fmt[x], fp_nums[y]);
    805       if (strcmp (buf1, buf2))
    806       {
    807 	printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
    808 	    fp_fmt[x], buf1, buf2);
    809 	fail++;
    810       }
    811       num++;
    812     }
    813 
    814   for (x = 0; int_fmt[x] != NULL ; x++)
    815     for (y = 0; int_nums[y] != 0 ; y++)
    816     {
    817       snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
    818       sprintf (buf2, int_fmt[x], int_nums[y]);
    819       if (strcmp (buf1, buf2))
    820       {
    821 	printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
    822 	    int_fmt[x], buf1, buf2);
    823 	fail++;
    824       }
    825       num++;
    826     }
    827   printf ("%d tests failed out of %d.\n", fail, num);
    828 }
    829 #endif /* SNPRINTF_TEST */
    830 
    831 #endif /* !HAVE_SNPRINTF */
    832 
    833