mirror of
https://github.com/postgres/postgres.git
synced 2025-06-05 23:56:58 +03:00
Back-patch commit 92fb64983 into the pre-9.6 branches. Without this, ecpg fails to build with the latest version of flex. It's not unreasonable that people would want to compile our old branches with recent tools. Per report from Дилян Палаузов. Discussion: https://postgr.es/m/d845c1af-e18d-6651-178f-9f08cdf37e10@aegee.org
480 lines
12 KiB
C
480 lines
12 KiB
C
/* src/interfaces/ecpg/preproc/ecpg.c */
|
|
|
|
/* Main for ecpg, the PostgreSQL embedded SQL precompiler. */
|
|
/* Copyright (c) 1996-2013, PostgreSQL Global Development Group */
|
|
|
|
#include "postgres_fe.h"
|
|
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include "getopt_long.h"
|
|
|
|
#include "extern.h"
|
|
|
|
int ret_value = 0;
|
|
bool autocommit = false,
|
|
auto_create_c = false,
|
|
system_includes = false,
|
|
force_indicator = true,
|
|
questionmarks = false,
|
|
regression_mode = false,
|
|
auto_prepare = false;
|
|
|
|
char *output_filename;
|
|
|
|
enum COMPAT_MODE compat = ECPG_COMPAT_PGSQL;
|
|
|
|
struct _include_path *include_paths = NULL;
|
|
struct cursor *cur = NULL;
|
|
struct typedefs *types = NULL;
|
|
struct _defines *defines = NULL;
|
|
|
|
static void
|
|
help(const char *progname)
|
|
{
|
|
printf(_("%s is the PostgreSQL embedded SQL preprocessor for C programs.\n\n"),
|
|
progname);
|
|
printf(_("Usage:\n"
|
|
" %s [OPTION]... FILE...\n\n"),
|
|
progname);
|
|
printf(_("Options:\n"));
|
|
printf(_(" -c automatically generate C code from embedded SQL code;\n"
|
|
" this affects EXEC SQL TYPE\n"));
|
|
printf(_(" -C MODE set compatibility mode; MODE can be one of\n"
|
|
" \"INFORMIX\", \"INFORMIX_SE\"\n"));
|
|
#ifdef YYDEBUG
|
|
printf(_(" -d generate parser debug output\n"));
|
|
#endif
|
|
printf(_(" -D SYMBOL define SYMBOL\n"));
|
|
printf(_(" -h parse a header file, this option includes option \"-c\"\n"));
|
|
printf(_(" -i parse system include files as well\n"));
|
|
printf(_(" -I DIRECTORY search DIRECTORY for include files\n"));
|
|
printf(_(" -o OUTFILE write result to OUTFILE\n"));
|
|
printf(_(" -r OPTION specify run-time behavior; OPTION can be:\n"
|
|
" \"no_indicator\", \"prepare\", \"questionmarks\"\n"));
|
|
printf(_(" --regression run in regression testing mode\n"));
|
|
printf(_(" -t turn on autocommit of transactions\n"));
|
|
printf(_(" -V, --version output version information, then exit\n"));
|
|
printf(_(" -?, --help show this help, then exit\n"));
|
|
printf(_("\nIf no output file is specified, the name is formed by adding .c to the\n"
|
|
"input file name, after stripping off .pgc if present.\n"));
|
|
printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
|
|
}
|
|
|
|
static void
|
|
add_include_path(char *path)
|
|
{
|
|
struct _include_path *ip = include_paths,
|
|
*new;
|
|
|
|
new = mm_alloc(sizeof(struct _include_path));
|
|
new->path = path;
|
|
new->next = NULL;
|
|
|
|
if (ip == NULL)
|
|
include_paths = new;
|
|
else
|
|
{
|
|
for (; ip->next != NULL; ip = ip->next);
|
|
ip->next = new;
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_preprocessor_define(char *define)
|
|
{
|
|
struct _defines *pd = defines;
|
|
char *ptr,
|
|
*define_copy = mm_strdup(define);
|
|
|
|
defines = mm_alloc(sizeof(struct _defines));
|
|
|
|
/* look for = sign */
|
|
ptr = strchr(define_copy, '=');
|
|
if (ptr != NULL)
|
|
{
|
|
char *tmp;
|
|
|
|
/* symbol has a value */
|
|
for (tmp = ptr - 1; *tmp == ' '; tmp--);
|
|
tmp[1] = '\0';
|
|
defines->old = define_copy;
|
|
defines->new = ptr + 1;
|
|
}
|
|
else
|
|
{
|
|
defines->old = define_copy;
|
|
defines->new = mm_strdup("1");
|
|
}
|
|
defines->pertinent = true;
|
|
defines->used = NULL;
|
|
defines->next = pd;
|
|
}
|
|
|
|
#define ECPG_GETOPT_LONG_REGRESSION 1
|
|
int
|
|
main(int argc, char *const argv[])
|
|
{
|
|
static struct option ecpg_options[] = {
|
|
{"regression", no_argument, NULL, ECPG_GETOPT_LONG_REGRESSION},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
int fnr,
|
|
c,
|
|
out_option = 0;
|
|
bool verbose = false,
|
|
header_mode = false;
|
|
struct _include_path *ip;
|
|
const char *progname;
|
|
char my_exec_path[MAXPGPATH];
|
|
char include_path[MAXPGPATH];
|
|
|
|
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("ecpg"));
|
|
|
|
progname = get_progname(argv[0]);
|
|
|
|
find_my_exec(argv[0], my_exec_path);
|
|
|
|
if (argc > 1)
|
|
{
|
|
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
|
|
{
|
|
help(progname);
|
|
exit(0);
|
|
}
|
|
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
|
|
{
|
|
printf("ecpg (PostgreSQL %s) %d.%d.%d\n", PG_VERSION,
|
|
MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
output_filename = NULL;
|
|
while ((c = getopt_long(argc, argv, "vcio:I:tD:dC:r:h", ecpg_options, NULL)) != -1)
|
|
{
|
|
switch (c)
|
|
{
|
|
case ECPG_GETOPT_LONG_REGRESSION:
|
|
regression_mode = true;
|
|
break;
|
|
case 'o':
|
|
output_filename = strdup(optarg);
|
|
if (strcmp(output_filename, "-") == 0)
|
|
base_yyout = stdout;
|
|
else
|
|
base_yyout = fopen(output_filename, PG_BINARY_W);
|
|
|
|
if (base_yyout == NULL)
|
|
{
|
|
fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
|
|
progname, output_filename, strerror(errno));
|
|
output_filename = NULL;
|
|
}
|
|
else
|
|
out_option = 1;
|
|
break;
|
|
case 'I':
|
|
add_include_path(optarg);
|
|
break;
|
|
case 't':
|
|
autocommit = true;
|
|
break;
|
|
case 'v':
|
|
verbose = true;
|
|
break;
|
|
case 'h':
|
|
header_mode = true;
|
|
/* this must include "-c" to make sense */
|
|
/* so do not place a "break;" here */
|
|
case 'c':
|
|
auto_create_c = true;
|
|
break;
|
|
case 'i':
|
|
system_includes = true;
|
|
break;
|
|
case 'C':
|
|
if (strncmp(optarg, "INFORMIX", strlen("INFORMIX")) == 0)
|
|
{
|
|
char pkginclude_path[MAXPGPATH];
|
|
char informix_path[MAXPGPATH];
|
|
|
|
compat = (strcmp(optarg, "INFORMIX") == 0) ? ECPG_COMPAT_INFORMIX : ECPG_COMPAT_INFORMIX_SE;
|
|
get_pkginclude_path(my_exec_path, pkginclude_path);
|
|
snprintf(informix_path, MAXPGPATH, "%s/informix/esql", pkginclude_path);
|
|
add_include_path(informix_path);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
|
|
return ILLEGAL_OPTION;
|
|
}
|
|
break;
|
|
case 'r':
|
|
if (strcmp(optarg, "no_indicator") == 0)
|
|
force_indicator = false;
|
|
else if (strcmp(optarg, "prepare") == 0)
|
|
auto_prepare = true;
|
|
else if (strcmp(optarg, "questionmarks") == 0)
|
|
questionmarks = true;
|
|
else
|
|
{
|
|
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
|
|
return ILLEGAL_OPTION;
|
|
}
|
|
break;
|
|
case 'D':
|
|
add_preprocessor_define(optarg);
|
|
break;
|
|
case 'd':
|
|
#ifdef YYDEBUG
|
|
base_yydebug = 1;
|
|
#else
|
|
fprintf(stderr, _("%s: parser debug support (-d) not available\n"),
|
|
progname);
|
|
#endif
|
|
break;
|
|
default:
|
|
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
|
|
return ILLEGAL_OPTION;
|
|
}
|
|
}
|
|
|
|
add_include_path(".");
|
|
add_include_path("/usr/local/include");
|
|
get_include_path(my_exec_path, include_path);
|
|
add_include_path(include_path);
|
|
add_include_path("/usr/include");
|
|
|
|
if (verbose)
|
|
{
|
|
fprintf(stderr, _("%s, the PostgreSQL embedded C preprocessor, version %d.%d.%d\n"),
|
|
progname, MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL);
|
|
fprintf(stderr, _("EXEC SQL INCLUDE ... search starts here:\n"));
|
|
for (ip = include_paths; ip != NULL; ip = ip->next)
|
|
fprintf(stderr, " %s\n", ip->path);
|
|
fprintf(stderr, _("end of search list\n"));
|
|
return 0;
|
|
}
|
|
|
|
if (optind >= argc) /* no files specified */
|
|
{
|
|
fprintf(stderr, _("%s: no input files specified\n"), progname);
|
|
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
|
|
return (ILLEGAL_OPTION);
|
|
}
|
|
else
|
|
{
|
|
/* after the options there must not be anything but filenames */
|
|
for (fnr = optind; fnr < argc; fnr++)
|
|
{
|
|
char *ptr2ext;
|
|
|
|
/* If argv[fnr] is "-" we have to read from stdin */
|
|
if (strcmp(argv[fnr], "-") == 0)
|
|
{
|
|
input_filename = mm_alloc(strlen("stdin") + 1);
|
|
strcpy(input_filename, "stdin");
|
|
base_yyin = stdin;
|
|
}
|
|
else
|
|
{
|
|
input_filename = mm_alloc(strlen(argv[fnr]) + 5);
|
|
strcpy(input_filename, argv[fnr]);
|
|
|
|
/* take care of relative paths */
|
|
ptr2ext = last_dir_separator(input_filename);
|
|
ptr2ext = (ptr2ext ? strrchr(ptr2ext, '.') : strrchr(input_filename, '.'));
|
|
|
|
/* no extension? */
|
|
if (ptr2ext == NULL)
|
|
{
|
|
ptr2ext = input_filename + strlen(input_filename);
|
|
|
|
/* no extension => add .pgc or .pgh */
|
|
ptr2ext[0] = '.';
|
|
ptr2ext[1] = 'p';
|
|
ptr2ext[2] = 'g';
|
|
ptr2ext[3] = (header_mode == true) ? 'h' : 'c';
|
|
ptr2ext[4] = '\0';
|
|
}
|
|
|
|
base_yyin = fopen(input_filename, PG_BINARY_R);
|
|
}
|
|
|
|
if (out_option == 0) /* calculate the output name */
|
|
{
|
|
if (strcmp(input_filename, "stdin") == 0)
|
|
base_yyout = stdout;
|
|
else
|
|
{
|
|
output_filename = strdup(input_filename);
|
|
|
|
ptr2ext = strrchr(output_filename, '.');
|
|
/* make extension = .c resp. .h */
|
|
ptr2ext[1] = (header_mode == true) ? 'h' : 'c';
|
|
ptr2ext[2] = '\0';
|
|
|
|
base_yyout = fopen(output_filename, PG_BINARY_W);
|
|
if (base_yyout == NULL)
|
|
{
|
|
fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
|
|
progname, output_filename, strerror(errno));
|
|
free(output_filename);
|
|
free(input_filename);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (base_yyin == NULL)
|
|
fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
|
|
progname, argv[fnr], strerror(errno));
|
|
else
|
|
{
|
|
struct cursor *ptr;
|
|
struct _defines *defptr;
|
|
struct typedefs *typeptr;
|
|
|
|
/* remove old cursor definitions if any are still there */
|
|
for (ptr = cur; ptr != NULL;)
|
|
{
|
|
struct cursor *this = ptr;
|
|
struct arguments *l1,
|
|
*l2;
|
|
|
|
free(ptr->command);
|
|
free(ptr->connection);
|
|
free(ptr->name);
|
|
for (l1 = ptr->argsinsert; l1; l1 = l2)
|
|
{
|
|
l2 = l1->next;
|
|
free(l1);
|
|
}
|
|
for (l1 = ptr->argsresult; l1; l1 = l2)
|
|
{
|
|
l2 = l1->next;
|
|
free(l1);
|
|
}
|
|
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);
|
|
}
|
|
|
|
for (defptr = defines; defptr != NULL; defptr = defptr->next)
|
|
{
|
|
struct _defines *this = defptr->next;
|
|
|
|
if (this && !this->pertinent)
|
|
{
|
|
defptr->next = this->next;
|
|
|
|
free(this->new);
|
|
free(this->old);
|
|
free(this);
|
|
}
|
|
}
|
|
|
|
/* and old typedefs */
|
|
for (typeptr = types; typeptr != NULL;)
|
|
{
|
|
struct typedefs *this = typeptr;
|
|
|
|
free(typeptr->name);
|
|
ECPGfree_struct_member(typeptr->struct_member_list);
|
|
free(typeptr->type);
|
|
typeptr = typeptr->next;
|
|
free(this);
|
|
}
|
|
types = NULL;
|
|
|
|
/* initialize whenever structures */
|
|
memset(&when_error, 0, sizeof(struct when));
|
|
memset(&when_nf, 0, sizeof(struct when));
|
|
memset(&when_warn, 0, sizeof(struct when));
|
|
|
|
/* and structure member lists */
|
|
memset(struct_member_list, 0, sizeof(struct_member_list));
|
|
|
|
/*
|
|
* and our variable counter for out of scope cursors'
|
|
* variables
|
|
*/
|
|
ecpg_internal_var = 0;
|
|
|
|
/* finally the actual connection */
|
|
connection = NULL;
|
|
|
|
/* initialize lex */
|
|
lex_init();
|
|
|
|
/* we need several includes */
|
|
/* but not if we are in header mode */
|
|
if (regression_mode)
|
|
fprintf(base_yyout, "/* Processed by ecpg (regression mode) */\n");
|
|
else
|
|
fprintf(base_yyout, "/* Processed by ecpg (%d.%d.%d) */\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL);
|
|
|
|
if (header_mode == false)
|
|
{
|
|
fprintf(base_yyout, "/* These include files are added by the preprocessor */\n#include <ecpglib.h>\n#include <ecpgerrno.h>\n#include <sqlca.h>\n");
|
|
|
|
/* add some compatibility headers */
|
|
if (INFORMIX_MODE)
|
|
fprintf(base_yyout, "/* Needed for informix compatibility */\n#include <ecpg_informix.h>\n");
|
|
|
|
fprintf(base_yyout, "/* End of automatic include section */\n");
|
|
}
|
|
|
|
if (regression_mode)
|
|
fprintf(base_yyout, "#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))\n");
|
|
|
|
output_line_number();
|
|
|
|
/* and parse the source */
|
|
base_yyparse();
|
|
|
|
/*
|
|
* Check whether all cursors were indeed opened. It does not
|
|
* really make sense to declare a cursor but not open it.
|
|
*/
|
|
for (ptr = cur; ptr != NULL; ptr = ptr->next)
|
|
if (!(ptr->opened))
|
|
mmerror(PARSE_ERROR, ET_WARNING, "cursor \"%s\" has been declared but not opened", ptr->name);
|
|
|
|
if (base_yyin != NULL && base_yyin != stdin)
|
|
fclose(base_yyin);
|
|
if (out_option == 0 && base_yyout != stdout)
|
|
fclose(base_yyout);
|
|
|
|
/*
|
|
* If there was an error, delete the output file.
|
|
*/
|
|
if (ret_value != 0)
|
|
{
|
|
if (strcmp(output_filename, "-") != 0 && unlink(output_filename) != 0)
|
|
fprintf(stderr, _("could not remove output file \"%s\"\n"), output_filename);
|
|
}
|
|
}
|
|
|
|
if (output_filename && out_option == 0)
|
|
free(output_filename);
|
|
|
|
free(input_filename);
|
|
}
|
|
}
|
|
return ret_value;
|
|
}
|