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