Saturday 22 July 2017

Union.c - A Short C Program showing use of a Union

/* union.c */
/* Ye don't get me I'm part o' the union... */
/* How to do a “union” in C. */
#include <stdio.h>
#include <ctype.h>
#include “conio.h”
/* We will say for now we can handle up to 100 records. */
#define MAX_RECORDS 100

struct resistor_info
{
char component_type;
long value;
int tolerance;
};

struct transistor_info
{
char component_type;
char type[4];
char number[10];
};

struct capacitor_info
{
char component_type;
float capacitance;
char  type;
};

/* This is the union - the names after the “struct ...” don't matter,
   so long as they are unique.
*/

union component_info
{
struct resistor_info resistor_part;
struct transistor_info transistor_part;
struct capacitor_info capacitor_part;
};

typedef union component_info COMPONENT_INFO;
/* The above ‑ typedef of “COMPONENT_INFO” allows us to refer to
  “union component_info” as just “COMPONENT_INFO”.
*/

/* Start of program */
void main()
{
   char component_type;

   /* We will use this to count records as they are read. */
   int record_count, record_counter;

     /* Declare an array of records for our components. */
   COMPONENT_INFO component_records[MAX_RECORDS], current_record;

   clrscr();

   printf (“Union.c - use of a union of data types.\n”);
   record_count = 0;
   /* Keep reading records until the end of the file (“feof”) */
   do
   {
      printf (“Enter Component Type (1=Resis, 2=Trans, 3=Capac, 4=Quit)>>>“);
      component_type = getche();

      /* Newline. */
      printf(“\n”);
      if (component_type != '4')
      {
         /* Set the “tag” field of our union to be the entered type. */
         current_record.resistor_part.component_type = component_type;
         switch (component_type)
         {
            /*Resistor specified. */
            case '1':
               printf (“Enter Resistance Value (ohms)>>>“);
               scanf (“%ld”,&current_record.resistor_part.value);
               printf (“Enter Tolerance Value (%%)>>>“);
               scanf (“%d”,&current_record.resistor_part.tolerance);
               break;

            /*Transistor specified. */
            case '2':
               printf (“Enter transistor type>>>“);
               scanf (“%s”,&current_record.transistor_part.type);
               printf (“Enter Transistor Number>>>“);
               scanf (“%s”,&current_record.transistor_part.number);
               break;

            /*Capacitor specified. */
            case '3':
               printf (“Enter Capacitor type (P or E)>>>“);
               scanf (“%c”,&current_record.capacitor_part.type);
               printf (“Enter Capacitance>>>“);
               scanf (“%f”,&current_record.capacitor_part.capacitance);
               break;

            default:
               /* Print a warning. */
               printf (“1 to 3, guv, try again!\n”);

         } /* End of “switch” */

         /* Store record if valid type was entered. */
         if (component_type >= '1' && component_type <= '3')
         {
            /* Copy all the data in our entered structure into the array of
               records and increment the record count. */
            component_records[record_count++] = current_record;
         }
         /* To stop “scanf” problems...ho hum. */
         fflush (stdin);
      } /* End of “if” */
   }
   while ((component_type != '4') && (record_count < MAX_RECORDS));

   printf (“\nAll records entered. Press 'Return' to see each one.\n\n”);

   record_counter = 0;
   while (record_counter < record_count)
   {
      /* Wait for keypress. */
      getch();

      printf (“Record %d:\n”,record_counter + 1);
      switch (component_records[record_counter].resistor_part.component_type)
      {
         /*Resistor specified. */
         case '1':
            printf (“Resistance Value is \t”);
            printf (“%ld\n”,component_records[record_counter].resistor_part.value);
            printf (“Tolerance Value is \t”);
            printf (“%d\n”,component_records[record_counter].resistor_part.tolerance);
            break;

         /*Transistor specified. */
         case '2':
            printf (“Transistor type is \t”);
            printf (“%s\n”,component_records[record_counter].transistor_part.type);
            printf (“Transistor Number is \t”);
            printf (“%s\n”,component_records[record_counter].transistor_part.number);
            break;

         /*Capacitor specified. */
         case '3':
            printf (“Capacitor type is \t”);
            printf (“%c\n”,component_records[record_counter].capacitor_part.type);
            printf (“Capacitance is \t”);
            printf (“%f\n”,component_records[record_counter].capacitor_part.capacitance);
            break;
      } /* End of switch. */
      record_counter ++;
   }

   printf ( “\n\nFinished. Press Return.\n”);
   getchar();
}

