/*
 * invfunc.c --  Author: Ray R. Larson  - version 0.1, March 1993
 *  
 * The functions in this file implement (read-only) access to
 * POSTGRES Inversion large objects via tcl/tk.
 *
 * Copyright 1993 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-fe.h"
#include "tmp/libpq-fs.h"
#include "utils/log.h"

#include <errno.h>

/* Global Declarations
 */

extern char dbnamebuffer[];
extern char *tcldbname;
extern int Debugging;
extern char *debug_file;
extern FILE *debug_port;


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


/*=============== Postgres Inversion Large Object Routines ================*/

/* PGTKlonames - get a list of inversion large object names given          */
/*               an inversion pathname                                     */
int
PGTKlonames(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    int i;
    PDIR *d;
    struct pgdirent *de;
    char buf[1024];

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

   /* the following is pretty much verbatim from the sls test program */
   /* by Mike Olson                                                   */

    if (dbnamebuffer[0] == '\0') {
    	strcpy(dbnamebuffer,getenv("USER"));
   	 if (Debugging) fprintf(debug_port,"database is %s\n", dbnamebuffer);
    }
    PQsetdb(dbnamebuffer);
    tcldbname = Tcl_SetVar(interp, "CURRENTDATABASE", dbnamebuffer,
			TCL_GLOBAL_ONLY);

    (void) PQexec("begin");
    if (p_chdir(argv[1]) < 0) {
	Tcl_AppendResult(interp, argv[0], ": couldn't cd to pathname ",
            argv[1],(char *) NULL);
	return TCL_ERROR;
      }
    d = p_opendir(argv[1]);
    for(de =p_readdir(d); de != NULL;de =p_readdir(d))  {
	struct pgstat st;
	char name[1024];
	int fd, n;
	sprintf(name,"%s",de->d_name);
	if (p_stat(name,&st)) printf ("pstat failed?\n") ;
	if ((st.st_mode & _S_IFMT) == S_IFDIR)
	  sprintf(buf,"{%s %d %s}",name,st.st_size,"<DIR>");
	else
	  sprintf(buf,"{%s %d %o}",name,st.st_size,st.st_mode);

        Tcl_AppendResult(interp, buf, " ", (char *) NULL);

    }
    p_closedir(d);
    (void) PQexec("end");
    return TCL_OK;
}


/* PGTKlocopy - copy inversion large object to a unix file or stdout */
#define	DIR_IN	0
#define	DIR_OUT	1
#define IBUFSIZ	8092

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

    int i;
    int srcfd, destfd;
    char *buf, *lbuf;
    int nread, nwrite, totread, nbytes;
    int done;


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

    if (argc == 2) { /* set default output to stdout */
	destfd = fileno(stdout);

    }

    if (argc == 3) {
	if ((strcmp(argv[2],"stdout")==0) || (strcmp(argv[2],"-")==0)) {
		/* set default output to stdout */
		destfd = fileno(stdout);
	}
        else if ((destfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
	  Tcl_AppendResult(interp, "Could not open output file \"", argv[2],
		"\"", (char *) NULL);
	  return TCL_ERROR;
	}
      }


    (void) PQexec("begin");

    if ((srcfd = p_open(argv[1], INV_READ)) < 0) {
      if((srcfd = p_open(argv[1], O_RDONLY)) < 0) {
	Tcl_AppendResult(interp, "Could not open database large object \"", argv[1],
			 "\"", (char *) NULL);
	(void) PQexec("end");
	return TCL_ERROR;
      }
    }
  
  
    if ((buf = (char *) malloc(IBUFSIZ)) == (char *) NULL) {
	Tcl_AppendResult(interp, "Could not allocate enough (8k) bytes for copy buffer",
		 (char *) NULL);
	return TCL_ERROR;
    }

    done = FALSE;

    while (done==FALSE) {
	lbuf = buf;
	totread = 0;
	nbytes = IBUFSIZ;

	/* read one inversion file system buffer's worth of data */
	while (nbytes > 0) {
	    if ((nread = p_read(srcfd, lbuf, nbytes)) < 0) {
		Tcl_AppendResult(interp, argv[0], ": read failed from inversion file",
		 	(char *) NULL);
		(void) PQexec("end");
		return TCL_ERROR;
	    } else if (nread == 0) {
		nbytes = 0;
		done = TRUE;
	    } else {
		nbytes -= nread;
		totread += nread;
		lbuf += nread;
	    }
	}

	/* write it out to the unix file */
	lbuf = buf;
	while (totread > 0) {
	    if ((nwrite = write(destfd, lbuf, totread)) <= 0) {
		Tcl_AppendResult(interp, argv[0], ": write failed to unix file",
		 	(char *) NULL);
		(void) PQexec("end");
		return TCL_ERROR;
	    }
	    totread -= nwrite;
	    lbuf += nwrite;
	}
    }

    (void) p_close(srcfd);
    if (destfd != fileno(stdout)) close(destfd);
    (void) PQexec("end");
    return TCL_OK;
}



/* PGTKlopwd - get the inversion pathname of the current directory */
int
PGTKlopwd(dummy, interp, argc, argv)
    ClientData dummy;			/* Not used. */
    Tcl_Interp *interp;			/* Current interpreter. */
    int argc;				/* Number of arguments. */
    char **argv;			/* Argument strings. */
{

    char pathname[MAXPATHLEN + 1];
    char *dbname;

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


    if (dbnamebuffer[0] == '\0') {
    	strcpy(dbnamebuffer,getenv("USER"));
   	 if (Debugging) fprintf(debug_port,"database is %s\n", dbnamebuffer);
    }
    PQsetdb(dbnamebuffer);
    tcldbname = Tcl_SetVar(interp, "CURRENTDATABASE", dbnamebuffer,
			TCL_GLOBAL_ONLY);

    (void) PQexec("begin");

    if (p_getwd(pathname) == NULL) {
	Tcl_AppendResult(interp, argv[0], ": couldn't get current pathname",
            pathname,(char *) NULL);
	return TCL_ERROR;
	}

    Tcl_AppendResult(interp, pathname,(char *) NULL);
    (void) PQexec("end");
    return TCL_OK;
}


/* PGTKlocd - change the current inversion directory */

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

    char pathname[MAXPATHLEN + 1];
    char *dbname;

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


    if (dbnamebuffer[0] == '\0') {
    	strcpy(dbnamebuffer,getenv("USER"));
   	 if (Debugging) fprintf(debug_port,"database is %s\n", dbnamebuffer);
    }
    PQsetdb(dbnamebuffer);
    tcldbname = Tcl_SetVar(interp, "CURRENTDATABASE", dbnamebuffer,
			TCL_GLOBAL_ONLY);

    (void) PQexec("begin");

	if (p_chdir(argv[1]) < 0) {
		Tcl_AppendResult(interp, argv[0], ": couldn't cd to pathname ",
        	    argv[1],(char *) NULL);
		return TCL_ERROR;
	}
	p_getwd(pathname);
	setenv("PFCWD",pathname,1);

    Tcl_AppendResult(interp, pathname,(char *) NULL);
    (void) PQexec("end");
    return TCL_OK;
}
