2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2013 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 +----------------------------------------------------------------------+
27 #include "zend_globals.h"
28 #include <zend_ini_parser.h>
29 #include "zend_ini_scanner.h"
32 # define YYDEBUG(s, c) printf("state: %d char: %c\n", s, c)
34 # define YYDEBUG(s, c)
37 #include "zend_ini_scanner_defs.h"
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)
47 #define YYGETCONDITION() SCNG(yy_state)
48 #define YYSETCONDITION(s) SCNG(yy_state) = s
50 #define STATE(name) yyc##name
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)
60 /* #define yymore() goto yymore_restart */
62 /* perform sanity check. If this message is triggered you should
63 increase the ZEND_MMAP_AHEAD value in the zend_streams.h file */
65 #if ZEND_MMAP_AHEAD < (YYMAXFILL + 1)
66 # error ZEND_MMAP_AHEAD should be greater than YYMAXFILL
70 /* How it works (for the core ini directives):
71 * ===========================================
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
86 * 5. User defined ini files (like .htaccess for apache) are parsed for each request and
87 * stored in separate hash defined by SAPI.
90 /* TODO: (ordered by importance :-)
91 * ===============================================================================
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()
101 #define SCNG INI_SCNG
103 ZEND_API ts_rsrc_id ini_scanner_globals_id;
105 ZEND_API zend_ini_scanner_globals ini_scanner_globals;
108 /* Eat leading whitespace */
109 #define EAT_LEADING_WHITESPACE() \
110 while (yytext[0]) { \
111 if (yytext[0] == ' ' || yytext[0] == '\t') { \
119 /* Eat trailing whitespace + extra char */
120 #define EAT_TRAILING_WHITESPACE_EX(ch) \
121 while (yyleng > 0 && ( \
122 (ch != 'X' && yytext[yyleng - 1] == ch) || \
123 yytext[yyleng - 1] == '\n' || \
124 yytext[yyleng - 1] == '\r' || \
125 yytext[yyleng - 1] == '\t' || \
126 yytext[yyleng - 1] == ' ') \
131 /* Eat trailing whitespace */
132 #define EAT_TRAILING_WHITESPACE() EAT_TRAILING_WHITESPACE_EX('X')
134 #define zend_ini_copy_value(retval, str, len) { \
135 Z_STRVAL_P(retval) = zend_strndup(str, len); \
136 Z_STRLEN_P(retval) = len; \
137 Z_TYPE_P(retval) = IS_STRING; \
140 #define RETURN_TOKEN(type, str, len) { \
141 zend_ini_copy_value(ini_lval, str, len); \
145 static void _yy_push_state(int new_state TSRMLS_DC)
147 zend_stack_push(&SCNG(state_stack), (void *) &YYGETCONDITION(), sizeof(int));
148 YYSETCONDITION(new_state);
151 #define yy_push_state(state_and_tsrm) _yy_push_state(yyc##state_and_tsrm)
153 static void yy_pop_state(TSRMLS_D)
156 zend_stack_top(&SCNG(state_stack), (void **) &stack_state);
157 YYSETCONDITION(*stack_state);
158 zend_stack_del_top(&SCNG(state_stack));
161 static void yy_scan_buffer(char *str, unsigned int len TSRMLS_DC)
163 YYCURSOR = (YYCTYPE*)str;
164 SCNG(yy_start) = YYCURSOR;
165 YYLIMIT = YYCURSOR + len;
168 #define ini_filename SCNG(filename)
170 /* {{{ init_ini_scanner()
172 static int init_ini_scanner(int scanner_mode, zend_file_handle *fh TSRMLS_DC)
175 if (scanner_mode != ZEND_INI_SCANNER_NORMAL && scanner_mode != ZEND_INI_SCANNER_RAW) {
176 zend_error(E_WARNING, "Invalid scanner mode");
181 SCNG(scanner_mode) = scanner_mode;
185 ini_filename = zend_strndup(fh->filename, strlen(fh->filename));
190 zend_stack_init(&SCNG(state_stack));
197 /* {{{ shutdown_ini_scanner()
199 void shutdown_ini_scanner(TSRMLS_D)
201 zend_stack_destroy(&SCNG(state_stack));
208 /* {{{ zend_ini_scanner_get_lineno()
210 int zend_ini_scanner_get_lineno(TSRMLS_D)
216 /* {{{ zend_ini_scanner_get_filename()
218 char *zend_ini_scanner_get_filename(TSRMLS_D)
220 return ini_filename ? ini_filename : "Unknown";
224 /* {{{ zend_ini_open_file_for_scanning()
226 int zend_ini_open_file_for_scanning(zend_file_handle *fh, int scanner_mode TSRMLS_DC)
231 if (zend_stream_fixup(fh, &buf, &size TSRMLS_CC) == FAILURE) {
235 if (init_ini_scanner(scanner_mode, fh TSRMLS_CC) == FAILURE) {
236 zend_file_handle_dtor(fh TSRMLS_CC);
240 yy_scan_buffer(buf, size TSRMLS_CC);
246 /* {{{ zend_ini_prepare_string_for_scanning()
248 int zend_ini_prepare_string_for_scanning(char *str, int scanner_mode TSRMLS_DC)
250 int len = strlen(str);
252 if (init_ini_scanner(scanner_mode, NULL TSRMLS_CC) == FAILURE) {
256 yy_scan_buffer(str, len TSRMLS_CC);
262 /* {{{ zend_ini_escape_string()
264 static void zend_ini_escape_string(zval *lval, char *str, int len, char quote_type TSRMLS_DC)
266 register char *s, *t;
269 zend_ini_copy_value(lval, str, len);
271 /* convert escape sequences */
272 s = t = Z_STRVAL_P(lval);
273 end = s + Z_STRLEN_P(lval);
284 if (*s != quote_type) {
302 if (*s == '\n' || (*s == '\r' && (*(s+1) != '\n'))) {
311 int ini_lex(zval *ini_lval TSRMLS_DC)
314 SCNG(yy_text) = YYCURSOR;
316 /* yymore_restart: */
318 if (YYCURSOR >= YYLIMIT) {
319 if (YYSTATE == STATE(ST_VALUE) || YYSTATE == STATE(ST_RAW)) {
326 /* Eat any UTF-8 BOM we find in the first 3 bytes */
327 if (YYCURSOR == SCNG(yy_start) && YYCURSOR + 3 < YYLIMIT) {
328 if (memcmp(YYCURSOR, "\xef\xbb\xbf", 3) == 0) {
334 re2c:yyfill:check = 0;
336 DNUM ([0-9]*[\.][0-9]+)|([0-9]+[\.][0-9]*)
337 NUMBER [-]?{LNUM}|{DNUM}
339 NEWLINE ("\r"|"\n"|"\r\n")
340 TABS_AND_SPACES [ \t]
342 CONSTANT [a-zA-Z_][a-zA-Z0-9_]*
343 LABEL [^=\n\r\t;|&$~(){}!"\[]+
344 TOKENS [:,.\[\]"'()|^&+-/*=%$!~<>?@{}]
348 SECTION_RAW_CHARS [^\]\n\r]
349 SINGLE_QUOTED_CHARS [^']
350 RAW_VALUE_CHARS [^\n\r;\000]
352 LITERAL_DOLLAR ("$"([^{\000]|("\\"{ANY_CHAR})))
353 VALUE_CHARS ([^$= \t\n\r;&|~()!"'\000]|{LITERAL_DOLLAR})
354 SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR})
356 <!*> := yyleng = YYCURSOR - SCNG(yy_text);
358 <INITIAL>"[" { /* Section start */
359 /* Enter section data lookup state */
360 if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
361 yy_push_state(ST_SECTION_RAW TSRMLS_CC);
363 yy_push_state(ST_SECTION_VALUE TSRMLS_CC);
368 <ST_VALUE,ST_SECTION_VALUE,ST_OFFSET>"'"{SINGLE_QUOTED_CHARS}+"'" { /* Raw string */
369 /* Eat leading and trailing single quotes */
370 if (yytext[0] == '\'' && yytext[yyleng - 1] == '\'') {
374 RETURN_TOKEN(TC_RAW, yytext, yyleng);
377 <ST_SECTION_RAW,ST_SECTION_VALUE>"]"{TABS_AND_SPACES}*{NEWLINE}? { /* End of section */
383 <INITIAL>{LABEL}"["{TABS_AND_SPACES}* { /* Start of option with offset */
384 /* Eat leading whitespace */
385 EAT_LEADING_WHITESPACE();
387 /* Eat trailing whitespace and [ */
388 EAT_TRAILING_WHITESPACE_EX('[');
390 /* Enter offset lookup state */
391 yy_push_state(ST_OFFSET TSRMLS_CC);
393 RETURN_TOKEN(TC_OFFSET, yytext, yyleng);
396 <ST_OFFSET>{TABS_AND_SPACES}*"]" { /* End of section or an option offset */
401 <ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{DOLLAR_CURLY} { /* Variable start */
402 yy_push_state(ST_VARNAME TSRMLS_CC);
403 return TC_DOLLAR_CURLY;
406 <ST_VARNAME>{LABEL} { /* Variable name */
407 /* Eat leading whitespace */
408 EAT_LEADING_WHITESPACE();
410 /* Eat trailing whitespace */
411 EAT_TRAILING_WHITESPACE();
413 RETURN_TOKEN(TC_VARNAME, yytext, yyleng);
416 <ST_VARNAME>"}" { /* Variable end */
417 yy_pop_state(TSRMLS_C);
421 <INITIAL,ST_VALUE>("true"|"on"|"yes"){TABS_AND_SPACES}* { /* TRUE value (when used outside option value/offset this causes parse error!) */
422 RETURN_TOKEN(BOOL_TRUE, "1", 1);
425 <INITIAL,ST_VALUE>("false"|"off"|"no"|"none"|"null"){TABS_AND_SPACES}* { /* FALSE value (when used outside option value/offset this causes parse error!)*/
426 RETURN_TOKEN(BOOL_FALSE, "", 0);
429 <INITIAL>{LABEL} { /* Get option name */
430 /* Eat leading whitespace */
431 EAT_LEADING_WHITESPACE();
433 /* Eat trailing whitespace */
434 EAT_TRAILING_WHITESPACE();
436 RETURN_TOKEN(TC_LABEL, yytext, yyleng);
439 <INITIAL>{TABS_AND_SPACES}*[=]{TABS_AND_SPACES}* { /* Start option value */
440 if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
441 yy_push_state(ST_RAW TSRMLS_CC);
443 yy_push_state(ST_VALUE TSRMLS_CC);
448 <ST_RAW>{RAW_VALUE_CHARS} { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
450 while (YYCURSOR < YYLIMIT) {
454 goto end_raw_value_chars;
467 yyleng = YYCURSOR - SCNG(yy_text);
469 /* Eat trailing semicolons */
470 while (yytext[yyleng - 1] == ';') {
474 /* Eat leading and trailing double quotes */
475 if (yytext[0] == '"' && yytext[yyleng - 1] == '"') {
480 yyleng = YYCURSOR - SCNG(yy_text);
482 RETURN_TOKEN(TC_RAW, yytext, yyleng);
485 <ST_SECTION_RAW>{SECTION_RAW_CHARS}+ { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
486 RETURN_TOKEN(TC_RAW, yytext, yyleng);
489 <ST_VALUE,ST_RAW>{TABS_AND_SPACES}*{NEWLINE} { /* End of option value */
495 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{CONSTANT} { /* Get constant option value */
496 RETURN_TOKEN(TC_CONSTANT, yytext, yyleng);
499 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{NUMBER} { /* Get number option value as string */
500 RETURN_TOKEN(TC_NUMBER, yytext, yyleng);
503 <INITIAL>{TOKENS} { /* Disallow these chars outside option values */
507 <ST_VALUE>{OPERATORS}{TABS_AND_SPACES}* { /* Boolean operators */
511 <ST_VALUE>[=] { /* Make = used in option value to trigger error */
517 <ST_VALUE>{VALUE_CHARS}+ { /* Get everything else as option/offset value */
518 RETURN_TOKEN(TC_STRING, yytext, yyleng);
521 <ST_SECTION_VALUE,ST_OFFSET>{SECTION_VALUE_CHARS}+ { /* Get rest as section/offset value */
522 RETURN_TOKEN(TC_STRING, yytext, yyleng);
525 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{TABS_AND_SPACES}*["] { /* Double quoted '"' string start */
526 yy_push_state(ST_DOUBLE_QUOTES TSRMLS_CC);
530 <ST_DOUBLE_QUOTES>["]{TABS_AND_SPACES}* { /* Double quoted '"' string ends */
531 yy_pop_state(TSRMLS_C);
535 <ST_DOUBLE_QUOTES>[^] { /* Escape double quoted string contents */
536 if (YYCURSOR > YYLIMIT) {
540 while (YYCURSOR < YYLIMIT) {
541 switch (*YYCURSOR++) {
543 if (YYCURSOR < YYLIMIT && YYCURSOR[-2] == '\\' && *YYCURSOR != '\r' && *YYCURSOR != '\n') {
548 if (*YYCURSOR == '{') {
553 if (YYCURSOR < YYLIMIT && *YYCURSOR != '"') {
565 yyleng = YYCURSOR - SCNG(yy_text);
567 zend_ini_escape_string(ini_lval, yytext, yyleng, '"' TSRMLS_CC);
568 return TC_QUOTED_STRING;
571 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{WHITESPACE} {
572 RETURN_TOKEN(TC_WHITESPACE, yytext, yyleng);
575 <INITIAL,ST_RAW>{TABS_AND_SPACES}+ {
580 <INITIAL>{TABS_AND_SPACES}*{NEWLINE} {
585 <INITIAL,ST_VALUE,ST_RAW>{TABS_AND_SPACES}*[;][^\r\n]*{NEWLINE} { /* Comment */
591 <INITIAL>{TABS_AND_SPACES}*[#][^\r\n]*{NEWLINE} { /* #Comment */
592 zend_error(E_DEPRECATED, "Comments starting with '#' are deprecated in %s on line %d", zend_ini_scanner_get_filename(TSRMLS_C), SCNG(lineno));
598 <ST_VALUE,ST_RAW>[^] { /* End of option value (if EOF is reached before EOL */