mirror of
https://github.com/apache/httpd.git
synced 2025-07-20 18:21:29 +00:00

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1873985 13f79535-47bb-0310-9956-ffa450edef68
507 lines
14 KiB
Plaintext
507 lines
14 KiB
Plaintext
/* 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]);
|
|
}
|
|
|
|
%%
|
|
|
|
|