#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: dbopsrch.c SYSTEM FUNCTIONS FOR OPENING AND SEARCHING AN EXISTING DATABASE */ /*----------------------------------------------------------------------------- FUNCTION: DBOPEN It opens an existing database. PARAMETERS: db_id = pointer to database number db_name = pointer to database name and path; replaced by name without path. access_mode = pointer to access mode = 0 for READ_ONLY 1 for READ_WRITE memory_mode = pointer to memory mode for directories 0 for directory on disk 1 for directory in memory ierr = pointer to error code RETURNS: VOID */ void DBOPEN(int *db_id, char *db_name, int *access_mode, int *memory_mode, int *ierr) { int i, id, d[2]; FILE_NAME_TYPE path_name, path, db_root; if ((*ierr = alignment_check()) != 0) return; /**jr++**/ if (first_database_call) { for (i = 0; i < MAX_OPEN_DATABASES; i++) database_table[i] = NULL; first_database_call = 0; } *ierr = 0; id = *db_id - 1; if ((id < 0) || (id >= MAX_OPEN_DATABASES)) { *ierr = INVALID_DATABASE_NUMBER; return; } if (database_table[id]) { db_ptr = database_table[id]; *ierr = DB_ALREADY_OPEN; return; } if ((database_table[id] = (DATABASE_TYPE *) malloc(DATABASE_SIZE)) == NULL) { db_ptr->error_code = INSUFFICIENT_MEMORY; db_ptr->error_data = BLOCK_DIR; goto error_found; } current_database = id; db_ptr = database_table[id]; set_byte((UBYTE *) db_ptr, 0, DATABASE_SIZE); db_ptr->memory_directory = *memory_mode; db_ptr->access_mode = *access_mode; db_ptr->block_dir_host = UNKNOWN_HOST; /* to be set in open_block_dir() */ db_ptr->block_host = UNKNOWN_HOST; /* to be set when block file is opened */ strtrim(path_name, db_name, sizeof(FILE_NAME_TYPE)); if (strlen(path_name) + 8 > sizeof(FILE_NAME_TYPE)) { /* need 8 char for file number, ext, NUL */ *ierr = PATHNAME_TOO_LONG; return; } split_path(path_name, path, db_root); db_ptr->path_nchar = strlen(path); strcpy(db_ptr->block_file, path); strcpy(db_ptr->block_dir_file, path); strcat(db_ptr->block_dir_file, db_root); strcat(db_ptr->block_dir_file, "dir.blk"); if (open_block_dir()) goto error_found; if (strncmp(db_ptr->block_dir_hdr.db_version, DATABASE_SYSTEM_VERSION, 2)) { *ierr = INCORRECT_DATABASE_VERSION; return; } if (db_ptr->memory_directory) { if (allocate_memory_for_directories()) goto error_found; while (db_ptr->block_dir_max_nentries < db_ptr->block_dir_hdr.dir_nentries) { if (add_memory_for_block_dir()) goto error_found; } if (db_ptr->block_dir_hdr.dir_nentries) if (load_block_dir()) goto error_found; } if (db_ptr->block_dir_hdr.dir_nentries) { d[0] = 0; d[1] = 0; i = BLOCK_PROFILE_SEARCH; DBSRCH(&i, (char *) d, ierr); } return; error_found: report_db_error("DBOPEN"); *ierr = db_ptr->error_code * 1000 - db_ptr->error_data; release_database(); } /*----------------------------------------------------------------------------- FUNCTION: DBSRCH It sets the database pointer to the first profile that satisfies the given search parameters. PARAMETERS: search_type = pointer to type of search requested search_parameters = pointer to appropriate parameters for given search type ierr = pointer to error code RETURNS: VOID */ void DBSRCH(int *search_type, char *search_parameters, int *ierr) { if ((!(db_ptr)) || first_database_call) { *ierr = DB_IS_NOT_OPEN; return; } if (db_ptr->create_mode) { *ierr = OPEN_FOR_CREATE; return; } if (!(db_ptr->block_dir_hdr.dir_nentries)) { *ierr = DATABASE_IS_EMPTY; return; } *ierr = 0; switch (*search_type) { case BLOCK_PROFILE_SEARCH: if (lock_on_blkprf_index((int *) search_parameters, ierr)) goto error_found; break; case TIME_SEARCH: if (lock_on_time((YMDHMS_TIME_TYPE *) search_parameters, ierr)) goto error_found; break; default: *ierr = INVALID_TYPE_REQUEST; } return; error_found: report_db_error("DBSRCH"); *ierr = db_ptr->error_code * 1000 - db_ptr->error_data; } /*----------------------------------------------------------------------------- FUNCTION: lock_on_blkprf_index PARAMETERS: d = block and profile index array ierr = pointer to error code on exit = 0 if okay NO_SUCH_BLOCK_PROFILE otherwise RETURNS: 0 if okay or NO_SUCH_BLOCK_PROFILE db_ptr->error_code otherwise */ int lock_on_blkprf_index(int *d, int *ierr) { int iblock, iprofile, oldblock; *ierr = 0; /*ef*/ if (((iblock = d[0]) < 0) || ((iprofile = d[1]) < 0) || (iblock >= db_ptr->block_dir_hdr.dir_nentries)) { *ierr = NO_SUCH_BLOCK_PROFILE; return(0); } if (iblock == db_ptr->block_dir_index) { if (!(db_ptr->block_is_open)) if (lock_on_block()) goto error_found; if (iprofile >= db_ptr->block_hdr.dir_nentries) { *ierr = NO_SUCH_BLOCK_PROFILE; return(0); } else { db_ptr->profile_dir_index = iprofile; if (lock_on_profile()) goto error_found; } } else { oldblock = db_ptr->block_dir_index; if (db_ptr->block_is_open) if (close_block()) goto error_found; db_ptr->block_dir_index = iblock; if (lock_on_block()) goto error_found; if (iprofile >= db_ptr->block_hdr.dir_nentries) { *ierr = NO_SUCH_BLOCK_PROFILE; db_ptr->block_dir_index = oldblock; if (oldblock > 0) if (lock_on_block()) goto error_found; } else { db_ptr->profile_dir_index = iprofile; if (lock_on_profile()) goto error_found; } } return(0); error_found: report_db_error("lock_on_blkprf_index"); return(db_ptr->error_code); } /*----------------------------------------------------------------------------- FUNCTION: lock_on_time It resets the database to the block and profile with time equal to or greater than the time key. PARAMETERS: t = pointer to YMDHMS_TIME_TYPE structure to search for ierr = pointer to error code on exit = 0 if okay INVALID_TIME if t is not valid SEARCH_BEFORE_BEGINNING SEARCH_BEYOND_END MODIFIED: Thu 09-22-1988 by E.F. to remove a bug found in application of DBSRCH. */ int lock_on_time(YMDHMS_TIME_TYPE *t, int *ierr) { ULONG tpck; LONG time_ofs; int oldind, newind, err_code; /*ef*/ YMDHMS_TIME_TYPE t0, t1; ULONG tpck0 = 0; LONG sec60 = 60; *ierr = 0; /*ef*/ if (invalid_time(t)) { *ierr = INVALID_TIME; return(0); } /* Initial check and possible block search is done with the key time rounded DOWN to the minute, and converted to packed block time. */ tpck = PCKTIM(t); /* Search the block directory if no block is open, or if t is outside the rounded minute block time range, or if it is within the first minute of that range; in the latter case the correct profile time could actually be in the preceding block, because of the overlap of the rounded block time ranges. A fresh search will then find the earlier of the two blocks that might contain the profile. The opposite case, of t in the last minute of the current range, is taken care of later. */ if (db_ptr->block_is_open) { UPCKTIM(&t1, &db_ptr->block_hdr.time1); DIFTIM(&t1, &t0, &sec60); tpck0 = PCKTIM(&t0); } if (!(db_ptr->block_is_open) || (tpck < tpck0) || (tpck > db_ptr->block_hdr.time2)) { oldind = db_ptr->block_dir_index; /* save, in case an old block needs closing */ if (search_for_block_by_time(tpck, ierr)) goto error_found; if ((db_ptr->block_is_open) && (oldind != db_ptr->block_dir_index)) { /* the old block that is open is not the new one that was found */ newind = db_ptr->block_dir_index; /*ef*/ db_ptr->block_dir_index = oldind; /*ef*/ if (close_block()) /* so close the old one */ goto error_found; db_ptr->block_dir_index = newind; /*ef*/ } if (!db_ptr->block_is_open) if (lock_on_block()) /*ef*/ goto error_found; } switch (*ierr) /* possible non-fatal error from block search */ { case SEARCH_BEFORE_BEGINNING: db_ptr->profile_dir_index = 0; break; case SEARCH_BEYOND_END: db_ptr->profile_dir_index = db_ptr->block_hdr.dir_nentries - 1; break; case 0: time_ofs = HTIMDIF(&(db_ptr->block_base_time), t); if (db_ptr->block_hdr.dir_time_flag == DIR_TIME_IN_SECONDS) { time_ofs /= 100; } /* HTIMDIF yields hundredths, regardless of whether its arguments use seconds or hundredths; so we just calculate hundredths in any case, and then convert to seconds if that is what the block is using. */ err_code = search_for_profile_by_time(time_ofs); /* An err_code of SEARCH_BEFORE_BEGINNING here can be ignored; it means the profile landed in a gap between blocks. If the key is after the last profile in the block, check the next block. */ if (err_code == SEARCH_BEYOND_END) { if (db_ptr->block_dir_index >= db_ptr->block_dir_hdr.dir_nentries - 1) *ierr = SEARCH_BEYOND_END; /* No more blocks to check. */ else { if (close_block()) goto error_found; db_ptr->block_dir_index++; if (lock_on_block()) goto error_found; time_ofs = HTIMDIF(&(db_ptr->block_base_time), t); if ( db_ptr->block_hdr.dir_time_flag == DIR_TIME_IN_SECONDS ) { time_ofs /= 100; } *ierr = search_for_profile_by_time(time_ofs); if (*ierr == SEARCH_BEFORE_BEGINNING) *ierr = 0; /* key was in gap between blocks */ } } } lock_on_profile(); return(0); error_found: report_db_error("lock_on_time"); return(db_ptr->error_code); } /*----------------------------------------------------------------------------- FUNCTION: search_for_block_by_time It searches the block directory for the earliest entry that includes the key time, or whose times are greater than the key time. On return, db_ptr->block_dir_index points to this entry. The search is based on the block end time, not the start time; the earliest block with an end time (minutes, rounded up) >= the key time. This could find the block BEFORE the right one, but will never find the block AFTER the right one, assuming the actual profile time ranges of the blocks do not overlap. PARAMETERS: tpck = packed block time key ierr = pointer to returned error code = 0 if entry found 6 if SEARCH_BEYOND_END 7 if SEARCH_BEFORE_BEGINNING RETURNS: 0 */ int search_for_block_by_time(ULONG tpck, int *ierr) { long ofs; LONG *key_base; int entry_length, nentries; int time2_ofs; *ierr = 0; if (tpck < db_ptr->block_dir_hdr.time1) { db_ptr->block_dir_index = 0; *ierr = SEARCH_BEFORE_BEGINNING; } else if (tpck > db_ptr->block_dir_hdr.time2) { db_ptr->block_dir_index = db_ptr->block_dir_hdr.dir_nentries - 1; *ierr = SEARCH_BEYOND_END; } else { entry_length = db_ptr->block_dir_hdr.dir_entry_nbytes; nentries = db_ptr->block_dir_hdr.dir_nentries; /* Find offset of block time2 (end time rounded up in minutes) in block directory entry. */ time2_ofs = sizeof(PRODUCER_ID_TYPE) + sizeof(ULONG); if (db_ptr->block_dir_loaded) { key_base = (LONG *) (db_ptr->block_dir + time2_ofs); db_ptr->block_dir_index = DSRCHGE((ULONG *)&tpck, (CHAR *)key_base, &entry_length, &nentries); } else { ofs = sizeof(BLOCK_DIR_HDR_TYPE) + time2_ofs; db_ptr->block_dir_index = dir_search_GE_file(db_ptr->fpblkdir, tpck, ofs, entry_length, nentries, db_ptr->block_dir_host, HOST_ENVIRONMENT); } if (db_ptr->block_dir_index == -1) { db_ptr->block_dir_index = nentries - 1; *ierr = SEARCH_BEYOND_END; /* jr+ bec. of change in DSRCHGE,... */ } } return(0); } /*----------------------------------------------------------------------------- FUNCTION: search_for_profile_by_time It searches the profile directory for the earliest entry that has a time equal to or greater than the key time. On return, db_ptr->profile_dir_index points to this entry. PARAMETER: time_ofs = number of seconds or hundredths of seconds from the first block time RETURNS: 0 if profile found SEARCH_BEFORE_BEGINNING SEARCH_BEYOND_END */ int search_for_profile_by_time(LONG time_ofs) { long ofs; ULONG *key_base; int entry_length, nentries; if (time_ofs < 0) { db_ptr->profile_dir_index = 0; return(SEARCH_BEFORE_BEGINNING); /* jr+ */ } entry_length = db_ptr->block_hdr.dir_entry_nbytes; nentries = db_ptr->block_hdr.dir_nentries; if (db_ptr->profile_dir_loaded) { key_base = (ULONG *) (db_ptr->profile_dir + sizeof(LONG)); db_ptr->profile_dir_index = DSRCHGE((ULONG *)&time_ofs, (CHAR *)key_base, &entry_length, &nentries); } else { ofs = db_ptr->block_hdr.dir_ofs + sizeof(LONG); db_ptr->profile_dir_index = dir_search_GE_file(db_ptr->fpblkdata, time_ofs, ofs, entry_length, nentries, db_ptr->block_host, HOST_ENVIRONMENT); } if (db_ptr->profile_dir_index == -1) { db_ptr->profile_dir_index = nentries - 1; return(SEARCH_BEYOND_END); /* jr+ */ } return(0); } /*----------------------------------------------------------------------------- FUNCTION: lock_on_block It resets the database to the block pointed to by db_ptr->block_dir_index. RETURNS: 0 if okay db_ptr->error_code otherwise */ int lock_on_block(void) { if (load_block_dir_entry()) goto error_found; if (open_block()) goto error_found; if (set_block_access()) goto error_found; return(0); error_found: report_db_error("lock_on_block"); return(db_ptr->error_code); } /*----------------------------------------------------------------------------- FUNCTION: lock_on_profile It resets the database to the profile pointed to by db_ptr->profile_dir_index. RETURNS: 0 if okay <0 otherwise */ int lock_on_profile(void) { if (load_profile_dir_entry()) goto error_found; if (set_profile_access()) goto error_found; db_ptr->profile_is_open = 1; return(0); error_found: report_db_error("lock_on_profile"); return(db_ptr->error_code); } /*----------------------------------------------------------------------------- FUNCTION: set_block_access() It sets block access data structures. RETURNS: 0 if okay db_ptr->error_code otherwise */ int set_block_access(void) { if (db_ptr->block_hdr.time1 != MAXULONG) { UPCKTIM(&(db_ptr->block_base_time), &(db_ptr->block_hdr.time1)); UPCKTIM(&(db_ptr->block_last_time), &(db_ptr->block_hdr.time2)); } if (load_data_list()) goto error_found; if (db_ptr->profile_dir_in_memory) if (load_profile_dir()) goto error_found; return(0); error_found: report_db_error("set_block_access"); return(db_ptr->error_code); } /*----------------------------------------------------------------------------- FUNCTION: set_profile_access It sets profile access data structures. RETURNS: 0 if okay READ_ERROR or SEEK_ERROR otherwise */ int set_profile_access(void) { unsigned int nb; long ofs; nb = db_ptr->block_hdr.data_dir_nentries * DATA_DIR_ENTRY_SIZE; ofs = db_ptr->profile_dir_entry_ptr->ofs; if ((db_ptr->error_code = read_data(db_ptr->fpblkdata, (char *)db_ptr->data_dir, ofs, nb)) != 0) { db_ptr->error_data = DATA_DIR; goto error_found; } if (db_ptr->block_host != HOST_ENVIRONMENT) { if (convert_array_struct((char *) db_ptr->data_dir, (char *) db_ptr->data_dir, db_ptr->block_host, HOST_ENVIRONMENT, db_ptr->block_hdr.data_dir_nentries, "data_dir_entry", &(dbint_sd[0].data_dir_entry)) == BADUINT) { db_ptr->error_code = CONVERT_ERROR; db_ptr->error_data = DATA_DIR; goto error_found; } } return(0); error_found: report_db_error("set_profile_access"); return(db_ptr->error_code); } /*----------------------------------------------------------------------------- FUNCTION: load_block_dir_entry() It loads the block directory entry pointed to by db_ptr->block_dir_index into *db_ptr->block_dir_entry_ptr RETURNS: 0 if okay READ_ERROR OR SEEK_ERROR otherwise */ int load_block_dir_entry(void) { unsigned int nb; long ofs; nb = db_ptr->block_dir_hdr.dir_entry_nbytes; /* ---> IF THE DIRECTORY IS IN MEMORY JUST SET ENTRY POINTER */ if (db_ptr->block_dir_loaded) { db_ptr->block_dir_entry_ptr = (BLOCK_DIR_ENTRY_TYPE *) (db_ptr->block_dir + nb * db_ptr->block_dir_index); } /* ---> OTHERWISE READ THE ENTRY FROM BLOCK DIRECTORY FILE INTO db_ptr->block_dir_entry AND THEN SET THE POINTER */ else { ofs = BLOCK_DIR_HDR_SIZE + nb * db_ptr->block_dir_index; if ((db_ptr->error_code = read_data(db_ptr->fpblkdir, (char *) &(db_ptr->block_dir_entry), ofs, nb)) != 0) { db_ptr->error_data = BLOCK_DIR; goto error_found; } if (db_ptr->block_dir_host != HOST_ENVIRONMENT) { if (convert_struct((char *) &(db_ptr->block_dir_entry), (char *) &(db_ptr->block_dir_entry), db_ptr->block_dir_host, HOST_ENVIRONMENT, "block_dir_entry", &(dbint_sd[0].block_dir_entry)) == BADUINT) { db_ptr->error_code = CONVERT_ERROR; db_ptr->error_data = BLOCK_DIR; goto error_found; } } db_ptr->block_dir_entry_ptr = &(db_ptr->block_dir_entry); } return(0); error_found: report_db_error("load_block_dir_entry"); return (db_ptr->error_code); } /*----------------------------------------------------------------------------- FUNCTION: load_profile_dir_entry() It loads the profile directory entry pointed to by db_ptr->profile_dir_index into *db_ptr->profile_dir_entry_ptr. RETURNS: 0 if okay READ_ERROR OR SEEK_ERROR otherwise */ int load_profile_dir_entry(void) { unsigned int nb; long ofs; char struct_def_name[20]; nb = db_ptr->block_hdr.dir_entry_nbytes; /* ---> IF THE DIRECTORY IS IN MEMORY JUST SET ENTRY POINTER */ if (db_ptr->profile_dir_loaded) { db_ptr->profile_dir_entry_ptr = (PROFILE_DIR_3_ENTRY_TYPE *) (db_ptr->profile_dir + nb * db_ptr->profile_dir_index); } /* ---> OTHERWISE READ THE ENTRY FROM PROFILE DIRECTORY FILE INTO db_ptr->profile_dir_entry AND THEN SET POINTER */ else { ofs = db_ptr->block_hdr.dir_ofs + nb * db_ptr->profile_dir_index; if ((db_ptr->error_code = read_data(db_ptr->fpblkdata, (char *) &(db_ptr->profile_dir_entry), ofs, nb)) != 0) { db_ptr->error_data = PROFILE_DIR; goto error_found; } if (db_ptr->block_host != HOST_ENVIRONMENT) { sprintf(struct_def_name, "profile_dir_%1d_entry", (int) db_ptr->block_hdr.dir_type); if (convert_struct((char *) &(db_ptr->profile_dir_entry), (char *) &(db_ptr->profile_dir_entry), db_ptr->block_host, HOST_ENVIRONMENT, struct_def_name, &(dbint_sd[0].profile_dir_0_entry)) == BADUINT) { db_ptr->error_code = CONVERT_ERROR; goto error_found; } } db_ptr->profile_dir_entry_ptr = &(db_ptr->profile_dir_entry); } return(0); error_found: report_db_error("load_profile_dir_entry"); return (db_ptr->error_code); }