]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Convert lpad
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 18 Mar 2021 18:56:53 +0000 (18:56 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 18 Mar 2021 20:15:54 +0000 (20:15 +0000)
src/lib/unlang/xlat_builtin.c
src/tests/keywords/pad

index 37b93bb4795441a014f608e5a40892c4982fe36a..4309f563108b21d844b6333ac1f5d298884eff0f 100644 (file)
@@ -1286,155 +1286,6 @@ static ssize_t xlat_func_integer(UNUSED TALLOC_CTX *ctx, char **out, size_t outl
        return -1;
 }
 
-
-/** Parse the 3 arguments to lpad / rpad.
- *
- * Parses a fmt string with the components @verbatim <tmpl> <pad_len> <pad_char>@endverbatim
- *
- * @param[out] vpt_p           Template to retrieve value to pad.
- * @param[out] pad_len_p       Length the string needs to be padded to.
- * @param[out] pad_char_p      Char to use for padding.
- * @param[in] request          The current request.
- * @param[in] fmt              string to parse.
- *
- * @return
- *     - <= 0 the negative offset the parse error ocurred at.
- *     - >0 how many bytes of fmt were parsed.
- */
-static ssize_t parse_pad(tmpl_t **vpt_p, size_t *pad_len_p, char *pad_char_p, request_t *request, char const *fmt)
-{
-       ssize_t                 slen;
-       unsigned long           pad_len;
-       char const              *p;
-       char                    *end;
-       tmpl_t                  *vpt;
-
-
-       *pad_char_p = ' ';              /* the default */
-
-       *vpt_p = NULL;
-
-       p = fmt;
-       fr_skip_whitespace(p);
-
-       if (*p != '&') {
-               REDEBUG("First argument must be an attribute reference");
-               return 0;
-       }
-
-       slen = tmpl_afrom_attr_substr(request, NULL, &vpt,
-                                     &FR_SBUFF_IN(p, strlen(p)),
-                                     &xlat_arg_parse_rules,
-                                     &(tmpl_rules_t){ .dict_def = request->dict });
-       if (slen <= 0) {
-               RPEDEBUG("Failed parsing input string");
-               return slen;
-       }
-
-       p = fmt + slen;
-
-       fr_skip_whitespace(p);
-
-       pad_len = strtoul(p, &end, 10);
-       if ((pad_len == ULONG_MAX) || (pad_len > 8192)) {
-               talloc_free(vpt);
-               REDEBUG("Invalid pad_len found at: %s", p);
-               return fmt - p;
-       }
-
-       p += (end - p);
-
-       /*
-        *      The pad_char_p character is optional.
-        *
-        *      But we must have a space after the previous number,
-        *      and we must have only ONE pad_char_p character.
-        */
-       if (*p) {
-               if (!isspace(*p)) {
-                       talloc_free(vpt);
-                       REDEBUG("Invalid text found at: %s", p);
-                       return fmt - p;
-               }
-
-               fr_skip_whitespace(p);
-
-               if (p[1] != '\0') {
-                       talloc_free(vpt);
-                       REDEBUG("Invalid text found at: %s", p);
-                       return fmt - p;
-               }
-
-               *pad_char_p = *p++;
-       }
-
-       *vpt_p = vpt;
-       *pad_len_p = pad_len;
-
-       return p - fmt;
-}
-
-
-/** Left pad a string
- *
-@verbatim
-%{lpad:&Attribute-Name <length> <char>}
-@endverbatim
- *
- * Example: (User-Name = "foo")
-@verbatim
-"%{lpad:&User-Name 5 x}" == "xxfoo"
-@endverbatim
- *
- * @ingroup xlat_functions
- */
-static ssize_t xlat_func_lpad(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen,
-                             UNUSED void const *mod_inst, UNUSED void const *xlat_inst,
-                             request_t *request, char const *fmt)
-{
-       char            fill;
-       size_t          pad;
-       ssize_t         len;
-       tmpl_t  *vpt;
-       char            *to_pad = NULL;
-
-       if (parse_pad(&vpt, &pad, &fill, request, fmt) <= 0) return 0;
-
-       if (!fr_cond_assert(vpt)) return 0;
-
-       /*
-        *      Print the attribute (left justified).  If it's too
-        *      big, we're done.
-        */
-       len = tmpl_aexpand(ctx, &to_pad, request, vpt, NULL, NULL);
-       if (len <= 0) return -1;
-
-       /*
-        *      Already big enough, no padding required...
-        */
-       if ((size_t) len >= pad) {
-               *out = to_pad;
-               return pad;
-       }
-
-       /*
-        *      Realloc is actually pretty cheap in most cases...
-        */
-       MEM(to_pad = talloc_realloc(ctx, to_pad, char, pad + 1));
-
-       /*
-        *      We have to shift the string to the right, and pad with
-        *      "fill" characters.
-        */
-       memmove(to_pad + (pad - len), to_pad, len + 1);
-       memset(to_pad, fill, pad - len);
-
-       *out = to_pad;
-
-       return pad;
-}
-
-
 /** Processes fmt as a map string and applies it to the current request
  *
  * e.g.
@@ -1627,13 +1478,98 @@ static ssize_t xlat_func_xlat(TALLOC_CTX *ctx, char **out, size_t outlen,
  *     Async xlat functions
  */
 
-static xlat_arg_parser_t const xlat_func_rpad_args[] = {
+static xlat_arg_parser_t const xlat_func_pad_args[] = {
        { .required = true, .type = FR_TYPE_STRING },
        { .required = true, .single = true, .type = FR_TYPE_UINT64 },
        { .concat = true, .type = FR_TYPE_STRING },
        XLAT_ARG_PARSER_TERMINATOR
 };
 
+
+/** lpad a string
+ *
+@verbatim
+%(rpad:&Attribute-Name <length> [<fill>])
+@endverbatim
+ *
+ * Example: (User-Name = "foo")
+@verbatim
+"%(rpad:%{User-Name} 5 x)" == "xxfoo"
+@endverbatim
+ *
+ * @ingroup xlat_functions
+ */
+static xlat_action_t xlat_func_lpad(UNUSED TALLOC_CTX *ctx, fr_dcursor_t *out,
+                                   request_t *request, UNUSED void const *xlat_inst,
+                                   UNUSED void *xlat_thread_inst,
+                                   fr_value_box_list_t *args)
+{
+       fr_value_box_t          *values = fr_dlist_head(args);
+       fr_value_box_list_t     *list = &values->vb_group;
+       fr_value_box_t          *pad = fr_dlist_next(args, values);
+       size_t                  pad_len = (size_t)pad->vb_uint64;
+       fr_value_box_t          *fill = fr_dlist_next(args, pad);
+       char const              *fill_str = NULL;
+       size_t                  fill_len = 0;
+
+       fr_value_box_t          *in = NULL;
+
+       /*
+        *      Fill is optional
+        */
+       if (fill) {
+               fill_str = fill->vb_strvalue;
+               fill_len = talloc_array_length(fill_str) - 1;
+       }
+
+       if (fill_len == 0) {
+               fill_str = " ";
+               fill_len = 1;
+       }
+
+       while ((in = fr_dlist_pop_head(list))) {
+               size_t                  len = talloc_array_length(in->vb_strvalue) - 1;
+               size_t                  remaining;
+               char                    *buff;
+               fr_sbuff_t              sbuff;
+               fr_sbuff_marker_t       m_data;
+
+               fr_dcursor_append(out, in);
+
+               if (len > pad_len) continue;
+
+               if (fr_value_box_bstr_realloc(in, &buff, in, pad_len) < 0) {
+                       RPEDEBUG("Failed reallocing input data");
+                       return XLAT_ACTION_FAIL;
+               }
+
+               fr_sbuff_init(&sbuff, buff, pad_len + 1);
+               fr_sbuff_marker(&m_data, &sbuff);
+               fr_sbuff_advance(&m_data, pad_len - len);       /* Mark where we want the data to go */
+               fr_sbuff_move(&m_data, &sbuff, len);            /* Shift the data */
+               fr_sbuff_set(&m_data, fr_sbuff_start(&sbuff) + (pad_len - len));
+               fr_sbuff_set_to_start(&sbuff);                  /* reset to the beginning of the buffer */
+
+               if (fill_len == 1) {
+                       memset(fr_sbuff_current(&sbuff), *fill_str, fr_sbuff_ahead(&m_data));
+                       continue;
+               }
+
+               /*
+                *      Copy fill as a repeating pattern
+                */
+               while ((remaining = fr_sbuff_ahead(&m_data))) {
+                       size_t to_copy = remaining >= fill_len ? fill_len : remaining;
+                       memcpy(fr_sbuff_current(&sbuff), fill_str, to_copy);    /* avoid \0 termination */
+                       fr_sbuff_advance(&sbuff, to_copy);
+               }
+               fr_sbuff_set_to_end(&sbuff);
+               fr_sbuff_terminate(&sbuff);                     /* Move doesn't re-terminate */
+       }
+
+       return XLAT_ACTION_DONE;
+}
+
 /** Right pad a string
  *
 @verbatim
@@ -3268,7 +3204,6 @@ int xlat_init(void)
        XLAT_REGISTER(debug_attr);
        xlat_register_legacy(NULL, "explode", xlat_func_explode, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN);
        XLAT_REGISTER(integer);
-       xlat_register_legacy(NULL, "lpad", xlat_func_lpad, NULL, NULL, 0, 0);
        XLAT_REGISTER(map);
        xlat_register_legacy(NULL, "nexttime", xlat_func_next_time, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN);
        xlat_register_legacy(NULL, "trigger", trigger_xlat, NULL, NULL, 0, 0);  /* On behalf of trigger.c */
@@ -3285,7 +3220,8 @@ do { \
        XLAT_REGISTER_ARGS("concat", xlat_func_concat, xlat_func_concat_args);
        XLAT_REGISTER_ARGS("join", xlat_func_join, xlat_func_join_args);
        XLAT_REGISTER_ARGS("pairs", xlat_func_pairs, xlat_func_pairs_args);
-       XLAT_REGISTER_ARGS("rpad", xlat_func_rpad, xlat_func_rpad_args);
+       XLAT_REGISTER_ARGS("lpad", xlat_func_lpad, xlat_func_pad_args);
+       XLAT_REGISTER_ARGS("rpad", xlat_func_rpad, xlat_func_pad_args);
 
 #define XLAT_REGISTER_MONO(_xlat, _func, _arg) \
 do { \
index 5d93165ba70ed7c9cff0df32ea5cfbac235332a3..a4c5c53017dbd97c59ff5f5a3772f8c14eb07c35 100644 (file)
@@ -32,17 +32,22 @@ if ("%(rpad:%{Tmp-String-0} 7 xy)" != "testxyx") {
 #
 #      lpad tests
 #
-if ("%{lpad:&Tmp-String-0 7}" != "   test") {
+if ("%(lpad:%{Tmp-String-0} 7)" != "   test") {
        test_fail
        return
 }
 
-if ("%{lpad:&Tmp-String-0 2}" != "test") {
+if ("%(lpad:%{Tmp-String-0} 2)" != "test") {
        test_fail
        return
 }
 
-if ("%{lpad:&Tmp-String-0 7 x}" != "xxxtest") {
+if ("%(lpad:%{Tmp-String-0} 7 x)" != "xxxtest") {
+       test_fail
+       return
+}
+
+if ("%(lpad:%{Tmp-String-0} 7 xy)" != "xyxtest") {
        test_fail
        return
 }