1 : /*
2 : +----------------------------------------------------------------------+
3 : | Zend Engine |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1998-2009 Zend Technologies Ltd. (http://www.zend.com) |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 2.00 of the Zend license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.zend.com/license/2_00.txt. |
11 : | If you did not receive a copy of the Zend license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@zend.com so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Authors: Zeev Suraski <zeev@zend.com> |
16 : | Jani Taskinen <jani@php.net> |
17 : | Marcus Boerger <helly@php.net> |
18 : | Nuno Lopes <nlopess@php.net> |
19 : | Scott MacVicar <scottmac@php.net> |
20 : +----------------------------------------------------------------------+
21 : */
22 :
23 : /* $Id: zend_ini_scanner.l 286913 2009-08-07 15:44:37Z jani $ */
24 :
25 : #include <errno.h>
26 : #include "zend.h"
27 : #include "zend_globals.h"
28 : #include <zend_ini_parser.h>
29 : #include "zend_ini_scanner.h"
30 :
31 : #if 0
32 : # define YYDEBUG(s, c) printf("state: %d char: %c\n", s, c)
33 : #else
34 : # define YYDEBUG(s, c)
35 : #endif
36 :
37 : #include "zend_ini_scanner_defs.h"
38 :
39 : #define YYCTYPE unsigned char
40 : /* allow the scanner to read one null byte after the end of the string (from ZEND_MMAP_AHEAD)
41 : * so that if will be able to terminate to match the current token (e.g. non-enclosed string) */
42 : #define YYFILL(n) { if (YYCURSOR > YYLIMIT) return 0; }
43 : #define YYCURSOR SCNG(yy_cursor)
44 : #define YYLIMIT SCNG(yy_limit)
45 : #define YYMARKER SCNG(yy_marker)
46 :
47 : #define YYGETCONDITION() SCNG(yy_state)
48 : #define YYSETCONDITION(s) SCNG(yy_state) = s
49 :
50 : #define STATE(name) yyc##name
51 :
52 : /* emulate flex constructs */
53 : #define BEGIN(state) YYSETCONDITION(STATE(state))
54 : #define YYSTATE YYGETCONDITION()
55 : #define yytext ((char*)SCNG(yy_text))
56 : #define yyleng SCNG(yy_leng)
57 : #define yyless(x) do { YYCURSOR = (unsigned char*)yytext + x; \
58 : yyleng = (unsigned int)x; } while(0)
59 :
60 : /* #define yymore() goto yymore_restart */
61 :
62 : /* perform sanity check. If this message is triggered you should
63 : increase the ZEND_MMAP_AHEAD value in the zend_streams.h file */
64 : /*!max:re2c */
65 : #if ZEND_MMAP_AHEAD < (YYMAXFILL + 1)
66 : # error ZEND_MMAP_AHEAD should be greater than YYMAXFILL
67 : #endif
68 :
69 :
70 : /* How it works (for the core ini directives):
71 : * ===========================================
72 : *
73 : * 1. Scanner scans file for tokens and passes them to parser.
74 : * 2. Parser parses the tokens and passes the name/value pairs to the callback
75 : * function which stores them in the configuration hash table.
76 : * 3. Later REGISTER_INI_ENTRIES() is called which triggers the actual
77 : * registering of ini entries and uses zend_get_configuration_directive()
78 : * to fetch the previously stored name/value pair from configuration hash table
79 : * and registers the static ini entries which match the name to the value
80 : * into EG(ini_directives) hash table.
81 : * 4. PATH section entries are used per-request from down to top, each overriding
82 : * previous if one exists. zend_alter_ini_entry() is called for each entry.
83 : * Settings in PATH section are ZEND_INI_SYSTEM accessible and thus mimics the
84 : * php_admin_* directives used within Apache httpd.conf when PHP is compiled as
85 : * module for Apache.
86 : * 5. User defined ini files (like .htaccess for apache) are parsed for each request and
87 : * stored in separate hash defined by SAPI.
88 : */
89 :
90 : /* TODO: (ordered by importance :-)
91 : * ===============================================================================
92 : *
93 : * - Separate constant lookup totally from plain strings (using CONSTANT pattern)
94 : * - Add #if .. #else .. #endif and ==, !=, <, > , <=, >= operators
95 : * - Add #include "some.ini"
96 : * - Allow variables to refer to options also when using parse_ini_file()
97 : *
98 : */
99 :
100 : /* Globals Macros */
101 : #define SCNG INI_SCNG
102 : #ifdef ZTS
103 : ZEND_API ts_rsrc_id ini_scanner_globals_id;
104 : #else
105 : ZEND_API zend_ini_scanner_globals ini_scanner_globals;
106 : #endif
107 :
108 : /* Eat trailing whitespace + extra char */
109 : #define EAT_TRAILING_WHITESPACE_EX(ch) \
110 : while (yyleng > 0 && ( \
111 : (ch != 'X' && yytext[yyleng - 1] == ch) || \
112 : yytext[yyleng - 1] == '\n' || \
113 : yytext[yyleng - 1] == '\r' || \
114 : yytext[yyleng - 1] == '\t' || \
115 : yytext[yyleng - 1] == ' ') \
116 : ) { \
117 : yyleng--; \
118 : }
119 :
120 : /* Eat trailing whitespace */
121 : #define EAT_TRAILING_WHITESPACE() EAT_TRAILING_WHITESPACE_EX('X')
122 :
123 : #define zend_ini_copy_value(retval, str, len) { \
124 : Z_STRVAL_P(retval) = zend_strndup(str, len); \
125 : Z_STRLEN_P(retval) = len; \
126 : Z_TYPE_P(retval) = IS_STRING; \
127 : }
128 :
129 : #define RETURN_TOKEN(type, str, len) { \
130 : zend_ini_copy_value(ini_lval, str, len); \
131 : return type; \
132 : }
133 :
134 : static void _yy_push_state(int new_state TSRMLS_DC)
135 783969 : {
136 783969 : zend_stack_push(&SCNG(state_stack), (void *) &YYGETCONDITION(), sizeof(int));
137 783969 : YYSETCONDITION(new_state);
138 783969 : }
139 :
140 : #define yy_push_state(state_and_tsrm) _yy_push_state(yyc##state_and_tsrm)
141 :
142 : static void yy_pop_state(TSRMLS_D)
143 18165 : {
144 : int *stack_state;
145 18165 : zend_stack_top(&SCNG(state_stack), (void **) &stack_state);
146 18165 : YYSETCONDITION(*stack_state);
147 18165 : zend_stack_del_top(&SCNG(state_stack));
148 18165 : }
149 :
150 : static void yy_scan_buffer(char *str, unsigned int len TSRMLS_DC)
151 35227 : {
152 35227 : YYCURSOR = (YYCTYPE*)str;
153 35227 : SCNG(yy_start) = YYCURSOR;
154 35227 : YYLIMIT = YYCURSOR + len;
155 35227 : }
156 :
157 : #define ini_filename SCNG(filename)
158 :
159 : /* {{{ init_ini_scanner()
160 : */
161 : static int init_ini_scanner(int scanner_mode, zend_file_handle *fh TSRMLS_DC)
162 35227 : {
163 : /* Sanity check */
164 35227 : if (scanner_mode != ZEND_INI_SCANNER_NORMAL && scanner_mode != ZEND_INI_SCANNER_RAW) {
165 0 : zend_error(E_WARNING, "Invalid scanner mode");
166 0 : return FAILURE;
167 : }
168 :
169 35227 : SCNG(lineno) = 1;
170 35227 : SCNG(scanner_mode) = scanner_mode;
171 35227 : SCNG(yy_in) = fh;
172 :
173 35227 : if (fh != NULL) {
174 17590 : ini_filename = zend_strndup(fh->filename, strlen(fh->filename));
175 : } else {
176 17637 : ini_filename = NULL;
177 : }
178 :
179 35227 : zend_stack_init(&SCNG(state_stack));
180 35227 : BEGIN(INITIAL);
181 :
182 35227 : return SUCCESS;
183 : }
184 : /* }}} */
185 :
186 : /* {{{ shutdown_ini_scanner()
187 : */
188 : void shutdown_ini_scanner(TSRMLS_D)
189 35227 : {
190 35227 : zend_stack_destroy(&SCNG(state_stack));
191 35227 : if (ini_filename) {
192 17590 : free(ini_filename);
193 : }
194 35227 : }
195 : /* }}} */
196 :
197 : /* {{{ zend_ini_scanner_get_lineno()
198 : */
199 : int zend_ini_scanner_get_lineno(TSRMLS_D)
200 4 : {
201 4 : return SCNG(lineno);
202 : }
203 : /* }}} */
204 :
205 : /* {{{ zend_ini_scanner_get_filename()
206 : */
207 : char *zend_ini_scanner_get_filename(TSRMLS_D)
208 5 : {
209 5 : return ini_filename ? ini_filename : "Unknown";
210 : }
211 : /* }}} */
212 :
213 : /* {{{ zend_ini_open_file_for_scanning()
214 : */
215 : int zend_ini_open_file_for_scanning(zend_file_handle *fh, int scanner_mode TSRMLS_DC)
216 17609 : {
217 : char *buf;
218 : size_t size;
219 :
220 17609 : if (zend_stream_fixup(fh, &buf, &size TSRMLS_CC) == FAILURE ||
221 : init_ini_scanner(scanner_mode, fh TSRMLS_CC) == FAILURE
222 : ) {
223 19 : return FAILURE;
224 : }
225 :
226 17590 : yy_scan_buffer(buf, size TSRMLS_CC);
227 :
228 17590 : return SUCCESS;
229 : }
230 : /* }}} */
231 :
232 : /* {{{ zend_ini_prepare_string_for_scanning()
233 : */
234 : int zend_ini_prepare_string_for_scanning(char *str, int scanner_mode TSRMLS_DC)
235 17637 : {
236 17637 : int len = strlen(str);
237 :
238 17637 : if (init_ini_scanner(scanner_mode, NULL TSRMLS_CC) == FAILURE) {
239 0 : return FAILURE;
240 : }
241 :
242 17637 : yy_scan_buffer(str, len TSRMLS_CC);
243 :
244 17637 : return SUCCESS;
245 : }
246 : /* }}} */
247 :
248 : /* {{{ zend_ini_escape_string()
249 : */
250 : static void zend_ini_escape_string(zval *lval, char *str, int len, char quote_type TSRMLS_DC)
251 18134 : {
252 : register char *s, *t;
253 : char *end;
254 :
255 18134 : zend_ini_copy_value(lval, str, len);
256 :
257 : /* convert escape sequences */
258 18134 : s = t = Z_STRVAL_P(lval);
259 18134 : end = s + Z_STRLEN_P(lval);
260 :
261 135828 : while (s < end) {
262 99560 : if (*s == '\\') {
263 16 : s++;
264 16 : if (s >= end) {
265 1 : *t++ = '\\';
266 1 : continue;
267 : }
268 15 : switch (*s) {
269 : case '"':
270 1 : if (*s != quote_type) {
271 0 : *t++ = '\\';
272 0 : *t++ = *s;
273 0 : break;
274 : }
275 : case '\\':
276 : case '$':
277 3 : *t++ = *s;
278 3 : Z_STRLEN_P(lval)--;
279 3 : break;
280 : default:
281 12 : *t++ = '\\';
282 12 : *t++ = *s;
283 : break;
284 : }
285 : } else {
286 99544 : *t++ = *s;
287 : }
288 99559 : if (*s == '\n' || (*s == '\r' && (*(s+1) != '\n'))) {
289 30 : SCNG(lineno)++;
290 : }
291 99559 : s++;
292 : }
293 18134 : *t = 0;
294 18134 : }
295 : /* }}} */
296 :
297 : int ini_lex(zval *ini_lval TSRMLS_DC)
298 2997793 : {
299 2997793 : restart:
300 2997793 : SCNG(yy_text) = YYCURSOR;
301 :
302 : /* yymore_restart: */
303 : /* detect EOF */
304 2997793 : if (YYCURSOR >= YYLIMIT) {
305 35218 : if (YYSTATE == STATE(ST_VALUE) || YYSTATE == STATE(ST_RAW)) {
306 48 : BEGIN(INITIAL);
307 48 : return 0;
308 : }
309 35170 : return 0;
310 : }
311 :
312 : /* Eat any UTF-8 BOM we find in the first 3 bytes */
313 2962575 : if (YYCURSOR == SCNG(yy_start) && YYCURSOR + 3 < YYLIMIT) {
314 35227 : if (memcmp(YYCURSOR, "\xef\xbb\xbf", 3) == 0) {
315 0 : YYCURSOR += 3;
316 0 : goto restart;
317 : }
318 : }
319 : /*!re2c
320 : re2c:yyfill:check = 0;
321 : LNUM [0-9]+
322 : DNUM ([0-9]*[\.][0-9]+)|([0-9]+[\.][0-9]*)
323 : NUMBER [-]?{LNUM}|{DNUM}
324 : ANY_CHAR (.|[\n\t])
325 : NEWLINE ("\r"|"\n"|"\r\n")
326 : TABS_AND_SPACES [ \t]
327 : WHITESPACE [ \t]+
328 : CONSTANT [a-zA-Z][a-zA-Z0-9_]*
329 : LABEL [^=\n\r\t ;|&$~(){}!"\[]+
330 : TOKENS [:,.\[\]"'()|^&+-/*=%$!~<>?@{}]
331 : OPERATORS [&|~()!]
332 : DOLLAR_CURLY "${"
333 :
334 : SECTION_RAW_CHARS [^\]\n\r]
335 : SINGLE_QUOTED_CHARS [^']
336 : RAW_VALUE_CHARS [^\n\r;]
337 :
338 : LITERAL_DOLLAR ("$"([^{\000]|("\\"{ANY_CHAR})))
339 : VALUE_CHARS ([^$= \t\n\r;&|~()!"'\000]|{LITERAL_DOLLAR})
340 : SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR})
341 :
342 : <!*> := yyleng = YYCURSOR - SCNG(yy_text);
343 :
344 : <INITIAL>"[" { /* Section start */
345 : /* Enter section data lookup state */
346 19304 : if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
347 19189 : yy_push_state(ST_SECTION_RAW TSRMLS_CC);
348 : } else {
349 115 : yy_push_state(ST_SECTION_VALUE TSRMLS_CC);
350 : }
351 19304 : return TC_SECTION;
352 : }
353 :
354 : <ST_VALUE,ST_SECTION_VALUE,ST_OFFSET>"'"{SINGLE_QUOTED_CHARS}+"'" { /* Raw string */
355 : /* Eat leading and trailing single quotes */
356 9 : if (yytext[0] == '\'' && yytext[yyleng - 1] == '\'') {
357 9 : SCNG(yy_text)++;
358 9 : yyleng = yyleng - 2;
359 : }
360 9 : RETURN_TOKEN(TC_RAW, yytext, yyleng);
361 : }
362 :
363 : <ST_SECTION_RAW,ST_SECTION_VALUE>"]"{TABS_AND_SPACES}*{NEWLINE}? { /* End of section */
364 19304 : BEGIN(INITIAL);
365 19304 : SCNG(lineno)++;
366 19304 : return ']';
367 : }
368 :
369 : <INITIAL>{LABEL}"["{TABS_AND_SPACES}* { /* Start of option with offset */
370 : /* Eat trailing whitespace and [ */
371 21 : EAT_TRAILING_WHITESPACE_EX('[');
372 :
373 : /* Enter offset lookup state */
374 21 : yy_push_state(ST_OFFSET TSRMLS_CC);
375 :
376 21 : RETURN_TOKEN(TC_OFFSET, yytext, yyleng);
377 : }
378 :
379 : <ST_OFFSET>{TABS_AND_SPACES}*"]" { /* End of section or an option offset */
380 21 : BEGIN(INITIAL);
381 21 : return ']';
382 : }
383 :
384 : <ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{DOLLAR_CURLY} { /* Variable start */
385 26 : yy_push_state(ST_VARNAME TSRMLS_CC);
386 26 : return TC_DOLLAR_CURLY;
387 : }
388 :
389 : <ST_VARNAME>{LABEL} { /* Variable name */
390 26 : RETURN_TOKEN(TC_VARNAME, yytext, yyleng);
391 : }
392 :
393 : <ST_VARNAME>"}" { /* Variable end */
394 26 : yy_pop_state(TSRMLS_C);
395 26 : return '}';
396 : }
397 :
398 : <INITIAL,ST_VALUE>("true"|"on"|"yes"){TABS_AND_SPACES}* { /* TRUE value (when used outside option value/offset this causes parse error!) */
399 106 : RETURN_TOKEN(BOOL_TRUE, "1", 1);
400 : }
401 :
402 : <INITIAL,ST_VALUE>("false"|"off"|"no"|"none"|"null"){TABS_AND_SPACES}* { /* FALSE value (when used outside option value/offset this causes parse error!)*/
403 35029 : RETURN_TOKEN(BOOL_FALSE, "", 0);
404 : }
405 :
406 : <INITIAL>{LABEL} { /* Get option name */
407 746458 : RETURN_TOKEN(TC_LABEL, yytext, yyleng);
408 : }
409 :
410 : <INITIAL>{TABS_AND_SPACES}*[=]{TABS_AND_SPACES}* { /* Start option value */
411 746479 : if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
412 60600 : yy_push_state(ST_RAW TSRMLS_CC);
413 : } else {
414 685879 : yy_push_state(ST_VALUE TSRMLS_CC);
415 : }
416 746479 : return '=';
417 : }
418 :
419 : <ST_RAW>{RAW_VALUE_CHARS}+ { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
420 : /* Eat leading and trailing double quotes */
421 60594 : if (yytext[0] == '"' && yytext[yyleng - 1] == '"') {
422 7384 : SCNG(yy_text)++;
423 7384 : yyleng = yyleng - 2;
424 : }
425 60594 : RETURN_TOKEN(TC_RAW, yytext, yyleng);
426 : }
427 :
428 : <ST_SECTION_RAW>{SECTION_RAW_CHARS}+ { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
429 19189 : RETURN_TOKEN(TC_RAW, yytext, yyleng);
430 : }
431 :
432 : <ST_VALUE,ST_RAW>{TABS_AND_SPACES}*{NEWLINE} { /* End of option value */
433 746409 : BEGIN(INITIAL);
434 746409 : SCNG(lineno)++;
435 746409 : return END_OF_LINE;
436 : }
437 :
438 : <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{CONSTANT} { /* Get constant option value */
439 35840 : RETURN_TOKEN(TC_CONSTANT, yytext, yyleng);
440 : }
441 :
442 : <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{NUMBER} { /* Get number option value as string */
443 368896 : RETURN_TOKEN(TC_NUMBER, yytext, yyleng);
444 : }
445 :
446 : <INITIAL>{TOKENS} { /* Disallow these chars outside option values */
447 0 : return yytext[0];
448 : }
449 :
450 : <ST_VALUE>{OPERATORS}{TABS_AND_SPACES}* { /* Boolean operators */
451 114 : return yytext[0];
452 : }
453 :
454 : <ST_VALUE>[=] { /* Make = used in option value to trigger error */
455 4 : yyless(0);
456 4 : BEGIN(INITIAL);
457 4 : return END_OF_LINE;
458 : }
459 :
460 : <ST_VALUE>{VALUE_CHARS}+ { /* Get everything else as option/offset value */
461 87991 : RETURN_TOKEN(TC_STRING, yytext, yyleng);
462 : }
463 :
464 : <ST_SECTION_VALUE,ST_OFFSET>{SECTION_VALUE_CHARS}+ { /* Get rest as section/offset value */
465 30 : RETURN_TOKEN(TC_STRING, yytext, yyleng);
466 : }
467 :
468 : <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{TABS_AND_SPACES}*["] { /* Double quoted '"' string start */
469 18139 : yy_push_state(ST_DOUBLE_QUOTES TSRMLS_CC);
470 18139 : return '"';
471 : }
472 :
473 : <ST_DOUBLE_QUOTES>["]{TABS_AND_SPACES}* { /* Double quoted '"' string ends */
474 18139 : yy_pop_state(TSRMLS_C);
475 18139 : return '"';
476 : }
477 :
478 : <ST_DOUBLE_QUOTES>[^] { /* Escape double quoted string contents */
479 18134 : if (YYCURSOR > YYLIMIT) {
480 0 : return 0;
481 : }
482 :
483 117695 : while (YYCURSOR < YYLIMIT) {
484 99561 : switch (*YYCURSOR++) {
485 : case '"':
486 18128 : if (YYCURSOR < YYLIMIT && YYCURSOR[-2] == '\\' && *YYCURSOR != '\r' && *YYCURSOR != '\n') {
487 1 : continue;
488 : }
489 18127 : break;
490 : case '$':
491 13 : if (*YYCURSOR == '{') {
492 7 : break;
493 : }
494 6 : continue;
495 : case '\\':
496 16 : if (YYCURSOR < YYLIMIT && *YYCURSOR != '"') {
497 14 : YYCURSOR++;
498 : }
499 : /* fall through */
500 : default:
501 81420 : continue;
502 : }
503 :
504 18134 : YYCURSOR--;
505 18134 : break;
506 : }
507 :
508 18134 : yyleng = YYCURSOR - SCNG(yy_text);
509 :
510 18134 : zend_ini_escape_string(ini_lval, yytext, yyleng, '"' TSRMLS_CC);
511 18134 : return TC_QUOTED_STRING;
512 : }
513 :
514 : <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{WHITESPACE} {
515 159 : RETURN_TOKEN(TC_WHITESPACE, yytext, yyleng);
516 : }
517 :
518 : <INITIAL,ST_RAW>{TABS_AND_SPACES}+ {
519 : /* eat whitespace */
520 3 : goto restart;
521 : }
522 :
523 : <INITIAL>{TABS_AND_SPACES}*{NEWLINE} {
524 20592 : SCNG(lineno)++;
525 20592 : return END_OF_LINE;
526 : }
527 :
528 : <INITIAL,ST_VALUE,ST_RAW>{TABS_AND_SPACES}*[;][^\r\n]*{NEWLINE} { /* Comment */
529 1527 : BEGIN(INITIAL);
530 1527 : SCNG(lineno)++;
531 1527 : return END_OF_LINE;
532 : }
533 :
534 : <INITIAL>{TABS_AND_SPACES}*[#][^\r\n]*{NEWLINE} { /* #Comment */
535 1 : zend_error(E_DEPRECATED, "Comments starting with '#' are deprecated in %s on line %d", zend_ini_scanner_get_filename(TSRMLS_C), SCNG(lineno));
536 1 : BEGIN(INITIAL);
537 1 : SCNG(lineno)++;
538 1 : return END_OF_LINE;
539 : }
540 :
541 : <ST_VALUE,ST_RAW>[^] { /* End of option value (if EOF is reached before EOL */
542 0 : BEGIN(INITIAL);
543 0 : return END_OF_LINE;
544 : }
545 :
546 : <*>[^] {
547 0 : return 0;
548 : }
549 :
550 : */
551 : }
|