/*
 * libpqfunc.c --  Author: Ray R. Larson  - version 0.5, March 1993
 *  
 * The functions in this file implement all of the 
 * POSTGRES libpq calls via tcl/tk.
 *
 * Copyright 1991 Regents of the University of California.
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

/* TK/Tcl includes */
#include "tkConfig.h"
#include "tkInt.h"


/* system includes */
#include <stdio.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <signal.h>
#include <sys/param.h>

/* Postgres/libpq includes */

#include "c.h"
#include "strings.h"
#include "tmp/libpq.h"
#include "tmp/libpq-fe.h"
#include "tmp/libpq-fs.h"
#include "utils/log.h"

#include <errno.h>

/* Global Declarations
 */

/*
 * Declarations for library procedures:
 */


/*
 * Postgres variables
 *
 *  Some of these variables are defined in pqexec.c and
 *  are used in the fe/be comm. protocol
 *
 */
extern char	*PQhost;     /* machine on which the backend is running */
extern char	*PQport;     /* comm. port with the postgres backend. */
extern char	*PQtty;      /* the tty where postgres msgs are displayed */
extern char	*PQoption;   /* optional args. to the backend  */
extern char	*PQdatabase; /* the postgres db to access.  */
extern int	PQportset;   /* 1 if comm. with backend is set */
extern int	PQxactid;    /* xact id of the current xact.  */
extern char	*PQinitstr;  /* initialisation string sent to backend */
extern int	PQtracep;    /* set to 1 if debugging is set */

char query_buffer[8192];  /* Max postgres buffer size */
int Debugging = FALSE;
char *debug_file;
extern FILE *debug_port;
int WaitingCopyInput = FALSE;
int WaitingCopyOutput = FALSE;
int RunOneCommand = FALSE;
char *OneCommand;
int Verbose = FALSE;
int Silent = FALSE;
int TerseOutput = FALSE;
int PrintAttNames = TRUE;
extern char *optarg;
extern int optind,opterr;

PortalBuffer *current_portal;
char *dbname;
char dbnamebuffer[60];
char *tcldbname; /* dbname in the Tcl address space - but don't rely on it */
char *lastquery; /* last string passed to PQexec */
char *current_portal_name; /* most recent portal name used */

extern char *getenv();
extern char *getwd();
static char *fileName;
static char *geometry;
static char *display;
static char *name;
static int synchronize;


Tk_ArgvInfo DB_argTable[] = {
    {"-file", TK_ARGV_STRING, (char *) NULL, (char *) &fileName,
	"File from which to read commands"},
    {"-rcfile", TK_ARGV_STRING, (char *) NULL, (char *) &tcl_RcFileName,
	"Override default ~/.PGTKrc startup file"},
    {"-geometry", TK_ARGV_STRING, "+450+200", (char *) &geometry,
	"Initial geometry for window"},
    {"-display", TK_ARGV_STRING, (char *) NULL, (char *) &display,
	"Display to use"},
    {"-name", TK_ARGV_STRING, (char *) NULL, (char *) &name,
	"Name to use for application"},
    {"-host", TK_ARGV_STRING, (char *) NULL, (char *) &PQhost,
	"Name of Postgres Host"},
    {"-port", TK_ARGV_STRING, (char *) NULL, (char *) &PQport,
	"Name of Postgres port"},
    {"-tty", TK_ARGV_STRING, (char *) NULL, (char *) &PQtty,
	"TTY for Postgres messages"},
    {"-options", TK_ARGV_STRING, (char *) NULL, (char *) &PQoption,
	"Optional arguments to Postgres backend"},
    {"-command", TK_ARGV_STRING, (char *) NULL, (char *) &OneCommand,
	"Single Postgres command to run - must be in quotes"},
    {"-debug", TK_ARGV_STRING, (char *) NULL, (char *) &debug_file,
	"File for debugging output"},
    {"-Nonames", TK_ARGV_CONSTANT, (char *) FALSE, (char *) &PrintAttNames,
	"Suppress printing of attribute names in tty output"},
    {"-Terse", TK_ARGV_CONSTANT, (char *) TRUE, (char *) &TerseOutput,
	"Terse output from Postgres - no tty formatting"},
    {"-Quiet", TK_ARGV_CONSTANT, (char *) FALSE, (char *) &Verbose,
	"run in quiet mode"},
    {"-Silent", TK_ARGV_CONSTANT, (char *) TRUE, (char *) &Silent,
	"run in silent mode - no tty output except for errors"},
    {"-sync", TK_ARGV_CONSTANT, (char *) 1, (char *) &synchronize,
	"Use synchronous mode for display server"},
    {"x", TK_ARGV_REST, (char *) NULL, (char *) &dbname,
	"Database name (must be the last argument)"},
    {"-?", TK_ARGV_HELP, (char *) NULL, (char *) NULL,
	"Print this message and quit."},
    {"-HELP", TK_ARGV_HELP, (char *) NULL, (char *) NULL,
	"Print this message and quit."},
    {(char *) NULL, TK_ARGV_END, (char *) NULL, (char *) NULL,
	(char *) NULL}
};


