/* #define DEBUG  */
/* #define DEBUG2 */
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include "sqlca.h"


#define SQL_NO_DATA_FOUND                                    1403
#define SQL_FETCH_OUT_OF_SEQUENCE                           -1002
#define SQL_ORACLE_INITIALIZATION_OR_SHUTDOWN_IN_PROGRESS   -1033
#define SQL_ORACLE_NOT_AVAILABLE                            -1034
#define SQL_UNABLE_TO_EXTEND_INDEX                          -1654
#define SQL_UNIQUE_CONSTRAINT_VIOLATED                      -1
#define SQL_NOT_CONNECTED_TO_ORACLE                         -3114
#define SQL_END_OF_FILE_ON_COMMUNICATION_CHANNEL            -3113
#define GO      0
#define NOGO    1



int sql_error();
int unpad();
char *field();
char *hhmmss();
void signal_handler();
char *right_trim();
char *string_cat();



EXEC SQL BEGIN DECLARE SECTION;
#define UNAME_LEN   20
#define PWD_LEN     40
VARCHAR username[UNAME_LEN];  /* VARCHAR is an Oracle-supplied struct */
varchar password[PWD_LEN];    /* varchar can be in lower case also. */
VARCHAR dynamic_statement[2000];
char    owner_name[30];
char    table_name[30];
char    table_column_name[50][30];  /* Up to 50 columns per table */
char    table_data_type[50][30];    /* Up to 50 columns per table */
char    table_data_int[50];         /* Data type represented as integer */
char    table_nullable[50][5];      /* Up to 50 columns per table */
int     number_of_columns_in_table;

char    index_name[10][30];         /* Up to 10 unique indexes per table */
char    index_column_name[50][30];  /* Up to 50 columns per index */
int     index_column_position[50];  /* Up to 50 columns per index */
int     number_of_columns_in_index;
int     column_number_within_index[50];
char    row_count[80];
EXEC SQL END DECLARE SECTION;



/******************************************************************************/
/*                                                                            */
/* Bit values of exit_code, OR'ed together:                                   */
/*                                                                            */
/*   1 = Access to file                                                       */
/*   2 = Header record                                                        */
/*   4 = Column or index error                                                */
/*   8 = Error in input data                                                  */
/*  16 = Error in database data                                               */
/*  32 = Trailer record                                                       */
/*  64 = Oracle error                                                         */
/* 128 = Unexpected Interruption                                              */
/*                                                                            */
/******************************************************************************/
static exit_code = 0;



