cf-lex.l 17.2 KB
Newer Older
Martin Mareš's avatar
Martin Mareš committed
1 2 3
/*
 *	BIRD -- Configuration Lexer
 *
4
 *	(c) 1998--2000 Martin Mares <mj@ucw.cz>
Martin Mareš's avatar
Martin Mareš committed
5 6 7 8
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

Martin Mareš's avatar
Martin Mareš committed
9
/**
10
 * DOC: Lexical analyzer
Martin Mareš's avatar
Martin Mareš committed
11
 *
12
 * The lexical analyzer used for configuration files and CLI commands
Martin Mareš's avatar
Martin Mareš committed
13
 * is generated using the |flex| tool accompanied by a couple of
Martin Mareš's avatar
Martin Mareš committed
14 15 16 17
 * functions maintaining the hash tables containing information about
 * symbols and keywords.
 *
 * Each symbol is represented by a &symbol structure containing name
18 19 20 21
 * of the symbol, its lexical scope, symbol class (%SYM_PROTO for a
 * name of a protocol, %SYM_CONSTANT for a constant etc.) and class
 * dependent data.  When an unknown symbol is encountered, it's
 * automatically added to the symbol table with class %SYM_VOID.
Martin Mareš's avatar
Martin Mareš committed
22 23 24 25 26
 *
 * The keyword tables are generated from the grammar templates
 * using the |gen_keywords.m4| script.
 */

Martin Mareš's avatar
Martin Mareš committed
27
%{
28
#undef REJECT     /* Avoid name clashes */
Martin Mareš's avatar
Martin Mareš committed
29 30 31

#include <errno.h>
#include <stdlib.h>
32
#include <stdarg.h>
33
#include <stdint.h>
34
#include <unistd.h>
35 36 37 38 39 40
#include <libgen.h>
#include <glob.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
Martin Mareš's avatar
Martin Mareš committed
41

42 43
#define PARSER 1

Martin Mareš's avatar
Martin Mareš committed
44
#include "nest/bird.h"
Martin Mareš's avatar
Martin Mareš committed
45
#include "nest/route.h"
46
#include "nest/protocol.h"
Martin Mareš's avatar
Martin Mareš committed
47
#include "filter/filter.h"
Martin Mareš's avatar
Martin Mareš committed
48 49
#include "conf/conf.h"
#include "conf/cf-parse.tab.h"
50
#include "lib/string.h"
51
#include "lib/hash.h"
Martin Mareš's avatar
Martin Mareš committed
52

53
struct keyword {
Martin Mareš's avatar
Martin Mareš committed
54 55 56
  byte *name;
  int value;
  struct keyword *next;
57 58
};

59
#include "conf/keywords.h"
Martin Mareš's avatar
Martin Mareš committed
60

61 62 63 64 65
/* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */
#ifdef SYM
#undef SYM
#endif

66

67
static uint cf_hash(byte *c);
Martin Mareš's avatar
Martin Mareš committed
68

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
#define KW_KEY(n)		n->name
#define KW_NEXT(n)		n->next
#define KW_EQ(a,b)		!strcmp(a,b)
#define KW_FN(k)		cf_hash(k)
#define KW_ORDER		8 /* Fixed */

#define SYM_KEY(n)		n->name, n->scope->active
#define SYM_NEXT(n)		n->next
#define SYM_EQ(a,s1,b,s2)	!strcmp(a,b) && s1 == s2
#define SYM_FN(k,s)		cf_hash(k)
#define SYM_ORDER		6 /* Initial */

#define SYM_REHASH		sym_rehash
#define SYM_PARAMS		/8, *1, 2, 2, 6, 20


HASH_DEFINE_REHASH_FN(SYM, struct symbol)

HASH(struct keyword) kw_hash;
Martin Mareš's avatar
Martin Mareš committed
88

89

Ondřej Zajíček's avatar
Ondřej Zajíček committed
90
struct sym_scope *conf_this_scope;
Martin Mareš's avatar
Martin Mareš committed
91

92
linpool *cfg_mem;
Martin Mareš's avatar
Martin Mareš committed
93

94
int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
95
struct include_file_stack *ifs;
96 97 98
static struct include_file_stack *ifs_head;

#define MAX_INCLUDE_DEPTH 8
Martin Mareš's avatar
Martin Mareš committed
99

100
#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd);
Martin Mareš's avatar
Martin Mareš committed
101 102
#define YY_NO_UNPUT
#define YY_FATAL_ERROR(msg) cf_error(msg)
103
#define YY_USER_ACTION ifs->chno += yyleng; ifs->toklen = yyleng;
Martin Mareš's avatar
Martin Mareš committed
104

105
static void cf_include(char *arg, int alen);
106 107
static int check_eof(void);

Martin Mareš's avatar
Martin Mareš committed
108 109 110
%}

