1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2021, PostgreSQL Global Development Group
5  *
6  * src/bin/psql/startup.c
7  */
8 #include "postgres_fe.h"
9 
10 #ifndef WIN32
11 #include <unistd.h>
12 #else							/* WIN32 */
13 #include <io.h>
14 #include <win32.h>
15 #endif							/* WIN32 */
16 
17 #include "command.h"
18 #include "common.h"
19 #include "common/logging.h"
20 #include "common/string.h"
21 #include "describe.h"
22 #include "fe_utils/print.h"
23 #include "getopt_long.h"
24 #include "help.h"
25 #include "input.h"
26 #include "mainloop.h"
27 #include "settings.h"
28 
29 /*
30  * Global psql options
31  */
32 PsqlSettings pset;
33 
34 #ifndef WIN32
35 #define SYSPSQLRC	"psqlrc"
36 #define PSQLRC		".psqlrc"
37 #else
38 #define SYSPSQLRC	"psqlrc"
39 #define PSQLRC		"psqlrc.conf"
40 #endif
41 
42 /*
43  * Structures to pass information between the option parsing routine
44  * and the main function
45  */
46 enum _actions
47 {
48 	ACT_SINGLE_QUERY,
49 	ACT_SINGLE_SLASH,
50 	ACT_FILE
51 };
52 
53 typedef struct SimpleActionListCell
54 {
55 	struct SimpleActionListCell *next;
56 	enum _actions action;
57 	char	   *val;
58 } SimpleActionListCell;
59 
60 typedef struct SimpleActionList
61 {
62 	SimpleActionListCell *head;
63 	SimpleActionListCell *tail;
64 } SimpleActionList;
65 
66 struct adhoc_opts
67 {
68 	char	   *dbname;
69 	char	   *host;
70 	char	   *port;
71 	char	   *username;
72 	char	   *logfilename;
73 	bool		no_readline;
74 	bool		no_psqlrc;
75 	bool		single_txn;
76 	bool		list_dbs;
77 	SimpleActionList actions;
78 };
79 
80 static void parse_psql_options(int argc, char *argv[],
81 							   struct adhoc_opts *options);
82 static void simple_action_list_append(SimpleActionList *list,
83 									  enum _actions action, const char *val);
84 static void process_psqlrc(char *argv0);
85 static void process_psqlrc_file(char *filename);
86 static void showVersion(void);
87 static void EstablishVariableSpace(void);
88 
89 #define NOPAGER		0
90 
91 static void
log_pre_callback(void)92 log_pre_callback(void)
93 {
94 	if (pset.queryFout && pset.queryFout != stdout)
95 		fflush(pset.queryFout);
96 }
97 
98 static void
log_locus_callback(const char ** filename,uint64 * lineno)99 log_locus_callback(const char **filename, uint64 *lineno)
100 {
101 	if (pset.inputfile)
102 	{
103 		*filename = pset.inputfile;
104 		*lineno = pset.lineno;
105 	}
106 	else
107 	{
108 		*filename = NULL;
109 		*lineno = 0;
110 	}
111 }
112 
113 /*
114  *
115  * main
116  *
117  */
118 int
main(int argc,char * argv[])119 main(int argc, char *argv[])
120 {
121 	struct adhoc_opts options;
122 	int			successResult;
123 	char	   *password = NULL;
124 	bool		new_pass;
125 
126 	pg_logging_init(argv[0]);
127 	pg_logging_set_pre_callback(log_pre_callback);
128 	pg_logging_set_locus_callback(log_locus_callback);
129 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("psql"));
130 
131 	if (argc > 1)
132 	{
133 		if ((strcmp(argv[1], "-?") == 0) || (argc == 2 && (strcmp(argv[1], "--help") == 0)))
134 		{
135 			usage(NOPAGER);
136 			exit(EXIT_SUCCESS);
137 		}
138 		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
139 		{
140 			showVersion();
141 			exit(EXIT_SUCCESS);
142 		}
143 	}
144 
145 	pset.progname = get_progname(argv[0]);
146 
147 	pset.db = NULL;
148 	pset.dead_conn = NULL;
149 	setDecimalLocale();
150 	pset.encoding = PQenv2encoding();
151 	pset.queryFout = stdout;
152 	pset.queryFoutPipe = false;
153 	pset.copyStream = NULL;
154 	pset.last_error_result = NULL;
155 	pset.cur_cmd_source = stdin;
156 	pset.cur_cmd_interactive = false;
157 
158 	/* We rely on unmentioned fields of pset.popt to start out 0/false/NULL */
159 	pset.popt.topt.format = PRINT_ALIGNED;
160 	pset.popt.topt.border = 1;
161 	pset.popt.topt.pager = 1;
162 	pset.popt.topt.pager_min_lines = 0;
163 	pset.popt.topt.start_table = true;
164 	pset.popt.topt.stop_table = true;
165 	pset.popt.topt.default_footer = true;
166 
167 	pset.popt.topt.csvFieldSep[0] = DEFAULT_CSV_FIELD_SEP;
168 	pset.popt.topt.csvFieldSep[1] = '\0';
169 
170 	pset.popt.topt.unicode_border_linestyle = UNICODE_LINESTYLE_SINGLE;
171 	pset.popt.topt.unicode_column_linestyle = UNICODE_LINESTYLE_SINGLE;
172 	pset.popt.topt.unicode_header_linestyle = UNICODE_LINESTYLE_SINGLE;
173 
174 	refresh_utf8format(&(pset.popt.topt));
175 
176 	/* We must get COLUMNS here before readline() sets it */
177 	pset.popt.topt.env_columns = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 0;
178 
179 	pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout)));
180 
181 	pset.getPassword = TRI_DEFAULT;
182 
183 	EstablishVariableSpace();
184 
185 	/* Create variables showing psql version number */
186 	SetVariable(pset.vars, "VERSION", PG_VERSION_STR);
187 	SetVariable(pset.vars, "VERSION_NAME", PG_VERSION);
188 	SetVariable(pset.vars, "VERSION_NUM", CppAsString2(PG_VERSION_NUM));
189 
190 	/* Initialize variables for last error */
191 	SetVariable(pset.vars, "LAST_ERROR_MESSAGE", "");
192 	SetVariable(pset.vars, "LAST_ERROR_SQLSTATE", "00000");
193 
194 	/* Default values for variables (that don't match the result of \unset) */
195 	SetVariableBool(pset.vars, "AUTOCOMMIT");
196 	SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1);
197 	SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2);
198 	SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3);
199 
200 	parse_psql_options(argc, argv, &options);
201 
202 	/*
203 	 * If no action was specified and we're in non-interactive mode, treat it
204 	 * as if the user had specified "-f -".  This lets single-transaction mode
205 	 * work in this case.
206 	 */
207 	if (options.actions.head == NULL && pset.notty)
208 		simple_action_list_append(&options.actions, ACT_FILE, NULL);
209 
210 	/* Bail out if -1 was specified but will be ignored. */
211 	if (options.single_txn && options.actions.head == NULL)
212 	{
213 		pg_log_fatal("-1 can only be used in non-interactive mode");
214 		exit(EXIT_FAILURE);
215 	}
216 
217 	if (!pset.popt.topt.fieldSep.separator &&
218 		!pset.popt.topt.fieldSep.separator_zero)
219 	{
220 		pset.popt.topt.fieldSep.separator = pg_strdup(DEFAULT_FIELD_SEP);
221 		pset.popt.topt.fieldSep.separator_zero = false;
222 	}
223 	if (!pset.popt.topt.recordSep.separator &&
224 		!pset.popt.topt.recordSep.separator_zero)
225 	{
226 		pset.popt.topt.recordSep.separator = pg_strdup(DEFAULT_RECORD_SEP);
227 		pset.popt.topt.recordSep.separator_zero = false;
228 	}
229 
230 	if (pset.getPassword == TRI_YES)
231 	{
232 		/*
233 		 * We can't be sure yet of the username that will be used, so don't
234 		 * offer a potentially wrong one.  Typical uses of this option are
235 		 * noninteractive anyway.
236 		 */
237 		password = simple_prompt("Password: ", false);
238 	}
239 
240 	/* loop until we have a password if requested by backend */
241 	do
242 	{
243 #define PARAMS_ARRAY_SIZE	8
244 		const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
245 		const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
246 
247 		keywords[0] = "host";
248 		values[0] = options.host;
249 		keywords[1] = "port";
250 		values[1] = options.port;
251 		keywords[2] = "user";
252 		values[2] = options.username;
253 		keywords[3] = "password";
254 		values[3] = password;
255 		keywords[4] = "dbname"; /* see do_connect() */
256 		values[4] = (options.list_dbs && options.dbname == NULL) ?
257 			"postgres" : options.dbname;
258 		keywords[5] = "fallback_application_name";
259 		values[5] = pset.progname;
260 		keywords[6] = "client_encoding";
261 		values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto";
262 		keywords[7] = NULL;
263 		values[7] = NULL;
264 
265 		new_pass = false;
266 		pset.db = PQconnectdbParams(keywords, values, true);
267 		free(keywords);
268 		free(values);
269 
270 		if (PQstatus(pset.db) == CONNECTION_BAD &&
271 			PQconnectionNeedsPassword(pset.db) &&
272 			!password &&
273 			pset.getPassword != TRI_NO)
274 		{
275 			/*
276 			 * Before closing the old PGconn, extract the user name that was
277 			 * actually connected with --- it might've come out of a URI or
278 			 * connstring "database name" rather than options.username.
279 			 */
280 			const char *realusername = PQuser(pset.db);
281 			char	   *password_prompt;
282 
283 			if (realusername && realusername[0])
284 				password_prompt = psprintf(_("Password for user %s: "),
285 										   realusername);
286 			else
287 				password_prompt = pg_strdup(_("Password: "));
288 			PQfinish(pset.db);
289 
290 			password = simple_prompt(password_prompt, false);
291 			free(password_prompt);
292 			new_pass = true;
293 		}
294 	} while (new_pass);
295 
296 	if (PQstatus(pset.db) == CONNECTION_BAD)
297 	{
298 		pg_log_error("%s", PQerrorMessage(pset.db));
299 		PQfinish(pset.db);
300 		exit(EXIT_BADCONN);
301 	}
302 
303 	psql_setup_cancel_handler();
304 
305 	PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
306 
307 	SyncVariables();
308 
309 	if (options.list_dbs)
310 	{
311 		int			success;
312 
313 		if (!options.no_psqlrc)
314 			process_psqlrc(argv[0]);
315 
316 		success = listAllDbs(NULL, false);
317 		PQfinish(pset.db);
318 		exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
319 	}
320 
321 	if (options.logfilename)
322 	{
323 		pset.logfile = fopen(options.logfilename, "a");
324 		if (!pset.logfile)
325 		{
326 			pg_log_fatal("could not open log file \"%s\": %m",
327 						 options.logfilename);
328 			exit(EXIT_FAILURE);
329 		}
330 	}
331 
332 	if (!options.no_psqlrc)
333 		process_psqlrc(argv[0]);
334 
335 	/*
336 	 * If any actions were given by user, process them in the order in which
337 	 * they were specified.  Note single_txn is only effective in this mode.
338 	 */
339 	if (options.actions.head != NULL)
340 	{
341 		PGresult   *res;
342 		SimpleActionListCell *cell;
343 
344 		successResult = EXIT_SUCCESS;	/* silence compiler */
345 
346 		if (options.single_txn)
347 		{
348 			if ((res = PSQLexec("BEGIN")) == NULL)
349 			{
350 				if (pset.on_error_stop)
351 				{
352 					successResult = EXIT_USER;
353 					goto error;
354 				}
355 			}
356 			else
357 				PQclear(res);
358 		}
359 
360 		for (cell = options.actions.head; cell; cell = cell->next)
361 		{
362 			if (cell->action == ACT_SINGLE_QUERY)
363 			{
364 				pg_logging_config(PG_LOG_FLAG_TERSE);
365 
366 				if (pset.echo == PSQL_ECHO_ALL)
367 					puts(cell->val);
368 
369 				successResult = SendQuery(cell->val)
370 					? EXIT_SUCCESS : EXIT_FAILURE;
371 			}
372 			else if (cell->action == ACT_SINGLE_SLASH)
373 			{
374 				PsqlScanState scan_state;
375 				ConditionalStack cond_stack;
376 
377 				pg_logging_config(PG_LOG_FLAG_TERSE);
378 
379 				if (pset.echo == PSQL_ECHO_ALL)
380 					puts(cell->val);
381 
382 				scan_state = psql_scan_create(&psqlscan_callbacks);
383 				psql_scan_setup(scan_state,
384 								cell->val, strlen(cell->val),
385 								pset.encoding, standard_strings());
386 				cond_stack = conditional_stack_create();
387 				psql_scan_set_passthrough(scan_state, (void *) cond_stack);
388 
389 				successResult = HandleSlashCmds(scan_state,
390 												cond_stack,
391 												NULL,
392 												NULL) != PSQL_CMD_ERROR
393 					? EXIT_SUCCESS : EXIT_FAILURE;
394 
395 				psql_scan_destroy(scan_state);
396 				conditional_stack_destroy(cond_stack);
397 			}
398 			else if (cell->action == ACT_FILE)
399 			{
400 				successResult = process_file(cell->val, false);
401 			}
402 			else
403 			{
404 				/* should never come here */
405 				Assert(false);
406 			}
407 
408 			if (successResult != EXIT_SUCCESS && pset.on_error_stop)
409 				break;
410 		}
411 
412 		if (options.single_txn)
413 		{
414 			if ((res = PSQLexec("COMMIT")) == NULL)
415 			{
416 				if (pset.on_error_stop)
417 				{
418 					successResult = EXIT_USER;
419 					goto error;
420 				}
421 			}
422 			else
423 				PQclear(res);
424 		}
425 
426 error:
427 		;
428 	}
429 
430 	/*
431 	 * or otherwise enter interactive main loop
432 	 */
433 	else
434 	{
435 		pg_logging_config(PG_LOG_FLAG_TERSE);
436 		connection_warnings(true);
437 		if (!pset.quiet)
438 			printf(_("Type \"help\" for help.\n\n"));
439 		initializeInput(options.no_readline ? 0 : 1);
440 		successResult = MainLoop(stdin);
441 	}
442 
443 	/* clean up */
444 	if (pset.logfile)
445 		fclose(pset.logfile);
446 	if (pset.db)
447 		PQfinish(pset.db);
448 	if (pset.dead_conn)
449 		PQfinish(pset.dead_conn);
450 	setQFout(NULL);
451 
452 	return successResult;
453 }
454 
455 
456 /*
457  * Parse command line options
458  */
459 
460 static void
parse_psql_options(int argc,char * argv[],struct adhoc_opts * options)461 parse_psql_options(int argc, char *argv[], struct adhoc_opts *options)
462 {
463 	static struct option long_options[] =
464 	{
465 		{"echo-all", no_argument, NULL, 'a'},
466 		{"no-align", no_argument, NULL, 'A'},
467 		{"command", required_argument, NULL, 'c'},
468 		{"dbname", required_argument, NULL, 'd'},
469 		{"echo-queries", no_argument, NULL, 'e'},
470 		{"echo-errors", no_argument, NULL, 'b'},
471 		{"echo-hidden", no_argument, NULL, 'E'},
472 		{"file", required_argument, NULL, 'f'},
473 		{"field-separator", required_argument, NULL, 'F'},
474 		{"field-separator-zero", no_argument, NULL, 'z'},
475 		{"host", required_argument, NULL, 'h'},
476 		{"html", no_argument, NULL, 'H'},
477 		{"list", no_argument, NULL, 'l'},
478 		{"log-file", required_argument, NULL, 'L'},
479 		{"no-readline", no_argument, NULL, 'n'},
480 		{"single-transaction", no_argument, NULL, '1'},
481 		{"output", required_argument, NULL, 'o'},
482 		{"port", required_argument, NULL, 'p'},
483 		{"pset", required_argument, NULL, 'P'},
484 		{"quiet", no_argument, NULL, 'q'},
485 		{"record-separator", required_argument, NULL, 'R'},
486 		{"record-separator-zero", no_argument, NULL, '0'},
487 		{"single-step", no_argument, NULL, 's'},
488 		{"single-line", no_argument, NULL, 'S'},
489 		{"tuples-only", no_argument, NULL, 't'},
490 		{"table-attr", required_argument, NULL, 'T'},
491 		{"username", required_argument, NULL, 'U'},
492 		{"set", required_argument, NULL, 'v'},
493 		{"variable", required_argument, NULL, 'v'},
494 		{"version", no_argument, NULL, 'V'},
495 		{"no-password", no_argument, NULL, 'w'},
496 		{"password", no_argument, NULL, 'W'},
497 		{"expanded", no_argument, NULL, 'x'},
498 		{"no-psqlrc", no_argument, NULL, 'X'},
499 		{"help", optional_argument, NULL, 1},
500 		{"csv", no_argument, NULL, 2},
501 		{NULL, 0, NULL, 0}
502 	};
503 
504 	int			optindex;
505 	int			c;
506 
507 	memset(options, 0, sizeof *options);
508 
509 	while ((c = getopt_long(argc, argv, "aAbc:d:eEf:F:h:HlL:no:p:P:qR:sStT:U:v:VwWxXz?01",
510 							long_options, &optindex)) != -1)
511 	{
512 		switch (c)
513 		{
514 			case 'a':
515 				SetVariable(pset.vars, "ECHO", "all");
516 				break;
517 			case 'A':
518 				pset.popt.topt.format = PRINT_UNALIGNED;
519 				break;
520 			case 'b':
521 				SetVariable(pset.vars, "ECHO", "errors");
522 				break;
523 			case 'c':
524 				if (optarg[0] == '\\')
525 					simple_action_list_append(&options->actions,
526 											  ACT_SINGLE_SLASH,
527 											  optarg + 1);
528 				else
529 					simple_action_list_append(&options->actions,
530 											  ACT_SINGLE_QUERY,
531 											  optarg);
532 				break;
533 			case 'd':
534 				options->dbname = pg_strdup(optarg);
535 				break;
536 			case 'e':
537 				SetVariable(pset.vars, "ECHO", "queries");
538 				break;
539 			case 'E':
540 				SetVariableBool(pset.vars, "ECHO_HIDDEN");
541 				break;
542 			case 'f':
543 				simple_action_list_append(&options->actions,
544 										  ACT_FILE,
545 										  optarg);
546 				break;
547 			case 'F':
548 				pset.popt.topt.fieldSep.separator = pg_strdup(optarg);
549 				pset.popt.topt.fieldSep.separator_zero = false;
550 				break;
551 			case 'h':
552 				options->host = pg_strdup(optarg);
553 				break;
554 			case 'H':
555 				pset.popt.topt.format = PRINT_HTML;
556 				break;
557 			case 'l':
558 				options->list_dbs = true;
559 				break;
560 			case 'L':
561 				options->logfilename = pg_strdup(optarg);
562 				break;
563 			case 'n':
564 				options->no_readline = true;
565 				break;
566 			case 'o':
567 				if (!setQFout(optarg))
568 					exit(EXIT_FAILURE);
569 				break;
570 			case 'p':
571 				options->port = pg_strdup(optarg);
572 				break;
573 			case 'P':
574 				{
575 					char	   *value;
576 					char	   *equal_loc;
577 					bool		result;
578 
579 					value = pg_strdup(optarg);
580 					equal_loc = strchr(value, '=');
581 					if (!equal_loc)
582 						result = do_pset(value, NULL, &pset.popt, true);
583 					else
584 					{
585 						*equal_loc = '\0';
586 						result = do_pset(value, equal_loc + 1, &pset.popt, true);
587 					}
588 
589 					if (!result)
590 					{
591 						pg_log_fatal("could not set printing parameter \"%s\"", value);
592 						exit(EXIT_FAILURE);
593 					}
594 
595 					free(value);
596 					break;
597 				}
598 			case 'q':
599 				SetVariableBool(pset.vars, "QUIET");
600 				break;
601 			case 'R':
602 				pset.popt.topt.recordSep.separator = pg_strdup(optarg);
603 				pset.popt.topt.recordSep.separator_zero = false;
604 				break;
605 			case 's':
606 				SetVariableBool(pset.vars, "SINGLESTEP");
607 				break;
608 			case 'S':
609 				SetVariableBool(pset.vars, "SINGLELINE");
610 				break;
611 			case 't':
612 				pset.popt.topt.tuples_only = true;
613 				break;
614 			case 'T':
615 				pset.popt.topt.tableAttr = pg_strdup(optarg);
616 				break;
617 			case 'U':
618 				options->username = pg_strdup(optarg);
619 				break;
620 			case 'v':
621 				{
622 					char	   *value;
623 					char	   *equal_loc;
624 
625 					value = pg_strdup(optarg);
626 					equal_loc = strchr(value, '=');
627 					if (!equal_loc)
628 					{
629 						if (!DeleteVariable(pset.vars, value))
630 							exit(EXIT_FAILURE); /* error already printed */
631 					}
632 					else
633 					{
634 						*equal_loc = '\0';
635 						if (!SetVariable(pset.vars, value, equal_loc + 1))
636 							exit(EXIT_FAILURE); /* error already printed */
637 					}
638 
639 					free(value);
640 					break;
641 				}
642 			case 'V':
643 				showVersion();
644 				exit(EXIT_SUCCESS);
645 			case 'w':
646 				pset.getPassword = TRI_NO;
647 				break;
648 			case 'W':
649 				pset.getPassword = TRI_YES;
650 				break;
651 			case 'x':
652 				pset.popt.topt.expanded = true;
653 				break;
654 			case 'X':
655 				options->no_psqlrc = true;
656 				break;
657 			case 'z':
658 				pset.popt.topt.fieldSep.separator_zero = true;
659 				break;
660 			case '0':
661 				pset.popt.topt.recordSep.separator_zero = true;
662 				break;
663 			case '1':
664 				options->single_txn = true;
665 				break;
666 			case '?':
667 				if (optind <= argc &&
668 					strcmp(argv[optind - 1], "-?") == 0)
669 				{
670 					/* actual help option given */
671 					usage(NOPAGER);
672 					exit(EXIT_SUCCESS);
673 				}
674 				else
675 				{
676 					/* getopt error (unknown option or missing argument) */
677 					goto unknown_option;
678 				}
679 				break;
680 			case 1:
681 				{
682 					if (!optarg || strcmp(optarg, "options") == 0)
683 						usage(NOPAGER);
684 					else if (optarg && strcmp(optarg, "commands") == 0)
685 						slashUsage(NOPAGER);
686 					else if (optarg && strcmp(optarg, "variables") == 0)
687 						helpVariables(NOPAGER);
688 					else
689 						goto unknown_option;
690 
691 					exit(EXIT_SUCCESS);
692 				}
693 				break;
694 			case 2:
695 				pset.popt.topt.format = PRINT_CSV;
696 				break;
697 			default:
698 		unknown_option:
699 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
700 						pset.progname);
701 				exit(EXIT_FAILURE);
702 				break;
703 		}
704 	}
705 
706 	/*
707 	 * if we still have arguments, use it as the database name and username
708 	 */
709 	while (argc - optind >= 1)
710 	{
711 		if (!options->dbname)
712 			options->dbname = argv[optind];
713 		else if (!options->username)
714 			options->username = argv[optind];
715 		else if (!pset.quiet)
716 			pg_log_warning("extra command-line argument \"%s\" ignored",
717 						   argv[optind]);
718 
719 		optind++;
720 	}
721 }
722 
723 
724 /*
725  * Append a new item to the end of the SimpleActionList.
726  * Note that "val" is copied if it's not NULL.
727  */
728 static void
simple_action_list_append(SimpleActionList * list,enum _actions action,const char * val)729 simple_action_list_append(SimpleActionList *list,
730 						  enum _actions action, const char *val)
731 {
732 	SimpleActionListCell *cell;
733 
734 	cell = (SimpleActionListCell *) pg_malloc(sizeof(SimpleActionListCell));
735 
736 	cell->next = NULL;
737 	cell->action = action;
738 	if (val)
739 		cell->val = pg_strdup(val);
740 	else
741 		cell->val = NULL;
742 
743 	if (list->tail)
744 		list->tail->next = cell;
745 	else
746 		list->head = cell;
747 	list->tail = cell;
748 }
749 
750 
751 /*
752  * Load .psqlrc file, if found.
753  */
754 static void
process_psqlrc(char * argv0)755 process_psqlrc(char *argv0)
756 {
757 	char		home[MAXPGPATH];
758 	char		rc_file[MAXPGPATH];
759 	char		my_exec_path[MAXPGPATH];
760 	char		etc_path[MAXPGPATH];
761 	char	   *envrc = getenv("PSQLRC");
762 
763 	if (find_my_exec(argv0, my_exec_path) < 0)
764 	{
765 		pg_log_fatal("could not find own program executable");
766 		exit(EXIT_FAILURE);
767 	}
768 
769 	get_etc_path(my_exec_path, etc_path);
770 
771 	snprintf(rc_file, MAXPGPATH, "%s/%s", etc_path, SYSPSQLRC);
772 	process_psqlrc_file(rc_file);
773 
774 	if (envrc != NULL && strlen(envrc) > 0)
775 	{
776 		/* might need to free() this */
777 		char	   *envrc_alloc = pstrdup(envrc);
778 
779 		expand_tilde(&envrc_alloc);
780 		process_psqlrc_file(envrc_alloc);
781 	}
782 	else if (get_home_path(home))
783 	{
784 		snprintf(rc_file, MAXPGPATH, "%s/%s", home, PSQLRC);
785 		process_psqlrc_file(rc_file);
786 	}
787 }
788 
789 
790 
791 static void
process_psqlrc_file(char * filename)792 process_psqlrc_file(char *filename)
793 {
794 	char	   *psqlrc_minor,
795 			   *psqlrc_major;
796 
797 #if defined(WIN32) && (!defined(__MINGW32__))
798 #define R_OK 4
799 #endif
800 
801 	psqlrc_minor = psprintf("%s-%s", filename, PG_VERSION);
802 	psqlrc_major = psprintf("%s-%s", filename, PG_MAJORVERSION);
803 
804 	/* check for minor version first, then major, then no version */
805 	if (access(psqlrc_minor, R_OK) == 0)
806 		(void) process_file(psqlrc_minor, false);
807 	else if (access(psqlrc_major, R_OK) == 0)
808 		(void) process_file(psqlrc_major, false);
809 	else if (access(filename, R_OK) == 0)
810 		(void) process_file(filename, false);
811 
812 	free(psqlrc_minor);
813 	free(psqlrc_major);
814 }
815 
816 
817 
818 /* showVersion
819  *
820  * This output format is intended to match GNU standards.
821  */
822 static void
showVersion(void)823 showVersion(void)
824 {
825 	puts("psql (PostgreSQL) " PG_VERSION);
826 }
827 
828 
829 
830 /*
831  * Substitute hooks and assign hooks for psql variables.
832  *
833  * This isn't an amazingly good place for them, but neither is anywhere else.
834  *
835  * By policy, every special variable that controls any psql behavior should
836  * have one or both hooks, even if they're just no-ops.  This ensures that
837  * the variable will remain present in variables.c's list even when unset,
838  * which ensures that it's known to tab completion.
839  */
840 
841 static char *
bool_substitute_hook(char * newval)842 bool_substitute_hook(char *newval)
843 {
844 	if (newval == NULL)
845 	{
846 		/* "\unset FOO" becomes "\set FOO off" */
847 		newval = pg_strdup("off");
848 	}
849 	else if (newval[0] == '\0')
850 	{
851 		/* "\set FOO" becomes "\set FOO on" */
852 		pg_free(newval);
853 		newval = pg_strdup("on");
854 	}
855 	return newval;
856 }
857 
858 static bool
autocommit_hook(const char * newval)859 autocommit_hook(const char *newval)
860 {
861 	return ParseVariableBool(newval, "AUTOCOMMIT", &pset.autocommit);
862 }
863 
864 static bool
on_error_stop_hook(const char * newval)865 on_error_stop_hook(const char *newval)
866 {
867 	return ParseVariableBool(newval, "ON_ERROR_STOP", &pset.on_error_stop);
868 }
869 
870 static bool
quiet_hook(const char * newval)871 quiet_hook(const char *newval)
872 {
873 	return ParseVariableBool(newval, "QUIET", &pset.quiet);
874 }
875 
876 static bool
singleline_hook(const char * newval)877 singleline_hook(const char *newval)
878 {
879 	return ParseVariableBool(newval, "SINGLELINE", &pset.singleline);
880 }
881 
882 static bool
singlestep_hook(const char * newval)883 singlestep_hook(const char *newval)
884 {
885 	return ParseVariableBool(newval, "SINGLESTEP", &pset.singlestep);
886 }
887 
888 static char *
fetch_count_substitute_hook(char * newval)889 fetch_count_substitute_hook(char *newval)
890 {
891 	if (newval == NULL)
892 		newval = pg_strdup("0");
893 	return newval;
894 }
895 
896 static bool
fetch_count_hook(const char * newval)897 fetch_count_hook(const char *newval)
898 {
899 	return ParseVariableNum(newval, "FETCH_COUNT", &pset.fetch_count);
900 }
901 
902 static bool
histfile_hook(const char * newval)903 histfile_hook(const char *newval)
904 {
905 	/*
906 	 * Someday we might try to validate the filename, but for now, this is
907 	 * just a placeholder to ensure HISTFILE is known to tab completion.
908 	 */
909 	return true;
910 }
911 
912 static char *
histsize_substitute_hook(char * newval)913 histsize_substitute_hook(char *newval)
914 {
915 	if (newval == NULL)
916 		newval = pg_strdup("500");
917 	return newval;
918 }
919 
920 static bool
histsize_hook(const char * newval)921 histsize_hook(const char *newval)
922 {
923 	return ParseVariableNum(newval, "HISTSIZE", &pset.histsize);
924 }
925 
926 static char *
ignoreeof_substitute_hook(char * newval)927 ignoreeof_substitute_hook(char *newval)
928 {
929 	int			dummy;
930 
931 	/*
932 	 * This tries to mimic the behavior of bash, to wit "If set, the value is
933 	 * the number of consecutive EOF characters which must be typed as the
934 	 * first characters on an input line before bash exits.  If the variable
935 	 * exists but does not have a numeric value, or has no value, the default
936 	 * value is 10.  If it does not exist, EOF signifies the end of input to
937 	 * the shell."  Unlike bash, however, we insist on the stored value
938 	 * actually being a valid integer.
939 	 */
940 	if (newval == NULL)
941 		newval = pg_strdup("0");
942 	else if (!ParseVariableNum(newval, NULL, &dummy))
943 		newval = pg_strdup("10");
944 	return newval;
945 }
946 
947 static bool
ignoreeof_hook(const char * newval)948 ignoreeof_hook(const char *newval)
949 {
950 	return ParseVariableNum(newval, "IGNOREEOF", &pset.ignoreeof);
951 }
952 
953 static char *
echo_substitute_hook(char * newval)954 echo_substitute_hook(char *newval)
955 {
956 	if (newval == NULL)
957 		newval = pg_strdup("none");
958 	return newval;
959 }
960 
961 static bool
echo_hook(const char * newval)962 echo_hook(const char *newval)
963 {
964 	Assert(newval != NULL);		/* else substitute hook messed up */
965 	if (pg_strcasecmp(newval, "queries") == 0)
966 		pset.echo = PSQL_ECHO_QUERIES;
967 	else if (pg_strcasecmp(newval, "errors") == 0)
968 		pset.echo = PSQL_ECHO_ERRORS;
969 	else if (pg_strcasecmp(newval, "all") == 0)
970 		pset.echo = PSQL_ECHO_ALL;
971 	else if (pg_strcasecmp(newval, "none") == 0)
972 		pset.echo = PSQL_ECHO_NONE;
973 	else
974 	{
975 		PsqlVarEnumError("ECHO", newval, "none, errors, queries, all");
976 		return false;
977 	}
978 	return true;
979 }
980 
981 static bool
echo_hidden_hook(const char * newval)982 echo_hidden_hook(const char *newval)
983 {
984 	Assert(newval != NULL);		/* else substitute hook messed up */
985 	if (pg_strcasecmp(newval, "noexec") == 0)
986 		pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC;
987 	else
988 	{
989 		bool		on_off;
990 
991 		if (ParseVariableBool(newval, NULL, &on_off))
992 			pset.echo_hidden = on_off ? PSQL_ECHO_HIDDEN_ON : PSQL_ECHO_HIDDEN_OFF;
993 		else
994 		{
995 			PsqlVarEnumError("ECHO_HIDDEN", newval, "on, off, noexec");
996 			return false;
997 		}
998 	}
999 	return true;
1000 }
1001 
1002 static bool
on_error_rollback_hook(const char * newval)1003 on_error_rollback_hook(const char *newval)
1004 {
1005 	Assert(newval != NULL);		/* else substitute hook messed up */
1006 	if (pg_strcasecmp(newval, "interactive") == 0)
1007 		pset.on_error_rollback = PSQL_ERROR_ROLLBACK_INTERACTIVE;
1008 	else
1009 	{
1010 		bool		on_off;
1011 
1012 		if (ParseVariableBool(newval, NULL, &on_off))
1013 			pset.on_error_rollback = on_off ? PSQL_ERROR_ROLLBACK_ON : PSQL_ERROR_ROLLBACK_OFF;
1014 		else
1015 		{
1016 			PsqlVarEnumError("ON_ERROR_ROLLBACK", newval, "on, off, interactive");
1017 			return false;
1018 		}
1019 	}
1020 	return true;
1021 }
1022 
1023 static char *
comp_keyword_case_substitute_hook(char * newval)1024 comp_keyword_case_substitute_hook(char *newval)
1025 {
1026 	if (newval == NULL)
1027 		newval = pg_strdup("preserve-upper");
1028 	return newval;
1029 }
1030 
1031 static bool
comp_keyword_case_hook(const char * newval)1032 comp_keyword_case_hook(const char *newval)
1033 {
1034 	Assert(newval != NULL);		/* else substitute hook messed up */
1035 	if (pg_strcasecmp(newval, "preserve-upper") == 0)
1036 		pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER;
1037 	else if (pg_strcasecmp(newval, "preserve-lower") == 0)
1038 		pset.comp_case = PSQL_COMP_CASE_PRESERVE_LOWER;
1039 	else if (pg_strcasecmp(newval, "upper") == 0)
1040 		pset.comp_case = PSQL_COMP_CASE_UPPER;
1041 	else if (pg_strcasecmp(newval, "lower") == 0)
1042 		pset.comp_case = PSQL_COMP_CASE_LOWER;
1043 	else
1044 	{
1045 		PsqlVarEnumError("COMP_KEYWORD_CASE", newval,
1046 						 "lower, upper, preserve-lower, preserve-upper");
1047 		return false;
1048 	}
1049 	return true;
1050 }
1051 
1052 static char *
histcontrol_substitute_hook(char * newval)1053 histcontrol_substitute_hook(char *newval)
1054 {
1055 	if (newval == NULL)
1056 		newval = pg_strdup("none");
1057 	return newval;
1058 }
1059 
1060 static bool
histcontrol_hook(const char * newval)1061 histcontrol_hook(const char *newval)
1062 {
1063 	Assert(newval != NULL);		/* else substitute hook messed up */
1064 	if (pg_strcasecmp(newval, "ignorespace") == 0)
1065 		pset.histcontrol = hctl_ignorespace;
1066 	else if (pg_strcasecmp(newval, "ignoredups") == 0)
1067 		pset.histcontrol = hctl_ignoredups;
1068 	else if (pg_strcasecmp(newval, "ignoreboth") == 0)
1069 		pset.histcontrol = hctl_ignoreboth;
1070 	else if (pg_strcasecmp(newval, "none") == 0)
1071 		pset.histcontrol = hctl_none;
1072 	else
1073 	{
1074 		PsqlVarEnumError("HISTCONTROL", newval,
1075 						 "none, ignorespace, ignoredups, ignoreboth");
1076 		return false;
1077 	}
1078 	return true;
1079 }
1080 
1081 static bool
prompt1_hook(const char * newval)1082 prompt1_hook(const char *newval)
1083 {
1084 	pset.prompt1 = newval ? newval : "";
1085 	return true;
1086 }
1087 
1088 static bool
prompt2_hook(const char * newval)1089 prompt2_hook(const char *newval)
1090 {
1091 	pset.prompt2 = newval ? newval : "";
1092 	return true;
1093 }
1094 
1095 static bool
prompt3_hook(const char * newval)1096 prompt3_hook(const char *newval)
1097 {
1098 	pset.prompt3 = newval ? newval : "";
1099 	return true;
1100 }
1101 
1102 static char *
verbosity_substitute_hook(char * newval)1103 verbosity_substitute_hook(char *newval)
1104 {
1105 	if (newval == NULL)
1106 		newval = pg_strdup("default");
1107 	return newval;
1108 }
1109 
1110 static bool
verbosity_hook(const char * newval)1111 verbosity_hook(const char *newval)
1112 {
1113 	Assert(newval != NULL);		/* else substitute hook messed up */
1114 	if (pg_strcasecmp(newval, "default") == 0)
1115 		pset.verbosity = PQERRORS_DEFAULT;
1116 	else if (pg_strcasecmp(newval, "verbose") == 0)
1117 		pset.verbosity = PQERRORS_VERBOSE;
1118 	else if (pg_strcasecmp(newval, "terse") == 0)
1119 		pset.verbosity = PQERRORS_TERSE;
1120 	else if (pg_strcasecmp(newval, "sqlstate") == 0)
1121 		pset.verbosity = PQERRORS_SQLSTATE;
1122 	else
1123 	{
1124 		PsqlVarEnumError("VERBOSITY", newval, "default, verbose, terse, sqlstate");
1125 		return false;
1126 	}
1127 
1128 	if (pset.db)
1129 		PQsetErrorVerbosity(pset.db, pset.verbosity);
1130 	return true;
1131 }
1132 
1133 static char *
show_context_substitute_hook(char * newval)1134 show_context_substitute_hook(char *newval)
1135 {
1136 	if (newval == NULL)
1137 		newval = pg_strdup("errors");
1138 	return newval;
1139 }
1140 
1141 static bool
show_context_hook(const char * newval)1142 show_context_hook(const char *newval)
1143 {
1144 	Assert(newval != NULL);		/* else substitute hook messed up */
1145 	if (pg_strcasecmp(newval, "never") == 0)
1146 		pset.show_context = PQSHOW_CONTEXT_NEVER;
1147 	else if (pg_strcasecmp(newval, "errors") == 0)
1148 		pset.show_context = PQSHOW_CONTEXT_ERRORS;
1149 	else if (pg_strcasecmp(newval, "always") == 0)
1150 		pset.show_context = PQSHOW_CONTEXT_ALWAYS;
1151 	else
1152 	{
1153 		PsqlVarEnumError("SHOW_CONTEXT", newval, "never, errors, always");
1154 		return false;
1155 	}
1156 
1157 	if (pset.db)
1158 		PQsetErrorContextVisibility(pset.db, pset.show_context);
1159 	return true;
1160 }
1161 
1162 static bool
hide_compression_hook(const char * newval)1163 hide_compression_hook(const char *newval)
1164 {
1165 	return ParseVariableBool(newval, "HIDE_TOAST_COMPRESSION",
1166 							 &pset.hide_compression);
1167 }
1168 
1169 static bool
hide_tableam_hook(const char * newval)1170 hide_tableam_hook(const char *newval)
1171 {
1172 	return ParseVariableBool(newval, "HIDE_TABLEAM", &pset.hide_tableam);
1173 }
1174 
1175 static void
EstablishVariableSpace(void)1176 EstablishVariableSpace(void)
1177 {
1178 	pset.vars = CreateVariableSpace();
1179 
1180 	SetVariableHooks(pset.vars, "AUTOCOMMIT",
1181 					 bool_substitute_hook,
1182 					 autocommit_hook);
1183 	SetVariableHooks(pset.vars, "ON_ERROR_STOP",
1184 					 bool_substitute_hook,
1185 					 on_error_stop_hook);
1186 	SetVariableHooks(pset.vars, "QUIET",
1187 					 bool_substitute_hook,
1188 					 quiet_hook);
1189 	SetVariableHooks(pset.vars, "SINGLELINE",
1190 					 bool_substitute_hook,
1191 					 singleline_hook);
1192 	SetVariableHooks(pset.vars, "SINGLESTEP",
1193 					 bool_substitute_hook,
1194 					 singlestep_hook);
1195 	SetVariableHooks(pset.vars, "FETCH_COUNT",
1196 					 fetch_count_substitute_hook,
1197 					 fetch_count_hook);
1198 	SetVariableHooks(pset.vars, "HISTFILE",
1199 					 NULL,
1200 					 histfile_hook);
1201 	SetVariableHooks(pset.vars, "HISTSIZE",
1202 					 histsize_substitute_hook,
1203 					 histsize_hook);
1204 	SetVariableHooks(pset.vars, "IGNOREEOF",
1205 					 ignoreeof_substitute_hook,
1206 					 ignoreeof_hook);
1207 	SetVariableHooks(pset.vars, "ECHO",
1208 					 echo_substitute_hook,
1209 					 echo_hook);
1210 	SetVariableHooks(pset.vars, "ECHO_HIDDEN",
1211 					 bool_substitute_hook,
1212 					 echo_hidden_hook);
1213 	SetVariableHooks(pset.vars, "ON_ERROR_ROLLBACK",
1214 					 bool_substitute_hook,
1215 					 on_error_rollback_hook);
1216 	SetVariableHooks(pset.vars, "COMP_KEYWORD_CASE",
1217 					 comp_keyword_case_substitute_hook,
1218 					 comp_keyword_case_hook);
1219 	SetVariableHooks(pset.vars, "HISTCONTROL",
1220 					 histcontrol_substitute_hook,
1221 					 histcontrol_hook);
1222 	SetVariableHooks(pset.vars, "PROMPT1",
1223 					 NULL,
1224 					 prompt1_hook);
1225 	SetVariableHooks(pset.vars, "PROMPT2",
1226 					 NULL,
1227 					 prompt2_hook);
1228 	SetVariableHooks(pset.vars, "PROMPT3",
1229 					 NULL,
1230 					 prompt3_hook);
1231 	SetVariableHooks(pset.vars, "VERBOSITY",
1232 					 verbosity_substitute_hook,
1233 					 verbosity_hook);
1234 	SetVariableHooks(pset.vars, "SHOW_CONTEXT",
1235 					 show_context_substitute_hook,
1236 					 show_context_hook);
1237 	SetVariableHooks(pset.vars, "HIDE_TOAST_COMPRESSION",
1238 					 bool_substitute_hook,
1239 					 hide_compression_hook);
1240 	SetVariableHooks(pset.vars, "HIDE_TABLEAM",
1241 					 bool_substitute_hook,
1242 					 hide_tableam_hook);
1243 }
1244