MINOR: sample: add a new "concat" converter
It's always a pain not to be able to combine variables. This commit
introduces the "concat" converter, which appends a delimiter, a variable's
contents and another delimiter to an existing string. The result is a string.
This makes it easier to build composite variables made of other variables.
diff --git a/src/sample.c b/src/sample.c
index 655e734..3e1a156 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -2531,6 +2531,83 @@
return 1;
}
+/* appends an optional const string, an optional variable contents and another
+ * optional const string to an existing string.
+ */
+static int sample_conv_concat(const struct arg *arg_p, struct sample *smp, void *private)
+{
+ struct chunk *trash;
+ struct sample tmp;
+ int max;
+
+ trash = get_trash_chunk();
+ trash->len = smp->data.u.str.len;
+ if (trash->len > trash->size - 1)
+ trash->len = trash->size - 1;
+
+ memcpy(trash->str, smp->data.u.str.str, trash->len);
+ trash->str[trash->len] = 0;
+
+ /* append first string */
+ max = arg_p[0].data.str.len;
+ if (max > trash->size - 1 - trash->len)
+ max = trash->size - 1 - trash->len;
+
+ if (max) {
+ memcpy(trash->str + trash->len, arg_p[0].data.str.str, max);
+ trash->len += max;
+ trash->str[trash->len] = 0;
+ }
+
+ /* append second string (variable) if it's found and we can turn it
+ * into a string.
+ */
+ smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt);
+ if (arg_p[1].type == ARGT_VAR && vars_get_by_desc(&arg_p[1].data.var, &tmp) &&
+ (sample_casts[tmp.data.type][SMP_T_STR] == c_none ||
+ sample_casts[tmp.data.type][SMP_T_STR](&tmp))) {
+
+ max = tmp.data.u.str.len;
+ if (max > trash->size - 1 - trash->len)
+ max = trash->size - 1 - trash->len;
+
+ if (max) {
+ memcpy(trash->str + trash->len, tmp.data.u.str.str, max);
+ trash->len += max;
+ trash->str[trash->len] = 0;
+ }
+ }
+
+ /* append third string */
+ max = arg_p[2].data.str.len;
+ if (max > trash->size - 1 - trash->len)
+ max = trash->size - 1 - trash->len;
+
+ if (max) {
+ memcpy(trash->str + trash->len, arg_p[2].data.str.str, max);
+ trash->len += max;
+ trash->str[trash->len] = 0;
+ }
+
+ smp->data.u.str = *trash;
+ smp->data.type = SMP_T_STR;
+ return 1;
+}
+
+/* This function checks the "concat" converter's arguments and extracts the
+ * variable name and its scope.
+ */
+static int smp_check_concat(struct arg *args, struct sample_conv *conv,
+ const char *file, int line, char **err)
+{
+ /* Try to decode a variable. */
+ if (args[1].data.str.len > 0 && !vars_check_arg(&args[1], NULL)) {
+ memprintf(err, "failed to register variable name '%s'", args[1].data.str.str);
+ return 0;
+ }
+ return 1;
+}
+
/************************************************************************/
/* All supported sample fetch functions must be declared here */
/************************************************************************/
@@ -2539,6 +2616,9 @@
static int
smp_fetch_true(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
+ if (!smp_make_rw(smp))
+ return 0;
+
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = 1;
return 1;
@@ -2841,6 +2921,7 @@
{ "word", sample_conv_word, ARG2(2,SINT,STR), sample_conv_field_check, SMP_T_STR, SMP_T_STR },
{ "regsub", sample_conv_regsub, ARG3(2,REG,STR,STR), sample_conv_regsub_check, SMP_T_STR, SMP_T_STR },
{ "sha1", sample_conv_sha1, 0, NULL, SMP_T_BIN, SMP_T_BIN },
+ { "concat", sample_conv_concat, ARG3(1,STR,STR,STR), smp_check_concat, SMP_T_STR, SMP_T_STR },
{ "and", sample_conv_binary_and, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },
{ "or", sample_conv_binary_or, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT },