]> git.ipfire.org Git - thirdparty/bird.git/blame - conf/cf-lex.l
Conf/Filters: Moved argument count to conf scope
[thirdparty/bird.git] / conf / cf-lex.l
CommitLineData
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 54struct 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 68static 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
86HASH_DEFINE_REHASH_FN(SYM, struct symbol)
87
88HASH(struct keyword) kw_hash;
c8f61a01 89
b7761af3
OZ
90
91static struct sym_scope *conf_this_scope;
82fc7be7 92
b35d72ac 93linpool *cfg_mem;
82fc7be7 94
48ec367a 95int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
9b7fdfc8 96struct include_file_stack *ifs;
4be266a9
OZ
97static struct include_file_stack *ifs_head;
98
99911873
MM
99#define QUOTED_BUFFER_SIZE 4096
100static BUFFER_(char) quoted_buffer;
101static char quoted_buffer_data[QUOTED_BUFFER_SIZE];
102static 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 115static void cf_include(char *arg, int alen);
48ec367a 116static int check_eof(void);
48ec367a 117
99911873
MM
118static 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
129ALPHA [a-zA-Z_]
130DIGIT [0-9]
131XIGIT [0-9a-fA-F]
132ALNUM [a-zA-Z_0-9]
133WHITE [ \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 2640x{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
286else: {
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 377static uint
99911873 378cf_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
399static struct include_file_stack *
400push_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
409static struct include_file_stack *
410pop_ifs(struct include_file_stack *old)
411{
412 yy_delete_buffer(old->buffer);
413 close(old->fd);
414 return old->prev;
415}
416
48ec367a 417static void
4be266a9 418enter_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 */
441void
442cf_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
458static void
459cf_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
531static int
532check_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 546static struct symbol *
99911873 547cf_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 */
580struct symbol *
f249d0b8 581cf_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 607struct symbol *
99911873 608cf_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 */
620struct symbol *
621cf_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 635struct symbol *
d272fe22 636cf_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
654static enum yytokentype
655cf_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
682static void
683cf_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 700void
48ec367a 701cf_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
736void
737cf_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
755void
756cf_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
770char *
771cf_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 */