]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - conf/cf-lex.l
Configuration strings are constant.
[thirdparty/bird.git] / conf / cf-lex.l
index f90e983b0fdd59fca32ab258a76d3e7849cf91ca..1d6cae2cd52212a5e89e561c5cfd4da984d13323 100644 (file)
@@ -30,6 +30,7 @@
 #include <errno.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <unistd.h>
 #include <libgen.h>
 #include <glob.h>
 #include "nest/route.h"
 #include "nest/protocol.h"
 #include "filter/filter.h"
+#include "filter/f-inst.h"
 #include "conf/conf.h"
 #include "conf/cf-parse.tab.h"
 #include "lib/string.h"
+#include "lib/hash.h"
 
 struct keyword {
   byte *name;
@@ -56,21 +59,36 @@ struct keyword {
 
 #include "conf/keywords.h"
 
-#define KW_HASH_SIZE 64
-static struct keyword *kw_hash[KW_HASH_SIZE];
-static int kw_hash_inited;
+/* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */
+#ifdef SYM
+#undef SYM
+#endif
 
-#define SYM_HASH_SIZE 128
 
-struct sym_scope {
-  struct sym_scope *next;              /* Next on scope stack */
-  struct symbol *name;                 /* Name of this scope */
-  int active;                          /* Currently entered */
-};
-static struct sym_scope *conf_this_scope;
+static uint cf_hash(const byte *c);
+
+#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;
+
 
-static int cf_hash(byte *c);
-static inline struct symbol * cf_get_sym(byte *c, uint h0);
+struct sym_scope *conf_this_scope;
 
 linpool *cfg_mem;
 
@@ -78,15 +96,27 @@ int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
 struct include_file_stack *ifs;
 static struct include_file_stack *ifs_head;
 
+#define QUOTED_BUFFER_SIZE  4096
+static BUFFER_(char) quoted_buffer;
+static char quoted_buffer_data[QUOTED_BUFFER_SIZE];
+static inline void quoted_buffer_init(void) {
+  quoted_buffer.used = 0;
+  quoted_buffer.size = QUOTED_BUFFER_SIZE;
+  quoted_buffer.data = quoted_buffer_data;
+}
+
 #define MAX_INCLUDE_DEPTH 8
 
 #define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd);
 #define YY_NO_UNPUT
 #define YY_FATAL_ERROR(msg) cf_error(msg)
+#define YY_USER_ACTION ifs->chno += yyleng; ifs->toklen = yyleng;
 
 static void cf_include(char *arg, int alen);
 static int check_eof(void);
 
+static enum yytokentype cf_lex_symbol(const char *data);
+
 %}
 
 %option noyywrap
@@ -94,24 +124,26 @@ static int check_eof(void);
 %option nounput
 %option noreject
 
-%x COMMENT CCOMM CLI
+%x COMMENT CCOMM CLI QUOTED APOSTROPHED INCLUDE
 
 ALPHA [a-zA-Z_]
 DIGIT [0-9]
 XIGIT [0-9a-fA-F]
 ALNUM [a-zA-Z_0-9]
 WHITE [ \t]
-include   ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
 
 %%
-{include} {
-  char *start, *end;
-
+^{WHITE}*include{WHITE}*\" {
   if (!ifs->depth)
     cf_error("Include not allowed in CLI");
 
-  start = strchr(yytext, '"');
-  start++;
+  BEGIN(INCLUDE);
+}
+
+<INCLUDE>[^"\n]+["]{WHITE}*; {
+  char *start, *end;
+
+  start = yytext;
 
   end = strchr(start, '"');
   *end = 0;
@@ -120,6 +152,101 @@ include   ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
     cf_error("Include with empty argument");
 
   cf_include(start, end-start);
+
+  BEGIN(INITIAL);
+}
+
+<INCLUDE>["]           cf_error("Include with empty argument");
+<INCLUDE>.             cf_error("Unterminated include");
+<INCLUDE>\n            cf_error("Unterminated include");
+<INCLUDE><<EOF>>       cf_error("Unterminated include");
+
+
+{DIGIT}+:{DIGIT}+ {
+  uint len1 UNUSED, len2;
+  u64 l;
+  char *e;
+
+  errno = 0;
+  l = bstrtoul10(yytext, &e);
+  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 = bstrtoul10(e+1, &e);
+  if (e && *e || (errno == ERANGE) || (l >> len2))
+    cf_error("Number out of range");
+  cf_lval.i64 |= l;
+
+  return VPN_RD;
+}
+
+[02]:{DIGIT}+:{DIGIT}+ {
+  uint len1, len2;
+  u64 l;
+  char *e;
+
+  if (yytext[0] == '0')
+  {
+    cf_lval.i64 = 0;
+    len1 = 16;
+    len2 = 32;
+  }
+  else
+  {
+    cf_lval.i64 = 2ULL << 48;
+    len1 = 32;
+    len2 = 16;
+  }
+
+  errno = 0;
+  l = bstrtoul10(yytext+2, &e);
+  if (e && (*e != ':') || (errno == ERANGE) || (l >> len1))
+    cf_error("ASN out of range");
+  cf_lval.i64 |= ((u64) l) << len2;
+
+  errno = 0;
+  l = bstrtoul10(e+1, &e);
+  if (e && *e || (errno == ERANGE) || (l >> len2))
+    cf_error("Number out of range");
+  cf_lval.i64 |= l;
+
+  return VPN_RD;
+}
+
+{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ {
+  unsigned long int l;
+  ip4_addr ip4;
+  char *e;
+
+  cf_lval.i64 = 1ULL << 48;
+
+  e = strchr(yytext, ':');
+  *e++ = '\0';
+  if (!ip4_pton(yytext, &ip4))
+    cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext);
+  cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16;
+
+  errno = 0;
+  l = bstrtoul10(e, &e);
+  if (e && *e || (errno == ERANGE) || (l >> 16))
+    cf_error("Number out of range");
+  cf_lval.i64 |= l;
+
+  return VPN_RD;
 }
 
 {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
@@ -138,7 +265,7 @@ include   ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
   char *e;
   unsigned long int l;
   errno = 0;
-  l = strtoul(yytext+2, &e, 16);
+  l = bstrtoul16(yytext+2, &e);
   if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
     cf_error("Number out of range");
   cf_lval.i = l;
@@ -149,7 +276,7 @@ include   ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
   char *e;
   unsigned long int l;
   errno = 0;
-  l = strtoul(yytext, &e, 10);
+  l = bstrtoul10(yytext, &e);
   if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
     cf_error("Number out of range");
   cf_lval.i = l;
@@ -161,29 +288,23 @@ else: {
   return ELSECOL;
 }
 
-({ALPHA}{ALNUM}*|[']({ALNUM}|[-]|[\.]|[:])*[']) {
-  if(*yytext == '\'') {
-    yytext[yyleng-1] = 0;
-    yytext++;
-  }
-  unsigned int h = cf_hash(yytext);
-  struct keyword *k = kw_hash[h & (KW_HASH_SIZE-1)];
-  while (k)
-    {
-      if (!strcmp(k->name, yytext))
-       {
-         if (k->value > 0)
-           return k->value;
-         else
-           {
-             cf_lval.i = -k->value;
-             return ENUM;
-           }
-       }
-      k=k->next;
-    }
-  cf_lval.s = cf_get_sym(yytext, h);
-  return SYM;
+['] {
+  BEGIN(APOSTROPHED);
+  quoted_buffer_init();
+}
+
+<APOSTROPHED>{ALNUM}|[-]|[.:]  BUFFER_PUSH(quoted_buffer) = yytext[0];
+<APOSTROPHED>\n                        cf_error("Unterminated symbol");
+<APOSTROPHED><<EOF>>           cf_error("Unterminated symbol");
+<APOSTROPHED>['] {
+  BEGIN(INITIAL);
+  BUFFER_PUSH(quoted_buffer) = 0;
+  return cf_lex_symbol(quoted_buffer_data);
+}
+<APOSTROPHED>.                 cf_error("Invalid character in apostrophed symbol");
+
+({ALPHA}{ALNUM}*) {
+  return cf_lex_symbol(yytext);
 }
 
 <CLI>(.|\n) {
@@ -199,19 +320,27 @@ else: {
   return yytext[0];
 }
 
-["][^"\n]*["] {
-  yytext[yyleng-1] = 0;
-  cf_lval.t = cfg_strdup(yytext+1);
+["] {
+  BEGIN(QUOTED);
+  quoted_buffer_init();
+}
+
+<QUOTED>\n     cf_error("Unterminated string");
+<QUOTED><<EOF>> cf_error("Unterminated string");
+<QUOTED>["]    {
+  BEGIN(INITIAL);
+  BUFFER_PUSH(quoted_buffer) = 0;
+  cf_lval.t = cfg_strdup(quoted_buffer_data);
   return TEXT;
 }
 
-["][^"\n]*\n   cf_error("Unterminated string");
+<QUOTED>.      BUFFER_PUSH(quoted_buffer) = yytext[0];
 
 <INITIAL,COMMENT><<EOF>>       { if (check_eof()) return END; }
 
 {WHITE}+
 
-\n     ifs->lino++;
+\n     ifs->lino++; ifs->chno = 0;
 
 #      BEGIN(COMMENT);
 
@@ -221,18 +350,20 @@ else: {
 
 <COMMENT>\n {
   ifs->lino++;
+  ifs->chno = 0;
   BEGIN(INITIAL);
 }
 
 <COMMENT>.
 
 <CCOMM>\*\/    BEGIN(INITIAL);
-<CCOMM>\n      ifs->lino++;
+<CCOMM>\n      ifs->lino++; ifs->chno = 0;
 <CCOMM>\/\*    cf_error("Comment nesting not supported");
 <CCOMM><<EOF>> cf_error("Unterminated comment");
 <CCOMM>.
 
 \!\= return NEQ;
+\!\~ return NMA;
 \<\= return LEQ;
 \>\= return GEQ;
 \&\& return AND;
@@ -243,17 +374,16 @@ else: {
 
 %%
 
-static int
-cf_hash(byte *c)
+static uint
+cf_hash(const byte *c)
 {
-  unsigned int h = 13;
+  uint h = 13 << 24;
 
   while (*c)
-    h = (h * 37) + *c++;
+    h = h + (h >> 2) + (h >> 5) + ((uint) *c++ << 24);
   return h;
 }
 
-
 /*
  * IFS stack - it contains structures needed for recursive processing
  * of include in config files. On the top of the stack is a structure
@@ -414,56 +544,26 @@ check_eof(void)
 }
 
 static struct symbol *
-cf_new_sym(byte *c, uint h0)
+cf_new_symbol(const byte *c)
 {
-  uint h = h0 & (SYM_HASH_SIZE-1);
-  struct symbol *s, **ht;
-  int l;
-
-  if (!new_config->sym_hash)
-    new_config->sym_hash = cfg_allocz(SYM_HASH_SIZE * sizeof(struct keyword *));
-  ht = new_config->sym_hash;
-  l = strlen(c);
+  struct symbol *s;
+
+  uint l = strlen(c);
   if (l > SYM_MAX_LEN)
     cf_error("Symbol too long");
-  s = cfg_alloc(sizeof(struct symbol) + l);
-  s->next = ht[h];
-  ht[h] = s;
-  s->scope = conf_this_scope;
-  s->class = SYM_VOID;
-  s->def = NULL;
-  s->aux = 0;
+
+  s = cfg_allocz(sizeof(struct symbol) + l + 1);
+  *s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
   strcpy(s->name, c);
-  return s;
-}
 
-static struct symbol *
-cf_find_sym(struct config *cfg, byte *c, uint h0)
-{
-  uint h = h0 & (SYM_HASH_SIZE-1);
-  struct symbol *s, **ht;
+  if (!new_config->sym_hash.data)
+    HASH_INIT(new_config->sym_hash, new_config->pool, SYM_ORDER);
 
-  if (ht = cfg->sym_hash)
-    {
-      for(s = ht[h]; s; s=s->next)
-       if (!strcmp(s->name, c) && s->scope->active)
-         return s;
-    }
-  if (ht = cfg->sym_fallback)
-    {
-      /* We know only top-level scope is active */
-      for(s = ht[h]; s; s=s->next)
-       if (!strcmp(s->name, c) && s->scope->active)
-         return s;
-    }
+  HASH_INSERT2(new_config->sym_hash, SYM, new_config->pool, s);
 
-  return NULL;
-}
+  add_tail(&(new_config->symbols), &(s->n));
 
-static inline struct symbol *
-cf_get_sym(byte *c, uint h0)
-{
-  return cf_find_sym(new_config, c, h0) ?: cf_new_sym(c, h0);
+  return s;
 }
 
 /**
@@ -478,9 +578,21 @@ cf_get_sym(byte *c, uint h0)
  * signify no match.
  */
 struct symbol *
-cf_find_symbol(struct config *cfg, byte *c)
+cf_find_symbol(const struct config *cfg, const byte *c)
 {
-  return cf_find_sym(cfg, c, cf_hash(c));
+  struct symbol *s;
+
+  if (cfg->sym_hash.data &&
+      (s = HASH_FIND(cfg->sym_hash, SYM, c, 1)))
+    return s;
+
+  /* In CLI command parsing, fallback points to the current config, otherwise it is NULL. */
+  if (cfg->fallback &&
+      cfg->fallback->sym_hash.data &&
+      (s = HASH_FIND(cfg->fallback->sym_hash, SYM, c, 1)))
+    return s;
+
+  return NULL;
 }
 
 /**
@@ -493,9 +605,31 @@ cf_find_symbol(struct config *cfg, byte *c)
  * existing symbol is found.
  */
 struct symbol *
-cf_get_symbol(byte *c)
+cf_get_symbol(const byte *c)
 {
-  return cf_get_sym(c, cf_hash(c));
+  return cf_find_symbol(new_config, c) ?: cf_new_symbol(c);
+}
+
+/**
+ * cf_localize_symbol - get the local instance of given symbol
+ * @sym: the symbol to localize
+ *
+ * This functions finds the symbol that is local to current scope
+ * for purposes of cf_define_symbol().
+ */
+struct symbol *
+cf_localize_symbol(struct symbol *sym)
+{
+  /* If the symbol type is void, it has been recently allocated just in this scope. */
+  if (!sym->class)
+    return sym;
+  
+  /* If the scope is the current, it is already defined in this scope. */
+  if (sym->scope == conf_this_scope)
+    cf_error("Symbol already defined");
+
+  /* Not allocated here yet, doing it now. */
+  return cf_new_symbol(sym->name);
 }
 
 struct symbol *
@@ -508,7 +642,7 @@ cf_default_name(char *template, int *counter)
   for(;;)
     {
       bsprintf(buf, template, ++(*counter));
-      s = cf_get_sym(buf, cf_hash(buf));
+      s = cf_get_symbol(buf);
       if (s->class == SYM_VOID)
        return s;
       if (!perc)
@@ -517,52 +651,48 @@ cf_default_name(char *template, int *counter)
   cf_error("Unable to generate default name");
 }
 
-/**
- * cf_define_symbol - define meaning of a symbol
- * @sym: symbol to be defined
- * @type: symbol class to assign
- * @def: class dependent data
- *
- * 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.
- */
-struct symbol *
-cf_define_symbol(struct symbol *sym, int type, void *def)
+static enum yytokentype
+cf_lex_symbol(const char *data)
 {
-  if (sym->class)
+  /* Have we defined such a symbol? */
+  struct symbol *sym = cf_get_symbol(data);
+  cf_lval.s = sym;
+
+  if (sym->class != SYM_VOID)
+    return CF_SYM_KNOWN;
+
+  /* Is it a keyword? */
+  struct keyword *k = HASH_FIND(kw_hash, KW, data);
+  if (k)
+  {
+    if (k->value > 0)
+      return k->value;
+    else
     {
-      if (sym->scope == conf_this_scope)
-       cf_error("Symbol already defined");
-      sym = cf_new_sym(sym->name, cf_hash(sym->name));
+      cf_lval.i = -k->value;
+      return ENUM;
     }
-  sym->class = type;
-  sym->def = def;
-  return sym;
+  }
+
+  /* OK, undefined symbol */
+  cf_lval.s = sym;
+  return CF_SYM_UNDEFINED;
 }
 
 static void
 cf_lex_init_kh(void)
 {
-  struct keyword *k;
+  HASH_INIT(kw_hash, &root_pool, KW_ORDER);
 
-  for(k=keyword_list; k->name; k++)
-    {
-      unsigned h = cf_hash(k->name) & (KW_HASH_SIZE-1);
-      k->next = kw_hash[h];
-      kw_hash[h] = k;
-    }
-  kw_hash_inited = 1;
+  struct keyword *k;
+  for (k=keyword_list; k->name; k++)
+    HASH_INSERT(kw_hash, KW, k);
 }
 
 /**
  * cf_lex_init - initialize the lexer
  * @is_cli: true if we're going to parse CLI command, false for configuration
+ * @c: configuration structure
  *
  * cf_lex_init() initializes the lexical analyzer and prepares it for
  * parsing of a new input.
@@ -570,11 +700,11 @@ cf_lex_init_kh(void)
 void
 cf_lex_init(int is_cli, struct config *c)
 {
-  if (!kw_hash_inited)
+  if (!kw_hash.data)
     cf_lex_init_kh();
 
   ifs_head = ifs = push_ifs(NULL);
-  if (!is_cli) 
+  if (!is_cli)
     {
       ifs->file_name = c->file_name;
       ifs->fd = c->file_fd;
@@ -589,7 +719,8 @@ cf_lex_init(int is_cli, struct config *c)
   else
     BEGIN(INITIAL);
 
-  conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
+  c->root_scope = cfg_allocz(sizeof(struct sym_scope));
+  conf_this_scope = c->root_scope;
   conf_this_scope->active = 1;
 }
 
@@ -612,6 +743,7 @@ cf_push_scope(struct symbol *sym)
   conf_this_scope = s;
   s->active = 1;
   s->name = sym;
+  s->slots = 0;
 }
 
 /**
@@ -626,25 +758,8 @@ cf_pop_scope(void)
 {
   conf_this_scope->active = 0;
   conf_this_scope = conf_this_scope->next;
-  ASSERT(conf_this_scope);
-}
 
-struct symbol *
-cf_walk_symbols(struct config *cf, struct symbol *sym, int *pos)
-{
-  for(;;)
-    {
-      if (!sym)
-       {
-         if (*pos >= SYM_HASH_SIZE)
-           return NULL;
-         sym = cf->sym_hash[(*pos)++];
-       }
-      else
-       sym = sym->next;
-      if (sym && sym->scope->active)
-       return sym;
-    }
+  ASSERT(conf_this_scope);
 }
 
 /**
@@ -657,9 +772,6 @@ cf_walk_symbols(struct config *cf, struct symbol *sym, int *pos)
 char *
 cf_symbol_class_name(struct symbol *sym)
 {
-  if (cf_symbol_is_constant(sym))
-    return "constant";
-
   switch (sym->class)
     {
     case SYM_VOID:
@@ -674,8 +786,12 @@ cf_symbol_class_name(struct symbol *sym)
       return "filter";
     case SYM_TABLE:
       return "routing table";
-    case SYM_ROA:
-      return "ROA table";
+    case SYM_ATTRIBUTE:
+      return "custom attribute";
+    case SYM_CONSTANT_RANGE:
+      return "constant";
+    case SYM_VARIABLE_RANGE:
+      return "variable";
     default:
       return "unknown type";
     }