 #ifdef RCSID
static char *RCSid = 
   "$Header: atmoci.c 17-sep-97.10:12:18 cchriste Exp $ ";
#endif /* RCSID */

/* Copyright (c) 1995, 1997 by Oracle Corporation */
/*
   NAME
     atmoci.c - <one-line expansion of the name>
   DESCRIPTION
     <short description of component this file declares/defines>
   PUBLIC FUNCTION(S)
     <list of external functions declared/defined - with one-line descriptions>
   PRIVATE FUNCTION(S)
     <list of static functions defined in .c file - with one-line descriptions>
   RETURNS
     <function return values, for .c file with single function>
   NOTES
     <other useful comments, qualifications, etc.>
   MODIFIED   (MM/DD/YY)
    jmcdonal   08/06/98 -  Convert to oci8 calls
    cchriste   09/17/97 -  fix API_VERSION, error reporting
    gwood      01/11/96 -  Creation
*/
/* Copyright (c) Oracle Corporation 1995, 1997.  All Rights Reserved.  */
/*
   NAME
      atmoci.c -- Trace ATM sample application using OCI interface to access 
      data in Oracle database.
   DESCRIPTION
      Simulates an ATM, allowing balance inquiries, deposits and withdrawals 
*/

#ifndef ORATYPES_ORACLE
# include <oratypes.h>
#endif

#ifndef ORASTDIO
# include <stdio.h>
# define ORASTDIO
#endif

#ifndef ORASTDLIB
# include <stdlib.h>
# define ORASTDLIB
#endif

#ifndef ORASTRING
# include <string.h>
# define ORASTRING
#endif

#ifndef EPC_ORACLE
# include <epc.h>
#endif

#ifndef  OCI_ORACLE
#  include <oci.h>
#endif


/*-----------------------------------------------------------------------------
 Literals
-----------------------------------------------------------------------------*/
#define S_LONG 4                        /* use instead of sizeof(long) */
#define TRUE 1 
#define FALSE 0
#define MAXLINE 80                      /* maximum length of input line */
#define API_VERSION 2
#define FDF_NAME "atm.fdf"              /* name of facility definition file */
#define VENDOR 192216243                /* vendor ID from FDF file */
#define FAC_NUMBER 1                    /* facility ID from FDF file */
#define FAC_VERSION "V1.0"              /* facility version string */
#define MAX_EVENT 9                     /* number of events */
#define BALANCES 1                      /* event numbers */
#define DEPOSIT 2
#define WITHDRAWAL 3
#define TRANSFER 4
#define QUICK_WITHDRAW 5
#define OVERDRAFT 6
#define USER_SESSION 7
#define VALIDATION_OK 8
#define VALIDATION_ERR 9
#define COLLECTION_NAME "sampatm"       /* name of collection file */
#define OCI_ERROR_MAXMSG_SIZE 1024

/*-----------------------------------------------------------------------------
 Macros to simplify input/output
-----------------------------------------------------------------------------*/
#define DISPLAY(text)  if (!FromFile) printf(text)
#define READ(text) if (FromFile) fgets(text, MAXLINE, InFile); else gets(text)

/*-----------------------------------------------------------------------------
 Function declarations
-----------------------------------------------------------------------------*/
void  init_atm(/* int argc, char *argv[] */);
void  end_atm(/* void */);
long  login_user(/* void */);
void  init_user_session(/* void */);
void  end_user_session(/* void */);
long  display_main_menu(/* void */);
void  get_balances(/* long acct_num */);
void  make_deposit(/* long acct_num */);
void  make_withdrawal(/* long acct_num */);
void  make_transfer(/* long acct_num */);
long  make_quick_withdrawal(/* long acct_num */);
long  atm_valid_acct_num(/* long acct_num */);
long  atm_valid_xfer_acct(/* long acct_num */);
float atm_sav_balance(/* long acct_num */);
float atm_chk_balance(/* long acct_num */);
void  atm_update_balance (/* long acct_num, float new_bal, text *sql_stmt */);
void  atm_sav_deposit(/* long acct_num, long amount */);
void  atm_chk_deposit(/* long acct_num, long amount */);
void  atm_sav_withdrawal(/* long acct_num, long amount, float old_bal */);
void  atm_chk_withdrawal(/* long acct_num, long amount, float old_bal */);
void  atm_txn(/* text *sql_stmt */);
void  atm_start_rw(/* void */);
void  atm_start_ro(/* void */);
void  atm_commit(/* void */);
void  atm_rollback(/* void */);
void  report_epc_error(/* char routine[20], char message[80], long status */);

/*-----------------------------------------------------------------------------
 Global declarations
-----------------------------------------------------------------------------*/

OCIBind    *oci_bndap = (OCIBind *)NULL;
OCIBind    *oci_bndbp = (OCIBind *)NULL;
OCIDefine  *oci_defp  = (OCIDefine *)NULL;
OCIEnv     *oci_envhp = (OCIEnv *)NULL;
OCIError   *oci_errhp = (OCIError *)NULL;
OCISvcCtx  *oci_svchp = (OCISvcCtx *)NULL;
OCIStmt    *oci_stmthp = (OCIStmt *)NULL;

FILE  *InFile;                          /* input file */
char  FromFile;                         /* true if reading from input file */

EPCFCTX Facctx = (EPCFCTX) 0;           /* facility context, initialized */

long  *Event_Flags;                     /* Trace event flags set by epc_init */
char  Regist_ID[80];                    /* reusable registration ID text */
long  Session_Handle;                   /* event instance handles */
long  Txn_Handle;



/*-----------------------------------------------------------------------------
 Main
 ----------------------------------------------------------------------------*/

main (argc, argv)
int  argc;
char *argv[];
{
char    user = TRUE;                    /* we're accepting users */
char    session;                        /* we have a valid user */
long    acct_num;                       /* user's account number */
long    choice;                         /* user's menu choice */
long    exit_early;                     /* user finished without picking */
                                        /* exit from menu */



init_atm (argc, argv);

while (user)
{
    acct_num = login_user();            /* returns valid acct_num or 0 */
    if (acct_num == 0)
    {
        session = FALSE;
        user = FALSE;
    }
    else
    {
        session = TRUE;
        init_user_session();            /* start user_session event */
    }

    while (session)
    {
        choice = display_main_menu();
        switch (choice)
        {
        case 1:
            get_balances(acct_num);
            break;

        case 2:
            make_deposit(acct_num);
            break;

        case 3:
            make_withdrawal(acct_num);
            break;

        case 4:
            make_transfer(acct_num);
            break;

        case 5:
            exit_early = make_quick_withdrawal(acct_num);
            if (exit_early) 
                { session = FALSE; }   /* exit after good quick withdraw */
            break;

        case 0:
            session = FALSE;
            break;

        default:
            DISPLAY("\nInvalid choice.\n");
            break;
        }                               /* end of switch */
    }                                   /* end of while (session) */

    if (user)
        { end_user_session(); }         /* finish this user */
}                                       /* end of while (user) */

end_atm();

return 0;
}



