]> sjero.net Git - wget/blobdiff - src/snprintf.c
Updated config.guess, config.sub, install.sh.
[wget] / src / snprintf.c
index ea8d5002d374e19fc81857c3bfe7a37655f05fb9..2f2698bea47d2c958f394ece7d7970a420b0c4f2 100644 (file)
@@ -62,7 +62,7 @@
  *    fixed return value to comply with C99
  *    fixed handling of snprintf(NULL, ...)
  *
- *  Hrvoje Niksic <hniksic@arsdigita.com> 2000-11-04
+ *  Hrvoje Niksic <hniksic@xemacs.org> 2000-11-04
  *    include <config.h> instead of "config.h".
  *    moved TEST_SNPRINTF stuff out of HAVE_SNPRINTF ifdef.
  *    include <stdio.h> for NULL.
  *    don't declare argument types to (v)snprintf if stdarg is not used.
  *    use int instead of short int as 2nd arg to va_arg.
  *
+ *  alexk (INN) 2002-08-21
+ *    use LLONG in fmtfp to handle more characters during floating
+ *    point conversion.
+ *
+ *  herb (Samba) 2002-12-19
+ *    actually print args for %g and %e
+ *
+ *  Hrvoje Niksic <hniksic@xemacs.org> 2005-04-15
+ *    write function definitions in the ansi2knr-friendly way.
+ *    if string precision is specified, don't read VALUE past it.
+ *    fix bug in fmtfp that caused 0.01 to be printed as 0.1.
+ *    don't include <ctype.h> because none of it is used.
+ *    interpret precision as number of significant digits with %g
+ *    omit trailing decimal zeros with %g
+ *
  **************************************************************/
 
-#ifdef HAVE_CONFIG_H
-# include <config.h>
+#include "wget.h"
+
+/* For testing purposes, always compile in the code. */
+#ifdef TEST_SNPRINTF
+# undef HAVE_SNPRINTF
+# undef HAVE_VSNPRINTF
+# ifndef SIZEOF_LONG_LONG
+#  ifdef __GNUC__
+#   define SIZEOF_LONG_LONG 8
+#  endif
+# endif
 #endif
 
 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
 
 #include <string.h>
 #include <sys/types.h>
-#include <stdio.h>             /* for NULL */
-#include <safe-ctype.h>
-
-/* varargs declarations: */
-
-#if defined(HAVE_STDARG_H)
-# include <stdarg.h>
-# define HAVE_STDARGS    /* let's hope that works everywhere (mj) */
-# define VA_LOCAL_DECL   va_list ap
-# define VA_START(f)     va_start(ap, f)
-# define VA_SHIFT(v,t)  ;   /* no-op for ANSI */
-# define VA_END          va_end(ap)
-#else
-# include <varargs.h>
-# undef HAVE_STDARGS
-# define VA_LOCAL_DECL   va_list ap
-# define VA_START(f)     va_start(ap)      /* f is ignored! */
-# define VA_SHIFT(v,t) v = va_arg(ap,t)
-# define VA_END        va_end(ap)
-#endif
+#include <stdio.h>              /* for NULL */
+
+#include <stdarg.h>
 
 #ifdef HAVE_LONG_DOUBLE
 #define LDOUBLE long double
 #define LDOUBLE double
 #endif
 
-#ifdef HAVE_LONG_LONG
+#if SIZEOF_LONG_LONG != 0
 # define LLONG long long
 #else
 # define LLONG long
 #endif
 
-#ifdef HAVE_STDARGS
+/* If we're running the test suite, rename snprintf and vsnprintf to
+   avoid conflicts with the system version.  */
+#ifdef TEST_SNPRINTF
+# define snprintf test_snprintf
+# define vsnprintf test_vsnprintf
+#endif
+
 int snprintf (char *str, size_t count, const char *fmt, ...);
 int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
-#else
-int snprintf ();
-int vsnprintf ();
-#endif
 
-static int dopr (char *buffer, size_t maxlen, const char *format, 
+static int dopr (char *buffer, size_t maxlen, const char *format,
                  va_list args);
 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
-                  char *value, int flags, int min, int max);
+                   const char *value, int flags, int min, int max);
 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
-                  LLONG value, int base, int min, int max, int flags);
+                   LLONG value, int base, int min, int max, int flags);
 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
