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