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