%option noyywrap
111 112 113
%option noinput
%option nounput
%option noreject
Martin Mareš's avatar
Martin Mareš committed
114

115
%x COMMENT CCOMM CLI
Martin Mareš's avatar
Martin Mareš committed
116 117 118 119 120 121

ALPHA [a-zA-Z_]
DIGIT [0-9]
XIGIT [0-9a-fA-F]
ALNUM [a-zA-Z_0-9]
WHITE [ \t]
122
include   ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
Martin Mareš's avatar
Martin Mareš committed
123 124

%%
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
{include} {
  char *start, *end;

  if (!ifs->depth)
    cf_error("Include not allowed in CLI");

  start = strchr(yytext, '"');
  start++;

  end = strchr(start, '"');
  *end = 0;

  if (start == end)
    cf_error("Include with empty argument");

  cf_include(start, end-start);
}
Martin Mareš's avatar
Martin Mareš committed
142

143
{DIGIT}+:{DIGIT}+ {
144 145
  uint len1 UNUSED, len2;
  u64 l;
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
  char *e;

  errno = 0;
  l = strtoul(yytext, &e, 10);
  if (e && (*e != ':') || (errno == ERANGE) || (l >> 32))
    cf_error("ASN out of range");

  if (l >> 16)
  {
    len1 = 32;
    len2 = 16;
    cf_lval.i64 = (2ULL << 48) | (((u64) l) << len2);
  }
  else
  {
    len1 = 16;
    len2 = 32;
    cf_lval.i64 = 0 | (((u64) l) << len2);
  }

  errno = 0;
  l = strtoul(e+1, &e, 10);
  if (e && *e || (errno == ERANGE) || (l >> len2))
    cf_error("Number out of range");
  cf_lval.i64 |= l;

  return VPN_RD;
}

175
[02]:{DIGIT}+:{DIGIT}+ {
176 177
  uint len1, len2;
  u64 l;
178 179 180
  char *e;

  if (yytext[0] == '0')
Ondřej Zajíček's avatar
Ondřej Zajíček committed
181
  {
182
    cf_lval.i64 = 0;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
183 184 185
    len1 = 16;
    len2 = 32;
  }
186
  else
Ondřej Zajíček's avatar
Ondřej Zajíček committed
187 188 189 190 191
  {
    cf_lval.i64 = 2ULL << 48;
    len1 = 32;
    len2 = 16;
  }
192 193 194

  errno = 0;
  l = strtoul(yytext+2, &e, 10);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
195
  if (e && (*e != ':') || (errno == ERANGE) || (l >> len1))
196
    cf_error("ASN out of range");
Ondřej Zajíček's avatar
Ondřej Zajíček committed
197 198
  cf_lval.i64 |= ((u64) l) << len2;

199 200
  errno = 0;
  l = strtoul(e+1, &e, 10);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
201 202
  if (e && *e || (errno == ERANGE) || (l >> len2))
    cf_error("Number out of range");
203
  cf_lval.i64 |= l;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
204

205 206 207
  return VPN_RD;
}

