]> eyrie.org Git - kerberos/krb5-strength.git/blob - portable/snprintf.c
Change CrackLib tests for system CrackLib
[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  * Copyright Patrick Powell 1995
30  * This code is based on code written by Patrick Powell (papowell@astart.com)
31  * It may be used for any purpose as long as this notice remains intact
32  * on all source code distributions
33  */
34
35 /**************************************************************
36  * Original:
37  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
38  * A bombproof version of doprnt (dopr) included.
39  * Sigh.  This sort of thing is always nasty do deal with.  Note that
40  * the version here does not include floating point...
41  *
42  * snprintf() is used instead of sprintf() as it does limit checks
43  * for string length.  This covers a nasty loophole.
44  *
45  * The other functions are there to prevent NULL pointers from
46  * causing nast effects.
47  *
48  * More Recently:
49  *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
50  *  This was ugly.  It is still ugly.  I opted out of floating point
51  *  numbers, but the formatter understands just about everything
52  *  from the normal C string format, at least as far as I can tell from
53  *  the Solaris 2.5 printf(3S) man page.
54  *
55  *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
56  *    Ok, added some minimal floating point support, which means this
57  *    probably requires libm on most operating systems.  Don't yet
58  *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
59  *    was pretty badly broken, it just wasn't being exercised in ways
60  *    which showed it, so that's been fixed.  Also, formated the code
61  *    to mutt conventions, and removed dead code left over from the
62  *    original.  Also, there is now a builtin-test, just compile with:
63  *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
64  *    and run snprintf for results.
65  * 
66  *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
67  *    The PGP code was using unsigned hexadecimal formats. 
68  *    Unfortunately, unsigned formats simply didn't work.
69  *
70  *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
71  *    The original code assumed that both snprintf() and vsnprintf() were
72  *    missing.  Some systems only have snprintf() but not vsnprintf(), so
73  *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
74  *
75  *  Andrew Tridgell (tridge@samba.org) Oct 1998
76  *    fixed handling of %.0f
77  *    added test for HAVE_LONG_DOUBLE
78  *
79  *  Russ Allbery <eagle@eyrie.org> 2000-08-26
80  *    fixed return value to comply with C99
81  *    fixed handling of snprintf(NULL, ...)
82  *    added explicit casts for double to long long int conversion
83  *
84  *  Hrvoje Niksic <hniksic@arsdigita.com> 2000-11-04
85  *    include <stdio.h> for NULL.
86  *    added support for long long.
87  *    don't declare argument types to (v)snprintf if stdarg is not used.
88  *
89  *  Hrvoje Niksic <hniksic@xemacs.org> 2005-04-15
90  *    use the PARAMS macro to handle prototypes.
91  *    write function definitions in the ansi2knr-friendly way.
92  *    if string precision is specified, don't read VALUE past it.
93  *    fix bug in fmtfp that caused 0.01 to be printed as 0.1.
94  *    don't include <ctype.h> because none of it is used.
95  *    interpret precision as number of significant digits with %g
96  *    omit trailing decimal zeros with %g
97  *
98  **************************************************************/
99
100 #include <config.h>
101
102 #include <string.h>
103 #include <ctype.h>
104 #include <sys/types.h>
105
106 #ifndef NULL
107 # define NULL 0
108 #endif
109
110 /* varargs declarations: */
111
112 #include <stdarg.h>
113 #define HAVE_STDARGS    /* let's hope that works everywhere (mj) */
114 #define VA_LOCAL_DECL   va_list ap
115 #define VA_START(f)     va_start(ap, f)
116 #define VA_SHIFT(v,t)  ;   /* no-op for ANSI */
117 #define VA_END          va_end(ap)
118
119 /* Assume all compilers support long double, per Autoconf documentation. */
120 #define LDOUBLE long double
121
122 #ifdef HAVE_LONG_LONG_INT
123 # define LLONG long long
124 #else
125 # define LLONG long
126 #endif
127
128 int snprintf (char *str, size_t count, const char *fmt, ...);
129 int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
130
131 static int dopr (char *buffer, size_t maxlen, const char *format, 
132                  va_list args);
133 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
134                    const char *value, int flags, int min, int max);
135 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
136                    LLONG value, int base, int min, int max, int flags);
137 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
138                   LDOUBLE fvalue, int min, int max, int flags);
139 static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
140
141 /*
142  * dopr(): poor man's version of doprintf
143  */
144
145 /* format read states */
146 #define DP_S_DEFAULT 0
147 #define DP_S_FLAGS   1
148 #define DP_S_MIN     2
149 #define DP_S_DOT     3
150 #define DP_S_MAX     4
151 #define DP_S_MOD     5
152 #define DP_S_MOD_L   6
153 #define DP_S_CONV    7
154 #define DP_S_DONE    8
155
156 /* format flags - Bits */
157 #define DP_F_MINUS      (1 << 0)
158 #define DP_F_PLUS       (1 << 1)
159 #define DP_F_SPACE      (1 << 2)
160 #define DP_F_NUM        (1 << 3)
161 #define DP_F_ZERO       (1 << 4)
162 #define DP_F_UP         (1 << 5)
163 #define DP_F_UNSIGNED   (1 << 6)
164 #define DP_F_FP_G       (1 << 7)
165
166 /* Conversion Flags */
167 #define DP_C_SHORT   1
168 #define DP_C_LONG    2
169 #define DP_C_LLONG   3
170 #define DP_C_LDOUBLE 4
171
172 #define char_to_int(p) (p - '0')
173 #define MAX(p,q) ((p >= q) ? p : q)
174 #define MIN(p,q) ((p <= q) ? p : q)
175
176 static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
177 {
178   char ch;
179   LLONG value;
180   LDOUBLE fvalue;
181   char *strvalue;
182   int min;
183   int max;
184   unsigned int state;
185   int flags;
186   int cflags;
187   int total;
188   size_t currlen;
189   
190   state = DP_S_DEFAULT;
191   currlen = flags = cflags = min = 0;
192   max = -1;
193   ch = *format++;
194   total = 0;
195
196   while (state != DP_S_DONE)
197   {
198     if (ch == '\0')
199       state = DP_S_DONE;
200
201     switch(state) 
202     {
203     case DP_S_DEFAULT:
204       if (ch == '%') 
205         state = DP_S_FLAGS;
206       else 
207         total += dopr_outch (buffer, &currlen, maxlen, ch);
208       ch = *format++;
209       break;
210     case DP_S_FLAGS:
211       switch (ch) 
212       {
213       case '-':
214         flags |= DP_F_MINUS;
215         ch = *format++;
216         break;
217       case '+':
218         flags |= DP_F_PLUS;
219         ch = *format++;
220         break;
221       case ' ':
222         flags |= DP_F_SPACE;
223         ch = *format++;
224         break;
225       case '#':
226         flags |= DP_F_NUM;
227         ch = *format++;
228         break;
229       case '0':
230         flags |= DP_F_ZERO;
231         ch = *format++;
232         break;
233       default:
234         state = DP_S_MIN;
235         break;
236       }
237       break;
238     case DP_S_MIN:
239       if ('0' <= ch && ch <= '9') 
240       {
241         min = 10*min + char_to_int (ch);
242         ch = *format++;
243       } 
244       else if (ch == '*') 
245       {
246         min = va_arg (args, int);
247         ch = *format++;
248         state = DP_S_DOT;
249       } 
250       else 
251         state = DP_S_DOT;
252       break;
253     case DP_S_DOT:
254       if (ch == '.') 
255       {
256         state = DP_S_MAX;
257         ch = *format++;
258       } 
259       else 
260         state = DP_S_MOD;
261       break;
262     case DP_S_MAX:
263       if ('0' <= ch && ch <= '9')
264       {
265         if (max < 0)
266           max = 0;
267         max = 10*max + char_to_int (ch);
268         ch = *format++;
269       } 
270       else if (ch == '*') 
271       {
272         max = va_arg (args, int);
273         ch = *format++;
274         state = DP_S_MOD;
275       } 
276       else 
277         state = DP_S_MOD;
278       break;
279     case DP_S_MOD:
280       switch (ch) 
281       {
282       case 'h':
283         cflags = DP_C_SHORT;
284         ch = *format++;
285         break;
286       case 'l':
287         cflags = DP_C_LONG;
288         ch = *format++;
289         break;
290       case 'L':
291         cflags = DP_C_LDOUBLE;
292         ch = *format++;
293         break;
294       default:
295         break;
296       }
297       if (cflags != DP_C_LONG)
298         state = DP_S_CONV;
299       else
300         state = DP_S_MOD_L;
301       break;
302     case DP_S_MOD_L:
303       switch (ch)
304       {
305       case 'l':
306         cflags = DP_C_LLONG;
307         ch = *format++;
308         break;
309       default:
310         break;
311       }
312       state = DP_S_CONV;
313       break;
314     case DP_S_CONV:
315       switch (ch) 
316       {
317       case 'd':
318       case 'i':
319         if (cflags == DP_C_SHORT) 
320           value = (short int) va_arg (args, int);
321         else if (cflags == DP_C_LONG)
322           value = va_arg (args, long int);
323         else if (cflags == DP_C_LLONG)
324           value = va_arg (args, LLONG);
325         else
326           value = va_arg (args, int);
327         total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
328         break;
329       case 'o':
330         flags |= DP_F_UNSIGNED;
331         if (cflags == DP_C_SHORT)
332           value = (unsigned short int) va_arg (args, unsigned int);
333         else if (cflags == DP_C_LONG)
334           value = va_arg (args, unsigned long int);
335         else if (cflags == DP_C_LLONG)
336           value = va_arg (args, unsigned LLONG);
337         else
338           value = va_arg (args, unsigned int);
339         total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
340         break;
341       case 'u':
342         flags |= DP_F_UNSIGNED;
343         if (cflags == DP_C_SHORT)
344           value = (unsigned short int) va_arg (args, unsigned int);
345         else if (cflags == DP_C_LONG)
346           value = va_arg (args, unsigned long int);
347         else if (cflags == DP_C_LLONG)
348           value = va_arg (args, unsigned LLONG);
349         else
350           value = va_arg (args, unsigned int);
351         total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
352         break;
353       case 'X':
354         flags |= DP_F_UP;
355       case 'x':
356         flags |= DP_F_UNSIGNED;
357         if (cflags == DP_C_SHORT)
358           value = (unsigned short int) va_arg (args, unsigned int);
359         else if (cflags == DP_C_LONG)
360           value = va_arg (args, unsigned long int);
361         else if (cflags == DP_C_LLONG)
362           value = va_arg (args, unsigned LLONG);
363         else
364           value = va_arg (args, unsigned int);
365         total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
366         break;
367       case 'f':
368         if (cflags == DP_C_LDOUBLE)
369           fvalue = va_arg (args, LDOUBLE);
370         else
371           fvalue = va_arg (args, double);
372         total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
373         break;
374       case 'E':
375         flags |= DP_F_UP;
376       case 'e':
377         if (cflags == DP_C_LDOUBLE)
378           fvalue = va_arg (args, LDOUBLE);
379         else
380           fvalue = va_arg (args, double);
381         total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
382         break;
383       case 'G':
384         flags |= DP_F_UP;
385       case 'g':
386         flags |= DP_F_FP_G;
387         if (cflags == DP_C_LDOUBLE)
388           fvalue = va_arg (args, LDOUBLE);
389         else
390           fvalue = va_arg (args, double);
391         if (max == 0)
392           /* C99 says: if precision [for %g] is zero, it is taken as one */
393           max = 1;
394         total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
395         break;
396       case 'c':
397         total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
398         break;
399       case 's':
400         strvalue = va_arg (args, char *);
401         total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
402         break;
403       case 'p':
404         strvalue = va_arg (args, void *);
405         total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
406                          max, flags);
407         break;
408       case 'n':
409         if (cflags == DP_C_SHORT) 
410         {
411           short int *num;
412           num = va_arg (args, short int *);
413           *num = currlen;
414         } 
415         else if (cflags == DP_C_LONG) 
416         {
417           long int *num;
418           num = va_arg (args, long int *);
419           *num = currlen;
420         }
421         else if (cflags == DP_C_LLONG) 
422         {
423           LLONG *num;
424           num = va_arg (args, LLONG *);
425           *num = currlen;
426         } 
427         else 
428         {
429           int *num;
430           num = va_arg (args, int *);
431           *num = currlen;
432         }
433         break;
434       case '%':
435         total += dopr_outch (buffer, &currlen, maxlen, ch);
436         break;
437       case 'w':
438         /* not supported yet, treat as next char */
439         format++;
440         break;
441       default:
442         /* Unknown, skip */
443         break;
444       }
445       ch = *format++;
446       state = DP_S_DEFAULT;
447       flags = cflags = min = 0;
448       max = -1;
449       break;
450     case DP_S_DONE:
451       break;
452     default:
453       /* hmm? */
454       break; /* some picky compilers need this */
455     }
456   }
457   if (buffer != NULL)
458   {
459     if (currlen < maxlen - 1) 
460       buffer[currlen] = '\0';
461     else 
462       buffer[maxlen - 1] = '\0';
463   }
464   return total;
465 }
466
467 static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
468                    const char *value, int flags, int min, int max)
469 {
470   int padlen, strln;     /* amount to pad */
471   int cnt = 0;
472   int total = 0;
473   
474   if (value == 0)
475   {
476     value = "(null)";
477   }
478
479   if (max < 0)
480     strln = strlen (value);
481   else
482     /* When precision is specified, don't read VALUE past precision. */
483     /*strln = strnlen (value, max);*/
484     for (strln = 0; strln < max && value[strln]; ++strln);
485   padlen = min - strln;
486   if (padlen < 0) 
487     padlen = 0;
488   if (flags & DP_F_MINUS) 
489     padlen = -padlen; /* Left Justify */
490
491   while (padlen > 0)
492   {
493     total += dopr_outch (buffer, currlen, maxlen, ' ');
494     --padlen;
495   }
496   while (*value && ((max < 0) || (cnt < max)))
497   {
498     total += dopr_outch (buffer, currlen, maxlen, *value++);
499     ++cnt;
500   }
501   while (padlen < 0)
502   {
503     total += dopr_outch (buffer, currlen, maxlen, ' ');
504     ++padlen;
505   }
506   return total;
507 }
508
509 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
510
511 static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
512                    LLONG value, int base, int min, int max, int flags)
513 {
514   int signvalue = 0;
515   unsigned LLONG uvalue;
516   char convert[24];
517   unsigned int place = 0;
518   int spadlen = 0; /* amount to space pad */
519   int zpadlen = 0; /* amount to zero pad */
520   const char *digits;
521   int total = 0;
522   
523   if (max < 0)
524     max = 0;
525
526   uvalue = value;
527
528   if(!(flags & DP_F_UNSIGNED))
529   {
530     if( value < 0 ) {
531       signvalue = '-';
532       uvalue = -value;
533     }
534     else
535       if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
536         signvalue = '+';
537     else
538       if (flags & DP_F_SPACE)
539         signvalue = ' ';
540   }
541   
542   if (flags & DP_F_UP)
543     /* Should characters be upper case? */
544     digits = "0123456789ABCDEF";
545   else
546     digits = "0123456789abcdef";
547
548   do {
549     convert[place++] = digits[uvalue % (unsigned)base];
550     uvalue = (uvalue / (unsigned)base );
551   } while(uvalue && (place < sizeof (convert)));
552   if (place == sizeof (convert)) place--;
553   convert[place] = 0;
554
555   zpadlen = max - place;
556   spadlen = min - MAX ((unsigned int)max, place) - (signvalue ? 1 : 0);
557   if (zpadlen < 0) zpadlen = 0;
558   if (spadlen < 0) spadlen = 0;
559   if (flags & DP_F_ZERO)
560   {
561     zpadlen = MAX(zpadlen, spadlen);
562     spadlen = 0;
563   }
564   if (flags & DP_F_MINUS) 
565     spadlen = -spadlen; /* Left Justifty */
566
567 #ifdef DEBUG_SNPRINTF
568   dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
569       zpadlen, spadlen, min, max, place));
570 #endif
571
572   /* Spaces */
573   while (spadlen > 0) 
574   {
575     total += dopr_outch (buffer, currlen, maxlen, ' ');
576     --spadlen;
577   }
578
579   /* Sign */
580   if (signvalue) 
581     total += dopr_outch (buffer, currlen, maxlen, signvalue);
582
583   /* Zeros */
584   if (zpadlen > 0) 
585   {
586     while (zpadlen > 0)
587     {
588       total += dopr_outch (buffer, currlen, maxlen, '0');
589       --zpadlen;
590     }
591   }
592
593   /* Digits */
594   while (place > 0) 
595     total += dopr_outch (buffer, currlen, maxlen, convert[--place]);
596   
597   /* Left Justified spaces */
598   while (spadlen < 0) {
599     total += dopr_outch (buffer, currlen, maxlen, ' ');
600     ++spadlen;
601   }
602
603   return total;
604 }
605
606 static LDOUBLE abs_val (LDOUBLE value)
607 {
608   LDOUBLE result = value;
609
610   if (value < 0)
611     result = -value;
612
613   return result;
614 }
615
616 static LLONG pow10_int (int exp)
617 {
618   LDOUBLE result = 1;
619
620   while (exp)
621   {
622     result *= 10;
623     exp--;
624   }
625   
626   return (LLONG) result;
627 }
628
629 static LLONG round_int (LDOUBLE value)
630 {
631   LLONG intpart;
632
633   intpart = (LLONG) value;
634   value = value - intpart;
635   if (value >= 0.5)
636     intpart++;
637
638   return intpart;
639 }
640
641 static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
642                   LDOUBLE fvalue, int min, int max, int flags)
643 {
644   int signvalue = 0;
645   LDOUBLE ufvalue;
646   char iconvert[24];
647   char fconvert[24];
648   size_t iplace = 0;
649   size_t fplace = 0;
650   int padlen = 0; /* amount to pad */
651   int zpadlen = 0; 
652   int total = 0;
653   LLONG intpart;
654   LLONG fracpart;
655   LLONG mask10;
656   int leadingfrac0s = 0; /* zeroes at the start of fractional part */
657   int omitzeros = 0;
658   size_t omitcount = 0;
659   
660   /* 
661    * AIX manpage says the default is 0, but Solaris says the default
662    * is 6, and sprintf on AIX defaults to 6
663    */
664   if (max < 0)
665     max = 6;
666
667   ufvalue = abs_val (fvalue);
668
669   if (fvalue < 0)
670     signvalue = '-';
671   else
672     if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
673       signvalue = '+';
674     else
675       if (flags & DP_F_SPACE)
676         signvalue = ' ';
677
678 #if 0
679   if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
680 #endif
681
682   intpart = (LLONG) ufvalue;
683
684   /* With %g precision is the number of significant digits, which
685      includes the digits in intpart. */
686   if (flags & DP_F_FP_G)
687     {
688       if (intpart != 0)
689         {
690           /* For each digit of INTPART, print one less fractional digit. */
691           LLONG temp = intpart;
692           for (temp = intpart; temp != 0; temp /= 10)
693             --max;
694           if (max < 0)
695             max = 0;
696         }
697       else
698         {
699           /* For each leading 0 in fractional part, print one more
700              fractional digit. */
701           LDOUBLE temp;
702           if (ufvalue > 0)
703             for (temp = ufvalue; temp < 0.1; temp *= 10)
704               ++max;
705         }
706     }
707
708   /* C99: trailing zeros are removed from the fractional portion of the
709      result unless the # flag is specified */
710   if ((flags & DP_F_FP_G) && !(flags & DP_F_NUM))
711     omitzeros = 1;
712
713 #if SIZEOF_LONG_LONG > 0
714 # define MAX_DIGITS 18          /* grok more digits with long long */
715 #else
716 # define MAX_DIGITS 9           /* just long */
717 #endif
718
719   /* 
720    * Sorry, we only support several digits past the decimal because of
721    * our conversion method
722    */
723   if (max > MAX_DIGITS)
724     max = MAX_DIGITS;
725
726   /* Factor of 10 with the needed number of digits, e.g. 1000 for max==3 */
727   mask10 = pow10_int (max);
728
729   /* We "cheat" by converting the fractional part to integer by
730    * multiplying by a factor of 10
731    */
732   fracpart = round_int (mask10 * (ufvalue - intpart));
733
734   if (fracpart >= mask10)
735   {
736     intpart++;
737     fracpart -= mask10;
738   }
739   else if (fracpart != 0)
740     /* If fracpart has less digits than the 10* mask, we need to
741        manually insert leading 0s.  For example 2.01's fractional part
742        requires one leading zero to distinguish it from 2.1. */
743     while (fracpart < mask10 / 10)
744       {
745         ++leadingfrac0s;
746         mask10 /= 10;
747       }
748
749 #ifdef DEBUG_SNPRINTF
750   dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
751 #endif
752
753   /* Convert integer part */
754   do {
755     iconvert[iplace++] = '0' + intpart % 10;
756     intpart = (intpart / 10);
757   } while(intpart && (iplace < sizeof(iconvert)));
758   if (iplace == sizeof(iconvert)) iplace--;
759   iconvert[iplace] = 0;
760
761   /* Convert fractional part */
762   do {
763     fconvert[fplace++] = '0' + fracpart % 10;
764     fracpart = (fracpart / 10);
765   } while(fracpart && (fplace < sizeof(fconvert)));
766   while (leadingfrac0s-- > 0 && fplace < sizeof(fconvert))
767     fconvert[fplace++] = '0';
768   if (fplace == sizeof(fconvert)) fplace--;
769   fconvert[fplace] = 0;
770   if (omitzeros)
771     while (omitcount < fplace && fconvert[omitcount] == '0')
772       ++omitcount;
773
774   /* -1 for decimal point, another -1 if we are printing a sign */
775   padlen = min - iplace - (max - omitcount) - 1 - ((signvalue) ? 1 : 0);
776   if (!omitzeros)
777     zpadlen = max - fplace;
778   if (zpadlen < 0)
779     zpadlen = 0;
780   if (padlen < 0) 
781     padlen = 0;
782   if (flags & DP_F_MINUS) 
783     padlen = -padlen; /* Left Justifty */
784
785   if ((flags & DP_F_ZERO) && (padlen > 0)) 
786   {
787     if (signvalue) 
788     {
789       total += dopr_outch (buffer, currlen, maxlen, signvalue);
790       --padlen;
791       signvalue = 0;
792     }
793     while (padlen > 0)
794     {
795       total += dopr_outch (buffer, currlen, maxlen, '0');
796       --padlen;
797     }
798   }
799   while (padlen > 0)
800   {
801     total += dopr_outch (buffer, currlen, maxlen, ' ');
802     --padlen;
803   }
804   if (signvalue) 
805     total += dopr_outch (buffer, currlen, maxlen, signvalue);
806
807   while (iplace > 0) 
808     total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
809
810   /*
811    * Decimal point.  This should probably use locale to find the correct
812    * char to print out.
813    */
814   if (max > 0 && (fplace > omitcount || zpadlen > 0))
815   {
816     total += dopr_outch (buffer, currlen, maxlen, '.');
817
818     while (fplace > omitcount) 
819       total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
820   }
821
822   while (zpadlen > 0)
823   {
824     total += dopr_outch (buffer, currlen, maxlen, '0');
825     --zpadlen;
826   }
827
828   while (padlen < 0) 
829   {
830     total += dopr_outch (buffer, currlen, maxlen, ' ');
831     ++padlen;
832   }
833
834   return total;
835 }
836
837 static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
838 {
839   if (*currlen + 1 < maxlen)
840     buffer[(*currlen)++] = c;
841   return 1;
842 }
843
844 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
845 {
846   if (str != NULL)
847     str[0] = 0;
848   return dopr(str, count, fmt, args);
849 }
850
851 /* VARARGS3 */
852 #ifdef HAVE_STDARGS
853 int snprintf (char *str,size_t count,const char *fmt,...)
854 #else
855 int snprintf (va_alist) va_dcl
856 #endif
857 {
858 #ifndef HAVE_STDARGS
859   char *str;
860   size_t count;
861   char *fmt;
862 #endif
863   VA_LOCAL_DECL;
864   int total;
865     
866   VA_START (fmt);
867   VA_SHIFT (str, char *);
868   VA_SHIFT (count, size_t );
869   VA_SHIFT (fmt, char *);
870   total = vsnprintf(str, count, fmt, ap);
871   VA_END;
872   return total;
873 }
874
875 #ifdef TEST_SNPRINTF
876 #ifndef LONG_STRING
877 #define LONG_STRING 1024
878 #endif
879 int main (void)
880 {
881   char buf1[LONG_STRING];
882   char buf2[LONG_STRING];
883   char *fp_fmt[] = {
884     "%-1.5f",
885     "%1.5f",
886     "%123.9f",
887     "%10.5f",
888     "% 10.5f",
889     "%+22.9f",
890     "%+4.9f",
891     "%01.3f",
892     "%4f",
893     "%3.1f",
894     "%3.2f",
895     "%.0f",
896     "%.1f",
897     NULL
898   };
899   double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 
900     0.9996, 1.996, 4.136, 0};
901   char *int_fmt[] = {
902     "%-1.5d",
903     "%1.5d",
904     "%123.9d",
905     "%5.5d",
906     "%10.5d",
907     "% 10.5d",
908     "%+22.33d",
909     "%01.3d",
910     "%4d",
911     NULL
912   };
913   long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
914   int x, y;
915   int fail = 0;
916   int num = 0;
917
918   printf ("Testing snprintf format codes against system sprintf...\n");
919
920   for (x = 0; fp_fmt[x] != NULL ; x++)
921     for (y = 0; fp_nums[y] != 0 ; y++)
922     {
923       snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
924       sprintf (buf2, fp_fmt[x], fp_nums[y]);
925       if (strcmp (buf1, buf2))
926       {
927         printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
928             fp_fmt[x], buf1, buf2);
929         fail++;
930       }
931       num++;
932     }
933
934   for (x = 0; int_fmt[x] != NULL ; x++)
935     for (y = 0; int_nums[y] != 0 ; y++)
936     {
937       snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
938       sprintf (buf2, int_fmt[x], int_nums[y]);
939       if (strcmp (buf1, buf2))
940       {
941         printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
942             int_fmt[x], buf1, buf2);
943         fail++;
944       }
945       num++;
946     }
947   printf ("%d tests failed out of %d.\n", fail, num);
948 }
949 #endif /* SNPRINTF_TEST */