]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge commit '2e5bfeb73ac25e236a24b6c1a88d0f2221ca303f' into thread-next
authorMaria Matejka <mq@ucw.cz>
Wed, 13 Jul 2022 12:14:37 +0000 (14:14 +0200)
committerMaria Matejka <mq@ucw.cz>
Wed, 13 Jul 2022 12:14:37 +0000 (14:14 +0200)
19 files changed:
1  2 
conf/cf-lex.l
conf/conf.h
conf/confbase.Y
doc/bird.sgml
filter/config.Y
filter/data.c
filter/data.h
filter/decl.m4
filter/f-inst.c
filter/f-inst.h
filter/f-util.c
filter/filter.c
filter/test.conf
lib/a-path.c
lib/a-path_test.c
lib/a-set.c
lib/attrs.h
nest/config.Y
sysdep/unix/main.c

diff --cc conf/cf-lex.l
Simple merge
diff --cc conf/conf.h
index 4ccaa54e923c8d34352bc6a78027b1089a92bae1,e0f94b63652085b70c3e358ed9dbeef850d99369..8b2e2dea5c34b3f874b7e8859a57bff205f5d254
@@@ -132,15 -133,11 +132,16 @@@ struct symbol 
  struct sym_scope {
    struct sym_scope *next;             /* Next on scope stack */
    struct symbol *name;                        /* Name of this scope */
 +
 +  HASH(struct symbol) hash;           /* Local symbol hash */
 +
    uint slots;                         /* Variable slots */
-   int active;                         /* Currently entered */
+   byte active;                                /* Currently entered */
+   byte soft_scopes;                   /* Number of soft scopes above */
  };
  
 +extern struct sym_scope *global_root_scope;
 +
  struct bytestring {
    size_t length;
    byte data[];
diff --cc conf/confbase.Y
index a603153c1489427ea1d9852a092805c9e0955fc2,1d5738ff06d1f917cc79c93cf73cec5fe2e4c45a..241c332d87d5d69fd79e51fafe2a03a6dbccffbf
@@@ -71,11 -71,11 +71,12 @@@ CF_DECL
    } xp;
    enum filter_return fret;
    enum ec_subtype ecs;
 -  struct f_dynamic_attr fda;
 +  struct ea_class *ea_class;
    struct f_static_attr fsa;
 +  struct f_attr_bit fab;
    struct f_lval flv;
    struct f_line *fl;
+   struct f_arg *fa;
    const struct filter *f;
    struct f_tree *e;
    struct f_trie *trie;
@@@ -153,16 -152,16 +154,16 @@@ conf: definition 
  
  definition:
     DEFINE symbol '=' term ';' {
 -     struct f_val *val = cfg_allocz(sizeof(struct f_val));
 -     if (f_eval(f_linearize($4, 1), cfg_mem, val) > F_RETURN) cf_error("Runtime error");
 -     cf_define_symbol($2, SYM_CONSTANT | val->type, val, val);
 +     struct f_val val;
-      if (f_eval(f_linearize($4), &val) > F_RETURN) cf_error("Runtime error");
++     if (f_eval(f_linearize($4, 1), &val) > F_RETURN) cf_error("Runtime error");
 +     cf_define_symbol($2, SYM_CONSTANT | val.type, val, lp_val_copy(cfg_mem, &val));
     }
   ;
  
  expr:
     NUM
-  | '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
+  | '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
 - | CF_SYM_KNOWN {
 + | symbol_known {
       if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
       $$ = SYM_VAL($1).i; }
   ;
diff --cc doc/bird.sgml
Simple merge
diff --cc filter/config.Y
index 8cecf9361442be23b36ce5e5c1c5d9f7908553ed,f256aacea9516fb1b8ead16a1a833347f47396ad..5ba4f7e658f19c153f92e5fe62c790e34d02f44e
@@@ -22,16 -22,36 +22,46 @@@ static inline u32 pair_b(u32 p) { retur
  #define f_generate_complex(fi_code, da, arg) \
    f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
  
 +#define f_generate_complex_sym(fi_code, sym, arg) ({ \
 +  if (sym->class != SYM_ATTRIBUTE) \
 +    cf_error("Can't empty %s: not an attribute", sym->name); \
 +  f_generate_complex(fi_code, sym->attribute, arg); \
 +})
 +
 +#define f_generate_complex_default(fi_code, da, arg, def) \
 +  f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_DEFAULT, f_new_inst(FI_EA_GET, da), f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = def })), arg), da)
 +  
 +
