/*
 * PGTK --  Author: Ray R. Larson  - version 0.1, Nov. 1991
 *
 *	PGTK is a replacement for the Postgres monitor program that uses
 *      John Ousterhout's Tcl and TK libraries to provide a more complete
 *      application building tool for Postgres under the X window system.
 *
 *      Version Info:
 *             v. 0.1 (Nov. 1991) - first cut - based on the wish program.
 *
 * 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>

/* Postgres/libpq includes */

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

#include <errno.h>

/* Global Declarations
 */

/*
 * Declarations for library procedures:
 */
extern int   isatty();
extern char *getenv();

/*
 * TK/Tcl variables
 */
Tk_Window w;			/* NULL means window has been deleted. */
Tk_TimerToken timeToken = 0;
int idleHandler = 0;
Tcl_Interp *interp;
int x, y;
Tcl_CmdBuf buffer;
int tty;

/*
 * 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 */

#define DEFAULT_EDITOR "/usr/ucb/vi"
static char *user_editor;     /* user's desired editor  */
static int tmon_temp;         /* file descriptor for temp. buffer file */
static char *pid_string;
static char *tmon_temp_filename;
static char query_buffer[8192];  /* Max postgres buffer size */
int RunOneCommand = FALSE;
char *OneCommand;
int Debugging = FALSE;
char *debug_file;
int Verbose = FALSE;
int Silent = FALSE;
int TerseOutput = FALSE;
int PrintAttNames = TRUE;
int WaitingCopyInput = FALSE;
int WaitingCopyOutput = FALSE;
extern char *optarg;
extern int optind,opterr;
extern FILE *debug_port;
PortalBuffer *current_portal;
char *dbname;
static char dbnamebuffer[20];
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 */
char *homedir;
char *sysdir;

char *TCLinitCmd =
    "if [file exists [info library]/init.tcl] {source [info library]/init.tcl}";

char *PGTKinitCmd =
    "if [file exists [info library]/PGTK.init] {source [info library]/PGTK.init}";


/*
 * Information for command-line options:
 */

int synchronize = 0;
int SkipInit = 0;
char *file = NULL;
char *rcfile = NULL; /* alternate startup file */
char *name = NULL;
char *display = NULL;
char *geometry = NULL;

Tk_ArgvInfo argTable[] = {
    {"-file", TK_ARGV_STRING, (char *) NULL, (char *) &file,
	"File from which to read commands"},
    {"-rcfile", TK_ARGV_STRING, (char *) NULL, (char *) &file,
	"Override default ~/.PGTKrc startup file"},
    {"-geometry", TK_ARGV_STRING, (char *) NULL, (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"},
    {"-skipinit", TK_ARGV_CONSTANT, (char *) TRUE, (char *) &SkipInit,
	"Don't load the standard pgtk.init initialization file"},
    {"-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}
};

void
StdinProc(clientData, mask)
    ClientData clientData;		/* Not used. */
    int mask;
{
    char line[200];
    static int gotPartial = 0;
    char *cmd;
    int result;

    if (mask & TK_READABLE) {
	if (fgets(line, 200, stdin) == NULL) {
	    if (!gotPartial) {
		if (tty) {
		    Tcl_Eval(interp, "destroy .", 0, (char **) NULL);
		    PQfinish();
		    exit(0);
		} else {
		    Tk_DeleteFileHandler(0);
		}
		return;
	    } else {
		line[0] = 0;
	    }
	}
	cmd = Tcl_AssembleCmd(buffer, line);
	if (cmd == NULL) {
	    gotPartial = 1;
	    return;
	}
	gotPartial = 0;
	result = Tcl_RecordAndEval(interp, cmd, 0);
	if (*interp->result != 0) {
	    if ((result != TCL_OK) || (tty)) {
		printf("%s\n", interp->result);
	    }
	}
	if (tty) {
	    printf("PGTK>> ");
	    fflush(stdout);
	}
    }
}

static void
StructureProc(clientData, eventPtr)
    ClientData clientData;	/* Information about window. */
    XEvent *eventPtr;		/* Information about event. */
{
    if (eventPtr->type == DestroyNotify) {
	w = NULL;
    }
}

int
GeometryCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Application window. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    Tk_Window window;
    int width, height;

    if (argc != 4) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" window width height\"", (char *) NULL);
	return TCL_ERROR;
    }
    window = Tk_NameToWindow(interp, argv[1], tkwin);
    if (window == NULL) {
	return TCL_ERROR;
    }
    width = atoi(argv[2]);
    height = atoi(argv[3]);
    Tk_GeometryRequest(window, width, height);
    return TCL_OK;
}