Thursday 20 July 2017

SLM.C - Sales Logger and Monitor Sample Program


/****************************************************************************
*   File:         SLM.C
*   Date:         17.7.17
*   Version:      1.0
*   Author        A.D. Johnson
*   System:       Linux (Most/All versions?)
*   Design Doc:
*   Description:  Sales Logger and Monitor Program for the Advanced C
*                 Programming Course. Sets up and maintains a simple
*                 database of customer purchases.
*                 Supplied as is Warts 'n' all - improve as you wish!
*   Changes:
*
****************************************************************************/

/***

Further comments:

This programme is simply a "teaching aid" for C programming and would probably
never be used or developed in the context suggested by the description.

The main programming features/methods  it attempts to illustrate are

1) Use of a doubly-linked list.
2) Use of Dynamic Memory allocation.
3) Storing binary data in a file.
4) Retrieving the binary data again
5) A very simple menu-based user interface.

Though the program is fully functional and well-debugged, major features are missing - such as

a) Ability to edit customer data.
b) Ability to edit purchase records
c) Ability to easily correct data entry errors.

As the point of this demonstration program was to illustrate the use of some simple data structures,
it didn't seem worth adding the additional features to a program that would never be used.

The code here could be adapted to other purposes - mainly to do with storing records in C linked
lists.

Modern languages make this sort of task much simpler and far less error prone. However, these
modern languages may not be usable on the system you are developing code for.

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <float.h>
#include <time.h>
#include <termios.h>    /* Linux System File */


/* Purchase Record Definition */
struct purchase_rec
{
   long item_code;
   int  quantity;
   float  unit_price;
   char date[30];
   struct purchase_rec *next_purchase_ptr;

};


/* Customer Record Definition. */
struct customer_record
{
  long   ref_no;
  char   name[40];
  char   address[3][50];
  char   telephone[20];
  float  amount_owing;
  long   no_of_items_bought;
  struct customer_record *next_cust_ptr;
  struct customer_record *prev_cust_ptr;
  struct purchase_rec *purchase_list_ptr;
};


/* Declare a new type called “FUNCTION_PTR”. */
typedef void    (*FUNCTION_PTR)(void);


struct menu_table_element
{
   char  keypress;              /*This field will hold the keypress.*/
   char  menu_item_text[50];
   FUNCTION_PTR handler_function; /*This will hold the function pointer. */
};


#define MAIN_MENU_SIZE 7
// We need function pre-declarations so we can build a table.
void enter_customer();
void display_customers();
void delete_customer();
void enter_purchase();
void display_purchases();
void finish();


/* Build up a table of function addresses, with “ID's”. */
struct menu_table_element menu_table[MAIN_MENU_SIZE] =
{
  '1', "Enter New Customer", enter_customer,
  '2', "Display Customer List", display_customers,
  '3', "Delete Customer", delete_customer,
  '4', "Enter a New Purchase", enter_purchase,
  '5', "Display Customer Purchases", display_purchases,
  '6', "Enter Payment",display_purchases,
  '0', "Quit", finish

};


/* Global pointer to start of customer list. */
struct customer_record *customer_list_head, *prev_cust_ptr;

/* Keep a current customer reference number. */
long customer_no=1000;
char *file_name_ptr,filename[80];

#define CUSTOMERS_FILE_NAME "slm.dat"


