]> granicus.if.org Git - postgresql/blob - src/bin/psql/psql.c
pq/signal() portability patch. Also psql copy prompt fix.
[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.42 1996/12/26 22:07:57 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <stdio.h>
15 #include <string.h>
16 #include <signal.h>
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <ctype.h>
23 #include "postgres.h"
24 #include "libpq-fe.h"
25 #include "pqsignal.h"
26 #include "stringutils.h"
27 #include "psqlHelp.h"
28 #ifdef NEED_STRDUP
29 #include "strdup.h"
30 #endif
31
32 #ifdef NOREADLINE
33 #include "rlstubs.h"
34 #else
35 /* from the GNU readline library */
36 #ifdef OLD_READLINE
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     return (readline(prompt));
404 }
405
406 /*
407  * gets_fromFile  prompt source
408  * 
409  * the routine to read from a file, the prompt argument is ignored the source
410  * argument is a FILE *
411  */
412 char           *
413 gets_fromFile(char *prompt, FILE * source)
414 {
415     char           *line;
416     int             len;
417
418     line = malloc(MAX_QUERY_BUFFER + 1);
419
420     /* read up to MAX_QUERY_BUFFER characters */
421     if (fgets(line, MAX_QUERY_BUFFER, source) == NULL)
422         return NULL;
423
424     line[MAX_QUERY_BUFFER - 1] = '\0';
425     len = strlen(line);
426     if (len == MAX_QUERY_BUFFER) {
427         fprintf(stderr, "line read exceeds maximum length.  Truncating at %d\n",
428                 MAX_QUERY_BUFFER);
429     }
430     return line;
431 }
432
433 /*
434  * SendQuery: send the query string to the backend return *success_p = 1 if
435  * the query executed successfully returns *success_p = 0 otherwise
436  */
437 void
438 SendQuery(bool * success_p, PsqlSettings * settings, const char *query,
439           const bool copy_in, const bool copy_out, FILE * copystream)
440 {
441
442     PGresult       *results;
443     PGnotify       *notify;
444
445     if (settings->singleStep)
446         fprintf(stdout, "\n**************************************"
447                 "*****************************************\n");
448
449     if (settings->echoQuery || settings->singleStep) {
450         fprintf(stderr, "QUERY: %s\n", query);
451         fflush(stderr);
452     }
453     if (settings->singleStep) {
454         fprintf(stdout, "\n**************************************"
455                 "*****************************************\n");
456         fflush(stdout);
457         printf("\npress return to continue ..\n");
458         gets_fromFile("", stdin);
459     }
460     results = PQexec(settings->db, query);
461     if (results == NULL) {
462         fprintf(stderr, "%s", PQerrorMessage(settings->db));
463         *success_p = false;
464     } else {
465         switch (PQresultStatus(results)) {
466         case PGRES_TUPLES_OK:
467             if (settings->gfname) {
468                 PsqlSettings    ps = *settings;
469                 FILE           *fp;
470                 ps.queryFout = stdout;
471                 fp = setFout(&ps, settings->gfname);
472                 if (!fp || fp == stdout) {
473                     *success_p = false;
474                     break;
475                 } else
476                     *success_p = true;
477                 PQprint(fp,
478                         results,
479                         &(settings->opt));
480                 if (ps.pipe)
481                     pclose(fp);
482                 else
483                     fclose(fp);
484                 free(settings->gfname);
485                 settings->gfname = NULL;
486                 break;
487             } else {
488                 *success_p = true;
489                 PQprint(settings->queryFout,
490                         results,
491                         &(settings->opt));
492                 fflush(settings->queryFout);
493             }
494             PQclear(results);
495             break;
496         case PGRES_EMPTY_QUERY:
497             *success_p = true;
498             break;
499         case PGRES_COMMAND_OK:
500             *success_p = true;
501             if (!settings->quiet)
502                 fprintf(stdout, "%s\n", PQcmdStatus(results));
503             break;
504         case PGRES_COPY_OUT:
505             *success_p = true;
506             if (copy_out) {
507                 handleCopyOut(results, settings->quiet, copystream);
508             } else {
509                 if (!settings->quiet)
510                     fprintf(stdout, "Copy command returns...\n");
511
512                 handleCopyOut(results, settings->quiet, stdout);
513             }
514             break;
515         case PGRES_COPY_IN:
516             *success_p = true;
517             if (copy_in)
518                 handleCopyIn(results, false, copystream);
519             else
520                 handleCopyIn(results, !settings->quiet, stdin);
521             break;
522         case PGRES_NONFATAL_ERROR:
523         case PGRES_FATAL_ERROR:
524         case PGRES_BAD_RESPONSE:
525             *success_p = false;
526             fprintf(stderr, "%s", PQerrorMessage(settings->db));
527             break;
528         }
529
530         if (PQstatus(settings->db) == CONNECTION_BAD) {
531             fprintf(stderr,
532                     "We have lost the connection to the backend, so "
533                     "further processing is impossible.  "
534                     "Terminating.\n");
535             exit(2);            /* we are out'ta here */
536         }
537         /* check for asynchronous returns */
538         notify = PQnotifies(settings->db);
539         if (notify) {
540             fprintf(stderr,
541                     "ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
542                     notify->relname, notify->be_pid);
543             free(notify);
544         }
545     }
546 }
547
548
549
550 static void
551 editFile(char *fname)
552 {
553     char           *editorName;
554     char           *sys;
555     editorName = getenv("EDITOR");
556     if (!editorName)
557         editorName = DEFAULT_EDITOR;
558     sys = malloc(strlen(editorName) + strlen(fname) + 32 + 1);
559     if (!sys) {
560         perror("malloc");
561         exit(1);
562     }
563     sprintf(sys, "exec '%s' '%s'", editorName, fname);
564     system(sys);
565     free(sys);
566 }
567
568 static          bool
569 toggle(PsqlSettings * settings, bool * sw, char *msg)
570 {
571     *sw = !*sw;
572     if (!settings->quiet)
573         fprintf(stderr, "turned %s %s\n", on(*sw), msg);
574     return *sw;
575 }
576
577
578
579 static void
580 unescape(char *dest, const char *source)
581 {
582     /*-----------------------------------------------------------------------------
583       Return as the string <dest> the value of string <source> with escape
584       sequences turned into the bytes they represent.
585     -----------------------------------------------------------------------------*/
586     char           *p;
587     bool            esc;        /* Last character we saw was the escape
588                                  * character (/) */
589
590     esc = false;                /* Haven't seen escape character yet */
591     for (p = (char *) source; *p; p++) {
592         char            c;      /* Our output character */
593
594         if (esc) {
595             switch (*p) {
596             case 'n':
597                 c = '\n';
598                 break;
599             case 'r':
600                 c = '\r';
601                 break;
602             case 't':
603                 c = '\t';
604                 break;
605             case 'f':
606                 c = '\f';
607                 break;
608             case '\\':
609                 c = '\\';
610                 break;
611             default:
612                 c = *p;
613             }
614             esc = false;
615         } else if (*p == '\\') {
616             esc = true;
617             c = ' ';            /* meaningless, but compiler doesn't know
618                                  * that */
619         } else {
620             c = *p;
621             esc = false;
622         }
623         if (!esc)
624             *dest++ = c;
625     }
626     *dest = '\0';               /* Terminating null character */
627 }
628
629
630
631 static void
632 parse_slash_copy(const char *args, char *table, const int table_len,
633                  char *file, const int file_len,
634                  bool * from_p, bool * error_p)
635 {
636
637     char            work_args[200];
638     /*
639      * A copy of the \copy command arguments, except that we modify it as we
640      * parse to suit our parsing needs.
641      */
642     char           *table_tok, *fromto_tok;
643
644     strncpy(work_args, args, sizeof(work_args));
645     work_args[sizeof(work_args) - 1] = '\0';
646
647     *error_p = false;           /* initial assumption */
648
649     table_tok = strtok(work_args, " ");
650     if (table_tok == NULL) {
651         fprintf(stderr, "\\copy needs arguments.\n");
652         *error_p = true;
653     } else {
654         strncpy(table, table_tok, table_len);
655         file[table_len - 1] = '\0';
656
657         fromto_tok = strtok(NULL, "  ");
658         if (fromto_tok == NULL) {
659             fprintf(stderr, "'FROM' or 'TO' must follow table name.\n");
660             *error_p = true;
661         } else {
662             if (strcasecmp(fromto_tok, "from") == 0)
663                 *from_p = true;
664             else if (strcasecmp(fromto_tok, "to") == 0)
665                 *from_p = false;
666             else {
667                 fprintf(stderr,
668                         "Unrecognized token found where "
669                         "'FROM' or 'TO' expected: '%s'.\n",
670                         fromto_tok);
671                 *error_p = true;
672             }
673             if (!*error_p) {
674                 char           *file_tok;
675
676                 file_tok = strtok(NULL, " ");
677                 if (file_tok == NULL) {
678                     fprintf(stderr, "A file pathname must follow '%s'.\n",
679                             fromto_tok);
680                     *error_p = true;
681                 } else {
682                     strncpy(file, file_tok, file_len);
683                     file[file_len - 1] = '\0';
684                     if (strtok(NULL, " ") != NULL) {
685                         fprintf(stderr,
686                              "You have extra tokens after the filename.\n");
687                         *error_p = true;
688                     }
689                 }
690             }
691         }
692     }
693 }
694
695
696
697 static void
698 do_copy(const char *args, PsqlSettings * settings)
699 {
700     /*---------------------------------------------------------------------------
701       Execute a \copy command (frontend copy).  We have to open a file, then
702       submit a COPY query to the backend and either feed it data from the
703       file or route its response into the file.
704
705       We do a text copy with default (tab) column delimiters.  Some day, we
706       should do all the things a backend copy can do.
707
708     ----------------------------------------------------------------------------*/
709     char            query[200];
710     /* The COPY command we send to the back end */
711     bool            from;
712     /* The direction of the copy is from a file to a table. */
713     char            file[MAXPATHLEN + 1];
714     /* The pathname of the file from/to which we copy */
715     char            table[NAMEDATALEN + 1];
716     /* The name of the table from/to which we copy */
717     bool            syntax_error;
718     /* The \c command has invalid syntax */
719     FILE           *copystream;
720
721     parse_slash_copy(args, table, sizeof(table), file, sizeof(file),
722                      &from, &syntax_error);
723
724     if (!syntax_error) {
725         strcpy(query, "COPY ");
726         strcat(query, table);
727
728         if (from)
729             strcat(query, " FROM stdin");
730         else
731             strcat(query, " TO stdout");
732
733         if (from) {
734             copystream = fopen(file, "r");
735         } else {
736             copystream = fopen(file, "w");
737         }
738         if (copystream == NULL)
739             fprintf(stderr,
740                     "Unable to open file %s which to copy, errno = %s (%d).",
741                     from ? "from" : "to", strerror(errno), errno);
742         else {
743             bool            success;    /* The query succeeded at the backend */
744
745             SendQuery(&success, settings, query, from, !from, copystream);
746             fclose(copystream);
747             if (!settings->quiet) {
748                 if (success)
749                     fprintf(stdout, "Successfully copied.\n");
750                 else
751                     fprintf(stdout, "Copy failed.\n");
752             }
753         }
754     }
755 }
756
757
758 static void
759 do_connect(const char *new_dbname, PsqlSettings * settings)
760 {
761
762     char           *dbname = PQdb(settings->db);
763     if (!new_dbname)
764         fprintf(stderr, "\\connect must be followed by a database name\n");
765     else {
766         PGconn         *olddb = settings->db;
767
768         printf("closing connection to database: %s\n", dbname);
769         settings->db = PQsetdb(PQhost(olddb), PQport(olddb),
770                                NULL, NULL, new_dbname);
771         printf("connecting to new database: %s\n", new_dbname);
772         if (PQstatus(settings->db) == CONNECTION_BAD) {
773             fprintf(stderr, "%s\n", PQerrorMessage(settings->db));
774             printf("reconnecting to %s\n", dbname);
775             settings->db = PQsetdb(PQhost(olddb), PQport(olddb),
776                                    NULL, NULL, dbname);
777             if (PQstatus(settings->db) == CONNECTION_BAD) {
778                 fprintf(stderr,
779                         "could not reconnect to %s. exiting\n", dbname);
780                 exit(2);
781             }
782         } else {
783             PQfinish(olddb);
784             free(settings->prompt);
785             settings->prompt = malloc(strlen(PQdb(settings->db)) + 10);
786             sprintf(settings->prompt, "%s%s", PQdb(settings->db), PROMPT);
787         }
788     }
789 }
790
791
792 static void
793 do_edit(const char *filename_arg, char *query, int *status_p)
794 {
795
796     int             fd;
797     char            tmp[64];
798     char           *fname;
799     int             cc;
800     const int       ql = strlen(query);
801     bool            error;
802
803     if (filename_arg) {
804         fname = (char *) filename_arg;
805         error = false;
806     } else {
807         sprintf(tmp, "/tmp/psql.%ld.%ld", (long) geteuid(), (long) getpid());
808         fname = tmp;
809         unlink(tmp);
810         if (ql > 0) {
811             if ((fd = open(tmp, O_EXCL | O_CREAT | O_WRONLY, 0600)) == -1) {
812                 perror(tmp);
813                 error = true;
814             } else {
815                 if (query[ql - 1] != '\n')
816                     strcat(query, "\n");
817                 if (write(fd, query, ql) != ql) {
818                     perror(tmp);
819                     close(fd);
820                     unlink(tmp);
821                     error = true;
822                 } else
823                     error = false;
824                 close(fd);
825             }
826         } else
827             error = false;
828     }
829
830     if (error)
831         *status_p = 1;
832     else {
833         editFile(fname);
834         if ((fd = open(fname, O_RDONLY)) == -1) {
835             perror(fname);
836             if (!filename_arg)
837                 unlink(fname);
838             *status_p = 1;
839         } else {
840             if ((cc = read(fd, query, MAX_QUERY_BUFFER)) == -1) {
841                 perror(fname);
842                 close(fd);
843                 if (!filename_arg)
844                     unlink(fname);
845                 *status_p = 1;
846             } else {
847                 query[cc] = '\0';
848                 close(fd);
849                 if (!filename_arg)
850                     unlink(fname);
851                 rightTrim(query);
852                 *status_p = 3;
853             }
854         }
855     }
856 }
857
858
859
860 static void
861 do_help(const char *topic)
862 {
863
864     if (!topic) {
865         char            left_center_right;      /* Which column we're
866                                                  * displaying */
867         int             i;      /* Index into QL_HELP[] */
868
869         printf("type \\h <cmd> where <cmd> is one of the following:\n");
870
871         left_center_right = 'L';/* Start with left column */
872         i = 0;
873         while (QL_HELP[i].cmd != NULL) {
874             switch (left_center_right) {
875             case 'L':
876                 printf("    %-25s", QL_HELP[i].cmd);
877                 left_center_right = 'C';
878                 break;
879             case 'C':
880                 printf("%-25s", QL_HELP[i].cmd);
881                 left_center_right = 'R';
882                 break;
883             case 'R':
884                 printf("%-25s\n", QL_HELP[i].cmd);
885                 left_center_right = 'L';
886                 break;
887             };
888             i++;
889         }
890         if (left_center_right != 'L')
891             puts("\n");
892         printf("type \\h * for a complete description of all commands\n");
893     } else {
894         int             i;      /* Index into QL_HELP[] */
895         bool            help_found;     /* We found the help he asked for */
896
897         help_found = false;     /* Haven't found it yet */
898         for (i = 0; QL_HELP[i].cmd; i++) {
899             if (strcmp(QL_HELP[i].cmd, topic) == 0 ||
900                 strcmp(topic, "*") == 0) {
901                 help_found = true;
902                 printf("Command: %s\n", QL_HELP[i].cmd);
903                 printf("Description: %s\n", QL_HELP[i].help);
904                 printf("Syntax:\n");
905                 printf("%s\n", QL_HELP[i].syntax);
906                 printf("\n");
907             }
908         }
909         if (!help_found)
910             printf("command not found, "
911                    "try \\h with no arguments to see available help\n");
912     }
913 }
914
915
916
917 static void
918 do_shell(const char *command)
919 {
920
921     if (!command) {
922         char           *sys;
923         char           *shellName;
924
925         shellName = getenv("SHELL");
926         if (shellName == NULL)
927             shellName = DEFAULT_SHELL;
928         sys = malloc(strlen(shellName) + 16);
929         if (!sys) {
930             perror("malloc");
931             exit(1);
932         }
933         sprintf(sys, "exec %s", shellName);
934         system(sys);
935         free(sys);
936     } else
937         system(command);
938 }
939
940
941
942 /*
943  * HandleSlashCmds:
944  * 
945  * Handles all the different commands that start with \ db_ptr is a pointer to
946  * the TgDb* structure line is the current input line prompt_ptr is a pointer
947  * to the prompt string, a pointer is used because the prompt can be used
948  * with a connection to a new database returns a status: 0 - send currently
949  * constructed query to backend (i.e. we got a \g) 1 - skip processing of
950  * this line, continue building up query 2 - terminate processing of this
951  * query entirely, 3 - new query supplied by edit
952  */
953 int
954 HandleSlashCmds(PsqlSettings * settings,
955                 char *line,
956                 char *query)
957 {
958     int             status = 1;
959     char           *optarg;
960     /*
961      * Pointer inside the <cmd> string to the argument of the slash command,
962      * assuming it is a one-character slash command.  If it's not a
963      * one-character command, this is meaningless.
964      */
965     char           *optarg2;
966     /*
967      * Pointer inside the <cmd> string to the argument of the slash command
968      * assuming it's not a one-character command.  If it's a one-character
969      * command, this is meaningless.
970      */
971     char           *cmd;
972     /*
973      * String: value of the slash command, less the slash and with escape
974      * sequences decoded.
975      */
976     int             blank_loc;
977     /* Offset within <cmd> of first blank */
978
979     cmd = malloc(strlen(line)); /* unescaping better not make string grow. */
980
981     unescape(cmd, line + 1);    /* sets cmd string */
982
983     /*
984      * Originally, there were just single character commands.  Now, we define
985      * some longer, friendly commands, but we have to keep the old single
986      * character commands too.  \c used to be what \connect is now.
987      * Complicating matters is the fact that with the single-character
988      * commands, you can start the argument right after the single character,
989      * so "\copy" would mean "connect to database named 'opy'".
990      */
991
992     if (strlen(cmd) > 1)
993         optarg = cmd + 1 + strspn(cmd + 1, " \t");
994     else
995         optarg = NULL;
996
997     blank_loc = strcspn(cmd, " \t");
998     if (blank_loc == 0)
999         optarg2 = NULL;
1000     else
1001         optarg2 = cmd + blank_loc + strspn(cmd + blank_loc, " \t");
1002
1003
1004     switch (cmd[0]) {
1005     case 'a':                   /* toggles to align fields on output */
1006         toggle(settings, &settings->opt.align, "field alignment");
1007         break;
1008     case 'C':                   /* define new caption */
1009         if (settings->opt.caption)
1010             free(settings->opt.caption);
1011         if (!optarg)
1012         {
1013             if (settings->opt.caption)
1014                 free(settings->opt.caption);
1015             settings->opt.caption = NULL;
1016         }
1017         else if (!(settings->opt.caption = strdup(optarg))) {
1018             perror("malloc");
1019             exit(1);
1020         }
1021         break;
1022     case 'c':{
1023             if (strncmp(cmd, "copy ", strlen("copy ")) == 0)
1024                 do_copy(optarg2, settings);
1025             else if (strncmp(cmd, "connect ", strlen("connect ")) == 0)
1026                 do_connect(optarg2, settings);
1027             else
1028                 do_connect(optarg, settings);
1029         }
1030         break;
1031     case 'd':                   /* \d describe tables or columns in a table */
1032         if (!optarg) {
1033             tableList(settings, 0);
1034             break;
1035         }
1036         if (strcmp(optarg, "*") == 0) {
1037             tableList(settings, 0);
1038             tableList(settings, 1);
1039         } else {
1040             tableDesc(settings, optarg);
1041         }
1042         break;
1043     case 'e':                   /* edit */
1044         {
1045             do_edit(optarg, query, &status);
1046             break;
1047         }
1048     case 'E':
1049         {
1050             FILE           *fd;
1051             static char    *lastfile;
1052             struct stat     st, st2;
1053             if (optarg) {
1054                 if (lastfile)
1055                     free(lastfile);
1056                 lastfile = malloc(strlen(optarg + 1));
1057                 if (!lastfile) {
1058                     perror("malloc");
1059                     exit(1);
1060                 }
1061                 strcpy(lastfile, optarg);
1062             } else if (!lastfile) {
1063                 fprintf(stderr, "\\r must be followed by a file name initially\n");
1064                 break;
1065             }
1066             stat(lastfile, &st);
1067             editFile(lastfile);
1068             if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "r")) == NULL)) {
1069                 perror(lastfile);
1070                 break;
1071             }
1072             if (st2.st_mtime == st.st_mtime) {
1073                 if (!settings->quiet)
1074                     fprintf(stderr, "warning: %s not modified. query not executed\n", lastfile);
1075                 fclose(fd);
1076                 break;
1077             }
1078             MainLoop(settings, fd);
1079             fclose(fd);
1080             break;
1081         }
1082     case 'f':
1083         {
1084             char           *fs = DEFAULT_FIELD_SEP;
1085             if (optarg)
1086                 fs = optarg;
1087             if (settings->opt.fieldSep)
1088                 free(settings->opt.fieldSep);
1089             if (!(settings->opt.fieldSep = strdup(fs))) {
1090                 perror("malloc");
1091                 exit(1);
1092             }
1093             if (!settings->quiet)
1094                 fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
1095             break;
1096         }
1097     case 'g':                   /* \g means send query */
1098         if (!optarg)
1099             settings->gfname = NULL;
1100         else if (!(settings->gfname = strdup(optarg))) {
1101             perror("malloc");
1102             exit(1);
1103         }
1104         status = 0;
1105         break;
1106     case 'h':                   /* help */
1107         {
1108             do_help(optarg);
1109             break;
1110         }
1111     case 'i':                   /* \i is include file */
1112         {
1113             FILE           *fd;
1114
1115             if (!optarg) {
1116                 fprintf(stderr, "\\i must be followed by a file name\n");
1117                 break;
1118             }
1119             if ((fd = fopen(optarg, "r")) == NULL) {
1120                 fprintf(stderr, "file named %s could not be opened\n", optarg);
1121                 break;
1122             }
1123             MainLoop(settings, fd);
1124             fclose(fd);
1125             break;
1126         }
1127     case 'l':                   /* \l is list database */
1128         listAllDbs(settings);
1129         break;
1130     case 'H':
1131         if (toggle(settings, &settings->opt.html3, "HTML3.0 tabular output"))
1132             settings->opt.standard = 0;
1133         break;
1134     case 'o':
1135         setFout(settings, optarg);
1136         break;
1137     case 'p':
1138         if (query) {
1139             fputs(query, stdout);
1140             fputc('\n', stdout);
1141         }
1142         break;
1143     case 'q':                   /* \q is quit */
1144         status = 2;
1145         break;
1146     case 'r':                   /* reset(clear) the buffer */
1147         query[0] = '\0';
1148         if (!settings->quiet)
1149             fprintf(stderr, "buffer reset(cleared)\n");
1150         break;
1151     case 's':                   /* \s is save history to a file */
1152         if (!optarg)
1153             optarg = "/dev/tty";
1154         if (write_history(optarg) != 0)
1155             fprintf(stderr, "cannot write history to %s\n", optarg);
1156         break;
1157     case 'm':                   /* monitor like type-setting */
1158         if (toggle(settings, &settings->opt.standard, "standard SQL separaters and padding")) {
1159             settings->opt.html3 = settings->opt.expanded = 0;
1160             settings->opt.align = settings->opt.header = 1;
1161             if (settings->opt.fieldSep)
1162                 free(settings->opt.fieldSep);
1163             settings->opt.fieldSep = strdup("|");
1164             if (!settings->quiet)
1165                 fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
1166         } else {
1167             if (settings->opt.fieldSep)
1168                 free(settings->opt.fieldSep);
1169             settings->opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
1170             if (!settings->quiet)
1171                 fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
1172         }
1173         break;
1174     case 't':                   /* toggle headers */
1175         toggle(settings, &settings->opt.header, "output headings and row count");
1176         break;
1177     case 'T':                   /* define html <table ...> option */
1178         if (settings->opt.tableOpt)
1179             free(settings->opt.tableOpt);
1180         if (!optarg)
1181             settings->opt.tableOpt = NULL;
1182         else if (!(settings->opt.tableOpt = strdup(optarg))) {
1183             perror("malloc");
1184             exit(1);
1185         }
1186         break;
1187     case 'x':
1188         toggle(settings, &settings->opt.expanded, "expanded table representation");
1189         break;
1190     case '!':
1191         do_shell(optarg);
1192         break;
1193     default:
1194     case '?':                   /* \? is help */
1195         slashUsage(settings);
1196         break;
1197     }
1198     free(cmd);
1199     return status;
1200 }
1201
1202 /*
1203  * MainLoop: main processing loop for reading lines of input and sending them
1204  * to the backend
1205  * 
1206  * this loop is re-entrant.  May be called by \i command which reads input from
1207  * a file
1208  * 
1209  * db_ptr must be initialized and set
1210  */
1211
1212 int
1213 MainLoop(PsqlSettings * settings, FILE * source)
1214 {
1215     char           *line;       /* line of input */
1216     int             len;        /* length of the line */
1217     char            query[MAX_QUERY_BUFFER];    /* multi-line query storage */
1218     int             successResult = 1;
1219     int             slashCmdStatus = 0;
1220     /*
1221      * slashCmdStatus can be: 0 - send currently constructed query to backend
1222      * (i.e. we got a \g) 1 - skip processing of this line, continue building
1223      * up query 2 - terminate processing of this query entirely 3 - new query
1224      * supplied by edit
1225      */
1226
1227     bool            querySent = false;
1228     bool            interactive;
1229     READ_ROUTINE    GetNextLine;
1230     bool            eof = 0;
1231     /* We've reached the end of our command input. */
1232     bool            success;
1233     bool            in_quote;
1234     bool            was_bslash; /* backslash */
1235     bool            was_dash;
1236     int             paren_level;
1237     char           *query_start;
1238
1239     interactive = ((source == stdin) && !settings->notty);
1240     if (interactive) {
1241         if (settings->prompt)
1242             free(settings->prompt);
1243         settings->prompt =
1244             malloc(strlen(PQdb(settings->db)) + strlen(PROMPT) + 1);
1245         if (settings->quiet)
1246             settings->prompt[0] = '\0';
1247         else
1248             sprintf(settings->prompt, "%s%s", PQdb(settings->db), PROMPT);
1249         if (settings->useReadline) {
1250             using_history();
1251             GetNextLine = gets_readline;
1252         } else
1253             GetNextLine = gets_noreadline;
1254     } else
1255         GetNextLine = gets_fromFile;
1256
1257     query[0] = '\0';
1258     in_quote = false;
1259     paren_level = 0;
1260     slashCmdStatus = -1;        /* set default */
1261
1262     /* main loop for getting queries and executing them */
1263     while (!eof) {
1264         if (slashCmdStatus == 3) {
1265             line = strdup(query);
1266             query[0] = '\0';
1267         } else {
1268             if (interactive && !settings->quiet) {
1269                 if (in_quote)
1270                     settings->prompt[strlen(settings->prompt)-3] = '\'';
1271                 else if (query[0] != '\0' && !querySent)
1272                     settings->prompt[strlen(settings->prompt)-3] = '-';
1273                 else
1274                     settings->prompt[strlen(settings->prompt)-3] = '=';
1275             }
1276             line = GetNextLine(settings->prompt, source);
1277             if (interactive && settings->useReadline && line != NULL)
1278                 add_history(line);      /* save non-empty lines in history */
1279         }
1280
1281         query_start = line;
1282
1283         if (line == NULL) {     /* No more input.  Time to quit */
1284             printf("EOF\n");    /* Goes on prompt line */
1285             eof = true;
1286         } else {
1287             if (!interactive && !settings->singleStep && !settings->quiet)
1288                 fprintf(stderr, "%s\n", line);
1289
1290             /* remove whitespaces on the right, incl. \n's */
1291             line = rightTrim(line);
1292
1293             if (line[0] == '\0') {
1294                 free(line);
1295                 continue;
1296             }
1297
1298             len = strlen(line);
1299
1300             if (settings->singleLineMode) {
1301                 SendQuery(&success, settings, line, false, false, 0);
1302                 successResult &= success;
1303                 querySent = true;
1304             } else {
1305                 int             i;
1306                 was_bslash = false;
1307                 was_dash = false;
1308
1309                 for (i = 0; i < len; i++) {
1310                     if (!in_quote && line[i] == '\\') {
1311                         char            hold_char = line[i];
1312
1313                         line[i] = '\0';
1314                         if (query_start[0] != '\0') {
1315                             if (query[0] != '\0') {
1316                                 strcat(query, "\n");
1317                                 strcat(query, query_start);
1318                             } else
1319                                 strcpy(query, query_start);
1320                         }
1321                         line[i] = hold_char;
1322                         query_start = line + i;
1323                         break;  /* handle command */
1324                     }
1325                     if (querySent && !isspace(line[i])) {
1326                         query[0] = '\0';
1327                         querySent = false;
1328                     }
1329                     if (!in_quote && was_dash && line[i] == '-') {
1330                         /* print comment at top of query */
1331                         if (settings->singleStep)
1332                             fprintf(stdout, "%s\n", line + i - 1);
1333                         line[i - 1] = '\0';     /* remove comment */
1334                         break;
1335                     }
1336                     was_dash = false;
1337
1338                     if (!in_quote && !paren_level &&
1339                         line[i] == ';') {
1340                         char            hold_char = line[i + 1];
1341
1342                         line[i + 1] = '\0';
1343                         if (query_start[0] != '\0') {
1344                             if (query[0] != '\0') {
1345                                 strcat(query, "\n");
1346                                 strcat(query, query_start);
1347                             } else
1348                                 strcpy(query, query_start);
1349                         }
1350                         SendQuery(&success, settings, query, false, false, 0);
1351                         successResult &= success;
1352                         line[i + 1] = hold_char;
1353                         query_start = line + i + 1;
1354                         querySent = true;
1355                     }
1356                     if (was_bslash)
1357                         was_bslash = false;
1358                     else if (line[i] == '\\')
1359                         was_bslash = true;
1360                     else if (line[i] == '\'')
1361                         in_quote ^= 1;
1362                     else if (!in_quote && line[i] == '(')
1363                         paren_level++;
1364                     else if (!in_quote && paren_level && line[i] == ')')
1365                         paren_level--;
1366                     else if (!in_quote && line[i] == '-')
1367                         was_dash = true;
1368                 }
1369             }
1370
1371             slashCmdStatus = -1;
1372             /* slash commands have to be on their own line */
1373             if (!in_quote && query_start[0] == '\\') {
1374                 slashCmdStatus = HandleSlashCmds(settings,
1375                                                  query_start,
1376                                                  query);
1377                 if (slashCmdStatus == 1) {
1378                     free(line);
1379                     continue;
1380                 }
1381                 if (slashCmdStatus == 2) {
1382                     free(line);
1383                     break;
1384                 }
1385             } else if (strlen(query) + strlen(query_start) > MAX_QUERY_BUFFER) {
1386                 fprintf(stderr, "query buffer max length of %d exceeded\n",
1387                         MAX_QUERY_BUFFER);
1388                 fprintf(stderr, "query line ignored\n");
1389             } else {
1390                 if (query_start[0] != '\0') {
1391                     querySent = false;
1392                     if (query[0] != '\0') {
1393                         strcat(query, "\n");
1394                         strcat(query, query_start);
1395                     } else
1396                         strcpy(query, query_start);
1397                 }
1398             }
1399
1400             if (slashCmdStatus == 0) {
1401                 SendQuery(&success, settings, query, false, false, 0);
1402                 successResult &= success;
1403                 querySent = true;
1404             }
1405             free(line);         /* free storage malloc'd by GetNextLine */
1406         }
1407     }                           /* while */
1408     return successResult;
1409 }
1410
1411 int
1412 main(int argc, char **argv)
1413 {
1414     extern char    *optarg;
1415     extern int      optind;
1416
1417     char           *dbname = NULL;
1418     char           *host = NULL;
1419     char           *port = NULL;
1420     char           *qfilename = NULL;
1421     char            errbuf[ERROR_MSG_LENGTH];
1422
1423     PsqlSettings    settings;
1424
1425     char           *singleQuery = NULL;
1426
1427     bool            listDatabases = 0;
1428     int             successResult = 1;
1429     bool            singleSlashCmd = 0;
1430     int             c;
1431
1432     memset(&settings, 0, sizeof settings);
1433     settings.opt.align = 1;
1434     settings.opt.header = 1;
1435     settings.queryFout = stdout;
1436     settings.opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
1437     settings.opt.pager = 1;
1438     if (!isatty(0) || !isatty(1))
1439         settings.quiet = settings.notty = 1;
1440 #ifndef NOREADLINE
1441     else
1442         settings.useReadline = 1;
1443 #endif
1444
1445     while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lh:Hnso:p:qStT:x")) != EOF) {
1446         switch (c) {
1447         case 'A':
1448             settings.opt.align = 0;
1449             break;
1450         case 'a':
1451             fe_setauthsvc(optarg, errbuf);
1452             break;
1453         case 'c':
1454             singleQuery = optarg;
1455             if (singleQuery[0] == '\\') {
1456                 singleSlashCmd = 1;
1457             }
1458             break;
1459         case 'd':
1460             dbname = optarg;
1461             break;
1462         case 'e':
1463             settings.echoQuery = 1;
1464             break;
1465         case 'f':
1466             qfilename = optarg;
1467             break;
1468         case 'F':
1469             settings.opt.fieldSep = optarg;
1470             break;
1471         case 'l':
1472             listDatabases = 1;
1473             break;
1474         case 'h':
1475             host = optarg;
1476             break;
1477         case 'H':
1478             settings.opt.html3 = 1;
1479             break;
1480         case 'n':
1481             settings.useReadline = 0;
1482             break;
1483         case 'o':
1484             setFout(&settings, optarg);
1485             break;
1486         case 'p':
1487             port = optarg;
1488             break;
1489         case 'q':
1490             settings.quiet = 1;
1491             break;
1492         case 's':
1493             settings.singleStep = 1;
1494             break;
1495         case 'S':
1496             settings.singleLineMode = 1;
1497             break;
1498         case 't':
1499             settings.opt.header = 0;
1500             break;
1501         case 'T':
1502             settings.opt.tableOpt = optarg;
1503             break;
1504         case 'x':
1505             settings.opt.expanded = 1;
1506             break;
1507         default:
1508             usage(argv[0]);
1509             break;
1510         }
1511     }
1512     /* if we still have an argument, use it as the database name */
1513     if (argc - optind == 1)
1514         dbname = argv[optind];
1515
1516     if (listDatabases)
1517         dbname = "template1";
1518
1519     settings.db = PQsetdb(host, port, NULL, NULL, dbname);
1520     dbname = PQdb(settings.db);
1521
1522     if (PQstatus(settings.db) == CONNECTION_BAD) {
1523         fprintf(stderr, "Connection to database '%s' failed.\n", dbname);
1524         fprintf(stderr, "%s", PQerrorMessage(settings.db));
1525         exit(1);
1526     }
1527     if (listDatabases) {
1528         exit(listAllDbs(&settings));
1529     }
1530     if (!settings.quiet && !singleQuery && !qfilename) {
1531         printf("Welcome to the POSTGRESQL interactive sql monitor:\n");
1532         printf("  Please read the file COPYRIGHT for copyright terms "
1533                "of POSTGRESQL\n\n");
1534         printf("   type \\? for help on slash commands\n");
1535         printf("   type \\q to quit\n");
1536         printf("   type \\g or terminate with semicolon to execute query\n");
1537         printf(" You are currently connected to the database: %s\n\n", dbname);
1538     }
1539     if (qfilename || singleSlashCmd) {
1540         /*
1541          * read in a file full of queries instead of reading in queries
1542          * interactively
1543          */
1544         char           *line;
1545
1546         if (singleSlashCmd) {
1547             /* Not really a query, but "Do what I mean, not what I say." */
1548             line = singleQuery;
1549         } else {
1550             line = malloc(strlen(qfilename) + 5);
1551             sprintf(line, "\\i %s", qfilename);
1552         }
1553         HandleSlashCmds(&settings, line, "");
1554     } else {
1555         if (singleQuery) {
1556             bool            success;    /* The query succeeded at the backend */
1557             SendQuery(&success, &settings, singleQuery, false, false, 0);
1558             successResult = success;
1559         } else
1560             successResult = MainLoop(&settings, stdin);
1561     }
1562
1563     PQfinish(settings.db);
1564
1565     return !successResult;
1566 }
1567
1568 #define COPYBUFSIZ  8192
1569
1570 static void
1571 handleCopyOut(PGresult * res, bool quiet, FILE * copystream)
1572 {
1573     bool            copydone;
1574     char            copybuf[COPYBUFSIZ];
1575     int             ret;
1576
1577     copydone = false;           /* Can't be done; haven't started. */
1578
1579     while (!copydone) {
1580         ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
1581
1582         if (copybuf[0] == '\\' &&
1583             copybuf[1] == '.' &&
1584             copybuf[2] == '\0') {
1585             copydone = true;    /* don't print this... */
1586         } else {
1587             fputs(copybuf, copystream);
1588             switch (ret) {
1589             case EOF:
1590                 copydone = true;
1591                 /* FALLTHROUGH */
1592             case 0:
1593                 fputc('\n', copystream);
1594                 break;
1595             case 1:
1596                 break;
1597             }
1598         }
1599     }
1600     fflush(copystream);
1601     PQendcopy(res->conn);
1602 }
1603
1604
1605
1606 static void
1607 handleCopyIn(PGresult * res, const bool mustprompt, FILE * copystream)
1608 {
1609     bool            copydone = false;
1610     bool            firstload;
1611     bool            linedone;
1612     char            copybuf[COPYBUFSIZ];
1613     char           *s;
1614     int             buflen;
1615     int             c;
1616
1617     if (mustprompt) {
1618         fputs("Enter info followed by a newline\n", stdout);
1619         fputs("End with a backslash and a "
1620               "period on a line by itself.\n", stdout);
1621     }
1622     while (!copydone) {         /* for each input line ... */
1623         if (mustprompt) {
1624             fputs(">> ", stdout);
1625             fflush(stdout);
1626         }
1627         firstload = true;
1628         linedone = false;
1629         while (!linedone) {     /* for each buffer ... */
1630             s = copybuf;
1631             buflen = COPYBUFSIZ;
1632             for (; buflen > 1 &&
1633                  !(linedone = (c = getc(copystream)) == '\n' || c == EOF);
1634                  --buflen) {
1635                 *s++ = c;
1636             }
1637             if (c == EOF) {
1638                 PQputline(res->conn, "\\.");
1639                 copydone = true;
1640                 break;
1641             }
1642             *s = '\0';
1643             PQputline(res->conn, copybuf);
1644             if (firstload) {
1645                 if (!strcmp(copybuf, "\\.")) {
1646                     copydone = true;
1647                 }
1648                 firstload = false;
1649             }
1650         }
1651         PQputline(res->conn, "\n");
1652     }
1653     PQendcopy(res->conn);
1654 }
1655
1656
1657
1658 /*
1659  * try to open fname and return a FILE *, if it fails, use stdout, instead
1660  */
1661
1662 FILE           *
1663 setFout(PsqlSettings * ps, char *fname)
1664 {
1665     if (ps->queryFout && ps->queryFout != stdout) {
1666         if (ps->pipe)
1667             pclose(ps->queryFout);
1668         else
1669             fclose(ps->queryFout);
1670     }
1671     if (!fname) {
1672         ps->queryFout = stdout;
1673         pqsignal(SIGPIPE, SIG_DFL);
1674     }
1675     else {
1676         if (*fname == '|') {
1677             pqsignal(SIGPIPE, SIG_IGN);
1678             ps->queryFout = popen(fname + 1, "w");
1679             ps->pipe = 1;
1680         } else {
1681             ps->queryFout = fopen(fname, "w");
1682             pqsignal(SIGPIPE, SIG_DFL);
1683             ps->pipe = 0;
1684         }
1685         if (!ps->queryFout) {
1686             perror(fname);
1687             ps->queryFout = stdout;
1688         }
1689     }
1690     return ps->queryFout;
1691 }