208
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ {
209 210
  unsigned long int l;
  ip4_addr ip4;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
211 212 213 214
  char *e;

  cf_lval.i64 = 1ULL << 48;

215
  e = strchr(yytext, ':');
Ondřej Zajíček's avatar
Ondřej Zajíček committed
216
  *e++ = '\0';
217 218
  if (!ip4_pton(yytext, &ip4))
    cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
219 220
  cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16;

221 222
  errno = 0;
  l = strtoul(e, &e, 10);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
223 224 225 226
  if (e && *e || (errno == ERANGE) || (l >> 16))
    cf_error("Number out of range");
  cf_lval.i64 |= l;

227 228 229
  return VPN_RD;
}

Martin Mareš's avatar
Martin Mareš committed
230
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
231
  if (!ip4_pton(yytext, &cf_lval.ip4))
232
    cf_error("Invalid IPv4 address %s", yytext);
233
  return IP4;
234 235 236
}

({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) {
237 238 239
  if (!ip6_pton(yytext, &cf_lval.ip6))
    cf_error("Invalid IPv6 address %s", yytext);
  return IP6;
Martin Mareš's avatar
Martin Mareš committed
240 241
}

242
0x{XIGIT}+ {
Martin Mareš's avatar
Martin Mareš committed
243
  char *e;
244
  unsigned long int l;
Martin Mareš's avatar
Martin Mareš committed
245 246
  errno = 0;
  l = strtoul(yytext+2, &e, 16);
247
  if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
Martin Mareš's avatar
Martin Mareš committed
248 249 250 251 252 253 254
    cf_error("Number out of range");
  cf_lval.i = l;
  return NUM;
}

{DIGIT}+ {
  char *e;
255
  unsigned long int l;
Martin Mareš's avatar
Martin Mareš committed
256 257
  errno = 0;
  l = strtoul(yytext, &e, 10);
258
  if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
Martin Mareš's avatar
Martin Mareš committed
259 260 261 262 263
    cf_error("Number out of range");
  cf_lval.i = l;
  return NUM;
}

264 265 266 267 268
else: {
  /* Hack to distinguish if..else from else: in case */
  return ELSECOL;
}

269
({ALPHA}{ALNUM}*|[']({ALNUM}|[-]|[\.]|[:])*[']) {
270 271 272 273
  if(*yytext == '\'') {
    yytext[yyleng-1] = 0;
    yytext++;
  }
274 275 276 277 278 279 280

  struct keyword *k = HASH_FIND(kw_hash, KW, yytext);
  if (k)
  {
    if (k->value > 0)
      return k->value;
    else
Martin Mareš's avatar
Martin Mareš committed
281
    {
282 283
      cf_lval.i = -k->value;
      return ENUM;
Martin Mareš's avatar
Martin Mareš committed
284
    }
285 286 287
  }

  cf_lval.s = cf_get_symbol(yytext);
Martin Mareš's avatar
Martin Mareš committed
288 289 290
  return SYM;
}

291
<CLI>(.|\n) {
292 293 294 295
  BEGIN(INITIAL);
  return CLI_MARKER;
}

296 297 298 299
\.\. {
  return DDOT;
}

300
[={}:;,.()+*/%<>~\[\]?!\|-] {
Martin Mareš's avatar
Martin Mareš committed
301 302 303 304
  return yytext[0];
}

["][^"\n]*["] {
305
  yytext[yyleng-1] = 0;
306
  cf_lval.t = cfg_strdup(yytext+1);
307
  yytext[yyleng-1] = '"';
Martin Mareš's avatar
Martin Mareš committed
308 309 310 311 312
  return TEXT;
}

["][^"\n]*\n	cf_error("Unterminated string");

313
<INITIAL,COMMENT><<EOF>>	{ if (check_eof()) return END; }
Martin Mareš's avatar
Martin Mareš committed
314 315 316

{WHITE}+

317
\n	ifs->lino++; ifs->chno = 0;
Martin Mareš's avatar
Martin Mareš committed
318

319
#	BEGIN(COMMENT);
Martin Mareš's avatar
Martin Mareš committed
320

321
\/\*	BEGIN(CCOMM);
Martin Mareš's avatar
Martin Mareš committed
322 323 324 325

.	cf_error("Unknown character");

<COMMENT>\n {
326
  ifs->lino++;
327
  ifs->chno = 0;
Martin Mareš's avatar
Martin Mareš committed
328 329 330 331 332 333
  BEGIN(INITIAL);
}

<COMMENT>.

<CCOMM>\*\/	BEGIN(INITIAL);
334
<CCOMM>\n	ifs->lino++; ifs->chno = 0;
Martin Mareš's avatar
Martin Mareš committed
335 336 337 338
<CCOMM>\/\*	cf_error("Comment nesting not supported");
<CCOMM><<EOF>>	cf_error("Unterminated comment");
<CCOMM>.

Pavel Machek's avatar
Pavel Machek committed
339
\!\= return NEQ;
340
\!\~ return NMA;
Pavel Machek's avatar
Pavel Machek committed
341 342
\<\= return LEQ;
\>\= return GEQ;
Pavel Machek's avatar
Pavel Machek committed
343 344
\&\& return AND;
\|\| return OR;
Pavel Machek's avatar
Pavel Machek committed
345

346 347 348
\[\= return PO;
\=\] return PC;

Martin Mareš's avatar
Martin Mareš committed
349 350
%%

351
static uint
Martin Mareš's avatar
Martin Mareš committed
352 353
cf_hash(byte *c)
{
354
  uint h = 13 << 24;
Martin Mareš's avatar
Martin Mareš committed
355 356

  while (*c)
357
    h = h + (h >> 2) + (h >> 5) + ((uint) *c++ << 24);
Martin Mareš's avatar
Martin Mareš committed
358 359 360
  return h;
}

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391

/*
 * IFS stack - it contains structures needed for recursive processing
 * of include in config files. On the top of the stack is a structure
 * for currently processed file. Other structures are either for
 * active files interrupted because of include directive (these have
 * fd and flex buffer) or for inactive files scheduled to be processed
 * later (when parent requested including of several files by wildcard
 * match - these do not have fd and flex buffer yet).
 *
 * FIXME: Most of these ifs and include functions are really sysdep/unix.
 */

static struct include_file_stack *
push_ifs(struct include_file_stack *old)
{
  struct include_file_stack *ret;
  ret = cfg_allocz(sizeof(struct include_file_stack));
  ret->lino = 1;
  ret->prev = old;
  return ret;
}

static struct include_file_stack *
pop_ifs(struct include_file_stack *old)
{
 yy_delete_buffer(old->buffer);
 close(old->fd);
 return old->prev;
}

392
static void
393
enter_ifs(struct include_file_stack *new)
394
{
395 396 397 398 399 400 401 402 403 404 405
  if (!new->buffer)
    {
      new->fd = open(new->file_name, O_RDONLY);
      if (new->fd < 0)
        {
          ifs = ifs->up;
	  cf_error("Unable to open included file %s: %m", new->file_name);
        }

      new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE);
    }
406

407 408
  yy_switch_to_buffer(new->buffer);
}
409

