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