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