]> sjero.net Git - wget/blob - src/snprintf.c
Updated config.guess, config.sub, install.sh.
[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 #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
130 int snprintf (char *str, size_t count, const char *fmt, ...);
131 int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
132
133 static int dopr (char *buffer, size_t maxlen, const char *format,
134                  va_list args);
135 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
136                    const char *value, int flags, int min, int max);
137 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
138                    LLONG value, int base, int min, int max, int flags);
139 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
140                   LDOUBLE fvalue, int min, int max, int flags);
141 static 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
178 static 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
469 static 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
514 static 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
609 static LDOUBLE abs_val (LDOUBLE value)
610 {
611   LDOUBLE result = value;
612
613   if (value < 0)
614     result = -value;
615
616   return result;
617 }
618
619 static 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
632 static 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
644 static 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
840 static 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
848 int 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
857 int 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
876 int 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 */