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