]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bsnprintf.c
Pull compiler warning cleanup code + other from master
[bacula/bacula] / bacula / src / lib / bsnprintf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2005-2012 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
376 #ifdef SECURITY_PROBLEM
377          case 'n':
378             if (cflags == DP_C_INT16) {
379                int16_t *num;
380                num = va_arg(args, int16_t *);
381                *num = currlen;
382             } else if (cflags == DP_C_INT32) {
383                int32_t *num;
384                num = va_arg(args, int32_t *);
385                *num = (int32_t)currlen;
386             } else if (cflags == DP_C_INT64) {
387                int64_t *num;
388                num = va_arg(args, int64_t *);
389                *num = (int64_t)currlen;
390             } else {
391                int32_t *num;
392                num = va_arg(args, int32_t *);
393                *num = (int32_t)currlen;
394             }
395             break;
396 #endif
397          case '%':
398             outch(ch);
399             break;
400          case 'w':
401             /* not supported yet, treat as next char */
402             ch = *format++;
403             break;
404          default:
405             /* Unknown, skip */
406             break;
407          }
408          ch = *format++;
409          state = DP_S_DEFAULT;
410          flags = cflags = min = 0;
411          max = -1;
412          break;
413       case DP_S_DONE:
414          break;
415       default:
416          /* hmm? */
417          break;                    /* some picky compilers need this */
418       }
419    }
420    if (currlen < maxlen - 1) {
421       buffer[currlen] = '\0';
422    } else {
423       buffer[maxlen - 1] = '\0';
424    }
425    return currlen;
426 }
427
428 static int32_t fmtstr(char *buffer, int32_t currlen, int32_t maxlen,
429                    const char *value, int flags, int min, int max)
430 {
431    int padlen, strln;              /* amount to pad */
432    int cnt = 0;
433    char ch;
434
435
436    if (flags & DP_F_DOT && max < 0) {   /* Max not specified */
437       max = 0;
438    } else if (max < 0) {
439       max = maxlen;
440    }
441    strln = strlen(value);
442    if (strln > max) {
443       strln = max;                /* truncate to max */
444    }
445    padlen = min - strln;
446    if (padlen < 0) {
447       padlen = 0;
448    }
449    if (flags & DP_F_MINUS) {
450       padlen = -padlen;            /* Left Justify */
451    }
452
453    while (padlen > 0) {
454       outch(' ');
455       --padlen;
456    }
457    while (*value && (cnt < max)) {
458       ch = *value++;
459       outch(ch);
460       ++cnt;
461    }
462    while (padlen < 0) {
463       outch(' ');
464       ++padlen;
465    }
466    return currlen;
467 }
468
469 static int32_t fmtwstr(char *buffer, int32_t currlen, int32_t maxlen,
470                    const wchar_t *value, int flags, int min, int max)
471 {
472    int padlen, strln;              /* amount to pad */
473    int cnt = 0;
474    char ch;
475
476
477    if (flags & DP_F_DOT && max < 0) {   /* Max not specified */
478       max = 0;
479    } else if (max < 0) {
480       max = maxlen;
481    }
482    strln = wcslen(value);
483    if (strln > max) {
484       strln = max;                /* truncate to max */
485    }
486    padlen = min - strln;
487    if (padlen < 0) {
488       padlen = 0;
489    }
490    if (flags & DP_F_MINUS) {
491       padlen = -padlen;            /* Left Justify */
492    }
493
494    while (padlen > 0) {
495       outch(' ');
496       --padlen;
497    }
498    while (*value && (cnt < max)) {
499       
500       ch = (*value++) & 0xff;
501       outch(ch);
502       ++cnt;
503    }
504    while (padlen < 0) {
505       outch(' ');
506       ++padlen;
507    }
508    return currlen;
509 }
510
511 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
512
513 static int32_t fmtint(char *buffer, int32_t currlen, int32_t maxlen,
514                    int64_t value, int base, int min, int max, int flags)
515 {
516    int signvalue = 0;
517    uint64_t uvalue;
518    char convert[25];
519    int place = 0;
520    int spadlen = 0;                /* amount to space pad */
521    int zpadlen = 0;                /* amount to zero pad */
522    int caps = 0;
523    const char *cvt_string;
524
525    if (max < 0) {
526       max = 0;
527    }
528
529    uvalue = value;
530
531    if (!(flags & DP_F_UNSIGNED)) {
532       if (value < 0) {
533          signvalue = '-';
534          uvalue = -value;
535       } else if (flags & DP_F_PLUS) {  /* Do a sign (+/i) */
536          signvalue = '+';
537       } else if (flags & DP_F_SPACE) {
538          signvalue = ' ';
539       }
540    }
541
542    if (flags & DP_F_UP) {
543       caps = 1;                    /* Should characters be upper case? */
544    }
545
546    cvt_string = caps ? "0123456789ABCDEF" : "0123456789abcdef";
547    do {
548       convert[place++] = cvt_string[uvalue % (unsigned)base];
549       uvalue = (uvalue / (unsigned)base);
550    } while (uvalue && (place < (int)sizeof(convert)));
551    if (place == (int)sizeof(convert)) {
552       place--;
553    }
554    convert[place] = 0;
555
556    zpadlen = max - place;
557    spadlen = min - MAX(max, place) - (signvalue ? 1 : 0);
558    if (zpadlen < 0)
559       zpadlen = 0;
560    if (spadlen < 0)
561       spadlen = 0;
562    if (flags & DP_F_ZERO) {
563       zpadlen = MAX(zpadlen, spadlen);
564       spadlen = 0;
565    }
566    if (flags & DP_F_MINUS)
567       spadlen = -spadlen;          /* Left Justifty */
568
569 #ifdef DEBUG_SNPRINTF
570    printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
571           zpadlen, spadlen, min, max, place);
572 #endif
573
574    /* Spaces */
575    while (spadlen > 0) {
576       outch(' ');
577       --spadlen;
578    }
579
580    /* Sign */
581    if (signvalue) {
582       outch(signvalue);
583    }
584
585    /* Zeros */
586    if (zpadlen > 0) {
587       while (zpadlen > 0) {
588          outch('0');
589          --zpadlen;
590       }
591    }
592
593    /* Output digits backward giving correct order */
594    while (place > 0) {
595       place--;
596       outch(convert[place]);
597    }
598
599    /* Left Justified spaces */
600    while (spadlen < 0) {
601       outch(' ');
602       ++spadlen;
603    }
604    return currlen;
605 }
606
607 #ifdef FP_OUTPUT
608
609 static LDOUBLE abs_val(LDOUBLE value)
610 {
611    LDOUBLE result = value;
612
613    if (value < 0)
614       result = -value;
615
616    return result;
617 }
618
619 static LDOUBLE pow10(int exp)
620 {
621    LDOUBLE result = 1;
622
623    while (exp) {
624       result *= 10;
625       exp--;
626    }
627
628    return result;
629 }
630
631 static int64_t round(LDOUBLE value)
632 {
633    int64_t intpart;
634
635    intpart = (int64_t)value;
636    value = value - intpart;
637    if (value >= 0.5)
638       intpart++;
639
640    return intpart;
641 }
642
643 static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
644                   LDOUBLE fvalue, int min, int max, int flags)
645 {
646    int signvalue = 0;
647    LDOUBLE ufvalue;
648 #ifndef HAVE_FCVT
649    char iconvert[311];
650    char fconvert[311];
651 #else
652    char iconvert[311];
653    char fconvert[311];
654    char *result;
655    char dummy[10];
656    int dec_pt, sig;
657    int r_length;
658    extern char *fcvt(double value, int ndigit, int *decpt, int *sign);
659 #endif
660    int iplace = 0;
661    int fplace = 0;
662    int padlen = 0;                 /* amount to pad */
663    int zpadlen = 0;
664    int caps = 0;
665    int64_t intpart;
666    int64_t fracpart;
667    const char *cvt_str;
668
669    /* 
670     * AIX manpage says the default is 0, but Solaris says the default
671     * is 6, and sprintf on AIX defaults to 6
672     */
673    if (max < 0)
674       max = 6;
675
676    ufvalue = abs_val(fvalue);
677
678    if (fvalue < 0)
679       signvalue = '-';
680    else if (flags & DP_F_PLUS)     /* Do a sign (+/i) */
681       signvalue = '+';
682    else if (flags & DP_F_SPACE)
683       signvalue = ' ';
684
685 #if 0
686    if (flags & DP_F_UP)
687       caps = 1;                    /* Should characters be upper case? */
688 #endif
689
690 #ifndef HAVE_FCVT
691    intpart = (int64_t)ufvalue;
692
693    /* 
694     * Sorry, we only support 9 digits past the decimal because of our 
695     * conversion method
696     */
697    if (max > 9)
698       max = 9;
699
700    /* We "cheat" by converting the fractional part to integer by
701     * multiplying by a factor of 10
702     */
703    fracpart = round((pow10(max)) * (ufvalue - intpart));
704
705    if (fracpart >= pow10(max)) {
706       intpart++;
707       fracpart -= (int64_t)pow10(max);
708    }
709
710 #ifdef DEBUG_SNPRINTF
711    printf("fmtfp: %g %lld.%lld min=%d max=%d\n",
712           (double)fvalue, intpart, fracpart, min, max);
713 #endif
714
715    /* Convert integer part */
716    cvt_str = caps ? "0123456789ABCDEF" : "0123456789abcdef";
717    do {
718       iconvert[iplace++] = cvt_str[(int)(intpart % 10)];
719       intpart = (intpart / 10);
720    } while (intpart && (iplace < (int)sizeof(iconvert)));
721
722    if (iplace == (int)sizeof(fconvert)) {
723       iplace--;
724    }
725    iconvert[iplace] = 0;
726
727    /* Convert fractional part */
728    cvt_str = caps ? "0123456789ABCDEF" : "0123456789abcdef";
729    do {
730       fconvert[fplace++] = cvt_str[fracpart % 10];
731       fracpart = (fracpart / 10);
732    } while (fracpart && (fplace < (int)sizeof(fconvert)));
733
734    if (fplace == (int)sizeof(fconvert)) {
735       fplace--;
736    }
737    fconvert[fplace] = 0;
738 #else                              /* use fcvt() */
739    if (max > 310) {
740       max = 310;
741    }
742 # ifdef HAVE_FCVTL
743    result = fcvtl(ufvalue, max, &dec_pt, &sig);
744 # else
745    result = fcvt(ufvalue, max, &dec_pt, &sig);
746 # endif
747
748    if (!result) {
749       r_length = 0;
750       dummy[0] = 0;
751       result = dummy;
752    } else {
753       r_length = strlen(result);
754    }
755
756    /*
757     * Fix broken fcvt implementation returns..
758     */
759
760    if (r_length == 0) {
761       result[0] = '0';
762       result[1] = '\0';
763       r_length = 1;
764    }
765
766    if (r_length < dec_pt)
767       dec_pt = r_length;
768
769    if (dec_pt <= 0) {
770       iplace = 1;
771       iconvert[0] = '0';
772       iconvert[1] = '\0';
773
774       fplace = 0;
775
776       while (r_length) {
777          fconvert[fplace++] = result[--r_length];
778       }
779
780       while ((dec_pt < 0) && (fplace < max)) {
781          fconvert[fplace++] = '0';
782          dec_pt++;
783       }
784    } else {
785       int c;
786
787       iplace = 0;
788       for (c = dec_pt; c; iconvert[iplace++] = result[--c]);
789       iconvert[iplace] = '\0';
790
791       result += dec_pt;
792       fplace = 0;
793
794       for (c = (r_length - dec_pt); c; fconvert[fplace++] = result[--c]);
795    }
796 #endif  /* HAVE_FCVT */
797
798    /* -1 for decimal point, another -1 if we are printing a sign */
799    padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
800    zpadlen = max - fplace;
801    if (zpadlen < 0) {
802       zpadlen = 0;
803    }
804    if (padlen < 0) {
805       padlen = 0;
806    }
807    if (flags & DP_F_MINUS) {
808       padlen = -padlen;            /* Left Justifty */
809    }
810
811    if ((flags & DP_F_ZERO) && (padlen > 0)) {
812       if (signvalue) {
813          outch(signvalue);
814          --padlen;
815          signvalue = 0;
816       }
817       while (padlen > 0) {
818          outch('0');
819          --padlen;
820       }
821    }
822    while (padlen > 0) {
823       outch(' ');
824       --padlen;
825    }
826    if (signvalue) {
827       outch(signvalue);
828    }
829
830    while (iplace > 0) {
831       iplace--;
832       outch(iconvert[iplace]);
833    }
834
835
836 #ifdef DEBUG_SNPRINTF
837    printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
838 #endif
839
840    /*
841     * Decimal point.  This should probably use locale to find the correct
842     * char to print out.
843     */
844    if (max > 0) {
845       outch('.');
846       while (fplace > 0) {
847          fplace--;
848          outch(fconvert[fplace]);
849       }
850    }
851
852    while (zpadlen > 0) {
853       outch('0');
854       --zpadlen;
855    }
856
857    while (padlen < 0) {
858       outch(' ');
859       ++padlen;
860    }
861    return currlen;
862 }
863 #endif  /* FP_OUTPUT */
864
865
866 #ifdef TEST_PROGRAM
867
868 #ifndef LONG_STRING
869 #define LONG_STRING 1024
870 #endif
871
872 int main(int argc, char *argv[])
873 {
874    char buf1[LONG_STRING];
875    char buf2[LONG_STRING];
876
877 #ifdef FP_OUTPUT
878    const char *fp_fmt[] = {
879       "%-1.5f",
880       "%1.5f",
881       "%123.9f",
882       "%10.5f",
883       "% 10.5f",
884       "%+22.9f",
885       "%+4.9f",
886       "%01.3f",
887       "%4f",
888       "%3.1f",
889       "%3.2f",
890       "%.0f",
891       "%.1f",
892       NULL
893    };
894    double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
895       0.9996, 1.996, 4.136, 6442452944.1234, 0, 23365.5
896    };
897 #endif
898    const char *int_fmt[] = {
899       "%-1.5d",
900       "%1.5d",
901       "%123.9d",
902       "%5.5d",
903       "%10.5d",
904       "% 10.5d",
905       "%+22.33d",
906       "%01.3d",
907       "%4d",
908       "%-1.5ld",
909       "%1.5ld",
910       "%123.9ld",
911       "%5.5ld",
912       "%10.5ld",
913       "% 10.5ld",
914       "%+22.33ld",
915       "%01.3ld",
916       "%4ld",
917       NULL
918    };
919    long int_nums[] = { -1, 134, 91340, 341, 0203, 0 };
920
921    const char *ll_fmt[] = {
922       "%-1.8lld",
923       "%1.8lld",
924       "%123.9lld",
925       "%5.8lld",
926       "%10.5lld",
927       "% 10.8lld",
928       "%+22.33lld",
929       "%01.3lld",
930       "%4lld",
931       NULL
932    };
933    int64_t ll_nums[] = { -1976, 789134567890LL, 91340, 34123, 0203, 0 };
934
935    const char *s_fmt[] = {
936       "%-1.8s",
937       "%1.8s",
938       "%123.9s",
939       "%5.8s",
940       "%10.5s",
941       "% 10.3s",
942       "%+22.1s",
943       "%01.3s",
944       "%s",
945       "%10s",
946       "%3s",
947       "%3.0s",
948       "%3.s",
949       NULL
950    };
951    const char *s_nums[] = { "abc", "def", "ghi", "123", "4567", "a", "bb", "ccccccc", NULL};
952
953    const char *ls_fmt[] = {
954       "%-1.8ls",
955       "%1.8ls",
956       "%123.9ls",
957       "%5.8ls",
958       "%10.5ls",
959       "% 10.3ls",
960       "%+22.1ls",
961       "%01.3ls",
962       "%ls",
963       "%10ls",
964       "%3ls",
965       "%3.0ls",
966       "%3.ls",
967       NULL
968    };
969    const wchar_t *ls_nums[] = { L"abc", L"def", L"ghi", L"123", L"4567", L"a", L"bb", L"ccccccc", NULL};
970
971
972
973    int x, y;
974    int fail = 0;
975    int num = 0;
976
977    printf("Testing snprintf format codes against system sprintf...\n");
978
979 #ifdef FP_OUTPUT
980    for (x = 0; fp_fmt[x] != NULL; x++)
981       for (y = 0; fp_nums[y] != 0; y++) {
982          bsnprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
983          sprintf(buf2, fp_fmt[x], fp_nums[y]);
984          if (strcmp(buf1, buf2)) {
985             printf
986                ("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
987                 fp_fmt[x], buf1, buf2);
988             fail++;
989          }
990          num++;
991       }
992 #endif
993
994    for (x = 0; int_fmt[x] != NULL; x++)
995       for (y = 0; int_nums[y] != 0; y++) {
996          int pcount, bcount;
997          bcount = bsnprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
998          printf("%s\n", buf1);
999          pcount = sprintf(buf2, int_fmt[x], int_nums[y]);
1000          if (bcount != pcount) {
1001             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1002                bcount, pcount);
1003          }
1004          if (strcmp(buf1, buf2)) {
1005             printf
1006                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1007                 int_fmt[x], buf1, buf2);
1008             fail++;
1009          }
1010          num++;
1011       }
1012
1013    for (x = 0; ll_fmt[x] != NULL; x++) {
1014       for (y = 0; ll_nums[y] != 0; y++) {
1015          int pcount, bcount;
1016          bcount = bsnprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
1017          printf("%s\n", buf1);
1018          pcount = sprintf(buf2, ll_fmt[x], ll_nums[y]);
1019          if (bcount != pcount) {
1020             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1021                bcount, pcount);
1022          }
1023          if (strcmp(buf1, buf2)) {
1024             printf
1025                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1026                 ll_fmt[x], buf1, buf2);
1027             fail++;
1028          }
1029          num++;
1030       }
1031    }
1032
1033    for (x = 0; s_fmt[x] != NULL; x++) {
1034       for (y = 0; s_nums[y] != 0; y++) {
1035          int pcount, bcount;
1036          bcount = bsnprintf(buf1, sizeof(buf1), s_fmt[x], s_nums[y]);
1037          printf("%s\n", buf1);
1038          pcount = sprintf(buf2, s_fmt[x], s_nums[y]);
1039          if (bcount != pcount) {
1040             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1041                bcount, pcount);
1042          }
1043          if (strcmp(buf1, buf2)) {
1044             printf
1045                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1046                 s_fmt[x], buf1, buf2);
1047             fail++;
1048          }
1049          num++;
1050       }
1051    }
1052
1053    for (x = 0; ls_fmt[x] != NULL; x++) {
1054       for (y = 0; ls_nums[y] != 0; y++) {
1055          int pcount, bcount;
1056          bcount = bsnprintf(buf1, sizeof(buf1), ls_fmt[x], ls_nums[y]);
1057          printf("%s\n", buf1);
1058          pcount = sprintf(buf2, ls_fmt[x], ls_nums[y]);
1059          if (bcount != pcount) {
1060             printf("bsnprintf count %d doesn't match sprintf count %d\n",
1061                bcount, pcount);
1062          }
1063          if (strcmp(buf1, buf2)) {
1064             printf
1065                ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
1066                 ls_fmt[x], buf1, buf2);
1067             fail++;
1068          }
1069          num++;
1070       }
1071    }
1072
1073
1074
1075    printf("%d tests failed out of %d.\n", fail, num);
1076
1077    exit(fail > 0);
1078 }
1079 #endif /* TEST_PROGRAM */
1080
1081 #endif /* USE_BSNPRINTF */