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