OSDN Git Service

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