Annotation of embedaddon/bird2/conf/cf-lex.l, revision 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>