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