xref: /PHP-7.1/Zend/zend_ini_scanner.l (revision f42d7bdd)
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 		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 int 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)) != 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 			/* intentional fall-through */
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()
221 */
init_ini_scanner(int scanner_mode,zend_file_handle * fh)222 static int init_ini_scanner(int scanner_mode, zend_file_handle *fh)
223 {
224 	/* Sanity check */
225 	if (scanner_mode != ZEND_INI_SCANNER_NORMAL && scanner_mode != ZEND_INI_SCANNER_RAW && scanner_mode != ZEND_INI_SCANNER_TYPED) {
226 		zend_error(E_WARNING, "Invalid scanner mode");
227 		return FAILURE;
228 	}
229 
230 	SCNG(lineno) = 1;
231 	SCNG(scanner_mode) = scanner_mode;
232 	SCNG(yy_in) = fh;
233 
234 	if (fh != NULL) {
235 		ini_filename = zend_strndup(fh->filename, strlen(fh->filename));
236 	} else {
237 		ini_filename = NULL;
238 	}
239 
240 	zend_stack_init(&SCNG(state_stack), sizeof(int));
241 	BEGIN(INITIAL);
242 
243 	return SUCCESS;
244 }
245 /* }}} */
246 
247 /* {{{ shutdown_ini_scanner()
248 */
shutdown_ini_scanner(void)249 void shutdown_ini_scanner(void)
250 {
251 	zend_stack_destroy(&SCNG(state_stack));
252 	if (ini_filename) {
253 		free(ini_filename);
254 	}
255 }
256 /* }}} */
257 
258 /* {{{ zend_ini_scanner_get_lineno()
259 */
zend_ini_scanner_get_lineno(void)260 ZEND_COLD int zend_ini_scanner_get_lineno(void)
261 {
262 	return SCNG(lineno);
263 }
264 /* }}} */
265 
266 /* {{{ zend_ini_scanner_get_filename()
267 */
zend_ini_scanner_get_filename(void)268 ZEND_COLD char *zend_ini_scanner_get_filename(void)
269 {
270 	return ini_filename ? ini_filename : "Unknown";
271 }
272 /* }}} */
273 
274 /* {{{ zend_ini_open_file_for_scanning()
275 */
zend_ini_open_file_for_scanning(zend_file_handle * fh,int scanner_mode)276 int zend_ini_open_file_for_scanning(zend_file_handle *fh, int scanner_mode)
277 {
278 	char *buf;
279 	size_t size;
280 
281 	if (zend_stream_fixup(fh, &buf, &size) == FAILURE) {
282 		return FAILURE;
283 	}
284 
285 	if (init_ini_scanner(scanner_mode, fh) == FAILURE) {
286 		zend_file_handle_dtor(fh);
287 		return FAILURE;
288 	}
289 
290 	yy_scan_buffer(buf, (unsigned int)size);
291 
292 	return SUCCESS;
293 }
294 /* }}} */
295 
296 /* {{{ zend_ini_prepare_string_for_scanning()
297 */
zend_ini_prepare_string_for_scanning(char * str,int scanner_mode)298 int zend_ini_prepare_string_for_scanning(char *str, int scanner_mode)
299 {
300 	int len = (int)strlen(str);
301 
302 	if (init_ini_scanner(scanner_mode, NULL) == FAILURE) {
303 		return FAILURE;
304 	}
305 
306 	yy_scan_buffer(str, len);
307 
308 	return SUCCESS;
309 }
310 /* }}} */
311 
312 /* {{{ zend_ini_escape_string()
313  */
zend_ini_escape_string(zval * lval,char * str,int len,char quote_type)314 static void zend_ini_escape_string(zval *lval, char *str, int len, char quote_type)
315 {
316 	register char *s, *t;
317 	char *end;
318 
319 	zend_ini_copy_value(lval, str, len);
320 
321 	/* convert escape sequences */
322 	s = t = Z_STRVAL_P(lval);
323 	end = s + Z_STRLEN_P(lval);
324 
325 	while (s < end) {
326 		if (*s == '\\') {
327 			s++;
328 			if (s >= end) {
329 				*t++ = '\\';
330 				continue;
331 			}
332 			switch (*s) {
333 				case '"':
334 					if (*s != quote_type) {
335 						*t++ = '\\';
336 						*t++ = *s;
337 						break;
338 					}
339 				case '\\':
340 				case '$':
341 					*t++ = *s;
342 					Z_STRLEN_P(lval)--;
343 					break;
344 				default:
345 					*t++ = '\\';
346 					*t++ = *s;
347 					break;
348 			}
349 		} else {
350 			*t++ = *s;
351 		}
352 		if (*s == '\n' || (*s == '\r' && (*(s+1) != '\n'))) {
353 			SCNG(lineno)++;
354 		}
355 		s++;
356 	}
357 	*t = 0;
358 }
359 /* }}} */
360 
ini_lex(zval * ini_lval)361 int ini_lex(zval *ini_lval)
362 {
363 restart:
364 	SCNG(yy_text) = YYCURSOR;
365 
366 /* yymore_restart: */
367 	/* detect EOF */
368 	if (YYCURSOR >= YYLIMIT) {
369 		if (YYSTATE == STATE(ST_VALUE) || YYSTATE == STATE(ST_RAW)) {
370 			BEGIN(INITIAL);
371 			return END_OF_LINE;
372 		}
373 		return 0;
374 	}
375 
376 	/* Eat any UTF-8 BOM we find in the first 3 bytes */
377 	if (YYCURSOR == SCNG(yy_start) && YYCURSOR + 3 < YYLIMIT) {
378 		if (memcmp(YYCURSOR, "\xef\xbb\xbf", 3) == 0) {
379 			YYCURSOR += 3;
380 			goto restart;
381 		}
382 	}
383 /*!re2c
384 re2c:yyfill:check = 0;
385 LNUM [0-9]+
386 DNUM ([0-9]*[\.][0-9]+)|([0-9]+[\.][0-9]*)
387 NUMBER [-]?{LNUM}|{DNUM}
388 ANY_CHAR (.|[\n\t])
389 NEWLINE	("\r"|"\n"|"\r\n")
390 TABS_AND_SPACES [ \t]
391 WHITESPACE [ \t]+
392 CONSTANT [a-zA-Z_][a-zA-Z0-9_]*
393 LABEL [^=\n\r\t;&|^$~(){}!"\[]+
394 TOKENS [:,.\[\]"'()&|^+-/*=%$!~<>?@{}]
395 OPERATORS [&|^~()!]
396 DOLLAR_CURLY "${"
397 
398 SECTION_RAW_CHARS [^\]\n\r]
399 SINGLE_QUOTED_CHARS [^']
400 RAW_VALUE_CHARS [^\n\r;\000]
401 
402 LITERAL_DOLLAR ("$"([^{\000]|("\\"{ANY_CHAR})))
403 VALUE_CHARS         ([^$= \t\n\r;&|^~()!"'\000]|{LITERAL_DOLLAR})
404 SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR})
405 
406 <!*> := yyleng = YYCURSOR - SCNG(yy_text);
407 
408 <INITIAL>"[" { /* Section start */
409 	/* Enter section data lookup state */
410 	if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
411 		BEGIN(ST_SECTION_RAW);
412 	} else {
413 		BEGIN(ST_SECTION_VALUE);
414 	}
415 	return TC_SECTION;
416 }
417 
418 <ST_VALUE,ST_SECTION_VALUE,ST_OFFSET>"'"{SINGLE_QUOTED_CHARS}+"'" { /* Raw string */
419 	/* Eat leading and trailing single quotes */
420 	if (yytext[0] == '\'' && yytext[yyleng - 1] == '\'') {
421 		SCNG(yy_text)++;
422 		yyleng = yyleng - 2;
423 	}
424 	RETURN_TOKEN(TC_RAW, yytext, yyleng);
425 }
426 
427 <ST_SECTION_RAW,ST_SECTION_VALUE>"]"{TABS_AND_SPACES}*{NEWLINE}? { /* End of section */
428 	BEGIN(INITIAL);
429 	SCNG(lineno)++;
430 	return ']';
431 }
432 
433 <INITIAL>{LABEL}"["{TABS_AND_SPACES}* { /* Start of option with offset */
434 	/* Eat leading whitespace */
435 	EAT_LEADING_WHITESPACE();
436 
437 	/* Eat trailing whitespace and [ */
438 	EAT_TRAILING_WHITESPACE_EX('[');
439 
440 	/* Enter offset lookup state */
441 	BEGIN(ST_OFFSET);
442 
443 	RETURN_TOKEN(TC_OFFSET, yytext, yyleng);
444 }
445 
446 <ST_OFFSET>{TABS_AND_SPACES}*"]" { /* End of section or an option offset */
447 	BEGIN(INITIAL);
448 	return ']';
449 }
450 
451 <ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{DOLLAR_CURLY} { /* Variable start */
452 	yy_push_state(ST_VARNAME);
453 	return TC_DOLLAR_CURLY;
454 }
455 
456 <ST_VARNAME>{LABEL} { /* Variable name */
457 	/* Eat leading whitespace */
458 	EAT_LEADING_WHITESPACE();
459 
460 	/* Eat trailing whitespace */
461 	EAT_TRAILING_WHITESPACE();
462 
463 	RETURN_TOKEN(TC_VARNAME, yytext, yyleng);
464 }
465 
466 <ST_VARNAME>"}" { /* Variable end */
467 	yy_pop_state();
468 	return '}';
469 }
470 
471 <INITIAL,ST_VALUE>("true"|"on"|"yes"){TABS_AND_SPACES}* { /* TRUE value (when used outside option value/offset this causes parse error!) */
472 	RETURN_TOKEN(BOOL_TRUE, "1", 1);
473 }
474 
475 <INITIAL,ST_VALUE>("false"|"off"|"no"|"none"){TABS_AND_SPACES}* { /* FALSE value (when used outside option value/offset this causes parse error!)*/
476 	RETURN_TOKEN(BOOL_FALSE, "", 0);
477 }
478 
479 <INITIAL,ST_VALUE>("null"){TABS_AND_SPACES}* {
480 	RETURN_TOKEN(NULL_NULL, "", 0);
481 }
482 
483 <INITIAL>{LABEL} { /* Get option name */
484 	/* Eat leading whitespace */
485 	EAT_LEADING_WHITESPACE();
486 
487 	/* Eat trailing whitespace */
488 	EAT_TRAILING_WHITESPACE();
489 
490 	RETURN_TOKEN(TC_LABEL, yytext, yyleng);
491 }
492 
493 <INITIAL>{TABS_AND_SPACES}*[=]{TABS_AND_SPACES}* { /* Start option value */
494 	if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) {
495 		BEGIN(ST_RAW);
496 	} else {
497 		BEGIN(ST_VALUE);
498 	}
499 	return '=';
500 }
501 
502 <ST_RAW>{RAW_VALUE_CHARS} { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
503 	unsigned char *sc = NULL;
504 	EAT_LEADING_WHITESPACE();
505 	while (YYCURSOR < YYLIMIT) {
506 		switch (*YYCURSOR) {
507 			case '\n':
508 			case '\r':
509 				goto end_raw_value_chars;
510 				break;
511 			case ';':
512 				if (sc == NULL) {
513 					sc = YYCURSOR;
514 				}
515 				YYCURSOR++;
516 				break;
517 			case '"':
518 				if (yytext[0] == '"') {
519 					sc = NULL;
520 				}
521 				YYCURSOR++;
522 				break;
523 			default:
524 				YYCURSOR++;
525 				break;
526 		}
527 	}
528 end_raw_value_chars:
529 	if (sc) {
530 		yyleng = sc - SCNG(yy_text);
531 	} else {
532 		yyleng = YYCURSOR - SCNG(yy_text);
533 	}
534 
535 	EAT_TRAILING_WHITESPACE();
536 
537 	/* Eat leading and trailing double quotes */
538 	if (yyleng > 1 && yytext[0] == '"' && yytext[yyleng - 1] == '"') {
539 		SCNG(yy_text)++;
540 		yyleng = yyleng - 2;
541 	}
542 
543 	RETURN_TOKEN(TC_RAW, yytext, yyleng);
544 }
545 
546 <ST_SECTION_RAW>{SECTION_RAW_CHARS}+ { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */
547 	RETURN_TOKEN(TC_RAW, yytext, yyleng);
548 }
549 
550 <ST_VALUE,ST_RAW>{TABS_AND_SPACES}*{NEWLINE} { /* End of option value */
551 	BEGIN(INITIAL);
552 	SCNG(lineno)++;
553 	return END_OF_LINE;
554 }
555 
556 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{CONSTANT} { /* Get constant option value */
557 	RETURN_TOKEN(TC_CONSTANT, yytext, yyleng);
558 }
559 
560 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{NUMBER} { /* Get number option value as string */
561 	RETURN_TOKEN(TC_NUMBER, yytext, yyleng);
562 }
563 
564 <INITIAL>{TOKENS} { /* Disallow these chars outside option values */
565 	return yytext[0];
566 }
567 
568 <ST_VALUE>{OPERATORS}{TABS_AND_SPACES}* { /* Boolean operators */
569 	return yytext[0];
570 }
571 
572 <ST_VALUE>[=] { /* Make = used in option value to trigger error */
573 	yyless(0);
574 	BEGIN(INITIAL);
575 	return END_OF_LINE;
576 }
577 
578 <ST_VALUE>{VALUE_CHARS}+ { /* Get everything else as option/offset value */
579 	RETURN_TOKEN(TC_STRING, yytext, yyleng);
580 }
581 
582 <ST_SECTION_VALUE,ST_OFFSET>{SECTION_VALUE_CHARS}+ { /* Get rest as section/offset value */
583 	RETURN_TOKEN(TC_STRING, yytext, yyleng);
584 }
585 
586 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{TABS_AND_SPACES}*["] { /* Double quoted '"' string start */
587 	yy_push_state(ST_DOUBLE_QUOTES);
588 	return '"';
589 }
590 
591 <ST_DOUBLE_QUOTES>["]{TABS_AND_SPACES}* { /* Double quoted '"' string ends */
592 	yy_pop_state();
593 	return '"';
594 }
595 
596 <ST_DOUBLE_QUOTES>[^] { /* Escape double quoted string contents */
597 	if (YYCURSOR > YYLIMIT) {
598 		return 0;
599 	}
600 
601 	while (YYCURSOR < YYLIMIT) {
602 		switch (*YYCURSOR++) {
603 			case '"':
604 				if (YYCURSOR < YYLIMIT && YYCURSOR[-2] == '\\' && *YYCURSOR != '\r' && *YYCURSOR != '\n') {
605 					continue;
606 				}
607 				break;
608 			case '$':
609 				if (*YYCURSOR == '{') {
610 					break;
611 				}
612 				continue;
613 			case '\\':
614 				if (YYCURSOR < YYLIMIT && *YYCURSOR != '"') {
615 					YYCURSOR++;
616 				}
617 				/* fall through */
618 			default:
619 				continue;
620 		}
621 
622 		YYCURSOR--;
623 		break;
624 	}
625 
626 	yyleng = YYCURSOR - SCNG(yy_text);
627 
628 	zend_ini_escape_string(ini_lval, yytext, yyleng, '"');
629 	return TC_QUOTED_STRING;
630 }
631 
632 <ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{WHITESPACE} {
633 	RETURN_TOKEN(TC_WHITESPACE, yytext, yyleng);
634 }
635 
636 <INITIAL,ST_RAW>{TABS_AND_SPACES}+ {
637 	/* eat whitespace */
638 	goto restart;
639 }
640 
641 <INITIAL>{TABS_AND_SPACES}*{NEWLINE} {
642 	SCNG(lineno)++;
643 	return END_OF_LINE;
644 }
645 
646 <INITIAL,ST_VALUE,ST_RAW>{TABS_AND_SPACES}*[;][^\r\n]*{NEWLINE} { /* Comment */
647 	BEGIN(INITIAL);
648 	SCNG(lineno)++;
649 	return END_OF_LINE;
650 }
651 
652 <ST_VALUE,ST_RAW>[^] { /* End of option value (if EOF is reached before EOL */
653 	BEGIN(INITIAL);
654 	return END_OF_LINE;
655 }
656 
657 <*>[^] {
658 	return 0;
659 }
660 
661 */
662 }
663