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