#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <time.h>
#include <errno.h>

#include "config.h"



#define MAX_OPEN_ARCHIVES	20

#define BASE_HANDLE		1000	/* used for allocating values for the file handles
					 * returned to the user program */

#define MAGIC_NUMBER_SIZE	4	/* size, in bytes, of magic number */

#define NOT_A_HANDLE		BASE_HANDLE - 1

#define NOT_A_DESCRIPTOR	-100

#define NOT_AN_OFFSET		-200	/* used for the "offset_of_next" field of
					 * the last file entry */

#define NO_ASSIGN		-300
#define PREVIOUS_ENTRY		-301
#define CURRENT_ENTRY		-302

#define NO_TRUNCATE		-400
#define START_OF_CURRENT_ENTRY	-401
#define END_OF_CURRENT_ENTRY	-402



typedef char		magic_number_type[ MAGIC_NUMBER_SIZE ];



struct private_index_entry_type {
  int			num;
  int			start_offset;		/* offset of start of index on file */
  int			offset_of_data;		/* offset of start of data on file */
  int			offset_of_next;		/* offset of start of next index on file */
  int			to_be_deleted;
};



struct full_index_entry_type {
  archive_index_entry_type	archive;
  struct private_index_entry_type	private;
  struct full_index_entry_type		*next;
};



struct archive_header_type {
  magic_number_type	magic_number;
};



struct archive_state_type {
  int				open_mode;
  struct full_index_entry_type	*root_index_entry_ptr;
  struct full_index_entry_type	*last_index_entry_ptr;
  struct full_index_entry_type	*current_index_entry_ptr;
};



static int				debug = FALSE;
static magic_number_type		magic_number;
static int				num_open_archives = 0;
static int				archive_handles[ MAX_OPEN_ARCHIVES ];
static int				archive_descriptors[ MAX_OPEN_ARCHIVES ];
static struct archive_header_type	*archive_header_ptrs[ MAX_OPEN_ARCHIVES ];
static struct archive_state_type	*archive_state_ptrs[ MAX_OPEN_ARCHIVES ];



/********************************************* PROTOS ********************************************/

int open_archive(	char	*archive_name,
			int	open_mode );

static
int Open_Read(		char	*archive_name );

static
int Open_Write(		char	*archive_name );

static
int Open_Append(	char	*archive_name );

static
int Open_Update(	char	*archive_name );

static
int Lock_Entire_File(	int	archive_des );

static
int Unlock_Entire_File(	int	archive_des );

static
int Archive_Already_Open(	char	*archive_name );

static
void Initialise_Archive_Handles_Array(	void );

static
void Initialise_Archive_Descriptors_Array(	void );

static
void Set_Magic_Number(	void );

static
int Copy_File_Indices_To_Memory(	int	archive_index );

static
int New_Archive_Handle(	void );

static
int Handle_In_Use(	int	handle );

int get_first_index(	int					archive_handle,
			archive_index_entry_type		*archive_index_entry_buf_ptr );

int get_next_index(	int					archive_handle,
			archive_index_entry_type		*archive_index_entry_buf_ptr );

int close_archive(	int	archive_handle );

static
int Adjust_Archive_Array_Entries(	int	archive_index );

static
void Overwrite_Gap_In_Archive_Array_Entries(	int	archive_index );

static
void Reset_Last_Valid_Array_Entries(	void );

static
int Free_Archive_Memory(		int	archive_index );

static
void Free_List(	struct full_index_entry_type		*index_entry_ptr );

static
int Remove_Deleted_Entries_From_Archive(	int	archive_index );

static
int Copy_Entry_To_New_File_Position(	int	archive_index,
					int	previous_entry_offset,
					int	dest_offset,
					int	source_offset );

static
int Assign_Not_An_Offset_And_Truncate(	int	archive_index,
					int	previous_entry_offset,
					int	current_entry_offset,
					int	assign_not_an_offset,
					int	truncate_position );

int insert_entry(	int		archive_handle,
			char		*entry_name,
			signature_type	signature,
			int		kind,
			void		*data_ptr,
			int		data_size );

int delete_entry(	int	archive_handle,
			int	entry_handle );

int get_entry_data(	int	archive_handle,
			int	entry_handle,
			void	*data_buf,
			int	buf_size );

int modify_entry(	int					archive_handle,
			int					entry_handle,
			archive_index_entry_type		*new_archive_index_entry_ptr );

static
struct full_index_entry_type *Find_Full_Index_Entry_In_List(
						struct full_index_entry_type	*root,
						int				entry_handle );

static
int Update_File_Header(	int				archive_index,
			struct archive_header_type	*header_ptr );

static
int Add_Index_Entry_To_List(	int				archive_index,
				struct full_index_entry_type	*new_index_entry_ptr );

static
int Archive_Index_From_Handle(	int	archive_handle );

static
void Print_File_Contents(	int	archive_index );

static
void Print_Header(	struct archive_header_type	*header_ptr );

static
void Print_State(	struct archive_state_type	*state_ptr );

static
void Print_Index_Entry(	struct full_index_entry_type		*index_entry_ptr );

static
void Print_Data_Entry(	void	*data_entry_ptr,
			int	data_entry_size );



/**************************************************************************************************
* open_archive
*
* Open the named archive.
* The supplied parameter "open_mode" indicates how the archive
* should be opened (read only, append, etc.).
* This is then used to call the appropriate open routine, and it is
* the value returned by this called routine that is returned here.
*
* Parameters:
*
**************************************************************************************************/
int open_archive(	char	*archive_name,
			int	open_mode )
{
  int	return_val;


  switch( open_mode ) {
    case OPEN_READ:
      return_val = Open_Read( archive_name );
      break;
    case OPEN_WRITE:
      return_val = Open_Write( archive_name );
      break;
    case OPEN_APPEND:
      return_val = Open_Append( archive_name );
      break;
    case OPEN_UPDATE:
      return_val = Open_Update( archive_name );
      break;
    default:
      if( debug )
	printf( "lib.c, open_archive():  open mode %d unknown\n", open_mode );
      errno = EINVAL;
      return_val = -1;
  } /* end switch */

  return( return_val );
} /* end open_archive() */



/**************************************************************************************************
* Open_Read
*
* Open the named archive for reading only.
* This assumes that the archive already exists; if it doesn't,
* no attempt is made to create it, but instead, an error code is returned.
* If the archive is opened successfully, the header is read into the
* appropriate memory buffer.
*
* Parameters:
*
* Return Values:
* Upon successful completion, the file descriptor is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
static
int Open_Read(	char	*archive_name )
{
  int		archive_des;
  int		o_flag;
  mode_t	mode;				/* mode_t is equiv. to unsigned long */
  size_t	header_size, state_size, full_index_entry_size;
  ssize_t	header_bytes_read;
  off_t		file_size, offset;
  struct archive_header_type		*this_archive_header_ptr;
  struct archive_state_type		*this_archive_state_ptr;
  int		close_return_val, comparison_val, copy_indices_return_val, free_memory_return_val;
  int		errno_save;
  int		current_archive_index, current_archive_handle;
  int		lock_return_val, unlock_return_val;


  /* Check to see if the maximum number of archives have already been opened. */
  if( num_open_archives == MAX_OPEN_ARCHIVES ) {
    if( debug ) {
      printf( "lib.c, Open_Read():  Cannot open archive - maximum number of archives (%d) " );
      printf( "already open\n", MAX_OPEN_ARCHIVES );
    }
    errno = EMFILE;
    return( -1 );
  }

  /* Check if the supplied archive name is a NULL pointer. */
  if( archive_name == NULL ) {
    if( debug )
      printf( "lib.c, Open_Read():  the supplied archive name is a NULL pointer!\n" );
    errno = EFAULT;
    return( -1 );
  }

  o_flag = O_RDWR;
  mode = S_IRUSR | S_IWUSR;	/* read and write access for user (owner) */
  archive_des = open( archive_name, o_flag, mode );
  if( archive_des == -1 ) {
    if( debug )
      printf( "lib.c, Open_Read():  error opening file %s - errno = %d\n", archive_name, errno );
    return( -1 );
  }

  /* Attempt to lock the file contents. */
  lock_return_val = Lock_Entire_File( archive_des );
  if( lock_return_val == -1 ) {
    if( debug )
      printf( "lib.c, Open_Read():  error locking file %s;  errno = %d\n", archive_name, errno );

    errno_save = errno;

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    return( -1 );
  } /* end  attempt to lock file contents */

  num_open_archives++;
  current_archive_index = num_open_archives - 1;

  /* If this is the first archive opened in this session, initialise the archive handles array,
   * the archive descriptors array and the archive names array.  If not, do not initialise
   * these arrays, since this will already have been done, and doing so again
   * will destroy the handles, descriptors and names in use. */
  if( num_open_archives == 1 ) {
    Initialise_Archive_Handles_Array();
    Initialise_Archive_Descriptors_Array();
  }

  /* Generate a new archive handle and store it. */
  current_archive_handle = New_Archive_Handle();
  archive_handles[ current_archive_index ] = current_archive_handle;

  /* Store the new descriptor. */
  archive_descriptors[ current_archive_index ] = archive_des;

  header_size = sizeof( struct archive_header_type );

  /* Check the size of the file against the header size. */
  file_size = lseek( archive_des, 0, SEEK_END );
  if( file_size == -1 ) {
    if( debug )
      printf( "error seeking to find file size in file %s - errno = %d\n", archive_name, errno );

    /* We now save the errno value generated by the lseek() error, since if an error occurs
     * during the close() call below, errno will be overwritten.  If this does happen, we restore
     * errno to this original value, so that this will be the value the user program gets.
     * The reasoning is that since we can only give the user program one errno value, we give
     * the one relating to the first error that occurred, under the assumption that this
     * is the error the user program is most interested in. */
    errno_save = errno;

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    return( -1 );
  }
  else if( file_size < header_size ) {
    if( debug ) {
      printf( "lib.c, Open_Read():  file %s is not a valid archive - ", archive_name );
      printf( "its size is less than header size\n" );
    }

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that the file is not a
     * valid archive.  By setting errno here (after the close call), we make sure of this. */
    errno = EINVAL;
    return( -1 );
  }

  /* At this point we know that the file we have opened is big enough to have a header.
   * We now want to check whether or not the data at the start of the file actually constitutes
   * a valid header.  We start by aquiring heap for the header. */
  this_archive_header_ptr = malloc( header_size );
  if( this_archive_header_ptr == NULL ) {
    if( debug )
      printf( "lib.c, Open_Read():  could not allocate memory for archive %s header\n",
	      archive_name );

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that the malloc() call
     * above failed.  By setting errno here (after the close call), we make sure of this. */
    errno = ENOSPC;
    return( -1 );
  }
  archive_header_ptrs[ current_archive_index ] = this_archive_header_ptr;

  /* Read in header_size bytes from file. */
  offset = 0;
  header_bytes_read = pread( archive_des, this_archive_header_ptr, header_size, offset );
  if( header_bytes_read == -1 ) {
    if( debug )
      printf( "lib.c, Open_Read():  error reading header of archive %s;  errno = %d\n",
	      archive_name, errno );

    /* We now save this errno value.  See earlier in this routine for
     * the reasoning behind this. */
    errno_save = errno;

    free( this_archive_header_ptr );

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    return( -1 );
  }
  else if( header_bytes_read != header_size ) {
    if( debug )
      printf( "lib.c, Open_Read():  only %d/%d header bytes read from archive %s\n",
	      header_bytes_read, header_size, archive_name );

    free( this_archive_header_ptr );

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that not all the header
     * was read.  By setting errno here (after the close call), we make sure of this. */
    errno = EIO;
    return( -1 );
  }

  /* At this point we have read header_size bytes from file.  Now check if these bytes
   * form a valid header by comparing them to the magic number. */
  Set_Magic_Number();
  comparison_val = memcmp( this_archive_header_ptr->magic_number, magic_number,
			   MAGIC_NUMBER_SIZE );
  if( comparison_val == 0 ) {
    if( debug )
      printf( "lib.c, Open_Read():  file %s contains the correct magic number\n", archive_name );
  }
  else {
    /* The file we have is some other kind of file, so close it and return. */
    if( debug )
      printf( "lib.c, Open_Read():  file %s does not contain the correct magic number\n",
	      archive_name );

    free( this_archive_header_ptr );

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that the file does not
     * contain the magic number.  By setting errno here (after the close call),
     * we make sure of this. */
    errno = EINVAL;
    return( -1 );
  }

  /* At this point we assume we are dealing with a valid archive. */

  /* Aquire heap for the archive state structure. */
  state_size = sizeof( struct archive_state_type );
  this_archive_state_ptr = malloc( state_size );
  if( this_archive_state_ptr == NULL ) {
    if( debug )
      printf( "lib.c, Open_Read():  could not allocate memory for archive %s state\n",
	      archive_name );

    free( this_archive_header_ptr );

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that the malloc() call
     * above failed.  By setting errno here (after the close call), we make sure of this. */
    errno = ENOSPC;
    return( -1 );
  }
  archive_state_ptrs[ current_archive_index ] = this_archive_state_ptr;

  /* Initialise the state fields. */
  this_archive_state_ptr->open_mode = OPEN_READ;
  this_archive_state_ptr->root_index_entry_ptr = NULL;
  this_archive_state_ptr->last_index_entry_ptr = NULL;
  this_archive_state_ptr->current_index_entry_ptr = NULL;

  /* Copy the file indices to the list in memory. */
  copy_indices_return_val = Copy_File_Indices_To_Memory( current_archive_index );
  if( copy_indices_return_val == -1 ) {
    if( debug )
      printf( "lib.c, Open_Read():  error copying the file indices to memory;  errno = %d\n",
	      errno );

    /* We now save this errno value.  See earlier in this routine for
     * the reasoning behind this. */
    errno_save = errno;

    /* Some of the file indices may have been copied to memory
     * (with heap being allocated as this was done), so free this. */
    free_memory_return_val = Free_Archive_Memory( current_archive_index );
    if( free_memory_return_val == -1 ) {
      if( debug ) {
	printf( "lib.c, Open_Read():  error freeing the archive memory\n" );
	printf( "errno = %d\n", errno );
      }

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Read():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    return( -1 );
  }

  /* Debug only - print the full index entry size. */
  if( debug )
    printf( "full_index_entry_size = %d\n", sizeof( struct full_index_entry_type ) );

  if( debug )
    printf( "lib.c, Open_Read(): after open, num_open_archives = %d\n", num_open_archives );

  return( current_archive_handle );
} /* end Open_Read() */



/**************************************************************************************************
* Open_Write
*
* Open the named archive for writing only.
* This routine is used when a new, empty archive is needed.  Thus,
* if a file already exists with the name given here, it is destroyed
* (truncated to length zero).  On the other hand, if no archive with
* the supplied name exists, a new archive is created.
* Either way, the archive is opened write-only.
*
* Parameters:
*
* Return Values:
* Upon successful completion, the file descriptor is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
static
int Open_Write(	char	*archive_name )
{
  int		archive_des;
  int		o_flag;
  mode_t	mode;				/* mode_t is equiv. to unsigned long */
  size_t	header_size, state_size;
  struct archive_header_type	*this_archive_header_ptr;
  struct archive_state_type	*this_archive_state_ptr;
  size_t	bytes_to_write;
  int		update_result, close_return_val, errno_save;
  int		current_archive_index, current_archive_handle;
  int		lock_return_val, unlock_return_val;


  /* Check to see if the maximum number of archives have already been opened. */
  if( num_open_archives == MAX_OPEN_ARCHIVES ) {
    if( debug ) {
      printf( "lib.c, Open_Write():  Cannot open archive - maximum number of archives (%d) " );
      printf( "already open\n", MAX_OPEN_ARCHIVES );
    }
    errno = EMFILE;
    return( -1 );
  }

  /* Check if the supplied archive name is a NULL pointer. */
  if( archive_name == NULL ) {
    if( debug )
      printf( "lib.c, Open_Write():  the supplied archive name is a NULL pointer!\n" );
    errno = EFAULT;
    return( -1 );
  }

  o_flag = O_RDWR | O_CREAT | O_TRUNC;
  mode = S_IRUSR | S_IWUSR;	/* read and write access for user (owner) */
  archive_des = open( archive_name, o_flag, mode );
  if( archive_des == -1 ) {
    if( debug )
      printf( "lib.c, Open_Write():  error opening file %s - errno = %d\n", archive_name, errno );
    return( -1 );
  }

  /* Attempt to lock the file contents. */
  lock_return_val = Lock_Entire_File( archive_des );
  if( lock_return_val == -1 ) {
    if( debug )
      printf( "lib.c, Open_Write():  error locking file %s;  errno = %d\n", archive_name, errno );

    errno_save = errno;

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Write():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    return( -1 );
  } /* end  attempt to lock file contents */

  num_open_archives++;
  current_archive_index = num_open_archives - 1;

  /* If this is the first archive opened in this session, initialise the archive handles array,
   * the archive descriptors array and the archive names array.  If not, do not initialise
   * these arrays, since this will already have been done, and doing so again
   * will destroy the handles, descriptors and names in use. */
  if( num_open_archives == 1 ) {
    Initialise_Archive_Handles_Array();
    Initialise_Archive_Descriptors_Array();
  }

  /* Generate a new archive handle and store it. */
  current_archive_handle = New_Archive_Handle();
  archive_handles[ current_archive_index ] = current_archive_handle;

  /* Store the new descriptor. */
  archive_descriptors[ current_archive_index ] = archive_des;

  /* Aquire heap for header. */
  header_size = sizeof( struct archive_header_type );
  this_archive_header_ptr = malloc( header_size );
  if( this_archive_header_ptr == NULL ) {
    if( debug )
      printf( "lib.c, Open_Write():  could not allocate memory for archive %s header\n",
	      archive_name );

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Write():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Write():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that the malloc() call
     * above failed.  By setting errno here (after the close call), we make sure of this. */
    errno = ENOSPC;
    return( -1 );
  }
  archive_header_ptrs[ current_archive_index ] = this_archive_header_ptr;

  /* Initialise header entries.
   * To set the header magic number, we first set the global magic number,
   * then copy this to the header. */
  Set_Magic_Number();
  memmove( this_archive_header_ptr->magic_number, magic_number, MAGIC_NUMBER_SIZE );

  /* Write header to file. */
  update_result = Update_File_Header( current_archive_index, this_archive_header_ptr );
  if( update_result == -1 ) {
    if( debug ) {
      printf( "lib.c, Open_Write():  error updating file header\n" );
      printf( "errno = %d\n", errno );
    }

    /* We now save the errno value generated by the Update_File_Header() error, since
     * if an error occurs during the close() call below, errno will be overwritten.
     * If this does happen, we restore errno to this original value,
     * so that this will be the value the user program gets.
     * The reasoning is that since we can only give the user program one errno value, we give
     * the one relating to the first error that occurred, under the assumption that this
     * is the error the user program is most interested in. */
    errno_save = errno;

    free( this_archive_header_ptr );

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Write():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Write():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    return( -1 );
  }

  /* Aquire heap for archive state structure. */
  state_size = sizeof( struct archive_state_type );
  this_archive_state_ptr = malloc( state_size );
  if( this_archive_state_ptr == NULL ) {
    if( debug )
      printf( "lib.c, Open_Write():  could not allocate memory for archive %s state\n",
	      archive_name );

    free( this_archive_header_ptr );

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Write():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Write():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that the malloc() call
     * above failed.  By setting errno here (after the close call), we make sure of this. */
    errno = ENOSPC;
    return( -1 );
  }
  archive_state_ptrs[ current_archive_index ] = this_archive_state_ptr;

  /* Initialise the state fields. */
  this_archive_state_ptr->open_mode = OPEN_WRITE;
  this_archive_state_ptr->root_index_entry_ptr = NULL;
  this_archive_state_ptr->last_index_entry_ptr = NULL;
  this_archive_state_ptr->current_index_entry_ptr = NULL;

  /* Debug only - print the full index entry size. */
  if( debug )
    printf( "full_index_entry_size = %d\n", sizeof( struct full_index_entry_type ) );

  if( debug )
    printf( "lib.c, Open_Write(): after open, num_open_archives = %d\n", num_open_archives );

  return( current_archive_handle );
} /* end Open_Write() */



