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