/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * ap_expr_scan.l, based on ssl_expr_scan.l from mod_ssl */ /* _________________________________________________________________ ** ** Expression Scanner ** _________________________________________________________________ */ %pointer %option batch %option never-interactive %option nodefault %option noyywrap %option reentrant %option bison-bridge %option warn %option noinput nounput noyy_top_state %option stack %x str expr %x var vararg %x regex regsub regflags %{ #include "util_expr_private.h" #include "util_expr_parse.h" #include "http_main.h" #include "http_log.h" #include "apr_lib.h" #undef YY_INPUT #define YY_INPUT(buf,result,max_size) \ { \ if ((result = MIN(max_size, yyextra->inputbuf \ + yyextra->inputlen \ - yyextra->inputptr)) <= 0) \ { \ result = YY_NULL; \ } \ else { \ memcpy(buf, yyextra->inputptr, result); \ yyextra->inputptr += result; \ } \ } /* * XXX: It would be nice if we could recover somehow, e.g. via * XXX: longjmp. It is not clear if the scanner is in any state * XXX: to be cleaned up, though. */ static int unreachable = 0; #define YY_FATAL_ERROR(msg) \ do { \ ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, \ APLOGNO(03296) \ "expr parser fatal error (BUG?): " \ "%s, exiting", msg); \ if (unreachable) { \ /* Not reached, silence [-Wunused-function] */ \ yy_fatal_error(msg, yyscanner); \ } \ else { \ abort(); \ } \ } while (0) #define YY_EXTRA_TYPE ap_expr_parse_ctx_t* #define PERROR(msg) do { \ yyextra->error2 = msg; \ return T_ERROR; \ } while (0) #define PERROR_CHAR(prefix, chr) do { \ char *msg; \ if (apr_isprint((chr))) { \ msg = apr_psprintf(yyextra->pool, prefix "'%c'", (char)(chr)); \ } \ else { \ msg = apr_psprintf(yyextra->pool, prefix "'\\x%.2X'", (int)(chr)); \ } \ PERROR(msg); \ } while (0) #define STACK_PUSH() do { \ ap_expr_parser_stack_t *sk; \ if (yyextra->spares) { \ sk = yyextra->spares; \ yyextra->spares = sk->next; \ } \ else { \ sk = apr_palloc(yyextra->ptemp, sizeof(*sk)); \ } \ sk->scan_ptr = sk->scan_buf; \ sk->scan_stop = sk->scan_buf[0] = '\0'; \ sk->scan_flag = 0; \ sk->next = yyextra->current; \ yyextra->current = sk; \ } while (0) #define STACK_POP() do { \ ap_expr_parser_stack_t *sk; \ sk = yyextra->current; \ yyextra->current = sk->next; \ sk->next = yyextra->spares; \ yyextra->spares = sk; \ } while (0) #define STATE_PUSH(st, sk) do { \ yy_push_state((st), yyscanner); \ if ((sk)) { \ STACK_PUSH(); \ } \ } while (0) #define STATE_POP(sk) do { \ if ((sk)) { \ STACK_POP(); \ } \ yy_pop_state(yyscanner); \ } while (0) #define str_ptr (yyextra->current->scan_ptr) #define str_buf (yyextra->current->scan_buf) #define str_stop (yyextra->current->scan_stop) #define str_flag (yyextra->current->scan_flag) #define STR_APPEND_CHECK(chr, chk) do { \ if ((chk) && apr_iscntrl((chr))) { \ PERROR_CHAR("Invalid string character ", (chr)); \ } \ if (str_ptr >= str_buf + sizeof(str_buf) - 1) { \ PERROR("String too long"); \ } \ *str_ptr++ = (char)(chr); \ } while (0) #define STR_APPEND_NOCHECK(chr) \ STR_APPEND_CHECK((chr), 0) #define STR_EMPTY() \ (str_ptr == str_buf) #define STR_RETURN() \ (apr_pstrdup(yyextra->pool, (*str_ptr = '\0', str_ptr = str_buf))) %} ANY (.|\n) SPACE [ \t\n] QUOTE ["'] TOKEN ([a-zA-Z][a-zA-Z0-9_]*) VAR_BEGIN (\%\{) VAR_ARG (\:) VAR_END (\}) VAREXP_BEGIN (\%\{\:) VAREXP_END (\:\}) REG_SEP [/#$%^|?!'",;:._-] BACKREF (\$[0-9]) %% %{ /* * Set initial state for string expressions */ if (yyextra->at_start) { yyextra->at_start = 0; if (yyextra->flags & AP_EXPR_FLAG_STRING_RESULT) { STATE_PUSH(str, 1); return T_EXPR_STRING; } else { STATE_PUSH(expr, 1); return T_EXPR_BOOL; } } %} /* * Back off INITIAL pushes */ <> { STATE_POP(0); /* */ if (YY_START != INITIAL) { PERROR("Unterminated string"); } yylval->cpVal = STR_RETURN(); STACK_POP(); /* ^ after this */ return T_STRING; } <> { STATE_POP(1); /* */ if (YY_START != INITIAL) { PERROR("Unterminated expression"); } } {QUOTE} { if (yytext[0] == str_stop) { if (!STR_EMPTY()) { yyless(0); /* come back below */ yylval->cpVal = STR_RETURN(); return T_STRING; } STATE_POP(1); /* */ return T_STR_END; } STR_APPEND_NOCHECK(yytext[0]); } /* regexp backref inside string/arg */ {BACKREF} { if (!STR_EMPTY()) { yyless(0); /* come back below */ yylval->cpVal = STR_RETURN(); return T_STRING; } yylval->num = yytext[1] - '0'; return T_BACKREF; } /* variable inside string/arg */ {VAR_BEGIN} { if (!STR_EMPTY()) { yyless(0); /* come back below */ yylval->cpVal = STR_RETURN(); return T_STRING; } STATE_PUSH(var, 1); return T_VAR_BEGIN; } {VAREXP_BEGIN} { if (!STR_EMPTY()) { yyless(0); /* come back below */ yylval->cpVal = STR_RETURN(); return T_STRING; } STATE_PUSH(expr, 1); return T_VAREXP_BEGIN; } /* Any non-octal or octal higher than 377 (decimal 255) is invalid */ \\([4-9][0-9]{2}|[8-9][0-9]{0,2}) { PERROR("Bad character escape sequence"); } \\[0-7]{1,3} { int result; (void)sscanf(yytext+1, "%o", &result); STR_APPEND_NOCHECK(result); } \\x[0-9A-Fa-f]{1,2} { int result; (void)sscanf(yytext+1, "%x", &result); STR_APPEND_NOCHECK(result); } \\n { STR_APPEND_NOCHECK('\n'); } \\r { STR_APPEND_NOCHECK('\r'); } \\t { STR_APPEND_NOCHECK('\t'); } \\b { STR_APPEND_NOCHECK('\b'); } \\f { STR_APPEND_NOCHECK('\f'); } \\. { STR_APPEND_CHECK(yytext[1], 1); } \n { PERROR("Unterminated string or variable"); } {ANY} { STR_APPEND_CHECK(yytext[0], 1); } {SPACE}+ { /* NOP */ } {QUOTE} { STATE_PUSH(str, 1); str_stop = yytext[0]; return T_STR_BEGIN; } {VAREXP_BEGIN} { STATE_PUSH(expr, 1); return T_VAREXP_BEGIN; } {VAREXP_END} { STATE_POP(1); /* */ return T_VAREXP_END; } {VAR_BEGIN} { STATE_PUSH(var, 1); return T_VAR_BEGIN; } {TOKEN} { yylval->cpVal = apr_pstrdup(yyextra->pool, yytext); return T_ID; } {VAR_ARG} { STATE_PUSH(vararg, 0); return yytext[0]; } {VAR_END} { yyless(0); /* let handle */ yylval->cpVal = STR_RETURN(); STATE_POP(0); /* */ return T_STRING; } {ANY} { STR_APPEND_CHECK(yytext[0], 1); } {VAR_END} { STATE_POP(1); /* */ return T_VAR_END; } {ANY} { PERROR_CHAR("Unexpected variable character ", yytext[0]); } <> { PERROR("Unterminated variable"); } /* * Regular Expression */ \/ { STATE_PUSH(regex, 1); str_stop = yytext[0]; str_flag = 'm'; return T_REGEX; } [ms]{REG_SEP} { STATE_PUSH(regex, 1); str_stop = yytext[1]; str_flag = yytext[0]; return (str_flag == 'm') ? T_REGEX : T_REGSUB; } {ANY} { if (yytext[0] == str_stop) { yylval->cpVal = STR_RETURN(); STATE_POP(0); /* */ if (str_flag == 'm') { STATE_PUSH(regflags, 0); } else { STATE_PUSH(regsub, 0); } return T_REG_MATCH; } STR_APPEND_CHECK(yytext[0], 1); } {ANY} { if (yytext[0] == str_stop) { yylval->cpVal = STR_RETURN(); STATE_POP(0); /* */ STATE_PUSH(regflags, 0); return T_STRING; } STR_APPEND_CHECK(yytext[0], 1); } {ANY} { if (!ap_strchr_c("ismg", yytext[0])) { if (apr_isalnum(yytext[0])) { PERROR("Invalid regexp flag(s)"); } yyless(0); /* not a flags, rewind */ yylval->cpVal = STR_RETURN(); STATE_POP(1); /* */ return T_REG_FLAGS; } STR_APPEND_NOCHECK(yytext[0]); } <> { yylval->cpVal = STR_RETURN(); STATE_POP(1); /* */ return T_REG_FLAGS; } <> { PERROR("Unterminated regexp"); } {BACKREF} { yylval->num = yytext[1] - '0'; return T_BACKREF; } /* * Operators */ ==? { return T_OP_STR_EQ; } "!=" { return T_OP_STR_NE; } "<" { return T_OP_STR_LT; } "<=" { return T_OP_STR_LE; } ">" { return T_OP_STR_GT; } ">=" { return T_OP_STR_GE; } "=~" { return T_OP_REG; } "!~" { return T_OP_NRE; } "and" { return T_OP_AND; } "&&" { return T_OP_AND; } "or" { return T_OP_OR; } "||" { return T_OP_OR; } "not" { return T_OP_NOT; } "!" { return T_OP_NOT; } "." { return T_OP_CONCAT; } "-in" { return T_OP_IN; } "-eq" { return T_OP_EQ; } "-ne" { return T_OP_NE; } "-ge" { return T_OP_GE; } "-le" { return T_OP_LE; } "-gt" { return T_OP_GT; } "-lt" { return T_OP_LT; } /* for compatibility with ssl_expr */ "lt" { return T_OP_LT; } "le" { return T_OP_LE; } "gt" { return T_OP_GT; } "ge" { return T_OP_GE; } "ne" { return T_OP_NE; } "eq" { return T_OP_EQ; } "in" { return T_OP_IN; } "-"[a-zA-Z_][a-zA-Z_0-9]+ { yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1); return T_OP_BINARY; } "-"[a-zA-Z_] { yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1); return T_OP_UNARY; } /* Apply substitution to a string */ "sub" { return T_OP_SUB; } /* Join a list into a string */ "join" { return T_OP_JOIN; } /* Split a string (or list) into a(nother) list */ "split" { return T_OP_SPLIT; } /* * Specials */ "true" { return T_TRUE; } "false" { return T_FALSE; } /* * Digits */ \-?[0-9]+ { yylval->cpVal = apr_pstrdup(yyextra->pool, yytext); return T_DIGIT; } /* * Identifiers */ {TOKEN} { yylval->cpVal = apr_pstrdup(yyextra->pool, yytext); return T_ID; } /* * These are parts of the grammar and are returned as is */ [(){},] { return yytext[0]; } /* * Anything else is an error */ {ANY} { PERROR_CHAR("Parse error near character ", yytext[0]); } %%