/**************************************************************************************************
* Open_Append
*
* Open the named archive for appending only.
*
* Parameters:
*
* Return Values:
* Upon successful completion, the file descriptor is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
static
int Open_Append(	char	*archive_name )
{
  int		archive_des;
  int		o_flag;
  mode_t	mode;					/* mode_t is equiv. to unsigned long */
  size_t	header_size, state_size, full_index_entry_size;
  struct archive_header_type	*this_archive_header_ptr;
  struct archive_state_type	*this_archive_state_ptr;
  size_t	bytes_to_write;
  ssize_t	header_bytes_read;
  off_t		file_size, offset;		/* off_t is equiv. to long */
  int		comparison_val, close_return_val, copy_indices_return_val, update_result;
  int		errno_save, free_memory_return_val;
  int		current_archive_index, current_archive_handle;
  int		lock_return_val, unlock_return_val;


  /* Check to see if the maximum number of archives have already been opened. */
  if( num_open_archives == MAX_OPEN_ARCHIVES ) {
    if( debug ) {
      printf( "lib.c, Open_Append():  Cannot open archive - maximum number of archives (%d) " );
      printf( "already open\n", MAX_OPEN_ARCHIVES );
    }
    errno = EMFILE;
    return( -1 );
  }

  /* Check if the supplied archive name is a NULL pointer. */
  if( archive_name == NULL ) {
    if( debug )
      printf( "lib.c, Open_Append():  the supplied archive name is a NULL pointer!\n" );
    errno = EFAULT;
    return( -1 );
  }

  o_flag = O_RDWR | O_CREAT;
  mode = S_IRUSR | S_IWUSR;	/* read and write access for user (owner) */
  archive_des = open( archive_name, o_flag, mode );
  if( archive_des == -1 ) {
    if( debug )
      printf( "lib.c, Open_Append():  error opening file %s - errno = %d\n", archive_name, errno );
    return( -1 );
  }

  /* Attempt to lock the file contents. */
  lock_return_val = Lock_Entire_File( archive_des );
  if( lock_return_val == -1 ) {
    if( debug )
      printf( "lib.c, Open_Append():  error locking file %s;  errno = %d\n", archive_name, errno );

    errno_save = errno;

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    return( -1 );
  } /* end  attempt to lock file contents */

  num_open_archives++;
  current_archive_index = num_open_archives - 1;

  /* If this is the first archive opened in this session, initialise the archive handles array,
   * the archive descriptors array and the archive names array.  If not, do not initialise
   * these arrays, since this will already have been done, and doing so again
   * will destroy the handles, descriptors and names in use. */
  if( num_open_archives == 1 ) {
    Initialise_Archive_Handles_Array();
    Initialise_Archive_Descriptors_Array();
  }

  /* Generate a new archive handle and store it. */
  current_archive_handle = New_Archive_Handle();
  archive_handles[ current_archive_index ] = current_archive_handle;

  /* Store the new descriptor. */
  archive_descriptors[ current_archive_index ] = archive_des;

  header_size = sizeof( struct archive_header_type );

  /* Find the file size, so we can tell if the file already contained data or not,
   * and if so, if the data is of the appropriate type for this library. */
  file_size = lseek( archive_des, 0, SEEK_END );
  if( file_size == -1 ) {
    if( debug )
      printf( "error seeking to find file size in file %s - errno = %d\n", archive_name, errno );

    /* We now save the errno value generated by the lseek() error, since if an error occurs
     * during the close() call below, errno will be overwritten.  If this does happen, we restore
     * errno to this original value, so that this will be the value the user program gets.
     * The reasoning is that since we can only give the user program one errno value, we give
     * the one relating to the first error that occurred, under the assumption that this
     * is the error the user program is most interested in. */
    errno_save = errno;

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Append():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    return( -1 );
  }
  else if( file_size == 0 ) {
    /* Either the file did not exist at all before the open() call, or it existed
     * but had no contents, ie. had size zero.  Either way, give the file a header. */

    /* Aquire heap for header. */
    this_archive_header_ptr = malloc( header_size );
    if( this_archive_header_ptr == NULL ) {
      if( debug )
	printf( "lib.c, Open_Append():  could not allocate memory for archive %s header\n",
		archive_name );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      /* If an error occurred in the close() call above, errno will now be set for that error.
       * But we do not want this - we want errno to reflect the fact that the malloc() call
       * above failed.  By setting errno here (after the close call), we make sure of this. */
      errno = ENOSPC;
      return( -1 );
    }
    archive_header_ptrs[ current_archive_index ] = this_archive_header_ptr;

    /* Initialise header entries.
     * To set the header magic number, we first set the global magic number,
     * then copy this to the header in memory. */
    Set_Magic_Number();
    memmove( this_archive_header_ptr->magic_number, magic_number, MAGIC_NUMBER_SIZE );

    /* Write header to file. */
    update_result = Update_File_Header( current_archive_index, this_archive_header_ptr );
    if( update_result == -1 ) {
      if( debug ) {
	printf( "lib.c, Open_Append():  error updating file header\n" );
	printf( "errno = %d\n", errno );
      }

      /* We now save this errno value.  See earlier in this routine for
       * the reasoning behind this. */
      errno_save = errno;

      free( this_archive_header_ptr );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );

	/* Restore errno to the saved value. */
	errno = errno_save;
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );

	/* Restore errno to the value saved just above. */
	errno = errno_save;
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      return( -1 );
    }
  }
  else if( file_size < header_size ) {
    if( debug ) {
      printf( "lib.c, Open_Append():  file %s is not a valid archive - ", archive_name );
      printf( "its size is less than header size\n" );
    }

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Append():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that the file is not a
     * valid archive.  By setting errno here (after the close call), we make sure of this. */
    errno = EINVAL;
    return( -1 );
  }
  else {
    /* At this point we know that the file we have opened is big enough to have a header.
     * We now want to check whether or not the data at the start of the file actually constitutes
     * a valid header.  We start by aquiring heap for the header. */
    this_archive_header_ptr = malloc( header_size );
    if( this_archive_header_ptr == NULL ) {
      if( debug )
	printf( "lib.c, Open_Append():  could not allocate memory for archive %s header\n",
		archive_name );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      /* If an error occurred in the close() call above, errno will now be set for that error.
       * But we do not want this - we want errno to reflect the fact that the malloc() call
       * above failed.  By setting errno here (after the close call), we make sure of this. */
      errno = ENOSPC;
      return( -1 );
    }
    archive_header_ptrs[ current_archive_index ] = this_archive_header_ptr;

    /* Read in header_size bytes from file. */
    offset = 0;
    header_bytes_read = pread( archive_des, this_archive_header_ptr, header_size, offset );
    if( header_bytes_read == -1 ) {
      if( debug )
	printf( "lib.c, Open_Append():  error reading header of archive %s;  errno = %d\n",
		archive_name, errno );

      /* We now save this errno value.  See earlier in this routine for
       * the reasoning behind this. */
      errno_save = errno;

      free( this_archive_header_ptr );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );

	/* Restore errno to the saved value. */
	errno = errno_save;
      }
      
      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );

	/* Restore errno to the saved value. */
	errno = errno_save;
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      return( -1 );
    }
    else if( header_bytes_read != header_size ) {
      if( debug )
	printf( "lib.c, Open_Append():  only %d/%d header bytes read from archive %s\n",
		header_bytes_read, header_size, archive_name );

      free( this_archive_header_ptr );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      /* If an error occurred in the close() call above, errno will now be set for that error.
       * But we do not want this - we want errno to reflect the fact that not all the header
       * was read.  By setting errno here (after the close call), we make sure of this. */
      errno = EIO;
      return( -1 );
    }

    /* At this point we have read header_size bytes from file.  Now check if these bytes
     * form a valid header by comparing them to the magic number. */
    Set_Magic_Number();
    comparison_val = memcmp( this_archive_header_ptr->magic_number, magic_number,
			     MAGIC_NUMBER_SIZE );
    if( comparison_val == 0 ) {
      if( debug )
	printf( "lib.c, Open_Append():  file %s contains the correct magic number\n",
		archive_name );
    }
    else {
      /* The file we have is some other kind of file, so close it and return. */
      if( debug )
	printf( "lib.c, Open_Append():  file %s does not contain the correct magic number\n",
		archive_name );

      free( this_archive_header_ptr );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      /* If an error occurred in the close() call above, errno will now be set for that error.
       * But we do not want this - we want errno to reflect the fact that the file does not
       * contain the magic number.  By setting errno here (after the close call),
       * we make sure of this. */
      errno = EINVAL;
      return( -1 );
    }

    /* At this point we assume we are dealing with a valid archive. */

  } /* end  else( file is big enough to have a valid header ) */

  /* At this point the file has been opened and memory allocated for the header.
   * Thus if this routine returns due to error at any point past here,
   * close the file and free the header memory. */

  /* Aquire heap for the archive state structure. */
  state_size = sizeof( struct archive_state_type );
  this_archive_state_ptr = malloc( state_size );
  if( this_archive_state_ptr == NULL ) {
    if( debug )
      printf( "lib.c, Open_Append():  could not allocate memory for archive %s state\n",
	      archive_name );

    free( this_archive_header_ptr );

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Append():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that the malloc() call
     * above failed.  By setting errno here (after the close call), we make sure of this. */
    errno = ENOSPC;
    return( -1 );
  }
  archive_state_ptrs[ current_archive_index ] = this_archive_state_ptr;

  /* Initialise the state fields. */
  this_archive_state_ptr->open_mode = OPEN_APPEND;
  this_archive_state_ptr->root_index_entry_ptr = NULL;
  this_archive_state_ptr->last_index_entry_ptr = NULL;
  this_archive_state_ptr->current_index_entry_ptr = NULL;

  /* Copy the file indices to the list in memory. */
  copy_indices_return_val = Copy_File_Indices_To_Memory( current_archive_index );
  if( copy_indices_return_val == -1 ) {
    if( debug )
      printf( "lib.c, Open_Append():  error copying the file indices to memory;  errno = %d\n",
	      errno );

    /* We now save this errno value.  See earlier in this routine for
     * the reasoning behind this. */
    errno_save = errno;

    /* Some of the file indices may have been copied to memory
     * (with heap being allocated as this was done), so free this. */
    free_memory_return_val = Free_Archive_Memory( current_archive_index );
    if( free_memory_return_val == -1 ) {
      if( debug ) {
	printf( "lib.c, Open_Append():  error freeing the archive memory\n" );
	printf( "errno = %d\n", errno );
      }

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Append():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Append():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    return( -1 );
  }

  /* Debug only - print the full index entry size. */
  if( debug )
    printf( "full_index_entry_size = %d\n", sizeof( struct full_index_entry_type ) );

  if( debug )
    printf( "lib.c, Open_Append():  after open, num_open_archives = %d\n", num_open_archives );

  return( current_archive_handle );
} /* end Open_Append() */



/**************************************************************************************************
* Open_Update
*
* Open the named archive for updating.
*
* Parameters:
*
* Return Values:
* Upon successful completion, the file descriptor is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
static
int Open_Update(	char	*archive_name )
{
  int		archive_des;
  int		o_flag;
  mode_t	mode;					/* mode_t is equiv. to unsigned long */
  size_t	header_size, state_size, full_index_entry_size;
  struct archive_header_type	*this_archive_header_ptr;
  struct archive_state_type	*this_archive_state_ptr;
  size_t	bytes_to_write;
  ssize_t	header_bytes_read;
  off_t		file_size, offset;		/* off_t is equiv. to long */
  int		comparison_val, close_return_val, copy_indices_return_val, update_result;
  int		errno_save, free_memory_return_val;
  int		current_archive_index, current_archive_handle;
  int		lock_return_val, unlock_return_val;


  /* Check to see if the maximum number of archives have already been opened. */
  if( num_open_archives == MAX_OPEN_ARCHIVES ) {
    if( debug ) {
      printf( "lib.c, Open_Update():  Cannot open archive - maximum number of archives (%d) " );
      printf( "already open\n", MAX_OPEN_ARCHIVES );
    }
    errno = EMFILE;
    return( -1 );
  }

  /* Check if the supplied archive name is a NULL pointer. */
  if( archive_name == NULL ) {
    if( debug )
      printf( "lib.c, Open_Update():  the supplied archive name is a NULL pointer!\n" );
    errno = EFAULT;
    return( -1 );
  }

  o_flag = O_RDWR | O_CREAT;
  mode = S_IRUSR | S_IWUSR;	/* read and write access for user (owner) */
  archive_des = open( archive_name, o_flag, mode );
  if( archive_des == -1 ) {
    if( debug )
      printf( "lib.c, Open_Update():  error opening file %s - errno = %d\n", archive_name, errno );
    return( -1 );
  }

  /* Attempt to lock the file contents. */
  lock_return_val = Lock_Entire_File( archive_des );
  if( lock_return_val == -1 ) {
    if( debug )
      printf( "lib.c, Open_Update():  error locking file %s;  errno = %d\n", archive_name, errno );

    errno_save = errno;

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    return( -1 );
  } /* end  attempt to lock file contents */

  num_open_archives++;
  current_archive_index = num_open_archives - 1;

  /* If this is the first archive opened in this session, initialise the archive handles array,
   * the archive descriptors array and the archive names array.  If not, do not initialise
   * these arrays, since this will already have been done, and doing so again
   * will destroy the handles, descriptors and names in use. */
  if( num_open_archives == 1 ) {
    Initialise_Archive_Handles_Array();
    Initialise_Archive_Descriptors_Array();
  }

  /* Generate a new archive handle and store it. */
  current_archive_handle = New_Archive_Handle();
  archive_handles[ current_archive_index ] = current_archive_handle;

  /* Store the new descriptor. */
  archive_descriptors[ current_archive_index ] = archive_des;

  header_size = sizeof( struct archive_header_type );

  /* Find the file size, so we can tell if the file already contained data or not,
   * and if so, if the data is of the appropriate type for this library. */
  file_size = lseek( archive_des, 0, SEEK_END );
  if( file_size == -1 ) {
    if( debug )
      printf( "lib.c, Open_Update():  error seeking to find file size in file %s - errno = %d\n",
	      archive_name, errno );

    /* We now save the errno value generated by the lseek() error, since if an error occurs
     * during the close() call below, errno will be overwritten.  If this does happen, we restore
     * errno to this original value, so that this will be the value the user program gets.
     * The reasoning is that since we can only give the user program one errno value, we give
     * the one relating to the first error that occurred, under the assumption that this
     * is the error the user program is most interested in. */
    errno_save = errno;

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Update():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    return( -1 );
  }
  else if( file_size == 0 ) {
    /* Either the file did not exist at all before the open() call, or it existed
     * but had no contents, ie. had size zero.  Either way, give the file a header. */

    /* Aquire heap for header. */
    this_archive_header_ptr = malloc( header_size );
    if( this_archive_header_ptr == NULL ) {
      if( debug )
	printf( "lib.c, Open_Update():  could not allocate memory for archive %s header\n",
		archive_name );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      /* If an error occurred in the close() call above, errno will now be set for that error.
       * But we do not want this - we want errno to reflect the fact that the malloc() call
       * above failed.  By setting errno here (after the close call), we make sure of this. */
      errno = ENOSPC;
      return( -1 );
    }
    archive_header_ptrs[ current_archive_index ] = this_archive_header_ptr;

    /* Initialise header entries.
     * To set the header magic number, we first set the global magic number,
     * then copy this to the header in memory. */
    Set_Magic_Number();
    memmove( this_archive_header_ptr->magic_number, magic_number, MAGIC_NUMBER_SIZE );

    /* Write header to file. */
    update_result = Update_File_Header( current_archive_index, this_archive_header_ptr );
    if( update_result == -1 ) {
      if( debug ) {
	printf( "lib.c, Open_Update():  error updating file header\n" );
	printf( "errno = %d\n", errno );
      }

      /* We now save this errno value.  See earlier in this routine for
       * the reasoning behind this. */
      errno_save = errno;

      free( this_archive_header_ptr );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );

      /* Restore errno to the saved value. */
	errno = errno_save;
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );

	/* Restore errno to the value saved just above. */
	errno = errno_save;
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      return( -1 );
    }
  }
  else if( file_size < header_size ) {
    if( debug ) {
      printf( "lib.c, Open_Update():  file %s is not a valid archive - ", archive_name );
      printf( "its size is less than header size\n" );
    }

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Update():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that the file is not a
     * valid archive.  By setting errno here (after the close call), we make sure of this. */
    errno = EINVAL;
    return( -1 );
  }
  else {
    /* At this point we know that the file we have opened is big enough to have a header.
     * We now want to check whether or not the data at the start of the file actually constitutes
     * a valid header.  We start by aquiring heap for the header. */
    this_archive_header_ptr = malloc( header_size );
    if( this_archive_header_ptr == NULL ) {
      if( debug )
	printf( "lib.c, Open_Update():  could not allocate memory for archive %s header\n",
		archive_name );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      /* If an error occurred in the close() call above, errno will now be set for that error.
       * But we do not want this - we want errno to reflect the fact that the malloc() call
       * above failed.  By setting errno here (after the close call), we make sure of this. */
      errno = ENOSPC;
      return( -1 );
    }
    archive_header_ptrs[ current_archive_index ] = this_archive_header_ptr;

    /* Read in header_size bytes from file. */
    offset = 0;
    header_bytes_read = pread( archive_des, this_archive_header_ptr, header_size, offset );
    if( header_bytes_read == -1 ) {
      if( debug )
	printf( "lib.c, Open_Update():  error reading header of archive %s;  errno = %d\n",
		archive_name, errno );

      /* We now save this errno value.  See earlier in this routine for
       * the reasoning behind this. */
      errno_save = errno;

      free( this_archive_header_ptr );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );

	/* Restore errno to the saved value. */
	errno = errno_save;
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );

	/* Restore errno to the saved value. */
	errno = errno_save;
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      return( -1 );
    }
    else if( header_bytes_read != header_size ) {
      if( debug )
	printf( "lib.c, Open_Update():  only %d/%d header bytes read from archive %s\n",
		header_bytes_read, header_size, archive_name );

      free( this_archive_header_ptr );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      /* If an error occurred in the close() call above, errno will now be set for that error.
       * But we do not want this - we want errno to reflect the fact that not all the header
       * was read.  By setting errno here (after the close call), we make sure of this. */
      errno = EIO;
      return( -1 );
    }

    /* At this point we have read header_size bytes from file.  Now check if these bytes
     * form a valid header by comparing them to the magic number. */
    Set_Magic_Number();
    comparison_val = memcmp( this_archive_header_ptr->magic_number, magic_number,
			    MAGIC_NUMBER_SIZE );
    if( comparison_val == 0 ) {
      if( debug )
	printf( "lib.c, Open_Update():  file %s contains the correct magic number\n",
		archive_name );
    }
    else {
      /* The file we have is some other kind of file, so close it and return. */
      if( debug )
	printf( "lib.c, Open_Update():  file %s does not contain the correct magic number\n",
		archive_name );

      free( this_archive_header_ptr );

      /* Attempt to unlock the file contents. */
      unlock_return_val = Unlock_Entire_File( archive_des );
      if( unlock_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error unlocking file %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Because we are returning control to the user program, we close the file. */
      close_return_val = close( archive_des );
      if( close_return_val == -1 ) {
	if( debug )
	  printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		  archive_name, errno );
      }

      /* Reset the last valid entry in each of the state arrays so that these entries are
       * listed as free for use.  This must be done before num_open_archives is decremented because
       * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
       * the number of open archives before the closure of this one. */
      Reset_Last_Valid_Array_Entries();

      num_open_archives--;

      /* If an error occurred in the close() call above, errno will now be set for that error.
       * But we do not want this - we want errno to reflect the fact that the file does not
       * contain the magic number.  By setting errno here (after the close call),
       * we make sure of this. */
      errno = EINVAL;
      return( -1 );
    }

    /* At this point we assume we are dealing with a valid archive. */

  } /* end  else( file is big enough to have a valid header ) */

  /* At this point the file has been opened and memory allocated for the header.
   * Thus if this routine returns due to error at any point past here,
   * close the file and free the header memory. */

  /* Aquire heap for the archive state structure. */
  state_size = sizeof( struct archive_state_type );
  this_archive_state_ptr = malloc( state_size );
  if( this_archive_state_ptr == NULL ) {
    if( debug )
      printf( "lib.c, Open_Update():  could not allocate memory for archive %s state\n",
	      archive_name );

    free( this_archive_header_ptr );

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Update():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		archive_name, errno );
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    /* If an error occurred in the close() call above, errno will now be set for that error.
     * But we do not want this - we want errno to reflect the fact that the malloc() call
     * above failed.  By setting errno here (after the close call), we make sure of this. */
    errno = ENOSPC;
    return( -1 );
  }
  archive_state_ptrs[ current_archive_index ] = this_archive_state_ptr;

  /* Initialise the state fields. */
  this_archive_state_ptr->open_mode = OPEN_UPDATE;
  this_archive_state_ptr->root_index_entry_ptr = NULL;
  this_archive_state_ptr->last_index_entry_ptr = NULL;
  this_archive_state_ptr->current_index_entry_ptr = NULL;

  /* Copy the file indices to the list in memory. */
  copy_indices_return_val = Copy_File_Indices_To_Memory( current_archive_index );
  if( copy_indices_return_val == -1 ) {
    if( debug )
      printf( "lib.c, Open_Update():  error copying the file indices to memory;  errno = %d\n",
	      errno );

    /* We now save this errno value.  See earlier in this routine for
     * the reasoning behind this. */
    errno_save = errno;

    /* Some of the file indices may have been copied to memory
     * (with heap being allocated as this was done), so free this. */
    free_memory_return_val = Free_Archive_Memory( current_archive_index );
    if( free_memory_return_val == -1 ) {
      if( debug ) {
	printf( "lib.c, Open_Update():  error freeing the archive memory\n" );
	printf( "errno = %d\n", errno );
      }

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Attempt to unlock the file contents. */
    unlock_return_val = Unlock_Entire_File( archive_des );
    if( unlock_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Update():  error unlocking file %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Because we are returning control to the user program, we close the file. */
    close_return_val = close( archive_des );
    if( close_return_val == -1 ) {
      if( debug )
	printf( "lib.c, Open_Update():  error closing archive %s;  errno = %d\n",
		archive_name, errno );

      /* Restore errno to the saved value. */
      errno = errno_save;
    }

    /* Reset the last valid entry in each of the state arrays so that these entries are
     * listed as free for use.  This must be done before num_open_archives is decremented because
     * the Reset_Last_Valid_Array_Entries() routine expects num_open_archives to represent
     * the number of open archives before the closure of this one. */
    Reset_Last_Valid_Array_Entries();

    num_open_archives--;

    return( -1 );
  }

  /* Debug only - print the full index entry size. */
  if( debug )
    printf( "full_index_entry_size = %d\n", sizeof( struct full_index_entry_type ) );

  if( debug )
    printf( "lib.c, Open_Update():  after open, num_open_archives = %d\n", num_open_archives );

  return( current_archive_handle );
} /* end Open_Update() */



/**************************************************************************************************
* Lock_Entire_File
*
* Lock the entire contents of the file specified by archive_index.
* At present the lockf() primitive is used.  A value of zero for the size argument to lockf()
* locks from the current offset through to the present or any future end-of-file.
* With the file pointer initially pointing to the start of the file,
* this locks the entire file contents.
* The first step, then, is to make sure the file pointer is at the start of the file.
* The second is to actually lock the file.
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
int Lock_Entire_File(	int	archive_des )
{
  off_t		seek_result;
  int		errno_save;
  int		function, lockf_return_val;
  long int	section_size_to_lock;


  /* Make sure the file pointer is at the start of the file. */
  seek_result = lseek( archive_des, 0, SEEK_SET );
  if( seek_result == -1 ) {
    if( debug ) {
      printf( "lib.c, Lock_Entire_File():  error moving file pointer to start of file " );
      printf( "with descriptor %d;  errno = %d\n", archive_des, errno );
    }

    return( -1 );
  }
  else if( seek_result != 0 ) {
    if( debug )
      printf( "lib.c, Lock_Entire_File():  file pointer moved to %d, not start of file\n",
	      seek_result );

    errno = EIO;
    return( -1 );
  }

  /* Now lock the file contents. */
  function = F_TLOCK;		/* test and lock section for exclusive use */
  section_size_to_lock = 0;
  lockf_return_val = lockf( archive_des, function, section_size_to_lock );
  if( lockf_return_val == -1 ) {
    if( debug )
      printf( "lib.c, Lock_Entire_File():  error locking file with descriptor %d;  errno = %d\n",
	      archive_des, errno );

    return( -1 );
  } /* end  if( locking failed ) */

  return( 0 );
} /* end Lock_Entire_File() */