410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
/**
 * cf_lex_unwind - unwind lexer state during error
 *
 * cf_lex_unwind() frees the internal state on IFS stack when the lexical
 * analyzer is terminated by cf_error().
 */
void
cf_lex_unwind(void)
{
  struct include_file_stack *n;

  for (n = ifs; n != ifs_head; n = n->prev)
    {
      /* Memory is freed automatically */
      if (n->buffer)
	yy_delete_buffer(n->buffer);
      if (n->fd)
        close(n->fd);
    }

  ifs = ifs_head;
}

433 434 435 436 437 438
static void
cf_include(char *arg, int alen)
{
  struct include_file_stack *base_ifs = ifs;
  int new_depth, rv, i;
  char *patt;
439
  glob_t g = {};
440 441 442 443

  new_depth = ifs->depth + 1;
  if (new_depth > MAX_INCLUDE_DEPTH)
    cf_error("Max include depth reached");
444

445 446 447 448 449 450 451 452 453 454 455
  /* expand arg to properly handle relative filenames */
  if (*arg != '/')
    {
      int dlen = strlen(ifs->file_name);
      char *dir = alloca(dlen + 1);
      patt = alloca(dlen + alen + 2);
      memcpy(dir, ifs->file_name, dlen + 1);
      sprintf(patt, "%s/%s", dirname(dir), arg);
    }
  else
    patt = arg;
456

457 458 459 460 461 462 463 464 465 466 467
  /* Skip globbing if there are no wildcards, mainly to get proper
     response when the included config file is missing */
  if (!strpbrk(arg, "?*["))
    {
      ifs = push_ifs(ifs);
      ifs->file_name = cfg_strdup(patt);
      ifs->depth = new_depth;
      ifs->up = base_ifs;
      enter_ifs(ifs);
      return;
    }
468

469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
  /* Expand the pattern */
  rv = glob(patt, GLOB_ERR | GLOB_NOESCAPE, NULL, &g);
  if (rv == GLOB_ABORTED)
    cf_error("Unable to match pattern %s: %m", patt);
  if ((rv != 0) || (g.gl_pathc <= 0))
    return;

  /*
   * Now we put all found files to ifs stack in reverse order, they
   * will be activated and processed in order as ifs stack is popped
   * by pop_ifs() and enter_ifs() in check_eof().
   */
  for(i = g.gl_pathc - 1; i >= 0; i--)
    {
      char *fname = g.gl_pathv[i];
      struct stat fs;

      if (stat(fname, &fs) < 0)
487 488 489 490
	{
	  globfree(&g);
	  cf_error("Unable to stat included file %s: %m", fname);
	}
491 492 493 494 495 496 497 498 499 500 501 502 503

      if (fs.st_mode & S_IFDIR)
        continue;

      /* Prepare new stack item */
      ifs = push_ifs(ifs);
      ifs->file_name = cfg_strdup(fname);
      ifs->depth = new_depth;
      ifs->up = base_ifs;
    }

  globfree(&g);
  enter_ifs(ifs);
504 505 506 507 508
}

