/* FILE: range_io.c Range I/O and checking routines */ #include "dbinc.h" #include "use_db.h" /* RANGE_TYPE, TIME_RANGE, etc. */ #define SUCCESS 0 #define FAILURE 1 int db_last(BLKPRF_INDEX_TYPE *last_blkprf) { int errcode; BLKPRF_INDEX_TYPE this_blkprf; this_blkprf.block = db_ptr->block_dir_index; this_blkprf.profile = db_ptr->profile_dir_index; last_blkprf->block = db_ptr->block_dir_hdr.dir_nentries - 1; last_blkprf->profile = 0; if (dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) last_blkprf)) return(FAILURE); last_blkprf->profile = db_ptr->block_hdr.dir_nentries - 1; if (dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) &this_blkprf)) return(FAILURE); return(SUCCESS); } /* FUNCTION: get_range Reads keyword and corresponding range from input text file PARAMETERS: fp = pointer to input text file range = pointer to RANGE_TYPE structure RETURNS: EOF on end-of-file occurring when reading keyword 0 on success 1 on error or end-of-file occurring after reading keyword */ int get_range(FILE *fp, RANGE_TYPE *range) { int errcode; long save_ofs; char keyword[25]; /* TIME_RANGE: or DAY_RANGE: or BLOCK_RANGE: or BLKPRF_RANGE: */ char nextword[25]; /* "all" or start of range */ YMDHMS_TIME_TYPE ymd_time; BLKPRF_INDEX_TYPE last_blkprf; int yearbase; if ( (errcode = fscanf(fp, "%19s", keyword)) != 1 ) return(errcode); /* EOF? */ save_ofs = ftell(fp); if (fscanf(fp, "%19s", nextword) != 1) return(FAILURE); if (strcmp(keyword, "TIME_RANGE:") == 0) { range->type = TIME_RANGE; if (stricmp(nextword, "all") == 0) { UPCKTIM(&(range->ru.time.start), &(db_ptr->block_dir_hdr.time1)); UPCKTIM(&(range->ru.time.end) , &(db_ptr->block_dir_hdr.time2)); return(SUCCESS); } /* else must be numerically specified: */ fseek(fp, save_ofs, 0L); return(get_time_range(fp, &(range->ru.time.start), &(range->ru.time.end)) == 1 ? SUCCESS : FAILURE); } if (strcmp(keyword, "DAY_RANGE:") == 0) { range->type = DAY_RANGE; if (stricmp(nextword, "all") == 0) { UPCKTIM(&ymd_time, &(db_ptr->block_dir_hdr.time1)); yearbase = ymd_time.year; range->ru.day.start = year_day(&ymd_time, yearbase); UPCKTIM(&ymd_time, &(db_ptr->block_dir_hdr.time2)); range->ru.day.end = year_day(&ymd_time, yearbase); range->ru.day.yearbase = yearbase; return(SUCCESS); } /* else must be numerically specified: */ fseek(fp, save_ofs, 0L); return(fscanf (fp, "%d %lf to %lf", &(range->ru.day.yearbase), &(range->ru.day.start), &(range->ru.day.end)) == 3 ? SUCCESS : FAILURE); } if (strcmp(keyword, "BLOCK_RANGE:") == 0) { range->type = BLOCK_RANGE; if (stricmp(nextword, "all") == 0) { range->ru.block.start = 0; range->ru.block.end = db_ptr->block_dir_hdr.dir_nentries - 1; return(SUCCESS); } /* else must be numerically specified: */ fseek(fp, save_ofs, 0L); return(fscanf (fp, " %d to %d", &(range->ru.block.start), &(range->ru.block.end)) == 2 ? SUCCESS : FAILURE); } if (strcmp(keyword, "BLKPRF_RANGE:") == 0) { range->type = BLKPRF_RANGE; if (stricmp(nextword, "all") == 0) { range->ru.blkprf.start.block = range->ru.blkprf.start.profile = 0; last_blkprf.block = db_ptr->block_dir_hdr.dir_nentries - 1; last_blkprf.profile = 0; if (dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) &last_blkprf)) return(FAILURE); range->ru.blkprf.end.block = last_blkprf.block; range->ru.blkprf.end.profile = db_ptr->block_hdr.dir_nentries - 1; return(SUCCESS); } /* else must be numerically specified: */ fseek(fp, save_ofs, 0L); return(fscanf (fp, " %d to %d", &(range->ru.block.start), &(range->ru.block.end)) == 2 ? SUCCESS : FAILURE); } return(FAILURE); /* unrecognized range option */ } /* FUNCTION: in_range It retrieves the current database position into and returns TRUE if it falls within the given , 0 otherwise (out-of-range or error). */ int in_range(char *current, RANGE_TYPE *range) { unsigned int n = sizeof(YMDHMS_TIME_TYPE); YMDHMS_TIME_TYPE now; switch(range->type) { case TIME_RANGE: return( check_time( (YMDHMS_TIME_TYPE *) current, &(range->ru.time.start), &(range->ru.time.end) ) ); case DAY_RANGE: if (dbget_cnf(TIME, (char *) &now, &n, "getting current time")) return(0); *((double *) current) = year_day(&now, range->ru.day.yearbase); return( *((double *) current) >= range->ru.day.start && *((double *) current) <= range->ru.day.end ); case BLOCK_RANGE: if (dbget_cnf(BLOCK_PROFILE_INDEX, current, &n, "getting block-profile index")) return(0); return( ((BLKPRF_INDEX_TYPE *) current)->block >= range->ru.block.start && ((BLKPRF_INDEX_TYPE *) current)->block <= range->ru.block.end); case BLKPRF_RANGE: if (dbget_cnf(BLOCK_PROFILE_INDEX, current, &n, "getting block-profile index")) return(0); return( BPCMP( (BLKPRF_INDEX_TYPE *) current, (BLKPRF_INDEX_TYPE *) &(range->ru.blkprf.start) ) >= 0 && BPCMP( (BLKPRF_INDEX_TYPE *) current, (BLKPRF_INDEX_TYPE *) &(range->ru.blkprf.end) ) <= 0); default: return(0); /* out-of-range to quit */ } } /* FUNCTION: goto_start_of_range It positions the database pointer at earliest profile within given range. */ int goto_start_of_range(RANGE_TYPE *range) { YMDHMS_TIME_TYPE time_start; int ierr; BLKPRF_INDEX_TYPE bp; switch(range->type) { case TIME_RANGE: ierr = dbsrch_cnf(TIME_SEARCH, (char *) &(range->ru.time.start)); break; case DAY_RANGE: yd_to_ymdhms_time(range->ru.day.start, range->ru.day.yearbase, &time_start); ierr = dbsrch_cnf(TIME_SEARCH, (char *) &time_start); break; case BLOCK_RANGE: bp.block = range->ru.block.start; bp.profile = 0; ierr = dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) &bp); break; case BLKPRF_RANGE: bp.block = range->ru.blkprf.start.block; bp.profile = range->ru.blkprf.start.profile; ierr = dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) &bp); break; } switch(ierr) { case 0: return(SUCCESS); case SEARCH_BEFORE_BEGINNING: return(SUCCESS); default: return(ierr); } } /* FUNCTION: goto_end_of_range It positions the database pointer at last profile within given range. NOTE: Because DBSRCH by TIME positions the database pointer at a profile with time >= key, we need to back up one profile in the > case. */ int goto_end_of_range(RANGE_TYPE *range) { YMDHMS_TIME_TYPE time_end, tnow; int ierr; BLKPRF_INDEX_TYPE bp; unsigned int n = sizeof(YMDHMS_TIME_TYPE); switch(range->type) { case TIME_RANGE: ierr = dbsrch_cnf( TIME_SEARCH, (char *) &(range->ru.time.end) ); if (!ierr) /* make sure that prof time <= end of range */ { /* since TIME_SEARCH positions at >= key */ if ( dbget_cnf(TIME, (char *) &tnow, &n, "getting time") ) return(FAILURE); if ( TIMCMP(&tnow, &(range->ru.time.end)) == 2) return(dbmove_cnf(-1)); return(SUCCESS); } break; case DAY_RANGE: yd_to_ymdhms_time(range->ru.day.end, range->ru.day.yearbase, &time_end); ierr = dbsrch_cnf( TIME_SEARCH, (char *) &(time_end) ); if (!ierr) /* make sure that prof time <= end of range */ { /* since TIME_SEARCH positions at >= key */ if ( dbget_cnf(TIME, (char *) &tnow, &n, "getting time") ) return(FAILURE); if ( TIMCMP(&tnow, &(time_end)) == 2) return(dbmove_cnf(-1)); return(SUCCESS); } break; case BLOCK_RANGE: bp.block = range->ru.block.end; bp.profile = 0; ierr = dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) &bp); if (!ierr) { bp.profile = db_ptr->block_hdr.dir_nentries - 1; ierr = dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) &bp); } return(ierr); case BLKPRF_RANGE: bp.block = range->ru.blkprf.end.block; bp.profile = range->ru.blkprf.end.profile; return(dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) &bp)); } switch(ierr) { case SEARCH_BEYOND_END: /* go to end of database */ bp.block = db_ptr->block_dir_hdr.dir_nentries - 1; /* last block no. */ bp.profile = 0; if (!dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) &bp)) { bp.profile = db_ptr->block_hdr.dir_nentries - 1; /* last profile no. */ return(dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) &bp)); } return(FAILURE); default: return(ierr); } } /* FUNCTION: count_range(range) Returns the number of profiles in the range, including the start and the end. */ int count_range(RANGE_TYPE *range) { BLKPRF_INDEX_TYPE bp_end, bp0; int nprofs; int ret; unsigned int n; ret = goto_end_of_range(range); if (ret) return -2; n = sizeof(bp_end); if (dbget_cnf(BLOCK_PROFILE_INDEX, (char *) &bp_end, &n, "getting block-profile index")) return(-3); ret = goto_start_of_range(range); if (ret) return -4; n = sizeof(bp_end); if (dbget_cnf(BLOCK_PROFILE_INDEX, (char *) &bp0, &n, "getting block-profile index")) return(-5); if (bp0.block == bp_end.block) { nprofs = (bp_end.profile - bp0.profile) + 1; return nprofs; } nprofs = get_nprofs() - bp0.profile + 1; bp0.profile = 0; while (1) { bp0.block += 1; ret = dbsrch_cnf(BLOCK_PROFILE_SEARCH, (char *) &bp0); if (ret) return -6; if (bp0.block == bp_end.block) { nprofs += bp_end.profile; return nprofs; } nprofs += get_nprofs(); } } /* FUNCTION: check_range(range) Checks given against the immediately preceding one (against database beginning if no preceding range). This is done by checking the current db position against the start of the given . The current db position is assumed to be 1 after the end of the immediately preceding range. RETURNS: GAP if current db position is < start of range OKAY if current db position is = start of range OVERLAP if current db position is > start of range BAD_RANGE if error (misspecified range, etc.) PARAMETER: range = pointer to range to check against current db position NOTE: It always updates the db position at one profile past the end of --in preparation for the next call to check_range. Comparison is always resolved in terms of block-profile indices regardless of the range type to allow for rounding in the case of time-type ranges. */ int check_range(RANGE_TYPE *range) { int save_bp[2], result; save_bp[0] = db_ptr->block_dir_index; /* save current position */ save_bp[1] = db_ptr->profile_dir_index; if (goto_start_of_range(range)) return(BAD_RANGE); result = (db_ptr->block_dir_index == save_bp[0]) ? /* if same block no. */ (db_ptr->profile_dir_index - save_bp[1]) : /* check profile no. */ (db_ptr->block_dir_index - save_bp[0]) ; if (goto_end_of_range(range)) return(BAD_RANGE); dbmove_cnf(1); /* so next call compares 1 profile beyond current range */ return( (result == 0) ? OKAY : ( (result > 0) ? GAP : OVERLAP ) ); } /* FUNCTION: check_for_gap_at_end(range) Checks last given against the end of the database. (Supplements check_range() which checks first given against beginning of the database). This is done by attempting to DBMOVE one profile beyond the last . RETURNS: GAP if there are more profiles beyond last OKAY if last covers to end of database ierr otherwise PARAMETER: range = pointer to range to check against current db position NOTE: It leaves the db position at one profile past the end of , if not already at the end. */ int check_for_gap_at_end(range) RANGE_TYPE *range; { int ierr = 0, nsteps = 1; if (goto_end_of_range(range)) return(BAD_RANGE); DBMOVE(&nsteps, &ierr); if (ierr == SEARCH_BEYOND_END) return(OKAY); if (ierr == 0) return(GAP); /* more profiles follow */ return(ierr); } /* FUNCTION: print_range Prints to stream file with optional lead string/delimiter. */ void print_range(FILE *fp, RANGE_TYPE *range, char *string) { fprintf(fp, "%s", string); /* optional lead string, newline and/or delimiter */ switch(range->type) { case TIME_RANGE: print_ymdhms_time(fp, &(range->ru.time.start)); fprintf(fp, " to "); print_ymdhms_time(fp, &(range->ru.time.end)); if (fp == stdout) fprintf(fp, "\n"); return; case DAY_RANGE: fprintf(fp, "%d %g to %g", range->ru.day.yearbase, range->ru.day.start, range->ru.day.end); if (fp == stdout) fprintf(fp, "\n"); return; case BLOCK_RANGE: fprintf(fp, "%d to %d", range->ru.block.start, range->ru.block.end); if (fp == stdout) fprintf(fp, "\n"); return; case BLKPRF_RANGE: fprintf(fp, "%d %d to %d %d", range->ru.blkprf.start.block, range->ru.blkprf.start.profile, range->ru.blkprf.end.block, range->ru.blkprf.end.profile); if (fp == stdout) fprintf(fp, "\n"); return; } }