1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-05 23:56:58 +03:00
Tom Lane 2bdee07abe Use "%option prefix" to set API names in ecpg's lexer.
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
2016-12-11 18:04:28 -05:00

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;
}