]> granicus.if.org Git - postgresql/commitdiff
libpq and psql.c have been modified to do various things they didn't do
authorMarc G. Fournier <scrappy@hub.org>
Thu, 18 Jul 1996 05:48:57 +0000 (05:48 +0000)
committerMarc G. Fournier <scrappy@hub.org>
Thu, 18 Jul 1996 05:48:57 +0000 (05:48 +0000)
before (plus some optimisations/bug fixes et al).  I've included a small
demo transcript below. Note that all of of the display
functionality/intelligence you see here, can be had merely by calling
the new LIBPQ PQprint() routine with the appropriate arguments/options,
including the HTML3 output guff.

submitted by:  Julian Assange <proff@suburbia.net>

src/bin/monitor/monitor.c
src/bin/psql/psql.c
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/libpq-fe.h

index f9bfa92237b61be70b5e5322377c67a9c6fcd5af..ecaf205175d45854275ffd7201420c0700a61975 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/bin/monitor/Attic/monitor.c,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $
+ *    $Header: /cvsroot/pgsql/src/bin/monitor/Attic/monitor.c,v 1.2 1996/07/18 05:48:34 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -642,6 +642,7 @@ handle_execution(char *query)
 {
     PGresult *result;
     int retval = 0;
+    PQprintOpt opt;
     
     result = PQexec(conn, query);
 
@@ -657,11 +658,18 @@ handle_execution(char *query)
        break;
     case PGRES_TUPLES_OK:
 /*     PQprintTuples(result,stdout,PrintAttNames,TerseOutput,COLWIDTH); */
-       if (TerseOutput)
+/*     if (TerseOutput)
            PQdisplayTuples(result,stdout,1,"",PrintAttNames,TerseOutput);
        else
-           PQdisplayTuples(result,stdout,1,"|",PrintAttNames,TerseOutput);
-       break;
+           PQdisplayTuples(result,stdout,1,"|",PrintAttNames,TerseOutput); */
+        memset(&opt, 0, sizeof opt);
+        opt.header = opt.align = opt.standard = 1;
+       if (TerseOutput)
+           opt.fieldSep = "";
+       else
+           opt.fieldSep = "|";
+        PQprint(stdout, result, &opt);
+        break;
     case PGRES_COPY_OUT:
        handle_copy_out(result);
        break;
index 738827a8cec797b4228581692bf38db9bf2e8cfe..3eadc49741555799239868d7c8cc23a6d713d193 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.3 1996/07/16 06:58:12 scrappy Exp $
+ *    $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.4 1996/07/18 05:48:40 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,8 +16,8 @@
 #include <signal.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <unistd.h>
 #include <fcntl.h>
-
 #include "libpq-fe.h"
 #include "stringutils.h"
 
@@ -32,52 +32,57 @@ extern char *readline(char *);      /* in rlstubs.c */
 #include "history.h"
 #else
 #include <readline/readline.h>
-#include <history.h>
+#include <readline/history.h>
 #endif
 #endif
 
 #define MAX_QUERY_BUFFER 20000
-#define MAX_FIELD_SEP_LENGTH 40
 
 #define COPYBUFSIZ     8192
 
-#define DEFAULT_FIELD_SEP " "
+#define DEFAULT_FIELD_SEP "|"
 #define DEFAULT_EDITOR  "vi"
 #define DEFAULT_SHELL  "/bin/sh"
 
 typedef struct _psqlSettings {
-    int echoQuery;        /* if 1, echo the query before sending it */
-    int quiet;            /* run quietly, no messages, no promt */
-    int singleStep;       /* if 1, prompt for each query */ 
-    int singleLineMode;   /* if 1, query terminated by newline */
-    int useReadline;      /* use the readline routines or not */
-    int printHeader;      /* print output field headers or not */
-    int fillAlign;        /* fill align the fields */
-    FILE *queryFout;      /* where to send the query results */
-    char fieldSep[MAX_FIELD_SEP_LENGTH];  /* field separator */
+    PGconn *db;                   /* connection to backend */
+    FILE *queryFout;       /* where to send the query results */
+    PQprintOpt opt;        /* options to be passed to PQprint */
+    char *prompt;         /* prompt to display */
+    char *gfname;         /* one-shot file output argument for \g */
+    bool notty;                   /* input or output is not a tty */
+    bool pipe;            /* queryFout is from a popen() */
+    bool echoQuery;        /* echo the query before sending it */
+    bool quiet;            /* run quietly, no messages, no promt */
+    bool singleStep;       /* prompt before for each query */ 
+    bool singleLineMode;   /* query terminated by newline */
+    bool useReadline;      /* use libreadline routines */
 } PsqlSettings;
 
 /* declarations for functions in this file */
 static void usage(char* progname);
 static void slashUsage();
-static void handleCopyOut(PGresult *res, int quiet);
-static void handleCopyIn(PGresult *res, int quiet);
-static int tableList(PGconn* conn, int deep_tablelist);
-static int tableDesc(PGconn* conn, char* table);
+static void handleCopyOut(PGresult *res, bool quiet);
+static void handleCopyIn(PGresult *res, bool quiet);
+static int tableList(PsqlSettings *ps, bool deep_tablelist);
+static int tableDesc(PsqlSettings *ps, char *table);
 
 char* gets_noreadline(char* prompt, FILE* source);
 char* gets_readline(char* prompt, FILE* source);
 char* gets_fromFile(char* prompt, FILE* source);
-int listAllDbs(PGconn *db, PsqlSettings *settings);
-int SendQuery(PGconn* db, char* query, PsqlSettings *settings);
-int HandleSlashCmds(PGconn** db_ptr,  
+int listAllDbs(PsqlSettings *settings);
+int SendQuery(PsqlSettings *settings, char* query);
+int HandleSlashCmds(PsqlSettings *settings,
                    char *line,
-                   char** prompt_ptr,
-                   char *query,
-                   PsqlSettings *settings);
-int MainLoop(PGconn** db_ptr, FILE *source, PsqlSettings *settings);
-FILE* setFout(char *fname);
+                   char *query);
+int MainLoop(PsqlSettings *settings, FILE *source);
+/* probably should move this into libpq */
+void PQprint(FILE *fp,
+                     PGresult *res, 
+                    PQprintOpt *po
+                    );
 
+FILE* setFout(PsqlSettings *ps, char *fname);
 
 /*
  * usage 
@@ -89,22 +94,24 @@ usage(char* progname)
 {
   fprintf(stderr,"Usage: %s [options] [dbname]\n",progname);
   fprintf(stderr,"\t -a authsvc              set authentication service\n"); 
-  fprintf(stderr,"\t -A                      turn off fill-justification when printing out attributes\n");
+  fprintf(stderr,"\t -A                      turn off alignment when printing out attributes\n");
   fprintf(stderr,"\t -c query                run single query (slash commands too)\n");
   fprintf(stderr,"\t -d dbName               specify database name\n");
   fprintf(stderr,"\t -e                      echo the query sent to the backend\n");
   fprintf(stderr,"\t -f filename             use file as a source of queries\n");
   fprintf(stderr,"\t -F sep                  set the field separator (default is " ")\n");
-  fprintf(stderr,"\t -h                      help information\n");
-  fprintf(stderr,"\t -H host                 set database server host\n");
+  fprintf(stderr,"\t -h host                 set database server host\n");
+  fprintf(stderr,"\t -H                      turn on html3.0 table output\n");
   fprintf(stderr,"\t -l                      list available databases\n");
   fprintf(stderr,"\t -n                      don't use readline library\n");
-  fprintf(stderr,"\t -o filename             send output to filename\n");
+  fprintf(stderr,"\t -o filename             send output to filename or (|pipe)\n");
   fprintf(stderr,"\t -p port                 set port number\n");
   fprintf(stderr,"\t -q                      run quietly (no messages, no prompts)\n");
   fprintf(stderr,"\t -s                      single step mode (prompts for each query)\n");
   fprintf(stderr,"\t -S                      single line mode (i.e. query terminated by newline)\n");
-  fprintf(stderr,"\t -T                      turn off printing of attribute names\n");
+  fprintf(stderr,"\t -t                      turn off printing of attribute headers\n");
+  fprintf(stderr,"\t -T html                 set html3.0 table command options (cf. -H)\n");
+  fprintf(stderr,"\t -x                      turn on expanded output (field names on left)");
   exit(1);
 }
 
@@ -113,29 +120,60 @@ usage(char* progname)
  *    print out usage for the backslash commands 
  */
 
+char *on(bool f)
+{
+       return f? "on": "off";
+}
+
 static void
-slashUsage()
+slashUsage(PsqlSettings *ps)
 {
-  fprintf(stderr,"\t \\a           -- toggle fill-justification of display of attributes\n");
+  fprintf(stderr,"\t \\a           -- toggle field-alignment (currenty %s)\n", on(ps->opt.align));
+  fprintf(stderr,"\t \\c [<captn>] -- set html3 caption (currently '%s')\n", ps->opt.caption? ps->opt.caption: "");
+  fprintf(stderr,"\t \\C <dbname>  -- connect to new database (currently '%s')\n", PQdb(ps->db));
   fprintf(stderr,"\t \\d [<table>] -- list tables in database or columns in <table>\n");
   fprintf(stderr,"\t \\d *         -- list tables in database and columns in all tables\n");
   fprintf(stderr,"\t \\e [<fname>] -- edit the current query buffer or <fname>\n");
-  fprintf(stderr,"\t \\f <sep>     -- change field separator\n");
-  fprintf(stderr,"\t \\g           -- query to backend\n");
-  fprintf(stderr,"\t \\h <command> -- help on syntax of sql commands\n");
+  fprintf(stderr,"\t \\f [<sep>]   -- change field separater (currently '%s')\n", ps->opt.fieldSep);
+  fprintf(stderr,"\t \\g [<fname>] -- send query to backend [and place results in <fname>]\n");
+  fprintf(stderr,"\t \\g |<cmd>    -- send query to backend and pipe results into <cmd>\n");
+  fprintf(stderr,"\t \\h [<cmd>]   -- help on syntax of sql commands\n");
   fprintf(stderr,"\t \\h *         -- complete description of all sql commands\n");
-  fprintf(stderr,"\t \\g           -- send query to backend\n");
-  fprintf(stderr,"\t \\i <fname>   -- read queries from filename\n");
+  fprintf(stderr,"\t \\H           -- toggle html3 output (currently %s)\n", on(ps->opt.html3));
+  fprintf(stderr,"\t \\i <fname>   -- read and execute queries from filename\n");
   fprintf(stderr,"\t \\l           -- list all databases\n");
-  fprintf(stderr,"\t \\o [<fname>] -- send query results file named <fname> or stdout\n");
+  fprintf(stderr,"\t \\o [<fname>] -- send query results to <fname> or stdout\n");
+  fprintf(stderr,"\t \\o |<cmd>    -- pipe query results through <cmd>\n");
   fprintf(stderr,"\t \\p           -- print the current query buffer\n");
   fprintf(stderr,"\t \\q           -- quit\n");
-  fprintf(stderr,"\t \\s [<fname>] -- save or print history\n");
-  fprintf(stderr,"\t \\t           -- toggle output field headers (defaults to on)\n");
-  fprintf(stderr,"\t \\! [<cmd>]   -- shell escape\n");
+  fprintf(stderr,"\t \\r [<fname>] -- edit <fname> then send contents to backend\n");
+  fprintf(stderr,"\t \\s [<fname>] -- print history or save it in <fname>\n");
+  fprintf(stderr,"\t \\S           -- toggle standard sql type-setting (currently %s)\n", on(ps->opt.standard));
+  fprintf(stderr,"\t \\t           -- toggle table output header (currently %s)\n", on(ps->opt.header));
+  fprintf(stderr,"\t \\T [<html>]  -- set html3.0 <table ...> options (currently '%s')\n", ps->opt.tableOpt? ps->opt.tableOpt: "");
+  fprintf(stderr,"\t \\x           -- toggle expanded output (currently %s)\n", on(ps->opt.expanded));
+  fprintf(stderr,"\t \\z           -- zorch current query buffer (i.e clear it)\n");
+  fprintf(stderr,"\t \\! [<cmd>]   -- shell escape or command\n");
   fprintf(stderr,"\t \\?           -- help\n");
 }
 
+PGresult *
+PSQLexec(PsqlSettings *ps, char *query)
+{
+       PGresult *res = PQexec(ps->db, query);
+       if (!res)
+               fputs(PQerrorMessage(ps->db), stderr);
+       else
+       {
+               if (PQresultStatus(res)==PGRES_COMMAND_OK ||
+                   PQresultStatus(res)==PGRES_TUPLES_OK)
+                       return res;
+               if (!ps->quiet)
+                       fputs(PQerrorMessage(ps->db), stderr);
+               PQclear(res);
+       }
+       return NULL;
+}
 /*
  * listAllDbs
  *
@@ -144,50 +182,37 @@ slashUsage()
  *    
  *
  */
-int 
-listAllDbs(PGconn *db, PsqlSettings *settings)
+
+int
+listAllDbs(PsqlSettings *ps)
 {
   PGresult *results;
   char* query = "select * from pg_database;";
 
-  results = PQexec(db, query);
-  if (results == NULL) {
-    fprintf(stderr,"%s", PQerrorMessage(db));
+  if (!(results=PSQLexec(ps, query)))
     return 1;
-  }
-
-  if (PQresultStatus(results) != PGRES_TUPLES_OK)
-    {
-      fprintf(stderr,"Unexpected error from executing: %s\n", query);
-      return 2;
-    }
   else
     {
-      PQdisplayTuples(results,
-                     settings->queryFout, 
-                     settings->fillAlign,
-                     settings->fieldSep,
-                     settings->printHeader,
-                     settings->quiet);
+      PQprint(ps->queryFout,
+                      results,
+                     &ps->opt);
       PQclear(results);
       return 0;
     }
 }
 
 /*
- * tableList (PGconn* conn)
  *
  * List The Database Tables
  *     returns 0 if all went well
  *    
  */
 int
-tableList (PGconn* conn, int deep_tablelist)
+tableList (PsqlSettings *ps, bool deep_tablelist)
 {
   char listbuf[256];
   int nColumns; 
   int i;
-  char* ru;
   char* rk;
   char* rr;
 
@@ -203,17 +228,8 @@ tableList (PGconn* conn, int deep_tablelist)
    add in the int4oideq function */
   strcat(listbuf,"  and usesysid = relowner");
   strcat(listbuf,"  ORDER BY relname ");
-  res = PQexec(conn,listbuf);
-  if (res == NULL) {
-      fprintf(stderr,"%s", PQerrorMessage(conn));
-      return (-1);
-  }
-
-  if ((PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res) <= 0)) {
-      fprintf(stderr,"No tables found in database %s.\n", PQdb(conn));
-      PQclear(res);
-      return (-1);
-  }
+  if (!(res=PSQLexec(ps, listbuf)))
+       return -1;
 
   /* first, print out the attribute names */
   nColumns = PQntuples(res);
@@ -236,14 +252,14 @@ tableList (PGconn* conn, int deep_tablelist)
 
        PQclear(res);
        for (i=0; i < nColumns; i++) {
-          tableDesc(conn,table[i]);
+          tableDesc(ps, table[i]);
        }
        free(table);
       }
       else {
        /* Display the information */
 
-       printf ("\nDatabase    = %s\n", PQdb(conn));
+       printf ("\nDatabase    = %s\n", PQdb(ps->db));
        printf (" +------------------+----------------------------------+----------+\n");
        printf (" |  Owner           |             Relation             |   Type   |\n");
        printf (" +------------------+----------------------------------+----------+\n");
@@ -272,7 +288,7 @@ tableList (PGconn* conn, int deep_tablelist)
 }
 
 /*
- * Describe a table   (PGconn* conn, char* table)
+ * Describe a table
  *
  * Describe the columns in a database table.
  *     returns 0 if all went well
@@ -280,7 +296,7 @@ tableList (PGconn* conn, int deep_tablelist)
  *
  */
 int
