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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>