/*************************************************************************
* Function:      gotoxy
* Parameters:    column - X, row - Y
* Description:   Moves printing position to specified screen/window location
*
* Returns:       Nothing
**************************************************************************/
void gotoxy(int x, int y)
{
    // This uses what's called an ANSI escape sequence to move the cursor
    // to a particular location in the window.
    printf("\033[%d;%dH", y, x);
}

/*************************************************************************
* Function:      clrscr
* Parameters:    none
* Description:   Clears the "screen" (console printing area)
*
* Returns:       Nothing
**************************************************************************/
void clrscr()
{
    // This uses what's called an ANSI escape sequence to clear the
    // viewing portion of the terminal window.
    printf ("\033c");
}

/*************************************************************************
* Function:      getch()
* Parameters:    none
* Description:   Waits for a single key to be pressed, rather than needing
                 "Enter" or "Return" to be pressed - hence the jiggery pokery
* Returns:       The key code that was pressed.
**************************************************************************/
char getch(void)
{
      int c=0;

      struct termios org_opts, new_opts;
      int res=0;
      //-----  store old IO settings in a structure -----------
      res=tcgetattr(fileno(stdin), &org_opts);

      //Keep a copy:
      new_opts = org_opts;

      //Update the bits we're interested in
      new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);

      //Put the settings into the system.
      tcsetattr(fileno(stdin), TCSANOW, &new_opts);
      //Check for a keypress
      c=getchar();
      //------  restore old settings ---------
      res=tcsetattr(fileno(stdin), TCSANOW, &org_opts);
      //assert(res==0);
      return(c);
}


/****************************************************************************
*   Function:     delay
*   Description:  Delay execution for a set time. We will use a Linux System
*                 function to delay execution but use a value in milliseconds
****************************************************************************/
void delay (long millisecs)
{
   struct timespec time_val, result_val;
   //Convert milliseconds to seconds and store it in a structure field.
   time_val.tv_sec = millisecs/1000;
   time_val.tv_nsec = (millisecs%1000)*1000000;

   //It seems overkill, but the system function takes 2 structures as
   //paramaters - and it's only the 1st one we're interested in here.
   nanosleep(&time_val, &result_val);

}

/****************************************************************************
*   Function:     delete_purchases
*   Description:  Free up all memory for a list of purchases.
****************************************************************************/
void delete_purchases(struct purchase_rec *purchase_ptr)
{
   struct purchase_rec *next_ptr;
   /* Scan down purchase list. */
   while (purchase_ptr)
   {
         next_ptr = purchase_ptr->next_purchase_ptr;
         free (purchase_ptr);
         purchase_ptr = next_ptr;
    }
}

/**************************************************************************
*   Function:     write_purchase_records
*   Description:  Write all purchase records for a customer to the file
*   Parameters:   file pointer, customer record pointer.
***************************************************************************/
void write_purchase_records(FILE *file_ptr, struct customer_record *record_ptr)
{
   long no_item_records, bytes_written;
   struct purchase_rec *item_record_ptr;

   /*Set up pointer to head of list of items. */
   item_record_ptr = record_ptr->purchase_list_ptr;

   for (no_item_records = 0;
        no_item_records < record_ptr->no_of_items_bought;
        no_item_records++)
   {
      /* Write the whole record to the output file. */
      bytes_written = fwrite ((char *)item_record_ptr, 1,
                           sizeof (struct purchase_rec), file_ptr);

      /* Check and set a flag for end of file. */
      if (bytes_written != sizeof (struct purchase_rec))
      {
         printf ("\nFile write error!\n");
      }
      item_record_ptr = item_record_ptr->next_purchase_ptr;
    }
 }



