Header And Logo

PostgreSQL
| The world's most advanced open source database.

ecpg.c

Go to the documentation of this file.
00001 /* src/interfaces/ecpg/preproc/ecpg.c */
00002 
00003 /* Main for ecpg, the PostgreSQL embedded SQL precompiler. */
00004 /* Copyright (c) 1996-2013, PostgreSQL Global Development Group */
00005 
00006 #include "postgres_fe.h"
00007 
00008 #include <unistd.h>
00009 #include <string.h>
00010 #include "getopt_long.h"
00011 
00012 #include "extern.h"
00013 
00014 int         ret_value = 0;
00015 bool        autocommit = false,
00016             auto_create_c = false,
00017             system_includes = false,
00018             force_indicator = true,
00019             questionmarks = false,
00020             regression_mode = false,
00021             auto_prepare = false;
00022 
00023 char       *output_filename;
00024 
00025 enum COMPAT_MODE compat = ECPG_COMPAT_PGSQL;
00026 
00027 struct _include_path *include_paths = NULL;
00028 struct cursor *cur = NULL;
00029 struct typedefs *types = NULL;
00030 struct _defines *defines = NULL;
00031 
00032 static void
00033 help(const char *progname)
00034 {
00035     printf(_("%s is the PostgreSQL embedded SQL preprocessor for C programs.\n\n"),
00036            progname);
00037     printf(_("Usage:\n"
00038              "  %s [OPTION]... FILE...\n\n"),
00039            progname);
00040     printf(_("Options:\n"));
00041     printf(_("  -c             automatically generate C code from embedded SQL code;\n"
00042              "                 this affects EXEC SQL TYPE\n"));
00043     printf(_("  -C MODE        set compatibility mode; MODE can be one of\n"
00044              "                 \"INFORMIX\", \"INFORMIX_SE\"\n"));
00045 #ifdef YYDEBUG
00046     printf(_("  -d             generate parser debug output\n"));
00047 #endif
00048     printf(_("  -D SYMBOL      define SYMBOL\n"));
00049     printf(_("  -h             parse a header file, this option includes option \"-c\"\n"));
00050     printf(_("  -i             parse system include files as well\n"));
00051     printf(_("  -I DIRECTORY   search DIRECTORY for include files\n"));
00052     printf(_("  -o OUTFILE     write result to OUTFILE\n"));
00053     printf(_("  -r OPTION      specify run-time behavior; OPTION can be:\n"
00054      "                 \"no_indicator\", \"prepare\", \"questionmarks\"\n"));
00055     printf(_("  --regression   run in regression testing mode\n"));
00056     printf(_("  -t             turn on autocommit of transactions\n"));
00057     printf(_("  --version      output version information, then exit\n"));
00058     printf(_("  -?, --help     show this help, then exit\n"));
00059     printf(_("\nIf no output file is specified, the name is formed by adding .c to the\n"
00060              "input file name, after stripping off .pgc if present.\n"));
00061     printf(_("\nReport bugs to <[email protected]>.\n"));
00062 }
00063 
00064 static void
00065 add_include_path(char *path)
00066 {
00067     struct _include_path *ip = include_paths,
00068                *new;
00069 
00070     new = mm_alloc(sizeof(struct _include_path));
00071     new->path = path;
00072     new->next = NULL;
00073 
00074     if (ip == NULL)
00075         include_paths = new;
00076     else
00077     {
00078         for (; ip->next != NULL; ip = ip->next);
00079         ip->next = new;
00080     }
00081 }
00082 
00083 static void
00084 add_preprocessor_define(char *define)
00085 {
00086     struct _defines *pd = defines;
00087     char       *ptr,
00088                *define_copy = mm_strdup(define);
00089 
00090     defines = mm_alloc(sizeof(struct _defines));
00091 
00092     /* look for = sign */
00093     ptr = strchr(define_copy, '=');
00094     if (ptr != NULL)
00095     {
00096         char       *tmp;
00097 
00098         /* symbol has a value */
00099         for (tmp = ptr - 1; *tmp == ' '; tmp--);
00100         tmp[1] = '\0';
00101         defines->old = define_copy;
00102         defines->new = ptr + 1;
00103     }
00104     else
00105     {
00106         defines->old = define_copy;
00107         defines->new = mm_strdup("1");
00108     }
00109     defines->pertinent = true;
00110     defines->used = NULL;
00111     defines->next = pd;
00112 }
00113 
00114 #define ECPG_GETOPT_LONG_HELP           1
00115 #define ECPG_GETOPT_LONG_VERSION        2
00116 #define ECPG_GETOPT_LONG_REGRESSION     3
00117 int
00118 main(int argc, char *const argv[])
00119 {
00120     static struct option ecpg_options[] = {
00121         {"help", no_argument, NULL, ECPG_GETOPT_LONG_HELP},
00122         {"version", no_argument, NULL, ECPG_GETOPT_LONG_VERSION},
00123         {"regression", no_argument, NULL, ECPG_GETOPT_LONG_REGRESSION},
00124         {NULL, 0, NULL, 0}
00125     };
00126 
00127     int         fnr,
00128                 c,
00129                 out_option = 0;
00130     bool        verbose = false,
00131                 header_mode = false;
00132     struct _include_path *ip;
00133     const char *progname;
00134     char        my_exec_path[MAXPGPATH];
00135     char        include_path[MAXPGPATH];
00136 
00137     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("ecpg"));
00138 
00139     progname = get_progname(argv[0]);
00140 
00141     find_my_exec(argv[0], my_exec_path);
00142 
00143     output_filename = NULL;
00144     while ((c = getopt_long(argc, argv, "vcio:I:tD:dC:r:h?", ecpg_options, NULL)) != -1)
00145     {
00146         switch (c)
00147         {
00148             case ECPG_GETOPT_LONG_VERSION:
00149                 printf("ecpg (PostgreSQL %s) %d.%d.%d\n", PG_VERSION,
00150                        MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL);
00151                 exit(0);
00152             case ECPG_GETOPT_LONG_HELP:
00153                 help(progname);
00154                 exit(0);
00155 
00156                 /*
00157                  * -? is an alternative spelling of --help. However it is also
00158                  * returned by getopt_long for unknown options. We can
00159                  * distinguish both cases by means of the optopt variable
00160                  * which is set to 0 if it was really -? and not an unknown
00161                  * option character.
00162                  */
00163             case '?':
00164                 if (optopt == 0)
00165                 {
00166                     help(progname);
00167                     exit(0);
00168                 }
00169                 break;
00170             case ECPG_GETOPT_LONG_REGRESSION:
00171                 regression_mode = true;
00172                 break;
00173             case 'o':
00174                 output_filename = strdup(optarg);
00175                 if (strcmp(output_filename, "-") == 0)
00176                     yyout = stdout;
00177                 else
00178                     yyout = fopen(output_filename, PG_BINARY_W);
00179 
00180                 if (yyout == NULL)
00181                 {
00182                     fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
00183                             progname, output_filename, strerror(errno));
00184                     output_filename = NULL;
00185                 }
00186                 else
00187                     out_option = 1;
00188                 break;
00189             case 'I':
00190                 add_include_path(optarg);
00191                 break;
00192             case 't':
00193                 autocommit = true;
00194                 break;
00195             case 'v':
00196                 verbose = true;
00197                 break;
00198             case 'h':
00199                 header_mode = true;
00200                 /* this must include "-c" to make sense */
00201                 /* so do not place a "break;" here */
00202             case 'c':
00203                 auto_create_c = true;
00204                 break;
00205             case 'i':
00206                 system_includes = true;
00207                 break;
00208             case 'C':
00209                 if (strncmp(optarg, "INFORMIX", strlen("INFORMIX")) == 0)
00210                 {
00211                     char        pkginclude_path[MAXPGPATH];
00212                     char        informix_path[MAXPGPATH];
00213 
00214                     compat = (strcmp(optarg, "INFORMIX") == 0) ? ECPG_COMPAT_INFORMIX : ECPG_COMPAT_INFORMIX_SE;
00215                     get_pkginclude_path(my_exec_path, pkginclude_path);
00216                     snprintf(informix_path, MAXPGPATH, "%s/informix/esql", pkginclude_path);
00217                     add_include_path(informix_path);
00218                 }
00219                 else
00220                 {
00221                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
00222                     return ILLEGAL_OPTION;
00223                 }
00224                 break;
00225             case 'r':
00226                 if (strcmp(optarg, "no_indicator") == 0)
00227                     force_indicator = false;
00228                 else if (strcmp(optarg, "prepare") == 0)
00229                     auto_prepare = true;
00230                 else if (strcmp(optarg, "questionmarks") == 0)
00231                     questionmarks = true;
00232                 else
00233                 {
00234                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
00235                     return ILLEGAL_OPTION;
00236                 }
00237                 break;
00238             case 'D':
00239                 add_preprocessor_define(optarg);
00240                 break;
00241             case 'd':
00242 #ifdef YYDEBUG
00243                 yydebug = 1;
00244 #else
00245                 fprintf(stderr, _("%s: parser debug support (-d) not available\n"),
00246                         progname);
00247 #endif
00248                 break;
00249             default:
00250                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
00251                 return ILLEGAL_OPTION;
00252         }
00253     }
00254 
00255     add_include_path(".");
00256     add_include_path("/usr/local/include");
00257     get_include_path(my_exec_path, include_path);
00258     add_include_path(include_path);
00259     add_include_path("/usr/include");
00260 
00261     if (verbose)
00262     {
00263         fprintf(stderr, _("%s, the PostgreSQL embedded C preprocessor, version %d.%d.%d\n"),
00264                 progname, MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL);
00265         fprintf(stderr, _("EXEC SQL INCLUDE ... search starts here:\n"));
00266         for (ip = include_paths; ip != NULL; ip = ip->next)
00267             fprintf(stderr, " %s\n", ip->path);
00268         fprintf(stderr, _("end of search list\n"));
00269         return 0;
00270     }
00271 
00272     if (optind >= argc)         /* no files specified */
00273     {
00274         fprintf(stderr, _("%s: no input files specified\n"), progname);
00275         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
00276         return (ILLEGAL_OPTION);
00277     }
00278     else
00279     {
00280         /* after the options there must not be anything but filenames */
00281         for (fnr = optind; fnr < argc; fnr++)
00282         {
00283             char       *ptr2ext;
00284 
00285             /* If argv[fnr] is "-" we have to read from stdin */
00286             if (strcmp(argv[fnr], "-") == 0)
00287             {
00288                 input_filename = mm_alloc(strlen("stdin") + 1);
00289                 strcpy(input_filename, "stdin");
00290                 yyin = stdin;
00291             }
00292             else
00293             {
00294                 input_filename = mm_alloc(strlen(argv[fnr]) + 5);
00295                 strcpy(input_filename, argv[fnr]);
00296 
00297                 /* take care of relative paths */
00298                 ptr2ext = last_dir_separator(input_filename);
00299                 ptr2ext = (ptr2ext ? strrchr(ptr2ext, '.') : strrchr(input_filename, '.'));
00300 
00301                 /* no extension? */
00302                 if (ptr2ext == NULL)
00303                 {
00304                     ptr2ext = input_filename + strlen(input_filename);
00305 
00306                     /* no extension => add .pgc or .pgh */
00307                     ptr2ext[0] = '.';
00308                     ptr2ext[1] = 'p';
00309                     ptr2ext[2] = 'g';
00310                     ptr2ext[3] = (header_mode == true) ? 'h' : 'c';
00311                     ptr2ext[4] = '\0';
00312                 }
00313 
00314                 yyin = fopen(input_filename, PG_BINARY_R);
00315             }
00316 
00317             if (out_option == 0)    /* calculate the output name */
00318             {
00319                 if (strcmp(input_filename, "stdin") == 0)
00320                     yyout = stdout;
00321                 else
00322                 {
00323                     output_filename = strdup(input_filename);
00324 
00325                     ptr2ext = strrchr(output_filename, '.');
00326                     /* make extension = .c resp. .h */
00327                     ptr2ext[1] = (header_mode == true) ? 'h' : 'c';
00328                     ptr2ext[2] = '\0';
00329 
00330                     yyout = fopen(output_filename, PG_BINARY_W);
00331                     if (yyout == NULL)
00332                     {
00333                         fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
00334                                 progname, output_filename, strerror(errno));
00335                         free(output_filename);
00336                         free(input_filename);
00337                         continue;
00338                     }
00339                 }
00340             }
00341 
00342             if (yyin == NULL)
00343                 fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
00344                         progname, argv[fnr], strerror(errno));
00345             else
00346             {
00347                 struct cursor *ptr;
00348                 struct _defines *defptr;
00349                 struct typedefs *typeptr;
00350 
00351                 /* remove old cursor definitions if any are still there */
00352                 for (ptr = cur; ptr != NULL;)
00353                 {
00354                     struct cursor *this = ptr;
00355                     struct arguments *l1,
00356                                *l2;
00357 
00358                     free(ptr->command);
00359                     free(ptr->connection);
00360                     free(ptr->name);
00361                     for (l1 = ptr->argsinsert; l1; l1 = l2)
00362                     {
00363                         l2 = l1->next;
00364                         free(l1);
00365                     }
00366                     for (l1 = ptr->argsresult; l1; l1 = l2)
00367                     {
00368                         l2 = l1->next;
00369                         free(l1);
00370                     }
00371                     ptr = ptr->next;
00372                     free(this);
00373                 }
00374                 cur = NULL;
00375 
00376                 /* remove non-pertinent old defines as well */
00377                 while (defines && !defines->pertinent)
00378                 {
00379                     defptr = defines;
00380                     defines = defines->next;
00381 
00382                     free(defptr->new);
00383                     free(defptr->old);
00384                     free(defptr);
00385                 }
00386 
00387                 for (defptr = defines; defptr != NULL; defptr = defptr->next)
00388                 {
00389                     struct _defines *this = defptr->next;
00390 
00391                     if (this && !this->pertinent)
00392                     {
00393                         defptr->next = this->next;
00394 
00395                         free(this->new);
00396                         free(this->old);
00397                         free(this);
00398                     }
00399                 }
00400 
00401                 /* and old typedefs */
00402                 for (typeptr = types; typeptr != NULL;)
00403                 {
00404                     struct typedefs *this = typeptr;
00405 
00406                     free(typeptr->name);
00407                     ECPGfree_struct_member(typeptr->struct_member_list);
00408                     free(typeptr->type);
00409                     typeptr = typeptr->next;
00410                     free(this);
00411                 }
00412                 types = NULL;
00413 
00414                 /* initialize whenever structures */
00415                 memset(&when_error, 0, sizeof(struct when));
00416                 memset(&when_nf, 0, sizeof(struct when));
00417                 memset(&when_warn, 0, sizeof(struct when));
00418 
00419                 /* and structure member lists */
00420                 memset(struct_member_list, 0, sizeof(struct_member_list));
00421 
00422                 /*
00423                  * and our variable counter for out of scope cursors'
00424                  * variables
00425                  */
00426                 ecpg_internal_var = 0;
00427 
00428                 /* finally the actual connection */
00429                 connection = NULL;
00430 
00431                 /* initialize lex */
00432                 lex_init();
00433 
00434                 /* we need several includes */
00435                 /* but not if we are in header mode */
00436                 if (regression_mode)
00437                     fprintf(yyout, "/* Processed by ecpg (regression mode) */\n");
00438                 else
00439                     fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL);
00440 
00441                 if (header_mode == false)
00442                 {
00443                     fprintf(yyout, "/* These include files are added by the preprocessor */\n#include <ecpglib.h>\n#include <ecpgerrno.h>\n#include <sqlca.h>\n");
00444 
00445                     /* add some compatibility headers */
00446                     if (INFORMIX_MODE)
00447                         fprintf(yyout, "/* Needed for informix compatibility */\n#include <ecpg_informix.h>\n");
00448 
00449                     fprintf(yyout, "/* End of automatic include section */\n");
00450                 }
00451 
00452                 if (regression_mode)
00453                     fprintf(yyout, "#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))\n");
00454 
00455                 output_line_number();
00456 
00457                 /* and parse the source */
00458                 base_yyparse();
00459 
00460                 /*
00461                  * Check whether all cursors were indeed opened.  It does not
00462                  * really make sense to declare a cursor but not open it.
00463                  */
00464                 for (ptr = cur; ptr != NULL; ptr = ptr->next)
00465                     if (!(ptr->opened))
00466                         mmerror(PARSE_ERROR, ET_WARNING, "cursor \"%s\" has been declared but not opened", ptr->name);
00467 
00468                 if (yyin != NULL && yyin != stdin)
00469                     fclose(yyin);
00470                 if (out_option == 0 && yyout != stdout)
00471                     fclose(yyout);
00472 
00473                 /*
00474                  * If there was an error, delete the output file.
00475                  */
00476                 if (ret_value != 0)
00477                 {
00478                     if (strcmp(output_filename, "-") != 0 && unlink(output_filename) != 0)
00479                         fprintf(stderr, _("could not remove output file \"%s\"\n"), output_filename);
00480                 }
00481             }
00482 
00483             if (output_filename && out_option == 0)
00484                 free(output_filename);
00485 
00486             free(input_filename);
00487         }
00488     }
00489     return ret_value;
00490 }