]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/bsnprintf.c
Backport from Bacula Enterprise
[bacula/bacula] / bacula / src / lib / bsnprintf.c
index 07b94bb135ab2cd6ae2abb46f759275463d73cfc..f669f52751bf5142a688c879cf81c0e9e09dcdc0 100644 (file)
@@ -1,3 +1,22 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2015 Kern Sibbald
+   Copyright (C) 2005-2014 Free Software Foundation Europe e.V.
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is 
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
 /*
  * Copyright Patrick Powell 1995
  *
  *
  *   Kern Sibbald, November MMV
  *
- *   Version $Id$
  */
-/*
-   Bacula® - The Network Backup Solution
-
-   Copyright (C) 2005-2006 Free Software Foundation Europe e.V.
-
-   The main author of Bacula is Kern Sibbald, with contributions from
-   many others, a complete list can be found in the file AUTHORS.
-   This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version two of the GNU General Public
-   License as published by the Free Software Foundation plus additions
-   that are listed in the file LICENSE.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA.
-
-   Bacula® is a registered trademark of John Walker.
-   The licensor of Bacula is the Free Software Foundation Europe
-   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
-   Switzerland, email:ftf@fsfeurope.org.
-*/
 
 
 
 #include "bacula.h"
+#include <wchar.h>
+
 #define FP_OUTPUT 1 /* Bacula uses floating point */
+
 /* Define the following if you want all the features of
  *  normal printf, but with all the security problems.
  *  For Bacula we turn this off, and it silently ignores
  *  formats that could pose a security problem.
  */
-#undef SECURITY_PROBLEM 
+#undef SECURITY_PROBLEM
 
 #ifdef USE_BSNPRINTF
 
@@ -62,7 +56,9 @@
 
 int bvsnprintf(char *buffer, int32_t maxlen, const char *format, va_list args);
 static int32_t fmtstr(char *buffer, int32_t currlen, int32_t maxlen,
-                   char *value, int flags, int min, int max);
+                   const char *value, int flags, int min, int max);
+static int32_t fmtwstr(char *buffer, int32_t currlen, int32_t maxlen,
+                   const wchar_t *value, int flags, int min, int max);
 static int32_t fmtint(char *buffer, int32_t currlen, int32_t maxlen,
                    int64_t value, int base, int min, int max, int flags);
 
@@ -76,8 +72,14 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
 #define fmtfp(b, c, m, f, min, max, fl) currlen
 #endif
 
-#define outch(c) {int len=currlen; if (currlen++ < maxlen) { buffer[len] = (c);}}
-
+/*
+ *  NOTE!!!! do not use this #define with a construct such
+ *    as outch(--place);.  It just will NOT work, because the
+ *    decrement of place is done ONLY if there is room in the
+ *    output buffer.
+ */
+#define outch(c) {int len=currlen; if (currlen < maxlen) \
+        { buffer[len] = (c); currlen++; }}
 
 /* format read states */
 #define DP_S_DEFAULT 0
@@ -100,10 +102,12 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
 #define DP_F_DOT        (1 << 7)
 
 /* Conversion Flags */
-#define DP_C_INT16   1
+#define DP_C_INT16    1
 #define DP_C_INT32    2
-#define DP_C_LDOUBLE 3
-#define DP_C_INT64   4
+#define DP_C_LDOUBLE  3
+#define DP_C_INT64    4
+#define DP_C_WCHAR    5      /* wide characters */
+#define DP_C_SIZE_T   6
 
 #define char_to_int(p) ((p)- '0')
 #undef MAX
@@ -112,8 +116,8 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
 /*
   You might ask why does Bacula have it's own printf routine? Well,
   There are two reasons: 1. Here (as opposed to library routines), we
-  define %d and %ld to be 32 bit; %lld and %q to be 64 bit.  2. We 
-  disable %n for security reasons.                
+  define %d and %ld to be 32 bit; %lld and %q to be 64 bit.  2. We
+  disable %n for security reasons.
  */
 
 int bsnprintf(char *str, int32_t size, const char *fmt,  ...)
@@ -133,6 +137,7 @@ int bvsnprintf(char *buffer, int32_t maxlen, const char *format, va_list args)
    char ch;
    int64_t value;
    char *strvalue;