main(argc,argv)
int argc;
char *argv[];
{
    FILE *in;
    char buffer[4098];
    char *result;
    char *pointer;
    char *left_pointer;
    char *right_pointer;
    int  action;
    int  count;
    int  i;
    int  j;
    int  k;
    int  x;
    int  length;
    int  go_nogo_flag = GO;
    int  rows_added = 0;
    int  rows_deleted = 0;
    int  rows_not_committed = 0;
    int  commitment_threshold = 2000;
    unsigned long final_row_count_specified;
    unsigned long actual_final_row_count;



    fprintf(stdout,
    "%s START %s, VERSION OF WEDNESDAY, MARCH 20, 2002, COMPILED ON %s AT %s\n",
            hhmmss(), argv[0], __DATE__, __TIME__);
    fflush(stdout);



    /**************************************************************************/
    /*                                                                        */
    /*  Tell UNIX to have our signal handler handle all interruptions.        */
    /*                                                                        */
    /**************************************************************************/
    for (i=0;i<40;i++)
    {
        signal(i,signal_handler);
    }



    /**************************************************************************/
    /*                                                                        */
    /*  Tell the Oracle Precompiler to insert a check for sqlcode after       */
    /*  every interface to Oracle and call sql_error() if sqlcode is          */
    /*  not zero.                                                             */
    /*                                                                        */
    /**************************************************************************/
    EXEC SQL WHENEVER SQLERROR DO sql_error(__FILE__,__LINE__);



    /**************************************************************************/
    /*                                                                        */
    /*  Establish initial connection to the Oracle Database Engine.           */
    /*                                                                        */
    /**************************************************************************/
    strncpy((char *)username.arr, "username goes here", UNAME_LEN);
    username.len = strlen((char *) username.arr);
    strncpy((char *)password.arr, "password goes here", PWD_LEN);
    password.len = strlen((char *) password.arr);
    EXEC SQL CONNECT :username IDENTIFIED BY :password;
    if (sqlca.sqlcode == 0)
    {
        fprintf(stdout,"%s CONNECTED TO ORACLE AS USER %s\n",hhmmss(),username.arr);
        fflush(stdout);
    }
    else
    {
        sql_error(__FILE__,__LINE__);
/*
        exit(253);
*/
    }



    /**************************************************************************/
    /*                                                                        */
    /*  Process all files specified in the argument list.                     */
    /*                                                                        */
    /**************************************************************************/
    for (i=1;i<argc;i++)
    {
        go_nogo_flag = GO;



        /**********************************************************************/
        /*                                                                    */
        /*  Get Owner Name                                                    */
        /*                                                                    */
        /**********************************************************************/
        strcpy(buffer,argv[i]);
        right_pointer = strchr(buffer,'.');
        if (right_pointer)
        {
            /******************************************************************/
            /*                                                                */
            /*  Get Table Name                                                */
            /*                                                                */
            /******************************************************************/
            *right_pointer = 0;
            strcpy(owner_name,buffer);
            left_pointer = right_pointer + 1;
            right_pointer = strchr(left_pointer,'.');
            if (right_pointer)
            {
                *right_pointer = 0;
                strcpy(table_name,left_pointer);
            }
            else
            {
                fprintf(stdout,
                        "%s ERROR: SECOND PERIOD NOT FOUND IN ARGUMENT[%d]: %s\n",
                        hhmmss(), i, buffer);
                fflush(stdout);
                exit_code |= 1; /* Error in file name */
                continue;
            }
        }
        else
        {
            fprintf(stdout,
                    "%s ERROR: FIRST PERIOD NOT FOUND IN ARGUMENT[%d]: %s\n",
                    hhmmss(), i, buffer);
            fflush(stdout);
            exit_code |= 1; /* Error in file name */
            continue;
        }




        /**********************************************************************/
        /*                                                                    */
        /*  Open Data Input File                                              */
        /*                                                                    */
        /**********************************************************************/
        in = fopen(argv[i],"r");
        if (!in)
        {
            fprintf(stdout,"%s ERROR %d TRYING TO OPEN %s: %s\n",
                    hhmmss(), errno, argv[i], strerror(errno));
            fflush(stdout);
            exit_code |= 1;
            continue;
        }



        /**********************************************************************/
        /*                                                                    */
        /*  Read header record in order to                                    */
        /*    A. verify if DELTA                                              */
        /*    B. get table name,                                              */
        /*    C. get final row count.                                         */
        /*                                                                    */
        /**********************************************************************/
        result = fgets(buffer,sizeof(buffer),in);
        if (result)
        {
            fprintf(stdout,"%s START PROCESSING FILE %s\n",
                    hhmmss(), argv[i]);
            fflush(stdout);
        }
        else
        {
            fprintf(stdout,"%s ERROR TRYING TO READ HEADER RECORD FROM %s\n",
                    hhmmss(), argv[i]);
            fflush(stdout);
            fclose(in);
            continue;
        }



        if (memcmp(buffer,"$~DELTA~",8) == 0)
        {
#ifdef DEBUG
            fprintf(stdout,"%s WE HAVE A DELTA\n", hhmmss());
            fflush(stdout);
#endif
        }
        else
        {
            fprintf(stdout,"%s ERROR: HEADER RECORD DOES NOT CONTAIN DELTA\n",
                    hhmmss());
            fprintf(stdout,"%s ERROR: %s", hhmmss(), buffer);
            fflush(stdout);
            fclose(in);
            exit_code |= 2;
            continue;
        }



        pointer = field(buffer,8);
        if (pointer)
        {
            sscanf(pointer,"%ld",&final_row_count_specified);
            fprintf(stdout,"%s FINAL ROW COUNT SHOULD BE %ld\n",
                    hhmmss(), final_row_count_specified);
        }
        else
        {
            fprintf(stdout,"%s ERROR: HEADER RECORD DOES NOT CONTAIN FINAL ROW COUNT\n",
                    hhmmss());
            fprintf(stdout,"%s ERROR: %s", hhmmss(), buffer);
            fflush(stdout);
            fclose(in);
            exit_code |= 2;
            continue;
        }




        if (memcmp(owner_name,"REF_",4) != 0)
        {
            fprintf(stdout,"%s ERROR: OWNER NAME %s DOES NOT BEGIN WITH REF_\n",
                    hhmmss(), owner_name);
            fflush(stdout);
            fclose(in);
            exit_code |= 2; /* Incorrect table name */
            continue;
        }



        pointer = strchr(buffer,'.');
        if (pointer)
        {
            if (memcmp(pointer+5,&table_name[4],4))
            {
                fprintf(stdout,"%s ERROR: HEADER TABLE NAME %4.4s DOES NOT MATCH FILE NAME %4.4s\n",
                    hhmmss(), pointer, table_name);
                fclose(in);
                exit_code |= 2; /* Incorrect table name */
                continue;
            }
        }



        if (memcmp(table_name,"REF_",4) != 0)
        {
            fprintf(stdout,"%s ERROR: TABLE NAME %s DOES NOT BEGIN WITH REF_\n",
                    hhmmss(), table_name);
            fflush(stdout);
            fclose(in);
            exit_code |= 2; /* Incorrect table name */
            continue;
        }

        fprintf(stdout,"%s START PROCESSING OWNER %s, TABLE %s\n",
                hhmmss(), owner_name, table_name);
        fflush(stdout);



        /**********************************************************************/
        /*                                                                    */
        /*  Get the unique index(es) for table_name within database_name      */
        /*  and respond if there are no unique indexes.                       */
        /*                                                                    */
        /**********************************************************************/
        sprintf((char *)dynamic_statement.arr,
"select index_name from all_indexes where UNIQUENESS='UNIQUE' and owner='%s' and table_name='%s'",
            owner_name, table_name);
        dynamic_statement.len = (unsigned short)strlen((char *)dynamic_statement.arr);
#ifdef DEBUG
        fprintf(stdout,"%s EXEC SQL %d %s\n",
                hhmmss(), dynamic_statement.len, dynamic_statement.arr);
        fflush(stdout);
#endif
        EXEC SQL PREPARE get_index_names FROM :dynamic_statement;
        EXEC SQL DECLARE index_name_cursor CURSOR for get_index_names;
        EXEC SQL OPEN index_name_cursor;
        for (j=0;;j++)
        {
            EXEC SQL FETCH index_name_cursor INTO :index_name[j];
            if (sqlca.sqlcode == SQL_NO_DATA_FOUND) break;
            if (sqlca.sqlcode) sql_error(__FILE__,__LINE__);
            unpad(index_name[j],sizeof(index_name[j]));
#ifdef DEBUG
            fprintf(stdout,"%s UNIQUE INDEX NAME[%d] = %s\n",
                    hhmmss(), j, index_name[j]);
            fflush(stdout);
#endif
        }
        if (j == 0)
        {
            fprintf(stdout,"%s ERROR: TABLE %s HAS NO UNIQUE INDEX\n",
                    hhmmss(), table_name);
            fflush(stdout);
            fclose(in);
            exit_code |= 4; /* No unique index */
            continue;
        }



        /**********************************************************************/
        /*                                                                    */
        /*  Now that we have at least one (1) unique index, get the           */
        /*  column names and column positions of the first unique index.      */
        /*  Why first?  Because any unique index will do.                     */
        /*                                                                    */
        /**********************************************************************/
        sprintf((char *)dynamic_statement.arr,
"select column_name,column_position from all_ind_columns where index_owner='%s' and index_name='%s'",
            owner_name, index_name[0]);
        dynamic_statement.len = (unsigned short)strlen((char *)dynamic_statement.arr);
#ifdef DEBUG
        fprintf(stdout,"%s EXEC SQL %d %s\n",
                hhmmss(), dynamic_statement.len, dynamic_statement.arr);
        fflush(stdout);
#endif
        EXEC SQL PREPARE get_index FROM :dynamic_statement;
        EXEC SQL DECLARE index_cursor CURSOR for get_index; 
        EXEC SQL OPEN index_cursor;
        for (j=0;;j++)
        {
            EXEC SQL FETCH index_cursor INTO :index_column_name[j], :index_column_position[j];
            if (sqlca.sqlcode == SQL_NO_DATA_FOUND) break;
            if (sqlca.sqlcode) sql_error(__FILE__,__LINE__);
            unpad(index_column_name[j],sizeof(index_column_name[j]));
#ifdef DEBUG
            fprintf(stdout,"%s INDEX_COLUMN_NAME[%d]=%s, INDEX_COLUMN_POSITION[%d]=%d\n",
                    hhmmss(), j, index_column_name[j], j, index_column_position[j]);
            fflush(stdout);
#endif
        }
        EXEC SQL CLOSE index_cursor;
        number_of_columns_in_index = j;
        if (number_of_columns_in_index <= 0)
        {
            fprintf(stdout,"%s ERROR: TABLE %s HAS NO INDEX\n",
                    hhmmss(), table_name);
            fflush(stdout);
            fclose(in);
            exit_code |= 4; /* No columns in index */
            continue;
        }



        /**********************************************************************/
        /*                                                                    */
        /*  Get the column names within this table.                           */
        /*                                                                    */
        /**********************************************************************/
        sprintf((char *)dynamic_statement.arr,
        "select column_name,data_type,nullable from all_tab_columns where owner='%s' and table_name='%s' order by column_id",
            owner_name, table_name);
        dynamic_statement.len = (unsigned short)strlen((char *)dynamic_statement.arr);
#ifdef DEBUG
        fprintf(stdout,"%s EXEC SQL %d %s\n",
                hhmmss(), dynamic_statement.len, dynamic_statement.arr);
        fflush(stdout);
#endif
        EXEC SQL PREPARE get_columns FROM :dynamic_statement;
        EXEC SQL DECLARE column_cursor CURSOR for get_columns;
        EXEC SQL OPEN column_cursor;
        for (j=0;;j++)
        {
            EXEC SQL FETCH column_cursor INTO :table_column_name[j],:table_data_type[j],:table_nullable[j];
            if (sqlca.sqlcode == SQL_NO_DATA_FOUND) break;
            if (sqlca.sqlcode) sql_error(__FILE__,__LINE__);
            unpad(table_column_name[j],sizeof(table_column_name[j]));
            unpad(table_data_type[j],sizeof(table_data_type[j]));
            unpad(table_nullable[j],sizeof(table_nullable[j]));
            if (strcmp(table_data_type[j],"VARCHAR2")==0)table_data_int[j]=1;
            else if (strcmp(table_data_type[j],"NUMBER")==0)table_data_int[j]=2;
            else if (strcmp(table_data_type[j],"DATE")==0)
            {
                length = strlen(table_column_name[j]);
                if (memcmp(&table_column_name[j][length-3],"_TM",3)==0)
                {
                    table_data_int[j]=4;
                }
                else table_data_int[j]=3;
            }
            else table_data_int[j]=9;


            /******************************************************************/
            /*                                                                */
            /*  table_allow_0 is really table_nullable because bad_words.sh   */
            /*  interprets "null" as a bad word.                              */
            /*                                                                */
            /******************************************************************/
#ifdef DEBUG
            fprintf(stdout,"%s COLUMN_NAME[%d]=%s, DATA_TYPE[%d]=%s, ALLOW_0[%d]=%s, DATA_INT[%d]=%d\n",
                    hhmmss(),
                    j, table_column_name[j],
                    j, table_data_type[j],
                    j, table_nullable[j],
                    j, table_data_int[j]);
            fflush(stdout);
#endif

        }
        EXEC SQL CLOSE column_cursor;
        number_of_columns_in_table = j;
        if (number_of_columns_in_table <= 0)
        {
            fprintf(stdout,"%s ERROR: TABLE %s HAS NO COLUMNS\n",
                    hhmmss(), table_name);
            fflush(stdout);
            fclose(in);
            exit_code |= 4; /* No columns in table */
            continue;
        }



        /**********************************************************************/
        /*                                                                    */
        /*  Correspond each of the column names in the index with the         */
        /*  position of that column in the table.                             */
        /*                                                                    */
        /**********************************************************************/
        for(j=0;j<number_of_columns_in_index;j++)
        {
            for(k=0;k<number_of_columns_in_table;k++)
            {
                if (strcmp(index_column_name[j],table_column_name[k]) == 0)
                {
#ifdef DEBUG
                    fprintf(stdout,"%s INDEX COLUMN %d, %s, IS TABLE COLUMN %d\n",
                            hhmmss(), j+1, index_column_name[j], k+1);
                    fflush(stdout);
#endif
                    break;
                }
            }
            if (k == number_of_columns_in_table)
            {
                fprintf(stdout,
                        "%s LOGIC ERROR: INDEX COLUMN NAME %s NOT FOUND IN TABLE COLUMN NAMES\n",
                        hhmmss(), index_column_name[j]);
                fflush(stdout);
                column_number_within_index[j] = -1;
                exit_code |= 4; /* Columns in index not matched with columns in table */
                go_nogo_flag = NOGO;
            }
            else
            {
                column_number_within_index[j] = k;
            }
        }



        /**********************************************************************/
        /*                                                                    */
        /*  If fatal errors have been encountered with this file, go to the   */
        /*  next one, if any.                                                 */
        /*                                                                    */
        /**********************************************************************/
        if (go_nogo_flag == NOGO)
        {
            fprintf(stdout,
                "%s FATAL ERROR(S) ENCOUNTERED; DISCONTINUE FURTHER PROCESSING OF FILE %s\n",
                hhmmss(), argv[i]);
            fflush(stdout);
            fclose(in);
            continue;
        }



        /**********************************************************************/
        /*                                                                    */
        /*  Read each subsequent record and add or delete as necessaary.      */
        /*                                                                    */
        /**********************************************************************/
        rows_added         = 0;
        rows_deleted       = 0;
        rows_not_committed = 0;
        while (fgets(buffer,sizeof(buffer),in))
        {
            if (memcmp(buffer,"A~",2) == 0)
            {
                /**************************************************************/
                /*                                                            */
                /*  Attempt to add (insert) record from table in database     */
                /*  and respond if already there.                             */
                /*                                                            */
                /**************************************************************/
#ifdef DEBUG2
                fprintf(stdout,"%s ADD RECORD %-50.50s\n", hhmmss(), buffer);
                fflush(stdout);
#endif
                sprintf((char *)dynamic_statement.arr,
                        "INSERT INTO %s.%s (", owner_name, table_name);
                for(j=0;j<number_of_columns_in_table;j++)
                {
                    strcat((char *)dynamic_statement.arr,table_column_name[j]);
                    if (j < number_of_columns_in_table - 1)
                    {
                        strcat((char *)dynamic_statement.arr,", ");
                    }
                    else
                    {
                        strcat((char *)dynamic_statement.arr,") ");
                    }
                }
                strcat((char *)dynamic_statement.arr,"VALUES (");
                for(j=0;j<number_of_columns_in_table;j++)
                {
                    pointer = field(&buffer[2],j+1);
                    if (pointer)
                    {
                        if (table_data_int[j] == 3)
                        {
                            if (memcmp(pointer,"        ",8) == 0)
                            {
                                strcat((char *)dynamic_statement.arr,"to_date(NULL, 'YYYY-MM-DD')");
                            }
                            else
                            {
                                strcat((char *)dynamic_statement.arr,"to_date('");
                                strcat((char *)dynamic_statement.arr,pointer);
                                strcat((char *)dynamic_statement.arr,"', 'YYYY-MM-DD')");
                            }
                        }
                        else if (table_data_int[j] == 4)
                        {
                            strcat((char *)dynamic_statement.arr,"to_date('");
                            strcat((char *)dynamic_statement.arr,pointer);
                            strcat((char *)dynamic_statement.arr,"', 'HH24.MI.SS')");
                        }
                        else
                        {
                            right_trim(pointer,table_nullable[j]);
                            if (*pointer)
                            {
                                strcat((char *)dynamic_statement.arr,"'");
                                string_cat((char *)dynamic_statement.arr,pointer);
                                strcat((char *)dynamic_statement.arr,"'");
                            }
                            else
                            {
                                strcat((char *)dynamic_statement.arr,"NULL");
                            }
                        }
                        if (j < number_of_columns_in_table - 1)
                        {
                            strcat((char *)dynamic_statement.arr,", ");
                        }
                        else
                        {
                            strcat((char *)dynamic_statement.arr,")");
                        }
                    }
                    else
                    {
                        fprintf(stdout,"%s ERROR: NOT ENOUGH COLUMNS TO ADD ROW:\n",
                                hhmmss());
                        fprintf(stdout,"%s ERROR: %s", hhmmss(), buffer);
                        fflush(stdout);
                        exit_code |= 8; /* Error in input data */
                        break;
                    }
                }
                dynamic_statement.len =
                        (unsigned short)strlen((char *)dynamic_statement.arr);
#ifdef DEBUG2
                fprintf(stdout,"%s EXEC SQL %d %s\n",
                        hhmmss(),
                        dynamic_statement.len, dynamic_statement.arr);
                fflush(stdout);
#endif



                EXEC SQL PREPARE insert_command FROM :dynamic_statement;



                /**************************************************************/
                /*                                                            */
                /*  Execute the insert command.                               */
                /*                                                            */
                /**************************************************************/
                while(1)
                {
                    EXEC SQL EXECUTE insert_command;
                    break;
                }
                if (sqlca.sqlcode == SQL_UNIQUE_CONSTRAINT_VIOLATED)
                {
                    fprintf(stdout,
                            "%s ERROR: DATA IN KEY OF RECORD TO BE ADDED IS ALREADY IN TABLE %s.%s\n",
                            hhmmss(), owner_name, table_name);
                    fprintf(stdout,"%s ERROR: %s", hhmmss(), buffer);
                    dynamic_statement.arr[dynamic_statement.len] = 0;
                    fprintf(stdout,"%s ERROR: %s\n", hhmmss(), dynamic_statement.arr);
                    fflush(stdout);
                    exit_code |= 16;    /* Error in database data */
                }
                else if (sqlca.sqlcode)
                {
                    action = sql_error(__FILE__,__LINE__);
                    fprintf(stdout,"%s ERROR: %s", hhmmss(), buffer);
                    if (action == 1)
                    {
                        fprintf(stdout,
                        "%s FATAL ORACLE ERROR %d PROCESSING FILE %s, TABLE %s, TRYING TO ADD %s\n",
                            hhmmss(), sqlca.sqlcode, argv[i], table_name, buffer);
                        fflush(stdout);
                        fclose(in);
                        continue;
                    }
                }
                else
                {
                    rows_added++;
                    rows_not_committed++;
                }
            }
            else if (memcmp(buffer,"D~",2) == 0)
            {
                /**************************************************************/
                /*                                                            */
                /*  Attempt to delete record from table in database and       */
                /*  respond if not there.                                     */
                /*                                                            */
                /**************************************************************/
#ifdef DEBUG2
                fprintf(stdout,"%s DELETE RECORD %-50.50s\n",
                        hhmmss(), buffer);
                fflush(stdout);
#endif
                sprintf((char *)dynamic_statement.arr,
                        "DELETE FROM %s.%s WHERE", owner_name, table_name);
                for(j=0;j<number_of_columns_in_index;j++)
                {
                    if (j > 0)
                    {
                        strcat((char *)dynamic_statement.arr," AND");
                    }
                    strcat((char *)dynamic_statement.arr," ");
                    strcat((char *)dynamic_statement.arr,index_column_name[j]);
                    pointer = field(&buffer[2],column_number_within_index[j]+1);
                    if (pointer)
                    {
                        /******************************************************/
                        /*                                                    */
                        /*  We do not need to check to see if the data is     */
                        /*  NULL, as we did for adding a DATE column,         */
                        /*  because a column of an index must never be NULL.  */
                        /*                                                    */
                        /******************************************************/
                        if (table_data_int[column_number_within_index[j]] == 3)
                        {
                            strcat((char *)dynamic_statement.arr,"=to_date('");
                            strcat((char *)dynamic_statement.arr,pointer);
                            strcat((char *)dynamic_statement.arr,"', 'YYYY-MM-DD')");
                        }
                        else if (table_data_int[column_number_within_index[j]] == 4)
                        {
                            strcat((char *)dynamic_statement.arr,"=to_date('");
                            strcat((char *)dynamic_statement.arr,pointer);
                            strcat((char *)dynamic_statement.arr,"', 'HH24.MI.SS')");
                        }
                        else
                        {
                            right_trim(pointer,table_nullable[column_number_within_index[j]]);
                            if (*pointer)
                            {
                                strcat((char *)dynamic_statement.arr,"='");
                                string_cat((char *)dynamic_statement.arr,pointer);
                                strcat((char *)dynamic_statement.arr,"'");
                            }
                            else
                            {
                                strcat((char *)dynamic_statement.arr," IS NULL");
                            }
                        }
                    }
                    else
                    {
                        fprintf(stdout,"%s ERROR: NOT ENOUGH KEY DATA TO DELETE ROW:\n",
                                hhmmss());
                        fprintf(stdout,"%s ERROR: %s",
                                hhmmss(), buffer);
                        fflush(stdout);
                        exit_code |= 8; /* Error in input data */
                        break;
                    }
                }
                dynamic_statement.len =
                        (unsigned short)strlen((char *)dynamic_statement.arr);
#ifdef DEBUG2
                fprintf(stdout,"%s EXEC SQL %d %s\n",
                        hhmmss(),
                        dynamic_statement.len, dynamic_statement.arr);
                fflush(stdout);
#endif

                EXEC SQL PREPARE delete_command FROM :dynamic_statement;



                /**************************************************************/
                /*                                                            */
                /*  Execute the delete command.  I have not, yet, figured out */
                /*  how to prevent the Pro C precompiler from inserting a     */
                /*  break statement when the data is not found.  That is why  */
                /*  I have a loop with a break;                               */
                /*                                                            */
                /**************************************************************/
                while(1)
                {
                    EXEC SQL EXECUTE delete_command;
                    break;
                }
                if (sqlca.sqlcode == SQL_NO_DATA_FOUND)
                {
                    fprintf(stdout,"%s ERROR: RECORD TO BE DELETED NOT IN TABLE %s.%s\n",
                            hhmmss(), owner_name, table_name);
                    fprintf(stdout,"%s ERROR: %s", hhmmss(), buffer);
                            fflush(stdout);
                    dynamic_statement.arr[dynamic_statement.len] = 0;
                    fprintf(stdout,"%s ERROR: %s\n", hhmmss(), dynamic_statement.arr);
                    fflush(stdout);
                    exit_code |= 16;    /* Error in database data */
                }
                else if (sqlca.sqlcode)
                {
                    action = sql_error(__FILE__,__LINE__);
                    fprintf(stdout,"%s ERROR: %s", hhmmss(), buffer);
                    if (action == 1)
                    {
                        fprintf(stdout,
                        "%s FATAL ORACLE ERROR %d PROCESSING FILE %s, TABLE %s, TRYING TO DELETE %s\n",
                            hhmmss(), sqlca.sqlcode, argv[i], table_name, buffer);
                        fflush(stdout);
                        fclose(in);
                        continue;
                    }
                }
                else
                {
                    rows_deleted++;
                    rows_not_committed++;
                }
            }
            else if (memcmp(buffer,"$~TRAIL~",8) == 0)
            {
                /**************************************************************/
                /*                                                            */
                /*  Commit remaining rows.                                    */
                /*                                                            */
                /**************************************************************/
                fprintf(stdout,"%s %4d ROWS WILL NOW BE COMMITTED TO TABLE %s.%s\n",
                        hhmmss(), rows_not_committed, owner_name, table_name);
                fflush(stdout);
                EXEC SQL COMMIT WORK;
                if (sqlca.sqlcode) sql_error(__FILE__,__LINE__);

                fprintf(stdout, "%s TRAILER RECORD ENCOUNTERED\n",
                        hhmmss());



                /**************************************************************/
                /*                                                            */
                /*  Verify add row counts.                                    */
                /*                                                            */
                /**************************************************************/
                pointer = field(buffer,5);
                if (pointer)
                {
                    x = atoi(pointer);
                    if (x == rows_added)
                    {
                        fprintf(stdout, "%s ADD ROW COUNTS MATCH %d = %d\n",
                                hhmmss(), x, rows_added);
                    }
                    else
                    {
                        fprintf(stdout,
                        "%s ERROR: ADD ROW COUNTS DO NOT MATCH: SPECIFIED = %d, ACTUAL = %d\n",
                            hhmmss(), x, rows_added);
                        exit_code |= 32;
                    }
                }
                else
                {
                    fprintf(stdout,
                            "%s ERROR: TRAILER RECORD DOES NOT CONTAIN ADD ROW COUNT\n",
                            hhmmss());
                    fprintf(stdout, "%s ERROR: %s\n", hhmmss(), buffer);
                    exit_code |= 32;
                }



                /**************************************************************/
                /*                                                            */
                /*  Verify delete row counts.                                 */
                /*                                                            */
                /**************************************************************/
                pointer = field(buffer,7);
                if (pointer)
                {
                    x = atoi(pointer);
                    if (x == rows_deleted)
                    {
                        fprintf(stdout, "%s DELETE ROW COUNTS MATCH %d = %d\n",
                                hhmmss(), x, rows_deleted);
                    }
                    else
                    {
                        fprintf(stdout,
                        "%s ERROR: DELETE ROW COUNTS DO NOT MATCH: SPECIFIED = %d, ACTUAL = %d\n",
                                hhmmss(), x, rows_deleted);
                        exit_code |= 32;
                    }
                }
                else
                {
                    fprintf(stdout,
                            "%s ERROR: TRAILER RECORD DOES NOT CONTAIN DELETE ROW COUNT\n",
                            hhmmss());
                    fprintf(stdout, "%s ERROR: %s\n", hhmmss(), buffer);
                    exit_code |= 32;
                }



                /**************************************************************/
                /*                                                            */
                /*  Verify final row counts.                                  */
                /*                                                            */
                /**************************************************************/
                sprintf((char *)dynamic_statement.arr,
                        "SELECT COUNT (*) FROM %s.%s",
                        owner_name, table_name);
                dynamic_statement.len = (unsigned short)strlen((char *)dynamic_statement.arr);
                EXEC SQL PREPARE get_row_count FROM :dynamic_statement;
                EXEC SQL DECLARE row_count_cursor CURSOR for get_row_count;
                EXEC SQL OPEN row_count_cursor;
                EXEC SQL FETCH row_count_cursor INTO :row_count;
                if (sqlca.sqlcode) sql_error(__FILE__,__LINE__);
                sscanf(row_count,"%ld",&actual_final_row_count);
                if (actual_final_row_count == final_row_count_specified)
                {
                    fprintf(stdout,"%s FINAL ROW COUNTS MATCH %ld = %ld\n",
                            hhmmss(),
                            final_row_count_specified,
                            actual_final_row_count);
                }
                else
                {
                    fprintf(stdout,
                    "%s ERROR: FINAL ROW COUNTS DO NOT MATCH: SPECIFIED = %ld, ACTUAL = %ld\n",
                            hhmmss(),
                            final_row_count_specified,
                            actual_final_row_count);
                    exit_code |= 32;
                }



                fprintf(stdout, "%s DONE PROCESSING FILE %s\n",
                        hhmmss(), argv[i]);
                fflush(stdout);
                fclose(in);
                in = 0;
                break;
            }
            else
            {
                fprintf(stdout,"%s ERROR: DATA RECORD IS NEITHER A~ NOR D~ NOR $~TRAIL~\n",
                        hhmmss());
                fprintf(stdout,"%s ERROR: %s", hhmmss(), buffer);
                fflush(stdout);
                fclose(in);
                exit_code |= 8; /* Error in input data */
                break;
            }



            /******************************************************************/
            /*                                                                */
            /*  Commit every so many records in order to free up buffers.     */
            /*                                                                */
            /******************************************************************/
            if (rows_not_committed >= commitment_threshold)
            {
                fprintf(stdout,"%s %4d ROWS WILL NOW BE COMMITTED TO TABLE %s.%s\n",
                        hhmmss(), rows_not_committed, owner_name, table_name);
                fflush(stdout);
                EXEC SQL COMMIT WORK;
                if (sqlca.sqlcode == 0)
                {
                    rows_not_committed = 0;
                }
                else
                {
                    sql_error(__FILE__,__LINE__);
                }
            }
        }



        /**********************************************************************/
        /*                                                                    */
        /*  Commit whatever records remain, if any.                           */
        /*                                                                    */
        /**********************************************************************/
        EXEC SQL COMMIT WORK;
        if (sqlca.sqlcode) sql_error(__FILE__,__LINE__);



        /**********************************************************************/
        /*                                                                    */
        /*  If we drop through the while(fgets()) loop and the input file is  */
        /*  still open, then we did ***NOT*** encounter a trailer record.     */
        /*                                                                    */
        /**********************************************************************/
        if (in)
        {
            fclose(in);
            fprintf(stdout,
            "%s ERROR: TRAILER RECORD NOT ENCOUNTERED; %d ROWS ADDED, %d ROWS DELETED\n",
                    hhmmss(), rows_added, rows_deleted);
            fprintf(stdout,
                    "%s DONE PROCESSING FILE %s\n",
                    hhmmss(), argv[i]);
            fflush(stdout);
            exit_code |= 32;
        }
    }



    /**************************************************************************/
    /*                                                                        */
    /*  Disconnect from database engine.                                      */
    /*                                                                        */
    /**************************************************************************/
    EXEC SQL COMMIT WORK RELEASE;
    if (sqlca.sqlcode) sql_error(__FILE__,__LINE__);


    fprintf(stdout,"%s END %s\n", hhmmss(), argv[0]);
    fflush(stdout);
    exit(exit_code);
}