-                 LDOUBLE fvalue, int min, int max, int flags);
-static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
+                  LDOUBLE fvalue, int min, int max, int flags);
+static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c);
 
 /*
  * dopr(): poor man's version of doprintf
@@ -147,13 +156,14 @@ static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
 #define DP_S_DONE    8
 
 /* format flags - Bits */
-#define DP_F_MINUS     (1 << 0)
-#define DP_F_PLUS      (1 << 1)
-#define DP_F_SPACE     (1 << 2)
-#define DP_F_NUM       (1 << 3)
-#define DP_F_ZERO      (1 << 4)
-#define DP_F_UP        (1 << 5)
-#define DP_F_UNSIGNED  (1 << 6)
+#define DP_F_MINUS      (1 << 0)
+#define DP_F_PLUS       (1 << 1)
+#define DP_F_SPACE      (1 << 2)
+#define DP_F_NUM        (1 << 3)
+#define DP_F_ZERO       (1 << 4)
+#define DP_F_UP         (1 << 5)
+#define DP_F_UNSIGNED   (1 << 6)
+#define DP_F_FP_G       (1 << 7)
 
 /* Conversion Flags */
 #define DP_C_SHORT   1
@@ -194,113 +204,113 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
     {
     case DP_S_DEFAULT:
       if (ch == '%') 
-       state = DP_S_FLAGS;
+        state = DP_S_FLAGS;
       else 
-       total += dopr_outch (buffer, &currlen, maxlen, ch);
+        total += dopr_outch (buffer, &currlen, maxlen, ch);
       ch = *format++;
       break;
     case DP_S_FLAGS:
       switch (ch) 
       {
       case '-':
-       flags |= DP_F_MINUS;
+        flags |= DP_F_MINUS;
         ch = *format++;
-       break;
+        break;
       case '+':
-       flags |= DP_F_PLUS;
+        flags |= DP_F_PLUS;
         ch = *format++;
-       break;
+        break;
       case ' ':
-       flags |= DP_F_SPACE;
+        flags |= DP_F_SPACE;
         ch = *format++;
-       break;
+        break;
       case '#':
-       flags |= DP_F_NUM;
+        flags |= DP_F_NUM;
         ch = *format++;
-       break;
+        break;
       case '0':
-       flags |= DP_F_ZERO;
+        flags |= DP_F_ZERO;
         ch = *format++;
-       break;
+        break;
       default:
-       state = DP_S_MIN;
-       break;
+        state = DP_S_MIN;
+        break;
       }
       break;
     case DP_S_MIN:
       if ('0' <= ch && ch <= '9')
       {
-       min = 10*min + char_to_int (ch);
-       ch = *format++;
+        min = 10*min + char_to_int (ch);
+        ch = *format++;
       } 
       else if (ch == '*') 
       {
-       min = va_arg (args, int);
-       ch = *format++;
-       state = DP_S_DOT;
+        min = va_arg (args, int);
+        ch = *format++;
+        state = DP_S_DOT;
       } 
       else 
-       state = DP_S_DOT;
+        state = DP_S_DOT;
       break;
     case DP_S_DOT:
       if (ch == '.') 
       {
-       state = DP_S_MAX;
-       ch = *format++;
+        state = DP_S_MAX;
+        ch = *format++;
       } 
       else 
-       state = DP_S_MOD;
+        state = DP_S_MOD;
       break;
     case DP_S_MAX:
       if ('0' <= ch && ch <= '9')
       {
-       if (max < 0)
-         max = 0;
-       max = 10*max + char_to_int (ch);
-       ch = *format++;
+        if (max < 0)
+          max = 0;
+        max = 10*max + char_to_int (ch);
+        ch = *format++;
       } 
       else if (ch == '*') 
       {
-       max = va_arg (args, int);
-       ch = *format++;
-       state = DP_S_MOD;
+        max = va_arg (args, int);
+        ch = *format++;
+        state = DP_S_MOD;
       } 
       else 
-       state = DP_S_MOD;
+        state = DP_S_MOD;
       break;
     case DP_S_MOD:
       switch (ch) 
       {
       case 'h':
-       cflags = DP_C_SHORT;
-       ch = *format++;
-       break;
+        cflags = DP_C_SHORT;
+        ch = *format++;
+        break;
       case 'l':
-       cflags = DP_C_LONG;
-       ch = *format++;
-       break;
+        cflags = DP_C_LONG;
+        ch = *format++;
+        break;
       case 'L':
-       cflags = DP_C_LDOUBLE;
-       ch = *format++;
-       break;
+        cflags = DP_C_LDOUBLE;
+        ch = *format++;
+        break;
       default:
-       break;
+        break;
       }
       if (cflags != DP_C_LONG)
-       state = DP_S_CONV;
+        state = DP_S_CONV;
       else
-       state = DP_S_MOD_L;
+        state = DP_S_MOD_L;
       break;
     case DP_S_MOD_L:
       switch (ch)
-       {
-       case 'l':
-         cflags = DP_C_LLONG;
-         ch = *format++;
-         break;
-       default:
-         break;
-       }
+        {
+        case 'l':
+          cflags = DP_C_LLONG;
+          ch = *format++;
+          break;
+        default:
+          break;
+        }
       state = DP_S_CONV;
       break;
     case DP_S_CONV:
@@ -308,126 +318,131 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
       {
       case 'd':
       case 'i':
-       if (cflags == DP_C_SHORT) 
-         value = (short int)va_arg (args, int);
-       else if (cflags == DP_C_LONG)
-         value = va_arg (args, long int);
-       else if (cflags == DP_C_LLONG)
-         value = va_arg (args, LLONG);
-       else
-         value = va_arg (args, int);
-       total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
-       break;
+        if (cflags == DP_C_SHORT) 
+          value = (short int) va_arg (args, int);
+        else if (cflags == DP_C_LONG)
+          value = va_arg (args, long int);
+        else if (cflags == DP_C_LLONG)
+          value = va_arg (args, LLONG);
+        else
+          value = va_arg (args, int);
+        total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+        break;
       case 'o':
-       flags |= DP_F_UNSIGNED;
-       if (cflags == DP_C_SHORT)
-         value = (short int)va_arg (args, int);
-       else if (cflags == DP_C_LONG)
-         value = va_arg (args, unsigned long int);
-       else if (cflags == DP_C_LLONG)
-         value = va_arg (args, unsigned LLONG);
-       else
-         value = va_arg (args, unsigned int);
-       total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
-       break;
+        flags |= DP_F_UNSIGNED;
+        if (cflags == DP_C_SHORT)
+          value = (unsigned short int) va_arg (args, unsigned int);
+        else if (cflags == DP_C_LONG)
+          value = va_arg (args, unsigned long int);
+        else if (cflags == DP_C_LLONG)
+          value = va_arg (args, unsigned LLONG);
+        else
+          value = va_arg (args, unsigned int);
+        total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+        break;
       case 'u':
-       flags |= DP_F_UNSIGNED;
-       if (cflags == DP_C_SHORT)
-         value = (unsigned short int)va_arg (args, int);
-       else if (cflags == DP_C_LONG)
-         value = va_arg (args, unsigned long int);
-       else if (cflags == DP_C_LLONG)
-         value = va_arg (args, unsigned LLONG);
-       else
-         value = va_arg (args, unsigned int);
-       total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
-       break;
+        flags |= DP_F_UNSIGNED;
+        if (cflags == DP_C_SHORT)
+          value = (unsigned short int) va_arg (args, unsigned int);
+        else if (cflags == DP_C_LONG)
+          value = va_arg (args, unsigned long int);
+        else if (cflags == DP_C_LLONG)
+          value = va_arg (args, unsigned LLONG);
+        else
+          value = va_arg (args, unsigned int);
+        total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+        break;
       case 'X':
-       flags |= DP_F_UP;
+        flags |= DP_F_UP;
       case 'x':
-       flags |= DP_F_UNSIGNED;
-       if (cflags == DP_C_SHORT)
-         value = (short int)va_arg (args, int);
-       else if (cflags == DP_C_LONG)
-         value = va_arg (args, unsigned long int);
-       else if (cflags == DP_C_LLONG)
-         value = va_arg (args, unsigned LLONG);
-       else
-         value = va_arg (args, unsigned int);
-       total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
-       break;
+        flags |= DP_F_UNSIGNED;
+        if (cflags == DP_C_SHORT)
+          value = (unsigned short int) va_arg (args, unsigned int);
+        else if (cflags == DP_C_LONG)
+          value = va_arg (args, unsigned long int);
+        else if (cflags == DP_C_LLONG)
+          value = va_arg (args, unsigned LLONG);
+        else
+          value = va_arg (args, unsigned int);
+        total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+        break;
       case 'f':
-       if (cflags == DP_C_LDOUBLE)
-         fvalue = va_arg (args, LDOUBLE);
-       else
-         fvalue = va_arg (args, double);
-       /* um, floating point? */
-       total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
-       break;
+        if (cflags == DP_C_LDOUBLE)
+          fvalue = va_arg (args, LDOUBLE);
+        else
+          fvalue = va_arg (args, double);
+        total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+        break;
       case 'E':
-       flags |= DP_F_UP;
+        flags |= DP_F_UP;
       case 'e':
-       if (cflags == DP_C_LDOUBLE)
-         fvalue = va_arg (args, LDOUBLE);
-       else
-         fvalue = va_arg (args, double);
-       break;
+        if (cflags == DP_C_LDOUBLE)
+          fvalue = va_arg (args, LDOUBLE);
+        else
+          fvalue = va_arg (args, double);
+        total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+        break;
       case 'G':
-       flags |= DP_F_UP;
+        flags |= DP_F_UP;
       case 'g':
-       if (cflags == DP_C_LDOUBLE)
-         fvalue = va_arg (args, LDOUBLE);
-       else
-         fvalue = va_arg (args, double);
-       break;
+        flags |= DP_F_FP_G;
+        if (cflags == DP_C_LDOUBLE)
+          fvalue = va_arg (args, LDOUBLE);
+        else
+          fvalue = va_arg (args, double);
+        if (max == 0)
+          /* C99 says: if precision [for %g] is zero, it is taken as one */
+          max = 1;
+        total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+        break;
       case 'c':
-       total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
-       break;
+        total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+        break;
       case 's':
-       strvalue = va_arg (args, char *);
-       total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
-       break;
+        strvalue = va_arg (args, char *);
+        total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+        break;
       case 'p':
-       strvalue = va_arg (args, void *);
-       total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
+        strvalue = va_arg (args, void *);
+        total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
                          max, flags);
-       break;
+        break;
       case 'n':
-       if (cflags == DP_C_SHORT) 
-       {
-         short int *num;
-         num = va_arg (args, short int *);
-         *num = currlen;
+        if (cflags == DP_C_SHORT) 
+        {
+          short int *num;
+          num = va_arg (args, short int *);
+          *num = currlen;
         }
-       else if (cflags == DP_C_LONG) 
-       {
-         long int *num;
-         num = va_arg (args, long int *);
-         *num = currlen;
+        else if (cflags == DP_C_LONG) 
+        {
+          long int *num;
+          num = va_arg (args, long int *);
+          *num = currlen;
         } 
-       else if (cflags == DP_C_LLONG) 
-       {
-         LLONG *num;
-         num = va_arg (args, LLONG *);
-         *num = currlen;
+        else if (cflags == DP_C_LLONG) 
+        {
+          LLONG *num;
+          num = va_arg (args, LLONG *);
+          *num = currlen;
         } 
-       else 
-       {
-         int *num;
-         num = va_arg (args, int *);
-         *num = currlen;
+        else 
+        {
+          int *num;
+          num = va_arg (args, int *);
+          *num = currlen;
         }
-       break;
+        break;
       case '%':
-       total += dopr_outch (buffer, &currlen, maxlen, ch);
-       break;
+        total += dopr_outch (buffer, &currlen, maxlen, ch);
+        break;
       case 'w':
-       /* not supported yet, treat as next char */
-       ch = *format++;
-       break;
+        /* not supported yet, treat as next char */
+        ch = *format++;
+        break;
       default:
-       /* Unknown, skip */
-       break;
+        /* Unknown, skip */
+        break;
       }
       ch = *format++;
       state = DP_S_DEFAULT;
@@ -452,7 +467,7 @@ static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
 }
 
 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
-                   char *value, int flags, int min, int max)
+                   const char *value, int flags, int min, int max)
 {
   int padlen, strln;     /* amount to pad */
   int cnt = 0;
@@ -460,12 +475,16 @@ static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
   
   if (value == 0)
   {
-    value = "<NULL>";
+    value = "(null)";
   }
 
-  for (strln = 0; value[strln]; ++strln); /* strlen */
-  if (max >= 0 && max < strln)
-    strln = max;
+  if (max < 0)
+    strln = strlen (value);
+  else
+    /* When precision is specified, don't read VALUE past precision. */
+    /*strln = strnlen (value, max);*/
+    for (strln = 0; strln < max && value[strln]; ++strln)
+      ;
   padlen = min - strln;
   if (padlen < 0) 
     padlen = 0;
@@ -493,7 +512,7 @@ static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
 
 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
-                  LLONG value, int base, int min, int max, int flags)
+                   LLONG value, int base, int min, int max, int flags)
 {
   int signvalue = 0;
   unsigned LLONG uvalue;
@@ -517,10 +536,10 @@ static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
     }
     else
       if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
