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