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