int
ResizeCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Application window. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    Tk_Window window;
    unsigned int width, height;

    if (argc != 4) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" window width height\"", (char *) NULL);
	return TCL_ERROR;
    }
    window = Tk_NameToWindow(interp, argv[1], tkwin);
    if (window == NULL) {
	return TCL_ERROR;
    }
    width = atoi(argv[2]);
    height = atoi(argv[3]);
    Tk_ResizeWindow(window, width, height);
    return TCL_OK;
}

int
MapCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Application window. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    Tk_Window window;

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

int
MoveCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Application window. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    Tk_Window window;
    int x, y;

    if (argc != 4) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" window x y\"", (char *) NULL);
	return TCL_ERROR;
    }
    window = Tk_NameToWindow(interp, argv[1], tkwin);
    if (window == NULL) {
	return TCL_ERROR;
    }
    x = atoi(argv[2]);
    y = atoi(argv[3]);
    Tk_MoveWindow(window, x, y);
    return TCL_OK;
}

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

    if (argc != 3) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" x y\"", (char *) NULL);
	return TCL_ERROR;
    }
    x = strtol(argv[1], (char **) NULL, 0);
    y = strtol(argv[2], (char **) NULL, 0);
    Tk_MakeWindowExist(w);
    XDrawPoint(Tk_Display(w), Tk_WindowId(w),
	    DefaultGCOfScreen(Tk_Screen(w)), x, y);
    return TCL_OK;
}

int
MovetoCmd(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    if (argc != 3) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" x y\"", (char *) NULL);
	return TCL_ERROR;
    }
    x = strtol(argv[1], (char **) NULL, 0);
    y = strtol(argv[2], (char **) NULL, 0);
    return TCL_OK;
}

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

    if (argc != 3) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" x y\"", (char *) NULL);
	return TCL_ERROR;
    }
    newX = strtol(argv[1], (char **) NULL, 0);
    newY = strtol(argv[2], (char **) NULL, 0);
    Tk_MakeWindowExist(w);
    XDrawLine(Tk_Display(w), Tk_WindowId(w),
	    DefaultGCOfScreen(Tk_Screen(w)), x, y, newX, newY);
    x = newX;
    y = newY;
    return TCL_OK;
}

