iof-bird-daemon / conf / cf-lex.l @ 725270cb
History | View | Annotate | Download (10.8 KB)
1 | 82fc7be7 | Martin Mares | /* |
---|---|---|---|
2 | * BIRD -- Configuration Lexer |
||
3 | * |
||
4 | d272fe22 | Martin Mares | * (c) 1998--2000 Martin Mares <mj@ucw.cz> |
5 | 82fc7be7 | Martin Mares | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. |
||
7 | */ |
||
8 | |||
9 | 06607335 | Martin Mares | /** |
10 | 2e9b2421 | Martin Mares | * DOC: Lexical analyzer |
11 | 06607335 | Martin Mares | * |
12 | 2e9b2421 | Martin Mares | * The lexical analyzer used for configuration files and CLI commands |
13 | 58f7d004 | Martin Mares | * is generated using the |flex| tool accompanied by a couple of |
14 | 06607335 | Martin Mares | * functions maintaining the hash tables containing information about |
15 | * symbols and keywords. |
||
16 | * |
||
17 | * Each symbol is represented by a &symbol structure containing name |
||
18 | 58f7d004 | Martin Mares | * of the symbol, its lexical scope, symbol class (%SYM_PROTO for a name of a protocol, |
19 | 06607335 | Martin Mares | * %SYM_NUMBER for a numeric constant etc.) and class dependent data. |
20 | * When an unknown symbol is encountered, it's automatically added to the |
||
21 | * symbol table with class %SYM_VOID. |
||
22 | * |
||
23 | * The keyword tables are generated from the grammar templates |
||
24 | * using the |gen_keywords.m4| script. |
||
25 | */ |
||
26 | |||
27 | 82fc7be7 | Martin Mares | %{ |
28 | cc12cf05 | Martin Mares | #undef REJECT /* Avoid name clashes */ |
29 | 82fc7be7 | Martin Mares | |
30 | #include <errno.h> |
||
31 | #include <stdlib.h> |
||
32 | cc12cf05 | Martin Mares | #include <stdarg.h> |
33 | 82fc7be7 | Martin Mares | |
34 | #include "nest/bird.h" |
||
35 | 944f008a | Martin Mares | #include "nest/route.h" |
36 | #include "filter/filter.h" |
||
37 | 82fc7be7 | Martin Mares | #include "conf/conf.h" |
38 | #include "conf/cf-parse.tab.h" |
||
39 | d272fe22 | Martin Mares | #include "lib/string.h" |
40 | 82fc7be7 | Martin Mares | |
41 | a412f01e | Martin Mares | struct keyword { |
42 | 82fc7be7 | Martin Mares | byte *name; |
43 | int value; |
||
44 | struct keyword *next; |
||
45 | a412f01e | Martin Mares | }; |
46 | |||
47 | 49e7e5ee | Martin Mares | #include "conf/keywords.h" |
48 | 82fc7be7 | Martin Mares | |
49 | #define KW_HASH_SIZE 64 |
||
50 | c9aae7f4 | Martin Mares | static struct keyword *kw_hash[KW_HASH_SIZE]; |
51 | static int kw_hash_inited; |
||
52 | |||
53 | 82fc7be7 | Martin Mares | #define SYM_HASH_SIZE 128 |
54 | #define SYM_MAX_LEN 32 |
||
55 | |||
56 | c8f61a01 | Martin Mares | struct sym_scope { |
57 | struct sym_scope *next; /* Next on scope stack */ |
||
58 | struct symbol *name; /* Name of this scope */ |
||
59 | int active; /* Currently entered */ |
||
60 | }; |
||
61 | static struct sym_scope *conf_this_scope; |
||
62 | |||
63 | 31b3e1bb | Martin Mares | int conf_lino; |
64 | |||
65 | 82fc7be7 | Martin Mares | static int cf_hash(byte *c); |
66 | static struct symbol *cf_find_sym(byte *c, unsigned int h0); |
||
67 | |||
68 | b35d72ac | Martin Mares | linpool *cfg_mem; |
69 | 82fc7be7 | Martin Mares | |
70 | int (*cf_read_hook)(byte *buf, unsigned int max); |
||
71 | |||
72 | #define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max); |
||
73 | #define YY_NO_UNPUT |
||
74 | #define YY_FATAL_ERROR(msg) cf_error(msg) |
||
75 | |||
76 | %} |
||
77 | |||
78 | %option noyywrap |
||
79 | |||
80 | bc2fb680 | Martin Mares | %x COMMENT CCOMM CLI |
81 | 82fc7be7 | Martin Mares | |
82 | ALPHA [a-zA-Z_] |
||
83 | DIGIT [0-9] |
||
84 | XIGIT [0-9a-fA-F] |
||
85 | ALNUM [a-zA-Z_0-9] |
||
86 | WHITE [ \t] |
||
87 | |||
88 | %% |
||
89 | |||
90 | {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ { |
||
91 | dce26783 | Martin Mares | #ifdef IPV6 |
92 | if (ipv4_pton_u32(yytext, &cf_lval.i32)) |
||
93 | return RTRID; |
||
94 | cf_error("Invalid IPv4 address %s", yytext); |
||
95 | #else |
||
96 | 82fc7be7 | Martin Mares | if (ip_pton(yytext, &cf_lval.a)) |
97 | return IPA; |
||
98 | dce26783 | Martin Mares | cf_error("Invalid IP address %s", yytext); |
99 | #endif |
||
100 | } |
||
101 | |||
102 | ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) { |
||
103 | #ifdef IPV6 |
||
104 | if (ip_pton(yytext, &cf_lval.a)) |
||
105 | return IPA; |
||
106 | cf_error("Invalid IP address %s", yytext); |
||
107 | #else |
||
108 | cf_error("This is an IPv4 router, therefore IPv6 addresses are not supported"); |
||
109 | #endif |
||
110 | 82fc7be7 | Martin Mares | } |
111 | |||
112 | 0x{DIGIT}+ { |
||
113 | char *e; |
||
114 | long int l; |
||
115 | errno = 0; |
||
116 | l = strtoul(yytext+2, &e, 16); |
||
117 | if (e && *e || errno == ERANGE || (long int)(int) l != l) |
||
118 | cf_error("Number out of range"); |
||
119 | cf_lval.i = l; |
||
120 | return NUM; |
||
121 | } |
||
122 | |||
123 | {DIGIT}+ { |
||
124 | char *e; |
||
125 | long int l; |
||
126 | errno = 0; |
||
127 | l = strtoul(yytext, &e, 10); |
||
128 | if (e && *e || errno == ERANGE || (long int)(int) l != l) |
||
129 | cf_error("Number out of range"); |
||
130 | cf_lval.i = l; |
||
131 | return NUM; |
||
132 | } |
||
133 | |||
134 | {ALPHA}{ALNUM}* { |
||
135 | unsigned int h = cf_hash(yytext); |
||
136 | struct keyword *k = kw_hash[h & (KW_HASH_SIZE-1)]; |
||
137 | while (k) |
||
138 | { |
||
139 | if (!strcmp(k->name, yytext)) |
||
140 | 944f008a | Martin Mares | { |
141 | if (k->value > 0) |
||
142 | return k->value; |
||
143 | else |
||
144 | { |
||
145 | cf_lval.i = -k->value; |
||
146 | return ENUM; |
||
147 | } |
||
148 | } |
||
149 | 82fc7be7 | Martin Mares | k=k->next; |
150 | } |
||
151 | cf_lval.s = cf_find_sym(yytext, h); |
||
152 | return SYM; |
||
153 | } |
||
154 | |||
155 | efe51e38 | Martin Mares | <CLI>(.|\n) { |
156 | bc2fb680 | Martin Mares | BEGIN(INITIAL); |
157 | return CLI_MARKER; |
||
158 | } |
||
159 | |||
160 | 69a20d2e | Martin Mares | [={}:;,.()+*/%<>~\[\]?!-] { |
161 | 82fc7be7 | Martin Mares | return yytext[0]; |
162 | } |
||
163 | |||
164 | ["][^"\n]*["] { |
||
165 | 49e4a4d1 | Martin Mares | yytext[yyleng-1] = 0; |
166 | ca0edc53 | Martin Mares | cf_lval.t = cfg_strdup(yytext+1); |
167 | 82fc7be7 | Martin Mares | return TEXT; |
168 | } |
||
169 | |||
170 | ["][^"\n]*\n cf_error("Unterminated string"); |
||
171 | |||
172 | <INITIAL,COMMENT><<EOF>> return END; |
||
173 | |||
174 | {WHITE}+ |
||
175 | |||
176 | 7f400d1c | Martin Mares | \n conf_lino++; |
177 | 82fc7be7 | Martin Mares | |
178 | 72614174 | Martin Mares | # BEGIN(COMMENT); |
179 | 82fc7be7 | Martin Mares | |
180 | 72614174 | Martin Mares | \/\* BEGIN(CCOMM); |
181 | 82fc7be7 | Martin Mares | |
182 | . cf_error("Unknown character"); |
||
183 | |||
184 | <COMMENT>\n { |
||
185 | 31b3e1bb | Martin Mares | conf_lino++; |
186 | 82fc7be7 | Martin Mares | BEGIN(INITIAL); |
187 | } |
||
188 | |||
189 | <COMMENT>. |
||
190 | |||
191 | <CCOMM>\*\/ BEGIN(INITIAL); |
||
192 | 31b3e1bb | Martin Mares | <CCOMM>\n conf_lino++; |
193 | 82fc7be7 | Martin Mares | <CCOMM>\/\* cf_error("Comment nesting not supported"); |
194 | <CCOMM><<EOF>> cf_error("Unterminated comment"); |
||
195 | <CCOMM>. |
||
196 | |||
197 | c8d5ffaf | Pavel Machek | \!\= return NEQ; |
198 | \<\= return LEQ; |
||
199 | \>\= return GEQ; |
||
200 | 5f4aee76 | Pavel Machek | \&\& return AND; |
201 | \|\| return OR; |
||
202 | c8d5ffaf | Pavel Machek | |
203 | 82fc7be7 | Martin Mares | %% |
204 | |||
205 | static int |
||
206 | cf_hash(byte *c) |
||
207 | { |
||
208 | unsigned int h = 13; |
||
209 | |||
210 | while (*c) |
||
211 | h = (h * 37) + *c++; |
||
212 | return h; |
||
213 | } |
||
214 | |||
215 | static struct symbol * |
||
216 | 04dc62a0 | Martin Mares | cf_new_sym(byte *c, unsigned int h) |
217 | { |
||
218 | struct symbol *s, **ht; |
||
219 | int l; |
||
220 | |||
221 | if (!new_config->sym_hash) |
||
222 | new_config->sym_hash = cfg_allocz(SYM_HASH_SIZE * sizeof(struct keyword *)); |
||
223 | ht = new_config->sym_hash; |
||
224 | l = strlen(c); |
||
225 | if (l > SYM_MAX_LEN) |
||
226 | cf_error("Symbol too long"); |
||
227 | s = cfg_alloc(sizeof(struct symbol) + l); |
||
228 | s->next = ht[h]; |
||
229 | ht[h] = s; |
||
230 | s->scope = conf_this_scope; |
||
231 | s->class = SYM_VOID; |
||
232 | s->def = NULL; |
||
233 | s->aux = 0; |
||
234 | strcpy(s->name, c); |
||
235 | return s; |
||
236 | } |
||
237 | |||
238 | static struct symbol * |
||
239 | 82fc7be7 | Martin Mares | cf_find_sym(byte *c, unsigned int h0) |
240 | { |
||
241 | unsigned int h = h0 & (SYM_HASH_SIZE-1); |
||
242 | c9aae7f4 | Martin Mares | struct symbol *s, **ht; |
243 | 82fc7be7 | Martin Mares | |
244 | c9aae7f4 | Martin Mares | if (ht = new_config->sym_hash) |
245 | { |
||
246 | for(s = ht[h]; s; s=s->next) |
||
247 | if (!strcmp(s->name, c) && s->scope->active) |
||
248 | return s; |
||
249 | } |
||
250 | if (new_config->sym_fallback) |
||
251 | { |
||
252 | /* We know only top-level scope is active */ |
||
253 | for(s = new_config->sym_fallback[h]; s; s=s->next) |
||
254 | c8f61a01 | Martin Mares | if (!strcmp(s->name, c) && s->scope->active) |
255 | bc2fb680 | Martin Mares | return s; |
256 | c9aae7f4 | Martin Mares | } |
257 | 04dc62a0 | Martin Mares | return cf_new_sym(c, h); |
258 | 82fc7be7 | Martin Mares | } |
259 | |||
260 | 06607335 | Martin Mares | /** |
261 | * cf_find_symbol - find a symbol by name |
||
262 | * @c: symbol name |
||
263 | * |
||
264 | * This functions searches the symbol table for a symbol of given |
||
265 | * name. First it examines the current scope, then the second recent |
||
266 | * one and so on until it either finds the symbol and returns a pointer |
||
267 | * to its &symbol structure or reaches the end of the scope chain |
||
268 | * and returns %NULL to signify no match. |
||
269 | */ |
||
270 | 8450be97 | Martin Mares | struct symbol * |
271 | 4107df1d | Martin Mares | cf_find_symbol(byte *c) |
272 | { |
||
273 | return cf_find_sym(c, cf_hash(c)); |
||
274 | } |
||
275 | |||
276 | struct symbol * |
||
277 | d272fe22 | Martin Mares | cf_default_name(char *template, int *counter) |
278 | 8450be97 | Martin Mares | { |
279 | char buf[32]; |
||
280 | struct symbol *s; |
||
281 | d272fe22 | Martin Mares | char *perc = strchr(template, '%'); |
282 | 8450be97 | Martin Mares | |
283 | d272fe22 | Martin Mares | for(;;) |
284 | 8450be97 | Martin Mares | { |
285 | d272fe22 | Martin Mares | bsprintf(buf, template, ++(*counter)); |
286 | 8450be97 | Martin Mares | s = cf_find_sym(buf, cf_hash(buf)); |
287 | d272fe22 | Martin Mares | if (!s) |
288 | break; |
||
289 | if (s->class == SYM_VOID) |
||
290 | return s; |
||
291 | if (!perc) |
||
292 | break; |
||
293 | 8450be97 | Martin Mares | } |
294 | d272fe22 | Martin Mares | cf_error("Unable to generate default name"); |
295 | 8450be97 | Martin Mares | } |
296 | |||
297 | 06607335 | Martin Mares | /** |
298 | * cf_define_symbol - define meaning of a symbol |
||
299 | * @sym: symbol to be defined |
||
300 | * @type: symbol class to assign |
||
301 | * @def: class dependent data |
||
302 | * |
||
303 | 04dc62a0 | Martin Mares | * Defines new meaning of a symbol. If the symbol is an undefined |
304 | * one (%SYM_VOID), it's just re-defined to the new type. If it's defined |
||
305 | * in different scope, a new symbol in current scope is created and the |
||
306 | * meaning is assigned to it. If it's already defined in the current scope, |
||
307 | * an error is reported via cf_error(). |
||
308 | * |
||
309 | * Result: Pointer to the newly defined symbol. If we are in the top-level |
||
310 | * scope, it's the same @sym as passed to the function. |
||
311 | 06607335 | Martin Mares | */ |
312 | 04dc62a0 | Martin Mares | struct symbol * |
313 | 4107df1d | Martin Mares | cf_define_symbol(struct symbol *sym, int type, void *def) |
314 | { |
||
315 | if (sym->class) |
||
316 | 04dc62a0 | Martin Mares | { |
317 | if (sym->scope == conf_this_scope) |
||
318 | cf_error("Symbol already defined"); |
||
319 | sym = cf_new_sym(sym->name, cf_hash(sym->name) & (SYM_HASH_SIZE-1)); |
||
320 | } |
||
321 | 4107df1d | Martin Mares | sym->class = type; |
322 | sym->def = def; |
||
323 | 04dc62a0 | Martin Mares | return sym; |
324 | 4107df1d | Martin Mares | } |
325 | |||
326 | c9aae7f4 | Martin Mares | static void |
327 | cf_lex_init_kh(void) |
||
328 | { |
||
329 | struct keyword *k; |
||
330 | |||
331 | for(k=keyword_list; k->name; k++) |
||
332 | { |
||
333 | unsigned h = cf_hash(k->name) & (KW_HASH_SIZE-1); |
||
334 | k->next = kw_hash[h]; |
||
335 | kw_hash[h] = k; |
||
336 | } |
||
337 | kw_hash_inited = 1; |
||
338 | } |
||
339 | |||
340 | 06607335 | Martin Mares | /** |
341 | * cf_lex_init - initialize the lexer |
||
342 | * @is_cli: true if we're going to parse CLI command, false for configuration |
||
343 | * |
||
344 | 2e9b2421 | Martin Mares | * cf_lex_init() initializes the lexical analyzer and prepares it for |
345 | 06607335 | Martin Mares | * parsing of a new input. |
346 | */ |
||
347 | 4107df1d | Martin Mares | void |
348 | bc2fb680 | Martin Mares | cf_lex_init(int is_cli) |
349 | 82fc7be7 | Martin Mares | { |
350 | c9aae7f4 | Martin Mares | if (!kw_hash_inited) |
351 | cf_lex_init_kh(); |
||
352 | 31b3e1bb | Martin Mares | conf_lino = 1; |
353 | bc2fb680 | Martin Mares | yyrestart(NULL); |
354 | if (is_cli) |
||
355 | BEGIN(CLI); |
||
356 | else |
||
357 | BEGIN(INITIAL); |
||
358 | c8f61a01 | Martin Mares | conf_this_scope = cfg_allocz(sizeof(struct sym_scope)); |
359 | conf_this_scope->active = 1; |
||
360 | 82fc7be7 | Martin Mares | } |
361 | |||
362 | 06607335 | Martin Mares | /** |
363 | * cf_push_scope - enter new scope |
||
364 | * @sym: symbol representing scope name |
||
365 | * |
||
366 | * If we want to enter a new scope to process declarations inside |
||
367 | * a nested block, we can just call cf_push_scope() to push a new |
||
368 | * scope onto the scope stack which will cause all new symbols to be |
||
369 | * defined in this scope and all existing symbols to be sought for |
||
370 | * in all scopes stored on the stack. |
||
371 | */ |
||
372 | 82fc7be7 | Martin Mares | void |
373 | c8f61a01 | Martin Mares | cf_push_scope(struct symbol *sym) |
374 | { |
||
375 | struct sym_scope *s = cfg_alloc(sizeof(struct sym_scope)); |
||
376 | |||
377 | s->next = conf_this_scope; |
||
378 | conf_this_scope = s; |
||
379 | s->active = 1; |
||
380 | s->name = sym; |
||
381 | } |
||
382 | |||
383 | 06607335 | Martin Mares | /** |
384 | * cf_pop_scope - leave a scope |
||
385 | * |
||
386 | * cf_pop_scope() pops the topmost scope from the scope stack, |
||
387 | * leaving all its symbols in the symbol table, but making them |
||
388 | * invisible to the rest of the config. |
||
389 | */ |
||
390 | c8f61a01 | Martin Mares | void |
391 | cf_pop_scope(void) |
||
392 | { |
||
393 | conf_this_scope->active = 0; |
||
394 | conf_this_scope = conf_this_scope->next; |
||
395 | ASSERT(conf_this_scope); |
||
396 | } |
||
397 | 4b87e256 | Martin Mares | |
398 | struct symbol * |
||
399 | cf_walk_symbols(struct config *cf, struct symbol *sym, int *pos) |
||
400 | { |
||
401 | for(;;) |
||
402 | { |
||
403 | if (!sym) |
||
404 | { |
||
405 | if (*pos >= SYM_HASH_SIZE) |
||
406 | return NULL; |
||
407 | sym = cf->sym_hash[(*pos)++]; |
||
408 | } |
||
409 | else |
||
410 | sym = sym->next; |
||
411 | if (sym && sym->scope->active) |
||
412 | return sym; |
||
413 | } |
||
414 | } |
||
415 | |||
416 | 06607335 | Martin Mares | /** |
417 | * cf_symbol_class_name - get name of a symbol class |
||
418 | * @sym: symbol |
||
419 | * |
||
420 | * This function returns a string representing the class |
||
421 | * of the given symbol. |
||
422 | */ |
||
423 | 4b87e256 | Martin Mares | char * |
424 | cf_symbol_class_name(struct symbol *sym) |
||
425 | { |
||
426 | switch (sym->class) |
||
427 | { |
||
428 | case SYM_VOID: |
||
429 | return "undefined"; |
||
430 | case SYM_PROTO: |
||
431 | return "protocol"; |
||
432 | case SYM_NUMBER: |
||
433 | return "numeric constant"; |
||
434 | case SYM_FUNCTION: |
||
435 | return "function"; |
||
436 | case SYM_FILTER: |
||
437 | return "filter"; |
||
438 | case SYM_TABLE: |
||
439 | return "routing table"; |
||
440 | c0b2f646 | Martin Mares | case SYM_IPA: |
441 | return "network address"; |
||
442 | 4b87e256 | Martin Mares | default: |
443 | return "unknown type"; |
||
444 | } |
||
445 | } |
||
446 | 58f94537 | Martin Mares | |
447 | |||
448 | /** |
||
449 | * DOC: Parser |
||
450 | * |
||
451 | 2e9b2421 | Martin Mares | * Both the configuration and CLI commands are analyzed using a syntax |
452 | 58f94537 | Martin Mares | * driven parser generated by the |bison| tool from a grammar which |
453 | * is constructed from information gathered from grammar snippets by |
||
454 | * the |gen_parser.m4| script. |
||
455 | * |
||
456 | * Grammar snippets are files (usually with extension |.Y|) contributed |
||
457 | 58f7d004 | Martin Mares | * by various BIRD modules in order to provide information about syntax of their |
458 | 58f94537 | Martin Mares | * configuration and their CLI commands. Each snipped consists of several |
459 | 725270cb | Martin Mares | * sections, each of them starting with a special keyword: |CF_HDR| for |
460 | 58f94537 | Martin Mares | * a list of |#include| directives needed by the C code, |CF_DEFINES| |
461 | * for a list of C declarations, |CF_DECLS| for |bison| declarations |
||
462 | * including keyword definitions specified as |CF_KEYWORDS|, |CF_GRAMMAR| |
||
463 | 2e9b2421 | Martin Mares | * for the grammar rules, |CF_CODE| for auxiliary C code and finally |
464 | 58f94537 | Martin Mares | * |CF_END| at the end of the snippet. |
465 | * |
||
466 | * To create references between the snippets, it's possible to define |
||
467 | * multi-part rules by utilizing the |CF_ADDTO| macro which adds a new |
||
468 | * alternative to a multi-part rule. |
||
469 | * |
||
470 | * CLI commands are defined using a |CF_CLI| macro. Its parameters are: |
||
471 | 2e9b2421 | Martin Mares | * the list of keywords determining the command, the list of parameters, |
472 | 58f94537 | Martin Mares | * help text for the parameters and help text for the command. |
473 | * |
||
474 | * Values of |enum| filter types can be defined using |CF_ENUM| with |
||
475 | * the following parameters: name of filter type, prefix common for all |
||
476 | 725270cb | Martin Mares | * literals of this type and names of all the possible values. |
477 | 58f94537 | Martin Mares | */ |