/**************************************************************************************************
* Unlock_Entire_File
*
* Unlock the entire contents of the file specified by archive_index.
* At present the lockf() primitive is used.  A value of zero for the size argument to lockf()
* unlocks from the current offset through to the present or any future end-of-file.
* With the file pointer initially pointing to the start of the file,
* this unlocks the entire file contents.
* The first step, then, is to make sure the file pointer is at the start of the file.
* The second is to actually unlock the file.
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
int Unlock_Entire_File(	int	archive_des )
{
  off_t		seek_result;
  int		errno_save;
  int		function, lockf_return_val;
  long int	section_size_to_unlock;


  /* Make sure the file pointer is at the start of the file. */
  seek_result = lseek( archive_des, 0, SEEK_SET );
  if( seek_result == -1 ) {
    if( debug ) {
      printf( "lib.c, Unlock_Entire_File():  error moving file pointer to start of file " );
      printf( "with descriptor %d;  errno = %d\n", archive_des, errno );
    }

    return( -1 );
  }
  else if( seek_result != 0 ) {
    if( debug )
      printf( "lib.c, Unlock_Entire_File():  file pointer moved to %d, not start of file\n",
	      seek_result );

    errno = EIO;
    return( -1 );
  }

  /* Now unlock the file contents. */
  function = F_ULOCK;		/* unlock a previously locked section */
  section_size_to_unlock = 0;
  lockf_return_val = lockf( archive_des, function, section_size_to_unlock );
  if( lockf_return_val == -1 ) {
    if( debug ) {
      printf( "lib.c, Unlock_Entire_File():  error unlocking file " );
      printf( "with descriptor %d;  errno = %d\n", archive_des, errno );
    }

    return( -1 );
  } /* end  if( unlocking failed ) */

  return( 0 );
} /* end Unlock_Entire_File() */



/**************************************************************************************************
* Initialise_Archive_Handles_Array
*
*
* Parameters:	none
*
* Return Values:
* None.
*
**************************************************************************************************/
static
void Initialise_Archive_Handles_Array(	void )
{
  int	array_index;


  for( array_index = 0;  array_index < MAX_OPEN_ARCHIVES;  array_index++ )
    archive_handles[ array_index ] = NOT_A_HANDLE;
} /* end Initialise_Archive_Handles_Array() */



/**************************************************************************************************
* Initialise_Archive_Descriptors_Array
*
*
* Parameters:	none
*
* Return Values:
* None.
*
**************************************************************************************************/
static
void Initialise_Archive_Descriptors_Array(	void )
{
  int	array_index;


  for( array_index = 0;  array_index < MAX_OPEN_ARCHIVES;  array_index++ )
    archive_descriptors[ array_index ] = NOT_A_DESCRIPTOR;
} /* end Initialise_Archive_Descriptors_Array() */



/**************************************************************************************************
* Set_Magic_Number
*
*
* Parameters:	none
*
* Return Values:
* None.
*
**************************************************************************************************/
static
void Set_Magic_Number(	void )
{
  int	i;


  for( i = 0;  i < MAGIC_NUMBER_SIZE;  i++ ) {
    magic_number[ i ] = 'm';
  }

  if( MAGIC_NUMBER_SIZE > 0 ) {
    magic_number[ 0 ] = 27;	/* escape code */
  }
} /* end Set_Magic_Number() */



