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