]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Filter: Add 'append' operator oz-append
authorOndrej Zajicek <santiago@crfreenet.org>
Tue, 3 Jun 2025 02:04:47 +0000 (04:04 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Tue, 1 Jul 2025 13:59:15 +0000 (15:59 +0200)
We need append() operator to concatenate strings / bytestrings.
Can be also written as '++', (e.g. "abcd" ++ "1234").

conf/cf-lex.l
conf/confbase.Y
doc/bird.sgml
filter/config.Y
filter/data.c
filter/data.h
filter/f-inst.c
filter/test.conf
lib/string.h

index 0119e1f55435ef40c23d66db082768cfd154fc9a..f13b1c12be593a6f7c784687a30efe8f890f40fb 100644 (file)
@@ -380,6 +380,7 @@ else: {
 \&\& return AND;
 \|\| return OR;
 \-\> return IMP;
+\+\+ return PP;
 
 \[\= return PO;
 \=\] return PC;
index b2ba3cafbfbb65a79727bfb2643c9a3536899fbe..27c422eabd53d5fd0170deedbc4668134d279928 100644 (file)
@@ -130,7 +130,7 @@ CF_DECLS
 }
 
 %token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
-%token GEQ LEQ NEQ AND OR IMP
+%token GEQ LEQ NEQ AND OR IMP PP
 %token PO PC
 %token <i> NUM ENUM_TOKEN
 %token <ip4> IP4
@@ -158,7 +158,7 @@ CF_DECLS
 %nonassoc PREFIX_DUMMY
 %left AND OR
 %nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA IMP PO PC
-%left '+' '-'
+%left '+' '-' PP
 %left '*' '/' '%'
 %left '!'
 %nonassoc '.'
index 5f91a8fd48bb023391e928e3ad731444c9799818..d981936afc38cd4fd4287d076f75d729cffd2849 100644 (file)
@@ -1613,17 +1613,21 @@ in the foot).
        This is a string of characters. There are no ways to modify strings in
        filters. You can pass them between functions, assign them to variables
        of type <cf/string/, print such variables, use standard string
-       comparison operations (e.g. <cf/=, !=, &lt;, &gt;, &lt;=, &gt;=/), but
-       you can't concatenate two strings. String literals are written as
-       <cf/"This is a string constant"/. Additionally matching (<cf/&tilde;,
-       !&tilde;/) operators could be used to match a string value against
-       a shell pattern (represented also as a string).
+       comparison operations (e.g. <cf/=, !=, &lt;, &gt;, &lt;=, &gt;=/), and
+       concatenate two strings with <cf>append(<m/A/, <m/B/)</cf> function or
+       <cf/++/ operator.
+
+       String literals are written as <cf/"This is a string constant"/.
+       Additionally matching (<cf/&tilde;, !&tilde;/) operators could be used
+       to match a string value against a shell pattern (represented also as a
+       string).
 
        <tag><label id="type-bytestring">bytestring</tag>
        This is a sequence of arbitrary bytes. There are no ways to modify
        bytestrings in filters. You can pass them between functions, assign
-       them to variables of type <cf/bytestring/, print such values, and
-       compare bytestings (<cf/=, !=/).
+       them to variables of type <cf/bytestring/, print such values, compare
+       bytestings (<cf/=, !=/), and concatenate two bytestrings with
+       <cf>append(<m/A/, <m/B/)</cf> function or <cf/++/ operator.
 
        Bytestring literals are written as a sequence of hexadecimal digit
        pairs, optionally colon-separated. A bytestring specified this way
@@ -1920,6 +1924,8 @@ parentheses <cf/(a*(b+c))/, comparison <cf/(a=b, a!=b, a&lt;b, a&gt;=b)/.</p>
 <p>Logical operations include unary not (<cf/!/), and (<cf/&amp;&amp;/), and or
 (<cf/&verbar;&verbar;/).</p>
 
+<p>Strings and bytestrings can be concatenated with <cf/++/ operator.</p>
+
 <p>Special operators include (<cf/&tilde;/, <cf/!&tilde;/) for "is (not) element
 of a set" operation - it can be used on:
 <itemize>
index 562530e2d154e84db07f88ecedc7b0a80185e484..d4ffa204346e8b892ec176997612e4818ffc130e 100644 (file)
@@ -368,6 +368,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
        DEFINED,
        ADD, DELETE, RESET,
        PREPEND,
+       APPEND,
        EMPTY,
        FILTER, WHERE, EVAL, ATTRIBUTE,
        FROM_HEX,
@@ -950,10 +951,13 @@ term:
  | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_LCLIST)); }
 
  | PREPEND '(' term ',' term ')' { $$ = f_dispatch_method_x("prepend", $3->type, $3, $5); }
+ | APPEND '(' term ',' term ')' { $$ = f_dispatch_method_x("append", $3->type, $3, $5); }
  | ADD '(' term ',' term ')' { $$ = f_dispatch_method_x("add", $3->type, $3, $5); }
  | DELETE '(' term ',' term ')' { $$ = f_dispatch_method_x("delete", $3->type, $3, $5); }
  | FILTER '(' term ',' term ')' { $$ = f_dispatch_method_x("filter", $3->type, $3, $5); }
 
