]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bsnprintf.c
Allow plugins to add drives to vss snapshot
[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 three of the GNU Affero 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 Affero 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               if (!strvalue) {
352                  strvalue = (char *)"<NULL>";
353               }
354               currlen = fmtstr(buffer, currlen, maxlen, strvalue, flags, min, max);
355             } else {
356               /* %ls means to edit wide characters */
357               wstrvalue = va_arg(args, wchar_t *);
358               if (!wstrvalue) {
359                  wstrvalue = (wchar_t *)L"<NULL>";
360               }
361               currlen = fmtwstr(buffer, currlen, maxlen, wstrvalue, flags, min, max);
362             }
363             break;
364          case 'p':
365             flags |= DP_F_UNSIGNED;
366             if (sizeof(char *) == 4) {
367                value = va_arg(args, uint32_t);
368             } else if (sizeof(char *) == 8) {
369                value = va_arg(args, uint64_t);
370             } else {
371                value = 0;             /* we have a problem */
372             }
373             currlen = fmtint(buffer, currlen, maxlen, value, 16, min, max, flags);
374             break;
375          case 'n':
376             if (cflags == DP_C_INT16) {
377                int16_t *num;
378                num = va_arg(args, int16_t *);
379 #ifdef SECURITY_PROBLEM
380                *num = currlen;
381 #endif
382             } else if (cflags == DP_C_INT32) {
383                int32_t *num;
384                num = va_arg(args, int32_t *);
385 #ifdef SECURITY_PROBLEM
386                *num = (int32_t)currlen;
387 #endif
388             } else if (cflags == DP_C_INT64) {
389                int64_t *num;
390                num = va_arg(args, int64_t *);
391 #ifdef SECURITY_PROBLEM
392                *num = (int64_t)currlen;
393 #endif
394             } else {
395                int32_t *num;
396                num = va_arg(args, int32_t *);
397 #ifdef SECURITY_PROBLEM
398                *num = (int32_t)currlen;
399 #endif
400             }
401             break;
402          case '%':
403             outch(ch);
404             break;
405          case 'w':
406             /* not supported yet, treat as next char */
407             ch = *format++;
408             break;
409          default:
410             /* Unknown, skip */
411             break;
412          }
413          ch = *format++;
414          state = DP_S_DEFAULT;
415          flags = cflags = min = 0;
416          max = -1;
417          break;
418       case DP_S_DONE:
419          break;
420       default:
421          /* hmm? */
422          break;                    /* some picky compilers need this */
423       }
424    }
425    if (currlen < maxlen - 1) {
426       buffer[currlen] = '\0';
427    } else {
428       buffer[maxlen - 1] = '\0';
429    }
430    return currlen;
431 }
432
433 static int32_t fmtstr(char *buffer, int32_t currlen, int32_t maxlen,
434                    const char *value, int flags, int min, int max)
435 {
436    int padlen, strln;              /* amount to pad */
437    int cnt = 0;
438    char ch;
439
440
441    if (flags & DP_F_DOT && max < 0) {   /* Max not specified */
442       max = 0;
443    } else if (max < 0) {
444       max = maxlen;
445    }
446    strln = strlen(value);
447    if (strln > max) {
448       strln = max;                /* truncate to max */
449    }
450    padlen = min - strln;
451    if (padlen < 0) {
452       padlen = 0;
453    }
454    if (flags & DP_F_MINUS) {
455       padlen = -padlen;            /* Left Justify */
456    }
457
458    while (padlen > 0) {
459       outch(' ');
460       --padlen;
461    }
462    while (*value && (cnt < max)) {
463       ch = *value++;
464       outch(ch);
465       ++cnt;
466    }
467    while (padlen < 0) {
468       outch(' ');
469       ++padlen;
470    }
471    return currlen;
472 }
473
474 static int32_t fmtwstr(char *buffer, int32_t currlen, int32_t maxlen,
475                    const wchar_t *value, int flags, int min, int max)
476 {
477    int padlen, strln;              /* amount to pad */
478    int cnt = 0;
479    char ch;
480
481
482    if (flags & DP_F_DOT && max < 0) {   /* Max not specified */
483       max = 0;
484    } else if (max < 0) {
485       max = maxlen;
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       dummy[0] = 0;
756       result = dummy;
757    } else {
758       r_length = strlen(result);
759    }
760
761    /*
762     * Fix broken fcvt implementation returns..
763     */
764
765    if (r_length == 0) {
766       result[0] = '0';
767       result[1] = '\0';
768       r_length = 1;
769    }
770
771    if (r_length < dec_pt)
772       dec_pt = r_length;
773
774    if (dec_pt <= 0) {
775       iplace = 1;
776       iconvert[0] = '0';
777       iconvert[1] = '\0';
778
779       fplace = 0;
780
781       while (r_length) {
782          fconvert[fplace++] = result[--r_length];
783       }
784
785       while ((dec_pt < 0) && (fplace < max)) {
786          fconvert[fplace++] = '0';
787          dec_pt++;
788       }
789    } else {
790       int c;
791
792       iplace = 0;
793       for (c = dec_pt; c; iconvert[iplace++] = result[--c]);
794       iconvert[iplace] = '\0';
795
796       result += dec_pt;
797       fplace = 0;
798
799       for (c = (r_length - dec_pt); c; fconvert[fplace++] = result[--c]);
800    }
801 #endif  /* HAVE_FCVT */
802
803    /* -1 for decimal point, another -1 if we are printing a sign */
804    padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
805    zpadlen = max - fplace;
806    if (zpadlen < 0) {
807       zpadlen = 0;
808    }
809    if (padlen < 0) {
810       padlen = 0;
811    }
812    if (flags & DP_F_MINUS) {
813       padlen = -padlen;            /* Left Justifty */
814    }
815
816    if ((flags & DP_F_ZERO) && (padlen > 0)) {
817       if (signvalue) {
818          outch(signvalue);
819          --padlen;
820          signvalue = 0;
821       }
822       while (padlen > 0) {
823          outch('0');
824          --padlen;
825       }
826    }
827    while (padlen > 0) {
828       outch(' ');
829       --padlen;
830    }
831    if (signvalue) {
832       outch(signvalue);
833    }
834
835    while (iplace > 0) {
836       iplace--;
837       outch(iconvert[iplace]);
838    }
839
840
841 #ifdef DEBUG_SNPRINTF
842    printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
843 #endif
844
845    /*
846     * Decimal point.  This should probably use locale to find the correct
847     * char to print out.
848     */
849    if (max > 0) {
850       outch('.');
851       while (fplace > 0) {
852          fplace--;
853          outch(fconvert[fplace]);
854       }
855    }
856
857    while (zpadlen > 0) {
858       outch('0');
859       --zpadlen;
860    }
861
862    while (padlen < 0) {
863       outch(' ');
864       ++padlen;
865    }
866    return currlen;
867 }
868 #endif  /* FP_OUTPUT */
869
870
871 #ifdef TEST_PROGRAM
872
873 #ifndef LONG_STRING
874 #define LONG_STRING 1024
875 #endif
876
877 int main(int argc, char *argv[])
878 {
879    char buf1[LONG_STRING];
880    char buf2[LONG_STRING];
881
882 #ifdef FP_OUTPUT
883    const char *fp_fmt[] = {
884       "%-1.5f",
885       "%1.5f",
886       "%123.9f",
887       "%10.5f",
888       "% 10.5f",
889       "%+22.9f",
890       "%+4.9f",
891       "%01.3f",
892       "%4f",
893       "%3.1f",
894       "%3.2f",
895       "%.0f",
896       "%.1f",
897       NULL
898    };
899    double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
900       0.9996, 1.996, 4.136, 6442452944.1234, 0, 23365.5
901    };
902 #endif
903    const char *int_fmt[] = {
904       "%-1.5d",
905       "%1.5d",
906       "%123.9d",
907       "%5.5d",
908       "%10.5d",
909       "% 10.5d",
910       "%+22.33d",
911       "%01.3d",
912       "%4d",
913       "%-1.5ld",
914       "%1.5ld",
915       "%123.9ld",
916       "%5.5ld",
917       "%10.5ld",
918       "% 10.5ld",
919       "%+22.33ld",
920       "%01.3ld",
921       "%4ld",
922       NULL
923    };
924    long int_nums[] = { -1, 134, 91340, 341, 0203, 0 };
925
926    const char *ll_fmt[] = {
927       "%-1.8lld",
928       "%1.8lld",
929       "%123.9lld",
930       "%5.8lld",
931       "%10.5lld",
932       "% 10.8lld",
933       "%+22.33lld",
934       "%01.3lld",
935       "%4lld",
936       NULL
937    };
938    int64_t ll_nums[] = { -1976, 789134567890LL, 91340, 34123, 0203, 0 };
939
940    const char *s_fmt[] = {
941       "%-1.8s",
942       "%1.8s",
943       "%123.9s",
944       "%5.8s",
945       "%10.5s",
946       "% 10.3s",
947       "%+22.1s",
948       "%01.3s",
949       "%s",
950       "%10s",
951       "%3s",
952       "%3.0s",
953       "%3.s",
954       NULL
955    };
956    const char *s_nums[] = { "abc", "def", "ghi", "123", "4567", "a", "bb", "ccccccc", NULL};
957
958    const char *ls_fmt[] = {
959       "%-1.8ls",
960       "%1.8ls",
961       "%123.9ls",
962       "%5.8ls",
963       "%10.5ls",
964       "% 10.3ls",
965       "%+22.1ls",
966       "%01.3ls",
967       "%ls",
968       "%10ls",
969       "%3ls",
970       "%3.0ls",
971       "%3.ls",
972       NULL
973    };
974    const wchar_t *ls_nums[] = { L"abc", L"def", L"ghi", L"123", L"4567", L"a", L"bb", L"ccccccc", NULL};
975
976
977
978    int x, y;
979    int fail = 0;
980    int num = 0;
981
982    printf("Testing snprintf format codes against system sprintf...\n");
983
984 #ifdef FP_OUTPUT
985    for (x = 0; fp_fmt[x] != NULL; x++)
986       for (y = 0; fp_nums[y] != 0; y++) {
987          bsnprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
988          sprintf(buf2, fp_fmt[x], fp_nums[y]);
989          if (strcmp(buf1, buf2)) {
990             printf
991                ("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
992                 fp_fmt[x], buf1, buf2);
993             fail++;
994          }
995          num++;
996       }
997 #endif
998
999    for (x = 0; int_fmt[x] != NULL; x++)
1000       for (y = 0; int_nums[y] != 0; y++) {
1001          int pcount, bcount;
1002          bcount = bsnprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
1003          printf("%s\n", buf1);
1004          pcount = sprintf(buf2, int_fmt[x], int_nums[y]);
1005          if (bcount != pcount) {
1006             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1007                bcount, pcount);
1008          }
1009          if (strcmp(buf1, buf2)) {
1010             printf
1011                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1012                 int_fmt[x], buf1, buf2);
1013             fail++;
1014          }
1015          num++;
1016       }
1017
1018    for (x = 0; ll_fmt[x] != NULL; x++) {
1019       for (y = 0; ll_nums[y] != 0; y++) {
1020          int pcount, bcount;
1021          bcount = bsnprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
1022          printf("%s\n", buf1);
1023          pcount = sprintf(buf2, ll_fmt[x], ll_nums[y]);
1024          if (bcount != pcount) {
1025             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1026                bcount, pcount);
1027          }
1028          if (strcmp(buf1, buf2)) {
1029             printf
1030                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1031                 ll_fmt[x], buf1, buf2);
1032             fail++;
1033          }
1034          num++;
1035       }
1036    }
1037
1038    for (x = 0; s_fmt[x] != NULL; x++) {
1039       for (y = 0; s_nums[y] != 0; y++) {
1040          int pcount, bcount;
1041          bcount = bsnprintf(buf1, sizeof(buf1), s_fmt[x], s_nums[y]);
1042          printf("%s\n", buf1);
1043          pcount = sprintf(buf2, s_fmt[x], s_nums[y]);
1044          if (bcount != pcount) {
1045             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1046                bcount, pcount);
1047          }
1048          if (strcmp(buf1, buf2)) {
1049             printf
1050                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1051                 s_fmt[x], buf1, buf2);
1052             fail++;
1053          }
1054          num++;
1055       }
1056    }
1057
1058    for (x = 0; ls_fmt[x] != NULL; x++) {
1059       for (y = 0; ls_nums[y] != 0; y++) {
1060          int pcount, bcount;
1061          bcount = bsnprintf(buf1, sizeof(buf1), ls_fmt[x], ls_nums[y]);
1062          printf("%s\n", buf1);
1063          pcount = sprintf(buf2, ls_fmt[x], ls_nums[y]);
1064          if (bcount != pcount) {
1065             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1066                bcount, pcount);
1067          }
1068          if (strcmp(buf1, buf2)) {
1069             printf
1070                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1071                 ls_fmt[x], buf1, buf2);
1072             fail++;
1073          }
1074          num++;
1075       }
1076    }
1077
1078
1079
1080    printf("%d tests failed out of %d.\n", fail, num);
1081
1082    exit(fail > 0);
1083 }
1084 #endif /* TEST_PROGRAM */
1085
1086 #endif /* USE_BSNPRINTF */