xref: /PHP-5.6/Zend/zend_ini_scanner.l (revision 3537e95d)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2016 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$ */
24 
25 #include <errno.h>
26 #include "zend.h"
27 #include "zend_API.h"
28 #include "zend_globals.h"
29 #include <zend_ini_parser.h>
30 #include "zend_ini_scanner.h"
31 
32 #if 0
33 # define YYDEBUG(s, c) printf("state: %d char: %c\n", s, c)
34 #else
35 # define YYDEBUG(s, c)
36 #endif
37 
38 #include "zend_ini_scanner_defs.h"
39 
40 #define YYCTYPE   unsigned char
41 /* allow the scanner to read one null byte after the end of the string (from ZEND_MMAP_AHEAD)
42  * so that if will be able to terminate to match the current token (e.g. non-enclosed string) */
43 #define YYFILL(n) { if (YYCURSOR > YYLIMIT) return 0; }
44 #define YYCURSOR  SCNG(yy_cursor)
45 #define YYLIMIT   SCNG(yy_limit)
46 #define YYMARKER  SCNG(yy_marker)
47 
48 #define YYGETCONDITION()  SCNG(yy_state)
49 #define YYSETCONDITION(s) SCNG(yy_state) = s
50 
51 #define STATE(name)  yyc##name
52 
53 /* emulate flex constructs */
54 #define BEGIN(state) YYSETCONDITION(STATE(state))
55 #define YYSTATE      YYGETCONDITION()
56 #define yytext       ((char*)SCNG(yy_text))
57 #define yyleng       SCNG(yy_leng)
58 #define yyless(x)    do {	YYCURSOR = (unsigned char*)yytext + x; \
59 							yyleng   = (unsigned int)x; } while(0)
60 
61 /* #define yymore()     goto yymore_restart */
62 
63 /* perform sanity check. If this message is triggered you should
64    increase the ZEND_MMAP_AHEAD value in the zend_streams.h file */
65 /*!max:re2c */
66 #if ZEND_MMAP_AHEAD < (YYMAXFILL + 1)
67 # error ZEND_MMAP_AHEAD should be greater than YYMAXFILL
68 #endif
69 
70 
71 /* How it works (for the core ini directives):
72  * ===========================================
73  *
74  * 1. Scanner scans file for tokens and passes them to parser.
75  * 2. Parser parses the tokens and passes the name/value pairs to the callback
76  *    function which stores them in the configuration hash table.
77  * 3. Later REGISTER_INI_ENTRIES() is called which triggers the actual
78  *    registering of ini entries and uses zend_get_configuration_directive()
79  *    to fetch the previously stored name/value pair from configuration hash table
80  *    and registers the static ini entries which match the name to the value
81  *    into EG(ini_directives) hash table.
82  * 4. PATH section entries are used per-request from down to top, each overriding
83  *    previous if one exists. zend_alter_ini_entry() is called for each entry.
84  *    Settings in PATH section are ZEND_INI_SYSTEM accessible and thus mimics the
85  *    php_admin_* directives used within Apache httpd.conf when PHP is compiled as
86  *    module for Apache.
87  * 5. User defined ini files (like .htaccess for apache) are parsed for each request and
88  *    stored in separate hash defined by SAPI.
89  */
90 
91 /* TODO: (ordered by importance :-)
92  * ===============================================================================
93  *
94  *  - Separate constant lookup totally from plain strings (using CONSTANT pattern)
95  *  - Add #if .. #else .. #endif and ==, !=, <, > , <=, >= operators
96  *  - Add #include "some.ini"
97  *  - Allow variables to refer to options also when using parse_ini_file()
98  *
99  */
100 
101 /* Globals Macros */
102 #define SCNG	INI_SCNG
103 #ifdef ZTS
104 ZEND_API ts_rsrc_id ini_scanner_globals_id;
105 #else
106 ZEND_API zend_ini_scanner_globals ini_scanner_globals;
107 #endif
108 
109 /* Eat leading whitespace */
110 #define EAT_LEADING_WHITESPACE()                     \
111 	while (yyleng) {                                 \
112 		if (yytext[0] == ' ' || yytext[0] == '\t') { \
113 			SCNG(yy_text)++;                         \
114 			yyleng--;                                \
115 		} else {                                     \
116 			break;                                   \
117 		}                                            \
118 	}
119 
120 /* Eat trailing whitespace + extra char */
121 #define EAT_TRAILING_WHITESPACE_EX(ch)              \
122 	while (yyleng && (                              \
123 		(ch != 'X' && yytext[yyleng - 1] ==  ch) || \
124 		yytext[yyleng - 1] == '\n' ||               \
125 		yytext[yyleng - 1] == '\r' ||               \
126 		yytext[yyleng - 1] == '\t' ||               \
127 		yytext[yyleng - 1] == ' ')                  \
128 	) {                                             \
129 		yyleng--;                                   \
130 	}
131 
132 /* Eat trailing whitespace */
133 #define EAT_TRAILING_WHITESPACE()	EAT_TRAILING_WHITESPACE_EX('X')
134 
135 #define zend_ini_copy_value(retval, str, len) {  \
136 	Z_STRVAL_P(retval) = zend_strndup(str, len); \
137 	Z_STRLEN_P(retval) = len;                    \
138 	Z_TYPE_P(retval) = IS_STRING;                \
139 }
140 
141 #define RETURN_TOKEN(type, str, len) {                       \
142 	if (SCNG(scanner_mode) == ZEND_INI_SCANNER_TYPED) {      \
143 		zend_ini_copy_typed_value(ini_lval, type, str, len); \
144 	} else {                                                 \
145 		zend_ini_copy_value(ini_lval, str, len);             \
146 	}                                                        \
147 	return type;                                             \
148 }
149 
convert_to_number(zval * retval,const char * str,const int str_len)150 static inline int convert_to_number(zval *retval, const char *str, const int str_len)
151 {
152 	zend_uchar type;
153 	int overflow;
154 	long lval;
155 	double dval;
156 
157 	if ((type = is_numeric_string_ex(str, str_len, &lval, &dval, 0, &overflow)) != 0) {
158 		if (type == IS_LONG) {
159 			ZVAL_LONG(retval, lval);
160 			return SUCCESS;
161 		} else if (type == IS_DOUBLE && !overflow) {
162 			ZVAL_DOUBLE(retval, dval);
163 			return SUCCESS;
164 		}
165 	}
166 
167 	return FAILURE;
168 }
169 
zend_ini_copy_typed_value(zval * retval,const int type,const char * str,int len)170 static void zend_ini_copy_typed_value(zval *retval, const int type, const char *str, int len)
171 {
172 	switch (type) {
173 		case BOOL_FALSE:
174 		case BOOL_TRUE:
175 			ZVAL_BOOL(retval, type == BOOL_TRUE);
176 			break;
177 
178 		case NULL_NULL:
179 			ZVAL_NULL(retval);
180 			break;
181 
182 		case TC_NUMBER:
183 			if (convert_to_number(retval, str, len) == SUCCESS) {
184 				break;
185 			}
186 			/* intentional fall-through */
187 		default:
188 			zend_ini_copy_value(retval, str, len);
189 	}
190 }
191 
_yy_push_state(int new_state TSRMLS_DC)192 static void _yy_push_state(int new_state TSRMLS_DC)
193 {
194 	zend_stack_push(&SCNG(state_stack), (void *) &YYGETCONDITION(), sizeof(int));
195 	YYSETCONDITION(new_state);
196 }
197 
198 #define yy_push_state(state_and_tsrm) _yy_push_state(yyc##state_and_tsrm)
199 
yy_pop_state(TSRMLS_D)200 static void yy_pop_state(TSRMLS_D)
201 {
202 	int *stack_state;
203 	zend_stack_top(&SCNG(state_stack), (void **) &stack_state);
204 	YYSETCONDITION(*stack_state);
205 	zend_stack_del_top(&SCNG(state_stack));
206 }
207 
yy_scan_buffer(char * str,unsigned int len TSRMLS_DC)208 static void yy_scan_buffer(char *str, unsigned int len TSRMLS_DC)
209 {
210 	YYCURSOR = (YYCTYPE*)str;
211 	SCNG(yy_start) = YYCURSOR;
212 	YYLIMIT  = YYCURSOR + len;
213 }
214 
215 #define ini_filename SCNG(filename)
216 
217 /* {{{ init_ini_scanner()
218 */
init_ini_scanner(int scanner_mode,zend_file_handle * fh TSRMLS_DC)219 static int init_ini_scanner(int scanner_mode, zend_file_handle *fh TSRMLS_DC)
220 {
221 	/* Sanity check */
222 	if (scanner_mode != ZEND_INI_SCANNER_NORMAL && scanner_mode != ZEND_INI_SCANNER_RAW && scanner_mode != ZEND_INI_SCANNER_TYPED) {
223 		zend_error(E_WARNING, "Invalid scanner mode");
224 		return FAILURE;
225 	}
226 
227 	SCNG(lineno) = 1;
228 	SCNG(scanner_mode) = scanner_mode;
229 	SCNG(yy_in) = fh;
230 
231 	if (fh != NULL) {
232 		ini_filename = zend_strndup(fh->filename, strlen(fh->filename));
233 	} else {
234 		ini_filename = NULL;
235 	}
236 
237 	zend_stack_init(&SCNG(state_stack));
238 	BEGIN(INITIAL);
239 
240 	return SUCCESS;
241 }
242 /* }}} */
243 
244 /* {{{ shutdown_ini_scanner()
245 */
shutdown_ini_scanner(TSRMLS_D)246 void shutdown_ini_scanner(TSRMLS_D)
247 {
248 	zend_stack_destroy(&SCNG(state_stack));
249 	if (ini_filename) {
250 		free(ini_filename);
251 	}
252 }
253 /* }}} */
254 
255 /* {{{ zend_ini_scanner_get_lineno()
256 */
zend_ini_scanner_get_lineno(TSRMLS_D)257 int zend_ini_scanner_get_lineno(TSRMLS_D)
258 {
259 	return SCNG(lineno);
260 }
261 /* }}} */
262 
263 /* {{{ zend_ini_scanner_get_filename()
264 */
zend_ini_scanner_get_filename(TSRMLS_D)265 char *zend_ini_scanner_get_filename(TSRMLS_D)
266 {
267 	return ini_filename ? ini_filename : "Unknown";
268 }
269 /* }}} */
270 
271 /* {{{ zend_ini_open_file_for_scanning()
272 */
zend_ini_open_file_for_scanning(zend_file_handle * fh,int scanner_mode TSRMLS_DC)273 int zend_ini_open_file_for_scanning(zend_file_handle *fh, int scanner_mode TSRMLS_DC)
274 {
275 	char *buf;
276 	size_t size;
277 
278 	if (zend_stream_fixup(fh, &buf, &size TSRMLS_CC) == FAILURE) {
279 		return FAILURE;
280 	}
281 
282 	if (init_ini_scanner(scanner_mode, fh TSRMLS_CC) == FAILURE) {
283 		zend_file_handle_dtor(fh TSRMLS_CC);
284 		return FAILURE;
285 	}
286 
287 	yy_scan_buffer(buf, size TSRMLS_CC);
288 
289 	return SUCCESS;
290 }
291 /* }}} */
292 
293 /* {{{ zend_ini_prepare_string_for_scanning()
294 */
zend_ini_prepare_string_for_scanning(char * str,int scanner_mode TSRMLS_DC)295 int zend_ini_prepare_string_for_scanning(char *str, int scanner_mode TSRMLS_DC)
296 {
297 	int len = strlen(str);
298 
299 	if (init_ini_scanner(scanner_mode, NULL TSRMLS_CC) == FAILURE) {
300 		return FAILURE;
301 	}
302 
303 	yy_scan_buffer(str, len TSRMLS_CC);
304 
305 	return SUCCESS;
306 }
307 /* }}} */
308 
309 /* {{{ zend_ini_escape_string()
310  */
zend_ini_escape_string(zval * lval,char * str,int len,char quote_type TSRMLS_DC)311 static void zend_ini_escape_string(zval *lval, char *str, int len, char quote_type TSRMLS_DC)
312 {
313 	register char *s, *t;
314 	char *end;
315 
316 	zend_ini_copy_value(lval, str, len);
317 
318 	/* convert escape sequences */
319 	s = t = Z_STRVAL_P(lval);
320 	end = s + Z_STRLEN_P(lval);
321 
322 	while (s < end) {
323 		if (*s == '\\') {
324 			s++;
325 			if (s >= end) {
326 				*t++ = '\\';
327 				continue;
328 			}
329 			switch (*s) {
330 				case '"':
331 					if (*s != quote_type) {
332 						*t++ = '\\';
333 						*t++ = *s;
334 						break;
335 					}
336 				case '\\':
337 				case '$':
338 					*t++ = *s;
339 					Z_STRLEN_P(lval)--;
340 					break;
341 				default:
342 					*t++ = '\\';
343 					*t++ = *s;
344 					break;
345 			}
346 		} else {
347 			*t++ = *s;
348 		}
349 		if (*s == '\n' || (*s == '\r' && (*(s+1) != '\n'))) {
350 			SCNG(lineno)++;
351 		}
352 		s++;
353 	}
354 	*t = 0;
355 }
356 /* }}} */
357 
ini_lex(zval * ini_lval TSRMLS_DC)358 int ini_lex(zval *ini_lval TSRMLS_DC)
359 {
360 restart:
361 	SCNG(yy_text) = YYCURSOR;
362 
363 /* yymore_restart: */
364 	/* detect EOF */
365 	if (YYCURSOR >= YYLIMIT) {
366 		if (YYSTATE == STATE(ST_VALUE) || YYSTATE == STATE(ST_RAW)) {
367 			BEGIN(INITIAL);
368 			return END_OF_LINE;
369 		}
370 		return 0;
371 	}
372 
373 	/* Eat any UTF-8 BOM we find in the first 3 bytes */
374 	if (YYCURSOR == SCNG(yy_start) && YYCURSOR + 3 < YYLIMIT) {
375 		if (memcmp(YYCURSOR, "\xef\xbb\xbf", 3) == 0) {
376 			YYCURSOR += 3;
377 			goto restart;
378 		}
379 	}
380 /*!re2c
381 re2c:yyfill:check = 0;
382 LNUM [0-9]+
383 DNUM ([0-9]*[\.][0-9]+)|([0-9]+[\.][0-9]*)
384 NUMBER [-]?{LNUM}|{DNUM}
385 ANY_CHAR (.|[\n\t])
386 NEWLINE	("\r"|"\n"|"\r\n")
387 TABS_AND_SPACES [ \t]
388 WHITESPACE [ \t]+
389 CONSTANT [a-zA-Z_][a-zA-Z0-9_]*
390 LABEL [^=\n\r\t;&|^$~(){}!"\[]+
391 TOKENS [:,.\[\]"'()&|^+-/*=%$!~<>?@{}]
392 OPERATORS [&|^~()!]
393 DOLLAR_CURLY "${"
394 
395 SECTION_RAW_CHARS [^\]\n\r]
396 SINGLE_QUOTED_CHARS [^']
397 RAW_VALUE_CHARS [^\n\r;\000]
398 
399 LITERAL_DOLLAR ("$"([^{\000]|("\\"{ANY_CHAR})))
400 VALUE_CHARS         ([^$= \t\n\r;&|^~()!"'\000]|{LITERAL_DOLLAR})
401 SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR})
402 
403 <!*> := yyleng = YYCURSOR - SCNG(yy_text);
404 
405 <INITIAL>"[" { /* Section start */
406 	/* Enter section data lookup state */
407 	if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
408 		yy_push_state(ST_SECTION_RAW TSRMLS_CC);
409 	} else {
410 		yy_push_state(ST_SECTION_VALUE TSRMLS_CC);
411 	}
412 	return TC_SECTION;
413 }
414 
415 <ST_VALUE,ST_SECTION_VALUE,ST_OFFSET>"'"{SINGLE_QUOTED_CHARS}+"'" { /* Raw string */
416 	/* Eat leading and trailing single quotes */
417 	if (yytext[0] == '\'' && yytext[yyleng - 1] == '\'') {
418 		SCNG(yy_text)++;
419 		yyleng = yyleng - 2;
420 	}
421 	RETURN_TOKEN(TC_RAW, yytext, yyleng);
422 }
423 
424 <ST_SECTION_RAW,ST_SECTION_VALUE>"]"{TABS_AND_SPACES}*{NEWLINE}? { /* End of section */
425 	BEGIN(INITIAL);
426 	SCNG(lineno)++;
427 	return ']';
428 }
429 
430 <INITIAL>{LABEL}"["{TABS_AND_SPACES}* { /* Start of option with offset */
431 	/* Eat leading whitespace */
432 	EAT_LEADING_WHITESPACE();
433 
434 	/* Eat trailing whitespace and [ */
435 	EAT_TRAILING_WHITESPACE_EX('[');
436 
437 	/* Enter offset lookup state */
438 	yy_push_state(ST_OFFSET TSRMLS_CC);
439 
440 	RETURN_TOKEN(TC_OFFSET, yytext, yyleng);
441 }
442 
443 <ST_OFFSET>{TABS_AND_SPACES}*"]" { /* End of section or an option offset */
444 	BEGIN(INITIAL);
445 	return ']';
446 }
447 
448 <ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{DOLLAR_CURLY} { /* Variable start */
449 	yy_push_state(ST_VARNAME TSRMLS_CC);
450 	return TC_DOLLAR_CURLY;
451 }
452 
453 <ST_VARNAME>{LABEL} { /* Variable name */
454 	/* Eat leading whitespace */
455 	EAT_LEADING_WHITESPACE();
456 
457 	/* Eat trailing whitespace */
458 	EAT_TRAILING_WHITESPACE();
459 
460 	RETURN_TOKEN(TC_VARNAME, yytext, yyleng);
461 }
462 
463 <ST_VARNAME>"}" { /* Variable end */
464 	yy_pop_state(TSRMLS_C);
465 	return '}';
466 }
467 
468 <INITIAL,ST_VALUE>("true"|"on"|"yes"){TABS_AND_SPACES}* { /* TRUE value (when used outside option value/offset this causes parse error!) */
469 	RETURN_TOKEN(BOOL_TRUE, "1", 1);
470 }
471 
472 <INITIAL,ST_VALUE>("false"|"off"|"no"|"none"){TABS_AND_SPACES}* { /* FALSE value (when used outside option value/offset this causes parse error!)*/
473 	RETURN_TOKEN(BOOL_FALSE, "", 0);
474 }
475 
476 <INITIAL,ST_VALUE>("null"){TABS_AND_SPACES}* {
477 	RETURN_TOKEN(NULL_NULL, "", 0);
478 }
479 
480 <INITIAL>{LABEL} { /* Get option name */
481 	/* Eat leading whitespace */
482 	EAT_LEADING_WHITESPACE();
483 
484 	/* Eat trailing whitespace */
485 	EAT_TRAILING_WHITESPACE();
486 
487 	RETURN_TOKEN(TC_LABEL, yytext, yyleng);
488 }
489 
490 <INITIAL>{TABS_AND_SPACES}*[=]{TABS_AND_SPACES}* { /* Start option value */
491 	if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
492 		yy_push_state(ST_RAW TSRMLS_CC);
493 	} else {
494 		yy_push_state(ST_VALUE TSRMLS_CC);
495 	}
496 	return '=';
497 }
498 
499 <ST_RAW>{RAW_VALUE_CHARS} { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
500 	char *sc = NULL;
501 	while (YYCURSOR < YYLIMIT) {
502 		switch (*YYCURSOR) {
503 			case '\n':
504 			case '\r':
505 				goto end_raw_value_chars;
506 				break;
507 			case ';':
508 				if (sc == NULL) {
509 					sc = YYCURSOR;
510 				}
511 				/* no break */
512 			default:
513 				YYCURSOR++;
514 				break;
515 		}
516 	}
517 end_raw_value_chars:
518 	yyleng = YYCURSOR - SCNG(yy_text);
519 
520 	/* Eat trailing semicolons */
521 	while (yytext[yyleng - 1] == ';') {
522 		yyleng--;
523 	}
524 
525 	/* Eat leading and trailing double quotes */
526 	if (yyleng > 1 && yytext[0] == '"' && yytext[yyleng - 1] == '"') {
527 		SCNG(yy_text)++;
528 		yyleng = yyleng - 2;
529 	} else if (sc) {
530 		YYCURSOR = sc;
531 		yyleng = YYCURSOR - SCNG(yy_text);
532 	}
533 	RETURN_TOKEN(TC_RAW, yytext, yyleng);
534 }
535 
536 <ST_SECTION_RAW>{SECTION_RAW_CHARS}+ { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
537 	RETURN_TOKEN(TC_RAW, yytext, yyleng);
538 }
539 
540 <ST_VALUE,ST_RAW>{TABS_AND_SPACES}*{NEWLINE} { /* End of option value */
541 	BEGIN(INITIAL);
542 	SCNG(lineno)++;
543 	return END_OF_LINE;
544 }
545 
546 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{CONSTANT} { /* Get constant option value */
547 	RETURN_TOKEN(TC_CONSTANT, yytext, yyleng);
548 }
549 
550 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{NUMBER} { /* Get number option value as string */
551 	RETURN_TOKEN(TC_NUMBER, yytext, yyleng);
552 }
553 
554 <INITIAL>{TOKENS} { /* Disallow these chars outside option values */
555 	return yytext[0];
556 }
557 
558 <ST_VALUE>{OPERATORS}{TABS_AND_SPACES}* { /* Boolean operators */
559 	return yytext[0];
560 }
561 
562 <ST_VALUE>[=] { /* Make = used in option value to trigger error */
563 	yyless(0);
564 	BEGIN(INITIAL);
565 	return END_OF_LINE;
566 }
567 
568 <ST_VALUE>{VALUE_CHARS}+ { /* Get everything else as option/offset value */
569 	RETURN_TOKEN(TC_STRING, yytext, yyleng);
570 }
571 
572 <ST_SECTION_VALUE,ST_OFFSET>{SECTION_VALUE_CHARS}+ { /* Get rest as section/offset value */
573 	RETURN_TOKEN(TC_STRING, yytext, yyleng);
574 }
575 
576 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{TABS_AND_SPACES}*["] { /* Double quoted '"' string start */
577 	yy_push_state(ST_DOUBLE_QUOTES TSRMLS_CC);
578 	return '"';
579 }
580 
581 <ST_DOUBLE_QUOTES>["]{TABS_AND_SPACES}* { /* Double quoted '"' string ends */
582 	yy_pop_state(TSRMLS_C);
583 	return '"';
584 }
585 
586 <ST_DOUBLE_QUOTES>[^] { /* Escape double quoted string contents */
587 	if (YYCURSOR > YYLIMIT) {
588 		return 0;
589 	}
590 
591 	while (YYCURSOR < YYLIMIT) {
592 		switch (*YYCURSOR++) {
593 			case '"':
594 				if (YYCURSOR < YYLIMIT && YYCURSOR[-2] == '\\' && *YYCURSOR != '\r' && *YYCURSOR != '\n') {
595 					continue;
596 				}
597 				break;
598 			case '$':
599 				if (*YYCURSOR == '{') {
600 					break;
601 				}
602 				continue;
603 			case '\\':
604 				if (YYCURSOR < YYLIMIT && *YYCURSOR != '"') {
605 					YYCURSOR++;
606 				}
607 				/* fall through */
608 			default:
609 				continue;
610 		}
611 
612 		YYCURSOR--;
613 		break;
614 	}
615 
616 	yyleng = YYCURSOR - SCNG(yy_text);
617 
618 	zend_ini_escape_string(ini_lval, yytext, yyleng, '"' TSRMLS_CC);
619 	return TC_QUOTED_STRING;
620 }
621 
622 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{WHITESPACE} {
623 	RETURN_TOKEN(TC_WHITESPACE, yytext, yyleng);
624 }
625 
626 <INITIAL,ST_RAW>{TABS_AND_SPACES}+ {
627 	/* eat whitespace */
628 	goto restart;
629 }
630 
631 <INITIAL>{TABS_AND_SPACES}*{NEWLINE} {
632 	SCNG(lineno)++;
633 	return END_OF_LINE;
634 }
635 
636 <INITIAL,ST_VALUE,ST_RAW>{TABS_AND_SPACES}*[;][^\r\n]*{NEWLINE} { /* Comment */
637 	BEGIN(INITIAL);
638 	SCNG(lineno)++;
639 	return END_OF_LINE;
640 }
641 
642 <INITIAL>{TABS_AND_SPACES}*[#][^\r\n]*{NEWLINE} { /* #Comment */
643 	zend_error(E_DEPRECATED, "Comments starting with '#' are deprecated in %s on line %d", zend_ini_scanner_get_filename(TSRMLS_C), SCNG(lineno));
644 	BEGIN(INITIAL);
645 	SCNG(lineno)++;
646 	return END_OF_LINE;
647 }
648 
649 <ST_VALUE,ST_RAW>[^] { /* End of option value (if EOF is reached before EOL */
650 	BEGIN(INITIAL);
651 	return END_OF_LINE;
652 }
653 
654 <*>[^] {
655 	return 0;
656 }
657 
658 */
659 }
660