+   wchar_t *wstrvalue;
    int min;
    int max;
    int state;
@@ -151,9 +156,9 @@ int bvsnprintf(char *buffer, int32_t maxlen, const char *format, va_list args)
    *buffer = 0;
 
    while (state != DP_S_DONE) {
-      if ((ch == '\0') || (currlen >= maxlen))
+      if ((ch == '\0') || (currlen >= maxlen)) {
          state = DP_S_DONE;
-
+      }
       switch (state) {
       case DP_S_DEFAULT:
          if (ch == '%') {
@@ -231,11 +236,17 @@ int bvsnprintf(char *buffer, int32_t maxlen, const char *format, va_list args)
          case 'l':
             cflags = DP_C_INT32;
             ch = *format++;
-            if (ch == 'l') {       /* It's a long long */
+            if (ch == 's') {
+               cflags = DP_C_WCHAR;
+            } else if (ch == 'l') {       /* It's a long long */
                cflags = DP_C_INT64;
                ch = *format++;
             }
             break;
+         case 'z':
+            cflags = DP_C_SIZE_T;
+            ch = *format++;
+            break;
          case 'L':
             cflags = DP_C_LDOUBLE;
             ch = *format++;
@@ -259,6 +270,8 @@ int bvsnprintf(char *buffer, int32_t maxlen, const char *format, va_list args)
                value = va_arg(args, int32_t);
             } else if (cflags == DP_C_INT64) {
                value = va_arg(args, int64_t);
+            } else if (cflags == DP_C_SIZE_T) {
+               value = va_arg(args, ssize_t);
             } else {
                value = va_arg(args, int);
             }
@@ -285,6 +298,8 @@ int bvsnprintf(char *buffer, int32_t maxlen, const char *format, va_list args)
                value = va_arg(args, uint32_t);
             } else if (cflags == DP_C_INT64) {
                value = va_arg(args, uint64_t);
+            } else if (cflags == DP_C_SIZE_T) {
+               value = va_arg(args, size_t);
             } else {
                value = va_arg(args, unsigned int);
             }
@@ -319,44 +334,58 @@ int bvsnprintf(char *buffer, int32_t maxlen, const char *format, va_list args)
             currlen = fmtfp(buffer, currlen, maxlen, fvalue, min, max, flags);
             break;
          case 'c':
-            outch(va_arg(args, int));
+            ch = va_arg(args, int);
+            outch(ch);
             break;
          case 's':
-            strvalue = va_arg(args, char *);
-            currlen = fmtstr(buffer, currlen, maxlen, strvalue, flags, min, max);
+            if (cflags != DP_C_WCHAR) {
+              strvalue = va_arg(args, char *);
+              if (!strvalue) {
+                 strvalue = (char *)"<NULL>";
+              }
+              currlen = fmtstr(buffer, currlen, maxlen, strvalue, flags, min, max);
+            } else {
+              /* %ls means to edit wide characters */
+              wstrvalue = va_arg(args, wchar_t *);
+              if (!wstrvalue) {
+                 wstrvalue = (wchar_t *)L"<NULL>";
+              }
+              currlen = fmtwstr(buffer, currlen, maxlen, wstrvalue, flags, min, max);
+            }
             break;
          case 'p':
             flags |= DP_F_UNSIGNED;
-            strvalue = va_arg(args, char *);
-            currlen = fmtint(buffer, currlen, maxlen, (long)strvalue, 16, min, max, flags);
+            if (sizeof(char *) == 4) {
+               value = va_arg(args, uint32_t);
+            } else if (sizeof(char *) == 8) {
+               value = va_arg(args, uint64_t);
+            } else {
+               value = 0;             /* we have a problem */
+            }
+            currlen = fmtint(buffer, currlen, maxlen, value, 16, min, max, flags);
             break;
+
+#ifdef SECURITY_PROBLEM
          case 'n':
             if (cflags == DP_C_INT16) {
                int16_t *num;
                num = va_arg(args, int16_t *);
-#ifdef SECURITY_PROBLEM
                *num = currlen;
-#endif
             } else if (cflags == DP_C_INT32) {
                int32_t *num;
                num = va_arg(args, int32_t *);
-#ifdef SECURITY_PROBLEM
                *num = (int32_t)currlen;
-#endif
             } else if (cflags == DP_C_INT64) {
                int64_t *num;
                num = va_arg(args, int64_t *);
-#ifdef SECURITY_PROBLEM
                *num = (int64_t)currlen;
-#endif
             } else {
                int32_t *num;
                num = va_arg(args, int32_t *);
-#ifdef SECURITY_PROBLEM
                *num = (int32_t)currlen;
-#endif
             }
             break;
+#endif
          case '%':
             outch(ch);
             break;
@@ -389,14 +418,12 @@ int bvsnprintf(char *buffer, int32_t maxlen, const char *format, va_list args)
 }
 
 static int32_t fmtstr(char *buffer, int32_t currlen, int32_t maxlen,
-                   char *value, int flags, int min, int max)
+                   const char *value, int flags, int min, int max)
 {
    int padlen, strln;              /* amount to pad */
    int cnt = 0;
+   char ch;
 
-   if (value == 0) {
-      value = "<NULL>";
-   }
 
    if (flags & DP_F_DOT && max < 0) {   /* Max not specified */
       max = 0;
@@ -420,7 +447,50 @@ static int32_t fmtstr(char *buffer, int32_t currlen, int32_t maxlen,
       --padlen;
    }
    while (*value && (cnt < max)) {
-      outch(*value++);
+      ch = *value++;
+      outch(ch);
+      ++cnt;
+   }
+   while (padlen < 0) {
+      outch(' ');
+      ++padlen;
+   }
+   return currlen;
+}
+
+static int32_t fmtwstr(char *buffer, int32_t currlen, int32_t maxlen,
+                   const wchar_t *value, int flags, int min, int max)
+{
+   int padlen, strln;              /* amount to pad */
+   int cnt = 0;
+   char ch;
+
+
+   if (flags & DP_F_DOT && max < 0) {   /* Max not specified */
+      max = 0;
+   } else if (max < 0) {
+      max = maxlen;
+   }
+   strln = wcslen(value);
+   if (strln > max) {
+      strln = max;                /* truncate to max */
+   }
+   padlen = min - strln;
+   if (padlen < 0) {
+      padlen = 0;
+   }
+   if (flags & DP_F_MINUS) {
+      padlen = -padlen;            /* Left Justify */
+   }
+
+   while (padlen > 0) {
+      outch(' ');
+      --padlen;
+   }
+   while (*value && (cnt < max)) {
+
+      ch = (*value++) & 0xff;
+      outch(ch);
       ++cnt;
    }
    while (padlen < 0) {
@@ -437,11 +507,12 @@ static int32_t fmtint(char *buffer, int32_t currlen, int32_t maxlen,
 {
    int signvalue = 0;
    uint64_t uvalue;
-   char convert[20];
+   char convert[25];
    int place = 0;
    int spadlen = 0;                /* amount to space pad */
    int zpadlen = 0;                /* amount to zero pad */
    int caps = 0;
+   const char *cvt_string;
 
    if (max < 0) {
       max = 0;
@@ -464,12 +535,12 @@ static int32_t fmtint(char *buffer, int32_t currlen, int32_t maxlen,
       caps = 1;                    /* Should characters be upper case? */
    }
 
+   cvt_string = caps ? "0123456789ABCDEF" : "0123456789abcdef";
    do {
-      convert[place++] = (caps ? "0123456789ABCDEF" : "0123456789abcdef")
-         [uvalue % (unsigned)base];
+      convert[place++] = cvt_string[uvalue % (unsigned)base];
       uvalue = (uvalue / (unsigned)base);
-   } while (uvalue && (place < 20));
-   if (place == 20) {
+   } while (uvalue && (place < (int)sizeof(convert)));
+   if (place == (int)sizeof(convert)) {
       place--;
    }
    convert[place] = 0;
@@ -511,9 +582,10 @@ static int32_t fmtint(char *buffer, int32_t currlen, int32_t maxlen,
       }
    }
 
-   /* Digits */
+   /* Output digits backward giving correct order */
    while (place > 0) {
-      outch(convert[--place]);
+      place--;
+      outch(convert[place]);
    }
 
    /* Left Justified spaces */
@@ -548,11 +620,11 @@ static LDOUBLE pow10(int exp)
    return result;
 }
 
-static long round(LDOUBLE value)
+static int64_t round(LDOUBLE value)
 {
-   long intpart;
+   int64_t intpart;
 
-   intpart = (long)value;
+   intpart = (int64_t)value;
    value = value - intpart;
    if (value >= 0.5)
       intpart++;
@@ -566,12 +638,13 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
    int signvalue = 0;
    LDOUBLE ufvalue;
 #ifndef HAVE_FCVT
-   char iconvert[20];
-   char fconvert[20];
+   char iconvert[311];
+   char fconvert[311];
 #else
    char iconvert[311];
    char fconvert[311];
    char *result;
+   char dummy[10];
    int dec_pt, sig;
    int r_length;
    extern char *fcvt(double value, int ndigit, int *decpt, int *sign);
@@ -580,11 +653,11 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
    int fplace = 0;
    int padlen = 0;                 /* amount to pad */
    int zpadlen = 0;
-   int caps = 0;
    int64_t intpart;
    int64_t fracpart;
+   const char *cvt_str;
 
-   /* 
+   /*
     * AIX manpage says the default is 0, but Solaris says the default
     * is 6, and sprintf on AIX defaults to 6
     */
@@ -600,16 +673,11 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
    else if (flags & DP_F_SPACE)
       signvalue = ' ';
 
-#if 0
-   if (flags & DP_F_UP)
-      caps = 1;                    /* Should characters be upper case? */
-#endif
-
 #ifndef HAVE_FCVT
-   intpart = (long)ufvalue;
+   intpart = (int64_t)ufvalue;
 
-   /* 
-    * Sorry, we only support 9 digits past the decimal because of our 
+   /*
+    * Sorry, we only support 9 digits past the decimal because of our
     * conversion method
     */
    if (max > 9)
@@ -624,40 +692,52 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
       intpart++;
       fracpart -= (int64_t)pow10(max);
    }
+
 #ifdef DEBUG_SNPRINTF
-   printf("fmtfp: %g %d.%d min=%d max=%d\n",
+   printf("fmtfp: %g %lld.%lld min=%d max=%d\n",
           (double)fvalue, intpart, fracpart, min, max);
 #endif
 
    /* Convert integer part */
+   cvt_str = "0123456789";
    do {
-      iconvert[iplace++] =
-         (caps ? "0123456789ABCDEF" : "0123456789abcdef")[intpart % 10];
+      iconvert[iplace++] = cvt_str[(int)(intpart % 10)];
       intpart = (intpart / 10);
-   } while (intpart && (iplace < 20));
-   if (iplace == 20)
+   } while (intpart && (iplace < (int)sizeof(iconvert)));
+
+   if (iplace == (int)sizeof(fconvert)) {
       iplace--;
+   }
    iconvert[iplace] = 0;
 
    /* Convert fractional part */
-   do {
-      fconvert[fplace++] =
-         (caps ? "0123456789ABCDEF" : "0123456789abcdef")[fracpart % 10];
+   cvt_str = "0123456789";
+   for (int fiter = max; fiter > 0; fiter--) {
+      fconvert[fplace++] = cvt_str[fracpart % 10];
       fracpart = (fracpart / 10);
-   } while (fracpart && (fplace < 20));
-   if (fplace == 20)
+   }
+
+   if (fplace == (int)sizeof(fconvert)) {
       fplace--;
+   }
    fconvert[fplace] = 0;
 #else                              /* use fcvt() */
-   if (max > 310)
+   if (max > 310) {
       max = 310;
+   }
 # ifdef HAVE_FCVTL
    result = fcvtl(ufvalue, max, &dec_pt, &sig);
 # else
    result = fcvt(ufvalue, max, &dec_pt, &sig);
 # endif
 
-   r_length = strlen(result);
+   if (!result) {
+      r_length = 0;
+      dummy[0] = 0;
+      result = dummy;
+   } else {
+      r_length = strlen(result);
+   }
 
    /*
     * Fix broken fcvt implementation returns..
@@ -679,8 +759,9 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
 
       fplace = 0;
 
-      while (r_length)
+      while (r_length) {
          fconvert[fplace++] = result[--r_length];
+      }
 
       while ((dec_pt < 0) && (fplace < max)) {
          fconvert[fplace++] = '0';
@@ -733,7 +814,8 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
    }
 
    while (iplace > 0) {
-      outch(iconvert[--iplace]);
+      iplace--;
+      outch(iconvert[iplace]);
    }
 
 
@@ -748,7 +830,8 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
    if (max > 0) {
       outch('.');
       while (fplace > 0) {
-         outch(fconvert[--fplace]);
+         fplace--;
+         outch(fconvert[fplace]);
       }
    }
 
@@ -771,13 +854,14 @@ static int32_t fmtfp(char *buffer, int32_t currlen, int32_t maxlen,
 #ifndef LONG_STRING
 #define LONG_STRING 1024
 #endif
-int main(void)
+
+int main(int argc, char *argv[])
 {
    char buf1[LONG_STRING];
    char buf2[LONG_STRING];
 
 #ifdef FP_OUTPUT
-   char *fp_fmt[] = {
+   const char *fp_fmt[] = {
       "%-1.5f",
       "%1.5f",
       "%123.9f",
@@ -791,13 +875,14 @@ int main(void)
       "%3.2f",
       "%.0f",
       "%.1f",
+      "%.2f",
       NULL
    };
-   double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
-      0.9996, 1.996, 4.136, 6442452944.1234, 0
+   double fp_nums[] = { 1.05, -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
+      0.9996, 1.996, 4.136, 6442452944.1234, 0, 23365.5
    };
 #endif
-   char *int_fmt[] = {
+   const char *int_fmt[] = {
       "%-1.5d",
       "%1.5d",
       "%123.9d",
@@ -820,7 +905,7 @@ int main(void)
    };
    long int_nums[] = { -1, 134, 91340, 341, 0203, 0 };
 
-   char *ll_fmt[] = {
+   const char *ll_fmt[] = {
       "%-1.8lld",
       "%1.8lld",
       "%123.9lld",
@@ -834,7 +919,7 @@ int main(void)
    };
    int64_t ll_nums[] = { -1976, 789134567890LL, 91340, 34123, 0203, 0 };
 
-   char *s_fmt[] = {
+   const char *s_fmt[] = {
       "%-1.8s",
       "%1.8s",
       "%123.9s",
@@ -850,8 +935,25 @@ int main(void)
       "%3.s",
       NULL
    };
-   char *s_nums[] = { "abc", "def", "ghi", "123", "4567", "a", "bb", "ccccccc", NULL};
-
+   const char *s_nums[] = { "abc", "def", "ghi", "123", "4567", "a", "bb", "ccccccc", NULL};
+
+   const char *ls_fmt[] = {
+      "%-1.8ls",
+      "%1.8ls",
+      "%123.9ls",
+      "%5.8ls",
+      "%10.5ls",
+      "% 10.3ls",
+      "%+22.1ls",
+      "%01.3ls",
+      "%ls",
+      "%10ls",
+      "%3ls",
+      "%3.0ls",
+      "%3.ls",
+      NULL
+   };
+   const wchar_t *ls_nums[] = { L"abc", L"def", L"ghi", L"123", L"4567", L"a", L"bb", L"ccccccc", NULL};
 
    int x, y;
    int fail = 0;
@@ -933,8 +1035,31 @@ int main(void)
       }
    }
 
+   for (x = 0; ls_fmt[x] != NULL; x++) {
+      for (y = 0; ls_nums[y] != 0; y++) {
+         int pcount, bcount;
+         bcount = bsnprintf(buf1, sizeof(buf1), ls_fmt[x], ls_nums[y]);
+         printf("%s\n", buf1);
+         pcount = sprintf(buf2, ls_fmt[x], ls_nums[y]);
+         if (bcount != pcount) {
+            printf("bsnprintf count %d doesn't match sprintf count %d\n",
+               bcount, pcount);
+         }
+         if (strcmp(buf1, buf2)) {
+            printf
+               ("bsnprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n",
+                ls_fmt[x], buf1, buf2);
+            fail++;
+         }
+         num++;
+      }
+   }
+
+
 
    printf("%d tests failed out of %d.\n", fail, num);
+
+   exit(fail > 0);
 }
 #endif /* TEST_PROGRAM */