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