/* **++ ** ** MODULE DESCRIPTION: ** ** This is first project in ECE 3311. ** "Transaction DataBase" ** BANKDB ** ** ** ** AUTHOR: ** ** Nasser Abbasi, Network Northeastern Student ** ** CREATION DATE: 10/16/93 ** ** DESIGN ISSUES: ** ** Please See the Design document. ** I have described functional design of each function in ** a separate document. ** ** INCLUDED FILES: ** ** stdio.h, string.h, stdlib.h ** ** PORTABILITY ISSUES: ** ** VAX/VMS VAXC/DECC compiler, and UNIX (gcc compiler only) ** on UNIX the compile command is ** gcc bankdb.c -lg++ ** ** ** ASSUMPTIONS: ** ** The transaction input file must be sorted according to the ** Date field in ascending order. ** ** year dates are assumed to 19**, so any year is taken as befor ** the year 2000 . **-- */ /* ** ** INCLUDE FILES ** */ #include #include #include /* ** ** MACRO DEFINITIONS ** */ #define DEBUG 1 #define TRUE 1 #define FALSE 0 #define GOOD 1 #define BAD 0 #define MAX_LINE_LENGTH 256 #define MAX_NAME_LENGTH 10 #define MAX_NAME_FIELD_SIZE 10 #define DATE_FIELD_SIZE 8 #define ACCOUNT_NUMBER_FIELD_SIZE 6 #define TYPE_FIELD_SIZE 1 #define MAX_AMOUNT_FIELD_SIZE 10 #define ACTION_FIELD_SIZE 1 /* ** ** PREPROCESSOR DIRECTIVE ** ** */ /* ** ** TYPE DEFINITIONS ** ** */ enum TRAN_TYPE { SAVING = 1, CHECKING = 2 }; enum ACTION_TYPE { DEPOSIT = 1, WITHDRAW = 2, TRANSFER = 3, OPEN = 4, BALANCE = 5 }; enum ACCOUNT_STATUS { OPENED = 1, CLOSED = 2 }; typedef unsigned long int UINT; typedef unsigned short STATUS; typedef unsigned short BOOLEAN; typedef struct { short MM; short DD; short YY; } DATE; /* * the following defines the data structure used by BANKDB internally * to keep track of accounts information. is it a linked list, each entry * in the list list is used to keep information about one account, both * saving and checking parts of the account is kept in the same record */ typedef struct { int account_number; char last_name[MAX_NAME_LENGTH + 1]; char first_name[MAX_NAME_LENGTH + 1]; struct { enum ACCOUNT_STATUS status; float saving_balance; DATE last_accessed; /* for interest calculations */ } saving_account; struct { enum ACCOUNT_STATUS status; float checking_balance; BOOLEAN is_went_below_once; BOOLEAN is_has_too_many_withdrawals_once; int number_of_withdrawals_so_far; } checking_account; struct account *next; } ACCOUNT; typedef struct accounts_database { int how_many_accounts; ACCOUNT * next; } ACCOUNTS_DATABASE; /* * * the following record is used to format the input transaction record * to. the input transaction is read as string, after parsing and * verifying it is this stored in this record for further processing */ typedef struct input_tran_record { DATE date; char last_name[MAX_NAME_FIELD_SIZE]; short last_name_size; char first_name[MAX_NAME_FIELD_SIZE]; short first_name_size; int account_number; enum TRAN_TYPE tran_type; float amount; enum ACTION_TYPE tran_action; } INPUT_TRAN_RECORD; /* ** ** FUNCTIONS PROTOTYPING ** ** */ STATUS process_transaction(char *, FILE *); STATUS process_deposit_tran(INPUT_TRAN_RECORD *); STATUS process_withdraw_tran(INPUT_TRAN_RECORD *); STATUS process_transfer_tran(INPUT_TRAN_RECORD *); STATUS process_balance_tran(INPUT_TRAN_RECORD *, FILE *); STATUS init_system(FILE **, FILE **); STATUS open_files(FILE **, FILE **); STATUS shutdown_system(FILE *, FILE *); STATUS get_input_line(char *, UINT *, FILE *); STATUS parse_input_line(INPUT_TRAN_RECORD *, char *); STATUS validate_input_tran(INPUT_TRAN_RECORD *); STATUS process_open_tran(INPUT_TRAN_RECORD *); STATUS open_saving_account(ACCOUNT *, INPUT_TRAN_RECORD *); STATUS open_checking_account(ACCOUNT *, INPUT_TRAN_RECORD *); STATUS open_new_account(INPUT_TRAN_RECORD *); STATUS exisiting_account(int, enum TRAN_TYPE); STATUS process_withdraw_from_checking(ACCOUNT *, INPUT_TRAN_RECORD *); STATUS process_withdraw_from_saving(ACCOUNT *, INPUT_TRAN_RECORD *); void process_withdraw_penalty(ACCOUNT *, int, float); ACCOUNT *get_the_account(int); float get_balance(int, enum TRAN_TYPE); void interest_calculations(ACCOUNT *, INPUT_TRAN_RECORD *); STATUS parse_date(char *, INPUT_TRAN_RECORD *, UINT *); STATUS parse_name(char *, INPUT_TRAN_RECORD *, UINT *); STATUS parse_account_number(char *, INPUT_TRAN_RECORD *, UINT *); STATUS parse_type(char *, INPUT_TRAN_RECORD *, UINT *); STATUS parse_amount(char *, INPUT_TRAN_RECORD *, UINT *); STATUS parse_action(char *, INPUT_TRAN_RECORD *, UINT *); void debug(char *); /* ** ** GLOBAL DATA. ONLY the pointer to the internal accounts DB is ** global in this program. nothing else. ** */ static ACCOUNTS_DATABASE accounts_database; /********************************************************************\ * * MAIN LINE * ********************************************************************/ main() { FILE *trans_file, *db_file; STATUS status; char input_line[MAX_LINE_LENGTH]; UINT input_line_size = 0; status = init_system(&trans_file, &db_file); if (status == BAD) { debug("MAIN: Error initilizing system \n"); return(1); } /* * now, we read one transaction at time from trans.act and * process it. we do this until an empty line is read */ while (TRUE) { status = get_input_line(input_line, &input_line_size, trans_file); if (status == BAD) { debug("\n MAIN: Completed work on trans.act ...Shutting down.. \n"); shutdown_system(trans_file, db_file); return(1); } else if (input_line_size == 0) { debug("\nMAIN: read an empty line, done processing..Shutting down \n"); shutdown_system(trans_file, db_file); return(1); } else { debug("----------------------------------------------------------\n"); debug(input_line); debug("\n"); status = process_transaction(input_line, db_file); if (status == BAD) debug("...Transaction rejected ....\n"); } } } /********************************************************************\ * * PROCESS_TRANSACTION * *********************************************************************/ STATUS process_transaction(char input_line[] /* the input transaction */ , FILE *db_file /* used for deposit */ ) { STATUS status; INPUT_TRAN_RECORD input_tran_record; status = parse_input_line(&input_tran_record, input_line); if (status == BAD) { debug("Failed to parse input line...\n"); return(BAD); } /* * now we have parsed the input and formatted it into the record * we validate it to make sure it is within the acceptable ranges * as in the specifications */ status = validate_input_tran(&input_tran_record); if (status == BAD) return(status); /* now we have a valid transaction fields, we process it */ switch (input_tran_record.tran_action) { case (OPEN) : status = process_open_tran(&input_tran_record); break; case (DEPOSIT) : status = process_deposit_tran(&input_tran_record); break; case (WITHDRAW): status = process_withdraw_tran(&input_tran_record); break; case (TRANSFER): status = process_transfer_tran(&input_tran_record); break; case (BALANCE): status = process_balance_tran(&input_tran_record, db_file); break; } return(status); } /********************************************************************\ * * INIT_SYSTEM * ********************************************************************/ STATUS init_system(FILE **trans_file /* the transaction input file */ , FILE **db_file /* the balance output file */ ) { STATUS status; status = open_files(trans_file, db_file); if (status == BAD) { printf("INIT_SYSTEM: ERROR opening files ...\n"); return(BAD); } else debug("INIT_SYSTEM: opened files ok...\n"); accounts_database.how_many_accounts = 0; accounts_database.next = NULL; return(GOOD); } /********************************************************************\ * * OPEN_FILES * ********************************************************************/ STATUS open_files(FILE **trans_file /* the transaction input file */ , FILE **db_file /* the balance output file */ ) { FILE *t; t = fopen("trans.act", "r"); if (t == NULL) { printf("OPEN_FILES: Error opening trans.act ...\n"); return(BAD); } else *trans_file = t; t = fopen("db.rec", "w"); if (t == NULL) { printf("OPEN_FILES: Error opening db.rec ...\n"); return(BAD); } else *db_file = t; return(GOOD); } /********************************************************************\ * * SHUTDOWN_SYSTEM * ********************************************************************/ STATUS shutdown_system(FILE *trans_file /* the transaction input file */ , FILE *db_file /* the balance output file */ ) { ACCOUNT * temp; ACCOUNT * next; fclose(trans_file); fclose(db_file); /* free memory allocated for internal linked list of records data base */ temp = accounts_database.next; while (temp != NULL) { next = (ACCOUNT *)(*temp).next; free(temp); temp = next; } ; return(GOOD); } /********************************************************************\ * * GET_INPUT_LINE * ********************************************************************/ STATUS get_input_line(char input_line[] /* the transaction */ , UINT *input_line_size /* to record its length */ , FILE *trans_file /* file to read it from */ ) { if (NULL == fgets(input_line, MAX_LINE_LENGTH, trans_file)) return(BAD); else { *input_line_size = strlen(input_line) - 1; /*to account for extra \n */ input_line[ *input_line_size] = '\0'; return(GOOD); } } /********************************************************************\ * * PARSE_INPUT_LINE * ********************************************************************/ STATUS parse_input_line( INPUT_TRAN_RECORD * input_tran_record /* format this transaction */ , char *input_line /* from this line */ ) { STATUS status; UINT index; index = 0; status = parse_date(input_line, input_tran_record, &index); if (status == BAD) return(BAD); index ++; /* skip over blank to next field */ status = parse_name(input_line, input_tran_record, &index); if (status == BAD) return(BAD); index ++; /* skip over blank to next field */ status = parse_account_number(input_line, input_tran_record, &index); if (status == BAD) return(BAD); index ++; /* skip over blank to next field */ status = parse_type(input_line , input_tran_record , &index); if (status == BAD) return(BAD); index ++; /* skip over blank to next field */ status = parse_amount(input_line, input_tran_record, &index); if (status == BAD) return(BAD); index ++; /* skip over blank to next field */ status = parse_action(input_line, input_tran_record, &index); return(status); } /********************************************************************\ * * VALIDATE_INPUT_TRAN * ********************************************************************/ STATUS validate_input_tran(INPUT_TRAN_RECORD * tran) { if (((*tran).date.DD < 1) || ((*tran).date.DD > 31)) { debug("Invalid day of month in date field detected....\n"); return(BAD); } if (((*tran).date.MM < 1) || ((*tran).date.MM > 12)) { debug("Invalid month in date field detected ...\n"); return(BAD); } if (((*tran).date.YY < 00) || ((*tran).date.YY > 99)) { debug("Invalid day of month in date field detected ...\n"); return(BAD); } if (((*tran).account_number < 111111) || ((*tran).account_number > 999999)) { debug("Invalid account number field detected ...\n"); return(BAD); } if (((*tran).tran_type != SAVING) && ((*tran).tran_type != CHECKING)) { debug("Invalid transaction type field detected ...\n"); return(BAD); } if (((*tran).amount < 0.00) || ((*tran).amount > 1000000.00)) { debug("Invalid amount field detected ...\n"); return(BAD); } if (((*tran).tran_action != OPEN) && ((*tran).tran_action != DEPOSIT) && ((*tran).tran_action != WITHDRAW) && ((*tran).tran_action != BALANCE) && ((*tran).tran_action != TRANSFER)) { debug("Invalid transaction action field detected ...\n"); return(BAD); } return(GOOD); } /********************************************************************\ * * PROCESS_OPEN_TRAN * ********************************************************************/ STATUS process_open_tran(INPUT_TRAN_RECORD * tran) { STATUS status; ACCOUNT * this; enum ACCOUNT_STATUS account_status = OPENED; if (((*tran).amount < 9.99) && ((*tran).tran_type == CHECKING)) { debug("PROCESS_OPEN_TRAN: Checking account amount too small to open\n"); return(BAD); } this = get_the_account((*tran).account_number); if (this != NULL) { /* * account already exist, see if the specific part of it * exist, i.e if the incoming tran is SAVING, see if SAVING * has already been opened, if so reject the tran else open * that part */ switch ( (*tran).tran_type) { case (SAVING): if ((*this).saving_account.status == OPENED) { debug("PROCESS_OPEN: Saving Account exist, cant open \n"); return(BAD); } debug("Opening the Saving part for an existing account...\n"); status = open_saving_account(this, tran); return(status); break; case (CHECKING): if ((*this).checking_account.status == OPENED) { debug("PROCESS_OPEN: Checking Account exist, cant open \n"); return(BAD); } debug("Opening the checking part for an existing account...\n"); status = open_checking_account(this, tran); return(status); break; } } else { status = open_new_account(tran); return(status); } } /********************************************************************\ * * OPEN_SAVING_ACCOUNT * ********************************************************************/ STATUS open_saving_account(ACCOUNT * this , INPUT_TRAN_RECORD * tran ) { STATUS status; enum ACCOUNT_STATUS account_status; (*this).saving_account.saving_balance = (*tran).amount; (*this).saving_account.last_accessed = (*tran).date; account_status = OPENED; (*this).saving_account.status = account_status; return(GOOD); } /********************************************************************\ * * OPEN_CHECKING_ACCOUNT * ********************************************************************/ STATUS open_checking_account(ACCOUNT * this , INPUT_TRAN_RECORD * tran ) { STATUS status; ACCOUNT * next; enum ACCOUNT_STATUS account_status = OPENED; /* * here I check for below 500, we already know this sum cant be * less that 9.99, as this has already be validated early on * in the validate_input_tran function */ if ((*tran).amount < 500.00) { (*tran).amount = (*tran).amount - 10; (*this).checking_account.is_went_below_once = TRUE; } else (*this).checking_account.is_went_below_once = FALSE; (*this).checking_account.checking_balance = (*tran).amount; (*this).checking_account.number_of_withdrawals_so_far = 0; (*this).checking_account.is_has_too_many_withdrawals_once = FALSE; account_status = OPENED; (*this).checking_account.status = account_status; return(GOOD); } /********************************************************************\ * * OPEN_NEW_ACCOUNT * ********************************************************************/ STATUS open_new_account(INPUT_TRAN_RECORD * tran) { STATUS status; ACCOUNT * next; ACCOUNT * this; enum ACCOUNT_STATUS account_status = OPENED; this = (ACCOUNT *)malloc(sizeof(ACCOUNT)); if (this == NULL) { debug("PROCESS_OPEN: Failed to allocate memory for an account \n"); return(BAD); } (*this).next = NULL; (*this).account_number = (*tran).account_number; memmove(&(*this).last_name[0], &(*tran).last_name[0] , (*tran).last_name_size); (*this).last_name[(*tran).last_name_size] = '\0'; memmove(&(*this).first_name[0], &(*tran).first_name[0] , (*tran).first_name_size); (*this).first_name[(*tran).first_name_size] = '\0'; if ((*tran).tran_type == CHECKING) { debug("Creating a new account and opening the checking part for it...\n"); status = open_checking_account(this, tran); /* initialize the other part of the account to close */ account_status = CLOSED; (*this).saving_account.status = account_status; } else { debug("Creating a new account and opening the saving part for it...\n"); status = open_saving_account(this, tran); /* initialize the other part of the account to close */ account_status = CLOSED; (*this).checking_account.status = account_status; } /* * the account entry has been created and filled with the data * from the transaction. now go over the linked list (accounts_db) * and add it at the end of the linked list */ accounts_database.how_many_accounts ++; if (accounts_database.next == NULL) accounts_database.next = this; else { next = accounts_database.next; while ((*next).next != NULL) next = (ACCOUNT *) (*next).next; (*next).next = (struct account *)this; } return(GOOD); } /********************************************************************\ * * EXISITING_ACCOUNT * ********************************************************************/ STATUS exisiting_account(int account_number , enum TRAN_TYPE type) { ACCOUNT * next; /* * if an account exist in the accounts database then return TRUE * else return FALSE */ next = accounts_database.next; while (next != NULL) { if ((*next).account_number == account_number) if (type == SAVING) if ((*next).saving_account.status == OPENED) return(TRUE); else return(FALSE); else if ((*next).checking_account.status == OPENED) return(TRUE); else ; else ; next = (ACCOUNT *)(*next).next; } ; return(FALSE); } /********************************************************************\ * * PROCESS_DEPOSIT_TRAN * ********************************************************************/ STATUS process_deposit_tran(INPUT_TRAN_RECORD * tran) { enum TRAN_TYPE tran_type; ACCOUNT * the_account; enum ACTION_TYPE action; if (! exisiting_account((*tran).account_number, (*tran).tran_type)) { debug("Account Does not exist, Please Open first ....\n"); return(BAD); } the_account = (ACCOUNT *)get_the_account((*tran).account_number); if ((*tran).tran_type == SAVING) { debug("Depositing into Saving ...\n"); /* first see if interest is due */ interest_calculations(the_account, tran); (*the_account).saving_account.saving_balance = (*the_account).saving_account.saving_balance + (*tran).amount; if (DEBUG) printf("...the Balance now in SAVING after depositing = %10.2f ...\n" , (*the_account).saving_account.saving_balance ); } else { debug("Depositing into checking ...\n"); (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance + (*tran).amount; if (DEBUG) printf("...the Balance now in checking account is = %10.2f ...\n", (*the_account).checking_account.checking_balance ); } return(GOOD); } /********************************************************************\ * * PROCESS_WITHDRAW_TRAN * ********************************************************************/ STATUS process_withdraw_tran(INPUT_TRAN_RECORD * tran) { ACCOUNT * the_account; if (! exisiting_account((*tran).account_number, (*tran).tran_type)) { debug("PROCESS_Withdraw_TRAN: Account Does not exist, Open first \n"); return(BAD); } the_account = (ACCOUNT *)get_the_account((*tran).account_number); if ((*tran).tran_type == SAVING) return(process_withdraw_from_saving(the_account, tran)); else return(process_withdraw_from_checking(the_account, tran)); } /********************************************************************\ * * PROCESS_WITHDRAW_FROM_CHECKING * ********************************************************************/ STATUS process_withdraw_from_checking(ACCOUNT * the_account , INPUT_TRAN_RECORD * tran) { enum TRAN_TYPE tran_type; enum ACTION_TYPE action; int less_500 = FALSE; debug("Withdrawing from checking ...\n"); if ((*the_account).checking_account.checking_balance <= (*tran).amount) { if (DEBUG) { printf("...Insufficient funds in checking to withdraw $%10.2f ..\n", (*tran).amount ); printf("...checking balance currently = $%10.2f \n", (*the_account).checking_account.checking_balance ); } return(BAD); } (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance - (*tran).amount; /* handle the case of less than 500 */ if ((*the_account).checking_account.checking_balance < 500.00) if ((*the_account).checking_account.is_went_below_once != TRUE) { if ((*the_account).checking_account.checking_balance < 9.99) { debug("Amount in checking is too small to withdraw from ...\n"); debug("less than $9.99 in account for penalty use ....\n"); /* Roll back, and reject this transaction */ debug("Rolling back the checking account...\n"); (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance + (*tran).amount; return(BAD); } (*the_account).checking_account.is_went_below_once = TRUE; (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance - 10.00; less_500 = TRUE; } else ; else ; /* now check for number of withdraws penalty */ process_withdraw_penalty(the_account, less_500,(*tran).amount); if (DEBUG) printf("...the Balance now in checking account is = %10.2f ...\n", (*the_account).checking_account.checking_balance ); return(GOOD); } /********************************************************************\ * * PROCESS_WITHDRAW_FROM_SAVING * ********************************************************************/ STATUS process_withdraw_from_saving(ACCOUNT * the_account , INPUT_TRAN_RECORD * tran ) { debug("Withdrawing from Saving ...\n"); if ((*the_account).saving_account.saving_balance <= (*tran).amount) { if (DEBUG) { printf("...Insufficient funds in Saving to withdraw $%10.2f from..\n", (*tran).amount ); printf("...Saving balance currently = $%10.2f \n", (*the_account).saving_account.saving_balance ); } return(BAD); } /* All is ok, we have enough money in saving to withdraw */ interest_calculations(the_account, tran); (*the_account).saving_account.saving_balance = (*the_account).saving_account.saving_balance - (*tran).amount; if (DEBUG) printf("...the Balance now in SAVING after depositing = %10.2f ...\n", (*the_account).saving_account.saving_balance ); return(GOOD); } /********************************************************************\ * * PROCESS_WITHDRAW_PENALTY * ********************************************************************/ void process_withdraw_penalty(ACCOUNT * the_account , int less_500 , float amount_withdrawn) { if ((*the_account).checking_account.is_has_too_many_withdrawals_once != TRUE) { (*the_account).checking_account.number_of_withdrawals_so_far ++; if ((*the_account).checking_account.number_of_withdrawals_so_far >= 5) { (*the_account).checking_account.is_has_too_many_withdrawals_once= TRUE; (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance - 15.00; /* if this made the account balance go less than 0 , roll back */ if ((*the_account).checking_account.checking_balance < 0.00) { (*the_account).checking_account.number_of_withdrawals_so_far --; (*the_account).checking_account.is_has_too_many_withdrawals_once = FALSE; (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance + 15.00; debug("WITHDRAW: 5th withdraw but insufficient funds"); debug(" to remove the $15. transaction Rejected...\n"); /* make sure to roll back the orginal amount allready withdrawn */ (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance + amount_withdrawn; /* make sure to roll back the penalty on going below 500 also */ /* it could be that going below 500 coincided with the 5th */ /* withdraw attempt */ if (less_500 == TRUE) { (*the_account).checking_account.is_went_below_once = FALSE; (*the_account).checking_account.checking_balance = (*the_account).checking_account.checking_balance + 10.00; } else ; } else ; } else ; } } /********************************************************************\ * * PROCESS_TRANSFER_TRAN * ********************************************************************/ STATUS process_transfer_tran(INPUT_TRAN_RECORD * tran) { enum TRAN_TYPE tran_type; float balance; STATUS status; /* first check that the account exist */ if (! exisiting_account((*tran).account_number, (*tran).tran_type)) { debug("Account Does not exist, Open first ........\n"); return(BAD); } debug("Processing a transfer transaction....\n"); /* * now check the account we are transferring to exist. it could be that * only the checking part of the account is open or vis versa. * in transfer we need both accounts to be opened first else we reject * the transaction */ if ((*tran).tran_type == SAVING) { tran_type = CHECKING; if (! exisiting_account((*tran).account_number, tran_type)) { if (DEBUG) printf("Checking Account %d to transfer to must be Opened first... \n" , (*tran).account_number ); return(BAD); } } else { tran_type = SAVING; if (! exisiting_account((*tran).account_number, tran_type)) { if (DEBUG) printf("Saving Account %d to transfer to must be opened first ..\n" , (*tran).account_number ); return(BAD); } } /* * so far so good. both saving and checking accounts are open * now, make sure sufficient funds exist */ balance = get_balance((*tran).account_number, (*tran).tran_type); if ((*tran).tran_type == SAVING) if ((*tran).amount > balance) { debug("not sufficient funds in saving account to transfer from...\n"); return(BAD); } else ; else if ((*tran).amount > balance) { debug("not sufficient funds in checking account to transfer from ...\n"); return(BAD); } else ; /* * so far so good, there is enough money to take from one account * and move to the other. go ahead and do the transfer. */ status = process_withdraw_tran(tran); if (status == BAD) return(BAD); /* * now switch the transaction type to the other so we can credit that * account */ if ((*tran).tran_type == SAVING) tran_type = CHECKING; else tran_type = SAVING; (*tran).tran_type = tran_type; status = process_deposit_tran(tran); if (status == BAD) return(BAD); return(GOOD); } /********************************************************************\ * * PROCESS_BALANCE_TRAN * ********************************************************************/ STATUS process_balance_tran(INPUT_TRAN_RECORD * tran, FILE *db_file) { ACCOUNT * the_account; float balance; char work_buf[80]; the_account = (ACCOUNT *)get_the_account((*tran).account_number); if ((*tran).tran_type == SAVING) interest_calculations(the_account, tran); switch ( (*tran).tran_type) { case (SAVING): debug("Processing a BALANCE for a Saving account ...\n"); balance = (*the_account).saving_account.saving_balance; break; case (CHECKING): debug("Processing a BALANCE for a Checking account ...\n"); balance = (*the_account).checking_account.checking_balance; break; } /* now print the balance record to the file */ fprintf(db_file , "%2d/%2d/%2d " , (*tran).date.MM, (*tran).date.DD, (*tran).date.YY ); memmove(work_buf , &(*tran).last_name , (*tran).last_name_size ); work_buf[(*tran).last_name_size] = '\0'; fprintf(db_file, "%s", work_buf); if ((*tran).last_name_size < MAX_NAME_LENGTH) { work_buf[0] = ','; memmove(&work_buf[1] , &(*tran).first_name , (*tran).first_name_size ); work_buf[(*tran).first_name_size + 1] = '\0'; fprintf(db_file, "%s", work_buf); } fprintf(db_file, " %6d", (*tran).account_number); fprintf(db_file, " %1d", (*tran).tran_type); fprintf(db_file, " %10.2f", balance); fprintf(db_file, " %1d\n", (*tran).tran_action); return(GOOD); } /********************************************************************\ * * GET_THE_ACCOUNT * ********************************************************************/ ACCOUNT *get_the_account(int account_number) { ACCOUNT * next; next = accounts_database.next; while (next != NULL) if ((*next).account_number == account_number) return(next); else next = (ACCOUNT *)(*next).next; return(NULL); } /********************************************************************\ * * GET_BALANCE * ********************************************************************/ float get_balance(int account_number , enum TRAN_TYPE tran_type) { ACCOUNT * account; account = (ACCOUNT *)get_the_account(account_number); if (tran_type == CHECKING) return((*account).checking_account.checking_balance); else return((*account).saving_account.saving_balance); } /********************************************************************\ * * INTEREST_CALCULATIONS * ********************************************************************/ void interest_calculations(ACCOUNT * account , INPUT_TRAN_RECORD * tran ) { long int number_of_years; long int number_of_months; int i, j; float money = 0.0; float old_money = 0.0; number_of_years = (*tran).date.YY - (*account).saving_account.last_accessed.YY; if (number_of_years < 0) { debug("FATAL: Transaction in trans.act is not in chronological order\n"); debug("FATAL: Please SOrt input transaction in correct DATE order\n"); exit(1); } number_of_months = (*tran).date.MM - (*account).saving_account.last_accessed.MM; if (number_of_years == 0 && number_of_months < 0) { debug("FATAL: Transaction in trans.act is not in chronological order\n"); debug("FATAL: Please SOrt input transaction in correct DATE order\n"); exit(1); } if( number_of_years != 0) number_of_months = number_of_months + (number_of_years * 12); money = (*account).saving_account.saving_balance; old_money = money; /* * calculate compound interest for the number of months since * account accessed */ for (i = 0; i < number_of_months; i ++) money = money + (money * 0.005); /* half percent for each month */ (*account).saving_account.saving_balance = money; if (DEBUG) { if (money - old_money > 0.0) { printf("...Interest Credit is due to saving account....\n"); printf("Crediting the saving account interest for %d months\n", number_of_months); printf("Balance before= %10.2f, Balance after interest paid = %10.2f\n" , old_money, money); printf("Interest amount credited to account = %10.2f \n", (money - old_money)); } else { printf("No Interest Credit is due to saving account at this time..\n"); printf("Saving account balance is : %10.2f ...\n", old_money); } } /* * now, we make sure we UPDATE the date we last used the SAVING * account, this way next time we access this account, we will * pay the correct interest for the correct time period */ (*account).saving_account.last_accessed = (*tran).date; } /********************************************************************\ * * PARSE_DATE * ********************************************************************/ STATUS parse_date(char input_line[] , INPUT_TRAN_RECORD *input_tran_record , UINT *index ) { char date[DATE_FIELD_SIZE + 1]; char work_buf[MAX_LINE_LENGTH + 1]; STATUS status; int i, field_size; for ((*index) = 0, i = 0, field_size = 0 ; input_line[(*index)] != ' ' && field_size <= DATE_FIELD_SIZE - 1 ; i ++, (*index) ++, field_size ++) { date[i] = input_line[(*index)]; if (((*index) == 2 || (*index) == 5) && input_line[(*index)] != '/') { debug("Date field must contain / between dates....\n"); return(BAD); } } if (field_size != DATE_FIELD_SIZE) { debug("Date field size is invalid, too short ....\n"); return(BAD); } else { memmove(&work_buf[0], &date[0], 2); work_buf[2] = '\0'; (*input_tran_record).date.MM = atoi(work_buf); memmove(&work_buf[0], &date[3], 2); work_buf[2] = '\0'; (*input_tran_record).date.DD = atoi(work_buf); memmove(&work_buf[0], &date[6], 2); work_buf[2] = '\0'; (*input_tran_record).date.YY = atoi(work_buf); } return(GOOD); } /********************************************************************\ * * PARSE_NAME * ********************************************************************/ STATUS parse_name(char input_line[] , INPUT_TRAN_RECORD *input_tran_record , UINT *index ) { char last_name[MAX_LINE_LENGTH + 1]; int last_name_size; int first_name_size; char first_name[MAX_LINE_LENGTH + 1]; char work_buf[MAX_LINE_LENGTH + 1]; STATUS status; int i, field_size, temp; for (field_size = 0, i = 0 ; input_line[ *index] != ' ' && input_line[ *index] != ',' && field_size <= MAX_NAME_FIELD_SIZE ; (*index) ++, field_size ++) { last_name[i] = input_line[ *index]; i ++; } last_name_size = field_size; memmove(&(*input_tran_record).last_name[0] , &last_name[0] , last_name_size ); (*input_tran_record).last_name_size = last_name_size; temp = field_size; if (field_size <= MAX_NAME_FIELD_SIZE) { if (input_line[ *index] == ',') { (*index) ++; /* skip over ',' */ field_size ++; for (i = 0 ; input_line[ *index] != ' ' && field_size <= MAX_NAME_FIELD_SIZE ; i ++, (*index) ++, field_size ++) first_name[i] = input_line[ *index]; if (input_line[ *index] != ' ') first_name_size = field_size - 2 - temp; else first_name_size = field_size - 1 - temp; memmove(&(*input_tran_record).first_name[0] , &first_name[0] , first_name_size ); (*input_tran_record).first_name_size = first_name_size; if (field_size > MAX_NAME_FIELD_SIZE && input_line[(*index)] != ' ') while (input_line[ *index] != ' ') (*index) ++; /* skip the rest of the name */ } else { first_name_size = 0; (*input_tran_record).first_name_size = first_name_size; while (input_line[ *index] != ' ') (*index) ++; /* skip the rest of the name */ } } else { first_name_size = 0; (*input_tran_record).first_name_size = first_name_size; while (input_line[ *index] != ' ') (*index) ++; /* skip the rest of the name */ } return(GOOD); } /********************************************************************\ * * PARSE_ACCOUNT_NUMBER * ********************************************************************/ STATUS parse_account_number(char input_line[] , INPUT_TRAN_RECORD *input_tran_record , UINT *index ) { char account_number[ACCOUNT_NUMBER_FIELD_SIZE + 1]; char work_buf[MAX_LINE_LENGTH + 1]; STATUS status; int i, field_size; for (field_size = 0, i = 0 ; input_line[(*index)] != ' ' ; i ++, (*index) ++, field_size ++) account_number[i] = input_line[(*index)]; if (field_size != ACCOUNT_NUMBER_FIELD_SIZE) { debug("PARSE_INPUT_LINE: Account_number field size is invalid\n"); return(BAD); } memmove(&work_buf[0] , &account_number[0] , ACCOUNT_NUMBER_FIELD_SIZE ); work_buf[ACCOUNT_NUMBER_FIELD_SIZE] = '\0'; (*input_tran_record).account_number = atoi(work_buf); return(GOOD); } /********************************************************************\ * * PARSE_TYPE * ********************************************************************/ STATUS parse_type(char input_line[] , INPUT_TRAN_RECORD *input_tran_record , UINT *index ) { char type[TYPE_FIELD_SIZE + 1]; char work_buf[MAX_LINE_LENGTH + 1]; STATUS status; int i, field_size; for (field_size = 0, i = 0 ; input_line[(*index)] != ' ' ; i ++, (*index) ++, field_size ++) type[i] = input_line[(*index)]; if (field_size != TYPE_FIELD_SIZE) { debug("PARSE_INPUT_LINE: TYPE field size is invalid\n"); return(BAD); } memmove(&work_buf[0], &type[0], TYPE_FIELD_SIZE); work_buf[TYPE_FIELD_SIZE] = '\0'; (*input_tran_record).tran_type = atoi(work_buf); return(GOOD); } /********************************************************************\ * * PARSE_AMOUNT * ********************************************************************/ STATUS parse_amount(char input_line[] , INPUT_TRAN_RECORD *input_tran_record , UINT *index ) { char amount[MAX_AMOUNT_FIELD_SIZE + 1]; char work_buf[MAX_LINE_LENGTH + 1]; STATUS status; int i, field_size; for (field_size = 0, i = 0 ; input_line[(*index)] != ' ' ; i ++, (*index) ++, field_size ++) amount[i] = input_line[(*index)]; if (field_size > MAX_AMOUNT_FIELD_SIZE) { debug("PARSE_INPUT_LINE: AMOUNT field size is invalid\n"); return(BAD); } memmove(&work_buf[0], &amount[0], field_size); work_buf[field_size] = '\0'; sscanf(&work_buf[0], "%f", &(*input_tran_record).amount); return(GOOD); } /********************************************************************\ * * PARSE_ACTION * ********************************************************************/ STATUS parse_action(char input_line[] , INPUT_TRAN_RECORD *input_tran_record , UINT *index ) { char action[ACTION_FIELD_SIZE + 1]; char work_buf[MAX_LINE_LENGTH + 1]; STATUS status; int i, field_size; for (field_size = 0, i = 0 ; input_line[(*index)] != ' ' && input_line[(*index)] != '\n' && input_line[(*index)] != '\0' ; i ++, (*index) ++, field_size ++) action[i] = input_line[(*index)]; if (field_size != ACTION_FIELD_SIZE) { debug("PARSE_INPUT_LINE: ACTION field size is invalid\n"); return(BAD); } memmove(&work_buf[0], &action[0], ACTION_FIELD_SIZE); work_buf[ACTION_FIELD_SIZE] = '\0'; (*input_tran_record).tran_action = atoi(work_buf); return(GOOD); } /********************************************************************\ * * DEBUG * ********************************************************************/ void debug(char *str) { if (DEBUG) printf("%s", str); }