-tableDesc (PGconn* conn, char* table)
+tableDesc (PsqlSettings *ps, char *table)
 {
   char descbuf[256];
   int nColumns;
@@ -302,16 +318,8 @@ tableDesc (PGconn* conn, char* table)
   strcat(descbuf,"    and a.attrelid = c.oid ");
   strcat(descbuf,"    and a.atttypid = t.oid ");
   strcat(descbuf,"  ORDER BY attnum ");
-  res = PQexec(conn,descbuf);
-  if (res == NULL) {
-    fprintf(stderr,"%s", PQerrorMessage(conn));
-    return (-1);
-   }
- if ((PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res) <= 0)) {
-   fprintf(stderr,"Couldn't find table %s!\n", table);
-   PQclear(res);
-   return (-1);
- }
+  if (!(res = PSQLexec(ps, descbuf)))
+       return -1;
   /* first, print out the attribute names */
   nColumns = PQntuples(res);
   if (nColumns > 0)
@@ -332,15 +340,15 @@ tableDesc (PGconn* conn, char* table)
       rsize = atoi(PQgetvalue(res,i,3));
       if (strcmp(rtype, "text") == 0) {
         printf ("%-32.32s |", rtype);
-        printf (" %-6s |",  "var" );
+        printf ("%6s |",  "var" );
       }
       else if (strcmp(rtype, "bpchar") == 0) {
         printf ("%-32.32s |", "char");
-        printf (" %-6i |", rsize > 0 ? rsize - 4 : 0 );
+        printf ("%6i |", rsize > 0 ? rsize - 4 : 0 );
       }
       else if (strcmp(rtype, "varchar") == 0) {
         printf ("%-32.32s |", rtype);
-        printf (" %-6i |", rsize > 0 ? rsize - 4 : 0 );
+        printf ("%6i |", rsize > 0 ? rsize - 4 : 0 );
       }
       else {
          /* array types start with an underscore */
@@ -355,9 +363,9 @@ tableDesc (PGconn* conn, char* table)
              free(newname);
          }
        if (rsize > 0) 
