LCOV - code coverage report
Current view: top level - src/bin/psql - startup.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 281 496 56.7 %
Date: 2017-09-29 13:40:31 Functions: 32 35 91.4 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.11