1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 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 
bsnprintf(char * str,int32_t size,const char * fmt,...)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 
bvsnprintf(char * buffer,int32_t maxlen,const char * format,va_list args)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 
fmtstr(char * buffer,int32_t currlen,int32_t maxlen,const char * value,int flags,int min,int max)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 
fmtwstr(char * buffer,int32_t currlen,int32_t maxlen,const wchar_t * value,int flags,int min,int max)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 
fmtint(char * buffer,int32_t currlen,int32_t maxlen,int64_t value,int base,int min,int max,int flags)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 
abs_val(LDOUBLE value)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 
pow10(int exp)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 
bround(LDOUBLE value)622 static int64_t bround(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 
fmtfp(char * buffer,int32_t currlen,int32_t maxlen,LDOUBLE fvalue,int min,int max,int flags)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 = bround((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 #ifdef TEST_PROGRAM
851 #include "unittests.h"
852 
853 #ifndef LONG_STRING
854 #define LONG_STRING 1024
855 #endif
856 #define MSGLEN    80
857 
main(int argc,char * argv[])858 int main(int argc, char *argv[])
859 {
860    Unittests bsnprintf_test("bsnprintf_test");
861    char buf1[LONG_STRING];
862    char buf2[LONG_STRING];
863    char msg[MSGLEN];
864    bool check_cont, check_nr;
865    int pcount, bcount;
866 
867 #ifdef FP_OUTPUT
868    const char *fp_fmt[] = {
869       "%-1.5f",
870       "%1.5f",
871       "%123.9f",
872       "%10.5f",
873       "% 10.5f",
874       "%+22.9f",
875       "%+4.9f",
876       "%01.3f",
877       "%4f",
878       "%3.1f",
879       "%3.2f",
880       "%.0f",
881       "%.1f",
882       "%.2f",
883       NULL
884    };
885    double fp_nums[] = { 1.05, -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
886       0.9996, 1.996, 4.136, 6442452944.1234, 0, 23365.5
887    };
888 #endif
889    const char *int_fmt[] = {
890       "%-1.5d",
891       "%1.5d",
892       "%123.9d",
893       "%5.5d",
894       "%10.5d",
895       "% 10.5d",
896       "%+22.33d",
897       "%01.3d",
898       "%4d",
899       "%-1.5ld",
900       "%1.5ld",
901       "%123.9ld",
902       "%5.5ld",
903       "%10.5ld",
904       "% 10.5ld",
905       "%+22.33ld",
906       "%01.3ld",
907       "%4ld",
908       NULL
909    };
910    long int_nums[] = { -1, 134, 91340, 341, 0203, 0 };
911 
912    const char *ll_fmt[] = {
913       "%-1.8lld",
914       "%1.8lld",
915       "%123.9lld",
916       "%5.8lld",
917       "%10.5lld",
918       "% 10.8lld",
919       "%+22.33lld",
920       "%01.3lld",
921       "%4lld",
922       NULL
923    };
924    int64_t ll_nums[] = { -1976, 789134567890LL, 91340, 34123, 0203, 0 };
925 
926    const char *s_fmt[] = {
927       "%-1.8s",
928       "%1.8s",
929       "%123.9s",
930       "%5.8s",
931       "%10.5s",
932       "% 10.3s",
933       "%+22.1s",
934       "%01.3s",
935       "%s",
936       "%10s",
937       "%3s",
938       "%3.0s",
939       "%3.s",
940       NULL
941    };
942    const char *s_nums[] = { "abc", "def", "ghi", "123", "4567", "a", "bb", "ccccccc", NULL};
943 
944    const char *ls_fmt[] = {
945       "%-1.8ls",
946       "%1.8ls",
947       "%123.9ls",
948       "%5.8ls",
949       "%10.5ls",
950       "% 10.3ls",
951       "%+22.1ls",
952       "%01.3ls",
953       "%ls",
954       "%10ls",
955       "%3ls",
956       "%3.0ls",
957       "%3.ls",
958       NULL
959    };
960    const wchar_t *ls_nums[] = { L"abc", L"def", L"ghi", L"123", L"4567", L"a", L"bb", L"ccccccc", NULL};
961 
962    int x, y;
963 
964    printf("\n\tTesting bsnprintf against system sprintf...\n\n");
965 #ifdef FP_OUTPUT
966    printf("Testing bsnprintf float format codes\n");
967    for (x = 0; fp_fmt[x] != NULL; x++){
968       check_cont = true;
969       check_nr = true;
970       for (y = 0; fp_nums[y] != 0; y++) {
971          bcount = bsnprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
972          pcount = sprintf(buf2, fp_fmt[x], fp_nums[y]);
973          if (bcount != pcount) {
974             check_nr = false;
975          }
976          if (strcmp(buf1, buf2) != 0){
977             check_cont = false;
978          }
979       }
980       snprintf(msg, MSGLEN, "Checking return length for format %s", fp_fmt[x]);
981       ok(check_nr, msg);
982       snprintf(msg, MSGLEN, "Checking format %s", fp_fmt[x]);
983       ok(check_cont, msg);
984    }
985 #endif
986 
987    printf("Testing bsnprintf int format codes\n");
988    for (x = 0; int_fmt[x] != NULL; x++){
989       check_cont = true;
990       check_nr = true;
991       for (y = 0; int_nums[y] != 0; y++) {
992          bcount = bsnprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
993          pcount = sprintf(buf2, int_fmt[x], int_nums[y]);
994          if (bcount != pcount) {
995             check_nr = false;
996          }
997          if (strcmp(buf1, buf2) != 0) {
998             check_cont = false;
999          }
1000       }
1001       snprintf(msg, MSGLEN, "Checking return length for format %s", int_fmt[x]);
1002       ok(check_nr, msg);
1003       snprintf(msg, MSGLEN, "Checking format %s", int_fmt[x]);
1004       ok(check_cont, msg);
1005    }
1006 
1007    printf("Testing bsnprintf long format codes\n");
1008    for (x = 0; ll_fmt[x] != NULL; x++) {
1009       check_cont = true;
1010       check_nr = true;
1011       for (y = 0; ll_nums[y] != 0; y++) {
1012          bcount = bsnprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
1013          pcount = sprintf(buf2, ll_fmt[x], ll_nums[y]);
1014          if (bcount != pcount) {
1015             check_nr = false;
1016          }
1017          if (strcmp(buf1, buf2) != 0) {
1018             check_cont = false;
1019          }
1020       }
1021       snprintf(msg, MSGLEN, "Checking return length for format %s", ll_fmt[x]);
1022       ok(check_nr, msg);
1023       snprintf(msg, MSGLEN, "Checking format %s", ll_fmt[x]);
1024       ok(check_cont, msg);
1025    }
1026 
1027    printf("Testing bsnprintf str format codes\n");
1028    for (x = 0; s_fmt[x] != NULL; x++) {
1029       check_cont = true;
1030       check_nr = true;
1031       for (y = 0; s_nums[y] != 0; y++) {
1032          bcount = bsnprintf(buf1, sizeof(buf1), s_fmt[x], s_nums[y]);
1033          pcount = sprintf(buf2, s_fmt[x], s_nums[y]);
1034          if (bcount != pcount) {
1035             check_nr = false;
1036          }
1037          if (strcmp(buf1, buf2) != 0) {
1038             check_cont = false;
1039          }
1040       }
1041       snprintf(msg, MSGLEN, "Checking return length for format %s", s_fmt[x]);
1042       ok(check_nr, msg);
1043       snprintf(msg, MSGLEN, "Checking format %s", s_fmt[x]);
1044       ok(check_cont, msg);
1045    }
1046 
1047    printf("Testing bsnprintf long str format codes\n");
1048    for (x = 0; ls_fmt[x] != NULL; x++) {
1049       check_cont = true;
1050       check_nr = true;
1051       for (y = 0; ls_nums[y] != 0; y++) {
1052          bcount = bsnprintf(buf1, sizeof(buf1), ls_fmt[x], ls_nums[y]);
1053          pcount = sprintf(buf2, ls_fmt[x], ls_nums[y]);
1054          if (bcount != pcount) {
1055             check_nr = false;
1056          }
1057          if (strcmp(buf1, buf2) != 0) {
1058             check_cont = false;
1059          }
1060       }
1061       snprintf(msg, MSGLEN, "Checking return length for format %s", ls_fmt[x]);
1062       ok(check_nr, msg);
1063       snprintf(msg, MSGLEN, "Checking format %s", ls_fmt[x]);
1064       ok(check_cont, msg);
1065    }
1066 
1067    return report();
1068 }
1069 #endif /* TEST_PROGRAM */
1070 
1071 #endif /* USE_BSNPRINTF */
1072