-           printf (" %-6i |", rsize);
+           printf (" %6i |", rsize);
        else
-           printf (" %-6s |", "var");
+           printf (" %6s |", "var");
       }
       printf("\n");
     }
@@ -396,7 +404,6 @@ gets_readline(char* prompt, FILE* source)
   return (readline(prompt));
 }
 
-
 /*
  * gets_fromFile  prompt source
  *    
@@ -426,14 +433,12 @@ gets_fromFile(char* prompt, FILE* source)
 }
 
 /*
- * SendQuery:
-     SendQuery: send the query string to the backend 
- *
+ *  SendQuery: send the query string to the backend 
  *  return 0 if the query executed successfully
  *  returns 1 otherwise
  */
 int
-SendQuery(PGconn* db, char* query, PsqlSettings *settings)
+SendQuery(PsqlSettings *settings, char *query)
 {
   PGresult* results;
   PGnotify* notify;
@@ -454,20 +459,41 @@ SendQuery(PGconn* db, char* query, PsqlSettings *settings)
        gets_fromFile("",stdin);
   }
 
-  results = PQexec(db, query);
+  results = PQexec(settings->db, query);
   if (results == NULL) {
-    fprintf(stderr,"%s",PQerrorMessage(db));
+    fprintf(stderr,"%s",PQerrorMessage(settings->db));
     return 1;
   }
 
   switch (PQresultStatus(results)) {
   case PGRES_TUPLES_OK:
-      PQdisplayTuples(results,
-                     settings->queryFout,
-                     settings->fillAlign,
-                     settings->fieldSep,
-                     settings->printHeader,
-                     settings->quiet);
+      if (settings->gfname)
+      {
+               PsqlSettings ps=*settings;
+               FILE *fp;
+               ps.queryFout=stdout;
+               fp=setFout(&ps, settings->gfname);
+               if (!fp || fp==stdout)
+               {
+                       status = 1;
+                       break;
+               }
+               PQprint(fp,
+                       results,
+                       &(settings->opt));
+               if (ps.pipe)
+                       pclose(fp);
+               else
+                       fclose(fp);
+               settings->gfname=NULL;
+               break;
+       } else 
+       {
+             PQprint(settings->queryFout,
+                             results,
+                             &(settings->opt));
+             fflush(settings->queryFout);
+       }
       PQclear(results);
       break;
   case PGRES_EMPTY_QUERY:
@@ -475,7 +501,7 @@ SendQuery(PGconn* db, char* query, PsqlSettings *settings)
     break;
   case PGRES_COMMAND_OK:
     if (!settings->quiet)
-      fprintf(stdout,"%s\n",PQcmdStatus(results));
+      fprintf(stdout,"%s\n", PQcmdStatus(results));
     break;
   case PGRES_COPY_OUT:
     handleCopyOut(results, settings->quiet);
@@ -487,13 +513,12 @@ SendQuery(PGconn* db, char* query, PsqlSettings *settings)
   case PGRES_FATAL_ERROR:
   case PGRES_BAD_RESPONSE:
     status = 1;
-    fprintf(stderr,"%s",PQerrorMessage(db));
+    fprintf(stderr,"%s",PQerrorMessage(settings->db));
     break;
-
   } 
 
   /* check for asynchronous returns */
-  notify = PQnotifies(db);
+  notify = PQnotifies(settings->db);
   if (notify) {
       fprintf(stderr,"ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
              notify->relname, notify->be_pid);
@@ -504,6 +529,71 @@ SendQuery(PGconn* db, char* query, PsqlSettings *settings)
 
 }
 
+void
+editFile(char *fname)
+{
+    char *editorName;
+    char *sys;
+    editorName = getenv("EDITOR");
+    if (!editorName)
+       editorName = DEFAULT_EDITOR;
+    sys=malloc(strlen(editorName)+strlen(fname)+32+1);
+    if (!sys)
+    {
+       perror("malloc");
+       exit(1);
+    }
+    sprintf(sys, "exec '%s' '%s'", editorName, fname);
+    system(sys);
+    free(sys);
+}
+
+bool
+toggle(PsqlSettings *settings, bool *sw, char *msg)
+{
+       *sw= !*sw;
+       if (!settings->quiet)
+           fprintf(stderr, "turned %s %s\n", on(*sw), msg);
+       return *sw;
+}
+
+char *
+decode(char *s)
+{
+       char *p, *d;
+       bool esc=0;
+       for (d=p=s; *p; p++)
+       {
+               char c=*p;
+               if (esc)
+               {
+                       switch(*p)
+                       {
+                       case 'n':
+                               c='\n';
+                               break;
+                       case 'r':
+                               c='\r';
+                               break;
+                       case 't':
+                               c='\t';
+                               break;
+                       case 'f':
+                               c='\f';
+                               break;
+                       }
+                       esc=0;
+               } else
+                       if (c=='\\')
+                       {
+                               esc=1;
+                               continue;
+                       }
+               *d++=c;
+       }
+       *d='\0';
+}
+
 /*
   HandleSlashCmds:
 
@@ -519,189 +609,161 @@ SendQuery(PGconn* db, char* query, PsqlSettings *settings)
        2 - terminate processing of this query entirely
 */
 int
