]> granicus.if.org Git - postgresql/blob - src/bin/psql/psql.c
Okay, this should pretty much clean up the psql/readline/history mess.
[postgresql] / src / bin / psql / psql.c
1 /*-------------------------------------------------------------------------
2  *
3  * psql.c--
4  *    an interactive front-end to postgreSQL
5  *
6  * Copyright (c) 1996, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *    $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.53 1997/01/25 22:51:56 scrappy Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <stdio.h>
15 #include <string.h>
16 #include <signal.h>
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <sys/param.h>  /* for MAXPATHLEN */
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <ctype.h>
24 #include "postgres.h"
25 #include "libpq-fe.h"
26 #include "pqsignal.h"
27 #include "stringutils.h"
28 #include "psqlHelp.h"
29 #ifdef NEED_STRDUP
30 #include "strdup.h"
31 #endif
32
33 #ifndef HAVE_LIBREADLINE
34 # include "rlstubs.h"
35 #else
36 # ifdef HAVE_READLINE_H
37 #  include <readline.h>
38 #  if defined(HAVE_HISTORY) || defined(HAVE_LIBHISTORY)
39 #   include <history.h>
40 #  endif
41 # else
42 #  include <readline/readline.h>
43 #  if defined(HAVE_HISTORY) || defined(HAVE_LIBHISTORY)
44 #   include <readline/history.h>
45 #  endif
46 # endif
47 #endif
48
49 #define PROMPT "=> "
50
51 #define MAX_QUERY_BUFFER 20000
52
53 #define COPYBUFSIZ  8192
54
55 #define DEFAULT_FIELD_SEP "|"
56 #define DEFAULT_EDITOR  "vi"
57 #define DEFAULT_SHELL  "/bin/sh"
58
59 typedef struct _psqlSettings {
60     PGconn         *db;         /* connection to backend */
61     FILE           *queryFout;  /* where to send the query results */
62     PQprintOpt      opt;        /* options to be passed to PQprint */
63     char           *prompt;     /* prompt to display */
64     char           *gfname;     /* one-shot file output argument for \g */
65     bool            notty;      /* input or output is not a tty */
66     bool            pipe;       /* queryFout is from a popen() */
67     bool            echoQuery;  /* echo the query before sending it */
68     bool            quiet;      /* run quietly, no messages, no promt */
69     bool            singleStep; /* prompt before for each query */
70     bool            singleLineMode;     /* query terminated by newline */
71     bool            useReadline;/* use libreadline routines */
72 }               PsqlSettings;
73
74 /* declarations for functions in this file */
75 static void     usage(char *progname);
76 static void     slashUsage();
77 static void     handleCopyOut(PGresult * res, bool quiet, FILE * copystream);
78 static void
79 handleCopyIn(PGresult * res, const bool mustprompt,
80              FILE * copystream);
81 static int      tableList(PsqlSettings * ps, bool deep_tablelist);
82 static int      tableDesc(PsqlSettings * ps, char *table);
83
84 char           *gets_noreadline(char *prompt, FILE * source);
85 char           *gets_readline(char *prompt, FILE * source);
86 char           *gets_fromFile(char *prompt, FILE * source);
87 int             listAllDbs(PsqlSettings * settings);
88 void
89 SendQuery(bool * success_p, PsqlSettings * settings, const char *query,
90           const bool copy_in, const bool copy_out, FILE * copystream);
91 int
92 HandleSlashCmds(PsqlSettings * settings,
93                 char *line,
94                 char *query);
95 int             MainLoop(PsqlSettings * settings, FILE * source);
96 /* probably should move this into libpq */
97 void
98 PQprint(FILE * fp,
99         PGresult * res,
100         PQprintOpt * po
101 );
102
103 FILE           *setFout(PsqlSettings * ps, char *fname);
104
105 /*
106  * usage print out usage for command line arguments
107  */
108
109 static void
110 usage(char *progname)
111 {
112     fprintf(stderr, "Usage: %s [options] [dbname]\n", progname);
113     fprintf(stderr, "\t -a authsvc              set authentication service\n");
114     fprintf(stderr, "\t -A                      turn off alignment when printing out attributes\n");
115     fprintf(stderr, "\t -c query                run single query (slash commands too)\n");
116     fprintf(stderr, "\t -d dbName               specify database name\n");
117     fprintf(stderr, "\t -e                      echo the query sent to the backend\n");
118     fprintf(stderr, "\t -f filename             use file as a source of queries\n");
119     fprintf(stderr, "\t -F sep                  set the field separator (default is " ")\n");
120     fprintf(stderr, "\t -h host                 set database server host\n");
121     fprintf(stderr, "\t -H                      turn on html3.0 table output\n");
122     fprintf(stderr, "\t -l                      list available databases\n");
123     fprintf(stderr, "\t -n                      don't use readline library\n");
124     fprintf(stderr, "\t -o filename             send output to filename or (|pipe)\n");
125     fprintf(stderr, "\t -p port                 set port number\n");
126     fprintf(stderr, "\t -q                      run quietly (no messages, no prompts)\n");
127     fprintf(stderr, "\t -s                      single step mode (prompts for each query)\n");
128     fprintf(stderr, "\t -S                      single line mode (i.e. query terminated by newline)\n");
129     fprintf(stderr, "\t -t                      turn off printing of headings and row count\n");
130     fprintf(stderr, "\t -T html                 set html3.0 table command options (cf. -H)\n");
131     fprintf(stderr, "\t -x                      turn on expanded output (field names on left)\n");
132     exit(1);
133 }
134
135 /*
136  * slashUsage print out usage for the backslash commands
137  */
138
139 static char    *
140 on(bool f)
141 {
142     return f ? "on" : "off";
143 }
144
145 static void
146 slashUsage(PsqlSettings * ps)
147 {
148     fprintf(stderr, " \\?           -- help\n");
149     fprintf(stderr, " \\a           -- toggle field-alignment (currenty %s)\n", on(ps->opt.align));
150     fprintf(stderr, " \\C [<captn>] -- set html3 caption (currently '%s')\n", ps->opt.caption ? ps->opt.caption : "");
151     fprintf(stderr, " \\connect <dbname>  -- connect to new database (currently '%s')\n", PQdb(ps->db));
152     fprintf(stderr, " \\copy {<table> to <file> | <file> from <table>}\n");
153     fprintf(stderr, " \\d [<table>] -- list tables in database or columns in <table>, * for all\n");
154     fprintf(stderr, " \\e [<fname>] -- edit the current query buffer or <fname>, \\E execute too\n");
155     fprintf(stderr, " \\f [<sep>]   -- change field separater (currently '%s')\n", ps->opt.fieldSep);
156     fprintf(stderr, " \\g [<fname>] [|<cmd>] -- send query to backend [and results in <fname> or pipe]\n");
157     fprintf(stderr, " \\h [<cmd>]   -- help on syntax of sql commands, * for all commands\n");
158     fprintf(stderr, " \\H           -- toggle html3 output (currently %s)\n", on(ps->opt.html3));
159     fprintf(stderr, " \\i <fname>   -- read and execute queries from filename\n");
160     fprintf(stderr, " \\l           -- list all databases\n");
161     fprintf(stderr, " \\m           -- toggle monitor-like table display (currently %s)\n", on(ps->opt.standard));
162     fprintf(stderr, " \\o [<fname>] [|<cmd>] -- send all query results to stdout, <fname>, or pipe\n");
163     fprintf(stderr, " \\p           -- print the current query buffer\n");
164     fprintf(stderr, " \\q           -- quit\n");
165     fprintf(stderr, " \\r           -- reset(clear) the query buffer\n");
166     fprintf(stderr, " \\s [<fname>] -- print history or save it in <fname>\n");
167     fprintf(stderr, " \\t           -- toggle table headings and row count (currently %s)\n", on(ps->opt.header));
168     fprintf(stderr, " \\T [<html>]  -- set html3.0 <table ...> options (currently '%s')\n", ps->opt.tableOpt ? ps->opt.tableOpt : "");
169     fprintf(stderr, " \\x           -- toggle expanded output (currently %s)\n", on(ps->opt.expanded));
170     fprintf(stderr, " \\! [<cmd>]   -- shell escape or command\n");
171 }
172
173 static PGresult *
174 PSQLexec(PsqlSettings * ps, char *query)
175 {
176     PGresult       *res = PQexec(ps->db, query);
177     if (!res)
178         fputs(PQerrorMessage(ps->db), stderr);
179     else {
180         if (PQresultStatus(res) == PGRES_COMMAND_OK ||
181             PQresultStatus(res) == PGRES_TUPLES_OK)
182             return res;
183         if (!ps->quiet)
184             fputs(PQerrorMessage(ps->db), stderr);
185         PQclear(res);
186     }
187     return NULL;
188 }
189 /*
190  * listAllDbs
191  * 
192  * list all the databases in the system returns 0 if all went well
193  * 
194  * 
195  */
196
197 int
198 listAllDbs(PsqlSettings * ps)
199 {
200     PGresult       *results;
201     char           *query = "select * from pg_database;";
202
203     if (!(results = PSQLexec(ps, query)))
204         return 1;
205     else {
206         PQprint(ps->queryFout,
207                 results,
208                 &ps->opt);
209         PQclear(results);
210         return 0;
211     }
212 }
213
214 /*
215  * List The Database Tables returns 0 if all went well
216  * 
217  */
218 int
219 tableList(PsqlSettings * ps, bool deep_tablelist)
220 {
221     char            listbuf[256];
222     int             nColumns;
223     int             i;
224     char           *rk;
225     char           *rr;
226
227     PGresult       *res;
228
229     listbuf[0] = '\0';
230     strcat(listbuf, "SELECT usename, relname, relkind, relhasrules");
231     strcat(listbuf, "  FROM pg_class, pg_user ");
232     strcat(listbuf, "WHERE ( relkind = 'r' OR relkind = 'i') ");
233     strcat(listbuf, "  and relname !~ '^pg_'");
234     strcat(listbuf, "  and relname !~ '^Inv[0-9]+'");
235     /*
236      * the usesysid = relowner won't work on stock 1.0 dbs, need to add in
237      * the int4oideq function
238      */
239     strcat(listbuf, "  and usesysid = relowner");
240     strcat(listbuf, "  ORDER BY relname ");
241     if (!(res = PSQLexec(ps, listbuf)))
242         return -1;
243
244     /* first, print out the attribute names */
245     nColumns = PQntuples(res);
246     if (nColumns > 0) {
247         if (deep_tablelist) {
248             /* describe everything here */
249             char          **table;
250             table = (char **) malloc(nColumns * sizeof(char *));
251             if (table == NULL)
252                 perror("malloc");
253
254             /* load table table */
255             for (i = 0; i < nColumns; i++) {
256                 table[i] = (char *) malloc(PQgetlength(res, i, 1) * sizeof(char) + 1);
257                 if (table[i] == NULL)
258                     perror("malloc");
259                 strcpy(table[i], PQgetvalue(res, i, 1));
260             }
261
262             PQclear(res);
263             for (i = 0; i < nColumns; i++) {
264                 tableDesc(ps, table[i]);
265             }
266             free(table);
267         } else {
268             /* Display the information */
269
270             printf("\nDatabase    = %s\n", PQdb(ps->db));
271             printf(" +------------------+----------------------------------+----------+\n");
272             printf(" |  Owner           |             Relation             |   Type   |\n");
273             printf(" +------------------+----------------------------------+----------+\n");
274
275             /* next, print out the instances */
276             for (i = 0; i < PQntuples(res); i++) {
277                 printf(" | %-16.16s", PQgetvalue(res, i, 0));
278                 printf(" | %-32.32s | ", PQgetvalue(res, i, 1));
279                 rk = PQgetvalue(res, i, 2);
280                 rr = PQgetvalue(res, i, 3);
281                 if (strcmp(rk, "r") == 0)
282                     printf("%-8.8s |", (rr[0] == 't') ? "view?" : "table");
283                 else
284                     printf("%-8.8s |", "index");
285                 printf("\n");
286             }
287             printf(" +------------------+----------------------------------+----------+\n");
288             PQclear(res);
289         }
290         return (0);
291
292     } else {
293         fprintf(stderr, "Couldn't find any tables!\n");
294         return (-1);
295     }
296 }
297
298 /*
299  * Describe a table
300  * 
301  * Describe the columns in a database table. returns 0 if all went well
302  * 
303  * 
304  */
305 int
306 tableDesc(PsqlSettings * ps, char *table)
307 {
308     char            descbuf[256];
309     int             nColumns;
310     char           *rtype;
311     int             i;
312     int             rsize;
313
314     PGresult       *res;
315
316     /* Build the query */
317
318     descbuf[0] = '\0';
319     strcat(descbuf, "SELECT a.attnum, a.attname, t.typname, a.attlen");
320     strcat(descbuf, "  FROM pg_class c, pg_attribute a, pg_type t ");
321     strcat(descbuf, "    WHERE c.relname = '");
322     strcat(descbuf, table);
323     strcat(descbuf, "'");
324     strcat(descbuf, "    and a.attnum > 0 ");
325     strcat(descbuf, "    and a.attrelid = c.oid ");
326     strcat(descbuf, "    and a.atttypid = t.oid ");
327     strcat(descbuf, "  ORDER BY attnum ");
328     if (!(res = PSQLexec(ps, descbuf)))
329         return -1;
330     /* first, print out the attribute names */
331     nColumns = PQntuples(res);
332     if (nColumns > 0) {
333         /*
334          * * Display the information
335          */
336
337         printf("\nTable    = %s\n", table);
338         printf("+----------------------------------+----------------------------------+-------+\n");
339         printf("|              Field               |              Type                | Length|\n");
340         printf("+----------------------------------+----------------------------------+-------+\n");
341
342         /* next, print out the instances */
343         for (i = 0; i < PQntuples(res); i++) {
344             printf("| %-32.32s | ", PQgetvalue(res, i, 1));
345             rtype = PQgetvalue(res, i, 2);
346             rsize = atoi(PQgetvalue(res, i, 3));
347             if (strcmp(rtype, "text") == 0) {
348                 printf("%-32.32s |", rtype);
349                 printf("%6s |", "var");
350             } else if (strcmp(rtype, "bpchar") == 0) {
351                 printf("%-32.32s |", "(bp)char");
352                 printf("%6i |", rsize > 0 ? rsize - 4 : 0);
353             } else if (strcmp(rtype, "varchar") == 0) {
354                 printf("%-32.32s |", rtype);
355                 printf("%6i |", rsize > 0 ? rsize - 4 : 0);
356             } else {
357                 /* array types start with an underscore */
358                 if (rtype[0] != '_')
359                     printf("%-32.32s |", rtype);
360                 else {
361                     char           *newname;
362                     newname = malloc(strlen(rtype) + 2);
363                     strcpy(newname, rtype + 1);
364                     strcat(newname, "[]");
365                     printf("%-32.32s |", newname);
366                     free(newname);
367                 }
368                 if (rsize > 0)
369                     printf("%6i |", rsize);
370                 else
371                     printf("%6s |", "var");
372             }
373             printf("\n");
374         }
375         printf("+----------------------------------+----------------------------------+-------+\n");
376
377         PQclear(res);
378         return (0);
379
380     } else {
381         fprintf(stderr, "Couldn't find table %s!\n", table);
382         return (-1);
383     }
384 }
385
386 typedef char   *(*READ_ROUTINE) (char *prompt, FILE * source);
387
388 /*
389  * gets_noreadline  prompt source gets a line of input without calling
390  * readline, the source is ignored
391  */
392 char           *
393 gets_noreadline(char *prompt, FILE * source)
394 {
395     fputs(prompt, stdout);
396     fflush(stdout);
397     return (gets_fromFile(prompt, stdin));
398 }
399
400 /*
401  * gets_readline  prompt source the routine to get input from GNU readline(),
402  * the source is ignored the prompt argument is used as the prompting string
403  */
404 char           *
405 gets_readline(char *prompt, FILE * source)
406 {
407     char *s = readline(prompt);
408     fputc('\r', stdout);
409     fflush(stdout);
410     return s;
411 }
412
413 /*
414  * gets_fromFile  prompt source
415  * 
416  * the routine to read from a file, the prompt argument is ignored the source
417  * argument is a FILE *
418  */
419 char           *
420 gets_fromFile(char *prompt, FILE * source)
421 {
422     char           *line;
423     int             len;
424
425     line = malloc(MAX_QUERY_BUFFER + 1);
426
427     /* read up to MAX_QUERY_BUFFER characters */
428     if (fgets(line, MAX_QUERY_BUFFER, source) == NULL)
429         return NULL;
430
431     line[MAX_QUERY_BUFFER - 1] = '\0';
432     len = strlen(line);
433     if (len == MAX_QUERY_BUFFER) {
434         fprintf(stderr, "line read exceeds maximum length.  Truncating at %d\n",
435                 MAX_QUERY_BUFFER);
436     }
437     return line;
438 }
439
440 /*
441  * SendQuery: send the query string to the backend return *success_p = 1 if
442  * the query executed successfully returns *success_p = 0 otherwise
443  */
444 void
445 SendQuery(bool * success_p, PsqlSettings * settings, const char *query,
446           const bool copy_in, const bool copy_out, FILE * copystream)
447 {
448
449     PGresult       *results;
450     PGnotify       *notify;
451
452     if (settings->singleStep)
453         fprintf(stdout, "\n**************************************"
454                 "*****************************************\n");
455
456     if (settings->echoQuery || settings->singleStep) {
457         fprintf(stderr, "QUERY: %s\n", query);
458         fflush(stderr);
459     }
460     if (settings->singleStep) {
461         fprintf(stdout, "\n**************************************"
462                 "*****************************************\n");
463         fflush(stdout);
464         printf("\npress return to continue ..\n");
465         gets_fromFile("", stdin);
466     }
467     results = PQexec(settings->db, query);
468     if (results == NULL) {
469         fprintf(stderr, "%s", PQerrorMessage(settings->db));
470         *success_p = false;
471     } else {
472         switch (PQresultStatus(results)) {
473         case PGRES_TUPLES_OK:
474             if (settings->gfname) {
475                 PsqlSettings    ps = *settings;
476                 FILE           *fp;
477                 ps.queryFout = stdout;
478                 fp = setFout(&ps, settings->gfname);
479                 if (!fp || fp == stdout) {
480                     *success_p = false;
481                     break;
482                 } else
483                     *success_p = true;
484                 PQprint(fp,
485                         results,
486                         &(settings->opt));
487                 if (ps.pipe)
488                     pclose(fp);
489                 else
490                     fclose(fp);
491                 free(settings->gfname);
492                 settings->gfname = NULL;
493                 break;
494             } else {
495                 *success_p = true;
496                 PQprint(settings->queryFout,
497                         results,
498                         &(settings->opt));
499                 fflush(settings->queryFout);
500             }
501             PQclear(results);
502             break;
503         case PGRES_EMPTY_QUERY:
504             *success_p = true;
505             break;
506         case PGRES_COMMAND_OK:
507             *success_p = true;
508             if (!settings->quiet)
509                 fprintf(stdout, "%s\n", PQcmdStatus(results));
510             break;
511         case PGRES_COPY_OUT:
512             *success_p = true;
513             if (copy_out) {
514                 handleCopyOut(results, settings->quiet, copystream);
515             } else {
516                 if (!settings->quiet)
517                     fprintf(stdout, "Copy command returns...\n");
518
519                 handleCopyOut(results, settings->quiet, stdout);
520             }
521             break;
522         case PGRES_COPY_IN:
523             *success_p = true;
524             if (copy_in)
525                 handleCopyIn(results, false, copystream);
526             else
527                 handleCopyIn(results, !settings->quiet, stdin);
528             break;
529         case PGRES_NONFATAL_ERROR:
530         case PGRES_FATAL_ERROR:
531         case PGRES_BAD_RESPONSE:
532             *success_p = false;
533             fprintf(stderr, "%s", PQerrorMessage(settings->db));
534             break;
535         }
536
537         if (PQstatus(settings->db) == CONNECTION_BAD) {
538             fprintf(stderr,
539                     "We have lost the connection to the backend, so "
540                     "further processing is impossible.  "
541                     "Terminating.\n");
542             exit(2);            /* we are out'ta here */
543         }
544         /* check for asynchronous returns */
545         notify = PQnotifies(settings->db);
546         if (notify) {
547             fprintf(stderr,
548                     "ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
549                     notify->relname, notify->be_pid);
550             free(notify);
551         }
552     }
553 }
554
555
556
557 static void
558 editFile(char *fname)
559 {
560     char           *editorName;
561     char           *sys;
562     editorName = getenv("EDITOR");
563     if (!editorName)
564         editorName = DEFAULT_EDITOR;
565     sys = malloc(strlen(editorName) + strlen(fname) + 32 + 1);
566     if (!sys) {
567         perror("malloc");
568         exit(1);
569     }
570     sprintf(sys, "exec '%s' '%s'", editorName, fname);
571     system(sys);
572     free(sys);
573 }
574
575 static          bool
576 toggle(PsqlSettings * settings, bool * sw, char *msg)
577 {
578     *sw = !*sw;
579     if (!settings->quiet)
580         fprintf(stderr, "turned %s %s\n", on(*sw), msg);
581     return *sw;
582 }
583
584
585
586 static void
587 unescape(char *dest, const char *source)
588 {
589     /*-----------------------------------------------------------------------------
590       Return as the string <dest> the value of string <source> with escape
591       sequences turned into the bytes they represent.
592     -----------------------------------------------------------------------------*/
593     char           *p;
594     bool            esc;        /* Last character we saw was the escape
595                                  * character (/) */
596
597     esc = false;                /* Haven't seen escape character yet */
598     for (p = (char *) source; *p; p++) {
599         char            c;      /* Our output character */
600
601         if (esc) {
602             switch (*p) {
603             case 'n':
604                 c = '\n';
605                 break;
606             case 'r':
607                 c = '\r';
608                 break;
609             case 't':
610                 c = '\t';
611                 break;
612             case 'f':
613                 c = '\f';
614                 break;
615             case '\\':
616                 c = '\\';
617                 break;
618             default:
619                 c = *p;
620             }
621             esc = false;
622         } else if (*p == '\\') {
623             esc = true;
624             c = ' ';            /* meaningless, but compiler doesn't know
625                                  * that */
626         } else {
627             c = *p;
628             esc = false;
629         }
630         if (!esc)
631             *dest++ = c;
632     }
633     *dest = '\0';               /* Terminating null character */
634 }
635
636
637
638 static void
639 parse_slash_copy(const char *args, char *table, const int table_len,
640                  char *file, const int file_len,
641                  bool * from_p, bool * error_p)
642 {
643
644     char            work_args[200];
645     /*
646      * A copy of the \copy command arguments, except that we modify it as we
647      * parse to suit our parsing needs.
648      */
649     char           *table_tok, *fromto_tok;
650
651     strncpy(work_args, args, sizeof(work_args));
652     work_args[sizeof(work_args) - 1] = '\0';
653
654     *error_p = false;           /* initial assumption */
655
656     table_tok = strtok(work_args, " ");
657     if (table_tok == NULL) {
658         fprintf(stderr, "\\copy needs arguments.\n");
659         *error_p = true;
660     } else {
661         strncpy(table, table_tok, table_len);
662         file[table_len - 1] = '\0';
663
664         fromto_tok = strtok(NULL, "  ");
665         if (fromto_tok == NULL) {
666             fprintf(stderr, "'FROM' or 'TO' must follow table name.\n");
667             *error_p = true;
668         } else {
669             if (strcasecmp(fromto_tok, "from") == 0)
670                 *from_p = true;
671             else if (strcasecmp(fromto_tok, "to") == 0)
672                 *from_p = false;
673             else {
674                 fprintf(stderr,
675                         "Unrecognized token found where "
676                         "'FROM' or 'TO' expected: '%s'.\n",
677                         fromto_tok);
678                 *error_p = true;
679             }
680             if (!*error_p) {
681                 char           *file_tok;
682
683                 file_tok = strtok(NULL, " ");
684                 if (file_tok == NULL) {
685                     fprintf(stderr, "A file pathname must follow '%s'.\n",
686                             fromto_tok);
687                     *error_p = true;
688                 } else {
689                     strncpy(file, file_tok, file_len);
690                     file[file_len - 1] = '\0';
691                     if (strtok(NULL, " ") != NULL) {
692                         fprintf(stderr,
693                              "You have extra tokens after the filename.\n");
694                         *error_p = true;
695                     }
696                 }
697             }
698         }
699     }
700 }
701
702
703
704 static void
705 do_copy(const char *args, PsqlSettings * settings)
706 {
707     /*---------------------------------------------------------------------------
708       Execute a \copy command (frontend copy).  We have to open a file, then
709       submit a COPY query to the backend and either feed it data from the
710       file or route its response into the file.
711
712       We do a text copy with default (tab) column delimiters.  Some day, we
713       should do all the things a backend copy can do.
714
715     ----------------------------------------------------------------------------*/
716     char            query[200];
717     /* The COPY command we send to the back end */
718     bool            from;
719     /* The direction of the copy is from a file to a table. */
720     char            file[MAXPATHLEN + 1];
721     /* The pathname of the file from/to which we copy */
722     char            table[NAMEDATALEN + 1];
723     /* The name of the table from/to which we copy */
724     bool            syntax_error;
725     /* The \c command has invalid syntax */
726     FILE           *copystream;
727
728     parse_slash_copy(args, table, sizeof(table), file, sizeof(file),
729                      &from, &syntax_error);
730
731     if (!syntax_error) {
732         strcpy(query, "COPY ");
733         strcat(query, table);
734
735         if (from)
736             strcat(query, " FROM stdin");
737         else
738             strcat(query, " TO stdout");
739
740         if (from) {
741             copystream = fopen(file, "r");
742         } else {
743             copystream = fopen(file, "w");
744         }
745         if (copystream == NULL)
746             fprintf(stderr,
747                     "Unable to open file %s which to copy, errno = %s (%d).",
748                     from ? "from" : "to", strerror(errno), errno);
749         else {
750             bool            success;    /* The query succeeded at the backend */
751
752             SendQuery(&success, settings, query, from, !from, copystream);
753             fclose(copystream);
754             if (!settings->quiet) {
755                 if (success)
756                     fprintf(stdout, "Successfully copied.\n");
757                 else
758                     fprintf(stdout, "Copy failed.\n");
759             }
760         }
761     }
762 }
763
764
765 static void
766 do_connect(const char *new_dbname, PsqlSettings * settings)
767 {
768
769     char           *dbname = PQdb(settings->db);
770     if (!new_dbname)
771         fprintf(stderr, "\\connect must be followed by a database name\n");
772     else {
773         PGconn         *olddb = settings->db;
774
775         printf("closing connection to database: %s\n", dbname);
776         settings->db = PQsetdb(PQhost(olddb), PQport(olddb),
777                                NULL, NULL, new_dbname);
778         printf("connecting to new database: %s\n", new_dbname);
779         if (PQstatus(settings->db) == CONNECTION_BAD) {
780             fprintf(stderr, "%s\n", PQerrorMessage(settings->db));
781             printf("reconnecting to %s\n", dbname);
782             settings->db = PQsetdb(PQhost(olddb), PQport(olddb),
783                                    NULL, NULL, dbname);
784             if (PQstatus(settings->db) == CONNECTION_BAD) {
785                 fprintf(stderr,
786                         "could not reconnect to %s. exiting\n", dbname);
787                 exit(2);
788             }
789         } else {
790             PQfinish(olddb);
791             free(settings->prompt);
792             settings->prompt = malloc(strlen(PQdb(settings->db)) + 10);
793             sprintf(settings->prompt, "%s%s", PQdb(settings->db), PROMPT);
794         }
795     }
796 }
797
798
799 static void
800 do_edit(const char *filename_arg, char *query, int *status_p)
801 {
802
803     int             fd;
804     char            tmp[64];
805     char           *fname;
806     int             cc;
807     const int       ql = strlen(query);
808     bool            error;
809
810     if (filename_arg) {
811         fname = (char *) filename_arg;
812         error = false;
813     } else {
814         sprintf(tmp, "/tmp/psql.%ld.%ld", (long) geteuid(), (long) getpid());
815         fname = tmp;
816         unlink(tmp);
817         if (ql > 0) {
818             if ((fd = open(tmp, O_EXCL | O_CREAT | O_WRONLY, 0600)) == -1) {
819                 perror(tmp);
820                 error = true;
821             } else {
822                 if (query[ql - 1] != '\n')
823                     strcat(query, "\n");
824                 if (write(fd, query, ql) != ql) {
825                     perror(tmp);
826                     close(fd);
827                     unlink(tmp);
828                     error = true;
829                 } else
830                     error = false;
831                 close(fd);
832             }
833         } else
834             error = false;
835     }
836
837     if (error)
838         *status_p = 1;
839     else {
840         editFile(fname);
841         if ((fd = open(fname, O_RDONLY)) == -1) {
842             perror(fname);
843             if (!filename_arg)
844                 unlink(fname);
845             *status_p = 1;
846         } else {
847             if ((cc = read(fd, query, MAX_QUERY_BUFFER)) == -1) {
848                 perror(fname);
849                 close(fd);
850                 if (!filename_arg)
851                     unlink(fname);
852                 *status_p = 1;
853             } else {
854                 query[cc] = '\0';
855                 close(fd);
856                 if (!filename_arg)
857                     unlink(fname);
858                 rightTrim(query);
859                 *status_p = 3;
860             }
861         }
862     }
863 }
864
865
866
867 static void
868 do_help(const char *topic)
869 {
870
871     if (!topic) {
872         char            left_center_right;      /* Which column we're
873                                                  * displaying */
874         int             i;      /* Index into QL_HELP[] */
875
876         printf("type \\h <cmd> where <cmd> is one of the following:\n");
877
878         left_center_right = 'L';/* Start with left column */
879         i = 0;
880         while (QL_HELP[i].cmd != NULL) {
881             switch (left_center_right) {
882             case 'L':
883                 printf("    %-25s", QL_HELP[i].cmd);
884                 left_center_right = 'C';
885                 break;
886             case 'C':
887                 printf("%-25s", QL_HELP[i].cmd);
888                 left_center_right = 'R';
889                 break;
890             case 'R':
891                 printf("%-25s\n", QL_HELP[i].cmd);
892                 left_center_right = 'L';
893                 break;
894             };
895             i++;
896         }
897         if (left_center_right != 'L')
898             puts("\n");
899         printf("type \\h * for a complete description of all commands\n");
900     } else {
901         int             i;      /* Index into QL_HELP[] */
902         bool            help_found;     /* We found the help he asked for */
903
904         help_found = false;     /* Haven't found it yet */
905         for (i = 0; QL_HELP[i].cmd; i++) {
906             if (strcmp(QL_HELP[i].cmd, topic) == 0 ||
907                 strcmp(topic, "*") == 0) {
908                 help_found = true;
909                 printf("Command: %s\n", QL_HELP[i].cmd);
910                 printf("Description: %s\n", QL_HELP[i].help);
911                 printf("Syntax:\n");
912                 printf("%s\n", QL_HELP[i].syntax);
913                 printf("\n");
914             }
915         }
916         if (!help_found)
917             printf("command not found, "
918                    "try \\h with no arguments to see available help\n");
919     }
920 }
921
922
923
924 static void
925 do_shell(const char *command)
926 {
927
928     if (!command) {
929         char           *sys;
930         char           *shellName;
931
932         shellName = getenv("SHELL");
933         if (shellName == NULL)
934             shellName = DEFAULT_SHELL;
935         sys = malloc(strlen(shellName) + 16);
936         if (!sys) {
937             perror("malloc");
938             exit(1);
939         }
940         sprintf(sys, "exec %s", shellName);
941         system(sys);
942         free(sys);
943     } else
944         system(command);
945 }
946
947
948
949 /*
950  * HandleSlashCmds:
951  * 
952  * Handles all the different commands that start with \ db_ptr is a pointer to
953  * the TgDb* structure line is the current input line prompt_ptr is a pointer
954  * to the prompt string, a pointer is used because the prompt can be used
955  * with a connection to a new database returns a status: 0 - send currently
956  * constructed query to backend (i.e. we got a \g) 1 - skip processing of
957  * this line, continue building up query 2 - terminate processing of this
958  * query entirely, 3 - new query supplied by edit
959  */
960 int
961 HandleSlashCmds(PsqlSettings * settings,
962                 char *line,
963                 char *query)
964 {
965     int             status = 1;
966     char           *optarg;
967     /*
968      * Pointer inside the <cmd> string to the argument of the slash command,
969      * assuming it is a one-character slash command.  If it's not a
970      * one-character command, this is meaningless.
971      */
972     char           *optarg2;
973     /*
974      * Pointer inside the <cmd> string to the argument of the slash command
975      * assuming it's not a one-character command.  If it's a one-character
976      * command, this is meaningless.
977      */
978     char           *cmd;
979     /*
980      * String: value of the slash command, less the slash and with escape
981      * sequences decoded.
982      */
983     int             blank_loc;
984     /* Offset within <cmd> of first blank */
985
986     cmd = malloc(strlen(line)); /* unescaping better not make string grow. */
987
988     unescape(cmd, line + 1);    /* sets cmd string */
989
990     /*
991      * Originally, there were just single character commands.  Now, we define
992      * some longer, friendly commands, but we have to keep the old single
993      * character commands too.  \c used to be what \connect is now.
994      * Complicating matters is the fact that with the single-character
995      * commands, you can start the argument right after the single character,
996      * so "\copy" would mean "connect to database named 'opy'".
997      */
998
999     if (strlen(cmd) > 1)
1000         optarg = cmd + 1 + strspn(cmd + 1, " \t");
1001     else
1002         optarg = NULL;
1003
1004     blank_loc = strcspn(cmd, " \t");
1005     if (blank_loc == 0)
1006         optarg2 = NULL;
1007     else
1008         optarg2 = cmd + blank_loc + strspn(cmd + blank_loc, " \t");
1009
1010
1011     switch (cmd[0]) {
1012     case 'a':                   /* toggles to align fields on output */
1013         toggle(settings, &settings->opt.align, "field alignment");
1014         break;
1015     case 'C':                   /* define new caption */
1016         if (settings->opt.caption)
1017             free(settings->opt.caption);
1018         if (!optarg)
1019         {
1020             if (settings->opt.caption)
1021                 free(settings->opt.caption);
1022             settings->opt.caption = NULL;
1023         }
1024         else if (!(settings->opt.caption = strdup(optarg))) {
1025             perror("malloc");
1026             exit(1);
1027         }
1028         break;
1029     case 'c':{
1030             if (strncmp(cmd, "copy ", strlen("copy ")) == 0)
1031                 do_copy(optarg2, settings);
1032             else if (strncmp(cmd, "connect ", strlen("connect ")) == 0)
1033                 do_connect(optarg2, settings);
1034             else
1035                 do_connect(optarg, settings);
1036         }
1037         break;
1038     case 'd':                   /* \d describe tables or columns in a table */
1039         if (!optarg) {
1040             tableList(settings, 0);
1041             break;
1042         }
1043         if (strcmp(optarg, "*") == 0) {
1044             tableList(settings, 0);
1045             tableList(settings, 1);
1046         } else {
1047             tableDesc(settings, optarg);
1048         }
1049         break;
1050     case 'e':                   /* edit */
1051         {
1052             do_edit(optarg, query, &status);
1053             break;
1054         }
1055     case 'E':
1056         {
1057             FILE           *fd;
1058             static char    *lastfile;
1059             struct stat     st, st2;
1060             if (optarg) {
1061                 if (lastfile)
1062                     free(lastfile);
1063                 lastfile = malloc(strlen(optarg + 1));
1064                 if (!lastfile) {
1065                     perror("malloc");
1066                     exit(1);
1067                 }
1068                 strcpy(lastfile, optarg);
1069             } else if (!lastfile) {
1070                 fprintf(stderr, "\\r must be followed by a file name initially\n");
1071                 break;
1072             }
1073             stat(lastfile, &st);
1074             editFile(lastfile);
1075             if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "r")) == NULL)) {
1076                 perror(lastfile);
1077                 break;
1078             }
1079             if (st2.st_mtime == st.st_mtime) {
1080                 if (!settings->quiet)
1081                     fprintf(stderr, "warning: %s not modified. query not executed\n", lastfile);
1082                 fclose(fd);
1083                 break;
1084             }
1085             MainLoop(settings, fd);
1086             fclose(fd);
1087             break;
1088         }
1089     case 'f':
1090         {
1091             char           *fs = DEFAULT_FIELD_SEP;
1092             if (optarg)
1093                 fs = optarg;
1094             if (settings->opt.fieldSep)
1095                 free(settings->opt.fieldSep);
1096             if (!(settings->opt.fieldSep = strdup(fs))) {
1097                 perror("malloc");
1098                 exit(1);
1099             }
1100             if (!settings->quiet)
1101                 fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
1102             break;
1103         }
1104     case 'g':                   /* \g means send query */
1105         if (!optarg)
1106             settings->gfname = NULL;
1107         else if (!(settings->gfname = strdup(optarg))) {
1108             perror("malloc");
1109             exit(1);
1110         }
1111         status = 0;
1112         break;
1113     case 'h':                   /* help */
1114         {
1115             do_help(optarg);
1116             break;
1117         }
1118     case 'i':                   /* \i is include file */
1119         {
1120             FILE           *fd;
1121
1122             if (!optarg) {
1123                 fprintf(stderr, "\\i must be followed by a file name\n");
1124                 break;
1125             }
1126             if ((fd = fopen(optarg, "r")) == NULL) {
1127                 fprintf(stderr, "file named %s could not be opened\n", optarg);
1128                 break;
1129             }
1130             MainLoop(settings, fd);
1131             fclose(fd);
1132             break;
1133         }
1134     case 'l':                   /* \l is list database */
1135         listAllDbs(settings);
1136         break;
1137     case 'H':
1138         if (toggle(settings, &settings->opt.html3, "HTML3.0 tabular output"))
1139             settings->opt.standard = 0;
1140         break;
1141     case 'o':
1142         setFout(settings, optarg);
1143         break;
1144     case 'p':
1145         if (query) {
1146             fputs(query, stdout);
1147             fputc('\n', stdout);
1148         }
1149         break;
1150     case 'q':                   /* \q is quit */
1151         status = 2;
1152         break;
1153     case 'r':                   /* reset(clear) the buffer */
1154         query[0] = '\0';
1155         if (!settings->quiet)
1156             fprintf(stderr, "buffer reset(cleared)\n");
1157         break;
1158     case 's':                   /* \s is save history to a file */
1159         if (!optarg)
1160             optarg = "/dev/tty";
1161         if (write_history(optarg) != 0)
1162             fprintf(stderr, "cannot write history to %s\n", optarg);
1163         break;
1164     case 'm':                   /* monitor like type-setting */
1165         if (toggle(settings, &settings->opt.standard, "standard SQL separaters and padding")) {
1166             settings->opt.html3 = settings->opt.expanded = 0;
1167             settings->opt.align = settings->opt.header = 1;
1168             if (settings->opt.fieldSep)
1169                 free(settings->opt.fieldSep);
1170             settings->opt.fieldSep = strdup("|");
1171             if (!settings->quiet)
1172                 fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
1173         } else {
1174             if (settings->opt.fieldSep)
1175                 free(settings->opt.fieldSep);
1176             settings->opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
1177             if (!settings->quiet)
1178                 fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
1179         }
1180         break;
1181     case 't':                   /* toggle headers */
1182         toggle(settings, &settings->opt.header, "output headings and row count");
1183         break;
1184     case 'T':                   /* define html <table ...> option */
1185         if (settings->opt.tableOpt)
1186             free(settings->opt.tableOpt);
1187         if (!optarg)
1188             settings->opt.tableOpt = NULL;
1189         else if (!(settings->opt.tableOpt = strdup(optarg))) {
1190             perror("malloc");
1191             exit(1);
1192         }
1193         break;
1194     case 'x':
1195         toggle(settings, &settings->opt.expanded, "expanded table representation");
1196         break;
1197     case '!':
1198         do_shell(optarg);
1199         break;
1200     default:
1201     case '?':                   /* \? is help */
1202         slashUsage(settings);
1203         break;
1204     }
1205     free(cmd);
1206     return status;
1207 }
1208
1209 /*
1210  * MainLoop: main processing loop for reading lines of input and sending them
1211  * to the backend
1212  * 
1213  * this loop is re-entrant.  May be called by \i command which reads input from
1214  * a file
1215  * 
1216  * db_ptr must be initialized and set
1217  */
1218
1219 int
1220 MainLoop(PsqlSettings * settings, FILE * source)
1221 {
1222     char           *line;       /* line of input */
1223     int             len;        /* length of the line */
1224     char            query[MAX_QUERY_BUFFER];    /* multi-line query storage */
1225     int             successResult = 1;
1226     int             slashCmdStatus = 0;
1227     /*
1228      * slashCmdStatus can be: 0 - send currently constructed query to backend
1229      * (i.e. we got a \g) 1 - skip processing of this line, continue building
1230      * up query 2 - terminate processing of this query entirely 3 - new query
1231      * supplied by edit
1232      */
1233
1234     bool            querySent = false;
1235     bool            interactive;
1236     READ_ROUTINE    GetNextLine;
1237     bool            eof = 0;
1238     /* We've reached the end of our command input. */
1239     bool            success;
1240     bool            in_quote;
1241     bool            was_bslash; /* backslash */
1242     bool            was_dash;
1243     int             paren_level;
1244     char           *query_start;
1245
1246     interactive = ((source == stdin) && !settings->notty);
1247     if (interactive) {
1248         if (settings->prompt)
1249             free(settings->prompt);
1250         settings->prompt =
1251             malloc(strlen(PQdb(settings->db)) + strlen(PROMPT) + 1);
1252         if (settings->quiet)
1253             settings->prompt[0] = '\0';
1254         else
1255             sprintf(settings->prompt, "%s%s", PQdb(settings->db), PROMPT);
1256         if (settings->useReadline) {
1257             using_history();
1258             GetNextLine = gets_readline;
1259         } else
1260             GetNextLine = gets_noreadline;
1261     } else
1262         GetNextLine = gets_fromFile;
1263
1264     query[0] = '\0';
1265     in_quote = false;
1266     paren_level = 0;
1267     slashCmdStatus = -1;        /* set default */
1268
1269     /* main loop for getting queries and executing them */
1270     while (!eof) {
1271         if (slashCmdStatus == 3) {
1272             paren_level = 0;
1273             line = strdup(query);
1274             query[0] = '\0';
1275         } else {
1276             if (interactive && !settings->quiet) {
1277                 if (in_quote)
1278                     settings->prompt[strlen(settings->prompt)-3] = '\'';
1279                 else if (query[0] != '\0' && !querySent)
1280                     settings->prompt[strlen(settings->prompt)-3] = '-';
1281                 else
1282                     settings->prompt[strlen(settings->prompt)-3] = '=';
1283             }
1284             line = GetNextLine(settings->prompt, source);
1285             if (interactive && settings->useReadline && line != NULL)
1286                 add_history(line);      /* save non-empty lines in history */
1287         }
1288
1289         query_start = line;
1290
1291         if (line == NULL) {     /* No more input.  Time to quit */
1292             if (!settings->quiet)
1293                 printf("EOF\n");        /* Goes on prompt line */
1294             eof = true;
1295         } else {
1296             /* remove whitespaces on the right, incl. \n's */
1297             line = rightTrim(line);
1298
1299             if (!interactive && !settings->singleStep && !settings->quiet)
1300                 fprintf(stderr, "%s\n", line);
1301
1302             if (line[0] == '\0') {
1303                 free(line);
1304                 continue;
1305             }
1306
1307             len = strlen(line);
1308
1309             if (settings->singleLineMode) {
1310                 SendQuery(&success, settings, line, false, false, 0);
1311                 successResult &= success;
1312                 querySent = true;
1313             } else {
1314                 int             i;
1315                 was_bslash = false;
1316                 was_dash = false;
1317
1318                 for (i = 0; i < len; i++) {
1319                     if (!in_quote && line[i] == '\\') {
1320                         char            hold_char = line[i];
1321
1322                         line[i] = '\0';
1323                         if (query_start[0] != '\0') {
1324                             if (query[0] != '\0') {
1325                                 strcat(query, "\n");
1326                                 strcat(query, query_start);
1327                             } else
1328                                 strcpy(query, query_start);
1329                         }
1330                         line[i] = hold_char;
1331                         query_start = line + i;
1332                         break;  /* handle command */
1333                     }
1334                     if (querySent && !isspace(line[i])) {
1335                         query[0] = '\0';
1336                         querySent = false;
1337                     }
1338                     if (!in_quote && was_dash && line[i] == '-') {
1339                         /* print comment at top of query */
1340                         if (settings->singleStep)
1341                             fprintf(stdout, "%s\n", line + i - 1);
1342                         line[i - 1] = '\0';     /* remove comment */
1343                         break;
1344                     }
1345                     was_dash = false;
1346
1347                     if (!in_quote && !paren_level &&
1348                         line[i] == ';') {
1349                         char            hold_char = line[i + 1];
1350
1351                         line[i + 1] = '\0';
1352                         if (query_start[0] != '\0') {
1353                             if (query[0] != '\0') {
1354                                 strcat(query, "\n");
1355                                 strcat(query, query_start);
1356                             } else
1357                                 strcpy(query, query_start);
1358                         }
1359                         SendQuery(&success, settings, query, false, false, 0);
1360                         successResult &= success;
1361                         line[i + 1] = hold_char;
1362                         query_start = line + i + 1;
1363                         querySent = true;
1364                     }
1365                     if (was_bslash)
1366                         was_bslash = false;
1367                     else if (line[i] == '\\')
1368                         was_bslash = true;
1369                     else if (line[i] == '\'')
1370                         in_quote ^= 1;
1371                     else if (!in_quote && line[i] == '(')
1372                         paren_level++;
1373                     else if (!in_quote && paren_level && line[i] == ')')
1374                         paren_level--;
1375                     else if (!in_quote && line[i] == '-')
1376                         was_dash = true;
1377                 }
1378             }
1379
1380             slashCmdStatus = -1;
1381             /* slash commands have to be on their own line */
1382             if (!in_quote && query_start[0] == '\\') {
1383                 slashCmdStatus = HandleSlashCmds(settings,
1384                                                  query_start,
1385                                                  query);
1386                 if (slashCmdStatus == 1) {
1387                     if (query[0] == '\0')
1388                         paren_level = 0;
1389                     free(line);
1390                     continue;
1391                 }
1392                 if (slashCmdStatus == 2) {
1393                     free(line);
1394                     break;
1395                 }
1396             } else if (strlen(query) + strlen(query_start) > MAX_QUERY_BUFFER) {
1397                 fprintf(stderr, "query buffer max length of %d exceeded\n",
1398                         MAX_QUERY_BUFFER);
1399                 fprintf(stderr, "query line ignored\n");
1400             } else {
1401                 if (query_start[0] != '\0') {
1402                     querySent = false;
1403                     if (query[0] != '\0') {
1404                         strcat(query, "\n");
1405                         strcat(query, query_start);
1406                     } else
1407                         strcpy(query, query_start);
1408                 }
1409             }
1410
1411             if (slashCmdStatus == 0) {
1412                 SendQuery(&success, settings, query, false, false, 0);
1413                 successResult &= success;
1414                 querySent = true;
1415             }
1416             free(line);         /* free storage malloc'd by GetNextLine */
1417         }
1418     }                           /* while */
1419     return successResult;
1420 }
1421
1422 int
1423 main(int argc, char **argv)
1424 {
1425     extern char    *optarg;
1426     extern int      optind;
1427
1428     char           *dbname = NULL;
1429     char           *host = NULL;
1430     char           *port = NULL;
1431     char           *qfilename = NULL;
1432     char            errbuf[ERROR_MSG_LENGTH];
1433
1434     PsqlSettings    settings;
1435
1436     char           *singleQuery = NULL;
1437
1438     bool            listDatabases = 0;
1439     int             successResult = 1;
1440     bool            singleSlashCmd = 0;
1441     int             c;
1442
1443     memset(&settings, 0, sizeof settings);
1444     settings.opt.align = 1;
1445     settings.opt.header = 1;
1446     settings.queryFout = stdout;
1447     settings.opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
1448     settings.opt.pager = 1;
1449     if (!isatty(0) || !isatty(1))
1450         settings.quiet = settings.notty = 1;
1451 #ifndef NOREADLINE
1452     else
1453         settings.useReadline = 1;
1454 #endif
1455
1456     while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lh:Hnso:p:qStT:x")) != EOF) {
1457         switch (c) {
1458         case 'A':
1459             settings.opt.align = 0;
1460             break;
1461         case 'a':
1462             fe_setauthsvc(optarg, errbuf);
1463             break;
1464         case 'c':
1465             singleQuery = optarg;
1466             if (singleQuery[0] == '\\') {
1467                 singleSlashCmd = 1;
1468             }
1469             break;
1470         case 'd':
1471             dbname = optarg;
1472             break;
1473         case 'e':
1474             settings.echoQuery = 1;
1475             break;
1476         case 'f':
1477             qfilename = optarg;
1478             break;
1479         case 'F':
1480             settings.opt.fieldSep = optarg;
1481             break;
1482         case 'l':
1483             listDatabases = 1;
1484             break;
1485         case 'h':
1486             host = optarg;
1487             break;
1488         case 'H':
1489             settings.opt.html3 = 1;
1490             break;
1491         case 'n':
1492             settings.useReadline = 0;
1493             break;
1494         case 'o':
1495             setFout(&settings, optarg);
1496             break;
1497         case 'p':
1498             port = optarg;
1499             break;
1500         case 'q':
1501             settings.quiet = 1;
1502             break;
1503         case 's':
1504             settings.singleStep = 1;
1505             break;
1506         case 'S':
1507             settings.singleLineMode = 1;
1508             break;
1509         case 't':
1510             settings.opt.header = 0;
1511             break;
1512         case 'T':
1513             settings.opt.tableOpt = optarg;
1514             break;
1515         case 'x':
1516             settings.opt.expanded = 1;
1517             break;
1518         default:
1519             usage(argv[0]);
1520             break;
1521         }
1522     }
1523     /* if we still have an argument, use it as the database name */
1524     if (argc - optind == 1)
1525         dbname = argv[optind];
1526
1527     if (listDatabases)
1528         dbname = "template1";
1529
1530     settings.db = PQsetdb(host, port, NULL, NULL, dbname);
1531     dbname = PQdb(settings.db);
1532
1533     if (PQstatus(settings.db) == CONNECTION_BAD) {
1534         fprintf(stderr, "Connection to database '%s' failed.\n", dbname);
1535         fprintf(stderr, "%s", PQerrorMessage(settings.db));
1536         exit(1);
1537     }
1538     if (listDatabases) {
1539         exit(listAllDbs(&settings));
1540     }
1541     if (!settings.quiet && !singleQuery && !qfilename) {
1542         printf("Welcome to the POSTGRESQL interactive sql monitor:\n");
1543         printf("  Please read the file COPYRIGHT for copyright terms "
1544                "of POSTGRESQL\n\n");
1545         printf("   type \\? for help on slash commands\n");
1546         printf("   type \\q to quit\n");
1547         printf("   type \\g or terminate with semicolon to execute query\n");
1548         printf(" You are currently connected to the database: %s\n\n", dbname);
1549     }
1550     if (qfilename || singleSlashCmd) {
1551         /*
1552          * read in a file full of queries instead of reading in queries
1553          * interactively
1554          */
1555         char           *line;
1556
1557         if (singleSlashCmd) {
1558             /* Not really a query, but "Do what I mean, not what I say." */
1559             line = singleQuery;
1560         } else {
1561             line = malloc(strlen(qfilename) + 5);
1562             sprintf(line, "\\i %s", qfilename);
1563         }
1564         HandleSlashCmds(&settings, line, "");
1565     } else {
1566         if (singleQuery) {
1567             bool            success;    /* The query succeeded at the backend */
1568             SendQuery(&success, &settings, singleQuery, false, false, 0);
1569             successResult = success;
1570         } else
1571             successResult = MainLoop(&settings, stdin);
1572     }
1573
1574     PQfinish(settings.db);
1575
1576     return !successResult;
1577 }
1578
1579 #define COPYBUFSIZ  8192
1580
1581 static void
1582 handleCopyOut(PGresult * res, bool quiet, FILE * copystream)
1583 {
1584     bool            copydone;
1585     char            copybuf[COPYBUFSIZ];
1586     int             ret;
1587
1588     copydone = false;           /* Can't be done; haven't started. */
1589
1590     while (!copydone) {
1591         ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
1592
1593         if (copybuf[0] == '\\' &&
1594             copybuf[1] == '.' &&
1595             copybuf[2] == '\0') {
1596             copydone = true;    /* don't print this... */
1597         } else {
1598             fputs(copybuf, copystream);
1599             switch (ret) {
1600             case EOF:
1601                 copydone = true;
1602                 /* FALLTHROUGH */
1603             case 0:
1604                 fputc('\n', copystream);
1605                 break;
1606             case 1:
1607                 break;
1608             }
1609         }
1610     }
1611     fflush(copystream);
1612     PQendcopy(res->conn);
1613 }
1614
1615
1616
1617 static void
1618 handleCopyIn(PGresult * res, const bool mustprompt, FILE * copystream)
1619 {
1620     bool            copydone = false;
1621     bool            firstload;
1622     bool            linedone;
1623     char            copybuf[COPYBUFSIZ];
1624     char           *s;
1625     int             buflen;
1626     int             c;
1627
1628     if (mustprompt) {
1629         fputs("Enter info followed by a newline\n", stdout);
1630         fputs("End with a backslash and a "
1631               "period on a line by itself.\n", stdout);
1632     }
1633     while (!copydone) {         /* for each input line ... */
1634         if (mustprompt) {
1635             fputs(">> ", stdout);
1636             fflush(stdout);
1637         }
1638         firstload = true;
1639         linedone = false;
1640         while (!linedone) {     /* for each buffer ... */
1641             s = copybuf;
1642             buflen = COPYBUFSIZ;
1643             for (; buflen > 1 &&
1644                  !(linedone = (c = getc(copystream)) == '\n' || c == EOF);
1645                  --buflen) {
1646                 *s++ = c;
1647             }
1648             if (c == EOF) {
1649                 PQputline(res->conn, "\\.");
1650                 copydone = true;
1651                 break;
1652             }
1653             *s = '\0';
1654             PQputline(res->conn, copybuf);
1655             if (firstload) {
1656                 if (!strcmp(copybuf, "\\.")) {
1657                     copydone = true;
1658                 }
1659                 firstload = false;
1660             }
1661         }
1662         PQputline(res->conn, "\n");
1663     }
1664     PQendcopy(res->conn);
1665 }
1666
1667
1668
1669 /*
1670  * try to open fname and return a FILE *, if it fails, use stdout, instead
1671  */
1672
1673 FILE           *
1674 setFout(PsqlSettings * ps, char *fname)
1675 {
1676     if (ps->queryFout && ps->queryFout != stdout) {
1677         if (ps->pipe)
1678             pclose(ps->queryFout);
1679         else
1680             fclose(ps->queryFout);
1681     }
1682     if (!fname) {
1683         ps->queryFout = stdout;
1684         pqsignal(SIGPIPE, SIG_DFL);
1685     }
1686     else {
1687         if (*fname == '|') {
1688             pqsignal(SIGPIPE, SIG_IGN);
1689             ps->queryFout = popen(fname + 1, "w");
1690             ps->pipe = 1;
1691         } else {
1692             ps->queryFout = fopen(fname, "w");
1693             pqsignal(SIGPIPE, SIG_DFL);
1694             ps->pipe = 0;
1695         }
1696         if (!ps->queryFout) {
1697             perror(fname);
1698             ps->queryFout = stdout;
1699         }
1700     }
1701     return ps->queryFout;
1702 }