int
PolyCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    GC gc;
    XColor *colorPtr;
    XGCValues values;
    Tk_3DBorder border;
    XPoint points[50], *pointPtr;
    int i, numPoints, width, foo;
    Tk_Window tkwin2;

    if ((argc < 8) || (argc > 50) || (argc & 01)) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" window color width x1 y1 x2 y2 ...\"", (char *) NULL);
	return TCL_ERROR;
    }
    tkwin2 = Tk_NameToWindow(interp, argv[1], tkwin);
    if (tkwin2 == NULL) {
	return TCL_ERROR;
    }
    colorPtr = Tk_GetColor(interp, tkwin2, (Colormap) None,
	    Tk_GetUid(argv[2]));
    if (colorPtr == NULL) {
	return TCL_ERROR;
    }
    values.foreground = colorPtr->pixel;
    gc = Tk_GetGC(tkwin2, GCForeground, &values);
    border = Tk_Get3DBorder(interp, tkwin2, (Colormap) None,
	    Tk_GetUid(argv[2]));
    if (border == NULL) {
	return TCL_ERROR;
    }
    if (Tcl_GetInt(interp, argv[3], &width) != TCL_OK) {
	return TCL_ERROR;
    }
    for (i = 4, pointPtr = points; i < argc; i += 2, pointPtr++) {
	if (Tcl_GetInt(interp, argv[i], &foo) != TCL_OK) {
	    return TCL_ERROR;
	}
	pointPtr->x = foo;
	if (Tcl_GetInt(interp, argv[i+1], &foo) != TCL_OK) {
	    return TCL_ERROR;
	}
	pointPtr->y = foo;
    }
    numPoints = (argc-4)/2;
    Tk_MakeWindowExist(tkwin2);
    XFillPolygon(Tk_Display(tkwin2), Tk_WindowId(tkwin2), gc, points,
	    numPoints, Nonconvex, CoordModeOrigin);
    Tk_Draw3DPolygon(Tk_Display(tkwin2), Tk_WindowId(tkwin2), border,
	    points, numPoints, width, 1);
    return TCL_OK;
}



int
BitmapCmd(tkwin, interp, argc, argv)
    Tk_Window tkwin;			/* Application window. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{
    Tk_Window window;
    Pixmap pixmap;
    int x, y;
    char filename [200];

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

    if (argv[2][0] != '@') {
	Tcl_AppendResult(interp, "Bitmap file name must start with '@'",
                   (char *) NULL);
	return TCL_ERROR;
    }

    /* get the bitmap from the file */
    pixmap = Tk_GetBitmap(interp,window,argv[2]);

    if (pixmap == NULL) {
	return TCL_ERROR;
    }
    /* probably will need to figure out the size of the bitmap and */
    /* do a geometry request for the frame ...                     */

    Tk_SetWindowBackgroundPixmap(window, pixmap);

    return TCL_OK;
}


/***************************************************************************
 * 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;
    }
#ifdef JUNK
    /* skip leading white space */
    while ( argv[1][j] != '\0' && isspace((int)argv[1][j])) j++;

    query_buffer[0] = 0;
#endif

    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);

#ifdef JUNK
    /*
     *  Stripping out comments (if any) from the query (should really be
     *  handled in the parser, of course).
     */
    while ( c = argv[1][j] != '\0') {
    	if (InAComment == false) {
		switch(c) {
			case '\n':    /* remove line breaks */
				query_buffer[i++] = ' ';
				break;
			case '/':     /* ignore comments */
				if (argv[1][j+1] == '*' )
					InAComment = true;
				else
					query_buffer[i++] = c;
				break;
			default:
				query_buffer[i++] = c;
				break;
		}
   	}
   	else {
		/*
		*  in comment mode, ignore chars until a '*' followed by a '/'
		*  is found
		*/
                if ( c == '*' && argv[1][j+1] == '/') {
			j++;
			InAComment = false;
		}
   	}
        j++;
    }
    /* null terminate the query buffer */
    query_buffer[i] = '\0';

    if (query_buffer[0] == 0) {
        query_buffer[0] = ' ';
        query_buffer[1] = 0;
    }
#endif
    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;
}


/***************************************************************************
 * Postgre 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[MAXPORTALS][20];
    int rule, i;

    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);
    PQpnames(pnames,rule);
    for (i = 0; i < MAXPORTALS; i++) {
       if(pnames[i] != NULL)
          Tcl_AppendResult(interp, pnames[i], " ", (char *) NULL);
    }
    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. */
{

    char pnames[MAXPORTALS][20];
    int rule;
    int numportals, i;

    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. */
{

    char pnames[MAXPORTALS][20];
    int rule;
    int numportals, i;

    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) ? PQgetvalue(p,tuple_index,fieldnumber) : "_null_", (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[20], fields[20], tupleindex[20];

    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,"%d.%d",k,x);
			for (j = 0; j < m; j++) {
				Tcl_SetVar2(interp, arg2, tupleindex, PQgetvalue(p,t+x,j) ? PQgetvalue(p,t+x,j) : "_null_",
				TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE);
			}
		}
	temp += n;
	}
    }

    return TCL_OK;
}


