]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/lex.c
b2a99ea1c287d60f31bc8059db77088bfdb4db0d
[bacula/bacula] / bacula / src / lib / lex.c
1 /*
2  * Lexical scanner for Bacula configuration file
3  *
4  *   Version $Id$
5  *
6  */
7
8 /*
9    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License as
13    published by the Free Software Foundation; either version 2 of
14    the License, or (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19    General Public License for more details.
20
21    You should have received a copy of the GNU General Public
22    License along with this program; if not, write to the Free
23    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
24    MA 02111-1307, USA.
25
26  */
27
28 #include "bacula.h"
29 #include "lex.h"
30
31 extern int debug_level;
32
33 /*
34  * Scan to "logical" end of line. I.e. end of line,
35  * or semicolon.
36  */
37 void scan_to_eol(LEX *lc)
38 {
39    Dmsg0(900, "start scan to eof\n");
40    while (lex_get_token(lc, T_ALL) != T_EOL)
41       { }
42    Dmsg0(900, "done scan to eof\n");
43 }
44
45    
46 /*
47  * Format a scanner error message 
48  */
49 static void s_err(char *file, int line, LEX *lc, char *msg, ...)
50 {
51    va_list arg_ptr;
52    char buf[MAXSTRING];
53    char more[MAXSTRING];
54
55    va_start(arg_ptr, msg);
56    bvsnprintf(buf, sizeof(buf), msg, arg_ptr);
57    va_end(arg_ptr);
58      
59    if (lc->line_no > lc->begin_line_no) {
60       bsnprintf(more, sizeof(more), 
61                 _("Problem probably begins at line %d.\n"), lc->begin_line_no);
62    } else {
63       more[0] = 0;
64    }
65    e_msg(file, line, M_ERROR_TERM, 0, _("Config error: %s\n\
66             : line %d, col %d of file %s\n%s\n%s"),
67       buf, lc->line_no, lc->col_no, lc->fname, lc->line, more);
68 }
69
70
71 /*
72  * Free the current file, and retrieve the contents
73  * of the previous packet if any.
74  */
75 LEX *lex_close_file(LEX *lf)
76 {
77    LEX *of;
78
79    Dmsg1(900, "Close lex file: %s\n", lf->fname);
80    if (lf == NULL) {
81       Emsg0(M_ABORT, 0, "Close of NULL file\n");
82    }
83    of = lf->next;
84    fclose(lf->fd);
85    Dmsg1(900, "Close cfg file %s\n", lf->fname);
86    free(lf->fname);
87    if (of) {
88       of->options = lf->options;      /* preserve options */
89       memcpy(lf, of, sizeof(LEX));
90       Dmsg1(900, "Restart scan of cfg file %s\n", of->fname);
91    } else {
92       of = lf;
93       lf = NULL;
94    }
95    free(of);
96    return lf;
97 }
98
99 /*     
100  * Open a new configuration file. We push the
101  * state of the current file (lf) so that we
102  * can do includes.  This is a bit of a hammer.
103  * Instead of passing back the pointer to the
104  * new packet, I simply replace the contents
105  * of the caller's packet with the new packet,
106  * and link the contents of the old packet into
107  * the next field.
108  *
109  */
110 LEX *lex_open_file(LEX *lf, char *filename, LEX_ERROR_HANDLER *scan_error) 
111               
112 {
113    LEX *nf;
114    FILE *fd;
115    char *fname = bstrdup(filename);
116
117    
118    if ((fd = fopen(fname, "r")) == NULL) {
119       Emsg2(M_ERROR_TERM, 0, _("Cannot open config file %s: %s\n"), 
120             fname, strerror(errno));
121    }
122    Dmsg1(900, "Open config file: %s\n", fname);
123    nf = (LEX *)malloc(sizeof(LEX));
124    if (lf) {     
125       memcpy(nf, lf, sizeof(LEX));
126       memset(lf, 0, sizeof(LEX));
127       lf->next = nf;                  /* if have lf, push it behind new one */
128       lf->options = nf->options;      /* preserve user options */
129    } else {
130       lf = nf;                        /* start new packet */
131       memset(lf, 0, sizeof(LEX));
132    }
133    lf->fd = fd;
134    lf->fname = fname;
135    lf->state = lex_none;
136    lf->ch = L_EOL;
137    if (scan_error) {
138       lf->scan_error = scan_error;
139    } else {
140       lf->scan_error = s_err;
141    }
142    Dmsg1(900, "Return lex=%x\n", lf);
143    return lf;
144 }
145
146 /*    
147  * Get the next character from the input.
148  *  Returns the character or
149  *    L_EOF if end of file
150  *    L_EOL if end of line
151  */
152 int lex_get_char(LEX *lf)
153 {
154    if (lf->ch == L_EOF) {
155       Emsg0(M_ABORT, 0, "get_char: called after EOF\n");
156    }
157    if (lf->ch == L_EOL) {
158       if (fgets(lf->line, MAXSTRING, lf->fd) == NULL) {
159          lf->ch = L_EOF;
160          if (lf->next) {
161             lex_close_file(lf);
162          }
163          return lf->ch;
164       }
165       lf->line_no++;
166       lf->col_no = 0;
167    }
168    lf->ch = lf->line[lf->col_no];
169    if (lf->ch == 0) {
170       lf->ch = L_EOL;
171    } else {
172       lf->col_no++;
173    }
174    Dmsg2(900, "lex_get_char: %c %d\n", lf->ch, lf->ch);
175    return lf->ch;
176 }
177
178 void lex_unget_char(LEX *lf)
179 {
180    lf->col_no--;      
181    if (lf->ch == L_EOL)
182       lf->ch = 0;
183 }
184
185
186 /*
187  * Add a character to the current string
188  */
189 static void add_str(LEX *lf, int ch)
190 {
191    if (lf->str_len >= MAXSTRING-3) {
192       Emsg3(M_ERROR_TERM, 0, _(
193            _("Config token too long, file: %s, line %d, begins at line %d\n")), 
194              lf->fname, lf->line_no, lf->begin_line_no);
195    }
196    lf->str[lf->str_len++] = ch;
197    lf->str[lf->str_len] = 0;
198 }
199
200 /*
201  * Begin the string
202  */
203 static void begin_str(LEX *lf, int ch)  
204 {
205    lf->str_len = 0;
206    lf->str[0] = 0;
207    if (ch != 0) {
208       add_str(lf, ch);
209    }
210    lf->begin_line_no = lf->line_no;   /* save start string line no */
211 }
212
213 #ifdef DEBUG
214 static char *lex_state_to_str(int state)
215 {
216    switch (state) {
217    case lex_none:          return "none";
218    case lex_comment:       return "comment";
219    case lex_number:        return "number";
220    case lex_ip_addr:       return "ip_addr";
221    case lex_identifier:    return "identifier";
222    case lex_string:        return "string";
223    case lex_quoted_string: return "quoted_string";
224    default:                return "??????";
225    }
226 }
227 #endif
228
229 /*
230  * Convert a lex token to a string
231  * used for debug/error printing.
232  */
233 char *lex_tok_to_str(int token)
234 {
235    switch(token) {
236    case L_EOF:             return "L_EOF";
237    case L_EOL:             return "L_EOL";
238    case T_NONE:            return "T_NONE";
239    case T_NUMBER:          return "T_NUMBER";
240    case T_IPADDR:          return "T_IPADDR";
241    case T_IDENTIFIER:      return "T_IDENTIFIER";
242    case T_UNQUOTED_STRING: return "T_UNQUOTED_STRING";
243    case T_QUOTED_STRING:   return "T_QUOTED_STRING";
244    case T_BOB:             return "T_BOB";
245    case T_EOB:             return "T_EOB";
246    case T_EQUALS:          return "T_EQUALS";
247    case T_ERROR:           return "T_ERROR";
248    case T_EOF:             return "T_EOF";
249    case T_COMMA:           return "T_COMMA";
250    case T_EOL:             return "T_EOL";
251    default:                return "??????";
252    }
253 }
254
255 static uint32_t scan_pint(LEX *lf, char *str)
256 {
257    int64_t val = 0;
258    if (!is_a_number(str)) {
259       scan_err1(lf, "expected a positive integer number, got: %s", str);
260       /* NOT REACHED */
261    } else {
262       errno = 0;
263       val = str_to_int64(str);
264       if (errno != 0 || val < 0) {
265          scan_err1(lf, "expected a postive integer number, got: %s", str);
266          /* NOT REACHED */
267       }
268    }
269    return (uint32_t)val;
270 }
271
272 /*        
273  * 
274  * Get the next token from the input
275  *
276  */
277 int
278 lex_get_token(LEX *lf, int expect)
279 {
280    int ch;
281    int token = T_NONE;
282    int esc_next = FALSE;
283
284    Dmsg0(900, "enter lex_get_token\n");
285    while (token == T_NONE) {
286       ch = lex_get_char(lf);
287       switch (lf->state) {
288       case lex_none:
289          Dmsg2(900, "Lex state lex_none ch=%d,%x\n", ch, ch);
290          if (B_ISSPACE(ch))  
291             break;
292          if (B_ISALPHA(ch)) {
293             if (lf->options & LOPT_NO_IDENT)
294                lf->state = lex_string;
295             else
296                lf->state = lex_identifier;
297             begin_str(lf, ch);
298             break;
299          }
300          if (B_ISDIGIT(ch)) {
301             lf->state = lex_number;
302             begin_str(lf, ch);
303             break;
304          }
305          Dmsg0(900, "Enter lex_none switch\n");
306          switch (ch) {
307          case L_EOF:
308             token = T_EOF;
309             Dmsg0(900, "got L_EOF set token=T_EOF\n");
310             break;
311          case '#':
312             lf->state = lex_comment;
313             break;
314          case '{':
315             token = T_BOB;
316             begin_str(lf, ch);
317             break;
318          case '}':
319             token = T_EOB;
320             begin_str(lf, ch);
321             break;
322          case '"':
323             lf->state = lex_quoted_string;
324             begin_str(lf, 0);
325             break;
326          case '=': 
327             token = T_EQUALS;
328             begin_str(lf, ch);
329             break;
330          case ',':
331             token = T_COMMA;
332             begin_str(lf, ch);
333             break;
334          case ';':
335             token = T_EOL;      /* treat ; like EOL */
336             break;
337          case L_EOL:
338             Dmsg0(900, "got L_EOL set token=T_EOL\n");
339             token = T_EOL;
340             break;
341          case '@':
342             lf->state = lex_include;
343             begin_str(lf, 0);
344             break;
345          default:
346             lf->state = lex_string;
347             begin_str(lf, ch);
348             break;
349          }
350          break;
351       case lex_comment:
352          Dmsg1(900, "Lex state lex_comment ch=%x\n", ch);
353          if (ch == L_EOL) {
354             lf->state = lex_none;
355             token = T_EOL;
356          } else if (ch == L_EOF) {
357             token = T_ERROR;
358          }
359          break;
360       case lex_number:
361          Dmsg2(900, "Lex state lex_number ch=%x %c\n", ch, ch);
362          if (ch == L_EOF) {
363             token = T_ERROR;
364             break;
365          }
366          /* Might want to allow trailing specifications here */
367          if (B_ISDIGIT(ch)) {
368             add_str(lf, ch);
369             break;
370          }
371
372          /* A valid number can be terminated by the following */
373          if (B_ISSPACE(ch) || ch == L_EOL || ch == ',' || ch == ';') {
374             token = T_NUMBER;
375             lf->state = lex_none;
376          } else {
377             lf->state = lex_string;
378          }
379          lex_unget_char(lf);
380          break;
381       case lex_ip_addr:
382          if (ch == L_EOF) {
383             token = T_ERROR;
384             break;
385          }
386          Dmsg1(900, "Lex state lex_ip_addr ch=%x\n", ch);
387          break;
388       case lex_string:
389          Dmsg1(900, "Lex state lex_string ch=%x\n", ch);
390          if (ch == L_EOF) {
391             token = T_ERROR;
392             break;
393          }
394          if (ch == '\n' || ch == L_EOL || ch == '=' || ch == '}' || ch == '{' ||
395              ch == '\r' || ch == ';' || ch == ',' || ch == '#' || (B_ISSPACE(ch)) ) {
396             lex_unget_char(lf);    
397             token = T_UNQUOTED_STRING;
398             lf->state = lex_none;
399             break;
400          } 
401          add_str(lf, ch);
402          break;
403       case lex_identifier:
404          Dmsg2(900, "Lex state lex_identifier ch=%x %c\n", ch, ch);
405          if (B_ISALPHA(ch)) {
406             add_str(lf, ch);
407             break;
408          } else if (B_ISSPACE(ch)) {
409             break;
410          } else if (ch == '\n' || ch == L_EOL || ch == '=' || ch == '}' || ch == '{' ||
411                     ch == '\r' || ch == ';' || ch == ','   || ch == '"' || ch == '#') {
412             lex_unget_char(lf);    
413             token = T_IDENTIFIER;
414             lf->state = lex_none;
415             break;
416          } else if (ch == L_EOF) {
417             token = T_ERROR;
418             lf->state = lex_none;
419             begin_str(lf, ch);
420             break;
421          }
422          /* Some non-alpha character => string */
423          lf->state = lex_string;
424          add_str(lf, ch);
425          break;
426       case lex_quoted_string:
427          Dmsg2(900, "Lex state lex_quoted_string ch=%x %c\n", ch, ch);
428          if (ch == L_EOF) {
429             token = T_ERROR;
430             break;
431          }
432          if (ch == L_EOL) {
433             esc_next = FALSE;
434             break;
435          }
436          if (esc_next) {
437             add_str(lf, ch);
438             esc_next = FALSE;
439             break;
440          }
441          if (ch == '\\') {
442             esc_next = TRUE;
443             break;
444          }
445          if (ch == '"') {
446             token = T_QUOTED_STRING;
447             lf->state = lex_none;
448             break;
449          }
450          add_str(lf, ch);
451          break;
452       case lex_include:            /* scanning a filename */
453          if (ch == L_EOF) {
454             token = T_ERROR;
455             break;
456          }
457          if (B_ISSPACE(ch) || ch == '\n' || ch == L_EOL || ch == '}' || ch == '{' ||
458              ch == ';' || ch == ','   || ch == '"' || ch == '#') {
459             lf->state = lex_none;
460             lf = lex_open_file(lf, lf->str, NULL);
461             break;
462          }
463          add_str(lf, ch);
464          break;
465       }
466       Dmsg4(900, "ch=%d state=%s token=%s %c\n", ch, lex_state_to_str(lf->state),
467         lex_tok_to_str(token), ch);
468    }
469    Dmsg2(900, "lex returning: line %d token: %s\n", lf->line_no, lex_tok_to_str(token));
470    lf->token = token;
471
472    /* 
473     * Here is where we check to see if the user has set certain 
474     *  expectations (e.g. 32 bit integer). If so, we do type checking
475     *  and possible additional scanning (e.g. for range).
476     */
477    switch (expect) {
478    case T_PINT32:
479       lf->pint32_val = scan_pint(lf, lf->str);
480       lf->pint32_val2 = lf->pint32_val;
481       token = T_PINT32;
482       break;
483
484    case T_PINT32_RANGE:
485       if (token == T_NUMBER) {
486          lf->pint32_val = scan_pint(lf, lf->str);
487          lf->pint32_val2 = lf->pint32_val;
488          token = T_PINT32;
489       } else {
490          char *p = strchr(lf->str, '-');
491          if (!p) {
492             scan_err2(lf, "expected an integer or a range, got %s: %s", 
493                lex_tok_to_str(token), lf->str);
494             token = T_ERROR;
495             break;
496          }
497          *p++ = 0;                       /* terminate first half of range */
498          lf->pint32_val  = scan_pint(lf, lf->str);
499          lf->pint32_val2 = scan_pint(lf, p);
500          token = T_PINT32_RANGE;
501       }
502       break;
503
504    case T_INT32:
505       if (token != T_NUMBER || !is_a_number(lf->str)) {
506          scan_err2(lf, "expected an integer number, got %s: %s",
507                lex_tok_to_str(token), lf->str);
508          token = T_ERROR;
509          break;
510       }
511       errno = 0;
512       lf->int32_val = (int32_t)str_to_int64(lf->str);
513       if (errno != 0) {
514          scan_err2(lf, "expected an integer number, got %s: %s",
515                lex_tok_to_str(token), lf->str);
516          token = T_ERROR;
517       } else {
518          token = T_INT32;
519       }
520       break;
521
522    case T_INT64:
523       Dmsg2(900, "int64=:%s: %f\n", lf->str, strtod(lf->str, NULL)); 
524       if (token != T_NUMBER || !is_a_number(lf->str)) {
525          scan_err2(lf, "expected an integer number, got %s: %s",
526                lex_tok_to_str(token), lf->str);
527          token = T_ERROR;
528          break;
529       }
530       errno = 0;
531       lf->int64_val = str_to_int64(lf->str);
532       if (errno != 0) {
533          scan_err2(lf, "expected an integer number, got %s: %s",
534                lex_tok_to_str(token), lf->str);
535          token = T_ERROR;
536       } else {
537          token = T_INT64;
538       }
539       break;
540
541    case T_NAME:
542       if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
543          scan_err2(lf, "expected a name, got %s: %s",
544                lex_tok_to_str(token), lf->str);
545          token = T_ERROR;
546       } else if (lf->str_len > MAX_RES_NAME_LENGTH) {
547          scan_err3(lf, "name %s length %d too long, max is %d\n", lf->str, 
548             lf->str_len, MAX_RES_NAME_LENGTH);
549          token = T_ERROR;
550       }
551       break;
552
553    case T_STRING:
554       if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
555          scan_err2(lf, "expected a string, got %s: %s",
556                lex_tok_to_str(token), lf->str);
557          token = T_ERROR;
558       } else {
559          token = T_STRING;
560       }
561       break;
562
563
564    default:
565       break;                          /* no expectation given */
566    }
567    lf->token = token;                 /* set possible new token */
568    return token;
569 }