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