1 /* src/interfaces/ecpg/preproc/ecpg.c */
2 
3 /* Main for ecpg, the PostgreSQL embedded SQL precompiler. */
4 /* Copyright (c) 1996-2018, PostgreSQL Global Development Group */
5 
6 #include "postgres_fe.h"
7 
8 #include <unistd.h>
9 
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\", \"ORACLE\"\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\n", PG_VERSION);
153 			exit(0);
154 		}
155 	}
156 
157 	output_filename = NULL;
158 	while ((c = getopt_long(argc, argv, "vcio:I:tD:dC:r:h", ecpg_options, NULL)) != -1)
159 	{
160 		switch (c)
161 		{
162 			case ECPG_GETOPT_LONG_REGRESSION:
163 				regression_mode = true;
164 				break;
165 			case 'o':
166 				output_filename = mm_strdup(optarg);
167 				if (strcmp(output_filename, "-") == 0)
168 					base_yyout = stdout;
169 				else
170 					base_yyout = fopen(output_filename, PG_BINARY_W);
171 
172 				if (base_yyout == NULL)
173 				{
174 					fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
175 							progname, output_filename, strerror(errno));
176 					output_filename = NULL;
177 				}
178 				else
179 					out_option = 1;
180 				break;
181 			case 'I':
182 				add_include_path(optarg);
183 				break;
184 			case 't':
185 				autocommit = true;
186 				break;
187 			case 'v':
188 				verbose = true;
189 				break;
190 			case 'h':
191 				header_mode = true;
192 				/* this must include "-c" to make sense, so fall through */
193 				/* FALLTHROUGH */
194 			case 'c':
195 				auto_create_c = true;
196 				break;
197 			case 'i':
198 				system_includes = true;
199 				break;
200 			case 'C':
201 				if (pg_strcasecmp(optarg, "INFORMIX") == 0 || pg_strcasecmp(optarg, "INFORMIX_SE") == 0)
202 				{
203 					char		pkginclude_path[MAXPGPATH];
204 					char		informix_path[MAXPGPATH];
205 
206 					compat = (pg_strcasecmp(optarg, "INFORMIX") == 0) ? ECPG_COMPAT_INFORMIX : ECPG_COMPAT_INFORMIX_SE;
207 					get_pkginclude_path(my_exec_path, pkginclude_path);
208 					snprintf(informix_path, MAXPGPATH, "%s/informix/esql", pkginclude_path);
209 					add_include_path(informix_path);
210 				}
211 				else if (strncmp(optarg, "ORACLE", strlen("ORACLE")) == 0)
212 				{
213 					compat = ECPG_COMPAT_ORACLE;
214 				}
215 				else
216 				{
217 					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
218 					return ILLEGAL_OPTION;
219 				}
220 				break;
221 			case 'r':
222 				if (strcmp(optarg, "no_indicator") == 0)
223 					force_indicator = false;
224 				else if (strcmp(optarg, "prepare") == 0)
225 					auto_prepare = true;
226 				else if (strcmp(optarg, "questionmarks") == 0)
227 					questionmarks = true;
228 				else
229 				{
230 					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
231 					return ILLEGAL_OPTION;
232 				}
233 				break;
234 			case 'D':
235 				add_preprocessor_define(optarg);
236 				break;
237 			case 'd':
238 #ifdef YYDEBUG
239 				base_yydebug = 1;
240 #else
241 				fprintf(stderr, _("%s: parser debug support (-d) not available\n"),
242 						progname);
243 #endif
244 				break;
245 			default:
246 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
247 				return ILLEGAL_OPTION;
248 		}
249 	}
250 
251 	add_include_path(".");
252 	add_include_path("/usr/local/include");
253 	get_include_path(my_exec_path, include_path);
254 	add_include_path(include_path);
255 	add_include_path("/usr/include");
256 
257 	if (verbose)
258 	{
259 		fprintf(stderr,
260 				_("%s, the PostgreSQL embedded C preprocessor, version %s\n"),
261 				progname, PG_VERSION);
262 		fprintf(stderr, _("EXEC SQL INCLUDE ... search starts here:\n"));
263 		for (ip = include_paths; ip != NULL; ip = ip->next)
264 			fprintf(stderr, " %s\n", ip->path);
265 		fprintf(stderr, _("end of search list\n"));
266 		return 0;
267 	}
268 
269 	if (optind >= argc)			/* no files specified */
270 	{
271 		fprintf(stderr, _("%s: no input files specified\n"), progname);
272 		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
273 		return ILLEGAL_OPTION;
274 	}
275 	else
276 	{
277 		/* after the options there must not be anything but filenames */
278 		for (fnr = optind; fnr < argc; fnr++)
279 		{
280 			char	   *ptr2ext;
281 
282 			/* If argv[fnr] is "-" we have to read from stdin */
283 			if (strcmp(argv[fnr], "-") == 0)
284 			{
285 				input_filename = mm_alloc(strlen("stdin") + 1);
286 				strcpy(input_filename, "stdin");
287 				base_yyin = stdin;
288 			}
289 			else
290 			{
291 				input_filename = mm_alloc(strlen(argv[fnr]) + 5);
292 				strcpy(input_filename, argv[fnr]);
293 
294 				/* take care of relative paths */
295 				ptr2ext = last_dir_separator(input_filename);
296 				ptr2ext = (ptr2ext ? strrchr(ptr2ext, '.') : strrchr(input_filename, '.'));
297 
298 				/* no extension? */
299 				if (ptr2ext == NULL)
300 				{
301 					ptr2ext = input_filename + strlen(input_filename);
302 
303 					/* no extension => add .pgc or .pgh */
304 					ptr2ext[0] = '.';
305 					ptr2ext[1] = 'p';
306 					ptr2ext[2] = 'g';
307 					ptr2ext[3] = (header_mode == true) ? 'h' : 'c';
308 					ptr2ext[4] = '\0';
309 				}
310 
311 				base_yyin = fopen(input_filename, PG_BINARY_R);
312 			}
313 
314 			if (out_option == 0)	/* calculate the output name */
315 			{
316 				if (strcmp(input_filename, "stdin") == 0)
317 					base_yyout = stdout;
318 				else
319 				{
320 					output_filename = mm_alloc(strlen(input_filename) + 3);
321 					strcpy(output_filename, input_filename);
322 
323 					ptr2ext = strrchr(output_filename, '.');
324 					/* make extension = .c resp. .h */
325 					ptr2ext[1] = (header_mode == true) ? 'h' : 'c';
326 					ptr2ext[2] = '\0';
327 
328 					base_yyout = fopen(output_filename, PG_BINARY_W);
329 					if (base_yyout == NULL)
330 					{
331 						fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
332 								progname, output_filename, strerror(errno));
333 						free(output_filename);
334 						output_filename = NULL;
335 						free(input_filename);
336 						continue;
337 					}
338 				}
339 			}
340 
341 			if (base_yyin == NULL)
342 				fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
343 						progname, argv[fnr], strerror(errno));
344 			else
345 			{
346 				struct cursor *ptr;
347 				struct _defines *defptr;
348 				struct typedefs *typeptr;
349 
350 				/* remove old cursor definitions if any are still there */
351 				for (ptr = cur; ptr != NULL;)
352 				{
353 					struct cursor *this = ptr;
354 					struct arguments *l1,
355 							   *l2;
356 
357 					free(ptr->command);
358 					free(ptr->connection);
359 					free(ptr->name);
360 					for (l1 = ptr->argsinsert; l1; l1 = l2)
361 					{
362 						l2 = l1->next;
363 						free(l1);
364 					}
365 					for (l1 = ptr->argsresult; l1; l1 = l2)
366 					{
367 						l2 = l1->next;
368 						free(l1);
369 					}
370 					ptr = ptr->next;
371 					free(this);
372 				}
373 				cur = NULL;
374 
375 				/* remove non-pertinent old defines as well */
376 				while (defines && !defines->pertinent)
377 				{
378 					defptr = defines;
379 					defines = defines->next;
380 
381 					free(defptr->new);
382 					free(defptr->old);
383 					free(defptr);
384 				}
385 
386 				for (defptr = defines; defptr != NULL; defptr = defptr->next)
387 				{
388 					struct _defines *this = defptr->next;
389 
390 					if (this && !this->pertinent)
391 					{
392 						defptr->next = this->next;
393 
394 						free(this->new);
395 						free(this->old);
396 						free(this);
397 					}
398 				}
399 
400 				/* and old typedefs */
401 				for (typeptr = types; typeptr != NULL;)
402 				{
403 					struct typedefs *this = typeptr;
404 
405 					free(typeptr->name);
406 					ECPGfree_struct_member(typeptr->struct_member_list);
407 					free(typeptr->type);
408 					typeptr = typeptr->next;
409 					free(this);
410 				}
411 				types = NULL;
412 
413 				/* initialize whenever structures */
414 				memset(&when_error, 0, sizeof(struct when));
415 				memset(&when_nf, 0, sizeof(struct when));
416 				memset(&when_warn, 0, sizeof(struct when));
417 
418 				/* and structure member lists */
419 				memset(struct_member_list, 0, sizeof(struct_member_list));
420 
421 				/*
422 				 * and our variable counter for out of scope cursors'
423 				 * variables
424 				 */
425 				ecpg_internal_var = 0;
426 
427 				/* finally the actual connection */
428 				connection = NULL;
429 
430 				/* initialize lex */
431 				lex_init();
432 
433 				/* we need several includes */
434 				/* but not if we are in header mode */
435 				if (regression_mode)
436 					fprintf(base_yyout, "/* Processed by ecpg (regression mode) */\n");
437 				else
438 					fprintf(base_yyout, "/* Processed by ecpg (%s) */\n", PG_VERSION);
439 
440 				if (header_mode == false)
441 				{
442 					fprintf(base_yyout, "/* These include files are added by the preprocessor */\n#include <ecpglib.h>\n#include <ecpgerrno.h>\n#include <sqlca.h>\n");
443 
444 					/* add some compatibility headers */
445 					if (INFORMIX_MODE)
446 						fprintf(base_yyout, "/* Needed for informix compatibility */\n#include <ecpg_informix.h>\n");
447 
448 					fprintf(base_yyout, "/* End of automatic include section */\n");
449 				}
450 
451 				if (regression_mode)
452 					fprintf(base_yyout, "#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))\n");
453 
454 				output_line_number();
455 
456 				/* and parse the source */
457 				base_yyparse();
458 
459 				/*
460 				 * Check whether all cursors were indeed opened.  It does not
461 				 * really make sense to declare a cursor but not open it.
462 				 */
463 				for (ptr = cur; ptr != NULL; ptr = ptr->next)
464 					if (!(ptr->opened))
465 						mmerror(PARSE_ERROR, ET_WARNING, "cursor \"%s\" has been declared but not opened", ptr->name);
466 
467 				if (base_yyin != NULL && base_yyin != stdin)
468 					fclose(base_yyin);
469 				if (out_option == 0 && base_yyout != stdout)
470 					fclose(base_yyout);
471 
472 				/*
473 				 * If there was an error, delete the output file.
474 				 */
475 				if (ret_value != 0)
476 				{
477 					if (strcmp(output_filename, "-") != 0 && unlink(output_filename) != 0)
478 						fprintf(stderr, _("could not remove output file \"%s\"\n"), output_filename);
479 				}
480 			}
481 
482 			if (output_filename && out_option == 0)
483 			{
484 				free(output_filename);
485 				output_filename = NULL;
486 			}
487 
488 			free(input_filename);
489 		}
490 	}
491 	return ret_value;
492 }
493