LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/preproc - ecpg.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 72.2 % 252 182
Test Date: 2026-03-03 13:15:30 Functions: 100.0 % 4 4
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-2026, 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              : static const char *progname;
      24              : char       *output_filename;
      25              : 
      26              : enum COMPAT_MODE compat = ECPG_COMPAT_PGSQL;
      27              : 
      28              : struct _include_path *include_paths = NULL;
      29              : struct cursor *cur = NULL;
      30              : struct typedefs *types = NULL;
      31              : struct _defines *defines = NULL;
      32              : struct declared_list *g_declared_list = NULL;
      33              : 
      34              : static void
      35            1 : help(const char *progname)
      36              : {
      37            1 :     printf(_("%s is the PostgreSQL embedded SQL preprocessor for C programs.\n\n"),
      38              :            progname);
      39            1 :     printf(_("Usage:\n"
      40              :              "  %s [OPTION]... FILE...\n\n"),
      41              :            progname);
      42            1 :     printf(_("Options:\n"));
      43            1 :     printf(_("  -c             automatically generate C code from embedded SQL code;\n"
      44              :              "                 this affects EXEC SQL TYPE\n"));
      45            1 :     printf(_("  -C MODE        set compatibility mode; MODE can be one of\n"
      46              :              "                 \"INFORMIX\", \"INFORMIX_SE\", \"ORACLE\"\n"));
      47              : #ifdef YYDEBUG
      48              :     printf(_("  -d             generate parser debug output\n"));
      49              : #endif
      50            1 :     printf(_("  -D SYMBOL      define SYMBOL\n"));
      51            1 :     printf(_("  -h             parse a header file, this option includes option \"-c\"\n"));
      52            1 :     printf(_("  -i             parse system include files as well\n"));
      53            1 :     printf(_("  -I DIRECTORY   search DIRECTORY for include files\n"));
      54            1 :     printf(_("  -o OUTFILE     write result to OUTFILE\n"));
      55            1 :     printf(_("  -r OPTION      specify run-time behavior; OPTION can be:\n"
      56              :              "                 \"no_indicator\", \"prepare\", \"questionmarks\"\n"));
      57            1 :     printf(_("  --regression   run in regression testing mode\n"));
      58            1 :     printf(_("  -t             turn on autocommit of transactions\n"));
      59            1 :     printf(_("  -V, --version  output version information, then exit\n"));
      60            1 :     printf(_("  -?, --help     show this help, then exit\n"));
      61            1 :     printf(_("\nIf no output file is specified, the name is formed by adding .c to the\n"
      62              :              "input file name, after stripping off .pgc if present.\n"));
      63            1 :     printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
      64            1 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
      65            1 : }
      66              : 
      67              : static void
      68          419 : add_include_path(char *path)
      69              : {
      70          419 :     struct _include_path *ip = include_paths,
      71              :                *new;
      72              : 
      73          419 :     new = mm_alloc(sizeof(struct _include_path));
      74          419 :     new->path = path;
      75          419 :     new->next = NULL;
      76              : 
      77          419 :     if (ip == NULL)
      78           69 :         include_paths = new;
      79              :     else
      80              :     {
      81         1072 :         for (; ip->next != NULL; ip = ip->next);
      82          350 :         ip->next = new;
      83              :     }
      84          419 : }
      85              : 
      86              : /*
      87              :  * Process a command line -D switch
      88              :  */
      89              : static void
      90            1 : add_preprocessor_define(char *define)
      91              : {
      92              :     /* copy the argument to avoid relying on argv storage */
      93            1 :     char       *define_copy = mm_strdup(define);
      94              :     char       *ptr;
      95              :     struct _defines *newdef;
      96              : 
      97            1 :     newdef = mm_alloc(sizeof(struct _defines));
      98              : 
      99              :     /* look for = sign */
     100            1 :     ptr = strchr(define_copy, '=');
     101            1 :     if (ptr != NULL)
     102              :     {
     103              :         /* symbol has a value */
     104              :         char       *tmp;
     105              : 
     106              :         /* strip any spaces between name and '=' */
     107            1 :         for (tmp = ptr - 1; tmp >= define_copy && *tmp == ' '; tmp--);
     108            1 :         tmp[1] = '\0';
     109              : 
     110              :         /*
     111              :          * Note we don't bother to separately malloc cmdvalue; it will never
     112              :          * be freed so that's not necessary.
     113              :          */
     114            1 :         newdef->cmdvalue = ptr + 1;
     115              :     }
     116              :     else
     117              :     {
     118              :         /* define it as "1"; again no need to malloc it */
     119            0 :         newdef->cmdvalue = "1";
     120              :     }
     121            1 :     newdef->name = define_copy;
     122            1 :     newdef->value = mm_strdup(newdef->cmdvalue);
     123            1 :     newdef->used = NULL;
     124            1 :     newdef->next = defines;
     125            1 :     defines = newdef;
     126            1 : }
     127              : 
     128              : #define ECPG_GETOPT_LONG_REGRESSION     1
     129              : int
     130           72 : main(int argc, char *const argv[])
     131              : {
     132              :     static struct option ecpg_options[] = {
     133              :         {"regression", no_argument, NULL, ECPG_GETOPT_LONG_REGRESSION},
     134              :         {NULL, 0, NULL, 0}
     135              :     };
     136              : 
     137              :     int         fnr,
     138              :                 c,
     139           72 :                 out_option = 0;
     140           72 :     bool        verbose = false,
     141           72 :                 header_mode = false;
     142              :     struct _include_path *ip;
     143              :     char        my_exec_path[MAXPGPATH];
     144              :     char        include_path[MAXPGPATH];
     145              : 
     146           72 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("ecpg"));
     147              : 
     148           72 :     progname = get_progname(argv[0]);
     149              : 
     150           72 :     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           72 :     if (argc > 1)
     157              :     {
     158           71 :         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
     159              :         {
     160            1 :             help(progname);
     161            1 :             exit(0);
     162              :         }
     163           70 :         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
     164              :         {
     165            1 :             printf("ecpg (PostgreSQL) %s\n", PG_VERSION);
     166            1 :             exit(0);
     167              :         }
     168              :     }
     169              : 
     170           70 :     output_filename = NULL;
     171          353 :     while ((c = getopt_long(argc, argv, "cC:dD:hiI:o:r:tv", ecpg_options, NULL)) != -1)
     172              :     {
     173          284 :         switch (c)
     174              :         {
     175            2 :             case 'c':
     176            2 :                 auto_create_c = true;
     177            2 :                 break;
     178           12 :             case 'C':
     179           12 :                 if (pg_strcasecmp(optarg, "INFORMIX") == 0 || pg_strcasecmp(optarg, "INFORMIX_SE") == 0)
     180           11 :                 {
     181              :                     char        pkginclude_path[MAXPGPATH];
     182              :                     char        informix_path[MAXPGPATH];
     183              : 
     184           11 :                     compat = (pg_strcasecmp(optarg, "INFORMIX") == 0) ? ECPG_COMPAT_INFORMIX : ECPG_COMPAT_INFORMIX_SE;
     185           11 :                     get_pkginclude_path(my_exec_path, pkginclude_path);
     186           11 :                     snprintf(informix_path, MAXPGPATH, "%s/informix/esql", pkginclude_path);
     187           11 :                     add_include_path(informix_path);
     188              :                 }
     189            1 :                 else if (pg_strcasecmp(optarg, "ORACLE") == 0)
     190              :                 {
     191            1 :                     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           12 :                 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            1 :             case 'D':
     208            1 :                 add_preprocessor_define(optarg);
     209            1 :                 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            1 :             case 'i':
     216            1 :                 system_includes = true;
     217            1 :                 break;
     218          132 :             case 'I':
     219          132 :                 add_include_path(optarg);
     220          132 :                 break;
     221           66 :             case 'o':
     222           66 :                 output_filename = mm_strdup(optarg);
     223           66 :                 if (strcmp(output_filename, "-") == 0)
     224            0 :                     base_yyout = stdout;
     225              :                 else
     226           66 :                     base_yyout = fopen(output_filename, PG_BINARY_W);
     227              : 
     228           66 :                 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           66 :                     out_option = 1;
     236           66 :                 break;
     237            3 :             case 'r':
     238            3 :                 if (pg_strcasecmp(optarg, "no_indicator") == 0)
     239            1 :                     force_indicator = false;
     240            2 :                 else if (pg_strcasecmp(optarg, "prepare") == 0)
     241            1 :                     auto_prepare = true;
     242            1 :                 else if (pg_strcasecmp(optarg, "questionmarks") == 0)
     243            1 :                     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            3 :                 break;
     250            0 :             case 't':
     251            0 :                 autocommit = true;
     252            0 :                 break;
     253            0 :             case 'v':
     254            0 :                 verbose = true;
     255            0 :                 break;
     256           66 :             case ECPG_GETOPT_LONG_REGRESSION:
     257           66 :                 regression_mode = true;
     258           66 :                 break;
     259            1 :             default:
     260            1 :                 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
     261            1 :                 return ILLEGAL_OPTION;
     262              :         }
     263              :     }
     264              : 
     265           69 :     add_include_path(".");
     266           69 :     add_include_path("/usr/local/include");
     267           69 :     get_include_path(my_exec_path, include_path);
     268           69 :     add_include_path(include_path);
     269           69 :     add_include_path("/usr/include");
     270              : 
     271           69 :     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           69 :     if (optind >= argc)          /* no files specified */
     284              :     {
     285            1 :         fprintf(stderr, _("%s: no input files specified\n"), progname);
     286            1 :         fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
     287            1 :         return ILLEGAL_OPTION;
     288              :     }
     289              :     else
     290              :     {
     291              :         /* after the options there must not be anything but filenames */
     292          136 :         for (fnr = optind; fnr < argc; fnr++)
     293              :         {
     294              :             char       *ptr2ext;
     295              : 
     296              :             /* If argv[fnr] is "-" we have to read from stdin */
     297           69 :             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           69 :                 input_filename = mm_alloc(strlen(argv[fnr]) + 5);
     306           69 :                 strcpy(input_filename, argv[fnr]);
     307              : 
     308              :                 /* take care of relative paths */
     309           69 :                 ptr2ext = last_dir_separator(input_filename);
     310           69 :                 ptr2ext = (ptr2ext ? strrchr(ptr2ext, '.') : strrchr(input_filename, '.'));
     311              : 
     312              :                 /* no extension? */
     313           69 :                 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           69 :                 base_yyin = fopen(input_filename, PG_BINARY_R);
     326              :             }
     327              : 
     328           69 :             if (out_option == 0)    /* calculate the output name */
     329              :             {
     330            2 :                 if (strcmp(input_filename, "stdin") == 0)
     331            0 :                     base_yyout = stdout;
     332              :                 else
     333              :                 {
     334            2 :                     output_filename = mm_alloc(strlen(input_filename) + 3);
     335            2 :                     strcpy(output_filename, input_filename);
     336              : 
     337            2 :                     ptr2ext = strrchr(output_filename, '.');
     338              :                     /* make extension = .c resp. .h */
     339            2 :                     ptr2ext[1] = (header_mode == true) ? 'h' : 'c';
     340            2 :                     ptr2ext[2] = '\0';
     341              : 
     342            2 :                     base_yyout = fopen(output_filename, PG_BINARY_W);
     343            2 :                     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           69 :             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           69 :                 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           69 :                 cur = NULL;
     391              : 
     392              :                 /* remove old declared statements if any are still there */
     393           69 :                 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           69 :                 prevdefptr = NULL;
     403           72 :                 for (defptr = defines; defptr != NULL; defptr = nextdefptr)
     404              :                 {
     405            3 :                     nextdefptr = defptr->next;
     406            3 :                     if (defptr->cmdvalue != NULL)
     407              :                     {
     408              :                         /* keep it, resetting the value */
     409            2 :                         free(defptr->value);
     410            2 :                         defptr->value = mm_strdup(defptr->cmdvalue);
     411            2 :                         prevdefptr = defptr;
     412              :                     }
     413              :                     else
     414              :                     {
     415              :                         /* remove it */
     416            1 :                         if (prevdefptr != NULL)
     417            0 :                             prevdefptr->next = nextdefptr;
     418              :                         else
     419            1 :                             defines = nextdefptr;
     420            1 :                         free(defptr->name);
     421            1 :                         free(defptr->value);
     422            1 :                         free(defptr);
     423              :                     }
     424              :                 }
     425              : 
     426              :                 /* and old typedefs */
     427           69 :                 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           69 :                 types = NULL;
     438              : 
     439              :                 /* initialize whenever structures */
     440           69 :                 memset(&when_error, 0, sizeof(struct when));
     441           69 :                 memset(&when_nf, 0, sizeof(struct when));
     442           69 :                 memset(&when_warn, 0, sizeof(struct when));
     443              : 
     444              :                 /* and structure member lists */
     445           69 :                 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           69 :                 ecpg_internal_var = 0;
     452              : 
     453              :                 /* finally the actual connection */
     454           69 :                 connection = NULL;
     455              : 
     456              :                 /* initialize lex */
     457           69 :                 lex_init();
     458              : 
     459              :                 /* we need several includes */
     460              :                 /* but not if we are in header mode */
     461           69 :                 if (regression_mode)
     462           67 :                     fprintf(base_yyout, "/* Processed by ecpg (regression mode) */\n");
     463              :                 else
     464            2 :                     fprintf(base_yyout, "/* Processed by ecpg (%s) */\n", PG_VERSION);
     465              : 
     466           69 :                 if (header_mode == false)
     467              :                 {
     468           69 :                     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           69 :                     if (INFORMIX_MODE)
     472           11 :                         fprintf(base_yyout, "/* Needed for informix compatibility */\n#include <ecpg_informix.h>\n");
     473              : 
     474           69 :                     fprintf(base_yyout, "/* End of automatic include section */\n");
     475              :                 }
     476              : 
     477           69 :                 if (regression_mode)
     478           67 :                     fprintf(base_yyout, "#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))\n");
     479              : 
     480           69 :                 output_line_number();
     481              : 
     482              :                 /* and parse the source */
     483           69 :                 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          109 :                 for (ptr = cur; ptr != NULL; ptr = ptr->next)
     490           41 :                     if (!(ptr->opened))
     491            4 :                         mmerror(PARSE_ERROR, ET_WARNING, "cursor \"%s\" has been declared but not opened", ptr->name);
     492              : 
     493           68 :                 if (base_yyin != NULL && base_yyin != stdin)
     494           68 :                     fclose(base_yyin);
     495           68 :                 if (out_option == 0 && base_yyout != stdout)
     496            1 :                     fclose(base_yyout);
     497              : 
     498              :                 /*
     499              :                  * If there was an error, delete the output file.
     500              :                  */
     501           68 :                 if (ret_value != 0)
     502              :                 {
     503            1 :                     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           68 :             if (output_filename && out_option == 0)
     509              :             {
     510            1 :                 free(output_filename);
     511            1 :                 output_filename = NULL;
     512              :             }
     513              : 
     514           68 :             free(input_filename);
     515              :         }
     516              :     }
     517           67 :     return ret_value;
     518              : }
        

Generated by: LCOV version 2.0-1