/*-----------------------------------------------------------------------------
  init_atm

     Session-wide settings:  input and output, Trace initialization, database
-----------------------------------------------------------------------------*/

void init_atm (argc, argv)

    int   argc;
    char *argv[];

{
    long   status;
    text  *username = (text *) "atm";
    text  *password = (text *) "sampleatm";
    text  *service  = (text *) "";  /* assume local default database */
    text   oci_msgbuf[OCI_ERROR_MAXMSG_SIZE];
    sword  oci_ret = OCI_SUCCESS;	    /* return status for OCI calls */
    sb4    oci_errcode = 0;		    /* returned as OCIErrorGet arg */


/******************************************************************************
    Set up input and output files
******************************************************************************/

    if (argc == 1)		/* no input file; use stdin, stdout */
        FromFile = FALSE;

    if (argc == 2)                           /* input file, use it */
    {
        if (argv[1] == NULL)
            fprintf(stderr, "Input filename is NULL\n");
        else
        {
            fprintf(stdout, "Input filename is %s\n", argv[1]);
    
            if ((InFile = fopen(argv[1], "r")) == NULL)
            {
                fprintf(stderr, "Can't open file %s\n", argv[1]);
                exit(1);
            }
            else                             /* we have an input file */
                FromFile = TRUE;
        }
    }

    if (argc > 2)
    {
        fprintf(stderr, "Too many arguments on command line\n");
        exit(1);
    }


/******************************************************************************
    Set up OCI for use ...
******************************************************************************/

    /* Init OCI and environment ... */

    oci_ret = OCIInitialize ((ub4)OCI_DEFAULT,
                             (dvoid *)0,
                             (dvoid *(*)(dvoid *, size_t))0,
                             (dvoid *(*)(dvoid *, dvoid *, size_t))0,
                             (void (*)(dvoid *, dvoid *))0);

    if (oci_ret != OCI_SUCCESS)
    {
        fprintf(stderr, "\n  OCIInitialize error: %d \n", oci_ret);
        exit(1);
    }

    oci_ret = OCIEnvInit ((OCIEnv **)&oci_envhp,
                          OCI_DEFAULT,
                          (size_t)0,
                          (dvoid **)0);

    if (oci_ret != OCI_SUCCESS)
    {
        fprintf(stderr, "\n  OCIEnvInit error: %d \n", oci_ret);
        exit(1);
    }


    /* Allocate OCI error and statement handles ... */

    oci_ret = OCIHandleAlloc ((dvoid *)oci_envhp,
                              (dvoid **)&oci_errhp,
                              OCI_HTYPE_ERROR,
                              (size_t)0,
                              (dvoid **)0);

    if (oci_ret != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_envhp, (ub4)1, (text *)NULL,
                      &oci_errcode, (text *)oci_msgbuf,
                      (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ENV);
        fprintf(stderr, "\n  OCIHandleAlloc error: %s \n", oci_msgbuf);
        exit(1);
    }

    oci_ret = OCIHandleAlloc ((dvoid *)oci_envhp,
                    (dvoid **)&oci_stmthp,
                    OCI_HTYPE_STMT,
                    (size_t)0,
                    (dvoid **)0);

    if (oci_ret != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL,
                     &oci_errcode, oci_msgbuf,
                     (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  OCIHandleAlloc error: %s \n", oci_msgbuf);
        exit(1);
    }


/******************************************************************************
    Connect to database;
******************************************************************************/

    oci_ret = OCILogon (oci_envhp,
                        oci_errhp,
                       &oci_svchp,
                        username,
                        (ub4)strlen((char *)username),
                        password,
                        (ub4)strlen((char *)password),
                        service,
                        (ub4)strlen((char *)service));

    if (oci_ret != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL,
                     &oci_errcode, oci_msgbuf,
                     (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  OCILogon error: %s \n", oci_msgbuf);
        if (oci_errhp != (OCIError *)NULL)
            OCIHandleFree ((dvoid *)oci_errhp, (ub4)OCI_HTYPE_ERROR);
        if (oci_svchp != (OCISvcCtx *)NULL)
            OCIHandleFree ((dvoid *)oci_svchp, (ub4)OCI_HTYPE_SVCCTX);
        exit(1);
    }


/******************************************************************************
    Initialize Trace session and start collection
******************************************************************************/

    status = epc_init(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, NULL, 
		      NULL, &Event_Flags, MAX_EVENT, EPC_K_ALLOC, 0, 0, 
                      NULL, NULL, &Facctx);

    if (status == epc_s_success)
        status = epc_collect(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 0, 
                             COLLECTION_NAME, FDF_NAME, 0, 0,&Facctx);
    else
        report_epc_error("init_atm", "Error on epc_init: ", status);

    if (status != epc_s_success)
        report_epc_error("init_atm", "Error on epc_collect: ", status);

    return;
}



/*-----------------------------------------------------------------------------
  end_atm

     Clean up session:  close files, end Trace collection, exit database
-----------------------------------------------------------------------------*/

void end_atm ()

{
    long status;


    if (FromFile)
        fclose(InFile);

    /* End DB connection, and free any allocated OCI handles ... */
    OCILogoff (oci_svchp, oci_errhp);

    if (oci_stmthp != (OCIStmt *)NULL)
        OCIHandleFree ((dvoid *)oci_stmthp, (ub4)OCI_HTYPE_STMT);
    if (oci_errhp != (OCIError *)NULL)
        OCIHandleFree ((dvoid *)oci_errhp, (ub4)OCI_HTYPE_ERROR);

    /* Stop the Trace collection ... */

    status = epc_cancel(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 
                        COLLECTION_NAME, 0,&Facctx);
    if (status != epc_s_success) 
        report_epc_error("end_atm", "Error on epc_cancel: ", status);

    status = epc_drop_fac(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 
                          &Facctx);
    if (status != epc_s_success)
        report_epc_error("end_atm", "Error on epc_drop_fac: ", status);

    return;
}



/*-----------------------------------------------------------------------------
  login_user

     Get account number from user and validate against database
-----------------------------------------------------------------------------*/

long login_user ()
{
char  good_input;
char  line[MAXLINE];
long  acct_num;
long  val_num;
long  status;
char  *record;                                 /* event record */

good_input = FALSE;
record = malloc(S_LONG);

DISPLAY("\n\n  Trace Manhattan Bank\n");
DISPLAY("Automated Teller Machine\n\n\n");
DISPLAY("Please enter your account number\n");
DISPLAY("or enter 0 to exit: ");

do
{
    READ(line);
    acct_num = atoi(line);

    if (acct_num == 0)                         /* allow user to exit without */
        { good_input = TRUE; }                 /* logging in */

    if (acct_num != 0)
    {
        atm_start_ro();
        val_num = atm_valid_acct_num(acct_num);   /* valid acct num or -1 */
        atm_rollback();
        if (val_num == -1)                     /* no account in database */
        {
            if (Event_Flags[VALIDATION_ERR])
            {
                memcpy(record, &acct_num, S_LONG);
                status = epc_event(API_VERSION, VENDOR, FAC_NUMBER, 
                                   VALIDATION_ERR, 0, record, S_LONG, 0, 0, 0,
                                   &Facctx);
                if (status != epc_s_success)
                    report_epc_error("login_user", "Error on epc_event: ", 
				     status);
            }
            DISPLAY("\nInvalid account number.  Enter your account number");
            DISPLAY("\nor enter 0 to exit: ");
        }
        else                                   /* account in database */
        {
            if (Event_Flags[VALIDATION_OK])
            {
                memcpy(record, &acct_num, S_LONG);
                status = epc_event(API_VERSION, VENDOR, FAC_NUMBER, 
                                   VALIDATION_OK, 0, record, S_LONG, 0, 0, 0,
                                   &Facctx);
                if (status != epc_s_success)
                    report_epc_error("login_user", "Error on epc_event: ", 
				     status);
            }
            good_input = TRUE;
        }
    }
} while (!good_input);

free(record);

return (acct_num);
}



/*-----------------------------------------------------------------------------
  init_user_session

     Start user_session event
-----------------------------------------------------------------------------*/

void init_user_session ()
{
long  status;

if (Event_Flags[USER_SESSION])
{
    status = epc_start_event(API_VERSION, VENDOR, FAC_NUMBER, 
                             USER_SESSION, &Session_Handle, 0, NULL, 
                             0, 0, 0, 0,&Facctx);
    if (status != epc_s_success)
        report_epc_error("init_user_session", "Error on epc_start_event: ", 
			 status);
}

return;
}



/*-----------------------------------------------------------------------------
  end_user_session

     End user_session event
-----------------------------------------------------------------------------*/

void end_user_session ()
{
long status;

                                 /* Reset cross_fac_1 now that we're done */
status = epc_cf_value(API_VERSION, 1, 0, NULL, NULL);
if (status != epc_s_success)
    report_epc_error("end_user_session", "Error on epc_cf_value: ", status);

if (Event_Flags[USER_SESSION])
{
    status = epc_end_event(API_VERSION, VENDOR, FAC_NUMBER, USER_SESSION, 
                           &Session_Handle, 0, NULL, 0, 0, 0, 0,&Facctx);
    if (status != epc_s_success)
        report_epc_error("end_user_session", "Error on epc_end_event: ", 
			 status);
}

return;
}



/*-----------------------------------------------------------------------------
  display_main_menu

     Display main menu and get user's choice
-----------------------------------------------------------------------------*/

long display_main_menu ()
{
char line[MAXLINE];
long choice;
char good_input;

good_input = FALSE;

DISPLAY("\n\nTrace Manhattan Bank\n");
DISPLAY("    ATM Main Menu\n");
DISPLAY("1. Balance\n");
DISPLAY("2. Deposit\n");
DISPLAY("3. Withdrawal\n");
DISPLAY("4. Transfer\n");
DISPLAY("5. Quick Withdrawal $50\n\n");
DISPLAY("0. Exit\n\n");
DISPLAY("Enter choice: ");

do
{
    READ(line);
    choice = atoi(line);

    if ((choice > -1) && (choice < 6))
        { good_input = TRUE; }
    else
        { DISPLAY("\nInvalid choice.  Enter choice: "); }
} while (!good_input);

return (choice);
}



/*-----------------------------------------------------------------------------
  get_balances

     Display balances of savings and checkings acct
-----------------------------------------------------------------------------*/

void get_balances (acct_num)
long acct_num;
{
long  status;
float sav_amt;
float chk_amt;
char  line[MAXLINE];

strcpy(Regist_ID, "Balances");
status = epc_cf_value(API_VERSION, 1, BALANCES, NULL, NULL);
if (status != epc_s_success)
    report_epc_error("get_balances", "Error on epc_cf_value: ", status);

status = epc_add_reg_id(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 
                        Regist_ID, 1,&Facctx);
if (status != epc_s_success)
    report_epc_error("get_balances", "Error on epc_add_reg_id: ", status);

if (Event_Flags[BALANCES])
{
    status = epc_start_event(API_VERSION, VENDOR, FAC_NUMBER, BALANCES, 
                             &Txn_Handle, 0, NULL, 0, 0, 0, 0,&Facctx);
    if (status != epc_s_success)
        report_epc_error("get_balances", "Error on epc_start_event: ", status);
}

atm_start_ro();
sav_amt = atm_sav_balance (acct_num);
chk_amt = atm_chk_balance (acct_num);
atm_commit();

if (Event_Flags[BALANCES])
{
    status = epc_end_event(API_VERSION, VENDOR, FAC_NUMBER, BALANCES, 
                           &Txn_Handle, 0, NULL, 0, 0, 0, 0,&Facctx);
    if (status != epc_s_success)
        report_epc_error("get_balances", "Error on epc_end_event: ", status);
}

status = epc_remove_reg_id(API_VERSION, VENDOR, FAC_NUMBER, 
                           FAC_VERSION, Regist_ID, 1,&Facctx);
if (status != epc_s_success)
    report_epc_error("get_balances", "Error on epc_remove_reg_id: ", status);

DISPLAY("\n\nAccount Balances\n\n");
if (!FromFile)
{
    printf("Checking:  %-.2f\n", chk_amt);
    printf("Savings :  %-.2f\n", sav_amt);
}
DISPLAY("\nPress ENTER to return to Main Menu: ");

READ(line);

return;
}



/*-----------------------------------------------------------------------------
  make_deposit

     Deposit money into either savings or checkings.
-----------------------------------------------------------------------------*/

void  make_deposit (acct_num)
long acct_num;
{
char   line[MAXLINE];
long   acct_type;
char   good_type;
char   good_amount;
long   txn_amt = 0;
long   status;
char   *record;                               /* event record */

good_type = FALSE;
good_amount = FALSE;
record = malloc(S_LONG);

strcpy(Regist_ID, "Deposit");
status = epc_cf_value(API_VERSION, 1, DEPOSIT, NULL, NULL);
if (status != epc_s_success)
    report_epc_error("make_deposit", "Error on epc_cf_value: ", status);

status = epc_add_reg_id(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 
                        Regist_ID, 1, &Facctx);
if (status != epc_s_success)
    report_epc_error("make_deposit", "Error on epc_add_reg_id: ", status);

DISPLAY("\n\nAccount Deposit\n\n");
DISPLAY("Deposit into:\n");
DISPLAY("1. Checking\n");
DISPLAY("2. Savings\n");
DISPLAY("\nEnter choice or 0 to cancel: ");

do                                             /* get account type */
{
    READ(line);
    acct_type = atoi(line);

    if ((acct_type < 0) || (acct_type > 2))    /* bad input */
        { DISPLAY("\nInvalid choice.  Enter choice: "); }
    else 
        { good_type = TRUE; }                  /* valid account type */

} while (!good_type);

if (acct_type == 0)                            /* cancel transaction */
{
    txn_amt = 0;
    good_amount = TRUE;
}

while (!good_amount)
{
    DISPLAY("\nEnter dollar amount to deposit or 0 to cancel: ");
    READ(line);
    txn_amt = atoi(line);

    if (txn_amt < 0)
        { DISPLAY("\nInvalid amount."); }
    else
        { good_amount = TRUE; }
}

if (txn_amt > 0)
{
    if (Event_Flags[DEPOSIT])
    {
        memset(record, 0, S_LONG);
        status = epc_start_event(API_VERSION, VENDOR, FAC_NUMBER, DEPOSIT, 
                                 &Txn_Handle, 0, record, S_LONG, 0, 0, 0,
                                 &Facctx);
        if (status != epc_s_success)
            report_epc_error("make_deposit", "Error on epc_start_event: ", 
			     status);
    }

    atm_start_rw();
    if (acct_type == 1)
        { atm_chk_deposit(acct_num, txn_amt); }
    if (acct_type == 2)
        { atm_sav_deposit(acct_num, txn_amt); }
    atm_commit();

    if (Event_Flags[DEPOSIT])
    {
        memcpy(record, &txn_amt, S_LONG);
        status = epc_end_event(API_VERSION, VENDOR, FAC_NUMBER, DEPOSIT, 
                               &Txn_Handle, 0, record, S_LONG, 0, 0, 0,
                               &Facctx);
        if (status != epc_s_success)
            report_epc_error("make_deposit", "Error on epc_end_event: ", 
			     status);
    }
}

status = epc_remove_reg_id(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 
			   Regist_ID, 1, &Facctx);
if (status != epc_s_success)
    report_epc_error("make_deposit", "Error on epc_remove_reg_id: ", status);

free(record);

return;
}



/*-----------------------------------------------------------------------------
  make_withdrawal

     Withdraw money from either savings or checkings.  If withdrawal is more
  than balance, record an overdraft event
-----------------------------------------------------------------------------*/

void make_withdrawal (acct_num)
long acct_num;
{
long   status;
char   line[MAXLINE];
char   txn_complete;
long   acct_type;
char   good_type;
long   txn_amt = 0;
char   good_amount;
float  acct_bal;
char   *wrecord, *orecord;          /* event records for withdraw, overdraft */

txn_complete = FALSE;
wrecord = malloc(S_LONG);
orecord = malloc(S_LONG);

strcpy(Regist_ID, "Withdrawal");
status = epc_cf_value(API_VERSION, 1, WITHDRAWAL, NULL, NULL);
if (status != epc_s_success)
    report_epc_error("make_withdrawal", "Error on epc_cf_value: ", status);

status = epc_add_reg_id(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 
                        Regist_ID, 1, &Facctx);
if (status != epc_s_success)
    report_epc_error("make_withdrawal", "Error on epc_add_reg_id: ", status);

do                                          /* get transaction input */
{
    good_type = FALSE;
    DISPLAY("\n\nAccount Withdrawal\n\n");
    DISPLAY("Withdraw from:\n");
    DISPLAY("1. Checking\n");
    DISPLAY("2. Savings\n");
    DISPLAY("\nEnter choice or 0 to cancel: ");

    do
    {
        READ(line);
        acct_type = atoi(line);

        if ((acct_type < 0) || (acct_type > 2))    /* bad input */
            { DISPLAY("\nInvalid choice.  Enter choice: "); }
        else
            { good_type = TRUE; }                  /* valid account type */
    } while (!good_type);

    if (acct_type == 0)                            /* cancel transaction */
    {
        txn_complete = TRUE;
        good_amount = TRUE;
    }
    else
        { good_amount = FALSE; }

    while (!good_amount)
    {
        DISPLAY("\nEnter dollar amount to withdraw or 0 to cancel: ");
        READ(line);
        txn_amt = atoi(line);

        if (txn_amt < 0)
            { DISPLAY("\nInvalid amount."); }
        else if (txn_amt > 0)
            { good_amount = TRUE; }
        else                                       /* txn_amt = 0; cancel */
            { txn_complete = TRUE; }
    }                                         /* end of good amount loop */

    if (!txn_complete)
    {
        if (Event_Flags[WITHDRAWAL])
        {
            memset(wrecord, 0, S_LONG);
            status = epc_start_event(API_VERSION, VENDOR, FAC_NUMBER, 
                                     WITHDRAWAL, &Txn_Handle, 0, wrecord, 
                                     S_LONG, 0, 0, 0, &Facctx);
            if (status != epc_s_success)
                report_epc_error("make_withdrawal", 
				 "Error on epc_start_event: ", status);
        }

        atm_start_rw();
        if (acct_type == 1)
            { acct_bal = atm_chk_balance(acct_num); }
        if (acct_type == 2)
            { acct_bal = atm_sav_balance(acct_num); }

        if (acct_bal > txn_amt)               /* sufficient funds */
        {
            if (acct_type == 1)
                { atm_chk_withdrawal(acct_num, txn_amt, acct_bal); }
            if (acct_type == 2)
                { atm_sav_withdrawal(acct_num, txn_amt, acct_bal); }
            atm_commit();
            txn_complete = TRUE;

            if (Event_Flags[WITHDRAWAL])
            {
                memcpy(wrecord, &txn_amt, S_LONG);
                status = epc_end_event(API_VERSION, VENDOR, FAC_NUMBER, 
                                       WITHDRAWAL, &Txn_Handle, 0, wrecord, 
                                       S_LONG, 0, 0, 0,&Facctx);
                if (status != epc_s_success)
                    report_epc_error("make_withdrawal", 
				     "Error on epc_end_event: ", status);
            }
        }
        else
    /* Record an overdraft; end the Withdrawal event with a txn_amt of 0 */
    /* to show that a transaction really didn't happen                   */
        {
            atm_rollback();                   /* cancel transaction */

            if (Event_Flags[OVERDRAFT])
            {
                memcpy(orecord, &txn_amt, S_LONG);
                status = epc_event(API_VERSION, VENDOR, FAC_NUMBER, OVERDRAFT, 
				   0, orecord, S_LONG, 0, 0, 0, &Facctx);
                if (status != epc_s_success)
                    report_epc_error("make_withdrawal", 
				     "Error on epc_event: ", status);
            }

            if (Event_Flags[WITHDRAWAL])
            {
                memset(wrecord, 0, S_LONG);
                status = epc_end_event(API_VERSION, VENDOR, FAC_NUMBER, 
                                       WITHDRAWAL, &Txn_Handle, 0, wrecord, 
                                       S_LONG, 0, 0, 0, &Facctx);
                if (status != epc_s_success)
                    report_epc_error("make_withdrawal", 
				     "Error on epc_end_event: ", status);
            }

            DISPLAY("\nBalance insufficient for withdrawal\n");
        }
    }

} while (!txn_complete);                 /* until txn complete or cancelled */

status = epc_remove_reg_id(API_VERSION, VENDOR, FAC_NUMBER, 
                           FAC_VERSION, Regist_ID, 1,&Facctx);
if (status != epc_s_success)
    report_epc_error("make_withdrawal", "Error on epc_remove_reg_id: ", 
		     status);

free(wrecord);
free(orecord);

return;
}



/*-----------------------------------------------------------------------------
  make_transfer

     Transfer money from one account to other.  Destination account might not
  be in this bank.
-----------------------------------------------------------------------------*/
void make_transfer (acct_num)
long acct_num;
{
long   status;
char   line[MAXLINE];
char   txn_complete;
char   good_source;
long   acct_type;
char   good_amount;
long   txn_amt = 0;
long   dest_num;
char   good_dest;
long   dest_type;
float  acct_bal;
char   *trecord, *orecord;          /* event records for transfer, overdraft */
char   *t_ptr;                      /* pointer to trecord */

txn_complete = FALSE;
trecord = malloc(S_LONG + S_LONG);
orecord = malloc(S_LONG);

strcpy(Regist_ID, "Transfer");
status = epc_cf_value(API_VERSION, 1, TRANSFER, NULL, NULL);
if (status != epc_s_success)
    report_epc_error("make_transfer", "Error on epc_cf_value: ", status);

status = epc_add_reg_id(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 
                        Regist_ID, 1, &Facctx);
if (status != epc_s_success)
    report_epc_error("make_transfer", "Error on epc_add_reg_id: ", status);

do                                            /* get transaction input */
{
    DISPLAY("\n\nAccount Transfer\n\n");
    DISPLAY("Transfer from:\n");
    DISPLAY("1. Checking\n");
    DISPLAY("2. Savings\n");
    DISPLAY("\nEnter choice or 0 to cancel: ");

    good_source = FALSE;
    do
    {
        READ(line);
        acct_type = atoi(line);

        if ((acct_type < 0) || (acct_type > 2))    /* bad input */
            { DISPLAY("\nInvalid choice.  Enter choice: "); }
        else
            { good_source = TRUE; }           /* valid account type */

    } while (!good_source);

    if (acct_type == 0)                       /* cancel transaction */
        { return; }

    good_amount = FALSE;
    while (!good_amount)
    {
        DISPLAY("\nEnter dollar amount to transfer or 0 to cancel: ");
        READ(line);
        txn_amt = atoi(line);

        if (txn_amt < 0)
            { DISPLAY("\nInvalid amount."); }
        if (txn_amt > 0)
            { good_amount = TRUE; }
        if (txn_amt == 0)                     /* cancel transaction */
            { return; }
    }

    DISPLAY("\nEnter destination account number or 0 to cancel: ");
    READ(line);
    dest_num = atoi(line);

    if (dest_num == 0)                       /* cancel transaction */
        { return; }

    DISPLAY("\nTransfer to:\n");
    DISPLAY("1. Checking\n");
    DISPLAY("2. Savings\n");
    DISPLAY("\nEnter choice or 0 to cancel: ");

    good_dest = FALSE;
    do
    {
        READ(line);
        dest_type = atoi(line);

        if ((dest_type < 0) || (dest_type > 2))    /* bad input */
            { DISPLAY("\nInvalid choice.  Enter choice: "); }
        else
            { good_dest = TRUE; }            /* valid account type */

    } while (!good_dest);

    if (dest_type == 0)                      /* cancel transaction */
        { return; }

/*----------------------------------------------------------------------------
    Now we have good source, destination, amount
-----------------------------------------------------------------------------*/
    if (Event_Flags[TRANSFER])
    {
        t_ptr = trecord;
        memset(t_ptr, 0, S_LONG);
        t_ptr += S_LONG;
        memcpy(t_ptr, &dest_num, S_LONG);
        status = epc_start_event(API_VERSION, VENDOR, FAC_NUMBER, TRANSFER, 
				 &Txn_Handle, 0, trecord, (S_LONG + S_LONG), 0, 
				 0, 0, &Facctx);
        if (status != epc_s_success)
            report_epc_error("make_transfer", "Error on epc_start_event: ", 
			     status);
    }

    atm_start_rw();
    if (acct_type == 1)
        { acct_bal = atm_chk_balance(acct_num); }
    if (acct_type == 2)
        { acct_bal = atm_sav_balance(acct_num); }

    if (acct_bal > txn_amt)                  /* sufficient funds */
    {
        if (acct_type == 1)
            { atm_chk_withdrawal(acct_num, txn_amt, acct_bal); }
        if (acct_type == 2)
            { atm_sav_withdrawal(acct_num, txn_amt, acct_bal); }

    /* Returns 0 if the account number is not for this branch of the bank */
        dest_num = atm_valid_xfer_acct(dest_num);
        if (dest_num)
        {
            if (dest_type == 1)
                { atm_chk_deposit(dest_num, txn_amt); }
            if (dest_type == 2)
                { atm_sav_deposit(dest_num, txn_amt); }
        }

        atm_commit();

        if (Event_Flags[TRANSFER])
        {
            t_ptr = trecord;
            memcpy(t_ptr, &txn_amt, S_LONG);
            t_ptr += S_LONG;
            memcpy(t_ptr, &dest_num, S_LONG);
            status = epc_end_event(API_VERSION, VENDOR, FAC_NUMBER, TRANSFER, 
				   &Txn_Handle, 0, trecord, (S_LONG + S_LONG), 
				   0, 0, 0, &Facctx);
            if (status != epc_s_success)
                report_epc_error("make_transfer", "Error on epc_end_event: ", 
				 status);
        }
        txn_complete = TRUE;
    }
    else                                     /* insufficient funds */
    {
        atm_rollback;                        /* cancel transaction */

        if (Event_Flags[OVERDRAFT])
        {
            memcpy(orecord, &txn_amt, S_LONG);
            status = epc_event(API_VERSION, VENDOR, FAC_NUMBER, OVERDRAFT, 
                               0, orecord, S_LONG, 0, 0, 0,&Facctx);
            if (status != epc_s_success)
                report_epc_error("make_transfer", "Error on epc_event: ", 
				 status);
        }

        if (Event_Flags[TRANSFER])
        {
            t_ptr = trecord;
            memset(t_ptr, 0, S_LONG);
            t_ptr += S_LONG;
            memcpy(t_ptr, &dest_num, S_LONG);
            status = epc_end_event(API_VERSION, VENDOR, FAC_NUMBER, TRANSFER, 
				   &Txn_Handle, 0, trecord, (S_LONG + S_LONG), 
				   0, 0, 0, &Facctx);
            if (status != epc_s_success)
                report_epc_error("make_transfer", "Error on epc_end_event: ", 
				 status);
        }

        DISPLAY("\nInsufficient funds.\n");
    }

} while (!txn_complete);

status = epc_remove_reg_id(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 
			   Regist_ID, 1, &Facctx);
if (status != epc_s_success)
    report_epc_error("make_transfer", "Error on epc_remove_reg_id: ", status);

free(trecord);
free(orecord);

return;
}



/*-----------------------------------------------------------------------------
  make_quick_withdrawal

     Withdraw $50 from checking.  If withdrawal is more than balance, record 
  an overdraft event.
-----------------------------------------------------------------------------*/

long make_quick_withdrawal (acct_num)
long acct_num;
{
long  status;
float chk_bal;
long  good_txn;
long  txn_amt = 50;
char  *qrecord, *orecord;

qrecord = malloc(S_LONG);
orecord = malloc(S_LONG);

strcpy(Regist_ID, "Quick Withdrawal");
status = epc_cf_value(API_VERSION, 1, QUICK_WITHDRAW, NULL, NULL);
if (status != epc_s_success)
    report_epc_error("make_quick_withdrawal", "Error on epc_start_event: ", 
		     status);
 
status = epc_add_reg_id(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 
                        Regist_ID, 1, &Facctx);
if (status != epc_s_success)
    report_epc_error("make_quick_withdrawal", "Error on epc_start_event: ", 
		     status);

if (Event_Flags[QUICK_WITHDRAW])
{
    memset(qrecord, 0, S_LONG);
    status = epc_start_event(API_VERSION, VENDOR, FAC_NUMBER, 
                             QUICK_WITHDRAW, &Txn_Handle, 0, qrecord, 
                             S_LONG, 0, 0, 0,&Facctx);
    if (status != epc_s_success)
        report_epc_error("make_quick_withdrawal", "Error on epc_start_event: ", 
			 status);
}

atm_start_rw();
chk_bal = atm_chk_balance(acct_num);

if (chk_bal > 50)
{
    atm_chk_withdrawal(acct_num, txn_amt, chk_bal);
    atm_commit();

    if (Event_Flags[QUICK_WITHDRAW])
    {
        memcpy(qrecord, &txn_amt, S_LONG);
        status = epc_end_event(API_VERSION, VENDOR, FAC_NUMBER, 
                               QUICK_WITHDRAW, &Txn_Handle, 0, qrecord, 
                               S_LONG, 0, 0, 0,&Facctx);
        if (status != epc_s_success)
            report_epc_error("make_quick_withdrawal", "Error on epc_collect: ", 
			     status);
    }
    good_txn = TRUE;
}
else                                       /* insufficient funds */
{
    atm_rollback;                          /* cancel transaction */

    if (Event_Flags[OVERDRAFT])
    {
        memcpy(orecord, &txn_amt, S_LONG);
        status = epc_event(API_VERSION, VENDOR, FAC_NUMBER, OVERDRAFT, 
                           0, orecord, S_LONG, 0, 0, 0,&Facctx);
        if (status != epc_s_success)
            report_epc_error("make_quick_withdrawal", "Error on epc_collect: ", 
			     status);
    }

    if (Event_Flags[QUICK_WITHDRAW])
    {
        memset(qrecord, 0, S_LONG);
        status = epc_end_event(API_VERSION, VENDOR, FAC_NUMBER, 
                               QUICK_WITHDRAW, &Txn_Handle, 0, qrecord, 
                               S_LONG, 0, 0, 0,&Facctx);
        if (status != epc_s_success)
            report_epc_error("make_quick_withdrawal", "Error on epc_collect: ", 
			     status);
    }
    DISPLAY("\nInsufficient funds.\n");
    good_txn = FALSE;
}

status = epc_remove_reg_id(API_VERSION, VENDOR, FAC_NUMBER, FAC_VERSION, 
			   Regist_ID, 1, &Facctx);
if (status != epc_s_success)
    report_epc_error("make_quick_withdrawal", "Error on epc_collect: ", 
		     status);

free(qrecord);
free(orecord);

return (good_txn);
}



          /***********************************************************
          *                                                          *
          *  Database access routines using OCI.  Datatypes are      *
          *  defined by s.h, part of the Oracle core library.  These *
          *  datatypes are also defined by oratypes.h, which can be  *
          *  found in $ORACLE_HOME/rdbms/demo along with the OCI     *
          *  header files.                                           *
          *                                                          *
          ***********************************************************/


/*-----------------------------------------------------------------------------
  atm_valid_acct_num

     Return acct_num if found in database; otherwise, return -1
-----------------------------------------------------------------------------*/

long atm_valid_acct_num (acct_num)

    long acct_num;

{
    text *sql_stmt = (text *)"SELECT count(*) FROM savings \
                                WHERE account_number = :acct_num";

    volatile sb4 rec_cnt;	/* for OCI volatile prevents register use */

    sb2   ind_cnt;
    sb2   ind_acct;
    sword oci_ret = OCI_SUCCESS;
    sb4   oci_errcode = 0;
    text  oci_msgbuf[OCI_ERROR_MAXMSG_SIZE];



    /* Prepare SQL stmt ... */
    if ((oci_ret = OCIStmtPrepare (oci_stmthp,
                                   oci_errhp,
                                   sql_stmt,
                                   (ub4)strlen((char *)sql_stmt),
                                   (ub4)OCI_NTV_SYNTAX,
                                   (ub4)OCI_DEFAULT)) != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIStmtPrepare error: %s \n", oci_msgbuf);
        return(-1);	/* ie same as not found condition */
    }

    /* Define output record count variable ... */
    if ((oci_ret = OCIDefineByPos (oci_stmthp,
                                  &oci_defp,
                                   oci_errhp,
                                   (ub4)1,
                                   (dvoid *)&rec_cnt,
                                   (sb4)sizeof(sb4),
                                   (ub2)SQLT_INT,
                                   (dvoid *)&ind_cnt,
                                   (ub2 *)0,
                                   (ub2 *)0,
                                   (ub4)OCI_DEFAULT)) != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIDefineByPos error: %s \n", oci_msgbuf);
        return(-1);	/* ie same as not found condition */
    }

    /* Bind input account number param ... */
    if ((oci_ret = OCIBindByName (oci_stmthp,
                                 &oci_bndap,	/*** or oci_bndap ??? ***/
                                  oci_errhp,
                                  (text *)":acct_num",
                                  (sb4)-1,
                                  (dvoid *)&acct_num,
                                  (sb4)sizeof(acct_num),
                                  (ub2)SQLT_INT,
                                  (dvoid *)&ind_acct,
                                  (ub2 *)0,
                                  (ub2 *)0,
                                  (ub4)0,
                                  (ub4 *)0,
                                  OCI_DEFAULT)) != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIBindByName error: %s \n", oci_msgbuf);
        return(-1);	/* ie same as not found condition */
    }

    /* Execute SQL stmt ... */
    oci_ret = OCIStmtExecute (oci_svchp,
                              oci_stmthp,
                              oci_errhp,
                              (ub4)1,
                              (ub4)0,
                              (OCISnapshot *)NULL,
                              (OCISnapshot *)NULL,
                              OCI_DEFAULT);

    if (oci_ret != OCI_SUCCESS)
    {
        if (oci_ret == OCI_NO_DATA)
        {
            return (-1);	/* shouldn't occur, but ... */
        }
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIStmtExecute error: %s \n", oci_msgbuf);
        return (-1);
    }

    else if (rec_cnt == 0)
    {
        return (-1);   /* for no records found */
    }

    return (acct_num);        /* success: found requested record */
}



/*-----------------------------------------------------------------------------
  atm_valid_xfer_acct

     Return acct_num if found in database; otherwise, return 0

     Note: this routine performs same search of Savings table for acct_num
           as the atm_valid_acct_num routine does, other than difference in
           returned value for failure to validate acct_num, so just use
           atm_valid_acct_num here with suitable return value.
-----------------------------------------------------------------------------*/

long atm_valid_xfer_acct (acct_num)

    long acct_num;

{
    if (atm_valid_acct_num(acct_num) == acct_num)
        return(acct_num);
    else
        return (0);
}



/*-----------------------------------------------------------------------------
  atm_balance

     Return savings or checking account balance for specified account number
-----------------------------------------------------------------------------*/

float atm_balance (acct_num, sql_stmt)

    long  acct_num;
    text *sql_stmt;

{
    volatile float balance = 0;

    sb2   ind_bal;
    sb2   ind_acct;
    sword oci_ret = OCI_SUCCESS;
    sb4   oci_errcode = 0;
    text  oci_msgbuf[OCI_ERROR_MAXMSG_SIZE];



    /* Prepare SQL stmt ... */
    if ((oci_ret = OCIStmtPrepare (oci_stmthp,
                                   oci_errhp,
                                   sql_stmt,
                                   (ub4)strlen((char *)sql_stmt),
                                   (ub4)OCI_NTV_SYNTAX,
                                   (ub4)OCI_DEFAULT)) != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIStmtPrepare error: %s \n", oci_msgbuf);
        exit(1);
    }

    /* Define output balance variable ... */
    if ((oci_ret = OCIDefineByPos (oci_stmthp,
                                  &oci_defp,
                                   oci_errhp,
                                   (ub4)1,
                                   (dvoid *)&balance,
                                   (sb4)sizeof(float),
                                   (ub2)SQLT_FLT,
                                   (dvoid *)&ind_bal,
                                   (ub2 *)0,
                                   (ub2 *)0,
                                   (ub4)OCI_DEFAULT)) != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIDefineByPos error: %s \n", oci_msgbuf);
        exit(1);
    }

    /* Bind input account number param ... */
    if ((oci_ret = OCIBindByName (oci_stmthp,
                                 &oci_bndap,	/*** or oci_bndap ??? ***/
                                  oci_errhp,
                                  (text *)":acct_num",
                                  (sb4)-1,
                                  (dvoid *)&acct_num,
                                  (sb4)sizeof(acct_num),
                                  (ub2)SQLT_INT,
                                  (dvoid *)&ind_acct,
                                  (ub2 *)0,
                                  (ub2 *)0,
                                  (ub4)0,
                                  (ub4 *)0,
                                  OCI_DEFAULT)) != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIBindByName error: %s \n", oci_msgbuf);
        exit(1);
    }

    /* Execute SQL stmt ... */
    oci_ret = OCIStmtExecute (oci_svchp,
                              oci_stmthp,
                              oci_errhp,
                              (ub4)1,
                              (ub4)0,
                              (OCISnapshot *)NULL,
                              (OCISnapshot *)NULL,
                              OCI_DEFAULT);

    if (oci_ret != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIStmtExecute error: %s \n", oci_msgbuf);
        exit(1);
    }

    return (balance);        /* success: return requested account balance */
}



/*-----------------------------------------------------------------------------
  atm_sav_balance

     Return savings account balance for account number
-----------------------------------------------------------------------------*/

float atm_sav_balance (acct_num)

    long  acct_num;

{
    text  *sql_stmt = (text *) "SELECT balance FROM savings \
                                  WHERE account_number = :acct_num";


    return (atm_balance(acct_num, sql_stmt));
}



/*-----------------------------------------------------------------------------
  atm_chk_balance

     Return checking account balance for account number
-----------------------------------------------------------------------------*/

float atm_chk_balance (acct_num)

    long  acct_num;

{
    text  *sql_stmt = (text *) "SELECT balance FROM checking \
                                  WHERE account_number = :acct_num";


    return (atm_balance(acct_num, sql_stmt));
}



/*-----------------------------------------------------------------------------
  atm_update_balance

     Update balance in savings/checking account to reflect deposit/withdrawl
-----------------------------------------------------------------------------*/

void  atm_update_balance (acct_num, new_balance, sql_stmt)

    long   acct_num;
    float  new_balance;
    text  *sql_stmt;

{
    volatile float balance = (float)new_balance;

    sb2   ind_bal;
    sb2   ind_acct;
    sword oci_ret = OCI_SUCCESS;
    sb4   oci_errcode = 0;
    text  oci_msgbuf[OCI_ERROR_MAXMSG_SIZE];



    /* Prepare SQL stmt ... */
    if ((oci_ret = OCIStmtPrepare (oci_stmthp,
                                   oci_errhp,
                                   sql_stmt,
                                   (ub4)strlen((char *)sql_stmt),
                                   (ub4)OCI_NTV_SYNTAX,
                                   (ub4)OCI_DEFAULT)) != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIStmtPrepare error: %s \n", oci_msgbuf);
        exit(1);
    }

    /* Bind input params ... */
    if ((oci_ret = OCIBindByName (oci_stmthp,
                                 &oci_bndbp,	/*** or oci_bndbp ??? ***/
                                  oci_errhp,
                                  (text *)":balance",
                                  (sb4)-1,
                                  (dvoid *)&balance,
                                  (sb4)sizeof(balance),
                                  (ub2)SQLT_FLT,
                                  (dvoid *)&ind_bal,
                                  (ub2 *)0,
                                  (ub2 *)0,
                                  (ub4)0,
                                  (ub4 *)0,
                                  OCI_DEFAULT)) != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIBindByName error: %s \n", oci_msgbuf);
        exit(1);
    }

    if ((oci_ret = OCIBindByName (oci_stmthp,
                                 &oci_bndap,	/*** or oci_bndap ??? ***/
                                  oci_errhp,
                                  (text *)":acct_num",
                                  (sb4)-1,
                                  (dvoid *)&acct_num,
                                  (sb4)sizeof(acct_num),
                                  (ub2)SQLT_INT,
                                  (dvoid *)&ind_acct,
                                  (ub2 *)0,
                                  (ub2 *)0,
                                  (ub4)0,
                                  (ub4 *)0,
                                  OCI_DEFAULT)) != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIBindByName error: %s \n", oci_msgbuf);
        exit(1);
    }

    /* Execute SQL stmt ... */
    oci_ret = OCIStmtExecute (oci_svchp,
                              oci_stmthp,
                              oci_errhp,
                              (ub4)1,
                              (ub4)0,
                              (OCISnapshot *)NULL,
                              (OCISnapshot *)NULL,
                              OCI_DEFAULT);

    if (oci_ret != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIStmtExecute error: %s \n", oci_msgbuf);
        exit(1);
    }

    return;
}



/*-----------------------------------------------------------------------------
  atm_sav_deposit

     Add amount to balance in savings account
-----------------------------------------------------------------------------*/

void  atm_sav_deposit (acct_num, amount)

    long   acct_num;
    long   amount;

{
    float  new_balance;
    text  *sql_stmt = (text *) "UPDATE savings SET balance = :balance \
                                  WHERE account_number = :acct_num";


    new_balance = amount + atm_sav_balance(acct_num);

    atm_update_balance (acct_num, new_balance, sql_stmt);

    return;
}



/*-----------------------------------------------------------------------------
  atm_chk_deposit

     Add amount to balance in checking account 
-----------------------------------------------------------------------------*/

void  atm_chk_deposit (acct_num, amount)

    long   acct_num;
    long   amount;

{
    float  new_balance;
    text  *sql_stmt = (text *) "UPDATE checking SET balance = :balance \
                                  WHERE account_number = :acct_num";


    new_balance = amount + atm_chk_balance(acct_num);

    atm_update_balance (acct_num, new_balance, sql_stmt);

    return;
}



/*-----------------------------------------------------------------------------
  atm_sav_withdrawal

     Subtract amount from balance in savings account; assumes amount is less 
  than balance (we checked balance before calling)
-----------------------------------------------------------------------------*/

void  atm_sav_withdrawal (acct_num, amount, sav_bal)

    long  acct_num;
    long  amount;
    float sav_bal;

{
    text  *sql_stmt = (text *) "UPDATE savings SET balance = :balance \
                                  WHERE account_number = :acct_num";


    sav_bal = sav_bal - amount;

    atm_update_balance (acct_num, sav_bal, sql_stmt);

    return;
}



/*-----------------------------------------------------------------------------
  atm_chk_withdrawal

     Subtract amount from balance in checking account; assumes amount is less 
  than balance (we checked balance before calling)
-----------------------------------------------------------------------------*/

void  atm_chk_withdrawal (acct_num, amount, chk_bal)

    long  acct_num;
    long  amount;
    float chk_bal;

{
    text  *sql_stmt = (text *) "UPDATE checking SET balance = :balance \
                                  WHERE account_number = :acct_num";


    chk_bal = chk_bal - amount;

    atm_update_balance (acct_num, chk_bal, sql_stmt);

    return;
}



/*-----------------------------------------------------------------------------
  atm_txn

     Start, commit, or rollback (RO or RW) transaction
-----------------------------------------------------------------------------*/

void  atm_txn (sql_stmt)

    text  *sql_stmt;

{
    sword oci_ret = OCI_SUCCESS;
    sb4   oci_errcode = 0;
    text  oci_msgbuf[OCI_ERROR_MAXMSG_SIZE];



    /* Prepare SQL stmt ... */
    if ((oci_ret = OCIStmtPrepare (oci_stmthp,
                                   oci_errhp,
                                   sql_stmt,
                                   (ub4)strlen((char *)sql_stmt),
                                   (ub4)OCI_NTV_SYNTAX,
                                   (ub4)OCI_DEFAULT)) != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIStmtPrepare error: %s \n", oci_msgbuf);
        exit(1);
    }

    /* Execute SQL stmt ... */
    oci_ret = OCIStmtExecute (oci_svchp,
                              oci_stmthp,
                              oci_errhp,
                              (ub4)1,
                              (ub4)0,
                              (OCISnapshot *)NULL,
                              (OCISnapshot *)NULL,
                              OCI_DEFAULT);

    if (oci_ret != OCI_SUCCESS)
    {
        OCIErrorGet ((dvoid *)oci_errhp, (ub4)1, (text *)NULL, &oci_errcode,
                     oci_msgbuf, (ub4)sizeof(oci_msgbuf), OCI_HTYPE_ERROR);
        fprintf(stderr, "\n  %s \n", sql_stmt);
        fprintf(stderr, "\n  OCIStmtExecute error: %s \n", oci_msgbuf);
        exit(1);
    }

    return;
}



/*-----------------------------------------------------------------------------
  atm_start_rw

     Start read-write transaction
-----------------------------------------------------------------------------*/

void  atm_start_rw ()

{
    text  *sql_stmt = (text *) "SET TRANSACTION READ WRITE";


    atm_txn (sql_stmt);

    return;
}



/*-----------------------------------------------------------------------------
  atm_start_ro

     Start read-only transaction
-----------------------------------------------------------------------------*/

void  atm_start_ro ()

{
    text  *sql_stmt = (text *) "SET TRANSACTION READ ONLY";


    atm_txn (sql_stmt);

    return;
}



/*-----------------------------------------------------------------------------
  atm_commit

     Commit transaction
-----------------------------------------------------------------------------*/

void  atm_commit ()

{
    text  *sql_stmt = (text *) "COMMIT";


    atm_txn (sql_stmt);

    return;
}



/*-----------------------------------------------------------------------------
  atm_rollback

     Rollback transaction
-----------------------------------------------------------------------------*/

void  atm_rollback ()

{
    text  *sql_stmt = (text *) "ROLLBACK";


    atm_txn (sql_stmt);

    return;
}



/*-----------------------------------------------------------------------------
  report_epc_error

    On error from Trace API call, print message and exit with status
-----------------------------------------------------------------------------*/

void  report_epc_error (routine, message, status)
char  routine[20];
char  message[80];
long  status;
{
fprintf(stderr, "Error in %s\n", routine);
fprintf(stderr, "%s%d\n", message, status);
fprintf(stderr, "Exiting\n");
exit(status);
}

