#include "tmp/libpq.h"
#include <stdio.h>

#define LL 1000		/* upper limit on length of a data line */
#define VPL '{'		/* left hand side of a var in template */
#define VPR '}'         /* right hand side of var in template */ 
#define MAX_COL 99      /* max number of columns per data line 
                           if MAX_COL is larger than 99, fix code in 
                           pg_process_data_file to handle more than 2 digits */ 
#define NULL_CHAR '&'


/**********************************************************************
  $Header: /private/postgres/src/contrib/3M/RCS/pg_exec.c,v 1.3 1991/05/15 08:37:17 mao Exp $
 **********************************************************************
 FILE: pg_exec.c
       pg_exec [-c | -d] databasename filename

       Author:  Steve Anastasi, 3M, 612-733-6970
                smanastasi@mmm.serc.3m.com

       

       Date: 6-29-90

       Processes ascii files as postgres commands.  This program takes
       a two arguments - a database name, and a filename or pathname
       of an ascii file.  The database must already exist.
       Two options are available:

         -c  Process the ascii file as a sequence of postgres
	     commands.  This is the default.  See the format of a
	     postgres command file below.

         -d  Process the ascii file as data file.  Data in the file is
	     imported according to a specification contained on the
	     first line of the file.  See the format of a import file
	     below. 

       Command file format:
       --------------------
       Each line is simply a postgres command that can be executed
       directly using PQexec.  Using pg_exec -c <filename> has the
       same effect as using the terminal monitor interface and
       including the filename (\i filename).  

       It is important that each postgres command is entirely
       contained on a single line and the each line is deliminated by
       a newline or return character.

       Import file format:
       -------------------
       An import file is composed of two sections.  The first section
       is the first line of the file.  It specifies the postgres
       command that should be used to add the data to the database.
       This is called the command template.

       The second section is the rest of the file.  Each line is a
       tab-deliminated record containing data.  Special tokens ({num})
       in the command template specify the column of data that should be
       used.  An example follows.

       append somerel (name={0}::text, id={1}, gpa={2}::float4)
       "David Byrne"  154323  "3.00"
       "Phillip E. Bach"      000321  "3.93"
       "Ronald Reagan"        000000  "0.00"
       "Cole Porter"  103423  "2.45"

       Note that each field is tab-delimited and each value must be in
       the proper syntax so it can be dropped right in for its {#}.

       In addition, no null values can be specified in a dataline by
       \tab\tab or any other trick.  For example, if {7} is the
       largest number (7) in the command template, then there must be
       8 values (0,1,2,...,7) on every line of data.
*/


/**********************************************************************
   pqexec_ret

   Looks at first character of the string returned by pqexec.  If
   there is an error, -1 is returned, else 1 is returned.
   */
int pqexec_ret (s)
  char *s;
{
  if (s[0] == 'R')
    return(-1);
  else
    return(1);
}





/**********************************************************************
   Read a text string from an ascii file.  A text string is defined as
   text deliminated by either a tab, newline or return character.
   In the process of reading a string, the file pointer gets advanced.
   Note that spaces do not deliminate strings.
   */

void get_string(fptr, string_buffer)
  FILE *fptr;
  char *string_buffer;
{
  int i;

  i = 0;
  if ((string_buffer[i] = getc(fptr)) != EOF) {
    while (string_buffer[i] != EOF &&
           string_buffer[i] != '\t' &&
           string_buffer[i] != '\n' &&
           string_buffer[i] != '\r') {
      i = i+1;
      string_buffer[i] = getc(fptr);
    }
  }    
  /* Now replace the last character with a null terminater */
  string_buffer[i] = '\0';
} 


/**********************************************************************
   Read a line from a text file.  A line is defined as any character
   string that ends in \n .  Returns a ptr to a null terminated string

   NOT CURRENTLY USED.  USE GET_STRING INSTEAD IF POSSIBLE
   */
   
char * readline(fptr)
  FILE *fptr;
{
  char buffer[LL];
  int i;

  i = 0;
  buffer[i] = getc(fptr);
  while (buffer[i] != EOF && buffer[i] != '\n')
    buffer[++i] = getc(fptr);
  buffer[i] = '\0';
  return(buffer);
}

  
/*********************************************************************
   pg_process_data_file 

   Reads formatted data from a ascii file and inserts the data into
   a postgres relation.  One file can contain data for only one
   relation.  

   File Format:
   The first line is a template to be used to specify how the data
   will get append to a relation.  The token VPL and VPR  indicates a place
   to drop in data from the data file.  In each VP is a number
   indicating what field from a data line to use.  (See top of file
   for definition of VPL and VPR.)

   Example:
   A datafile might look like:

   append parameter (name={0}::text, age={1}, weight={2}::float4)
   Steve Anastasi 	26 	190.04
   Saavik Stargazer 	1 	67.30
   etc.,
 
   Note that each field is tab delimited and each line ends in a newline
   or return character.

   Limitations:  At most, MAX_COL data columns can be handled
                 Every column in the data file must have a value 
   */
 

void pg_process_data_file(fptr)
  FILE *fptr;

