]> eyrie.org Git - kerberos/krb5-strength.git/blob - portable/snprintf.c
Declare fast forward from 3.1-2
[kerberos/krb5-strength.git] / portable / snprintf.c
1 /*
2  * Replacement for a missing snprintf or vsnprintf.
3  *
4  * The following implementation of snprintf was taken mostly verbatim from
5  * <http://www.fiction.net/blong/programs/>; it is the version of snprintf
6  * used in Mutt.  A possibly newer version is used in wget, found at
7  * <https://github.com/wertarbyte/wget/blob/master/src/snprintf.c>.
8  *
9  * Please do not reformat or otherwise change this file more than necessary so
10  * that later merges with the original source are easy.  Bug fixes and
11  * improvements should be sent back to the original author.
12  *
13  * The canonical version of this file is maintained in the rra-c-util package,
14  * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
15  */
16
17 /*
18  * If we're running the test suite, rename snprintf and vsnprintf to avoid
19  * conflicts with the system version.
20  */
21 #if TESTING
22 # undef snprintf
23 # undef vsnprintf
24 # define snprintf test_snprintf
25 # define vsnprintf test_vsnprintf
26 #endif
27
28 /*
29  * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7
30  * could you use the __format__ form of the attributes, which is what we use
31  * (to avoid confusion with other macros).
32  */
33 #ifndef __attribute__
34 #    if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
35 #        define __attribute__(spec) /* empty */
36 #    endif
37 #endif
38
39 /*
40  * Older Clang doesn't support __attribute__((fallthrough)) properly and
41  * complains about the empty statement that it is decorating.  Suppress that
42  * warning.  Also suppress warnings about unknown attributes to handle older
43  * Clang versions.
44  */
45 #if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__))
46 #    pragma GCC diagnostic ignored "-Wattributes"
47 #    pragma GCC diagnostic ignored "-Wmissing-declarations"
48 #endif
49
50 /* Specific to rra-c-util, but only when debugging is enabled. */
51 #ifdef DEBUG_SNPRINTF
52 # include <util/messages.h>
53 #endif
54
55 /*
56  * Copyright Patrick Powell 1995
57  * This code is based on code written by Patrick Powell (papowell@astart.com)
58  * It may be used for any purpose as long as this notice remains intact
59  * on all source code distributions
60  *
61  * There is no SPDX-License-Identifier registered for this license.
62  */
63
64 /**************************************************************
65  * Original:
66  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
67  * A bombproof version of doprnt (dopr) included.
68  * Sigh.  This sort of thing is always nasty do deal with.  Note that
69  * the version here does not include floating point...
70  *
71  * snprintf() is used instead of sprintf() as it does limit checks
72  * for string length.  This covers a nasty loophole.
73  *
74  * The other functions are there to prevent NULL pointers from
75  * causing nast effects.
76  *
77  * More Recently:
78  *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
79  *  This was ugly.  It is still ugly.  I opted out of floating point
80  *  numbers, but the formatter understands just about everything
81  *  from the normal C string format, at least as far as I can tell from
82  *  the Solaris 2.5 printf(3S) man page.
83  *
84  *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
85  *    Ok, added some minimal floating point support, which means this
86  *    probably requires libm on most operating systems.  Don't yet
87  *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
88  *    was pretty badly broken, it just wasn't being exercised in ways
89  *    which showed it, so that's been fixed.  Also, formatted the code
90  *    to mutt conventions, and removed dead code left over from the
91  *    original.  Also, there is now a builtin-test, just compile with:
92  *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
93  *    and run snprintf for results.
94  * 
95  *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
96  *    The PGP code was using unsigned hexadecimal formats. 
97  *    Unfortunately, unsigned formats simply didn't work.
98  *
99  *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
100  *    The original code assumed that both snprintf() and vsnprintf() were
101  *    missing.  Some systems only have snprintf() but not vsnprintf(), so
102  *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
103  *
104  *  Andrew Tridgell (tridge@samba.org) Oct 1998
105  *    fixed handling of %.0f
106  *    added test for HAVE_LONG_DOUBLE
107  *
108  *  Russ Allbery <eagle@eyrie.org> 2000-08-26
109  *    fixed return value to comply with C99
110  *    fixed handling of snprintf(NULL, ...)
111  *    added explicit casts for double to long long int conversion
112  *    fixed various warnings with GCC 7
113  *    fixed various warnings with Clang
114  *
115  *  Hrvoje Niksic <hniksic@xemacs.org> 2000-11-04
116  *    include <config.h> instead of "config.h".
117  *    moved TEST_SNPRINTF stuff out of HAVE_SNPRINTF ifdef.
118  *    include <stdio.h> for NULL.
119  *    added support and test cases for long long.
120  *    don't declare argument types to (v)snprintf if stdarg is not used.
121  *    use int instead of short int as 2nd arg to va_arg.
122  *
123  *  alexk (INN) 2002-08-21
124  *    use LLONG in fmtfp to handle more characters during floating
125  *    point conversion.
126  *
127  *  herb (Samba) 2002-12-19
128  *    actually print args for %g and %e
129  *
130  *  Hrvoje Niksic <hniksic@xemacs.org> 2005-04-15
131  *    use the PARAMS macro to handle prototypes.
132  *    write function definitions in the ansi2knr-friendly way.
133  *    if string precision is specified, don't read VALUE past it.
134  *    fix bug in fmtfp that caused 0.01 to be printed as 0.1.
135  *    don't include <ctype.h> because none of it is used.
136  *    interpret precision as number of significant digits with %g
137  *    omit trailing decimal zeros with %g
138  *
139  **************************************************************/
140
141 #include <config.h>
142
143 #include <string.h>
144 #include <ctype.h>
145 #include <sys/types.h>
146
147 #ifndef NULL
148 # define NULL 0
149 #endif
150
151 /* varargs declarations: */
152
153 #include <stdarg.h>
154
155 /* Assume all compilers support long double, per Autoconf documentation. */
156 #define LDOUBLE long double
157
158 #ifdef HAVE_LONG_LONG_INT
159 # define LLONG long long
160 #else
161 # define LLONG long
162 #endif
163
164 int snprintf (char *str, size_t count, const char *fmt, ...);
165 int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
166
167 static int dopr (char *buffer, size_t maxlen, const char *format, 
168                  va_list args);
169 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
170                    const char *value, int flags, int min, int max);
171 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
172                    LLONG value, int base, int min, int max, int flags);
173 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
174                   LDOUBLE fvalue, int min, int max, int flags);
175 static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
176
177 /*
178  * dopr(): poor man's version of doprintf
179  */
180
181 /* format read states */
182 #define DP_S_DEFAULT 0
183 #define DP_S_FLAGS   1
184 #define DP_S_MIN     2
185 #define DP_S_DOT     3
186 #define DP_S_MAX     4
187 #define DP_S_MOD     5
188 #define DP_S_MOD_L   6
189 #define DP_S_CONV    7
190 #define DP_S_DONE    8
191
192 /* format flags - Bits */
193 #define DP_F_MINUS      (1 << 0)
194 #define DP_F_PLUS       (1 << 1)
195 #define DP_F_SPACE      (1 << 2)
196 #define DP_F_NUM        (1 << 3)
197 #define DP_F_ZERO       (1 << 4)
198 #define DP_F_UP         (1 << 5)
199 #define DP_F_UNSIGNED   (1 << 6)
200 #define DP_F_FP_G       (1 << 7)
201
202 /* Conversion Flags */
203 #define DP_C_SHORT   1
204 #define DP_C_LONG    2
205 #define DP_C_LLONG   3
206 #define DP_C_LDOUBLE 4
207
208 #define char_to_int(p) (p - '0')
209 #define MAX(p,q) ((p >= q) ? p : q)
210 #define MIN(p,q) ((p <= q) ? p : q)
211
212 static int 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   unsigned 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         __attribute__((fallthrough));
392         /* fall through */
393       case 'x':
394         flags |= DP_F_UNSIGNED;
395         if (cflags == DP_C_SHORT)
396           value = (unsigned short int) va_arg (args, unsigned int);
397         else if (cflags == DP_C_LONG)
398           value = va_arg (args, unsigned long int);
399         else if (cflags == DP_C_LLONG)
400           value = va_arg (args, unsigned LLONG);
401         else
402           value = va_arg (args, unsigned int);
403         total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
404         break;
405       case 'f':
406         if (cflags == DP_C_LDOUBLE)
407           fvalue = va_arg (args, LDOUBLE);
408         else
409           fvalue = (LDOUBLE) va_arg (args, double);
410         total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
411         break;
412       case 'E':
413         flags |= DP_F_UP;
414         __attribute__((fallthrough));
415         /* fall through */
416       case 'e':
417         if (cflags == DP_C_LDOUBLE)
418           fvalue = va_arg (args, LDOUBLE);
419         else
420           fvalue = (LDOUBLE) va_arg (args, double);
421         total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
422         break;
423       case 'G':
424         flags |= DP_F_UP;
425         __attribute__((fallthrough));
426         /* fall through */
427       case 'g':
428         flags |= DP_F_FP_G;
429         if (cflags == DP_C_LDOUBLE)
430           fvalue = va_arg (args, LDOUBLE);
431         else
432           fvalue = (LDOUBLE) va_arg (args, double);
433         if (max == 0)
434           /* C99 says: if precision [for %g] is zero, it is taken as one */
435           max = 1;
436         total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
437         break;
438       case 'c':
439         total += dopr_outch (buffer, &currlen, maxlen,
440                              (char) va_arg (args, int));
441         break;
442       case 's':
443         strvalue = va_arg (args, char *);
444         total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
445         break;
446       case 'p':
447         strvalue = va_arg (args, void *);
448         total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
449                          max, flags);
450         break;
451       case 'n':
452         if (cflags == DP_C_SHORT) 
453         {
454           short int *num;
455           num = va_arg (args, short int *);
456           *num = (short) currlen;
457         } 
458         else if (cflags == DP_C_LONG) 
459         {
460           long int *num;
461           num = va_arg (args, long int *);
462           *num = currlen;
463         }
464         else if (cflags == DP_C_LLONG) 
465         {
466           LLONG *num;
467           num = va_arg (args, LLONG *);
468           *num = currlen;
469         } 
470         else 
471         {
472           int *num;
473           num = va_arg (args, int *);
474           *num = (int) currlen;
475         }
476         break;
477       case '%':
478         total += dopr_outch (buffer, &currlen, maxlen, ch);
479         break;
480       case 'w':
481         /* not supported yet, treat as next char */
482         format++;
483         break;
484       default:
485         /* Unknown, skip */
486         break;
487       }
488       ch = *format++;
489       state = DP_S_DEFAULT;
490       flags = cflags = min = 0;
491       max = -1;
492       break;
493     case DP_S_DONE:
494       break;
495     default:
496       /* hmm? */
497       break; /* some picky compilers need this */
498     }
499   }
500   if (buffer != NULL)
501   {
502     if (currlen < maxlen - 1) 
503       buffer[currlen] = '\0';
504     else 
505       buffer[maxlen - 1] = '\0';
506   }
507   return total;
508 }
509
510 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
511                    const char *value, int flags, int min, int max)
512 {
513   int padlen, strln;     /* amount to pad */
514   int cnt = 0;
515   int total = 0;
516   
517   if (value == 0)
518   {
519     value = "(null)";
520   }
521
522   if (max < 0)
523     strln = (int) strlen (value);
524   else
525     /* When precision is specified, don't read VALUE past precision. */
526     /*strln = strnlen (value, max);*/
527     for (strln = 0; strln < max && value[strln]; ++strln);
528   padlen = min - strln;
529   if (padlen < 0) 
530     padlen = 0;
531   if (flags & DP_F_MINUS) 
532     padlen = -padlen; /* Left Justify */
533
534   while (padlen > 0)
535   {
536     total += dopr_outch (buffer, currlen, maxlen, ' ');
537     --padlen;
538   }
539   while (*value && ((max < 0) || (cnt < max)))
540   {
541     total += dopr_outch (buffer, currlen, maxlen, *value++);
542     ++cnt;
543   }
544   while (padlen < 0)
545   {
546     total += dopr_outch (buffer, currlen, maxlen, ' ');
547     ++padlen;
548   }
549   return total;
550 }
551
552 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
553
554 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
555                    LLONG value, int base, int min, int max, int flags)
556 {
557   char signvalue = 0;
558   unsigned LLONG uvalue;
559   char convert[24];
560   unsigned int place = 0;
561   int spadlen = 0; /* amount to space pad */
562   int zpadlen = 0; /* amount to zero pad */
563   const char *digits;
564   int total = 0;
565   
566   if (max < 0)
567     max = 0;
568
569   uvalue = value;
570
571   if(!(flags & DP_F_UNSIGNED))
572   {
573     if( value < 0 ) {
574       signvalue = '-';
575       uvalue = -value;
576     }
577     else
578       if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
579         signvalue = '+';
580     else
581       if (flags & DP_F_SPACE)
582         signvalue = ' ';
583   }
584   
585   if (flags & DP_F_UP)
586     /* Should characters be upper case? */
587     digits = "0123456789ABCDEF";
588   else
589     digits = "0123456789abcdef";
590
591   do {
592     convert[place++] = digits[uvalue % (unsigned)base];
593     uvalue = (uvalue / (unsigned)base );
594   } while(uvalue && (place < sizeof (convert)));
595   if (place == sizeof (convert)) place--;
596   convert[place] = 0;
597
598   zpadlen = max - place;
599   spadlen = min - MAX ((unsigned int)max, place) - (signvalue ? 1 : 0);
600   if (zpadlen < 0) zpadlen = 0;
601   if (spadlen < 0) spadlen = 0;
602   if (flags & DP_F_ZERO)
603   {
604     zpadlen = MAX(zpadlen, spadlen);
605     spadlen = 0;
606   }
607   if (flags & DP_F_MINUS) 
608     spadlen = -spadlen; /* Left Justifty */
609
610 #ifdef DEBUG_SNPRINTF
611   debug ("zpad: %d, spad: %d, min: %d, max: %d, place: %u\n",
612          zpadlen, spadlen, min, max, place);
613 #endif
614
615   /* Spaces */
616   while (spadlen > 0) 
617   {
618     total += dopr_outch (buffer, currlen, maxlen, ' ');
619     --spadlen;
620   }
621
622   /* Sign */
623   if (signvalue) 
624     total += dopr_outch (buffer, currlen, maxlen, signvalue);
625
626   /* Zeros */
627   if (zpadlen > 0) 
628   {
629     while (zpadlen > 0)
630     {
631       total += dopr_outch (buffer, currlen, maxlen, '0');
632       --zpadlen;
633     }
634   }
635
636   /* Digits */
637   while (place > 0) 
638     total += dopr_outch (buffer, currlen, maxlen, convert[--place]);
639   
640   /* Left Justified spaces */
641   while (spadlen < 0) {
642     total += dopr_outch (buffer, currlen, maxlen, ' ');
643     ++spadlen;
644   }
645
646   return total;
647 }
648
649 static LDOUBLE abs_val (LDOUBLE value)
650 {
651   LDOUBLE result = value;
652
653   if (value < 0)
654     result = -value;
655
656   return result;
657 }
658
659 static LLONG pow10_int (unsigned int exp)
660 {
661   LDOUBLE result = 1;
662
663   while (exp)
664   {
665     result *= 10;
666     exp--;
667   }
668   
669   return (LLONG) result;
670 }
671
672 static LLONG round_int (LDOUBLE value)
673 {
674   LLONG intpart;
675
676   intpart = (LLONG) value;
677   value = value - intpart;
678   if (value >= (LDOUBLE) 0.5)
679     intpart++;
680
681   return intpart;
682 }
683
684 /*
685  * GCC 7.1 issues this warning at the point of the function definition header
686  * (not in any actual code), and I can't figure out what's triggering it since
687  * the comparison form doesn't appear anywhere in this code.  Since this is
688  * rarely-used portability code, suppress the warning.
689  */
690 #pragma GCC diagnostic ignored "-Wstrict-overflow"
691
692 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
693                   LDOUBLE fvalue, int min, int max, int flags)
694 {
695   char signvalue = 0;
696   LDOUBLE ufvalue;
697   char iconvert[24];
698   char fconvert[24];
699   size_t iplace = 0;
700   size_t fplace = 0;
701   long padlen = 0; /* amount to pad */
702   long zpadlen = 0; 
703   int total = 0;
704   LLONG intpart;
705   LLONG fracpart;
706   LLONG mask10;
707   int leadingfrac0s = 0; /* zeroes at the start of fractional part */
708   int omitzeros = 0;
709   size_t omitcount = 0;
710   
711   /* 
712    * AIX manpage says the default is 0, but Solaris says the default
713    * is 6, and sprintf on AIX defaults to 6
714    */
715   if (max < 0)
716     max = 6;
717
718   ufvalue = abs_val (fvalue);
719
720   if (fvalue < 0)
721     signvalue = '-';
722   else
723     if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
724       signvalue = '+';
725     else
726       if (flags & DP_F_SPACE)
727         signvalue = ' ';
728
729 #if 0
730   if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
731 #endif
732
733   intpart = (LLONG) ufvalue;
734
735   /* With %g precision is the number of significant digits, which
736      includes the digits in intpart. */
737   if (flags & DP_F_FP_G)
738     {
739       if (intpart != 0)
740         {
741           /* For each digit of INTPART, print one less fractional digit. */
742           LLONG temp;
743           for (temp = intpart; temp != 0; temp /= 10)
744             --max;
745           if (max < 0)
746             max = 0;
747         }
748       else
749         {
750           /* For each leading 0 in fractional part, print one more
751              fractional digit. */
752           LDOUBLE temp;
753           if (ufvalue > 0)
754             for (temp = ufvalue; temp < (LDOUBLE) 0.1; temp *= 10)
755               ++max;
756         }
757     }
758
759   /* C99: trailing zeros are removed from the fractional portion of the
760      result unless the # flag is specified */
761   if ((flags & DP_F_FP_G) && !(flags & DP_F_NUM))
762     omitzeros = 1;
763
764 #if SIZEOF_LONG_LONG > 0
765 # define MAX_DIGITS 18          /* grok more digits with long long */
766 #else
767 # define MAX_DIGITS 9           /* just long */
768 #endif
769
770   /* 
771    * Sorry, we only support several digits past the decimal because of
772    * our conversion method
773    */
774   if (max > MAX_DIGITS)
775     max = MAX_DIGITS;
776
777   /* Factor of 10 with the needed number of digits, e.g. 1000 for max==3 */
778   mask10 = pow10_int (max);
779
780   /* We "cheat" by converting the fractional part to integer by
781    * multiplying by a factor of 10
782    */
783   fracpart = round_int (mask10 * (ufvalue - intpart));
784
785   if (fracpart >= mask10)
786   {
787     intpart++;
788     fracpart -= mask10;
789   }
790   else if (fracpart != 0)
791     /* If fracpart has less digits than the 10* mask, we need to
792        manually insert leading 0s.  For example 2.01's fractional part
793        requires one leading zero to distinguish it from 2.1. */
794     while (fracpart < mask10 / 10)
795       {
796         ++leadingfrac0s;
797         mask10 /= 10;
798       }
799
800 #ifdef DEBUG_SNPRINTF
801 # ifdef HAVE_LONG_LONG_INT
802   debug ("fmtfp: %Lf =? %lld.%lld\n", fvalue, intpart, fracpart);
803 # else
804   debug ("fmtfp: %Lf =? %ld.%ld\n", fvalue, intpart, fracpart);
805 # endif
806 #endif
807
808   /* Convert integer part */
809   do {
810     iconvert[iplace++] = (char) ('0' + (intpart % 10));
811     intpart = (intpart / 10);
812   } while(intpart && (iplace < sizeof(iconvert)));
813   if (iplace == sizeof(iconvert)) iplace--;
814   iconvert[iplace] = 0;
815
816   /* Convert fractional part */
817   do {
818     fconvert[fplace++] = (char) ('0' + (fracpart % 10));
819     fracpart = (fracpart / 10);
820   } while(fracpart && (fplace < sizeof(fconvert)));
821   while (leadingfrac0s-- > 0 && fplace < sizeof(fconvert))
822     fconvert[fplace++] = '0';
823   if (fplace == sizeof(fconvert)) fplace--;
824   fconvert[fplace] = 0;
825   if (omitzeros)
826     while (omitcount < fplace && fconvert[omitcount] == '0')
827       ++omitcount;
828
829   /* -1 for decimal point, another -1 if we are printing a sign */
830   padlen = min - iplace - (max - omitcount) - 1 - ((signvalue) ? 1 : 0);
831   if (!omitzeros)
832     zpadlen = max - fplace;
833   if (zpadlen < 0)
834     zpadlen = 0;
835   if (padlen < 0) 
836     padlen = 0;
837   if (flags & DP_F_MINUS) 
838     padlen = -padlen; /* Left Justifty */
839
840   if ((flags & DP_F_ZERO) && (padlen > 0)) 
841   {
842     if (signvalue) 
843     {
844       total += dopr_outch (buffer, currlen, maxlen, signvalue);
845       --padlen;
846       signvalue = 0;
847     }
848     while (padlen > 0)
849     {
850       total += dopr_outch (buffer, currlen, maxlen, '0');
851       --padlen;
852     }
853   }
854   while (padlen > 0)
855   {
856     total += dopr_outch (buffer, currlen, maxlen, ' ');
857     --padlen;
858   }
859   if (signvalue) 
860     total += dopr_outch (buffer, currlen, maxlen, signvalue);
861
862   while (iplace > 0) 
863     total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
864
865   /*
866    * Decimal point.  This should probably use locale to find the correct
867    * char to print out.
868    */
869   if (max > 0 && (fplace > omitcount || zpadlen > 0))
870   {
871     total += dopr_outch (buffer, currlen, maxlen, '.');
872
873     while (fplace > omitcount) 
874       total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
875   }
876
877   while (zpadlen > 0)
878   {
879     total += dopr_outch (buffer, currlen, maxlen, '0');
880     --zpadlen;
881   }
882
883   while (padlen < 0) 
884   {
885     total += dopr_outch (buffer, currlen, maxlen, ' ');
886     ++padlen;
887   }
888
889   return total;
890 }
891
892 static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
893 {
894   if (*currlen + 1 < maxlen)
895     buffer[(*currlen)++] = c;
896   return 1;
897 }
898
899 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
900 {
901   if (str != NULL)
902     str[0] = 0;
903   return dopr(str, count, fmt, args);
904 }
905
906 int snprintf (char *str, size_t count, const char *fmt,...)
907 {
908   va_list ap;
909   int total;
910     
911   va_start(ap, fmt);
912   total = vsnprintf(str, count, fmt, ap);
913   va_end(ap);
914   return total;
915 }
916
917 #ifdef TEST_SNPRINTF
918 #ifndef LONG_STRING
919 #define LONG_STRING 1024
920 #endif
921 int main (void)
922 {
923   char buf1[LONG_STRING];
924   char buf2[LONG_STRING];
925   char *fp_fmt[] = {
926     "%-1.5f",
927     "%1.5f",
928     "%123.9f",
929     "%10.5f",
930     "% 10.5f",
931     "%+22.9f",
932     "%+4.9f",
933     "%01.3f",
934     "%4f",
935     "%3.1f",
936     "%3.2f",
937     "%.0f",
938     "%.1f",
939     NULL
940   };
941   double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 
942     0.9996, 1.996, 4.136, 0};
943   char *int_fmt[] = {
944     "%-1.5d",
945     "%1.5d",
946     "%123.9d",
947     "%5.5d",
948     "%10.5d",
949     "% 10.5d",
950     "%+22.33d",
951     "%01.3d",
952     "%4d",
953     NULL
954   };
955   long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
956   int x, y;
957   int fail = 0;
958   int num = 0;
959
960   printf ("Testing snprintf format codes against system sprintf...\n");
961
962   for (x = 0; fp_fmt[x] != NULL ; x++)
963     for (y = 0; fp_nums[y] != 0 ; y++)
964     {
965       snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
966       sprintf (buf2, fp_fmt[x], fp_nums[y]);
967       if (strcmp (buf1, buf2))
968       {
969         printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
970             fp_fmt[x], buf1, buf2);
971         fail++;
972       }
973       num++;
974     }
975
976   for (x = 0; int_fmt[x] != NULL ; x++)
977     for (y = 0; int_nums[y] != 0 ; y++)
978     {
979       snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
980       sprintf (buf2, int_fmt[x], int_nums[y]);
981       if (strcmp (buf1, buf2))
982       {
983         printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
984             int_fmt[x], buf1, buf2);
985         fail++;
986       }
987       num++;
988     }
989   printf ("%d tests failed out of %d.\n", fail, num);
990   return 0;
991 }
992 #endif /* TEST_SNPRINTF */