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