/***************************************************************************
*   Function:     write_disk_file
*   Description:  Writes all customer details to a data file.
***************************************************************************/
void write_disk_file(char *data_file_name)
{
    FILE *file_ptr;
    /* Set up a value to hold the length of our record. */
    int  record_length = sizeof (struct customer_record);
    int  bytes_written;
    long customer_count = 0;

    struct customer_record *customer_ptr;

    if ((!data_file_name) || (!*data_file_name))
    {
        data_file_name = CUSTOMERS_FILE_NAME;
    }

    /* Set things up ready to write the file - use "file_ptr" to access it./
    file_ptr = fopen (data_file_name,"wb");

    if (file_ptr == NULL)
    {
      printf ("*** Could not open %s!", data_file_name);
    }
    else
    {
        /* Write the current max customer number to file first. */
        fwrite ((char *)&customer_no, 1, sizeof (long), file_ptr);

        /* Set to start of list. */
        customer_ptr = customer_list_head;

        /* Keep writing records using a "do" loop. */
        while (customer_ptr != NULL)
        {
              /* Read the whole record from the input file. */
              bytes_written = fwrite ((char *)customer_ptr, 1,
                                sizeof (struct customer_record), file_ptr);

              /* Check bytes actually written to file. */
              if (bytes_written != record_length)
              {
                 printf ("\nFile write error for %s (Disk Full?)\n",data_file_name);
              }

              /* Check for purchase records: */
              if (customer_ptr->no_of_items_bought)
              {
                 /* Write the purchase records for this customer. */
                 write_purchase_records (file_ptr, customer_ptr);
              }

              /* Move to next record in list. */
              customer_ptr = customer_ptr->next_cust_ptr;
              customer_count++;
        }
        /* Close the file we were using. */
        fclose (file_ptr);
        printf ( "\n\n%ld record(s) written\n",customer_count);
    }
    delay (1500);
}


/****************************************************************************
*   Function:     finish
*   Description:  Free up all dynamically allocated memory, close files
*                 and exit.
****************************************************************************/
void finish(void)
{
   struct customer_record *next_cust_ptr, *customer_ptr = customer_list_head;

    write_disk_file(filename);
    printf ("\nRecords saved. Farewell!\n");
    delay (1000);

   /* Free all memory for purchase and customer records....*/
   while (customer_ptr)
   {
      next_cust_ptr = customer_ptr->next_cust_ptr;
      delete_purchases(customer_ptr->purchase_list_ptr);
      free (customer_ptr);
      customer_ptr = next_cust_ptr;
   }

   exit (0);
}

/****************************************************************************
*   Function:     display_customer_record
*   Description:  Display one customer's Details
*   Parameters:   Pointer to Customer's Record
*
*   Returns:      Nothing.
****************************************************************************/
void display_cust_record (struct customer_record *record_ptr)
{
   printf ("\nName:\t\t\%s\n", record_ptr->name);
   printf ("Ref No:\t\t%ld\n", record_ptr->ref_no);
   printf ("Address:\t%s\n", record_ptr->address[0]);
   printf ("\t\t%s\n", record_ptr->address[1]);
   printf ("\t\t%s\n", record_ptr->address[2]);
   printf ("Tel. \t\t%s\n", record_ptr->telephone);
   printf ("Credit / Debit\t£%05.02f\n", record_ptr->amount_owing);
   printf ("No of items:\t%ld", record_ptr->no_of_items_bought);
}

/****************************************************************************
*   Function:     display_purchase_record
*   Description:  Display a customer's purchase record
*   Parameters:   Pointer to purchase Record
*
*   Returns:      Nothing.
****************************************************************************/
void display_purchase_record (struct purchase_rec *record_ptr)
{
   printf ("\nItem Code\t%ld\n", record_ptr->item_code);
   printf ("Quantity:\t%d\n", record_ptr->quantity);
   printf ("Unit Price:\t£%03.2f\n", record_ptr->unit_price);
   printf ("Date: %s\t\t", record_ptr->date);
}

/****************************************************************************
*   Function:     get_customer_record
*   Description:  Gets a record pointer for a given customer number.
*   Parameters:   none
*
*   Returns:      Pointer to the customer's record.
****************************************************************************/
struct customer_record *get_customer_record (void)
{
   struct customer_record *record_ptr;
   long ref_no;
   char resp;

