2 * Lexical scanner for Bacula configuration file
10 Copyright (C) 2000-2006 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
28 extern int debug_level;
30 /* Debug level for this source file */
31 static const int dbglvl = 5000;
34 * Scan to "logical" end of line. I.e. end of line,
35 * or semicolon, but stop on T_EOB (same as end of
36 * line except it is not eaten).
38 void scan_to_eol(LEX *lc)
41 Dmsg0(dbglvl, "start scan to eof\n");
42 while ((token = lex_get_token(lc, T_ALL)) != T_EOL) {
51 * Get next token, but skip EOL
53 int scan_to_next_not_eol(LEX * lc)
57 token = lex_get_token(lc, T_ALL);
58 } while (token == T_EOL);
63 * Format a scanner error message
65 static void s_err(const char *file, int line, LEX *lc, const char *msg, ...)
71 va_start(arg_ptr, msg);
72 bvsnprintf(buf, sizeof(buf), msg, arg_ptr);
75 if (lc->err_type == 0) { /* M_ERROR_TERM by default */
76 lc->err_type = M_ERROR_TERM;
79 if (lc->line_no > lc->begin_line_no) {
80 bsnprintf(more, sizeof(more),
81 _("Problem probably begins at line %d.\n"), lc->begin_line_no);
85 if (lc->line_no > 0) {
86 e_msg(file, line, lc->err_type, 0, _("Config error: %s\n"
87 " : line %d, col %d of file %s\n%s\n%s"),
88 buf, lc->line_no, lc->col_no, lc->fname, lc->line, more);
90 e_msg(file, line, lc->err_type, 0, _("Config error: %s\n"), buf);
94 void lex_set_default_error_handler(LEX *lf)
96 lf->scan_error = s_err;
100 * Set err_type used in error_handler
101 * return the old value
103 int lex_set_error_handler_error_type(LEX *lf, int err_type)
105 int old = lf->err_type;
106 lf->err_type = err_type;
111 * Free the current file, and retrieve the contents
112 * of the previous packet if any.
114 LEX *lex_close_file(LEX *lf)
119 Emsg0(M_ABORT, 0, _("Close of NULL file\n"));
121 Dmsg1(dbglvl, "Close lex file: %s\n", lf->fname);
125 Dmsg1(dbglvl, "Close cfg file %s\n", lf->fname);
128 of->options = lf->options; /* preserve options */
129 memcpy(lf, of, sizeof(LEX));
130 Dmsg1(dbglvl, "Restart scan of cfg file %s\n", of->fname);
140 * Open a new configuration file. We push the
141 * state of the current file (lf) so that we
142 * can do includes. This is a bit of a hammer.
143 * Instead of passing back the pointer to the
144 * new packet, I simply replace the contents
145 * of the caller's packet with the new packet,
146 * and link the contents of the old packet into
150 LEX *lex_open_file(LEX *lf, const char *filename, LEX_ERROR_HANDLER *scan_error)
155 char *fname = bstrdup(filename);
158 if ((fd = fopen(fname, "rb")) == NULL) {
161 Dmsg1(400, "Open config file: %s\n", fname);
162 nf = (LEX *)malloc(sizeof(LEX));
164 memcpy(nf, lf, sizeof(LEX));
165 memset(lf, 0, sizeof(LEX));
166 lf->next = nf; /* if have lf, push it behind new one */
167 lf->options = nf->options; /* preserve user options */
169 lf = nf; /* start new packet */
170 memset(lf, 0, sizeof(LEX));
171 lex_set_error_handler_error_type(lf, M_ERROR_TERM);
174 lf->scan_error = scan_error;
176 lex_set_default_error_handler(lf);
180 lf->state = lex_none;
182 Dmsg1(dbglvl, "Return lex=%x\n", lf);
187 * Get the next character from the input.
188 * Returns the character or
189 * L_EOF if end of file
190 * L_EOL if end of line
192 int lex_get_char(LEX *lf)
194 if (lf->ch == L_EOF) {
195 Emsg0(M_ABORT, 0, _("get_char: called after EOF\n"));
197 if (lf->ch == L_EOL) {
198 if (bfgets(lf->line, MAXSTRING, lf->fd) == NULL) {
207 Dmsg2(1000, "fget line=%d %s", lf->line_no, lf->line);
209 lf->ch = (uint8_t)lf->line[lf->col_no];
215 Dmsg2(dbglvl, "lex_get_char: %c %d\n", lf->ch, lf->ch);
219 void lex_unget_char(LEX *lf)
221 if (lf->ch == L_EOL) {
222 lf->ch = 0; /* End of line, force read of next one */
224 lf->col_no--; /* Backup to re-read char */
231 * Add a character to the current string
233 static void add_str(LEX *lf, int ch)
235 if (lf->str_len >= MAXSTRING-3) {
236 Emsg3(M_ERROR_TERM, 0, _(
237 _("Config token too long, file: %s, line %d, begins at line %d\n")),
238 lf->fname, lf->line_no, lf->begin_line_no);
240 lf->str[lf->str_len++] = ch;
241 lf->str[lf->str_len] = 0;
247 static void begin_str(LEX *lf, int ch)
254 lf->begin_line_no = lf->line_no; /* save start string line no */
258 static const char *lex_state_to_str(int state)
261 case lex_none: return _("none");
262 case lex_comment: return _("comment");
263 case lex_number: return _("number");
264 case lex_ip_addr: return _("ip_addr");
265 case lex_identifier: return _("identifier");
266 case lex_string: return _("string");
267 case lex_quoted_string: return _("quoted_string");
268 default: return "??????";
274 * Convert a lex token to a string
275 * used for debug/error printing.
277 const char *lex_tok_to_str(int token)
280 case L_EOF: return "L_EOF";
281 case L_EOL: return "L_EOL";
282 case T_NONE: return "T_NONE";
283 case T_NUMBER: return "T_NUMBER";
284 case T_IPADDR: return "T_IPADDR";
285 case T_IDENTIFIER: return "T_IDENTIFIER";
286 case T_UNQUOTED_STRING: return "T_UNQUOTED_STRING";
287 case T_QUOTED_STRING: return "T_QUOTED_STRING";
288 case T_BOB: return "T_BOB";
289 case T_EOB: return "T_EOB";
290 case T_EQUALS: return "T_EQUALS";
291 case T_ERROR: return "T_ERROR";
292 case T_EOF: return "T_EOF";
293 case T_COMMA: return "T_COMMA";
294 case T_EOL: return "T_EOL";
295 default: return "??????";
299 static uint32_t scan_pint(LEX *lf, char *str)
302 if (!is_a_number(str)) {
303 scan_err1(lf, _("expected a positive integer number, got: %s"), str);
307 val = str_to_int64(str);
308 if (errno != 0 || val < 0) {
309 scan_err1(lf, _("expected a positive integer number, got: %s"), str);
313 return (uint32_t)val;
318 * Get the next token from the input
322 lex_get_token(LEX *lf, int expect)
326 bool esc_next = false;
328 Dmsg0(dbglvl, "enter lex_get_token\n");
329 while (token == T_NONE) {
330 ch = lex_get_char(lf);
333 Dmsg2(dbglvl, "Lex state lex_none ch=%d,%x\n", ch, ch);
337 if (lf->options & LOPT_NO_IDENT || lf->options & LOPT_STRING) {
338 lf->state = lex_string;
340 lf->state = lex_identifier;
346 if (lf->options & LOPT_STRING) {
347 lf->state = lex_string;
349 lf->state = lex_number;
354 Dmsg0(dbglvl, "Enter lex_none switch\n");
358 Dmsg0(dbglvl, "got L_EOF set token=T_EOF\n");
361 lf->state = lex_comment;
372 lf->state = lex_quoted_string;
384 if (expect != T_SKIP_EOL) {
385 token = T_EOL; /* treat ; like EOL */
389 Dmsg0(dbglvl, "got L_EOL set token=T_EOL\n");
390 if (expect != T_SKIP_EOL) {
395 lf->state = lex_include;
399 lf->state = lex_string;
405 Dmsg1(dbglvl, "Lex state lex_comment ch=%x\n", ch);
407 lf->state = lex_none;
408 if (expect != T_SKIP_EOL) {
411 } else if (ch == L_EOF) {
416 Dmsg2(dbglvl, "Lex state lex_number ch=%x %c\n", ch, ch);
421 /* Might want to allow trailing specifications here */
427 /* A valid number can be terminated by the following */
428 if (B_ISSPACE(ch) || ch == L_EOL || ch == ',' || ch == ';') {
430 lf->state = lex_none;
432 lf->state = lex_string;
441 Dmsg1(dbglvl, "Lex state lex_ip_addr ch=%x\n", ch);
444 Dmsg1(dbglvl, "Lex state lex_string ch=%x\n", ch);
449 if (ch == '\n' || ch == L_EOL || ch == '=' || ch == '}' || ch == '{' ||
450 ch == '\r' || ch == ';' || ch == ',' || ch == '#' || (B_ISSPACE(ch)) ) {
452 token = T_UNQUOTED_STRING;
453 lf->state = lex_none;
459 Dmsg2(dbglvl, "Lex state lex_identifier ch=%x %c\n", ch, ch);
463 } else if (B_ISSPACE(ch)) {
465 } else if (ch == '\n' || ch == L_EOL || ch == '=' || ch == '}' || ch == '{' ||
466 ch == '\r' || ch == ';' || ch == ',' || ch == '"' || ch == '#') {
468 token = T_IDENTIFIER;
469 lf->state = lex_none;
471 } else if (ch == L_EOF) {
473 lf->state = lex_none;
477 /* Some non-alpha character => string */
478 lf->state = lex_string;
481 case lex_quoted_string:
482 Dmsg2(dbglvl, "Lex state lex_quoted_string ch=%x %c\n", ch, ch);
501 token = T_QUOTED_STRING;
502 lf->state = lex_none;
507 case lex_include: /* scanning a filename */
512 if (B_ISSPACE(ch) || ch == '\n' || ch == L_EOL || ch == '}' || ch == '{' ||
513 ch == ';' || ch == ',' || ch == '"' || ch == '#') {
514 /* Keep the original LEX so we can print an error if the included file can't be opened. */
517 lf->state = lex_none;
518 lf = lex_open_file(lf, lf->str, lf->scan_error);
521 scan_err2(lfori, _("Cannot open included config file %s: %s\n"),
522 lfori->str, be.strerror());
530 Dmsg4(dbglvl, "ch=%d state=%s token=%s %c\n", ch, lex_state_to_str(lf->state),
531 lex_tok_to_str(token), ch);
533 Dmsg2(dbglvl, "lex returning: line %d token: %s\n", lf->line_no, lex_tok_to_str(token));
537 * Here is where we check to see if the user has set certain
538 * expectations (e.g. 32 bit integer). If so, we do type checking
539 * and possible additional scanning (e.g. for range).
543 lf->pint32_val = scan_pint(lf, lf->str);
544 lf->pint32_val2 = lf->pint32_val;
549 if (token == T_NUMBER) {
550 lf->pint32_val = scan_pint(lf, lf->str);
551 lf->pint32_val2 = lf->pint32_val;
554 char *p = strchr(lf->str, '-');
556 scan_err2(lf, _("expected an integer or a range, got %s: %s"),
557 lex_tok_to_str(token), lf->str);
561 *p++ = 0; /* terminate first half of range */
562 lf->pint32_val = scan_pint(lf, lf->str);
563 lf->pint32_val2 = scan_pint(lf, p);
564 token = T_PINT32_RANGE;
569 if (token != T_NUMBER || !is_a_number(lf->str)) {
570 scan_err2(lf, _("expected an integer number, got %s: %s"),
571 lex_tok_to_str(token), lf->str);
576 lf->int32_val = (int32_t)str_to_int64(lf->str);
578 scan_err2(lf, _("expected an integer number, got %s: %s"),
579 lex_tok_to_str(token), lf->str);
587 Dmsg2(dbglvl, "int64=:%s: %f\n", lf->str, strtod(lf->str, NULL));
588 if (token != T_NUMBER || !is_a_number(lf->str)) {
589 scan_err2(lf, _("expected an integer number, got %s: %s"),
590 lex_tok_to_str(token), lf->str);
595 lf->int64_val = str_to_int64(lf->str);
597 scan_err2(lf, _("expected an integer number, got %s: %s"),
598 lex_tok_to_str(token), lf->str);
606 if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
607 scan_err2(lf, _("expected a name, got %s: %s"),
608 lex_tok_to_str(token), lf->str);
610 } else if (lf->str_len > MAX_RES_NAME_LENGTH) {
611 scan_err3(lf, _("name %s length %d too long, max is %d\n"), lf->str,
612 lf->str_len, MAX_RES_NAME_LENGTH);
618 if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
619 scan_err2(lf, _("expected a string, got %s: %s"),
620 lex_tok_to_str(token), lf->str);
629 break; /* no expectation given */
631 lf->token = token; /* set possible new token */