]>
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> |
61e67253 | 33 | #include <stdint.h> |
60fd666b | 34 | #include <unistd.h> |
4be266a9 OZ |
35 | #include <libgen.h> |
36 | #include <glob.h> | |
37 | #include <fcntl.h> | |
38 | #include <sys/stat.h> | |
39 | #include <sys/types.h> | |
40 | #include <sys/stat.h> | |
82fc7be7 | 41 | |
93e868c7 OZ |
42 | #define PARSER 1 |
43 | ||
82fc7be7 | 44 | #include "nest/bird.h" |
944f008a | 45 | #include "nest/route.h" |
e304fd4b | 46 | #include "nest/protocol.h" |
944f008a | 47 | #include "filter/filter.h" |
8bdb05ed | 48 | #include "filter/f-inst.h" |
82fc7be7 MM |
49 | #include "conf/conf.h" |
50 | #include "conf/cf-parse.tab.h" | |
d272fe22 | 51 | #include "lib/string.h" |
b7761af3 | 52 | #include "lib/hash.h" |
82fc7be7 | 53 | |
a412f01e | 54 | struct keyword { |
82fc7be7 MM |
55 | byte *name; |
56 | int value; | |
57 | struct keyword *next; | |
a412f01e MM |
58 | }; |
59 | ||
49e7e5ee | 60 | #include "conf/keywords.h" |
82fc7be7 | 61 | |
4fec4306 OZ |
62 | /* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */ |
63 | #ifdef SYM | |
64 | #undef SYM | |
65 | #endif | |
66 | ||
c9aae7f4 | 67 | |
99911873 | 68 | static uint cf_hash(const byte *c); |
82fc7be7 | 69 | |
b7761af3 OZ |
70 | #define KW_KEY(n) n->name |
71 | #define KW_NEXT(n) n->next | |
72 | #define KW_EQ(a,b) !strcmp(a,b) | |
73 | #define KW_FN(k) cf_hash(k) | |
74 | #define KW_ORDER 8 /* Fixed */ | |
75 | ||
76 | #define SYM_KEY(n) n->name, n->scope->active | |
77 | #define SYM_NEXT(n) n->next | |
78 | #define SYM_EQ(a,s1,b,s2) !strcmp(a,b) && s1 == s2 | |
79 | #define SYM_FN(k,s) cf_hash(k) | |
80 | #define SYM_ORDER 6 /* Initial */ | |
81 | ||
82 | #define SYM_REHASH sym_rehash | |
83 | #define SYM_PARAMS /8, *1, 2, 2, 6, 20 | |
84 | ||
85 | ||
86 | HASH_DEFINE_REHASH_FN(SYM, struct symbol) | |
87 | ||
88 | HASH(struct keyword) kw_hash; | |
c8f61a01 | 89 | |
b7761af3 OZ |
90 | |
91 | static struct sym_scope *conf_this_scope; | |
82fc7be7 | 92 | |
b35d72ac | 93 | linpool *cfg_mem; |
82fc7be7 | 94 | |
48ec367a | 95 | int (*cf_read_hook)(byte *buf, unsigned int max, int fd); |
9b7fdfc8 | 96 | struct include_file_stack *ifs; |
4be266a9 OZ |
97 | static struct include_file_stack *ifs_head; |
98 | ||
99911873 MM |
99 | #define QUOTED_BUFFER_SIZE 4096 |
100 | static BUFFER_(char) quoted_buffer; | |
101 | static char quoted_buffer_data[QUOTED_BUFFER_SIZE]; | |
102 | static inline void quoted_buffer_init(void) { | |
103 | quoted_buffer.used = 0; | |
104 | quoted_buffer.size = QUOTED_BUFFER_SIZE; | |
105 | quoted_buffer.data = quoted_buffer_data; | |
106 | } | |
107 | ||
4be266a9 | 108 | #define MAX_INCLUDE_DEPTH 8 |
82fc7be7 | 109 | |
4be266a9 | 110 | #define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd); |
82fc7be7 MM |
111 | #define YY_NO_UNPUT |
112 | #define YY_FATAL_ERROR(msg) cf_error(msg) | |
d50b0bc4 | 113 | #define YY_USER_ACTION ifs->chno += yyleng; ifs->toklen = yyleng; |
82fc7be7 | 114 | |
4be266a9 | 115 | static void cf_include(char *arg, int alen); |
48ec367a | 116 | static int check_eof(void); |
48ec367a | 117 | |
99911873 MM |
118 | static enum yytokentype cf_lex_symbol(const char *data); |
119 | ||
82fc7be7 MM |
120 | %} |
121 | ||
122 | %option noyywrap | |
506fa1a7 OZ |
123 | %option noinput |
124 | %option nounput | |
125 | %option noreject | |
82fc7be7 | 126 | |
99911873 | 127 | %x COMMENT CCOMM CLI QUOTED APOSTROPHED INCLUDE |
82fc7be7 MM |
128 | |
129 | ALPHA [a-zA-Z_] | |
130 | DIGIT [0-9] | |
131 | XIGIT [0-9a-fA-F] | |
132 | ALNUM [a-zA-Z_0-9] | |
133 | WHITE [ \t] | |
134 | ||
135 | %% | |
99911873 | 136 | ^{WHITE}*include{WHITE}*\" { |
4be266a9 OZ |
137 | if (!ifs->depth) |
138 | cf_error("Include not allowed in CLI"); | |
139 | ||
99911873 MM |
140 | BEGIN(INCLUDE); |
141 | } | |
142 | ||
143 | <INCLUDE>[^"\n]+["]{WHITE}*; { | |
144 | char *start, *end; | |
145 | ||
146 | start = yytext; | |
4be266a9 OZ |
147 | |
148 | end = strchr(start, '"'); | |
149 | *end = 0; | |
150 | ||
151 | if (start == end) | |
152 | cf_error("Include with empty argument"); | |
153 | ||
154 | cf_include(start, end-start); | |
99911873 MM |
155 | |
156 | BEGIN(INITIAL); | |
4be266a9 | 157 | } |
82fc7be7 | 158 | |
99911873 MM |
159 | <INCLUDE>["] cf_error("Include with empty argument"); |
160 | <INCLUDE>. cf_error("Unterminated include"); | |
161 | <INCLUDE>\n cf_error("Unterminated include"); | |
162 | <INCLUDE><<EOF>> cf_error("Unterminated include"); | |
163 | ||
164 | ||
2282030b | 165 | {DIGIT}+:{DIGIT}+ { |
e521150b OZ |
166 | uint len1 UNUSED, len2; |
167 | u64 l; | |
2282030b JMM |
168 | char *e; |
169 | ||
170 | errno = 0; | |
5c864e2c | 171 | l = bstrtoul10(yytext, &e); |
2282030b JMM |
172 | if (e && (*e != ':') || (errno == ERANGE) || (l >> 32)) |
173 | cf_error("ASN out of range"); | |
174 | ||
175 | if (l >> 16) | |
176 | { | |
177 | len1 = 32; | |
178 | len2 = 16; | |
179 | cf_lval.i64 = (2ULL << 48) | (((u64) l) << len2); | |
180 | } | |
181 | else | |
182 | { | |
183 | len1 = 16; | |
184 | len2 = 32; | |
185 | cf_lval.i64 = 0 | (((u64) l) << len2); | |
186 | } | |
187 | ||
188 | errno = 0; | |
5c864e2c | 189 | l = bstrtoul10(e+1, &e); |
2282030b JMM |
190 | if (e && *e || (errno == ERANGE) || (l >> len2)) |
191 | cf_error("Number out of range"); | |
192 | cf_lval.i64 |= l; | |
193 | ||
194 | return VPN_RD; | |
195 | } | |
196 | ||
d311368b | 197 | [02]:{DIGIT}+:{DIGIT}+ { |
e521150b OZ |
198 | uint len1, len2; |
199 | u64 l; | |
d311368b | 200 | char *e; |
d311368b JMM |
201 | |
202 | if (yytext[0] == '0') | |
62e64905 | 203 | { |
d311368b | 204 | cf_lval.i64 = 0; |
62e64905 OZ |
205 | len1 = 16; |
206 | len2 = 32; | |
207 | } | |
d311368b | 208 | else |
62e64905 OZ |
209 | { |
210 | cf_lval.i64 = 2ULL << 48; | |
211 | len1 = 32; | |
212 | len2 = 16; | |
213 | } | |
d311368b JMM |
214 | |
215 | errno = 0; | |
5c864e2c | 216 | l = bstrtoul10(yytext+2, &e); |
62e64905 | 217 | if (e && (*e != ':') || (errno == ERANGE) || (l >> len1)) |
d311368b | 218 | cf_error("ASN out of range"); |
62e64905 OZ |
219 | cf_lval.i64 |= ((u64) l) << len2; |
220 | ||
d311368b | 221 | errno = 0; |
5c864e2c | 222 | l = bstrtoul10(e+1, &e); |
62e64905 OZ |
223 | if (e && *e || (errno == ERANGE) || (l >> len2)) |
224 | cf_error("Number out of range"); | |
d311368b | 225 | cf_lval.i64 |= l; |
62e64905 | 226 | |
d311368b JMM |
227 | return VPN_RD; |
228 | } | |
229 | ||
2282030b | 230 | {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ { |
d311368b | 231 | unsigned long int l; |
d311368b | 232 | ip4_addr ip4; |
62e64905 OZ |
233 | char *e; |
234 | ||
235 | cf_lval.i64 = 1ULL << 48; | |
236 | ||
2282030b | 237 | e = strchr(yytext, ':'); |
62e64905 | 238 | *e++ = '\0'; |
2282030b JMM |
239 | if (!ip4_pton(yytext, &ip4)) |
240 | cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext); | |
62e64905 OZ |
241 | cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16; |
242 | ||
d311368b | 243 | errno = 0; |
5c864e2c | 244 | l = bstrtoul10(e, &e); |
62e64905 OZ |
245 | if (e && *e || (errno == ERANGE) || (l >> 16)) |
246 | cf_error("Number out of range"); | |
247 | cf_lval.i64 |= l; | |
248 | ||
d311368b JMM |
249 | return VPN_RD; |
250 | } | |
251 | ||
82fc7be7 | 252 | {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ { |
fe9f1a6d | 253 | if (!ip4_pton(yytext, &cf_lval.ip4)) |
88a183c6 | 254 | cf_error("Invalid IPv4 address %s", yytext); |
fe9f1a6d | 255 | return IP4; |
dce26783 MM |
256 | } |
257 | ||
258 | ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) { | |
fe9f1a6d OZ |
259 | if (!ip6_pton(yytext, &cf_lval.ip6)) |
260 | cf_error("Invalid IPv6 address %s", yytext); | |
261 | return IP6; | |
82fc7be7 MM |
262 | } |
263 | ||
506fa1a7 | 264 | 0x{XIGIT}+ { |
82fc7be7 | 265 | char *e; |
c32c3f88 | 266 | unsigned long int l; |
82fc7be7 | 267 | errno = 0; |
5c864e2c | 268 | l = bstrtoul16(yytext+2, &e); |
c32c3f88 | 269 | if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l) |
82fc7be7 MM |
270 | cf_error("Number out of range"); |
271 | cf_lval.i = l; | |
272 | return NUM; | |
273 | } | |
274 | ||
275 | {DIGIT}+ { | |
276 | char *e; | |
c32c3f88 | 277 | unsigned long int l; |
82fc7be7 | 278 | errno = 0; |
5c864e2c | 279 | l = bstrtoul10(yytext, &e); |
c32c3f88 | 280 | if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l) |
82fc7be7 MM |
281 | cf_error("Number out of range"); |
282 | cf_lval.i = l; | |
283 | return NUM; | |
284 | } | |
285 | ||
26d92bb8 OZ |
286 | else: { |
287 | /* Hack to distinguish if..else from else: in case */ | |
288 | return ELSECOL; | |
289 | } | |
290 | ||
99911873 MM |
291 | ['] { |
292 | BEGIN(APOSTROPHED); | |
293 | quoted_buffer_init(); | |
294 | } | |
b7761af3 | 295 | |
99911873 MM |
296 | <APOSTROPHED>{ALNUM}|[-]|[.:] BUFFER_PUSH(quoted_buffer) = yytext[0]; |
297 | <APOSTROPHED>\n cf_error("Unterminated symbol"); | |
298 | <APOSTROPHED><<EOF>> cf_error("Unterminated symbol"); | |
299 | <APOSTROPHED>['] { | |
300 | BEGIN(INITIAL); | |
301 | BUFFER_PUSH(quoted_buffer) = 0; | |
302 | return cf_lex_symbol(quoted_buffer_data); | |
303 | } | |
304 | <APOSTROPHED>. cf_error("Invalid character in apostrophed symbol"); | |
b7761af3 | 305 | |
99911873 MM |
306 | ({ALPHA}{ALNUM}*) { |
307 | return cf_lex_symbol(yytext); | |
82fc7be7 MM |
308 | } |
309 | ||
efe51e38 | 310 | <CLI>(.|\n) { |
bc2fb680 MM |
311 | BEGIN(INITIAL); |
312 | return CLI_MARKER; | |
313 | } | |
314 | ||
b8cc390e OZ |
315 | \.\. { |
316 | return DDOT; | |
317 | } | |
318 | ||
f9491630 | 319 | [={}:;,.()+*/%<>~\[\]?!\|-] { |
82fc7be7 MM |
320 | return yytext[0]; |
321 | } | |
322 | ||
99911873 MM |
323 | ["] { |
324 | BEGIN(QUOTED); | |
325 | quoted_buffer_init(); | |
326 | } | |
327 | ||
328 | <QUOTED>\n cf_error("Unterminated string"); | |
329 | <QUOTED><<EOF>> cf_error("Unterminated string"); | |
330 | <QUOTED>["] { | |
331 | BEGIN(INITIAL); | |
332 | BUFFER_PUSH(quoted_buffer) = 0; | |
333 | cf_lval.t = cfg_strdup(quoted_buffer_data); | |
82fc7be7 MM |
334 | return TEXT; |
335 | } | |
336 | ||
99911873 | 337 | <QUOTED>. BUFFER_PUSH(quoted_buffer) = yytext[0]; |
82fc7be7 | 338 | |
4be266a9 | 339 | <INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; } |
82fc7be7 MM |
340 | |
341 | {WHITE}+ | |
342 | ||
d50b0bc4 | 343 | \n ifs->lino++; ifs->chno = 0; |
82fc7be7 | 344 | |
72614174 | 345 | # BEGIN(COMMENT); |
82fc7be7 | 346 | |
72614174 | 347 | \/\* BEGIN(CCOMM); |
82fc7be7 MM |
348 | |
349 | . cf_error("Unknown character"); | |
350 | ||
351 | <COMMENT>\n { | |
4be266a9 | 352 | ifs->lino++; |
d50b0bc4 | 353 | ifs->chno = 0; |
82fc7be7 MM |
354 | BEGIN(INITIAL); |
355 | } | |
356 | ||
357 | <COMMENT>. | |
358 | ||
359 | <CCOMM>\*\/ BEGIN(INITIAL); | |
d50b0bc4 | 360 | <CCOMM>\n ifs->lino++; ifs->chno = 0; |
82fc7be7 MM |
361 | <CCOMM>\/\* cf_error("Comment nesting not supported"); |
362 | <CCOMM><<EOF>> cf_error("Unterminated comment"); | |
363 | <CCOMM>. | |
364 | ||
c8d5ffaf | 365 | \!\= return NEQ; |
768d5e10 | 366 | \!\~ return NMA; |
c8d5ffaf PM |
367 | \<\= return LEQ; |
368 | \>\= return GEQ; | |
5f4aee76 PM |
369 | \&\& return AND; |
370 | \|\| return OR; | |
c8d5ffaf | 371 | |
cf186034 OZ |
372 | \[\= return PO; |
373 | \=\] return PC; | |
374 | ||
82fc7be7 MM |
375 | %% |
376 | ||
b7761af3 | 377 | static uint |
99911873 | 378 | cf_hash(const byte *c) |
82fc7be7 | 379 | { |
b7761af3 | 380 | uint h = 13 << 24; |
82fc7be7 MM |
381 | |
382 | while (*c) | |
b7761af3 | 383 | h = h + (h >> 2) + (h >> 5) + ((uint) *c++ << 24); |
82fc7be7 MM |
384 | return h; |
385 | } | |
386 | ||
4be266a9 OZ |
387 | /* |
388 | * IFS stack - it contains structures needed for recursive processing | |
389 | * of include in config files. On the top of the stack is a structure | |
390 | * for currently processed file. Other structures are either for | |
391 | * active files interrupted because of include directive (these have | |
392 | * fd and flex buffer) or for inactive files scheduled to be processed | |
393 | * later (when parent requested including of several files by wildcard | |
394 | * match - these do not have fd and flex buffer yet). | |
395 | * | |
396 | * FIXME: Most of these ifs and include functions are really sysdep/unix. | |
4be266a9 OZ |
397 | */ |
398 | ||
399 | static struct include_file_stack * | |
400 | push_ifs(struct include_file_stack *old) | |
401 | { | |
402 | struct include_file_stack *ret; | |
403 | ret = cfg_allocz(sizeof(struct include_file_stack)); | |
404 | ret->lino = 1; | |
405 | ret->prev = old; | |
406 | return ret; | |
407 | } | |
408 | ||
409 | static struct include_file_stack * | |
410 | pop_ifs(struct include_file_stack *old) | |
411 | { | |
412 | yy_delete_buffer(old->buffer); | |
413 | close(old->fd); | |
414 | return old->prev; | |
415 | } | |
416 | ||
48ec367a | 417 | static void |
4be266a9 | 418 | enter_ifs(struct include_file_stack *new) |
48ec367a | 419 | { |
4be266a9 OZ |
420 | if (!new->buffer) |
421 | { | |
422 | new->fd = open(new->file_name, O_RDONLY); | |
423 | if (new->fd < 0) | |
424 | { | |
425 | ifs = ifs->up; | |
426 | cf_error("Unable to open included file %s: %m", new->file_name); | |
427 | } | |
428 | ||
429 | new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE); | |
430 | } | |
48ec367a | 431 | |
4be266a9 OZ |
432 | yy_switch_to_buffer(new->buffer); |
433 | } | |
48ec367a | 434 | |
0c3d9dac OZ |
435 | /** |
436 | * cf_lex_unwind - unwind lexer state during error | |
437 | * | |
438 | * cf_lex_unwind() frees the internal state on IFS stack when the lexical | |
439 | * analyzer is terminated by cf_error(). | |
440 | */ | |
441 | void | |
442 | cf_lex_unwind(void) | |
443 | { | |
444 | struct include_file_stack *n; | |
445 | ||
446 | for (n = ifs; n != ifs_head; n = n->prev) | |
447 | { | |
448 | /* Memory is freed automatically */ | |
449 | if (n->buffer) | |
450 | yy_delete_buffer(n->buffer); | |
451 | if (n->fd) | |
452 | close(n->fd); | |
453 | } | |
454 | ||
455 | ifs = ifs_head; | |
456 | } | |
457 | ||
4be266a9 OZ |
458 | static void |
459 | cf_include(char *arg, int alen) | |
460 | { | |
461 | struct include_file_stack *base_ifs = ifs; | |
462 | int new_depth, rv, i; | |
463 | char *patt; | |
0c3d9dac | 464 | glob_t g = {}; |
4be266a9 OZ |
465 | |
466 | new_depth = ifs->depth + 1; | |
467 | if (new_depth > MAX_INCLUDE_DEPTH) | |
468 | cf_error("Max include depth reached"); | |
48ec367a | 469 | |
4be266a9 OZ |
470 | /* expand arg to properly handle relative filenames */ |
471 | if (*arg != '/') | |
472 | { | |
473 | int dlen = strlen(ifs->file_name); | |
474 | char *dir = alloca(dlen + 1); | |
475 | patt = alloca(dlen + alen + 2); | |
476 | memcpy(dir, ifs->file_name, dlen + 1); | |
477 | sprintf(patt, "%s/%s", dirname(dir), arg); | |
478 | } | |
479 | else | |
480 | patt = arg; | |
48ec367a | 481 | |
4be266a9 OZ |
482 | /* Skip globbing if there are no wildcards, mainly to get proper |
483 | response when the included config file is missing */ | |
484 | if (!strpbrk(arg, "?*[")) | |
485 | { | |
486 | ifs = push_ifs(ifs); | |
487 | ifs->file_name = cfg_strdup(patt); | |
488 | ifs->depth = new_depth; | |
489 | ifs->up = base_ifs; | |
490 | enter_ifs(ifs); | |
491 | return; | |
492 | } | |
48ec367a | 493 | |
4be266a9 OZ |
494 | /* Expand the pattern */ |
495 | rv = glob(patt, GLOB_ERR | GLOB_NOESCAPE, NULL, &g); | |
496 | if (rv == GLOB_ABORTED) | |
497 | cf_error("Unable to match pattern %s: %m", patt); | |
498 | if ((rv != 0) || (g.gl_pathc <= 0)) | |
499 | return; | |
500 | ||
501 | /* | |
502 | * Now we put all found files to ifs stack in reverse order, they | |
503 | * will be activated and processed in order as ifs stack is popped | |
504 | * by pop_ifs() and enter_ifs() in check_eof(). | |
505 | */ | |
506 | for(i = g.gl_pathc - 1; i >= 0; i--) | |
507 | { | |
508 | char *fname = g.gl_pathv[i]; | |
509 | struct stat fs; | |
510 | ||
511 | if (stat(fname, &fs) < 0) | |
0c3d9dac OZ |
512 | { |
513 | globfree(&g); | |
514 | cf_error("Unable to stat included file %s: %m", fname); | |
515 | } | |
4be266a9 OZ |
516 | |
517 | if (fs.st_mode & S_IFDIR) | |
518 | continue; | |
519 | ||
520 | /* Prepare new stack item */ | |
521 | ifs = push_ifs(ifs); | |
522 | ifs->file_name = cfg_strdup(fname); | |
523 | ifs->depth = new_depth; | |
524 | ifs->up = base_ifs; | |
525 | } | |
526 | ||
527 | globfree(&g); | |
528 | enter_ifs(ifs); | |
48ec367a OF |
529 | } |
530 | ||
531 | static int | |
532 | check_eof(void) | |
533 | { | |
4be266a9 OZ |
534 | if (ifs == ifs_head) |
535 | { | |
536 | /* EOF in main config file */ | |
537 | ifs->lino = 1; /* Why this? */ | |
538 | return 1; | |
539 | } | |
48ec367a | 540 | |
4be266a9 OZ |
541 | ifs = pop_ifs(ifs); |
542 | enter_ifs(ifs); | |
543 | return 0; | |
48ec367a OF |
544 | } |
545 | ||
04dc62a0 | 546 | static struct symbol * |
99911873 | 547 | cf_new_symbol(const byte *c) |
04dc62a0 | 548 | { |
b7761af3 OZ |
549 | struct symbol *s; |
550 | ||
551 | uint l = strlen(c); | |
04dc62a0 MM |
552 | if (l > SYM_MAX_LEN) |
553 | cf_error("Symbol too long"); | |
b7761af3 | 554 | |
0b39b1cb MM |
555 | s = cfg_allocz(sizeof(struct symbol) + l + 1); |
556 | *s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, }; | |
04dc62a0 | 557 | strcpy(s->name, c); |
82fc7be7 | 558 | |
b7761af3 OZ |
559 | if (!new_config->sym_hash.data) |
560 | HASH_INIT(new_config->sym_hash, new_config->pool, SYM_ORDER); | |
9b9a7143 | 561 | |
b7761af3 | 562 | HASH_INSERT2(new_config->sym_hash, SYM, new_config->pool, s); |
9b9a7143 | 563 | |
f249d0b8 MM |
564 | add_tail(&(new_config->symbols), &(s->n)); |
565 | ||
b7761af3 | 566 | return s; |
82fc7be7 MM |
567 | } |
568 | ||
06607335 MM |
569 | /** |
570 | * cf_find_symbol - find a symbol by name | |
9b9a7143 OZ |
571 | * @cfg: specificed config |
572 | * @c: symbol name | |
573 | * | |
574 | * This functions searches the symbol table in the config @cfg for a symbol of | |
575 | * given name. First it examines the current scope, then the second recent one | |
576 | * and so on until it either finds the symbol and returns a pointer to its | |
577 | * &symbol structure or reaches the end of the scope chain and returns %NULL to | |
578 | * signify no match. | |
579 | */ | |
580 | struct symbol * | |
f249d0b8 | 581 | cf_find_symbol(const struct config *cfg, const byte *c) |
9b9a7143 | 582 | { |
b7761af3 OZ |
583 | struct symbol *s; |
584 | ||
585 | if (cfg->sym_hash.data && | |
586 | (s = HASH_FIND(cfg->sym_hash, SYM, c, 1))) | |
587 | return s; | |
588 | ||
0b39b1cb | 589 | /* In CLI command parsing, fallback points to the current config, otherwise it is NULL. */ |
b7761af3 OZ |
590 | if (cfg->fallback && |
591 | cfg->fallback->sym_hash.data && | |
592 | (s = HASH_FIND(cfg->fallback->sym_hash, SYM, c, 1))) | |
593 | return s; | |
594 | ||
595 | return NULL; | |
9b9a7143 OZ |
596 | } |
597 | ||
598 | /** | |
599 | * cf_get_symbol - get a symbol by name | |
06607335 MM |
600 | * @c: symbol name |
601 | * | |
9b9a7143 OZ |
602 | * This functions searches the symbol table of the currently parsed config |
603 | * (@new_config) for a symbol of given name. It returns either the already | |
604 | * existing symbol or a newly allocated undefined (%SYM_VOID) symbol if no | |
605 | * existing symbol is found. | |
06607335 | 606 | */ |
4107df1d | 607 | struct symbol * |
99911873 | 608 | cf_get_symbol(const byte *c) |
4107df1d | 609 | { |
b7761af3 | 610 | return cf_find_symbol(new_config, c) ?: cf_new_symbol(c); |
4107df1d MM |
611 | } |
612 | ||
0b39b1cb MM |
613 | /** |
614 | * cf_localize_symbol - get the local instance of given symbol | |
615 | * @sym: the symbol to localize | |
616 | * | |
617 | * This functions finds the symbol that is local to current scope | |
618 | * for purposes of cf_define_symbol(). | |
619 | */ | |
620 | struct symbol * | |
621 | cf_localize_symbol(struct symbol *sym) | |
622 | { | |
623 | /* If the symbol type is void, it has been recently allocated just in this scope. */ | |
624 | if (!sym->class) | |
625 | return sym; | |
626 | ||
627 | /* If the scope is the current, it is already defined in this scope. */ | |
628 | if (sym->scope == conf_this_scope) | |
629 | cf_error("Symbol already defined"); | |
630 | ||
631 | /* Not allocated here yet, doing it now. */ | |
632 | return cf_new_symbol(sym->name); | |
633 | } | |
634 | ||
8450be97 | 635 | struct symbol * |
d272fe22 | 636 | cf_default_name(char *template, int *counter) |
8450be97 | 637 | { |
9b9a7143 | 638 | char buf[SYM_MAX_LEN]; |
8450be97 | 639 | struct symbol *s; |
d272fe22 | 640 | char *perc = strchr(template, '%'); |
8450be97 | 641 | |
d272fe22 | 642 | for(;;) |
8450be97 | 643 | { |
d272fe22 | 644 | bsprintf(buf, template, ++(*counter)); |
b7761af3 | 645 | s = cf_get_symbol(buf); |
d272fe22 MM |
646 | if (s->class == SYM_VOID) |
647 | return s; | |
648 | if (!perc) | |
649 | break; | |
8450be97 | 650 | } |
d272fe22 | 651 | cf_error("Unable to generate default name"); |
8450be97 MM |
652 | } |
653 | ||
99911873 MM |
654 | static enum yytokentype |
655 | cf_lex_symbol(const char *data) | |
656 | { | |
9eef9c64 MM |
657 | /* Have we defined such a symbol? */ |
658 | struct symbol *sym = cf_get_symbol(data); | |
659 | cf_lval.s = sym; | |
660 | ||
661 | if (sym->class != SYM_VOID) | |
662 | return CF_SYM_KNOWN; | |
663 | ||
664 | /* Is it a keyword? */ | |
99911873 MM |
665 | struct keyword *k = HASH_FIND(kw_hash, KW, data); |
666 | if (k) | |
667 | { | |
668 | if (k->value > 0) | |
669 | return k->value; | |
670 | else | |
671 | { | |
672 | cf_lval.i = -k->value; | |
673 | return ENUM; | |
674 | } | |
675 | } | |
676 | ||
9eef9c64 MM |
677 | /* OK, undefined symbol */ |
678 | cf_lval.s = sym; | |
679 | return CF_SYM_UNDEFINED; | |
99911873 MM |
680 | } |
681 | ||
c9aae7f4 MM |
682 | static void |
683 | cf_lex_init_kh(void) | |
684 | { | |
b7761af3 | 685 | HASH_INIT(kw_hash, &root_pool, KW_ORDER); |
c9aae7f4 | 686 | |
b7761af3 OZ |
687 | struct keyword *k; |
688 | for (k=keyword_list; k->name; k++) | |
689 | HASH_INSERT(kw_hash, KW, k); | |
c9aae7f4 MM |
690 | } |
691 | ||
06607335 MM |
692 | /** |
693 | * cf_lex_init - initialize the lexer | |
694 | * @is_cli: true if we're going to parse CLI command, false for configuration | |
8e433d6a | 695 | * @c: configuration structure |
06607335 | 696 | * |
2e9b2421 | 697 | * cf_lex_init() initializes the lexical analyzer and prepares it for |
06607335 MM |
698 | * parsing of a new input. |
699 | */ | |
82fc7be7 | 700 | void |
48ec367a | 701 | cf_lex_init(int is_cli, struct config *c) |
82fc7be7 | 702 | { |
b7761af3 | 703 | if (!kw_hash.data) |
c9aae7f4 | 704 | cf_lex_init_kh(); |
4be266a9 OZ |
705 | |
706 | ifs_head = ifs = push_ifs(NULL); | |
c8cafc8e | 707 | if (!is_cli) |
4be266a9 OZ |
708 | { |
709 | ifs->file_name = c->file_name; | |
710 | ifs->fd = c->file_fd; | |
711 | ifs->depth = 1; | |
712 | } | |
713 | ||
bc2fb680 | 714 | yyrestart(NULL); |
4be266a9 OZ |
715 | ifs->buffer = YY_CURRENT_BUFFER; |
716 | ||
bc2fb680 MM |
717 | if (is_cli) |
718 | BEGIN(CLI); | |
719 | else | |
720 | BEGIN(INITIAL); | |
4be266a9 | 721 | |
c8f61a01 MM |
722 | conf_this_scope = cfg_allocz(sizeof(struct sym_scope)); |
723 | conf_this_scope->active = 1; | |
82fc7be7 MM |
724 | } |
725 | ||
06607335 MM |
726 | /** |
727 | * cf_push_scope - enter new scope | |
728 | * @sym: symbol representing scope name | |
729 | * | |
730 | * If we want to enter a new scope to process declarations inside | |
731 | * a nested block, we can just call cf_push_scope() to push a new | |
732 | * scope onto the scope stack which will cause all new symbols to be | |
733 | * defined in this scope and all existing symbols to be sought for | |
734 | * in all scopes stored on the stack. | |
735 | */ | |
c8f61a01 MM |
736 | void |
737 | cf_push_scope(struct symbol *sym) | |
738 | { | |
739 | struct sym_scope *s = cfg_alloc(sizeof(struct sym_scope)); | |
740 | ||
741 | s->next = conf_this_scope; | |
742 | conf_this_scope = s; | |
743 | s->active = 1; | |
744 | s->name = sym; | |
63e76204 | 745 | s->slots = 0; |
c8f61a01 MM |
746 | } |
747 | ||
06607335 MM |
748 | /** |
749 | * cf_pop_scope - leave a scope | |
750 | * | |
751 | * cf_pop_scope() pops the topmost scope from the scope stack, | |
752 | * leaving all its symbols in the symbol table, but making them | |
753 | * invisible to the rest of the config. | |
754 | */ | |
c8f61a01 MM |
755 | void |
756 | cf_pop_scope(void) | |
757 | { | |
758 | conf_this_scope->active = 0; | |
759 | conf_this_scope = conf_this_scope->next; | |
760 | ASSERT(conf_this_scope); | |
761 | } | |
4b87e256 | 762 | |
06607335 MM |
763 | /** |
764 | * cf_symbol_class_name - get name of a symbol class | |
765 | * @sym: symbol | |
766 | * | |
767 | * This function returns a string representing the class | |
768 | * of the given symbol. | |
769 | */ | |
4b87e256 MM |
770 | char * |
771 | cf_symbol_class_name(struct symbol *sym) | |
772 | { | |
773 | switch (sym->class) | |
774 | { | |
775 | case SYM_VOID: | |
776 | return "undefined"; | |
777 | case SYM_PROTO: | |
778 | return "protocol"; | |
1103b32e OZ |
779 | case SYM_TEMPLATE: |
780 | return "protocol template"; | |
4b87e256 MM |
781 | case SYM_FUNCTION: |
782 | return "function"; | |
783 | case SYM_FILTER: | |
784 | return "filter"; | |
785 | case SYM_TABLE: | |
786 | return "routing table"; | |
c0e958e0 MM |
787 | case SYM_ATTRIBUTE: |
788 | return "custom attribute"; | |
789 | case SYM_CONSTANT_RANGE: | |
790 | return "constant"; | |
791 | case SYM_VARIABLE_RANGE: | |
792 | return "variable"; | |
4b87e256 MM |
793 | default: |
794 | return "unknown type"; | |
795 | } | |
796 | } | |
58f94537 MM |
797 | |
798 | ||
799 | /** | |
800 | * DOC: Parser | |
801 | * | |
2e9b2421 | 802 | * Both the configuration and CLI commands are analyzed using a syntax |
58f94537 MM |
803 | * driven parser generated by the |bison| tool from a grammar which |
804 | * is constructed from information gathered from grammar snippets by | |
805 | * the |gen_parser.m4| script. | |
806 | * | |
807 | * Grammar snippets are files (usually with extension |.Y|) contributed | |
58f7d004 | 808 | * by various BIRD modules in order to provide information about syntax of their |
58f94537 | 809 | * configuration and their CLI commands. Each snipped consists of several |
725270cb | 810 | * sections, each of them starting with a special keyword: |CF_HDR| for |
58f94537 MM |
811 | * a list of |#include| directives needed by the C code, |CF_DEFINES| |
812 | * for a list of C declarations, |CF_DECLS| for |bison| declarations | |
813 | * including keyword definitions specified as |CF_KEYWORDS|, |CF_GRAMMAR| | |
2e9b2421 | 814 | * for the grammar rules, |CF_CODE| for auxiliary C code and finally |
58f94537 MM |
815 | * |CF_END| at the end of the snippet. |
816 | * | |
817 | * To create references between the snippets, it's possible to define | |
818 | * multi-part rules by utilizing the |CF_ADDTO| macro which adds a new | |
819 | * alternative to a multi-part rule. | |
820 | * | |
821 | * CLI commands are defined using a |CF_CLI| macro. Its parameters are: | |
2e9b2421 | 822 | * the list of keywords determining the command, the list of parameters, |
58f94537 MM |
823 | * help text for the parameters and help text for the command. |
824 | * | |
825 | * Values of |enum| filter types can be defined using |CF_ENUM| with | |
826 | * the following parameters: name of filter type, prefix common for all | |
725270cb | 827 | * literals of this type and names of all the possible values. |
58f94537 | 828 | */ |