]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/lex.c
Added fix for bug #1275 where acl or xattr data is saved for virtual filenames genera...
[bacula/bacula] / bacula / src / lib / lex.c
index 11b9af015639f7bda9f64e1c82df3dda8feb6baa..03b64ac0d23498426259807bbc5a9c9bd7756be5 100644 (file)
@@ -1,3 +1,30 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2000-2008 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 and included
+   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 Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
 /*
  * Lexical scanner for Bacula configuration file
  *
@@ -6,21 +33,6 @@
  *   Version $Id$
  *
  */
-/*
-   Copyright (C) 2000-2006 Kern Sibbald
-
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License
-   version 2 as amended with additional clauses defined in the
-   file LICENSE in the main source directory.
-
-   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 
-   the file LICENSE for additional details.
-
- */
-
 
 #include "bacula.h"
 #include "lex.h"
@@ -121,7 +133,12 @@ LEX *lex_close_file(LEX *lf)
    Dmsg1(dbglvl, "Close lex file: %s\n", lf->fname);
 
    of = lf->next;
-   fclose(lf->fd);
+   if (lf->bpipe) {
+      close_bpipe(lf->bpipe);
+      lf->bpipe = NULL;
+   } else {
+      fclose(lf->fd);
+   }
    Dmsg1(dbglvl, "Close cfg file %s\n", lf->fname);
    free(lf->fname);
    if (of) {
@@ -152,10 +169,18 @@ LEX *lex_open_file(LEX *lf, const char *filename, LEX_ERROR_HANDLER *scan_error)
 {
    LEX *nf;
    FILE *fd;
+   BPIPE *bpipe = NULL;
    char *fname = bstrdup(filename);
 
 
-   if ((fd = fopen(fname, "r")) == NULL) {
+   if (fname[0] == '|') {
+      if ((bpipe = open_bpipe(fname+1, 0, "rb")) == NULL) {
+         free(fname);
+         return NULL;
+      }
+      fd = bpipe->rfd;
+   } else if ((fd = fopen(fname, "rb")) == NULL) {
+      free(fname);
       return NULL;
    }
    Dmsg1(400, "Open config file: %s\n", fname);
@@ -165,6 +190,11 @@ LEX *lex_open_file(LEX *lf, const char *filename, LEX_ERROR_HANDLER *scan_error)
       memset(lf, 0, sizeof(LEX));
       lf->next = nf;                  /* if have lf, push it behind new one */
       lf->options = nf->options;      /* preserve user options */
+      /*
+       * preserve err_type to prevent bacula exiting on 'reload' 
+       * if config is invalid. Fixes bug #877         
+       */
+      lf->err_type = nf->err_type;    
    } else {
       lf = nf;                        /* start new packet */
       memset(lf, 0, sizeof(LEX));
@@ -176,6 +206,7 @@ LEX *lex_open_file(LEX *lf, const char *filename, LEX_ERROR_HANDLER *scan_error)
       lex_set_default_error_handler(lf);
    }
    lf->fd = fd;
+   lf->bpipe = bpipe;
    lf->fname = fname;
    lf->state = lex_none;
    lf->ch = L_EOL;
@@ -192,7 +223,8 @@ LEX *lex_open_file(LEX *lf, const char *filename, LEX_ERROR_HANDLER *scan_error)
 int lex_get_char(LEX *lf)
 {
    if (lf->ch == L_EOF) {
-      Emsg0(M_ABORT, 0, _("get_char: called after EOF\n"));
+      Emsg0(M_ABORT, 0, _("get_char: called after EOF."
+         " You may have a open double quote without the closing double quote.\n"));
    }
    if (lf->ch == L_EOL) {
       if (bfgets(lf->line, MAXSTRING, lf->fd) == NULL) {
@@ -265,6 +297,10 @@ static const char *lex_state_to_str(int state)
    case lex_identifier:    return _("identifier");
    case lex_string:        return _("string");
    case lex_quoted_string: return _("quoted_string");
+   case lex_include:       return _("include");
+   case lex_include_quoted_string: return _("include_quoted_string");
+   case lex_utf8_bom:      return _("UTF-8 Byte Order Mark");
+   case lex_utf16_le_bom:  return _("UTF-16le Byte Order Mark");
    default:                return "??????";
    }
 }
@@ -292,6 +328,8 @@ const char *lex_tok_to_str(int token)
    case T_EOF:             return "T_EOF";
    case T_COMMA:           return "T_COMMA";
    case T_EOL:             return "T_EOL";
+   case T_UTF8_BOM:        return "T_UTF8_BOM";
+   case T_UTF16_BOM:       return "T_UTF16_BOM";
    default:                return "??????";
    }
 }
@@ -313,6 +351,23 @@ static uint32_t scan_pint(LEX *lf, char *str)
    return (uint32_t)val;
 }
 
