]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
rearrange in preparation for function arguments
authorAlan T. DeKok <aland@freeradius.org>
Sat, 28 Jun 2025 15:11:41 +0000 (11:11 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Sat, 28 Jun 2025 15:12:15 +0000 (11:12 -0400)
src/lib/util/skip.c
src/lib/util/skip.h

index 81d7129491616e55b92ec83ec642e20be1675c23..c6102ea34d90bbe0ef8f3ea6118a402f3ceb6546 100644 (file)
@@ -111,80 +111,43 @@ ssize_t fr_skip_string(char const *start, char const *end)
        return -(p - start);
 }
 
-/**  Skip an xlat expression.
- *
- *  This is a simple "peek ahead" parser which tries to not be wrong.  It may accept
- *  some things which will later parse as invalid (e.g. unknown attributes, etc.)
- *  But it also rejects all malformed expressions.
- *
- *  It's used as a quick hack because the full parser isn't always available.
+/** Skip a generic {...} or (...) arguments
  *
- *  @param[in] start   start of the expression, MUST point to the "%{" or "%("
- *  @param[in] end     end of the string (or NULL for zero-terminated strings)
- *  @return
- *     >0 length of the string which was parsed
- *     <=0 on error
  */
-ssize_t fr_skip_xlat(char const *start, char const *end)
+ssize_t fr_skip_brackets(char const *start, char const *end, char end_quote)
 {
-       int     depth = 1;              /* caller skips '{' */
        ssize_t slen;
-       char    quote, end_quote;
        char const *p = start;
 
-       /*
-        *      At least %{1}
-        */
-       if (end && ((start + 4) > end)) {
-               fr_strerror_const("Invalid expansion");
-               return 0;
-       }
-
-       if ((*p != '%') && (*p != '$')) {
-               fr_strerror_const("Unexpected character in expansion");
-               return -(p - start);
-       }
-
-       p++;
-       if ((*p != '{') && (*p != '(')) {
-               char const *q = p;
+       while ((end && (p < end)) || *p) {
+               if (*p == end_quote) {
+                       p++;
+                       return p - start;
+               }
 
                /*
-                *      New xlat syntax: %foo(...)
+                *      Expressions.  Arguably we want to
+                *      differentiate conditions and function
+                *      arguments, but it's not clear how to do that
+                *      in a pre-parsing stage.
                 */
-               while (isalnum((int) *q) || (*q == '.') || (*q == '_') || (*q == '-')) {
-                       q++;
-               }
-               if (*q == '(') {
-                       p = q;
-                       goto do_quote;
-               }
-
-               fr_strerror_const("Invalid character after '%'");
-               return -(p - start);
-       }
-
-do_quote:
-       quote = *(p++);
-       if (quote == '{') {
-               end_quote = '}';
-       } else {
-               end_quote = ')';
-       }
-
-       while ((end && (p < end)) || (*p >= ' ')) {
-               if (*p == quote) {
+               if (*p == '(') {
                        p++;
-                       depth++;
+                       slen = fr_skip_brackets(p, end, ')');
+                       if (slen <= 0) return slen - (p - start);
+
+               next:
+                       fr_assert((size_t) slen <= (size_t) (end - p));
+                       p += slen;
                        continue;
                }
 
-               if (*p == end_quote) {
-                       p++;
-                       depth--;
-                       if (!depth) return p - start;
-
-                       continue;
+               /*
+                *      A quoted string.
+                */
+               if ((*p == '"') || (*p == '\'') || (*p == '`')) {
+                       slen = fr_skip_string(p, end);
+                       goto next;
                }
 
                /*
@@ -193,16 +156,19 @@ do_quote:
                if ((p[0] == '$') || (p[0] == '%')) {
                        if (end && (p + 2) >= end) break;
 
-                       if ((p[1] == '{') || ((p[0] == '$') && (p[1] == '('))) {
-                               slen = fr_skip_xlat(p, end);
-
-                       check:
-                               if (slen <= 0) return -(p - start) + slen;
-
-                               p += slen;
+                       /*
+                        *      %% inside of an xlat
+                        */
+                       if ((p[0] == '%') && (p[1] == '%')) {
+                               p += 2;
                                continue;
                        }
 
+                       if ((p[1] == '{') || (p[1] == '(')) {
+                               slen = fr_skip_xlat(p, end);
+                               goto next;
+                       }
+
                        /*
                         *      Bare $ or %, just leave it alone.
                         */
@@ -211,19 +177,8 @@ do_quote:
                }
 
                /*
-                *      A quoted string.
-                */
-               if ((*p == '"') || (*p == '\'') || (*p == '`')) {
-                       slen = fr_skip_string(p, end);
-                       goto check;
-               }
-
-               /*
-                *      @todo - bare '(' is a condition or nested
-                *      expression.  The brackets need to balance
-                *      here, too.
+                *      Escapes are special.
                 */
-
                if (*p != '\\') {
                        p++;
                        continue;
@@ -245,6 +200,73 @@ do_quote:
        return -(p - start);
 }
 
+/**  Skip an xlat expression.
+ *
+ *  This is a simple "peek ahead" parser which tries to not be wrong.  It may accept
+ *  some things which will later parse as invalid (e.g. unknown attributes, etc.)
+ *  But it also rejects all malformed expressions.
+ *
+ *  It's used as a quick hack because the full parser isn't always available.
+ *
+ *  @param[in] start   start of the expression, MUST point to the "%{" or "%("
+ *  @param[in] end     end of the string (or NULL for zero-terminated strings)
+ *  @return
+ *     >0 length of the string which was parsed
+ *     <=0 on error
+ */
+ssize_t fr_skip_xlat(char const *start, char const *end)
+{
+       ssize_t slen;
+       char const *p = start;
+
+       /*
+        *      At least %{1} or $(.)
+        */
+       if (end && ((end - start) < 4)) {
+               fr_strerror_const("Invalid expansion");
+               return 0;
+       }
+
+       if (!((memcmp(p, "%{", 2) == 0) || /* xlat */
+             (memcmp(p, "${", 2) == 0) || /* config file macro */
+             (memcmp(p, "$(", 2) == 0))) {  /* shell expansion in an back-ticks argument */
+               fr_strerror_const("Invalid expansion");
+               return 0;
+       }
+       p++;
+
+       if (*p == '(') {
+               p++;            /* skip the '(' */
+               slen = fr_skip_brackets(p, end, ')');
+
+       } else if (*p == '{') {
+               p++;            /* skip the '{' */
+               slen = fr_skip_brackets(p, end, '}');
+
+       } else {
+               char const *q = p;
+
+               /*
+                *      New xlat syntax: %foo(...)
+                */
+               while (isalnum((int) *q) || (*q == '.') || (*q == '_') || (*q == '-')) {
+                       q++;
+               }
+
+               if (*q != '(') {
+                       fr_strerror_const("Invalid character after '%'");
+                       return -(p - start);
+               }
+
+               p = q + 1;
+
+               slen = fr_skip_brackets(p, end, ')');
+       }
+
+       if (slen <= 0) return slen - (p - start);
+       return slen + (p - start);
+}
+
 /**  Skip a conditional expression.
  *
  *  This is a simple "peek ahead" parser which tries to not be wrong.  It may accept
index 454b2be2e4dea94522833f61e4bb02d6790b1875..5794acf720d53dd4521f237a4d3aa9ae1f508de1 100644 (file)
@@ -51,6 +51,8 @@ extern "C" {
 
 ssize_t                fr_skip_string(char const *start, char const *end) CC_HINT(nonnull(1));
 
+ssize_t                fr_skip_brackets(char const *start, char const *end, char end_quote);
+
 ssize_t                fr_skip_xlat(char const *start, char const *end) CC_HINT(nonnull(1));
 
 ssize_t                fr_skip_condition(char const *start, char const *end, bool const terminal[static UINT8_MAX + 1],