/***************************************************************************
 * Parse command line args for POSTGRES stuff
 **************************************************************************/
DB_ParseArgs(interp)
    Tcl_Interp *interp;			/* Current interpreter. */
{
    int code;
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
    char *argvlist;
    
    /* this is a little strange, but since there is no real */
    /* access to main for Tk to get the "real" args, we use */
    /* the versions put into the Tcl interpreter and rebuild*/
    /* the command line argc and argv.                      */
    code = Tcl_VarEval(interp, "set all_argv [concat $argv0 $argv]",
		        (char *) NULL);
    argvlist = Tcl_GetVar(interp, "all_argv", TCL_GLOBAL_ONLY);
    
    Tcl_SplitList(interp, argvlist, &argc, &argv);

    /* OK, now we can reparse the command line */
    if (Tk_ParseArgv(interp,(Tk_Window)NULL, &argc, argv, DB_argTable,0)
	!= TCL_OK) {
      fprintf(stderr,"%s\n",interp->result);
      exit(1);
    }
    
    if (debug_file != NULL) {
	  Debugging = TRUE;
	  debug_port = fopen(debug_file,"w+");
	  if (debug_port == NULL) {
	      fprintf(stderr,"Unable to open debug file %s \n", debug_file);
	      exit(1);
	  }
	  PQtracep = 1;
    }
    if (OneCommand != NULL) {
       Silent = TRUE;
       Verbose = FALSE;
       RunOneCommand = TRUE;
    }
    if (Silent)
       Verbose = FALSE;

    /* find default database */
    if ((dbname = argv[1]) == NULL)
	if ((dbname = getenv("DATABASE")) == NULL)
	    dbname = getenv("USER");

    tcldbname = Tcl_SetVar(interp, "CURRENTDATABASE", dbname,
			TCL_GLOBAL_ONLY);

    
}

/***************************************************************************
 * LIBPQ functions in Tcl command wrapping
 **************************************************************************/
int
PGTKsetdb(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{


    if (argc != 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" databasename\"", (char *) NULL);
	return TCL_ERROR;
    }
    strcpy(dbnamebuffer,argv[1]);
    if (Debugging) fprintf(debug_port,"database is %s\n", dbnamebuffer);
    PQsetdb(dbnamebuffer);
    tcldbname = Tcl_SetVar(interp, "CURRENTDATABASE", argv[1],
			TCL_GLOBAL_ONLY);

    return TCL_OK;
}

int
PGTKdb(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{


    if (argc != 1) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		"\"", (char *) NULL);
	return TCL_ERROR;
    }

    Tcl_AppendResult(interp, PQdb(), (char *) NULL);
    tcldbname = Tcl_SetVar(interp, "CURRENTDATABASE", PQdb(),
			TCL_GLOBAL_ONLY);
    return TCL_OK;
}

