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