summaryrefslogtreecommitdiffstats
path: root/src/interfaces/ecpg/preproc/pgc.l
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/ecpg/preproc/pgc.l')
-rw-r--r--src/interfaces/ecpg/preproc/pgc.l1740
1 files changed, 1740 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l
new file mode 100644
index 0000000..7a03566
--- /dev/null
+++ b/src/interfaces/ecpg/preproc/pgc.l
@@ -0,0 +1,1740 @@
+%top{
+/*-------------------------------------------------------------------------
+ *
+ * pgc.l
+ * lexical scanner for ecpg
+ *
+ * This is a modified version of src/backend/parser/scan.l
+ *
+ * The ecpg scanner is not backup-free, so the fail rules are
+ * only here to simplify syncing this file with scan.l.
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/interfaces/ecpg/preproc/pgc.l
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <ctype.h>
+#include <limits.h>
+
+#include "common/string.h"
+
+#include "preproc_extern.h"
+#include "preproc.h"
+}
+
+%{
+
+/* LCOV_EXCL_START */
+
+extern YYSTYPE base_yylval;
+
+static int xcdepth = 0; /* depth of nesting in slash-star comments */
+static char *dolqstart = NULL; /* current $foo$ quote start string */
+
+/*
+ * literalbuf is used to accumulate literal values when multiple rules
+ * are needed to parse a single literal. Call startlit to reset buffer
+ * to empty, addlit to add text. Note that the buffer is permanently
+ * malloc'd to the largest size needed so far in the current run.
+ */
+static char *literalbuf = NULL; /* expandable buffer */
+static int literallen; /* actual current length */
+static int literalalloc; /* current allocated buffer size */
+
+/* Used for detecting global state together with braces_open */
+static int parenths_open;
+
+/* Used to tell parse_include() whether the command was #include or #include_next */
+static bool include_next;
+
+#define startlit() (literalbuf[0] = '\0', literallen = 0)
+static void addlit(char *ytext, int yleng);
+static void addlitchar(unsigned char);
+static int process_integer_literal(const char *token, YYSTYPE *lval);
+static void parse_include(void);
+static bool ecpg_isspace(char ch);
+static bool isdefine(void);
+static bool isinformixdefine(void);
+
+char *token_start;
+
+/* vars to keep track of start conditions when scanning literals */
+static int state_before_str_start;
+static int state_before_str_stop;
+
+struct _yy_buffer
+{
+ YY_BUFFER_STATE buffer;
+ long lineno;
+ char *filename;
+ struct _yy_buffer *next;
+} *yy_buffer = NULL;
+
+static char *old;
+
+/*
+ * Vars for handling ifdef/elif/endif constructs. preproc_tos is the current
+ * nesting depth of such constructs, and stacked_if_value[preproc_tos] is the
+ * state for the innermost level. (For convenience, stacked_if_value[0] is
+ * initialized as though we are in the active branch of some outermost IF.)
+ * The active field is true if the current branch is active (being expanded).
+ * The saw_active field is true if we have found any successful branch,
+ * so that all subsequent branches of this level should be skipped.
+ * The else_branch field is true if we've found an 'else' (so that another
+ * 'else' or 'elif' at this level is an error.)
+ * For IFs nested within an inactive branch, all branches always have active
+ * set to false, but saw_active and else_branch are maintained normally.
+ * ifcond is valid only while evaluating an if-condition; it's true if we
+ * are doing ifdef, false if ifndef.
+ */
+#define MAX_NESTED_IF 128
+static short preproc_tos;
+static bool ifcond;
+static struct _if_value
+{
+ bool active;
+ bool saw_active;
+ bool else_branch;
+} stacked_if_value[MAX_NESTED_IF];
+
+%}
+
+%option 8bit
+%option never-interactive
+%option nodefault
+%option noinput
+%option noyywrap
+%option warn
+%option yylineno
+%option prefix="base_yy"
+
+/*
+ * OK, here is a short description of lex/flex rules behavior.
+ * The longest pattern which matches an input string is always chosen.
+ * For equal-length patterns, the first occurring in the rules list is chosen.
+ * INITIAL is the starting state, to which all non-conditional rules apply.
+ * Exclusive states change parsing rules while the state is active. When in
+ * an exclusive state, only those rules defined for that state apply.
+ *
+ * We use exclusive states for quoted strings, extended comments,
+ * and to eliminate parsing troubles for numeric strings.
+ * Exclusive states:
+ * <xb> bit string literal
+ * <xc> extended C-style comments
+ * <xd> delimited identifiers (double-quoted identifiers)
+ * <xdc> double-quoted strings in C
+ * <xh> hexadecimal numeric string
+ * <xn> national character quoted strings
+ * <xq> standard quoted strings
+ * <xqs> quote stop (detect continued strings)
+ * <xe> extended quoted strings (support backslash escape sequences)
+ * <xqc> single-quoted strings in C
+ * <xdolq> $foo$ quoted strings
+ * <xui> quoted identifier with Unicode escapes
+ * <xus> quoted string with Unicode escapes
+ * <xcond> condition of an EXEC SQL IFDEF construct
+ * <xskip> skipping the inactive part of an EXEC SQL IFDEF construct
+ *
+ * Note: we intentionally don't mimic the backend's <xeu> state; we have
+ * no need to distinguish it from <xe> state.
+ *
+ * Remember to add an <<EOF>> case whenever you add a new exclusive state!
+ * The default one is probably not the right thing.
+ */
+
+%x xb
+%x xc
+%x xd
+%x xdc
+%x xh
+%x xn
+%x xq
+%x xqs
+%x xe
+%x xqc
+%x xdolq
+%x xui
+%x xus
+%x xcond
+%x xskip
+
+/* Additional exclusive states that are specific to ECPG */
+%x C SQL incl def def_ident undef
+
+/*
+ * In order to make the world safe for Windows and Mac clients as well as
+ * Unix ones, we accept either \n or \r as a newline. A DOS-style \r\n
+ * sequence will be seen as two successive newlines, but that doesn't cause
+ * any problems. SQL-style comments, which start with -- and extend to the
+ * next newline, are treated as equivalent to a single whitespace character.
+ *
+ * NOTE a fine point: if there is no newline following --, we will absorb
+ * everything to the end of the input as a comment. This is correct. Older
+ * versions of Postgres failed to recognize -- as a comment if the input
+ * did not end with a newline.
+ *
+ * XXX perhaps \f (formfeed) should be treated as a newline as well?
+ *
+ * XXX if you change the set of whitespace characters, fix ecpg_isspace()
+ * to agree.
+ */
+
+space [ \t\n\r\f]
+horiz_space [ \t\f]
+newline [\n\r]
+non_newline [^\n\r]
+
+comment ("--"{non_newline}*)
+
+whitespace ({space}+|{comment})
+
+/*
+ * SQL requires at least one newline in the whitespace separating
+ * string literals that are to be concatenated. Silly, but who are we
+ * to argue? Note that {whitespace_with_newline} should not have * after
+ * it, whereas {whitespace} should generally have a * after it...
+ */
+
+horiz_whitespace ({horiz_space}|{comment})
+whitespace_with_newline ({horiz_whitespace}*{newline}{whitespace}*)
+
+quote '
+/* If we see {quote} then {quotecontinue}, the quoted string continues */
+quotecontinue {whitespace_with_newline}{quote}
+
+/*
+ * {quotecontinuefail} is needed to avoid lexer backup when we fail to match
+ * {quotecontinue}. It might seem that this could just be {whitespace}*,
+ * but if there's a dash after {whitespace_with_newline}, it must be consumed
+ * to see if there's another dash --- which would start a {comment} and thus
+ * allow continuation of the {quotecontinue} token.
+ */
+quotecontinuefail {whitespace}*"-"?
+
+/* Bit string
+ */
+xbstart [bB]{quote}
+xbinside [^']*
+
+/* Hexadecimal number */
+xhstart [xX]{quote}
+xhinside [^']*
+
+/* National character */
+xnstart [nN]{quote}
+
+/* Quoted string that allows backslash escapes */
+xestart [eE]{quote}
+xeinside [^\\']+
+xeescape [\\][^0-7]
+xeoctesc [\\][0-7]{1,3}
+xehexesc [\\]x[0-9A-Fa-f]{1,2}
+xeunicode [\\](u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})
+
+/* Extended quote
+ * xqdouble implements embedded quote, ''''
+ */
+xqstart {quote}
+xqdouble {quote}{quote}
+xqcquote [\\]{quote}
+xqinside [^']+
+
+/* $foo$ style quotes ("dollar quoting")
+ * The quoted string starts with $foo$ where "foo" is an optional string
+ * in the form of an identifier, except that it may not contain "$",
+ * and extends to the first occurrence of an identical string.
+ * There is *no* processing of the quoted text.
+ *
+ * {dolqfailed} is an error rule to avoid scanner backup when {dolqdelim}
+ * fails to match its trailing "$".
+ */
+dolq_start [A-Za-z\200-\377_]
+dolq_cont [A-Za-z\200-\377_0-9]
+dolqdelim \$({dolq_start}{dolq_cont}*)?\$
+dolqfailed \${dolq_start}{dolq_cont}*
+dolqinside [^$]+
+
+/* Double quote
+ * Allows embedded spaces and other special characters into identifiers.
+ */
+dquote \"
+xdstart {dquote}
+xdstop {dquote}
+xddouble {dquote}{dquote}
+xdinside [^"]+
+
+/* Quoted identifier with Unicode escapes */
+xuistart [uU]&{dquote}
+
+/* Quoted string with Unicode escapes */
+xusstart [uU]&{quote}
+
+/* special stuff for C strings */
+xdcqq \\\\
+xdcqdq \\\"
+xdcother [^"]
+xdcinside ({xdcqq}|{xdcqdq}|{xdcother})
+
+
+/* C-style comments
+ *
+ * The "extended comment" syntax closely resembles allowable operator syntax.
+ * The tricky part here is to get lex to recognize a string starting with
+ * slash-star as a comment, when interpreting it as an operator would produce
+ * a longer match --- remember lex will prefer a longer match! Also, if we
+ * have something like plus-slash-star, lex will think this is a 3-character
+ * operator whereas we want to see it as a + operator and a comment start.
+ * The solution is two-fold:
+ * 1. append {op_chars}* to xcstart so that it matches as much text as
+ * {operator} would. Then the tie-breaker (first matching rule of same
+ * length) ensures xcstart wins. We put back the extra stuff with yyless()
+ * in case it contains a star-slash that should terminate the comment.
+ * 2. In the operator rule, check for slash-star within the operator, and
+ * if found throw it back with yyless(). This handles the plus-slash-star
+ * problem.
+ * Dash-dash comments have similar interactions with the operator rule.
+ */
+xcstart \/\*{op_chars}*
+xcstop \*+\/
+xcinside [^*/]+
+
+digit [0-9]
+ident_start [A-Za-z\200-\377_]
+ident_cont [A-Za-z\200-\377_0-9\$]
+
+identifier {ident_start}{ident_cont}*
+
+array ({ident_cont}|{whitespace}|[\[\]\+\-\*\%\/\(\)\>\.])*
+
+/* Assorted special-case operators and operator-like tokens */
+typecast "::"
+dot_dot \.\.
+colon_equals ":="
+
+/*
+ * These operator-like tokens (unlike the above ones) also match the {operator}
+ * rule, which means that they might be overridden by a longer match if they
+ * are followed by a comment start or a + or - character. Accordingly, if you
+ * add to this list, you must also add corresponding code to the {operator}
+ * block to return the correct token in such cases. (This is not needed in
+ * psqlscan.l since the token value is ignored there.)
+ */
+equals_greater "=>"
+less_equals "<="
+greater_equals ">="
+less_greater "<>"
+not_equals "!="
+
+/*
+ * "self" is the set of chars that should be returned as single-character
+ * tokens. "op_chars" is the set of chars that can make up "Op" tokens,
+ * which can be one or more characters long (but if a single-char token
+ * appears in the "self" set, it is not to be returned as an Op). Note
+ * that the sets overlap, but each has some chars that are not in the other.
+ *
+ * If you change either set, adjust the character lists appearing in the
+ * rule for "operator"!
+ */
+self [,()\[\].;\:\+\-\*\/\%\^\<\>\=]
+op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
+operator {op_chars}+
+
+/* we no longer allow unary minus in numbers.
+ * instead we pass it separately to parser. there it gets
+ * coerced via doNegate() -- Leon aug 20 1999
+ *
+ * {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
+ *
+ * {realfail1} and {realfail2} are added to prevent the need for scanner
+ * backup when the {real} rule fails to match completely.
+ */
+
+integer {digit}+
+decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*))
+decimalfail {digit}+\.\.
+real ({integer}|{decimal})[Ee][-+]?{digit}+
+realfail1 ({integer}|{decimal})[Ee]
+realfail2 ({integer}|{decimal})[Ee][-+]
+
+param \${integer}
+
+/* special characters for other dbms */
+/* we have to react differently in compat mode */
+informix_special [\$]
+
+other .
+
+/*
+ * Dollar quoted strings are totally opaque, and no escaping is done on them.
+ * Other quoted strings must allow some special characters such as single-quote
+ * and newline.
+ * Embedded single-quotes are implemented both in the SQL standard
+ * style of two adjacent single quotes "''" and in the Postgres/Java style
+ * of escaped-quote "\'".
+ * Other embedded escaped characters are matched explicitly and the leading
+ * backslash is dropped from the string.
+ * Note that xcstart must appear before operator, as explained above!
+ * Also whitespace (comment) must appear before operator.
+ */
+
+/* some stuff needed for ecpg */
+exec [eE][xX][eE][cC]
+sql [sS][qQ][lL]
+define [dD][eE][fF][iI][nN][eE]
+include [iI][nN][cC][lL][uU][dD][eE]
+include_next [iI][nN][cC][lL][uU][dD][eE]_[nN][eE][xX][tT]
+import [iI][mM][pP][oO][rR][tT]
+undef [uU][nN][dD][eE][fF]
+
+/* C version of hex number */
+xch 0[xX][0-9A-Fa-f]*
+
+ccomment "//".*\n
+
+if [iI][fF]
+ifdef [iI][fF][dD][eE][fF]
+ifndef [iI][fF][nN][dD][eE][fF]
+else [eE][lL][sS][eE]
+elif [eE][lL][iI][fF]
+endif [eE][nN][dD][iI][fF]
+
+struct [sS][tT][rR][uU][cC][tT]
+
+exec_sql {exec}{space}*{sql}{space}*
+ipdigit ({digit}|{digit}{digit}|{digit}{digit}{digit})
+ip {ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit}
+
+/* we might want to parse all cpp include files */
+cppinclude {space}*#{include}{space}*
+cppinclude_next {space}*#{include_next}{space}*
+
+/* take care of cpp lines, they may also be continued */
+/* first a general line for all commands not starting with "i" */
+/* and then the other commands starting with "i", we have to add these
+ * separately because the cppline production would match on "include" too
+ */
+cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+\/)|.|\\{space}*{newline})*{newline}
+
+%%
+
+%{
+ /* code to execute during start of each call of yylex() */
+ token_start = NULL;
+%}
+
+<SQL>{
+{whitespace} {
+ /* ignore */
+ }
+} /* <SQL> */
+
+<C,SQL>{
+{xcstart} {
+ token_start = yytext;
+ state_before_str_start = YYSTATE;
+ xcdepth = 0;
+ BEGIN(xc);
+ /* Put back any characters past slash-star; see above */
+ yyless(2);
+ fputs("/*", yyout);
+ }
+} /* <C,SQL> */
+
+<xc>{
+{xcstart} {
+ if (state_before_str_start == SQL)
+ {
+ xcdepth++;
+ /* Put back any characters past slash-star; see above */
+ yyless(2);
+ fputs("/_*", yyout);
+ }
+ else if (state_before_str_start == C)
+ {
+ ECHO;
+ }
+ }
+
+{xcstop} {
+ if (state_before_str_start == SQL)
+ {
+ if (xcdepth <= 0)
+ {
+ ECHO;
+ BEGIN(SQL);
+ token_start = NULL;
+ }
+ else
+ {
+ xcdepth--;
+ fputs("*_/", yyout);
+ }
+ }
+ else if (state_before_str_start == C)
+ {
+ ECHO;
+ BEGIN(C);
+ token_start = NULL;
+ }
+ }
+
+{xcinside} {
+ ECHO;
+ }
+
+{op_chars} {
+ ECHO;
+ }
+
+\*+ {
+ ECHO;
+ }
+
+<<EOF>> {
+ mmfatal(PARSE_ERROR, "unterminated /* comment");
+ }
+} /* <xc> */
+
+<SQL>{
+{xbstart} {
+ token_start = yytext;
+ state_before_str_start = YYSTATE;
+ BEGIN(xb);
+ startlit();
+ }
+} /* <SQL> */
+
+<xh>{xhinside} |
+<xb>{xbinside} {
+ addlit(yytext, yyleng);
+ }
+<xb><<EOF>> { mmfatal(PARSE_ERROR, "unterminated bit string literal"); }
+
+<SQL>{xhstart} {
+ token_start = yytext;
+ state_before_str_start = YYSTATE;
+ BEGIN(xh);
+ startlit();
+ }
+<xh><<EOF>> { mmfatal(PARSE_ERROR, "unterminated hexadecimal string literal"); }
+
+<C>{xqstart} {
+ token_start = yytext;
+ state_before_str_start = YYSTATE;
+ BEGIN(xqc);
+ startlit();
+ }
+
+<SQL>{
+{xnstart} {
+ /* National character.
+ * Transfer it as-is to the backend.
+ */
+ token_start = yytext;
+ state_before_str_start = YYSTATE;
+ BEGIN(xn);
+ startlit();
+ }
+
+{xqstart} {
+ token_start = yytext;
+ state_before_str_start = YYSTATE;
+ BEGIN(xq);
+ startlit();
+ }
+{xestart} {
+ token_start = yytext;
+ state_before_str_start = YYSTATE;
+ BEGIN(xe);
+ startlit();
+ }
+{xusstart} {
+ token_start = yytext;
+ state_before_str_start = YYSTATE;
+ BEGIN(xus);
+ startlit();
+ }
+} /* <SQL> */
+
+<xb,xh,xq,xqc,xe,xn,xus>{quote} {
+ /*
+ * When we are scanning a quoted string and see an end
+ * quote, we must look ahead for a possible continuation.
+ * If we don't see one, we know the end quote was in fact
+ * the end of the string. To reduce the lexer table size,
+ * we use a single "xqs" state to do the lookahead for all
+ * types of strings.
+ */
+ state_before_str_stop = YYSTATE;
+ BEGIN(xqs);
+ }
+<xqs>{quotecontinue} {
+ /*
+ * Found a quote continuation, so return to the in-quote
+ * state and continue scanning the literal. Nothing is
+ * added to the literal's contents.
+ */
+ BEGIN(state_before_str_stop);
+ }
+<xqs>{quotecontinuefail} |
+<xqs>{other} |
+<xqs><<EOF>> {
+ /*
+ * Failed to see a quote continuation. Throw back
+ * everything after the end quote, and handle the string
+ * according to the state we were in previously.
+ */
+ yyless(0);
+ BEGIN(state_before_str_start);
+
+ switch (state_before_str_stop)
+ {
+ case xb:
+ if (literalbuf[strspn(literalbuf, "01")] != '\0')
+ mmerror(PARSE_ERROR, ET_ERROR, "invalid bit string literal");
+ base_yylval.str = psprintf("b'%s'", literalbuf);
+ return BCONST;
+ case xh:
+ if (literalbuf[strspn(literalbuf, "0123456789abcdefABCDEF")] != '\0')
+ mmerror(PARSE_ERROR, ET_ERROR, "invalid hex string literal");
+ base_yylval.str = psprintf("x'%s'", literalbuf);
+ return XCONST;
+ case xq:
+ /* fallthrough */
+ case xqc:
+ base_yylval.str = psprintf("'%s'", literalbuf);
+ return SCONST;
+ case xe:
+ base_yylval.str = psprintf("E'%s'", literalbuf);
+ return SCONST;
+ case xn:
+ base_yylval.str = psprintf("N'%s'", literalbuf);
+ return SCONST;
+ case xus:
+ base_yylval.str = psprintf("U&'%s'", literalbuf);
+ return USCONST;
+ default:
+ mmfatal(PARSE_ERROR, "unhandled previous state in xqs\n");
+ }
+ }
+
+<xq,xe,xn,xus>{xqdouble} { addlit(yytext, yyleng); }
+<xqc>{xqcquote} { addlit(yytext, yyleng); }
+<xq,xqc,xn,xus>{xqinside} { addlit(yytext, yyleng); }
+<xe>{xeinside} {
+ addlit(yytext, yyleng);
+ }
+<xe>{xeunicode} {
+ addlit(yytext, yyleng);
+ }
+<xe>{xeescape} {
+ addlit(yytext, yyleng);
+ }
+<xe>{xeoctesc} {
+ addlit(yytext, yyleng);
+ }
+<xe>{xehexesc} {
+ addlit(yytext, yyleng);
+ }
+<xe>. {
+ /* This is only needed for \ just before EOF */
+ addlitchar(yytext[0]);
+ }
+<xq,xqc,xe,xn,xus><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted string"); }
+
+<SQL>{
+{dolqdelim} {
+ token_start = yytext;
+ if (dolqstart)
+ free(dolqstart);
+ dolqstart = mm_strdup(yytext);
+ BEGIN(xdolq);
+ startlit();
+ addlit(yytext, yyleng);
+ }
+{dolqfailed} {
+ /* throw back all but the initial "$" */
+ yyless(1);
+ /* and treat it as {other} */
+ return yytext[0];
+ }
+} /* <SQL> */
+
+<xdolq>{dolqdelim} {
+ if (strcmp(yytext, dolqstart) == 0)
+ {
+ addlit(yytext, yyleng);
+ free(dolqstart);
+ dolqstart = NULL;
+ BEGIN(SQL);
+ base_yylval.str = mm_strdup(literalbuf);
+ return SCONST;
+ }
+ else
+ {
+ /*
+ * When we fail to match $...$ to dolqstart, transfer
+ * the $... part to the output, but put back the final
+ * $ for rescanning. Consider $delim$...$junk$delim$
+ */
+ addlit(yytext, yyleng - 1);
+ yyless(yyleng - 1);
+ }
+ }
+<xdolq>{dolqinside} {
+ addlit(yytext, yyleng);
+ }
+<xdolq>{dolqfailed} {
+ addlit(yytext, yyleng);
+ }
+<xdolq>. {
+ /* single quote or dollar sign */
+ addlitchar(yytext[0]);
+ }
+<xdolq><<EOF>> { mmfatal(PARSE_ERROR, "unterminated dollar-quoted string"); }
+
+<SQL>{
+{xdstart} {
+ state_before_str_start = YYSTATE;
+ BEGIN(xd);
+ startlit();
+ }
+{xuistart} {
+ state_before_str_start = YYSTATE;
+ BEGIN(xui);
+ startlit();
+ }
+} /* <SQL> */
+
+<xd>{xdstop} {
+ BEGIN(state_before_str_start);
+ if (literallen == 0)
+ mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier");
+ /*
+ * The server will truncate the identifier here. We do
+ * not, as (1) it does not change the result; (2) we don't
+ * know what NAMEDATALEN the server might use; (3) this
+ * code path is also taken for literal query strings in
+ * PREPARE and EXECUTE IMMEDIATE, which can certainly be
+ * longer than NAMEDATALEN.
+ */
+ base_yylval.str = mm_strdup(literalbuf);
+ return CSTRING;
+ }
+<xdc>{xdstop} {
+ BEGIN(state_before_str_start);
+ base_yylval.str = mm_strdup(literalbuf);
+ return CSTRING;
+ }
+<xui>{dquote} {
+ BEGIN(state_before_str_start);
+ if (literallen == 2) /* "U&" */
+ mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier");
+ /* The backend will truncate the identifier here. We do not as it does not change the result. */
+ base_yylval.str = psprintf("U&\"%s\"", literalbuf);
+ return UIDENT;
+ }
+<xd,xui>{xddouble} {
+ addlit(yytext, yyleng);
+ }
+<xd,xui>{xdinside} {
+ addlit(yytext, yyleng);
+ }
+<xd,xui><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted identifier"); }
+<C>{xdstart} {
+ state_before_str_start = YYSTATE;
+ BEGIN(xdc);
+ startlit();
+ }
+<xdc>{xdcinside} {
+ addlit(yytext, yyleng);
+ }
+<xdc><<EOF>> { mmfatal(PARSE_ERROR, "unterminated quoted string"); }
+
+<SQL>{
+{typecast} {
+ return TYPECAST;
+ }
+
+{dot_dot} {
+ return DOT_DOT;
+ }
+
+{colon_equals} {
+ return COLON_EQUALS;
+ }
+
+{equals_greater} {
+ return EQUALS_GREATER;
+ }
+
+{less_equals} {
+ return LESS_EQUALS;
+ }
+
+{greater_equals} {
+ return GREATER_EQUALS;
+ }
+
+{less_greater} {
+ /* We accept both "<>" and "!=" as meaning NOT_EQUALS */
+ return NOT_EQUALS;
+ }
+
+{not_equals} {
+ /* We accept both "<>" and "!=" as meaning NOT_EQUALS */
+ return NOT_EQUALS;
+ }
+
+{informix_special} {
+ /* are we simulating Informix? */
+ if (INFORMIX_MODE)
+ {
+ unput(':');
+ }
+ else
+ return yytext[0];
+ }
+
+{self} {
+ /*
+ * We may find a ';' inside a structure
+ * definition in a TYPE or VAR statement.
+ * This is not an EOL marker.
+ */
+ if (yytext[0] == ';' && struct_level == 0)
+ BEGIN(C);
+ return yytext[0];
+ }
+
+{operator} {
+ /*
+ * Check for embedded slash-star or dash-dash; those
+ * are comment starts, so operator must stop there.
+ * Note that slash-star or dash-dash at the first
+ * character will match a prior rule, not this one.
+ */
+ int nchars = yyleng;
+ char *slashstar = strstr(yytext, "/*");
+ char *dashdash = strstr(yytext, "--");
+
+ if (slashstar && dashdash)
+ {
+ /* if both appear, take the first one */
+ if (slashstar > dashdash)
+ slashstar = dashdash;
+ }
+ else if (!slashstar)
+ slashstar = dashdash;
+ if (slashstar)
+ nchars = slashstar - yytext;
+
+ /*
+ * For SQL compatibility, '+' and '-' cannot be the
+ * last char of a multi-char operator unless the operator
+ * contains chars that are not in SQL operators.
+ * The idea is to lex '=-' as two operators, but not
+ * to forbid operator names like '?-' that could not be
+ * sequences of SQL operators.
+ */
+ if (nchars > 1 &&
+ (yytext[nchars - 1] == '+' ||
+ yytext[nchars - 1] == '-'))
+ {
+ int ic;
+
+ for (ic = nchars - 2; ic >= 0; ic--)
+ {
+ char c = yytext[ic];
+ if (c == '~' || c == '!' || c == '@' ||
+ c == '#' || c == '^' || c == '&' ||
+ c == '|' || c == '`' || c == '?' ||
+ c == '%')
+ break;
+ }
+ if (ic < 0)
+ {
+ /*
+ * didn't find a qualifying character, so remove
+ * all trailing [+-]
+ */
+ do {
+ nchars--;
+ } while (nchars > 1 &&
+ (yytext[nchars - 1] == '+' ||
+ yytext[nchars - 1] == '-'));
+ }
+ }
+
+ if (nchars < yyleng)
+ {
+ /* Strip the unwanted chars from the token */
+ yyless(nchars);
+ /*
+ * If what we have left is only one char, and it's
+ * one of the characters matching "self", then
+ * return it as a character token the same way
+ * that the "self" rule would have.
+ */
+ if (nchars == 1 &&
+ strchr(",()[].;:+-*/%^<>=", yytext[0]))
+ return yytext[0];
+ /*
+ * Likewise, if what we have left is two chars, and
+ * those match the tokens ">=", "<=", "=>", "<>" or
+ * "!=", then we must return the appropriate token
+ * rather than the generic Op.
+ */
+ if (nchars == 2)
+ {
+ if (yytext[0] == '=' && yytext[1] == '>')
+ return EQUALS_GREATER;
+ if (yytext[0] == '>' && yytext[1] == '=')
+ return GREATER_EQUALS;
+ if (yytext[0] == '<' && yytext[1] == '=')
+ return LESS_EQUALS;
+ if (yytext[0] == '<' && yytext[1] == '>')
+ return NOT_EQUALS;
+ if (yytext[0] == '!' && yytext[1] == '=')
+ return NOT_EQUALS;
+ }
+ }
+
+ base_yylval.str = mm_strdup(yytext);
+ return Op;
+ }
+
+{param} {
+ base_yylval.ival = atol(yytext+1);
+ return PARAM;
+ }
+
+{ip} {
+ base_yylval.str = mm_strdup(yytext);
+ return IP;
+ }
+} /* <SQL> */
+
+<C,SQL>{
+{integer} {
+ return process_integer_literal(yytext, &base_yylval);
+ }
+{decimal} {
+ base_yylval.str = mm_strdup(yytext);
+ return FCONST;
+ }
+{decimalfail} {
+ /* throw back the .., and treat as integer */
+ yyless(yyleng - 2);
+ return process_integer_literal(yytext, &base_yylval);
+ }
+{real} {
+ base_yylval.str = mm_strdup(yytext);
+ return FCONST;
+ }
+{realfail1} {
+ /*
+ * throw back the [Ee], and figure out whether what
+ * remains is an {integer} or {decimal}.
+ */
+ yyless(yyleng - 1);
+ return process_integer_literal(yytext, &base_yylval);
+ }
+{realfail2} {
+ /* throw back the [Ee][+-], and proceed as above */
+ yyless(yyleng - 2);
+ return process_integer_literal(yytext, &base_yylval);
+ }
+} /* <C,SQL> */
+
+<SQL>{
+:{identifier}((("->"|\.){identifier})|(\[{array}\]))* {
+ base_yylval.str = mm_strdup(yytext+1);
+ return CVARIABLE;
+ }
+
+{identifier} {
+ if (!isdefine())
+ {
+ int kwvalue;
+
+ /* Is it an SQL/ECPG keyword? */
+ kwvalue = ScanECPGKeywordLookup(yytext);
+ if (kwvalue >= 0)
+ return kwvalue;
+
+ /* Is it a C keyword? */
+ kwvalue = ScanCKeywordLookup(yytext);
+ if (kwvalue >= 0)
+ return kwvalue;
+
+ /*
+ * None of the above. Return it as an identifier.
+ *
+ * The backend will attempt to truncate and case-fold
+ * the identifier, but I see no good reason for ecpg
+ * to do so; that's just another way that ecpg could get
+ * out of step with the backend.
+ */
+ base_yylval.str = mm_strdup(yytext);
+ return IDENT;
+ }
+ }
+
+{other} {
+ return yytext[0];
+ }
+} /* <SQL> */
+
+ /*
+ * Begin ECPG-specific rules
+ */
+
+<C>{exec_sql} { BEGIN(SQL); return SQL_START; }
+<C>{informix_special} {
+ /* are we simulating Informix? */
+ if (INFORMIX_MODE)
+ {
+ BEGIN(SQL);
+ return SQL_START;
+ }
+ else
+ return S_ANYTHING;
+ }
+<C>{ccomment} { ECHO; }
+<C>{xch} {
+ char* endptr;
+
+ errno = 0;
+ base_yylval.ival = strtoul((char *)yytext,&endptr,16);
+ if (*endptr != '\0' || errno == ERANGE)
+ {
+ errno = 0;
+ base_yylval.str = mm_strdup(yytext);
+ return SCONST;
+ }
+ return ICONST;
+ }
+<C>{cppinclude} {
+ if (system_includes)
+ {
+ include_next = false;
+ BEGIN(incl);
+ }
+ else
+ {
+ base_yylval.str = mm_strdup(yytext);
+ return CPP_LINE;
+ }
+ }
+<C>{cppinclude_next} {
+ if (system_includes)
+ {
+ include_next = true;
+ BEGIN(incl);
+ }
+ else
+ {
+ base_yylval.str = mm_strdup(yytext);
+ return CPP_LINE;
+ }
+ }
+<C,SQL>{cppline} {
+ base_yylval.str = mm_strdup(yytext);
+ return CPP_LINE;
+ }
+<C>{identifier} {
+ /*
+ * Try to detect a function name:
+ * look for identifiers at the global scope
+ * keep the last identifier before the first '(' and '{'
+ */
+ if (braces_open == 0 && parenths_open == 0)
+ {
+ if (current_function)
+ free(current_function);
+ current_function = mm_strdup(yytext);
+ }
+ /* Informix uses SQL defines only in SQL space */
+ /* however, some defines have to be taken care of for compatibility */
+ if ((!INFORMIX_MODE || !isinformixdefine()) && !isdefine())
+ {
+ int kwvalue;
+
+ kwvalue = ScanCKeywordLookup(yytext);
+ if (kwvalue >= 0)
+ return kwvalue;
+ else
+ {
+ base_yylval.str = mm_strdup(yytext);
+ return IDENT;
+ }
+ }
+ }
+<C>{xcstop} { mmerror(PARSE_ERROR, ET_ERROR, "nested /* ... */ comments"); }
+<C>":" { return ':'; }
+<C>";" { return ';'; }
+<C>"," { return ','; }
+<C>"*" { return '*'; }
+<C>"%" { return '%'; }
+<C>"/" { return '/'; }
+<C>"+" { return '+'; }
+<C>"-" { return '-'; }
+<C>"(" { parenths_open++; return '('; }
+<C>")" { parenths_open--; return ')'; }
+<C,xskip>{space} { ECHO; }
+<C>\{ { return '{'; }
+<C>\} { return '}'; }
+<C>\[ { return '['; }
+<C>\] { return ']'; }
+<C>\= { return '='; }
+<C>"->" { return S_MEMBER; }
+<C>">>" { return S_RSHIFT; }
+<C>"<<" { return S_LSHIFT; }
+<C>"||" { return S_OR; }
+<C>"&&" { return S_AND; }
+<C>"++" { return S_INC; }
+<C>"--" { return S_DEC; }
+<C>"==" { return S_EQUAL; }
+<C>"!=" { return S_NEQUAL; }
+<C>"+=" { return S_ADD; }
+<C>"-=" { return S_SUB; }
+<C>"*=" { return S_MUL; }
+<C>"/=" { return S_DIV; }
+<C>"%=" { return S_MOD; }
+<C>"->*" { return S_MEMPOINT; }
+<C>".*" { return S_DOTPOINT; }
+<C>{other} { return S_ANYTHING; }
+<C>{exec_sql}{define}{space}* { BEGIN(def_ident); }
+<C>{informix_special}{define}{space}* {
+ /* are we simulating Informix? */
+ if (INFORMIX_MODE)
+ {
+ BEGIN(def_ident);
+ }
+ else
+ {
+ yyless(1);
+ return S_ANYTHING;
+ }
+ }
+<C>{exec_sql}{undef}{space}* { BEGIN(undef); }
+<C>{informix_special}{undef}{space}* {
+ /* are we simulating Informix? */
+ if (INFORMIX_MODE)
+ {
+ BEGIN(undef);
+ }
+ else
+ {
+ yyless(1);
+ return S_ANYTHING;
+ }
+ }
+<undef>{identifier}{space}*";" {
+ struct _defines *ptr, *ptr2 = NULL;
+ int i;
+
+ /*
+ * Skip the ";" and trailing whitespace. Note that yytext
+ * contains at least one non-space character plus the ";"
+ */
+ for (i = strlen(yytext)-2;
+ i > 0 && ecpg_isspace(yytext[i]);
+ i-- )
+ ;
+ yytext[i+1] = '\0';
+
+
+ for (ptr = defines; ptr != NULL; ptr2 = ptr, ptr = ptr->next)
+ {
+ if (strcmp(yytext, ptr->olddef) == 0)
+ {
+ if (ptr2 == NULL)
+ defines = ptr->next;
+ else
+ ptr2->next = ptr->next;
+ free(ptr->newdef);
+ free(ptr->olddef);
+ free(ptr);
+ break;
+ }
+ }
+
+ BEGIN(C);
+ }
+<undef>{other}|\n {
+ mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL UNDEF command");
+ yyterminate();
+ }
+<C>{exec_sql}{include}{space}* { BEGIN(incl); }
+<C>{informix_special}{include}{space}* {
+ /* are we simulating Informix? */
+ if (INFORMIX_MODE)
+ {
+ BEGIN(incl);
+ }
+ else
+ {
+ yyless(1);
+ return S_ANYTHING;
+ }
+ }
+<C,xskip>{exec_sql}{ifdef}{space}* {
+ if (preproc_tos >= MAX_NESTED_IF-1)
+ mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
+ preproc_tos++;
+ stacked_if_value[preproc_tos].active = false;
+ stacked_if_value[preproc_tos].saw_active = false;
+ stacked_if_value[preproc_tos].else_branch = false;
+ ifcond = true;
+ BEGIN(xcond);
+ }
+<C,xskip>{informix_special}{ifdef}{space}* {
+ /* are we simulating Informix? */
+ if (INFORMIX_MODE)
+ {
+ if (preproc_tos >= MAX_NESTED_IF-1)
+ mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
+ preproc_tos++;
+ stacked_if_value[preproc_tos].active = false;
+ stacked_if_value[preproc_tos].saw_active = false;
+ stacked_if_value[preproc_tos].else_branch = false;
+ ifcond = true;
+ BEGIN(xcond);
+ }
+ else
+ {
+ yyless(1);
+ return S_ANYTHING;
+ }
+ }
+<C,xskip>{exec_sql}{ifndef}{space}* {
+ if (preproc_tos >= MAX_NESTED_IF-1)
+ mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
+ preproc_tos++;
+ stacked_if_value[preproc_tos].active = false;
+ stacked_if_value[preproc_tos].saw_active = false;
+ stacked_if_value[preproc_tos].else_branch = false;
+ ifcond = false;
+ BEGIN(xcond);
+ }
+<C,xskip>{informix_special}{ifndef}{space}* {
+ /* are we simulating Informix? */
+ if (INFORMIX_MODE)
+ {
+ if (preproc_tos >= MAX_NESTED_IF-1)
+ mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
+ preproc_tos++;
+ stacked_if_value[preproc_tos].active = false;
+ stacked_if_value[preproc_tos].saw_active = false;
+ stacked_if_value[preproc_tos].else_branch = false;
+ ifcond = false;
+ BEGIN(xcond);
+ }
+ else
+ {
+ yyless(1);
+ return S_ANYTHING;
+ }
+ }
+<C,xskip>{exec_sql}{elif}{space}* {
+ if (preproc_tos == 0)
+ mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
+ if (stacked_if_value[preproc_tos].else_branch)
+ mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
+ ifcond = true;
+ BEGIN(xcond);
+ }
+<C,xskip>{informix_special}{elif}{space}* {
+ /* are we simulating Informix? */
+ if (INFORMIX_MODE)
+ {
+ if (preproc_tos == 0)
+ mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
+ if (stacked_if_value[preproc_tos].else_branch)
+ mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
+ ifcond = true;
+ BEGIN(xcond);
+ }
+ else
+ {
+ yyless(1);
+ return S_ANYTHING;
+ }
+ }
+
+<C,xskip>{exec_sql}{else}{space}*";" { /* only exec sql endif pops the stack, so take care of duplicated 'else' */
+ if ( preproc_tos == 0 )
+ mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
+ else if (stacked_if_value[preproc_tos].else_branch)
+ mmfatal(PARSE_ERROR, "more than one EXEC SQL ELSE");
+ else
+ {
+ stacked_if_value[preproc_tos].else_branch = true;
+ stacked_if_value[preproc_tos].active =
+ (stacked_if_value[preproc_tos-1].active &&
+ !stacked_if_value[preproc_tos].saw_active);
+ stacked_if_value[preproc_tos].saw_active = true;
+
+ if (stacked_if_value[preproc_tos].active)
+ BEGIN(C);
+ else
+ BEGIN(xskip);
+ }
+ }
+<C,xskip>{informix_special}{else}{space}*";" {
+ /* are we simulating Informix? */
+ if (INFORMIX_MODE)
+ {
+ if ( preproc_tos == 0 )
+ mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
+ else if (stacked_if_value[preproc_tos].else_branch)
+ mmfatal(PARSE_ERROR, "more than one EXEC SQL ELSE");
+ else
+ {
+ stacked_if_value[preproc_tos].else_branch = true;
+ stacked_if_value[preproc_tos].active =
+ (stacked_if_value[preproc_tos-1].active &&
+ !stacked_if_value[preproc_tos].saw_active);
+ stacked_if_value[preproc_tos].saw_active = true;
+
+ if (stacked_if_value[preproc_tos].active)
+ BEGIN(C);
+ else
+ BEGIN(xskip);
+ }
+ }
+ else
+ {
+ yyless(1);
+ return S_ANYTHING;
+ }
+ }
+<C,xskip>{exec_sql}{endif}{space}*";" {
+ if (preproc_tos == 0)
+ mmfatal(PARSE_ERROR, "unmatched EXEC SQL ENDIF");
+ else
+ preproc_tos--;
+
+ if (stacked_if_value[preproc_tos].active)
+ BEGIN(C);
+ else
+ BEGIN(xskip);
+ }
+<C,xskip>{informix_special}{endif}{space}*";" {
+ /* are we simulating Informix? */
+ if (INFORMIX_MODE)
+ {
+ if (preproc_tos == 0)
+ mmfatal(PARSE_ERROR, "unmatched EXEC SQL ENDIF");
+ else
+ preproc_tos--;
+
+ if (stacked_if_value[preproc_tos].active)
+ BEGIN(C);
+ else
+ BEGIN(xskip);
+ }
+ else
+ {
+ yyless(1);
+ return S_ANYTHING;
+ }
+ }
+
+<xskip>{other} { /* ignore */ }
+
+<xcond>{identifier}{space}*";" {
+ {
+ struct _defines *defptr;
+ unsigned int i;
+ bool this_active;
+
+ /*
+ * Skip the ";" and trailing whitespace. Note that yytext
+ * contains at least one non-space character plus the ";"
+ */
+ for (i = strlen(yytext)-2;
+ i > 0 && ecpg_isspace(yytext[i]);
+ i-- )
+ ;
+ yytext[i+1] = '\0';
+
+ for (defptr = defines;
+ defptr != NULL &&
+ strcmp(yytext, defptr->olddef) != 0;
+ defptr = defptr->next)
+ /* skip */ ;
+
+ this_active = (defptr ? ifcond : !ifcond);
+ stacked_if_value[preproc_tos].active =
+ (stacked_if_value[preproc_tos-1].active &&
+ !stacked_if_value[preproc_tos].saw_active &&
+ this_active);
+ stacked_if_value[preproc_tos].saw_active |= this_active;
+ }
+
+ if (stacked_if_value[preproc_tos].active)
+ BEGIN(C);
+ else
+ BEGIN(xskip);
+ }
+
+<xcond>{other}|\n {
+ mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL IFDEF command");
+ yyterminate();
+ }
+<def_ident>{identifier} {
+ old = mm_strdup(yytext);
+ BEGIN(def);
+ startlit();
+ }
+<def_ident>{other}|\n {
+ mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL DEFINE command");
+ yyterminate();
+ }
+<def>{space}*";" {
+ struct _defines *ptr, *this;
+
+ for (ptr = defines; ptr != NULL; ptr = ptr->next)
+ {
+ if (strcmp(old, ptr->olddef) == 0)
+ {
+ free(ptr->newdef);
+ ptr->newdef = mm_strdup(literalbuf);
+ }
+ }
+ if (ptr == NULL)
+ {
+ this = (struct _defines *) mm_alloc(sizeof(struct _defines));
+
+ /* initial definition */
+ this->olddef = old;
+ this->newdef = mm_strdup(literalbuf);
+ this->next = defines;
+ this->used = NULL;
+ defines = this;
+ }
+
+ BEGIN(C);
+ }
+<def>[^;] { addlit(yytext, yyleng); }
+<incl>\<[^\>]+\>{space}*";"? { parse_include(); }
+<incl>{dquote}{xdinside}{dquote}{space}*";"? { parse_include(); }
+<incl>[^;\<\>\"]+";" { parse_include(); }
+<incl>{other}|\n {
+ mmfatal(PARSE_ERROR, "syntax error in EXEC SQL INCLUDE command");
+ yyterminate();
+ }
+
+<<EOF>> {
+ if (yy_buffer == NULL)
+ {
+ if ( preproc_tos > 0 )
+ {
+ preproc_tos = 0;
+ mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
+ }
+ yyterminate();
+ }
+ else
+ {
+ struct _yy_buffer *yb = yy_buffer;
+ int i;
+ struct _defines *ptr;
+
+ for (ptr = defines; ptr; ptr = ptr->next)
+ if (ptr->used == yy_buffer)
+ {
+ ptr->used = NULL;
+ break;
+ }
+
+ if (yyin != NULL)
+ fclose(yyin);
+
+ yy_delete_buffer( YY_CURRENT_BUFFER );
+ yy_switch_to_buffer(yy_buffer->buffer);
+
+ yylineno = yy_buffer->lineno;
+
+ /* We have to output the filename only if we change files here */
+ i = strcmp(input_filename, yy_buffer->filename);
+
+ free(input_filename);
+ input_filename = yy_buffer->filename;
+
+ yy_buffer = yy_buffer->next;
+ free(yb);
+
+ if (i != 0)
+ output_line_number();
+
+ }
+ }
+
+<INITIAL>{other}|\n { mmfatal(PARSE_ERROR, "internal error: unreachable state; please report this to <%s>", PACKAGE_BUGREPORT); }
+
+%%
+
+/* LCOV_EXCL_STOP */
+
+void
+lex_init(void)
+{
+ braces_open = 0;
+ parenths_open = 0;
+ current_function = NULL;
+
+ yylineno = 1;
+
+ /* initialize state for if/else/endif */
+ preproc_tos = 0;
+ stacked_if_value[preproc_tos].active = true;
+ stacked_if_value[preproc_tos].saw_active = true;
+ stacked_if_value[preproc_tos].else_branch = false;
+
+ /* initialize literal buffer to a reasonable but expansible size */
+ if (literalbuf == NULL)
+ {
+ literalalloc = 1024;
+ literalbuf = (char *) mm_alloc(literalalloc);
+ }
+ startlit();
+
+ BEGIN(C);
+}
+
+static void
+addlit(char *ytext, int yleng)
+{
+ /* enlarge buffer if needed */
+ if ((literallen+yleng) >= literalalloc)
+ {
+ do
+ literalalloc *= 2;
+ while ((literallen+yleng) >= literalalloc);
+ literalbuf = (char *) realloc(literalbuf, literalalloc);
+ }
+ /* append new data, add trailing null */
+ memcpy(literalbuf+literallen, ytext, yleng);
+ literallen += yleng;
+ literalbuf[literallen] = '\0';
+}
+
+static void
+addlitchar(unsigned char ychar)
+{
+ /* enlarge buffer if needed */
+ if ((literallen+1) >= literalalloc)
+ {
+ literalalloc *= 2;
+ literalbuf = (char *) realloc(literalbuf, literalalloc);
+ }
+ /* append new data, add trailing null */
+ literalbuf[literallen] = ychar;
+ literallen += 1;
+ literalbuf[literallen] = '\0';
+}
+
+/*
+ * Process {integer}. Note this will also do the right thing with {decimal},
+ * ie digits and a decimal point.
+ */
+static int
+process_integer_literal(const char *token, YYSTYPE *lval)
+{
+ int val;
+ char *endptr;
+
+ errno = 0;
+ val = strtoint(token, &endptr, 10);
+ if (*endptr != '\0' || errno == ERANGE)
+ {
+ /* integer too large (or contains decimal pt), treat it as a float */
+ lval->str = mm_strdup(token);
+ return FCONST;
+ }
+ lval->ival = val;
+ return ICONST;
+}
+
+static void
+parse_include(void)
+{
+ /* got the include file name */
+ struct _yy_buffer *yb;
+ struct _include_path *ip;
+ char inc_file[MAXPGPATH];
+ unsigned int i;
+
+ yb = mm_alloc(sizeof(struct _yy_buffer));
+
+ yb->buffer = YY_CURRENT_BUFFER;
+ yb->lineno = yylineno;
+ yb->filename = input_filename;
+ yb->next = yy_buffer;
+
+ yy_buffer = yb;
+
+ /*
+ * skip the ";" if there is one and trailing whitespace. Note that
+ * yytext contains at least one non-space character plus the ";"
+ */
+ for (i = strlen(yytext)-2;
+ i > 0 && ecpg_isspace(yytext[i]);
+ i--)
+ ;
+
+ if (yytext[i] == ';')
+ i--;
+
+ yytext[i+1] = '\0';
+
+ yyin = NULL;
+
+ /* If file name is enclosed in '"' remove these and look only in '.' */
+ /* Informix does look into all include paths though, except filename starts with '/' */
+ if (yytext[0] == '"' && yytext[i] == '"' &&
+ ((compat != ECPG_COMPAT_INFORMIX && compat != ECPG_COMPAT_INFORMIX_SE) || yytext[1] == '/'))
+ {
+ yytext[i] = '\0';
+ memmove(yytext, yytext+1, strlen(yytext));
+
+ strlcpy(inc_file, yytext, sizeof(inc_file));
+ yyin = fopen(inc_file, "r");
+ if (!yyin)
+ {
+ if (strlen(inc_file) <= 2 || strcmp(inc_file + strlen(inc_file) - 2, ".h") != 0)
+ {
+ strcat(inc_file, ".h");
+ yyin = fopen(inc_file, "r");
+ }
+ }
+
+ }
+ else
+ {
+ if ((yytext[0] == '"' && yytext[i] == '"') || (yytext[0] == '<' && yytext[i] == '>'))
+ {
+ yytext[i] = '\0';
+ memmove(yytext, yytext+1, strlen(yytext));
+ }
+
+ for (ip = include_paths; yyin == NULL && ip != NULL; ip = ip->next)
+ {
+ if (strlen(ip->path) + strlen(yytext) + 4 > MAXPGPATH)
+ {
+ fprintf(stderr, _("Error: include path \"%s/%s\" is too long on line %d, skipping\n"), ip->path, yytext, yylineno);
+ continue;
+ }
+ snprintf (inc_file, sizeof(inc_file), "%s/%s", ip->path, yytext);
+ yyin = fopen(inc_file, "r");
+ if (!yyin)
+ {
+ if (strcmp(inc_file + strlen(inc_file) - 2, ".h") != 0)
+ {
+ strcat(inc_file, ".h");
+ yyin = fopen( inc_file, "r" );
+ }
+ }
+ /* if the command was "include_next" we have to disregard the first hit */
+ if (yyin && include_next)
+ {
+ fclose (yyin);
+ yyin = NULL;
+ include_next = false;
+ }
+ }
+ }
+ if (!yyin)
+ mmfatal(NO_INCLUDE_FILE, "could not open include file \"%s\" on line %d", yytext, yylineno);
+
+ input_filename = mm_strdup(inc_file);
+ yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE ));
+ yylineno = 1;
+ output_line_number();
+
+ BEGIN(C);
+}
+
+/*
+ * ecpg_isspace() --- return true if flex scanner considers char whitespace
+ */
+static bool
+ecpg_isspace(char ch)
+{
+ if (ch == ' ' ||
+ ch == '\t' ||
+ ch == '\n' ||
+ ch == '\r' ||
+ ch == '\f')
+ return true;
+ return false;
+}
+
+static bool isdefine(void)
+{
+ struct _defines *ptr;
+
+ /* is it a define? */
+ for (ptr = defines; ptr; ptr = ptr->next)
+ {
+ if (strcmp(yytext, ptr->olddef) == 0 && ptr->used == NULL)
+ {
+ struct _yy_buffer *yb;
+
+ yb = mm_alloc(sizeof(struct _yy_buffer));
+
+ yb->buffer = YY_CURRENT_BUFFER;
+ yb->lineno = yylineno;
+ yb->filename = mm_strdup(input_filename);
+ yb->next = yy_buffer;
+
+ ptr->used = yy_buffer = yb;
+
+ yy_scan_string(ptr->newdef);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool isinformixdefine(void)
+{
+ const char *new = NULL;
+
+ if (strcmp(yytext, "dec_t") == 0)
+ new = "decimal";
+ else if (strcmp(yytext, "intrvl_t") == 0)
+ new = "interval";
+ else if (strcmp(yytext, "dtime_t") == 0)
+ new = "timestamp";
+
+ if (new)
+ {
+ struct _yy_buffer *yb;
+
+ yb = mm_alloc(sizeof(struct _yy_buffer));
+
+ yb->buffer = YY_CURRENT_BUFFER;
+ yb->lineno = yylineno;
+ yb->filename = mm_strdup(input_filename);
+ yb->next = yy_buffer;
+ yy_buffer = yb;
+
+ yy_scan_string(new);
+ return true;
+ }
+
+ return false;
+}