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