1 %top{
2 /*-------------------------------------------------------------------------
3  *
4  * pgc.l
5  *	  lexical scanner for ecpg
6  *
7  * This is a modified version of src/backend/parser/scan.l
8  *
9  * The ecpg scanner is not backup-free, so the fail rules are
10  * only here to simplify syncing this file with scan.l.
11  *
12  *
13  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
14  * Portions Copyright (c) 1994, Regents of the University of California
15  *
16  * IDENTIFICATION
17  *	  src/interfaces/ecpg/preproc/pgc.l
18  *
19  *-------------------------------------------------------------------------
20  */
21 #include "postgres_fe.h"
22 
23 #include <ctype.h>
24 #include <limits.h>
25 
26 #include "common/string.h"
27 
28 #include "preproc_extern.h"
29 #include "preproc.h"
30 }
31 
32 %{
33 
34 /* LCOV_EXCL_START */
35 
36 extern YYSTYPE base_yylval;
37 
38 static int		xcdepth = 0;	/* depth of nesting in slash-star comments */
39 static char	   *dolqstart = NULL;	/* current $foo$ quote start string */
40 
41 /*
42  * literalbuf is used to accumulate literal values when multiple rules
43  * are needed to parse a single literal.  Call startlit to reset buffer
44  * to empty, addlit to add text.  Note that the buffer is permanently
45  * malloc'd to the largest size needed so far in the current run.
46  */
47 static char	   *literalbuf = NULL;		/* expandable buffer */
48 static int		literallen;				/* actual current length */
49 static int		literalalloc;			/* current allocated buffer size */
50 
51 /* Used for detecting global state together with braces_open */
52 static int		parenths_open;
53 
54 /* Used to tell parse_include() whether the command was #include or #include_next */
55 static bool		include_next;
56 
57 #define startlit()	(literalbuf[0] = '\0', literallen = 0)
58 static void addlit(char *ytext, int yleng);
59 static void addlitchar(unsigned char);
60 static int	process_integer_literal(const char *token, YYSTYPE *lval);
61 static void parse_include(void);
62 static bool ecpg_isspace(char ch);
63 static bool isdefine(void);
64 static bool isinformixdefine(void);
65 
66 char *token_start;
67 
68 /* vars to keep track of start conditions when scanning literals */
69 static int state_before_str_start;
70 static int state_before_str_stop;
71 
72 struct _yy_buffer
73 {
74 	YY_BUFFER_STATE		buffer;
75 	long				lineno;
76 	char			   *filename;
77 	struct _yy_buffer  *next;
78 } *yy_buffer = NULL;
79 
80 static char *old;
81 
82 /*
83  * Vars for handling ifdef/elif/endif constructs.  preproc_tos is the current
84  * nesting depth of such constructs, and stacked_if_value[preproc_tos] is the
85  * state for the innermost level.  (For convenience, stacked_if_value[0] is
86  * initialized as though we are in the active branch of some outermost IF.)
87  * The active field is true if the current branch is active (being expanded).
88  * The saw_active field is true if we have found any successful branch,
89  * so that all subsequent branches of this level should be skipped.
90  * The else_branch field is true if we've found an 'else' (so that another
91  * 'else' or 'elif' at this level is an error.)
92  * For IFs nested within an inactive branch, all branches always have active
93  * set to false, but saw_active and else_branch are maintained normally.
94  * ifcond is valid only while evaluating an if-condition; it's true if we
95  * are doing ifdef, false if ifndef.
96  */
97 #define MAX_NESTED_IF 128
98 static short preproc_tos;
99 static bool ifcond;
100 static struct _if_value
101 {
102 	bool active;
103 	bool saw_active;
104 	bool else_branch;
105 } stacked_if_value[MAX_NESTED_IF];
106 
107 %}
108 
109 %option 8bit
110 %option never-interactive
111 %option nodefault
112 %option noinput
113 %option noyywrap
114 %option warn
115 %option yylineno
116 %option prefix="base_yy"
117 
118 /*
119  * OK, here is a short description of lex/flex rules behavior.
120  * The longest pattern which matches an input string is always chosen.
121  * For equal-length patterns, the first occurring in the rules list is chosen.
122  * INITIAL is the starting state, to which all non-conditional rules apply.
123  * Exclusive states change parsing rules while the state is active.  When in
124  * an exclusive state, only those rules defined for that state apply.
125  *
126  * We use exclusive states for quoted strings, extended comments,
127  * and to eliminate parsing troubles for numeric strings.
128  * Exclusive states:
129  *  <xb> bit string literal
130  *  <xc> extended C-style comments
131  *  <xd> delimited identifiers (double-quoted identifiers)
132  *  <xdc> double-quoted strings in C
133  *  <xh> hexadecimal numeric string
134  *  <xn> national character quoted strings
135  *  <xq> standard quoted strings
136  *  <xqs> quote stop (detect continued strings)
137  *  <xe> extended quoted strings (support backslash escape sequences)
138  *  <xqc> single-quoted strings in C
139  *  <xdolq> $foo$ quoted strings
140  *  <xui> quoted identifier with Unicode escapes
141  *  <xus> quoted string with Unicode escapes
142  *  <xcond> condition of an EXEC SQL IFDEF construct
143  *  <xskip> skipping the inactive part of an EXEC SQL IFDEF construct
144  *
145  * Note: we intentionally don't mimic the backend's <xeu> state; we have
146  * no need to distinguish it from <xe> state.
147  *
148  * Remember to add an <<EOF>> case whenever you add a new exclusive state!
149  * The default one is probably not the right thing.
150  */
151 
152 %x xb
153 %x xc
154 %x xd
155 %x xdc
156 %x xh
157 %x xn
158 %x xq
159 %x xqs
160 %x xe
161 %x xqc
162 %x xdolq
163 %x xui
164 %x xus
165 %x xcond
166 %x xskip
167 
168 /* Additional exclusive states that are specific to ECPG */
169 %x C SQL incl def def_ident undef
170 
171 /*
172  * In order to make the world safe for Windows and Mac clients as well as
173  * Unix ones, we accept either \n or \r as a newline.  A DOS-style \r\n
174  * sequence will be seen as two successive newlines, but that doesn't cause
175  * any problems.  SQL-style comments, which start with -- and extend to the
176  * next newline, are treated as equivalent to a single whitespace character.
177  *
178  * NOTE a fine point: if there is no newline following --, we will absorb
179  * everything to the end of the input as a comment.  This is correct.  Older
180  * versions of Postgres failed to recognize -- as a comment if the input
181  * did not end with a newline.
182  *
183  * XXX perhaps \f (formfeed) should be treated as a newline as well?
184  *
185  * XXX if you change the set of whitespace characters, fix ecpg_isspace()
186  * to agree.
187  */
188 
189 space			[ \t\n\r\f]
190 horiz_space		[ \t\f]
191 newline			[\n\r]
192 non_newline		[^\n\r]
193 
194 comment			("--"{non_newline}*)
195 
196 whitespace		({space}+|{comment})
197 
198 /*
199  * SQL requires at least one newline in the whitespace separating
200  * string literals that are to be concatenated.  Silly, but who are we
201  * to argue?  Note that {whitespace_with_newline} should not have * after
202  * it, whereas {whitespace} should generally have a * after it...
203  */
204 
205 horiz_whitespace		({horiz_space}|{comment})
206 whitespace_with_newline	({horiz_whitespace}*{newline}{whitespace}*)
207 
208 quote			'
209 /* If we see {quote} then {quotecontinue}, the quoted string continues */
210 quotecontinue	{whitespace_with_newline}{quote}
211 
212 /*
213  * {quotecontinuefail} is needed to avoid lexer backup when we fail to match
214  * {quotecontinue}.  It might seem that this could just be {whitespace}*,
215  * but if there's a dash after {whitespace_with_newline}, it must be consumed
216  * to see if there's another dash --- which would start a {comment} and thus
217  * allow continuation of the {quotecontinue} token.
218  */
219 quotecontinuefail	{whitespace}*"-"?
220 
221 /* Bit string
222  */
223 xbstart			[bB]{quote}
224 xbinside		[^']*
225 
226 /* Hexadecimal number */
227 xhstart			[xX]{quote}
228 xhinside		[^']*
229 
230 /* National character */
231 xnstart			[nN]{quote}
232 
233 /* Quoted string that allows backslash escapes */
234 xestart			[eE]{quote}
235 xeinside		[^\\']+
236 xeescape		[\\][^0-7]
237 xeoctesc		[\\][0-7]{1,3}
238 xehexesc		[\\]x[0-9A-Fa-f]{1,2}
239 xeunicode		[\\](u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})
240 
241 /* Extended quote
242  * xqdouble implements embedded quote, ''''
243  */
244 xqstart			{quote}
245 xqdouble		{quote}{quote}
246 xqcquote		[\\]{quote}
247 xqinside		[^']+
248 
249 /* $foo$ style quotes ("dollar quoting")
250  * The quoted string starts with $foo$ where "foo" is an optional string
251  * in the form of an identifier, except that it may not contain "$",
252  * and extends to the first occurrence of an identical string.
253  * There is *no* processing of the quoted text.
254  *
255  * {dolqfailed} is an error rule to avoid scanner backup when {dolqdelim}
256  * fails to match its trailing "$".
257  */
258 dolq_start		[A-Za-z\200-\377_]
259 dolq_cont		[A-Za-z\200-\377_0-9]
260 dolqdelim		\$({dolq_start}{dolq_cont}*)?\$
261 dolqfailed		\${dolq_start}{dolq_cont}*
262 dolqinside		[^$]+
263 
264 /* Double quote
265  * Allows embedded spaces and other special characters into identifiers.
266  */
267 dquote			\"
268 xdstart			{dquote}
269 xdstop			{dquote}
270 xddouble		{dquote}{dquote}
271 xdinside		[^"]+
272 
273 /* Quoted identifier with Unicode escapes */
274 xuistart		[uU]&{dquote}
275 
276 /* Quoted string with Unicode escapes */
277 xusstart		[uU]&{quote}
278 
279 /* special stuff for C strings */
280 xdcqq			\\\\
281 xdcqdq			\\\"
282 xdcother		[^"]
283 xdcinside		({xdcqq}|{xdcqdq}|{xdcother})
284 
285 
286 /* C-style comments
287  *
288  * The "extended comment" syntax closely resembles allowable operator syntax.
289  * The tricky part here is to get lex to recognize a string starting with
290  * slash-star as a comment, when interpreting it as an operator would produce
291  * a longer match --- remember lex will prefer a longer match!  Also, if we
292  * have something like plus-slash-star, lex will think this is a 3-character
293  * operator whereas we want to see it as a + operator and a comment start.
294  * The solution is two-fold:
295  * 1. append {op_chars}* to xcstart so that it matches as much text as
296  *    {operator} would. Then the tie-breaker (first matching rule of same
297  *    length) ensures xcstart wins.  We put back the extra stuff with yyless()
298  *    in case it contains a star-slash that should terminate the comment.
299  * 2. In the operator rule, check for slash-star within the operator, and
300  *    if found throw it back with yyless().  This handles the plus-slash-star
301  *    problem.
302  * Dash-dash comments have similar interactions with the operator rule.
303  */
304 xcstart			\/\*{op_chars}*
305 xcstop			\*+\/
306 xcinside		[^*/]+
307 
308 digit			[0-9]
309 ident_start		[A-Za-z\200-\377_]
310 ident_cont		[A-Za-z\200-\377_0-9\$]
311 
312 identifier		{ident_start}{ident_cont}*
313 
314 array			({ident_cont}|{whitespace}|[\[\]\+\-\*\%\/\(\)\>\.])*
315 
316 /* Assorted special-case operators and operator-like tokens */
317 typecast		"::"
318 dot_dot			\.\.
319 colon_equals	":="
320 
321 /*
322  * These operator-like tokens (unlike the above ones) also match the {operator}
323  * rule, which means that they might be overridden by a longer match if they
324  * are followed by a comment start or a + or - character. Accordingly, if you
325  * add to this list, you must also add corresponding code to the {operator}
326  * block to return the correct token in such cases. (This is not needed in
327  * psqlscan.l since the token value is ignored there.)
328  */
329 equals_greater	"=>"
330 less_equals		"<="
331 greater_equals	">="
332 less_greater	"<>"
333 not_equals		"!="
334 
335 /*
336  * "self" is the set of chars that should be returned as single-character
337  * tokens.  "op_chars" is the set of chars that can make up "Op" tokens,
338  * which can be one or more characters long (but if a single-char token
339  * appears in the "self" set, it is not to be returned as an Op).  Note
340  * that the sets overlap, but each has some chars that are not in the other.
341  *
342  * If you change either set, adjust the character lists appearing in the
343  * rule for "operator"!
344  */
345 self			[,()\[\].;\:\+\-\*\/\%\^\<\>\=]
346 op_chars		[\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
347 operator		{op_chars}+
348 
349 /* we no longer allow unary minus in numbers.
350  * instead we pass it separately to parser. there it gets
351  * coerced via doNegate() -- Leon aug 20 1999
352  *
353  * {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
354  *
355  * {realfail1} and {realfail2} are added to prevent the need for scanner
356  * backup when the {real} rule fails to match completely.
357  */
358 
359 integer			{digit}+
360 decimal			(({digit}*\.{digit}+)|({digit}+\.{digit}*))
361 decimalfail		{digit}+\.\.
362 real			({integer}|{decimal})[Ee][-+]?{digit}+
363 realfail1		({integer}|{decimal})[Ee]
364 realfail2		({integer}|{decimal})[Ee][-+]
365 
366 param			\${integer}
367 
368 /* special characters for other dbms */
369 /* we have to react differently in compat mode */
370 informix_special	[\$]
371 
372 other			.
373 
374 /*
375  * Dollar quoted strings are totally opaque, and no escaping is done on them.
376  * Other quoted strings must allow some special characters such as single-quote
377  *  and newline.
378  * Embedded single-quotes are implemented both in the SQL standard
379  *  style of two adjacent single quotes "''" and in the Postgres/Java style
380  *  of escaped-quote "\'".
381  * Other embedded escaped characters are matched explicitly and the leading
382  *  backslash is dropped from the string.
383  * Note that xcstart must appear before operator, as explained above!
384  *  Also whitespace (comment) must appear before operator.
385  */
386 
387 /* some stuff needed for ecpg */
388 exec			[eE][xX][eE][cC]
389 sql				[sS][qQ][lL]
390 define			[dD][eE][fF][iI][nN][eE]
391 include			[iI][nN][cC][lL][uU][dD][eE]
392 include_next	[iI][nN][cC][lL][uU][dD][eE]_[nN][eE][xX][tT]
393 import			[iI][mM][pP][oO][rR][tT]
394 undef			[uU][nN][dD][eE][fF]
395 
396 /* C version of hex number */
397 xch				0[xX][0-9A-Fa-f]*
398 
399 ccomment		"//".*\n
400 
401 if				[iI][fF]
402 ifdef			[iI][fF][dD][eE][fF]
403 ifndef			[iI][fF][nN][dD][eE][fF]
404 else			[eE][lL][sS][eE]
405 elif			[eE][lL][iI][fF]
406 endif			[eE][nN][dD][iI][fF]
407 
408 struct			[sS][tT][rR][uU][cC][tT]
409 
410 exec_sql		{exec}{space}*{sql}{space}*
411 ipdigit			({digit}|{digit}{digit}|{digit}{digit}{digit})
412 ip				{ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit}
413 
414 /* we might want to parse all cpp include files */
415 cppinclude		{space}*#{include}{space}*
416 cppinclude_next		{space}*#{include_next}{space}*
417 
418 /* take care of cpp lines, they may also be continued */
419 /* first a general line for all commands not starting with "i" */
420 /* and then the other commands starting with "i", we have to add these
421  * separately because the cppline production would match on "include" too
422  */
423 cppline			{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+\/)|.|\\{space}*{newline})*{newline}
424 
425 %%
426 
427 %{
428 		/* code to execute during start of each call of yylex() */
429 		token_start = NULL;
430 %}
431 
432 <SQL>{
433 {whitespace}	{
434 					/* ignore */
435 				}
436 } /* <SQL> */
437 
438 <C,SQL>{
439 {xcstart}		{
440 					token_start = yytext;
441 					state_before_str_start = YYSTATE;
442 					xcdepth = 0;
443 					BEGIN(xc);
444 					/* Put back any characters past slash-star; see above */
445 					yyless(2);
446 					fputs("/*", yyout);
447 				}
448 } /* <C,SQL> */
449 
450 <xc>{
451 {xcstart}		{
452 					if (state_before_str_start == SQL)
453 					{
454 						xcdepth++;
455 						/* Put back any characters past slash-star; see above */
456 						yyless(2);
457 						fputs("/_*", yyout);
458 					}
459 					else if (state_before_str_start == C)
460 					{
461 						ECHO;
462 					}
463 				}
464 
465 {xcstop}		{
466 					if (state_before_str_start == SQL)
467 					{
468 						if (xcdepth <= 0)
469 						{
470 							ECHO;
471 							BEGIN(SQL);
472 							token_start = NULL;
473 						}
474 						else
475 						{
476 							xcdepth--;
477 							fputs("*_/", yyout);
478 						}
479 					}
480 					else if (state_before_str_start == C)
481 					{
482 						ECHO;
483 						BEGIN(C);
484 						token_start = NULL;
485 					}
486 				}
487 
488 {xcinside}		{
489 					ECHO;
490 				}
491 
492 {op_chars}		{
493 					ECHO;
494 				}
495 
496 \*+				{
497 					ECHO;
498 				}
499 
500 <<EOF>>			{
501 					mmfatal(PARSE_ERROR, "unterminated /* comment");
502 				}
503 } /* <xc> */
504 
505 <SQL>{
506 {xbstart}		{
507 					token_start = yytext;
508 					state_before_str_start = YYSTATE;
509 					BEGIN(xb);
510 					startlit();
511 				}
512 } /* <SQL> */
513 
514 <xh>{xhinside}	|
515 <xb>{xbinside}	{
516 					addlit(yytext, yyleng);
517 				}
518 <xb><<EOF>>		{ mmfatal(PARSE_ERROR, "unterminated bit string literal"); }
519 
520 <SQL>{xhstart}	{
521 					token_start = yytext;
522 					state_before_str_start = YYSTATE;
523 					BEGIN(xh);
524 					startlit();
525 				}
526 <xh><<EOF>>		{ mmfatal(PARSE_ERROR, "unterminated hexadecimal string literal"); }
527 
528 <C>{xqstart}	{
529 					token_start = yytext;
530 					state_before_str_start = YYSTATE;
531 					BEGIN(xqc);
532 					startlit();
533 				}
534 
535 <SQL>{
536 {xnstart}		{
537 					/* National character.
538 					 * Transfer it as-is to the backend.
539 					 */
540 					token_start = yytext;
541 					state_before_str_start = YYSTATE;
542 					BEGIN(xn);
543 					startlit();
544 				}
545 
546 {xqstart}		{
547 					token_start = yytext;
548 					state_before_str_start = YYSTATE;
549 					BEGIN(xq);
550 					startlit();
551 				}
552 {xestart}		{
553 					token_start = yytext;
554 					state_before_str_start = YYSTATE;
555 					BEGIN(xe);
556 					startlit();
557 				}
558 {xusstart}		{
559 					token_start = yytext;
560 					state_before_str_start = YYSTATE;
561 					BEGIN(xus);
562 					startlit();
563 				}
564 } /* <SQL> */
565 
566 <xb,xh,xq,xqc,xe,xn,xus>{quote} {
567 					/*
568 					 * When we are scanning a quoted string and see an end
569 					 * quote, we must look ahead for a possible continuation.
570 					 * If we don't see one, we know the end quote was in fact
571 					 * the end of the string.  To reduce the lexer table size,
572 					 * we use a single "xqs" state to do the lookahead for all
573 					 * types of strings.
574 					 */
575 					state_before_str_stop = YYSTATE;
576 					BEGIN(xqs);
577 				}
578 <xqs>{quotecontinue} {
579 					/*
580 					 * Found a quote continuation, so return to the in-quote
581 					 * state and continue scanning the literal.  Nothing is
582 					 * added to the literal's contents.
583 					 */
584 					BEGIN(state_before_str_stop);
585 				}
586 <xqs>{quotecontinuefail} |
587 <xqs>{other} |
588 <xqs><<EOF>>	{
589 					/*
590 					 * Failed to see a quote continuation.  Throw back
591 					 * everything after the end quote, and handle the string
592 					 * according to the state we were in previously.
593 					 */
594 					yyless(0);
595 					BEGIN(state_before_str_start);
596 
597 					switch (state_before_str_stop)
598 					{
599 						case xb:
600 							if (literalbuf[strspn(literalbuf, "01")] != '\0')
601 								mmerror(PARSE_ERROR, ET_ERROR, "invalid bit string literal");
602 							base_yylval.str = psprintf("b'%s'", literalbuf);
603 							return BCONST;
604 						case xh:
605 							if (literalbuf[strspn(literalbuf, "0123456789abcdefABCDEF")] != '\0')
606 								mmerror(PARSE_ERROR, ET_ERROR, "invalid hex string literal");
607 							base_yylval.str = psprintf("x'%s'", literalbuf);
608 							return XCONST;
609 						case xq:
610 							/* fallthrough */
611 						case xqc:
612 							base_yylval.str = psprintf("'%s'", literalbuf);
613 							return SCONST;
614 						case xe:
615 							base_yylval.str = psprintf("E'%s'", literalbuf);
616 							return SCONST;
617 						case xn:
618 							base_yylval.str = psprintf("N'%s'", literalbuf);
619 							return SCONST;
620 						case xus:
621 							base_yylval.str = psprintf("U&'%s'", literalbuf);
622 							return USCONST;
623 						default:
624 							mmfatal(PARSE_ERROR, "unhandled previous state in xqs\n");
625 					}
626 				}
627 
628 <xq,xe,xn,xus>{xqdouble}	{ addlit(yytext, yyleng); }
629 <xqc>{xqcquote}				{ addlit(yytext, yyleng); }
630 <xq,xqc,xn,xus>{xqinside}	{ addlit(yytext, yyleng); }
631 <xe>{xeinside}  {
632 					addlit(yytext, yyleng);
633 				}
634 <xe>{xeunicode} {
635 					addlit(yytext, yyleng);
636 				}
637 <xe>{xeescape}  {
638 					addlit(yytext, yyleng);
639 				}
640 <xe>{xeoctesc}  {
641 					addlit(yytext, yyleng);
642 				}
643 <xe>{xehexesc}  {
644 					addlit(yytext, yyleng);
645 				}
646 <xe>.			{
647 					/* This is only needed for \ just before EOF */
648 					addlitchar(yytext[0]);
649 				}
650 <xq,xqc,xe,xn,xus><<EOF>>	{ mmfatal(PARSE_ERROR, "unterminated quoted string"); }
651 
652 <SQL>{
653 {dolqdelim}		{
654 					token_start = yytext;
655 					if (dolqstart)
656 						free(dolqstart);
657 					dolqstart = mm_strdup(yytext);
658 					BEGIN(xdolq);
659 					startlit();
660 					addlit(yytext, yyleng);
661 				}
662 {dolqfailed}	{
663 					/* throw back all but the initial "$" */
664 					yyless(1);
665 					/* and treat it as {other} */
666 					return yytext[0];
667 				}
668 } /* <SQL> */
669 
670 <xdolq>{dolqdelim} {
671 					if (strcmp(yytext, dolqstart) == 0)
672 					{
673 						addlit(yytext, yyleng);
674 						free(dolqstart);
675 						dolqstart = NULL;
676 						BEGIN(SQL);
677 						base_yylval.str = mm_strdup(literalbuf);
678 						return SCONST;
679 					}
680 					else
681 					{
682 						/*
683 						 * When we fail to match $...$ to dolqstart, transfer
684 						 * the $... part to the output, but put back the final
685 						 * $ for rescanning.  Consider $delim$...$junk$delim$
686 						 */
687 						addlit(yytext, yyleng - 1);
688 						yyless(yyleng - 1);
689 					}
690 				}
691 <xdolq>{dolqinside} {
692 					addlit(yytext, yyleng);
693 				}
694 <xdolq>{dolqfailed} {
695 					addlit(yytext, yyleng);
696 				}
697 <xdolq>.		{
698 					/* single quote or dollar sign */
699 					addlitchar(yytext[0]);
700 				}
701 <xdolq><<EOF>>	{ mmfatal(PARSE_ERROR, "unterminated dollar-quoted string"); }
702 
703 <SQL>{
704 {xdstart}		{
705 					state_before_str_start = YYSTATE;
706 					BEGIN(xd);
707 					startlit();
708 				}
709 {xuistart}		{
710 					state_before_str_start = YYSTATE;
711 					BEGIN(xui);
712 					startlit();
713 				}
714 } /* <SQL> */
715 
716 <xd>{xdstop}	{
717 					BEGIN(state_before_str_start);
718 					if (literallen == 0)
719 						mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier");
720 					/*
721 					 * The server will truncate the identifier here.  We do
722 					 * not, as (1) it does not change the result; (2) we don't
723 					 * know what NAMEDATALEN the server might use; (3) this
724 					 * code path is also taken for literal query strings in
725 					 * PREPARE and EXECUTE IMMEDIATE, which can certainly be
726 					 * longer than NAMEDATALEN.
727 					 */
728 					base_yylval.str = mm_strdup(literalbuf);
729 					return CSTRING;
730 				}
731 <xdc>{xdstop}	{
732 					BEGIN(state_before_str_start);
733 					base_yylval.str = mm_strdup(literalbuf);
734 					return CSTRING;
735 				}
736 <xui>{dquote}	{
737 					BEGIN(state_before_str_start);
738 					if (literallen == 2) /* "U&" */
739 						mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier");
740 					/* The backend will truncate the identifier here. We do not as it does not change the result. */
741 					base_yylval.str = psprintf("U&\"%s\"", literalbuf);
742 					return UIDENT;
743 				}
744 <xd,xui>{xddouble}	{
745 					addlit(yytext, yyleng);
746 				}
747 <xd,xui>{xdinside}	{
748 					addlit(yytext, yyleng);
749 				}
750 <xd,xui><<EOF>>	{ mmfatal(PARSE_ERROR, "unterminated quoted identifier"); }
751 <C>{xdstart}	{
752 					state_before_str_start = YYSTATE;
753 					BEGIN(xdc);
754 					startlit();
755 				}
756 <xdc>{xdcinside}	{
757 					addlit(yytext, yyleng);
758 				}
759 <xdc><<EOF>>	{ mmfatal(PARSE_ERROR, "unterminated quoted string"); }
760 
761 <SQL>{
762 {typecast}		{
763 					return TYPECAST;
764 				}
765 
766 {dot_dot}		{
767 					return DOT_DOT;
768 				}
769 
770 {colon_equals}	{
771 					return COLON_EQUALS;
772 				}
773 
774 {equals_greater} {
775 					return EQUALS_GREATER;
776 				}
777 
778 {less_equals}	{
779 					return LESS_EQUALS;
780 				}
781 
782 {greater_equals} {
783 					return GREATER_EQUALS;
784 				}
785 
786 {less_greater}	{
787 					/* We accept both "<>" and "!=" as meaning NOT_EQUALS */
788 					return NOT_EQUALS;
789 				}
790 
791 {not_equals}	{
792 					/* We accept both "<>" and "!=" as meaning NOT_EQUALS */
793 					return NOT_EQUALS;
794 				}
795 
796 {informix_special}	{
797 			  /* are we simulating Informix? */
798 				if (INFORMIX_MODE)
799 				{
800 					unput(':');
801 				}
802 				else
803 					return yytext[0];
804 				}
805 
806 {self}			{
807 					/*
808 					 * We may find a ';' inside a structure
809 					 * definition in a TYPE or VAR statement.
810 					 * This is not an EOL marker.
811 					 */
812 					if (yytext[0] == ';' && struct_level == 0)
813 						BEGIN(C);
814 					return yytext[0];
815 				}
816 
817 {operator}		{
818 					/*
819 					 * Check for embedded slash-star or dash-dash; those
820 					 * are comment starts, so operator must stop there.
821 					 * Note that slash-star or dash-dash at the first
822 					 * character will match a prior rule, not this one.
823 					 */
824 					int			nchars = yyleng;
825 					char	   *slashstar = strstr(yytext, "/*");
826 					char	   *dashdash = strstr(yytext, "--");
827 
828 					if (slashstar && dashdash)
829 					{
830 						/* if both appear, take the first one */
831 						if (slashstar > dashdash)
832 							slashstar = dashdash;
833 					}
834 					else if (!slashstar)
835 						slashstar = dashdash;
836 					if (slashstar)
837 						nchars = slashstar - yytext;
838 
839 					/*
840 					 * For SQL compatibility, '+' and '-' cannot be the
841 					 * last char of a multi-char operator unless the operator
842 					 * contains chars that are not in SQL operators.
843 					 * The idea is to lex '=-' as two operators, but not
844 					 * to forbid operator names like '?-' that could not be
845 					 * sequences of SQL operators.
846 					 */
847 					if (nchars > 1 &&
848 						(yytext[nchars - 1] == '+' ||
849 						 yytext[nchars - 1] == '-'))
850 					{
851 						int			ic;
852 
853 						for (ic = nchars - 2; ic >= 0; ic--)
854 						{
855 							char c = yytext[ic];
856 							if (c == '~' || c == '!' || c == '@' ||
857 								c == '#' || c == '^' || c == '&' ||
858 								c == '|' || c == '`' || c == '?' ||
859 								c == '%')
860 								break;
861 						}
862 						if (ic < 0)
863 						{
864 							/*
865 							 * didn't find a qualifying character, so remove
866 							 * all trailing [+-]
867 							 */
868 							do {
869 								nchars--;
870 							} while (nchars > 1 &&
871 								 (yytext[nchars - 1] == '+' ||
872 								  yytext[nchars - 1] == '-'));
873 						}
874 					}
875 
876 					if (nchars < yyleng)
877 					{
878 						/* Strip the unwanted chars from the token */
879 						yyless(nchars);
880 						/*
881 						 * If what we have left is only one char, and it's
882 						 * one of the characters matching "self", then
883 						 * return it as a character token the same way
884 						 * that the "self" rule would have.
885 						 */
886 						if (nchars == 1 &&
887 							strchr(",()[].;:+-*/%^<>=", yytext[0]))
888 							return yytext[0];
889 						/*
890 						 * Likewise, if what we have left is two chars, and
891 						 * those match the tokens ">=", "<=", "=>", "<>" or
892 						 * "!=", then we must return the appropriate token
893 						 * rather than the generic Op.
894 						 */
895 						if (nchars == 2)
896 						{
897 							if (yytext[0] == '=' && yytext[1] == '>')
898 								return EQUALS_GREATER;
899 							if (yytext[0] == '>' && yytext[1] == '=')
900 								return GREATER_EQUALS;
901 							if (yytext[0] == '<' && yytext[1] == '=')
902 								return LESS_EQUALS;
903 							if (yytext[0] == '<' && yytext[1] == '>')
904 								return NOT_EQUALS;
905 							if (yytext[0] == '!' && yytext[1] == '=')
906 								return NOT_EQUALS;
907 						}
908 					}
909 
910 					base_yylval.str = mm_strdup(yytext);
911 					return Op;
912 				}
913 
914 {param}			{
915 					base_yylval.ival = atol(yytext+1);
916 					return PARAM;
917 				}
918 
919 {ip}			{
920 					base_yylval.str = mm_strdup(yytext);
921 					return IP;
922 				}
923 }  /* <SQL> */
924 
925 <C,SQL>{
926 {integer}		{
927 					return process_integer_literal(yytext, &base_yylval);
928 				}
929 {decimal}		{
930 					base_yylval.str = mm_strdup(yytext);
931 					return FCONST;
932 				}
933 {decimalfail}	{
934 					/* throw back the .., and treat as integer */
935 					yyless(yyleng - 2);
936 					return process_integer_literal(yytext, &base_yylval);
937 				}
938 {real}			{
939 					base_yylval.str = mm_strdup(yytext);
940 					return FCONST;
941 				}
942 {realfail1}		{
943 					/*
944 					 * throw back the [Ee], and figure out whether what
945 					 * remains is an {integer} or {decimal}.
946 					 */
947 					yyless(yyleng - 1);
948 					return process_integer_literal(yytext, &base_yylval);
949 				}
950 {realfail2}		{
951 					/* throw back the [Ee][+-], and proceed as above */
952 					yyless(yyleng - 2);
953 					return process_integer_literal(yytext, &base_yylval);
954 				}
955 } /* <C,SQL> */
956 
957 <SQL>{
958 :{identifier}((("->"|\.){identifier})|(\[{array}\]))*	{
959 					base_yylval.str = mm_strdup(yytext+1);
960 					return CVARIABLE;
961 				}
962 
963 {identifier}	{
964 					if (!isdefine())
965 					{
966 						int		kwvalue;
967 
968 						/* Is it an SQL/ECPG keyword? */
969 						kwvalue = ScanECPGKeywordLookup(yytext);
970 						if (kwvalue >= 0)
971 							return kwvalue;
972 
973 						/* Is it a C keyword? */
974 						kwvalue = ScanCKeywordLookup(yytext);
975 						if (kwvalue >= 0)
976 							return kwvalue;
977 
978 						/*
979 						 * None of the above.  Return it as an identifier.
980 						 *
981 						 * The backend will attempt to truncate and case-fold
982 						 * the identifier, but I see no good reason for ecpg
983 						 * to do so; that's just another way that ecpg could get
984 						 * out of step with the backend.
985 						 */
986 						base_yylval.str = mm_strdup(yytext);
987 						return IDENT;
988 					}
989 				}
990 
991 {other}			{
992 					return yytext[0];
993 				}
994 } /* <SQL> */
995 
996 	/*
997 	 * Begin ECPG-specific rules
998 	 */
999 
1000 <C>{exec_sql}		{ BEGIN(SQL); return SQL_START; }
1001 <C>{informix_special}	{
1002 						/* are we simulating Informix? */
1003 						if (INFORMIX_MODE)
1004 						{
1005 							BEGIN(SQL);
1006 							return SQL_START;
1007 						}
1008 						else
1009 							return S_ANYTHING;
1010 					 }
1011 <C>{ccomment}		{ ECHO; }
1012 <C>{xch}			{
1013 						char* endptr;
1014 
1015 						errno = 0;
1016 						base_yylval.ival = strtoul((char *)yytext,&endptr,16);
1017 						if (*endptr != '\0' || errno == ERANGE)
1018 						{
1019 							errno = 0;
1020 							base_yylval.str = mm_strdup(yytext);
1021 							return SCONST;
1022 						}
1023 						return ICONST;
1024 					}
1025 <C>{cppinclude}		{
1026 						if (system_includes)
1027 						{
1028 							include_next = false;
1029 							BEGIN(incl);
1030 						}
1031 						else
1032 						{
1033 							base_yylval.str = mm_strdup(yytext);
1034 							return CPP_LINE;
1035 						}
1036 					}
1037 <C>{cppinclude_next}		{
1038 						if (system_includes)
1039 						{
1040 							include_next = true;
1041 							BEGIN(incl);
1042 						}
1043 						else
1044 						{
1045 							base_yylval.str = mm_strdup(yytext);
1046 							return CPP_LINE;
1047 						}
1048 					}
1049 <C,SQL>{cppline}	{
1050 						base_yylval.str = mm_strdup(yytext);
1051 						return CPP_LINE;
1052 					}
1053 <C>{identifier}		{
1054 						/*
1055 						 * Try to detect a function name:
1056 						 * look for identifiers at the global scope
1057 						 * keep the last identifier before the first '(' and '{'
1058 						 */
1059 						if (braces_open == 0 && parenths_open == 0)
1060 						{
1061 							if (current_function)
1062 								free(current_function);
1063 							current_function = mm_strdup(yytext);
1064 						}
1065 						/* Informix uses SQL defines only in SQL space */
1066 						/* however, some defines have to be taken care of for compatibility */
1067 						if ((!INFORMIX_MODE || !isinformixdefine()) && !isdefine())
1068 						{
1069 							int		kwvalue;
1070 
1071 							kwvalue = ScanCKeywordLookup(yytext);
1072 							if (kwvalue >= 0)
1073 								return kwvalue;
1074 							else
1075 							{
1076 								base_yylval.str = mm_strdup(yytext);
1077 								return IDENT;
1078 							}
1079 						}
1080 					}
1081 <C>{xcstop}			{ mmerror(PARSE_ERROR, ET_ERROR, "nested /* ... */ comments"); }
1082 <C>":"				{ return ':'; }
1083 <C>";"				{ return ';'; }
1084 <C>","				{ return ','; }
1085 <C>"*"				{ return '*'; }
1086 <C>"%"				{ return '%'; }
1087 <C>"/"				{ return '/'; }
1088 <C>"+"				{ return '+'; }
1089 <C>"-"				{ return '-'; }
1090 <C>"("				{ parenths_open++; return '('; }
1091 <C>")"				{ parenths_open--; return ')'; }
1092 <C,xskip>{space}		{ ECHO; }
1093 <C>\{				{ return '{'; }
1094 <C>\}				{ return '}'; }
1095 <C>\[				{ return '['; }
1096 <C>\]				{ return ']'; }
1097 <C>\=				{ return '='; }
1098 <C>"->"				{ return S_MEMBER; }
1099 <C>">>"				{ return S_RSHIFT; }
1100 <C>"<<"				{ return S_LSHIFT; }
1101 <C>"||"				{ return S_OR; }
1102 <C>"&&"				{ return S_AND; }
1103 <C>"++"				{ return S_INC; }
1104 <C>"--"				{ return S_DEC; }
1105 <C>"=="				{ return S_EQUAL; }
1106 <C>"!="				{ return S_NEQUAL; }
1107 <C>"+="				{ return S_ADD; }
1108 <C>"-="				{ return S_SUB; }
1109 <C>"*="				{ return S_MUL; }
1110 <C>"/="				{ return S_DIV; }
1111 <C>"%="				{ return S_MOD; }
1112 <C>"->*"			{ return S_MEMPOINT; }
1113 <C>".*"				{ return S_DOTPOINT; }
1114 <C>{other}			{ return S_ANYTHING; }
1115 <C>{exec_sql}{define}{space}*	{ BEGIN(def_ident); }
1116 <C>{informix_special}{define}{space}*	{
1117 						/* are we simulating Informix? */
1118 						if (INFORMIX_MODE)
1119 						{
1120 							BEGIN(def_ident);
1121 						}
1122 						else
1123 						{
1124 							yyless(1);
1125 							return S_ANYTHING;
1126 						}
1127 					}
1128 <C>{exec_sql}{undef}{space}*		{ BEGIN(undef); }
1129 <C>{informix_special}{undef}{space}* {
1130 						/* are we simulating Informix? */
1131 						if (INFORMIX_MODE)
1132 						{
1133 							BEGIN(undef);
1134 						}
1135 						else
1136 						{
1137 							yyless(1);
1138 							return S_ANYTHING;
1139 						}
1140 					}
1141 <undef>{identifier}{space}*";" {
1142 					struct _defines *ptr, *ptr2 = NULL;
1143 					int i;
1144 
1145 					/*
1146 					 *	Skip the ";" and trailing whitespace. Note that yytext
1147 					 *	contains at least one non-space character plus the ";"
1148 					 */
1149 					for (i = strlen(yytext)-2;
1150 						 i > 0 && ecpg_isspace(yytext[i]);
1151 						 i-- )
1152 						;
1153 					yytext[i+1] = '\0';
1154 
1155 
1156 					for (ptr = defines; ptr != NULL; ptr2 = ptr, ptr = ptr->next)
1157 					{
1158 						if (strcmp(yytext, ptr->olddef) == 0)
1159 						{
1160 							if (ptr2 == NULL)
1161 								defines = ptr->next;
1162 							else
1163 								ptr2->next = ptr->next;
1164 							free(ptr->newdef);
1165 							free(ptr->olddef);
1166 							free(ptr);
1167 							break;
1168 						}
1169 					}
1170 
1171 					BEGIN(C);
1172 				}
1173 <undef>{other}|\n {
1174 						mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL UNDEF command");
1175 						yyterminate();
1176 				}
1177 <C>{exec_sql}{include}{space}*	{ BEGIN(incl); }
1178 <C>{informix_special}{include}{space}* {
1179 					  /* are we simulating Informix? */
1180 					  if (INFORMIX_MODE)
1181 					  {
1182 						  BEGIN(incl);
1183 					  }
1184 					  else
1185 					  {
1186 						  yyless(1);
1187 						  return S_ANYTHING;
1188 					  }
1189 					}
1190 <C,xskip>{exec_sql}{ifdef}{space}* {
1191 					  if (preproc_tos >= MAX_NESTED_IF-1)
1192 						  mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
1193 					  preproc_tos++;
1194 					  stacked_if_value[preproc_tos].active = false;
1195 					  stacked_if_value[preproc_tos].saw_active = false;
1196 					  stacked_if_value[preproc_tos].else_branch = false;
1197 					  ifcond = true;
1198 					  BEGIN(xcond);
1199 					}
1200 <C,xskip>{informix_special}{ifdef}{space}* {
1201 					  /* are we simulating Informix? */
1202 					  if (INFORMIX_MODE)
1203 					  {
1204 						  if (preproc_tos >= MAX_NESTED_IF-1)
1205 							  mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
1206 						  preproc_tos++;
1207 						  stacked_if_value[preproc_tos].active = false;
1208 						  stacked_if_value[preproc_tos].saw_active = false;
1209 						  stacked_if_value[preproc_tos].else_branch = false;
1210 						  ifcond = true;
1211 						  BEGIN(xcond);
1212 					  }
1213 					  else
1214 					  {
1215 						  yyless(1);
1216 						  return S_ANYTHING;
1217 					  }
1218 					}
1219 <C,xskip>{exec_sql}{ifndef}{space}* {
1220 					  if (preproc_tos >= MAX_NESTED_IF-1)
1221 						  mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
1222 					  preproc_tos++;
1223 					  stacked_if_value[preproc_tos].active = false;
1224 					  stacked_if_value[preproc_tos].saw_active = false;
1225 					  stacked_if_value[preproc_tos].else_branch = false;
1226 					  ifcond = false;
1227 					  BEGIN(xcond);
1228 					}
1229 <C,xskip>{informix_special}{ifndef}{space}* {
1230 					  /* are we simulating Informix? */
1231 					  if (INFORMIX_MODE)
1232 					  {
1233 						  if (preproc_tos >= MAX_NESTED_IF-1)
1234 							  mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
1235 						  preproc_tos++;
1236 						  stacked_if_value[preproc_tos].active = false;
1237 						  stacked_if_value[preproc_tos].saw_active = false;
1238 						  stacked_if_value[preproc_tos].else_branch = false;
1239 						  ifcond = false;
1240 						  BEGIN(xcond);
1241 					  }
1242 					  else
1243 					  {
1244 						  yyless(1);
1245 						  return S_ANYTHING;
1246 					  }
1247 					}
1248 <C,xskip>{exec_sql}{elif}{space}*	{
1249 						if (preproc_tos == 0)
1250 							mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
1251 						if (stacked_if_value[preproc_tos].else_branch)
1252 							mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
1253 						ifcond = true;
1254 						BEGIN(xcond);
1255 					}
1256 <C,xskip>{informix_special}{elif}{space}* {
1257 					/* are we simulating Informix? */
1258 					if (INFORMIX_MODE)
1259 					{
1260 						if (preproc_tos == 0)
1261 							mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
1262 						if (stacked_if_value[preproc_tos].else_branch)
1263 							mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
1264 						ifcond = true;
1265 						BEGIN(xcond);
1266 					}
1267 					else
1268 					{
1269 						yyless(1);
1270 						return S_ANYTHING;
1271 					}
1272 				}
1273 
1274 <C,xskip>{exec_sql}{else}{space}*";" {	/* only exec sql endif pops the stack, so take care of duplicated 'else' */
1275 					if ( preproc_tos == 0 )
1276 						mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
1277 					else if (stacked_if_value[preproc_tos].else_branch)
1278 						mmfatal(PARSE_ERROR, "more than one EXEC SQL ELSE");
1279 					else
1280 					{
1281 						stacked_if_value[preproc_tos].else_branch = true;
1282 						stacked_if_value[preproc_tos].active =
1283 							(stacked_if_value[preproc_tos-1].active &&
1284 							 !stacked_if_value[preproc_tos].saw_active);
1285 						stacked_if_value[preproc_tos].saw_active = true;
1286 
1287 						if (stacked_if_value[preproc_tos].active)
1288 							BEGIN(C);
1289 						else
1290 							BEGIN(xskip);
1291 					}
1292 				}
1293 <C,xskip>{informix_special}{else}{space}*";"	{
1294 					/* are we simulating Informix? */
1295 					if (INFORMIX_MODE)
1296 					{
1297 						if ( preproc_tos == 0 )
1298 							mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
1299 						else if (stacked_if_value[preproc_tos].else_branch)
1300 							mmfatal(PARSE_ERROR, "more than one EXEC SQL ELSE");
1301 						else
1302 						{
1303 							stacked_if_value[preproc_tos].else_branch = true;
1304 							stacked_if_value[preproc_tos].active =
1305 								(stacked_if_value[preproc_tos-1].active &&
1306 								 !stacked_if_value[preproc_tos].saw_active);
1307 							stacked_if_value[preproc_tos].saw_active = true;
1308 
1309 							if (stacked_if_value[preproc_tos].active)
1310 								BEGIN(C);
1311 							else
1312 								BEGIN(xskip);
1313 						}
1314 					}
1315 					else
1316 					{
1317 						yyless(1);
1318 						return S_ANYTHING;
1319 					}
1320 				}
1321 <C,xskip>{exec_sql}{endif}{space}*";" {
1322 					if (preproc_tos == 0)
1323 						mmfatal(PARSE_ERROR, "unmatched EXEC SQL ENDIF");
1324 					else
1325 						preproc_tos--;
1326 
1327 					if (stacked_if_value[preproc_tos].active)
1328 					   BEGIN(C);
1329 					else
1330 					   BEGIN(xskip);
1331 				}
1332 <C,xskip>{informix_special}{endif}{space}*";"	{
1333 					/* are we simulating Informix? */
1334 					if (INFORMIX_MODE)
1335 					{
1336 						if (preproc_tos == 0)
1337 							mmfatal(PARSE_ERROR, "unmatched EXEC SQL ENDIF");
1338 						else
1339 							preproc_tos--;
1340 
1341 						if (stacked_if_value[preproc_tos].active)
1342 							BEGIN(C);
1343 						else
1344 							BEGIN(xskip);
1345 					}
1346 					else
1347 					{
1348 						yyless(1);
1349 						return S_ANYTHING;
1350 					}
1351 				}
1352 
1353 <xskip>{other}		{ /* ignore */ }
1354 
1355 <xcond>{identifier}{space}*";" {
1356 					{
1357 						struct _defines *defptr;
1358 						unsigned int i;
1359 						bool this_active;
1360 
1361 						/*
1362 						 *	Skip the ";" and trailing whitespace. Note that yytext
1363 						 *	contains at least one non-space character plus the ";"
1364 						 */
1365 						for (i = strlen(yytext)-2;
1366 							 i > 0 && ecpg_isspace(yytext[i]);
1367 							 i-- )
1368 							;
1369 						yytext[i+1] = '\0';
1370 
1371 						for (defptr = defines;
1372 							 defptr != NULL &&
1373 							 strcmp(yytext, defptr->olddef) != 0;
1374 							 defptr = defptr->next)
1375 							/* skip */ ;
1376 
1377 						this_active = (defptr ? ifcond : !ifcond);
1378 						stacked_if_value[preproc_tos].active =
1379 							(stacked_if_value[preproc_tos-1].active &&
1380 							 !stacked_if_value[preproc_tos].saw_active &&
1381 							 this_active);
1382 						stacked_if_value[preproc_tos].saw_active |= this_active;
1383 					}
1384 
1385 					if (stacked_if_value[preproc_tos].active)
1386 						BEGIN(C);
1387 					else
1388 						BEGIN(xskip);
1389 				}
1390 
1391 <xcond>{other}|\n	{
1392 				mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL IFDEF command");
1393 				yyterminate();
1394 			}
1395 <def_ident>{identifier} {
1396 				old = mm_strdup(yytext);
1397 				BEGIN(def);
1398 				startlit();
1399 			}
1400 <def_ident>{other}|\n	{
1401 				mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL DEFINE command");
1402 				yyterminate();
1403 			}
1404 <def>{space}*";"	{
1405 						struct _defines *ptr, *this;
1406 
1407 						for (ptr = defines; ptr != NULL; ptr = ptr->next)
1408 						{
1409 							 if (strcmp(old, ptr->olddef) == 0)
1410 							 {
1411 								free(ptr->newdef);
1412 								ptr->newdef = mm_strdup(literalbuf);
1413 							 }
1414 						}
1415 						if (ptr == NULL)
1416 						{
1417 							this = (struct _defines *) mm_alloc(sizeof(struct _defines));
1418 
1419 							/* initial definition */
1420 							this->olddef = old;
1421 							this->newdef = mm_strdup(literalbuf);
1422 							this->next = defines;
1423 							this->used = NULL;
1424 							defines = this;
1425 						}
1426 
1427 						BEGIN(C);
1428 					}
1429 <def>[^;]			{ addlit(yytext, yyleng); }
1430 <incl>\<[^\>]+\>{space}*";"?		{	parse_include(); }
1431 <incl>{dquote}{xdinside}{dquote}{space}*";"?	{	parse_include(); }
1432 <incl>[^;\<\>\"]+";"		{ parse_include(); }
1433 <incl>{other}|\n		{
1434 					mmfatal(PARSE_ERROR, "syntax error in EXEC SQL INCLUDE command");
1435 					yyterminate();
1436 				}
1437 
1438 <<EOF>>				{
1439 					if (yy_buffer == NULL)
1440 					{
1441 						if ( preproc_tos > 0 )
1442 						{
1443 							preproc_tos = 0;
1444 							mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
1445 						}
1446 						yyterminate();
1447 					}
1448 					else
1449 					{
1450 						struct _yy_buffer *yb = yy_buffer;
1451 						int i;
1452 						struct _defines *ptr;
1453 
1454 						for (ptr = defines; ptr; ptr = ptr->next)
1455 							if (ptr->used == yy_buffer)
1456 							{
1457 								ptr->used = NULL;
1458 								break;
1459 							}
1460 
1461 						if (yyin != NULL)
1462 							fclose(yyin);
1463 
1464 						yy_delete_buffer( YY_CURRENT_BUFFER );
1465 						yy_switch_to_buffer(yy_buffer->buffer);
1466 
1467 						yylineno = yy_buffer->lineno;
1468 
1469 						/* We have to output the filename only if we change files here */
1470 						i = strcmp(input_filename, yy_buffer->filename);
1471 
1472 						free(input_filename);
1473 						input_filename = yy_buffer->filename;
1474 
1475 						yy_buffer = yy_buffer->next;
1476 						free(yb);
1477 
1478 						if (i != 0)
1479 							output_line_number();
1480 
1481 					}
1482 				}
1483 
1484 <INITIAL>{other}|\n	{ mmfatal(PARSE_ERROR, "internal error: unreachable state; please report this to <%s>", PACKAGE_BUGREPORT); }
1485 
1486 %%
1487 
1488 /* LCOV_EXCL_STOP */
1489 
1490 void
1491 lex_init(void)
1492 {
1493 	braces_open = 0;
1494 	parenths_open = 0;
1495 	current_function = NULL;
1496 
1497 	yylineno = 1;
1498 
1499 	/* initialize state for if/else/endif */
1500 	preproc_tos = 0;
1501 	stacked_if_value[preproc_tos].active = true;
1502 	stacked_if_value[preproc_tos].saw_active = true;
1503 	stacked_if_value[preproc_tos].else_branch = false;
1504 
1505 	/* initialize literal buffer to a reasonable but expansible size */
1506 	if (literalbuf == NULL)
1507 	{
1508 		literalalloc = 1024;
1509 		literalbuf = (char *) mm_alloc(literalalloc);
1510 	}
1511 	startlit();
1512 
1513 	BEGIN(C);
1514 }
1515 
1516 static void
1517 addlit(char *ytext, int yleng)
1518 {
1519 	/* enlarge buffer if needed */
1520 	if ((literallen+yleng) >= literalalloc)
1521 	{
1522 		do
1523 			literalalloc *= 2;
1524 		while ((literallen+yleng) >= literalalloc);
1525 		literalbuf = (char *) realloc(literalbuf, literalalloc);
1526 	}
1527 	/* append new data, add trailing null */
1528 	memcpy(literalbuf+literallen, ytext, yleng);
1529 	literallen += yleng;
1530 	literalbuf[literallen] = '\0';
1531 }
1532 
1533 static void
1534 addlitchar(unsigned char ychar)
1535 {
1536 	/* enlarge buffer if needed */
1537 	if ((literallen+1) >= literalalloc)
1538 	{
1539 		literalalloc *= 2;
1540 		literalbuf = (char *) realloc(literalbuf, literalalloc);
1541 	}
1542 	/* append new data, add trailing null */
1543 	literalbuf[literallen] = ychar;
1544 	literallen += 1;
1545 	literalbuf[literallen] = '\0';
1546 }
1547 
1548 /*
1549  * Process {integer}.  Note this will also do the right thing with {decimal},
1550  * ie digits and a decimal point.
1551  */
1552 static int
1553 process_integer_literal(const char *token, YYSTYPE *lval)
1554 {
1555 	int			val;
1556 	char	   *endptr;
1557 
1558 	errno = 0;
1559 	val = strtoint(token, &endptr, 10);
1560 	if (*endptr != '\0' || errno == ERANGE)
1561 	{
1562 		/* integer too large (or contains decimal pt), treat it as a float */
1563 		lval->str = mm_strdup(token);
1564 		return FCONST;
1565 	}
1566 	lval->ival = val;
1567 	return ICONST;
1568 }
1569 
1570 static void
1571 parse_include(void)
1572 {
1573 	/* got the include file name */
1574 	struct _yy_buffer *yb;
1575 	struct _include_path *ip;
1576 	char inc_file[MAXPGPATH];
1577 	unsigned int i;
1578 
1579 	yb = mm_alloc(sizeof(struct _yy_buffer));
1580 
1581 	yb->buffer =	YY_CURRENT_BUFFER;
1582 	yb->lineno = yylineno;
1583 	yb->filename = input_filename;
1584 	yb->next = yy_buffer;
1585 
1586 	yy_buffer = yb;
1587 
1588 	/*
1589 	 * skip the ";" if there is one and trailing whitespace. Note that
1590 	 * yytext contains at least one non-space character plus the ";"
1591 	 */
1592 	for (i = strlen(yytext)-2;
1593 		 i > 0 && ecpg_isspace(yytext[i]);
1594 		 i--)
1595 		;
1596 
1597 	if (yytext[i] == ';')
1598 		i--;
1599 
1600 	yytext[i+1] = '\0';
1601 
1602 	yyin = NULL;
1603 
1604 	/* If file name is enclosed in '"' remove these and look only in '.' */
1605 	/* Informix does look into all include paths though, except filename starts with '/' */
1606 	if (yytext[0] == '"' && yytext[i] == '"' &&
1607 		((compat != ECPG_COMPAT_INFORMIX && compat != ECPG_COMPAT_INFORMIX_SE) || yytext[1] == '/'))
1608 	{
1609 		yytext[i] = '\0';
1610 		memmove(yytext, yytext+1, strlen(yytext));
1611 
1612 		strlcpy(inc_file, yytext, sizeof(inc_file));
1613 		yyin = fopen(inc_file, "r");
1614 		if (!yyin)
1615 		{
1616 			if (strlen(inc_file) <= 2 || strcmp(inc_file + strlen(inc_file) - 2, ".h") != 0)
1617 			{
1618 				strcat(inc_file, ".h");
1619 				yyin = fopen(inc_file, "r");
1620 			}
1621 		}
1622 
1623 	}
1624 	else
1625 	{
1626 		if ((yytext[0] == '"' && yytext[i] == '"') || (yytext[0] == '<' && yytext[i] == '>'))
1627 		{
1628 			yytext[i] = '\0';
1629 			memmove(yytext, yytext+1, strlen(yytext));
1630 		}
1631 
1632 		for (ip = include_paths; yyin == NULL && ip != NULL; ip = ip->next)
1633 		{
1634 			if (strlen(ip->path) + strlen(yytext) + 4 > MAXPGPATH)
1635 			{
1636 				fprintf(stderr, _("Error: include path \"%s/%s\" is too long on line %d, skipping\n"), ip->path, yytext, yylineno);
1637 				continue;
1638 			}
1639 			snprintf (inc_file, sizeof(inc_file), "%s/%s", ip->path, yytext);
1640 			yyin = fopen(inc_file, "r");
1641 			if (!yyin)
1642 			{
1643 				if (strcmp(inc_file + strlen(inc_file) - 2, ".h") != 0)
1644 				{
1645 					strcat(inc_file, ".h");
1646 					yyin = fopen( inc_file, "r" );
1647 				}
1648 			}
1649 			/* if the command was "include_next" we have to disregard the first hit */
1650 			if (yyin && include_next)
1651 			{
1652 				fclose (yyin);
1653 				yyin = NULL;
1654 				include_next = false;
1655 			}
1656 		}
1657 	}
1658 	if (!yyin)
1659 		mmfatal(NO_INCLUDE_FILE, "could not open include file \"%s\" on line %d", yytext, yylineno);
1660 
1661 	input_filename = mm_strdup(inc_file);
1662 	yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE ));
1663 	yylineno = 1;
1664 	output_line_number();
1665 
1666 	BEGIN(C);
1667 }
1668 
1669 /*
1670  * ecpg_isspace() --- return true if flex scanner considers char whitespace
1671  */
1672 static bool
1673 ecpg_isspace(char ch)
1674 {
1675 	if (ch == ' ' ||
1676 		ch == '\t' ||
1677 		ch == '\n' ||
1678 		ch == '\r' ||
1679 		ch == '\f')
1680 		return true;
1681 	return false;
1682 }
1683 
1684 static bool isdefine(void)
1685 {
1686 	struct _defines *ptr;
1687 
1688 	/* is it a define? */
1689 	for (ptr = defines; ptr; ptr = ptr->next)
1690 	{
1691 		if (strcmp(yytext, ptr->olddef) == 0 && ptr->used == NULL)
1692 		{
1693 			struct _yy_buffer *yb;
1694 
1695 			yb = mm_alloc(sizeof(struct _yy_buffer));
1696 
1697 			yb->buffer =  YY_CURRENT_BUFFER;
1698 			yb->lineno = yylineno;
1699 			yb->filename = mm_strdup(input_filename);
1700 			yb->next = yy_buffer;
1701 
1702 			ptr->used = yy_buffer = yb;
1703 
1704 			yy_scan_string(ptr->newdef);
1705 			return true;
1706 		}
1707 	}
1708 
1709 	return false;
1710 }
1711 
1712 static bool isinformixdefine(void)
1713 {
1714 	const char *new = NULL;
1715 
1716 	if (strcmp(yytext, "dec_t") == 0)
1717 		new = "decimal";
1718 	else if (strcmp(yytext, "intrvl_t") == 0)
1719 		new = "interval";
1720 	else if (strcmp(yytext, "dtime_t") == 0)
1721 		new = "timestamp";
1722 
1723 	if (new)
1724 	{
1725 		struct _yy_buffer *yb;
1726 
1727 		yb = mm_alloc(sizeof(struct _yy_buffer));
1728 
1729 		yb->buffer =  YY_CURRENT_BUFFER;
1730 		yb->lineno = yylineno;
1731 		yb->filename = mm_strdup(input_filename);
1732 		yb->next = yy_buffer;
1733 		yy_buffer = yb;
1734 
1735 		yy_scan_string(new);
1736 		return true;
1737 	}
1738 
1739 	return false;
1740 }
1741