]> granicus.if.org Git - postgresql/blob - src/bin/psql/psql.c
Changed psql \h command to print commands in three columns.
[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.22 1996/10/09 00:15:44 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <stdio.h>
15 #include <string.h>
16 #include <signal.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include "libpq-fe.h"
22 #include "stringutils.h"
23
24 #include "psqlHelp.h"
25
26 #ifdef NOREADLINE
27 extern char *readline(char *);  /* in rlstubs.c */
28 #else
29 /* from the GNU readline library */
30 #ifdef OLD_READLINE
31 #include "readline.h"
32 #include "history.h"
33 #else
34 #include <readline/readline.h>
35 #include <readline/history.h>
36 #endif
37 #endif
38
39 #define MAX_QUERY_BUFFER 20000
40
41 #define COPYBUFSIZ      8192
42
43 #define DEFAULT_FIELD_SEP "|"
44 #define DEFAULT_EDITOR  "vi"
45 #define DEFAULT_SHELL  "/bin/sh"
46
47 typedef struct _psqlSettings {
48     PGconn *db;            /* connection to backend */
49     FILE *queryFout;       /* where to send the query results */
50     PQprintOpt opt;        /* options to be passed to PQprint */
51     char *prompt;          /* prompt to display */
52     char *gfname;          /* one-shot file output argument for \g */
53     bool notty;            /* input or output is not a tty */
54     bool pipe;             /* queryFout is from a popen() */
55     bool echoQuery;        /* echo the query before sending it */
56     bool quiet;            /* run quietly, no messages, no promt */
57     bool singleStep;       /* prompt before for each query */ 
58     bool singleLineMode;   /* query terminated by newline */
59     bool useReadline;      /* use libreadline routines */
60 } PsqlSettings;
61
62 /* declarations for functions in this file */
63 static void usage(char *progname);
64 static void slashUsage();
65 static void handleCopyOut(PGresult *res, bool quiet);
66 static void handleCopyIn(PGresult *res, bool quiet);
67 static int tableList(PsqlSettings *ps, bool deep_tablelist);
68 static int tableDesc(PsqlSettings *ps, char *table);
69
70 char *gets_noreadline(char *prompt, FILE *source);
71 char *gets_readline(char *prompt, FILE *source);
72 char *gets_fromFile(char *prompt, FILE *source);
73 int listAllDbs(PsqlSettings *settings);
74 int SendQuery(PsqlSettings *settings, char *query);
75 int HandleSlashCmds(PsqlSettings *settings,
76                     char *line,
77                     char *query);
78 int MainLoop(PsqlSettings *settings, FILE *source);
79 /* probably should move this into libpq */
80 void PQprint(FILE *fp,
81                      PGresult *res, 
82                      PQprintOpt *po
83                      );
84
85 FILE *setFout(PsqlSettings *ps, char *fname);
86
87 /*
88  * usage 
89  *   print out usage for command line arguments 
90  */
91
92 static void  
93 usage(char *progname)
94 {
95   fprintf(stderr,"Usage: %s [options] [dbname]\n",progname);
96   fprintf(stderr,"\t -a authsvc              set authentication service\n"); 
97   fprintf(stderr,"\t -A                      turn off alignment when printing out attributes\n");
98   fprintf(stderr,"\t -c query                run single query (slash commands too)\n");
99   fprintf(stderr,"\t -d dbName               specify database name\n");
100   fprintf(stderr,"\t -e                      echo the query sent to the backend\n");
101   fprintf(stderr,"\t -f filename             use file as a source of queries\n");
102   fprintf(stderr,"\t -F sep                  set the field separator (default is " ")\n");
103   fprintf(stderr,"\t -h host                 set database server host\n");
104   fprintf(stderr,"\t -H                      turn on html3.0 table output\n");
105   fprintf(stderr,"\t -l                      list available databases\n");
106   fprintf(stderr,"\t -n                      don't use readline library\n");
107   fprintf(stderr,"\t -o filename             send output to filename or (|pipe)\n");
108   fprintf(stderr,"\t -p port                 set port number\n");
109   fprintf(stderr,"\t -q                      run quietly (no messages, no prompts)\n");
110   fprintf(stderr,"\t -s                      single step mode (prompts for each query)\n");
111   fprintf(stderr,"\t -S                      single line mode (i.e. query terminated by newline)\n");
112   fprintf(stderr,"\t -t                      turn off printing of headings and row count\n");
113   fprintf(stderr,"\t -T html                 set html3.0 table command options (cf. -H)\n");
114   fprintf(stderr,"\t -x                      turn on expanded output (field names on left)\n");
115   exit(1);
116 }
117
118 /*
119  * slashUsage
120  *    print out usage for the backslash commands 
121  */
122
123 char *on(bool f)
124 {
125         return f? "on": "off";
126 }
127
128 static void
129 slashUsage(PsqlSettings *ps)
130 {
131   fprintf(stderr,"\t \\?           -- help\n");
132   fprintf(stderr,"\t \\a           -- toggle field-alignment (currenty %s)\n", on(ps->opt.align));
133   fprintf(stderr,"\t \\C [<captn>] -- set html3 caption (currently '%s')\n", ps->opt.caption? ps->opt.caption: "");
134   fprintf(stderr,"\t \\c <dbname>  -- connect to new database (currently '%s')\n", PQdb(ps->db));
135   fprintf(stderr,"\t \\d [<table>] -- list tables in database or columns in <table>,* for all\n");
136   fprintf(stderr,"\t \\e [<fname>] -- edit the current query buffer or <fname>,\\E execute too\n");
137   fprintf(stderr,"\t \\f [<sep>]   -- change field separater (currently '%s')\n", ps->opt.fieldSep);
138   fprintf(stderr,"\t \\g [<fname>] -- send query to backend [and place results in <fname>]\n");
139   fprintf(stderr,"\t \\g |<cmd>    -- send query to backend and pipe results into <cmd>\n");
140   fprintf(stderr,"\t \\h [<cmd>]   -- help on syntax of sql commands, * for all commands\n");
141   fprintf(stderr,"\t \\H           -- toggle html3 output (currently %s)\n", on(ps->opt.html3));
142   fprintf(stderr,"\t \\i <fname>   -- read and execute queries from filename\n");
143   fprintf(stderr,"\t \\l           -- list all databases\n");
144   fprintf(stderr,"\t \\m           -- toggle monitor-like table display (currently %s)\n", on(ps->opt.standard));
145   fprintf(stderr,"\t \\o [<fname>] -- send all query results to <fname> or stdout\n");
146   fprintf(stderr,"\t \\o |<cmd>    -- pipe all query results through <cmd>\n");
147   fprintf(stderr,"\t \\p           -- print the current query buffer\n");
148   fprintf(stderr,"\t \\q           -- quit\n");
149   fprintf(stderr,"\t \\r           -- reset(clear) the query buffer\n");
150   fprintf(stderr,"\t \\s [<fname>] -- print history or save it in <fname>\n");
151   fprintf(stderr,"\t \\t           -- toggle table headings and row count (currently %s)\n", on(ps->opt.header));
152   fprintf(stderr,"\t \\T [<html>]  -- set html3.0 <table ...> options (currently '%s')\n", ps->opt.tableOpt? ps->opt.tableOpt: "");
153   fprintf(stderr,"\t \\x           -- toggle expanded output (currently %s)\n", on(ps->opt.expanded));
154   fprintf(stderr,"\t \\! [<cmd>]   -- shell escape or command\n");
155 }
156
157 PGresult *
158 PSQLexec(PsqlSettings *ps, char *query)
159 {
160         PGresult *res = PQexec(ps->db, query);
161         if (!res)
162                 fputs(PQerrorMessage(ps->db), stderr);
163         else
164         {
165                 if (PQresultStatus(res)==PGRES_COMMAND_OK ||
166                     PQresultStatus(res)==PGRES_TUPLES_OK)
167                         return res;
168                 if (!ps->quiet)
169                         fputs(PQerrorMessage(ps->db), stderr);
170                 PQclear(res);
171         }
172         return NULL;
173 }
174 /*
175  * listAllDbs
176  *
177  * list all the databases in the system
178  *     returns 0 if all went well
179  *    
180  *
181  */
182
183 int
184 listAllDbs(PsqlSettings *ps)
185 {
186   PGresult *results;
187   char *query = "select * from pg_database;";
188
189   if (!(results=PSQLexec(ps, query)))
190     return 1;
191   else
192     {
193       PQprint(ps->queryFout,
194                       results,
195                       &ps->opt);
196       PQclear(results);
197       return 0;
198     }
199 }
200
201 /*
202  *
203  * List The Database Tables
204  *     returns 0 if all went well
205  *    
206  */
207 int
208 tableList (PsqlSettings *ps, bool deep_tablelist)
209 {
210   char listbuf[256];
211   int nColumns; 
212   int i;
213   char *rk;
214   char *rr;
215
216   PGresult *res;
217
218   listbuf[0] = '\0';
219   strcat(listbuf,"SELECT usename, relname, relkind, relhasrules");
220   strcat(listbuf,"  FROM pg_class, pg_user ");
221   strcat(listbuf,"WHERE ( relkind = 'r' OR relkind = 'i') ");
222   strcat(listbuf,"  and relname !~ '^pg_'");
223   strcat(listbuf,"  and relname !~ '^Inv[0-9]+'");
224 /* the usesysid = relowner won't work on stock 1.0 dbs, need to 
225    add in the int4oideq function */
226   strcat(listbuf,"  and usesysid = relowner");
227   strcat(listbuf,"  ORDER BY relname ");
228   if (!(res=PSQLexec(ps, listbuf)))
229         return -1;
230
231   /* first, print out the attribute names */
232   nColumns = PQntuples(res);
233   if (nColumns > 0)
234   {
235       if ( deep_tablelist ) {
236           /* describe everything here */
237           char **table;
238           table = (char**)malloc(nColumns * sizeof(char*));
239           if ( table == NULL )
240               perror("malloc");
241           
242           /* load table table */
243           for (i=0; i < nColumns; i++) {
244               table[i] = (char *) malloc(PQgetlength(res,i,1) * sizeof(char) + 1);
245               if ( table[i] == NULL )
246                   perror("malloc");
247               strcpy(table[i],PQgetvalue(res,i,1));
248           }
249
250         PQclear(res);
251         for (i=0; i < nColumns; i++) {
252            tableDesc(ps, table[i]);
253         }
254         free(table);
255       }
256       else {
257         /* Display the information */
258
259         printf ("\nDatabase    = %s\n", PQdb(ps->db));
260         printf (" +------------------+----------------------------------+----------+\n");
261         printf (" |  Owner           |             Relation             |   Type   |\n");
262         printf (" +------------------+----------------------------------+----------+\n");
263
264         /* next, print out the instances */
265         for (i=0; i < PQntuples(res); i++) {
266             printf (" | %-16.16s", PQgetvalue(res,i,0));
267             printf (" | %-32.32s | ", PQgetvalue(res,i,1));
268             rk =  PQgetvalue(res,i,2);
269             rr =  PQgetvalue(res,i,3);
270             if (strcmp(rk, "r") == 0)
271                 printf ("%-8.8s |", (rr[0] == 't') ? "view?" : "table" );
272             else
273                 printf ("%-8.8s |", "index");
274             printf("\n");
275         }
276         printf (" +------------------+----------------------------------+----------+\n");
277         PQclear(res);
278       }
279       return (0);
280   
281   } else {
282     fprintf (stderr, "Couldn't find any tables!\n");
283     return (-1);
284   }
285 }
286
287 /*
288  * Describe a table
289  *
290  * Describe the columns in a database table.
291  *     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     /*
325     ** Display the information
326     */
327
328     printf ("\nTable    = %s\n", table);
329     printf ("+----------------------------------+----------------------------------+-------+\n");
330     printf ("|              Field               |              Type                | Length|\n");
331     printf ("+----------------------------------+----------------------------------+-------+\n");
332
333     /* next, print out the instances */
334     for (i=0; i < PQntuples(res); i++) {
335       printf ("| %-32.32s | ", PQgetvalue(res,i,1));
336       rtype = PQgetvalue(res,i,2);
337       rsize = atoi(PQgetvalue(res,i,3));
338       if (strcmp(rtype, "text") == 0) {
339         printf ("%-32.32s |", rtype);
340         printf ("%6s |",  "var" );
341       }
342       else if (strcmp(rtype, "bpchar") == 0) {
343         printf ("%-32.32s |", "char");
344         printf ("%6i |", rsize > 0 ? rsize - 4 : 0 );
345       }
346       else if (strcmp(rtype, "varchar") == 0) {
347         printf ("%-32.32s |", rtype);
348         printf ("%6i |", rsize > 0 ? rsize - 4 : 0 );
349       }
350       else {
351           /* array types start with an underscore */
352           if (rtype[0] != '_')
353               printf ("%-32.32s |", rtype);
354           else  {
355               char *newname;
356               newname = malloc(strlen(rtype) + 2);
357               strcpy(newname, rtype+1);
358               strcat(newname, "[]");
359               printf ("%-32.32s |", newname);
360               free(newname);
361           }
362         if (rsize > 0) 
363             printf ("%6i |", rsize);
364         else
365             printf ("%6s |", "var");
366       }
367       printf("\n");
368     }
369     printf ("+----------------------------------+----------------------------------+-------+\n");
370
371     PQclear(res);
372     return (0);
373   
374   } else {
375       fprintf (stderr, "Couldn't find table %s!\n", table);
376     return (-1);
377   }
378 }
379
380 typedef char *(*READ_ROUTINE)(char *prompt, FILE *source);
381
382 /* gets_noreadline  prompt source
383       gets a line of input without calling readline, the source is ignored
384 */
385 char *
386 gets_noreadline(char *prompt, FILE *source)
387 {
388     fputs(prompt, stdout);
389     fflush(stdout);
390     return(gets_fromFile(prompt,stdin));
391 }
392
393 /*
394  * gets_readline  prompt source
395  *   the routine to get input from GNU readline(), the source is ignored 
396  * the prompt argument is used as the prompting string
397  */
398 char *
399 gets_readline(char *prompt, FILE *source)
400 {
401   return (readline(prompt));
402 }
403
404 /*
405  * gets_fromFile  prompt source
406  *    
407  * the routine to read from a file, the prompt argument is ignored
408  * the source argument is a FILE *
409  */
410 char *
411 gets_fromFile(char *prompt, FILE *source)
412 {
413   char *line;
414   int len;
415
416   line = malloc(MAX_QUERY_BUFFER+1);
417
418   /* read up to MAX_QUERY_BUFFER characters */
419   if (fgets(line, MAX_QUERY_BUFFER, source) == NULL)
420     return NULL;
421
422   line[MAX_QUERY_BUFFER-1] = '\0';
423   len = strlen(line);
424   if (len == MAX_QUERY_BUFFER)
425     {
426       fprintf(stderr, "line read exceeds maximum length.  Truncating at %d\n", MAX_QUERY_BUFFER);
427     }
428   
429   return line;
430 }
431
432 /*
433  *  SendQuery: send the query string to the backend 
434  *  return 0 if the query executed successfully
435  *  returns 1 otherwise
436  */
437 int
438 SendQuery(PsqlSettings *settings, char *query)
439 {
440   PGresult *results;
441   PGnotify *notify;
442   int status = 0;
443
444   if (settings->singleStep)
445         fprintf(stdout, "\n*******************************************************************************\n");
446
447   if (settings->echoQuery || settings->singleStep) {
448       fprintf(stderr,"QUERY: %s\n",query);
449       fflush(stderr);
450   }
451
452   if (settings->singleStep) {
453         fprintf(stdout, "\n*******************************************************************************\n");
454         fflush(stdout);
455         printf("\npress return to continue ..\n");
456         gets_fromFile("",stdin);
457   }
458
459   results = PQexec(settings->db, query);
460   if (results == NULL) {
461     fprintf(stderr,"%s",PQerrorMessage(settings->db));
462     return 1;
463   }
464
465   switch (PQresultStatus(results)) {
466   case PGRES_TUPLES_OK:
467       if (settings->gfname)
468       {
469                 PsqlSettings ps=*settings;
470                 FILE *fp;
471                 ps.queryFout=stdout;
472                 fp=setFout(&ps, settings->gfname);
473                 if (!fp || fp==stdout)
474                 {
475                         status = 1;
476                         break;
477                 }
478                 PQprint(fp,
479                         results,
480                         &(settings->opt));
481                 if (ps.pipe)
482                         pclose(fp);
483                 else
484                         fclose(fp);
485                 settings->gfname=NULL;
486                 break;
487         } else 
488         {
489               PQprint(settings->queryFout,
490                               results,
491                               &(settings->opt));
492               fflush(settings->queryFout);
493         }
494       PQclear(results);
495       break;
496   case PGRES_EMPTY_QUERY:
497     /* do nothing */
498     break;
499   case PGRES_COMMAND_OK:
500     if (!settings->quiet)
501       fprintf(stdout,"%s\n", PQcmdStatus(results));
502     break;
503   case PGRES_COPY_OUT:
504     handleCopyOut(results, settings->quiet);
505     break;
506   case PGRES_COPY_IN:
507     handleCopyIn(results, settings->quiet);
508     break;
509   case PGRES_NONFATAL_ERROR:
510   case PGRES_FATAL_ERROR:
511   case PGRES_BAD_RESPONSE:
512     status = 1;
513     fprintf(stderr,"%s",PQerrorMessage(settings->db));
514     break;
515   } 
516
517   /* check for asynchronous returns */
518   notify = PQnotifies(settings->db);
519   if (notify) {
520       fprintf(stderr,"ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
521               notify->relname, notify->be_pid);
522       free(notify);
523   }
524
525   return status;
526
527 }
528
529 void
530 editFile(char *fname)
531 {
532     char *editorName;
533     char *sys;
534     editorName = getenv("EDITOR");
535     if (!editorName)
536         editorName = DEFAULT_EDITOR;
537     sys=malloc(strlen(editorName)+strlen(fname)+32+1);
538     if (!sys)
539     {
540         perror("malloc");
541         exit(1);
542     }
543     sprintf(sys, "exec '%s' '%s'", editorName, fname);
544     system(sys);
545     free(sys);
546 }
547
548 bool
549 toggle(PsqlSettings *settings, bool *sw, char *msg)
550 {
551         *sw= !*sw;
552         if (!settings->quiet)
553             fprintf(stderr, "turned %s %s\n", on(*sw), msg);
554         return *sw;
555 }
556
557 void
558 decode(char *s)
559 {
560         char *p, *d;
561         bool esc=0;
562         for (d=p=s; *p; p++)
563         {
564                 char c=*p;
565                 if (esc)
566                 {
567                         switch(*p)
568                         {
569                         case 'n':
570                                 c='\n';
571                                 break;
572                         case 'r':
573                                 c='\r';
574                                 break;
575                         case 't':
576                                 c='\t';
577                                 break;
578                         case 'f':
579                                 c='\f';
580                                 break;
581                         }
582                         esc=0;
583                 } else
584                         if (c=='\\')
585                         {
586                                 esc=1;
587                                 continue;
588                         }
589                 *d++=c;
590         }
591         *d='\0';
592 }
593
594 /*
595   HandleSlashCmds:
596
597   Handles all the different commands that start with \ 
598      db_ptr is a pointer to the TgDb* structure
599      line is the current input line
600      prompt_ptr is a pointer to the prompt string,
601                   a pointer is used because the prompt can be used with 
602                   a connection to a new database
603   returns a status:
604        0 - send currently constructed query to backend (i.e. we got a \g)
605        1 - skip processing of this line, continue building up query
606        2 - terminate processing of this query entirely
607 */
608 int
609 HandleSlashCmds(PsqlSettings *settings,
610                 char *line, 
611                 char *query)
612 {
613   int status = 1;
614   char *optarg = NULL;
615   int len;
616
617   len = strlen(line);
618   if (len > 2)
619   {
620       optarg = leftTrim(line+2);
621       decode(optarg);
622   }
623   switch (line[1])
624   {
625     case 'a': /* toggles to align fields on output */
626         toggle(settings, &settings->opt.align, "field alignment");
627         break;
628     case 'C': /* define new caption */
629         if (settings->opt.caption)
630                 free(settings->opt.caption);
631         if (!optarg)
632                 settings->opt.caption=NULL;
633         else
634                 if (!(settings->opt.caption=strdup(optarg)))
635                 {
636                         perror("malloc");
637                         exit(1);
638                 }
639         break;
640     case 'c':  /* \c means connect to new database */
641     {
642           char *dbname=PQdb(settings->db);
643           if (!optarg) {
644               fprintf(stderr,"\\c must be followed by a database name\n");
645               break;
646           }
647           {
648               PGconn *olddb=settings->db;
649               
650               printf("closing connection to database: %s\n", dbname);
651               settings->db = PQsetdb(PQhost(olddb), PQport(olddb), NULL, NULL, optarg);
652               printf("connecting to new database: %s\n", optarg);
653               if (PQstatus(settings->db) == CONNECTION_BAD) {
654                   fprintf(stderr,"%s\n", PQerrorMessage(settings->db));
655                   printf("reconnecting to %s\n", dbname);
656                   settings->db = PQsetdb(PQhost(olddb), PQport(olddb), 
657                                NULL, NULL, dbname);
658                   if (PQstatus(settings->db) == CONNECTION_BAD) {
659                       fprintf(stderr, 
660                               "could not reconnect to %s. exiting\n", dbname);
661                       exit(2);
662                   }
663                   break;
664               }
665               PQfinish(olddb);
666               free(settings->prompt);
667               settings->prompt = malloc(strlen(PQdb(settings->db)) + 10);
668               sprintf(settings->prompt,"%s=> ", PQdb(settings->db));
669             break;
670           }
671       }
672       break;
673     case 'd':     /* \d describe tables or columns in a table */
674         if (!optarg) {
675           tableList(settings, 0);
676           break;
677         } 
678         if (strcmp(optarg, "*") == 0 ) {
679            tableList(settings, 0);
680            tableList(settings, 1);
681         }
682         else {
683            tableDesc(settings, optarg);
684         }
685         break;
686     case 'e':
687     {
688         int fd;
689         char tmp[64];
690         char *fname;
691         int cc;
692         int ql = strlen(query);
693         if (optarg)
694                 fname=optarg;
695         else
696         {
697                 sprintf(tmp, "/tmp/psql.%d.%d", geteuid(), getpid());
698                 fname=tmp;
699                 unlink(tmp);
700                 if (ql)
701                 {
702                         if ((fd=open(tmp, O_EXCL|O_CREAT|O_WRONLY, 0600))==-1)
703                         {
704                                 perror(tmp);
705                                 break;
706                         }
707                         if (query[ql-1]!='\n')
708                                 strcat(query, "\n");
709                         if (write(fd, query, ql)!=ql)
710                         {
711                                 perror(tmp);
712                                 close(fd);
713                                 unlink(tmp);
714                                 break;
715                         }
716                         close(fd);
717                 }
718         }
719         editFile(fname);
720         if ((fd=open(fname, O_RDONLY))==-1)
721         {
722                 perror(fname);
723                 if (!optarg)
724                         unlink(fname);
725                 break;
726         }
727         if ((cc=read(fd, query, MAX_QUERY_BUFFER))==-1)
728         {
729                 perror(fname);
730                 close(fd);
731                 if (!optarg)
732                         unlink(fname);
733                 break;
734         }       
735         query[cc]='\0';
736         close(fd);
737         if (!optarg)
738                 unlink(fname);
739         rightTrim(query);
740         if (query[strlen(query)-1]==';')
741                 return 0;
742         break;
743     }
744     case 'E':
745     {
746         FILE *fd;
747         static char *lastfile;
748         struct stat st, st2;
749         if (optarg)
750         {
751                 if (lastfile)
752                         free(lastfile);
753                 lastfile=malloc(strlen(optarg+1));
754                 if (!lastfile)
755                 {
756                         perror("malloc");
757                         exit(1);
758                 }
759                 strcpy(lastfile, optarg);
760         } else if (!lastfile)
761                 {
762                           fprintf(stderr,"\\r must be followed by a file name initially\n");
763                           break;
764                 }
765         stat(lastfile, &st);
766         editFile(lastfile);
767         if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "r")) == NULL))
768           {
769             perror(lastfile);
770             break;
771           }
772         if (st2.st_mtime==st.st_mtime)
773         {
774                 if (!settings->quiet)
775                         fprintf(stderr, "warning: %s not modified. query not executed\n", lastfile);
776                 fclose(fd);
777                 break;
778         }
779         MainLoop(settings, fd);
780         fclose(fd);
781         break;
782     }
783     case 'f':
784     {
785         char *fs=DEFAULT_FIELD_SEP;
786         if (optarg)
787                 fs=optarg;
788         if (settings->opt.fieldSep);
789                 free(settings->opt.fieldSep);
790         if (!(settings->opt.fieldSep=strdup(fs)))
791         {
792                 perror("malloc");
793                 exit(1);
794         }
795         if (!settings->quiet)
796                 fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
797         break;
798     }
799     case 'g':  /* \g means send query */
800       settings->gfname = optarg;
801       status = 0;     
802       break;
803     case 'h':
804     {
805         char *cmd;
806         int i, numCmds;
807         int all_help = 0;
808         char left_center_right = 'L';
809
810         if (!optarg) {
811             printf("type \\h <cmd> where <cmd> is one of the following:\n");
812             i = 0;
813             while (QL_HELP[i].cmd != NULL)
814               {
815                 switch(left_center_right)
816                 {
817                         case 'L':
818                                 printf("    %-25s", QL_HELP[i].cmd);
819                                 left_center_right = 'C';
820                                 break;
821                         case 'C':
822                                 printf("%-25s", QL_HELP[i].cmd);
823                                 left_center_right = 'R';
824                                 break;
825                         case 'R':
826                                 printf("%-25s\n", QL_HELP[i].cmd);
827                                 left_center_right = 'L';
828                                 break;
829                 };
830                 i++;
831               }
832              if (left_center_right != 'L')
833                 puts("\n");
834              printf("type \\h * for a complete description of all commands\n");
835           }
836         else
837           {
838           cmd = optarg;
839
840           numCmds = 0;
841           while (QL_HELP[numCmds++].cmd != NULL);
842
843           numCmds = numCmds - 1;
844
845           if (strcmp(cmd, "*") == 0 ) {
846              all_help=1;
847           }
848
849           for (i=0; i<numCmds;i++)  {
850               if (strcmp(QL_HELP[i].cmd, cmd) == 0 || all_help)    {
851                 printf("Command: %s\n",QL_HELP[i].cmd);
852                 printf("Description: %s\n", QL_HELP[i].help);
853                 printf("Syntax:\n");
854                 printf("%s\n", QL_HELP[i].syntax);
855                 if ( all_help ) {
856                    printf("\n");
857                 }
858                 else {
859                    break;
860                }
861             }
862           }
863           if (i == numCmds && ! all_help)
864             printf("command not found,  try \\h with no arguments to see available help\n");
865         }
866         break;
867     }
868     case 'i':     /* \i is include file */
869       {
870         FILE *fd;
871
872         if (!optarg) {
873           fprintf(stderr,"\\i must be followed by a file name\n");
874           break;
875         }
876
877         if ((fd = fopen(optarg, "r")) == NULL)
878           {
879             fprintf(stderr,"file named %s could not be opened\n",optarg);
880             break;
881           }
882         MainLoop(settings, fd);
883         fclose(fd);
884         break;
885     }
886     case 'l':     /* \l is list database */
887       listAllDbs(settings);
888       break;
889     case 'H':
890       if (toggle(settings, &settings->opt.html3, "HTML3.0 tablular output"))
891           settings->opt.standard = 0;
892       break;
893     case 'o':
894       setFout(settings, optarg);
895       break;
896     case 'p':
897       if (query)
898       {
899               fputs(query, stdout);
900               fputc('\n', stdout);
901       }
902       break;
903     case 'q': /* \q is quit */
904       status = 2;
905       break;
906     case 'r': /* reset(clear) the buffer */
907       query[0]='\0';
908       if (!settings->quiet)
909           fprintf(stderr, "buffer reset(cleared)\n");
910       break;
911     case 's': /* \s is save history to a file */
912         if (!optarg)
913                 optarg="/dev/tty";
914         if (write_history(optarg) != 0)
915             fprintf(stderr,"cannot write history to %s\n",optarg);
916         break;
917     case 'm': /* monitor like type-setting */
918       if (toggle(settings, &settings->opt.standard, "standard SQL separaters and padding"))
919       {
920           settings->opt.html3 = settings->opt.expanded = 0;
921           settings->opt.align = settings->opt.header = 1;
922           free(settings->opt.fieldSep);
923           settings->opt.fieldSep=strdup("|");
924           if (!settings->quiet)
925               fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
926       } else
927       {
928           free(settings->opt.fieldSep);
929           settings->opt.fieldSep=strdup(DEFAULT_FIELD_SEP);
930           if (!settings->quiet)
931               fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
932       }
933       break;
934     case 't': /* toggle headers */
935       toggle(settings, &settings->opt.header, "output headings and row count");
936       break;
937     case 'T': /* define html <table ...> option */
938         if (settings->opt.tableOpt)
939                 free(settings->opt.tableOpt);
940         if (!optarg)
941                 settings->opt.tableOpt=NULL;
942         else
943                 if (!(settings->opt.tableOpt=strdup(optarg)))
944                 {
945                         perror("malloc");
946                         exit(1);
947                 }
948         break;
949     case 'x':
950       toggle(settings, &settings->opt.expanded, "expanded table representation");
951       break;
952     case '!':
953       if (!optarg) {
954           char *sys;
955           char *shellName;
956           shellName = getenv("SHELL");
957           if (shellName == NULL) 
958               shellName = DEFAULT_SHELL;
959           sys = malloc(strlen(shellName)+16);
960           if (!sys)
961           {
962                 perror("malloc");
963                 exit(1);
964           }
965           sprintf(sys,"exec %s", shellName);
966           system(sys);
967           free(sys);
968       }
969       else
970           system(optarg);
971       break;
972     default:
973     case '?':     /* \? is help */
974       slashUsage(settings);
975       break;
976     }
977   return status;
978 }
979
980 /* 
981  MainLoop: main processing loop for reading lines of input
982  and sending them to the backend
983
984  this loop is re-entrant.  May be called by \i command
985  which reads input from a file
986
987  *db_ptr must be initialized and set
988 */
989
990 int
991 MainLoop(PsqlSettings *settings, FILE *source)
992 {
993   char *line;                   /* line of input */
994   int len;                      /* length of the line */
995   char query[MAX_QUERY_BUFFER]; /* multi-line query storage */
996   int exitStatus = 0;
997   int slashCmdStatus = 0;
998  /* slashCmdStatus can be:
999        0 - send currently constructed query to backend (i.e. we got a \g)
1000        1 - skip processing of this line, continue building up query
1001        2 - terminate processing of this query entirely
1002   */
1003
1004   bool sendQuery = 0;
1005   bool querySent = 0;
1006   bool interactive;
1007   READ_ROUTINE GetNextLine;
1008   bool connected = 1;  
1009     /* We are connected to the backend (last time we looked) */
1010   bool eof = 0;
1011     /* We've reached the end of our command input. */
1012
1013   interactive = ((source == stdin) && !settings->notty);
1014 #define PROMPT "=> "
1015   if (interactive) {
1016     if (settings->prompt)
1017         free(settings->prompt);
1018     settings->prompt = malloc(strlen(PQdb(settings->db)) + strlen(PROMPT) + 1);
1019     if (settings->quiet)
1020       settings->prompt[0] = '\0';
1021     else
1022       sprintf(settings->prompt,"%s%s", PQdb(settings->db), PROMPT);
1023     if (settings->useReadline) {
1024         using_history();
1025         GetNextLine = gets_readline;
1026     } else
1027         GetNextLine = gets_noreadline;
1028
1029   }
1030   else
1031     GetNextLine = gets_fromFile;
1032
1033   query[0] = '\0';
1034   
1035   /* main loop for getting queries and executing them */
1036   while (connected && !eof) {
1037     line = GetNextLine(settings->prompt, source);
1038     if (line == NULL) {   /* No more input.  Time to quit */
1039       printf("EOF\n");  /* Goes on prompt line */
1040       eof = 1;
1041     } else {
1042       exitStatus = 0;
1043       line = rightTrim(line); /* remove whitespaces on the right, incl. \n's */
1044
1045       if (line[0] == '\0') {
1046           free(line);
1047           continue;
1048       }
1049
1050       /* filter out comment lines that begin with --,
1051          this could be incorrect if -- is part of a quoted string.
1052          But we won't go through the trouble of detecting that.  If you have
1053          -- in your quoted string, be careful and don't start a line with it */
1054       if (line[0] == '-' && line[1] == '-') {
1055           if (settings->singleStep) /* in single step mode, show comments */
1056               fprintf(stdout,"%s\n",line);
1057           free(line);
1058           continue;
1059       }
1060       if (line[0] != '\\' && querySent)
1061       {
1062           query[0]='\0';
1063           querySent = 0;
1064       }
1065
1066       len = strlen(line);
1067
1068       if (interactive && settings->useReadline)
1069           add_history(line);      /* save non-empty lines in history */
1070       
1071       /* do the query immediately if we are doing single line queries 
1072        or if the last character is a semicolon */
1073       sendQuery = settings->singleLineMode || (line[len-1] == ';') ;
1074
1075       /* normally, \ commands have to be start the line,
1076          but for backwards compatibility with monitor,
1077          check for \g at the end of line */
1078       if (len > 2 && !sendQuery) 
1079         {
1080           if (line[len-1]=='g' && line[len-2]=='\\')
1081             {
1082             sendQuery = 1;
1083             line[len-2]='\0';
1084           }
1085         }
1086       
1087       /* slash commands have to be on their own line */
1088       if (line[0] == '\\') {
1089           slashCmdStatus = HandleSlashCmds(settings,
1090                                            line, 
1091                                            query);
1092         if (slashCmdStatus == 1) {
1093           free(line);
1094           continue;
1095         }
1096         if (slashCmdStatus == 2) {
1097           free(line);
1098           break;
1099         }
1100         if (slashCmdStatus == 0)
1101           sendQuery = 1;
1102       }
1103       else
1104         if (strlen(query) + len > MAX_QUERY_BUFFER)
1105           {
1106             fprintf(stderr,"query buffer max length of %d exceeded\n",MAX_QUERY_BUFFER);
1107             fprintf(stderr,"query line ignored\n");
1108           }
1109       else
1110         if (query[0]!='\0') {
1111             strcat(query,"\n");
1112             strcat(query,line);
1113         }
1114       else
1115         strcpy(query,line);
1116       
1117       if (sendQuery && query[0] != '\0')
1118         {
1119             /* echo the line read from the file,
1120              unless we are in single_step mode, because single_step mode
1121              will echo anyway */
1122           if (!interactive && !settings->singleStep && !settings->quiet) 
1123             fprintf(stderr,"%s\n", query);
1124
1125           exitStatus = SendQuery(settings, query);
1126           querySent = 1;
1127           if (PQstatus(settings->db) == CONNECTION_BAD) {
1128             connected = 0;
1129             fprintf(stderr, "We have lost the connection to the backend, so "
1130                     "further processing is impossible.  Terminating.\n");
1131           }
1132         }
1133       free(line); /* free storage malloc'd by GetNextLine */
1134     }      
1135   } /* while */
1136   return exitStatus;
1137
1138
1139 int
1140 main(int argc, char **argv)
1141 {
1142   extern char *optarg;
1143   extern int optind;
1144   
1145   char *dbname = NULL;
1146   char *host = NULL;
1147   char *port = NULL;
1148   char *qfilename = NULL;
1149   char errbuf[ERROR_MSG_LENGTH];
1150
1151   PsqlSettings settings;
1152
1153   char *singleQuery = NULL;
1154
1155   bool listDatabases = 0 ;
1156   int exitStatus = 0;
1157   bool singleSlashCmd = 0;
1158   int c;
1159
1160   memset(&settings, 0, sizeof settings);
1161   settings.opt.align = 1;
1162   settings.opt.header = 1;
1163   settings.queryFout = stdout;
1164   settings.opt.fieldSep=strdup(DEFAULT_FIELD_SEP);
1165   settings.opt.pager = 1;
1166   if (!isatty(0) || !isatty(1))
1167         settings.quiet = settings.notty = 1;
1168 #ifndef NOREADLINE
1169   else
1170         settings.useReadline = 1;
1171 #endif
1172
1173   while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lh:Hnso:p:qStT:x")) != EOF) {
1174     switch (c) {
1175     case 'A':
1176         settings.opt.align = 0;
1177         break;
1178     case 'a':
1179         fe_setauthsvc(optarg, errbuf);
1180         break;
1181     case 'c':
1182         singleQuery = optarg;
1183         if ( singleQuery[0] == '\\' ) {
1184             singleSlashCmd=1;
1185         }
1186         break;
1187     case 'd':
1188       dbname = optarg;
1189       break;
1190     case 'e':
1191       settings.echoQuery = 1;
1192       break;
1193     case 'f':
1194       qfilename = optarg;
1195       break;
1196     case 'F':
1197       settings.opt.fieldSep=optarg;
1198       break;
1199     case 'l':
1200       listDatabases = 1;
1201       break;
1202     case 'h':
1203       host = optarg;
1204       break;
1205     case 'H':
1206       settings.opt.html3 = 1;
1207       break;
1208     case 'n':
1209       settings.useReadline = 0;
1210       break;
1211     case 'o':
1212       setFout(&settings, optarg);
1213       break;
1214     case 'p':
1215       port = optarg;
1216       break;
1217     case 'q':
1218       settings.quiet = 1;
1219       break;
1220     case 's':
1221       settings.singleStep = 1;
1222       break;
1223     case 'S':
1224       settings.singleLineMode = 1;
1225       break;
1226     case 't':
1227       settings.opt.header = 0;
1228       break;
1229     case 'T':
1230       settings.opt.tableOpt = optarg;
1231       break;
1232     case 'x':
1233       settings.opt.expanded = 1;
1234       break;
1235     default:
1236       usage(argv[0]);
1237       break;
1238     }
1239   }
1240   /* if we still have an argument, use it as the database name */
1241   if (argc - optind == 1)
1242     dbname = argv[optind];
1243
1244   if (listDatabases)
1245     dbname = "template1";
1246   
1247   settings.db = PQsetdb(host, port, NULL, NULL, dbname);
1248   dbname = PQdb(settings.db);
1249
1250   if (PQstatus(settings.db) == CONNECTION_BAD) {
1251     fprintf(stderr,"Connection to database '%s' failed.\n", dbname);
1252     fprintf(stderr,"%s",PQerrorMessage(settings.db));
1253     exit(1);
1254   }
1255   if (listDatabases) {
1256       exit(listAllDbs(&settings));
1257     }
1258
1259   if (!settings.quiet && !singleQuery && !qfilename) {
1260     printf("Welcome to the POSTGRES95 interactive sql monitor:\n");
1261     printf("  Please read the file COPYRIGHT for copyright terms of POSTGRES95\n\n");
1262     printf("   type \\? for help on slash commands\n");
1263     printf("   type \\q to quit\n");
1264     printf("   type \\g or terminate with semicolon to execute query\n");
1265     printf(" You are currently connected to the database: %s\n\n", dbname);
1266      }
1267
1268   if (qfilename || singleSlashCmd) {
1269       /* read in a file full of queries instead of reading in queries
1270          interactively */
1271       char *line;
1272
1273       if ( singleSlashCmd ) {
1274         /* Not really a query, but "Do what I mean, not what I say." */
1275         line = singleQuery;
1276       }
1277       else {
1278         line = malloc(strlen(qfilename) + 5);
1279         sprintf(line,"\\i %s", qfilename);
1280       }
1281       HandleSlashCmds(&settings, line, "");
1282       
1283    } else {
1284        if (singleQuery) {
1285            exitStatus = SendQuery(&settings, singleQuery);
1286        }
1287        else 
1288            exitStatus = MainLoop(&settings, stdin);
1289    }
1290
1291   PQfinish(settings.db);
1292
1293   return exitStatus;
1294 }
1295
1296 #define COPYBUFSIZ      8192
1297
1298 static void
1299 handleCopyOut(PGresult *res, bool quiet)
1300 {
1301     bool copydone = false;
1302     char copybuf[COPYBUFSIZ];
1303     int ret;
1304
1305     if (!quiet)
1306         fprintf(stdout, "Copy command returns...\n");
1307     
1308     while (!copydone) {
1309         ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
1310         
1311         if (copybuf[0] == '\\' &&
1312             copybuf[1] == '.' &&
1313             copybuf[2] =='\0') {
1314             copydone = true;    /* don't print this... */
1315         } else {
1316             fputs(copybuf, stdout);
1317             switch (ret) {
1318             case EOF:
1319                 copydone = true;
1320                 /*FALLTHROUGH*/
1321             case 0:
1322                 fputc('\n', stdout);
1323                 break;
1324             case 1:
1325                 break;
1326             }
1327         }
1328     }
1329     fflush(stdout);
1330     PQendcopy(res->conn);
1331 }
1332
1333
1334 static void
1335 handleCopyIn(PGresult *res, bool quiet)
1336 {
1337     bool copydone = false;
1338     bool firstload;
1339     bool linedone;
1340     char copybuf[COPYBUFSIZ];
1341     char *s;
1342     int buflen;
1343     int c;
1344     
1345     if (!quiet) {
1346         fputs("Enter info followed by a newline\n", stdout);
1347         fputs("End with a backslash and a period on a line by itself.\n", stdout);
1348     }
1349     
1350     /*
1351      * eat extra newline still in input buffer
1352      *
1353      */
1354     fflush(stdin);
1355     if ((c = getc(stdin)) != '\n' && c != EOF) {
1356         (void) ungetc(c, stdin);
1357     }
1358     
1359     while (!copydone) {                 /* for each input line ... */
1360         if (!quiet) {
1361             fputs(">> ", stdout);
1362             fflush(stdout);
1363         }
1364         firstload = true;
1365         linedone = false;
1366         while (!linedone) {             /* for each buffer ... */
1367             s = copybuf;
1368             buflen = COPYBUFSIZ;
1369             for (; buflen > 1 &&
1370                  !(linedone = (c = getc(stdin)) == '\n' || c == EOF);
1371                  --buflen) {
1372                 *s++ = c;
1373             }
1374             if (c == EOF) {
1375                 /* reading from stdin, but from a file */
1376                 PQputline(res->conn, "\\.");
1377                 copydone = true;
1378                 break;
1379             }
1380             *s = '\0';
1381             PQputline(res->conn, copybuf);
1382             if (firstload) {
1383                 if (!strcmp(copybuf, "\\.")) {
1384                     copydone = true;
1385                 }
1386                 firstload = false;
1387             }
1388         }
1389         PQputline(res->conn, "\n");
1390     }
1391     PQendcopy(res->conn);
1392 }
1393
1394 /* try to open fname and return a FILE *,
1395    if it fails, use stdout, instead */
1396
1397 FILE *
1398 setFout(PsqlSettings *ps, char *fname)
1399 {
1400         if (ps->queryFout && ps->queryFout != stdout)
1401         {
1402                 if (ps->pipe)
1403                         pclose(ps->queryFout);
1404                 else
1405                         fclose(ps->queryFout);
1406         }
1407         if (!fname)
1408                 ps->queryFout = stdout;
1409         else
1410         {
1411                 if (*fname == '|')
1412                 {
1413                         signal(SIGPIPE, SIG_IGN);
1414                         ps->queryFout = popen(fname+1, "w");
1415                         ps->pipe = 1;
1416                 }
1417                 else
1418                 {
1419                         ps->queryFout = fopen(fname, "w");
1420                         ps->pipe = 0;
1421                 }
1422                 if (!ps->queryFout) {
1423                     perror(fname);
1424                     ps->queryFout = stdout;
1425                 }
1426         }
1427     return ps->queryFout;
1428 }