/**************************************************************************************************
* Copy_File_Indices_To_Memory
*
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
int Copy_File_Indices_To_Memory(	int	archive_index )
{
  int					this_archive_des;
  struct archive_header_type		*this_archive_header_ptr;
  struct archive_state_type		*this_archive_state_ptr;
  int					finished = FALSE;
  struct full_index_entry_type		*new_full_index_entry_ptr;
  size_t				header_size, full_index_entry_size, bytes_to_read;
  ssize_t				bytes_read;
  int					add_to_list_ret_val;
  off_t					file_size, offset, seek_result;


  this_archive_des = archive_descriptors[ archive_index ];
  this_archive_header_ptr = archive_header_ptrs[ archive_index ];
  this_archive_state_ptr = archive_state_ptrs[ archive_index ];

  if( debug )
    printf( "lib.c, Copy_File_Indices_To_Memory():  archive_index = %d, archive_des = %d\n",
	    archive_index, this_archive_des );

  header_size = sizeof( struct archive_header_type );
  full_index_entry_size = sizeof( struct full_index_entry_type );

  file_size = lseek( this_archive_des, 0, SEEK_END );
  if( file_size == -1 ) {
    if( debug ) {
      printf( "lib.c, Copy_File_Indices_To_Memory():  error finding file size with lseek()\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }

  /* Use the file size to see if there are any entries at all in the file. */
  if( file_size < header_size ) {
    if( debug ) {
      printf( "lib.c, Copy_File_Indices_To_Memory():  file size is less than header size.\n" );
      printf( "\tThis should NEVER happen in this routine!\n" );
    }
    errno = EINVAL;
    return( -1 );
  }
  else if( file_size == header_size ) {
    if( debug )
      printf( "lib.c, Copy_File_Indices_To_Memory():  no entries in file\n" );
    return( 0 );
  }

  /* Set file pointer to just after header. */
  offset = ( off_t )header_size;
  seek_result = lseek( this_archive_des, offset, SEEK_SET );
  if( seek_result == -1 ) {
    if( debug ) {
      printf( "lib.c, Copy_File_Indices_To_Memory():  error setting fp after header\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }
  else if( seek_result != offset ) {
    if( debug ) {
      printf( "lib.c, Copy_File_Indices_To_Memory():  lseek() to set fp after header: " );
      printf( "fp to %d not %d\n", seek_result, offset );
    }
    errno = EIO;
    return( -1 );
  }

  while( finished == FALSE ) {
    /* Add indices from file onto the linked list in memory, aquiring heap as we go. */
    new_full_index_entry_ptr = malloc( full_index_entry_size );
    if( new_full_index_entry_ptr == NULL ) {
      if( debug )
	printf( "lib.c, Copy_File_Indices_To_Memory():  could not allocate memory for index\n" );
      errno = ENOSPC;
      return( -1 );
    }

    /* Debug: print the current file position. */
    seek_result = tell( this_archive_des );
    if( seek_result == -1 ) {
      if( debug )
	printf( "lib.c, Copy_File_Indices_To_Memory():  error from tell(); errno = %d\n", errno );
      free( new_full_index_entry_ptr );
      return( -1 );
    }
    if( debug )
      printf( "lib.c, Copy_File_Indices_To_Memory():  about to read; bytes before here = %d\n",
	      seek_result );

    /* Read index entry from file. */
    bytes_to_read = full_index_entry_size;
    bytes_read = read( this_archive_des, ( void * )new_full_index_entry_ptr, bytes_to_read );
    if( bytes_read == -1 ) {
      if( debug ) {
	printf( "lib.c, Copy_File_Indices_To_Memory():  error reading index entry of archive %d\n",
		this_archive_des );
	printf( "errno = %d\n", errno );
      }
      free( new_full_index_entry_ptr );
      return( -1 );
    }
    else if( bytes_read != bytes_to_read ) {
      if( debug )
	printf( "lib.c, Copy_File_Indices_To_Memory():  only %d/%d index entry bytes read\n",
		bytes_read, bytes_to_read );
      errno = EIO;
      free( new_full_index_entry_ptr );
      return( -1 );
    }
    else {
      if( debug ) {
	printf( "lib.c, Copy_File_Indices_To_Memory():  index entry read ok\n" );
	/*Print_Index_Entry( new_full_index_entry_ptr );*/
      }
    }

    /* Make sure the "next" field of the new entry is NULL. */
    if( new_full_index_entry_ptr->next != NULL ) {
      if( debug ) {
	printf( "lib.c, Copy_File_Indices_To_Memory():  the \"next\" field of the new entry " );
	printf( "is not NULL.\n\tNow setting it to NULL and continuing...\n" );
      }
      new_full_index_entry_ptr->next = NULL;
    }

    add_to_list_ret_val = Add_Index_Entry_To_List( archive_index, new_full_index_entry_ptr );
    if( add_to_list_ret_val == -1 ) {
      if( debug ) {
	printf( "lib.c, Copy_File_Indices_To_Memory():  error adding index entry to list; " );
	printf( "errno = %d\n", errno );
      }
      free( new_full_index_entry_ptr );
      return( -1 );
    }

    if( debug ) {
      printf( "lib.c, Copy_File_Indices_To_Memory():  index entry added to list ok;  " );
      printf( "now Print_State()\n" );
      Print_State( this_archive_state_ptr );
    }

    /* File pointer now points to the end of the index entry.  Use the "offset_of_next" field
     * to find out the file offset of the next entry, if there is one. */
    offset = ( off_t )new_full_index_entry_ptr->private.offset_of_next;
    if( debug )
      printf( "lib.c, Copy_File_Indices_To_Memory():  offset of next entry = %d\n", offset );

    if( offset == NOT_AN_OFFSET ) {
      /* The current entry is the last one in the file. */
      finished = TRUE;
    }
    else {
      /* Move the file pointer to the next entry. */
      seek_result = lseek( this_archive_des, offset, SEEK_SET );
      if( seek_result == -1 ) {
	if( debug ) {
	  printf( "lib.c, Copy_File_Indices_To_Memory():  error setting fp to next file entry; " );
	  printf( "errno = %d\n", errno );
	}
	free( new_full_index_entry_ptr );
	return( -1 );
      }
      else if( seek_result != offset ) {
	if( debug ) {
	  printf( "lib.c, Copy_File_Indices_To_Memory():  lseek() to move fp to next entry: " );
	  printf( "fp to %d not %d\n", seek_result, offset );
	}
	errno = EIO;
	free( new_full_index_entry_ptr );
	return( -1 );
      }
      else {
	if( debug ) {
	  printf( "lib.c, Copy_File_Indices_To_Memory():\n" );
	  printf( "\tlseek() to move fp to the next entry worked ok\n" );
	}
      }
    }
  } /* end while */

  return( 0 );
} /* end Copy_File_Indices_To_Memory() */



/**************************************************************************************************
* New_Archive_Handle
*
* This generates a new archive handle.  It first looks in the archive_handles array
* to see if there are any "gaps" in the allocated handles due to archive closures.
* If so, it uses the first missing handle as the newly-generated handle, otherwise it
* uses a value one greater than the current highest-valued handle.
*
* Parameters:
*
* Return Values:
* The value of the newly-generated handle is returned, which will be some value
* greater than BASE_HANDLE.
*
**************************************************************************************************/
static
int New_Archive_Handle(	void )
{
  int	new_handle;


  new_handle = BASE_HANDLE + 1;

  while( Handle_In_Use( new_handle ) )
    new_handle++;

  return( new_handle );
} /* end New_Archive_Handle() */



/**************************************************************************************************
* Handle_In_Use
*
* This determines if the given handle is already in use, by looking through the
* archive_handles array.
*
* Parameters:
*
* Return Values:
* If the given handle is currently in use, a value of TRUE (1) is returned,
* otherwise a value of FALSE (0) is returned.
*
**************************************************************************************************/
static
int Handle_In_Use(	int	handle )
{
  int	num_handles_in_use, i = 0, this_handle_in_use = FALSE;


  num_handles_in_use = num_open_archives - 1;

  while( ( this_handle_in_use == FALSE ) && ( i < num_handles_in_use ) ) {
    if( archive_handles[ i ] == handle )
      this_handle_in_use = TRUE;
    i++;
  }

  return( this_handle_in_use );
} /* end Handle_In_Use() */



/**************************************************************************************************
* get_first_index
*
* This reads the first index from the file.
* The index is copied into the buffer pointed to by the supplied buffer pointer.
*
* Parameters:
*
* Return Values:
* If the index entry is successfully copied into the buffer, a handle to the index entry
* is returned.  This handle is in fact the entry number for the entry.
* Otherwise, if there are no indices that are not marked "to be deleted",
* a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
int get_first_index(	int					archive_handle,
			archive_index_entry_type		*archive_index_entry_buf_ptr )
{
  int				archive_index;
  struct archive_state_type	*this_archive_state_ptr;
  int				return_val;


  /* Check if the file descriptor presented is valid, and if so, get the appropriate index. */
  archive_index = Archive_Index_From_Handle( archive_handle );
  if( archive_index == -1 ) {
    if( debug ) {
      printf( "lib.c, get_first_index():  file des. given does not refer to an open file\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }
  this_archive_state_ptr = archive_state_ptrs[ archive_index ];

  /* Check if this operation is allowed given the mode of opening for the archive. */
  if( ( this_archive_state_ptr->open_mode != OPEN_READ   ) &&
      ( this_archive_state_ptr->open_mode != OPEN_UPDATE ) ) {
    if( debug )
      printf( "lib.c, get_first_index():  this operation not allowed;  open mode = %d\n",
	      this_archive_state_ptr->open_mode );
    errno = EACCES;
    return( -1 );
  }

  this_archive_state_ptr->current_index_entry_ptr = this_archive_state_ptr->root_index_entry_ptr;

  return_val = get_next_index( archive_handle, archive_index_entry_buf_ptr );

  return( return_val );
} /* end get_first_index() */



/**************************************************************************************************
* get_next_index
*
* This reads the next index from the file, ie. the index that is currently pointed to
* by the file pointer.
* The index is copied into the buffer pointed to by the supplied buffer pointer.
*
* Parameters:
*
* Return Values:
* If the index entry is successfully copied into the buffer, a handle to the index entry
* is returned.  This handle is in fact the entry number for the entry.
* Otherwise, if there are no indices between the current file pointer position and
* the end of the file that are not marked "to_be_deleted", a value of 0 is returned.
* If, however, an error is encountered, a value of -1 is returned
* and errno is set to indicate the error.
*
**************************************************************************************************/
int get_next_index(	int					archive_handle,
			archive_index_entry_type		*archive_index_entry_buf_ptr )
{
  int					archive_index;
  struct archive_header_type		*this_archive_header_ptr;
  struct archive_state_type		*this_archive_state_ptr;
  struct full_index_entry_type		*current_full_index_entry_ptr;
  size_t				header_size, full_index_entry_size, bytes_to_read,
					archive_index_entry_size;
  ssize_t				bytes_read;
  off_t					offset, seek_result;
  int					update_result, return_val;


  /* Check if the file descriptor presented is valid, and if so, get the appropriate index. */
  archive_index = Archive_Index_From_Handle( archive_handle );
  if( archive_index == -1 ) {
    if( debug ) {
      printf( "lib.c, get_next_index():  file descriptor given does not refer to an open file\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }
  this_archive_header_ptr = archive_header_ptrs[ archive_index ];
  this_archive_state_ptr = archive_state_ptrs[ archive_index ];

  /* Check if this operation is allowed given the mode of opening for the archive. */
  if( ( this_archive_state_ptr->open_mode != OPEN_READ   ) &&
      ( this_archive_state_ptr->open_mode != OPEN_UPDATE ) ) {
    if( debug )
      printf( "lib.c, get_next_index():  this operation not allowed;  open mode = %d\n",
	      this_archive_state_ptr->open_mode );
    errno = EACCES;
    return( -1 );
  }

  /* we use the following variable just to reduce the code required to refer to the
   * current full index entry ptr in the future */
  current_full_index_entry_ptr = this_archive_state_ptr->current_index_entry_ptr;

  header_size = sizeof( struct archive_header_type );
  full_index_entry_size = sizeof( struct full_index_entry_type );
  archive_index_entry_size = sizeof( archive_index_entry_type );

  if( current_full_index_entry_ptr == NULL ) {
    /* There are no more entries in the list. */
    if( debug )
      printf( "lib.c, get_next_index():  not an entry\n" );
    return( NOT_AN_ENTRY );
  }
  else {
    /* we haven't yet reached the end of the list of indices in memory */

    /* copy the public part of the current index entry (ie. the index entry pointed to by the
     * current_index_entry pointer) to the buffer pointed to by the supplied buffer pointer */
    memmove( archive_index_entry_buf_ptr, &current_full_index_entry_ptr->archive,
	     archive_index_entry_size );

    if( current_full_index_entry_ptr->private.to_be_deleted ) {
      if( debug )
	printf( "lib.c, get_next_index():  this index entry marked to_be_deleted\n" );

      /* increment current index entry pointer */
      this_archive_state_ptr->current_index_entry_ptr =
					this_archive_state_ptr->current_index_entry_ptr->next;

      return_val = get_next_index( archive_handle, archive_index_entry_buf_ptr );
    } /* end  if( index entry marked to_be_deleted ) */
    else {
      /* increment current index entry pointer */
      this_archive_state_ptr->current_index_entry_ptr =
					this_archive_state_ptr->current_index_entry_ptr->next;

      return_val = current_full_index_entry_ptr->private.num;
    } /* end  else( index entry not marked to_be_deleted ) */
  } /* end  else (current_full_index_entry_ptr != NULL) */

  return( return_val );
} /* end get_next_index() */



/**************************************************************************************************
* close_archive
*
* Close the archive associated with the supplied descriptor.
*
* Parameters:
*
* Return Values:
* Upon successful completion, a value of 0 is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
int close_archive(	int	archive_handle )
{
  int	archive_index, this_archive_des;
  int	remove_deleted_return_val, free_memory_return_val, adjust_array_entries_return_val;
  int	unlock_return_val;
  int	return_val;


  /* Check if the file handle presented is valid.  archive_index is not used here as an
   * index into the appropriate archive arrays.  Instead, its value is just used to check if
   * the Archive_Index_From_Handle() routine returned an error code. */
  archive_index = Archive_Index_From_Handle( archive_handle );
  if( archive_index == -1 ) {
    if( debug ) {
      printf( "lib.c, close_archive():  file descriptor given does not refer to an open file\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }
  this_archive_des = archive_descriptors[ archive_index ];

  remove_deleted_return_val = Remove_Deleted_Entries_From_Archive( archive_index );
  if( remove_deleted_return_val == -1 ) {
    if( debug ) {
      printf( "lib.c, close_archive():  error removing deleted entries from archive\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }

  if( debug )
    Print_File_Contents( archive_index );

  free_memory_return_val = Free_Archive_Memory( archive_index );
  if( free_memory_return_val == -1 ) {
    if( debug ) {
      printf( "lib.c, close_archive():  error freeing the archive memory\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }

  /* Attempt to unlock the file contents. */
  unlock_return_val = Unlock_Entire_File( this_archive_des );
  if( unlock_return_val == -1 ) {
    if( debug )
      printf( "lib.c, close_archive():  error unlocking file with descriptor %d;  errno = %d\n",
	      this_archive_des, errno );
  }

  return_val = close( this_archive_des );
  if( return_val == -1 ) {
    if( debug ) {
      printf( "lib.c, close_archive():  error closing archive with des = %d; ", this_archive_des );
      printf( "errno = %d\n", errno );
    }
  }

  /* Adjust the archive state array entries to "fill the gap" left by the archive being closed. */
  adjust_array_entries_return_val = Adjust_Archive_Array_Entries( archive_index );
  if( adjust_array_entries_return_val == -1 ) {
    if( debug ) {
      printf( "lib.c, close_archive():  error adjusting the archive state array entries\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }

  num_open_archives--;

  return( return_val );
} /* end close_archive() */



/**************************************************************************************************
* Adjust_Archive_Array_Entries
*
* Adjust the archive state array entries to "fill the gap" left by the archive being closed.
* Copy the last valid entry in each array into the location in that array associated with the
* archive being closed (unless it is the archive associated with the last valid array entry
* that is being closed).
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
int Adjust_Archive_Array_Entries(	int	archive_index )
{
  int	index_of_last_valid_array_entry;


  index_of_last_valid_array_entry = num_open_archives - 1;

  if( archive_index == index_of_last_valid_array_entry ) {
    /* It is the archive associated with the last valid array entry that is being closed,
     * so we only need to reset the last valid entry in each state array. */
    Reset_Last_Valid_Array_Entries();
  }
  else if( archive_index < index_of_last_valid_array_entry ) {
    /* The archive being closed is not the one associated with the last valid array entry,
     * so we need to overwrite the gap as well as
     * reset the last valid entry in each state array. */
    Overwrite_Gap_In_Archive_Array_Entries( archive_index );
    Reset_Last_Valid_Array_Entries();
  }
  else {
    if( debug ) {
      printf( "lib.c, close_archive():  archive_index is too large - it is greater than " );
      printf( "the index of the last valid array entry\n" );
    }
    errno = EINVAL;
    return( -1 );
  }

  return( 0 );
} /* end Adjust_Archive_Array_Entries() */



/**************************************************************************************************
* Overwrite_Gap_In_Archive_Array_Entries
*
* Copy the last valid entry in each archive state array into the location in that array
* associated with the archive being closed.
*
* Parameters:
*
* Return Values:
* None.
*
**************************************************************************************************/
static
void Overwrite_Gap_In_Archive_Array_Entries(	int	archive_index )
{
  int	index_of_last_valid_array_entry;


  index_of_last_valid_array_entry = num_open_archives - 1;

  /* Overwrite handles array. */
  archive_handles[ archive_index ] = archive_handles[ index_of_last_valid_array_entry ];

  /* Overwrite descriptors array. */
  archive_descriptors[ archive_index ] = archive_descriptors[ index_of_last_valid_array_entry ];

  /* Overwrite header pointers array. */
  archive_header_ptrs[ archive_index ] = archive_header_ptrs[ index_of_last_valid_array_entry ];

  /* Overwrite state pointers array. */
  archive_state_ptrs[ archive_index ] = archive_state_ptrs[ index_of_last_valid_array_entry ];
} /* end Overwrite_Gap_In_Archive_Array_Entries() */



/**************************************************************************************************
* Reset_Last_Valid_Array_Entries
*
* Reset the last valid entry in each state array by marking the relevant entry
* in each array as "not in use".
*
* Parameters:
*
* Return Values:
* None.
*
**************************************************************************************************/
static
void Reset_Last_Valid_Array_Entries(	void )
{
  int	index_of_last_valid_array_entry;


  index_of_last_valid_array_entry = num_open_archives - 1;

  /* Reset the last valid entry in the handles array. */
  archive_handles[ index_of_last_valid_array_entry ] = NOT_A_HANDLE;

  /* Reset the last valid entry in the descriptors array. */
  archive_descriptors[ index_of_last_valid_array_entry ] = NOT_A_DESCRIPTOR;

  /* Reset the last valid entry in the header pointers array. */
  archive_header_ptrs[ index_of_last_valid_array_entry ] = NULL;

  /* Reset the last valid entry in the state pointers array. */
  archive_state_ptrs[ index_of_last_valid_array_entry ] = NULL;
} /* end Reset_Last_Valid_Array_Entries() */



/**************************************************************************************************
* Free_Archive_Memory
*
* Free all memory dynamically allocated for the archive whose descriptor is given.
* This includes the header memory and the state memory.
* Most of the allocated memory will be in the state, specifically in the linked list used
* to hold the index entries.
*
* Parameters:
*
* Return Values:
* None.
*
**************************************************************************************************/
static
int Free_Archive_Memory(	int	archive_index )
{
  struct archive_header_type	*this_archive_header_ptr;
  struct archive_state_type	*this_archive_state_ptr;
  struct full_index_entry_type	*root;


  if( debug )
    printf( "lib.c, Free_Archive_Memory():  archive_index = %d\n", archive_index );

  /* Free header memory. */
  this_archive_header_ptr = archive_header_ptrs[ archive_index ];
  free( this_archive_header_ptr );

  /* Free state - first free linked list of index entries... */
  this_archive_state_ptr = archive_state_ptrs[ archive_index ];
  root = this_archive_state_ptr->root_index_entry_ptr;
  if( root == NULL ) {
    /* linked list of index entries is empty - nothing to free */
  }
  else
    Free_List( root );

  /* ...then the state pointer for this archive. */
  free( this_archive_state_ptr );

  return( 0 );
} /* end Free_Archive_Memory() */



/**************************************************************************************************
* Free_List
*
* Free all dynamically allocated memory associated with the linked list
* whose root pointer is given.
* The freeing is done recursively - this routine will thus sometimes be called with
* a pointer that is part of the way down the list as its argument.  In this case,
* the pointer given is the head of the rest of the list.
*
* Parameters:
*
* Return Values:
* None.
*
**************************************************************************************************/
static
void Free_List(	struct full_index_entry_type		*index_entry_ptr )
{
  if( index_entry_ptr->next == NULL )
    free( index_entry_ptr );
  else {
    Free_List( index_entry_ptr->next );
    free( index_entry_ptr );
  }
} /* end Free_List() */



/**************************************************************************************************
* Remove_Deleted_Entries_From_Archive
*
* Remove the entries that have been marked as deleted from the archive.
*
* Parameters:
*
* Return Values:
* Upon successful completion, a value of 0 is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
static
int Remove_Deleted_Entries_From_Archive(	int	archive_index )
{
  int					this_archive_des;
  struct archive_header_type		*this_archive_header_ptr;
  struct archive_state_type		*this_archive_state_ptr;
  size_t				header_size, full_index_entry_size;
  off_t					file_size;
  int					file_finished, non_deleted_entry_found;
  int					return_val;
  int					previous_offset, current_offset, lookahead_offset;
  struct full_index_entry_type		*current_full_index_entry_ptr,
					*lookahead_full_index_entry_ptr;
  ssize_t				bytes_written, bytes_read;
  int					data_entry_offset, data_entry_size;
  int					end_of_current_entry_offset, next_entry_offset;
  int					offset_to_copy_to, offset_to_copy_from;

  /* If the last entry in the file is deleted, there will no longer be an index entry that has
   * the value NOT_AN_OFFSET in its "offset_of_next" field, ie. no entry that signals the end
   * of the file.  The "assign_not_an_offset" variable is used to indicate which entry, if any,
   * is the last valid file entry after the removal of the gaps and the entries marked as deleted.
   * This entry then has the value NOT_AN_OFFSET assigned to its "offset_of_next" field,
   * and thus takes over the role as the entry that marks the end of the file. */
  int					assign_not_an_offset = NO_ASSIGN;

  /* When entries marked as deleted are copied over by entries from later in the file, there
   * ends up being a gap at the end of the file, ie. a section immediately before the end
   * of the file containing useless information.  The "truncate_position" variable is used to
   * indicate where this gap starts.  The file is then truncated there. */
  int					truncate_position = NO_TRUNCATE;


  this_archive_des = archive_descriptors[ archive_index ];
  this_archive_header_ptr = archive_header_ptrs[ archive_index ];
  this_archive_state_ptr = archive_state_ptrs[ archive_index ];

  if( debug )
    printf( "lib.c, Remove_Deleted_Entries...():  archive_index = %d, archive_des = %d\n",
	    archive_index, this_archive_des );

  header_size = sizeof( struct archive_header_type );
  full_index_entry_size = sizeof( struct full_index_entry_type );

  file_size = lseek( this_archive_des, 0, SEEK_END );
  if( file_size == -1 ) {
    if( debug ) {
      printf( "lib.c, Remove_Deleted_Entries...():  error finding the file size with lseek()\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }

  /* Use the file size to see if there are any entries at all in the file. */
  if( file_size < header_size ) {
    if( debug ) {
      printf( "lib.c, Remove_Deleted_Entries...():  file size is less than header size.\n" );
      printf( "\tThis should NEVER happen in this routine!\n" );
    }
    errno = EINVAL;
    return( -1 );
  }
  else if( file_size == header_size ) {
    if( debug )
      printf( "lib.c, Remove_Deleted_Entries...():  no entries in file\n" );
    return( 0 );
  }

  current_full_index_entry_ptr = malloc( full_index_entry_size );
  if( current_full_index_entry_ptr == NULL ) {
    if( debug )
      printf( "lib.c, Remove_Deleted_Entries...():  malloc() failed for current index entry\n" );
    errno = ENOSPC;
    return( -1 );
  }

  lookahead_full_index_entry_ptr = malloc( full_index_entry_size );
  if( lookahead_full_index_entry_ptr == NULL ) {
    if( debug )
      printf( "lib.c, Remove_Deleted_Entries...():  malloc() failed for lookahead index entry\n" );
    errno = ENOSPC;
    free( current_full_index_entry_ptr );
    return( -1 );
  }

  for( file_finished = FALSE, previous_offset = NOT_AN_OFFSET, current_offset = header_size;
       file_finished == FALSE; ) {
    bytes_read = pread( this_archive_des, current_full_index_entry_ptr, full_index_entry_size,
			current_offset );
    if( bytes_read == -1 ) {
      if( debug ) {
	printf( "lib.c, Remove_Deleted_Entries...():  error reading current index entry\n" );
	printf( "errno = %d\n", errno );
      }
      free( current_full_index_entry_ptr );
      free( lookahead_full_index_entry_ptr );
      return( -1 );
    }
    else if( bytes_read != full_index_entry_size ) {
      if( debug )
	printf( "lib.c, Remove_Deleted_Entries...():  only %d/%d current index entry bytes read\n",
		bytes_read, full_index_entry_size );
      errno = EIO;
      free( current_full_index_entry_ptr );
      free( lookahead_full_index_entry_ptr );
      return( -1 );
    }

    if( current_full_index_entry_ptr->private.to_be_deleted ) {
      if( debug ) {
	printf( "lib.c, Remove_Deleted_Entries...():  index entry %d is marked as deleted;\n",
		current_full_index_entry_ptr->private.num );
	printf( "\tnow looking for the next entry not marked (A code)...\n" );
      }

      lookahead_offset = current_full_index_entry_ptr->private.offset_of_next;
      if( lookahead_offset == NOT_AN_OFFSET ) {
	if( debug ) {
	  printf( "lib.c, Remove_Deleted_Entries...():  lookahead_offset == NOT_AN_OFFSET;\n" );
	  printf( "\tno need to enter lookahead while{} loop (exit point 1)\n" );
	}
	file_finished = TRUE;	/* exit point 1 */

	/* If previous_offset is currently NOT_AN_OFFSET, then this last entry must also be
	 * the first, ie. the only entry in the file, so do nothing, since this lone entry
	 * will be removed.  Otherwise, the NOT_AN_OFFSET value is assigned to the
	 * previous index entry. */
	assign_not_an_offset = ( previous_offset == NOT_AN_OFFSET ) ? NO_ASSIGN : PREVIOUS_ENTRY;

	/* Here the current entry is marked as deleted, and there are no entries after it,
	 * so truncate the file at the start of the current entry. */
	truncate_position = START_OF_CURRENT_ENTRY;
      }

      non_deleted_entry_found = FALSE;
      while( ( file_finished == FALSE ) && ( non_deleted_entry_found == FALSE ) ) {
	bytes_read = pread( this_archive_des, lookahead_full_index_entry_ptr,
			    full_index_entry_size, lookahead_offset );
	if( bytes_read == -1 ) {
	  if( debug ) {
	    printf( "lib.c, Remove_Deleted_Entries...():  error reading lookahead index entry " );
	    printf( "(A code);  errno = %d\n", errno );
	  }
	  free( current_full_index_entry_ptr );
	  free( lookahead_full_index_entry_ptr );
	  return( -1 );
	}
	else if( bytes_read != full_index_entry_size ) {
	  if( debug ) {
	    printf( "lib.c, Remove_Deleted_Entries...():  only %d/%d " );
	    printf( "lookahead index entry bytes read\n", bytes_read, full_index_entry_size );
	  }
	  errno = EIO;
	  free( current_full_index_entry_ptr );
	  free( lookahead_full_index_entry_ptr );
	  return( -1 );
	}

	if( lookahead_full_index_entry_ptr->private.to_be_deleted ) {
	  if( debug ) {
	    printf( "lib.c, Remove_Deleted_Entries...():  index entry %d is marked as deleted;\n",
		    lookahead_full_index_entry_ptr->private.num );
	    printf( "\tnow looking for next entry that is not marked (A lookahead code)...\n" );
	  }
	  lookahead_offset = lookahead_full_index_entry_ptr->private.offset_of_next;
	  if( lookahead_offset == NOT_AN_OFFSET ) {
	    if( debug ) {
	      printf( "lib.c, Remove_Deleted_Entries...():  lookahead_offset == NOT_AN_OFFSET; " );
	      printf( "no need to enter lookahead while{} loop (exit point 2)\n" );
	    }
	    file_finished = TRUE;	/* exit point 2 */

	    /* If previous_offset is currently NOT_AN_OFFSET, then all entries are marked
	     * as deleted, and the file will be truncated at the start of them,
	     * ie. straight after the header, so no need to do anything.
	     * Otherwise, the NOT_AN_OFFSET value is assigned to the previous index entry. */
	    assign_not_an_offset = ( previous_offset == NOT_AN_OFFSET ) ? NO_ASSIGN
									: PREVIOUS_ENTRY;

	    /* Here the current entry is marked as deleted, and there are no entries after it
	     * that are not marked as deleted,
	     * so truncate the file at the start of the current entry. */
	    truncate_position = START_OF_CURRENT_ENTRY;
	  }
	}
	else {
	  if( debug ) {
	    printf( "lib.c, Remove_Deleted_Entries...():  index entry %d not marked as deleted;\n",
		    lookahead_full_index_entry_ptr->private.num );
	    printf( "\tabout to copy this lookahead entry to current_offset position (A code)\n" );
	  }
	  non_deleted_entry_found = TRUE;

	  /* We leave current_offset as it is, so that next time through the outer for{} loop,
	   * current_offset will be in the right place so that the gap created by this copying
	   * will be picked up by the gap-checking code following. */
	  return_val = Copy_Entry_To_New_File_Position( archive_index, previous_offset,
							current_offset, lookahead_offset );
	  if( return_val == -1 ) {
	    if( debug ) {
	      printf( "lib.c, Remove_Deleted_Entries...():  error copying entry to " );
	      printf( "new file position (A code);  errno = %d\n", errno );
	    }
	    free( current_full_index_entry_ptr );
	    free( lookahead_full_index_entry_ptr );
	    return( -1 );
	  }
	}
      } /* end lookahead while{} */
    } /* end if current entry to be deleted */
    else if( ( next_entry_offset = current_full_index_entry_ptr->private.offset_of_next ) ==
	     NOT_AN_OFFSET ) {
      /* Either there is nothing at all after this in the file, or just a gap.
       * Either way, there are no entries after it. */
      if( debug ) {
	printf( "lib.c, Remove_Deleted_Entries...():  next_entry_offset == NOT_AN_OFFSET " );
	printf( "(exit point 3)\n" );
      }
      file_finished = TRUE;	/* exit point 3 */

      /* No assigning need be done - this last entry already has the NOT_AN_OFFSET value
       * in its "offset_of_next" field. */
      assign_not_an_offset = NO_ASSIGN;

      /* The current entry is not marked as deleted, and there are no entries after it.
       * Now check if there is a gap after it. */
      data_entry_offset = current_full_index_entry_ptr->private.offset_of_data;
      data_entry_size = current_full_index_entry_ptr->archive.data_size;

      end_of_current_entry_offset = data_entry_offset + data_entry_size;

      if( debug ) {
	printf( "lib.c, Remove_Deleted_Entries...():  at exit point 3:\n" );
	printf( "\tend_of_current_entry_offset = %d, file_size = %d\n",
		end_of_current_entry_offset, file_size );
      }

      if( end_of_current_entry_offset == file_size ) {
	/* There is no gap after this entry, so no need to truncate. */
	truncate_position = NO_TRUNCATE;
      }
      else if( end_of_current_entry_offset < file_size ) {
	/* There is a gap after the current entry, so truncate at the end of it. */
	truncate_position = END_OF_CURRENT_ENTRY;
      }
      else {
	if( debug ) {
	  printf( "lib.c, Remove_Deleted_Entries...():  this should NEVER happen:  " );
	  printf( "end_of_current_entry_offset > file_size (exit point 3)\n" );
	}
	errno = ENOEXEC;
	free( current_full_index_entry_ptr );
	free( lookahead_full_index_entry_ptr );
	return( -1 );
      }
    }
    else {
      /* There is at least one entry after current entry in the file. */

      /* Check if there is a gap following this entry. */
      data_entry_offset = current_full_index_entry_ptr->private.offset_of_data;
      data_entry_size = current_full_index_entry_ptr->archive.data_size;

      end_of_current_entry_offset = data_entry_offset + data_entry_size;

      /* We know at this point that next_entry_offset != NOT_AN_OFFSET, ie. that there is
       * at least one more entry in the file, because of the check just above. */
      if( next_entry_offset != end_of_current_entry_offset ) {
	if( debug ) {
	  printf( "lib.c, Remove_Deleted_Entries...():  current_offset = %d, ", current_offset );
	  printf( "and there is a gap ahead of it\n" );
	  printf( "\tnext_entry_offset = %d and end_of_current_entry_offset = %d\n",
		  next_entry_offset, end_of_current_entry_offset );
	  printf( "\tabout to find next entry not marked to copy into gap (B code)...\n" );
	}

	/* Jump the gap to get the next entry.
	 * We know there is a next entry because of the NOT_AN_OFFSET check above. */
	lookahead_offset = next_entry_offset;

	non_deleted_entry_found = FALSE;
	while( ( file_finished == FALSE ) && ( non_deleted_entry_found == FALSE ) ) {
	  bytes_read = pread( this_archive_des, lookahead_full_index_entry_ptr,
			      full_index_entry_size, lookahead_offset );
	  if( bytes_read == -1 ) {
	    if( debug ) {
	      printf( "lib.c, Remove_Deleted_Entries...():  error reading " );
	      printf( "lookahead index entry (B code);  errno = %d\n", errno );
	    }
	    free( current_full_index_entry_ptr );
	    free( lookahead_full_index_entry_ptr );
	    return( -1 );
	  }
	  else if( bytes_read != full_index_entry_size ) {
	    if( debug ) {
	      printf( "lib.c, Remove_Deleted_Entries...():  only %d/%d " );
	      printf( "lookahead index entry bytes read\n", bytes_read, full_index_entry_size );
	    }
	    errno = EIO;
	    free( current_full_index_entry_ptr );
	    free( lookahead_full_index_entry_ptr );
	    return( -1 );
	  }

	  if( lookahead_full_index_entry_ptr->private.to_be_deleted ) {
	    if( debug ) {
	      printf( "lib.c, Remove_Deleted_Entries...():  index entry %d is marked as deleted; ",
		      lookahead_full_index_entry_ptr->private.num );
	      printf( "now looking for next entry that is not marked (B lookahead code)...\n" );
	    }
	    lookahead_offset = lookahead_full_index_entry_ptr->private.offset_of_next;
	    if( lookahead_offset == NOT_AN_OFFSET ) {
	      if( debug ) {
		printf( "lib.c, Remove_Deleted_Entries...():  " );
		printf( "lookahead_offset == NOT_AN_OFFSET; " );
		printf( "no need to enter lookahead while{} loop (exit point 4)\n" );
	      }
	      file_finished = TRUE;	/* exit point 4 */

	      /* After the current entry, which is not marked as deleted, there is
	       * an arbitrary-sized gap followed by any number of entries marked as deleted,
	       * then the end of the file.
	       * Thus assign the NOT_AN_OFFSET value to the current index entry. */
	      assign_not_an_offset = ( previous_offset == NOT_AN_OFFSET ) ? NO_ASSIGN
									  : PREVIOUS_ENTRY;

	      /* Following the same reasoning as above, truncate at the end of
	       * the current entry. */
	      truncate_position = END_OF_CURRENT_ENTRY;
	    }
	  }
	  else {
	    if( debug ) {
	      printf( "lib.c, Remove_Deleted_Entries...():  index entry %d not marked deleted\n",
		      lookahead_full_index_entry_ptr->private.num );
	      printf( "\tabout to copy this lookahead entry into the gap (B code)\n" );
	    }
	    non_deleted_entry_found = TRUE;

	    /* We now copy the lookahead entry into the gap.  The "previous" offset as far as
	     * Copy_Entry_To_New_File_Position() is concerned, ie. the offset of the entry before
	     * the position in the file to copy to, is given by "current_offset" in this case,
	     * since in this section of code, the current entry being looked at is always
	     * the one immediately before the gap. */
	    offset_to_copy_to = end_of_current_entry_offset;	/* the start of the gap */
	    offset_to_copy_from = lookahead_offset;
	    return_val = Copy_Entry_To_New_File_Position( archive_index, current_offset,
							  offset_to_copy_to,
							  offset_to_copy_from );
	    if( return_val == -1 ) {
	      if( debug ) {
		printf( "lib.c, Remove_Deleted_Entries...():  error copying entry to " );
		printf( "new file position (B code);  errno = %d\n", errno );
	      }
	      free( current_full_index_entry_ptr );
	      free( lookahead_full_index_entry_ptr );
	      return( -1 );
	    }

	    /* Advance current_offset to the offset of the new entry (where the gap was).
	     * This is, of course, where the current entry finishes.
	     * No need to check if current offset is NOT_AN_OFFSET - it cannot be. */
	    current_offset = end_of_current_entry_offset;
	  }
	} /* end lookahead while{} */
      } /* end else if( next_entry_offset != end_of_current_entry_offset ) */
      else {
	if( debug ) {
	  printf( "lib.c, Remove_Deleted_Entries...():  current_offset = %d, ", current_offset );
	  printf( "and no gap ahead of it\n " );
	  printf( "\tnext_entry_offset and end_of_current_entry_offset match\n" );
	}

	/* Also, no entries marked "to_be_deleted" have been encountered so far. */
	if( debug )
	  printf( "\tindex entry %d is not marked to be deleted\n",
		  current_full_index_entry_ptr->private.num );

	/* Set "previous_offset" to the "current_offset" value,
	 * and set "current_offset" to the start of the next entry. */
	previous_offset = current_offset;
	current_offset = current_full_index_entry_ptr->private.offset_of_next;
	if( debug )
	  printf( "lib.c, Remove_Deleted_Entries...():  new current_offset value: %d\n",
		  current_offset );
	if( current_offset == NOT_AN_OFFSET ) {
	  if( debug ) {
	    printf( "lib.c, Remove_Deleted_Entries...():  current_offset == NOT_AN_OFFSET; " );
	    printf( "now finishing for{} loop (exit point 5)\n" );
	  }
	  file_finished = TRUE;	/* exit point 5 */

	  /* No gaps or entries marked as deleted have been encountered so far,
	   * ie. the file is still "clean".  As a result, the last entry will still have the
	   * NOT_AN_OFFSET value in its "offset_of_next" field, so no assigning needed. */
	  assign_not_an_offset = NO_ASSIGN;

	  /* The current entry is not marked as deleted, and file is still "clean" (see above),
	   * so no need to truncate. */
	  truncate_position == NO_TRUNCATE;
	}
      } /* end else no gap */
    } /* end else current entry is not to be deleted */
  } /* end for */

  free( current_full_index_entry_ptr );
  free( lookahead_full_index_entry_ptr );

  return_val = Assign_Not_An_Offset_And_Truncate( archive_index,
						  previous_offset, current_offset,
						  assign_not_an_offset, truncate_position );
  if( return_val == -1 ) {
    if( debug ) {
      printf( "lib.c, Remove_Deleted_Entries...():  error value returned from " );
      printf( "Assign_Not_An_Offset_And_Truncate();  errno = %d\n", errno );
    }
    return( -1 );
  }

  return( 0 );
} /* end Remove_Deleted_Entries_From_Archive() */



/**************************************************************************************************
* Copy_Entry_To_New_File_Position
*
* Copy an archive entry to an earlier position in the file.  This routine is called by
* Remove_Deleted_Entries_From_Archive() which actually removes from the file
* the entries marked for deletion.
* The new copy of the entry is re-numbered to reflect its new position in the file.
*
* Parameters:
*
* Return Values:
* Upon successful completion, a value of 0 is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
static
int Copy_Entry_To_New_File_Position(	int	archive_index,
					int	previous_entry_offset,
					int	dest_offset,
					int	source_offset )
{
  int					this_archive_des;
  ssize_t				bytes_read, bytes_written;
  size_t				full_index_entry_size;
  int					old_data_offset, new_data_offset, data_entry_size;
  struct full_index_entry_type		*previous_full_index_entry_ptr,
					*source_full_index_entry_ptr;
  int					new_entry_num;
  void					*data_entry_ptr;


  this_archive_des = archive_descriptors[ archive_index ];

  if( debug ) {
    printf( "lib.c, Copy_Entry_To_New_File_Position():  just entered\n" );
    printf( "\tarchive_index = %d, archive_des = %d\n", archive_index, this_archive_des );
    printf( "\tprevious_entry_offset = %d, dest_offset = %d, source_offset = %d\n",
	    previous_entry_offset, dest_offset, source_offset );
  }

  full_index_entry_size = sizeof( struct full_index_entry_type );

  /* Allocate memory for the source index entry. */
  source_full_index_entry_ptr = malloc( full_index_entry_size );
  if( source_full_index_entry_ptr == NULL ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  couldn't allocate for " );
      printf( "source index entry\n" );
    }
    errno = ENOSPC;
    return( -1 );
  }

  /* Read the source index entry into memory. */
  bytes_read = pread( this_archive_des, source_full_index_entry_ptr, full_index_entry_size,
		      source_offset );
  if( bytes_read == -1 ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  error reading source index entry\n" );
      printf( "errno = %d\n", errno );
    }
    free( source_full_index_entry_ptr );
    return( -1 );
  }
  else if( bytes_read != full_index_entry_size ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  only " );
      printf( "%d/%d source index entry bytes read\n", bytes_read, full_index_entry_size );
    }
    errno = EIO;
    free( source_full_index_entry_ptr );
    return( -1 );
  }

  /* Update the start_offset field of the entry to be copied. */
  source_full_index_entry_ptr->private.start_offset = dest_offset;

  /* Write the source index entry to the destination offset. */
  bytes_written = pwrite( this_archive_des, source_full_index_entry_ptr, full_index_entry_size,
			  dest_offset );
  if( bytes_written == -1 ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  error writing source index entry " );
      printf( "to new destination;  errno = %d\n", errno );
    }
    free( source_full_index_entry_ptr );
    return( -1 );
  }
  else if( bytes_written != full_index_entry_size ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  only " );
      printf( "%d/%d source index entry bytes written to new destination\n",
	      bytes_written, full_index_entry_size );
    }
    errno = EIO;
    free( source_full_index_entry_ptr );
    return( -1 );
  }

  /* We now copy the data entry to the new location.  First allocate for the data entry. */
  data_entry_size = source_full_index_entry_ptr->archive.data_size;
  data_entry_ptr = malloc( data_entry_size );
  if( data_entry_ptr == NULL ) {
    if( debug )
      printf( "lib.c, Copy_Entry_To_New_File_Position():  couldn't allocate for data entry\n" );
    errno = ENOSPC;
    free( source_full_index_entry_ptr );
    return( -1 );
  }

  /* Read the data entry into memory. */
  old_data_offset = source_full_index_entry_ptr->private.offset_of_data;
  if( debug )
    printf( "lib.c, Copy_Entry_To_New_File_Position():  old data offset = %d\n", old_data_offset );

  bytes_read = pread( this_archive_des, data_entry_ptr, data_entry_size, old_data_offset );
  if( bytes_read == -1 ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  error reading data entry\n" );
      printf( "errno = %d\n", errno );
    }
    free( source_full_index_entry_ptr );
    free( data_entry_ptr );
    return( -1 );
  }
  else if( bytes_read != data_entry_size ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  only " );
      printf( "%d/%d data entry bytes read\n", bytes_read, data_entry_size );
    }
    errno = EIO;
    free( source_full_index_entry_ptr );
    free( data_entry_ptr );
    return( -1 );
  }

  /* The new data offset is the size of an index entry past where the index was copied to. */
  new_data_offset = dest_offset + full_index_entry_size;
  if( debug )
    printf( "lib.c, Copy_Entry_To_New_File_Position():  new data offset = %d\n", new_data_offset );

  /* Write the data entry to the new offset. */
  bytes_written = pwrite( this_archive_des, data_entry_ptr, data_entry_size, new_data_offset );
  if( bytes_written == -1 ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  error writing data entry\n" );
      printf( "errno = %d\n", errno );
    }
    free( source_full_index_entry_ptr );
    free( data_entry_ptr );
    return( -1 );
  }
  else if( bytes_written != data_entry_size ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  only " );
      printf( "%d/%d data entry bytes written\n", bytes_written, data_entry_size );
    }
    errno = EIO;
    free( source_full_index_entry_ptr );
    free( data_entry_ptr );
    return( -1 );
  }

  /* We now re-number the newly copied entry. */
  if( previous_entry_offset == NOT_AN_OFFSET ) {
    /* There is no previous entry in the file - the position being copied to is immediately
     * after the header, ie. the first entry in the file. */
    new_entry_num = 1;
  }
  else {
    previous_full_index_entry_ptr = malloc( full_index_entry_size );
    if( previous_full_index_entry_ptr == NULL ) {
      if( debug ) {
	printf( "lib.c, Copy_Entry_To_New_File_Position():  couldn't allocate for " );
	printf( "previous index entry\n" );
      }
      errno = ENOSPC;
      free( source_full_index_entry_ptr );
      free( data_entry_ptr );
      return( -1 );
    }

    bytes_read = pread( this_archive_des, previous_full_index_entry_ptr, full_index_entry_size,
			previous_entry_offset );
    if( bytes_read == -1 ) {
      if( debug ) {
	printf( "lib.c, Copy_Entry_To_New_File_Position():  " );
	printf( "error reading previous index entry\n" );
	printf( "errno = %d\n", errno );
      }
      free( source_full_index_entry_ptr );
      free( data_entry_ptr );
      free( previous_full_index_entry_ptr );
      return( -1 );
    }
    else if( bytes_read != full_index_entry_size ) {
      if( debug ) {
	printf( "lib.c, Copy_Entry_To_New_File_Position():  only " );
	printf( "%d/%d previous index entry bytes read\n", bytes_read, full_index_entry_size );
      }
      errno = EIO;
      free( source_full_index_entry_ptr );
      free( data_entry_ptr );
      free( previous_full_index_entry_ptr );
      return( -1 );
    }

    new_entry_num = previous_full_index_entry_ptr->private.num + 1;
  }
  source_full_index_entry_ptr->private.num = new_entry_num;

  /* Also update the data offset field. */
  source_full_index_entry_ptr->private.offset_of_data = new_data_offset;

  /* Write the changes to file. */
  bytes_written = pwrite( this_archive_des, source_full_index_entry_ptr, full_index_entry_size,
			  dest_offset );
  if( bytes_written == -1 ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  error updating entry number " );
      printf( "and data offset for just-copied entry\n" );
      printf( "errno = %d\n", errno );
    }
    free( source_full_index_entry_ptr );
    free( data_entry_ptr );
    if( previous_entry_offset != NOT_AN_OFFSET )    /* this space will only have been allocated */
      free( previous_full_index_entry_ptr );	    /* if there is a previous entry */
    return( -1 );
  }
  else if( bytes_written != full_index_entry_size ) {
    if( debug ) {
      printf( "lib.c, Copy_Entry_To_New_File_Position():  only " );
      printf( "%d/%d bytes written in updating entry number and data offset\n",
	      bytes_written, full_index_entry_size );
    }
    errno = EIO;
    free( source_full_index_entry_ptr );
    free( data_entry_ptr );
    if( previous_entry_offset != NOT_AN_OFFSET )    /* this space will only have been allocated */
      free( previous_full_index_entry_ptr );        /* if there is a previous entry */
    return( -1 );
  }

  /* If there is a previous entry, update the offset_of_next field in its index. */
  if( previous_entry_offset != NOT_AN_OFFSET ) {
    /* Since there is a previous entry, memory will already have been allocated for it above,
     * and it will already have read into that memory. */
    previous_full_index_entry_ptr->private.offset_of_next = dest_offset;

    bytes_written = pwrite( this_archive_des, previous_full_index_entry_ptr, full_index_entry_size,
			    previous_entry_offset );
    if( bytes_written == -1 ) {
      if( debug ) {
	printf( "lib.c, Copy_Entry_To_New_File_Position():  " );
	printf( "error writing previous index entry\n" );
	printf( "errno = %d\n", errno );
      }
      free( source_full_index_entry_ptr );
      free( data_entry_ptr );
      free( previous_full_index_entry_ptr );
      return( -1 );
    }
    else if( bytes_written != full_index_entry_size ) {
      if( debug ) {
	printf( "lib.c, Copy_Entry_To_New_File_Position():  only " );
	printf( "%d/%d previous index entry bytes written\n",
		bytes_written, full_index_entry_size );
      }
      errno = EIO;
      free( source_full_index_entry_ptr );
      free( data_entry_ptr );
      free( previous_full_index_entry_ptr );
      return( -1 );
    }

    free( previous_full_index_entry_ptr );
  }

  free( source_full_index_entry_ptr );
  free( data_entry_ptr );

  return( 0 );
} /* end Copy_Entry_To_New_File_Position() */



/**************************************************************************************************
* Assign_Not_An_Offset_And_Truncate
*
*
* Parameters:
*
* Return Values:
* Upon successful completion, a value of 0 is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
static
int Assign_Not_An_Offset_And_Truncate(	int	archive_index,
					int	previous_entry_offset,
					int	current_entry_offset,
					int	assign_not_an_offset,
					int	truncate_position )
{
  int					this_archive_des;
  size_t				full_index_entry_size;
  struct full_index_entry_type		*full_index_entry_ptr;
  ssize_t				bytes_read, bytes_written;
  int					offset_of_file_contents_to_modify;
  int					offset_of_current_entry_data, size_of_current_entry_data;
  int					end_of_current_entry_offset;
  int					return_val;


  this_archive_des = archive_descriptors[ archive_index ];

  if( debug ) {
    printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  just entered\n" );
    printf( "\tarchive_index = %d, archive_des = %d\n", archive_index, this_archive_des );
    printf( "\tprevious_entry_offset = %d, current_entry_offset = %d\n",
	    previous_entry_offset, current_entry_offset );
    printf( "\tassign_not_an_offset = %d, truncate_position = %d\n",
	    assign_not_an_offset, truncate_position );
  }

  full_index_entry_size = sizeof( struct full_index_entry_type );

  /* First assign the NOT_AN_OFFSET value to the "offset_of_next" field of
   * the appropriate entry. */
  if( assign_not_an_offset == NO_ASSIGN ) {
    /* Do nothing - either there are no entries in the file or the last entry already has
     * the NOT_AN_OFFSET value in its "offset_of_next" field. */
  }
  else if( ( assign_not_an_offset == PREVIOUS_ENTRY ) ||
	   ( assign_not_an_offset == CURRENT_ENTRY  ) ) {
    /* Work out which offset we want to use. */
    offset_of_file_contents_to_modify = ( assign_not_an_offset == PREVIOUS_ENTRY ) ?
					previous_entry_offset : current_entry_offset;
    if( debug ) {
      printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  " );
      printf( "offset_of_file_contents_to_modify = %d\n", offset_of_file_contents_to_modify );
    }

    full_index_entry_ptr = malloc( full_index_entry_size );
    if( full_index_entry_ptr == NULL ) {
      if( debug ) {
	printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  couldn't allocate for " );
	printf( "previous index entry\n" );
      }
      errno = ENOSPC;
      return( -1 );
    }

    /* Read the current version of the index entry to be modified. */
    bytes_read = pread( this_archive_des, full_index_entry_ptr, full_index_entry_size,
			offset_of_file_contents_to_modify );
    if( bytes_read == -1 ) {
      if( debug ) {
	printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  error reading index entry\n" );
	printf( "errno = %d\n", errno );
      }
      free( full_index_entry_ptr );
      return( -1 );
    }
    else if( bytes_read != full_index_entry_size ) {
      if( debug ) {
	printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  only " );
	printf( "%d/%d index entry bytes read\n", bytes_read, full_index_entry_size );
      }
      errno = EIO;
      free( full_index_entry_ptr );
      return( -1 );
    }

    full_index_entry_ptr->private.offset_of_next = NOT_AN_OFFSET;

    /* Now write the change back to file. */
    bytes_written = pwrite( this_archive_des, full_index_entry_ptr, full_index_entry_size,
			    offset_of_file_contents_to_modify );
    if( bytes_written == -1 ) {
      if( debug ) {
	printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  error writing index entry\n" );
	printf( "errno = %d\n", errno );
      }
      free( full_index_entry_ptr );
      return( -1 );
    }
    else if( bytes_written != full_index_entry_size ) {
      if( debug ) {
	printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  only " );
	printf( "%d/%d index entry bytes written\n", bytes_written, full_index_entry_size );
      }
      errno = EIO;
      free( full_index_entry_ptr );
      return( -1 );
    }

    free( full_index_entry_ptr );
  } /* end  else if( assign_not_an_offset == PREVIOUS_ENTRY or CURRENT_ENTRY ) */
  else {
    if( debug ) {
      printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  Oops! assign_not_an_offset = %d\n",
	      assign_not_an_offset );
      printf( "\tand it should never be this value\n" );
    }
    errno = EINVAL;
    return( -1 );
  }

  /* Now truncate the file at the position indicated by truncate_position. */
  switch( truncate_position ) {
    case NO_TRUNCATE:
      if( debug )
	printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  no need to truncate\n" );
      break;
    case START_OF_CURRENT_ENTRY:
      return_val = ftruncate( this_archive_des, current_entry_offset );
      if( return_val == -1 ) {
	if( debug ) {
	  printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  error truncating file at " );
	  printf( "start of current entry\n" );
	  printf( "errno = %d\n", errno );
	}
	return( -1 );
      }

      break;
    case END_OF_CURRENT_ENTRY:
      /* Allocate memory to read the current entry into. */
      full_index_entry_ptr = malloc( full_index_entry_size );
      if( full_index_entry_ptr == NULL ) {
	if( debug ) {
	  printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  in attempting to truncate at\n " );
	  printf( "\tthe end of the current entry, error allocating for current index entry\n" );
	}
	errno = ENOSPC;
	return( -1 );
      }

      /* Read the current index entry to get the information needed to calculate
       * the offset of the end of the current entry. */
      bytes_read = pread( this_archive_des, full_index_entry_ptr, full_index_entry_size,
			 current_entry_offset );
      if( bytes_read == -1 ) {
	if( debug ) {
	  printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  in attempting to truncate at\n" );
	  printf( "\tthe end of the current entry, error reading index entry\n" );
	  printf( "errno = %d\n", errno );
	}
	free( full_index_entry_ptr );
	return( -1 );
      }
      else if( bytes_read != full_index_entry_size ) {
	if( debug ) {
	  printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  in attempting to truncate at\n" );
	  printf( "\tthe end of the current entry, only %d/%d index entry bytes read\n",
		  bytes_read, full_index_entry_size );
	}
	errno = EIO;
	free( full_index_entry_ptr );
	return( -1 );
      }

      offset_of_current_entry_data = full_index_entry_ptr->private.offset_of_data;
      size_of_current_entry_data = full_index_entry_ptr->archive.data_size;

      end_of_current_entry_offset = offset_of_current_entry_data + size_of_current_entry_data;

      if( debug ) {
	printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  about to truncate " );
	printf( "at END_OF_CURRENT_ENTRY:\n" );
	printf( "\toffset_of_current_entry_data = %d, size_of_current_entry_data = %d\n",
		offset_of_current_entry_data, size_of_current_entry_data );
	printf( "\tend_of_current_entry_offset (the truncate offset) = %d\n",
		end_of_current_entry_offset );
      }

      return_val = ftruncate( this_archive_des, end_of_current_entry_offset );
      if( return_val == -1 ) {
	if( debug ) {
	  printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  error truncating file at " );
	  printf( "end of current entry\n" );
	  printf( "errno = %d\n", errno );
	}
	free( full_index_entry_ptr );
	return( -1 );
      }

      free( full_index_entry_ptr );

      break;
    default:
      if( debug ) {
	printf( "lib.c, Assign_Not_An_Offset_And_Truncate():  Oops! truncate_position = %d\n",
		truncate_position );
	printf( "\tand it should never be this value\n" );
      }
      errno = EINVAL;
      return( -1 );
  }

  return( 0 );
} /* end Assign_Not_An_Offset_And_Truncate() */



/**************************************************************************************************
* insert_entry
*
* Add an entry onto the end of the archive.
*
* Parameters:
*
* Return Values:
* Upon successful completion, the number of bytes actually written is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
int insert_entry(	int		archive_handle,
			char		*entry_name,
			signature_type	signature,
			int		kind,
			void		*data_ptr,
			int		data_size )
{
  int				archive_index, this_archive_des;
  struct archive_header_type	*this_archive_header_ptr;
  struct archive_state_type	*this_archive_state_ptr;
  size_t			full_index_entry_size;
  struct full_index_entry_type	*new_full_index_entry_ptr;
  int				new_entry_num;
  time_t			current_time;
  off_t				initial_file_size, seek_result, offset, offset_of_data,
				bytes_before_new_data_entry;
  ssize_t			bytes_written;
  int				return_val, add_index_entry_return_val, update_result;
  struct full_index_entry_type	*root, *previous_full_index_entry_ptr;
  int				entry_name_size;


  /* Check if the file handle presented is valid, and if so, get the appropriate index. */
  archive_index = Archive_Index_From_Handle( archive_handle );
  if( archive_index == -1 ) {
    if( debug ) {
      printf( "lib.c, insert_entry():  file descriptor given does not refer to an open file\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }
  this_archive_des = archive_descriptors[ archive_index ];
  this_archive_header_ptr = archive_header_ptrs[ archive_index ];
  this_archive_state_ptr = archive_state_ptrs[ archive_index ];

  /* make sure that this operation is allowed given the mode of opening for the archive */
  if( ( this_archive_state_ptr->open_mode != OPEN_WRITE  ) &&
      ( this_archive_state_ptr->open_mode != OPEN_APPEND ) &&
      ( this_archive_state_ptr->open_mode != OPEN_UPDATE ) ) {
    if( debug )
      printf( "lib.c, insert_entry():  this operation not allowed;  open mode = %d\n",
	      this_archive_state_ptr->open_mode );
    errno = EACCES;
    return( -1 );
  }

  /* Check if the entry name given is a NULL pointer. */
  if( entry_name == NULL ) {
    if( debug )
      printf( "lib.c, insert_entry():  the entry name given is a NULL pointer\n" );
    errno = EFAULT;
    return( -1 );
  }

  /* create index entry in memory */
  full_index_entry_size = sizeof( struct full_index_entry_type );
  new_full_index_entry_ptr = malloc( full_index_entry_size );
  if( new_full_index_entry_ptr == NULL ) {
    if( debug )
      printf( "lib.c, insert_entry():  could not allocate memory for new index entry\n" );
    errno = ENOSPC;
    return( -1 );
  }

  /* Find the entry number of the previous entry, if one exists. */
  previous_full_index_entry_ptr = this_archive_state_ptr->last_index_entry_ptr;
  if( previous_full_index_entry_ptr == NULL ) {
    if( debug ) {
      printf( "lib.c, insert_entry():  can't get the previous entry number - " );
      printf( "no previous entry\n" );
    }
    new_entry_num = 1;
  }
  else {
    new_entry_num = previous_full_index_entry_ptr->private.num + 1;
  }

  /* find the file size, to be used for the start_offset member below */
  initial_file_size = lseek( this_archive_des, 0, SEEK_END );
  if( initial_file_size == -1 ) {
    if( debug )
      printf( "lib.c, insert_entry():  error finding file size with lseek();  errno = %d\n",
	      errno );
    free( new_full_index_entry_ptr );
    return( -1 );
  }
  else {
    if( debug )
      printf( "lib.c, insert_entry():  lseek() to find initial_file_size ok;  size = %d\n",
	      initial_file_size );
  }

  /* Calculate the offset of the data entry. */
  offset_of_data = initial_file_size + full_index_entry_size;

  if( debug ) {
    /* Print the string size of the incoming entry name. */
    entry_name_size = strlen( entry_name );
    printf( "lib.c, insert_entry():  size of entry name = %d\n", entry_name_size );
  }

  /* Assign values to the index entry fields. */

  /* Copy the first MAX_ENTRY_NAME_SIZE characters, ie. the characters with indices
   * 0 to (MAX_ENTRY_NAME_SIZE - 1) inclusive, of the supplied entry_name string into the
   * name field, then null-terminate the name field at index MAX_ENTRY_NAME_SIZE (the last). */
  strncpy( new_full_index_entry_ptr->archive.name, entry_name, MAX_ENTRY_NAME_SIZE );
  new_full_index_entry_ptr->archive.name[ MAX_ENTRY_NAME_SIZE ] = NULL_CHAR;

  /* Copy the first SIGNATURE_SIZE bytes of the supplied signature into the signature field. */
  memmove( new_full_index_entry_ptr->archive.signature, signature, SIGNATURE_SIZE );

  /* time() returns the number of seconds elapsed since 00:00:00 UTC, January 1, 1970. */
  current_time = time( NULL );
  if( debug )
    printf( "lib.c, insert_entry():  current_time = %ld\n", current_time );
  new_full_index_entry_ptr->archive.date_created	= current_time;
  new_full_index_entry_ptr->archive.date_last_modified	= current_time;

  new_full_index_entry_ptr->archive.data_size		= data_size;
  new_full_index_entry_ptr->archive.kind		= kind;

  new_full_index_entry_ptr->private.num			= new_entry_num;
  new_full_index_entry_ptr->private.start_offset	= initial_file_size;
  new_full_index_entry_ptr->private.offset_of_data	= offset_of_data;
  new_full_index_entry_ptr->private.offset_of_next	= NOT_AN_OFFSET;
  new_full_index_entry_ptr->private.to_be_deleted	= FALSE;
  new_full_index_entry_ptr->next			= NULL;

  /* write data entry to file, leaving space for the index */
  bytes_before_new_data_entry = lseek( this_archive_des, offset_of_data, SEEK_SET );
  if( bytes_before_new_data_entry == -1 ) {
    if( debug ) {
      printf( "lib.c, insert_entry():  error setting fp for new data entry\n" );
      printf( "errno = %d\n", errno );
    }
    free( new_full_index_entry_ptr );
    return( -1 );
  }
  else if( bytes_before_new_data_entry != offset_of_data ) {
    if( debug )
      printf( "lib.c, insert_entry():  lseek() for index entry moved fp to %d not %d\n",
	      bytes_before_new_data_entry, offset_of_data );
    errno = EIO;
    free( new_full_index_entry_ptr );
    return( -1 );
  }
  else {
    if( debug ) {
      printf( "lib.c, insert_entry():  lseek() to set fp for new data entry ok\n" );
      printf( "bytes_before_new_data_entry = %d\n", bytes_before_new_data_entry );
    }
  }
  bytes_written = write( this_archive_des, data_ptr, data_size );
  if( bytes_written == -1 ) {
    if( debug )
      printf( "lib.c, insert_entry():  error writing data entry to file;  errno = %d\n", errno );
    ftruncate( this_archive_des, initial_file_size );
    free( new_full_index_entry_ptr );
    return( -1 );
  }
  else if( bytes_written != data_size ) {
    if( debug )
      printf( "lib.c, insert_entry():  only %d/%d data entry bytes written\n",
	      bytes_written, data_size );
    errno = EIO;
    ftruncate( this_archive_des, initial_file_size );
    free( new_full_index_entry_ptr );
    return( -1 );
  }
  else {
    if( debug )
      printf( "lib.c, insert_entry():  data entry written to file ok\n" );
    return_val = ( int )bytes_written;
  }

  /* write index to file, in gap previously left for it;  the index is written where
   * the previous end of file was */
  offset = initial_file_size;
  seek_result = lseek( this_archive_des, offset, SEEK_SET );
  if( seek_result == -1 ) {
    if( debug ) {
      printf( "lib.c, insert_entry():  error moving fp for index entry\n" );
      printf( "errno = %d\n", errno );
    }
    ftruncate( this_archive_des, initial_file_size );
    free( new_full_index_entry_ptr );
    return( -1 );
  }
  else if( seek_result != offset ) {
    if( debug )
      printf( "lib.c, insert_entry():  lseek for index entry moved fp to %d not %d\n",
	      seek_result, offset );
    errno = EIO;
    ftruncate( this_archive_des, initial_file_size );
    free( new_full_index_entry_ptr );
    return( -1 );
  }
  bytes_written = write( this_archive_des, new_full_index_entry_ptr, full_index_entry_size );
  if( bytes_written == -1 ) {
    if( debug )
      printf( "lib.c, insert_entry():  error writing index entry;  errno = %d\n", errno );
    ftruncate( this_archive_des, initial_file_size );
    free( new_full_index_entry_ptr );
    return( -1 );
  }
  else if( bytes_written != full_index_entry_size ) {
    if( debug )
      printf( "lib.c, insert_entry():  only %d/%d index entry bytes written\n",
	      bytes_written, full_index_entry_size );
    errno = EIO;
    ftruncate( this_archive_des, initial_file_size );
    free( new_full_index_entry_ptr );
    return( -1 );
  }
  else {
    if( debug )
      printf( "lib.c, insert_entry():  index entry written to file ok\n" );
  }

  /* If there was at least one entry in the archive before this new one, change the index
   * of the previous entry so that its "offset_of_next" field refers to this new entry. */
  if( previous_full_index_entry_ptr ) {
    previous_full_index_entry_ptr->private.offset_of_next = initial_file_size;

    /* Now write this change to file. */
    offset = previous_full_index_entry_ptr->private.start_offset;
    seek_result = lseek( this_archive_des, offset, SEEK_SET );
    if( seek_result == -1 ) {
      if( debug ) {
	printf( "lib.c, insert_entry():  error moving fp for writing previous index entry\n" );
	printf( "errno = %d\n", errno );
      }
      ftruncate( this_archive_des, initial_file_size );
      free( new_full_index_entry_ptr );
      return( -1 );
    }
    else if( seek_result != offset ) {
      if( debug ) {
	printf( "lib.c, insert_entry():  lseek() for writing previous index entry " );
	printf( "moved fp to %d not %d\n", seek_result, offset );
      }
      errno = EIO;
      ftruncate( this_archive_des, initial_file_size );
      free( new_full_index_entry_ptr );
      return( -1 );
    }
    bytes_written = write( this_archive_des, previous_full_index_entry_ptr,
			   full_index_entry_size );
    if( bytes_written == -1 ) {
      if( debug )
	printf( "lib.c, insert_entry():  error writing previous index entry;  errno = %d\n",
		errno );
      ftruncate( this_archive_des, initial_file_size );
      free( new_full_index_entry_ptr );
      return( -1 );
    }
    else if( bytes_written != full_index_entry_size ) {
      if( debug )
	printf( "lib.c, insert_entry():  only %d/%d previous index entry bytes written\n",
		bytes_written, full_index_entry_size );
      errno = EIO;
      ftruncate( this_archive_des, initial_file_size );
      free( new_full_index_entry_ptr );
      return( -1 );
    }
  }
  else {
    if( debug ) {
      printf( "lib.c, insert_entry():  can't alter previous index - this new entry " );
      printf( "is the first in this archive\n" );
    }
  }

  /* Add the new index entry to the linked list kept in memory. */
  add_index_entry_return_val = Add_Index_Entry_To_List( archive_index, new_full_index_entry_ptr );
  if( add_index_entry_return_val == -1 ) {
    if( debug ) {
      printf( "lib.c, insert_entry():  error adding index entry to linked list in memory; " );
      printf( "errno = %d\n", errno );
    }
    ftruncate( this_archive_des, initial_file_size );
    free( new_full_index_entry_ptr );
    return ( -1 );
  }

  return( return_val );
} /* end insert_entry() */



/**************************************************************************************************
* delete_entry
*
* Delete an entry from the archive.  This routine in fact just marks the given entry as deleted,
* leaving the actual deleting to be done by the close_archive() routine.
*
* Parameters:
*
* Return Values:
* Upon successful completion, a value of 0 is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
int delete_entry(	int	archive_handle,
			int	entry_handle )
{
  int				archive_index, this_archive_des;
  struct archive_header_type	*this_archive_header_ptr;
  struct archive_state_type	*this_archive_state_ptr;
  struct full_index_entry_type	*root, *full_index_entry_ptr, *last_full_index_entry_ptr;
  int				last_entry_num;
  size_t			full_index_entry_size;
  off_t				index_entry_offset, seek_result;
  ssize_t			bytes_written;
  int				update_result, return_val;


  /* Check if the file handle presented is valid, and if so, get the appropriate index. */
  archive_index = Archive_Index_From_Handle( archive_handle );
  if( archive_index == -1 ) {
    if( debug ) {
      printf( "lib.c, delete_entry():  file descriptor given does not refer to an open file\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }
  this_archive_des = archive_descriptors[ archive_index ];
  this_archive_header_ptr = archive_header_ptrs[ archive_index ];
  this_archive_state_ptr = archive_state_ptrs[ archive_index ];
  root = this_archive_state_ptr->root_index_entry_ptr;

  /* Check if this operation is allowed given the mode of opening for the archive. */
  if( this_archive_state_ptr->open_mode != OPEN_UPDATE ) {
    if( debug )
      printf( "lib.c, delete_entry():  this operation not allowed;  open mode = %d\n",
	      this_archive_state_ptr->open_mode );
    errno = EACCES;
    return( -1 );
  }

  /* Find the entry number of the last index entry, for use in range checking below. */
  last_full_index_entry_ptr = this_archive_state_ptr->last_index_entry_ptr;
  if( last_full_index_entry_ptr == NULL ) {
    if( debug )
      printf( "lib.c, delete_entry():  no entries to delete\n" );
    return( 0 );
  }
  else {
    last_entry_num = last_full_index_entry_ptr->private.num;
  }

  /* Check if the entry handle presented is in the accepted range of values. */
  if( ( entry_handle < 1 ) || ( entry_handle > last_entry_num ) ) {
    if( debug ) {
      printf( "lib.c, delete_entry():  the entry handle given is outside the acceptable range " );
      printf( "- it does not refer to a valid entry.\n" );
    }
    return( 0 );
  }

  /* find the entry in the index */
  full_index_entry_ptr = Find_Full_Index_Entry_In_List( root, entry_handle );
  if( full_index_entry_ptr ) {
    if( debug )
      printf( "lib.c, delete_entry():  entry found\n" );
    if( full_index_entry_ptr->private.to_be_deleted == TRUE ) {
      /* The entry is already marked as deleted - return successfully. */
      return( 0 );
    }

    /* mark entry as deleted */
    full_index_entry_ptr->private.to_be_deleted = TRUE;

    /* find position in file for this entry, and write it to file */
    full_index_entry_size = sizeof( struct full_index_entry_type );
    index_entry_offset = full_index_entry_ptr->private.start_offset;
    if( debug )
      printf( "lib.c, delete_entry():  index_entry_offset = %d\n", index_entry_offset );
    seek_result = lseek( this_archive_des, index_entry_offset, SEEK_SET );
    if( seek_result == -1 ) {
      if( debug ) {
	printf( "lib.c, delete_entry():  error moving fp for index entry\n" );
	printf( "errno = %d\n", errno );
      }

      /* Because the change could not be made to file, undo the change already made to memory. */
      full_index_entry_ptr->private.to_be_deleted = FALSE;

      return( -1 );
    }
    else if( seek_result != index_entry_offset ) {
      if( debug )
	printf( "lib.c, delete_entry():  lseek() for index entry moved fp to %d not %d\n",
		seek_result, index_entry_offset );
      errno = EIO;
      full_index_entry_ptr->private.to_be_deleted = FALSE;	/* see above for explanation */
      return( -1 );
    }
    bytes_written = write( this_archive_des, full_index_entry_ptr, full_index_entry_size );
    if( bytes_written == -1 ) {
      if( debug )
	printf( "lib.c, delete_entry():  error updating index entry;  errno = %d\n", errno );
      full_index_entry_ptr->private.to_be_deleted = FALSE;	/* see above for explanation */
      return( -1 );
    }
    else if( bytes_written != full_index_entry_size ) {
      if( debug )
	printf( "lib.c, delete_entry():  only %d/%d index entry bytes written\n",
		bytes_written, full_index_entry_size );
      errno = EIO;
      full_index_entry_ptr->private.to_be_deleted = FALSE;	/* see above for explanation */
      return( -1 );
    }
    else {
      if( debug )
	printf( "lib.c, delete_entry():  index entry updated on file ok\n" );
      return_val = 0;
    }
  }
  else {
    if( debug )
      printf( "lib.c, delete_entry():  entry number %d not found\n", entry_handle );
    return( 0 );
  }

  return( return_val );
} /* end delete_entry() */



/**************************************************************************************************
* get_entry_data
*
* This attempts to get the entry data from file for the entry whose number is entry_handle.
* It reads up to buf_size bytes from the file associated with archive_des into the
* buffer pointed to by data_buf, that is, it will read the entire data entry unless it
* is bigger than buf_size, in which case it will read buf_size bytes.
*
* Parameters:
*
* Return Values:
* Upon successful completion, the number of bytes actually read is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
int get_entry_data(	int	archive_handle,
			int	entry_handle,
			void	*data_buf,
			int	buf_size )
{
  int				archive_index, this_archive_des;
  struct archive_header_type	*this_archive_header_ptr;
  struct archive_state_type	*this_archive_state_ptr;
  struct full_index_entry_type	*root, *full_index_entry_ptr, *last_full_index_entry_ptr;
  int				last_entry_num;
  size_t			bytes_to_read;
  int				data_entry_size;
  off_t				offset_of_data, seek_result;
  ssize_t			bytes_read;
  int				return_val;


  /* Check if the file handle presented is valid, and if so, get the appropriate index. */
  archive_index = Archive_Index_From_Handle( archive_handle );
  if( archive_index == -1 ) {
    if( debug ) {
      printf( "lib.c, get_entry_data():  file descriptor given does not refer to an open file\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }
  this_archive_des = archive_descriptors[ archive_index ];
  this_archive_header_ptr = archive_header_ptrs[ archive_index ];
  this_archive_state_ptr = archive_state_ptrs[ archive_index ];
  root = this_archive_state_ptr->root_index_entry_ptr;

  /* Check if this operation is allowed given the mode of opening for the archive. */
  if( ( this_archive_state_ptr->open_mode != OPEN_READ   ) &&
      ( this_archive_state_ptr->open_mode != OPEN_UPDATE ) ) {
    if( debug )
      printf( "lib.c, get_entry_data():  this operation not allowed;  open mode = %d\n",
	      this_archive_state_ptr->open_mode );
    errno = EACCES;
    return( -1 );
  }

  /* Find the entry number of the last index entry, for use in range checking below. */
  last_full_index_entry_ptr = this_archive_state_ptr->last_index_entry_ptr;
  if( last_full_index_entry_ptr == NULL ) {
    if( debug )
      printf( "lib.c, get_entry_data():  no entries in file\n" );
    errno = EINVAL;
    return( -1 );
  }
  else {
    last_entry_num = last_full_index_entry_ptr->private.num;
  }

  /* Check if the entry handle presented is in the accepted range of values. */
  if( ( entry_handle < 1 ) || ( entry_handle > last_entry_num ) ) {
    if( debug ) {
      printf( "lib.c, get_entry_data():  entry handle given is outside acceptable range " );
      printf( "- it does not refer to a valid entry.\n" );
    }
    errno = EINVAL;
    return( -1 );
  }

  /* find the entry in the index */
  full_index_entry_ptr = Find_Full_Index_Entry_In_List( root, entry_handle );
  if( full_index_entry_ptr ) {
    if( debug )
      printf( "lib.c, get_entry_data():  entry found\n" );
    if( full_index_entry_ptr->private.to_be_deleted == TRUE ) {
      if( debug ) {
	printf( "lib.c, get_entry_data():  the entry with that handle is marked " );
	printf( "\"to_be_deleted\"\n" );
      }
      errno = EINVAL;
      return( -1 );
    }

    offset_of_data = full_index_entry_ptr->private.offset_of_data;
    if( debug )
      printf( "lib.c, get_entry_data():  offset_of_data = %d\n", offset_of_data );

    /* The number of bytes to read is the lesser of the size of the data block and
     * the buffer size given. */
    data_entry_size = full_index_entry_ptr->archive.data_size;
    bytes_to_read = MIN_VAL( data_entry_size, buf_size );
    if( debug )
      printf( "lib.c, get_entry_data():  data entry size = %d, buf_size = %d, min. = %d\n",
	      data_entry_size, buf_size, bytes_to_read );

    /* Move the file pointer to the start of the data entry. */
    seek_result = lseek( this_archive_des, offset_of_data, SEEK_SET );
    if( seek_result == -1 ) {
      if( debug ) {
	printf( "lib.c, get_entry_data():  error moving fp to start of data entry\n" );
	printf( "errno = %d\n", errno );
      }
      return( -1 );
    }
    else if( seek_result != offset_of_data ) {
      if( debug )
	printf( "lib.c, get_entry_data():  lseek() to start of data entry moved fp to %d not %d\n",
		seek_result, offset_of_data );
      errno = EIO;
      return( -1 );
    }

    /* read this number of bytes into the buffer given */
    bytes_read = read( this_archive_des, data_buf, bytes_to_read );
    if( bytes_read == -1 ) {
      if( debug )
	printf( "lib.c, get_entry_data():  error reading data entry;  errno = %d\n", errno );
      return( -1 );
    }
    else if( bytes_read != bytes_to_read ) {
      if( debug )
	printf( "lib.c, get_entry_data():  only %d/%d data entry bytes read\n",
		bytes_read, bytes_to_read );
      errno = EIO;
      return( -1 );
    }

    return_val = ( int )bytes_read;
  }
  else {
    if( debug )
      printf( "lib.c, get_entry_data():  entry %d not found\n", entry_handle );
    errno = EINVAL;
    return( -1 );
  }

  return( return_val );
} /* end get_entry_data() */



/**************************************************************************************************
* modify_entry
*
* Modify the index entry information associated with an entry.
* Only three of the six fields in the "archive_index_entry_type" structure are modified
* by the incoming data: the "name", "signature" and "kind" fields.
* A fourth field, the "date_last_modified" field, is updated according to
* when the modification takes place.
*
* Parameters:
*
* Return Values:
* Upon successful completion, a value of 0 is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
int modify_entry(	int					archive_handle,
			int					entry_handle,
			archive_index_entry_type		*new_archive_index_entry_ptr )
{
  int				archive_index, this_archive_des;
  struct archive_header_type	*this_archive_header_ptr;
  struct archive_state_type	*this_archive_state_ptr;
  struct full_index_entry_type	*root, *full_index_entry_ptr, *last_full_index_entry_ptr,
				*temp_full_index_entry_ptr;
  int				last_entry_num;
  size_t			full_index_entry_size;
  off_t				index_entry_offset, seek_result;
  ssize_t			bytes_written;
  long int			current_time;


  /* Check if the file handle presented is valid, and if so, get the appropriate index. */
  archive_index = Archive_Index_From_Handle( archive_handle );
  if( archive_index == -1 ) {
    if( debug ) {
      printf( "lib.c, modify_entry():  file descriptor given does not refer to an open file\n" );
      printf( "errno = %d\n", errno );
    }
    return( -1 );
  }
  this_archive_des = archive_descriptors[ archive_index ];
  this_archive_header_ptr = archive_header_ptrs[ archive_index ];
  this_archive_state_ptr = archive_state_ptrs[ archive_index ];
  root = this_archive_state_ptr->root_index_entry_ptr;

  /* Check if this operation is allowed given the mode of opening for the archive. */
  if( this_archive_state_ptr->open_mode != OPEN_UPDATE ) {
    if( debug )
      printf( "lib.c, modify_entry():  this operation not allowed;  open mode = %d\n",
	      this_archive_state_ptr->open_mode );
    errno = EACCES;
    return( -1 );
  }

  /* Find the entry number of the last index entry, for use in range checking below. */
  last_full_index_entry_ptr = this_archive_state_ptr->last_index_entry_ptr;
  if( last_full_index_entry_ptr == NULL ) {
    if( debug )
      printf( "lib.c, modify_entry():  no entries in file\n" );
    errno = EINVAL;
    return( -1 );
  }
  else {
    last_entry_num = last_full_index_entry_ptr->private.num;
  }

  /* Check if the entry handle presented is in the accepted range of values. */
  if( ( entry_handle < 1 ) || ( entry_handle > last_entry_num ) ) {
    if( debug ) {
      printf( "lib.c, modify_entry():  the entry handle given is outside the acceptable range " );
      printf( "- it does not refer to a valid entry.\n" );
    }
    errno = EINVAL;
    return( -1 );
  }

  /* Check if the pointer to the new data is NULL. */
  if( new_archive_index_entry_ptr == NULL ) {
    if( debug )
      printf( "lib.c, modify_entry():  the pointer to the new data is NULL\n" );
    errno = EFAULT;
    return( -1 );
  }

  /* find the entry in the index */
  full_index_entry_ptr = Find_Full_Index_Entry_In_List( root, entry_handle );
  if( full_index_entry_ptr ) {
    if( debug )
      printf( "lib.c, modify_entry():  entry found\n" );
    if( full_index_entry_ptr->private.to_be_deleted == TRUE ) {
      if( debug )
	printf( "lib.c, modify_entry():  the entry with that handle is marked as deleted\n" );
      errno = EINVAL;
      return( -1 );
    }

    /* We now copy the linked list index entry just found to a temporary index entry,
     * then modify this temporary index entry according to the new data, instead of
     * modifying the linked list directly.
     * We then make the changes to file, then lastly to the linked list entry.  Doing
     * things this way means that if there is an error making the changes to file, the changes
     * to the linked list need not be reversed.
     * We start by allocating memory for the temporary entry. */
    full_index_entry_size = sizeof( struct full_index_entry_type );
    temp_full_index_entry_ptr = malloc( full_index_entry_size );
    if( temp_full_index_entry_ptr == NULL ) {
      if( debug )
	printf( "lib.c, modify_entry():  could not allocate memory for temporary index entry\n" );
      errno = ENOSPC;
      return( -1 );
    }

    /* Copy the linked list index entry to the temporary index entry just allocated for. */
    memmove( temp_full_index_entry_ptr, full_index_entry_ptr, full_index_entry_size );

    /* Modify the "name", "signature" and "kind" fields of the temporary index entry
     * from the supplied data. */

    /* Copy the first MAX_ENTRY_NAME_SIZE characters, ie. the characters with indices
     * 0 to (MAX_ENTRY_NAME_SIZE - 1) inclusive, of the supplied entry_name string into the
     * "name" field, then null-terminate the name field at index MAX_ENTRY_NAME_SIZE (the last). */
    strncpy( temp_full_index_entry_ptr->archive.name, new_archive_index_entry_ptr->name,
	     MAX_ENTRY_NAME_SIZE );
    temp_full_index_entry_ptr->archive.name[ MAX_ENTRY_NAME_SIZE ] = NULL_CHAR;

    /* Copy the first SIGNATURE_SIZE bytes of the supplied signature into the "signature" field. */
    memmove( temp_full_index_entry_ptr->archive.signature, new_archive_index_entry_ptr->signature,
	     SIGNATURE_SIZE );

    /* Update the "kind" field. */
    temp_full_index_entry_ptr->archive.kind = new_archive_index_entry_ptr->kind;

    /* Update the "date_last_modified" field. */
    current_time = time( NULL );
    temp_full_index_entry_ptr->archive.date_last_modified = current_time;

    /* Debug - check if the temporary index entry is correctly changed. */
    if( debug ) {
      printf( "lib.c, modify_entry():  check if the temporary index entry is correctly changed " );
      printf( "using Print_Index_Entry():\n" );
      Print_Index_Entry( temp_full_index_entry_ptr );
    }

    /* Now copy these changes to file. */

    /* Find the file offset of the index entry to be modified. */
    index_entry_offset = full_index_entry_ptr->private.start_offset;
    if( debug )
      printf( "lib.c, modify_entry():  index_entry_offset = %d\n", index_entry_offset );

    /* Seek to this offset. */
    seek_result = lseek( this_archive_des, index_entry_offset, SEEK_SET );
    if( seek_result == -1 ) {
      if( debug ) {
	printf( "lib.c, modify_entry():  error lseek()ing to position in file where " );
	printf( "modified index entry is to be written;  errno = %d\n", errno );
      }
      free( temp_full_index_entry_ptr );
      return( -1 );
    }
    else if( seek_result != index_entry_offset ) {
      if( debug ) {
	printf( "lib.c, modify_entry():  lseek() to file position where modified index entry " );
	printf( "is to be written moved fp to %d not %d\n", seek_result, index_entry_offset );
      }
      errno = EIO;
      free( temp_full_index_entry_ptr );
      return( -1 );
    }

    /* Write the temporary index entry to file at this offset. */
    bytes_written = write( this_archive_des, temp_full_index_entry_ptr, full_index_entry_size );
    if( bytes_written == -1 ) {
      if( debug )
	printf( "lib.c, modify_entry():  error writing modified index entry;  errno = %d\n",
		errno );
      free( temp_full_index_entry_ptr );
      return( -1 );
    }
    else if( bytes_written != full_index_entry_size ) {
      if( debug )
	printf( "lib.c, modify_entry():  only %d/%d modified index entry bytes written\n",
		bytes_written, full_index_entry_size );
      errno = EIO;
      free( temp_full_index_entry_ptr );
      return( -1 );
    }

    /* Debug - check if the change made it to file. */
    if( debug ) {
      printf( "lib.c, modify_entry():  check if the modified index entry made it to file " );
      printf( "using Print_File_Contents():\n" );
      Print_File_Contents( archive_index );
    }

    /* Make the change to the linked list in memory. */
    memmove( full_index_entry_ptr, temp_full_index_entry_ptr, full_index_entry_size );

    /* Debug - check if the list in memory is correctly changed. */
    if( debug ) {
      printf( "lib.c, modify_entry():  check if the list in memory is correctly changed " );
      printf( "using Print_State():\n" );
      Print_State( this_archive_state_ptr );
    }

    free( temp_full_index_entry_ptr );
  }
  else {
    if( debug )
      printf( "lib.c, modify_entry():  entry %d not found\n", entry_handle );
    errno = EINVAL;
    return( -1 );
  }

  return( 0 );
} /* end modify_entry() */



/**************************************************************************************************
* Find_Full_Index_Entry_In_List
*
* This attempts to find the index entry in the index that has the index number given (this
* is the handle that the user program has).
* If successful, a pointer to the index entry is returned.  If not, a NULL pointer is returned.
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
struct full_index_entry_type *Find_Full_Index_Entry_In_List(
						struct full_index_entry_type	*root,
						int				entry_handle )
{
  struct full_index_entry_type	*this_ptr;


  this_ptr = root;

  while( ( this_ptr != NULL ) && ( this_ptr->private.num != entry_handle ) ) {
    this_ptr = this_ptr->next;
  }

  return( this_ptr );
} /* end Find_Full_Index_Entry_In_List() */



/**************************************************************************************************
* Update_File_Header
*
* This updates, on file, the header of the archive whose descriptor is given.
* It does this simply by writing to file the header pointed to by the supplied header pointer.
*
* Parameters:
*
* Return Values:
* Upon successful completion, the number of bytes actually written is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
static
int Update_File_Header(	int				archive_index,
			struct archive_header_type	*header_ptr )
{
  int		this_archive_des;
  off_t		offset;
  size_t	header_size;
  ssize_t	bytes_written;
  int		return_val;


  this_archive_des = archive_descriptors[ archive_index ];

  if( debug )
    printf( "lib.c, Update_File_Header():  archive_index = %d, archive_des = %d\n",
	    archive_index, this_archive_des );

  /* We use pwrite so that the file pointer is not changed. */
  header_size = sizeof( struct archive_header_type );
  offset = ( off_t )0;		/* header is at the start of the file */
  bytes_written = pwrite( this_archive_des, header_ptr, header_size, offset );
  if( bytes_written == -1 ) {
    if( debug )
      printf( "lib.c, Update_File_Header():  error writing header;  errno = %d\n", errno );
    return( -1 );
  }
  else if( bytes_written != header_size ) {
    if( debug )
      printf( "lib.c, Update_File_Header():  only %d/%d file header bytes written\n",
	      bytes_written, header_size );
    errno = EIO;
    return( -1 );
  }
  else {
    if( debug )
      printf( "lib.c, Update_File_Header():  file header written ok\n" );
    return_val = ( int )bytes_written;
  }

  return( return_val );
} /* end Update_File_Header() */



/**************************************************************************************************
* Add_Index_Entry_To_List
*
* This adds a new index entry to the linked list kept in memory.
* It simply takes the supplied pointer to the new index entry, and sets
* the last entry in the list to point to it, ie. dynamic memory allocation
* is not done here - it is assumed that this has been done previously.
*
* Parameters:
*
* Return Values:
* Upon successful completion, a value of 0 is returned.
* Otherwise, a value of -1 is returned and errno is set to indicate the error.
*
**************************************************************************************************/
static
int Add_Index_Entry_To_List(	int				archive_index,
				struct full_index_entry_type	*new_full_index_entry_ptr )
{
  struct archive_state_type	*this_archive_state_ptr;


  if( debug )
    printf( "lib.c, Add_Index_Entry_To_List():  archive_index = %d\n", archive_index );

  this_archive_state_ptr = archive_state_ptrs[ archive_index ];

  if( new_full_index_entry_ptr == NULL ) {
    if( debug )
      printf( "lib.c, Add_Index_Entry_To_List():  new_full_index_entry_ptr == NULL\n" );
    errno = EFAULT;
    return( -1 );
  }
  else if( this_archive_state_ptr->root_index_entry_ptr == NULL ) {
    /* this is the first index entry for this archive */
    this_archive_state_ptr->root_index_entry_ptr	= new_full_index_entry_ptr;
    this_archive_state_ptr->last_index_entry_ptr	= new_full_index_entry_ptr;
    this_archive_state_ptr->current_index_entry_ptr	= new_full_index_entry_ptr;
  }
  else {
    /* there are already one or more index entries in the list */
    this_archive_state_ptr->last_index_entry_ptr->next	= new_full_index_entry_ptr;
    this_archive_state_ptr->last_index_entry_ptr	= new_full_index_entry_ptr;
    this_archive_state_ptr->current_index_entry_ptr	= new_full_index_entry_ptr;
  }

  return( 0 );
} /* end Add_Index_Entry_To_List() */



/**************************************************************************************************
* Archive_Index_From_Handle
*
* This attempts to return the archive index corresponding to the
* file descriptor given.
* The index is found by looking at each entry of the
* archive_descriptors array until the supplied file descriptor is found,
* then returning the index of that entry.
* This is possible because there is a one-to-one mapping between each index
* of the array, and the corresponding value, which in turn is because each
* descriptor generated by open() is unique.
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
int Archive_Index_From_Handle(	int	archive_handle )
{
  int	archive_index = -1;
  int	i = 0, found = FALSE;


  while( !found && ( i < num_open_archives ) ) {
    if( archive_handles[ i ] == archive_handle ) {
      found = TRUE;
      archive_index = i;
    }
    i++;
  } /* end while */

  if( !found ) {
    if( debug ) {
      printf( "lib.c, Archive_Index_From_Handle():  given handle not found in array - " );
      printf( "it does not refer to an open archive\n" );
    }
    errno = EBADF;
    return( -1 );
  }

  return( archive_index );
} /* end Archive_Index_From_Handle() */



/**************************************************************************************************
* Print_File_Contents
*
* This is a debugging routine.
* It is declared static, ie. it cannot be called from outside this library.
* It prints, to standard output, the entire contents of the archive whose descriptor is given.
* This includes the header, and for each entry in the archive, its index entry and data entry.
* The data entry is printed as a character string.
* The file pointer is left in the same place it was in before this routine was called.
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
void Print_File_Contents(	int	archive_index )
{
  int				this_archive_des;
  off_t				file_size, seek_result;
  size_t			header_size, full_index_entry_size;
  int				initial_file_pos, current_file_pos;
  struct archive_header_type	*header_ptr;
  ssize_t			bytes_read;
  struct full_index_entry_type	*current_full_index_entry_ptr;
  int				data_entry_size, offset_of_data, offset_of_next;
  void				*data_entry_ptr;


  this_archive_des = archive_descriptors[ archive_index ];

  printf( "\nlib.c, Print_File_Contents():  archive_index = %d, archive_des = %d\n",
	  archive_index, this_archive_des );

  /* Find the initial position of the file pointer. */
  initial_file_pos = tell( this_archive_des );

  header_size = sizeof( struct archive_header_type );
  full_index_entry_size = sizeof( struct full_index_entry_type );

  /* Find the size of the file. */
  file_size = lseek( this_archive_des, 0, SEEK_END );

  /* Use the file size to see if there are any entries at all in the file. */
  if( file_size < header_size ) {
    printf( "lib.c, Print_File_Contents():  file size is less than header size.\n" );
    printf( "\tThis should NEVER happen in this routine!\n" );
    return;
  }

  /* Set the file pointer to the start of the file. */
  seek_result = lseek( this_archive_des, 0, SEEK_SET );
  if( seek_result == -1 ) {
    printf( "lib.c, Print_File_Contents():  error setting file pointer to start of file\n" );
    printf( "errno = %d\n", errno );
    return;
  }
  else if( seek_result != 0 ) {
    printf( "lib.c, Print_File_Contents():  lseek() to set file pointer to start of file " );
    printf( "moved fp to %d not %d\n", seek_result, 0 );
    return;
  }

  /* Allocate memory for header. */
  header_ptr = malloc( header_size );
  if( header_ptr == NULL ) {
    printf( "lib.c, Print_File_Contents():  could not allocate memory for header\n" );
    return;
  }

  /* Read the header from file. */
  bytes_read = read( this_archive_des, header_ptr, header_size );
  if( bytes_read == -1 ) {
    printf( "lib.c, Print_File_Contents():  error reading archive %d header\n", this_archive_des );
    printf( "errno = %d\n", errno );
    free( header_ptr );
    return;
  }
  else if( bytes_read != header_size ) {
    printf( "lib.c, Print_File_Contents():  only %d/%d header bytes read from archive %d\n",
	    bytes_read, header_size, this_archive_des );
    free( header_ptr );
    return;
  }

  /* Print the header. */
  Print_Header( header_ptr );

  /* Free the memory allocated for the header. */
  free( header_ptr );

  if( file_size == header_size ) {
    printf( "lib.c, Print_File_Contents():  no entries in file\n" );
    return;
  }

  /* Allocate memory to store the index entries in
   * (every index entry will use this one block of memory). */
  current_full_index_entry_ptr = malloc( full_index_entry_size );
  if( current_full_index_entry_ptr == NULL ) {
    printf( "lib.c, Print_File_Contents():  could not allocate index entry memory\n" );
    return;
  }

  /* Because of the above check we know there is at least one entry in the file at this point. */
  offset_of_next = header_size;

  while( offset_of_next != NOT_AN_OFFSET ) {
    /* File pointer should not be at the end of the file inside this for loop - check this. */
    current_file_pos = tell( this_archive_des );
    if( current_file_pos == file_size ) {
      printf( "lib.c, Print_File_Contents():  file pointer should not be at end of file, " );
      printf( "but it is\n" );
      free( current_full_index_entry_ptr );
      return;
    }

    /* Seek to the start of the next entry. */
    seek_result = lseek( this_archive_des, offset_of_next, SEEK_SET );
    if( seek_result == -1 ) {
      printf( "lib.c, Print_File_Contents():  error setting fp to start of next entry\n" );
      printf( "errno = %d\n", errno );
      free( current_full_index_entry_ptr );
      return;
    }
    else if( seek_result != offset_of_next ) {
      printf( "lib.c, Print_File_Contents():  lseek() to set fp to start of next entry " );
      printf( "moved fp to %d not %d\n", seek_result, offset_of_next );
      free( current_full_index_entry_ptr );
      return;
    }

    /* Read the index entry from file - file pointer is already in the correct place. */
    bytes_read = read( this_archive_des, current_full_index_entry_ptr, full_index_entry_size );
    if( bytes_read == -1 ) {
      printf( "lib.c, Print_File_Contents():  error reading index entry of " );
      printf( "archive %d; errno = %d", this_archive_des, errno );
      free( current_full_index_entry_ptr );
      return;
    }
    else if( bytes_read != full_index_entry_size ) {
      printf( "lib.c, Print_File_Contents():  only %d/%d index entry bytes read from archive %d\n",
	      bytes_read, full_index_entry_size, this_archive_des );
      free( current_full_index_entry_ptr );
      return;
    }

    /* Print the index entry. */
    printf( "lib.c, Print_File_Contents():  about to Print_Index_Entry():\n" );
    Print_Index_Entry( current_full_index_entry_ptr );
    current_file_pos = tell( this_archive_des );
    printf( "\tfile pos after index entry = %d\n", current_file_pos );

    /* Find the size of the data entry associated with this index entry. */
    data_entry_size = current_full_index_entry_ptr->archive.data_size;

    /* Allocate memory for the data entry. */
    data_entry_ptr = malloc( data_entry_size );
    if( data_entry_ptr == NULL ) {
      printf( "lib.c, Print_File_Contents():  couldn't malloc mem for data entry\n" );
      return;
    }

    /* Find the offset of the data entry. */
    offset_of_data = current_full_index_entry_ptr->private.offset_of_data;
    printf( "lib.c, Print_File_Contents():  offset of data entry = %d\n", offset_of_data );

    /* Seek to the start of the data entry. */
    seek_result = lseek( this_archive_des, offset_of_data, SEEK_SET );
    if( seek_result == -1 ) {
      printf( "lib.c, Print_File_Contents():  error setting file pointer to start of data\n" );
      printf( "errno = %d\n", errno );
      free( current_full_index_entry_ptr );
      free( data_entry_ptr );
      return;
    }
    else if( seek_result != offset_of_data ) {
      printf( "lib.c, Print_File_Contents():  lseek() to set file pointer to start of data " );
      printf( "moved fp to %d not %d\n", seek_result, offset_of_data );
      free( current_full_index_entry_ptr );
      free( data_entry_ptr );
      return;
    }

    /* Read the data entry from file. */
    bytes_read = read( this_archive_des, data_entry_ptr, data_entry_size );
    if( bytes_read == -1 ) {
      printf( "lib.c, Print_File_Contents():  error reading data entry of " );
      printf( "archive %d; errno = %d", this_archive_des, errno );
      free( current_full_index_entry_ptr );
      free( data_entry_ptr );
      return;
    }
    else if( bytes_read != data_entry_size ) {
      printf( "lib.c, Print_File_Contents():  only %d/%d data entry bytes read from archive %d\n",
	      bytes_read, data_entry_size, this_archive_des );
      free( current_full_index_entry_ptr );
      free( data_entry_ptr );
      return;
    }

    /* Print the data entry. */
    printf( "lib.c, Print_File_Contents():  about to Print_Data_Entry():\n" );
    Print_Data_Entry( data_entry_ptr, data_entry_size );
    current_file_pos = tell( this_archive_des );
    printf( "\tfile pos after data entry = %d\n", current_file_pos );

    /* Free the memory allocated for the data entry. */
    free( data_entry_ptr );

    /* Find the offset of the next entry. */
    offset_of_next = current_full_index_entry_ptr->private.offset_of_next;
    printf( "lib.c, Print_File_Contents():  offset of next entry = %d\n", offset_of_next );
  } /* end while */

  /* Free the index entry memory. */
  free( current_full_index_entry_ptr );

  /* Restore the file pointer to its original position. */
  seek_result = lseek( this_archive_des, initial_file_pos, SEEK_SET );
  if( seek_result == -1 ) {
    printf( "lib.c, Print_File_Contents():  error restoring fp to original position\n" );
    printf( "errno = %d\n", errno );
    return;
  }
  else if( seek_result != initial_file_pos ) {
    printf( "lib.c, Print_File_Contents():  lseek() to restore fp to original position " );
    printf( "moved fp to %d not %d\n", seek_result, initial_file_pos );
    return;
  }
} /* end Print_File_Contents() */



/**************************************************************************************************
* Print_Header
*
* This is a debugging routine.
* It is declared static, ie. it cannot be called from outside this library.
* It prints, to standard output, the values of the header fields for the header pointed to
* by the given pointer.
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
void Print_Header(	struct archive_header_type	*header_ptr )
{
  int	i, ascii_value;


  printf( "lib.c, Print_Header():\n" );

  printf( "magic number = " );
  for( i = 0;  i < MAGIC_NUMBER_SIZE;  i++ )
    printf( "%c", header_ptr->magic_number[ i ] );
  printf( "\n" );
} /* end Print_Header() */



/**************************************************************************************************
* Print_State
*
* This is a debugging routine.
* It is declared static, ie. it cannot be called from outside this library.
* It prints, to standard output, the state information for the archive whose descriptor is given.
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
void Print_State(	struct archive_state_type	*state_ptr )
{
  struct full_index_entry_type		*this_index_entry_ptr;


  printf( "lib.c, Print_State():\n" );
  printf( "open_mode = %d\n", state_ptr->open_mode );

  this_index_entry_ptr = state_ptr->root_index_entry_ptr;
  while( this_index_entry_ptr ) {
    Print_Index_Entry( this_index_entry_ptr );
    this_index_entry_ptr = this_index_entry_ptr->next;
  }
} /* end Print_State() */



/**************************************************************************************************
* Print_Index_Entry
*
* This is a debugging routine.
* It is declared static, ie. it cannot be called from outside this library.
* It prints, to standard output, the information contained in the index entry
* pointed to by the index entry pointer given.
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
void Print_Index_Entry(	struct full_index_entry_type	*index_entry_ptr )
{
  printf( "entry num = %d, start offset = %d, offset of data = %d, offset of next = %d, ",
	  index_entry_ptr->private.num,
	  index_entry_ptr->private.start_offset,
	  index_entry_ptr->private.offset_of_data,
	  index_entry_ptr->private.offset_of_next );
  printf( "to be deleted = %d\n",
	  index_entry_ptr->private.to_be_deleted );

  printf( "entry name = %s, signature = %s\n",
	  index_entry_ptr->archive.name,
	  index_entry_ptr->archive.signature );
  printf( "date created = %ld, date last modified = %ld, data size = %d, kind = %d\n",
	  index_entry_ptr->archive.date_created,
	  index_entry_ptr->archive.date_last_modified,
	  index_entry_ptr->archive.data_size,
	  index_entry_ptr->archive.kind );

  printf( "next pointer = %p\n", index_entry_ptr->next );
} /* end Print_Index_Entry() */



/**************************************************************************************************
* Print_Data_Entry
*
* This is a debugging routine.
* It is declared static, ie. it cannot be called from outside this library.
* It prints, to standard output, the information contained in the data entry
* pointed to by the pointer given.  The data is printed as a character string, but
* this is an arbitrary choice - other formats, such as hex or octal, are just as valid.
*
* Parameters:
*
* Return Values:
*
**************************************************************************************************/
static
void Print_Data_Entry(	void	*data_entry_ptr,
			int	data_entry_size )
{
  char	*print_str;	/* the string that will actually be printed to screen */


  /* Allocate memory for the print string. */
  print_str = ( char * )malloc( data_entry_size + 1 );
  if( print_str == NULL ) {
    printf( "lib.c, Print_Data_Entry():  couldn't allocate for the print string\n" );
    return;
  }

  /* Copy the entry data to the print string. */
  strncpy( print_str, data_entry_ptr, data_entry_size );

  /* Terminate the print string with a null character. */
  print_str[ data_entry_size ] = NULL_CHAR;

  printf( "\tThis data entry = %s\n", print_str );

  free( print_str );
} /* end Print_Data_Entry() */
