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