+ | term PP term { $$ = f_dispatch_method_x("append", $1->type, $1, $3); }
+
  | ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
  | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
  | ASPA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ASPA_CHECK_EXPLICIT, $5, $7, $3); }
index f5a9e5eecf5263290702f6a34179c59693339c7e..9b052f894a5d5b95901e22eeac4ed5422c6e5b17 100644 (file)
@@ -668,3 +668,15 @@ val_dump(const struct f_val *v) {
   val_format(v, &b);
   return val_dump_buffer;
 }
+
+const struct adata *
+bytestring_append(struct linpool *pool, const struct adata *v1, const struct adata *v2)
+{
+  if (!v1 || !v2)
+    return v1 ?: v2;
+
+  struct adata *res = lp_alloc_adata(pool, v1->length + v2->length);
+  memcpy(res->data, v1->data, v1->length);
+  memcpy(res->data + v1->length, v2->data, v2->length);
+  return res;
+}
index f36820cba4d20ab4a82449c4e99afe276e62ca60..82871daf21d767780b2c4462d6d28665b5709125 100644 (file)
@@ -325,6 +325,8 @@ const struct adata *clist_filter(struct linpool *pool, const struct adata *list,
 const struct adata *eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
 const struct adata *lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
 
+const struct adata *bytestring_append(struct linpool *pool, const struct adata *v1, const struct adata *v2);
+
 
 /* Special undef value for paths and clists */
 
index f94580a662e7acaf5868b7730e2b1d0645050231..234a3610344add71ab8c6a3316dff01a02ccf941 100644 (file)
     RESULT(T_PATH, ad, [[ as_path_prepend(fpool, v1.val.ad, v2.val.i) ]]);
   }
 
+  /* String append */
+  INST(FI_STRING_APPEND, 2, 1) {
+    ARG(1, T_STRING);
+    ARG(2, T_STRING);
+    METHOD_CONSTRUCTOR("append");
+    RESULT(T_STRING, s, [[ lp_strcat(fpool, v1.val.s, v2.val.s) ]]);
+  }
+
+  /* Bytestring append */
+  INST(FI_BYTESTRING_APPEND, 2, 1) {
+    ARG(1, T_BYTESTRING);
+    ARG(2, T_BYTESTRING);
+    METHOD_CONSTRUCTOR("append");
+    RESULT(T_BYTESTRING, ad, [[ bytestring_append(fpool, v1.val.ad, v2.val.ad) ]]);
+  }
+
   /* Community list add */
   INST(FI_CLIST_ADD_PAIR, 2, 1) {
     ARG(1, T_CLIST);
index 3df9b6975262d2ed905f5f5de6204f00f9d21fb6..0540b111a4a84c19d24d75fc5715ea0fc3086f1c 100644 (file)
@@ -241,8 +241,8 @@ bt_test_suite(t_int_set, "Testing sets of integers");
 
 
 /*
- *     Testing string matching
- *     -----------------------
+ *     Testing strings
+ *     ---------------
  */
 
 function t_string()
@@ -254,9 +254,13 @@ function t_string()
        bt_assert(st ~ "Hello");
        bt_assert(st ~ "Hell?");
        bt_assert(st !~ "ell*");
+
+       bt_assert("abcd".append(format(1234)) = "abcd1234");
+       bt_assert(append("He", "llo") = st);
+       bt_assert("Hello" ++ " " ++ "world" = "Hello world");
 }
 
-bt_test_suite(t_string, "Testing string matching");
+bt_test_suite(t_string, "Testing strings");
 
 
 
@@ -284,6 +288,11 @@ function t_bytestring()
        bt_assert(from_hex("01:12:23:34:45:56:67:78:89:9a:ab:bc:cd:de:ef:f0") = bs2);
        bt_assert(from_hex(format(bs2)) = bs2);
        bt_assert(from_hex(" 0112:23-34455667 78-89 - 9a-ab bc:cd : de:eff0 ") = bs2);
+
+       bt_assert(hex:6778899a.append(hex:01122334) = hex:6778899a01122334);
+       bt_assert(append(hex:01122334, hex:6778899a) = hex:011223346778899a);
+       bt_assert(hex:01:12:23:34:45:56 ++ hex:67:78:89:9a:ab ++ hex:bc:cd:de:ef:f0 = bs2);
+
 }
 
 bt_test_suite(t_bytestring, "Testing bytestrings");
index 8831666c7d73bc158dca08b161e1a72da30cf47f..0107c5949e5103fb7af00077bb7cef662969f86e 100644 (file)
@@ -66,6 +66,17 @@ lp_strdup(linpool *lp, const char *c)
   return z;
 }
 
+static inline char *
+lp_strcat(linpool *lp, const char *s1, const char *s2)
+{
+  size_t l1 = strlen(s1);
+  size_t l2 = strlen(s2);
+  char *z = lp_allocu(lp, l1 + l2 + 1);
+  memcpy(z, s1, l1);
+  memcpy(z + l1, s2, l2 + 1);
+  return z;
+}
+
 static inline void
 memset32(void *D, u32 val, uint n)
 {