]>
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 | |
1103b32e OZ |
18 | * of the symbol, its lexical scope, symbol class (%SYM_PROTO for a |
19 | * name of a protocol, %SYM_CONSTANT for a constant etc.) and class | |
20 | * dependent data. When an unknown symbol is encountered, it's | |
21 | * automatically added to the symbol table with class %SYM_VOID. | |
06607335 MM |
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> |
60fd666b | 33 | #include <unistd.h> |
4be266a9 OZ |
34 | #include <libgen.h> |
35 | #include <glob.h> | |
36 | #include <fcntl.h> | |
37 | #include <sys/stat.h> | |
38 | #include <sys/types.h> | |
39 | #include <sys/stat.h> | |
82fc7be7 | 40 | |
93e868c7 OZ |
41 | #define PARSER 1 |
42 | ||
82fc7be7 | 43 | #include "nest/bird.h" |
944f008a | 44 | #include "nest/route.h" |
e304fd4b | 45 | #include "nest/protocol.h" |
944f008a | 46 | #include "filter/filter.h" |
82fc7be7 MM |
47 | #include "conf/conf.h" |
48 | #include "conf/cf-parse.tab.h" | |
d272fe22 | 49 | #include "lib/string.h" |
82fc7be7 | 50 | |
a412f01e | 51 | struct keyword { |
82fc7be7 MM |
52 | byte *name; |
53 | int value; | |
54 | struct keyword *next; | |
a412f01e MM |
55 | }; |
56 | ||
49e7e5ee | 57 | #include "conf/keywords.h" |
82fc7be7 MM |
58 | |
59 | #define KW_HASH_SIZE 64 | |
c9aae7f4 MM |
60 | static struct keyword *kw_hash[KW_HASH_SIZE]; |
61 | static int kw_hash_inited; | |
62 | ||
82fc7be7 MM |
63 | #define SYM_HASH_SIZE 128 |
64 | #define SYM_MAX_LEN 32 | |
65 | ||
c8f61a01 MM |
66 | struct sym_scope { |
67 | struct sym_scope *next; /* Next on scope stack */ | |
68 | struct symbol *name; /* Name of this scope */ | |
69 | int active; /* Currently entered */ | |
70 | }; | |
71 | static struct sym_scope *conf_this_scope; | |
72 | ||
82fc7be7 MM |
73 | static int cf_hash(byte *c); |
74 | static struct symbol *cf_find_sym(byte *c, unsigned int h0); | |
75 | ||
b35d72ac | 76 | linpool *cfg_mem; |
82fc7be7 | 77 | |
48ec367a | 78 | int (*cf_read_hook)(byte *buf, unsigned int max, int fd); |
9b7fdfc8 | 79 | struct include_file_stack *ifs; |
4be266a9 OZ |
80 | static struct include_file_stack *ifs_head; |
81 | ||
82 | #define MAX_INCLUDE_DEPTH 8 | |
82fc7be7 | 83 | |
4be266a9 | 84 | #define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd); |
82fc7be7 MM |
85 | #define YY_NO_UNPUT |
86 | #define YY_FATAL_ERROR(msg) cf_error(msg) | |
87 | ||
4be266a9 | 88 | static void cf_include(char *arg, int alen); |
48ec367a | 89 | static int check_eof(void); |
48ec367a | 90 | |
82fc7be7 MM |
91 | %} |
92 | ||
93 | %option noyywrap | |
506fa1a7 OZ |
94 | %option noinput |
95 | %option nounput | |
96 | %option noreject | |
82fc7be7 | 97 | |
bc2fb680 | 98 | %x COMMENT CCOMM CLI |
82fc7be7 MM |
99 | |
100 | ALPHA [a-zA-Z_] | |
101 | DIGIT [0-9] | |
102 | XIGIT [0-9a-fA-F] | |
103 | ALNUM [a-zA-Z_0-9] | |
104 | WHITE [ \t] | |
48ec367a | 105 | include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*; |
82fc7be7 MM |
106 | |
107 | %% | |
4be266a9 OZ |
108 | {include} { |
109 | char *start, *end; | |
110 | ||
111 | if (!ifs->depth) | |
112 | cf_error("Include not allowed in CLI"); | |
113 | ||
114 | start = strchr(yytext, '"'); | |
115 | start++; | |
116 | ||
117 | end = strchr(start, '"'); | |
118 | *end = 0; | |
119 | ||
120 | if (start == end) | |
121 | cf_error("Include with empty argument"); | |
122 | ||
123 | cf_include(start, end-start); | |
124 | } | |
82fc7be7 MM |
125 | |
126 | {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ { | |
dce26783 MM |
127 | #ifdef IPV6 |
128 | if (ipv4_pton_u32(yytext, &cf_lval.i32)) | |
129 | return RTRID; | |
130 | cf_error("Invalid IPv4 address %s", yytext); | |
131 | #else | |
82fc7be7 MM |
132 | if (ip_pton(yytext, &cf_lval.a)) |
133 | return IPA; | |
dce26783 MM |
134 | cf_error("Invalid IP address %s", yytext); |
135 | #endif | |
136 | } | |
137 | ||
138 | ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) { | |
139 | #ifdef IPV6 | |
140 | if (ip_pton(yytext, &cf_lval.a)) | |
141 | return IPA; | |
142 | cf_error("Invalid IP address %s", yytext); | |
143 | #else | |
144 | cf_error("This is an IPv4 router, therefore IPv6 addresses are not supported"); | |
145 | #endif | |
82fc7be7 MM |
146 | } |
147 | ||
506fa1a7 | 148 | 0x{XIGIT}+ { |
82fc7be7 | 149 | char *e; |
c32c3f88 | 150 | unsigned long int l; |
82fc7be7 MM |
151 | errno = 0; |
152 | l = strtoul(yytext+2, &e, 16); | |
c32c3f88 | 153 | if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l) |
82fc7be7 MM |
154 | cf_error("Number out of range"); |
155 | cf_lval.i = l; | |
156 | return NUM; | |
157 | } | |
158 | ||
159 | {DIGIT}+ { | |
160 | char *e; | |
c32c3f88 | 161 | unsigned long int l; |
82fc7be7 MM |
162 | errno = 0; |
163 | l = strtoul(yytext, &e, 10); | |
c32c3f88 | 164 | if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l) |
82fc7be7 MM |
165 | cf_error("Number out of range"); |
166 | cf_lval.i = l; | |
167 | return NUM; | |
168 | } | |
169 | ||
26d92bb8 OZ |
170 | else: { |
171 | /* Hack to distinguish if..else from else: in case */ | |
172 | return ELSECOL; | |
173 | } | |
174 | ||
1f64a487 | 175 | ({ALPHA}{ALNUM}*|[']({ALNUM}|[-]|[\.]|[:])*[']) { |
0efd6462 OF |
176 | if(*yytext == '\'') { |
177 | yytext[yyleng-1] = 0; | |
178 | yytext++; | |
179 | } | |
82fc7be7 MM |
180 | unsigned int h = cf_hash(yytext); |
181 | struct keyword *k = kw_hash[h & (KW_HASH_SIZE-1)]; | |
182 | while (k) | |
183 | { | |
184 | if (!strcmp(k->name, yytext)) | |
944f008a MM |
185 | { |
186 | if (k->value > 0) | |
187 | return k->value; | |
188 | else | |
189 | { | |
190 | cf_lval.i = -k->value; | |
191 | return ENUM; | |
192 | } | |
193 | } | |
82fc7be7 MM |
194 | k=k->next; |
195 | } | |
196 | cf_lval.s = cf_find_sym(yytext, h); | |
197 | return SYM; | |
198 | } | |
199 | ||
efe51e38 | 200 | <CLI>(.|\n) { |
bc2fb680 MM |
201 | BEGIN(INITIAL); |
202 | return CLI_MARKER; | |
203 | } | |
204 | ||
b8cc390e OZ |
205 | \.\. { |
206 | return DDOT; | |
207 | } | |
208 | ||
f9491630 | 209 | [={}:;,.()+*/%<>~\[\]?!\|-] { |
82fc7be7 MM |
210 | return yytext[0]; |
211 | } | |
212 | ||
213 | ["][^"\n]*["] { | |
49e4a4d1 | 214 | yytext[yyleng-1] = 0; |
ca0edc53 | 215 | cf_lval.t = cfg_strdup(yytext+1); |
82fc7be7 MM |
216 | return TEXT; |
217 | } | |
218 | ||
219 | ["][^"\n]*\n cf_error("Unterminated string"); | |
220 | ||
4be266a9 | 221 | <INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; } |
82fc7be7 MM |
222 | |
223 | {WHITE}+ | |
224 | ||
4be266a9 | 225 | \n ifs->lino++; |
82fc7be7 | 226 | |
72614174 | 227 | # BEGIN(COMMENT); |
82fc7be7 | 228 | |
72614174 | 229 | \/\* BEGIN(CCOMM); |
82fc7be7 MM |
230 | |
231 | . cf_error("Unknown character"); | |
232 | ||
233 | <COMMENT>\n { | |
4be266a9 | 234 | ifs->lino++; |
82fc7be7 MM |
235 | BEGIN(INITIAL); |
236 | } | |
237 | ||
238 | <COMMENT>. | |
239 | ||
240 | <CCOMM>\*\/ BEGIN(INITIAL); | |
4be266a9 | 241 | <CCOMM>\n ifs->lino++; |
82fc7be7 MM |
242 | <CCOMM>\/\* cf_error("Comment nesting not supported"); |
243 | <CCOMM><<EOF>> cf_error("Unterminated comment"); | |
244 | <CCOMM>. | |
245 | ||
c8d5ffaf PM |
246 | \!\= return NEQ; |
247 | \<\= return LEQ; | |
248 | \>\= return GEQ; | |
5f4aee76 PM |
249 | \&\& return AND; |
250 | \|\| return OR; | |
c8d5ffaf | 251 | |
cf186034 OZ |
252 | \[\= return PO; |
253 | \=\] return PC; | |
254 | ||
82fc7be7 MM |
255 | %% |
256 | ||
257 | static int | |
258 | cf_hash(byte *c) | |
259 | { | |
260 | unsigned int h = 13; | |
261 | ||
262 | while (*c) | |
263 | h = (h * 37) + *c++; | |
264 | return h; | |
265 | } | |
266 | ||
4be266a9 OZ |
267 | |
268 | /* | |
269 | * IFS stack - it contains structures needed for recursive processing | |
270 | * of include in config files. On the top of the stack is a structure | |
271 | * for currently processed file. Other structures are either for | |
272 | * active files interrupted because of include directive (these have | |
273 | * fd and flex buffer) or for inactive files scheduled to be processed | |
274 | * later (when parent requested including of several files by wildcard | |
275 | * match - these do not have fd and flex buffer yet). | |
276 | * | |
277 | * FIXME: Most of these ifs and include functions are really sysdep/unix. | |
4be266a9 OZ |
278 | */ |
279 | ||
280 | static struct include_file_stack * | |
281 | push_ifs(struct include_file_stack *old) | |
282 | { | |
283 | struct include_file_stack *ret; | |
284 | ret = cfg_allocz(sizeof(struct include_file_stack)); | |
285 | ret->lino = 1; | |
286 | ret->prev = old; | |
287 | return ret; | |
288 | } | |
289 | ||
290 | static struct include_file_stack * | |
291 | pop_ifs(struct include_file_stack *old) | |
292 | { | |
293 | yy_delete_buffer(old->buffer); | |
294 | close(old->fd); | |
295 | return old->prev; | |
296 | } | |
297 | ||
48ec367a | 298 | static void |
4be266a9 | 299 | enter_ifs(struct include_file_stack *new) |
48ec367a | 300 | { |
4be266a9 OZ |
301 | if (!new->buffer) |
302 | { | |
303 | new->fd = open(new->file_name, O_RDONLY); | |
304 | if (new->fd < 0) | |
305 | { | |
306 | ifs = ifs->up; | |
307 | cf_error("Unable to open included file %s: %m", new->file_name); | |
308 | } | |
309 | ||
310 | new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE); | |
311 | } | |
48ec367a | 312 | |
4be266a9 OZ |
313 | yy_switch_to_buffer(new->buffer); |
314 | } | |
48ec367a | 315 | |
0c3d9dac OZ |
316 | /** |
317 | * cf_lex_unwind - unwind lexer state during error | |
318 | * | |
319 | * cf_lex_unwind() frees the internal state on IFS stack when the lexical | |
320 | * analyzer is terminated by cf_error(). | |
321 | */ | |
322 | void | |
323 | cf_lex_unwind(void) | |
324 | { | |
325 | struct include_file_stack *n; | |
326 | ||
327 | for (n = ifs; n != ifs_head; n = n->prev) | |
328 | { | |
329 | /* Memory is freed automatically */ | |
330 | if (n->buffer) | |
331 | yy_delete_buffer(n->buffer); | |
332 | if (n->fd) | |
333 | close(n->fd); | |
334 | } | |
335 | ||
336 | ifs = ifs_head; | |
337 | } | |
338 | ||
4be266a9 OZ |
339 | static void |
340 | cf_include(char *arg, int alen) | |
341 | { | |
342 | struct include_file_stack *base_ifs = ifs; | |
343 | int new_depth, rv, i; | |
344 | char *patt; | |
0c3d9dac | 345 | glob_t g = {}; |
4be266a9 OZ |
346 | |
347 | new_depth = ifs->depth + 1; | |
348 | if (new_depth > MAX_INCLUDE_DEPTH) | |
349 | cf_error("Max include depth reached"); | |
48ec367a | 350 | |
4be266a9 OZ |
351 | /* expand arg to properly handle relative filenames */ |
352 | if (*arg != '/') | |
353 | { | |
354 | int dlen = strlen(ifs->file_name); | |
355 | char *dir = alloca(dlen + 1); | |
356 | patt = alloca(dlen + alen + 2); | |
357 | memcpy(dir, ifs->file_name, dlen + 1); | |
358 | sprintf(patt, "%s/%s", dirname(dir), arg); | |
359 | } | |
360 | else | |
361 | patt = arg; | |
48ec367a | 362 | |
4be266a9 OZ |
363 | /* Skip globbing if there are no wildcards, mainly to get proper |
364 | response when the included config file is missing */ | |
365 | if (!strpbrk(arg, "?*[")) | |
366 | { | |
367 | ifs = push_ifs(ifs); | |
368 | ifs->file_name = cfg_strdup(patt); | |
369 | ifs->depth = new_depth; | |
370 | ifs->up = base_ifs; | |
371 | enter_ifs(ifs); | |
372 | return; | |
373 | } | |
48ec367a | 374 | |
4be266a9 OZ |
375 | /* Expand the pattern */ |
376 | rv = glob(patt, GLOB_ERR | GLOB_NOESCAPE, NULL, &g); | |
377 | if (rv == GLOB_ABORTED) | |
378 | cf_error("Unable to match pattern %s: %m", patt); | |
379 | if ((rv != 0) || (g.gl_pathc <= 0)) | |
380 | return; | |
381 | ||
382 | /* | |
383 | * Now we put all found files to ifs stack in reverse order, they | |
384 | * will be activated and processed in order as ifs stack is popped | |
385 | * by pop_ifs() and enter_ifs() in check_eof(). | |
386 | */ | |
387 | for(i = g.gl_pathc - 1; i >= 0; i--) | |
388 | { | |
389 | char *fname = g.gl_pathv[i]; | |
390 | struct stat fs; | |
391 | ||
392 | if (stat(fname, &fs) < 0) | |
0c3d9dac OZ |
393 | { |
394 | globfree(&g); | |
395 | cf_error("Unable to stat included file %s: %m", fname); | |
396 | } | |
4be266a9 OZ |
397 | |
398 | if (fs.st_mode & S_IFDIR) | |
399 | continue; | |
400 | ||
401 | /* Prepare new stack item */ | |
402 | ifs = push_ifs(ifs); | |
403 | ifs->file_name = cfg_strdup(fname); | |
404 | ifs->depth = new_depth; | |
405 | ifs->up = base_ifs; | |
406 | } | |
407 | ||
408 | globfree(&g); | |
409 | enter_ifs(ifs); | |
48ec367a OF |
410 | } |
411 | ||
412 | static int | |
413 | check_eof(void) | |
414 | { | |
4be266a9 OZ |
415 | if (ifs == ifs_head) |
416 | { | |
417 | /* EOF in main config file */ | |
418 | ifs->lino = 1; /* Why this? */ | |
419 | return 1; | |
420 | } | |
48ec367a | 421 | |
4be266a9 OZ |
422 | ifs = pop_ifs(ifs); |
423 | enter_ifs(ifs); | |
424 | return 0; | |
48ec367a OF |
425 | } |
426 | ||
04dc62a0 MM |
427 | static struct symbol * |
428 | cf_new_sym(byte *c, unsigned int h) | |
429 | { | |
430 | struct symbol *s, **ht; | |
431 | int l; | |
432 | ||
433 | if (!new_config->sym_hash) | |
434 | new_config->sym_hash = cfg_allocz(SYM_HASH_SIZE * sizeof(struct keyword *)); | |
435 | ht = new_config->sym_hash; | |
436 | l = strlen(c); | |
437 | if (l > SYM_MAX_LEN) | |
438 | cf_error("Symbol too long"); | |
439 | s = cfg_alloc(sizeof(struct symbol) + l); | |
440 | s->next = ht[h]; | |
441 | ht[h] = s; | |
442 | s->scope = conf_this_scope; | |
443 | s->class = SYM_VOID; | |
444 | s->def = NULL; | |
445 | s->aux = 0; | |
446 | strcpy(s->name, c); | |
447 | return s; | |
448 | } | |
449 | ||
82fc7be7 MM |
450 | static struct symbol * |
451 | cf_find_sym(byte *c, unsigned int h0) | |
452 | { | |
453 | unsigned int h = h0 & (SYM_HASH_SIZE-1); | |
c9aae7f4 | 454 | struct symbol *s, **ht; |
82fc7be7 | 455 | |
c9aae7f4 MM |
456 | if (ht = new_config->sym_hash) |
457 | { | |
458 | for(s = ht[h]; s; s=s->next) | |
459 | if (!strcmp(s->name, c) && s->scope->active) | |
460 | return s; | |
461 | } | |
462 | if (new_config->sym_fallback) | |
463 | { | |
464 | /* We know only top-level scope is active */ | |
465 | for(s = new_config->sym_fallback[h]; s; s=s->next) | |
c8f61a01 | 466 | if (!strcmp(s->name, c) && s->scope->active) |
bc2fb680 | 467 | return s; |
c9aae7f4 | 468 | } |
04dc62a0 | 469 | return cf_new_sym(c, h); |
82fc7be7 MM |
470 | } |
471 | ||
06607335 MM |
472 | /** |
473 | * cf_find_symbol - find a symbol by name | |
474 | * @c: symbol name | |
475 | * | |
476 | * This functions searches the symbol table for a symbol of given | |
477 | * name. First it examines the current scope, then the second recent | |
478 | * one and so on until it either finds the symbol and returns a pointer | |
479 | * to its &symbol structure or reaches the end of the scope chain | |
480 | * and returns %NULL to signify no match. | |
481 | */ | |
4107df1d MM |
482 | struct symbol * |
483 | cf_find_symbol(byte *c) | |
484 | { | |
485 | return cf_find_sym(c, cf_hash(c)); | |
486 | } | |
487 | ||
8450be97 | 488 | struct symbol * |
d272fe22 | 489 | cf_default_name(char *template, int *counter) |
8450be97 MM |
490 | { |
491 | char buf[32]; | |
492 | struct symbol *s; | |
d272fe22 | 493 | char *perc = strchr(template, '%'); |
8450be97 | 494 | |
d272fe22 | 495 | for(;;) |
8450be97 | 496 | { |
d272fe22 | 497 | bsprintf(buf, template, ++(*counter)); |
8450be97 | 498 | s = cf_find_sym(buf, cf_hash(buf)); |
d272fe22 MM |
499 | if (!s) |
500 | break; | |
501 | if (s->class == SYM_VOID) | |
502 | return s; | |
503 | if (!perc) | |
504 | break; | |
8450be97 | 505 | } |
d272fe22 | 506 | cf_error("Unable to generate default name"); |
8450be97 MM |
507 | } |
508 | ||
06607335 MM |
509 | /** |
510 | * cf_define_symbol - define meaning of a symbol | |
511 | * @sym: symbol to be defined | |
512 | * @type: symbol class to assign | |
513 | * @def: class dependent data | |
514 | * | |
04dc62a0 MM |
515 | * Defines new meaning of a symbol. If the symbol is an undefined |
516 | * one (%SYM_VOID), it's just re-defined to the new type. If it's defined | |
517 | * in different scope, a new symbol in current scope is created and the | |
518 | * meaning is assigned to it. If it's already defined in the current scope, | |
519 | * an error is reported via cf_error(). | |
520 | * | |
521 | * Result: Pointer to the newly defined symbol. If we are in the top-level | |
522 | * scope, it's the same @sym as passed to the function. | |
06607335 | 523 | */ |
04dc62a0 | 524 | struct symbol * |
4107df1d MM |
525 | cf_define_symbol(struct symbol *sym, int type, void *def) |
526 | { | |
527 | if (sym->class) | |
04dc62a0 MM |
528 | { |
529 | if (sym->scope == conf_this_scope) | |
530 | cf_error("Symbol already defined"); | |
531 | sym = cf_new_sym(sym->name, cf_hash(sym->name) & (SYM_HASH_SIZE-1)); | |
532 | } | |
4107df1d MM |
533 | sym->class = type; |
534 | sym->def = def; | |
04dc62a0 | 535 | return sym; |
4107df1d MM |
536 | } |
537 | ||
c9aae7f4 MM |
538 | static void |
539 | cf_lex_init_kh(void) | |
540 | { | |
541 | struct keyword *k; | |
542 | ||
543 | for(k=keyword_list; k->name; k++) | |
544 | { | |
545 | unsigned h = cf_hash(k->name) & (KW_HASH_SIZE-1); | |
546 | k->next = kw_hash[h]; | |
547 | kw_hash[h] = k; | |
548 | } | |
549 | kw_hash_inited = 1; | |
550 | } | |
551 | ||
06607335 MM |
552 | /** |
553 | * cf_lex_init - initialize the lexer | |
554 | * @is_cli: true if we're going to parse CLI command, false for configuration | |
555 | * | |
2e9b2421 | 556 | * cf_lex_init() initializes the lexical analyzer and prepares it for |
06607335 MM |
557 | * parsing of a new input. |
558 | */ | |
82fc7be7 | 559 | void |
48ec367a | 560 | cf_lex_init(int is_cli, struct config *c) |
82fc7be7 | 561 | { |
c9aae7f4 MM |
562 | if (!kw_hash_inited) |
563 | cf_lex_init_kh(); | |
4be266a9 OZ |
564 | |
565 | ifs_head = ifs = push_ifs(NULL); | |
566 | if (!is_cli) | |
567 | { | |
568 | ifs->file_name = c->file_name; | |
569 | ifs->fd = c->file_fd; | |
570 | ifs->depth = 1; | |
571 | } | |
572 | ||
bc2fb680 | 573 | yyrestart(NULL); |
4be266a9 OZ |
574 | ifs->buffer = YY_CURRENT_BUFFER; |
575 | ||
bc2fb680 MM |
576 | if (is_cli) |
577 | BEGIN(CLI); | |
578 | else | |
579 | BEGIN(INITIAL); | |
4be266a9 | 580 | |
c8f61a01 MM |
581 | conf_this_scope = cfg_allocz(sizeof(struct sym_scope)); |
582 | conf_this_scope->active = 1; | |
82fc7be7 MM |
583 | } |
584 | ||
06607335 MM |
585 | /** |
586 | * cf_push_scope - enter new scope | |
587 | * @sym: symbol representing scope name | |
588 | * | |
589 | * If we want to enter a new scope to process declarations inside | |
590 | * a nested block, we can just call cf_push_scope() to push a new | |
591 | * scope onto the scope stack which will cause all new symbols to be | |
592 | * defined in this scope and all existing symbols to be sought for | |
593 | * in all scopes stored on the stack. | |
594 | */ | |
c8f61a01 MM |
595 | void |
596 | cf_push_scope(struct symbol *sym) | |
597 | { | |
598 | struct sym_scope *s = cfg_alloc(sizeof(struct sym_scope)); | |
599 | ||
600 | s->next = conf_this_scope; | |
601 | conf_this_scope = s; | |
602 | s->active = 1; | |
603 | s->name = sym; | |
604 | } | |
605 | ||
06607335 MM |
606 | /** |
607 | * cf_pop_scope - leave a scope | |
608 | * | |
609 | * cf_pop_scope() pops the topmost scope from the scope stack, | |
610 | * leaving all its symbols in the symbol table, but making them | |
611 | * invisible to the rest of the config. | |
612 | */ | |
c8f61a01 MM |
613 | void |
614 | cf_pop_scope(void) | |
615 | { | |
616 | conf_this_scope->active = 0; | |
617 | conf_this_scope = conf_this_scope->next; | |
618 | ASSERT(conf_this_scope); | |
619 | } | |
4b87e256 MM |
620 | |
621 | struct symbol * | |
622 | cf_walk_symbols(struct config *cf, struct symbol *sym, int *pos) | |
623 | { | |
624 | for(;;) | |
625 | { | |
626 | if (!sym) | |
627 | { | |
628 | if (*pos >= SYM_HASH_SIZE) | |
629 | return NULL; | |
630 | sym = cf->sym_hash[(*pos)++]; | |
631 | } | |
632 | else | |
633 | sym = sym->next; | |
634 | if (sym && sym->scope->active) | |
635 | return sym; | |
636 | } | |
637 | } | |
638 | ||
06607335 MM |
639 | /** |
640 | * cf_symbol_class_name - get name of a symbol class | |
641 | * @sym: symbol | |
642 | * | |
643 | * This function returns a string representing the class | |
644 | * of the given symbol. | |
645 | */ | |
4b87e256 MM |
646 | char * |
647 | cf_symbol_class_name(struct symbol *sym) | |
648 | { | |
b2f00837 | 649 | if (cf_symbol_is_constant(sym)) |
1103b32e OZ |
650 | return "constant"; |
651 | ||
4b87e256 MM |
652 | switch (sym->class) |
653 | { | |
654 | case SYM_VOID: | |
655 | return "undefined"; | |
656 | case SYM_PROTO: | |
657 | return "protocol"; | |
1103b32e OZ |
658 | case SYM_TEMPLATE: |
659 | return "protocol template"; | |
4b87e256 MM |
660 | case SYM_FUNCTION: |
661 | return "function"; | |
662 | case SYM_FILTER: | |
663 | return "filter"; | |
664 | case SYM_TABLE: | |
665 | return "routing table"; | |
af582c48 OZ |
666 | case SYM_ROA: |
667 | return "ROA table"; | |
4b87e256 MM |
668 | default: |
669 | return "unknown type"; | |
670 | } | |
671 | } | |
58f94537 MM |
672 | |
673 | ||
674 | /** | |
675 | * DOC: Parser | |
676 | * | |
2e9b2421 | 677 | * Both the configuration and CLI commands are analyzed using a syntax |
58f94537 MM |
678 | * driven parser generated by the |bison| tool from a grammar which |
679 | * is constructed from information gathered from grammar snippets by | |
680 | * the |gen_parser.m4| script. | |
681 | * | |
682 | * Grammar snippets are files (usually with extension |.Y|) contributed | |
58f7d004 | 683 | * by various BIRD modules in order to provide information about syntax of their |
58f94537 | 684 | * configuration and their CLI commands. Each snipped consists of several |
725270cb | 685 | * sections, each of them starting with a special keyword: |CF_HDR| for |
58f94537 MM |
686 | * a list of |#include| directives needed by the C code, |CF_DEFINES| |
687 | * for a list of C declarations, |CF_DECLS| for |bison| declarations | |
688 | * including keyword definitions specified as |CF_KEYWORDS|, |CF_GRAMMAR| | |
2e9b2421 | 689 | * for the grammar rules, |CF_CODE| for auxiliary C code and finally |
58f94537 MM |
690 | * |CF_END| at the end of the snippet. |
691 | * | |
692 | * To create references between the snippets, it's possible to define | |
693 | * multi-part rules by utilizing the |CF_ADDTO| macro which adds a new | |
694 | * alternative to a multi-part rule. | |
695 | * | |
696 | * CLI commands are defined using a |CF_CLI| macro. Its parameters are: | |
2e9b2421 | 697 | * the list of keywords determining the command, the list of parameters, |
58f94537 MM |
698 | * help text for the parameters and help text for the command. |
699 | * | |
700 | * Values of |enum| filter types can be defined using |CF_ENUM| with | |
701 | * the following parameters: name of filter type, prefix common for all | |
725270cb | 702 | * literals of this type and names of all the possible values. |
58f94537 | 703 | */ |