]>
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 MM |
9 | /** |
10 | * DOC: Lexical analyser | |
11 | * | |
12 | * The lexical analyser used for configuration files and CLI commands | |
13 | * is generated using the |flex| tool accompanied with a couple of | |
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 | |
18 | * of the symbol, its scope, symbol class (%SYM_PROTO for a name of a protocol, | |
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 MM |
35 | #include "nest/route.h" |
36 | #include "filter/filter.h" | |
82fc7be7 MM |
37 | #include "conf/conf.h" |
38 | #include "conf/cf-parse.tab.h" | |
d272fe22 | 39 | #include "lib/string.h" |
82fc7be7 | 40 | |
a412f01e | 41 | struct keyword { |
82fc7be7 MM |
42 | byte *name; |
43 | int value; | |
44 | struct keyword *next; | |
a412f01e MM |
45 | }; |
46 | ||
49e7e5ee | 47 | #include "conf/keywords.h" |
82fc7be7 MM |
48 | |
49 | #define KW_HASH_SIZE 64 | |
c9aae7f4 MM |
50 | static struct keyword *kw_hash[KW_HASH_SIZE]; |
51 | static int kw_hash_inited; | |
52 | ||
82fc7be7 MM |
53 | #define SYM_HASH_SIZE 128 |
54 | #define SYM_MAX_LEN 32 | |
55 | ||
c8f61a01 MM |
56 | 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 | ||
31b3e1bb MM |
63 | int conf_lino; |
64 | ||
82fc7be7 MM |
65 | static int cf_hash(byte *c); |
66 | static struct symbol *cf_find_sym(byte *c, unsigned int h0); | |
67 | ||
b35d72ac | 68 | linpool *cfg_mem; |
82fc7be7 MM |
69 | |
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 | ||
bc2fb680 | 80 | %x COMMENT CCOMM CLI |
82fc7be7 MM |
81 | |
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}+ { | |
dce26783 MM |
91 | #ifdef IPV6 |
92 | if (ipv4_pton_u32(yytext, &cf_lval.i32)) | |
93 | return RTRID; | |
94 | cf_error("Invalid IPv4 address %s", yytext); | |
95 | #else | |
82fc7be7 MM |
96 | if (ip_pton(yytext, &cf_lval.a)) |
97 | return IPA; | |
dce26783 MM |
98 | 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 | |
82fc7be7 MM |
110 | } |
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)) | |
944f008a MM |
140 | { |
141 | if (k->value > 0) | |
142 | return k->value; | |
143 | else | |
144 | { | |
145 | cf_lval.i = -k->value; | |
146 | return ENUM; | |
147 | } | |
148 | } | |
82fc7be7 MM |
149 | k=k->next; |
150 | } | |
151 | cf_lval.s = cf_find_sym(yytext, h); | |
152 | return SYM; | |
153 | } | |
154 | ||
efe51e38 | 155 | <CLI>(.|\n) { |
bc2fb680 MM |
156 | BEGIN(INITIAL); |
157 | return CLI_MARKER; | |
158 | } | |
159 | ||
69a20d2e | 160 | [={}:;,.()+*/%<>~\[\]?!-] { |
82fc7be7 MM |
161 | return yytext[0]; |
162 | } | |
163 | ||
164 | ["][^"\n]*["] { | |
49e4a4d1 | 165 | yytext[yyleng-1] = 0; |
ca0edc53 | 166 | cf_lval.t = cfg_strdup(yytext+1); |
82fc7be7 MM |
167 | return TEXT; |
168 | } | |
169 | ||
170 | ["][^"\n]*\n cf_error("Unterminated string"); | |
171 | ||
172 | <INITIAL,COMMENT><<EOF>> return END; | |
173 | ||
174 | {WHITE}+ | |
175 | ||
7f400d1c | 176 | \n conf_lino++; |
82fc7be7 | 177 | |
72614174 | 178 | # BEGIN(COMMENT); |
82fc7be7 | 179 | |
72614174 | 180 | \/\* BEGIN(CCOMM); |
82fc7be7 MM |
181 | |
182 | . cf_error("Unknown character"); | |
183 | ||
184 | <COMMENT>\n { | |
31b3e1bb | 185 | conf_lino++; |
82fc7be7 MM |
186 | BEGIN(INITIAL); |
187 | } | |
188 | ||
189 | <COMMENT>. | |
190 | ||
191 | <CCOMM>\*\/ BEGIN(INITIAL); | |
31b3e1bb | 192 | <CCOMM>\n conf_lino++; |
82fc7be7 MM |
193 | <CCOMM>\/\* cf_error("Comment nesting not supported"); |
194 | <CCOMM><<EOF>> cf_error("Unterminated comment"); | |
195 | <CCOMM>. | |
196 | ||
c8d5ffaf PM |
197 | \!\= return NEQ; |
198 | \<\= return LEQ; | |
199 | \>\= return GEQ; | |
5f4aee76 PM |
200 | \&\& return AND; |
201 | \|\| return OR; | |
c8d5ffaf | 202 | |
82fc7be7 MM |
203 | %% |
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 | ||
04dc62a0 MM |
215 | static struct symbol * |
216 | 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 | ||
82fc7be7 MM |
238 | static struct symbol * |
239 | cf_find_sym(byte *c, unsigned int h0) | |
240 | { | |
241 | unsigned int h = h0 & (SYM_HASH_SIZE-1); | |
c9aae7f4 | 242 | struct symbol *s, **ht; |
82fc7be7 | 243 | |
c9aae7f4 MM |
244 | 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) | |
c8f61a01 | 254 | if (!strcmp(s->name, c) && s->scope->active) |
bc2fb680 | 255 | return s; |
c9aae7f4 | 256 | } |
04dc62a0 | 257 | return cf_new_sym(c, h); |
82fc7be7 MM |
258 | } |
259 | ||
06607335 MM |
260 | /** |
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 | */ | |
4107df1d MM |
270 | struct symbol * |
271 | cf_find_symbol(byte *c) | |
272 | { | |
273 | return cf_find_sym(c, cf_hash(c)); | |
274 | } | |
275 | ||
8450be97 | 276 | struct symbol * |
d272fe22 | 277 | cf_default_name(char *template, int *counter) |
8450be97 MM |
278 | { |
279 | char buf[32]; | |
280 | struct symbol *s; | |
d272fe22 | 281 | char *perc = strchr(template, '%'); |
8450be97 | 282 | |
d272fe22 | 283 | for(;;) |
8450be97 | 284 | { |
d272fe22 | 285 | bsprintf(buf, template, ++(*counter)); |
8450be97 | 286 | s = cf_find_sym(buf, cf_hash(buf)); |
d272fe22 MM |
287 | if (!s) |
288 | break; | |
289 | if (s->class == SYM_VOID) | |
290 | return s; | |
291 | if (!perc) | |
292 | break; | |
8450be97 | 293 | } |
d272fe22 | 294 | cf_error("Unable to generate default name"); |
8450be97 MM |
295 | } |
296 | ||
06607335 MM |
297 | /** |
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 | * | |
04dc62a0 MM |
303 | * 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. | |
06607335 | 311 | */ |
04dc62a0 | 312 | struct symbol * |
4107df1d MM |
313 | cf_define_symbol(struct symbol *sym, int type, void *def) |
314 | { | |
315 | if (sym->class) | |
04dc62a0 MM |
316 | { |
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 | } | |
4107df1d MM |
321 | sym->class = type; |
322 | sym->def = def; | |
04dc62a0 | 323 | return sym; |
4107df1d MM |
324 | } |
325 | ||
c9aae7f4 MM |
326 | 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 | ||
06607335 MM |
340 | /** |
341 | * cf_lex_init - initialize the lexer | |
342 | * @is_cli: true if we're going to parse CLI command, false for configuration | |
343 | * | |
344 | * cf_lex_init() initializes the lexical analyser and prepares it for | |
345 | * parsing of a new input. | |
346 | */ | |
82fc7be7 | 347 | void |
bc2fb680 | 348 | cf_lex_init(int is_cli) |
82fc7be7 | 349 | { |
c9aae7f4 MM |
350 | if (!kw_hash_inited) |
351 | cf_lex_init_kh(); | |
31b3e1bb | 352 | conf_lino = 1; |
bc2fb680 MM |
353 | yyrestart(NULL); |
354 | if (is_cli) | |
355 | BEGIN(CLI); | |
356 | else | |
357 | BEGIN(INITIAL); | |
c8f61a01 MM |
358 | conf_this_scope = cfg_allocz(sizeof(struct sym_scope)); |
359 | conf_this_scope->active = 1; | |
82fc7be7 MM |
360 | } |
361 | ||
06607335 MM |
362 | /** |
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 | */ | |
c8f61a01 MM |
372 | void |
373 | 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 | ||
06607335 MM |
383 | /** |
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 | */ | |
c8f61a01 MM |
390 | 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 | } | |
4b87e256 MM |
397 | |
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 | ||
06607335 MM |
416 | /** |
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 | */ | |
4b87e256 MM |
423 | 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"; | |
c0b2f646 MM |
440 | case SYM_IPA: |
441 | return "network address"; | |
4b87e256 MM |
442 | default: |
443 | return "unknown type"; | |
444 | } | |
445 | } | |
58f94537 MM |
446 | |
447 | ||
448 | /** | |
449 | * DOC: Parser | |
450 | * | |
451 | * Both the configuration and CLI commands are analysed using a syntax | |
452 | * 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 | * by various BIRD modules to provide information about syntax of their | |
458 | * configuration and their CLI commands. Each snipped consists of several | |
459 | * section, each of them starting with a special keyword: |CF_HDR| for | |
460 | * 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 | * for the grammar rules, |CF_CODE| for auxillary C code and finally | |
464 | * |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 | * the list of keywords determining the command, the list of paramaters, | |
472 | * 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 | * literals of this type, names of all the possible values. | |
477 | */ |