1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 *  ap_expr_scan.l, based on ssl_expr_scan.l from mod_ssl
19 */
20
21/*  _________________________________________________________________
22**
23**  Expression Scanner
24**  _________________________________________________________________
25*/
26
27%pointer
28%option batch
29%option never-interactive
30%option nodefault
31%option noyywrap
32%option reentrant
33%option bison-bridge
34%option warn
35%option noinput nounput noyy_top_state
36%option stack
37%x str
38%x var
39%x vararg
40%x regex regex_flags
41
42%{
43#include "util_expr_private.h"
44#include "util_expr_parse.h"
45
46#undef  YY_INPUT
47#define YY_INPUT(buf,result,max_size)                       \
48{                                                           \
49    if ((result = MIN(max_size, yyextra->inputbuf           \
50                              + yyextra->inputlen           \
51                              - yyextra->inputptr)) <= 0)   \
52    {                                                       \
53        result = YY_NULL;                                   \
54    }                                                       \
55    else {                                                  \
56        memcpy(buf, yyextra->inputptr, result);             \
57        yyextra->inputptr += result;                        \
58    }                                                       \
59}
60
61#define YY_EXTRA_TYPE ap_expr_parse_ctx_t*
62
63#define PERROR(msg) do { yyextra->error2 = msg ; return T_ERROR; } while (0)
64
65#define str_ptr     (yyextra->scan_ptr)
66#define str_buf     (yyextra->scan_buf)
67#define str_del     (yyextra->scan_del)
68
69#define STR_APPEND(c) do {                          \
70        *str_ptr++ = (c);                           \
71        if (str_ptr >= str_buf + sizeof(str_buf))   \
72            PERROR("String too long");              \
73    } while (0)
74
75%}
76
77
78%%
79
80  char  regex_buf[MAX_STRING_LEN];
81  char *regex_ptr = NULL;
82  char  regex_del = '\0';
83
84%{
85 /*
86  * Set initial state for string expressions
87  */
88  if (yyextra->at_start) {
89    yyextra->at_start = 0;
90    if (yyextra->flags & AP_EXPR_FLAG_STRING_RESULT) {
91        BEGIN(str);
92        return T_EXPR_STRING;
93    }
94    else {
95        return T_EXPR_BOOL;
96    }
97  }
98%}
99
100 /*
101  * Whitespaces
102  */
103[ \t\n]+ {
104    /* NOP */
105}
106
107 /*
108  * strings ("..." and '...')
109  */
110["'] {
111    str_ptr = str_buf;
112    str_del = yytext[0];
113    BEGIN(str);
114    return T_STR_BEGIN;
115}
116<str>["'] {
117    if (yytext[0] == str_del) {
118        if (YY_START == var) {
119            PERROR("Unterminated variable in string");
120        }
121        else if (str_ptr == str_buf) {
122            BEGIN(INITIAL);
123            return T_STR_END;
124        }
125        else {
126            /* return what we have so far and scan delimiter again */
127            *str_ptr = '\0';
128            yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
129            yyless(0);
130            str_ptr = str_buf;
131            return T_STRING;
132        }
133    }
134    else {
135        STR_APPEND(yytext[0]);
136    }
137}
138<str,var,vararg>\n {
139    PERROR("Unterminated string or variable");
140}
141<var,vararg><<EOF>> {
142    PERROR("Unterminated string or variable");
143}
144<str><<EOF>> {
145    if (!(yyextra->flags & AP_EXPR_FLAG_STRING_RESULT)) {
146        PERROR("Unterminated string or variable");
147    }
148    else {
149        *str_ptr = '\0';
150        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
151        str_ptr = str_buf;
152        BEGIN(INITIAL);
153        return T_STRING;
154    }
155}
156
157<str,vararg>\\[0-7]{1,3} {
158    int result;
159
160    (void)sscanf(yytext+1, "%o", &result);
161    if (result > 0xff) {
162        PERROR("Escape sequence out of bound");
163    }
164    else {
165        STR_APPEND(result);
166    }
167}
168<str,vararg>\\[0-9]+ {
169    PERROR("Bad escape sequence");
170}
171<str,vararg>\\n      { STR_APPEND('\n'); }
172<str,vararg>\\r      { STR_APPEND('\r'); }
173<str,vararg>\\t      { STR_APPEND('\t'); }
174<str,vararg>\\b      { STR_APPEND('\b'); }
175<str,vararg>\\f      { STR_APPEND('\f'); }
176<str,vararg>\\(.|\n) { STR_APPEND(yytext[1]); }
177
178 /* regexp backref inside string/arg */
179<str,vararg>[$][0-9] {
180    if (str_ptr != str_buf) {
181        /* return what we have so far and scan '$x' again */
182        *str_ptr = '\0';
183        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
184        str_ptr = str_buf;
185        yyless(0);
186        return T_STRING;
187    }
188    else {
189        yylval->num = yytext[1] - '0';
190        return T_REGEX_BACKREF;
191    }
192}
193
194<str,vararg>[^\\\n"'%}$]+ {
195    char *cp = yytext;
196    while (*cp != '\0') {
197        STR_APPEND(*cp);
198        cp++;
199    }
200}
201
202 /* variable inside string/arg */
203<str,vararg>%\{ {
204    if (str_ptr != str_buf) {
205        /* return what we have so far and scan '%{' again */
206        *str_ptr = '\0';
207        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
208        yyless(0);
209        str_ptr = str_buf;
210        return T_STRING;
211    }
212    else {
213        yy_push_state(var, yyscanner);
214        return T_VAR_BEGIN;
215    }
216}
217
218<vararg>[%$] {
219     STR_APPEND(yytext[0]);
220}
221
222<str>[%}$] {
223     STR_APPEND(yytext[0]);
224}
225
226%\{ {
227    yy_push_state(var, yyscanner);
228    return T_VAR_BEGIN;
229}
230
231[$][0-9] {
232    yylval->num = yytext[1] - '0';
233    return T_REGEX_BACKREF;
234}
235
236 /*
237  * fixed name variable expansion %{XXX} and function call in %{func:arg} syntax
238  */
239<var>[a-zA-Z][a-zA-Z0-9_]* {
240    yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
241    return T_ID;
242}
243
244<var>\} {
245    yy_pop_state(yyscanner);
246    return T_VAR_END;
247}
248
249<var>: {
250    BEGIN(vararg);
251    return yytext[0];
252}
253
254<var>.|\n {
255    char *msg = apr_psprintf(yyextra->pool,
256                             "Invalid character in variable name '%c'", yytext[0]);
257    PERROR(msg);
258}
259
260<vararg>\} {
261    if (str_ptr != str_buf) {
262        /* return what we have so far and scan '}' again */
263        *str_ptr = '\0';
264        yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
265        str_ptr = str_buf;
266        yyless(0);
267        return T_STRING;
268    }
269    else {
270        yy_pop_state(yyscanner);
271        return T_VAR_END;
272    }
273}
274
275 /*
276  * Regular Expression
277  */
278"m"[/#$%^,;:_\?\|\^\-\!\.\'\"] {
279    regex_del = yytext[1];
280    regex_ptr = regex_buf;
281    BEGIN(regex);
282}
283"/" {
284    regex_del = yytext[0];
285    regex_ptr = regex_buf;
286    BEGIN(regex);
287}
288<regex>.|\n {
289    if (yytext[0] == regex_del) {
290        *regex_ptr = '\0';
291        BEGIN(regex_flags);
292    }
293    else {
294        *regex_ptr++ = yytext[0];
295        if (regex_ptr >= regex_buf + sizeof(regex_buf))
296            PERROR("Regexp too long");
297    }
298}
299<regex_flags>i {
300    yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
301    BEGIN(INITIAL);
302    return T_REGEX_I;
303}
304<regex_flags>.|\n {
305    yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
306    yyless(0);
307    BEGIN(INITIAL);
308    return T_REGEX;
309}
310<regex_flags><<EOF>> {
311    yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
312    BEGIN(INITIAL);
313    return T_REGEX;
314}
315
316 /*
317  * Operators
318  */
319==?   { return T_OP_STR_EQ; }
320"!="  { return T_OP_STR_NE; }
321"<"   { return T_OP_STR_LT; }
322"<="  { return T_OP_STR_LE; }
323">"   { return T_OP_STR_GT; }
324">="  { return T_OP_STR_GE; }
325"=~"  { return T_OP_REG; }
326"!~"  { return T_OP_NRE; }
327"and" { return T_OP_AND; }
328"&&"  { return T_OP_AND; }
329"or"  { return T_OP_OR; }
330"||"  { return T_OP_OR; }
331"not" { return T_OP_NOT; }
332"!"   { return T_OP_NOT; }
333"."   { return T_OP_CONCAT; }
334"-in"  { return T_OP_IN; }
335"-eq"  { return T_OP_EQ; }
336"-ne"  { return T_OP_NE; }
337"-ge"  { return T_OP_GE; }
338"-le"  { return T_OP_LE; }
339"-gt"  { return T_OP_GT; }
340"-lt"  { return T_OP_LT; }
341
342 /* for compatibility with ssl_expr */
343"lt"  { return T_OP_LT; }
344"le"  { return T_OP_LE; }
345"gt"  { return T_OP_GT; }
346"ge"  { return T_OP_GE; }
347"ne"  { return T_OP_NE; }
348"eq"  { return T_OP_EQ; }
349"in"  { return T_OP_IN; }
350
351"-"[a-zA-Z_] {
352    yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
353    return T_OP_UNARY;
354}
355
356"-"[a-zA-Z_][a-zA-Z_0-9]+ {
357    yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
358    return T_OP_BINARY;
359}
360
361 /*
362  * Specials
363  */
364"true"  { return T_TRUE; }
365"false" { return T_FALSE; }
366
367 /*
368  * Digits
369  */
370-?[0-9]+ {
371    yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
372    return T_DIGIT;
373}
374
375 /*
376  * Identifiers
377  */
378[a-zA-Z][a-zA-Z0-9_]* {
379    yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
380    return T_ID;
381}
382
383 /*
384  * These are parts of the grammar and are returned as is
385  */
386[(){},:] {
387    return yytext[0];
388}
389
390 /*
391  * Anything else is an error
392  */
393.|\n {
394    char *msg = apr_psprintf(yyextra->pool, "Parse error near '%c'", yytext[0]);
395    PERROR(msg);
396}
397
398%%
399
400
401