OSDN Git Service

Update copyright to 2004.
[pg-rex/syncrep.git] / src / bin / psql / startup.c
1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2004, PostgreSQL Global Development Group
5  *
6  * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.98 2004/08/29 04:13:02 momjian Exp $
7  */
8 #include "postgres_fe.h"
9
10 #include <sys/types.h>
11
12 #ifndef WIN32
13 #include <unistd.h>
14 #else                                                   /* WIN32 */
15 #include <io.h>
16 #include <windows.h>
17 #include <win32.h>
18 #endif   /* WIN32 */
19
20 #include "getopt_long.h"
21
22 #ifndef HAVE_OPTRESET
23 int                     optreset;
24 #endif
25
26 #include <locale.h>
27
28 #include "libpq-fe.h"
29
30 #include "command.h"
31 #include "common.h"
32 #include "describe.h"
33 #include "help.h"
34 #include "input.h"
35 #include "mainloop.h"
36 #include "print.h"
37 #include "settings.h"
38 #include "variables.h"
39
40 #include "mb/pg_wchar.h"
41
42 /*
43  * Global psql options
44  */
45 PsqlSettings pset;
46
47 #define SYSPSQLRC       "psqlrc"
48 #define PSQLRC          ".psqlrc"
49
50 /*
51  * Structures to pass information between the option parsing routine
52  * and the main function
53  */
54 enum _actions
55 {
56         ACT_NOTHING = 0,
57         ACT_SINGLE_SLASH,
58         ACT_LIST_DB,
59         ACT_SINGLE_QUERY,
60         ACT_FILE
61 };
62
63 struct adhoc_opts
64 {
65         char       *dbname;
66         char       *host;
67         char       *port;
68         char       *username;
69         enum _actions action;
70         char       *action_string;
71         bool            no_readline;
72         bool            no_psqlrc;
73 };
74
75 static void parse_psql_options(int argc, char *argv[],
76                                    struct adhoc_opts * options);
77 static void process_psqlrc(char *argv0);
78 static void process_psqlrc_file(char *filename);
79 static void showVersion(void);
80
81 #ifdef USE_SSL
82 static void printSSLInfo(void);
83 #endif
84
85 #ifdef WIN32
86 static void
87                         checkWin32Codepage(void);
88 #endif
89
90 /*
91  *
92  * main
93  *
94  */
95 int
96 main(int argc, char *argv[])
97 {
98         struct adhoc_opts options;
99         int                     successResult;
100
101         char       *username = NULL;
102         char       *password = NULL;
103         bool            need_pass;
104
105         set_pglocale_pgservice(argv[0], "psql");
106
107         pset.progname = get_progname(argv[0]);
108
109         if (argc > 1)
110         {
111                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
112                 {
113                         usage();
114                         exit(EXIT_SUCCESS);
115                 }
116                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
117                 {
118                         showVersion();
119                         exit(EXIT_SUCCESS);
120                 }
121         }
122
123 #ifdef WIN32
124         setvbuf(stderr,NULL,_IONBF,0);
125 #endif
126         pset.cur_cmd_source = stdin;
127         pset.cur_cmd_interactive = false;
128         pset.encoding = PQenv2encoding();
129
130         pset.vars = CreateVariableSpace();
131         if (!pset.vars)
132         {
133                 fprintf(stderr, gettext("%s: out of memory\n"), pset.progname);
134                 exit(EXIT_FAILURE);
135         }
136         pset.popt.topt.format = PRINT_ALIGNED;
137         pset.queryFout = stdout;
138         pset.popt.topt.border = 1;
139         pset.popt.topt.pager = 1;
140         pset.popt.default_footer = true;
141
142         SetVariable(pset.vars, "VERSION", PG_VERSION_STR);
143
144         /* Default values for variables that are used in noninteractive cases */
145         SetVariableBool(pset.vars, "AUTOCOMMIT");
146         SetVariable(pset.vars, "VERBOSITY", "default");
147         pset.verbosity = PQERRORS_DEFAULT;
148
149         pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout)));
150
151         /* This is obsolete and should be removed sometime. */
152 #ifdef PSQL_ALWAYS_GET_PASSWORDS
153         pset.getPassword = true;
154 #else
155         pset.getPassword = false;
156 #endif
157
158 #ifndef HAVE_UNIX_SOCKETS
159         /* default to localhost on platforms without unix sockets */
160         options.host = "localhost";
161 #endif
162
163         parse_psql_options(argc, argv, &options);
164
165         if (!pset.popt.topt.fieldSep)
166                 pset.popt.topt.fieldSep = pg_strdup(DEFAULT_FIELD_SEP);
167         if (!pset.popt.topt.recordSep)
168                 pset.popt.topt.recordSep = pg_strdup(DEFAULT_RECORD_SEP);
169
170         if (options.username)
171         {
172                 /*
173                  * The \001 is a hack to support the deprecated -u option which
174                  * issues a username prompt. The recommended option is -U followed
175                  * by the name on the command line.
176                  */
177                 if (strcmp(options.username, "\001") == 0)
178                         username = simple_prompt("User name: ", 100, true);
179                 else
180                         username = pg_strdup(options.username);
181         }
182
183         if (pset.getPassword)
184                 password = simple_prompt("Password: ", 100, false);
185
186         /* loop until we have a password if requested by backend */
187         do
188         {
189                 need_pass = false;
190                 pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL,
191                         options.action == ACT_LIST_DB ? "template1" : options.dbname,
192                                                            username, password);
193
194                 if (PQstatus(pset.db) == CONNECTION_BAD &&
195                         strcmp(PQerrorMessage(pset.db), "fe_sendauth: no password supplied\n") == 0 &&
196                         !feof(stdin))
197                 {
198                         PQfinish(pset.db);
199                         need_pass = true;
200                         free(password);
201                         password = NULL;
202                         password = simple_prompt("Password: ", 100, false);
203                 }
204         } while (need_pass);
205
206         free(username);
207         free(password);
208
209         if (PQstatus(pset.db) == CONNECTION_BAD)
210         {
211                 fprintf(stderr, "%s: %s", pset.progname, PQerrorMessage(pset.db));
212                 PQfinish(pset.db);
213                 exit(EXIT_BADCONN);
214         }
215
216         PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
217
218         SyncVariables();
219
220         /* Grab the backend server version */
221         pset.sversion = PQserverVersion(pset.db);
222
223         if (options.action == ACT_LIST_DB)
224         {
225                 int                     success = listAllDbs(false);
226
227                 PQfinish(pset.db);
228                 exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
229         }
230
231         /*
232          * Now find something to do
233          */
234
235         /*
236          * process file given by -f
237          */
238         if (options.action == ACT_FILE && strcmp(options.action_string, "-") != 0)
239         {
240                 if (!options.no_psqlrc)
241                         process_psqlrc(argv[0]);
242
243                 successResult = process_file(options.action_string);
244         }
245
246         /*
247          * process slash command if one was given to -c
248          */
249         else if (options.action == ACT_SINGLE_SLASH)
250         {
251                 PsqlScanState scan_state;
252
253                 if (VariableEquals(pset.vars, "ECHO", "all"))
254                         puts(options.action_string);
255
256                 scan_state = psql_scan_create();
257                 psql_scan_setup(scan_state,
258                                                 options.action_string,
259                                                 strlen(options.action_string));
260
261                 successResult = HandleSlashCmds(scan_state, NULL) != CMD_ERROR
262                         ? EXIT_SUCCESS : EXIT_FAILURE;
263
264                 psql_scan_destroy(scan_state);
265         }
266
267         /*
268          * If the query given to -c was a normal one, send it
269          */
270         else if (options.action == ACT_SINGLE_QUERY)
271         {
272                 if (VariableEquals(pset.vars, "ECHO", "all"))
273                         puts(options.action_string);
274
275                 successResult = SendQuery(options.action_string)
276                         ? EXIT_SUCCESS : EXIT_FAILURE;
277         }
278
279         /*
280          * or otherwise enter interactive main loop
281          */
282         else
283         {
284                 if (!QUIET() && !pset.notty)
285                 {
286                         printf(gettext("Welcome to %s %s, the PostgreSQL interactive terminal.\n\n"
287                                                    "Type:  \\copyright for distribution terms\n"
288                                                    "       \\h for help with SQL commands\n"
289                                                    "       \\? for help with psql commands\n"
290                                                    "       \\g or terminate with semicolon to execute query\n"
291                                                    "       \\q to quit\n\n"),
292                                    pset.progname, PG_VERSION);
293 #ifdef USE_SSL
294                         printSSLInfo();
295 #endif
296 #ifdef WIN32
297                         checkWin32Codepage();
298 #endif
299                 }
300
301                 /* Default values for variables that are used in interactive case */
302                 SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1);
303                 SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2);
304                 SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3);
305
306                 if (!options.no_psqlrc)
307                         process_psqlrc(argv[0]);
308                 if (!pset.notty)
309                         initializeInput(options.no_readline ? 0 : 1);
310                 if (options.action_string)              /* -f - was used */
311                         pset.inputfile = "<stdin>";
312
313                 successResult = MainLoop(stdin);
314         }
315
316         /* clean up */
317         PQfinish(pset.db);
318         setQFout(NULL);
319
320         return successResult;
321 }
322
323
324
325 /*
326  * Parse command line options
327  */
328
329 static void
330 parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
331 {
332         static struct option long_options[] =
333         {
334                 {"echo-all", no_argument, NULL, 'a'},
335                 {"no-align", no_argument, NULL, 'A'},
336                 {"command", required_argument, NULL, 'c'},
337                 {"dbname", required_argument, NULL, 'd'},
338                 {"echo-queries", no_argument, NULL, 'e'},
339                 {"echo-hidden", no_argument, NULL, 'E'},
340                 {"file", required_argument, NULL, 'f'},
341                 {"field-separator", required_argument, NULL, 'F'},
342                 {"host", required_argument, NULL, 'h'},
343                 {"html", no_argument, NULL, 'H'},
344                 {"list", no_argument, NULL, 'l'},
345                 {"no-readline", no_argument, NULL, 'n'},
346                 {"output", required_argument, NULL, 'o'},
347                 {"port", required_argument, NULL, 'p'},
348                 {"pset", required_argument, NULL, 'P'},
349                 {"quiet", no_argument, NULL, 'q'},
350                 {"record-separator", required_argument, NULL, 'R'},
351                 {"single-step", no_argument, NULL, 's'},
352                 {"single-line", no_argument, NULL, 'S'},
353                 {"tuples-only", no_argument, NULL, 't'},
354                 {"table-attr", required_argument, NULL, 'T'},
355                 {"username", required_argument, NULL, 'U'},
356                 {"set", required_argument, NULL, 'v'},
357                 {"variable", required_argument, NULL, 'v'},
358                 {"version", no_argument, NULL, 'V'},
359                 {"password", no_argument, NULL, 'W'},
360                 {"expanded", no_argument, NULL, 'x'},
361                 {"no-psqlrc", no_argument, NULL, 'X'},
362                 {"help", no_argument, NULL, '?'},
363                 {NULL, 0, NULL, 0}
364         };
365
366         int                     optindex;
367         extern char *optarg;
368         extern int      optind;
369         int                     c;
370         bool            used_old_u_option = false;
371
372         memset(options, 0, sizeof *options);
373
374         while ((c = getopt_long(argc, argv, "aAc:d:eEf:F:h:Hlno:p:P:qR:sStT:uU:v:VWxX?",
375                                                         long_options, &optindex)) != -1)
376         {
377                 switch (c)
378                 {
379                         case 'a':
380                                 SetVariable(pset.vars, "ECHO", "all");
381                                 break;
382                         case 'A':
383                                 pset.popt.topt.format = PRINT_UNALIGNED;
384                                 break;
385                         case 'c':
386                                 options->action_string = optarg;
387                                 if (optarg[0] == '\\')
388                                 {
389                                         options->action = ACT_SINGLE_SLASH;
390                                         options->action_string++;
391                                 }
392                                 else
393                                         options->action = ACT_SINGLE_QUERY;
394                                 break;
395                         case 'd':
396                                 options->dbname = optarg;
397                                 break;
398                         case 'e':
399                                 SetVariable(pset.vars, "ECHO", "queries");
400                                 break;
401                         case 'E':
402                                 SetVariableBool(pset.vars, "ECHO_HIDDEN");
403                                 break;
404                         case 'f':
405                                 options->action = ACT_FILE;
406                                 options->action_string = optarg;
407                                 break;
408                         case 'F':
409                                 pset.popt.topt.fieldSep = pg_strdup(optarg);
410                                 break;
411                         case 'h':
412                                 options->host = optarg;
413                                 break;
414                         case 'H':
415                                 pset.popt.topt.format = PRINT_HTML;
416                                 break;
417                         case 'l':
418                                 options->action = ACT_LIST_DB;
419                                 break;
420                         case 'n':
421                                 options->no_readline = true;
422                                 break;
423                         case 'o':
424                                 setQFout(optarg);
425                                 break;
426                         case 'p':
427                                 options->port = optarg;
428                                 break;
429                         case 'P':
430                                 {
431                                         char       *value;
432                                         char       *equal_loc;
433                                         bool            result;
434
435                                         value = pg_strdup(optarg);
436                                         equal_loc = strchr(value, '=');
437                                         if (!equal_loc)
438                                                 result = do_pset(value, NULL, &pset.popt, true);
439                                         else
440                                         {
441                                                 *equal_loc = '\0';
442                                                 result = do_pset(value, equal_loc + 1, &pset.popt, true);
443                                         }
444
445                                         if (!result)
446                                         {
447                                                 fprintf(stderr, gettext("%s: couldn't set printing parameter \"%s\"\n"), pset.progname, value);
448                                                 exit(EXIT_FAILURE);
449                                         }
450
451                                         free(value);
452                                         break;
453                                 }
454                         case 'q':
455                                 SetVariableBool(pset.vars, "QUIET");
456                                 break;
457                         case 'R':
458                                 pset.popt.topt.recordSep = pg_strdup(optarg);
459                                 break;
460                         case 's':
461                                 SetVariableBool(pset.vars, "SINGLESTEP");
462                                 break;
463                         case 'S':
464                                 SetVariableBool(pset.vars, "SINGLELINE");
465                                 break;
466                         case 't':
467                                 pset.popt.topt.tuples_only = true;
468                                 break;
469                         case 'T':
470                                 pset.popt.topt.tableAttr = pg_strdup(optarg);
471                                 break;
472                         case 'u':
473                                 pset.getPassword = true;
474                                 options->username = "\001";             /* hopefully nobody has
475                                                                                                  * that username */
476                                 /* this option is out */
477                                 used_old_u_option = true;
478                                 break;
479                         case 'U':
480                                 options->username = optarg;
481                                 break;
482                         case 'v':
483                                 {
484                                         char       *value;
485                                         char       *equal_loc;
486
487                                         value = pg_strdup(optarg);
488                                         equal_loc = strchr(value, '=');
489                                         if (!equal_loc)
490                                         {
491                                                 if (!DeleteVariable(pset.vars, value))
492                                                 {
493                                                         fprintf(stderr, gettext("%s: could not delete variable \"%s\"\n"),
494                                                                         pset.progname, value);
495                                                         exit(EXIT_FAILURE);
496                                                 }
497                                         }
498                                         else
499                                         {
500                                                 *equal_loc = '\0';
501                                                 if (!SetVariable(pset.vars, value, equal_loc + 1))
502                                                 {
503                                                         fprintf(stderr, gettext("%s: could not set variable \"%s\"\n"),
504                                                                         pset.progname, value);
505                                                         exit(EXIT_FAILURE);
506                                                 }
507                                         }
508
509                                         free(value);
510                                         break;
511                                 }
512                         case 'V':
513                                 showVersion();
514                                 exit(EXIT_SUCCESS);
515                         case 'W':
516                                 pset.getPassword = true;
517                                 break;
518                         case 'x':
519                                 pset.popt.topt.expanded = true;
520                                 break;
521                         case 'X':
522                                 options->no_psqlrc = true;
523                                 break;
524                         case '?':
525                                 /* Actual help option given */
526                                 if (strcmp(argv[optind - 1], "-?") == 0 || strcmp(argv[optind - 1], "--help") == 0)
527                                 {
528                                         usage();
529                                         exit(EXIT_SUCCESS);
530                                 }
531                                 /* unknown option reported by getopt */
532                                 else
533                                 {
534                                         fprintf(stderr, gettext("Try \"%s --help\" for more information.\n"),
535                                                         pset.progname);
536                                         exit(EXIT_FAILURE);
537                                 }
538                                 break;
539                         default:
540                                 fprintf(stderr, gettext("Try \"%s --help\" for more information.\n"),
541                                                 pset.progname);
542                                 exit(EXIT_FAILURE);
543                                 break;
544                 }
545         }
546
547         /*
548          * if we still have arguments, use it as the database name and
549          * username
550          */
551         while (argc - optind >= 1)
552         {
553                 if (!options->dbname)
554                         options->dbname = argv[optind];
555                 else if (!options->username)
556                         options->username = argv[optind];
557                 else if (!QUIET())
558                         fprintf(stderr, gettext("%s: warning: extra command-line argument \"%s\" ignored\n"),
559                                         pset.progname, argv[optind]);
560
561                 optind++;
562         }
563
564         if (used_old_u_option && !QUIET())
565                 fprintf(stderr, gettext("%s: Warning: The -u option is deprecated. Use -U.\n"), pset.progname);
566
567 }
568
569
570 /*
571  * Load .psqlrc file, if found.
572  */
573 static void
574 process_psqlrc(char *argv0)
575 {
576         char       *psqlrc;
577         char       home[MAXPGPATH];
578         char       global_file[MAXPGPATH];
579         char       my_exec_path[MAXPGPATH];
580         char       etc_path[MAXPGPATH];
581
582         find_my_exec(argv0, my_exec_path);
583         get_etc_path(my_exec_path, etc_path);
584
585         snprintf(global_file, MAXPGPATH, "%s/%s", etc_path, SYSPSQLRC);
586         process_psqlrc_file(global_file);
587
588         if (get_home_path(home))
589         {
590                 psqlrc = pg_malloc(strlen(home) + 1 + strlen(PSQLRC) + 1);
591                 sprintf(psqlrc, "%s/%s", home, PSQLRC);
592                 process_psqlrc_file(psqlrc);
593         }
594 }
595
596
597
598 static void
599 process_psqlrc_file(char *filename)
600 {
601         char       *psqlrc;
602
603 #if defined(WIN32) && (!defined(__MINGW32__))
604 #define R_OK 4
605 #endif
606
607         psqlrc = pg_malloc(strlen(filename) + 1 + strlen(PG_VERSION) + 1);
608         sprintf(psqlrc, "%s-%s", filename, PG_VERSION);
609
610         if (access(psqlrc, R_OK) == 0)
611                 process_file(psqlrc);
612         else if (access(filename, R_OK) == 0)
613                         process_file(filename);
614         free(psqlrc);
615 }
616
617
618
619 /* showVersion
620  *
621  * This output format is intended to match GNU standards.
622  */
623 static void
624 showVersion(void)
625 {
626         puts("psql (PostgreSQL) " PG_VERSION);
627
628 #if defined(USE_READLINE)
629         puts(gettext("contains support for command-line editing"));
630 #endif
631 }
632
633
634
635 /*
636  * printSSLInfo
637  *
638  * Prints information about the current SSL connection, if SSL is in use
639  */
640 #ifdef USE_SSL
641 static void
642 printSSLInfo(void)
643 {
644         int                     sslbits = -1;
645         SSL                *ssl;
646
647         ssl = PQgetssl(pset.db);
648         if (!ssl)
649                 return;                                 /* no SSL */
650
651         SSL_get_cipher_bits(ssl, &sslbits);
652         printf(gettext("SSL connection (cipher: %s, bits: %i)\n\n"),
653                    SSL_get_cipher(ssl), sslbits);
654 }
655
656 #endif
657
658
659
660 /*
661  * checkWin32Codepage
662  *
663  * Prints a warning when win32 console codepage differs from Windows codepage
664  */
665 #ifdef WIN32
666 static void
667 checkWin32Codepage(void)
668 {
669         unsigned int wincp, concp;
670
671         wincp = GetACP();
672         concp = GetConsoleCP();
673         if (wincp != concp) {
674           printf("Warning: Console codepage (%u) differs from windows codepage (%u)\n"
675                          "         8-bit characters will not work correctly. See PostgreSQL\n"
676                          "         documentation \"Installation on Windows\" for details.\n\n",
677                          concp, wincp);
678         }
679 }
680 #endif