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