]> granicus.if.org Git - postgresql/blob - src/interfaces/ecpg/preproc/pgc.l
Commit old versions into main branch again.
[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-2002, PostgreSQL Global Development Group
11  * Portions Copyright (c) 1994, Regents of the University of California
12  *
13  *
14  * IDENTIFICATION
15  *        $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/pgc.l,v 1.96 2002/07/01 06:56:10 meskes Exp $
16  *
17  *-------------------------------------------------------------------------
18  */
19 #include "postgres_fe.h"
20
21 #include <ctype.h>
22 #include <sys/types.h>
23 #include <limits.h>
24 #include <errno.h>
25
26 #include "extern.h"
27 #include "preproc.h"
28
29 /* some versions of lex define this as a macro */
30 #if defined(yywrap)
31 #undef yywrap
32 #endif /* yywrap */
33
34 #define YY_NO_UNPUT
35
36 extern YYSTYPE yylval;
37
38 static int              xcdepth = 0;    /* depth of nesting in slash-star comments */
39
40 /*
41  * literalbuf is used to accumulate literal values when multiple rules
42  * are needed to parse a single literal.  Call startlit to reset buffer
43  * to empty, addlit to add text.  Note that the buffer is permanently
44  * malloc'd to the largest size needed so far in the current run.
45  */
46 static char    *literalbuf = NULL;              /* expandable buffer */
47 static int              literallen;                             /* actual current length */
48 static int              literalalloc;                   /* current allocated buffer size */
49
50 #define startlit()      (literalbuf[0] = '\0', literallen = 0)
51 static void addlit(char *ytext, int yleng);
52 static void addlitchar (unsigned char);
53
54 char *token_start;
55 int state_before;
56
57 struct _yy_buffer 
58
59         YY_BUFFER_STATE         buffer;
60         long                            lineno;
61         char                            *filename;
62         struct _yy_buffer       *next;
63 } *yy_buffer = NULL;
64
65 static char *old;
66
67 #define MAX_NESTED_IF 128
68 static short preproc_tos;
69 static short ifcond;
70 static struct _if_value 
71 {
72         short condition;
73         short else_branch;
74 } stacked_if_value[MAX_NESTED_IF];
75
76 %}
77
78 %option yylineno
79 %s C SQL incl def def_ident
80
81 /*
82  * OK, here is a short description of lex/flex rules behavior.
83  * The longest pattern which matches an input string is always chosen.
84  * For equal-length patterns, the first occurring in the rules list is chosen.
85  * INITIAL is the starting state, to which all non-conditional rules apply.
86  * Exclusive states change parsing rules while the state is active.  When in
87  * an exclusive state, only those rules defined for that state apply.
88  *
89  * We use exclusive states for quoted strings, extended comments,
90  * and to eliminate parsing troubles for numeric strings.
91  * Exclusive states:
92  *      <xbit> bit string literal
93  *      <xc> extended C-style comments - thomas 1997-07-12
94  *      <xd> delimited identifiers (double-quoted identifiers) - thomas 1997-10-27
95  *      <xh> hexadecimal numeric string - thomas 1997-11-16
96  *      <xq> quoted strings - thomas 1997-07-30
97  */
98
99 %x xbit
100 %x xc
101 %x xd
102 %x xdc
103 %x xh
104 %x xq
105 %x xpre
106 %x xcond
107 %x xskip
108
109 /* Bit string
110  */
111 xbitstart               [bB]{quote}
112 xbitstop                {quote}
113 xbitinside              [^']*
114 xbitcat                 {quote}{whitespace_with_newline}{quote}
115
116 /* Hexadecimal number
117  */
118 xhstart                 [xX]{quote}
119 xhstop                  {quote}
120 xhinside                [^']+
121 xhcat                   {quote}{whitespace_with_newline}{quote}
122
123 /* C version of hex number
124  */
125 xch                     0[xX][0-9A-Fa-f]*
126
127 /* Extended quote
128  * xqdouble implements SQL92 embedded quote
129  * xqcat allows strings to cross input lines
130  */
131 quote                   '
132 xqstart                 {quote}
133 xqstop                  {quote}
134 xqdouble                {quote}{quote}
135 xqinside                [^\\']+
136 xqescape                [\\][^0-7]
137 xqoctesc                [\\][0-7]{1,3}
138 xqcat                   {quote}{whitespace_with_newline}{quote}
139
140 /* Double quote
141  * Allows embedded spaces and other special characters into identifiers.
142  */
143 dquote                  \"
144 xdstart                 {dquote}
145 xdstop                  {dquote}
146 xddouble                                {dquote}{dquote}
147 xdinside                [^"]+
148
149 /* special stuff for C strings */
150 xdcqq                   \\\\
151 xdcqdq                  \\\"
152 xdcother                [^"]
153 xdcinside               ({xdcqq}|{xdcqdq}|{xdcother})
154
155 /* C-style comments
156  *
157  * The "extended comment" syntax closely resembles allowable operator syntax.
158  * The tricky part here is to get lex to recognize a string starting with
159  * slash-star as a comment, when interpreting it as an operator would produce
160  * a longer match --- remember lex will prefer a longer match!  Also, if we
161  * have something like plus-slash-star, lex will think this is a 3-character
162  * operator whereas we want to see it as a + operator and a comment start.
163  * The solution is two-fold:
164  * 1. append {op_chars}* to xcstart so that it matches as much text as
165  *        {operator} would. Then the tie-breaker (first matching rule of same
166  *        length) ensures xcstart wins.  We put back the extra stuff with yyless()
167  *        in case it contains a star-slash that should terminate the comment.
168  * 2. In the operator rule, check for slash-star within the operator, and
169  *        if found throw it back with yyless().  This handles the plus-slash-star
170  *        problem.
171  * SQL92-style comments, which start with dash-dash, have similar interactions
172  * with the operator rule.
173  */
174 xcstart                 \/\*{op_chars}*
175 xcstop                  \*+\/
176 xcinside                [^*/]+
177
178 digit                   [0-9]
179 letter                  [\200-\377_A-Za-z]
180 letter_or_digit [\200-\377_A-Za-z0-9]
181
182 identifier              {letter}{letter_or_digit}*
183
184 typecast                "::"
185
186 /*
187  * "self" is the set of chars that should be returned as single-character
188  * tokens.      "op_chars" is the set of chars that can make up "Op" tokens,
189  * which can be one or more characters long (but if a single-char token
190  * appears in the "self" set, it is not to be returned as an Op).  Note
191  * that the sets overlap, but each has some chars that are not in the other.
192  *
193  * If you change either set, adjust the character lists appearing in the
194  * rule for "operator"!
195  */
196 self                    [,()\[\].;$\:\+\-\*\/\%\^\<\>\=]
197 op_chars                [\~\!\@\#\^\&\|\`\?\$\+\-\*\/\%\<\>\=]
198 operator                {op_chars}+
199
200 /* we no longer allow unary minus in numbers.
201  * instead we pass it separately to parser. there it gets
202  * coerced via doNegate() -- Leon aug 20 1999
203  */
204
205 integer                 {digit}+
206 decimal                 (({digit}*\.{digit}+)|({digit}+\.{digit}*))
207 real                    ((({digit}*\.{digit}+)|({digit}+\.{digit}*)|({digit}+))([Ee][-+]?{digit}+))
208
209 param                   \${integer}
210
211 /*
212  * In order to make the world safe for Windows and Mac clients as well as
213  * Unix ones, we accept either \n or \r as a newline.  A DOS-style \r\n
214  * sequence will be seen as two successive newlines, but that doesn't cause
215  * any problems.  SQL92-style comments, which start with -- and extend to the
216  * next newline, are treated as equivalent to a single whitespace character.
217  *
218  * NOTE a fine point: if there is no newline following --, we will absorb
219  * everything to the end of the input as a comment.  This is correct.  Older
220  * versions of Postgres failed to recognize -- as a comment if the input
221  * did not end with a newline.
222  *
223  * XXX perhaps \f (formfeed) should be treated as a newline as well?
224  */
225
226 ccomment                "//".*\n
227
228 space                   [ \t\n\r\f]
229 horiz_space             [ \t\f]
230 newline                                 [\n\r]
231 non_newline             [^\n\r]
232
233 comment                 ("--"{non_newline}*)
234
235 whitespace              ({space}+|{comment})
236
237 /*
238  * SQL92 requires at least one newline in the whitespace separating
239  * string literals that are to be concatenated.  Silly, but who are we
240  * to argue?  Note that {whitespace_with_newline} should not have * after
241  * it, whereas {whitespace} should generally have a * after it...
242  */
243
244 horiz_whitespace        ({horiz_space}|{comment})
245 whitespace_with_newline ({horiz_whitespace}*{newline}{whitespace}*)
246
247 other                   .
248
249 /* some stuff needed for ecpg */
250 exec    [eE][xX][eE][cC]
251 sql             [sS][qQ][lL]
252 define  [dD][eE][fF][iI][nN][eE]
253 include [iI][nN][cC][lL][uU][dD][eE]
254
255 ifdef   [iI][fF][dD][eE][fF]
256 ifndef  [iI][fF][nN][dD][eE][fF]
257 else    [eE][lL][sS][eE]
258 elif    [eE][lL][iI][fF]
259 endif   [eE][nN][dD][iI][fF]
260
261 exec_sql                {exec}{space}*{sql}{space}*
262 ipdigit                 ({digit}|{digit}{digit}|{digit}{digit}{digit})
263 ip                      {ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit}
264
265 /* Take care of cpp continuation lines */
266 cppline                 {space}*#(.*\\{space})*.*
267
268 /*
269  * Quoted strings must allow some special characters such as single-quote
270  *      and newline.
271  * Embedded single-quotes are implemented both in the SQL92-standard
272  *      style of two adjacent single quotes "''" and in the Postgres/Java style
273  *      of escaped-quote "\'".
274  * Other embedded escaped characters are matched explicitly and the leading
275  *      backslash is dropped from the string. - thomas 1997-09-24
276  * Note that xcstart must appear before operator, as explained above!
277  *      Also whitespace (comment) must appear before operator.
278  */
279
280 %%
281
282 %{
283                                        /* code to execute during start of each call of yylex() */
284                                        token_start = NULL;
285 %}
286
287 <SQL>{whitespace}       { /* ignore */ }
288
289 {xcstart}                       {
290                                                 token_start = yytext;
291                                                 state_before = YYSTATE;
292                                                 xcdepth = 0;
293                                                 BEGIN(xc);
294                                                 /* Put back any characters past slash-star; see above */
295                                                 yyless(2);
296                                                 fputs("/*", yyout);
297                                         }
298 <xc>{xcstart}           {
299                                                 xcdepth++;
300                                                 /* Put back any characters past slash-star; see above */
301                                                 yyless(2);
302                                                 fputs("/*", yyout);
303                                         }
304
305 <xc>{xcstop}            {
306                                                 ECHO;
307                                                 if (xcdepth <= 0)
308                                                 {
309                                                         BEGIN(state_before);
310                                                         token_start = NULL;
311                                                 }
312                                                 else
313                                                         xcdepth--;
314                                         }
315
316 <xc>{xcinside}          { ECHO; }
317 <xc>{op_chars}          { ECHO; }
318
319 <xc><<EOF>>                     { mmerror(PARSE_ERROR, ET_ERROR, "Unterminated /* comment"); }
320
321 <SQL>{xbitstart}        {
322                                                 token_start = yytext;
323                                                 BEGIN(xbit);
324                                                 startlit();
325                                                 addlitchar('b');
326                                         }
327 <xbit>{xbitstop}        {
328                                                 BEGIN(SQL);
329                                                 if (literalbuf[strspn(literalbuf, "01") + 1] != '\0')
330                                                         mmerror(PARSE_ERROR, ET_ERROR, "invalid bit string input.");
331                                                 yylval.str = literalbuf;
332                                                 return BITCONST;
333                                         }
334
335 <xh>{xhinside}  |
336 <xbit>{xbitinside}      { addlit(yytext, yyleng); }
337 <xh>{xhcat}             |
338 <xbit>{xbitcat}         { /* ignore */ }
339 <xbit><<EOF>>           { mmerror(PARSE_ERROR, ET_ERROR, "Unterminated bit string"); }
340
341 <SQL>{xhstart}          {
342                                                 token_start = yytext;
343                                                 BEGIN(xh);
344                                                 startlit();
345                                         }
346 <xh>{xhstop}            {
347                                                 long val;
348                                                 char* endptr;
349
350                                                 BEGIN(SQL);
351                                                 errno = 0;
352                                                 val = strtol(literalbuf, &endptr, 16);
353                                                 if (*endptr != '\0' || errno == ERANGE
354 #ifdef HAVE_LONG_INT_64
355                                                         /* if long > 32 bits, check for overflow of int4 */
356                                                         || val != (long) ((int32) val)
357 #endif
358                                                         )
359                                                         mmerror(PARSE_ERROR, ET_ERROR, "Bad hexadecimal integer input");
360                                                 yylval.ival = val;
361                                                 return ICONST;
362                                         }
363
364 <xh><<EOF>>                     { mmerror(PARSE_ERROR, ET_ERROR, "Unterminated hexadecimal integer"); }
365
366 <C,SQL>{xqstart}        {
367                                                 token_start = yytext;
368                                                 state_before = YYSTATE;
369                                                 BEGIN(xq);
370                                                 startlit();
371                                         }
372 <xq>{xqstop}            {
373                                                 BEGIN(state_before);
374                                                 yylval.str = mm_strdup(literalbuf);
375                                                 printf("MM: %s\n", yylval.str);
376                                                 return SCONST;
377                                         }
378 <xq>{xqdouble}          { addlitchar('\''); }
379 <xq>{xqinside}          { addlit(yytext, yyleng); }
380 <xq>{xqescape}          { addlit(yytext, yyleng); }
381 <xq>{xqoctesc}          { addlit(yytext, yyleng); }
382 <xq>{xqcat}                     { /* ignore */ }
383
384 <xq><<EOF>>                     { mmerror(PARSE_ERROR, ET_ERROR, "Unterminated quoted string"); }
385
386 <SQL>{xdstart}          {
387                                                 state_before = YYSTATE;
388                                                 BEGIN(xd);
389                                                 startlit();
390                                         }
391 <xd>{xdstop}            {
392                                                 BEGIN(state_before);
393                                                 if (literallen == 0)
394                                                         mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier");
395                                                 if (literallen >= NAMEDATALEN)
396                                                 {
397                                                         sprintf(errortext, "identifier \"%s\" will be truncated to \"%.*s\"",
398                                                                         literalbuf, NAMEDATALEN-1, literalbuf);
399                                                         literalbuf[NAMEDATALEN-1] = '\0';
400                                                         mmerror(PARSE_ERROR, ET_WARNING, errortext);
401                                                 }
402                                         
403                                                 yylval.str = mm_strdup(literalbuf);
404                                                 return CSTRING;
405                                         }
406 <xdc>{xdstop}           {
407                                                 BEGIN(state_before);
408                                                 yylval.str = mm_strdup(literalbuf);
409                                                 return CSTRING;
410                                         }
411 <xd>{xddouble}          { addlitchar('"'); }
412 <xd>{xdinside}          { addlit(yytext, yyleng); }
413 <xd,xdc><<EOF>>         { mmerror(PARSE_ERROR, ET_ERROR, "Unterminated quoted identifier"); }
414 <C,SQL>{xdstart}        {
415                                                 state_before = YYSTATE;
416                                                 BEGIN(xdc);
417                                                 startlit();
418                                         }
419 <xdc>{xdcinside}        { addlit(yytext, yyleng); }
420 <SQL>{typecast}         { return TYPECAST; }
421 <SQL>{self}                     { /*
422                                            * We may find a ';' inside a structure
423                                            * definition in a TYPE or VAR statement.
424                                            * This is not an EOL marker.
425                                            */
426                                           if (yytext[0] == ';' && struct_level == 0)
427                                                  BEGIN C;
428                                           return yytext[0];
429                                         }
430 <SQL>{operator}         {
431                                                 /*
432                                                  * Check for embedded slash-star or dash-dash; those
433                                                  * are comment starts, so operator must stop there.
434                                                  * Note that slash-star or dash-dash at the first
435                                                  * character will match a prior rule, not this one.
436                                                  */
437                                                 int             nchars = yyleng;
438                                                 char   *slashstar = strstr(yytext, "/*");
439                                                 char   *dashdash = strstr(yytext, "--");
440
441                                                 if (slashstar && dashdash)
442                                                 {
443                                                         /* if both appear, take the first one */
444                                                         if (slashstar > dashdash)
445                                                                 slashstar = dashdash;
446                                                 }
447                                                 else if (!slashstar)
448                                                         slashstar = dashdash;
449                                                 if (slashstar)
450                                                         nchars = slashstar - yytext;
451
452                                                 /*
453                                                  * For SQL92 compatibility, '+' and '-' cannot be the
454                                                  * last char of a multi-char operator unless the operator
455                                                  * contains chars that are not in SQL92 operators.
456                                                  * The idea is to lex '=-' as two operators, but not
457                                                  * to forbid operator names like '?-' that could not be
458                                                  * sequences of SQL92 operators.
459                                                  */
460                                                 while (nchars > 1 &&
461                                                            (yytext[nchars-1] == '+' ||
462                                                                 yytext[nchars-1] == '-'))
463                                                 {
464                                                         int             ic;
465
466                                                         for (ic = nchars-2; ic >= 0; ic--)
467                                                         {
468                                                                 if (strchr("~!@#^&|`?$%", yytext[ic]))
469                                                                         break;
470                                                         }
471                                                         if (ic >= 0)
472                                                                 break; /* found a char that makes it OK */
473                                                         nchars--; /* else remove the +/-, and check again */
474                                                 }
475
476                                                 if (nchars < yyleng)
477                                                 {
478                                                         /* Strip the unwanted chars from the token */
479                                                         yyless(nchars);
480                                                         /*
481                                                          * If what we have left is only one char, and it's
482                                                          * one of the characters matching "self", then
483                                                          * return it as a character token the same way
484                                                          * that the "self" rule would have.
485                                                          */
486                                                         if (nchars == 1 &&
487                                                                 strchr(",()[].;$:+-*/%^<>=", yytext[0]))
488                                                                 return yytext[0];
489                                                 }
490
491                                                 /* Convert "!=" operator to "<>" for compatibility */
492                                                 if (strcmp(yytext, "!=") == 0)
493                                                         yylval.str = mm_strdup("<>");
494                                                 else
495                                                         yylval.str = mm_strdup(yytext);
496                                                 return Op;
497                                         }
498 <SQL>{param}            {
499                                                 yylval.ival = atol(yytext+1);
500                                                 return PARAM;
501                                         }
502 <C,SQL>{integer}        {
503                                                 long val;
504                                                 char* endptr;
505
506                                                 errno = 0;
507                                                 val = strtol((char *)yytext, &endptr,10);
508                                                 if (*endptr != '\0' || errno == ERANGE
509 #ifdef HAVE_LONG_INT_64
510                                                         /* if long > 32 bits, check for overflow of int4 */
511                                                         || val != (long) ((int32) val)
512 #endif
513                                                         )
514                                                 {
515                                                         errno = 0;
516                                                         yylval.str = mm_strdup(yytext);
517                                                         return FCONST;
518                                                 }
519                                                 yylval.ival = val;
520                                                 return ICONST;
521                                         }
522 <SQL>{ip}                       {
523                                                 yylval.str = mm_strdup(yytext);
524                                                 return IP;
525                                         }
526 {decimal}                       {
527                                                 yylval.str = mm_strdup(yytext);
528                                                 return FCONST;
529                                         }
530 <C,SQL>{real}           {
531                                                 yylval.str = mm_strdup(yytext);
532                                                 return FCONST;
533                                         }
534 <SQL>:{identifier}(("->"|\.){identifier})*      {
535                                                 yylval.str = mm_strdup(yytext+1);
536                                                 return(CVARIABLE);
537                                         }
538 <SQL>{identifier}       {
539                                                 ScanKeyword    *keyword;
540                                                 struct _defines *ptr;
541
542                                                 /* Is it an SQL keyword? */
543                                                 keyword = ScanKeywordLookup(yytext);
544                                                 if (keyword != NULL)
545                                                         return keyword->value;
546
547                                                 /* Is it an ECPG keyword? */
548                                                 keyword = ScanECPGKeywordLookup( yytext);
549                                                 if (keyword != NULL)
550                                                         return keyword->value;
551
552                                                 /* How about a DEFINE? */
553                                                 for (ptr = defines; ptr; ptr = ptr->next)
554                                                 {
555                                                         if (strcmp(yytext, ptr->old) == 0)
556                                                         {
557                                                                 struct _yy_buffer *yb;
558
559                                                                 yb = mm_alloc(sizeof(struct _yy_buffer));
560
561                                                                 yb->buffer =  YY_CURRENT_BUFFER;
562                                                                 yb->lineno = yylineno;
563                                                                 yb->filename = mm_strdup(input_filename);
564                                                                 yb->next = yy_buffer;
565
566                                                                 yy_buffer = yb;
567
568                                                                 yy_scan_string(ptr->new);
569                                                                 break;
570                                                         }
571                                                 }
572
573                                                 /*
574                                                  * None of the above.  Return it as an identifier.
575                                                  *
576                                                  * The backend would attempt to truncate and case-fold
577                                                  * the identifier, but I see no good reason for ecpg
578                                                  * to do so; that's just another way that ecpg could get
579                                                  * out of step with the backend.
580                                                  */
581                                                 if (ptr == NULL)
582                                                 {
583                                                         yylval.str = mm_strdup( yytext);
584                                                         return IDENT;
585                                                 }
586                                         }
587 <SQL>{other}            { return yytext[0]; }
588 <C>{exec_sql}           { BEGIN SQL; return SQL_START; }
589 <C>{ccomment}           { /* ignore */ }
590 <C>{xch}                        {
591                                                 char* endptr;
592
593                                                 errno = 0;
594                                                 yylval.ival = strtol((char *)yytext,&endptr,16);
595                                                 if (*endptr != '\0' || errno == ERANGE)
596                                                 {
597                                                         errno = 0;
598                                                         yylval.str = mm_strdup(yytext);
599                                                         return SCONST;
600                                                 }
601                                                 return ICONST;
602                                         }
603 <C>{cppline}            {
604                                                 yylval.str = mm_strdup(yytext);
605                                                 return(CPP_LINE);
606                                         }
607 <C>{identifier}         {
608                                                 ScanKeyword             *keyword;
609
610                                                 keyword = ScanCKeywordLookup(yytext);
611                                                 if (keyword != NULL) {
612                                                         return keyword->value;
613                                                 }
614                                                 else
615                                                 {
616                                                         struct _defines *ptr;
617
618                                                         for (ptr = defines; ptr; ptr = ptr->next)
619                                                         {
620                                                                 if (strcmp(yytext, ptr->old) == 0)
621                                                                 {
622                                                                         struct _yy_buffer *yb;
623
624                                                                         yb = mm_alloc(sizeof(struct _yy_buffer));
625
626                                                                                                 yb->buffer =  YY_CURRENT_BUFFER;
627                                                                                                 yb->lineno = yylineno;
628                                                                                                 yb->filename = mm_strdup(input_filename);
629                                                                                                 yb->next = yy_buffer;
630
631                                                                                                 yy_buffer = yb;
632
633                                                                         yy_scan_string(ptr->new);
634                                                                         break;
635                                                                 }
636                                                         }
637                                                         if (ptr == NULL)
638                                                         {
639                                                                 yylval.str = mm_strdup(yytext);
640                                                                 return IDENT;
641                                                         }
642                                                 }
643                                         }
644 <C>";"                          { return(';'); }
645 <C>","                          { return(','); }
646 <C>"*"                          { return('*'); }
647 <C>"%"                          { return('%'); }
648 <C>"/"                          { return('/'); }
649 <C>"+"                          { return('+'); }
650 <C>"-"                          { return('-'); }
651 <C>"("                          { return('('); }
652 <C>")"                          { return(')'); }
653 <C>{space}                      { ECHO; }
654 <C>\{                           { return('{'); }
655 <C>\}                           { return('}'); }
656 <C>\[                           { return('['); }
657 <C>\]                           { return(']'); }
658 <C>\=                           { return('='); }
659 <C>"->"                         { return(S_MEMBER); }
660 <C>">>"                         { return(S_RSHIFT); }
661 <C>"<<"                         { return(S_LSHIFT); }
662 <C>"||"                         { return(S_OR); }
663 <C>"&&"                         { return(S_AND); }
664 <C>"++"                         { return(S_INC); }
665 <C>"--"                         { return(S_DEC); }
666 <C>"=="                         { return(S_EQUAL); }
667 <C>"!="                         { return(S_NEQUAL); }
668 <C>"+="                         { return(S_ADD); }
669 <C>"-="                         { return(S_SUB); }
670 <C>"*="                         { return(S_MUL); }
671 <C>"/="                         { return(S_DIV); }
672 <C>"%="                         { return(S_MOD); }
673 <C>"->*"                        { return(S_MEMPOINT); }
674 <C>".*"                         { return(S_DOTPOINT); }
675 <C>{other}                      { return S_ANYTHING; }
676
677 <C>{exec_sql}{define}{space}*   { BEGIN(def_ident); }
678 <C>{exec_sql}{include}{space}*  { BEGIN(incl); }
679
680 <C,xskip>{exec_sql}{ifdef}{space}*      { ifcond = TRUE; BEGIN(xcond); }
681 <C,xskip>{exec_sql}{ifndef}{space}* { ifcond = FALSE; BEGIN(xcond); }
682
683 <C,xskip>{exec_sql}{elif}{space}*       {       /* pop stack */
684                                                 if ( preproc_tos == 0 ) {
685                                                         mmerror(PARSE_ERROR, ET_FATAL, "Missing matching 'EXEC SQL IFDEF / EXEC SQL IFNDEF'");
686                                                 }
687                                                 else if ( stacked_if_value[preproc_tos].else_branch )
688                                                         mmerror(PARSE_ERROR, ET_FATAL, "Missing 'EXEC SQL ENDIF;'");
689                                                 else
690                                                         preproc_tos--;
691
692                                                 ifcond = TRUE; BEGIN(xcond);
693                                         }
694
695 <C,xskip>{exec_sql}{else}{space}*";" {  /* only exec sql endif pops the stack, so take care of duplicated 'else' */
696                                                 if ( stacked_if_value[preproc_tos].else_branch ) {
697                                                         mmerror(PARSE_ERROR, ET_FATAL, "Duplicated 'EXEC SQL ELSE;'");
698                                                 }
699                                                 else {
700                                                         stacked_if_value[preproc_tos].else_branch = TRUE;
701                                                         stacked_if_value[preproc_tos].condition =
702                                                         (stacked_if_value[preproc_tos-1].condition &&
703                                                          ! stacked_if_value[preproc_tos].condition);
704
705                                                         if ( stacked_if_value[preproc_tos].condition )
706                                                                 BEGIN(C);
707                                                         else
708                                                                 BEGIN(xskip);
709                                                 }
710                                         }
711 <C,xskip>{exec_sql}{endif}{space}*";" {
712                                                 if ( preproc_tos == 0 )
713                                                         mmerror(PARSE_ERROR, ET_FATAL, "Unmatched 'EXEC SQL ENDIF;'");
714                                                 else
715                                                         preproc_tos--;
716
717                                                 if ( stacked_if_value[preproc_tos].condition )
718                                                    BEGIN(C);
719                                                 else
720                                                    BEGIN(xskip);
721                                         }
722
723 <xskip>{other}          { /* ignore */ }
724
725 <xcond>{identifier}{space}*";" {
726                                                 if ( preproc_tos >= MAX_NESTED_IF-1 ) {
727                                                         mmerror(PARSE_ERROR, ET_FATAL, "Too many nested 'EXEC SQL IFDEF' conditions");
728                                                 }
729                                                 else 
730                                                 {
731                                                         struct _defines *defptr;
732                                                         unsigned int i;
733
734                                                         /* skip the ";" and trailing whitespace. Note that yytext contains
735                                                            at least one non-space character plus the ";" */
736                                                         for ( i = strlen(yytext)-2;
737                                                                   i > 0 && isspace((unsigned char) yytext[i]);
738                                                                   i-- )
739                                                         {}
740                                                         yytext[i+1] = '\0';
741
742                                                         for ( defptr = defines; defptr != NULL &&
743                                                                   ( strcmp(yytext, defptr->old) != 0 ); defptr = defptr->next );
744
745                                                         preproc_tos++;
746                                                         stacked_if_value[preproc_tos].else_branch = FALSE;
747                                                         stacked_if_value[preproc_tos].condition =
748                                                         ( (defptr ? ifcond : !ifcond) && stacked_if_value[preproc_tos-1].condition );
749                                                 }
750
751                                                 if ( stacked_if_value[preproc_tos].condition )
752                                                    BEGIN C;
753                                                 else
754                                                    BEGIN(xskip);
755                                         }
756
757 <def_ident>{identifier} {
758                                 old = mm_strdup(yytext);
759                                 BEGIN(def);
760                                 startlit();
761                         }
762 <def>{space}*";"        {
763                                                 struct _defines *ptr, *this;
764
765                                                 for (ptr = defines; ptr != NULL; ptr = ptr->next)
766                                                 {
767                                                          if (strcmp(old, ptr->old) == 0)
768                                                          {
769                                                                 free(ptr->new);
770                                                                 /* ptr->new = mm_strdup(scanstr(literalbuf));*/
771                                                                 ptr->new = mm_strdup(literalbuf);
772                                                          }
773                                                 }
774                                                 if (ptr == NULL)
775                                                 {
776                                                                                                 this = (struct _defines *) mm_alloc(sizeof(struct _defines));
777
778                                                                                                 /* initial definition */
779                                                                                                 this->old = old;
780                                                                                                 this->new = mm_strdup(literalbuf);
781                                                         this->next = defines;
782                                                         defines = this;
783                                                 }
784
785                                                 BEGIN(C);
786                                         }
787 <def>[^;]                       { addlit(yytext, yyleng); }
788
789 <incl>[^;]+";"          { 
790                                                 /* got the include file name */
791                                                 struct _yy_buffer *yb;
792                                                 struct _include_path *ip;
793                                                 char inc_file[MAXPGPATH];
794                                                 unsigned int i;
795
796                                                 yb = mm_alloc(sizeof(struct _yy_buffer));
797
798                                                 yb->buffer =    YY_CURRENT_BUFFER;
799                                                 yb->lineno = yylineno;
800                                                 yb->filename = input_filename;
801                                                 yb->next = yy_buffer;
802
803                                                 yy_buffer = yb;
804
805                                                 /*
806                                                  * skip the ";" and trailing whitespace. Note that yytext contains
807                                                  * at least one non-space character plus the ";" 
808                                                  */
809                                                 for ( i = strlen(yytext)-2;
810                                                         i > 0 && isspace((unsigned char) yytext[i]);
811                                                         i-- )
812                                                         {}
813                                                 yytext[i+1] = '\0';
814
815                                                 yyin = NULL;
816                                                 for (ip = include_paths; yyin == NULL && ip != NULL; ip = ip->next)
817                                                 {
818                                                         if (strlen(ip->path) + strlen(yytext) + 3 > MAXPGPATH)
819                                                         {
820                                                                 fprintf(stderr, "Error: Path %s/%s is too long in line %d, skipping.\n", ip->path, yytext, yylineno);
821                                                                 continue;
822                                                         }
823                                                         sprintf (inc_file, "%s/%s", ip->path, yytext);
824                                                         yyin = fopen( inc_file, "r" );
825                                                         if (!yyin)
826                                                         {
827                                                                 if (strcmp(inc_file + strlen(inc_file) - 2, ".h"))
828                                                                 {
829                                                                         strcat(inc_file, ".h");
830                                                                         yyin = fopen( inc_file, "r" );
831                                                                 }
832                                                         }
833                                                 }
834                                                 if (!yyin)
835                                                 {
836                                                         sprintf(errortext, "Cannot open include file %s in line %d\n", yytext, yylineno);
837                                                         mmerror(NO_INCLUDE_FILE, ET_FATAL, errortext);
838                                                 }
839
840                                                 input_filename = mm_strdup(inc_file);
841                                                 yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE ));
842                                                 yylineno = 1;
843                                                 output_line_number();
844
845                                                 BEGIN C;
846                                         }
847
848 <<EOF>>                         {
849                                                 if (yy_buffer == NULL) {
850                                                 if ( preproc_tos > 0 ) 
851                                                 {
852                                                         preproc_tos = 0;
853                                                         mmerror(PARSE_ERROR, ET_FATAL, "Missing 'EXEC SQL ENDIF;'");
854                                                 }
855                                                         yyterminate();
856                                                         }
857                                                 else
858                                                 {
859                                                         struct _yy_buffer *yb = yy_buffer;
860                                                         int i;
861
862                                                         if (yyin != NULL)
863                                                                 fclose(yyin);
864
865                                                         yy_delete_buffer( YY_CURRENT_BUFFER );
866                                                         yy_switch_to_buffer(yy_buffer->buffer);
867
868                                                         yylineno = yy_buffer->lineno;
869
870                                                         /* We have to output the filename only if we change files here */
871                                                         i = strcmp(input_filename, yy_buffer->filename);
872
873                                                         free(input_filename);
874                                                         input_filename = yy_buffer->filename;
875
876                                                         yy_buffer = yy_buffer->next;
877                                                         free(yb);
878
879                                                         if (i != 0)
880                                                                 output_line_number();
881                                                 }
882                                         }
883 %%
884 void
885 lex_init(void)
886 {
887         braces_open = 0;
888
889         preproc_tos = 0;
890         yylineno = 1;
891         ifcond = TRUE;
892         stacked_if_value[preproc_tos].condition = ifcond;
893         stacked_if_value[preproc_tos].else_branch = FALSE;
894
895         /* initialize literal buffer to a reasonable but expansible size */
896         if (literalbuf == NULL)
897         {
898                 literalalloc = 128;
899                 literalbuf = (char *) malloc(literalalloc);
900         }
901         startlit();
902
903         BEGIN C;
904 }
905
906 static void
907 addlit(char *ytext, int yleng)
908 {
909         /* enlarge buffer if needed */
910         if ((literallen+yleng) >= literalalloc)
911         {
912                 do 
913                         literalalloc *= 2;
914                 while ((literallen+yleng) >= literalalloc);
915                 literalbuf = (char *) realloc(literalbuf, literalalloc);
916         }
917         /* append new data, add trailing null */
918         memcpy(literalbuf+literallen, ytext, yleng);
919         literallen += yleng;
920         literalbuf[literallen] = '\0';
921 }
922
923 static void
924 addlitchar(unsigned char ychar)
925 {
926         /* enlarge buffer if needed */
927         if ((literallen+1) >= literalalloc)
928         {
929                 literalalloc *= 2;
930                 literalbuf = (char *) realloc(literalbuf, literalalloc);
931         }
932         /* append new data, add trailing null */
933         literalbuf[literallen] = ychar;
934         literallen += 1;
935         literalbuf[literallen] = '\0';
936 }
937
938 int
939 yywrap(void)
940 {
941         return(1);
942 }
943