From: Alan T. DeKok Date: Sat, 28 Jun 2025 15:11:41 +0000 (-0400) Subject: rearrange in preparation for function arguments X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=696e2b9231a9598ebd5df1eb64e1205e4cb2ef9e;p=thirdparty%2Ffreeradius-server.git rearrange in preparation for function arguments --- diff --git a/src/lib/util/skip.c b/src/lib/util/skip.c index 81d71294916..c6102ea34d9 100644 --- a/src/lib/util/skip.c +++ b/src/lib/util/skip.c @@ -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 diff --git a/src/lib/util/skip.h b/src/lib/util/skip.h index 454b2be2e4d..5794acf720d 100644 --- a/src/lib/util/skip.h +++ b/src/lib/util/skip.h @@ -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],