1/* This file is NOT part of Wget, but is used by Wget on the systems
2   where vsnprintf() is not defined.  It has been written by Patrick
3   Powell and modified by other people.  All the copyright and other
4   notices have been left intact.
5
6   My changes are documented at the bottom, along with other changes.
7   I hereby place my modifications to this file under the public
8   domain.  */
9
10/*
11 * Copyright Patrick Powell 1995
12 * This code is based on code written by Patrick Powell (papowell@astart.com)
13 * It may be used for any purpose as long as this notice remains intact
14 * on all source code distributions
15 */
16
17/**************************************************************
18 * Original:
19 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
20 * A bombproof version of doprnt (dopr) included.
21 * Sigh.  This sort of thing is always nasty do deal with.  Note that
22 * the version here does not include floating point...
23 *
24 * snprintf() is used instead of sprintf() as it does limit checks
25 * for string length.  This covers a nasty loophole.
26 *
27 * The other functions are there to prevent NULL pointers from
28 * causing nast effects.
29 *
30 * More Recently:
31 *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
32 *  This was ugly.  It is still ugly.  I opted out of floating point
33 *  numbers, but the formatter understands just about everything
34 *  from the normal C string format, at least as far as I can tell from
35 *  the Solaris 2.5 printf(3S) man page.
36 *
37 *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
38 *    Ok, added some minimal floating point support, which means this
39 *    probably requires libm on most operating systems.  Don't yet
40 *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
41 *    was pretty badly broken, it just wasn't being exercised in ways
42 *    which showed it, so that's been fixed.  Also, formated the code
43 *    to mutt conventions, and removed dead code left over from the
44 *    original.  Also, there is now a builtin-test, just compile with:
45 *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
46 *    and run snprintf for results.
47 *
48 *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
49 *    The PGP code was using unsigned hexadecimal formats.
50 *    Unfortunately, unsigned formats simply didn't work.
51 *
52 *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
53 *    The original code assumed that both snprintf() and vsnprintf() were
54 *    missing.  Some systems only have snprintf() but not vsnprintf(), so
55 *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
56 *
57 *  Andrew Tridgell (tridge@samba.org) Oct 1998
58 *    fixed handling of %.0f
59 *    added test for HAVE_LONG_DOUBLE
60 *
61 *  Russ Allbery <rra@stanford.edu> 2000-08-26
62 *    fixed return value to comply with C99
63 *    fixed handling of snprintf(NULL, ...)
64 *
65 *  Hrvoje Niksic <hniksic@xemacs.org> 2000-11-04
66 *    include <config.h> instead of "config.h".
67 *    moved TEST_SNPRINTF stuff out of HAVE_SNPRINTF ifdef.
68 *    include <stdio.h> for NULL.
69 *    added support and test cases for long long.
70 *    don't declare argument types to (v)snprintf if stdarg is not used.
71 *    use int instead of short int as 2nd arg to va_arg.
72 *
73 *  alexk (INN) 2002-08-21
74 *    use LLONG in fmtfp to handle more characters during floating
75 *    point conversion.
76 *
77 *  herb (Samba) 2002-12-19
78 *    actually print args for %g and %e
79 *
80 *  Hrvoje Niksic <hniksic@xemacs.org> 2005-04-15
81 *    write function definitions in the ansi2knr-friendly way.
82 *    if string precision is specified, don't read VALUE past it.
83 *    fix bug in fmtfp that caused 0.01 to be printed as 0.1.
84 *    don't include <ctype.h> because none of it is used.
85 *    interpret precision as number of significant digits with %g
86 *    omit trailing decimal zeros with %g
87 *
88 **************************************************************/
89
90#include "wget.h"
91
92/* For testing purposes, always compile in the code. */
93#ifdef TEST_SNPRINTF
94# undef HAVE_SNPRINTF
95# undef HAVE_VSNPRINTF
96# ifndef SIZEOF_LONG_LONG
97#  ifdef __GNUC__
98#   define SIZEOF_LONG_LONG 8
99#  endif
100# endif
101#endif
102
103#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
104
105#include <string.h>
106#include <sys/types.h>
107#include <stdio.h>              /* for NULL */
108
109#include <stdarg.h>
110
111#ifdef HAVE_LONG_DOUBLE
112#define LDOUBLE long double
113#else
114#define LDOUBLE double
115#endif
116
117#if SIZEOF_LONG_LONG != 0
118# define LLONG long long
119#else
120# define LLONG long
121#endif
122
123/* If we're running the test suite, rename snprintf and vsnprintf to
124   avoid conflicts with the system version.  */
125#ifdef TEST_SNPRINTF
126# define snprintf test_snprintf
127# define vsnprintf test_vsnprintf
128#endif
129
130int snprintf (char *str, size_t count, const char *fmt, ...);
131int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
132
133static int dopr (char *buffer, size_t maxlen, const char *format,
134                 va_list args);
135static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
136                   const char *value, int flags, int min, int max);
137static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
138                   LLONG value, int base, int min, int max, int flags);
139static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
140                  LDOUBLE fvalue, int min, int max, int flags);
141static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c);
142
143/*
144 * dopr(): poor man's version of doprintf
145 */
146
147/* format read states */
148#define DP_S_DEFAULT 0
149#define DP_S_FLAGS   1
150#define DP_S_MIN     2
151#define DP_S_DOT     3
152#define DP_S_MAX     4
153#define DP_S_MOD     5
154#define DP_S_MOD_L   6
155#define DP_S_CONV    7
156#define DP_S_DONE    8
157
158/* format flags - Bits */
159#define DP_F_MINUS      (1 << 0)
160#define DP_F_PLUS       (1 << 1)
161#define DP_F_SPACE      (1 << 2)
162#define DP_F_NUM        (1 << 3)
163#define DP_F_ZERO       (1 << 4)
164#define DP_F_UP         (1 << 5)
165#define DP_F_UNSIGNED   (1 << 6)
166#define DP_F_FP_G       (1 << 7)
167
168/* Conversion Flags */
169#define DP_C_SHORT   1
170#define DP_C_LONG    2
171#define DP_C_LLONG   3
172#define DP_C_LDOUBLE 4
173
174#define char_to_int(p) (p - '0')
175#define MAX(p,q) ((p >= q) ? p : q)
176#define MIN(p,q) ((p <= q) ? p : q)
177
178static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
179{
180  char ch;
181  LLONG value;
182  LDOUBLE fvalue;
183  char *strvalue;
184  int min;
185  int max;
186  int state;
187  int flags;
188  int cflags;
189  int total;
190  size_t currlen;
191
192  state = DP_S_DEFAULT;
193  currlen = flags = cflags = min = 0;
194  max = -1;
195  ch = *format++;
196  total = 0;
197
198  while (state != DP_S_DONE)
199  {
200    if (ch == '\0')
201      state = DP_S_DONE;
202
203    switch(state)
204    {
205    case DP_S_DEFAULT:
206      if (ch == '%')
207        state = DP_S_FLAGS;
208      else
209        total += dopr_outch (buffer, &currlen, maxlen, ch);
210      ch = *format++;
211      break;
212    case DP_S_FLAGS:
213      switch (ch)
214      {
215      case '-':
216        flags |= DP_F_MINUS;
217        ch = *format++;
218        break;
219      case '+':
220        flags |= DP_F_PLUS;
221        ch = *format++;
222        break;
223      case ' ':
224        flags |= DP_F_SPACE;
225        ch = *format++;
226        break;
227      case '#':
228        flags |= DP_F_NUM;
229        ch = *format++;
230        break;
231      case '0':
232        flags |= DP_F_ZERO;
233        ch = *format++;
234        break;
235      default:
236        state = DP_S_MIN;
237        break;
238      }
239      break;
240    case DP_S_MIN:
241      if ('0' <= ch && ch <= '9')
242      {
243        min = 10*min + char_to_int (ch);
244        ch = *format++;
245      }
246      else if (ch == '*')
247      {
248        min = va_arg (args, int);
249        ch = *format++;
250        state = DP_S_DOT;
251      }
252      else
253        state = DP_S_DOT;
254      break;
255    case DP_S_DOT:
256      if (ch == '.')
257      {
258        state = DP_S_MAX;
259        ch = *format++;
260      }
261      else
262        state = DP_S_MOD;
263      break;
264    case DP_S_MAX:
265      if ('0' <= ch && ch <= '9')
266      {
267        if (max < 0)
268          max = 0;
269        max = 10*max + char_to_int (ch);
270        ch = *format++;
271      }
272      else if (ch == '*')
273      {
274        max = va_arg (args, int);
275        ch = *format++;
276        state = DP_S_MOD;
277      }
278      else
279        state = DP_S_MOD;
280      break;
281    case DP_S_MOD:
282      switch (ch)
283      {
284      case 'h':
285        cflags = DP_C_SHORT;
286        ch = *format++;
287        break;
288      case 'l':
289        cflags = DP_C_LONG;
290        ch = *format++;
291        break;
292      case 'L':
293        cflags = DP_C_LDOUBLE;
294        ch = *format++;
295        break;
296      default:
297        break;
298      }
299      if (cflags != DP_C_LONG)
300        state = DP_S_CONV;
301      else
302        state = DP_S_MOD_L;
303      break;
304    case DP_S_MOD_L:
305      switch (ch)
306        {
307        case 'l':
308          cflags = DP_C_LLONG;
309          ch = *format++;
310          break;
311        default:
312          break;
313        }
314      state = DP_S_CONV;
315      break;
316    case DP_S_CONV:
317      switch (ch)
318      {
319      case 'd':
320      case 'i':
321        if (cflags == DP_C_SHORT)
322          value = (short int) va_arg (args, int);
323        else if (cflags == DP_C_LONG)
324          value = va_arg (args, long int);
325        else if (cflags == DP_C_LLONG)
326          value = va_arg (args, LLONG);
327        else
328          value = va_arg (args, int);
329        total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
330        break;
331      case 'o':
332        flags |= DP_F_UNSIGNED;
333        if (cflags == DP_C_SHORT)
334          value = (unsigned short int) va_arg (args, unsigned int);
335        else if (cflags == DP_C_LONG)
336          value = va_arg (args, unsigned long int);
337        else if (cflags == DP_C_LLONG)
338          value = va_arg (args, unsigned LLONG);
339        else
340          value = va_arg (args, unsigned int);
341        total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
342        break;
343      case 'u':
344        flags |= DP_F_UNSIGNED;
345        if (cflags == DP_C_SHORT)
346          value = (unsigned short int) va_arg (args, unsigned int);
347        else if (cflags == DP_C_LONG)
348          value = va_arg (args, unsigned long int);
349        else if (cflags == DP_C_LLONG)
350          value = va_arg (args, unsigned LLONG);
351        else
352          value = va_arg (args, unsigned int);
353        total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
354        break;
355      case 'X':
356        flags |= DP_F_UP;
357      case 'x':
358        flags |= DP_F_UNSIGNED;
359        if (cflags == DP_C_SHORT)
360          value = (unsigned short int) va_arg (args, unsigned int);
361        else if (cflags == DP_C_LONG)
362          value = va_arg (args, unsigned long int);
363        else if (cflags == DP_C_LLONG)
364          value = va_arg (args, unsigned LLONG);
365        else
366          value = va_arg (args, unsigned int);
367        total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
368        break;
369      case 'f':
370        if (cflags == DP_C_LDOUBLE)
371          fvalue = va_arg (args, LDOUBLE);
372        else
373          fvalue = va_arg (args, double);
374        total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
375        break;
376      case 'E':
377        flags |= DP_F_UP;
378      case 'e':
379        if (cflags == DP_C_LDOUBLE)
380          fvalue = va_arg (args, LDOUBLE);
381        else
382          fvalue = va_arg (args, double);
383        total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
384        break;
385      case 'G':
386        flags |= DP_F_UP;
387      case 'g':
388        flags |= DP_F_FP_G;
389        if (cflags == DP_C_LDOUBLE)
390          fvalue = va_arg (args, LDOUBLE);
391        else
392          fvalue = va_arg (args, double);
393        if (max == 0)
394          /* C99 says: if precision [for %g] is zero, it is taken as one */
395          max = 1;
396        total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
397        break;
398      case 'c':
399        total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
400        break;
401      case 's':
402        strvalue = va_arg (args, char *);
403        total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
404        break;
405      case 'p':
406        strvalue = va_arg (args, void *);
407        total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
408                         max, flags);
409        break;
410      case 'n':
411        if (cflags == DP_C_SHORT)
412        {
413          short int *num;
414          num = va_arg (args, short int *);
415          *num = currlen;
416        }
417        else if (cflags == DP_C_LONG)
418        {
419          long int *num;
420          num = va_arg (args, long int *);
421          *num = currlen;
422        }
423        else if (cflags == DP_C_LLONG)
424        {
425          LLONG *num;
426          num = va_arg (args, LLONG *);
427          *num = currlen;
428        }
429        else
430        {
431          int *num;
432          num = va_arg (args, int *);
433          *num = currlen;
434        }
435        break;
436      case '%':
437        total += dopr_outch (buffer, &currlen, maxlen, ch);
438        break;
439      case 'w':
440        /* not supported yet, treat as next char */
441        ch = *format++;
442        break;
443      default:
444        /* Unknown, skip */
445        break;
446      }
447      ch = *format++;
448      state = DP_S_DEFAULT;
449      flags = cflags = min = 0;
450      max = -1;
451      break;
452    case DP_S_DONE:
453      break;
454    default:
455      /* hmm? */
456      break; /* some picky compilers need this */
457    }
458  }
459  if (buffer != NULL)
460  {
461    if (currlen < maxlen - 1)
462      buffer[currlen] = '\0';
463    else
464      buffer[maxlen - 1] = '\0';
465  }
466  return total;
467}
468
469static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
470                   const char *value, int flags, int min, int max)
471{
472  int padlen, strln;     /* amount to pad */
473  int cnt = 0;
474  int total = 0;
475
476  if (value == 0)
477  {
478    value = "(null)";
479  }
480
481  if (max < 0)
482    strln = strlen (value);
483  else
484    /* When precision is specified, don't read VALUE past precision. */
485    /*strln = strnlen (value, max);*/
486    for (strln = 0; strln < max && value[strln]; ++strln)
487      ;
488  padlen = min - strln;
489  if (padlen < 0)
490    padlen = 0;
491  if (flags & DP_F_MINUS)
492    padlen = -padlen; /* Left Justify */
493
494  while (padlen > 0)
495  {
496    total += dopr_outch (buffer, currlen, maxlen, ' ');
497    --padlen;
498  }
499  while (*value && ((max < 0) || (cnt < max)))
500  {
501    total += dopr_outch (buffer, currlen, maxlen, *value++);
502    ++cnt;
503  }
504  while (padlen < 0)
505  {
506    total += dopr_outch (buffer, currlen, maxlen, ' ');
507    ++padlen;
508  }
509  return total;
510}
511
512/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
513
514static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
515                   LLONG value, int base, int min, int max, int flags)
516{
517  int signvalue = 0;
518  unsigned LLONG uvalue;
519  char convert[24];
520  int place = 0;
521  int spadlen = 0; /* amount to space pad */
522  int zpadlen = 0; /* amount to zero pad */
523  const char *digits;
524  int total = 0;
525
526  if (max < 0)
527    max = 0;
528
529  uvalue = value;
530
531  if(!(flags & DP_F_UNSIGNED))
532  {
533    if( value < 0 ) {
534      signvalue = '-';
535      uvalue = -value;
536    }
537    else
538      if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
539        signvalue = '+';
540    else
541      if (flags & DP_F_SPACE)
542        signvalue = ' ';
543  }
544
545  if (flags & DP_F_UP)
546    /* Should characters be upper case? */
547    digits = "0123456789ABCDEF";
548  else
549    digits = "0123456789abcdef";
550
551  do {
552    convert[place++] = digits[uvalue % (unsigned)base];
553    uvalue = (uvalue / (unsigned)base );
554  } while(uvalue && (place < sizeof (convert)));
555  if (place == sizeof (convert)) place--;
556  convert[place] = 0;
557
558  zpadlen = max - place;
559  spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
560  if (zpadlen < 0) zpadlen = 0;
561  if (spadlen < 0) spadlen = 0;
562  if (flags & DP_F_ZERO)
563  {
564    zpadlen = MAX(zpadlen, spadlen);
565    spadlen = 0;
566  }
567  if (flags & DP_F_MINUS)
568    spadlen = -spadlen; /* Left Justifty */
569
570#ifdef DEBUG_SNPRINTF
571  dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
572      zpadlen, spadlen, min, max, place));
573#endif
574
575  /* Spaces */
576  while (spadlen > 0)
577  {
578    total += dopr_outch (buffer, currlen, maxlen, ' ');
579    --spadlen;
580  }
581
582  /* Sign */
583  if (signvalue)
584    total += dopr_outch (buffer, currlen, maxlen, signvalue);
585
586  /* Zeros */
587  if (zpadlen > 0)
588  {
589    while (zpadlen > 0)
590    {
591      total += dopr_outch (buffer, currlen, maxlen, '0');
592      --zpadlen;
593    }
594  }
595
596  /* Digits */
597  while (place > 0)
598    total += dopr_outch (buffer, currlen, maxlen, convert[--place]);
599
600  /* Left Justified spaces */
601  while (spadlen < 0) {
602    total += dopr_outch (buffer, currlen, maxlen, ' ');
603    ++spadlen;
604  }
605
606  return total;
607}
608
609static LDOUBLE abs_val (LDOUBLE value)
610{
611  LDOUBLE result = value;
612
613  if (value < 0)
614    result = -value;
615
616  return result;
617}
618
619static LDOUBLE pow10_int (int exp)
620{
621  LDOUBLE result = 1;
622
623  while (exp)
624  {
625    result *= 10;
626    exp--;
627  }
628
629  return result;
630}
631
632static LLONG round_int (LDOUBLE value)
633{
634  LLONG intpart;
635
636  intpart = value;
637  value = value - intpart;
638  if (value >= 0.5)
639    intpart++;
640
641  return intpart;
642}
643
644static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
645                  LDOUBLE fvalue, int min, int max, int flags)
646{
647  int signvalue = 0;
648  LDOUBLE ufvalue;
649  char iconvert[24];
650  char fconvert[24];
651  int iplace = 0;
652  int fplace = 0;
653  int padlen = 0; /* amount to pad */
654  int zpadlen = 0;
655  int total = 0;
656  LLONG intpart;
657  LLONG fracpart;
658  LLONG mask10;
659  int leadingfrac0s = 0; /* zeros at the start of fractional part */
660  int omitzeros = 0;
661  int omitcount = 0;
662
663  /*
664   * AIX manpage says the default is 0, but Solaris says the default
665   * is 6, and sprintf on AIX defaults to 6
666   */
667  if (max < 0)
668    max = 6;
669
670  ufvalue = abs_val (fvalue);
671
672  if (fvalue < 0)
673    signvalue = '-';
674  else
675    if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
676      signvalue = '+';
677    else
678      if (flags & DP_F_SPACE)
679        signvalue = ' ';
680
681#if 0
682  if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
683#endif
684
685  intpart = ufvalue;
686
687  /* With %g precision is the number of significant digits, which
688     includes the digits in intpart. */
689  if (flags & DP_F_FP_G)
690    {
691      if (intpart != 0)
692        {
693          /* For each digit of INTPART, print one less fractional digit. */
694          LLONG temp = intpart;
695          for (temp = intpart; temp != 0; temp /= 10)
696            --max;
697          if (max < 0)
698            max = 0;
699        }
700      else
701        {
702          /* For each leading 0 in fractional part, print one more
703             fractional digit. */
704          LDOUBLE temp;
705          if (ufvalue != 0)
706            for (temp = ufvalue; temp < 0.1; temp *= 10)
707              ++max;
708        }
709    }
710
711  /* C99: trailing zeros are removed from the fractional portion of the
712     result unless the # flag is specified */
713  if ((flags & DP_F_FP_G) && !(flags & DP_F_NUM))
714    omitzeros = 1;
715
716#if SIZEOF_LONG_LONG > 0
717# define MAX_DIGITS 18          /* grok more digits with long long */
718#else
719# define MAX_DIGITS 9           /* just long */
720#endif
721
722  /*
723   * Sorry, we only support several digits past the decimal because of
724   * our conversion method
725   */
726  if (max > MAX_DIGITS)
727    max = MAX_DIGITS;
728
729  /* Factor of 10 with the needed number of digits, e.g. 1000 for max==3 */
730  mask10 = pow10_int (max);
731
732  /* We "cheat" by converting the fractional part to integer by
733   * multiplying by a factor of 10
734   */
735  fracpart = round_int (mask10 * (ufvalue - intpart));
736
737  if (fracpart >= mask10)
738  {
739    intpart++;
740    fracpart -= mask10;
741  }
742  else if (fracpart != 0)
743    /* If fracpart has less digits than the 10* mask, we need to
744       manually insert leading 0s.  For example 2.01's fractional part
745       requires one leading zero to distinguish it from 2.1. */
746    while (fracpart < mask10 / 10)
747      {
748        ++leadingfrac0s;
749        mask10 /= 10;
750      }
751
752#ifdef DEBUG_SNPRINTF
753  dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
754#endif
755
756  /* Convert integer part */
757  do {
758    iconvert[iplace++] = '0' + intpart % 10;
759    intpart = (intpart / 10);
760  } while(intpart && (iplace < sizeof(iconvert)));
761  if (iplace == sizeof(iconvert)) iplace--;
762  iconvert[iplace] = 0;
763
764  /* Convert fractional part */
765  do {
766    fconvert[fplace++] = '0' + fracpart % 10;
767    fracpart = (fracpart / 10);
768  } while(fracpart && (fplace < sizeof(fconvert)));
769  while (leadingfrac0s-- > 0 && fplace < sizeof(fconvert))
770    fconvert[fplace++] = '0';
771  if (fplace == sizeof(fconvert)) fplace--;
772  fconvert[fplace] = 0;
773  if (omitzeros)
774    while (omitcount < fplace && fconvert[omitcount] == '0')
775      ++omitcount;
776
777  /* -1 for decimal point, another -1 if we are printing a sign */
778  padlen = min - iplace - (max - omitcount) - 1 - ((signvalue) ? 1 : 0);
779  if (!omitzeros)
780    zpadlen = max - fplace;
781  if (zpadlen < 0)
782    zpadlen = 0;
783  if (padlen < 0)
784    padlen = 0;
785  if (flags & DP_F_MINUS)
786    padlen = -padlen; /* Left Justifty */
787
788  if ((flags & DP_F_ZERO) && (padlen > 0))
789  {
790    if (signvalue)
791    {
792      total += dopr_outch (buffer, currlen, maxlen, signvalue);
793      --padlen;
794      signvalue = 0;
795    }
796    while (padlen > 0)
797    {
798      total += dopr_outch (buffer, currlen, maxlen, '0');
799      --padlen;
800    }
801  }
802  while (padlen > 0)
803  {
804    total += dopr_outch (buffer, currlen, maxlen, ' ');
805    --padlen;
806  }
807  if (signvalue)
808    total += dopr_outch (buffer, currlen, maxlen, signvalue);
809
810  while (iplace > 0)
811    total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
812
813  /*
814   * Decimal point.  This should probably use locale to find the correct
815   * char to print out.
816   */
817  if (max > 0 && (fplace > omitcount || zpadlen > 0))
818  {
819    total += dopr_outch (buffer, currlen, maxlen, '.');
820
821    while (fplace > omitcount)
822      total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
823  }
824
825  while (zpadlen > 0)
826  {
827    total += dopr_outch (buffer, currlen, maxlen, '0');
828    --zpadlen;
829  }
830
831  while (padlen < 0)
832  {
833    total += dopr_outch (buffer, currlen, maxlen, ' ');
834    ++padlen;
835  }
836
837  return total;
838}
839
840static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
841{
842  if (*currlen + 1 < maxlen)
843    buffer[(*currlen)++] = c;
844  return 1;
845}
846
847#ifndef HAVE_VSNPRINTF
848int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
849{
850  if (str != NULL)
851    str[0] = 0;
852  return dopr(str, count, fmt, args);
853}
854#endif /* !HAVE_VSNPRINTF */
855
856#ifndef HAVE_SNPRINTF
857int snprintf (char *str, size_t count, const char *fmt,...)
858{
859  va_list ap;
860  int total;
861
862  va_start (ap, fmt);
863  total = vsnprintf (str, count, fmt, ap);
864  va_end (ap);
865  return total;
866}
867#endif /* !HAVE_SNPRINTF */
868#endif /* !HAVE_SNPRINTF || !HAVE_VSNPRINTF */
869
870#ifdef TEST_SNPRINTF
871
872# ifndef LONG_STRING
873#  define LONG_STRING 1024
874# endif
875
876int main (void)
877{
878  char buf1[LONG_STRING];
879  char buf2[LONG_STRING];
880  char *fp_fmt[] = {
881    /* %f formats */
882    "%f",
883    "%-1.5f",
884    "%1.5f",
885    "%123.9f",
886    "%10.5f",
887    "% 10.5f",
888    "%+22.9f",
889    "%+4.9f",
890    "%01.3f",
891    "%4f",
892    "%3.1f",
893    "%3.2f",
894    "%.0f",
895    "%.1f",
896    "%#10.1f",
897#if SIZEOF_LONG_LONG != 0
898    "%.16f",
899    "%18.16f",
900    "%-16.16f",
901#endif
902    /* %g formats */
903    "%g",
904    "%1.5g",
905    "%-1.5g",
906    "%.9g",
907    "%123.9g",
908    "%#123.9g",
909#if SIZEOF_LONG_LONG != 0
910    "%.16g",
911    "%20.16g",
912#endif
913    NULL
914  };
915  double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
916                       0.9996, 1.996, 4.136, 0.00205, 0.0001, 321.000009,
917                       0};
918  char *int_fmt[] = {
919    "%-1.5d",
920    "%1.5d",
921    "%123.9d",
922    "%5.5d",
923    "%10.5d",
924    "% 10.5d",
925    "%+22.33d",
926    "%01.3d",
927    "%4d",
928    NULL
929  };
930  long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
931#if SIZEOF_LONG_LONG != 0
932  char *llong_fmt[] = {
933    "%lld",             "%llu",
934    "%-1.5lld",         "%-1.5llu",
935    "%1.5lld",          "%1.5llu",
936    "%123.9lld",        "%123.9llu",
937    "%5.5lld",          "%5.5llu",
938    "%10.5lld",         "%10.5llu",
939    "% 10.5lld",        "% 10.5llu",
940    "%+22.33lld",       "%+22.33llu",
941    "%01.3lld",         "%01.3llu",
942    "%4lld",            "%4llu",
943    NULL
944  };
945  long long llong_nums[] = {
946    ~(long long)0,              /* all-1 bit pattern */
947    (~(unsigned long long)0) >> 1, /* largest signed long long */
948    /* random... */
949    -150, 134, 91340, 341,
950    0
951  };
952#endif
953  int x, y;
954  int fail = 0;
955  int num = 0;
956
957  printf ("Testing snprintf format codes against system sprintf...\n");
958
959  for (x = 0; fp_fmt[x] != NULL ; x++)
960    for (y = 0; fp_nums[y] != 0 ; y++)
961    {
962      snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
963      sprintf (buf2, fp_fmt[x], fp_nums[y]);
964      if (strcmp (buf1, buf2))
965      {
966        printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
967            fp_fmt[x], buf1, buf2);
968        fail++;
969      }
970      num++;
971    }
972
973  for (x = 0; int_fmt[x] != NULL ; x++)
974    for (y = 0; int_nums[y] != 0 ; y++)
975    {
976      snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
977      sprintf (buf2, int_fmt[x], int_nums[y]);
978      if (strcmp (buf1, buf2))
979      {
980        printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
981            int_fmt[x], buf1, buf2);
982        fail++;
983      }
984      num++;
985    }
986
987#if SIZEOF_LONG_LONG != 0
988  for (x = 0; llong_fmt[x] != NULL ; x++)
989    for (y = 0; llong_nums[y] != 0 ; y++)
990    {
991      snprintf (buf1, sizeof (buf1), llong_fmt[x], llong_nums[y]);
992      sprintf (buf2, llong_fmt[x], llong_nums[y]);
993      if (strcmp (buf1, buf2))
994      {
995        printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
996            llong_fmt[x], buf1, buf2);
997        fail++;
998      }
999      num++;
1000    }
1001#endif
1002
1003  printf ("%d tests failed out of %d.\n", fail, num);
1004  return 0;
1005}
1006#endif /* TEST_SNPRINTF */
1007