X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Flib%2Flex.c;h=03b64ac0d23498426259807bbc5a9c9bd7756be5;hb=2df589363f0c60f94ca9d5e856dfaf7cc5fcb178;hp=2fa93a7d13e5ea0fd4413d4b960f124c30265be7;hpb=b18f11080d87758b5f809ee6e032d1f3fe87b1eb;p=bacula%2Fbacula diff --git a/bacula/src/lib/lex.c b/bacula/src/lib/lex.c index 2fa93a7d13..03b64ac0d2 100644 --- a/bacula/src/lib/lex.c +++ b/bacula/src/lib/lex.c @@ -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, "rb")) == 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,7 +379,13 @@ lex_get_token(LEX *lf, int expect) int ch; int token = T_NONE; bool esc_next = false; - int unicode_count = 0; + /* 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) { @@ -396,15 +457,25 @@ lex_get_token(LEX *lf, int expect) lf->state = lex_include; begin_str(lf, 0); break; - case 0xEF: + 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); - break; + } 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; + } } - lf->state = lex_unicode_mark; - unicode_count = 1; break; default: lf->state = lex_string; @@ -515,48 +586,89 @@ lex_get_token(LEX *lf, int expect) } add_str(lf, ch); break; - case lex_include: /* scanning a filename */ + case lex_include_quoted_string: if (ch == L_EOF) { token = T_ERROR; break; } - if (B_ISSPACE(ch) || ch == '\n' || ch == L_EOL || ch == '}' || ch == '{' || - ch == ';' || ch == ',' || ch == '"' || ch == '#') { + 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.strerror()); + lfori->str, be.bstrerror()); return T_ERROR; } break; } add_str(lf, ch); break; - case lex_unicode_mark: + case lex_include: /* scanning a filename */ if (ch == L_EOF) { token = T_ERROR; break; } - unicode_count++; - if (unicode_count == 2) { - if (ch != 0xBB) { - token = T_ERROR; - break; - } - } else if (unicode_count == 3) { - if (ch != 0xBF) { - token = T_ERROR; - break; - } - token = T_UNICODE_MARK; + 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.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), @@ -634,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"),