static int
check_eof(void)
{
509 510 511 512 513 514
  if (ifs == ifs_head)
    {
      /* EOF in main config file */
      ifs->lino = 1; /* Why this? */
      return 1;
    }
515

516 517 518
  ifs = pop_ifs(ifs);
  enter_ifs(ifs);
  return 0;
519 520
}

521
static struct symbol *
522
cf_new_symbol(byte *c)
523
{
524 525 526
  struct symbol *s;

  uint l = strlen(c);
527 528
  if (l > SYM_MAX_LEN)
    cf_error("Symbol too long");
529

530 531 532 533 534 535
  s = cfg_alloc(sizeof(struct symbol) + l);
  s->scope = conf_this_scope;
  s->class = SYM_VOID;
  s->def = NULL;
  s->aux = 0;
  strcpy(s->name, c);
Martin Mareš's avatar
Martin Mareš committed
536

537 538
  if (!new_config->sym_hash.data)
    HASH_INIT(new_config->sym_hash, new_config->pool, SYM_ORDER);
539

540
  HASH_INSERT2(new_config->sym_hash, SYM, new_config->pool, s);
541

542
  return s;
Martin Mareš's avatar
Martin Mareš committed
543 544
}

Martin Mareš's avatar
Martin Mareš committed
545 546
/**
 * cf_find_symbol - find a symbol by name
547 548 549 550 551 552 553 554 555 556 557 558
 * @cfg: specificed config
 * @c: symbol name
 *
 * This functions searches the symbol table in the config @cfg for a symbol of
 * given name. First it examines the current scope, then the second recent one
 * and so on until it either finds the symbol and returns a pointer to its
 * &symbol structure or reaches the end of the scope chain and returns %NULL to
 * signify no match.
 */
struct symbol *
cf_find_symbol(struct config *cfg, byte *c)
{
559 560 561 562 563 564 565 566 567 568 569 570
  struct symbol *s;

  if (cfg->sym_hash.data &&
      (s = HASH_FIND(cfg->sym_hash, SYM, c, 1)))
    return s;

  if (cfg->fallback &&
      cfg->fallback->sym_hash.data &&
      (s = HASH_FIND(cfg->fallback->sym_hash, SYM, c, 1)))
    return s;

  return NULL;
571 572 573 574
}

