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