mirror of
https://github.com/postgres/postgres.git
synced 2025-05-11 05:41:32 +03:00
This makes the -? and -V options work consistently with other binaries. --help and --version are now only recognized as the first option, i.e. "ecpg --foobar --help" no longer prints the help, but that's consistent with most of our other binaries, too. Backpatch to all supported versions. Haribabu Kommi Discussion: <CAJrrPGfnRXvmCzxq6Dy=stAWebfNHxiL+Y_z7uqksZUCkW_waQ@mail.gmail.com>
484 lines
12 KiB
C
484 lines
12 KiB
C
/* src/interfaces/ecpg/preproc/ecpg.c */
|
|
|
|
/* Main for ecpg, the PostgreSQL embedded SQL precompiler. */
|
|
/* Copyright (c) 1996-2014, 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]);
|
|
|
|
if (find_my_exec(argv[0], my_exec_path) < 0)
|
|
{
|
|
fprintf(stderr, _("%s: could not locate my own executable path\n"), argv[0]);
|
|
return (ILLEGAL_OPTION);
|
|
}
|
|
|
|
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 = mm_strdup(optarg);
|
|
if (strcmp(output_filename, "-") == 0)
|
|
yyout = stdout;
|
|
else
|
|
yyout = fopen(output_filename, PG_BINARY_W);
|
|
|
|
if (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
|
|
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");
|
|
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';
|
|
}
|
|
|
|
yyin = fopen(input_filename, PG_BINARY_R);
|
|
}
|
|
|
|
if (out_option == 0) /* calculate the output name */
|
|
{
|
|
if (strcmp(input_filename, "stdin") == 0)
|
|
yyout = stdout;
|
|
else
|
|
{
|
|
output_filename = mm_strdup(input_filename);
|
|
|
|
ptr2ext = strrchr(output_filename, '.');
|
|
/* make extension = .c resp. .h */
|
|
ptr2ext[1] = (header_mode == true) ? 'h' : 'c';
|
|
ptr2ext[2] = '\0';
|
|
|
|
yyout = fopen(output_filename, PG_BINARY_W);
|
|
if (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 (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(yyout, "/* Processed by ecpg (regression mode) */\n");
|
|
else
|
|
fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL);
|
|
|
|
if (header_mode == false)
|
|
{
|
|
fprintf(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(yyout, "/* Needed for informix compatibility */\n#include <ecpg_informix.h>\n");
|
|
|
|
fprintf(yyout, "/* End of automatic include section */\n");
|
|
}
|
|
|
|
if (regression_mode)
|
|
fprintf(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 (yyin != NULL && yyin != stdin)
|
|
fclose(yyin);
|
|
if (out_option == 0 && yyout != stdout)
|
|
fclose(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;
|
|
}
|