/**
 * cf_get_symbol - get a symbol by name
Martin Mareš's avatar
Martin Mareš committed
575 576
 * @c: symbol name
 *
577 578 579 580
 * This functions searches the symbol table of the currently parsed config
 * (@new_config) for a symbol of given name. It returns either the already
 * existing symbol or a newly allocated undefined (%SYM_VOID) symbol if no
 * existing symbol is found.
Martin Mareš's avatar
Martin Mareš committed
581
 */
582
struct symbol *
583
cf_get_symbol(byte *c)
584
{
585
  return cf_find_symbol(new_config, c) ?: cf_new_symbol(c);
586 587
}

588
struct symbol *
589
cf_default_name(char *template, int *counter)
590
{
591
  char buf[SYM_MAX_LEN];
592
  struct symbol *s;
593
  char *perc = strchr(template, '%');
594

595
  for(;;)
596
    {
597
      bsprintf(buf, template, ++(*counter));
598
      s = cf_get_symbol(buf);
599 600 601 602
      if (s->class == SYM_VOID)
	return s;
      if (!perc)
	break;
603
    }
604
  cf_error("Unable to generate default name");
605 606
}

Martin Mareš's avatar
Martin Mareš committed
607 608 609 610 611 612
/**
 * cf_define_symbol - define meaning of a symbol
 * @sym: symbol to be defined
 * @type: symbol class to assign
 * @def: class dependent data
 *
613 614 615 616 617 618 619 620
 * Defines new meaning of a symbol. If the symbol is an undefined
 * one (%SYM_VOID), it's just re-defined to the new type. If it's defined
 * in different scope, a new symbol in current scope is created and the
 * meaning is assigned to it. If it's already defined in the current scope,
 * an error is reported via cf_error().
 *
 * Result: Pointer to the newly defined symbol. If we are in the top-level
 * scope, it's the same @sym as passed to the function.
Martin Mareš's avatar
Martin Mareš committed
621
 */
622
struct symbol *
623 624 625
cf_define_symbol(struct symbol *sym, int type, void *def)
{
  if (sym->class)
626 627 628
    {
      if (sym->scope == conf_this_scope)
	cf_error("Symbol already defined");
629
      sym = cf_new_symbol(sym->name);
630
    }
631 632
  sym->class = type;
  sym->def = def;
633
  return sym;
634 635
}

636 637 638
static void
cf_lex_init_kh(void)
{
639
  HASH_INIT(kw_hash, &root_pool, KW_ORDER);
640

641 642 643
  struct keyword *k;
  for (k=keyword_list; k->name; k++)
    HASH_INSERT(kw_hash, KW, k);
644 645
}

Martin Mareš's avatar
Martin Mareš committed
646 647 648
/**
 * cf_lex_init - initialize the lexer
 * @is_cli: true if we're going to parse CLI command, false for configuration
649
 * @c: configuration structure
Martin Mareš's avatar
Martin Mareš committed
650
 *
651
 * cf_lex_init() initializes the lexical analyzer and prepares it for
Martin Mareš's avatar
Martin Mareš committed
652 653
 * parsing of a new input.
 */
Martin Mareš's avatar
Martin Mareš committed
654
void
655
cf_lex_init(int is_cli, struct config *c)
Martin Mareš's avatar
Martin Mareš committed
656
{
657
  if (!kw_hash.data)
658
    cf_lex_init_kh();
659 660

  ifs_head = ifs = push_ifs(NULL);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
661
  if (!is_cli)
662 663 664 665 666 667
    {
      ifs->file_name = c->file_name;
      ifs->fd = c->file_fd;
      ifs->depth = 1;
    }

668
  yyrestart(NULL);
669 670
  ifs->buffer = YY_CURRENT_BUFFER;

671 672 673 674
  if (is_cli)
    BEGIN(CLI);
  else
    BEGIN(INITIAL);
675

Ondřej Zajíček's avatar
Ondřej Zajíček committed
676 677
  c->root_scope = cfg_allocz(sizeof(struct sym_scope));
  conf_this_scope = c->root_scope;
Martin Mareš's avatar
Martin Mareš committed
678
  conf_this_scope->active = 1;
Martin Mareš's avatar
Martin Mareš committed
679 680
}