int
PGTKreset(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{


    if (argc != 1) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		"\"", (char *) NULL);
	return TCL_ERROR;
    }
    PQreset();
    return TCL_OK;
}

int
PGTKfinish(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{


    if (argc != 1) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		"\"", (char *) NULL);
	return TCL_ERROR;
    }
    PQfinish();
    return TCL_OK;
}

int
PGTKexec(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    char *result, *tmp, c;
    int i = 0, j = 0;
    bool InAComment = false;

    if (argc != 2) {
	Tcl_AppendResult(interp, "wrong # args: should be '", argv[0],
		"\{<PostQuel Query>\}'", (char *) NULL);
	return TCL_ERROR;
    }

    if (Debugging) fprintf(debug_port,
        "argv0 = %s \n argv1 = %s \n argv2 = %s\n",argv[0],argv[1],argv[2]);

    lastquery = Tcl_SetVar(interp, "LASTQUERY", argv[1],
			TCL_GLOBAL_ONLY);
    if (Debugging) fprintf(debug_port,
       "lastquery = %s\n, j= %d, i=%d\n",argv[1],j,i);

    strcpy(query_buffer,argv[1]);

    if (Debugging) fprintf (debug_port,"query buffer is: %s",query_buffer);

    /* ship it off to Postgres */
    result = PQexec(query_buffer);

    /* check the results and set Tcl return string */
    switch(result[0]) {
        case 'A':
        case 'P':
            Tcl_AppendResult(interp, &(result[1]), (char *) NULL);
	    current_portal_name = Tcl_SetVar(interp, "CURRENTPORTAL", &(result[1]),
			TCL_GLOBAL_ONLY);
            break;

        case 'E':
            Tcl_AppendResult(interp, "Postgres Fatal Error: ", &(result[1]), (char *) NULL);
            return TCL_ERROR;
            break;

        case 'C':
            Tcl_AppendResult(interp, &(result[1]), (char *) NULL);
            break;

        case 'I':
            PQFlushI(1);
            Tcl_AppendResult(interp, &(result[0]), (char *) NULL);
            break;

        case 'R':
            Tcl_AppendResult(interp, "Postgres Error: ", &(result[1]), (char *) NULL);
            return TCL_ERROR;
            break;

        case 'B':
            Tcl_AppendResult(interp, "Postgres ready for Copy output", (char *) NULL);
            WaitingCopyOutput = TRUE;
            break;

        case 'D':
            Tcl_AppendResult(interp, "Postgres ready for Copy input",  (char *) NULL);
            WaitingCopyInput = TRUE;
            break;

        default:
            Tcl_AppendResult(interp, "Unknown Postgres Return Code: ", &(result[0]), (char *) NULL);
          }
    return TCL_OK;
}


/***************************************************************************
 * Postgres Portal manipulation
 **************************************************************************/

int
PGTKnportals(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int rule;
    int numportals;

    if (argc > 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" <0|1>\"", (char *) NULL);
	return TCL_ERROR;
    }
    if (argc == 2) {
        Tcl_GetInt(interp,argv[1],&rule);
        numportals = PQnportals(rule);
    }
    else
        numportals = PQnportals();

    sprintf(nbuffer,"%d",numportals);
    Tcl_AppendResult(interp, nbuffer, (char *) NULL);
    return TCL_OK;
}

int
PGTKpnames(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{


    char **pnames;
    char *namedata;
    int rule, i;
    int numportals;

    if (argc != 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" <0|1>\"", (char *) NULL);
	return TCL_ERROR;
    }

    Tcl_GetInt(interp,argv[1],&rule);
    numportals = PQnportals(rule);


    if ((pnames = (char **) malloc((numportals+1) * sizeof(char *))) 
	== NULL) {
	Tcl_AppendResult(interp, "Out of Memory in ", argv[0],
		" (PGTKpnames)", (char *) NULL);
	return TCL_ERROR;
    }

    for (i = 0; i < numportals; i++) {
      if ((pnames[i] = (char *) malloc(PortalNameLength)) == NULL) {
	Tcl_AppendResult(interp, "Out of Memory in ", argv[0],
			 " (PGTKpnames)", (char *) NULL);
	return TCL_ERROR;
      }
    }

    PQpnames(pnames,rule);
    for (i = 0; i < numportals; i++) {
       if(pnames[i] != NULL) {
          Tcl_AppendResult(interp, pnames[i], " ", (char *) NULL);
	  free(pnames[i]);
	}
    }
    free(pnames);

    return TCL_OK;
}

