]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bsnprintf.c
Fix for TLS bugs #1568 and #1599
[bacula/bacula] / bacula / src / lib / bsnprintf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2005-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Copyright Patrick Powell 1995
30  *
31  * This code is based on code written by Patrick Powell
32  * (papowell@astart.com) It may be used for any purpose as long
33  * as this notice remains intact on all source code distributions.
34  *
35  * Adapted for Bacula -- note there were lots of bugs in
36  *     the original code: %lld and %s were seriously broken, and
37  *     with FP turned off %f seg faulted.
38  *
39  *   Kern Sibbald, November MMV
40  *
41  */
42
43
44
45 #include "bacula.h"
46 #include <wchar.h>
47
48 #define FP_OUTPUT 1 /* Bacula uses floating point */
49
50 /* Define the following if you want all the features of
51  *  normal printf, but with all the security problems.
52  *  For Bacula we turn this off, and it silently ignores
53  *  formats that could pose a security problem.
54  */
55 #undef SECURITY_PROBLEM 
56
57 #ifdef USE_BSNPRINTF
58
59 #ifdef HAVE_LONG_DOUBLE
60 #define LDOUBLE long double
61 #else
62 #define LDOUBLE double
63 #endif
64
65 int bvsnprintf(char *buffer, int32_t maxlen, const char *format, va_list args);
66 static int32_t fmtstr(char *buffer, int32_t currlen, int32_t maxlen,
67                    const char *value, int flags, int min, int max);
68 static int32_t fmtwstr(char *buffer, int32_t currlen, int32_t maxlen,
69                    const wchar_t *value, int flags, int min, int max);
70 static int32_t fmtint(char *buffer, int32_t currlen, int32_t maxlen,
71                    int64_t value, int base, int min, int max, int flags);
72
73 #ifdef FP_OUTPUT
74 # ifdef HAVE_FCVTL
75 #  define fcvt fcvtl
76 # endif
77 static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
78                   LDOUBLE fvalue, int min, int max, int flags);
79 #else
80 #define fmtfp(b, c, m, f, min, max, fl) currlen
81 #endif
82
83 /*
84  *  NOTE!!!! do not use this #define with a construct such
85  *    as outch(--place);.  It just will NOT work, because the
86  *    decrement of place is done ONLY if there is room in the 
87  *    output buffer.
88  */
89 #define outch(c) {int len=currlen; if (currlen < maxlen) \
90         { buffer[len] = (c); currlen++; }}
91
92 /* format read states */
93 #define DP_S_DEFAULT 0
94 #define DP_S_FLAGS   1
95 #define DP_S_MIN     2
96 #define DP_S_DOT     3
97 #define DP_S_MAX     4
98 #define DP_S_MOD     5
99 #define DP_S_CONV    6
100 #define DP_S_DONE    7
101
102 /* format flags - Bits */
103 #define DP_F_MINUS      (1 << 0)
104 #define DP_F_PLUS       (1 << 1)
105 #define DP_F_SPACE      (1 << 2)
106 #define DP_F_NUM        (1 << 3)
107 #define DP_F_ZERO       (1 << 4)
108 #define DP_F_UP         (1 << 5)
109 #define DP_F_UNSIGNED   (1 << 6)
110 #define DP_F_DOT        (1 << 7)
111
112 /* Conversion Flags */
113 #define DP_C_INT16    1
114 #define DP_C_INT32    2
115 #define DP_C_LDOUBLE  3
116 #define DP_C_INT64    4
117 #define DP_C_WCHAR    5      /* wide characters */
118 #define DP_C_SIZE_T   6
119
120 #define char_to_int(p) ((p)- '0')
121 #undef MAX
122 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
123
124 /*
125   You might ask why does Bacula have it's own printf routine? Well,
126   There are two reasons: 1. Here (as opposed to library routines), we
127   define %d and %ld to be 32 bit; %lld and %q to be 64 bit.  2. We 
128   disable %n for security reasons.                
129  */
130
131 int bsnprintf(char *str, int32_t size, const char *fmt,  ...)
132 {
133    va_list   arg_ptr;
134    int len;
135
136    va_start(arg_ptr, fmt);
137    len = bvsnprintf(str, size, fmt, arg_ptr);
138    va_end(arg_ptr);
139    return len;
140 }
141
142
143 int bvsnprintf(char *buffer, int32_t maxlen, const char *format, va_list args)
144 {
145    char ch;
146    int64_t value;
147    char *strvalue;
148    wchar_t *wstrvalue;
149    int min;
150    int max;
151    int state;
152    int flags;
153    int cflags;
154    int32_t currlen;
155    int base;
156 #ifdef FP_OUTPUT
157    LDOUBLE fvalue;
158 #endif
159
160    state = DP_S_DEFAULT;
161    currlen = flags = cflags = min = 0;
162    max = -1;
163    ch = *format++;
164    *buffer = 0;
165
166    while (state != DP_S_DONE) {
167       if ((ch == '\0') || (currlen >= maxlen)) {
168          state = DP_S_DONE;
169       }
170       switch (state) {
171       case DP_S_DEFAULT:
172          if (ch == '%') {
173             state = DP_S_FLAGS;
174          } else {
175             outch(ch);
176          }
177          ch = *format++;
178          break;
179       case DP_S_FLAGS:
180          switch (ch) {
181          case '-':
182             flags |= DP_F_MINUS;
183             ch = *format++;
184             break;
185          case '+':
186             flags |= DP_F_PLUS;
187             ch = *format++;
188             break;
189          case ' ':
190             flags |= DP_F_SPACE;
191             ch = *format++;
192             break;
193          case '#':
194             flags |= DP_F_NUM;
195             ch = *format++;
196             break;
197          case '0':
198             flags |= DP_F_ZERO;
199             ch = *format++;
200             break;
201          default:
202             state = DP_S_MIN;
203             break;
204          }
205          break;
206       case DP_S_MIN:
207          if (isdigit((unsigned char)ch)) {
208             min = 10 * min + char_to_int(ch);
209             ch = *format++;
210          } else if (ch == '*') {
211             min = va_arg(args, int);
212             ch = *format++;
213             state = DP_S_DOT;
214          } else
215             state = DP_S_DOT;
216          break;
217       case DP_S_DOT:
218          if (ch == '.') {
219             state = DP_S_MAX;
220             flags |= DP_F_DOT;
221             ch = *format++;
222          } else
223             state = DP_S_MOD;
224          break;
225       case DP_S_MAX:
226          if (isdigit((unsigned char)ch)) {
227             if (max < 0)
228                max = 0;
229             max = 10 * max + char_to_int(ch);
230             ch = *format++;
231          } else if (ch == '*') {
232             max = va_arg(args, int);
233             ch = *format++;
234             state = DP_S_MOD;
235          } else
236             state = DP_S_MOD;
237          break;
238       case DP_S_MOD:
239          switch (ch) {
240          case 'h':
241             cflags = DP_C_INT16;
242             ch = *format++;
243             break;
244          case 'l':
245             cflags = DP_C_INT32;
246             ch = *format++;
247             if (ch == 's') {
248                cflags = DP_C_WCHAR;
249             } else if (ch == 'l') {       /* It's a long long */
250                cflags = DP_C_INT64;
251                ch = *format++;
252             }
253             break;
254          case 'z':
255             cflags = DP_C_SIZE_T;
256             ch = *format++;
257             break;
258          case 'L':
259             cflags = DP_C_LDOUBLE;
260             ch = *format++;
261             break;
262          case 'q':                 /* same as long long */
263             cflags = DP_C_INT64;
264             ch = *format++;
265             break;
266          default:
267             break;
268          }
269          state = DP_S_CONV;
270          break;
271       case DP_S_CONV:
272          switch (ch) {
273          case 'd':
274          case 'i':
275             if (cflags == DP_C_INT16) {
276                value = va_arg(args, int32_t);
277             } else if (cflags == DP_C_INT32) {
278                value = va_arg(args, int32_t);
279             } else if (cflags == DP_C_INT64) {
280                value = va_arg(args, int64_t);
281             } else if (cflags == DP_C_SIZE_T) {
282                value = va_arg(args, ssize_t);
283             } else {
284                value = va_arg(args, int);
285             }
286             currlen = fmtint(buffer, currlen, maxlen, value, 10, min, max, flags);
287             break;
288          case 'X':
289          case 'x':
290          case 'o':
291          case 'u':
292             if (ch == 'o') {
293                base = 8;
294             } else if (ch == 'x') {
295                base = 16;
296             } else if (ch == 'X') {
297                base = 16;
298                flags |= DP_F_UP;
299             } else {
300                base = 10;
301             }
302             flags |= DP_F_UNSIGNED;
303             if (cflags == DP_C_INT16) {
304                value = va_arg(args, uint32_t);
305             } else if (cflags == DP_C_INT32) {
306                value = va_arg(args, uint32_t);
307             } else if (cflags == DP_C_INT64) {
308                value = va_arg(args, uint64_t);
309             } else if (cflags == DP_C_SIZE_T) {
310                value = va_arg(args, size_t);
311             } else {
312                value = va_arg(args, unsigned int);
313             }
314             currlen = fmtint(buffer, currlen, maxlen, value, base, min, max, flags);
315             break;
316          case 'f':
317             if (cflags == DP_C_LDOUBLE) {
318                fvalue = va_arg(args, LDOUBLE);
319             } else {
320                fvalue = va_arg(args, double);
321             }
322             currlen = fmtfp(buffer, currlen, maxlen, fvalue, min, max, flags);
323             break;
324          case 'E':
325             flags |= DP_F_UP;
326          case 'e':
327             if (cflags == DP_C_LDOUBLE) {
328                fvalue = va_arg(args, LDOUBLE);
329             } else {
330                fvalue = va_arg(args, double);
331             }
332             currlen = fmtfp(buffer, currlen, maxlen, fvalue, min, max, flags);
333             break;
334          case 'G':
335             flags |= DP_F_UP;
336          case 'g':
337             if (cflags == DP_C_LDOUBLE) {
338                fvalue = va_arg(args, LDOUBLE);
339             } else {
340                fvalue = va_arg(args, double);
341             }
342             currlen = fmtfp(buffer, currlen, maxlen, fvalue, min, max, flags);
343             break;
344          case 'c':
345             ch = va_arg(args, int); 
346             outch(ch);
347             break;
348          case 's':
349             if (cflags != DP_C_WCHAR) {
350               strvalue = va_arg(args, char *);
351               currlen = fmtstr(buffer, currlen, maxlen, strvalue, flags, min, max);
352             } else {
353               /* %ls means to edit wide characters */
354               wstrvalue = va_arg(args, wchar_t *);
355               currlen = fmtwstr(buffer, currlen, maxlen, wstrvalue, flags, min, max);
356             }
357             break;
358          case 'p':
359             flags |= DP_F_UNSIGNED;
360             if (sizeof(char *) == 4) {
361                value = va_arg(args, uint32_t);
362             } else if (sizeof(char *) == 8) {
363                value = va_arg(args, uint64_t);
364             } else {
365                value = 0;             /* we have a problem */
366             }
367             currlen = fmtint(buffer, currlen, maxlen, value, 16, min, max, flags);
368             break;
369          case 'n':
370             if (cflags == DP_C_INT16) {
371                int16_t *num;
372                num = va_arg(args, int16_t *);
373 #ifdef SECURITY_PROBLEM
374                *num = currlen;
375 #endif
376             } else if (cflags == DP_C_INT32) {
377                int32_t *num;
378                num = va_arg(args, int32_t *);
379 #ifdef SECURITY_PROBLEM
380                *num = (int32_t)currlen;
381 #endif
382             } else if (cflags == DP_C_INT64) {
383                int64_t *num;
384                num = va_arg(args, int64_t *);
385 #ifdef SECURITY_PROBLEM
386                *num = (int64_t)currlen;
387 #endif
388             } else {
389                int32_t *num;
390                num = va_arg(args, int32_t *);
391 #ifdef SECURITY_PROBLEM
392                *num = (int32_t)currlen;
393 #endif
394             }
395             break;
396          case '%':
397             outch(ch);
398             break;
399          case 'w':
400             /* not supported yet, treat as next char */
401             ch = *format++;
402             break;
403          default:
404             /* Unknown, skip */
405             break;
406          }
407          ch = *format++;
408          state = DP_S_DEFAULT;
409          flags = cflags = min = 0;
410          max = -1;
411          break;
412       case DP_S_DONE:
413          break;
414       default:
415          /* hmm? */
416          break;                    /* some picky compilers need this */
417       }
418    }
419    if (currlen < maxlen - 1) {
420       buffer[currlen] = '\0';
421    } else {
422       buffer[maxlen - 1] = '\0';
423    }
424    return currlen;
425 }
426
427 static int32_t fmtstr(char *buffer, int32_t currlen, int32_t maxlen,
428                    const char *value, int flags, int min, int max)
429 {
430    int padlen, strln;              /* amount to pad */
431    int cnt = 0;
432    char ch;
433
434
435    if (flags & DP_F_DOT && max < 0) {   /* Max not specified */
436       max = 0;
437    } else if (max < 0) {
438       max = maxlen;
439    }
440    if (!value) {
441       value = "<NULL>";
442    }
443    strln = strlen(value);
444    if (strln > max) {
445       strln = max;                /* truncate to max */
446    }
447    padlen = min - strln;
448    if (padlen < 0) {
449       padlen = 0;
450    }
451    if (flags & DP_F_MINUS) {
452       padlen = -padlen;            /* Left Justify */
453    }
454
455    while (padlen > 0) {
456       outch(' ');
457       --padlen;
458    }
459    while (*value && (cnt < max)) {
460       ch = *value++;
461       outch(ch);
462       ++cnt;
463    }
464    while (padlen < 0) {
465       outch(' ');
466       ++padlen;
467    }
468    return currlen;
469 }
470
471 static int32_t fmtwstr(char *buffer, int32_t currlen, int32_t maxlen,
472                    const wchar_t *value, int flags, int min, int max)
473 {
474    int padlen, strln;              /* amount to pad */
475    int cnt = 0;
476    char ch;
477
478
479    if (flags & DP_F_DOT && max < 0) {   /* Max not specified */
480       max = 0;
481    } else if (max < 0) {
482       max = maxlen;
483    }
484    if (!value) {
485       value = L"<NULL>";
486    }
487    strln = wcslen(value);
488    if (strln > max) {
489       strln = max;                /* truncate to max */
490    }
491    padlen = min - strln;
492    if (padlen < 0) {
493       padlen = 0;
494    }
495    if (flags & DP_F_MINUS) {
496       padlen = -padlen;            /* Left Justify */
497    }
498
499    while (padlen > 0) {
500       outch(' ');
501       --padlen;
502    }
503    while (*value && (cnt < max)) {
504       
505       ch = (*value++) & 0xff;
506       outch(ch);
507       ++cnt;
508    }
509    while (padlen < 0) {
510       outch(' ');
511       ++padlen;
512    }
513    return currlen;
514 }
515
516 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
517
518 static int32_t fmtint(char *buffer, int32_t currlen, int32_t maxlen,
519                    int64_t value, int base, int min, int max, int flags)
520 {
521    int signvalue = 0;
522    uint64_t uvalue;
523    char convert[25];
524    int place = 0;
525    int spadlen = 0;                /* amount to space pad */
526    int zpadlen = 0;                /* amount to zero pad */
527    int caps = 0;
528    const char *cvt_string;
529
530    if (max < 0) {
531       max = 0;
532    }
533
534    uvalue = value;
535
536    if (!(flags & DP_F_UNSIGNED)) {
537       if (value < 0) {
538          signvalue = '-';
539          uvalue = -value;
540       } else if (flags & DP_F_PLUS) {  /* Do a sign (+/i) */
541          signvalue = '+';
542       } else if (flags & DP_F_SPACE) {
543          signvalue = ' ';
544       }
545    }
546
547    if (flags & DP_F_UP) {
548       caps = 1;                    /* Should characters be upper case? */
549    }
550
551    cvt_string = caps ? "0123456789ABCDEF" : "0123456789abcdef";
552    do {
553       convert[place++] = cvt_string[uvalue % (unsigned)base];
554       uvalue = (uvalue / (unsigned)base);
555    } while (uvalue && (place < (int)sizeof(convert)));
556    if (place == (int)sizeof(convert)) {
557       place--;
558    }
559    convert[place] = 0;
560
561    zpadlen = max - place;
562    spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
563    if (zpadlen < 0)
564       zpadlen = 0;
565    if (spadlen < 0)
566       spadlen = 0;
567    if (flags & DP_F_ZERO) {
568       zpadlen = MAX(zpadlen, spadlen);
569       spadlen = 0;
570    }
571    if (flags & DP_F_MINUS)
572       spadlen = -spadlen;          /* Left Justifty */
573
574 #ifdef DEBUG_SNPRINTF
575    printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
576           zpadlen, spadlen, min, max, place);
577 #endif
578
579    /* Spaces */
580    while (spadlen > 0) {
581       outch(' ');
582       --spadlen;
583    }
584
585    /* Sign */
586    if (signvalue) {
587       outch(signvalue);
588    }
589
590    /* Zeros */
591    if (zpadlen > 0) {
592       while (zpadlen > 0) {
593          outch('0');
594          --zpadlen;
595       }
596    }
597
598    /* Output digits backward giving correct order */
599    while (place > 0) {
600       place--;
601       outch(convert[place]);
602    }
603
604    /* Left Justified spaces */
605    while (spadlen < 0) {
606       outch(' ');
607       ++spadlen;
608    }
609    return currlen;
610 }
611
612 #ifdef FP_OUTPUT
613
614 static LDOUBLE abs_val(LDOUBLE value)
615 {
616    LDOUBLE result = value;
617
618    if (value < 0)
619       result = -value;
620
621    return result;
622 }
623
624 static LDOUBLE pow10(int exp)
625 {
626    LDOUBLE result = 1;
627
628    while (exp) {
629       result *= 10;
630       exp--;
631    }
632
633    return result;
634 }
635
636 static int64_t round(LDOUBLE value)
637 {
638    int64_t intpart;
639
640    intpart = (int64_t)value;
641    value = value - intpart;
642    if (value >= 0.5)
643       intpart++;
644
645    return intpart;
646 }
647
648 static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
649                   LDOUBLE fvalue, int min, int max, int flags)
650 {
651    int signvalue = 0;
652    LDOUBLE ufvalue;
653 #ifndef HAVE_FCVT
654    char iconvert[311];
655    char fconvert[311];
656 #else
657    char iconvert[311];
658    char fconvert[311];
659    char *result;
660    char dummy[10];
661    int dec_pt, sig;
662    int r_length;
663    extern char *fcvt(double value, int ndigit, int *decpt, int *sign);
664 #endif
665    int iplace = 0;
666    int fplace = 0;
667    int padlen = 0;                 /* amount to pad */
668    int zpadlen = 0;
669    int caps = 0;
670    int64_t intpart;
671    int64_t fracpart;
672    const char *cvt_str;
673
674    /* 
675     * AIX manpage says the default is 0, but Solaris says the default
676     * is 6, and sprintf on AIX defaults to 6
677     */
678    if (max < 0)
679       max = 6;
680
681    ufvalue = abs_val(fvalue);
682
683    if (fvalue < 0)
684       signvalue = '-';
685    else if (flags & DP_F_PLUS)     /* Do a sign (+/i) */
686       signvalue = '+';
687    else if (flags & DP_F_SPACE)
688       signvalue = ' ';
689
690 #if 0
691    if (flags & DP_F_UP)
692       caps = 1;                    /* Should characters be upper case? */
693 #endif
694
695 #ifndef HAVE_FCVT
696    intpart = (int64_t)ufvalue;
697
698    /* 
699     * Sorry, we only support 9 digits past the decimal because of our 
700     * conversion method
701     */
702    if (max > 9)
703       max = 9;
704
705    /* We "cheat" by converting the fractional part to integer by
706     * multiplying by a factor of 10
707     */
708    fracpart = round((pow10(max)) * (ufvalue - intpart));
709
710    if (fracpart >= pow10(max)) {
711       intpart++;
712       fracpart -= (int64_t)pow10(max);
713    }
714
715 #ifdef DEBUG_SNPRINTF
716    printf("fmtfp: %g %lld.%lld min=%d max=%d\n",
717           (double)fvalue, intpart, fracpart, min, max);
718 #endif
719
720    /* Convert integer part */
721    cvt_str = caps ? "0123456789ABCDEF" : "0123456789abcdef";
722    do {
723       iconvert[iplace++] = cvt_str[(int)(intpart % 10)];
724       intpart = (intpart / 10);
725    } while (intpart && (iplace < (int)sizeof(iconvert)));
726
727    if (iplace == (int)sizeof(fconvert)) {
728       iplace--;
729    }
730    iconvert[iplace] = 0;
731
732    /* Convert fractional part */
733    cvt_str = caps ? "0123456789ABCDEF" : "0123456789abcdef";
734    do {
735       fconvert[fplace++] = cvt_str[fracpart % 10];
736       fracpart = (fracpart / 10);
737    } while (fracpart && (fplace < (int)sizeof(fconvert)));
738
739    if (fplace == (int)sizeof(fconvert)) {
740       fplace--;
741    }
742    fconvert[fplace] = 0;
743 #else                              /* use fcvt() */
744    if (max > 310) {
745       max = 310;
746    }
747 # ifdef HAVE_FCVTL
748    result = fcvtl(ufvalue, max, &dec_pt, &sig);
749 # else
750    result = fcvt(ufvalue, max, &dec_pt, &sig);
751 # endif
752
753    if (!result) {
754       r_length = 0;
755       result = dummy;
756    } else {
757       r_length = strlen(result);
758    }
759
760    /*
761     * Fix broken fcvt implementation returns..
762     */
763
764    if (r_length == 0) {
765       result[0] = '0';
766       result[1] = '\0';
767       r_length = 1;
768    }
769
770    if (r_length < dec_pt)
771       dec_pt = r_length;
772
773    if (dec_pt <= 0) {
774       iplace = 1;
775       iconvert[0] = '0';
776       iconvert[1] = '\0';
777
778       fplace = 0;
779
780       while (r_length) {
781          fconvert[fplace++] = result[--r_length];
782       }
783
784       while ((dec_pt < 0) && (fplace < max)) {
785          fconvert[fplace++] = '0';
786          dec_pt++;
787       }
788    } else {
789       int c;
790
791       iplace = 0;
792       for (c = dec_pt; c; iconvert[iplace++] = result[--c]);
793       iconvert[iplace] = '\0';
794
795       result += dec_pt;
796       fplace = 0;
797
798       for (c = (r_length - dec_pt); c; fconvert[fplace++] = result[--c]);
799    }
800 #endif  /* HAVE_FCVT */
801
802    /* -1 for decimal point, another -1 if we are printing a sign */
803    padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
804    zpadlen = max - fplace;
805    if (zpadlen < 0) {
806       zpadlen = 0;
807    }
808    if (padlen < 0) {
809       padlen = 0;
810    }
811    if (flags & DP_F_MINUS) {
812       padlen = -padlen;            /* Left Justifty */
813    }
814
815    if ((flags & DP_F_ZERO) && (padlen > 0)) {
816       if (signvalue) {
817          outch(signvalue);
818          --padlen;
819          signvalue = 0;
820       }
821       while (padlen > 0) {
822          outch('0');
823          --padlen;
824       }
825    }
826    while (padlen > 0) {
827       outch(' ');
828       --padlen;
829    }
830    if (signvalue) {
831       outch(signvalue);
832    }
833
834    while (iplace > 0) {
835       iplace--;
836       outch(iconvert[iplace]);
837    }
838
839
840 #ifdef DEBUG_SNPRINTF
841    printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
842 #endif
843
844    /*
845     * Decimal point.  This should probably use locale to find the correct
846     * char to print out.
847     */
848    if (max > 0) {
849       outch('.');
850       while (fplace > 0) {
851          fplace--;
852          outch(fconvert[fplace]);
853       }
854    }
855
856    while (zpadlen > 0) {
857       outch('0');
858       --zpadlen;
859    }
860
861    while (padlen < 0) {
862       outch(' ');
863       ++padlen;
864    }
865    return currlen;
866 }
867 #endif  /* FP_OUTPUT */
868
869
870 #ifdef TEST_PROGRAM
871
872 #ifndef LONG_STRING
873 #define LONG_STRING 1024
874 #endif
875
876 int main(int argc, char *argv[])
877 {
878    char buf1[LONG_STRING];
879    char buf2[LONG_STRING];
880
881 #ifdef FP_OUTPUT
882    const char *fp_fmt[] = {
883       "%-1.5f",
884       "%1.5f",
885       "%123.9f",
886       "%10.5f",
887       "% 10.5f",
888       "%+22.9f",
889       "%+4.9f",
890       "%01.3f",
891       "%4f",
892       "%3.1f",
893       "%3.2f",
894       "%.0f",
895       "%.1f",
896       NULL
897    };
898    double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
899       0.9996, 1.996, 4.136, 6442452944.1234, 0, 23365.5
900    };
901 #endif
902    const char *int_fmt[] = {
903       "%-1.5d",
904       "%1.5d",
905       "%123.9d",
906       "%5.5d",
907       "%10.5d",
908       "% 10.5d",
909       "%+22.33d",
910       "%01.3d",
911       "%4d",
912       "%-1.5ld",
913       "%1.5ld",
914       "%123.9ld",
915       "%5.5ld",
916       "%10.5ld",
917       "% 10.5ld",
918       "%+22.33ld",
919       "%01.3ld",
920       "%4ld",
921       NULL
922    };
923    long int_nums[] = { -1, 134, 91340, 341, 0203, 0 };
924
925    const char *ll_fmt[] = {
926       "%-1.8lld",
927       "%1.8lld",
928       "%123.9lld",
929       "%5.8lld",
930       "%10.5lld",
931       "% 10.8lld",
932       "%+22.33lld",
933       "%01.3lld",
934       "%4lld",
935       NULL
936    };
937    int64_t ll_nums[] = { -1976, 789134567890LL, 91340, 34123, 0203, 0 };
938
939    const char *s_fmt[] = {
940       "%-1.8s",
941       "%1.8s",
942       "%123.9s",
943       "%5.8s",
944       "%10.5s",
945       "% 10.3s",
946       "%+22.1s",
947       "%01.3s",
948       "%s",
949       "%10s",
950       "%3s",
951       "%3.0s",
952       "%3.s",
953       NULL
954    };
955    const char *s_nums[] = { "abc", "def", "ghi", "123", "4567", "a", "bb", "ccccccc", NULL};
956
957    const char *ls_fmt[] = {
958       "%-1.8ls",
959       "%1.8ls",
960       "%123.9ls",
961       "%5.8ls",
962       "%10.5ls",
963       "% 10.3ls",
964       "%+22.1ls",
965       "%01.3ls",
966       "%ls",
967       "%10ls",
968       "%3ls",
969       "%3.0ls",
970       "%3.ls",
971       NULL
972    };
973    const wchar_t *ls_nums[] = { L"abc", L"def", L"ghi", L"123", L"4567", L"a", L"bb", L"ccccccc", NULL};
974
975
976
977    int x, y;
978    int fail = 0;
979    int num = 0;
980
981    printf("Testing snprintf format codes against system sprintf...\n");
982
983 #ifdef FP_OUTPUT
984    for (x = 0; fp_fmt[x] != NULL; x++)
985       for (y = 0; fp_nums[y] != 0; y++) {
986          bsnprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
987          sprintf(buf2, fp_fmt[x], fp_nums[y]);
988          if (strcmp(buf1, buf2)) {
989             printf
990                ("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
991                 fp_fmt[x], buf1, buf2);
992             fail++;
993          }
994          num++;
995       }
996 #endif
997
998    for (x = 0; int_fmt[x] != NULL; x++)
999       for (y = 0; int_nums[y] != 0; y++) {
1000          int pcount, bcount;
1001          bcount = bsnprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
1002          printf("%s\n", buf1);
1003          pcount = sprintf(buf2, int_fmt[x], int_nums[y]);
1004          if (bcount != pcount) {
1005             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1006                bcount, pcount);
1007          }
1008          if (strcmp(buf1, buf2)) {
1009             printf
1010                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1011                 int_fmt[x], buf1, buf2);
1012             fail++;
1013          }
1014          num++;
1015       }
1016
1017    for (x = 0; ll_fmt[x] != NULL; x++) {
1018       for (y = 0; ll_nums[y] != 0; y++) {
1019          int pcount, bcount;
1020          bcount = bsnprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
1021          printf("%s\n", buf1);
1022          pcount = sprintf(buf2, ll_fmt[x], ll_nums[y]);
1023          if (bcount != pcount) {
1024             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1025                bcount, pcount);
1026          }
1027          if (strcmp(buf1, buf2)) {
1028             printf
1029                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1030                 ll_fmt[x], buf1, buf2);
1031             fail++;
1032          }
1033          num++;
1034       }
1035    }
1036
1037    for (x = 0; s_fmt[x] != NULL; x++) {
1038       for (y = 0; s_nums[y] != 0; y++) {
1039          int pcount, bcount;
1040          bcount = bsnprintf(buf1, sizeof(buf1), s_fmt[x], s_nums[y]);
1041          printf("%s\n", buf1);
1042          pcount = sprintf(buf2, s_fmt[x], s_nums[y]);
1043          if (bcount != pcount) {
1044             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1045                bcount, pcount);
1046          }
1047          if (strcmp(buf1, buf2)) {
1048             printf
1049                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1050                 s_fmt[x], buf1, buf2);
1051             fail++;
1052          }
1053          num++;
1054       }
1055    }
1056
1057    for (x = 0; ls_fmt[x] != NULL; x++) {
1058       for (y = 0; ls_nums[y] != 0; y++) {
1059          int pcount, bcount;
1060          bcount = bsnprintf(buf1, sizeof(buf1), ls_fmt[x], ls_nums[y]);
1061          printf("%s\n", buf1);
1062          pcount = sprintf(buf2, ls_fmt[x], ls_nums[y]);
1063          if (bcount != pcount) {
1064             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1065                bcount, pcount);
1066          }
1067          if (strcmp(buf1, buf2)) {
1068             printf
1069                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1070                 ls_fmt[x], buf1, buf2);
1071             fail++;
1072          }
1073          num++;
1074       }
1075    }
1076
1077
1078
1079    printf("%d tests failed out of %d.\n", fail, num);
1080
1081    exit(fail > 0);
1082 }
1083 #endif /* TEST_PROGRAM */
1084
1085 #endif /* USE_BSNPRINTF */