+ static int
+ f_new_var(struct sym_scope *s)
+ {
+   /*
+    * - A variable is an offset on vstack from vbase.
+    * - Vbase is set on filter start / function call.
+    * - Scopes contain anonymous scopes (blocks) inside filter/function scope
+    * - Each scope knows number of vars in that scope
+    * - Offset is therefore a sum of 'slots' up to named scope
+    * - New variables are added on top of vstk, so intermediate values cannot
+    *   be there during FI_VAR_INIT. I.e. no 'var' inside 'term'.
+    * - Also, each f_line must always have its scope, otherwise a variable may
+    *   be defined but not initialized if relevant f_line is not executed.
+    */
+   int offset = s->slots++;
+   while (!s->name)
+   {
+     s = s->next;
+     ASSERT(s);
+     offset += s->slots;
+   }
+   if (offset >= 0xff)
+     cf_error("Too many variables, at most 255 allowed");
+   return offset;
+ }
  /*
   * Sets and their items are during parsing handled as lists, linked
   * through left ptr. The first item in a list also contains a pointer
@@@ -287,8 -303,10 +317,9 @@@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UN
        INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
        SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
        IF, THEN, ELSE, CASE,
+       FOR, IN, DO,
        TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
 -      FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
 -      PREFERENCE,
 +      FROM, GW, NET, MASK, PROTO, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
        ROA_CHECK, ASN, SRC, DST,
        IS_V4, IS_V6,
        LEN, MAXLEN,
  %nonassoc ELSE
  
  %type <xp> cmds_int cmd_prep
- %type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
+ %type <x> term cmd cmd_var cmds cmds_scoped constant constructor print_list var var_init var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
 -%type <fda> dynamic_attr
  %type <fsa> static_attr
 +%type <fab> attr_bit
  %type <f> filter where_filter
  %type <fl> filter_body function_body
  %type <flv> lvalue
@@@ -549,10 -575,10 +595,10 @@@ set_atom
   | VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
   | ENUM   { $$.type = pair_a($1); $$.val.i = pair_b($1); }
   | '(' term ')' {
-      if (f_eval(f_linearize($2), &($$)) > F_RETURN) cf_error("Runtime error");
 -     if (f_eval(f_linearize($2, 1), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error");
++     if (f_eval(f_linearize($2, 1), &($$)) > F_RETURN) cf_error("Runtime error");
       if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
     }
 - | CF_SYM_KNOWN {
 + | symbol_known {
       cf_assert_symbol($1, SYM_CONSTANT);
       if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
       $$ = *$1->val;
@@@ -722,7 -750,8 +770,8 @@@ var_list: /* EMPTY */ { $$ = NULL; 
   | var_list ',' term { $$ = $3; $$->next = $1; }
  
  function_call:
-    symbol_known '(' var_list ')' {
 -   CF_SYM_KNOWN '(' var_list ')'
++   symbol_known '(' var_list ')'
+    {
       if ($1->class != SYM_FUNCTION)
         cf_error("You can't call something which is not a function. Really.");
  
@@@ -866,14 -891,45 +909,45 @@@ print_list: /* EMPTY */ { $$ = NULL; 
     }
   ;
  
+ var_init:
+    /* empty */ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { }); }
+  | '=' term { $$ = $2; }
+  ;
+ var:
+    type symbol var_init ';' {
+      struct symbol *sym = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
+      $$ = f_new_inst(FI_VAR_INIT, $3, sym);
+    }
+ for_var:
+    type symbol { $$ = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
+  | CF_SYM_KNOWN { $$ = $1; cf_assert_symbol($1, SYM_VARIABLE); }
+  ;
  cmd:
-    IF term THEN block {
+    '{' cmds_scoped '}' {
+      $$ = $2;
+    }
+  | IF term THEN cmd {
       $$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
     }
-  | IF term THEN block ELSE block {
+  | IF term THEN cmd ELSE cmd {
       $$ = f_new_inst(FI_CONDITION, $2, $4, $6);
     }
 - | CF_SYM_KNOWN '=' term ';' {
+  | FOR {
+      /* Reserve space for walk data on stack */
+      cf_push_scope(NULL);
+      conf_this_scope->slots += 2;
+    } for_var IN
+    /* Parse term in the parent scope */
+    { conf_this_scope->active = 0; } term { conf_this_scope->active = 1; }
+    DO cmd {
+      cf_pop_scope();
+      $$ = f_new_inst(FI_FOR_INIT, $6, $3);
+      $$->next = f_new_inst(FI_FOR_NEXT, $3, $9);
+    }
 + | symbol_known '=' term ';' {
       switch ($1->class) {
         case SYM_VARIABLE_RANGE:
         $$ = f_new_inst(FI_VAR_SET, $3, $1);
diff --cc filter/data.c
index 9dab19153898b3aa0373f5ba350ed819cc358e49,87c438fcf9d1f07d5d21b3468a56202b7b0afd48..d26b07f540f7c9358c8c0091a86c69c52adee8b4
@@@ -62,15 -57,22 +62,16 @@@ static const char * const f_type_str[] 
  };
  
  const char *
 -f_type_name(enum f_type t)
 +f_type_name(btype t)
  {
 -  if (t < ARRAY_SIZE(f_type_str))
 -    return f_type_str[t] ?: "?";
 -
 -  if ((t == T_SET) || (t == T_PREFIX_SET))
 -    return "set";
 -
 -  return "?";
 +  return (t < ARRAY_SIZE(f_type_str)) ? (f_type_str[t] ?: "?") : "?";
  }
  
 -enum f_type
 -f_type_element_type(enum f_type t)
 +btype
 +f_type_element_type(btype t)
  {
    switch(t) {
+     case T_PATH:   return T_INT;
      case T_CLIST:  return T_PAIR;
      case T_ECLIST: return T_EC;
      case T_LCLIST: return T_LC;
@@@ -90,8 -94,19 +93,11 @@@ const struct f_val f_const_empty_path 
  }, f_const_empty_lclist = {
    .type = T_LCLIST,
    .val.ad = &null_adata,
+ }, f_const_empty_prefix_set = {
+   .type = T_PREFIX_SET,
+   .val.ti = &f_const_empty_trie,
  };
  
 -static struct adata *
 -adata_empty(struct linpool *pool, int l)
 -{
 -  struct adata *res = lp_alloc(pool, sizeof(struct adata) + l);
 -  res->length = l;
 -  return res;
 -}
 -
  static void
  pm_format(const struct f_path_mask *p, buffer *buf)
  {
diff --cc filter/data.h
index 23db4a85a160ecba53f4c30947cb1b375cf68525,c96856ce3d56e2b8681bd4c45494a3f2a8218c1d..c1e7c736cc3e5e4eb37d07ebe8d2b121c46e49e5
@@@ -227,18 -300,8 +229,18 @@@ undef_value(struct f_val v
      (v.val.ad == &null_adata);
  }
  
- extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist;
+ extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist, f_const_empty_prefix_set;
 +static inline const struct f_val *f_get_empty(btype t)
 +{
 +  switch (t) {
 +    case T_PATH:      return &f_const_empty_path;
 +    case T_CLIST:     return &f_const_empty_clist;
 +    case T_ECLIST:    return &f_const_empty_eclist;
 +    case T_LCLIST:    return &f_const_empty_lclist;
 +    default:          return NULL;
 +  }
 +}
  
 -enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres);
 +enum filter_return f_eval(const struct f_line *expr, struct f_val *pres);
  
  #endif
diff --cc filter/decl.m4
index c59cd7f3b2258532855c1d1f7b0c438f69057795,fc3808df1078ff76d438e0bb670022c344ad309c..e24721272d48ced1aec70bd23ba6c89fe757a00a
@@@ -640,7 -660,8 +660,8 @@@ FID_WR_PUT(4)m4_dn
  struct f_inst {
    struct f_inst *next;                        /* Next instruction */
    enum f_instruction_code fi_code;    /* Instruction code */
 -  enum f_type type;                   /* Type of returned value, if known */
+   enum f_instruction_flags flags;     /* Flags, instruction-specific */
 +  btype type;                         /* Type of returned value, if known */
    int size;                           /* How many instructions are underneath */
    int lineno;                         /* Line number */
    union {
diff --cc filter/f-inst.c
index e7b642abfff422fbaac59dbcfec6ad846851bd3f,0fb04ba797b4a29b5dbbd577d765b2aeb2c9bdb8..fff93517940a9f94302f348a9d871eec51a678a9
      RESULT_VAL(val);
    }
  
 -      enum f_type t_var = (sym->class & 0xff);
 -      enum f_type t_arg = f_type_element_type(f1->type);
+   INST(FI_FOR_INIT, 1, 0) {
+     NEVER_CONSTANT;
+     ARG_ANY(1);
+     SYMBOL;
+     FID_NEW_BODY()
+     ASSERT((sym->class & ~0xff) == SYM_VARIABLE);
+     /* Static type check */
+     if (f1->type)
+     {
++      enum btype t_var = (sym->class & 0xff);
++      enum btype t_arg = f_type_element_type(f1->type);
+       if (!t_arg)
+         cf_error("Value of expression in FOR must be iterable, got %s",
+                f_type_name(f1->type));
+       if (t_var != t_arg)
+       cf_error("Loop variable '%s' in FOR must be %s, is %s",
+                sym->name, f_type_name(t_arg), f_type_name(t_var));
+     }
+     FID_INTERPRET_BODY()
+     /* Dynamic type check */
+     if ((sym->class & 0xff) != f_type_element_type(v1.type))
+       runtime("Mismatched argument and variable type");
+     /* Setup the index */
+     v2 = (struct f_val) { .type = T_INT, .val.i = 0 };
+     /* Keep v1 and v2 on the stack */
+     fstk->vcnt += 2;
+   }
+   INST(FI_FOR_NEXT, 2, 0) {
+     NEVER_CONSTANT;
+     SYMBOL;
+     /* Type checks are done in FI_FOR_INIT */
+     /* Loop variable */
+     struct f_val *var = &fstk->vstk[curline.vbase + sym->offset];
+     int step = 0;
+     switch(v1.type)
+     {
+     case T_PATH:
+       var->type = T_INT;
+       step = as_path_walk(v1.val.ad, &v2.val.i, &var->val.i);
+       break;
+     case T_CLIST:
+       var->type = T_PAIR;
+       step = int_set_walk(v1.val.ad, &v2.val.i, &var->val.i);
+       break;
+     case T_ECLIST:
+       var->type = T_EC;
+       step = ec_set_walk(v1.val.ad, &v2.val.i, &var->val.ec);
+       break;
+     case T_LCLIST:
+       var->type = T_LC;
+       step = lc_set_walk(v1.val.ad, &v2.val.i, &var->val.lc);
+       break;
+     default:
+       runtime( "Clist or lclist expected" );
+     }
+     if (step)
+     {
+       /* Keep v1 and v2 on the stack */
+       fstk->vcnt += 2;
+       /* Repeat this instruction */
+       curline.pos--;
+       /* Execute the loop body */
+       LINE(1, 0);
+       /* Space for loop variable, may be unused */
+       fstk->vcnt += 1;
+     }
+     else
+       var->type = T_VOID;
+   }
    INST(FI_CONDITION, 1, 0) {
      ARG(1, T_BOOL);
      if (v1.val.i)
      ACCESS_RTE;
      ACCESS_EATTRS;
  
 -    f_rta_cow(fs);
 -    ea_unset_attr(fs->eattrs, fs->pool, 1, da.ea_code);
 +    ea_unset_attr(fs->eattrs, 1, da);
 +  }
 +
 +  INST(FI_DEFAULT, 2, 1) {
 +    ARG_ANY(1);
 +    ARG_ANY(2);
++    RESULT_TYPE(f_type_element_type(v2.type));
 +
 +    log(L_INFO "Type of arg 1 is: %d", v1.type);
 +
 +    if (v1.type == T_VOID)
 +      RESULT_VAL(v2);
 +    else
 +      RESULT_VAL(v1);
    }
  
    INST(FI_LENGTH, 1, 1) {     /* Get length of */
  
    INST(FI_CALL, 0, 1) {
      NEVER_CONSTANT;
+     VARARG;
      SYMBOL;
  
 -      enum f_type b_type = b->arg->class & 0xff;
+     /* Fake result type declaration */
+     RESULT_TYPE(T_VOID);
+     FID_NEW_BODY()
+     ASSERT(sym->class == SYM_FUNCTION);
+     if (whati->varcount != sym->function->args)
+       cf_error("Function '%s' expects %u arguments, got %u arguments",
+              sym->name, sym->function->args, whati->varcount);
+     /* Typecheck individual arguments */
+     struct f_inst *a = fvar;
+     struct f_arg *b = sym->function->arg_list;
+     for (uint i = 1; a && b; a = a->next, b = b->next, i++)
+     {
++      enum btype b_type = b->arg->class & 0xff;
+       if (a->type && (a->type != b_type) && !f_const_promotion(a, b_type))
+       cf_error("Argument %u of '%s' must be %s, got %s",
+                i, sym->name, f_type_name(b_type), f_type_name(a->type));
+     }
+     ASSERT(!a && !b);
+     /* Add implicit void slot for the return value */
+     struct f_inst *tmp = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
+     tmp->next = whati->fvar;
+     whati->fvar = tmp;
+     what->size += tmp->size;
+     /* Mark recursive calls, they have dummy f_line */
+     if (!sym->function->len)
+       what->flags |= FIF_RECURSIVE;
      FID_SAME_BODY()
-       if (!(f1->sym->flags & SYM_FLAG_SAME))
-       return 0;
+     if (!(f1->sym->flags & SYM_FLAG_SAME) && !(f1_->flags & FIF_RECURSIVE))
+       return 0;
  
      FID_ITERATE_BODY()
+     if (!(what->flags & FIF_RECURSIVE))
        BUFFER_PUSH(fit->lines) = whati->sym->function;
  
      FID_INTERPRET_BODY()
diff --cc filter/f-inst.h
Simple merge
diff --cc filter/f-util.c
index fb93ee80c62fe4ee8c4dd24be29219167bac2424,fdb314b50bdc138e5165fa9452683eb15f5e8cb3..82a06bddc7fe51e20ad2336be46b2372ecdf1860
@@@ -37,6 -37,147 +37,6 @@@ struct filter *f_new_where(struct f_ins
                                   f_new_inst(FI_DIE, F_REJECT));
  
    struct filter *f = cfg_allocz(sizeof(struct filter));
-   f->root = f_linearize(cond);
+   f->root = f_linearize(cond, 0);
    return f;
  }
 -
 -#define CA_KEY(n)     n->name, n->fda.type
 -#define CA_NEXT(n)    n->next
 -#define CA_EQ(na,ta,nb,tb)    (!strcmp(na,nb) && (ta == tb))
 -#define CA_FN(n,t)    (mem_hash(n, strlen(n)) ^ (t*0xaae99453U))
 -#define CA_ORDER      8 /* Fixed */
 -
 -struct ca_storage {
 -  struct ca_storage *next;
 -  struct f_dynamic_attr fda;
 -  u32 uc;
 -  char name[0];
 -};
 -
 -HASH(struct ca_storage) ca_hash;
 -
 -static struct idm ca_idm;
 -static struct ca_storage **ca_storage;
 -static uint ca_storage_max;
 -
 -static void
 -ca_free(resource *r)
 -{
 -  struct custom_attribute *ca = (void *) r;
 -  struct ca_storage *cas = HASH_FIND(ca_hash, CA, ca->name, ca->fda->type);
 -  ASSERT(cas);
 -
 -  ca->name = NULL;
 -  ca->fda = NULL;
 -  if (!--cas->uc) {
 -    uint id = EA_CUSTOM_ID(cas->fda.ea_code);
 -    idm_free(&ca_idm, id);
 -    HASH_REMOVE(ca_hash, CA, cas);
 -    ca_storage[id] = NULL;
 -    mb_free(cas);
 -  }
 -}
 -
 -static void
 -ca_dump(resource *r)
 -{
 -  struct custom_attribute *ca = (void *) r;
 -  debug("name \"%s\" id 0x%04x ea_type 0x%02x f_type 0x%02x\n",
 -      ca->name, ca->fda->ea_code, ca->fda->type, ca->fda->f_type);
 -}
 -
 -static struct resclass ca_class = {
 -  .name = "Custom attribute",
 -  .size = sizeof(struct custom_attribute),
 -  .free = ca_free,
 -  .dump = ca_dump,
 -  .lookup = NULL,
 -  .memsize = NULL,
 -};
 -
 -struct custom_attribute *
 -ca_lookup(pool *p, const char *name, int f_type)
 -{
 -  int ea_type;
 -
 -  switch (f_type) {
 -    case T_INT:
 -      ea_type = EAF_TYPE_INT;
 -      break;
 -    case T_IP:
 -      ea_type = EAF_TYPE_IP_ADDRESS;
 -      break;
 -    case T_QUAD:
 -      ea_type = EAF_TYPE_ROUTER_ID;
 -      break;
 -    case T_PATH:
 -      ea_type = EAF_TYPE_AS_PATH;
 -      break;
 -    case T_CLIST:
 -      ea_type = EAF_TYPE_INT_SET;
 -      break;
 -    case T_ECLIST:
 -      ea_type = EAF_TYPE_EC_SET;
 -      break;
 -    case T_LCLIST:
 -      ea_type = EAF_TYPE_LC_SET;
 -      break;
 -    default:
 -      cf_error("Custom route attribute of unsupported type");
 -  }
 -
 -  static int inited = 0;
 -  if (!inited) {
 -    idm_init(&ca_idm, &root_pool, 8);
 -    HASH_INIT(ca_hash, &root_pool, CA_ORDER);
 -
 -    ca_storage_max = 256;
 -    ca_storage = mb_allocz(&root_pool, sizeof(struct ca_storage *) * ca_storage_max);
 -
 -    inited++;
 -  }
 -
 -  struct ca_storage *cas = HASH_FIND(ca_hash, CA, name, ea_type);
 -  if (cas) {
 -    cas->uc++;
 -  } else {
 -
 -    uint id = idm_alloc(&ca_idm);
 -
 -    if (id >= EA_CUSTOM_BIT)
 -      cf_error("Too many custom attributes.");
 -
 -    if (id >= ca_storage_max) {
 -      ca_storage_max *= 2;
 -      ca_storage = mb_realloc(ca_storage, sizeof(struct ca_storage *) * ca_storage_max * 2);
 -    }
 -
 -    cas = mb_allocz(&root_pool, sizeof(struct ca_storage) + strlen(name) + 1);
 -    cas->fda = f_new_dynamic_attr(ea_type, f_type, EA_CUSTOM(id));
 -    cas->uc = 1;
 -
 -    strcpy(cas->name, name);
 -    ca_storage[id] = cas;
 -
 -    HASH_INSERT(ca_hash, CA, cas);
 -  }
 -
 -  struct custom_attribute *ca = ralloc(p, &ca_class);
 -  ca->fda = &(cas->fda);
 -  ca->name = cas->name;
 -  return ca;
 -}
 -
 -const char *
 -ea_custom_name(uint ea)
 -{
 -  uint id = EA_CUSTOM_ID(ea);
 -  if (id >= ca_storage_max)
 -    return NULL;
 -
 -  if (!ca_storage[id])
 -    return NULL;
 -
 -  return ca_storage[id]->name;
 -}
 -
diff --cc filter/filter.c
Simple merge
Simple merge
diff --cc lib/a-path.c
Simple merge
index 38f77642398a5eccfe246772108af5870bd11c09,a0f3f0e3363dbf7c1be2ee3d6ded7a6a99d3d827..c6f8ce8b8fba9713d51b1eba02ea692f53f6b83c
@@@ -9,9 -9,10 +9,10 @@@
  #include "test/birdtest.h"
  #include "test/bt-utils.h"
  
 -#include "nest/route.h"
 -#include "nest/attrs.h"
 +#include "nest/rt.h"
 +#include "lib/attrs.h"
  #include "lib/resource.h"
+ #include "filter/data.h"
  
  #define TESTS_NUM 30
  #define AS_PATH_LENGTH 1000
diff --cc lib/a-set.c
Simple merge
diff --cc lib/attrs.h
Simple merge
diff --cc nest/config.Y
Simple merge
Simple merge