/***************************************************************************
 * Main routine
 **************************************************************************/
int
main(argc, argv)
    int argc;
    char **argv;
{
    char *args, *p;
    char buf[20];
    int result;
    Tk_3DBorder border;

    interp = Tcl_CreateInterp();
    if (Tk_ParseArgv(interp, (Tk_Window) NULL, &argc, argv, argTable, 0)
	    != TCL_OK) {
	fprintf(stderr, "%s\n", interp->result);
	exit(1);
    }
    if (name == NULL) {
	if (file != NULL) {
	    p = file;
	} else {
	    p = argv[0];
	}
	name = strrchr(p, '/');
	if (name != NULL) {
	    name++;
	} else {
	    name = p;
	}
    }
    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);

    w = Tk_CreateMainWindow(interp, display, name);
    if (w == NULL) {
	fprintf(stderr, "%s\n", interp->result);
	exit(1);
    }
    Tk_SetClass(w, "Tk");
    Tk_CreateEventHandler(w, StructureNotifyMask, StructureProc,
	    (ClientData) NULL);
    tty = isatty(0);

    args = Tcl_Merge(argc-1, argv+1);
    Tcl_SetVar(interp, "argv", args, TCL_GLOBAL_ONLY);
    ckfree(args);
    sprintf(buf, "%d", argc-1);
    Tcl_SetVar(interp, "argc", buf, TCL_GLOBAL_ONLY);

    if (synchronize) {
	XSynchronize(Tk_Display(w), True);
    }
    Tk_GeometryRequest(w, 200, 200);
    border = Tk_Get3DBorder(interp, w, None, "#4eee94");
    if (border == NULL) {
	Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
	Tk_SetWindowBackground(w, WhitePixelOfScreen(Tk_Screen(w)));
    } else {
	Tk_SetBackgroundFromBorder(w, border);
    }
    XSetForeground(Tk_Display(w), DefaultGCOfScreen(Tk_Screen(w)),
	    BlackPixelOfScreen(Tk_Screen(w)));
    Tcl_CreateCommand(interp, "dot", DotCmd, (ClientData) w,
	    (void (*)()) NULL);
    Tcl_CreateCommand(interp, "geometry", GeometryCmd, (ClientData) w,
	    (void (*)()) NULL);
    Tcl_CreateCommand(interp, "lineto", LinetoCmd, (ClientData) w,
	    (void (*)()) NULL);
    Tcl_CreateCommand(interp, "map", MapCmd, (ClientData) w,
	    (void (*)()) NULL);
    Tcl_CreateCommand(interp, "move", MoveCmd, (ClientData) w,
	    (void (*)()) NULL);
    Tcl_CreateCommand(interp, "moveto", MovetoCmd, (ClientData) w,
	    (void (*)()) NULL);
    Tcl_CreateCommand(interp, "poly", PolyCmd, (ClientData) w,
	    (void (*)()) NULL);
    Tcl_CreateCommand(interp, "resize", ResizeCmd, (ClientData) w,
	    (void (*)()) NULL);
    Tcl_CreateCommand(interp, "bitmap", BitmapCmd, (ClientData) w,
	    (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQsetdb", PGTKsetdb, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQdb", PGTKdb, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQreset", PGTKreset, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQfinish", PGTKfinish, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQexec", PGTKexec, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQnportals", PGTKnportals, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQpnames", PGTKpnames, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQparray", PGTKparray, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQclear", PGTKclear, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQrulep", PGTKrulep, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQntuples", PGTKntuples, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQngroups", PGTKngroups, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQnfieldsGroup", PGTKnfieldsGroup, (ClientData)NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQfnameGroup", PGTKfnameGroup, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQfnumberGroup", PGTKfnumberGroup, (ClientData)NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQgetgroup", PGTKgetgroup, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQnfields", PGTKnfields, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQfnumber", PGTKfnumber, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQfname", PGTKfname, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQftype", PGTKftype, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQsametype", PGTKsametype, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQgetvalue", PGTKgetvalue, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQgetline", PGTKgetline, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQputline", PGTKputline, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQendcopy", PGTKendcopy, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQtrace", PGTKtrace, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQuntrace", PGTKuntrace, (ClientData) NULL,
            (void (*)()) NULL);
    Tcl_CreateCommand(interp,"PQtuplearray", PGTKtuplearray, (ClientData) NULL,
            (void (*)()) NULL);

    if (geometry != NULL) {
	result = Tcl_VarEval(interp, "wm geometry . ", geometry,
		"; wm positionfrom . user", (char *) NULL);
	if (result != TCL_OK) {
	    char *msg;

	    msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
	    if (msg == NULL) {
		msg = interp->result;
	    }
	    fprintf(stderr, "%s\n", msg);
	    goto error;
	}
    }
    if (SkipInit != 0) { /* source the standard init files */
printf("reading initialization files...\n");
    	result = Tcl_VarEval(interp, TCLinitCmd, 0,
		(char *)NULL);
    	if (result != TCL_OK) {
       		char *msg;
       		msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
       		if (msg == NULL) {
           		msg = interp->result;
       		}
       		fprintf(stderr, "Error in included pgtk.init library file \n");
       		goto error;
    	}

    	result = Tcl_VarEval(interp, PGTKinitCmd, 0,
		(char *)NULL);
    	if (result != TCL_OK) {
       		char *msg;
       		msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
       		if (msg == NULL) {
           		msg = interp->result;
       		}
       		fprintf(stderr, "Error in included pgtk.init library file \n");
       		goto error;
    	}

    }

    if (rcfile != NULL) {
	result = Tcl_VarEval(interp, "source ", rcfile, (char *) NULL);
        if (result != TCL_OK) {
           char *msg;

           msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
           if (msg == NULL) {
               msg = interp->result;
           }
           fprintf(stderr, "Error in included rcfile %s \n%s\n",rcfile, msg);
       }
   }
    else {
        rcfile = "~/.PGTKrc";
	result = Tcl_VarEval(interp, "source ", rcfile, (char *) NULL);
	if (result != TCL_OK) {
           char *msg;
           msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
           if (msg == NULL) {
               msg = interp->result;
           }
           fprintf(stderr, "Error in included %s file\n%s\n",rcfile, msg);
           /* goto error; */
	}
    }

    if (file != NULL) {
	result = Tcl_VarEval(interp, "source ", file, (char *) NULL);
	if (result != TCL_OK) {
	    char *msg;

	    msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
	    if (msg == NULL) {
		msg = interp->result;
	    }
	    fprintf(stderr, "%s\n", msg);
	    goto error;
	}
	tty = 0;
    } else {
	tty = isatty(0);
	Tk_CreateFileHandler(0, TK_READABLE|TK_EXCEPTION, StdinProc,
		(ClientData) 0);
	if (tty) {
	    printf("window is %s\n", Tk_Name(w));
	    printf("PGTK>> ");
	}
    }
    fflush(stdout);
    buffer = Tcl_CreateCmdBuf();
    (void) Tcl_Eval(interp, "update", 0, (char **) NULL);

    /*
     * The window could have been deleted as part of the startup
     * script.  If so, don't try to map it.
     */

    if (w != NULL) {
	Tk_MapWindow(w);
    }
    Tk_MainLoop();
    PQfinish();
    exit(0);

error:
    Tcl_Eval(interp, "destroy .", 0, (char **) NULL);
    PQfinish();
    exit(1);
    return 0;
}
