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