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