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