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