#define FIRST_MODULE
#include "dbinc.h"
/*****************************************************************************
*                                                                            *
*      COMMON OCEANOGRAPHIC DATA ACCESS SYSTEM (CODAS)                       *
*                                                                            *
*      WRITTEN BY:  RAMON CABRERA, ERIC FIRING, and JULIE RANADA             *
*                   JOINT INSTITUTE FOR MARINE AND ATMOSPHERIC RESEARCH      *
*                   1000 POPE ROAD  MSB 312                                  *
*                   HONOLULU, HI 96822                                       *
*                                                                            *
*      VERSION:     3.10                                                     *
*                                                                            *
*      DATE:        APRIL 1989, AUGUST 1995                                  *
*                                                                            *
*****************************************************************************/
/*

       FILE:  dbcommon.c

              SYSTEM FUNCTIONS FOR ACCESSING DATABASE WHETHER FOR
              CREATE, READ, OR UPDATE = DBCLOSE and supporting routines;
              routines common to both DBCREATE and DBOPEN

*/
/*-----------------------------------------------------------------------------

   FUNCTION:  DBCLOSE

      It closes the current database.

   PARAMETERS:

      ierr = pointer to error code

   RETURNS:  VOID

*/
#if PROTOTYPE_ALLOWED
void DBCLOSE(int *ierr)
#else
void DBCLOSE(ierr)
int *ierr;
#endif
{
   unsigned int nb;

   *ierr = 0;
   if ((!(db)) || first_database_call)
   {
      *ierr = DB_IS_NOT_OPEN;
      return;
   }
   if (db->new_block_is_open)
   {
      *ierr = NEW_BLOCK_IS_OPEN;
      return;
   }
/*
 ---> CLOSE ANY OPEN BLOCK
*/
   if (db->block_is_open)
      if (close_block())
         goto error_found;
/*
 ---> IF BLOCK DIRECTORY WAS MODIFIED UPDATE IT
*/
   if (db->block_dir_modified)
   {
/*
 ---> IF BLOCK DIR NOT IN MEMORY LOAD IT
*/
      if (!(db->block_dir_in_memory))
      {
         nb = db->block_dir_hdr.dir_nentries
            * db->block_dir_hdr.dir_entry_nbytes;
         if ((db->block_dir = malloc(nb)) == NULL)
         {
            db->error_code = INSUFFICIENT_MEMORY;
            db->error_data = BLOCK_DIR;
            goto error_found;
         }
         db->block_dir_in_memory = 1;
         if (load_block_dir())
            goto error_found;
      }
      else if (!(db->block_dir_loaded))
      {
         if (load_block_dir())
            goto error_found;
      }
/*
 ---> UPDATE HEADER AND DIRECTORY AND REWRITE THEM
*/
      update_block_dir();
      if (write_block_dir_hdr())
         goto error_found;
      if (write_block_dir())
         goto error_found;
   }
/*
 ---> CLOSE BLOCK DIRECTORY
*/
   if (close_block_dir())
      goto error_found;
   if (db->create_mode)
      if (EXISTS(db->temporary_data_file))
         remove(db->temporary_data_file);
/*
 ---> RELEASE DATABASE
*/
   release_database();
   return;

   error_found:
      report_db_error("DBCLOSE");
      *ierr = db->error_code * 1000 - db->error_data;
      release_database();
}

/*-----------------------------------------------------------------------------

   FUNCTION:  report_db_error

      It writes to standard output a short description of a database-related
      error.

   PARAMETER:

      fun = pointer to name of function reporting the error

*/
NAME_LIST_ENTRY_TYPE ERROR_CODE[] =
{
   {"EOF",                          -1},
   {"UNABLE_TO_OPEN",               -2},
   {"FILE_DOES_NOT_EXIST",          -3},
   {"FILE_ALREADY_EXISTS",          -4},
   {"FILE_PROTECTION_ERROR",        -5},
   {"READ_ERROR",                   -6},
   {"WRITE_ERROR",                  -7},
   {"SEEK_ERROR",                   -8},
   {"CLOSE_ERROR",                  -9},
   {"INSUFFICIENT_MEMORY",         -10},
   {"",                              0},  /* no error */
   {"INVALID_DATABASE_NUMBER",       1},
   {"DB_IS_NOT_OPEN",                2},
   {"DB_ALREADY_OPEN",               3},
   {"NO_OPEN_FOR_WRITE",             4},
   {"NO_OPEN_FOR_CREATE",            5},
   {"OPEN_FOR_CREATE",               6},
   {"DATABASE_IS_EMPTY",             7},
   {"INCORRECT_DATABASE_VERSION",    8},
   {"UNEXPECTED_STRUCTURE_SIZE",     9},
   {"NO_BLOCK_IS_OPEN",             11},
   {"NO_PROFILE_IS_OPEN",           12},
   {"NO_NEW_BLOCK_IS_OPEN",         13},
   {"NO_NEW_PROFILE_IS_OPEN",       14},
   {"NEW_BLOCK_IS_OPEN",            15},
   {"NEW_PROFILE_IS_OPEN",          16},
   {"BLOCK_DIR_IS_FULL",            21},
   {"BLOCK_IS_FULL",                22},
   {"INSUFFICIENT_SPACE",           23},
   {"BLOCK_DIR_IS_EMPTY",           26},
   {"PROFILE_DIR_IS_EMPTY",         27},
   {"DATA_LIST_IS_EMPTY",           28},
   {"INVALID_TYPE_REQUEST",         31},
   {"INVALID_TIME",                 32},
   {"INVALID_POSITION",             33},
   {"INVALID_VALUE_TYPE",           34},
   {"INVALID_ACCESS_TYPE",          35},
   {"NO_SUCH_BLOCK_PROFILE",        36},
   {"INVALID_BUFFER_SIZE",          37},
   {"INVALID_DIRECTORY_TYPE",       38},
   {"MAXIMUM_SIZE_EXCEEDED",        39},
   {"SEARCH_BEYOND_END",            41},
   {"SEARCH_BEFORE_BEGINNING",      42},
   {"SEARCH_ERROR",                 43}, /* 1999/07/13 Pierre Jaccard */
   {"ZERO_SCALE",                   51},
   {"INVALID_SCALE",                52}, /* 1999/07/13 Pierre Jaccard */
   {"PATHNAME_TOO_LONG",            61},
   {"DBNAME_TOO_LONG",              62},
   {"UNDEFINED_STRUCTURE",          70},
   {"UNKNOWN_HOST_ERROR",           71},
   {"INCOMPATIBLE_HOST_ERROR",      72},
   {"STRUCTURE_NOT_PRESENT",        73},
   {"NO_SUCH_ELEMENT",              74},
   {"FORMAT_ERROR",                 80},
   {"INVALID_NUMBER",               98}, /* 1999/07/13 Pierre Jaccard */
   {"CONVERT_ERROR",                99}  /* 1999/07/13 Pierre Jaccard */
};

