1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero 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    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero 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 /*
24  * Lexical scanner for BAREOS configuration file
25  *
26  * Kern Sibbald, 2000
27  */
28 
29 #include "include/bareos.h"
30 #include "lex.h"
31 #include "lib/edit.h"
32 #include "lib/parse_conf.h"
33 #include "lib/berrno.h"
34 #include <glob.h>
35 
36 extern int debug_level;
37 
38 /* Debug level for this source file */
39 static const int debuglevel = 5000;
40 
41 /*
42  * Scan to "logical" end of line. I.e. end of line,
43  *   or semicolon, but stop on BCT_EOB (same as end of
44  *   line except it is not eaten).
45  */
ScanToEol(LEX * lc)46 void ScanToEol(LEX* lc)
47 {
48   int token;
49 
50   Dmsg0(debuglevel, "start scan to eof\n");
51   while ((token = LexGetToken(lc, BCT_ALL)) != BCT_EOL) {
52     if (token == BCT_EOB) {
53       LexUngetChar(lc);
54       return;
55     }
56   }
57 }
58 
59 /*
60  * Get next token, but skip EOL
61  */
ScanToNextNotEol(LEX * lc)62 int ScanToNextNotEol(LEX* lc)
63 {
64   int token;
65 
66   do {
67     token = LexGetToken(lc, BCT_ALL);
68   } while (token == BCT_EOL);
69 
70   return token;
71 }
72 
73 /*
74  * Format a scanner error message
75  */
s_err(const char * file,int line,LEX * lc,const char * msg,...)76 static void s_err(const char* file, int line, LEX* lc, const char* msg, ...)
77 {
78   va_list ap;
79   int len, maxlen;
80   PoolMem buf(PM_NAME), more(PM_NAME);
81 
82   while (1) {
83     maxlen = buf.size() - 1;
84     va_start(ap, msg);
85     len = Bvsnprintf(buf.c_str(), maxlen, msg, ap);
86     va_end(ap);
87 
88     if (len < 0 || len >= (maxlen - 5)) {
89       buf.ReallocPm(maxlen + maxlen / 2);
90       continue;
91     }
92 
93     break;
94   }
95 
96   if (lc->err_type == 0) { /* M_ERROR_TERM by default */
97     lc->err_type = M_ERROR_TERM;
98   }
99 
100   if (lc->line_no > lc->begin_line_no) {
101     Mmsg(more, _("Problem probably begins at line %d.\n"), lc->begin_line_no);
102   } else {
103     PmStrcpy(more, "");
104   }
105 
106   if (lc->line_no > 0) {
107     e_msg(file, line, lc->err_type, 0,
108           _("Config error: %s\n"
109             "            : line %d, col %d of file %s\n%s\n%s"),
110           buf.c_str(), lc->line_no, lc->col_no, lc->fname, lc->line,
111           more.c_str());
112   } else {
113     e_msg(file, line, lc->err_type, 0, _("Config error: %s\n"), buf.c_str());
114   }
115 
116   lc->error_counter++;
117 }
118 
119 /*
120  * Format a scanner warning message
121  */
s_warn(const char * file,int line,LEX * lc,const char * msg,...)122 static void s_warn(const char* file, int line, LEX* lc, const char* msg, ...)
123 {
124   va_list ap;
125   int len, maxlen;
126   PoolMem buf(PM_NAME), more(PM_NAME);
127 
128   while (1) {
129     maxlen = buf.size() - 1;
130     va_start(ap, msg);
131     len = Bvsnprintf(buf.c_str(), maxlen, msg, ap);
132     va_end(ap);
133 
134     if (len < 0 || len >= (maxlen - 5)) {
135       buf.ReallocPm(maxlen + maxlen / 2);
136       continue;
137     }
138 
139     break;
140   }
141 
142   if (lc->line_no > lc->begin_line_no) {
143     Mmsg(more, _("Problem probably begins at line %d.\n"), lc->begin_line_no);
144   } else {
145     PmStrcpy(more, "");
146   }
147 
148   if (lc->line_no > 0) {
149     p_msg(file, line, 0,
150           _("Config warning: %s\n"
151             "            : line %d, col %d of file %s\n%s\n%s"),
152           buf.c_str(), lc->line_no, lc->col_no, lc->fname, lc->line,
153           more.c_str());
154   } else {
155     p_msg(file, line, 0, _("Config warning: %s\n"), buf.c_str());
156   }
157 }
158 
LexSetDefaultErrorHandler(LEX * lf)159 void LexSetDefaultErrorHandler(LEX* lf) { lf->ScanError = s_err; }
160 
LexSetDefaultWarningHandler(LEX * lf)161 void LexSetDefaultWarningHandler(LEX* lf) { lf->scan_warning = s_warn; }
162 
163 /*
164  * Set err_type used in error_handler
165  */
LexSetErrorHandlerErrorType(LEX * lf,int err_type)166 void LexSetErrorHandlerErrorType(LEX* lf, int err_type)
167 {
168   LEX* lex = lf;
169   while (lex) {
170     lex->err_type = err_type;
171     lex = lex->next;
172   }
173 }
174 
175 /*
176  * Free the current file, and retrieve the contents of the previous packet if
177  * any.
178  */
LexCloseFile(LEX * lf)179 LEX* LexCloseFile(LEX* lf)
180 {
181   LEX* of;
182 
183   if (lf == NULL) { Emsg0(M_ABORT, 0, _("Close of NULL file\n")); }
184   Dmsg1(debuglevel, "Close lex file: %s\n", lf->fname);
185 
186   of = lf->next;
187   if (lf->bpipe) {
188     CloseBpipe(lf->bpipe);
189     lf->bpipe = NULL;
190   } else {
191     fclose(lf->fd);
192   }
193   Dmsg1(debuglevel, "Close cfg file %s\n", lf->fname);
194   free(lf->fname);
195   FreeMemory(lf->line);
196   FreeMemory(lf->str);
197   lf->line = NULL;
198   if (of) {
199     of->options = lf->options;              /* preserve options */
200     of->error_counter += lf->error_counter; /* summarize the errors */
201     memcpy(lf, of, sizeof(LEX));
202     Dmsg1(debuglevel, "Restart scan of cfg file %s\n", of->fname);
203   } else {
204     of = lf;
205     lf = NULL;
206   }
207   free(of);
208   return lf;
209 }
210 
211 /*
212  * Add lex structure for an included config file.
213  */
lex_add(LEX * lf,const char * filename,FILE * fd,Bpipe * bpipe,LEX_ERROR_HANDLER * ScanError,LEX_WARNING_HANDLER * scan_warning)214 static inline LEX* lex_add(LEX* lf,
215                            const char* filename,
216                            FILE* fd,
217                            Bpipe* bpipe,
218                            LEX_ERROR_HANDLER* ScanError,
219                            LEX_WARNING_HANDLER* scan_warning)
220 {
221   LEX* nf;
222 
223   Dmsg1(100, "open config file: %s\n", filename);
224   nf = (LEX*)malloc(sizeof(LEX));
225   if (lf) {
226     memcpy(nf, lf, sizeof(LEX));
227     memset(lf, 0, sizeof(LEX));
228     lf->next = nf;             /* if have lf, push it behind new one */
229     lf->options = nf->options; /* preserve user options */
230     /*
231      * preserve err_type to prevent bareos exiting on 'reload'
232      * if config is invalid.
233      */
234     lf->err_type = nf->err_type;
235   } else {
236     lf = nf; /* start new packet */
237     memset(lf, 0, sizeof(LEX));
238     LexSetErrorHandlerErrorType(lf, M_ERROR_TERM);
239   }
240 
241   if (ScanError) {
242     lf->ScanError = ScanError;
243   } else {
244     LexSetDefaultErrorHandler(lf);
245   }
246 
247   if (scan_warning) {
248     lf->scan_warning = scan_warning;
249   } else {
250     LexSetDefaultWarningHandler(lf);
251   }
252 
253   lf->fd = fd;
254   lf->bpipe = bpipe;
255   lf->fname = strdup(filename ? filename : "");
256   lf->line = GetMemory(1024);
257   lf->str = GetMemory(256);
258   lf->str_max_len = SizeofPoolMemory(lf->str);
259   lf->state = lex_none;
260   lf->ch = L_EOL;
261 
262   return lf;
263 }
264 
265 #ifdef HAVE_GLOB
IsWildcardString(const char * string)266 static inline bool IsWildcardString(const char* string)
267 {
268   return (strchr(string, '*') || strchr(string, '?'));
269 }
270 #endif
271 
272 /*
273  * Open a new configuration file. We push the
274  * state of the current file (lf) so that we
275  * can do includes.  This is a bit of a hammer.
276  * Instead of passing back the pointer to the
277  * new packet, I simply replace the contents
278  * of the caller's packet with the new packet,
279  * and link the contents of the old packet into
280  * the next field.
281  */
lex_open_file(LEX * lf,const char * filename,LEX_ERROR_HANDLER * ScanError,LEX_WARNING_HANDLER * scan_warning)282 LEX* lex_open_file(LEX* lf,
283                    const char* filename,
284                    LEX_ERROR_HANDLER* ScanError,
285                    LEX_WARNING_HANDLER* scan_warning)
286 {
287   FILE* fd;
288   Bpipe* bpipe = NULL;
289   char* bpipe_filename = NULL;
290 
291   if (filename[0] == '|') {
292     bpipe_filename = strdup(filename);
293     if ((bpipe = OpenBpipe(bpipe_filename + 1, 0, "rb")) == NULL) {
294       free(bpipe_filename);
295       return NULL;
296     }
297     free(bpipe_filename);
298     fd = bpipe->rfd;
299     return lex_add(lf, filename, fd, bpipe, ScanError, scan_warning);
300   } else {
301 #ifdef HAVE_GLOB
302     int globrc;
303     glob_t fileglob;
304     char* filename_expanded = NULL;
305 
306     /*
307      * Flag GLOB_NOMAGIC is a GNU extension, therefore manually check if string
308      * is a wildcard string.
309      */
310 
311     /*
312      * Clear fileglob at least required for mingw version of glob()
313      */
314     memset(&fileglob, 0, sizeof(fileglob));
315     globrc = glob(filename, 0, NULL, &fileglob);
316 
317     if ((globrc == GLOB_NOMATCH) && (IsWildcardString(filename))) {
318       /*
319        * fname is a wildcard string, but no matching files have been found.
320        * Ignore this include statement and continue.
321        */
322       return lf;
323     } else if (globrc != 0) {
324       /*
325        * glob() error has occurred. Giving up.
326        */
327       return NULL;
328     }
329 
330     Dmsg2(100, "glob %s: %i files\n", filename, fileglob.gl_pathc);
331     for (size_t i = 0; i < fileglob.gl_pathc; i++) {
332       filename_expanded = fileglob.gl_pathv[i];
333       if ((fd = fopen(filename_expanded, "rb")) == NULL) {
334         globfree(&fileglob);
335         return NULL;
336       }
337       lf = lex_add(lf, filename_expanded, fd, bpipe, ScanError, scan_warning);
338     }
339     globfree(&fileglob);
340 #else
341     if ((fd = fopen(filename, "rb")) == NULL) { return NULL; }
342     lf = lex_add(lf, filename, fd, bpipe, ScanError, scan_warning);
343 #endif
344     return lf;
345   }
346 }
347 
lex_new_buffer(LEX * lf,LEX_ERROR_HANDLER * ScanError,LEX_WARNING_HANDLER * scan_warning)348 LEX* lex_new_buffer(LEX* lf,
349                     LEX_ERROR_HANDLER* ScanError,
350                     LEX_WARNING_HANDLER* scan_warning)
351 {
352   lf = lex_add(lf, NULL, NULL, NULL, ScanError, scan_warning);
353   Dmsg1(debuglevel, "Return lex=%x\n", lf);
354 
355   return lf;
356 }
357 
358 /*
359  * Get the next character from the input.
360  *  Returns the character or
361  *    L_EOF if end of file
362  *    L_EOL if end of line
363  */
LexGetChar(LEX * lf)364 int LexGetChar(LEX* lf)
365 {
366   if (lf->ch == L_EOF) {
367     Emsg0(M_ABORT, 0,
368           _("get_char: called after EOF."
369             " You may have a open double quote without the closing double "
370             "quote.\n"));
371   }
372 
373   if (lf->ch == L_EOL) {
374     /*
375      * See if we are really reading a file otherwise we have reached EndOfFile.
376      */
377     if (!lf->fd || bfgets(lf->line, lf->fd) == NULL) {
378       lf->ch = L_EOF;
379       if (lf->next) {
380         if (lf->fd) { LexCloseFile(lf); }
381       }
382       return lf->ch;
383     }
384     lf->line_no++;
385     lf->col_no = 0;
386     Dmsg2(1000, "fget line=%d %s", lf->line_no, lf->line);
387   }
388 
389   lf->ch = (uint8_t)lf->line[lf->col_no];
390   if (lf->ch == 0) {
391     lf->ch = L_EOL;
392   } else if (lf->ch == '\n') {
393     lf->ch = L_EOL;
394     lf->col_no++;
395   } else {
396     lf->col_no++;
397   }
398   Dmsg2(debuglevel, "LexGetChar: %c %d\n", lf->ch, lf->ch);
399 
400   return lf->ch;
401 }
402 
LexUngetChar(LEX * lf)403 void LexUngetChar(LEX* lf)
404 {
405   if (lf->ch == L_EOL) {
406     lf->ch = 0; /* End of line, force read of next one */
407   } else {
408     lf->col_no--; /* Backup to re-read char */
409   }
410 }
411 
412 /*
413  * Add a character to the current string
414  */
add_str(LEX * lf,int ch)415 static void add_str(LEX* lf, int ch)
416 {
417   /*
418    * The default config string is sized to 256 bytes.
419    * If we need longer config strings its increased with 256 bytes each time.
420    */
421   if ((lf->str_len + 3) >= lf->str_max_len) {
422     lf->str = CheckPoolMemorySize(lf->str, lf->str_max_len + 256);
423     lf->str_max_len = SizeofPoolMemory(lf->str);
424   }
425 
426   lf->str[lf->str_len++] = ch;
427   lf->str[lf->str_len] = 0;
428 }
429 
430 /*
431  * Begin the string
432  */
BeginStr(LEX * lf,int ch)433 static void BeginStr(LEX* lf, int ch)
434 {
435   lf->str_len = 0;
436   lf->str[0] = 0;
437   if (ch != 0) { add_str(lf, ch); }
438   lf->begin_line_no = lf->line_no; /* save start string line no */
439 }
440 
lex_state_to_str(int state)441 static const char* lex_state_to_str(int state)
442 {
443   switch (state) {
444     case lex_none:
445       return _("none");
446     case lex_comment:
447       return _("comment");
448     case lex_number:
449       return _("number");
450     case lex_ip_addr:
451       return _("ip_addr");
452     case lex_identifier:
453       return _("identifier");
454     case lex_string:
455       return _("string");
456     case lex_quoted_string:
457       return _("quoted_string");
458     case lex_include:
459       return _("include");
460     case lex_include_quoted_string:
461       return _("include_quoted_string");
462     case lex_utf8_bom:
463       return _("UTF-8 Byte Order Mark");
464     case lex_utf16_le_bom:
465       return _("UTF-16le Byte Order Mark");
466     default:
467       return "??????";
468   }
469 }
470 
471 /*
472  * Convert a lex token to a string
473  * used for debug/error printing.
474  */
lex_tok_to_str(int token)475 const char* lex_tok_to_str(int token)
476 {
477   switch (token) {
478     case L_EOF:
479       return "L_EOF";
480     case L_EOL:
481       return "L_EOL";
482     case BCT_NONE:
483       return "BCT_NONE";
484     case BCT_NUMBER:
485       return "BCT_NUMBER";
486     case BCT_IPADDR:
487       return "BCT_IPADDR";
488     case BCT_IDENTIFIER:
489       return "BCT_IDENTIFIER";
490     case BCT_UNQUOTED_STRING:
491       return "BCT_UNQUOTED_STRING";
492     case BCT_QUOTED_STRING:
493       return "BCT_QUOTED_STRING";
494     case BCT_BOB:
495       return "BCT_BOB";
496     case BCT_EOB:
497       return "BCT_EOB";
498     case BCT_EQUALS:
499       return "BCT_EQUALS";
500     case BCT_ERROR:
501       return "BCT_ERROR";
502     case BCT_EOF:
503       return "BCT_EOF";
504     case BCT_COMMA:
505       return "BCT_COMMA";
506     case BCT_EOL:
507       return "BCT_EOL";
508     case BCT_UTF8_BOM:
509       return "BCT_UTF8_BOM";
510     case BCT_UTF16_BOM:
511       return "BCT_UTF16_BOM";
512     default:
513       return "??????";
514   }
515 }
516 
scan_pint(LEX * lf,char * str)517 static uint32_t scan_pint(LEX* lf, char* str)
518 {
519   int64_t val = 0;
520 
521   if (!Is_a_number(str)) {
522     scan_err1(lf, _("expected a positive integer number, got: %s"), str);
523   } else {
524     errno = 0;
525     val = str_to_int64(str);
526     if (errno != 0 || val < 0) {
527       scan_err1(lf, _("expected a positive integer number, got: %s"), str);
528     }
529   }
530 
531   return (uint32_t)(val & 0xffffffff);
532 }
533 
scan_pint64(LEX * lf,char * str)534 static uint64_t scan_pint64(LEX* lf, char* str)
535 {
536   uint64_t val = 0;
537 
538   if (!Is_a_number(str)) {
539     scan_err1(lf, _("expected a positive integer number, got: %s"), str);
540   } else {
541     errno = 0;
542     val = str_to_uint64(str);
543     if (errno != 0) {
544       scan_err1(lf, _("expected a positive integer number, got: %s"), str);
545     }
546   }
547 
548   return val;
549 }
550 
551 class TemporaryBuffer {
552  public:
TemporaryBuffer(FILE * fd)553   TemporaryBuffer(FILE* fd) : buf(GetPoolMemory(PM_NAME)), fd_(fd)
554   {
555     pos_ = ftell(fd_);
556   }
~TemporaryBuffer()557   ~TemporaryBuffer()
558   {
559     FreePoolMemory(buf);
560     fseek(fd_, pos_, SEEK_SET);
561   }
562   POOLMEM* buf;
563 
564  private:
565   FILE* fd_;
566   long pos_;
567 };
568 
NextLineContinuesWithQuotes(LEX * lf)569 static bool NextLineContinuesWithQuotes(LEX* lf)
570 {
571   TemporaryBuffer t(lf->fd);
572 
573   if (bfgets(t.buf, lf->fd) != NULL) {
574     int i = 0;
575     while (t.buf[i] != '\0') {
576       if (t.buf[i] == '"') { return true; }
577       if (t.buf[i] != ' ' && t.buf[i] != '\t') { return false; }
578       ++i;
579     };
580   }
581   return false;
582 }
583 
CurrentLineContinuesWithQuotes(LEX * lf)584 static bool CurrentLineContinuesWithQuotes(LEX* lf)
585 {
586   int i = lf->col_no;
587   while (lf->line[i] != '\0') {
588     if (lf->line[i] == '"') { return true; }
589     if (lf->line[i] != ' ' && lf->line[i] != '\t') { return false; }
590     ++i;
591   };
592   return false;
593 }
594 
595 /*
596  *
597  * Get the next token from the input
598  *
599  */
LexGetToken(LEX * lf,int expect)600 int LexGetToken(LEX* lf, int expect)
601 {
602   int ch;
603   int token = BCT_NONE;
604   bool continue_string = false;
605   bool esc_next = false;
606   /* Unicode files, especially on Win32, may begin with a "Byte Order Mark"
607      to indicate which transmission format the file is in. The codepoint for
608      this mark is U+FEFF and is represented as the octets EF-BB-BF in UTF-8
609      and as FF-FE in UTF-16le(little endian) and  FE-FF in UTF-16(big endian).
610      We use a distinct state for UTF-8 and UTF-16le, and use bom_bytes_seen
611      to tell which byte we are expecting. */
612   int bom_bytes_seen = 0;
613 
614   Dmsg0(debuglevel, "enter LexGetToken\n");
615   while (token == BCT_NONE) {
616     ch = LexGetChar(lf);
617     switch (lf->state) {
618       case lex_none:
619         Dmsg2(debuglevel, "Lex state lex_none ch=%d,%x\n", ch, ch);
620         if (B_ISSPACE(ch)) break;
621         if (B_ISALPHA(ch)) {
622           if (lf->options & LOPT_NO_IDENT || lf->options & LOPT_STRING) {
623             lf->state = lex_string;
624           } else {
625             lf->state = lex_identifier;
626           }
627           BeginStr(lf, ch);
628           break;
629         }
630         if (B_ISDIGIT(ch)) {
631           if (lf->options & LOPT_STRING) {
632             lf->state = lex_string;
633           } else {
634             lf->state = lex_number;
635           }
636           BeginStr(lf, ch);
637           break;
638         }
639         Dmsg0(debuglevel, "Enter lex_none switch\n");
640         switch (ch) {
641           case L_EOF:
642             token = BCT_EOF;
643             Dmsg0(debuglevel, "got L_EOF set token=T_EOF\n");
644             break;
645           case '#':
646             lf->state = lex_comment;
647             break;
648           case '{':
649             token = BCT_BOB;
650             BeginStr(lf, ch);
651             break;
652           case '}':
653             token = BCT_EOB;
654             BeginStr(lf, ch);
655             break;
656           case ' ':
657             if (continue_string) { continue; }
658             break;
659           case '"':
660             lf->state = lex_quoted_string;
661             if (!continue_string) { BeginStr(lf, 0); }
662             break;
663           case '=':
664             token = BCT_EQUALS;
665             BeginStr(lf, ch);
666             break;
667           case ',':
668             token = BCT_COMMA;
669             BeginStr(lf, ch);
670             break;
671           case ';':
672             if (expect != BCT_SKIP_EOL) {
673               token = BCT_EOL; /* treat ; like EOL */
674             }
675             break;
676           case L_EOL:
677             if (continue_string) {
678               continue;
679             } else {
680               Dmsg0(debuglevel, "got L_EOL set token=BCT_EOL\n");
681               if (expect != BCT_SKIP_EOL) { token = BCT_EOL; }
682             }
683             break;
684           case '@':
685             /* In NO_EXTERN mode, @ is part of a string */
686             if (lf->options & LOPT_NO_EXTERN) {
687               lf->state = lex_string;
688               BeginStr(lf, ch);
689             } else {
690               lf->state = lex_include;
691               BeginStr(lf, 0);
692             }
693             break;
694           case 0xEF: /* probably a UTF-8 BOM */
695           case 0xFF: /* probably a UTF-16le BOM */
696           case 0xFE: /* probably a UTF-16be BOM (error)*/
697             if (lf->line_no != 1 || lf->col_no != 1) {
698               lf->state = lex_string;
699               BeginStr(lf, ch);
700             } else {
701               bom_bytes_seen = 1;
702               if (ch == 0xEF) {
703                 lf->state = lex_utf8_bom;
704               } else if (ch == 0xFF) {
705                 lf->state = lex_utf16_le_bom;
706               } else {
707                 scan_err0(lf, _("This config file appears to be in an "
708                                 "unsupported Unicode format (UTF-16be). Please "
709                                 "resave as UTF-8\n"));
710                 return BCT_ERROR;
711               }
712             }
713             break;
714           default:
715             lf->state = lex_string;
716             BeginStr(lf, ch);
717             break;
718         }
719         break;
720       case lex_comment:
721         Dmsg1(debuglevel, "Lex state lex_comment ch=%x\n", ch);
722         if (ch == L_EOL) {
723           lf->state = lex_none;
724           if (expect != BCT_SKIP_EOL) { token = BCT_EOL; }
725         } else if (ch == L_EOF) {
726           token = BCT_ERROR;
727         }
728         break;
729       case lex_number:
730         Dmsg2(debuglevel, "Lex state lex_number ch=%x %c\n", ch, ch);
731         if (ch == L_EOF) {
732           token = BCT_ERROR;
733           break;
734         }
735         /* Might want to allow trailing specifications here */
736         if (B_ISDIGIT(ch)) {
737           add_str(lf, ch);
738           break;
739         }
740 
741         /* A valid number can be terminated by the following */
742         if (B_ISSPACE(ch) || ch == L_EOL || ch == ',' || ch == ';') {
743           token = BCT_NUMBER;
744           lf->state = lex_none;
745         } else {
746           lf->state = lex_string;
747         }
748         LexUngetChar(lf);
749         break;
750       case lex_ip_addr:
751         if (ch == L_EOF) {
752           token = BCT_ERROR;
753           break;
754         }
755         Dmsg1(debuglevel, "Lex state lex_ip_addr ch=%x\n", ch);
756         break;
757       case lex_string:
758         Dmsg1(debuglevel, "Lex state lex_string ch=%x\n", ch);
759         if (ch == L_EOF) {
760           token = BCT_ERROR;
761           break;
762         }
763         if (ch == '\n' || ch == L_EOL || ch == '=' || ch == '}' || ch == '{'
764             || ch == '\r' || ch == ';' || ch == ',' || ch == '#'
765             || (B_ISSPACE(ch))) {
766           LexUngetChar(lf);
767           token = BCT_UNQUOTED_STRING;
768           lf->state = lex_none;
769           break;
770         }
771         add_str(lf, ch);
772         break;
773       case lex_identifier:
774         Dmsg2(debuglevel, "Lex state lex_identifier ch=%x %c\n", ch, ch);
775         if (B_ISALPHA(ch)) {
776           add_str(lf, ch);
777           break;
778         } else if (B_ISSPACE(ch)) {
779           break;
780         } else if (ch == '\n' || ch == L_EOL || ch == '=' || ch == '}'
781                    || ch == '{' || ch == '\r' || ch == ';' || ch == ','
782                    || ch == '"' || ch == '#') {
783           LexUngetChar(lf);
784           token = BCT_IDENTIFIER;
785           lf->state = lex_none;
786           break;
787         } else if (ch == L_EOF) {
788           token = BCT_ERROR;
789           lf->state = lex_none;
790           BeginStr(lf, ch);
791           break;
792         }
793         /* Some non-alpha character => string */
794         lf->state = lex_string;
795         add_str(lf, ch);
796         break;
797       case lex_quoted_string:
798         Dmsg2(debuglevel, "Lex state lex_quoted_string ch=%x %c\n", ch, ch);
799         if (ch == L_EOF) {
800           token = BCT_ERROR;
801           break;
802         }
803         if (ch == L_EOL) {
804           esc_next = false;
805           break;
806         }
807         if (esc_next) {
808           add_str(lf, ch);
809           esc_next = false;
810           break;
811         }
812         if (ch == '\\') {
813           esc_next = true;
814           break;
815         }
816         if (ch == '"') {
817           if (NextLineContinuesWithQuotes(lf)
818               || CurrentLineContinuesWithQuotes(lf)) {
819             continue_string = true;
820             lf->state = lex_none;
821             continue;
822           } else {
823             token = BCT_QUOTED_STRING;
824             /*
825              * Since we may be scanning a quoted list of names,
826              *  we get the next character (a comma indicates another
827              *  one), then we put it back for rescanning.
828              */
829             LexGetChar(lf);
830             LexUngetChar(lf);
831             lf->state = lex_none;
832           }
833           break;
834         }
835         continue_string = false;
836         add_str(lf, ch);
837         break;
838       case lex_include_quoted_string:
839         if (ch == L_EOF) {
840           token = BCT_ERROR;
841           break;
842         }
843         if (esc_next) {
844           add_str(lf, ch);
845           esc_next = false;
846           break;
847         }
848         if (ch == '\\') {
849           esc_next = true;
850           break;
851         }
852         if (ch == '"') {
853           /* Keep the original LEX so we can print an error if the included file
854            * can't be opened. */
855           LEX* lfori = lf;
856           /* Skip the double quote when restarting parsing */
857           LexGetChar(lf);
858 
859           lf->state = lex_none;
860           lf = lex_open_file(lf, lf->str, lf->ScanError, lf->scan_warning);
861           if (lf == NULL) {
862             BErrNo be;
863             scan_err2(lfori, _("Cannot open included config file %s: %s\n"),
864                       lfori->str, be.bstrerror());
865             return BCT_ERROR;
866           }
867           break;
868         }
869         add_str(lf, ch);
870         break;
871       case lex_include: /* scanning a filename */
872         if (ch == L_EOF) {
873           token = BCT_ERROR;
874           break;
875         }
876         if (ch == '"') {
877           lf->state = lex_include_quoted_string;
878           break;
879         }
880 
881 
882         if (B_ISSPACE(ch) || ch == '\n' || ch == L_EOL || ch == '}' || ch == '{'
883             || ch == ';' || ch == ',' || ch == '"' || ch == '#') {
884           /* Keep the original LEX so we can print an error if the included file
885            * can't be opened. */
886           LEX* lfori = lf;
887 
888           lf->state = lex_none;
889           lf = lex_open_file(lf, lf->str, lf->ScanError, lf->scan_warning);
890           if (lf == NULL) {
891             BErrNo be;
892             scan_err2(lfori, _("Cannot open included config file %s: %s\n"),
893                       lfori->str, be.bstrerror());
894             return BCT_ERROR;
895           }
896           break;
897         }
898         add_str(lf, ch);
899         break;
900       case lex_utf8_bom:
901         /* we only end up in this state if we have read an 0xEF
902            as the first byte of the file, indicating we are probably
903            reading a UTF-8 file */
904         if (ch == 0xBB && bom_bytes_seen == 1) {
905           bom_bytes_seen++;
906         } else if (ch == 0xBF && bom_bytes_seen == 2) {
907           token = BCT_UTF8_BOM;
908           lf->state = lex_none;
909         } else {
910           token = BCT_ERROR;
911         }
912         break;
913       case lex_utf16_le_bom:
914         /* we only end up in this state if we have read an 0xFF
915            as the first byte of the file -- indicating that we are
916            probably dealing with an Intel based (little endian) UTF-16 file*/
917         if (ch == 0xFE) {
918           token = BCT_UTF16_BOM;
919           lf->state = lex_none;
920         } else {
921           token = BCT_ERROR;
922         }
923         break;
924     }
925     Dmsg4(debuglevel, "ch=%d state=%s token=%s %c\n", ch,
926           lex_state_to_str(lf->state), lex_tok_to_str(token), ch);
927   }
928   Dmsg2(debuglevel, "lex returning: line %d token: %s\n", lf->line_no,
929         lex_tok_to_str(token));
930   lf->token = token;
931 
932   /*
933    * Here is where we check to see if the user has set certain
934    *  expectations (e.g. 32 bit integer). If so, we do type checking
935    *  and possible additional scanning (e.g. for range).
936    */
937   switch (expect) {
938     case BCT_PINT16:
939       lf->u.pint16_val = (scan_pint(lf, lf->str) & 0xffff);
940       lf->u2.pint16_val = lf->u.pint16_val;
941       token = BCT_PINT16;
942       break;
943 
944     case BCT_PINT32:
945       lf->u.pint32_val = scan_pint(lf, lf->str);
946       lf->u2.pint32_val = lf->u.pint32_val;
947       token = BCT_PINT32;
948       break;
949 
950     case BCT_PINT32_RANGE:
951       if (token == BCT_NUMBER) {
952         lf->u.pint32_val = scan_pint(lf, lf->str);
953         lf->u2.pint32_val = lf->u.pint32_val;
954         token = BCT_PINT32;
955       } else {
956         char* p = strchr(lf->str, '-');
957         if (!p) {
958           scan_err2(lf, _("expected an integer or a range, got %s: %s"),
959                     lex_tok_to_str(token), lf->str);
960           token = BCT_ERROR;
961           break;
962         }
963         *p++ = 0; /* Terminate first half of range */
964         lf->u.pint32_val = scan_pint(lf, lf->str);
965         lf->u2.pint32_val = scan_pint(lf, p);
966         token = BCT_PINT32_RANGE;
967       }
968       break;
969 
970     case BCT_INT16:
971       if (token != BCT_NUMBER || !Is_a_number(lf->str)) {
972         scan_err2(lf, _("expected an integer number, got %s: %s"),
973                   lex_tok_to_str(token), lf->str);
974         token = BCT_ERROR;
975         break;
976       }
977       errno = 0;
978       lf->u.int16_val = (int16_t)str_to_int64(lf->str);
979       if (errno != 0) {
980         scan_err2(lf, _("expected an integer number, got %s: %s"),
981                   lex_tok_to_str(token), lf->str);
982         token = BCT_ERROR;
983       } else {
984         token = BCT_INT16;
985       }
986       break;
987 
988     case BCT_INT32:
989       if (token != BCT_NUMBER || !Is_a_number(lf->str)) {
990         scan_err2(lf, _("expected an integer number, got %s: %s"),
991                   lex_tok_to_str(token), lf->str);
992         token = BCT_ERROR;
993         break;
994       }
995       errno = 0;
996       lf->u.int32_val = (int32_t)str_to_int64(lf->str);
997       if (errno != 0) {
998         scan_err2(lf, _("expected an integer number, got %s: %s"),
999                   lex_tok_to_str(token), lf->str);
1000         token = BCT_ERROR;
1001       } else {
1002         token = BCT_INT32;
1003       }
1004       break;
1005 
1006     case BCT_INT64:
1007       Dmsg2(debuglevel, "int64=:%s: %f\n", lf->str, strtod(lf->str, NULL));
1008       if (token != BCT_NUMBER || !Is_a_number(lf->str)) {
1009         scan_err2(lf, _("expected an integer number, got %s: %s"),
1010                   lex_tok_to_str(token), lf->str);
1011         token = BCT_ERROR;
1012         break;
1013       }
1014       errno = 0;
1015       lf->u.int64_val = str_to_int64(lf->str);
1016       if (errno != 0) {
1017         scan_err2(lf, _("expected an integer number, got %s: %s"),
1018                   lex_tok_to_str(token), lf->str);
1019         token = BCT_ERROR;
1020       } else {
1021         token = BCT_INT64;
1022       }
1023       break;
1024 
1025     case BCT_PINT64_RANGE:
1026       if (token == BCT_NUMBER) {
1027         lf->u.pint64_val = scan_pint64(lf, lf->str);
1028         lf->u2.pint64_val = lf->u.pint64_val;
1029         token = BCT_PINT64;
1030       } else {
1031         char* p = strchr(lf->str, '-');
1032         if (!p) {
1033           scan_err2(lf, _("expected an integer or a range, got %s: %s"),
1034                     lex_tok_to_str(token), lf->str);
1035           token = BCT_ERROR;
1036           break;
1037         }
1038         *p++ = 0; /* Terminate first half of range */
1039         lf->u.pint64_val = scan_pint64(lf, lf->str);
1040         lf->u2.pint64_val = scan_pint64(lf, p);
1041         token = BCT_PINT64_RANGE;
1042       }
1043       break;
1044 
1045     case BCT_NAME:
1046       if (token != BCT_IDENTIFIER && token != BCT_UNQUOTED_STRING
1047           && token != BCT_QUOTED_STRING) {
1048         scan_err2(lf, _("expected a name, got %s: %s"), lex_tok_to_str(token),
1049                   lf->str);
1050         token = BCT_ERROR;
1051       } else if (lf->str_len > MAX_RES_NAME_LENGTH) {
1052         scan_err3(lf, _("name %s length %d too long, max is %d\n"), lf->str,
1053                   lf->str_len, MAX_RES_NAME_LENGTH);
1054         token = BCT_ERROR;
1055       }
1056       break;
1057 
1058     case BCT_STRING:
1059       if (token != BCT_IDENTIFIER && token != BCT_UNQUOTED_STRING
1060           && token != BCT_QUOTED_STRING) {
1061         scan_err2(lf, _("expected a string, got %s: %s"), lex_tok_to_str(token),
1062                   lf->str);
1063         token = BCT_ERROR;
1064       } else {
1065         token = BCT_STRING;
1066       }
1067       break;
1068 
1069 
1070     default:
1071       break; /* no expectation given */
1072   }
1073   lf->token = token; /* set possible new token */
1074   return token;
1075 }
1076