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