+static uint64_t scan_pint64(LEX *lf, char *str)
+{
+   uint64_t val = 0;
+   if (!is_a_number(str)) {
+      scan_err1(lf, _("expected a positive integer number, got: %s"), str);
+      /* NOT REACHED */
+   } else {
+      errno = 0;
+      val = str_to_uint64(str);
+      if (errno != 0) {
+         scan_err1(lf, _("expected a positive integer number, got: %s"), str);
+         /* NOT REACHED */
+      }
+   }
+   return val;
+}
+
 /*
  *
  * Get the next token from the input
@@ -324,6 +379,13 @@ lex_get_token(LEX *lf, int expect)
    int ch;
    int token = T_NONE;
    bool esc_next = false;
+   /* Unicode files, especially on Win32, may begin with a "Byte Order Mark"
+      to indicate which transmission format the file is in. The codepoint for
+      this mark is U+FEFF and is represented as the octets EF-BB-BF in UTF-8
+      and as FF-FE in UTF-16le(little endian) and  FE-FF in UTF-16(big endian).
+      We use a distinct state for UTF-8 and UTF-16le, and use bom_bytes_seen
+      to tell which byte we are expecting. */
+   int bom_bytes_seen = 0;
 
    Dmsg0(dbglvl, "enter lex_get_token\n");
    while (token == T_NONE) {
@@ -395,6 +457,26 @@ lex_get_token(LEX *lf, int expect)
             lf->state = lex_include;
             begin_str(lf, 0);
             break;
+         case 0xEF: /* probably a UTF-8 BOM */
+         case 0xFF: /* probably a UTF-16le BOM */
+         case 0xFE: /* probably a UTF-16be BOM (error)*/
+            if (lf->line_no != 1 || lf->col_no != 1)
+            {
+               lf->state = lex_string;
+               begin_str(lf, ch);
+            } else {
+               bom_bytes_seen = 1;
+               if (ch == 0xEF) {
+                  lf->state = lex_utf8_bom;
+               } else if (ch == 0xFF) {
+                  lf->state = lex_utf16_le_bom;
+               } else {
+                  scan_err0(lf, _("This config file appears to be in an "
+                     "unsupported Unicode format (UTF-16be). Please resave as UTF-8\n"));
+                  return T_ERROR;
+               }
+            }
+            break;
          default:
             lf->state = lex_string;
             begin_str(lf, ch);
@@ -504,28 +586,90 @@ lex_get_token(LEX *lf, int expect)
          }
          add_str(lf, ch);
          break;
+      case lex_include_quoted_string:
+         if (ch == L_EOF) {
+            token = T_ERROR;
+            break;
+         }
+         if (esc_next) {
+            add_str(lf, ch);
+            esc_next = false;
+            break;
+         }
+         if (ch == '\\') {
+            esc_next = true;
+            break;
+         }
+         if (ch == '"') {
+            /* Keep the original LEX so we can print an error if the included file can't be opened. */
+            LEX* lfori = lf;
+            /* Skip the double quote when restarting parsing */
+            lex_get_char(lf);
+
+            lf->state = lex_none;
+            lf = lex_open_file(lf, lf->str, lf->scan_error);
+            if (lf == NULL) {
+               berrno be;
+               scan_err2(lfori, _("Cannot open included config file %s: %s\n"),
+                  lfori->str, be.bstrerror());
+               return T_ERROR;
+            }
+            break;
+         }
+         add_str(lf, ch);
+         break;
       case lex_include:            /* scanning a filename */
          if (ch == L_EOF) {
             token = T_ERROR;
             break;
          }
+         if (ch == '"') {
+            lf->state = lex_include_quoted_string;
+            break;
+         }
+
+
          if (B_ISSPACE(ch) || ch == '\n' || ch == L_EOL || ch == '}' || ch == '{' ||
              ch == ';' || ch == ','   || ch == '"' || ch == '#') {
             /* Keep the original LEX so we can print an error if the included file can't be opened. */
             LEX* lfori = lf;
-            
+
             lf->state = lex_none;
             lf = lex_open_file(lf, lf->str, lf->scan_error);
             if (lf == NULL) {
                berrno be;
                scan_err2(lfori, _("Cannot open included config file %s: %s\n"),
-                  lfori->str, be.strerror());
+                  lfori->str, be.bstrerror());
                return T_ERROR;
             }
             break;
          }
          add_str(lf, ch);
          break;
+      case lex_utf8_bom:
+         /* we only end up in this state if we have read an 0xEF 
+            as the first byte of the file, indicating we are probably
+            reading a UTF-8 file */
+         if (ch == 0xBB && bom_bytes_seen == 1) {
+            bom_bytes_seen++;
+         } else if (ch == 0xBF && bom_bytes_seen == 2) {
+            token = T_UTF8_BOM;
+            lf->state = lex_none;
+         } else {
+            token = T_ERROR;
+         }
+         break;
+      case lex_utf16_le_bom:
+         /* we only end up in this state if we have read an 0xFF 
+            as the first byte of the file -- indicating that we are
+            probably dealing with an Intel based (little endian) UTF-16 file*/
+         if (ch == 0xFE) {
+            token = T_UTF16_BOM;
+            lf->state = lex_none;
+         } else {
+            token = T_ERROR;
+         }
+         break;
       }
       Dmsg4(dbglvl, "ch=%d state=%s token=%s %c\n", ch, lex_state_to_str(lf->state),
         lex_tok_to_str(token), ch);
@@ -602,6 +746,26 @@ lex_get_token(LEX *lf, int expect)
       }
       break;
 
+   case T_PINT64_RANGE:
+      if (token == T_NUMBER) {
+         lf->pint64_val = scan_pint64(lf, lf->str);
+         lf->pint64_val2 = lf->pint64_val;
+         token = T_PINT64;
+      } else {
+         char *p = strchr(lf->str, '-');
+         if (!p) {
+            scan_err2(lf, _("expected an integer or a range, got %s: %s"),
+               lex_tok_to_str(token), lf->str);
+            token = T_ERROR;
+            break;
+         }
+         *p++ = 0;                       /* terminate first half of range */
+         lf->pint64_val  = scan_pint64(lf, lf->str);
+         lf->pint64_val2 = scan_pint64(lf, p);
+         token = T_PINT64_RANGE;
+      }
+      break;
+
    case T_NAME:
       if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
          scan_err2(lf, _("expected a name, got %s: %s"),