NAME_LIST_ENTRY_TYPE DATA_CODE[] =
{
   {"",                          0},
   {"DATASET_ID",              100},
   {"PRODUCER_ID",             101},
   {"TIME",                    102},
   {"POSITION",                103},
   {"DATA_MASK",               104},
   {"DATA_PROC_MASK",          105},
   {"DEPTH_RANGE",             106},

   {"DATABASE_VERSION",        200},
   {"PRODUCER_HOST",           201},
   {"DATABASE_NUMBER",         202},
   {"BLOCK_PROFILE_INDEX",     203},

   {"BLOCK_DIR_FILE",          300},
   {"BLOCK_FILE",              301},
   {"PRODUCER_DEF_FILE",       302},
   {"TEMP_DATA_FILE",          303},
   {"BLOCK_DIR_HDR",           304},
   {"BLOCK_DIR",               305},
   {"BLOCK_HDR",               306},
   {"DATA_LIST",               307},
   {"PROFILE_DIR",             308},
   {"DATA_DIR",                309},
   {"PROFILE_DATA",            310},
   {"PRODUCER_DEF",            311},
   {"STRUCTURE_DEF",           312},
   {"STRUCTURE_FORMATS",       313}
};

#if PROTOTYPE_ALLOWED
void report_db_error(char *fun)
#else
void report_db_error(fun)
char *fun;
#endif
{
   printf("\n CODAS ERROR:   %-30s", get_name(ERROR_CODE, db->error_code));
   printf("\t  ACCESSING:   %s", get_name(DATA_CODE, db->error_data));
   printf("\n FROM FUNCTION: %-30s", fun);                 /*ef*/
   printf("\t  IN DATABASE: %d\n", current_database + 1);   /*ef*/
}