int
PGTKparray(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    if (argc > 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname\"", (char *) NULL);
	return TCL_ERROR;
    }
    /* set the global current portal buffer */
    if (argc == 2)
       current_portal = PQparray(argv[1]);
    else
       current_portal = PQparray("blank");
    return TCL_OK;
}

int
PGTKclear(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    if (argc != 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname\"", (char *) NULL);
	return TCL_ERROR;
    }
    /* set the global current portal buffer */
    PQclear(argv[1]);
    return TCL_OK;
}

int
PGTKrulep(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int rule;
    PortalBuffer *p;

    if (argc > 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" <portalname>\"", (char *) NULL);
	return TCL_ERROR;
    }
    if (argc == 1) {
       if (current_portal)
           p = current_portal;
       else
           p = current_portal = PQparray("blank");
    }
    else {
           p = current_portal = PQparray(argv[1]);
    }
    rule = PQrulep(p);
    sprintf(nbuffer,"%d",rule);
    Tcl_AppendResult(interp, nbuffer, (char *) NULL);
    return TCL_OK;
}

int
PGTKntuples(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int tuples;
    PortalBuffer *p;

    if (argc > 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" <portalname>\"", (char *) NULL);
	return TCL_ERROR;
    }
    if (argc == 1) {
       if (current_portal )
           p = current_portal;
       else
           p = current_portal = PQparray("blank");
    }
    else {
           p = current_portal = PQparray(argv[1]);
    }
    tuples = PQntuples(p);
    sprintf(nbuffer,"%d",tuples);
    Tcl_AppendResult(interp, nbuffer, (char *) NULL);
    return TCL_OK;
}

int
PGTKngroups(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int groups;
    PortalBuffer *p;

    if (argc > 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" <portalname>\"", (char *) NULL);
	return TCL_ERROR;
    }
    if (argc == 1) {
       if (current_portal )
           p = current_portal;
       else
           p = current_portal = PQparray("blank");
    }
    else {
           p = current_portal = PQparray(argv[1]);
    }
    groups = PQngroups(p);
    sprintf(nbuffer,"%d",groups);
    Tcl_AppendResult(interp, nbuffer, (char *) NULL);
    return TCL_OK;
}

int
PGTKntuplesGroup(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int tuples, group;
    PortalBuffer *p;

    if (argc != 3) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname groupindex#\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
    group = strtol(argv[2], (char **) NULL, 0);
    tuples = PQntuplesGroup(p,group);
    sprintf(nbuffer,"%d",tuples);
    Tcl_AppendResult(interp, nbuffer, (char *) NULL);
    return TCL_OK;
}

int
PGTKnfieldsGroup(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int fields, group;
    PortalBuffer *p;

    if (argc != 3) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname groupindex#\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
    group = strtol(argv[2], (char **) NULL, 0);
    fields = PQnfieldsGroup(p,group);
    sprintf(nbuffer,"%d",fields);
    Tcl_AppendResult(interp, nbuffer, (char *) NULL);
    return TCL_OK;
}

int
PGTKfnameGroup(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    int field, group;
    PortalBuffer *p;

    if (argc != 4) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname groupindex# field#\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
    group = strtol(argv[2], (char **) NULL, 0);
    field = strtol(argv[3], (char **) NULL, 0);
    Tcl_AppendResult(interp, PQfnameGroup(p,group,field) , (char *) NULL);
    return TCL_OK;
}

