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