LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/preproc - ecpg.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 138 252 54.8 %
Date: 2024-04-25 23:11:32 Functions: 3 4 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* src/interfaces/ecpg/preproc/ecpg.c */
       2             : 
       3             : /* Main for ecpg, the PostgreSQL embedded SQL precompiler. */
       4             : /* Copyright (c) 1996-2024, PostgreSQL Global Development Group */
       5             : 
       6             : #include "postgres_fe.h"
       7             : 
       8             : #include <unistd.h>
       9             : 
      10             : #include "getopt_long.h"
      11             : 
      12             : #include "preproc_extern.h"
      13             : 
      14             : int         ret_value = 0;
      15             : bool        autocommit = false,
      16             :             auto_create_c = false,
      17             :             system_includes = false,
      18             :             force_indicator = true,
      19             :             questionmarks = false,
      20             :             regression_mode = false,
      21             :             auto_prepare = false;
      22             : 
      23             : char       *output_filename;
      24             : 
      25             : enum COMPAT_MODE compat = ECPG_COMPAT_PGSQL;
      26             : 
      27             : struct _include_path *include_paths = NULL;
      28             : struct cursor *cur = NULL;
      29             : struct typedefs *types = NULL;
      30             : struct _defines *defines = NULL;
      31             : struct declared_list *g_declared_list = NULL;
      32             : 
      33             : static void
      34           0 : help(const char *progname)
      35             : {
      36           0 :     printf(_("%s is the PostgreSQL embedded SQL preprocessor for C programs.\n\n"),
      37             :            progname);
      38           0 :     printf(_("Usage:\n"
      39             :              "  %s [OPTION]... FILE...\n\n"),
      40             :            progname);
      41           0 :     printf(_("Options:\n"));
      42           0 :     printf(_("  -c             automatically generate C code from embedded SQL code;\n"
      43             :              "                 this affects EXEC SQL TYPE\n"));
      44           0 :     printf(_("  -C MODE        set compatibility mode; MODE can be one of\n"
      45             :              "                 \"INFORMIX\", \"INFORMIX_SE\", \"ORACLE\"\n"));
      46             : #ifdef YYDEBUG
      47             :     printf(_("  -d             generate parser debug output\n"));
      48             : #endif
      49           0 :     printf(_("  -D SYMBOL      define SYMBOL\n"));
      50           0 :     printf(_("  -h             parse a header file, this option includes option \"-c\"\n"));
      51           0 :     printf(_("  -i             parse system include files as well\n"));
      52           0 :     printf(_("  -I DIRECTORY   search DIRECTORY for include files\n"));
      53           0 :     printf(_("  -o OUTFILE     write result to OUTFILE\n"));
      54           0 :     printf(_("  -r OPTION      specify run-time behavior; OPTION can be:\n"
      55             :              "                 \"no_indicator\", \"prepare\", \"questionmarks\"\n"));
      56           0 :     printf(_("  --regression   run in regression testing mode\n"));
      57           0 :     printf(_("  -t             turn on autocommit of transactions\n"));
      58           0 :     printf(_("  -V, --version  output version information, then exit\n"));
      59           0 :     printf(_("  -?, --help     show this help, then exit\n"));
      60           0 :     printf(_("\nIf no output file is specified, the name is formed by adding .c to the\n"
      61             :              "input file name, after stripping off .pgc if present.\n"));
      62           0 :     printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
      63           0 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
      64           0 : }
      65             : 
      66             : static void
      67         812 : add_include_path(char *path)
      68             : {
      69         812 :     struct _include_path *ip = include_paths,
      70             :                *new;
      71             : 
      72         812 :     new = mm_alloc(sizeof(struct _include_path));
      73         812 :     new->path = path;
      74         812 :     new->next = NULL;
      75             : 
      76         812 :     if (ip == NULL)
      77         132 :         include_paths = new;
      78             :     else
      79             :     {
      80        2100 :         for (; ip->next != NULL; ip = ip->next);
      81         680 :         ip->next = new;
      82             :     }
      83         812 : }
      84             : 
      85             : /*
      86             :  * Process a command line -D switch
      87             :  */
      88             : static void
      89           2 : add_preprocessor_define(char *define)
      90             : {
      91             :     /* copy the argument to avoid relying on argv storage */
      92           2 :     char       *define_copy = mm_strdup(define);
      93             :     char       *ptr;
      94             :     struct _defines *newdef;
      95             : 
      96           2 :     newdef = mm_alloc(sizeof(struct _defines));
      97             : 
      98             :     /* look for = sign */
      99           2 :     ptr = strchr(define_copy, '=');
     100           2 :     if (ptr != NULL)
     101             :     {
     102             :         /* symbol has a value */
     103             :         char       *tmp;
     104             : 
     105             :         /* strip any spaces between name and '=' */
     106           2 :         for (tmp = ptr - 1; tmp >= define_copy && *tmp == ' '; tmp--);
     107           2 :         tmp[1] = '\0';
     108             : 
     109             :         /*
     110             :          * Note we don't bother to separately malloc cmdvalue; it will never
     111             :          * be freed so that's not necessary.
     112             :          */
     113           2 :         newdef->cmdvalue = ptr + 1;
     114             :     }
     115             :     else
     116             :     {
     117             :         /* define it as "1"; again no need to malloc it */
     118           0 :         newdef->cmdvalue = "1";
     119             :     }
     120           2 :     newdef->name = define_copy;
     121           2 :     newdef->value = mm_strdup(newdef->cmdvalue);
     122           2 :     newdef->used = NULL;
     123           2 :     newdef->next = defines;
     124           2 :     defines = newdef;
     125           2 : }
     126             : 
     127             : #define ECPG_GETOPT_LONG_REGRESSION     1
     128             : int
     129         132 : main(int argc, char *const argv[])
     130             : {
     131             :     static struct option ecpg_options[] = {
     132             :         {"regression", no_argument, NULL, ECPG_GETOPT_LONG_REGRESSION},
     133             :         {NULL, 0, NULL, 0}
     134             :     };
     135             : 
     136             :     int         fnr,
     137             :                 c,
     138         132 :                 out_option = 0;
     139         132 :     bool        verbose = false,
     140         132 :                 header_mode = false;
     141             :     struct _include_path *ip;
     142             :     const char *progname;
     143             :     char        my_exec_path[MAXPGPATH];
     144             :     char        include_path[MAXPGPATH];
     145             : 
     146         132 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("ecpg"));
     147             : 
     148         132 :     progname = get_progname(argv[0]);
     149             : 
     150         132 :     if (find_my_exec(argv[0], my_exec_path) < 0)
     151             :     {
     152           0 :         fprintf(stderr, _("%s: could not locate my own executable path\n"), argv[0]);
     153           0 :         return ILLEGAL_OPTION;
     154             :     }
     155             : 
     156         132 :     if (argc > 1)
     157             :     {
     158         132 :         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
     159             :         {
     160           0 :             help(progname);
     161           0 :             exit(0);
     162             :         }
     163         132 :         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
     164             :         {
     165           0 :             printf("ecpg (PostgreSQL) %s\n", PG_VERSION);
     166           0 :             exit(0);
     167             :         }
     168             :     }
     169             : 
     170         132 :     output_filename = NULL;
     171         696 :     while ((c = getopt_long(argc, argv, "cC:dD:hiI:o:r:tv", ecpg_options, NULL)) != -1)
     172             :     {
     173         564 :         switch (c)
     174             :         {
     175           4 :             case 'c':
     176           4 :                 auto_create_c = true;
     177           4 :                 break;
     178          22 :             case 'C':
     179          22 :                 if (pg_strcasecmp(optarg, "INFORMIX") == 0 || pg_strcasecmp(optarg, "INFORMIX_SE") == 0)
     180          20 :                 {
     181             :                     char        pkginclude_path[MAXPGPATH];
     182             :                     char        informix_path[MAXPGPATH];
     183             : 
     184          20 :                     compat = (pg_strcasecmp(optarg, "INFORMIX") == 0) ? ECPG_COMPAT_INFORMIX : ECPG_COMPAT_INFORMIX_SE;
     185          20 :                     get_pkginclude_path(my_exec_path, pkginclude_path);
     186          20 :                     snprintf(informix_path, MAXPGPATH, "%s/informix/esql", pkginclude_path);
     187          20 :                     add_include_path(informix_path);
     188             :                 }
     189           2 :                 else if (pg_strcasecmp(optarg, "ORACLE") == 0)
     190             :                 {
     191           2 :                     compat = ECPG_COMPAT_ORACLE;
     192             :                 }
     193             :                 else
     194             :                 {
     195           0 :                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
     196           0 :                     return ILLEGAL_OPTION;
     197             :                 }
     198          22 :                 break;
     199           0 :             case 'd':
     200             : #ifdef YYDEBUG
     201             :                 base_yydebug = 1;
     202             : #else
     203           0 :                 fprintf(stderr, _("%s: parser debug support (-d) not available\n"),
     204             :                         progname);
     205             : #endif
     206           0 :                 break;
     207           2 :             case 'D':
     208           2 :                 add_preprocessor_define(optarg);
     209           2 :                 break;
     210           0 :             case 'h':
     211           0 :                 header_mode = true;
     212             :                 /* this must include "-c" to make sense: */
     213           0 :                 auto_create_c = true;
     214           0 :                 break;
     215           2 :             case 'i':
     216           2 :                 system_includes = true;
     217           2 :                 break;
     218         264 :             case 'I':
     219         264 :                 add_include_path(optarg);
     220         264 :                 break;
     221         132 :             case 'o':
     222         132 :                 output_filename = mm_strdup(optarg);
     223         132 :                 if (strcmp(output_filename, "-") == 0)
     224           0 :                     base_yyout = stdout;
     225             :                 else
     226         132 :                     base_yyout = fopen(output_filename, PG_BINARY_W);
     227             : 
     228         132 :                 if (base_yyout == NULL)
     229             :                 {
     230           0 :                     fprintf(stderr, _("%s: could not open file \"%s\": %m\n"),
     231             :                             progname, output_filename);
     232           0 :                     output_filename = NULL;
     233             :                 }
     234             :                 else
     235         132 :                     out_option = 1;
     236         132 :                 break;
     237           6 :             case 'r':
     238           6 :                 if (pg_strcasecmp(optarg, "no_indicator") == 0)
     239           2 :                     force_indicator = false;
     240           4 :                 else if (pg_strcasecmp(optarg, "prepare") == 0)
     241           2 :                     auto_prepare = true;
     242           2 :                 else if (pg_strcasecmp(optarg, "questionmarks") == 0)
     243           2 :                     questionmarks = true;
     244             :                 else
     245             :                 {
     246           0 :                     fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
     247           0 :                     return ILLEGAL_OPTION;
     248             :                 }
     249           6 :                 break;
     250           0 :             case 't':
     251           0 :                 autocommit = true;
     252           0 :                 break;
     253           0 :             case 'v':
     254           0 :                 verbose = true;
     255           0 :                 break;
     256         132 :             case ECPG_GETOPT_LONG_REGRESSION:
     257         132 :                 regression_mode = true;
     258         132 :                 break;
     259           0 :             default:
     260           0 :                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
     261           0 :                 return ILLEGAL_OPTION;
     262             :         }
     263             :     }
     264             : 
     265         132 :     add_include_path(".");
     266         132 :     add_include_path("/usr/local/include");
     267         132 :     get_include_path(my_exec_path, include_path);
     268         132 :     add_include_path(include_path);
     269         132 :     add_include_path("/usr/include");
     270             : 
     271         132 :     if (verbose)
     272             :     {
     273           0 :         fprintf(stderr,
     274           0 :                 _("%s, the PostgreSQL embedded C preprocessor, version %s\n"),
     275             :                 progname, PG_VERSION);
     276           0 :         fprintf(stderr, _("EXEC SQL INCLUDE ... search starts here:\n"));
     277           0 :         for (ip = include_paths; ip != NULL; ip = ip->next)
     278           0 :             fprintf(stderr, " %s\n", ip->path);
     279           0 :         fprintf(stderr, _("end of search list\n"));
     280           0 :         return 0;
     281             :     }
     282             : 
     283         132 :     if (optind >= argc)          /* no files specified */
     284             :     {
     285           0 :         fprintf(stderr, _("%s: no input files specified\n"), progname);
     286           0 :         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
     287           0 :         return ILLEGAL_OPTION;
     288             :     }
     289             :     else
     290             :     {
     291             :         /* after the options there must not be anything but filenames */
     292         266 :         for (fnr = optind; fnr < argc; fnr++)
     293             :         {
     294             :             char       *ptr2ext;
     295             : 
     296             :             /* If argv[fnr] is "-" we have to read from stdin */
     297         134 :             if (strcmp(argv[fnr], "-") == 0)
     298             :             {
     299           0 :                 input_filename = mm_alloc(strlen("stdin") + 1);
     300           0 :                 strcpy(input_filename, "stdin");
     301           0 :                 base_yyin = stdin;
     302             :             }
     303             :             else
     304             :             {
     305         134 :                 input_filename = mm_alloc(strlen(argv[fnr]) + 5);
     306         134 :                 strcpy(input_filename, argv[fnr]);
     307             : 
     308             :                 /* take care of relative paths */
     309         134 :                 ptr2ext = last_dir_separator(input_filename);
     310         134 :                 ptr2ext = (ptr2ext ? strrchr(ptr2ext, '.') : strrchr(input_filename, '.'));
     311             : 
     312             :                 /* no extension? */
     313         134 :                 if (ptr2ext == NULL)
     314             :                 {
     315           0 :                     ptr2ext = input_filename + strlen(input_filename);
     316             : 
     317             :                     /* no extension => add .pgc or .pgh */
     318           0 :                     ptr2ext[0] = '.';
     319           0 :                     ptr2ext[1] = 'p';
     320           0 :                     ptr2ext[2] = 'g';
     321           0 :                     ptr2ext[3] = (header_mode == true) ? 'h' : 'c';
     322           0 :                     ptr2ext[4] = '\0';
     323             :                 }
     324             : 
     325         134 :                 base_yyin = fopen(input_filename, PG_BINARY_R);
     326             :             }
     327             : 
     328         134 :             if (out_option == 0)    /* calculate the output name */
     329             :             {
     330           0 :                 if (strcmp(input_filename, "stdin") == 0)
     331           0 :                     base_yyout = stdout;
     332             :                 else
     333             :                 {
     334           0 :                     output_filename = mm_alloc(strlen(input_filename) + 3);
     335           0 :                     strcpy(output_filename, input_filename);
     336             : 
     337           0 :                     ptr2ext = strrchr(output_filename, '.');
     338             :                     /* make extension = .c resp. .h */
     339           0 :                     ptr2ext[1] = (header_mode == true) ? 'h' : 'c';
     340           0 :                     ptr2ext[2] = '\0';
     341             : 
     342           0 :                     base_yyout = fopen(output_filename, PG_BINARY_W);
     343           0 :                     if (base_yyout == NULL)
     344             :                     {
     345           0 :                         fprintf(stderr, _("%s: could not open file \"%s\": %m\n"),
     346             :                                 progname, output_filename);
     347           0 :                         free(output_filename);
     348           0 :                         output_filename = NULL;
     349           0 :                         free(input_filename);
     350           0 :                         continue;
     351             :                     }
     352             :                 }
     353             :             }
     354             : 
     355         134 :             if (base_yyin == NULL)
     356           0 :                 fprintf(stderr, _("%s: could not open file \"%s\": %m\n"),
     357           0 :                         progname, argv[fnr]);
     358             :             else
     359             :             {
     360             :                 struct cursor *ptr;
     361             :                 struct _defines *defptr;
     362             :                 struct _defines *prevdefptr;
     363             :                 struct _defines *nextdefptr;
     364             :                 struct typedefs *typeptr;
     365             :                 struct declared_list *list;
     366             : 
     367             :                 /* remove old cursor definitions if any are still there */
     368         134 :                 for (ptr = cur; ptr != NULL;)
     369             :                 {
     370           0 :                     struct cursor *this = ptr;
     371             :                     struct arguments *l1,
     372             :                                *l2;
     373             : 
     374           0 :                     free(ptr->command);
     375           0 :                     free(ptr->connection);
     376           0 :                     free(ptr->name);
     377           0 :                     for (l1 = ptr->argsinsert; l1; l1 = l2)
     378             :                     {
     379           0 :                         l2 = l1->next;
     380           0 :                         free(l1);
     381             :                     }
     382           0 :                     for (l1 = ptr->argsresult; l1; l1 = l2)
     383             :                     {
     384           0 :                         l2 = l1->next;
     385           0 :                         free(l1);
     386             :                     }
     387           0 :                     ptr = ptr->next;
     388           0 :                     free(this);
     389             :                 }
     390         134 :                 cur = NULL;
     391             : 
     392             :                 /* remove old declared statements if any are still there */
     393         134 :                 for (list = g_declared_list; list != NULL;)
     394             :                 {
     395           0 :                     struct declared_list *this = list;
     396             : 
     397           0 :                     list = list->next;
     398           0 :                     free(this);
     399             :                 }
     400             : 
     401             :                 /* restore defines to their command-line state */
     402         134 :                 prevdefptr = NULL;
     403         140 :                 for (defptr = defines; defptr != NULL; defptr = nextdefptr)
     404             :                 {
     405           6 :                     nextdefptr = defptr->next;
     406           6 :                     if (defptr->cmdvalue != NULL)
     407             :                     {
     408             :                         /* keep it, resetting the value */
     409           4 :                         free(defptr->value);
     410           4 :                         defptr->value = mm_strdup(defptr->cmdvalue);
     411           4 :                         prevdefptr = defptr;
     412             :                     }
     413             :                     else
     414             :                     {
     415             :                         /* remove it */
     416           2 :                         if (prevdefptr != NULL)
     417           0 :                             prevdefptr->next = nextdefptr;
     418             :                         else
     419           2 :                             defines = nextdefptr;
     420           2 :                         free(defptr->name);
     421           2 :                         free(defptr->value);
     422           2 :                         free(defptr);
     423             :                     }
     424             :                 }
     425             : 
     426             :                 /* and old typedefs */
     427         134 :                 for (typeptr = types; typeptr != NULL;)
     428             :                 {
     429           0 :                     struct typedefs *this = typeptr;
     430             : 
     431           0 :                     free(typeptr->name);
     432           0 :                     ECPGfree_struct_member(typeptr->struct_member_list);
     433           0 :                     free(typeptr->type);
     434           0 :                     typeptr = typeptr->next;
     435           0 :                     free(this);
     436             :                 }
     437         134 :                 types = NULL;
     438             : 
     439             :                 /* initialize whenever structures */
     440         134 :                 memset(&when_error, 0, sizeof(struct when));
     441         134 :                 memset(&when_nf, 0, sizeof(struct when));
     442         134 :                 memset(&when_warn, 0, sizeof(struct when));
     443             : 
     444             :                 /* and structure member lists */
     445         134 :                 memset(struct_member_list, 0, sizeof(struct_member_list));
     446             : 
     447             :                 /*
     448             :                  * and our variable counter for out of scope cursors'
     449             :                  * variables
     450             :                  */
     451         134 :                 ecpg_internal_var = 0;
     452             : 
     453             :                 /* finally the actual connection */
     454         134 :                 connection = NULL;
     455             : 
     456             :                 /* initialize lex */
     457         134 :                 lex_init();
     458             : 
     459             :                 /* we need several includes */
     460             :                 /* but not if we are in header mode */
     461         134 :                 if (regression_mode)
     462         134 :                     fprintf(base_yyout, "/* Processed by ecpg (regression mode) */\n");
     463             :                 else
     464           0 :                     fprintf(base_yyout, "/* Processed by ecpg (%s) */\n", PG_VERSION);
     465             : 
     466         134 :                 if (header_mode == false)
     467             :                 {
     468         134 :                     fprintf(base_yyout, "/* These include files are added by the preprocessor */\n#include <ecpglib.h>\n#include <ecpgerrno.h>\n#include <sqlca.h>\n");
     469             : 
     470             :                     /* add some compatibility headers */
     471         134 :                     if (INFORMIX_MODE)
     472          20 :                         fprintf(base_yyout, "/* Needed for informix compatibility */\n#include <ecpg_informix.h>\n");
     473             : 
     474         134 :                     fprintf(base_yyout, "/* End of automatic include section */\n");
     475             :                 }
     476             : 
     477         134 :                 if (regression_mode)
     478         134 :                     fprintf(base_yyout, "#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))\n");
     479             : 
     480         134 :                 output_line_number();
     481             : 
     482             :                 /* and parse the source */
     483         134 :                 base_yyparse();
     484             : 
     485             :                 /*
     486             :                  * Check whether all cursors were indeed opened.  It does not
     487             :                  * really make sense to declare a cursor but not open it.
     488             :                  */
     489         208 :                 for (ptr = cur; ptr != NULL; ptr = ptr->next)
     490          74 :                     if (!(ptr->opened))
     491           0 :                         mmerror(PARSE_ERROR, ET_WARNING, "cursor \"%s\" has been declared but not opened", ptr->name);
     492             : 
     493         134 :                 if (base_yyin != NULL && base_yyin != stdin)
     494         134 :                     fclose(base_yyin);
     495         134 :                 if (out_option == 0 && base_yyout != stdout)
     496           0 :                     fclose(base_yyout);
     497             : 
     498             :                 /*
     499             :                  * If there was an error, delete the output file.
     500             :                  */
     501         134 :                 if (ret_value != 0)
     502             :                 {
     503           0 :                     if (strcmp(output_filename, "-") != 0 && unlink(output_filename) != 0)
     504           0 :                         fprintf(stderr, _("could not remove output file \"%s\"\n"), output_filename);
     505             :                 }
     506             :             }
     507             : 
     508         134 :             if (output_filename && out_option == 0)
     509             :             {
     510           0 :                 free(output_filename);
     511           0 :                 output_filename = NULL;
     512             :             }
     513             : 
     514         134 :             free(input_filename);
     515             :         }
     516             :     }
     517         132 :     return ret_value;
     518             : }

Generated by: LCOV version 1.14