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