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