   do
   {

      if (customer_list_head==NULL)
      {
        printf ("***No customers in the database!\n");
        delay (1000);
        return 0;
      }
      clrscr();
      fflush (stdin);
      printf ("Enter reference number >>>");
      scanf ("%ld", &ref_no);

      record_ptr = customer_list_head;

      /* Scan through the linked list till we find our reference number. */
      do
      {
         if (record_ptr->ref_no == ref_no)
         {
            display_cust_record(record_ptr);
            break;
         }
         else
         {
            /* Pass to next customer in list. */
            record_ptr = record_ptr->next_cust_ptr;
         }
      }
      while (record_ptr != NULL);
      /* Check to see if we found a customer. */
      if (record_ptr ==NULL)
      {
        printf ("\n\n*** Error - no such customer!\n");
        delay (1000);
      }
      else
      {
         printf ("\nIs this correct? [Y/N] ");
         fflush(stdin);
         resp = getchar();
         if (toupper (getchar()) != 'Y')
         {
            /* Force the loop round again. */
            record_ptr = NULL;
         }
      }
   }
   while(record_ptr == NULL);

   return (record_ptr);
}


/****************************************************************************
*   Function:     read_customer_record
*   Description:  Read in Customer details from keyboard.
*   Parameters:   Pointer to Customer's Record
*
*   Returns:      Nothing.
****************************************************************************/
void read_customer_record (struct customer_record *record_ptr)
{

   int i;

   do
   {
      printf ("Enter the name >>>");
      gets (record_ptr->name);

      for (i = 0; i < 3; i++)
      {
         printf ("Enter the Address[%d]>>>",i+1);
         gets (record_ptr->address[i]);
      }

      printf ("\nEnter Tel No. >>>");
      gets(record_ptr->telephone);

      printf ("\nIs this correct? (Y = Yes)");
   }
   while (toupper(getch())!='Y');

   record_ptr->purchase_list_ptr = NULL;
   /* Increment the reference number that we are using. */
   record_ptr->ref_no = customer_no++;

}

/****************************************************************************
*   Function:     display_customers
*   Description:  Display all customer details
*   Parameters:   Pointer to Customer's Record
*
*   Returns:      Nothing.
****************************************************************************/
void display_customers()
{
    struct customer_record *customer_ptr;
    /* Set to start of list. */
    customer_ptr = customer_list_head;

    clrscr();
    /* Keep writing records using a "do" loop. */
    while (customer_ptr != NULL)
    {
          display_cust_record(customer_ptr);
          /* Move to next record in list. */
          customer_ptr = customer_ptr->next_cust_ptr;

          printf ("\nPress Any Key to Advance...");
          getch();

    }
    getch();
}
/**************************************************************************
*   Function:     get_time_date
*   description:  returns a pointer to a string with current time & date
***************************************************************************/
char *get_time_date(void)
{
   time_t t;
   static char temp_buffer[30];

   //Call the system function to get a time value.
   time(&t);
   //Format the time value into a string.
   strncpy (temp_buffer, ctime(&t), 24);
   //Terminate the string so it can be displayed.
   temp_buffer[25] = 0;
   return (temp_buffer);
}

/****************************************************************************
*   Function:     read_purchase_record
*   Description:  Read in purchase details from keyboard.
*   Parameters:   Pointer to purchase Record
*
*   Returns:      Nothing.
****************************************************************************/
void read_purchase_record (struct purchase_rec *record_ptr)
{
   char resp;
   do
   {
      fflush (stdin);
      printf ("Enter the item code  >>>");
      scanf ("%ld",&record_ptr->item_code);

      printf ("Enter the quantity   >>>");
      scanf ("%d",&record_ptr->quantity);

      printf ("Enter the unit price >>> £");
      scanf ("%f",&record_ptr->unit_price);

      printf ("\nIs this correct? (Y = Yes)");
      resp = toupper(getchar());
   }
   while (resp=='Y');

   /* Get time and date and store it in this purchase record. */
   strcpy (record_ptr->date, get_time_date());
}

