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