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