Martin Mareš's avatar
Martin Mareš committed
681 682 683 684 685 686 687 688 689 690
/**
 * cf_push_scope - enter new scope
 * @sym: symbol representing scope name
 *
 * If we want to enter a new scope to process declarations inside
 * a nested block, we can just call cf_push_scope() to push a new
 * scope onto the scope stack which will cause all new symbols to be
 * defined in this scope and all existing symbols to be sought for
 * in all scopes stored on the stack.
 */
Martin Mareš's avatar
Martin Mareš committed
691 692 693 694 695 696 697 698 699 700 701
void
cf_push_scope(struct symbol *sym)
{
  struct sym_scope *s = cfg_alloc(sizeof(struct sym_scope));

  s->next = conf_this_scope;
  conf_this_scope = s;
  s->active = 1;
  s->name = sym;
}

Martin Mareš's avatar
Martin Mareš committed
702 703 704 705 706 707 708
/**
 * cf_pop_scope - leave a scope
 *
 * cf_pop_scope() pops the topmost scope from the scope stack,
 * leaving all its symbols in the symbol table, but making them
 * invisible to the rest of the config.
 */
Martin Mareš's avatar
Martin Mareš committed
709 710 711 712 713 714 715
void
cf_pop_scope(void)
{
  conf_this_scope->active = 0;
  conf_this_scope = conf_this_scope->next;
  ASSERT(conf_this_scope);
}
716

Martin Mareš's avatar
Martin Mareš committed
717 718 719 720 721 722 723
/**
 * cf_symbol_class_name - get name of a symbol class
 * @sym: symbol
 *
 * This function returns a string representing the class
 * of the given symbol.
 */
724 725 726
char *
cf_symbol_class_name(struct symbol *sym)
{
727
  if (cf_symbol_is_constant(sym))
728 729
    return "constant";

730 731 732 733 734 735
  switch (sym->class)
    {
    case SYM_VOID:
      return "undefined";
    case SYM_PROTO:
      return "protocol";
736 737
    case SYM_TEMPLATE:
      return "protocol template";
738 739 740 741 742 743 744 745 746 747
    case SYM_FUNCTION:
      return "function";
    case SYM_FILTER:
      return "filter";
    case SYM_TABLE:
      return "routing table";
    default:
      return "unknown type";
    }
}
748 749 750 751 752


/**
 * DOC: Parser
 *
753
 * Both the configuration and CLI commands are analyzed using a syntax
754 755 756 757 758
 * driven parser generated by the |bison| tool from a grammar which
 * is constructed from information gathered from grammar snippets by
 * the |gen_parser.m4| script.
 *
 * Grammar snippets are files (usually with extension |.Y|) contributed
Martin Mareš's avatar
Martin Mareš committed
759
 * by various BIRD modules in order to provide information about syntax of their
760
 * configuration and their CLI commands. Each snipped consists of several
761
 * sections, each of them starting with a special keyword: |CF_HDR| for
762 763 764
 * a list of |#include| directives needed by the C code, |CF_DEFINES|
 * for a list of C declarations, |CF_DECLS| for |bison| declarations
 * including keyword definitions specified as |CF_KEYWORDS|, |CF_GRAMMAR|
765
 * for the grammar rules, |CF_CODE| for auxiliary C code and finally
766 767 768 769 770 771 772
 * |CF_END| at the end of the snippet.
 *
 * To create references between the snippets, it's possible to define
 * multi-part rules by utilizing the |CF_ADDTO| macro which adds a new
 * alternative to a multi-part rule.
 *
 * CLI commands are defined using a |CF_CLI| macro. Its parameters are:
773
 * the list of keywords determining the command, the list of parameters,
774 775 776 777
 * help text for the parameters and help text for the command.
 *
 * Values of |enum| filter types can be defined using |CF_ENUM| with
 * the following parameters: name of filter type, prefix common for all
778
 * literals of this type and names of all the possible values.
779
 */