-HandleSlashCmds(PGconn** db_ptr, 
+HandleSlashCmds(PsqlSettings *settings,
                char* line, 
-               char** prompt_ptr, 
-               char *query,
-               PsqlSettings *settings)
+               char *query)
 {
-  int status = 0;
-  PGconn* db = *db_ptr;
-  char* dbname = PQdb(db);
+  int status = 1;
   char *optarg = NULL;
   int len;
 
   len = strlen(line);
   if (len > 2)
+  {
       optarg = leftTrim(line+2);
+      decode(optarg);
+  }
   switch (line[1])
     {
-    case 'a': /* toggles to fill fields on output */
-       if (settings->fillAlign)
-           settings->fillAlign = 0;
-       else 
-           settings->fillAlign = 1;
-       if (!settings->quiet)
-           fprintf(stderr,"turning %s fill-justification\n",
-                   (settings->fillAlign) ? "on" : "off" );
+    case 'a': /* toggles to align fields on output */
+        toggle(settings, &settings->opt.align, "field alignment");
        break;
-    case 'c':  /* \c means connect to new database */
+    case 'c': /* define new caption */
+       if (settings->opt.caption)
+               free(settings->opt.caption);
+       if (!optarg)
+               settings->opt.caption=NULL;
+       else
+               if (!(settings->opt.caption=dupstr(optarg)))
+               {
+                       perror("malloc");
+                       exit(1);
+               }
+       break;
+    case 'C':  /* \C means connect to new database */
       {
+         char *dbname=PQdb(settings->db);
          if (!optarg) {
              fprintf(stderr,"\\c must be followed by a database name\n");
-             status = 1;
-             break;
-         }
-         if (strcmp(optarg, dbname) == 0)  {
-             fprintf(stderr,"already connected to %s\n", dbname);
-             status = 1;
              break;
          }
-         else {
-             PGconn *olddb;
+         {
+             PGconn *olddb=settings->db;
              
-             printf("closing connection to database:%s\n", dbname);
-             olddb = db;
-             db = PQsetdb(PQhost(olddb), PQport(olddb), NULL, NULL, optarg);
-             *db_ptr = db;
+             printf("closing connection to database: %s\n", dbname);
+             settings->db = PQsetdb(PQhost(olddb), PQport(olddb), NULL, NULL, optarg);
              printf("connecting to new database: %s\n", optarg);
-             if (PQstatus(db) == CONNECTION_BAD) {
-                 fprintf(stderr,"%s\n", PQerrorMessage(db));
+             if (PQstatus(settings->db) == CONNECTION_BAD) {
+                 fprintf(stderr,"%s\n", PQerrorMessage(settings->db));
                  printf("reconnecting to %s\n", dbname);
-                 db = PQsetdb(PQhost(olddb), PQport(olddb), 
+                 settings->db = PQsetdb(PQhost(olddb), PQport(olddb), 
                               NULL, NULL, dbname);
-                 *db_ptr = db;
-                 if (PQstatus(db) == CONNECTION_BAD) {
+                 if (PQstatus(settings->db) == CONNECTION_BAD) {
                      fprintf(stderr, 
-                             "could not reconnect to %s.  exiting\n", dbname);
+                             "could not reconnect to %s. exiting\n", dbname);
                      exit(2);
                  }
-                 status = 1;
                  break;
              }
              PQfinish(olddb);
-             free(*prompt_ptr);
-             *prompt_ptr = malloc(strlen(optarg) + 10);
-             sprintf(*prompt_ptr,"%s=> ", optarg);
-             status = 1;
+             free(settings->prompt);
+             settings->prompt = malloc(strlen(PQdb(settings->db)) + 10);
+             sprintf(settings->prompt,"%s=> ", PQdb(settings->db));
            break;
          }
       }
       break;
     case 'd':     /* \d describe tables or columns in a table */
-      {
        if (!optarg) {
-          tableList(db,0);
-         status = 1;
+          tableList(settings, 0);
          break;
-       }
-       if ( strcmp(optarg,"*") == 0 ) {
-          tableList(db, 0);
-          tableList(db, 1);
+       } 
+       if (strcmp(optarg, "*") == 0 ) {
+          tableList(settings, 0);
+          tableList(settings, 1);
        }
        else {
-          tableDesc(db,optarg);
+          tableDesc(settings, optarg);
        }
-       status = 1;
        break;
-      }
     case 'e':
       {
-       char s[256];
        int fd;
-       int ql = strlen(query);
-       int f_arg = 0;
+       char tmp[64];
+       char *fname;
        int cc;
+       int ql = strlen(query);
         if (optarg)
-        {
-               f_arg = 1;
-               strcpy(s, optarg);
-        }
+               fname=optarg;
         else
-        {
-               sprintf(s, "/tmp/psql.%d.%d", getuid(), getpid());
-               unlink(s);
+       {
+               sprintf(tmp, "/tmp/psql.%d.%d", getuid(), getpid());
+               fname=tmp;
+               unlink(tmp);
                if (ql)
                {
-                       if ((fd=open(s, O_EXCL|O_CREAT|O_WRONLY, 0600))==-1)
+                       if ((fd=open(tmp, O_EXCL|O_CREAT|O_WRONLY, 0600))==-1)
                        {
-                               perror(s);
+                               perror(tmp);
                                break;
                        }
                        if (query[ql-1]!='\n')
                                strcat(query, "\n");
                        if (write(fd, query, ql)!=ql)
                        {
-                               perror(s);
+                               perror(tmp);
                                close(fd);
-                               unlink(s);
+                               unlink(tmp);
                                break;
                        }
                        close(fd);
                }
        }
+       editFile(fname);
+       if ((fd=open(fname, O_RDONLY))==-1)
        {
-           char sys[256];
-           char *editorName;
-           editorName = getenv("EDITOR");
-           if (editorName == NULL)
-               editorName = DEFAULT_EDITOR;
-           sprintf(sys, "exec %s %s", editorName, s);
-           system(sys);
-        }
-       if ((fd=open(s, O_RDONLY))==-1)
-       {
-               if (!f_arg)
-                       unlink(s);
+               perror(fname);
+               if (!optarg)
+                       unlink(fname);
                break;
        }
        if ((cc=read(fd, query, MAX_QUERY_BUFFER))==-1)
         {
-               perror(s);
+               perror(fname);
                close(fd);
-               if (!f_arg)
-                       unlink(s);
+               if (!optarg)
+                       unlink(fname);
                break;
        }       
        query[cc]='\0';
        close(fd);
-       if (!f_arg)
-               unlink(s);
+       if (!optarg)
+               unlink(fname);
        rightTrim(query);
        if (query[strlen(query)-1]==';')
                return 0;
        break;
       }
-  case 'f':
-      if (optarg)
-         strcpy(settings->fieldSep,optarg);
-      else
-          strcpy(settings->fieldSep,DEFAULT_FIELD_SEP);
-      break;
-  case 'g':  /* \g means send query */
-      status = 0;     
-       break;
-    case 'i':     /* \i is include file */
-      {
-       FILE* fd;
-
-       if (!optarg) {
-         fprintf(stderr,"\\i must be followed by a file name\n");
-         status = 1;
-         break;
+    case 'f':
+    {
+        char *fs=DEFAULT_FIELD_SEP;
+       if (optarg)
+               fs=optarg;
+        if (settings->opt.fieldSep);
+               free(settings->opt.fieldSep);
+       if (!(settings->opt.fieldSep=dupstr(fs)))
+       {
+               perror("malloc");
+               exit(1);
        }
-
-       if ( (fd = fopen(optarg, "r")) == NULL)
-         {
-           fprintf(stderr,"file named %s could not be opened\n",optarg);
-           status = 1;
-           break;
-         }
-       MainLoop(&db, fd, settings);
-       fclose(fd);
-       status = 1;
+       if (!settings->quiet)
+               fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
        break;
-      }
+    }
+  case 'g':  /* \g means send query */
+      settings->gfname = optarg;
+      status = 0;     
+      break;
     case 'h':
       {
        char* cmd;
@@ -727,7 +789,7 @@ HandleSlashCmds(PGconn** db_ptr,
 
          numCmds = numCmds - 1;
 
-         if ( strcmp(cmd,"*") == 0 ) {
+         if (strcmp(cmd, "*") == 0 ) {
             all_help=1;
          }
 
@@ -748,74 +810,143 @@ HandleSlashCmds(PGconn** db_ptr,
          if (i == numCmds && ! all_help)
            printf("command not found,  try \\h with no arguments to see available help\n");
        }
-       status = 1;
        break;
       }
+    case 'i':     /* \i is include file */
+        {
+        FILE* fd;
+        if (!optarg) {
+          fprintf(stderr,"\\i must be followed by a file name\n");
+          break;
+        }
+        if ((fd = fopen(optarg, "r")) == NULL)
+          {
+            fprintf(stderr,"file named %s could not be opened\n",optarg);
+            break;
+          }
+        MainLoop(settings, fd);
+        fclose(fd);
+        break;
+        }
     case 'l':     /* \l is list database */
-      listAllDbs(db,settings);
-      status = 1;
+       listAllDbs(settings);
+       break;
+    case 'H':
+       if (toggle(settings, &settings->opt.html3, "HTML3.0 tablular output"))
+           settings->opt.standard = 0;
       break;
     case 'o':
-      settings->queryFout = setFout(optarg);
+      setFout(settings, optarg);
       break;
     case 'p':
        if (query) {
            fputs(query, stdout);
            fputc('\n', stdout);
-           status = 1;
        }
        break;
-    case 'r':
-        query[0] = '\0';
-        status = 1;
-        break;          
     case 'q': /* \q is quit */
       status = 2;
       break;
-    case 's': /* \s is save history to a file */
-      {
-       char* fname;
-
-       if (!optarg) {
-         fprintf(stderr,"\\s must be followed by a file name\n");
-         status = 1;
-         break;
+    case 'r':
+    {
+      FILE* fd;
+      static char *lastfile;
+      struct stat st, st2;
+      if (optarg) { 
+       if (lastfile) free(lastfile);
+       lastfile=malloc(strlen(optarg+1));
+       if (!lastfile) {
+          perror("malloc");
+          exit(1);
+       }  
+       strcpy(lastfile, optarg);
+      } else 
+       if (!lastfile) {
+          fprintf(stderr, "\\r must be followed by a file name initially\n"); 
+          break;
        }
-
-       fname = optarg;
-       if (write_history(fname) != 0)
-         {
-           fprintf(stderr,"cannot write history to %s\n",fname);
-         }
-       status = 1;
-       break;
+      stat(lastfile, &st); 
+      editFile(lastfile);
+      if ((stat(lastfile, &st2) == -1) || 
+        ((fd = fopen(lastfile, "r")) == NULL)) {
+          perror(lastfile);
+          break;
       }
-    case 't':
-       if ( settings->printHeader )
-            settings->printHeader = 0;
-       else
-            settings->printHeader = 1;
+      if (st2.st_mtime==st.st_mtime) { 
        if (!settings->quiet)
-           fprintf(stderr,"turning %s printing of field headers\n",
-                   (settings->printHeader) ? "on" : "off" );
+          fprintf(stderr, "warning: %s not modified. query not executed\n", 
+                  lastfile);  
+       fclose(fd);
        break;
-    case '!':
-      if (!optarg) {
-         char sys[256];
-         char *shellName;
-         shellName = getenv("SHELL");
-         if (shellName == NULL) 
-             shellName = DEFAULT_SHELL;
-         sprintf(sys,"exec %s", shellName);
-         system(sys);
+      } 
+      MainLoop(settings, fd);
+      fclose(fd);
+      break;
       }
+    case 's': /* \s is save history to a file */
+      if (!optarg) optarg="/dev/tty";
+      if (write_history(optarg) != 0)
+          fprintf(stderr,"cannot write history to %s\n",optarg);
+      break;
+    case 'S': /* standard SQL output */
+      if (toggle(settings, &settings->opt.standard, 
+                "standard SQL separaters and padding")) {
+       settings->opt.html3 = settings->opt.expanded = 0;
+       settings->opt.align = settings->opt.header = 1;
+       free(settings->opt.fieldSep);
+       settings->opt.fieldSep=dupstr("|");
+       if (!settings->quiet)
+          fprintf(stderr, "field separater changed to '%s'\n", 
+                  settings->opt.fieldSep);
+      } else {
+       free(settings->opt.fieldSep);
+       settings->opt.fieldSep=dupstr(DEFAULT_FIELD_SEP);
+       if (!settings->quiet)
+          fprintf(stderr, "field separater changed to '%s'\n", 
+                  settings->opt.fieldSep); 
+      }
+      break;
+    case 't': /* toggle headers */
+      toggle(settings, &settings->opt.header, "output headers");
+      break; 
+    case 'T': /* define html <table ...> option */
+      if (settings->opt.tableOpt) free(settings->opt.tableOpt);
+      if (!optarg) settings->opt.tableOpt=NULL;
       else
-         system(optarg);
+       if (!(settings->opt.tableOpt=dupstr(optarg))) {
+          perror("malloc");
+          exit(1);
+       }
+      break;
+    case 'x':
+      toggle(settings, &settings->opt.expanded, 
+            "expanded table representation");     
+      break;
+    case 'z': /* zorch buffer */
+      query[0]='\0';
+      if (!settings->quiet) fprintf(stderr, "zorched current query buffer\n");
+      break;  
+    case '!':
+      if (!optarg) {
+       char *sys;
+       char *shellName;
+       shellName = getenv("SHELL");
+       if (shellName == NULL) shellName = DEFAULT_SHELL;
+       sys = malloc(strlen(shellName)+16);
+       if (!sys) {
+          perror("malloc");
+          exit(1);
+       }
+       sprintf(sys,"exec %s", shellName);
+       system(sys);
+       free(sys);
+      } else system(optarg);
       break;
     default:
     case '?':     /* \? is help */
-      slashUsage();
-      status = 1;
+      slashUsage(settings);
       break;
     }
   return status;
@@ -830,19 +961,14 @@ HandleSlashCmds(PGconn** db_ptr,
 
  *db_ptr must be initialized and set
 */
+
 int
-MainLoop(PGconn** db_ptr, 
-        FILE* source,
-        PsqlSettings *settings)
+MainLoop(PsqlSettings *settings, FILE* source)
 {
-  char* prompt;                 /* readline prompt */
   char* line;                   /* line of input*/
   int len;                      /* length of the line */
   char query[MAX_QUERY_BUFFER]; /* multi-line query storage */
-  PGconn* db = *db_ptr;
-  char* dbname = PQdb(db);
   int exitStatus = 0;
-
   int slashCmdStatus = 0;
  /* slashCmdStatus can be:
        0 - send currently constructed query to backend (i.e. we got a \g)
@@ -850,18 +976,21 @@ MainLoop(PGconn** db_ptr,
        2 - terminate processing of this query entirely
   */
 
-  int send_query = 0;
-  int interactive;
+  bool sendQuery = 0;
+  bool querySent = 0;
+  bool interactive;
   READ_ROUTINE GetNextLine;
 
-  interactive = (source == stdin);
-
+  interactive = ((source == stdin) && !settings->notty);
+#define PROMPT "=> "
   if (interactive) {
-    prompt = malloc(strlen(dbname) + 10);
+    if (settings->prompt)
+       free(settings->prompt);
+    settings->prompt = malloc(strlen(PQdb(settings->db)) + strlen(PROMPT) + 1);
     if (settings->quiet)
-      prompt[0] = '\0';
+      settings->prompt[0] = '\0';
     else
-      sprintf(prompt,"%s=> ", dbname);
+      sprintf(settings->prompt,"%s%s", PQdb(settings->db), PROMPT);
     if (settings->useReadline) {
        using_history();
        GetNextLine = gets_readline;
@@ -875,7 +1004,7 @@ MainLoop(PGconn** db_ptr,
   query[0] = '\0';
   
   /* main loop for getting queries and executing them */
-  while ((line = GetNextLine(prompt, source)) != NULL)
+  while ((line = GetNextLine(settings->prompt, source)) != NULL)
     {
        exitStatus = 0;
       line = rightTrim(line); /* remove whitespaces on the right, incl. \n's */
@@ -895,6 +1024,11 @@ MainLoop(PGconn** db_ptr,
          free(line);
          continue;
       }
+      if (line[0] != '\\' && querySent)
+      {
+         query[0]='\0';
+          querySent = 0;
+      }
 
       len = strlen(line);
 
@@ -903,34 +1037,31 @@ MainLoop(PGconn** db_ptr,
       
       /* do the query immediately if we are doing single line queries 
        or if the last character is a semicolon */
-      send_query = settings->singleLineMode || (line[len-1] == ';') ;
+      sendQuery = settings->singleLineMode || (line[len-1] == ';') ;
 
       /* normally, \ commands have to be start the line,
         but for backwards compatibility with monitor,
         check for \g at the end of line */
-      if (len > 2 && !send_query) 
+      if (len > 2 && !sendQuery) 
        {
          if (line[len-1]=='g' && line[len-2]=='\\')
            {
-           send_query = 1;
+           sendQuery = 1;
            line[len-2]='\0';
          }
        }
       
       /* slash commands have to be on their own line */
       if (line[0] == '\\') {
-         slashCmdStatus = HandleSlashCmds(db_ptr, 
+         slashCmdStatus = HandleSlashCmds(settings,
                                           line, 
-                                          &prompt, 
-                                          query,
-                                          settings);
-       db = *db_ptr; /* in case \c changed the database */
+                                          query);
        if (slashCmdStatus == 1)
          continue;
        if (slashCmdStatus == 2)
          break;
        if (slashCmdStatus == 0)
-         send_query = 1;
+         sendQuery = 1;
       }
       else
        if (strlen(query) + len > MAX_QUERY_BUFFER)
@@ -946,30 +1077,29 @@ MainLoop(PGconn** db_ptr,
       else
        strcpy(query,line);
       
-      if (send_query && query[0] != '\0')
+      if (sendQuery && query[0] != '\0')
        {
            /* echo the line read from the file,
             unless we are in single_step mode, because single_step mode
             will echo anyway */
-         if (!interactive && !settings->singleStep) 
-           fprintf(stderr,"%s\n",query);
+         if (!interactive && !settings->singleStep && !settings->quiet
+           fprintf(stderr,"%s\n", query);
 
-         exitStatus = SendQuery(db, query, settings);
-         query[0] = '\0';
+         exitStatus = SendQuery(settings, query);
+          querySent = 1;
        }
       
        free(line); /* free storage malloc'd by GetNextLine */
     } /* while */
-  return exitStatus;
+    return exitStatus;
 } 
 
 int
 main(int argc, char** argv)
 {
   extern char* optarg;
-  extern int optind, opterr;
+  extern int optind;
   
-  PGconn *db;
   char* dbname = NULL;
   char* host = NULL;
   char* port = NULL;
@@ -980,31 +1110,27 @@ main(int argc, char** argv)
 
   char* singleQuery = NULL;
 
-  int listDatabases = 0 ;
+  bool listDatabases = 0 ;
   int exitStatus = 0;
-  int singleSlashCmd = 0;
+  bool singleSlashCmd = 0;
   int c;
 
-
-#ifdef NOREADLINE
-  settings.useReadline = 0;
-#else
-  settings.useReadline = 1;
-#endif
-
-  settings.quiet = 0;
-  settings.fillAlign = 1;
-  settings.printHeader = 1;
-  settings.echoQuery = 0;
-  settings.singleStep = 0;
-  settings.singleLineMode = 0;
+  memset(&settings, 0, sizeof settings);
+  settings.opt.align = 1;
+  settings.opt.header = 1;
   settings.queryFout = stdout;
-  strcpy(settings.fieldSep, DEFAULT_FIELD_SEP);
+  settings.opt.fieldSep=dupstr(DEFAULT_FIELD_SEP);
+  if (!isatty(0) || !isatty(1))
+       settings.quiet = settings.notty = 1;
+  else
+#ifndef NOREADLINE
+       settings.useReadline = 1;
+#endif
 
-  while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lhH:nso:p:qST")) != EOF) {
+  while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lhH:nso:p:qStTx")) != EOF) {
     switch (c) {
     case 'A':
-       settings.fillAlign = 0;
+       settings.opt.align = 0;
        break;
     case 'a':
        fe_setauthsvc(optarg, errbuf);
@@ -1025,20 +1151,23 @@ main(int argc, char** argv)
       qfilename = optarg;
       break;
     case 'F':
-       strncpy(settings.fieldSep,optarg,MAX_FIELD_SEP_LENGTH); 
-       break;
+      settings.opt.fieldSep=dupstr(optarg);
+      break;
     case 'l':
       listDatabases = 1;
       break;
-    case 'H':
+    case 'h':
       host = optarg;
       break;
+    case 'H':
+      settings.opt.html3 = 1;
+      break;
     case 'n':
-       settings.useReadline = 0;
-       break;
+      settings.useReadline = 0;
+      break;
     case 'o':
-       settings.queryFout = setFout(optarg);
-       break;
+      setFout(&settings, optarg);
+      break;
     case 'p':
       port = optarg;
       break;
@@ -1051,10 +1180,15 @@ main(int argc, char** argv)
     case 'S':
       settings.singleLineMode = 1;
       break;
+    case 't':
+      settings.opt.header = 0;
+      break;
     case 'T':
-       settings.printHeader = 0;
-       break;
-    case 'h':
+      settings.opt.tableOpt = dupstr(optarg);
+      break;
+    case 'x':
+      settings.opt.expanded = 0;
+      break;
     default:
       usage(argv[0]);
       break;
@@ -1067,16 +1201,16 @@ main(int argc, char** argv)
   if (listDatabases)
     dbname = "template1";
   
-  db = PQsetdb(host, port, NULL, NULL, dbname);
-  dbname = PQdb(db);
+  settings.db = PQsetdb(host, port, NULL, NULL, dbname);
+  dbname = PQdb(settings.db);
 
-  if (PQstatus(db) == CONNECTION_BAD) {
+  if (PQstatus(settings.db) == CONNECTION_BAD) {
     fprintf(stderr,"Connection to database '%s' failed.\n", dbname);
-    fprintf(stderr,"%s",PQerrorMessage(db));
+    fprintf(stderr,"%s",PQerrorMessage(settings.db));
     exit(1);
   }
   if (listDatabases) {
-      exit(listAllDbs(db,&settings));
+      exit(listAllDbs(&settings));
     }
 
   if (!settings.quiet && !singleQuery && !qfilename) {
@@ -1092,7 +1226,6 @@ main(int argc, char** argv)
       /* read in a file full of queries instead of reading in queries
         interactively */
       char *line;
-      char prompt[100];
 
       if ( singleSlashCmd ) {
        /* Not really a query, but "Do what I mean, not what I say." */
@@ -1102,24 +1235,25 @@ main(int argc, char** argv)
        line = malloc(strlen(qfilename) + 5);
        sprintf(line,"\\i %s", qfilename);
       }
-      HandleSlashCmds(&db, line, (char**)prompt, "", &settings);
+      HandleSlashCmds(&settings, line, "");
       
    } else {
        if (singleQuery) {
-          exitStatus = SendQuery(db, singleQuery, &settings);
+          exitStatus = SendQuery(&settings, singleQuery);
        }
        else 
-          exitStatus = MainLoop(&db, stdin, &settings);
+          exitStatus = MainLoop(&settings, stdin);
    }
 
-  PQfinish(db);
+  PQfinish(settings.db);
 
   return exitStatus;
 }
 
+#define COPYBUFSIZ     8192
 
 static void
-handleCopyOut(PGresult *res, int quiet)
+handleCopyOut(PGresult *res, bool quiet)
 {
     bool copydone = false;
     char copybuf[COPYBUFSIZ];
@@ -1153,7 +1287,7 @@ handleCopyOut(PGresult *res, int quiet)
 
 
 static void
-handleCopyIn(PGresult *res, int quiet)
+handleCopyIn(PGresult *res, bool quiet)
 {
     bool copydone = false;
     bool firstload;
@@ -1212,24 +1346,38 @@ handleCopyIn(PGresult *res, int quiet)
     PQendcopy(res->conn);
 }
 
-
 /* try to open fname and return a FILE*,
    if it fails, use stdout, instead */
+
 FILE* 
-setFout(char *fname)
+setFout(PsqlSettings *ps, char *fname)
 {
-    FILE *queryFout;
-
-    if (!fname)
-       queryFout = stdout;
-    else {
-       queryFout = fopen(fname, "w");
-       if (!queryFout) {
-           perror(fname);
-           queryFout = stdout;
+        if (ps->queryFout && ps->queryFout != stdout)
+       {
+               if (ps->pipe)
+                       pclose(ps->queryFout);
+               else
+                       fclose(ps->queryFout);
        }
-    }
-
-    return queryFout;
+       if (!fname)
+               ps->queryFout = stdout;
+       else
+       {
+               if (*fname == '|')
+               {
+                       signal(SIGPIPE, SIG_IGN);
+                       ps->queryFout = popen(fname+1, "w");
+                       ps->pipe = 1;
+               }
+               else
+               {
+                       ps->queryFout = fopen(fname, "w");
+                       ps->pipe = 0;
+               }
+               if (!ps->queryFout) {
+                   perror(fname);
+                   ps->queryFout = stdout;
+               }
+       }
+    return ps->queryFout;
 }
index 1ca135151d00f17b3b8f8ab51ad7bd223833b715..99410cfcbc5cae67c2ae0c95360aa8e419320d82 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.2 1996/07/12 04:53:59 scrappy Exp $
+ *    $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.3 1996/07/18 05:48:56 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,6 @@
 #include "postgres.h"
 #include "libpq/pqcomm.h"
 #include "libpq-fe.h"
-#include <signal.h>
 
 /* the tuples array in a PGresGroup  has to grow to accommodate the tuples */
 /* returned.  Each time, we grow by this much: */
@@ -39,7 +38,6 @@ static PGresult* makePGresult(PGconn *conn, char *pname);
 static void addTuple(PGresult *res, PGresAttValue *tup);
 static PGresAttValue* getTuple(PGconn *conn, PGresult *res, int binary);
 static PGresult* makeEmptyPGresult(PGconn *conn, ExecStatusType status);
-static void fill(int length, int max, char filler, FILE *fp);
 
 /*
  * PQclear -
@@ -624,133 +622,13 @@ PQendcopy(PGconn *conn)
     }
 }
 
-/* simply send out max-length number of filler characters to fp */
-static void
-fill (int length, int max, char filler, FILE *fp)
-{
-  int count;
-  char filltmp[2];
-
-  filltmp[0] = filler;
-  filltmp[1] = 0;
-  count = max - length;
-  while (count-- >= 0)
-    {
-      fprintf(fp, "%s", filltmp);
-    }
- }
-
-
-/*
- * PQdisplayTuples()
- *
- * a better version of PQprintTuples()
- * that can optionally do padding of fields with spaces and use different
- * field separators 
- */
-void
-PQdisplayTuples(PGresult *res,
-               FILE *fp,      /* where to send the output */
-               int fillAlign, /* pad the fields with spaces */
-               char *fieldSep,  /* field separator */
-               int printHeader, /* display headers? */
-               int quiet
-               )
-{
-#define DEFAULT_FIELD_SEP " "
-
-    char *pager;
-    int i, j;
-    int nFields;
-    int nTuples;
-    int fLength[MAX_FIELDS];
-    int usePipe = 0;
-
-    if (fieldSep == NULL)
-       fieldSep == DEFAULT_FIELD_SEP;
-
-    if (fp == NULL) 
-       fp = stdout;
-    if (fp == stdout) {
-       /* try to pipe to the pager program if possible */
-       pager=getenv("PAGER");
-       if (pager != NULL) {
-           fp = popen(pager, "w");
-           if (fp) {
-               usePipe = 1;
-               signal(SIGPIPE, SIG_IGN);
-           } else
-               fp = stdout;
-       }
-    }
-
-    /* Get some useful info about the results */
-    nFields = PQnfields(res);
-    nTuples = PQntuples(res);
-  
-    /* Zero the initial field lengths */
-    for (j=0  ; j < nFields; j++) {
-      fLength[j] = strlen(PQfname(res,j));
-    }
-    /* Find the max length of each field in the result */
-    /* will be somewhat time consuming for very large results */
-    if (fillAlign) {
-       for (i=0; i < nTuples; i++) {
-           for (j=0  ; j < nFields; j++) {
-               if (PQgetlength(res,i,j) > fLength[j])
-                   fLength[j] = PQgetlength(res,i,j);
-           }
-       }
-    }
-
-    if (printHeader) {
-       /* first, print out the attribute names */
-       for (i=0; i < nFields; i++) {
-           fputs(PQfname(res,i), fp);
-           if (fillAlign)
-               fill (strlen (PQfname(res,i)), fLength[i], ' ', fp);
-           fputs(fieldSep,fp);
-       }
-       fprintf(fp, "\n");
-  
-       /* Underline the attribute names */
-       for (i=0; i < nFields; i++) {
-           if (fillAlign)
-               fill (0, fLength[i], '-', fp);
-           fputs(fieldSep,fp);
-       }
-       fprintf(fp, "\n");
-    }
-
-    /* next, print out the instances */
-    for (i=0; i < nTuples; i++) {
-      for (j=0  ; j < nFields; j++) {
-        fprintf(fp, "%s", PQgetvalue(res,i,j));
-       if (fillAlign)
-           fill (strlen (PQgetvalue(res,i,j)), fLength[j], ' ', fp);
-       fputs(fieldSep,fp);
-      }
-      fprintf(fp, "\n");
-    }
-  
-    if (!quiet)
-       fprintf (fp, "\nQuery returned %d row%s.\n",PQntuples(res),
-                (PQntuples(res) == 1) ? "" : "s");
-  
-    fflush(fp);
-    if (usePipe) {
-       pclose(fp);
-       signal(SIGPIPE, SIG_DFL);
-    }
-}
-
 
 
 /*
- * PQprintTuples()
+ * print_tuples()
  *
  * This is the routine that prints out the tuples that
- *  are returned from the backend.
+ * are returned from the backend.
  * Right now all columns are of fixed length,
  * this should be changed to allow wrap around for
  * tuples values that are wider.
@@ -821,6 +699,295 @@ PQprintTuples(PGresult *res,
     }
 }
 
+/*
+ * new PQprintTuples routine (proff@suburbia.net)
+ * PQprintOpt is a typedef (structure) that containes
+ * various flags and options. consult libpq-fe.h for
+ * details
+ */
+
+void
+PQprint(FILE *fout,
+              PGresult *res,
+             PQprintOpt *po
+              )
+{
+    int nFields;
+
+    nFields = PQnfields(res);
+
+    if ( nFields > 0 ) {  /* only print tuples with at least 1 field.  */
+       int i,j;
+       int nTups;
+       int *fieldMax=NULL; /* keep -Wall happy */
+       unsigned char *fieldNotNum=NULL; /* keep -Wall happy */
+       char **fields=NULL; /*keep -Wall happy */
+       char **fieldNames;
+       int fieldMaxLen=0;
+       char *border=NULL;
+        int numFieldName;
+       int fs_len=strlen(po->fieldSep);
+       nTups = PQntuples(res);
+       if (!(fieldNames=(char **)calloc(nFields, sizeof (char *))))
+       {
+               perror("calloc");
+               exit(1);
+       }
+       if (!(fieldNotNum=(unsigned char *)calloc(nFields, 1)))
+       {
+               perror("calloc");
+               exit(1);
+       }
+       if (!(fieldMax=(int *)calloc(nFields, sizeof(int))))
+       {
+               perror("calloc");
+               exit(1);
+       }
+       for (numFieldName=0; po->fieldName && po->fieldName[numFieldName]; numFieldName++);
+       for (j=0; j < nFields; j++)
+       {
+               int len;
+               char *s=(j<numFieldName && po->fieldName[j][0])? po->fieldName[j]: PQfname(res, j);
+               fieldNames[j]=s;
+               len=s? strlen(s): 0;
+               fieldMax[j] = len;
+               /*
+               if (po->header && len<5)
+                       len=5; 
+               */
+               len+=fs_len;
+               if (len>fieldMaxLen)
+                       fieldMaxLen=len;
+       }
+       if (!po->expanded && (po->align || po->html3))
+       {
+               if (!(fields=(char **)calloc(nFields*(nTups+1), sizeof(char *))))
+               {
+                       perror("calloc");
+                       exit(1);
+               }
+       } else
+               if (po->header && !po->html3)
+               {
+                       if (po->expanded)
+                       {
+                               if (po->align)
+                                       fprintf(fout, "%-*s%s Value\n", fieldMaxLen-fs_len, "Field", po->fieldSep);
+                               else
+                                       fprintf(fout, "%s%sValue\n", "Field", po->fieldSep);
+                       } else
+                       {
+                               int len=0;
+                               for (j=0; j < nFields; j++)
+                               {
+                                       char *s=fieldNames[j];
+                                       fputs(s, fout);
+                                       len+=strlen(s)+fs_len;
+                                       if ((j+1)<nFields)
+                                               fputs(po->fieldSep, fout);
+                               }
+                               fputc('\n', fout);
+                               for (len-=fs_len; len--; fputc('-', fout));
+                               fputc('\n', fout);
+                       }
+               }
+       if (po->expanded && po->html3)
+       {
+               if (po->caption)
+                       fprintf(fout, "<centre><h2>%s</h2></centre>\n", po->caption);
+               else
+                       fprintf(fout, "<centre><h2>Query retrieved %d tuples * %d fields</h2></centre>\n", nTups, nFields);
+       }
+        for (i = 0; i < nTups; i++) {
+           char buf[8192*2+1];
+           if (po->expanded)
+           {
+               if (po->html3)
+                       fprintf(fout, "<table %s><caption align=high>%d</caption>\n", po->tableOpt? po->tableOpt: "", i);
+               else
+                       fprintf(fout, "-- RECORD %d --\n", i);
+           }
+            for (j = 0; j < nFields; j++) {
+                char *pval, *p, *o;
+               int plen;
+               if ((plen=PQgetlength(res,i,j))<1 || !(pval=PQgetvalue(res,i,j)) || !*pval)
+               {
+                       if (po->align || po->expanded)
+                               continue;
+                       goto efield;
+               }
+               for (p=pval, o=buf; *p; *(o++)=*(p++))
+               {
+                       if ((fs_len==1 && (*p==*(po->fieldSep))) || *p=='\\')
+                               *(o++)='\\';
+                       if (po->align && !((*p >='0' && *p<='9') || *p=='.' || *p=='E' || *p=='e' || *p==' ' || *p=='-'))
+                               fieldNotNum[j]=1;
+               }
+               *o='\0';
+               if (!po->expanded && (po->align || po->html3))
+               {
+                       int n=strlen(buf);
+                       if (n>fieldMax[j])
+                               fieldMax[j]=n;
+                       if (!(fields[i*nFields+j]=(char *)malloc(n+1)))
+                       {
+                               perror("malloc");
+                               exit(1);
+                       }
+                       strcpy(fields[i*nFields+j], buf);
+               } else
+               {
+                       if (po->expanded)
+                       {
+                               if (po->html3)
+                                       fprintf(fout, "<tr><td align=left><b>%s</b></td><td align=%s>%s</td></tr>\n",
+                                               fieldNames[j], fieldNotNum[j]? "left": "right", buf);
+                               else
+                               {
+                                       if (po->align)
+                                               fprintf(fout, "%-*s%s %s\n", fieldMaxLen-fs_len, fieldNames[j], po->fieldSep, buf);
+                                       else
+                                               fprintf(fout, "%s%s%s\n", fieldNames[j], po->fieldSep, buf);
+                               }
+                       }
+                       else
+                       {
+                               if (!po->html3)
+                               {
+                                       fputs(buf, fout);
+efield:
+                                       if ((j+1)<nFields)
+                                               fputs(po->fieldSep, fout);
+                                       else
+                                               fputc('\n', fout);
+                               }
+                       }
+               }
+           }
+           if (po->html3 && po->expanded)
+               fputs("</table>\n", fout);
+       }
+       if (!po->expanded && (po->align || po->html3))
+       {
+               if (po->html3)
+               {
+                       if (po->header)
+                       {
+                               if (po->caption)
+                                       fprintf(fout, "<table %s><caption align=high>%s</caption>\n", po->tableOpt? po->tableOpt: "", po->caption);
+                               else
+                                       fprintf(fout, "<table %s><caption align=high>Retrieved %d tuples * %d fields</caption>\n", po->tableOpt? po->tableOpt: "", nTups, nFields);
+                       } else
+                               fprintf(fout, "<table %s>", po->tableOpt? po->tableOpt: "");
+               }
+               if (po->header)
+               {
+                       if (po->html3)
+                               fputs("<tr>", fout);
+                       else
+                       {
+                               int tot=0;
+                               int n=0;
+                               char *p;
+                               for (; n<nFields; n++)
+                                       tot+=fieldMax[n]+fs_len+(po->standard? 2: 0);
+                               if (po->standard)
+                                       tot+=fs_len*2+2;
+                               if (!(p=border=malloc(tot+1)))
+                               {
+                                       perror("malloc");
+                                       exit(1);
+                               }
+                               if (po->standard)
+                               {
+                                       char *fs=po->fieldSep;
+                                       while (*fs++)
+                                               *p++='+';
+                               }
+                               for (j=0; j <nFields; j++)
+                               {
+                                       int len;
+                                       for (len=fieldMax[j] + (po->standard? 2:0) ; len--; *p++='-');
+                                       if (po->standard || (j+1)<nFields)
+                                       {
+                                               char *fs=po->fieldSep;
+                                               while (*fs++)
+                                                       *p++='+';
+                                       } 
+                               }
+                               *p='\0';
+                               if (po->standard)
+                                       fprintf(fout, "%s\n", border);
+                       }
+                       if (po->standard)
+                               fputs(po->fieldSep, fout);
+                       for (j=0; j < nFields; j++)
+                       {
+                               char *s=PQfname(res, j);
+                               if (po->html3)
+                               {
+                                       fprintf(fout, "<th align=%s>%s</th>", fieldNotNum[j]? "left": "right",
+                                               fieldNames[j]);
+                               } else
+                               {
+                                       int n=strlen(s);
+                                       if (n>fieldMax[j])
+                                               fieldMax[j]=n;
+                                       if (po->standard)
+                                               fprintf(fout, fieldNotNum[j]? " %-*s ": " %*s ", fieldMax[j], s);
+                                       else
+                                               fprintf(fout, fieldNotNum[j]? "%-*s": "%*s", fieldMax[j], s);
+                                       if (po->standard || (j+1)<nFields)
+                                               fputs(po->fieldSep, fout);
+                               }
+                       }
+                       if (po->html3)
+                               fputs("</tr>\n", fout);
+                       else
+                               fprintf(fout, "\n%s\n", border);
+               }
+               for (i = 0; i < nTups; i++)
+               {
+                       if (po->html3)
+                               fputs("<tr>", fout);
+                       else
+                               if (po->standard)
+                                       fputs(po->fieldSep, fout);
+                       
+                       for (j = 0; j < nFields; j++)
+                       {
+                               char *p=fields[i*nFields+j];
+                               if (po->html3)
+                                       fprintf(fout, "<td align=%s>%s</td>", fieldNotNum[j]? "left": "right", p? p: "");
+
+                               else
+                               {
+                                       fprintf(fout, fieldNotNum[j]? (po->standard? " %-*s ": "%-*s"): (po->standard? " %*s ": "%*s"), fieldMax[j], p? p: "");
+                                       if (po->standard || (j+1)<nFields)
+                                               fputs(po->fieldSep, fout);
+                               }
+                               if (p)
+                                       free(p);
+                       }
+                       if (po->html3)
+                               fputs("</tr>", fout);
+                       else
+                               if (po->standard)
+                                       fprintf(fout, "\n%s", border);
+                       fputc('\n', fout);
+               }
+               free(fields);
+       }
+       free(fieldMax);
+       free(fieldNotNum);
+       free(fieldNames);
+       if (border)
+               free(border);
+       if (po->html3 && !po->expanded)
+               fputs("</table>\n", fout);
+       }
+}
+
 
 /* ----------------
  *     PQfn -  Send a function call to the POSTGRES backend.
index af3413b437594f783cff038cbcafc2d463ac87ed..5adb5ade3355cb0d832a016808c7b83adc350f1f 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-fe.h,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $
+ * $Id: libpq-fe.h,v 1.2 1996/07/18 05:48:57 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -127,6 +127,17 @@ typedef struct pg_result{
   PGconn* conn;
 } PGresult;
 
+typedef struct _PQprintOpt {
+    bool header;           /* print output field headers or not */
+    bool align;            /* fill align the fields */
+    bool standard;         /* old brain dead format */
+    bool html3;            /* output html tables */
+    bool expanded;         /* expand tables */
+    char *fieldSep;        /* field separator */
+    char *tableOpt;       /* insert to HTML <table ...> */
+    char *caption;         /* HTML <caption> */
+    char **fieldName;      /* null terminated array of repalcement field names */
+} PQprintOpt;
 
 /* ===  in fe-connect.c === */
   /* make a new client connection to the backend */
@@ -165,13 +176,6 @@ extern char* PQoidStatus(PGresult *res);
 extern char* PQgetvalue(PGresult *res, int tup_num, int field_num);
 extern int PQgetlength(PGresult *res, int tup_num, int field_num);
 extern void PQclear(PGresult* res);
-/* PQdisplayTuples() is a better version of PQprintTuples() */
-extern void PQdisplayTuples(PGresult *res,
-                           FILE *fp,      /* where to send the output */
-                           int fillAlign, /* pad the fields with spaces */
-                           char *fieldSep,  /* field separator */
-                           int printHeader, /* display headers? */
-                           int quiet);
 extern void PQprintTuples(PGresult* res, 
                          FILE* fout,      /* output stream */
                          int printAttName,/* print attribute names or not*/
@@ -179,6 +183,10 @@ extern void PQprintTuples(PGresult* res,
                          int width        /* width of column, 
                                              if 0, use variable width */
                          );
+extern void PQprint(FILE* fout,      /* output stream */
+                   PGresult* res, 
+                   PQprintOpt *ps   /* option structure */
+                   );
 extern PGnotify* PQnotifies(PGconn *conn);
 extern PGresult* PQfn(PGconn* conn,
                      int fnid,