/**************************************************************************
*   Function:     read_purchase_records
*   Description:  Reads all purchase records for a customer from the file
*   Parameters:   file pointer, customer record pointer.
***************************************************************************/
void read_purchase_records(FILE *file_ptr, struct customer_record *record_ptr)
{
   long no_item_records, bytes_read;
   struct purchase_rec *item_record_ptr, *prev_record_ptr=NULL;

   for (no_item_records = 0;
        no_item_records < record_ptr->no_of_items_bought;
        no_item_records++)
   {
      /* Allocate memory to hold one customer record. */
      item_record_ptr = malloc (sizeof (struct purchase_rec));
      /* Set pointer field to NULL. */
      item_record_ptr->next_purchase_ptr = NULL;
      if (item_record_ptr == NULL)
      {
         printf ("\nFailed to allocate memory whilst reading disk file!!\n");
         finish();
      }

      /* Set up pointer to this new record from the previous record. */
      if (prev_record_ptr!= NULL)
      {
         prev_record_ptr->next_purchase_ptr=item_record_ptr;
      }

      /* If this is the 1st purchase record read,
         set up the head of the list in the customer record. */
      if (no_item_records == 0)
      {
          record_ptr->purchase_list_ptr = item_record_ptr;
      }

      /* Read the whole record from the input file. */
      bytes_read = fread ((char *)item_record_ptr, 1,
                           sizeof (struct purchase_rec), file_ptr);

      if (bytes_read != sizeof (struct purchase_rec))
      {
         printf ("\nFile read error!\n");
         finish();
      }
      prev_record_ptr = item_record_ptr;
    }
 }

/**************************************************************************
*   Function:     read_disk_file
*   Description:  Reads all customer details from a file.
***************************************************************************/
void read_disk_file(char *data_file_name)
{
    FILE *file_ptr;
    /* Set up a value to hold the length of our record. */
    int  record_length = sizeof (struct customer_record);
    int  bytes_read, end_of_file=0;
    long customer_count = 0;

    struct customer_record *customer_ptr, *prev_ptr;

    /* Set things up ready to read the input file - use "file_ptr" to access it./
    file_ptr = fopen (data_file_name,"rb");

    if (file_ptr == NULL)
    {
      printf ("*** Could not open %s!", data_file_name);
    }
    else
    {
        /* Get the current max customer number - 1st thing in file. */
        fread ((char *)&customer_no, 1, sizeof (long), file_ptr);

        /* Keep reading records using a "do" loop. */
        do
        {
           /* Allocate memory to hold one customer record. */
           customer_ptr = malloc (record_length);
           if (customer_ptr == NULL)
           {
              printf ("\nFailed to allocate memory whilst reading disk file!!\n");
              finish();
           }
           else
           {
              /* Read the whole record from the input file. */
              bytes_read = fread ((char *)customer_ptr, 1,
                                sizeof (struct customer_record), file_ptr);

              /* Check and set a flag for end of file. */
              end_of_file = feof (file_ptr);
              if ((bytes_read != record_length) && !(end_of_file))
              {
                 printf ("\nFile read error for %s!\n",data_file_name);
                 finish();
              }

              /* Check for purchase records: */
              if ((customer_ptr->no_of_items_bought) && (!end_of_file))
              {
                 /* Read the purchase records for this customer. */
                 read_purchase_records (file_ptr, customer_ptr);
              }
              /* At end of file, we will have erroneously created a customer
                 record, so delete it. */
              if (end_of_file)
              {
                  free (customer_ptr);
              }
              else
              {
                  /* If this is the 1st record read, set up
                     the head of the list. */
                  if (customer_count == 0)
                  {
                     customer_list_head = customer_ptr;
                     customer_ptr->prev_cust_ptr=NULL;
                  }
                  else
                  {
                      /* Add record onto list of customers. */
                      prev_ptr->next_cust_ptr = customer_ptr;
                      //Set previous record back from this one.
                      customer_ptr->prev_cust_ptr=prev_ptr;
                  }
                  prev_ptr = customer_ptr;
                  /* Set end of list. */
                  customer_ptr->next_cust_ptr = NULL;
                  customer_count++;
              }

           }
        }
        while (!feof(file_ptr));

        /* Close the file we were using. */
        fclose (file_ptr);
        printf ( "\n\n%ld Records read\n",customer_count);
    }
    delay (1500);
}