{
  char header[LL], command_string[LL*2], data[LL], tc;
  char var_num[3], tk[MAX_COL][LL];
  int i, h_ctr, c_ctr, token_count, var_val, EOF_REACHED;

  EOF_REACHED = 0;
  get_string(fptr, header);
  /* Find out how many data fields should be on each data line.  This
     is done by finding the maximum number enclosed in VPL and VPR. 
     The number of data fields is this number plus 1 (since counting
     starts at 0. */
  token_count = 0;
  i = 0;
  while (header[i] != '\0')  {
    if (header[i] == VPL) {
      /* see if there is one or two digits after VPL */
      if (header[i+2] == VPR) {  /* one digit */
        var_num[0] = '0';
        var_num[1] = header[i+1];
        var_num[2] = '\0';
      }
      else if (header[i+3] == VPR) { /* 2 digits */
        var_num[0] = header[i+1];
        var_num[1] = header[i+2];
        var_num[2] = '\0';
      }
      else { /* problem */
        printf("Error in data file.  Unknown {  } syntax\n");
        abort();
      }
      sscanf(var_num, "%d", &var_val);
      if (var_val > token_count)
        token_count = var_val;
    }
    i = i+1;
  }

  /* Iterate thru all data lines */
  while (!EOF_REACHED) {
    /* Now get as many strings from the data file as specified by
       token count */
    for (i=0;i<=token_count;i++) {
      get_string(fptr, tk[i]);
      if (tk[i][0] == '\0') 
	EOF_REACHED = 1;
    }

    /* Now start to copy header into command_string, and replace
       {x} with tk[x] */
    h_ctr = 0;
    c_ctr = 0;
    while (header[h_ctr] != '\0' && c_ctr < LL*2 && !EOF_REACHED) {
      /* printf("H[%d]: %c\nC: %s\n",h_ctr,header[h_ctr],command_string); */
      if (header[h_ctr] == VPL) {
        /* see if there is one or two digits after VPL */
        if (header[h_ctr+2] == VPR) {  /* one digit */
          var_num[0] = '0';
          var_num[1] = header[h_ctr+1];
          var_num[2] = '\0';
          h_ctr = h_ctr + 3;  /* advance to after VPR */
        }
        else if (header[h_ctr+3] == VPR) { /* 2 digits */
          var_num[0] = header[h_ctr+1];
          var_num[1] = header[h_ctr+2];
          var_num[2] = '\0';
          h_ctr = h_ctr + 4;  /* advance to after VPR */
        }
        else { /* problem */
          printf("Error in data file.  Unknown {  } syntax\n");
          abort();
        }
        sscanf(var_num, "%d", &var_val);
  
      /* now splice in tk[var_val] to command_string */
        i = 0;
        while (*(tk[var_val]+i) != '\0') {
          command_string[c_ctr] = *(tk[var_val]+i);
          i = i+1;
          c_ctr = c_ctr+1;
        }
      } /* header[h_ctr] == VPL */
      else { /* just copy from header to command string */
        command_string[c_ctr] = header[h_ctr];
        h_ctr = h_ctr + 1;
        c_ctr = c_ctr + 1;
      }
    }
    if (!EOF_REACHED) {
      /* Now execute the query */
      command_string[c_ctr] = '\0';
      printf("Exec: %s\n", command_string); 
      if (pqexec_ret(PQexec(command_string)) < 0)
        printf("*Error* %s\n", command_string);
    }
  } /* end while not EOF_REACHED */
  printf("\n");
}    
  

/*******************************************************************
  pg_process_command_file

  Reads a ascii file of postgres commands and executes them.  Each
  command must be on a single line.
  */

void pg_process_command_file(fptr)
  FILE *fptr;

{
  char command[255];
  int EOF_REACHED;

  EOF_REACHED = 0;

  while (!EOF_REACHED) {
    get_string(fptr, command);
    if (command[0] == '\0') 
      EOF_REACHED = 1;
    else {
      printf("Exec: %s\n", command); 
      if (pqexec_ret(PQexec(command)) < 0)
        printf("*Error* %s\n", command);
    }
  }
  printf("\n");
}

  
  
/******************************************************************* */
main (argc, argv)
  int argc;
  char *argv[];

{
  FILE *fopen(), *fptr;
  char db_name[16],filename[80], *s, option;
  int place, j, error;  /* error: -1= bad command line, -2=bad file, -3=bad */
		 /* db name */

  error = 0;
  /* check for bad command line */
  if (argc < 3)
    error = -1;
  else if (argc==3 && (argv[1][0] == '-' || argv[2][0] == '-'))
    error = -1;
  else if (argc==4 && (argv[1][0] != '-' || argv[2][0] == '-' ||
		      argv[3][0] == '-'))
    error = -1;
  else if (argc > 4)
    error = -1;

  if (error == -1) {
    printf("Usage: pg_exec [-c | -d] dbname filename\n");
    exit(-1);
  }

  /* get the option */
  if (argv[1][0] == '-')
    option = argv[1][1];
  else
    option = 'c';   /* the default */

  /* get the database */
  if (argv[1][0] == '-')
    place = 2;
  else
    place = 1;

  j = 0;
  while (argv[place][j] != '\0' && j < 16) {

    db_name[j] = argv[place][j];
    j = j + 1;
  }

  j =0;
  /* get file name */
  place = place +1;
  while (argv[place][j] != '\0' && j < 80) {
    filename[j] = argv[place][j];
    j = j + 1;
  }

  
  /* Now do the right thing */

  PQsetdb(db_name); 

  printf("*Database: %s\n", PQdb());

  fptr = fopen(filename, "r");;
  if (fptr == (FILE *) NULL) {
    printf("Had problem opening %s.\n", filename);
    exit(-1);
  }

  if (option == 'c')
    pg_process_command_file(fptr);
  else if (option == 'd')
    pg_process_data_file(fptr);

  close (fptr);
}


