Annotation of embedaddon/bird2/conf/cf-lex.l, revision 1.1.1.1
1.1 misho 1: /*
2: * BIRD -- Configuration Lexer
3: *
4: * (c) 1998--2000 Martin Mares <mj@ucw.cz>
5: *
6: * Can be freely distributed and used under the terms of the GNU GPL.
7: */
8:
9: /**
10: * DOC: Lexical analyzer
11: *
12: * The lexical analyzer used for configuration files and CLI commands
13: * is generated using the |flex| tool accompanied by 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 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.
22: *
23: * The keyword tables are generated from the grammar templates
24: * using the |gen_keywords.m4| script.
25: */
26:
27: %{
28: #undef REJECT /* Avoid name clashes */
29:
30: #include <errno.h>
31: #include <stdlib.h>
32: #include <stdarg.h>
33: #include <stdint.h>
34: #include <unistd.h>
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>
41:
42: #define PARSER 1
43:
44: #include "nest/bird.h"
45: #include "nest/route.h"
46: #include "nest/protocol.h"
47: #include "filter/filter.h"
48: #include "filter/f-inst.h"
49: #include "conf/conf.h"
50: #include "conf/cf-parse.tab.h"
51: #include "lib/string.h"
52: #include "lib/hash.h"
53:
54: struct keyword {
55: byte *name;
56: int value;
57: struct keyword *next;
58: };
59:
60: #include "conf/keywords.h"
61:
62: /* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */
63: #ifdef SYM
64: #undef SYM
65: #endif
66:
67:
68: static uint cf_hash(const byte *c);
69:
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;
89:
90:
91: struct sym_scope *conf_this_scope;
92:
93: linpool *cfg_mem;
94:
95: int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
96: struct include_file_stack *ifs;
97: static struct include_file_stack *ifs_head;
98:
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:
108: #define MAX_INCLUDE_DEPTH 8
109:
110: #define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd);
111: #define YY_NO_UNPUT
112: #define YY_FATAL_ERROR(msg) cf_error(msg)
113: #define YY_USER_ACTION ifs->chno += yyleng; ifs->toklen = yyleng;
114:
115: static void cf_include(char *arg, int alen);
116: static int check_eof(void);
117:
118: static enum yytokentype cf_lex_symbol(const char *data);
119:
120: %}
121:
122: %option noyywrap
123: %option noinput
124: %option nounput
125: %option noreject
126:
127: %x COMMENT CCOMM CLI QUOTED APOSTROPHED INCLUDE
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: %%
136: ^{WHITE}*include{WHITE}*\" {
137: if (!ifs->depth)
138: cf_error("Include not allowed in CLI");
139:
140: BEGIN(INCLUDE);
141: }
142:
143: <INCLUDE>[^"\n]+["]{WHITE}*; {
144: char *start, *end;
145:
146: start = yytext;
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);
155:
156: BEGIN(INITIAL);
157: }
158:
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:
165: {DIGIT}+:{DIGIT}+ {
166: uint len1 UNUSED, len2;
167: u64 l;
168: char *e;
169:
170: errno = 0;
171: l = bstrtoul10(yytext, &e);
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;
189: l = bstrtoul10(e+1, &e);
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:
197: [02]:{DIGIT}+:{DIGIT}+ {
198: uint len1, len2;
199: u64 l;
200: char *e;
201:
202: if (yytext[0] == '0')
203: {
204: cf_lval.i64 = 0;
205: len1 = 16;
206: len2 = 32;
207: }
208: else
209: {
210: cf_lval.i64 = 2ULL << 48;
211: len1 = 32;
212: len2 = 16;
213: }
214:
215: errno = 0;
216: l = bstrtoul10(yytext+2, &e);
217: if (e && (*e != ':') || (errno == ERANGE) || (l >> len1))
218: cf_error("ASN out of range");
219: cf_lval.i64 |= ((u64) l) << len2;
220:
221: errno = 0;
222: l = bstrtoul10(e+1, &e);
223: if (e && *e || (errno == ERANGE) || (l >> len2))
224: cf_error("Number out of range");
225: cf_lval.i64 |= l;
226:
227: return VPN_RD;
228: }
229:
230: {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ {
231: unsigned long int l;
232: ip4_addr ip4;
233: char *e;
234:
235: cf_lval.i64 = 1ULL << 48;
236:
237: e = strchr(yytext, ':');
238: *e++ = '\0';
239: if (!ip4_pton(yytext, &ip4))
240: cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext);
241: cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16;
242:
243: errno = 0;
244: l = bstrtoul10(e, &e);
245: if (e && *e || (errno == ERANGE) || (l >> 16))
246: cf_error("Number out of range");
247: cf_lval.i64 |= l;
248:
249: return VPN_RD;
250: }
251:
252: {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
253: if (!ip4_pton(yytext, &cf_lval.ip4))
254: cf_error("Invalid IPv4 address %s", yytext);
255: return IP4;
256: }
257:
258: ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) {
259: if (!ip6_pton(yytext, &cf_lval.ip6))
260: cf_error("Invalid IPv6 address %s", yytext);
261: return IP6;
262: }
263:
264: 0x{XIGIT}+ {
265: char *e;
266: unsigned long int l;
267: errno = 0;
268: l = bstrtoul16(yytext+2, &e);
269: if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
270: cf_error("Number out of range");
271: cf_lval.i = l;
272: return NUM;
273: }
274:
275: {DIGIT}+ {
276: char *e;
277: unsigned long int l;
278: errno = 0;
279: l = bstrtoul10(yytext, &e);
280: if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
281: cf_error("Number out of range");
282: cf_lval.i = l;
283: return NUM;
284: }
285:
286: else: {
287: /* Hack to distinguish if..else from else: in case */
288: return ELSECOL;
289: }
290:
291: ['] {
292: BEGIN(APOSTROPHED);
293: quoted_buffer_init();
294: }
295:
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");
305:
306: ({ALPHA}{ALNUM}*) {
307: return cf_lex_symbol(yytext);
308: }
309:
310: <CLI>(.|\n) {
311: BEGIN(INITIAL);
312: return CLI_MARKER;
313: }
314:
315: \.\. {
316: return DDOT;
317: }
318:
319: [={}:;,.()+*/%<>~\[\]?!\|-] {
320: return yytext[0];
321: }
322:
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);
334: return TEXT;
335: }
336:
337: <QUOTED>. BUFFER_PUSH(quoted_buffer) = yytext[0];
338:
339: <INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; }
340:
341: {WHITE}+
342:
343: \n ifs->lino++; ifs->chno = 0;
344:
345: # BEGIN(COMMENT);
346:
347: \/\* BEGIN(CCOMM);
348:
349: . cf_error("Unknown character");
350:
351: <COMMENT>\n {
352: ifs->lino++;
353: ifs->chno = 0;
354: BEGIN(INITIAL);
355: }
356:
357: <COMMENT>.
358:
359: <CCOMM>\*\/ BEGIN(INITIAL);
360: <CCOMM>\n ifs->lino++; ifs->chno = 0;
361: <CCOMM>\/\* cf_error("Comment nesting not supported");
362: <CCOMM><<EOF>> cf_error("Unterminated comment");
363: <CCOMM>.
364:
365: \!\= return NEQ;
366: \!\~ return NMA;
367: \<\= return LEQ;
368: \>\= return GEQ;
369: \&\& return AND;
370: \|\| return OR;
371:
372: \[\= return PO;
373: \=\] return PC;
374:
375: %%
376:
377: static uint
378: cf_hash(const byte *c)
379: {
380: uint h = 13 << 24;
381:
382: while (*c)
383: h = h + (h >> 2) + (h >> 5) + ((uint) *c++ << 24);
384: return h;
385: }
386:
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.
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:
417: static void
418: enter_ifs(struct include_file_stack *new)
419: {
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: }
431:
432: yy_switch_to_buffer(new->buffer);
433: }
434:
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:
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;
464: glob_t g = {};
465:
466: new_depth = ifs->depth + 1;
467: if (new_depth > MAX_INCLUDE_DEPTH)
468: cf_error("Max include depth reached");
469:
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;
481:
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: }
493:
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)
512: {
513: globfree(&g);
514: cf_error("Unable to stat included file %s: %m", fname);
515: }
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);
529: }
530:
531: static int
532: check_eof(void)
533: {
534: if (ifs == ifs_head)
535: {
536: /* EOF in main config file */
537: ifs->lino = 1; /* Why this? */
538: return 1;
539: }
540:
541: ifs = pop_ifs(ifs);
542: enter_ifs(ifs);
543: return 0;
544: }
545:
546: static struct symbol *
547: cf_new_symbol(const byte *c)
548: {
549: struct symbol *s;
550:
551: uint l = strlen(c);
552: if (l > SYM_MAX_LEN)
553: cf_error("Symbol too long");
554:
555: s = cfg_allocz(sizeof(struct symbol) + l + 1);
556: *s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
557: strcpy(s->name, c);
558:
559: if (!new_config->sym_hash.data)
560: HASH_INIT(new_config->sym_hash, new_config->pool, SYM_ORDER);
561:
562: HASH_INSERT2(new_config->sym_hash, SYM, new_config->pool, s);
563:
564: add_tail(&(new_config->symbols), &(s->n));
565:
566: return s;
567: }
568:
569: /**
570: * cf_find_symbol - find a symbol by name
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 *
581: cf_find_symbol(const struct config *cfg, const byte *c)
582: {
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:
589: /* In CLI command parsing, fallback points to the current config, otherwise it is NULL. */
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;
596: }
597:
598: /**
599: * cf_get_symbol - get a symbol by name
600: * @c: symbol name
601: *
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.
606: */
607: struct symbol *
608: cf_get_symbol(const byte *c)
609: {
610: return cf_find_symbol(new_config, c) ?: cf_new_symbol(c);
611: }
612:
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:
635: struct symbol *
636: cf_default_name(char *template, int *counter)
637: {
638: char buf[SYM_MAX_LEN];
639: struct symbol *s;
640: char *perc = strchr(template, '%');
641:
642: for(;;)
643: {
644: bsprintf(buf, template, ++(*counter));
645: s = cf_get_symbol(buf);
646: if (s->class == SYM_VOID)
647: return s;
648: if (!perc)
649: break;
650: }
651: cf_error("Unable to generate default name");
652: }
653:
654: static enum yytokentype
655: cf_lex_symbol(const char *data)
656: {
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? */
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:
677: /* OK, undefined symbol */
678: cf_lval.s = sym;
679: return CF_SYM_UNDEFINED;
680: }
681:
682: static void
683: cf_lex_init_kh(void)
684: {
685: HASH_INIT(kw_hash, &root_pool, KW_ORDER);
686:
687: struct keyword *k;
688: for (k=keyword_list; k->name; k++)
689: HASH_INSERT(kw_hash, KW, k);
690: }
691:
692: /**
693: * cf_lex_init - initialize the lexer
694: * @is_cli: true if we're going to parse CLI command, false for configuration
695: * @c: configuration structure
696: *
697: * cf_lex_init() initializes the lexical analyzer and prepares it for
698: * parsing of a new input.
699: */
700: void
701: cf_lex_init(int is_cli, struct config *c)
702: {
703: if (!kw_hash.data)
704: cf_lex_init_kh();
705:
706: ifs_head = ifs = push_ifs(NULL);
707: if (!is_cli)
708: {
709: ifs->file_name = c->file_name;
710: ifs->fd = c->file_fd;
711: ifs->depth = 1;
712: }
713:
714: yyrestart(NULL);
715: ifs->buffer = YY_CURRENT_BUFFER;
716:
717: if (is_cli)
718: BEGIN(CLI);
719: else
720: BEGIN(INITIAL);
721:
722: c->root_scope = cfg_allocz(sizeof(struct sym_scope));
723: conf_this_scope = c->root_scope;
724: conf_this_scope->active = 1;
725: }
726:
727: /**
728: * cf_push_scope - enter new scope
729: * @sym: symbol representing scope name
730: *
731: * If we want to enter a new scope to process declarations inside
732: * a nested block, we can just call cf_push_scope() to push a new
733: * scope onto the scope stack which will cause all new symbols to be
734: * defined in this scope and all existing symbols to be sought for
735: * in all scopes stored on the stack.
736: */
737: void
738: cf_push_scope(struct symbol *sym)
739: {
740: struct sym_scope *s = cfg_alloc(sizeof(struct sym_scope));
741:
742: s->next = conf_this_scope;
743: conf_this_scope = s;
744: s->active = 1;
745: s->name = sym;
746: s->slots = 0;
747: }
748:
749: /**
750: * cf_pop_scope - leave a scope
751: *
752: * cf_pop_scope() pops the topmost scope from the scope stack,
753: * leaving all its symbols in the symbol table, but making them
754: * invisible to the rest of the config.
755: */
756: void
757: cf_pop_scope(void)
758: {
759: conf_this_scope->active = 0;
760: conf_this_scope = conf_this_scope->next;
761:
762: ASSERT(conf_this_scope);
763: }
764:
765: /**
766: * cf_symbol_class_name - get name of a symbol class
767: * @sym: symbol
768: *
769: * This function returns a string representing the class
770: * of the given symbol.
771: */
772: char *
773: cf_symbol_class_name(struct symbol *sym)
774: {
775: switch (sym->class)
776: {
777: case SYM_VOID:
778: return "undefined";
779: case SYM_PROTO:
780: return "protocol";
781: case SYM_TEMPLATE:
782: return "protocol template";
783: case SYM_FUNCTION:
784: return "function";
785: case SYM_FILTER:
786: return "filter";
787: case SYM_TABLE:
788: return "routing table";
789: case SYM_ATTRIBUTE:
790: return "custom attribute";
791: case SYM_CONSTANT_RANGE:
792: return "constant";
793: case SYM_VARIABLE_RANGE:
794: return "variable";
795: default:
796: return "unknown type";
797: }
798: }
799:
800:
801: /**
802: * DOC: Parser
803: *
804: * Both the configuration and CLI commands are analyzed using a syntax
805: * driven parser generated by the |bison| tool from a grammar which
806: * is constructed from information gathered from grammar snippets by
807: * the |gen_parser.m4| script.
808: *
809: * Grammar snippets are files (usually with extension |.Y|) contributed
810: * by various BIRD modules in order to provide information about syntax of their
811: * configuration and their CLI commands. Each snipped consists of several
812: * sections, each of them starting with a special keyword: |CF_HDR| for
813: * a list of |#include| directives needed by the C code, |CF_DEFINES|
814: * for a list of C declarations, |CF_DECLS| for |bison| declarations
815: * including keyword definitions specified as |CF_KEYWORDS|, |CF_GRAMMAR|
816: * for the grammar rules, |CF_CODE| for auxiliary C code and finally
817: * |CF_END| at the end of the snippet.
818: *
819: * To create references between the snippets, it's possible to define
820: * multi-part rules by utilizing the |CF_ADDTO| macro which adds a new
821: * alternative to a multi-part rule.
822: *
823: * CLI commands are defined using a |CF_CLI| macro. Its parameters are:
824: * the list of keywords determining the command, the list of parameters,
825: * help text for the parameters and help text for the command.
826: *
827: * Values of |enum| filter types can be defined using |CF_ENUM| with
828: * the following parameters: name of filter type, prefix common for all
829: * literals of this type and names of all the possible values.
830: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>