/****************************************************************************
*   Function:    enter_customer
*   Description: Allocate memory for a new customer record and read details *
****************************************************************************/
void enter_customer(void)
{
   struct customer_record *record_ptr,*current_ptr=NULL;

   clrscr();

   /*Allocate memory for new purchase record. */
   record_ptr = malloc (sizeof (struct customer_record));
   /*Check we got it. */
   if (record_ptr == NULL)
   {
      printf ("\n\n***Unable to allocate memory for customer record!\n\n");
   }
   else
   {
      /*Set up "head" pointer now that we have read a record. */
      if (customer_list_head == NULL)
      {
         customer_list_head = record_ptr;
      }
      else
      {
         current_ptr = customer_list_head;
         /* Add this record onto the list. */
         while (current_ptr->next_cust_ptr)
         {
            current_ptr = current_ptr->next_cust_ptr;
         }
         current_ptr->next_cust_ptr = record_ptr;
      }
      record_ptr->next_cust_ptr = NULL;

      /* Set list of purchases to be NULL.. */
      record_ptr->purchase_list_ptr = NULL;
      /* Set item details. */
      record_ptr->amount_owing = 0;
      record_ptr->no_of_items_bought = 0;
      //Link back to the previous
      record_ptr->prev_cust_ptr=current_ptr;
      /* Now read in customer details. */
      read_customer_record (record_ptr);
   }
}

/****************************************************************************
*   Function:    enter_purchase
*   Description: get customer refernece number, allocate memory and read
*                details.
****************************************************************************/
void enter_purchase(void)
{
   struct customer_record *record_ptr;
   struct purchase_rec *purchase_ptr, *new_purchase_ptr;
   //long ref_no;

   record_ptr = get_customer_record();

   if (record_ptr==NULL)
   {
        printf ("***No customers in the database!\n");
        return;
   }

   /*Allocate memory for new purchase record. */
   new_purchase_ptr = malloc (sizeof (struct purchase_rec));

   if (!new_purchase_ptr)
   {
      printf ("\n\n***Could not allocate memory for new purchase!\n");
   }
   else
   {
     /* Read in the details. */
     read_purchase_record(new_purchase_ptr);

     /* Set "next" field as this record will now form end of list. */
     new_purchase_ptr->next_purchase_ptr = NULL;
     /*Count the purchases for this customer. */
     record_ptr->no_of_items_bought++;

     /* Add on the amount owing. */
     record_ptr->amount_owing += new_purchase_ptr->unit_price *
                                 new_purchase_ptr->quantity;

     /* Add this pruchase onto the list... */
     purchase_ptr = record_ptr->purchase_list_ptr;

     /* If this is the 1st purchase, set head of purchase list.*/
     if (!purchase_ptr)
     {
         record_ptr->purchase_list_ptr=new_purchase_ptr;
     }
     else
     {
         /* Search to end of list. */
         while (purchase_ptr->next_purchase_ptr)
         {
            purchase_ptr = purchase_ptr->next_purchase_ptr;
         }
        /* Add on the new purchase record...*/
        purchase_ptr->next_purchase_ptr = new_purchase_ptr;

     }
   }
}

/****************************************************************************
*   Function:    enter_payment
*   Description: Enter a payment made by a customer.
*                details.
****************************************************************************/
void enter_payment(void)
{
   struct customer_record *record_ptr;
   float payment;

   record_ptr = get_customer_record();
   printf ("\n\nEnter amount of payment >>>");
   /*Get the amount paid. */
   scanf ("%f", &payment);
   /* Update the record accordingly. */
   record_ptr->amount_owing -= payment;
}

