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