-       signvalue = '+';
+        signvalue = '+';
     else
       if (flags & DP_F_SPACE)
-       signvalue = ' ';
+        signvalue = ' ';
   }
   
   if (flags & DP_F_UP)
@@ -597,7 +616,7 @@ static LDOUBLE abs_val (LDOUBLE value)
   return result;
 }
 
-static LDOUBLE pow10 (int exp)
+static LDOUBLE pow10_int (int exp)
 {
   LDOUBLE result = 1;
 
@@ -610,9 +629,9 @@ static LDOUBLE pow10 (int exp)
   return result;
 }
 
-static long round (LDOUBLE value)
+static LLONG round_int (LDOUBLE value)
 {
-  long intpart;
+  LLONG intpart;
 
   intpart = value;
   value = value - intpart;
@@ -623,20 +642,23 @@ static long round (LDOUBLE value)
 }
 
 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
-                 LDOUBLE fvalue, int min, int max, int flags)
+                  LDOUBLE fvalue, int min, int max, int flags)
 {
   int signvalue = 0;
   LDOUBLE ufvalue;
-  char iconvert[20];
-  char fconvert[20];
+  char iconvert[24];
+  char fconvert[24];
   int iplace = 0;
   int fplace = 0;
   int padlen = 0; /* amount to pad */
   int zpadlen = 0; 
-  int caps = 0;
   int total = 0;
-  long intpart;
-  long fracpart;
+  LLONG intpart;
+  LLONG fracpart;
+  LLONG mask10;
+  int leadingfrac0s = 0; /* zeros at the start of fractional part */
+  int omitzeros = 0;
+  int omitcount = 0;
   
   /* 
    * AIX manpage says the default is 0, but Solaris says the default
@@ -654,7 +676,7 @@ static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
       signvalue = '+';
     else
       if (flags & DP_F_SPACE)
-       signvalue = ' ';
+        signvalue = ' ';
 
 #if 0
   if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
@@ -662,23 +684,70 @@ static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
 
   intpart = ufvalue;
 
+  /* With %g precision is the number of significant digits, which
+     includes the digits in intpart. */
+  if (flags & DP_F_FP_G)
+    {
+      if (intpart != 0)
+        {
+          /* For each digit of INTPART, print one less fractional digit. */
+          LLONG temp = intpart;
+          for (temp = intpart; temp != 0; temp /= 10)
+            --max;
+          if (max < 0)
+            max = 0;
+        }
+      else
+        {
+          /* For each leading 0 in fractional part, print one more
+             fractional digit. */
+          LDOUBLE temp;
+          if (ufvalue != 0)
+            for (temp = ufvalue; temp < 0.1; temp *= 10)
+              ++max;
+        }
+    }
+
+  /* C99: trailing zeros are removed from the fractional portion of the
+     result unless the # flag is specified */
+  if ((flags & DP_F_FP_G) && !(flags & DP_F_NUM))
+    omitzeros = 1;
+
+#if SIZEOF_LONG_LONG > 0
+# define MAX_DIGITS 18          /* grok more digits with long long */
+#else
+# define MAX_DIGITS 9           /* just long */
+#endif
+
   /* 
-   * Sorry, we only support 9 digits past the decimal because of our 
-   * conversion method
+   * Sorry, we only support several digits past the decimal because of
+   * our conversion method
    */
-  if (max > 9)
-    max = 9;
+  if (max > MAX_DIGITS)
+    max = MAX_DIGITS;
+
+  /* Factor of 10 with the needed number of digits, e.g. 1000 for max==3 */
+  mask10 = pow10_int (max);
 
   /* We "cheat" by converting the fractional part to integer by
    * multiplying by a factor of 10
    */
-  fracpart = round ((pow10 (max)) * (ufvalue - intpart));
+  fracpart = round_int (mask10 * (ufvalue - intpart));
 
-  if (fracpart >= pow10 (max))
+  if (fracpart >= mask10)
   {
     intpart++;
-    fracpart -= pow10 (max);
+    fracpart -= mask10;
   }
+  else if (fracpart != 0)
+    /* If fracpart has less digits than the 10* mask, we need to
+       manually insert leading 0s.  For example 2.01's fractional part
+       requires one leading zero to distinguish it from 2.1. */
+    while (fracpart < mask10 / 10)
+      {
+        ++leadingfrac0s;
+        mask10 /= 10;
+      }
 
 #ifdef DEBUG_SNPRINTF
   dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
@@ -686,25 +755,29 @@ static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
 
   /* Convert integer part */
   do {
-    iconvert[iplace++] =
-      (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
+    iconvert[iplace++] = '0' + intpart % 10;
     intpart = (intpart / 10);
-  } while(intpart && (iplace < 20));
-  if (iplace == 20) iplace--;
+  } while(intpart && (iplace < sizeof(iconvert)));
+  if (iplace == sizeof(iconvert)) iplace--;
   iconvert[iplace] = 0;
 
   /* Convert fractional part */
   do {
-    fconvert[fplace++] =
-      (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
+    fconvert[fplace++] = '0' + fracpart % 10;
     fracpart = (fracpart / 10);
-  } while(fracpart && (fplace < 20));
-  if (fplace == 20) fplace--;
+  } while(fracpart && (fplace < sizeof(fconvert)));
+  while (leadingfrac0s-- > 0 && fplace < sizeof(fconvert))
+    fconvert[fplace++] = '0';
+  if (fplace == sizeof(fconvert)) fplace--;
   fconvert[fplace] = 0;
+  if (omitzeros)
+    while (omitcount < fplace && fconvert[omitcount] == '0')
+      ++omitcount;
 
   /* -1 for decimal point, another -1 if we are printing a sign */
-  padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
-  zpadlen = max - fplace;
+  padlen = min - iplace - (max - omitcount) - 1 - ((signvalue) ? 1 : 0);
+  if (!omitzeros)
+    zpadlen = max - fplace;
   if (zpadlen < 0)
     zpadlen = 0;
   if (padlen < 0) 
@@ -741,11 +814,11 @@ static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
    * Decimal point.  This should probably use locale to find the correct
    * char to print out.
    */
-  if (max > 0)
+  if (max > 0 && (fplace > omitcount || zpadlen > 0))
   {
     total += dopr_outch (buffer, currlen, maxlen, '.');
 
-    while (fplace > 0
+    while (fplace > omitcount
       total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
   }
 
@@ -781,27 +854,14 @@ int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
 #endif /* !HAVE_VSNPRINTF */
 
 #ifndef HAVE_SNPRINTF
-/* VARARGS3 */
-#ifdef HAVE_STDARGS
-int snprintf (char *str,size_t count,const char *fmt,...)
-#else
-int snprintf (va_alist) va_dcl
-#endif
+int snprintf (char *str, size_t count, const char *fmt,...)
 {
-#ifndef HAVE_STDARGS
-  char *str;
-  size_t count;
-  char *fmt;
-#endif
-  VA_LOCAL_DECL;
+  va_list ap;
   int total;
-    
-  VA_START (fmt);
-  VA_SHIFT (str, char *);
-  VA_SHIFT (count, size_t );
-  VA_SHIFT (fmt, char *);
-  total = vsnprintf(str, count, fmt, ap);
-  VA_END;
+
+  va_start (ap, fmt);
+  total = vsnprintf (str, count, fmt, ap);
+  va_end (ap);
   return total;
 }
 #endif /* !HAVE_SNPRINTF */
@@ -809,16 +869,17 @@ int snprintf (va_alist) va_dcl
 
 #ifdef TEST_SNPRINTF
 
-#include <stdio.h>
+# ifndef LONG_STRING
+#  define LONG_STRING 1024
+# endif
 
-#ifndef LONG_STRING
-#define LONG_STRING 1024
-#endif
 int main (void)
 {
   char buf1[LONG_STRING];
   char buf2[LONG_STRING];
   char *fp_fmt[] = {
+    /* %f formats */
+    "%f",
     "%-1.5f",
     "%1.5f",
     "%123.9f",
@@ -832,10 +893,28 @@ int main (void)
     "%3.2f",
     "%.0f",
     "%.1f",
+    "%#10.1f",
+#if SIZEOF_LONG_LONG != 0
+    "%.16f",
+    "%18.16f",
+    "%-16.16f",
+#endif
+    /* %g formats */
+    "%g",
+    "%1.5g",
+    "%-1.5g",
+    "%.9g",
+    "%123.9g",
+    "%#123.9g",
+#if SIZEOF_LONG_LONG != 0
+    "%.16g",
+    "%20.16g",
+#endif
     NULL
   };
   double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 
-    0.9996, 1.996, 4.136, 0};
+                       0.9996, 1.996, 4.136, 0.00205, 0.0001, 321.000009,
+                       0};
   char *int_fmt[] = {
     "%-1.5d",
     "%1.5d",
@@ -849,22 +928,22 @@ int main (void)
     NULL
   };
   long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
-#ifdef HAVE_LONG_LONG
+#if SIZEOF_LONG_LONG != 0
   char *llong_fmt[] = {
-    "%lld",            "%llu",
-    "%-1.5lld",                "%-1.5llu",
-    "%1.5lld",         "%1.5llu",
-    "%123.9lld",       "%123.9llu",
-    "%5.5lld",         "%5.5llu",
-    "%10.5lld",                "%10.5llu",
-    "% 10.5lld",       "% 10.5llu",
-    "%+22.33lld",      "%+22.33llu",
-    "%01.3lld",                "%01.3llu",
-    "%4lld",           "%4llu",
+    "%lld",             "%llu",
+    "%-1.5lld",         "%-1.5llu",
+    "%1.5lld",          "%1.5llu",
+    "%123.9lld",        "%123.9llu",
+    "%5.5lld",          "%5.5llu",
+    "%10.5lld",         "%10.5llu",
+    "% 10.5lld",        "% 10.5llu",
+    "%+22.33lld",       "%+22.33llu",
+    "%01.3lld",         "%01.3llu",
+    "%4lld",            "%4llu",
     NULL
   };
   long long llong_nums[] = {
-    ~(long long)0,             /* all-1 bit pattern */
+    ~(long long)0,              /* all-1 bit pattern */
     (~(unsigned long long)0) >> 1, /* largest signed long long */
     /* random... */
     -150, 134, 91340, 341,
@@ -884,9 +963,9 @@ int main (void)
       sprintf (buf2, fp_fmt[x], fp_nums[y]);
       if (strcmp (buf1, buf2))
       {
-       printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
-           fp_fmt[x], buf1, buf2);
-       fail++;
+        printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
+            fp_fmt[x], buf1, buf2);
+        fail++;
       }
       num++;
     }
@@ -898,14 +977,14 @@ int main (void)
       sprintf (buf2, int_fmt[x], int_nums[y]);
       if (strcmp (buf1, buf2))
       {
-       printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
-           int_fmt[x], buf1, buf2);
-       fail++;
+        printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
+            int_fmt[x], buf1, buf2);
+        fail++;
       }
       num++;
     }
 
-#ifdef HAVE_LONG_LONG
+#if SIZEOF_LONG_LONG != 0
   for (x = 0; llong_fmt[x] != NULL ; x++)
     for (y = 0; llong_nums[y] != 0 ; y++)
     {
@@ -913,14 +992,15 @@ int main (void)
       sprintf (buf2, llong_fmt[x], llong_nums[y]);
       if (strcmp (buf1, buf2))
       {
-       printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
-           llong_fmt[x], buf1, buf2);
-       fail++;
+        printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
+            llong_fmt[x], buf1, buf2);
+        fail++;
       }
       num++;
     }
 #endif
 
   printf ("%d tests failed out of %d.\n", fail, num);
+  return 0;
 }
-#endif /* SNPRINTF_TEST */
+#endif /* TEST_SNPRINTF */