diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index 938559670f3b93862c7709435b4252bbb47ee670..a49cc6b60d536b643520a6c570f2131db26e94fc 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -12,7 +12,9 @@ #include "extern.h" struct _include_path *include_paths; -int ret_value = OK, autocommit = 0; +struct _defines *defines = NULL; +int autocommit = 0; +int ret_value = OK; struct cursor *cur = NULL; struct typedefs *types = NULL; @@ -20,7 +22,7 @@ static void usage(char *progname) { fprintf(stderr, "ecpg - the postgresql preprocessor, version: %d.%d.%d\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL); - fprintf(stderr, "Usage: %s: [-v] [-t] [-I include path] [ -o output file name] file1 [file2] ...\n", progname); + fprintf(stderr, "Usage: %s: [-v] [-t] [-I include path] [ -o output file name] [-D define name] file1 [file2] ...\n", progname); } static void @@ -33,6 +35,18 @@ add_include_path(char *path) include_paths->next = ip; } +static void +add_preprocessor_define(char *define) +{ + struct _defines *pd = defines; + + defines = mm_alloc(sizeof(struct _defines)); + defines->old = strdup(define); + defines->new = strdup(""); + defines->pertinent = true; + defines->next = pd; +} + int main(int argc, char *const argv[]) { @@ -46,7 +60,7 @@ main(int argc, char *const argv[]) add_include_path("/usr/local/include"); add_include_path("."); - while ((c = getopt(argc, argv, "vo:I:t")) != EOF) + while ((c = getopt(argc, argv, "vo:I:tD:")) != EOF) { switch (c) { @@ -74,6 +88,9 @@ main(int argc, char *const argv[]) fprintf(stderr, " %s\n", ip->path); fprintf(stderr, "End of search list.\n"); return OK; + case 'D': + add_preprocessor_define(optarg); + break; default: usage(argv[0]); return ILLEGAL_OPTION; @@ -97,7 +114,9 @@ main(int argc, char *const argv[]) strcpy(input_filename, argv[fnr]); - ptr2ext = strrchr(input_filename, '.'); + /* take care of relative paths */ + ptr2ext = strrchr(input_filename, '/'); + ptr2ext = (ptr2ext ? strrchr(ptr2ext, '.') : strrchr(input_filename, '.')); /* no extension? */ if (ptr2ext == NULL) { @@ -170,16 +189,29 @@ main(int argc, char *const argv[]) ptr = ptr->next; free(this); } + cur = NULL; + + /* remove non-pertinent old defines as well */ + while ( defines && !defines->pertinent ) { + defptr = defines; + defines = defines->next; + + free(defptr->new); + free(defptr->old); + free(defptr); + } - /* remove old defines as well */ - for (defptr = defines; defptr != NULL;) + for (defptr = defines; defptr != NULL; defptr = defptr->next ) { - struct _defines *this = defptr; + struct _defines *this = defptr->next; + + if ( this && !this->pertinent ) { + defptr->next = this->next; - free(defptr->new); - free(defptr->old); - defptr = defptr->next; + free(this->new); + free(this->old); free(this); + } } /* and old typedefs */ @@ -193,12 +225,13 @@ main(int argc, char *const argv[]) typeptr = typeptr->next; free(this); } + types = NULL; /* initialize lex */ lex_init(); /* we need two includes */ - fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/* These two include files are added by the preprocessor */\n#include <ecpgtype.h>\n#include <ecpglib.h>\n\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL); + fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/* These two include files are added by the preprocessor */\n#include <ecpgtype.h>\n#include <ecpglib.h>\n#line 1 \"%s\"\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL, input_filename); /* and parse the source */ yyparse(); diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index d84144262ea47323a70d711122642402b2d15629..53f6584343986b0207b85511f36cf77ced966e10 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/pgc.l,v 1.46 1999/10/25 03:07:59 tgl Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/pgc.l,v 1.47 1999/12/21 17:42:16 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -59,9 +59,16 @@ struct _yy_buffer { YY_BUFFER_STATE buffer; struct _yy_buffer * next; } *yy_buffer = NULL; -struct _defines *defines = NULL; static char *old; +#define MAX_NESTED_IF 128 +static short preproc_tos; +static short ifcond; +static struct _if_value { + short condition; + short else_branch; +} stacked_if_value[MAX_NESTED_IF]; + %} %option yylineno %s C SQL incl def def_ident @@ -94,6 +101,9 @@ static char *old; %x xdc %x xh %x xq +%x xpre +%x xcond +%x xskip /* Binary number */ @@ -142,15 +152,15 @@ xdcinside ({xdcqq}|{xdcqdq}|{xdcother}) /* Comments * Ignored by the scanner and parser. */ -xcline [\/][\*].*[\*][\/]{space}*\n* +xcline [\/][\*].*[\*][\/]{line_end}+ xcstart [\/][\*]{op_and_self}* -xcstop {op_and_self}*[\*][\/]({space}*|\n) +xcstop {op_and_self}*[\*][\/]{space_or_nl}* xcinside [^*]* xcstar [^/] digit [0-9] letter [\200-\377_A-Za-z] -letter_or_digit [\200-\377_A-Za-z0-9] +letter_or_digit [\200-\377_A-Za-z0-9] identifier {letter}{letter_or_digit}* @@ -167,23 +177,34 @@ operator {op_and_self}+ integer {digit}+ decimal (({digit}*\.{digit}+)|({digit}+\.{digit}*)) -real ((({digit}*\.{digit}+)|({digit}+\.{digit}*)|({digit}+))([Ee][-+]?{digit}+)) +real ((({digit}*\.{digit}+)|({digit}+\.{digit}*)|({digit}+))([Ee][-+]?{digit}+)) param \${integer} comment ("--"|"//").* ccomment "//".*\n -space [ \t\n\r\f] +space [ \t\r\f] +space_or_nl [ \t\r\f\n] +line_end {space}*\n other . /* 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] -sql [sS][qQ][lL] -cppline {space}*#.*(\\{space}*\n)*\n* +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] + +exec_sql {exec}{space_or_nl}*{sql}{space_or_nl}* + +/* Take care of cpp continuation lines */ +cppline {space}*#(.*\\{line_end})*.* /* DO NOT PUT ANY COMMENTS IN THE FOLLOWING SECTION. * AT&T lex does not properly handle C-style comments in this second lex block. @@ -268,7 +289,6 @@ cppline {space}*#.*(\\{space}*\n)*\n* <xq>{xqcat} { } - <SQL>{xdstart} { BEGIN(xd); startlit(); @@ -406,9 +426,9 @@ cppline {space}*#.*(\\{space}*\n)*\n* } } } -<SQL>{space} { /* ignore */ } +<SQL>{space_or_nl} { /* ignore */ } <SQL>{other} { return yytext[0]; } -<C>{exec}{space}*{sql} { BEGIN SQL; return SQL_START; } +<C>{exec_sql} { BEGIN SQL; return SQL_START; } <C>{ccomment} { /* ignore */ } <C>{xch} { char* endptr; @@ -473,22 +493,106 @@ cppline {space}*#.*(\\{space}*\n)*\n* <C>"-" { return('-'); } <C>"(" { return('('); } <C>")" { return(')'); } -<C>{space} { ECHO; } +<C>{space_or_nl} { ECHO; } <C>\{ { return('{'); } <C>\} { return('}'); } <C>\[ { return('['); } <C>\] { return(']'); } <C>\= { return('='); } <C>{other} { return S_ANYTHING; } -<C>{exec}{space}{sql}{space}{define} {BEGIN(def_ident);} -<def_ident>{space} {} + +<C>{exec_sql}{define}{space_or_nl}* { BEGIN(def_ident); } +<C>{exec_sql}{include}{space_or_nl}* { BEGIN(incl); } + +<C,xskip>{exec_sql}{ifdef}{space_or_nl}* { ifcond = TRUE; BEGIN(xcond); } +<C,xskip>{exec_sql}{ifndef}{space_or_nl}* { ifcond = FALSE; BEGIN(xcond); } + +<C,xskip>{exec_sql}{elif}{space_or_nl}* { /* pop stack */ + if ( preproc_tos == 0 ) { + yyerror("ERROR: missing matching 'EXEC SQL IFDEF / EXEC SQL IFNDEF'"); + } + else if ( stacked_if_value[preproc_tos].else_branch ) { + yyerror("ERROR: missing 'EXEC SQL ENDIF;'"); + } + else { + preproc_tos--; + } + + ifcond = TRUE; BEGIN(xcond); + } + +<C,xskip>{exec_sql}{else}{space_or_nl}*";" { /* only exec sql endif pops the stack, so take care of duplicated 'else' */ + if ( stacked_if_value[preproc_tos].else_branch ) { + yyerror("ERROR: duplicated 'EXEC SQL ELSE;'"); + } + else { + stacked_if_value[preproc_tos].else_branch = TRUE; + stacked_if_value[preproc_tos].condition = + (stacked_if_value[preproc_tos-1].condition && + ! stacked_if_value[preproc_tos].condition); + + if ( stacked_if_value[preproc_tos].condition ) { + BEGIN(C); + } + else { + BEGIN(xskip); + } + } + } +<C,xskip>{exec_sql}{endif}{space_or_nl}*";" { + if ( preproc_tos == 0 ) { + yyerror("ERROR: unmatched 'EXEC SQL ENDIF;'"); + } + else { + preproc_tos--; + } + + if ( stacked_if_value[preproc_tos].condition ) { + BEGIN(C); + } + else { + BEGIN(xskip); + } + } + +<xskip>{other} { /* ignore */ } + +<xcond>{identifier}{space_or_nl}*";" { + if ( preproc_tos >= MAX_NESTED_IF-1 ) { + yyerror("ERROR: too many nested 'EXEC SQL IFDEF' conditions"); + } + else { + struct _defines *defptr; + unsigned 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 && isspace(yytext[i]); i-- ) {} + yytext[i+1] = '\0'; + + for ( defptr = defines; defptr != NULL && + ( strcmp((char*)yytext, defptr->old) != 0 ); defptr = defptr->next ); + + preproc_tos++; + stacked_if_value[preproc_tos].else_branch = FALSE; + stacked_if_value[preproc_tos].condition = + ( (defptr ? ifcond : !ifcond) && stacked_if_value[preproc_tos-1].condition ); + } + + if ( stacked_if_value[preproc_tos].condition ) { + BEGIN C; + } + else { + BEGIN(xskip); + } + } + <def_ident>{identifier} { old = mm_strdup(yytext); BEGIN(def); startlit(); } -<def>{space} /* eat the whitespace */ -<def>";" { +<def>{space_or_nl}*";" { struct _defines *ptr, *this; for (ptr = defines; ptr != NULL; ptr = ptr->next) @@ -517,12 +621,12 @@ cppline {space}*#.*(\\{space}*\n)*\n* <def>[^";"] { addlit(yytext, yyleng); } -<C>{exec}{space}{sql}{space}{include} { BEGIN(incl); } -<incl>{space} /* eat the whitespace */ -<incl>[^ \t\n]+ { /* got the include file name */ + +<incl>[^";"]+";" { /* 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)); @@ -533,8 +637,10 @@ cppline {space}*#.*(\\{space}*\n)*\n* yy_buffer = yb; - if (yytext[strlen(yytext) - 1] == ';') - yytext[strlen(yytext) - 1] = '\0'; + /* skip the ";" and trailing whitespace. Note that yytext contains + at least one non-space character plus the ";" */ + for ( i = strlen(yytext)-2; i > 0 && isspace(yytext[i]); i-- ) {} + yytext[i+1] = '\0'; yyin = NULL; for (ip = include_paths; yyin == NULL && ip != NULL; ip = ip->next) @@ -564,13 +670,20 @@ cppline {space}*#.*(\\{space}*\n)*\n* input_filename = mm_strdup(inc_file); yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE )); - yylineno = 0; + yylineno = 1; output_line_number(); BEGIN C; } -<incl>";" { BEGIN C; } -<<EOF>> { if (yy_buffer == NULL) + +<<EOF>> { + if ( preproc_tos > 0 ) { + preproc_tos = 0; + + yyerror("ERROR: missing 'EXEC SQL ENDIF;'"); + } + + if (yy_buffer == NULL) yyterminate(); else { @@ -596,7 +709,12 @@ cppline {space}*#.*(\\{space}*\n)*\n* void lex_init(void) { - braces_open = 0; + braces_open = 0; + + preproc_tos = 0; + ifcond = TRUE; + stacked_if_value[preproc_tos].condition = ifcond; + stacked_if_value[preproc_tos].else_branch = FALSE; /* initialize literal buffer to a reasonable but expansible size */ if (literalbuf == NULL) @@ -626,6 +744,6 @@ addlit(char *ytext, int yleng) } int yywrap(void) -{ +{ return 1; } diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 2ba33e05d0e034f68bdca5991680db3db4a047d5..999ae050c9a2d42a6d539236092327642692061e 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -27,6 +27,7 @@ static char *connection = NULL; static int QueryIsRule = 0, ForUpdateNotAllowed = 0, FoundInto = 0; static struct this_type actual_type[STRUCT_DEPTH]; static char *actual_storage[STRUCT_DEPTH]; +static char *actual_startline[STRUCT_DEPTH]; /* temporarily store struct members while creating the data structure */ struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL }; @@ -69,7 +70,7 @@ void output_line_number() { if (input_filename) - fprintf(yyout, "\n#line %d \"%s\"\n", yylineno + 1, input_filename); + fprintf(yyout, "\n#line %d \"%s\"\n", yylineno, input_filename); } static void @@ -479,6 +480,20 @@ make_name(void) return(name); } +static char * +hashline_number() +{ + if (input_filename) + { + char* line = mm_alloc(strlen("\n#line %d \"%s\"\n") + 21 + strlen(input_filename)); + sprintf(line, "\n#line %d \"%s\"\n", yylineno, input_filename); + + return line; + } + + return EMPTY; +} + static void output_statement(char * stmt, int mode) { @@ -757,7 +772,7 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim %type <str> columnList DeleteStmt LockStmt UpdateStmt CursorStmt %type <str> NotifyStmt columnElem copy_dirn UnlistenStmt copy_null %type <str> copy_delimiter ListenStmt CopyStmt copy_file_name opt_binary -%type <str> opt_with_copy FetchStmt opt_direction fetch_how_many opt_portal_name +%type <str> opt_with_copy FetchStmt direction fetch_how_many portal_name %type <str> ClosePortalStmt DropStmt VacuumStmt opt_verbose %type <str> opt_analyze opt_va_list va_list ExplainStmt index_params %type <str> index_list func_index index_elem opt_type opt_class access_method_clause @@ -807,7 +822,7 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim %type <str> enum_type civariableonly ECPGCursorStmt ECPGDeallocate %type <str> ECPGFree ECPGDeclare ECPGVar sql_variable_declarations %type <str> sql_declaration sql_variable_list sql_variable opt_at -%type <str> struct_type s_struct declaration variable_declarations +%type <str> struct_type s_struct declaration declarations variable_declarations %type <str> s_struct s_union union_type ECPGSetAutocommit on_off %type <type_enum> simple_type varchar_type @@ -1870,20 +1885,36 @@ TruncateStmt: TRUNCATE TABLE relation_name * *****************************************************************************/ -FetchStmt: FETCH opt_direction fetch_how_many opt_portal_name INTO into_list +FetchStmt: FETCH direction fetch_how_many portal_name INTO into_list { if (strcmp($2, "relative") == 0 && atol($3) == 0L) mmerror(ET_ERROR, "FETCH/RELATIVE at current position is not supported"); $$ = cat_str(4, make_str("fetch"), $2, $3, $4); } - | MOVE opt_direction fetch_how_many opt_portal_name + | FETCH fetch_how_many portal_name INTO into_list + { + $$ = cat_str(3, make_str("fetch"), $2, $3); + } + | FETCH portal_name INTO into_list { - $$ = cat_str(4, make_str("fetch"), $2, $3, $4); + $$ = cat_str(2, make_str("fetch"), $2); + } + | MOVE direction fetch_how_many portal_name + { + $$ = cat_str(4, make_str("move"), $2, $3, $4); + } + | MOVE fetch_how_many portal_name + { + $$ = cat_str(3, make_str("move"), $2, $3); + } + | MOVE portal_name + { + $$ = cat_str(2, make_str("move"), $2); } ; -opt_direction: FORWARD { $$ = make_str("forward"); } +direction: FORWARD { $$ = make_str("forward"); } | BACKWARD { $$ = make_str("backward"); } | RELATIVE { $$ = make_str("relative"); } | ABSOLUTE @@ -1891,7 +1922,6 @@ opt_direction: FORWARD { $$ = make_str("forward"); } mmerror(ET_WARN, "FETCH/ABSOLUTE not supported, backend will use RELATIVE"); $$ = make_str("absolute"); } - | /*EMPTY*/ { $$ = EMPTY; /* default */ } ; fetch_how_many: Iconst { $$ = $1; } @@ -1899,13 +1929,11 @@ fetch_how_many: Iconst { $$ = $1; } | ALL { $$ = make_str("all"); } | NEXT { $$ = make_str("next"); } | PRIOR { $$ = make_str("prior"); } - | /*EMPTY*/ { $$ = EMPTY; /*default*/ } ; -opt_portal_name: IN name { $$ = cat2_str(make_str("in"), $2); } +portal_name: IN name { $$ = cat2_str(make_str("in"), $2); } | FROM name { $$ = cat2_str(make_str("from"), $2); } -/* | name { $$ = cat2_str(make_str("in"), $1); */ - | /*EMPTY*/ { $$ = EMPTY; } + | name { $$ = cat2_str(make_str("in"), $1); } ; /***************************************************************************** @@ -4475,7 +4503,6 @@ ECPGDeallocate: SQL_DEALLOCATE SQL_PREPARE ident { $$ = cat_str(3, make_str("ECP ECPGDeclaration: sql_startdeclare { fputs("/* exec sql begin declare section */", yyout); - output_line_number(); } variable_declarations sql_enddeclare { @@ -4488,18 +4515,16 @@ sql_startdeclare : ecpgstart BEGIN_TRANS DECLARE SQL_SECTION ';' {} sql_enddeclare: ecpgstart END_TRANS DECLARE SQL_SECTION ';' {} -variable_declarations: /* empty */ - { - $$ = EMPTY; - } - | declaration variable_declarations - { - $$ = cat2_str($1, $2); - } +variable_declarations: /* empty */ { $$ = EMPTY; } + | declarations { $$ = $1; } + +declarations: declaration { $$ = $1; } + | declarations declaration { $$ = cat2_str($1, $2); } declaration: storage_clause { actual_storage[struct_level] = mm_strdup($1); + actual_startline[struct_level] = hashline_number(); } type { @@ -4509,7 +4534,7 @@ declaration: storage_clause } variable_list ';' { - $$ = cat_str(4, $1, $3.type_str, $5, make_str(";\n")); + $$ = cat_str(5, actual_startline[struct_level], $1, $3.type_str, $5, make_str(";\n")); } storage_clause : S_EXTERN { $$ = make_str("extern"); } @@ -5416,7 +5441,7 @@ c_stuff: c_anything { $$ = $1; } } c_list: c_term { $$ = $1; } - | c_term ',' c_list { $$ = cat_str(3, $1, make_str(","), $3); } + | c_list ',' c_term { $$ = cat_str(3, $1, make_str(","), $3); } c_term: c_stuff { $$ = $1; } | '{' c_list '}' { $$ = cat_str(3, make_str("{"), $2, make_str("}")); } diff --git a/src/interfaces/ecpg/preproc/type.h b/src/interfaces/ecpg/preproc/type.h index 7cdc476e83da4187fd2b0945ac7227aad9ef79df..1af1e63344fb3f711b37f3a557456c6cf1c848ed 100644 --- a/src/interfaces/ecpg/preproc/type.h +++ b/src/interfaces/ecpg/preproc/type.h @@ -119,6 +119,7 @@ struct _defines { char *old; char *new; + int pertinent; struct _defines *next; };