[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
[0387]
[0388]
[0389]
[0390]
[0391]
[0392]
[0393]
[0394]
[0395]
[0396]
[0397]
[0398]
[0399]
[0400]
[0401]
[0402]
[0403]
[0404]
[0405]
[0406]
[0407]
[0408]
[0409]
[0410]
[0411]
[0412]
[0413]
/*
 * 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 <stdlib.h>
#include <stdio.h>
#include <ssdef.h>
#include <rms.h>
#include <fscndef.h>
#include <string.h>
#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;
}