/* * Low-level routines for accessing bookreader files via RMS. Records * are read via rfa address. * * int bkf_open ( char *fname, char *defname, void **retctx ); * int bkf_close ( void *ctx ); * char *bkf_last_error ( void *ctx ); * int bkf_read ( void *ctx, file_address *rfa, *bufaddr, *length ); * int bkf_read_page ( void *ctx, id, *page_length, *bufaddr, *length ); * int bkf_lookup_section ( void *ctx, int sect-id, int *part ); * int bkf_lookup_first_section ( void *ctx, int sect-id, int *part, int *fsec ); * * Function values returned are VMS condition codes. specifying a * null pointer for rfa causes a sequential read. * * Author: David Jones * Date: 10-SEP-1995 */ #include #include #include #include #include #include #include "bookreader_recdef.h" #include "bookfile_io.h" /* verify prototypes */ int sys$open(), sys$connect(), sys$close(), sys$get(); struct rfa_context { struct FAB fab; struct RAB rab; struct XABFHC xabfhc; /* for setting network block count */ struct XABITM xabitm; /* for setting mode */ struct { short length, code; long *bufaddr, *retlen, term; } xab_itemlist; long network_block_count; int high_id; /* highest allowed id */ int superbuf_alloc; bkrdr_recptr root; /* master info rec for file (1st) */ bkrdr_recptr lrec; /* page id -> file_address mapping */ bkrdr_recptr superbuf; /* Buffer for assembling large recs */ long *secmap; /* Section id -> page id mapping */ long *first_sec; /* First section id for page */ char *ubuf; /* buffer for I/O operations */ int partcount, sectcount; /* Local, aligned copy of root page */ /* cells */ char last_error[128]; /* text describing error */ }; typedef struct rfa_context *rfactx; /* * Since opens don't have a valid context, last error for NULL ctx is global. */ static char *last_open_error = ""; char *bkf_last_error ( void *vctx ) { rfactx ctx; if ( vctx ) { ctx = (rfactx) vctx; return ctx->last_error; } else return last_open_error; } /****************************************************************************/ /* Utility routine to allocate new buffer and copy contents of existing * buffer to it */ static bkrdr_recptr duplicate_record ( bkrdr_recptr source, int length ) { bkrdr_recptr new; new = (bkrdr_recptr) malloc ( length ); if ( new ) memcpy ( new, source, length ); return new; } /****************************************************************************/ /* Close bookreader file and deallocate context structures. */ int bkf_close ( void *vctx ) { rfactx ctx; int status; ctx = (rfactx) vctx; if ( !ctx ) return SS$_BADPARAM; status = sys$close ( &ctx->fab ); if ( ctx->root ) free ( ctx->root ); if ( ctx->lrec ) free ( ctx->lrec ); if ( ctx->secmap ) free ( ctx->secmap ); if ( ctx->ubuf ) free ( ctx->ubuf ); if ( ctx->superbuf_alloc > 0 ) free ( ctx->superbuf ); free ( ctx ); return status; } /****************************************************************************/ /* Open bookreader file and initialize context structures. * Arguments: * fname Name of bookreader file. * defname Default file specification, if NULL, will use * decw$book:.decw$book. * retctx Address of opaque pointer. */ int bkf_open ( char *fname, char *defname, void **retctx ) { rfactx ctx; bkrdr_recptr currec; int status, length; char default_name[128]; /* * Allocate and initialize context structures. */ last_open_error = ""; /* clear previous error */ *retctx = (void *) 0; ctx = (rfactx) malloc ( sizeof(struct rfa_context) ); if ( !ctx ) return 0; ctx->fab = cc$rms_fab; ctx->rab = cc$rms_rab; ctx->xabfhc = cc$rms_xabfhc; ctx->network_block_count = 127; ctx->high_id = 0; ctx->partcount = 0; ctx->sectcount = 0; ctx->superbuf_alloc = 0; ctx->root = (bkrdr_recptr) 0; ctx->lrec = (bkrdr_recptr) 0; ctx->ubuf = (char *) 0; ctx->fab.fab$l_fna = fname; ctx->fab.fab$b_fns = strlen(fname); if ( defname ) { ctx->fab.fab$l_dna = defname; ctx->fab.fab$b_dns = strlen ( defname ); } else { strcpy ( default_name, "DECW$BOOK:.DECW$BOOK" ); ctx->fab.fab$l_dna = default_name; ctx->fab.fab$b_dns = strlen(default_name); } ctx->fab.fab$b_shr = FAB$M_SHRGET; ctx->fab.fab$l_xab = (char *) &ctx->xabfhc; ctx->fab.fab$b_rtv = -1; /* retrieval window size */ ctx->xabfhc.xab$l_nxt = (char *) &ctx->xabitm; memset ( &ctx->xabitm, 0, sizeof(ctx->xabitm) ); ctx->xabitm.xab$b_cod = XAB$C_ITM; ctx->xabitm.xab$b_bln = XAB$K_ITMLEN; ctx->xabitm.xab$l_itemlist = (char *) &ctx->xab_itemlist; ctx->xabitm.xab$b_mode = XAB$K_SETMODE; ctx->xab_itemlist.length = sizeof(ctx->network_block_count); ctx->xab_itemlist.code = XAB$_NET_BLOCK_COUNT; ctx->xab_itemlist.bufaddr = &ctx->network_block_count; ctx->xab_itemlist.retlen = (long *) 0; ctx->xab_itemlist.term = 0; ctx->rab.rab$l_fab = &ctx->fab; ctx->rab.rab$b_rac = RAB$C_RFA; ctx->rab.rab$l_rop = RAB$M_LOC | RAB$M_RAH; /* locate mode and read-ahead*/ ctx->rab.rab$b_mbc = 127; /* multi-block count */ /* * Open file and connect record stream. */ status = sys$open ( &ctx->fab ); if ( (status&1) == 1 ) { if ( ctx->xabfhc.xab$w_lrl == 0 ) { ctx->rab.rab$w_usz = 32767; } else { ctx->rab.rab$w_usz = ctx->xabfhc.xab$w_lrl; } status = sys$connect ( &ctx->rab ); if ( (status&1) == 0 ) { last_open_error = "Error in SYS$CONNECT call"; sys$close ( &ctx->fab ); free ( ctx ); ctx = (rfactx) 0; } } else { last_open_error = "Error in SYS$OPEN call"; free ( ctx ); ctx = (rfactx) 0; } *retctx = (void *) ctx; if ( !ctx ) return status; /* * The FHC XAB on the open retrieved the longest record length (LRL) of * the file. Allocate a buffer. */ ctx->secmap = (long *) 0; ctx->ubuf = malloc ( ctx->rab.rab$w_usz ); /* * Read the initial page, this if first record in file so read sequentially. */ status = bkf_read ( (void *) ctx, BKF_NEXT_REC, &currec, &length ); if ( (status&1) == 1 ) { ctx->root = duplicate_record ( currec, length ); if ( !ctx->root ) return SS$_INSFMEM; ctx->partcount = ctx->root->first.partcount; ctx->sectcount = ctx->root->first.sectioncount; /* * Do sanity checks on root record and read last record. */ status = bkf_read ( (void *) ctx, &ctx->root->first.lastptr, &currec, &length ); if ( (status&1) == 1 ) { ctx->lrec = duplicate_record ( currec, length ); /* * Do sanity checks of last record and set page id limit. */ ctx->high_id = ((length - 6) / sizeof(ctx->lrec->last.dir[1])) - 1; } } return status; } /****************************************************************************/ /* Read RMS record from bookreader file. */ int bkf_read ( void *vctx, struct file_address *rfa, bkrdr_recptr *bufaddr, int *length ) { int status; rfactx ctx; ctx = (rfactx) vctx; if ( rfa ) { /* direct read to specified rfa */ ctx->rab.rab$b_rac = RAB$C_RFA; ctx->rab.rab$w_rfa[0] = rfa->wrd[0]; ctx->rab.rab$w_rfa[1] = rfa->wrd[1]; ctx->rab.rab$w_rfa[2] = rfa->wrd[2]; } else { /* Read sequentially */ ctx->rab.rab$b_rac = RAB$C_SEQ; } ctx->rab.rab$l_ubf = (char *) ctx->ubuf; status = sys$get ( &ctx->rab ); if ( (status&1) ) { *bufaddr = (bkrdr_recptr) ctx->rab.rab$l_rbf; *length = ctx->rab.rab$w_rsz; } else { *bufaddr = (bkrdr_recptr) ""; *length = 0; } return status; } /************************************************************************/ int bkf_read_page ( void *vctx, int id, int *page_length, bkrdr_recptr *bufaddr, int *length ) { int status, i; rfactx ctx; ctx = (rfactx) vctx; /* * Do limit checks on requested ID. */ if ( id < 0 || id > ctx->high_id ) { strcpy ( ctx->last_error, "Bad page number" ); return SS$_BADPARAM; } /* * Locate record from directory in the lrec. */ *page_length = ctx->lrec->last.dir[id].size; if ( id == 0 ) { /* We already have a copy of the root page, return it */ status = 1; *length = *page_length; *bufaddr = ctx->root; } else { /* * Read initial record. */ status = bkf_read ( vctx, &ctx->lrec->last.dir[id].rfa, bufaddr, length ); if ( (status&1) == 0 ) return status; /* * If page is larger than requested, assemble into large buffer. */ if ( *length < *page_length ) { /* * Allocate oversize buffer to accomodate whole thing. */ if ( ctx->superbuf_alloc < *page_length ) { if ( ctx->superbuf_alloc > 0 ) { free ( ctx->superbuf ); ctx->superbuf_alloc = 0; } ctx->superbuf = malloc ( *page_length + 15000 ); if ( !ctx->superbuf ) return SS$_INSFMEM; ctx->superbuf_alloc = *page_length + 15000; } /* * Copy peices into superbuf. */ memcpy ( ctx->superbuf->raw, (*bufaddr)->raw, *length ); for ( i = *length; i < *page_length; i += *length ) { /* * Last 10 chars of superbuf record should be rfa next record. * Just assume sequential read will do. */ status = bkf_read ( vctx, BKF_NEXT_REC, bufaddr, length ); if ( (status&1) == 0 ) return status; if ( ((*bufaddr)->gen.type == BKREC_CONT_END) || ((*bufaddr)->gen.type == BKREC_CONT_MID) ) { /* * Adjust state to trim tail off of superbuf and header * off of bufaddr. */ i = i - 10; *length = *length - 6; } else { break; /* unexpected record type */ } memcpy ( &ctx->superbuf->raw[i], (*bufaddr)->raw+6, *length ); } /* * update values. */ *bufaddr = ctx->superbuf; *length = ctx->superbuf->gen.length = *page_length; } } return status; } /***************************************************************************/ /* Convert section number to part number. */ int bkf_lookup_section ( void *vctx, int sect_id, int *part ) { int status, i, j, start, startpage, page, length, page_length; rfactx ctx; bkrdr_recptr tmp; ctx = (rfactx) vctx; if ( sect_id > ctx->sectcount ) return SS$_BADPARAM; if ( sect_id < 0 ) return SS$_BADPARAM; if ( !ctx->secmap ) { /* do initial load */ status = bkf_read_page ( vctx, ctx->root->first.index_page, &page_length, &tmp, &length ); if ( (status&1) == 0 ) return status; if ( tmp->gen.type != BKREC_SECMAP ) return SS$_BUGCHECK; i = (length - sizeof(tmp->gen)) / sizeof(tmp->secmap.map[1]); if ( i-1 != ctx->sectcount ) return SS$_BUGCHECK; /* * build our own secmap aligned. Negative numbers in map * are offset to entry for first section on that page. */ ctx->secmap = (long *) malloc ((i+ctx->partcount+1) * sizeof(long)); if ( !ctx->secmap ) return SS$_INSFMEM; ctx->first_sec = &ctx->secmap[i]; /* first sections */ for ( j = 0; j <= ctx->partcount; j++ ) ctx->first_sec[j] = -1; for ( j = 0; j < i; j++ ) { page = tmp->secmap.map[j]; #ifdef DEBUG printf("/bki/ secmap[%d] = %d\n", j, page ); #endif ctx->secmap[j] = page; if ( page < 0 || page > ctx->partcount ) { printf("secmap[%d] = %d is out of range\n", j, page ); return SS$_BUGCHECK; } else if ( ctx->first_sec[page] < 0 ) { ctx->first_sec[page] = j; /* first section of page */ } } } *part = ctx->secmap[sect_id]; return 1; } int bkf_lookup_first_section ( void *vctx, int sect_id, int *part, int *first ) { int status, page; rfactx ctx; ctx = (rfactx) vctx; if ( sect_id > ctx->sectcount ) return SS$_BADPARAM; if ( sect_id < 0 ) return SS$_BADPARAM; if ( !ctx->secmap ) { /* do initial load */ status = bkf_lookup_section ( vctx, sect_id, part ); if ( (status&1) == 0 ) return status; } page = ctx->secmap[sect_id]; *first = ctx->first_sec[page]; *part = page; return 1; } int bkf_lookup_part_section ( void *vctx, int part_id, int *first ) { int status, dummy; rfactx ctx; ctx = (rfactx) vctx; if ( part_id > ctx->partcount ) return SS$_BADPARAM; if ( part_id < 0 ) return SS$_BADPARAM; if ( !ctx->secmap ) { /* do initial load */ status = bkf_lookup_section ( vctx, 1, &dummy ); if ( (status&1) == 0 ) return status; } *first = ctx->first_sec[part_id]; return 1; }