]> granicus.if.org Git - apache/blob - server/util_expr_scan.l
Follow up to r1853874: CHANGES entry.
[apache] / server / util_expr_scan.l
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
38 %x str expr
39 %x var vararg
40 %x regex regsub regflags
41
42 %{
43 #include "util_expr_private.h"
44 #include "util_expr_parse.h"
45 #include "http_main.h"
46 #include "http_log.h"
47 #include "apr_lib.h"
48
49 #undef  YY_INPUT
50 #define YY_INPUT(buf,result,max_size)                       \
51 {                                                           \
52     if ((result = MIN(max_size, yyextra->inputbuf           \
53                               + yyextra->inputlen           \
54                               - yyextra->inputptr)) <= 0)   \
55     {                                                       \
56         result = YY_NULL;                                   \
57     }                                                       \
58     else {                                                  \
59         memcpy(buf, yyextra->inputptr, result);             \
60         yyextra->inputptr += result;                        \
61     }                                                       \
62 }
63
64 /*
65  * XXX: It would be nice if we could recover somehow, e.g. via
66  * XXX: longjmp. It is not clear if the scanner is in any state
67  * XXX: to be cleaned up, though.
68  */
69 static int unreachable = 0;
70 #define YY_FATAL_ERROR(msg)                                     \
71     do {                                                        \
72         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, \
73                      APLOGNO(03296)                             \
74                      "expr parser fatal error (BUG?): "         \
75                      "%s, exiting", msg);                       \
76         if (unreachable) {                                      \
77             /* Not reached, silence [-Wunused-function] */      \
78             yy_fatal_error(msg, yyscanner);                     \
79         }                                                       \
80         else {                                                  \
81             abort();                                            \
82         }                                                       \
83     } while (0)
84
85 #define YY_EXTRA_TYPE ap_expr_parse_ctx_t*
86
87 #define PERROR(msg) do {    \
88     yyextra->error2 = msg;  \
89     return T_ERROR;         \
90 } while (0)
91
92 #define PERROR_CHAR(prefix, chr) do {                                       \
93     char *msg;                                                              \
94     if (apr_isprint((chr))) {                                               \
95         msg = apr_psprintf(yyextra->pool, prefix "'%c'", (char)(chr));      \
96     }                                                                       \
97     else {                                                                  \
98         msg = apr_psprintf(yyextra->pool, prefix "'\\x%.2X'", (int)(chr));  \
99     }                                                                       \
100     PERROR(msg);                                                            \
101 } while (0)
102
103 #define STACK_PUSH() do {                               \
104     ap_expr_parser_stack_t *sk;                         \
105     if (yyextra->spares) {                              \
106         sk = yyextra->spares;                           \
107         yyextra->spares = sk->next;                     \
108     }                                                   \
109     else {                                              \
110         sk = apr_palloc(yyextra->ptemp, sizeof(*sk));   \
111     }                                                   \
112     sk->scan_ptr  = sk->scan_buf;                       \
113     sk->scan_stop = sk->scan_buf[0] = '\0';             \
114     sk->scan_flag = 0;                                  \
115     sk->next = yyextra->current;                        \
116     yyextra->current = sk;                              \
117 } while (0)
118
119 #define STACK_POP() do {            \
120     ap_expr_parser_stack_t *sk;     \
121     sk = yyextra->current;          \
122     yyextra->current = sk->next;    \
123     sk->next = yyextra->spares;     \
124     yyextra->spares = sk;           \
125 } while (0)
126
127 #define STATE_PUSH(st, sk) do {     \
128     yy_push_state((st), yyscanner); \
129     if ((sk)) {                     \
130         STACK_PUSH();               \
131     }                               \
132 } while (0)
133
134 #define STATE_POP(sk) do {          \
135     if ((sk)) {                     \
136         STACK_POP();                \
137     }                               \
138     yy_pop_state(yyscanner);        \
139 } while (0)
140
141 #define str_ptr  (yyextra->current->scan_ptr)
142 #define str_buf  (yyextra->current->scan_buf)
143 #define str_stop (yyextra->current->scan_stop)
144 #define str_flag (yyextra->current->scan_flag)
145
146 #define STR_APPEND_CHECK(chr, chk) do {                     \
147     if ((chk) && apr_iscntrl((chr))) {                      \
148         PERROR_CHAR("Invalid string character ", (chr));    \
149     }                                                       \
150     if (str_ptr >= str_buf + sizeof(str_buf) - 1) {         \
151         PERROR("String too long");                          \
152     }                                                       \
153     *str_ptr++ = (char)(chr);                               \
154 } while (0)
155
156 #define STR_APPEND_NOCHECK(chr) \
157     STR_APPEND_CHECK((chr), 0)
158
159 #define STR_EMPTY() \
160     (str_ptr == str_buf)
161
162 #define STR_RETURN() \
163     (apr_pstrdup(yyextra->pool, (*str_ptr = '\0', str_ptr = str_buf)))
164
165 %}
166
167 ANY          (.|\n)
168 SPACE        [ \t\n]
169 QUOTE        ["']
170 TOKEN        ([a-zA-Z][a-zA-Z0-9_]*)
171 VAR_BEGIN    (\%\{)
172 VAR_ARG      (\:)
173 VAR_END      (\})
174 VAREXP_BEGIN (\%\{\:)
175 VAREXP_END   (\:\})
176 REG_SEP      [/#$%^|?!'",;:._-]
177 BACKREF      (\$[0-9])
178
179 %%
180
181 %{
182  /*
183   * Set initial state for string expressions
184   */
185   if (yyextra->at_start) {
186     yyextra->at_start = 0;
187     if (yyextra->flags & AP_EXPR_FLAG_STRING_RESULT) {
188         STATE_PUSH(str, 1);
189         return T_EXPR_STRING;
190     }
191     else {
192         STATE_PUSH(expr, 1);
193         return T_EXPR_BOOL;
194     }
195   }
196 %}
197
198  /*
199   * Back off INITIAL pushes
200   */
201 <str><<EOF>> {
202     STATE_POP(0); /* <str> */
203     if (YY_START != INITIAL) {
204         PERROR("Unterminated string");
205     }
206     yylval->cpVal = STR_RETURN();
207     STACK_POP(); /* ^ after this */
208     return T_STRING;
209 }
210 <expr><<EOF>> {
211     STATE_POP(1); /* <expr> */
212     if (YY_START != INITIAL) {
213         PERROR("Unterminated expression");
214     }
215 }
216
217 <str>{QUOTE} {
218     if (yytext[0] == str_stop) {
219         if (!STR_EMPTY()) {
220             yyless(0); /* come back below */
221             yylval->cpVal = STR_RETURN();
222             return T_STRING;
223         }
224         STATE_POP(1); /* <str> */
225         return T_STR_END;
226     }
227     STR_APPEND_NOCHECK(yytext[0]);
228 }
229
230  /* regexp backref inside string/arg */
231 <str,vararg,regsub>{BACKREF} {
232     if (!STR_EMPTY()) {
233         yyless(0); /* come back below */
234         yylval->cpVal = STR_RETURN();
235         return T_STRING;
236     }
237     yylval->num = yytext[1] - '0';
238     return T_BACKREF;
239 }
240
241  /* variable inside string/arg */
242 <str,vararg,regsub>{VAR_BEGIN} {
243     if (!STR_EMPTY()) {
244         yyless(0); /* come back below */
245         yylval->cpVal = STR_RETURN();
246         return T_STRING;
247     }
248     STATE_PUSH(var, 1);
249     return T_VAR_BEGIN;
250 }
251
252 <str,vararg,regsub>{VAREXP_BEGIN} {
253     if (!STR_EMPTY()) {
254         yyless(0); /* come back below */
255         yylval->cpVal = STR_RETURN();
256         return T_STRING;
257     }
258     STATE_PUSH(expr, 1);
259     return T_VAREXP_BEGIN;
260 }
261
262  /* Any non-octal or octal higher than 377 (decimal 255) is invalid */
263 <str,vararg,regsub>\\([4-9][0-9]{2}|[8-9][0-9]{0,2}) {
264     PERROR("Bad character escape sequence");
265 }
266 <str,vararg,regsub>\\[0-7]{1,3} {
267     int result;
268     (void)sscanf(yytext+1, "%o", &result);
269     STR_APPEND_NOCHECK(result);
270 }
271 <str,vararg,regsub>\\x[0-9A-Fa-f]{1,2} {
272     int result;
273     (void)sscanf(yytext+1, "%x", &result);
274     STR_APPEND_NOCHECK(result);
275 }
276 <str,vararg,regsub>\\n  { STR_APPEND_NOCHECK('\n'); }
277 <str,vararg,regsub>\\r  { STR_APPEND_NOCHECK('\r'); }
278 <str,vararg,regsub>\\t  { STR_APPEND_NOCHECK('\t'); }
279 <str,vararg,regsub>\\b  { STR_APPEND_NOCHECK('\b'); }
280 <str,vararg,regsub>\\f  { STR_APPEND_NOCHECK('\f'); }
281 <str,vararg,regsub>\\.  { STR_APPEND_CHECK(yytext[1], 1); }
282
283 <str,vararg,regsub>\n {
284     PERROR("Unterminated string or variable");
285 }
286
287 <str>{ANY} {
288     STR_APPEND_CHECK(yytext[0], 1);
289 }
290
291 <expr>{SPACE}+ { 
292     /* NOP */
293 }
294
295 <expr>{QUOTE} {
296     STATE_PUSH(str, 1);
297     str_stop = yytext[0];
298     return T_STR_BEGIN;
299 }
300
301 <expr>{VAREXP_BEGIN} {
302     STATE_PUSH(expr, 1);
303     return T_VAREXP_BEGIN;
304 }
305 <expr>{VAREXP_END} {
306     STATE_POP(1); /* <expr> */
307     return T_VAREXP_END;
308 }
309
310
311 <expr>{VAR_BEGIN} {
312     STATE_PUSH(var, 1);
313     return T_VAR_BEGIN;
314 }
315 <var>{TOKEN} {
316     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
317     return T_ID;
318 }
319 <var>{VAR_ARG} {
320     STATE_PUSH(vararg, 0);
321     return yytext[0];
322 }
323 <vararg>{VAR_END} {
324     yyless(0); /* let <var> handle */
325     yylval->cpVal = STR_RETURN();
326     STATE_POP(0); /* <vararg> */
327     return T_STRING;
328 }
329 <vararg>{ANY} {
330     STR_APPEND_CHECK(yytext[0], 1);
331 }
332 <var>{VAR_END} {
333     STATE_POP(1); /* <var> */
334     return T_VAR_END;
335 }
336 <var>{ANY} {
337     PERROR_CHAR("Unexpected variable character ", yytext[0]);
338 }
339 <var,vararg><<EOF>> {
340     PERROR("Unterminated variable");
341 }
342
343
344  /*
345   * Regular Expression
346   */
347 <expr>\/ {
348     STATE_PUSH(regex, 1);
349     str_stop = yytext[0];
350     str_flag = 'm';
351     return T_REGEX;
352 }
353 <expr>[ms]{REG_SEP} {
354     STATE_PUSH(regex, 1);
355     str_stop = yytext[1];
356     str_flag = yytext[0];
357     return (str_flag == 'm') ? T_REGEX : T_REGSUB;
358 }
359 <regex>{ANY} {
360     if (yytext[0] == str_stop) {
361         yylval->cpVal = STR_RETURN();
362         STATE_POP(0); /* <regex> */
363         if (str_flag == 'm') {
364             STATE_PUSH(regflags, 0);
365         }
366         else {
367             STATE_PUSH(regsub, 0);
368         }
369         return T_REG_MATCH;
370     }
371     STR_APPEND_CHECK(yytext[0], 1);
372 }
373 <regsub>{ANY} {
374     if (yytext[0] == str_stop) {
375         yylval->cpVal = STR_RETURN();
376         STATE_POP(0); /* <regsub> */
377         STATE_PUSH(regflags, 0);
378         return T_STRING;
379     }
380     STR_APPEND_CHECK(yytext[0], 1);
381 }
382 <regflags>{ANY} {
383     if (!ap_strchr_c("ismg", yytext[0])) {
384         if (apr_isalnum(yytext[0])) {
385             PERROR("Invalid regexp flag(s)");
386         }
387         yyless(0); /* not a flags, rewind */
388         yylval->cpVal = STR_RETURN();
389         STATE_POP(1); /* <regflags> */
390         return T_REG_FLAGS;
391     }
392     STR_APPEND_NOCHECK(yytext[0]);
393 }
394 <regflags><<EOF>> {
395     yylval->cpVal = STR_RETURN();
396     STATE_POP(1); /* <regflags> */
397     return T_REG_FLAGS;
398 }
399 <regex,regsub><<EOF>> {
400     PERROR("Unterminated regexp");
401 }
402
403 <expr>{BACKREF} {
404     yylval->num = yytext[1] - '0';
405     return T_BACKREF;
406 }
407
408  /*
409   * Operators
410   */
411 <expr>==?   { return T_OP_STR_EQ; }
412 <expr>"!="  { return T_OP_STR_NE; }
413 <expr>"<"   { return T_OP_STR_LT; }
414 <expr>"<="  { return T_OP_STR_LE; }
415 <expr>">"   { return T_OP_STR_GT; }
416 <expr>">="  { return T_OP_STR_GE; }
417 <expr>"=~"  { return T_OP_REG; }
418 <expr>"!~"  { return T_OP_NRE; }
419 <expr>"and" { return T_OP_AND; }
420 <expr>"&&"  { return T_OP_AND; }
421 <expr>"or"  { return T_OP_OR; }
422 <expr>"||"  { return T_OP_OR; }
423 <expr>"not" { return T_OP_NOT; }
424 <expr>"!"   { return T_OP_NOT; }
425 <expr>"."   { return T_OP_CONCAT; }
426 <expr>"-in" { return T_OP_IN; }
427 <expr>"-eq" { return T_OP_EQ; }
428 <expr>"-ne" { return T_OP_NE; }
429 <expr>"-ge" { return T_OP_GE; }
430 <expr>"-le" { return T_OP_LE; }
431 <expr>"-gt" { return T_OP_GT; }
432 <expr>"-lt" { return T_OP_LT; }
433
434  /* for compatibility with ssl_expr */
435 <expr>"lt"  { return T_OP_LT; }
436 <expr>"le"  { return T_OP_LE; }
437 <expr>"gt"  { return T_OP_GT; }
438 <expr>"ge"  { return T_OP_GE; }
439 <expr>"ne"  { return T_OP_NE; }
440 <expr>"eq"  { return T_OP_EQ; }
441 <expr>"in"  { return T_OP_IN; }
442
443 <expr>"-"[a-zA-Z_][a-zA-Z_0-9]+ {
444     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
445     return T_OP_BINARY;
446 }
447
448 <expr>"-"[a-zA-Z_] {
449     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
450     return T_OP_UNARY;
451 }
452
453  /* Apply subtitution to a string */
454 <expr>"sub"   {
455     return T_OP_SUB;
456 }
457
458  /* Join a list into a string */
459 <expr>"join"  {
460     return T_OP_JOIN;
461 }
462
463  /* Split a string (or list) into a(nother) list */
464 <expr>"split" {
465     return T_OP_SPLIT;
466 }
467
468  /*
469   * Specials
470   */
471 <expr>"true"  { return T_TRUE; }
472 <expr>"false" { return T_FALSE; }
473
474  /*
475   * Digits
476   */
477 <expr>\-?[0-9]+ {
478     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
479     return T_DIGIT;
480 }
481
482  /*
483   * Identifiers
484   */
485 <expr>{TOKEN} {
486     yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
487     return T_ID;
488 }
489
490  /*
491   * These are parts of the grammar and are returned as is
492   */
493 <expr>[(){},] {
494     return yytext[0];
495 }
496
497  /*
498   * Anything else is an error
499   */
500 <INITIAL,expr>{ANY} {
501     PERROR_CHAR("Parse error near character ", yytext[0]);
502 }
503
504 %%
505
506