xref: /PHP-7.3/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@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 #else
108 ZEND_API zend_ini_scanner_globals ini_scanner_globals;
109 #endif
110 
111 #define ZEND_SYSTEM_INI CG(ini_parser_unbuffered_errors)
112 
113 /* Eat leading whitespace */
114 #define EAT_LEADING_WHITESPACE()                     \
115 	while (yyleng) {                                 \
116 		if (yytext[0] == ' ' || yytext[0] == '\t') { \
117 			SCNG(yy_text)++;                         \
118 			yyleng--;                                \
119 		} else {                                     \
120 			break;                                   \
121 		}                                            \
122 	}
123 
124 /* Eat trailing whitespace + extra char */
125 #define EAT_TRAILING_WHITESPACE_EX(ch)              \
126 	while (yyleng && (                              \
127 		(ch != 'X' && yytext[yyleng - 1] ==  ch) || \
128 		yytext[yyleng - 1] == '\n' ||               \
129 		yytext[yyleng - 1] == '\r' ||               \
130 		yytext[yyleng - 1] == '\t' ||               \
131 		yytext[yyleng - 1] == ' ')                  \
132 	) {                                             \
133 		yyleng--;                                   \
134 	}
135 
136 /* Eat trailing whitespace */
137 #define EAT_TRAILING_WHITESPACE()	EAT_TRAILING_WHITESPACE_EX('X')
138 
139 #define zend_ini_copy_value(retval, str, len)	\
140 	ZVAL_NEW_STR(retval, zend_string_init(str, len, ZEND_SYSTEM_INI))
141 
142 
143 #define RETURN_TOKEN(type, str, len) {                             \
144 	if (SCNG(scanner_mode) == ZEND_INI_SCANNER_TYPED &&            \
145 		(YYSTATE == STATE(ST_VALUE) || YYSTATE == STATE(ST_RAW))) {\
146 		zend_ini_copy_typed_value(ini_lval, type, str, len);       \
147 	} else {                                                       \
148 		zend_ini_copy_value(ini_lval, str, len);                   \
149 	}                                                              \
150 	return type;                                                   \
151 }
152 
convert_to_number(zval * retval,const char * str,const int str_len)153 static inline int convert_to_number(zval *retval, const char *str, const int str_len)
154 {
155 	zend_uchar type;
156 	int overflow;
157 	zend_long lval;
158 	double dval;
159 
160 	if ((type = is_numeric_string_ex(str, str_len, &lval, &dval, 0, &overflow)) != 0) {
161 		if (type == IS_LONG) {
162 			ZVAL_LONG(retval, lval);
163 			return SUCCESS;
164 		} else if (type == IS_DOUBLE && !overflow) {
165 			ZVAL_DOUBLE(retval, dval);
166 			return SUCCESS;
167 		}
168 	}
169 
170 	return FAILURE;
171 }
172 
zend_ini_copy_typed_value(zval * retval,const int type,const char * str,int len)173 static void zend_ini_copy_typed_value(zval *retval, const int type, const char *str, int len)
174 {
175 	switch (type) {
176 		case BOOL_FALSE:
177 		case BOOL_TRUE:
178 			ZVAL_BOOL(retval, type == BOOL_TRUE);
179 			break;
180 
181 		case NULL_NULL:
182 			ZVAL_NULL(retval);
183 			break;
184 
185 		case TC_NUMBER:
186 			if (convert_to_number(retval, str, len) == SUCCESS) {
187 				break;
188 			}
189 			/* intentional fall-through */
190 		default:
191 			zend_ini_copy_value(retval, str, len);
192 	}
193 }
194 
_yy_push_state(int new_state)195 static void _yy_push_state(int new_state)
196 {
197 	zend_stack_push(&SCNG(state_stack), (void *) &YYGETCONDITION());
198 	YYSETCONDITION(new_state);
199 }
200 
201 #define yy_push_state(state_and_tsrm) _yy_push_state(yyc##state_and_tsrm)
202 
yy_pop_state(void)203 static void yy_pop_state(void)
204 {
205 	int *stack_state = zend_stack_top(&SCNG(state_stack));
206 	YYSETCONDITION(*stack_state);
207 	zend_stack_del_top(&SCNG(state_stack));
208 }
209 
yy_scan_buffer(char * str,unsigned int len)210 static void yy_scan_buffer(char *str, unsigned int len)
211 {
212 	YYCURSOR = (YYCTYPE*)str;
213 	SCNG(yy_start) = YYCURSOR;
214 	YYLIMIT  = YYCURSOR + len;
215 }
216 
217 #define ini_filename SCNG(filename)
218 
219 /* {{{ init_ini_scanner()
220 */
init_ini_scanner(int scanner_mode,zend_file_handle * fh)221 static int 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_strndup(fh->filename, strlen(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()
247 */
shutdown_ini_scanner(void)248 void shutdown_ini_scanner(void)
249 {
250 	zend_stack_destroy(&SCNG(state_stack));
251 	if (ini_filename) {
252 		free(ini_filename);
253 	}
254 }
255 /* }}} */
256 
257 /* {{{ zend_ini_scanner_get_lineno()
258 */
zend_ini_scanner_get_lineno(void)259 ZEND_COLD int zend_ini_scanner_get_lineno(void)
260 {
261 	return SCNG(lineno);
262 }
263 /* }}} */
264 
265 /* {{{ zend_ini_scanner_get_filename()
266 */
zend_ini_scanner_get_filename(void)267 ZEND_COLD char *zend_ini_scanner_get_filename(void)
268 {
269 	return ini_filename ? ini_filename : "Unknown";
270 }
271 /* }}} */
272 
273 /* {{{ zend_ini_open_file_for_scanning()
274 */
zend_ini_open_file_for_scanning(zend_file_handle * fh,int scanner_mode)275 int zend_ini_open_file_for_scanning(zend_file_handle *fh, int scanner_mode)
276 {
277 	char *buf;
278 	size_t size;
279 
280 	if (zend_stream_fixup(fh, &buf, &size) == FAILURE) {
281 		return FAILURE;
282 	}
283 
284 	if (init_ini_scanner(scanner_mode, fh) == FAILURE) {
285 		zend_file_handle_dtor(fh);
286 		return FAILURE;
287 	}
288 
289 	yy_scan_buffer(buf, (unsigned int)size);
290 
291 	return SUCCESS;
292 }
293 /* }}} */
294 
295 /* {{{ zend_ini_prepare_string_for_scanning()
296 */
zend_ini_prepare_string_for_scanning(char * str,int scanner_mode)297 int zend_ini_prepare_string_for_scanning(char *str, int scanner_mode)
298 {
299 	int len = (int)strlen(str);
300 
301 	if (init_ini_scanner(scanner_mode, NULL) == FAILURE) {
302 		return FAILURE;
303 	}
304 
305 	yy_scan_buffer(str, len);
306 
307 	return SUCCESS;
308 }
309 /* }}} */
310 
311 /* {{{ zend_ini_escape_string()
312  */
zend_ini_escape_string(zval * lval,char * str,int len,char quote_type)313 static void zend_ini_escape_string(zval *lval, char *str, int len, char quote_type)
314 {
315 	register char *s, *t;
316 	char *end;
317 
318 	zend_ini_copy_value(lval, str, len);
319 
320 	/* convert escape sequences */
321 	s = t = Z_STRVAL_P(lval);
322 	end = s + Z_STRLEN_P(lval);
323 
324 	while (s < end) {
325 		if (*s == '\\') {
326 			s++;
327 			if (s >= end) {
328 				*t++ = '\\';
329 				continue;
330 			}
331 			switch (*s) {
332 				case '"':
333 					if (*s != quote_type) {
334 						*t++ = '\\';
335 						*t++ = *s;
336 						break;
337 					}
338 				case '\\':
339 				case '$':
340 					*t++ = *s;
341 					Z_STRLEN_P(lval)--;
342 					break;
343 				default:
344 					*t++ = '\\';
345 					*t++ = *s;
346 					break;
347 			}
348 		} else {
349 			*t++ = *s;
350 		}
351 		if (*s == '\n' || (*s == '\r' && (*(s+1) != '\n'))) {
352 			SCNG(lineno)++;
353 		}
354 		s++;
355 	}
356 	*t = 0;
357 }
358 /* }}} */
359 
ini_lex(zval * ini_lval)360 int ini_lex(zval *ini_lval)
361 {
362 restart:
363 	SCNG(yy_text) = YYCURSOR;
364 
365 /* yymore_restart: */
366 	/* detect EOF */
367 	if (YYCURSOR >= YYLIMIT) {
368 		if (YYSTATE == STATE(ST_VALUE) || YYSTATE == STATE(ST_RAW)) {
369 			BEGIN(INITIAL);
370 			return END_OF_LINE;
371 		}
372 		return 0;
373 	}
374 
375 	/* Eat any UTF-8 BOM we find in the first 3 bytes */
376 	if (YYCURSOR == SCNG(yy_start) && YYCURSOR + 3 < YYLIMIT) {
377 		if (memcmp(YYCURSOR, "\xef\xbb\xbf", 3) == 0) {
378 			YYCURSOR += 3;
379 			goto restart;
380 		}
381 	}
382 /*!re2c
383 re2c:yyfill:check = 0;
384 LNUM [0-9]+
385 DNUM ([0-9]*[\.][0-9]+)|([0-9]+[\.][0-9]*)
386 NUMBER [-]?{LNUM}|{DNUM}
387 ANY_CHAR (.|[\n\t])
388 NEWLINE	("\r"|"\n"|"\r\n")
389 TABS_AND_SPACES [ \t]
390 WHITESPACE [ \t]+
391 CONSTANT [a-zA-Z_][a-zA-Z0-9_]*
392 LABEL [^=\n\r\t;&|^$~(){}!"\[]+
393 TOKENS [:,.\[\]"'()&|^+-/*=%$!~<>?@{}]
394 OPERATORS [&|^~()!]
395 DOLLAR_CURLY "${"
396 
397 SECTION_RAW_CHARS [^\]\n\r]
398 SINGLE_QUOTED_CHARS [^']
399 RAW_VALUE_CHARS [^\n\r;\000]
400 
401 LITERAL_DOLLAR ("$"([^{\000]|("\\"{ANY_CHAR})))
402 VALUE_CHARS         ([^$= \t\n\r;&|^~()!"'\000]|{LITERAL_DOLLAR})
403 SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR})
404 
405 <!*> := yyleng = YYCURSOR - SCNG(yy_text);
406 
407 <INITIAL>"[" { /* Section start */
408 	/* Enter section data lookup state */
409 	if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
410 		BEGIN(ST_SECTION_RAW);
411 	} else {
412 		BEGIN(ST_SECTION_VALUE);
413 	}
414 	return TC_SECTION;
415 }
416 
417 <ST_VALUE,ST_SECTION_VALUE,ST_OFFSET>"'"{SINGLE_QUOTED_CHARS}+"'" { /* Raw string */
418 	/* Eat leading and trailing single quotes */
419 	if (yytext[0] == '\'' && yytext[yyleng - 1] == '\'') {
420 		SCNG(yy_text)++;
421 		yyleng = yyleng - 2;
422 	}
423 	RETURN_TOKEN(TC_RAW, yytext, yyleng);
424 }
425 
426 <ST_SECTION_RAW,ST_SECTION_VALUE>"]"{TABS_AND_SPACES}*{NEWLINE}? { /* End of section */
427 	BEGIN(INITIAL);
428 	SCNG(lineno)++;
429 	return ']';
430 }
431 
432 <INITIAL>{LABEL}"["{TABS_AND_SPACES}* { /* Start of option with offset */
433 	/* Eat leading whitespace */
434 	EAT_LEADING_WHITESPACE();
435 
436 	/* Eat trailing whitespace and [ */
437 	EAT_TRAILING_WHITESPACE_EX('[');
438 
439 	/* Enter offset lookup state */
440 	BEGIN(ST_OFFSET);
441 
442 	RETURN_TOKEN(TC_OFFSET, yytext, yyleng);
443 }
444 
445 <ST_OFFSET>{TABS_AND_SPACES}*"]" { /* End of section or an option offset */
446 	BEGIN(INITIAL);
447 	return ']';
448 }
449 
450 <ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{DOLLAR_CURLY} { /* Variable start */
451 	yy_push_state(ST_VARNAME);
452 	return TC_DOLLAR_CURLY;
453 }
454 
455 <ST_VARNAME>{LABEL} { /* Variable name */
456 	/* Eat leading whitespace */
457 	EAT_LEADING_WHITESPACE();
458 
459 	/* Eat trailing whitespace */
460 	EAT_TRAILING_WHITESPACE();
461 
462 	RETURN_TOKEN(TC_VARNAME, yytext, yyleng);
463 }
464 
465 <ST_VARNAME>"}" { /* Variable end */
466 	yy_pop_state();
467 	return '}';
468 }
469 
470 <INITIAL,ST_VALUE>("true"|"on"|"yes"){TABS_AND_SPACES}* { /* TRUE value (when used outside option value/offset this causes parse error!) */
471 	RETURN_TOKEN(BOOL_TRUE, "1", 1);
472 }
473 
474 <INITIAL,ST_VALUE>("false"|"off"|"no"|"none"){TABS_AND_SPACES}* { /* FALSE value (when used outside option value/offset this causes parse error!)*/
475 	RETURN_TOKEN(BOOL_FALSE, "", 0);
476 }
477 
478 <INITIAL,ST_VALUE>("null"){TABS_AND_SPACES}* {
479 	RETURN_TOKEN(NULL_NULL, "", 0);
480 }
481 
482 <INITIAL>{LABEL} { /* Get option name */
483 	/* Eat leading whitespace */
484 	EAT_LEADING_WHITESPACE();
485 
486 	/* Eat trailing whitespace */
487 	EAT_TRAILING_WHITESPACE();
488 
489 	RETURN_TOKEN(TC_LABEL, yytext, yyleng);
490 }
491 
492 <INITIAL>{TABS_AND_SPACES}*[=]{TABS_AND_SPACES}* { /* Start option value */
493 	if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
494 		BEGIN(ST_RAW);
495 	} else {
496 		BEGIN(ST_VALUE);
497 	}
498 	return '=';
499 }
500 
501 <ST_RAW>{RAW_VALUE_CHARS} { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
502 	unsigned char *sc = NULL;
503 	EAT_LEADING_WHITESPACE();
504 	while (YYCURSOR < YYLIMIT) {
505 		switch (*YYCURSOR) {
506 			case '\n':
507 			case '\r':
508 				goto end_raw_value_chars;
509 				break;
510 			case ';':
511 				if (sc == NULL) {
512 					sc = YYCURSOR;
513 				}
514 				YYCURSOR++;
515 				break;
516 			case '"':
517 				if (yytext[0] == '"') {
518 					sc = NULL;
519 				}
520 				YYCURSOR++;
521 				break;
522 			default:
523 				YYCURSOR++;
524 				break;
525 		}
526 	}
527 end_raw_value_chars:
528 	if (sc) {
529 		yyleng = sc - SCNG(yy_text);
530 	} else {
531 		yyleng = YYCURSOR - SCNG(yy_text);
532 	}
533 
534 	EAT_TRAILING_WHITESPACE();
535 
536 	/* Eat leading and trailing double quotes */
537 	if (yyleng > 1 && yytext[0] == '"' && yytext[yyleng - 1] == '"') {
538 		SCNG(yy_text)++;
539 		yyleng = yyleng - 2;
540 	}
541 
542 	RETURN_TOKEN(TC_RAW, yytext, yyleng);
543 }
544 
545 <ST_SECTION_RAW>{SECTION_RAW_CHARS}+ { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
546 	RETURN_TOKEN(TC_RAW, yytext, yyleng);
547 }
548 
549 <ST_VALUE,ST_RAW>{TABS_AND_SPACES}*{NEWLINE} { /* End of option value */
550 	BEGIN(INITIAL);
551 	SCNG(lineno)++;
552 	return END_OF_LINE;
553 }
554 
555 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{CONSTANT} { /* Get constant option value */
556 	RETURN_TOKEN(TC_CONSTANT, yytext, yyleng);
557 }
558 
559 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{NUMBER} { /* Get number option value as string */
560 	RETURN_TOKEN(TC_NUMBER, yytext, yyleng);
561 }
562 
563 <INITIAL>{TOKENS} { /* Disallow these chars outside option values */
564 	return yytext[0];
565 }
566 
567 <ST_VALUE>{OPERATORS}{TABS_AND_SPACES}* { /* Boolean operators */
568 	return yytext[0];
569 }
570 
571 <ST_VALUE>[=] { /* Make = used in option value to trigger error */
572 	yyless(0);
573 	BEGIN(INITIAL);
574 	return END_OF_LINE;
575 }
576 
577 <ST_VALUE>{VALUE_CHARS}+ { /* Get everything else as option/offset value */
578 	RETURN_TOKEN(TC_STRING, yytext, yyleng);
579 }
580 
581 <ST_SECTION_VALUE,ST_OFFSET>{SECTION_VALUE_CHARS}+ { /* Get rest as section/offset value */
582 	RETURN_TOKEN(TC_STRING, yytext, yyleng);
583 }
584 
585 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{TABS_AND_SPACES}*["] { /* Double quoted '"' string start */
586 	yy_push_state(ST_DOUBLE_QUOTES);
587 	return '"';
588 }
589 
590 <ST_DOUBLE_QUOTES>["]{TABS_AND_SPACES}* { /* Double quoted '"' string ends */
591 	yy_pop_state();
592 	return '"';
593 }
594 
595 <ST_DOUBLE_QUOTES>[^] { /* Escape double quoted string contents */
596 	if (YYCURSOR > YYLIMIT) {
597 		return 0;
598 	}
599 
600 	while (YYCURSOR < YYLIMIT) {
601 		switch (*YYCURSOR++) {
602 			case '"':
603 				if (YYCURSOR < YYLIMIT && YYCURSOR[-2] == '\\' && *YYCURSOR != '\r' && *YYCURSOR != '\n') {
604 					continue;
605 				}
606 				break;
607 			case '$':
608 				if (*YYCURSOR == '{') {
609 					break;
610 				}
611 				continue;
612 			case '\\':
613 				if (YYCURSOR < YYLIMIT && *YYCURSOR != '"') {
614 					YYCURSOR++;
615 				}
616 				/* fall through */
617 			default:
618 				continue;
619 		}
620 
621 		YYCURSOR--;
622 		break;
623 	}
624 
625 	yyleng = YYCURSOR - SCNG(yy_text);
626 
627 	zend_ini_escape_string(ini_lval, yytext, yyleng, '"');
628 	return TC_QUOTED_STRING;
629 }
630 
631 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{WHITESPACE} {
632 	RETURN_TOKEN(TC_WHITESPACE, yytext, yyleng);
633 }
634 
635 <INITIAL,ST_RAW>{TABS_AND_SPACES}+ {
636 	/* eat whitespace */
637 	goto restart;
638 }
639 
640 <INITIAL>{TABS_AND_SPACES}*{NEWLINE} {
641 	SCNG(lineno)++;
642 	return END_OF_LINE;
643 }
644 
645 <INITIAL,ST_VALUE,ST_RAW>{TABS_AND_SPACES}*[;][^\r\n]*{NEWLINE} { /* Comment */
646 	BEGIN(INITIAL);
647 	SCNG(lineno)++;
648 	return END_OF_LINE;
649 }
650 
651 <ST_VALUE,ST_RAW>[^] { /* End of option value (if EOF is reached before EOL */
652 	BEGIN(INITIAL);
653 	return END_OF_LINE;
654 }
655 
656 <*>[^] {
657 	return 0;
658 }
659 
660 */
661 }
662