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