We need append() operator to concatenate strings / bytestrings.
Can be also written as '++', (e.g. "abcd" ++ "1234").
\&\& return AND;
\|\| return OR;
\-\> return IMP;
+\+\+ return PP;
\[\= return PO;
\=\] return PC;
}
%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
%nonassoc PREFIX_DUMMY
%left AND OR
%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA IMP PO PC
-%left '+' '-'
+%left '+' '-' PP
%left '*' '/' '%'
%left '!'
%nonassoc '.'
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/=, !=, <, >, <=, >=/), but
- you can't concatenate two strings. String literals are written as
- <cf/"This is a string constant"/. Additionally matching (<cf/˜,
- !˜/) operators could be used to match a string value against
- a shell pattern (represented also as a string).
+ comparison operations (e.g. <cf/=, !=, <, >, <=, >=/), 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/˜, !˜/) 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
<p>Logical operations include unary not (<cf/!/), and (<cf/&&/), and or
(<cf/||/).</p>
+<p>Strings and bytestrings can be concatenated with <cf/++/ operator.</p>
+
<p>Special operators include (<cf/˜/, <cf/!˜/) for "is (not) element
of a set" operation - it can be used on:
<itemize>
DEFINED,
ADD, DELETE, RESET,
PREPEND,
+ APPEND,
EMPTY,
FILTER, WHERE, EVAL, ATTRIBUTE,
FROM_HEX,
| '-' '-' '-' 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); }
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;
+}
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 */
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);
/*
- * Testing string matching
- * -----------------------
+ * Testing strings
+ * ---------------
*/
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");
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");
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)
{