int
PGTKfnumberGroup(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int field, group;
    PortalBuffer *p;

    if (argc != 4) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname groupindex# fieldname\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
    group = strtol(argv[2], (char **) NULL, 0);
    field = PQfnumberGroup(p,group,argv[3]);
    sprintf(nbuffer,"%d",field);
    Tcl_AppendResult(interp, nbuffer, (char *) NULL);
    return TCL_OK;
}


int
PGTKgetgroup(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int tuple_index, group;
    PortalBuffer *p;

    if (argc != 3) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname tuple_index#\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
    tuple_index = strtol(argv[2], (char **) NULL, 0);
    group = PQgetgroup(p,tuple_index);
    sprintf(nbuffer,"%d",group);
    Tcl_AppendResult(interp, nbuffer, (char *) NULL);
    return TCL_OK;
}

int
PGTKnfields(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int tuple_index, fields;
    PortalBuffer *p;

    if (argc != 3) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname tuple_index#\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
    tuple_index = strtol(argv[2], (char **) NULL, 0);
    fields = PQnfields(p,tuple_index);
    sprintf(nbuffer,"%d",fields);
    Tcl_AppendResult(interp, nbuffer, (char *) NULL);
    return TCL_OK;
}

int
PGTKfnumber(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int tuple_index, fieldnumber;
    PortalBuffer *p;

    if (argc != 4) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname tuple_index# field_name\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
    tuple_index = strtol(argv[2], (char **) NULL, 0);
    fieldnumber = PQfnumber(p,tuple_index, argv[3]);
    sprintf(nbuffer,"%d",fieldnumber);
    Tcl_AppendResult(interp, nbuffer, (char *) NULL);
    return TCL_OK;
}

int
PGTKfname(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    int tuple_index, fieldnumber;
    PortalBuffer *p;

    if (argc != 4) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname tuple_index# field_number#\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
    tuple_index = strtol(argv[2], (char **) NULL, 0);
    fieldnumber = strtol(argv[3], (char **) NULL, 0);
    Tcl_AppendResult(interp, PQfname(p,tuple_index,fieldnumber) , (char *) NULL);
    return TCL_OK;
}

int
PGTKftype(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int tuple_index, fieldnumber, ftype;
    PortalBuffer *p;

    if (argc != 4) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname tuple_index# field_number#\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
   tuple_index = strtol(argv[2], (char **) NULL, 0);
   fieldnumber = strtol(argv[3], (char **) NULL, 0);
   ftype = PQftype(p,tuple_index, fieldnumber);
   sprintf(nbuffer,"%d",ftype);
   Tcl_AppendResult(interp, nbuffer , (char *) NULL);
    return TCL_OK;
}

int
PGTKsametype(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    int tuple_index1, tuple_index2;
    PortalBuffer *p;

    if (argc != 4) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname tuple_index1# tuple_index2#\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
   tuple_index1 = strtol(argv[2], (char **) NULL, 0);
   tuple_index2 = strtol(argv[3], (char **) NULL, 0);
   if ( PQsametype(p,tuple_index1, tuple_index2) == 1)
      Tcl_AppendResult(interp, "1", (char *) NULL);
   else
      Tcl_AppendResult(interp, "0", (char *) NULL);
    return TCL_OK;
}

int
PGTKgetvalue(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int tuple_index, fieldnumber, ftype;
    PortalBuffer *p;

    if (argc != 4) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" portalname tuple_index# field_number#\"", (char *) NULL);
	return TCL_ERROR;
    }
    p = current_portal = PQparray(argv[1]);
   tuple_index = strtol(argv[2], (char **) NULL, 0);
   fieldnumber = strtol(argv[3], (char **) NULL, 0);
   Tcl_AppendResult(interp, PQgetvalue(p,tuple_index,fieldnumber) , (char *) NULL);
    return TCL_OK;
}

