| /* 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 | |
| */ | |
| <str><<EOF>> { | |
| STATE_POP(0); /* <str> */ | |
| if (YY_START != INITIAL) { | |
| PERROR("Unterminated string"); | |
| } | |
| yylval->cpVal = STR_RETURN(); | |
| STACK_POP(); /* ^ after this */ | |
| return T_STRING; | |
| } | |
| <expr><<EOF>> { | |
| STATE_POP(1); /* <expr> */ | |
| if (YY_START != INITIAL) { | |
| PERROR("Unterminated expression"); | |
| } | |
| } | |
| <str>{QUOTE} { | |
| if (yytext[0] == str_stop) { | |
| if (!STR_EMPTY()) { | |
| yyless(0); /* come back below */ | |
| yylval->cpVal = STR_RETURN(); | |
| return T_STRING; | |
| } | |
| STATE_POP(1); /* <str> */ | |
| return T_STR_END; | |
| } | |
| STR_APPEND_NOCHECK(yytext[0]); | |
| } | |
| /* regexp backref inside string/arg */ | |
| <str,vararg,regsub>{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 */ | |
| <str,vararg,regsub>{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; | |
| } | |
| <str,vararg,regsub>{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 */ | |
| <str,vararg,regsub>\\([4-9][0-9]{2}|[8-9][0-9]{0,2}) { | |
| PERROR("Bad character escape sequence"); | |
| } | |
| <str,vararg,regsub>\\[0-7]{1,3} { | |
| int result; | |
| (void)sscanf(yytext+1, "%o", &result); | |
| STR_APPEND_NOCHECK(result); | |
| } | |
| <str,vararg,regsub>\\x[0-9A-Fa-f]{1,2} { | |
| int result; | |
| (void)sscanf(yytext+1, "%x", &result); | |
| STR_APPEND_NOCHECK(result); | |
| } | |
| <str,vararg,regsub>\\n { STR_APPEND_NOCHECK('\n'); } | |
| <str,vararg,regsub>\\r { STR_APPEND_NOCHECK('\r'); } | |
| <str,vararg,regsub>\\t { STR_APPEND_NOCHECK('\t'); } | |
| <str,vararg,regsub>\\b { STR_APPEND_NOCHECK('\b'); } | |
| <str,vararg,regsub>\\f { STR_APPEND_NOCHECK('\f'); } | |
| <str,vararg,regsub>\\. { STR_APPEND_CHECK(yytext[1], 1); } | |
| <str,vararg,regsub>\n { | |
| PERROR("Unterminated string or variable"); | |
| } | |
| <str>{ANY} { | |
| STR_APPEND_CHECK(yytext[0], 1); | |
| } | |
| <expr>{SPACE}+ { | |
| /* NOP */ | |
| } | |
| <expr>{QUOTE} { | |
| STATE_PUSH(str, 1); | |
| str_stop = yytext[0]; | |
| return T_STR_BEGIN; | |
| } | |
| <expr>{VAREXP_BEGIN} { | |
| STATE_PUSH(expr, 1); | |
| return T_VAREXP_BEGIN; | |
| } | |
| <expr>{VAREXP_END} { | |
| STATE_POP(1); /* <expr> */ | |
| return T_VAREXP_END; | |
| } | |
| <expr>{VAR_BEGIN} { | |
| STATE_PUSH(var, 1); | |
| return T_VAR_BEGIN; | |
| } | |
| <var>{TOKEN} { | |
| yylval->cpVal = apr_pstrdup(yyextra->pool, yytext); | |
| return T_ID; | |
| } | |
| <var>{VAR_ARG} { | |
| STATE_PUSH(vararg, 0); | |
| return yytext[0]; | |
| } | |
| <vararg>{VAR_END} { | |
| yyless(0); /* let <var> handle */ | |
| yylval->cpVal = STR_RETURN(); | |
| STATE_POP(0); /* <vararg> */ | |
| return T_STRING; | |
| } | |
| <vararg>{ANY} { | |
| STR_APPEND_CHECK(yytext[0], 1); | |
| } | |
| <var>{VAR_END} { | |
| STATE_POP(1); /* <var> */ | |
| return T_VAR_END; | |
| } | |
| <var>{ANY} { | |
| PERROR_CHAR("Unexpected variable character ", yytext[0]); | |
| } | |
| <var,vararg><<EOF>> { | |
| PERROR("Unterminated variable"); | |
| } | |
| /* | |
| * Regular Expression | |
| */ | |
| <expr>\/ { | |
| STATE_PUSH(regex, 1); | |
| str_stop = yytext[0]; | |
| str_flag = 'm'; | |
| return T_REGEX; | |
| } | |
| <expr>[ms]{REG_SEP} { | |
| STATE_PUSH(regex, 1); | |
| str_stop = yytext[1]; | |
| str_flag = yytext[0]; | |
| return (str_flag == 'm') ? T_REGEX : T_REGSUB; | |
| } | |
| <regex>{ANY} { | |
| if (yytext[0] == str_stop) { | |
| yylval->cpVal = STR_RETURN(); | |
| STATE_POP(0); /* <regex> */ | |
| if (str_flag == 'm') { | |
| STATE_PUSH(regflags, 0); | |
| } | |
| else { | |
| STATE_PUSH(regsub, 0); | |
| } | |
| return T_REG_MATCH; | |
| } | |
| STR_APPEND_CHECK(yytext[0], 1); | |
| } | |
| <regsub>{ANY} { | |
| if (yytext[0] == str_stop) { | |
| yylval->cpVal = STR_RETURN(); | |
| STATE_POP(0); /* <regsub> */ | |
| STATE_PUSH(regflags, 0); | |
| return T_STRING; | |
| } | |
| STR_APPEND_CHECK(yytext[0], 1); | |
| } | |
| <regflags>{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); /* <regflags> */ | |
| return T_REG_FLAGS; | |
| } | |
| STR_APPEND_NOCHECK(yytext[0]); | |
| } | |
| <regflags><<EOF>> { | |
| yylval->cpVal = STR_RETURN(); | |
| STATE_POP(1); /* <regflags> */ | |
| return T_REG_FLAGS; | |
| } | |
| <regex,regsub><<EOF>> { | |
| PERROR("Unterminated regexp"); | |
| } | |
| <expr>{BACKREF} { | |
| yylval->num = yytext[1] - '0'; | |
| return T_BACKREF; | |
| } | |
| /* | |
| * Operators | |
| */ | |
| <expr>==? { return T_OP_STR_EQ; } | |
| <expr>"!=" { return T_OP_STR_NE; } | |
| <expr>"<" { return T_OP_STR_LT; } | |
| <expr>"<=" { return T_OP_STR_LE; } | |
| <expr>">" { return T_OP_STR_GT; } | |
| <expr>">=" { return T_OP_STR_GE; } | |
| <expr>"=~" { return T_OP_REG; } | |
| <expr>"!~" { return T_OP_NRE; } | |
| <expr>"and" { return T_OP_AND; } | |
| <expr>"&&" { return T_OP_AND; } | |
| <expr>"or" { return T_OP_OR; } | |
| <expr>"||" { return T_OP_OR; } | |
| <expr>"not" { return T_OP_NOT; } | |
| <expr>"!" { return T_OP_NOT; } | |
| <expr>"." { return T_OP_CONCAT; } | |
| <expr>"-in" { return T_OP_IN; } | |
| <expr>"-eq" { return T_OP_EQ; } | |
| <expr>"-ne" { return T_OP_NE; } | |
| <expr>"-ge" { return T_OP_GE; } | |
| <expr>"-le" { return T_OP_LE; } | |
| <expr>"-gt" { return T_OP_GT; } | |
| <expr>"-lt" { return T_OP_LT; } | |
| /* for compatibility with ssl_expr */ | |
| <expr>"lt" { return T_OP_LT; } | |
| <expr>"le" { return T_OP_LE; } | |
| <expr>"gt" { return T_OP_GT; } | |
| <expr>"ge" { return T_OP_GE; } | |
| <expr>"ne" { return T_OP_NE; } | |
| <expr>"eq" { return T_OP_EQ; } | |
| <expr>"in" { return T_OP_IN; } | |
| <expr>"-"[a-zA-Z_][a-zA-Z_0-9]+ { | |
| yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1); | |
| return T_OP_BINARY; | |
| } | |
| <expr>"-"[a-zA-Z_] { | |
| yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1); | |
| return T_OP_UNARY; | |
| } | |
| /* Apply substitution to a string */ | |
| <expr>"sub" { | |
| return T_OP_SUB; | |
| } | |
| /* Join a list into a string */ | |
| <expr>"join" { | |
| return T_OP_JOIN; | |
| } | |
| /* Split a string (or list) into a(nother) list */ | |
| <expr>"split" { | |
| return T_OP_SPLIT; | |
| } | |
| /* | |
| * Specials | |
| */ | |
| <expr>"true" { return T_TRUE; } | |
| <expr>"false" { return T_FALSE; } | |
| /* | |
| * Digits | |
| */ | |
| <expr>\-?[0-9]+ { | |
| yylval->cpVal = apr_pstrdup(yyextra->pool, yytext); | |
| return T_DIGIT; | |
| } | |
| /* | |
| * Identifiers | |
| */ | |
| <expr>{TOKEN} { | |
| yylval->cpVal = apr_pstrdup(yyextra->pool, yytext); | |
| return T_ID; | |
| } | |
| /* | |
| * These are parts of the grammar and are returned as is | |
| */ | |
| <expr>[(){},] { | |
| return yytext[0]; | |
| } | |
| /* | |
| * Anything else is an error | |
| */ | |
| <INITIAL,expr>{ANY} { | |
| PERROR_CHAR("Parse error near character ", yytext[0]); | |
| } | |
| %% | |