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