int
PGTKgetline(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char linebuffer[8192]; /* max portalbuffer size */

    if (!WaitingCopyOutput) {
	Tcl_AppendResult(interp, "No Postgres copy command active", (char *) NULL);
	return TCL_ERROR;
    }
    PQgetline(linebuffer, 8192);
    Tcl_AppendResult(interp, linebuffer , (char *) NULL);
    return TCL_OK;
}

int
PGTKputline(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char linebuffer[8192]; /* max portalbuffer size */
    int i;

    if (argc < 2) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		"field_1_val field_2_val ... field_n_val\"", (char *) NULL);
	return TCL_ERROR;
    }
    if (!WaitingCopyInput) {
	Tcl_AppendResult(interp, "No Postgres copy command active", (char *) NULL);
	return TCL_ERROR;
    }
    if (argc == 2)
       PQputline(argv[1]);
    else {
       for (i = 1; i <= argc-1; i++) {
           strcat(linebuffer,argv[i]);
           if (i < argc-1)
              strcat(linebuffer,"\t");
       }
       PQputline(linebuffer);
    }
    return TCL_OK;
}

int
PGTKendcopy(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    if (WaitingCopyInput || WaitingCopyOutput) {
    	PQendcopy();
        WaitingCopyInput = FALSE;
        WaitingCopyOutput = FALSE;
    }
    return TCL_OK;
}

int
PGTKtrace(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    PQtrace();
    return TCL_OK;
}

int
PGTKuntrace(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    PQuntrace();
    return TCL_OK;
}


/***************************************************************************
 * Composite routines using libpq
 **************************************************************************/

int
PGTKtuplearray(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char nbuffer[20];
    int rule;
    PortalBuffer *p;
    int j,k,g,m,n,t,x;
    int temp, temp1;
    char *arg1, *arg2 ;
    char n_groups[40], fields[40], tupleindex[40];
    char *valptr;

    if (argc == 1) {
       /* set some defaults if no portal or array var specified */
       arg1 = "blank";
       arg2 = "LASTRESULT";
    }

    if (argc == 2) {
       /* assume only an array name given */
	arg1 = "blank";
	arg2 = argv[1];

    }

    if (argc == 3) {
       /* both portal and array name given */
	arg1 = argv[1];
	arg2 = argv[2];

    }

    p = current_portal = PQparray(arg1);
    g = PQngroups (p);
    sprintf(n_groups,"%d",g);
    Tcl_SetVar2(interp, arg2, "GROUPS", n_groups,
		TCL_GLOBAL_ONLY );
    t = 0;
    temp = 0;

    for (k =0; k < g; k++) {
	n = PQntuplesGroup(p,k);  /* zero based index.  */
	m = PQnfieldsGroup(p,k);

	sprintf(fields,"TUPLES.%d",k);
	sprintf(tupleindex,"%d",n);
	Tcl_SetVar2(interp, arg2, fields,  tupleindex,
		TCL_GLOBAL_ONLY );

	if ( m > 0 ) {  /* only print tuples with at least 1 field.  */
                /* set the field names element */
		sprintf(fields,"FIELDS.%d",k);
		for (x=0; x < m; x++) {
			Tcl_SetVar2(interp, arg2, fields,  PQfnameGroup(p,k,x),
				TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);
		}

		/* get the types for each field (if any fields) */
		t += temp;

		sprintf(fields,"TYPES.%d",k);
		if (n > 0) {
			for (j = 0; j < m; j++) {
				sprintf(tupleindex,"%d", PQftype(p,t,j));
				Tcl_SetVar2(interp, arg2, fields, tupleindex,
				TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);
			}
		}
		/* get the tuples */
		for (x = 0; x < n; x++) {
			sprintf(tupleindex,"%03d.%06d",k,x);
			for (j = 0; j < m; j++) {
				valptr = PQgetvalue(p,t+x,j);
                                if (valptr == NULL) valptr = "(null)";
				Tcl_SetVar2(interp, arg2, tupleindex, valptr,
				TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);
			}
		}
	temp += n;
	}
    }

    return TCL_OK;
}


