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