int sql_error(file, line)
char *file;
int line;
{
    char temp[512];
    sprintf(temp,"%s %%-10.10s %%4d ERROR: %%5d %%-%d.%ds",
            hhmmss(), sqlca.sqlerrm.sqlerrml,sqlca.sqlerrm.sqlerrml);
    fprintf(stdout, temp, file, line, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
    if (!strchr(sqlca.sqlerrm.sqlerrmc,'\n')) fprintf(stdout,"\n");
    fflush(stdout);


    sprintf(temp,"%s %%-10.10s %%4d ERROR: %%-%d.%ds",
            hhmmss(),
            dynamic_statement.len,
            dynamic_statement.len);
    fprintf(stdout, temp, file, line, dynamic_statement.arr);
    if (!strchr((char *)dynamic_statement.arr,'\n')) fprintf(stdout,"\n");
    fflush(stdout);


    exit_code |= 64;    /* Oracle error */
    switch (sqlca.sqlcode)
    {
        case SQL_UNABLE_TO_EXTEND_INDEX:    return(1);
        case SQL_NOT_CONNECTED_TO_ORACLE:
        case SQL_END_OF_FILE_ON_COMMUNICATION_CHANNEL:
                    fprintf(stdout,"%s CATASTROPHIC ERROR, EXITING ENTIRE PROGRAM\n",
                            hhmmss());
                    fflush(stdout);
                    exit(254);
        default:    return(0);
    }
    exit(255);
}



int unpad(string,length)
char string[];
int  length;
{
    int i;
    for (i=length-1; i >= 0; i--)
    {
        if (string[i] == ' ') string[i] = 0;
    }
    return(0);
}



char *field(string,number)
char *string;
int number;
{
    static char answer[1024];
    char *left_pointer;
    char *right_pointer;
    int i;

    if (number <= 0) return((char *)0);
    right_pointer = string - 1;
    for(i=0;i<number;i++)
    {
        left_pointer = right_pointer + 1;
        right_pointer = strchr(left_pointer,'~');
        if (right_pointer)
        {
            continue;
        }
        else
        {
            return((char *)0);
        }
    }
    *right_pointer = 0;
    strcpy(answer,left_pointer);
    *right_pointer = '~';
    return(answer);
}

char *hhmmss()
{
    time_t time_now;
    struct tm *time_pointer;
    static char time_formatted[10];

    time(&time_now);
    time_pointer = localtime(&time_now);
    sprintf(time_formatted,"%02d:%02d:%02d",
            time_pointer->tm_hour,
            time_pointer->tm_min,
            time_pointer->tm_sec);
    return(time_formatted);
}

void signal_handler(signal_received)
int signal_received;
{
    if (signal_received == 20)
    {
        fprintf(stdout,"%s SIGNAL %d RECEIVED; CONTINUE\n", hhmmss(), signal_received);
        fflush(stdout);
        return;
    }
    else
    {
        fprintf(stdout,"%s SIGNAL %d RECEIVED; EXIT GRACEFULLY\n", hhmmss(), signal_received);
        fflush(stdout);
        exit_code |= 128;
        exit(exit_code);
    }
}

char *right_trim(string,nullable_flag)
char *string;
char *nullable_flag;
{
    int i;

    for(i = strlen(string) - 1; i >= 0; i--)
    {
        if (string[i] != ' ') break;
        string[i] = 0;
    }
    if ((nullable_flag[0] == 'N') && (string[0] == 0))
    {
        string[0] = ' ';
        string[1] = 0;
    }
    return(string);
}



char *string_cat(destination,source)
char *destination;
char *source;
{
    char *here, *there;

    /**************************************************************************/
    /*                                                                        */
    /*  Find end of destination string.                                       */
    /*                                                                        */
    /**************************************************************************/
    there = destination;
    while (*there)
    {
        there++;
    }

    here=source;
    while (*here)
    {
        if (*here == '\'')
        {
            *there = '\'';
            there++;
        }
        *there = *here;
        here++;
        there++;
    }
    *there = 0;
    return(destination);
}