/*-----------------------------------------------------------------------------

     FUNCTION:  close_block_dir()

        It closes an open block directory file.

     RETURNS:  0 if okay
               CLOSE_ERROR otherwise

*/
#if PROTOTYPE_ALLOWED
int close_block_dir(void)
#else
int close_block_dir()
#endif
{
   if (fclose(db->fpblkdir))
   {
      db->error_code = CLOSE_ERROR;
      db->error_data = BLOCK_DIR_FILE;
      goto error_found;
   }
   db->block_dir_is_open = 0;
   db->block_dir_loaded = 0;
   return(0);

   error_found:
      report_db_error("close_block_dir");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

     FUNCTION:  close_block()

        It closes a block data file.

     RETURNS:  0 if okay
               db->error_code otherwise

*/
#if PROTOTYPE_ALLOWED
int close_block(void)
#else
int close_block()
#endif
{
   unsigned int nb, temp_dir_loaded = 0;

/*
 ---> IF BLOCK WAS MODIFIED UPDATE IT
*/
   if (db->block_modified)
   {
/*
 ---> IF PROFILE DIR NOT IN MEMORY LOAD IT
*/
      if (!(db->profile_dir_in_memory))
      {
         nb = (db->block_hdr.dir_nentries) * db->block_hdr.dir_entry_nbytes;
         if ((db->profile_dir = malloc(nb)) == NULL)
         {
            db->error_code = INSUFFICIENT_MEMORY;
            db->error_data = PROFILE_DIR;
            goto error_found;
         }
         temp_dir_loaded = 1;
         if (load_profile_dir())
            goto error_found;
      }
      else if (!(db->profile_dir_loaded))
      {
         if (load_profile_dir())
            goto error_found;
      }
/*
 ---> UPDATE HEADER AND DIRECTORY AND REWRITE THEM
*/
      update_block();
      if (write_block_hdr())
         goto error_found;
      if (write_profile_dir())
         goto error_found;
      update_block_dir_entry();
      if (write_block_dir_entry())
         goto error_found;
      db->block_dir_modified = 1;
   }
/*
 ---> CLOSE BLOCK FILE
*/
   if (temp_dir_loaded)
   {
      free(db->profile_dir);
      db->profile_dir_loaded = 0;
   }
   release_block();
   return(0);

   error_found:
      if (temp_dir_loaded)
      {
         free(db->profile_dir);
         db->profile_dir_loaded = 0;
      }
      release_block();
      report_db_error("close_block");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  release_database

      It closes files and releases all memory allocated for a database.

   RETURNS:  VOID

*/
#if PROTOTYPE_ALLOWED
void release_database(void)
#else
void release_database()
#endif
{
   if (db)
   {
      if (db->block_is_open)
         fclose(db->fpblkdata);
      if (db->block_dir_is_open)
         fclose(db->fpblkdir);
      if (db->producer_in_memory)
         free(db->producer);
      if (db->block_dir_in_memory)
         free(db->block_dir);
      if (db->profile_dir_in_memory)
         free(db->profile_dir);
      if (db->next_strdef != NULL)
         free(db->next_strdef);
      else if (db->prev_strdef != NULL)
         free(db->struct_def);
      if (db->block_strdef_loaded)
         free(db->block_strdef);
      free(database_table[current_database]);
   }
   db = NULL;
   database_table[current_database] = NULL;
}

/*-----------------------------------------------------------------------------

   FUNCTION:  release_block

      It closes the block file and resets the respective flags.

   RETURNS:  VOID

*/
#if PROTOTYPE_ALLOWED
void release_block(void)
#else
void release_block()
#endif
{
   if (db->block_is_open)
      fclose(db->fpblkdata);
   if (!db->create_mode)
      if (db->block_strdef_loaded)
      {
         if (db->struct_def == db->block_strdef)
            db->struct_def = NULL;
         free(db->block_strdef);
         db->block_strdef_loaded = 0;
      }
   db->block_is_open = 0;
   db->block_modified = 0;
   db->profile_is_open = 0;
   db->new_block_is_open = 0;
   db->new_profile_is_open = 0;
   db->data_list_loaded = 0;
   db->formats_matched = 0;
   db->profile_dir_loaded = 0;
   db->block_host = UNKNOWN_HOST;
}

/*-----------------------------------------------------------------------------

   FUNCTION:  update_block_dir

      IT SETS THE STDR OF THE BLOCK DIR HDR BASED ON THE CURRENT ENTRIES' STDRS

      NOTES:
           - BLOCK_DIRECTORY MUST ALREADY BE OPEN
           - HEADER MUST BE IN  db->block_dir_hdr
           - BLOCK DIR MUST BE IN  db->block_dir

     RETURNS:  VOID
*/
#if PROTOTYPE_ALLOWED
void update_block_dir(void)
#else
void update_block_dir()
#endif
{
   unsigned int nb;
   int i, ndel;
   long minE = MAXLONG, minW = MAXLONG, maxE = MINLONG, maxW = MINLONG;
   long l1, l2;
   BLOCK_DIR_ENTRY_TYPE *ptr;
/*
 ---> SET FIELDS AS FOR AN EMPTY DIRECTORY
*/
   db->block_dir_hdr.time1 = MAXULONG;
   db->block_dir_hdr.time2 = MINULONG;
   db->block_dir_hdr.lon1 = MAXLONG;
   db->block_dir_hdr.lat1 = MAXLONG;
   db->block_dir_hdr.depth1 = MAXSHORT;
   db->block_dir_hdr.lon2 = MINLONG;
   db->block_dir_hdr.lat2 = MINLONG;
   db->block_dir_hdr.depth2 = MINSHORT;
   db->block_dir_hdr.data_mask = 0;
   db->block_dir_hdr.data_proc_mask = 0;
/*
 ---> ERASE EMPTY BLOCKS
*/
   nb = db->block_dir_hdr.dir_entry_nbytes;
   ptr = db->block_dir_entry_ptr = (BLOCK_DIR_ENTRY_TYPE *) db->block_dir;
   ndel = 0;
   for (i = 0; i < (int)db->block_dir_hdr.dir_nentries; i++)
   {
      if (ptr != db->block_dir_entry_ptr)
         move_byte((char *) ptr, (char *) db->block_dir_entry_ptr, nb);
      if (db->block_dir_entry_ptr->file_id < 0)
      {
         sprintf( (db->block_file+db->path_nchar),
            db->block_dir_hdr.block_file_template,
            -db->block_dir_entry_ptr->file_id);
         db->fpblkdata = open_binary_file(db->block_file, READ_WRITE);
         fclose(db->fpblkdata);
         ndel++;
      }
      else db->block_dir_entry_ptr += 1;
      ptr += 1;
   }
   db->block_dir_hdr.dir_nentries -= ndel;
/*
 ---> SORT BLOCK DIR
*/
   sort_block_dir();
/*
 ---> UPDATE HEADER
*/
   db->block_dir_entry_ptr = (BLOCK_DIR_ENTRY_TYPE *) db->block_dir;
   db->block_dir_hdr.time1 = db->block_dir_entry_ptr->time1;
   db->block_dir_hdr.time2 = (db->block_dir_entry_ptr +
      (db->block_dir_hdr.dir_nentries - 1))->time2;
   for (i = 0; i < (int)db->block_dir_hdr.dir_nentries; i++, (db->block_dir_entry_ptr)++)
   {
      db->block_dir_hdr.lat1 = min_val(db->block_dir_hdr.lat1,
         db->block_dir_entry_ptr->lat1);
      db->block_dir_hdr.lat2 = max_val(db->block_dir_hdr.lat2,
         db->block_dir_entry_ptr->lat2);
      db->block_dir_hdr.depth1 = min_val(db->block_dir_hdr.depth1,
         db->block_dir_entry_ptr->depth1);
      db->block_dir_hdr.depth2 = max_val(db->block_dir_hdr.depth2,
         db->block_dir_entry_ptr->depth2);
      db->block_dir_hdr.data_mask = (db->block_dir_hdr.data_mask)
          | (db->block_dir_entry_ptr->data_mask);
      db->block_dir_hdr.data_proc_mask = (db->block_dir_hdr.data_proc_mask)
          | (db->block_dir_entry_ptr->data_proc_mask);
/*
 ---> SPECIAL TREATMENT FOR LONGITUDE
*/
      if (db->block_dir_entry_ptr->lon1 != MAXLONG)
      {
         if ((l1 = db->block_dir_entry_ptr->lon1) > 129600000L)
            l1 -= 129600000L;
         if ((l2 = db->block_dir_entry_ptr->lon2) > 129600000L)
            l2 -= 129600000L;
         if (l1 <= 64800000L)
         {
            minE = min_val(minE, l1);
            maxE = max_val(maxE, l1);
         }
         else
         {
            minW = min_val(minW, l1);
            maxW = max_val(maxW, l1);
         }
         if (l2 <= 64800000L)
         {
            minE = min_val(minE, l2);
            maxE = max_val(maxE, l2);
         }
         else
         {
            minW = min_val(minW, l2);
            maxW = max_val(maxW, l2);
         }
      }
      if (minE == MAXLONG)
      {
         db->block_dir_hdr.lon1 = minW;
         db->block_dir_hdr.lon2 = maxW;
      }
      else if (minW == MAXLONG)
      {
         db->block_dir_hdr.lon1 = minE;
         db->block_dir_hdr.lon2 = maxE;
      }
      else
      {
         l1 = minW - maxE;
         l2 = minE - maxW + 129600000L;
         if (l1 <= l2)
         {
            db->block_dir_hdr.lon1 = minE;
            db->block_dir_hdr.lon2 = maxW;
         }
         else
         {
            db->block_dir_hdr.lon1 = minW;
            db->block_dir_hdr.lon2 = maxE + 129600000L;
         }
      }
   }
}

/*-----------------------------------------------------------------------------

   FUNCTION:  update_block

      It sets the position, time, and depth range of the block header based
      on the current entries' position, time and depth ranges.

   NOTE:

      - Block must already be open.
      - Header must be in db->block_hdr.
      - Profile directory must be in db->profile_dir.
      - Block access must be set.

   RETURNS:  VOID

*/
#if PROTOTYPE_ALLOWED
void update_block(void)
#else
void update_block()
#endif
{
   YMDHMS_TIME_TYPE tstruct;
   char *ptr;
   long mintime, tdiff;
   long minE = MAXLONG, minW = MAXLONG, maxE = MINLONG, maxW = MINLONG;
   long l1, l2;
   int i, ndel=0, profile_time_in_seconds, profile_key_type;
   unsigned int nb;

/*
 ---> ERASE EMPTY PROFILES
*/
   nb = db->block_hdr.dir_entry_nbytes;
   ptr = db->profile_dir;
   db->profile_dir_entry_ptr = (PROFILE_DIR_3_ENTRY_TYPE *) ptr;
   ndel = 0;
   for (i = 0; i < (int)db->block_hdr.dir_nentries; i++)
   {
      if (ptr != (CHAR *) db->profile_dir_entry_ptr)
         move_byte(ptr, (char *) db->profile_dir_entry_ptr, nb);
      if (db->profile_dir_entry_ptr->ofs < 0) ndel++;
      else db->profile_dir_entry_ptr = (PROFILE_DIR_3_ENTRY_TYPE *)
         ((char *) db->profile_dir_entry_ptr + nb);
      ptr += nb;
   }
   db->block_hdr.dir_nentries -= ndel;

   /* if there are no profiles left in a block, this block should be deleted */

   if (db->block_hdr.dir_nentries == 0)
   {
      if (db->block_dir_entry_ptr->file_id > 0)
         db->block_dir_entry_ptr->file_id = -db->block_dir_entry_ptr->file_id;
      if (write_block_dir_entry())
         report_db_error("update_block");
      db->block_dir_modified = 1;
      return;
   }

/*
 ---> SORT PROFILE DIR
*/
   sort_profile_dir();
/*
 ---> SET BLOCK TIME, POSITION RANGE, AND DEPTH RANGE
*/
   mintime = ((PROFILE_DIR_3_ENTRY_TYPE *)db->profile_dir)->time;
   nb = db->block_hdr.dir_entry_nbytes;
   profile_key_type = db->block_hdr.dir_type;
   for (i = 0; i < (int)db->block_hdr.dir_nentries; i++)
   {
      db->profile_dir_entry_ptr = (PROFILE_DIR_3_ENTRY_TYPE *)
         (db->profile_dir + i * nb);
      if ((profile_key_type == 1) || (profile_key_type == 3))
         /* profile directory entry includes position */
      {
         if (db->profile_dir_entry_ptr->lat != MAXLONG)
         {
            db->block_hdr.lat1 = min_val(db->block_hdr.lat1,
               db->profile_dir_entry_ptr->lat);
            db->block_hdr.lat2 = max_val(db->block_hdr.lat2,
               db->profile_dir_entry_ptr->lat);
         }

         if (db->profile_dir_entry_ptr->lon != MAXLONG)
         {
            if ((l1 = db->profile_dir_entry_ptr->lon) <= 64800000L)
            {
               minE = min_val(minE, l1);
               maxE = max_val(maxE, l1);
            }
            else
            {
               minW = min_val(minW, l1);
               maxW = max_val(maxW, l1);
            }
         }
      }
      if ((profile_key_type == 2) || (profile_key_type == 3))
            /* profile directory entry includes depth */
      {
         db->block_hdr.depth1 = min_val(db->block_hdr.depth1,
            db->profile_dir_entry_ptr->depth1);
         db->block_hdr.depth2 = max_val(db->block_hdr.depth2,
            db->profile_dir_entry_ptr->depth2);
      }
   }
/*
 ---> SET BLOCK TIME RANGE AND DISPLACE PROFILE TIMES IF NEEDED
*/
   profile_time_in_seconds =
         (db->block_hdr.dir_time_flag == DIR_TIME_IN_SECONDS);
   if ((mintime < 0) ||
       (profile_time_in_seconds && mintime >= 60) ||
       (!profile_time_in_seconds && mintime >= 6000))
                           /* WE HAVE TO CHANGE BLOCK BASE TIME    */
                           /* SO PROFILE TIME OFFSETS ARE POSITIVE */
   {
      move_byte((char *) &(db->block_base_time), (char *) &(tstruct),
         sizeof(YMDHMS_TIME_TYPE));
      if (profile_time_in_seconds)
         DIFTIM(&(tstruct), &(db->block_base_time), &mintime);
      else
         HDIFTIM(&(tstruct), &(db->block_base_time), &mintime);
      db->block_hdr.time1 = PCKTIM(&(db->block_base_time));
      UPCKTIM(&(db->block_base_time), &(db->block_hdr.time1));
      if (profile_time_in_seconds)
         tdiff = TIMDIF(&(db->block_base_time), &(tstruct));
      else
         tdiff = HTIMDIF(&(db->block_base_time), &(tstruct));
      for (i = 0;  i < db->block_hdr.dir_nentries;  i++)
      {
         db->profile_dir_entry_ptr = (PROFILE_DIR_3_ENTRY_TYPE *)
            (db->profile_dir + i * nb);
         db->profile_dir_entry_ptr->time += tdiff;
      }
   }
   tdiff = db->profile_dir_entry_ptr->time;
   /* tdiff is now the offset, in seconds or hundredths, of
      the last profile in the block.  Round it up to the next
      minute if necessary, so that when converted to minutes it
      will be >= the actual last time offset in the block.
   */
   if (profile_time_in_seconds)
   {
      if (tdiff % 60)
         tdiff += 60;
      DIFTIM(&(db->block_base_time), &(db->block_last_time), &tdiff);
   }
   else
   {
      if (tdiff % 6000)
         tdiff += 6000;
      HDIFTIM(&(db->block_base_time), &(db->block_last_time), &tdiff);
   }
   db->block_hdr.time2 = PCKTIM(&(db->block_last_time));
/*
 ---> SET LONGITUDE RANGE IF NEEDED
*/
   if ((profile_key_type == 1) || (profile_key_type == 3))
   {
      if (minE == MAXLONG)
      {
         db->block_hdr.lon1 = minW;
         db->block_hdr.lon2 = maxW;
      }
      else if (minW == MAXLONG)
      {
         db->block_hdr.lon1 = minE;
         db->block_hdr.lon2 = maxE;
      }
      else
      {
         l1 = minW - maxE;
         l2 = minE - maxW + 129600000L;
         if (l1 <= l2)
         {
            db->block_hdr.lon1 = minE;
            db->block_hdr.lon2 = maxW;
         }
         else
         {
            db->block_hdr.lon1 = minW;
            db->block_hdr.lon2 = maxE + 129600000L;
         }
      }
   }
}

/*-----------------------------------------------------------------------------

   FUNCTION:  update_block_dir_entry

      It sets the fields of the block directory entry for the current block.

   RETURNS: VOID

*/
#if PROTOTYPE_ALLOWED
void update_block_dir_entry(void)
#else
void update_block_dir_entry()
#endif
{
   strcpy((char *)&(db->block_dir_entry_ptr->producer_id),
      (char *)&(db->block_hdr.producer_id));
   db->block_dir_entry_ptr->time1 = db->block_hdr.time1;
   db->block_dir_entry_ptr->time2 = db->block_hdr.time2;
   db->block_dir_entry_ptr->lon1 = db->block_hdr.lon1;
   db->block_dir_entry_ptr->lon2 = db->block_hdr.lon2;
   db->block_dir_entry_ptr->lat1 = db->block_hdr.lat1;
   db->block_dir_entry_ptr->lat2 = db->block_hdr.lat2;
   db->block_dir_entry_ptr->depth1 = db->block_hdr.depth1;
   db->block_dir_entry_ptr->depth2 = db->block_hdr.depth2;
   db->block_dir_entry_ptr->block_nbytes = db->block_hdr.block_nbytes;
   db->block_dir_entry_ptr->data_mask = db->block_hdr.data_mask;
   db->block_dir_entry_ptr->data_proc_mask = db->block_hdr.data_proc_mask;
}

/*-----------------------------------------------------------------------------

   FUNCTION: write_block_dir_hdr

      It rewrites the block directory header.  Note that the block directory
      must be open.

   RETURNS:  0 if successful
             db->error_code otherwise

*/
#if PROTOTYPE_ALLOWED
int write_block_dir_hdr(void)
#else
int write_block_dir_hdr()
#endif
{
   unsigned int nb = BLOCK_DIR_HDR_SIZE;

   if (write_data(db->fpblkdir, (char *) &(db->block_dir_hdr), 0L, nb) != 0)
   {
      db->error_code = WRITE_ERROR;
      db->error_data = BLOCK_DIR_HDR;
      goto error_found;
   }
   return(0);

   error_found:
      report_db_error("write_block_dir_hdr");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  write_block_dir

      It rewrites the block directory.  This is useful after the directory
      has been sorted.  The block directory must be open and the directory
      must be loaded in memory at the location pointed to by db->block_dir.

   RETURNS:  0 if successful
             db->error_code otherwise

*/
#if PROTOTYPE_ALLOWED
int write_block_dir(void)
#else
int write_block_dir()
#endif
{
   unsigned int nb;
   long ofs = BLOCK_DIR_HDR_SIZE;

   nb = db->block_dir_hdr.dir_nentries * db->block_dir_hdr.dir_entry_nbytes;
   if ((db->error_code = write_data(db->fpblkdir, db->block_dir, ofs, nb)) != 0)
   {
      db->error_data = BLOCK_DIR;
      goto error_found;
   }
   return(0);

   error_found:
      report_db_error("write_block_dir");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  write_block_dir_entry

      It rewrites the current block directory entry.

   RETURNS:  0 if successful
             db->error_code otherwise

*/
#if PROTOTYPE_ALLOWED
int write_block_dir_entry(void)
#else
int write_block_dir_entry()
#endif
{
   unsigned int nb;
   long ofs;

   nb = db->block_dir_hdr.dir_entry_nbytes;
   ofs = BLOCK_DIR_HDR_SIZE + nb * db->block_dir_index;
   if ((db->error_code = write_data(db->fpblkdir,
      (char *) db->block_dir_entry_ptr, ofs, nb)) != 0)
   {
      db->error_data = BLOCK_DIR;
      goto error_found;
   }
   return(0);

   error_found:
      report_db_error("write_block_dir_entry");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  write_block_hdr

      It rewrites the block header.  The block must be open.

   RETURNS:  0 if successful
             db->error_code otherwise

*/
#if PROTOTYPE_ALLOWED
int write_block_hdr(void)
#else
int write_block_hdr()
#endif
{
   unsigned int nb = BLOCK_HDR_SIZE;

   if (write_data(db->fpblkdata, (char *) &(db->block_hdr), 0L, nb) != 0)
   {
      db->error_code = WRITE_ERROR;
      db->error_data = BLOCK_HDR;
      goto error_found;
   }
   return(0);

   error_found:
      report_db_error("write_block_hdr");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  write_profile_dir

      It rewrites the profile directory.  This is useful after the directory
      has been sorted.  The profile directory must be open and loaded into
      memory at the location pointed to by db->profile_dir.

   RETURNS:  0 if successful
             db->error_code otherwise

*/
#if PROTOTYPE_ALLOWED
int write_profile_dir(void)
#else
int write_profile_dir()
#endif
{
   unsigned int nb;
   long ofs;

   ofs = db->block_hdr.dir_ofs;
   nb = db->block_hdr.dir_nentries * db->block_hdr.dir_entry_nbytes;
   if ((db->error_code = write_data(db->fpblkdata, db->profile_dir, ofs, nb))
      != 0)
   {
      db->error_data = PROFILE_DIR;
      goto error_found;
   }
   return(0);

   error_found:
      report_db_error("write_profile_dir");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  sort_block_dir

      It sorts the block directory into ascending order by time.

   RETURNS:  VOID

*/
#if PROTOTYPE_ALLOWED
void sort_block_dir(void)
#else
void sort_block_dir()
#endif
{
   int l, n;

   l = db->block_dir_hdr.dir_entry_nbytes;
   n = db->block_dir_hdr.dir_nentries;
   db->block_dir_entry_ptr = (BLOCK_DIR_ENTRY_TYPE *) db->block_dir;
   if (db->block_dir_hdr.dir_nentries > 1)
      DSORTAS((char *) db->block_dir,
         (char *) &(db->block_dir_entry_ptr->time1), &l, &n);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  sort_profile_dir

      It sorts the profile directory into ascending order by time.

   RETURNS:  VOID

*/
#if PROTOTYPE_ALLOWED
void sort_profile_dir(void)
#else
void sort_profile_dir()
#endif
{
   int l, n;

   l = db->block_hdr.dir_entry_nbytes;
   n = db->block_hdr.dir_nentries;

   db->profile_dir_entry_ptr = (PROFILE_DIR_3_ENTRY_TYPE *) db->profile_dir;
   if (db->block_hdr.dir_nentries > 1)
      DSORTAS((CHAR *)db->profile_dir,
         (CHAR *)&(db->profile_dir_entry_ptr->time), &l, &n);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  load_block_dir()

      It loads the block directory into the memory location pointed to by
      db->block_dir.

   RETURNS:  0 if okay
             READ_ERROR OR SEEK_ERROR otherwise

*/
#if PROTOTYPE_ALLOWED
int load_block_dir(void)
#else
int load_block_dir()
#endif
{
   unsigned int nb;
   long ofs;

/*
 ---> READ BLOCK DIRECTORY
*/
   ofs = BLOCK_DIR_HDR_SIZE;
   if (db->block_dir_hdr.dir_nentries)
   {
      nb = db->block_dir_hdr.dir_nentries * db->block_dir_hdr.dir_entry_nbytes;
      if ((db->error_code = read_data(db->fpblkdir, db->block_dir,
         ofs, nb)) != 0)
      {
         db->error_data = BLOCK_DIR;
         goto error_found;
      }
      if (db->block_dir_host != HOST_ENVIRONMENT)
      {
         if (convert_array_struct((char *) db->block_dir,
                                  (char *) db->block_dir,
                                  db->block_dir_host, HOST_ENVIRONMENT,
                                  db->block_dir_hdr.dir_nentries,
                                  "block_dir_entry",
                                  &(dbint_sd[0].block_dir_entry)) == BADUINT)
         {
            db->error_code = CONVERT_ERROR;
            db->error_data = BLOCK_DIR;
            goto error_found;
         }
      }
   }
   db->block_dir_loaded = 1;
   return(0);

   error_found:
      report_db_error("load_block_dir");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

    FUNCTION:  load_profile_dir()

       It loads the profile directory into memory.

    RETURNS:  0 if okay
              SEEK_ERROR OR READ_ERROR otherwise

*/
#if PROTOTYPE_ALLOWED
int load_profile_dir(void)
#else
int load_profile_dir()
#endif
{
   unsigned int nb;
   long ofs;
   char struct_def_name[20];

   if (db->block_hdr.dir_nentries)
   {
      nb = db->block_hdr.dir_nentries * db->block_hdr.dir_entry_nbytes;
      ofs = db->block_hdr.dir_ofs;
      if ((db->error_code = read_data(db->fpblkdata, db->profile_dir, ofs, nb))
         != 0)
      {
         db->error_data = PROFILE_DIR;
         goto error_found;
      }
   }
   if (db->block_host != HOST_ENVIRONMENT)
   {
      sprintf(struct_def_name, "profile_dir_%1d_entry", (int) db->block_hdr.dir_type);
      if (convert_array_struct(db->profile_dir, db->profile_dir,
                               db->block_host, HOST_ENVIRONMENT,
                               db->block_hdr.dir_nentries,
                               struct_def_name,
                               &(dbint_sd[0].profile_dir_0_entry)) == BADUINT)
      {
         db->error_code = CONVERT_ERROR;
         db->error_data = PROFILE_DIR;
         goto error_found;
      }
   }
   db->profile_dir_loaded = 1;
   return(0);

   error_found:
      db->error_data = PROFILE_DIR;
      report_db_error("load_profile_dir");
      return (db->error_code);
}

/*-----------------------------------------------------------------------------

    FUNCTION:  load_data_list()

       It loads the block data access list.

    RETURNS:  0 if okay
              SEEK_ERROR OR READ_ERROR otherwise

*/
#if PROTOTYPE_ALLOWED
int load_data_list(void)
#else
int load_data_list()
#endif
{
   unsigned int nb;
   long ofs;

   nb = db->block_hdr.data_list_nentries * DATA_LIST_ENTRY_SIZE;
   ofs = db->block_hdr.data_list_ofs;
   if ((db->error_code = read_data(db->fpblkdata, (char *) db->data_list, ofs,
      nb)) != 0)
   {
      db->error_data = DATA_LIST;
      goto error_found;
   }
   if (db->block_host != HOST_ENVIRONMENT &&
      convert_array_struct((char *) db->data_list, (char *) db->data_list,
      db->block_host, HOST_ENVIRONMENT,
      db->block_hdr.data_list_nentries, "data_list_entry",
      &(dbint_sd[0].data_list_entry)) == BADUINT)
   {
      db->error_code = CONVERT_ERROR;
      db->error_data = DATA_LIST;
      goto error_found;
   }
   db->data_list_loaded = 1;
   return(0);

   error_found:
      report_db_error("load_data_list");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

     FUNCTION:  open_block_dir()

        It opens a block directory file and loads the header into block_dir_hdr.

     RETURNS:  0  if okay
               UNABLE_TO_OPEN OR READ_ERROR otherwise

*/
#if PROTOTYPE_ALLOWED
int open_block_dir(void)
#else
int open_block_dir()
#endif
{
   int nb;

   if ((db->fpblkdir = open_binary_file(db->block_dir_file, db->access_mode))
      == NULL)
   {
      db->error_code = UNABLE_TO_OPEN;
      db->error_data = BLOCK_DIR_FILE;
      goto error_found;
   }
   nb = BLOCK_DIR_HDR_SIZE;
   if (fread((CHAR *) &(db->block_dir_hdr), 1, nb, db->fpblkdir) != nb)
   {
      db->error_code = READ_ERROR;
      db->error_data = BLOCK_DIR_HDR;
      goto error_found;
   }
/*
 ---> CHECK MACHINE SIGNATURE -- jr+ 95/07
*/
   if ( (db->block_dir_host = identify_ms_code(db->block_dir_hdr.ms_code))
      == UNKNOWN_HOST )
   {
      if (!strncmp(db->block_dir_hdr.ms_code, "\0\0\0\0", 4))
      {
         /* uninitialized field in old database */
         union {ULONG all; UBYTE bytes[4];} nsq;
         nsq.all = db->block_dir_hdr.next_seq_number;
         if (nsq.bytes[0] == 0)  /* big-endian */
         {
            db->block_dir_host = SUN3_COMPATIBLE_HOST;
         }
         else                    /* little-endian */
         {
            db->block_dir_host = PC_COMPATIBLE_HOST;
         }
         /* This identification should work so long as the old database
            is either PC or SUN3, which I expect to be the case.
            It might be wise to add a warning here--run mkblkdir on
            these old databases so that they are brought up to date.
         */
      }
      else
      {
         db->error_code = UNKNOWN_HOST_ERROR;
         db->error_data = BLOCK_DIR_HDR;
         goto error_found;
      }
   }
   if (db->block_dir_host != HOST_ENVIRONMENT)
   {
      if (db->access_mode == READ_WRITE)   /* cross-environment read-write */
                                           /* access not permitted for now */
      {
         db->error_code = INCOMPATIBLE_HOST_ERROR;
         db->error_data = BLOCK_DIR_HDR;
         goto error_found;
      }
      else
      {
         if (convert_struct((char *) &(db->block_dir_hdr),
                            (char *) &(db->block_dir_hdr),
                            db->block_dir_host, HOST_ENVIRONMENT,
                            "block_dir_hdr",
                            &(dbint_sd[0].block_dir_hdr)) == BADUINT)
         {
            db->error_code = CONVERT_ERROR;
            db->error_data = BLOCK_DIR_HDR;
            goto error_found;
         }
      }
   }

   db->block_dir_is_open = 1;
   return(0);

   error_found:
      report_db_error("open_block_dir");
      return(db->error_code);
}

/*--------------------------------------------------------------------------

   FUNCTION:  open_block()

      It opens a block data file and loads the header into block_hdr.
      db->block_dir_entry_ptr points to the corresponding block
      directory entry.

   RETURNS:  0 if okay
             UNABLE_TO_OPEN OR READ_ERROR otherwise

*/
#if PROTOTYPE_ALLOWED
int open_block(void)
#else
int open_block()
#endif
{
   int nb;

/*
 ---> SET BLOCK FILE NAME
*/
   sprintf((db->block_file + db->path_nchar),
      db->block_dir_hdr.block_file_template, db->block_dir_entry_ptr->file_id);
/*
 ---> ATTEMPT TO OPEN BLOCK FILE
*/
   if ((db->fpblkdata = open_binary_file(db->block_file, db->access_mode)) == NULL)
   {
      db->error_code = UNABLE_TO_OPEN;
      db->error_data = BLOCK_FILE;
      goto error_found;
   }
/*
 ---> READ HEADER
*/
   nb = BLOCK_HDR_SIZE;
   if (fread((char *) &(db->block_hdr), 1, nb, db->fpblkdata) != nb)
   {
      db->error_code = READ_ERROR;
      db->error_data = BLOCK_HDR;
      goto error_found;
   }
   db->block_is_open = 1;
/*
 ---> CHECK MACHINE SIGNATURE
*/
/*
   if (strncmp(db->block_hdr.ms_code,
      MS_TABLE[identify_ms((char *) MACHINE_SIGNATURE)].ms_code, 4))
   {
      db->error_code = INCOMPATIBLE_HOST_ERROR;
      db->error_data = BLOCK_HDR;
      goto error_found;
   }
*/
   if ( (db->block_host = identify_ms_code(db->block_hdr.ms_code))
      == UNKNOWN_HOST)
   {
      db->error_code = UNKNOWN_HOST_ERROR;
      db->error_data = BLOCK_HDR;
      goto error_found;
   }
   if (db->block_host != HOST_ENVIRONMENT)
   {
      if (db->access_mode == READ_WRITE)   /* cross-environment read-write */
                                           /* access not permitted for now */
      {
         db->error_code = INCOMPATIBLE_HOST_ERROR;
         db->error_data = BLOCK_HDR;
         goto error_found;
      }
      else
      {
         if (convert_struct((char *) &(db->block_hdr),
                            (char *) &(db->block_hdr),
                            db->block_host, HOST_ENVIRONMENT,
                            "block_hdr", &(dbint_sd[0].block_hdr)) == BADUINT)
         {
            db->error_code = CONVERT_ERROR;
            db->error_data = BLOCK_HDR;
            goto error_found;
         }
      }
   }
   return(0);

   error_found:
      report_db_error("open_block");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

    FUNCTION:  allocate_memory_for_directories

       It allocates memory for the block and profile directories.

    RETURNS:  0 if okay
              < 0 otherwise

*/
#if PROTOTYPE_ALLOWED
int allocate_memory_for_directories(void)
#else
int allocate_memory_for_directories()
#endif
{
   unsigned int nb;

   nb = MAX_BLOCK_DIR_NBYTES;
   if ((db->block_dir = malloc(nb)) == NULL)
   {
      db->error_code = INSUFFICIENT_MEMORY;
      db->error_data = BLOCK_DIR;
      goto error_found;
   }
   db->block_dir_in_memory = 1;
   nb = MAX_PROFILE_DIR_NBYTES;
   if ((db->profile_dir = malloc(nb)) == NULL)
   {
      db->error_code = INSUFFICIENT_MEMORY;
      db->error_data = PROFILE_DIR;
      goto error_found;
   }
   db->profile_dir_in_memory = 1;
   return(0);

   error_found:
      report_db_error("allocate_memory_for_directories");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  alignment_check

      It checks that all structures internal to the database have the
      expected sizes.

   RETURNS:  0 if okay
             UNEXPECTED_STRUCTURE_SIZE otherwise

*/
#if PROTOTYPE_ALLOWED
int alignment_check(void)
#else
int alignment_check()
#endif
{
   if ((BLOCK_DIR_HDR_SIZE != EXPECTED_BLOCK_DIR_HDR_SIZE) ||
      (BLOCK_DIR_ENTRY_SIZE != EXPECTED_BLOCK_DIR_ENTRY_SIZE) ||
      (BLOCK_HDR_SIZE != EXPECTED_BLOCK_HDR_SIZE) ||
      (DATA_LIST_ENTRY_SIZE != EXPECTED_DATA_LIST_ENTRY_SIZE) ||
      (STRUCT_DEF_HDR_SIZE != EXPECTED_STRUCT_DEF_HDR_SIZE) ||
      (STRUCT_DEF_ELEM_SIZE != EXPECTED_STRUCT_DEF_ELEM_SIZE) ||
      (PROFILE_DIR_0_ENTRY_SIZE != XPCTD_PROFILE_DIR_0_ENTRY_SIZE) ||
      (PROFILE_DIR_1_ENTRY_SIZE != XPCTD_PROFILE_DIR_1_ENTRY_SIZE) ||
      (PROFILE_DIR_2_ENTRY_SIZE != XPCTD_PROFILE_DIR_2_ENTRY_SIZE) ||
      (PROFILE_DIR_3_ENTRY_SIZE != XPCTD_PROFILE_DIR_3_ENTRY_SIZE) ||
      (DATA_DIR_ENTRY_SIZE != EXPECTED_DATA_DIR_ENTRY_SIZE))
      return(UNEXPECTED_STRUCTURE_SIZE);
   else
      return(0);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  identify_ms

      It identifies the system corresponding to a given machine signature.

   ARGUMENT:

      ms = machine signature to be identified

   RETURNS:

      machine id (0, 1, 2, ...) if known
      UNKNOWN_HOST (-1) otherwise

*/
#if PROTOTYPE_ALLOWED
int identify_ms(char *ms)
#else
int identify_ms(ms)
char *ms;
#endif
{
   int i = 0;

   while (*(MS_TABLE[i].ms_name))
   {
      if (!strncmp(ms, (char *)MS_TABLE[i].signature, MS_SIZE))
         return(i);
      i++;
   }
   return(UNKNOWN_HOST);
}

/*-----------------------------------------------------------------------------

    FUNCTION:  identify_ms_code

       Given the ms_code ("0000", "1111", "1122", etc.), it returns
       the machine type for that code.

   PARAMETER:

       ms_code = string containing machine signature code

   RETURNS:

      0, 1, 2, ... for machine type assigned to that code
      UNKNOWN_HOST (-1) if unrecognized

*/
#if PROTOTYPE_ALLOWED
int identify_ms_code(char *ms_code)
#else
int identify_ms_code(ms_code)
char *ms_code;
#endif
{
   int i = 0;

   while (*(MS_TABLE[i].ms_name))
   {
      if (!strncmp(ms_code, MS_TABLE[i].ms_code, 4))
         return(i);
      i++;
   }
   return(UNKNOWN_HOST);
}

