]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/lex.c
9e07c6a5185b0888ca5621a2e05e80f0e807ff24
[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  * Free the current file, and retrieve the contents
35  * of the previous packet if any.
36  */
37 LEX *
38 lex_close_file(LEX *lf)
39 {
40    LEX *of;
41
42    Dmsg1(20, "Close lex file: %s\n", lf->fname);
43    if (lf == NULL) {
44       Emsg0(M_ABORT, 0, "Close of NULL file\n");
45    }
46    of = lf->next;
47    fclose(lf->fd);
48    Dmsg1(29, "Close cfg file %s\n", lf->fname);
49    free(lf->fname);
50    if (of) {
51       of->options = lf->options;      /* preserve options */
52       memcpy(lf, of, sizeof(LEX));
53       Dmsg1(29, "Restart scan of cfg file %s\n", of->fname);
54    } else {
55       of = lf;
56       lf = NULL;
57    }
58    free(of);
59    return lf;
60 }
61
62 /*     
63  * Open a new configuration file. We push the
64  * state of the current file (lf) so that we
65  * can do includes.  This is a bit of a hammer.
66  * Instead of passing back the pointer to the
67  * new packet, I simply replace the contents
68  * of the caller's packet with the new packet,
69  * and link the contents of the old packet into
70  * the next field.
71  *
72  */
73 LEX *
74 lex_open_file(LEX *lf, char *filename) 
75 {
76    LEX *nf;
77    FILE *fd;
78    char *fname = bstrdup(filename);
79
80    
81    if ((fd = fopen(fname, "r")) == NULL) {
82       Emsg2(M_ABORT, 0, "Cannot open config file %s: %s\n", fname, strerror(errno));
83    }
84    Dmsg1(29, "Open config file: %s\n", fname);
85    nf = (LEX *)malloc(sizeof(LEX));
86    if (lf) {     
87       memcpy(nf, lf, sizeof(LEX));
88       memset(lf, 0, sizeof(LEX));
89       lf->next = nf;                  /* if have lf, push it behind new one */
90       lf->options = nf->options;      /* preserve user options */
91    } else {
92       lf = nf;                        /* start new packet */
93       memset(lf, 0, sizeof(LEX));
94    }
95    lf->fd = fd;
96    lf->fname = fname;
97    lf->state = lex_none;
98    lf->ch = L_EOL;
99    Dmsg1(29, "Return lex=%x\n", lf);
100    return lf;
101 }
102
103 /*    
104  * Get the next character from the input.
105  *  Returns the character or
106  *    L_EOF if end of file
107  *    L_EOL if end of line
108  */
109 int
110 lex_get_char(LEX *lf)
111 {
112    if (lf->ch == L_EOF)
113       Emsg0(M_ABORT, 0, "get_char: called after EOF\n");
114    if (lf->ch == L_EOL) {
115       if (fgets(lf->line, MAXSTRING, lf->fd) == NULL) {
116          lf->ch = L_EOF;
117          if (lf->next) {
118             lex_close_file(lf);
119          }
120          return lf->ch;
121       }
122       lf->line_no++;
123       lf->col_no = 0;
124    }
125    lf->ch = lf->line[lf->col_no];
126    if (lf->ch == 0) {
127       lf->ch = L_EOL;
128    } else {
129       lf->col_no++;
130    }
131    Dmsg2(900, "lex_get_char: %c %d\n", lf->ch, lf->ch);
132    return lf->ch;
133 }
134
135 void
136 lex_unget_char(LEX *lf)
137 {
138    lf->col_no--;      
139    if (lf->ch == L_EOL)
140       lf->ch = 0;
141 }
142
143
144 /*
145  * Add a character to the current string
146  */
147 static void add_str(LEX *lf, int ch)
148 {
149    if (lf->str_len >= MAXSTRING-3) {
150       Emsg2(M_ABORT, 0, "Token too long, file: %s, line %s\n", lf->fname, lf->line_no);
151    }
152    lf->str[lf->str_len++] = ch;
153    lf->str[lf->str_len] = 0;
154 }
155
156 /*
157  * Begin the string
158  */
159 static void begin_str(LEX *lf, int ch)  
160 {
161    lf->str_len = 0;
162    lf->str[0] = 0;
163    if (ch != 0)
164       add_str(lf, ch);
165 }
166
167 #ifdef DEBUG
168 static char *
169 lex_state_to_str(int state)
170 {
171    switch (state) {
172       case lex_none:          return "none";
173       case lex_comment:       return "comment";
174       case lex_number:        return "number";
175       case lex_ip_addr:       return "ip_addr";
176       case lex_identifier:    return "identifier";
177       case lex_string:        return "string";
178       case lex_quoted_string: return "quoted_string";
179       default:                return "??????";
180    }
181 }
182 #endif
183
184 /*
185  * Convert a lex token to a string
186  * used for debug/error printing.
187  */
188 char *
189 lex_tok_to_str(int token)
190 {
191    switch(token) {
192       case L_EOF:           return "L_EOF";
193       case L_EOL:           return "L_EOL";
194       case T_NONE:          return "T_NONE";
195       case T_NUMBER:        return "T_NUMBER";
196       case T_IPADDR:        return "T_IPADDR";
197       case T_IDENTIFIER:    return "T_IDENTIFIER";
198       case T_STRING:        return "T_STRING";
199       case T_QUOTED_STRING: return "T_QUOTED_STRING";
200       case T_BOB:           return "T_BOB";
201       case T_EOB:           return "T_EOB";
202       case T_EQUALS:        return "T_EQUALS";
203       case T_ERROR:         return "T_ERROR";
204       case T_EOF:           return "T_EOF";
205       case T_COMMA:         return "T_COMMA";
206       case T_EOL:           return "T_EOL";
207       default:              return "??????";
208    }
209 }
210
211 /*        
212  * 
213  * Get the next token from the input
214  *
215  */
216 int
217 lex_get_token(LEX *lf)
218 {
219    int ch;
220    int token = T_NONE;
221    int esc_next = FALSE;
222
223    Dmsg0(290, "enter lex_get_token\n");
224    while (token == T_NONE) {
225       ch = lex_get_char(lf);
226       switch (lf->state) {
227          case lex_none:
228             Dmsg2(290, "Lex state lex_none ch=%d,%x\n", ch, ch);
229             if (ISSPACE(ch))  
230                break;
231             if (ISALPHA(ch)) {
232                if (lf->options & LOPT_NO_IDENT)
233                   lf->state = lex_string;
234                else
235                   lf->state = lex_identifier;
236                begin_str(lf, ch);
237                break;
238             }
239             if (ISDIGIT(ch)) {
240                lf->state = lex_number;
241                begin_str(lf, ch);
242                break;
243             }
244             Dmsg0(290, "Enter lex_none switch\n");
245             switch (ch) {
246                case L_EOF:
247                   token = T_EOF;
248                   Dmsg0(290, "got L_EOF set token=T_EOF\n");
249                   break;
250                case '#':
251                   lf->state = lex_comment;
252                   break;
253                case '{':
254                   token = T_BOB;
255                   begin_str(lf, ch);
256                   break;
257                case '}':
258                   token = T_EOB;
259                   begin_str(lf, ch);
260                   break;
261                case '"':
262                   lf->state = lex_quoted_string;
263                   begin_str(lf, 0);
264                   break;
265                case '=': 
266                   token = T_EQUALS;
267                   begin_str(lf, ch);
268                   break;
269                case ',':
270                   token = T_COMMA;
271                   begin_str(lf, ch);
272                   break;
273                case ';':
274                   token = T_EOL;      /* treat ; like EOL */
275                   break;
276                case L_EOL:
277                   Dmsg0(290, "got L_EOL set token=T_EOL\n");
278                   token = T_EOL;
279                   break;
280                case '@':
281                   lf->state = lex_include;
282                   begin_str(lf, 0);
283                   break;
284                default:
285                   lf->state = lex_string;
286                   begin_str(lf, ch);
287                   break;
288             }
289             break;
290          case lex_comment:
291             Dmsg1(290, "Lex state lex_comment ch=%x\n", ch);
292             if (ch == L_EOL) {
293                lf->state = lex_none;
294                token = T_EOL;
295             }
296             break;
297          case lex_number:
298             Dmsg2(290, "Lex state lex_number ch=%x %c\n", ch, ch);
299             /* Might want to allow trailing specifications here */
300             if (ISDIGIT(ch)) {
301                add_str(lf, ch);
302                break;
303             }
304
305             /* A valid number can be terminated by the following */
306             if (ISSPACE(ch) || ch == L_EOL || ch == ',' || ch == ';') {
307                token = T_NUMBER;
308                lf->state = lex_none;
309             } else {
310                lf->state = lex_string;
311             }
312             lex_unget_char(lf);
313             break;
314          case lex_ip_addr:
315             Dmsg1(290, "Lex state lex_ip_addr ch=%x\n", ch);
316             break;
317          case lex_string:
318             Dmsg1(290, "Lex state lex_string ch=%x\n", ch);
319             if (ch == '\n' || ch == L_EOL || ch == '=' || ch == '}' || ch == '{' ||
320                 ch == ';' || ch == ',' || ch == '#' || (ISSPACE(ch)) ) {
321                lex_unget_char(lf);    
322                token = T_STRING;
323                lf->state = lex_none;
324                break;
325             } 
326             add_str(lf, ch);
327             break;
328          case lex_identifier:
329             Dmsg2(290, "Lex state lex_identifier ch=%x %c\n", ch, ch);
330             if (ISALPHA(ch)) {
331                add_str(lf, ch);
332                break;
333             } else if (ISSPACE(ch)) {
334                break;
335             } else if (ch == '\n' || ch == L_EOL || ch == '=' || ch == '}' || ch == '{' ||
336                        ch == ';' || ch == ','   || ch == '"' || ch == '#') {
337                lex_unget_char(lf);    
338                token = T_IDENTIFIER;
339                lf->state = lex_none;
340                break;
341             } else if (ch == L_EOF) {
342                token = T_ERROR;
343                lf->state = lex_none;
344                begin_str(lf, ch);
345                break;
346             }
347             /* Some non-alpha character => string */
348             lf->state = lex_string;
349             add_str(lf, ch);
350             break;
351          case lex_quoted_string:
352             Dmsg2(290, "Lex state lex_quoted_string ch=%x %c\n", ch, ch);
353             if (ch == L_EOL) {
354                esc_next = FALSE;
355                break;
356             }
357             if (esc_next) {
358                add_str(lf, ch);
359                esc_next = FALSE;
360                break;
361             }
362             if (ch == '\\') {
363                esc_next = TRUE;
364                break;
365             }
366             if (ch == '"') {
367                token = T_QUOTED_STRING;
368                lf->state = lex_none;
369                break;
370             }
371             add_str(lf, ch);
372             break;
373          case lex_include:            /* scanning a filename */
374             if (ISSPACE(ch) || ch == '\n' || ch == L_EOL || ch == '}' || ch == '{' ||
375                 ch == ';' || ch == ','   || ch == '"' || ch == '#') {
376                lf->state = lex_none;
377                lf = lex_open_file(lf, lf->str);
378                break;
379             }
380             add_str(lf, ch);
381             break;
382       }
383       Dmsg4(290, "ch=%d state=%s token=%s %c\n", ch, lex_state_to_str(lf->state),
384         lex_tok_to_str(token), ch);
385    }
386    Dmsg2(290, "lex returning: line %d token: %s\n", lf->line_no, lex_tok_to_str(token));
387    lf->token = token;
388    return token;
389 }