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