/****************************************************************************
*   Function:    display_purchases
*   Description: Display list of purchases for a given customer.
****************************************************************************/
void display_purchases(void)
{
   struct customer_record *customer_ptr;
   struct purchase_rec *purchase_ptr;
   char resp[10];
   //long ref_no;

   customer_ptr = get_customer_record();
   if (customer_ptr)
   {
      /* Get pointer to purchase list. */
      purchase_ptr = customer_ptr->purchase_list_ptr;
   }
   else
   {
      purchase_ptr = NULL;
   }

   if (!purchase_ptr)
   {
      printf ("\nThis customer has not made any purchases!\n");
      delay (1000);
   }
   else
   {
      clrscr();
      fflush(stdin);
      /* Display the list of purchases, pausing between each. */
      while (purchase_ptr)
      {

         display_purchase_record(purchase_ptr);

         purchase_ptr = purchase_ptr->next_purchase_ptr;
         printf ("\n\n------\n\n");
      }
      fflush(stdin);

      getch();
      getch();


   }
}

/****************************************************************************
*   Function:    delete_customer
*   Description: Deletes a customers details.
****************************************************************************/
void delete_customer(void)
{
   struct customer_record *record_ptr, *prev_record_ptr,*next_record_ptr;

   record_ptr = get_customer_record();

   if (record_ptr==NULL)
   {
        printf ("***No customers in the database!\n");
        return;
   }

   /*Remove the current customer from the chain. */
   if (record_ptr != customer_list_head)
   {
      //Get pointers to next and previous records.
      next_record_ptr = record_ptr->next_cust_ptr;
      prev_record_ptr = record_ptr->prev_cust_ptr;
      //Now set the pointers to remove the current element from the list
      prev_record_ptr->next_cust_ptr=next_record_ptr;

      //Check if this is the last element of the list.
      if (next_record_ptr)
      {
            next_record_ptr->prev_cust_ptr=prev_record_ptr;
      }

   }
   else
   {
      //Reset this the new record is the start of the list.
      customer_list_head = customer_list_head->next_cust_ptr;

      //Is the list now empty?
      if (customer_list_head)
      {
            //Set the record properly as we're at the start of the list
            customer_list_head->prev_cust_ptr = NULL;
      }
   }
   /* Delete the purchase list. */
   delete_purchases (record_ptr->purchase_list_ptr);
   /* Free record's memory. */
   free (record_ptr);
}

/****************************************************************************
*   Function:    process_menu_option
*   Description: Takes required action for keypress.
****************************************************************************/
void process_menu_option(char option)
{
    int i;
    //Scan through the table and check which function needs to be called.
    for (i=0;i<MAIN_MENU_SIZE;i++)
    {
        if (option == menu_table[i].keypress)
        {
            (*menu_table[i].handler_function)();
        }

    }
}

/****************************************************************************
*   Function:    display_menu
*   Description: Display the menu for the program.
****************************************************************************/
void display_menu(void)
{
    int i;
    clrscr();
    gotoxy(25,3);
    printf ("Sales Logger and Monitor");
    gotoxy(25,4);
    printf ("------------------------");

    for (i=0;i<MAIN_MENU_SIZE;i++)
    {
        printf ("\n\n\t\t%c. %s",menu_table[i].keypress,menu_table[i].menu_item_text);
    }
    printf ("\n\n");
}

/****************************************************************************
*   Function:    sign_on
*   Description: Display sign on message.
****************************************************************************/
void sign_on (void)
{
   clrscr();
   gotoxy (12,10);
   printf ("Sales Logger and Monitor by A.D. Johnson - VERSION 1.0.\n\n\n\n");
   delay (1500);
}

/****************************************************************************
*   Function:    main
*   Description: The main program!
****************************************************************************/
int main (int argument_count, char *argument_values[])
{

   sign_on();
   /* Has user supplied filename on command line? */
   if (argument_count > 1)
   {
      file_name_ptr = argument_values[1];
   }
   else
   {
      /* Assume a default filename. */
      file_name_ptr = CUSTOMERS_FILE_NAME;
   }

   /* Read the records in from the supplied file. */
   read_disk_file(file_name_ptr);

   do
   {
      display_menu();
      process_menu_option(getch());
   }
   while (1);
}