]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add %substr() xlat with tests
authorNick Porter <nick@portercomputing.co.uk>
Tue, 20 Feb 2024 16:40:47 +0000 (16:40 +0000)
committerNick Porter <nick@portercomputing.co.uk>
Tue, 20 Feb 2024 16:40:47 +0000 (16:40 +0000)
src/lib/unlang/xlat_builtin.c
src/tests/keywords/substr [new file with mode: 0644]

index 87bcfb688f08fa7c4c7f43065dd6dbf9f0b155dc..816c73310c13df460f890d4222eced95a3a7df69 100644 (file)
@@ -2966,6 +2966,82 @@ static xlat_action_t xlat_func_strlen(TALLOC_CTX *ctx, fr_dcursor_t *out,
        return XLAT_ACTION_DONE;
 }
 
+static xlat_arg_parser_t const xlat_func_substr_args[] = {
+       { .single = true, .required = true, .type = FR_TYPE_VOID },
+       { .single = true, .required = true, .type = FR_TYPE_INT32 },
+       { .single = true, .type = FR_TYPE_INT32 },
+       XLAT_ARG_PARSER_TERMINATOR
+};
+
+/** Extract a substring from string / octets data
+ *
+ * Second parameter is start position, optional third parameter is length
+ * Negative start / length count from RHS of data.
+ *
+ * Example: (User-Name = "hello")
+@verbatim
+%substr(&User-Name, 1, 3) == 'ell'
+@endverbatim
+ *
+ * @ingroup xlat_functions
+ */
+static xlat_action_t xlat_func_substr(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx,
+                                     request_t *request, fr_value_box_list_t *args)
+{
+       fr_value_box_t  *in = NULL, *start_vb, *len_vb, *vb;
+       int32_t         start, end, len;
+
+       XLAT_ARGS(args, &in, &start_vb, &len_vb);
+
+       if (!((in->type == FR_TYPE_STRING) || (in->type == FR_TYPE_OCTETS))) {
+               RPEDEBUG("substr only valid for string or octets data");
+               return XLAT_ACTION_FAIL;
+       }
+
+       if (start_vb->vb_int32 > (int32_t)in->vb_length) return XLAT_ACTION_DONE;
+
+       if (start_vb->vb_int32 < 0) {
+               start = in->vb_length + start_vb->vb_int32;
+               if (start < 0) start = 0;
+       } else {
+               start = start_vb->vb_int32;
+       }
+
+       if (len_vb) {
+               if (len_vb->vb_int32 < 0) {
+                       end = in->vb_length + len_vb->vb_int32;
+                       if (end < 0) return XLAT_ACTION_DONE;
+               } else {
+                       end = start + len_vb->vb_int32;
+                       if (end > (int32_t)in->vb_length) end = in->vb_length;
+               }
+       } else {
+               end = in->vb_length;
+       }
+
+       if (start >= end) return XLAT_ACTION_DONE;
+
+       MEM(vb = fr_value_box_alloc(ctx, in->type, NULL));
+
+       len = end - start;
+       switch (in->type) {
+       case FR_TYPE_STRING:
+               fr_value_box_bstrndup(vb, vb, NULL, &in->vb_strvalue[start], len, in->tainted);
+               break;
+       case FR_TYPE_OCTETS:
+       {
+               uint8_t *buf;
+               fr_value_box_mem_alloc(vb, &buf, vb, NULL, len, in->tainted);
+               memcpy(buf, &in->vb_octets[start], len);
+       }
+               break;
+       default:
+               fr_assert(0);
+       }
+       fr_dcursor_append(out, vb);
+
+       return XLAT_ACTION_DONE;
+}
 
 #ifdef HAVE_REGEX_PCRE2
 /** Perform regex substitution TODO CHECK
@@ -3785,6 +3861,7 @@ do { \
        XLAT_REGISTER_ARGS("length", xlat_func_length, FR_TYPE_SIZE, xlat_func_length_args);
        XLAT_REGISTER_ARGS("lpad", xlat_func_lpad, FR_TYPE_STRING, xlat_func_pad_args);
        XLAT_REGISTER_ARGS("rpad", xlat_func_rpad, FR_TYPE_STRING, xlat_func_pad_args);
+       XLAT_REGISTER_ARGS("substr", xlat_func_substr, FR_TYPE_VOID, xlat_func_substr_args);
 
        /*
         *      The inputs to these functions are variable.
diff --git a/src/tests/keywords/substr b/src/tests/keywords/substr
new file mode 100644 (file)
index 0000000..cb42286
--- /dev/null
@@ -0,0 +1,52 @@
+#
+#  PRE: if return
+#
+string test_string
+octets test_octets
+uint32 test_int
+
+&test_string = "hello world"
+&test_octets = 0x01234567
+&test_int = 123456
+
+if !(%substr(%{test_string}, 1, 3) == 'ell') {
+       test_fail
+}
+
+if !(%substr(%{test_string}, 5) == ' world') {
+       test_fail
+}
+
+if !(%substr(%{test_string}, -3) == 'rld') {
+       test_fail
+}
+
+if !(%substr(%{test_string}, -20) == 'hello world') {
+       test_fail
+}
+
+if !(%substr(%{test_string}, -4, 2) == 'or') {
+       test_fail
+}
+
+if !(%substr(%{test_string}, -10, -3) == 'ello wo') {
+       test_fail
+}
+
+if (%substr(%{test_string}, 20)) {
+       test_fail
+}
+
+if (%substr(%{test_string}, 5, -7)) {
+       test_fail
+}
+
+if !(%substr(%{test_octets}, 1, 2) == 0x2345) {
+       test_fail
+}
+
+if (%substr(%{test_int}, 1, 2)) {
+       test_fail
+}
+
+success