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