[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]
/*
 * Define routines for handling indices
 *
 *   int bki_create_context ( void *bkfctx, void **context );
 *   int bki_delete_context ( void *ctx );
 *   int bki_find_index ( void *ctx, char *pattern, int pat_type,
 *		char name[256], int *type, int *count);
 *   int bki_find_index_end ( void *ctx )
 *
 *   int bki_open_index ( void *ctx, char *name );   
 *   int bki_read_index ( void *ctx, char name[256] );
 *   int bki_close_index ( void *ctx );
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ssdef.h>

#include "bookreader_recdef.h"
#include "bookfile_io.h"
#include "bookfile_index.h"	/* validate prototypes */

struct index_context {
    void *bkf;			/* bkf_open context for doing I/O */
    bkrdr_recptr root;		/* pointer to root part for file */
    bkrdr_recptr cur;		/* Current open index page */
    int cur_alloc;		/* Allocation size of cur */
    int offset;			/* Current read offset in cur */
    int find_context;		/* Offset for find_index */
};
typedef struct index_context *bkictx;
static bkrdr_recptr lookup_index ( bkictx ctx, char *name );	/* fwd ref */
static int strmatchwild ( char *cand, char *pattern );

int bki_create_context ( void *bkf, void **context )
{
    bkictx ctx;
    int status, page_length, length;
    /*
     * allocate context block.
     */
    ctx = (bkictx) malloc ( sizeof(struct index_context) );
    if ( !ctx ) return SS$_INSFMEM;
    ctx->bkf = bkf;
    ctx->cur_alloc = 0;
    ctx->offset = 0;
    ctx->find_context = 0;
    /*
     * Make local copy of pointer to root part of file (part 0).  We assume
     * bkf_read returns address of a static structure for part 0.
     */
    status = bkf_read_page ( bkf, 0, &page_length, &ctx->root, &length );
    *context = (void *) ctx;
    return status;
}

int bki_delete_context ( void *context )
{
    bkictx ctx;
    /*
     * Deallocate any structures and sub-structures allocated.
     */
    ctx = (bkictx) context;

    if ( ctx->cur_alloc > 0 ) {
	free ( ctx->cur );
	ctx->cur_alloc = 0;
    }
    free ( ctx );
    return 1;
}
/*****************************************************************************/
/* Scan indexes listed in the root part for index with matching name(s).
 */
int bki_find_index ( void *context, char *pattern, int pat_type, 
	char name[256], int *type, int *count )
{
    bkictx ctx;
    bkrdr_recptr root, subrec;
    int status, has_wild, i;

    ctx = (bkictx) context;
    root = ctx->root;
    /*
     *  See if find_index is in progress.
     */
    if ( ctx->find_context == 0 ) {
	/* 
	 * Start new search, Examine for wildcards.
	 */
	for ( i = has_wild = 0; pattern[i]; i++ ) 
		if ( pattern[i] == '*' || pattern[i] == '%' ) {
	    has_wild = 1;
	    break;
	}
	if ( pat_type == -1 ) has_wild = 1;
	if ( has_wild ) {
	    /* Set find_context to offset of first sub-record */
	    ctx->find_context = sizeof(root->first);
	} else {
	    /* Do direct match */
	    subrec = lookup_index ( ctx, name );
	    if ( !subrec ) return 0;

	    *type = subrec->table.keyid;
	    *count = subrec->table.count;
	    strncpy ( name, subrec->table.title, subrec->table.tit_len );
	    return 1;
	}
    }
    /*
     * Continue search.
     */
    for ( i = ctx->find_context; i < root->gen.length;
		i += subrec->gen.length ) {
	subrec = (bkrdr_recptr) &root->reloff[i];
	if ( subrec->gen.length <= 0 ) break;	/* invalid subrec */
        /*
	 * Test only the table header records.
	 */
	if ( subrec->gen.type == BKSBREC_TABLE ) {
	    if ( pat_type >= 0 ) 	/* make sure type matches */
			if ( pat_type != subrec->table.keyid ) continue;
	    if ( 0 == strmatchwild ( subrec->table.title, pattern ) ) {
		*type = subrec->table.keyid;
		*count = subrec->table.count;
		strncpy ( name, subrec->table.title, subrec->table.tit_len );
		name[subrec->table.tit_len] = '\0';
		ctx->find_context = i + subrec->gen.length;
		return 1;
	    }
	}
    }
    /*
     * No more entires.
     */
    ctx->find_context = i;
    return 2;
}
/*
 * Return pointer to matching index record.
 */
static bkrdr_recptr lookup_index ( bkictx ctx, char *name )
{
    bkrdr_recptr subrec, root;
    int i, namlen;

    root = ctx->root;
    namlen = strlen ( name ) + 1;
    /*
     * Scan subrecords of root.
     */
    for ( i = sizeof(root->first); i < root->gen.length; 
		i+= subrec->gen.length ) {
	subrec = (bkrdr_recptr) &root->reloff[i];
	if ( subrec->gen.length <= 0 ) break;
	/*
	 * Check table records found for matching name.
	 */
	if ( subrec->gen.type == BKSBREC_TABLE ) {
	    if ( subrec->table.tit_len == namlen ) if 
		( 0==strncmp(subrec->table.title, name, namlen)) return subrec;
	}
    }
   return (bkrdr_recptr) 0;
}
/**************************************************************************/
/* Reset find context.
 */
int bki_find_index_end ( void *context )
{
    bkictx ctx;

    ctx = (bkictx) context;
    ctx->find_context = 0;
    return 1;
}

/************************************************************************/
/* Emulate VMS wildcard matching routine for c strings.  Algorithm obtained
 * by decoding VAX version of STR$MATCH_WILD.  A successful match returns
 * 0 to correspond with success status of tu_strncmp() routine.
 */
#define WC_FAIL 1
#define WC_MATCH 0
static int strmatchwild ( char *cand, char *pattern )
{
   char *save_cand, *save_pattern;
   /*
    * Main loop, check each character in pattern string.
    */
   for ( save_cand = (char *) 0; *pattern; pattern++ ) {
	if ( *pattern == '*' ) {
	    /*
	     * Wildcard character, if it is last character in pattern we know
	     * we have match.
	     */
	    if ( pattern[1] == '\0' ) return WC_MATCH;
	    /*
	     * checkpoint the current position of
	     * both strings so we can resume at the succeeding candidate
	     * string character if substring starting at current position
	     * fails.
	     */
	    save_cand = cand; 
	    save_pattern = pattern;

	} else if ( *cand++ != *pattern ) if ( *pattern != '%' ) {
	    /*
	     * Characters did not match, if no '*'s in preceding pattern,
	     * then strings do not match.
	     */
	    if ( !save_cand ) return WC_FAIL;
	    /*
	     * Resume search at next candidate character, updating
	     * the candidate checkpoint position.  If no more characters,
	     * then strings do not match.
	     */
	    if ( *save_cand++ == '\0' ) return WC_FAIL;
	    cand = save_cand;
	    pattern = save_pattern;
	}
   }
   /*
    * If pattern matched to this point, we succeed if candidate also at end.
    */
   if ( *cand == '\0' ) return WC_MATCH;
   return WC_FAIL;
}
/**************************************************************************/
int bki_open_index ( void *context, char *name )
{
    bkictx ctx;
    bkrdr_recptr tblrec, ndxrec;
    int status, page_length, length;

    /*
     * Lookup index.
     */
    ctx = (bkictx) context;
    tblrec = lookup_index ( ctx, name );
    if ( !tblrec ) return 0;
    /*
     * read page and make copy.
     */
    status = bkf_read_page ( ctx->bkf, tblrec->table.part, &page_length,
	&ndxrec, &length );
    if ( (status&1) == 0 ) return status;
    if ( ndxrec->gen.type != BKREC_INDEX ) return SS$_BUGCHECK;

    if ( ctx->cur_alloc <= page_length ) {
	/* Allocate buffer */
	if ( ctx->cur_alloc > 0 ) {
	    ctx->cur_alloc = 0;
	    free ( ctx->cur );
	}
	ctx->cur = (bkrdr_recptr) malloc ( page_length + 15000 );
	if ( !ctx->cur ) return SS$_INSFMEM;
	ctx->cur_alloc = page_length + 15000;
    }
    memcpy ( ctx->cur, ndxrec, page_length );
    ndxrec = ctx->cur;
    /*
     * Initalize offset.
     */
    ctx->offset = sizeof(ndxrec->gen);
    return 1;
}
int bki_close_index ( void *context )
{
    bkictx ctx;

    ctx = (bkictx) context;
    ctx->offset = 0;
    return 1;		/* nothing to do */
}
/****************************************************************************/
/* Read table index entry and current offset and return data to caller.
 */
int bki_read_index ( void *context, 
	short hdr[9], 		/* Index entry attributes + hor,ver of name */
	unsigned char attr[4],  /* Fontno, x, y, length of name */
	char name[256], 	/* Entry name */
	char **desc, 		/* Display descricption. */
	long *value )		/* Section number or special value */
{

    bkictx ctx;
    bkrdr_recptr ndxrec, subrec;
    struct text_rec *trec;
    int status, offset, i, j, sublen, namlen;
    long *vptr;
    /*
     * Recover context and do consitency checks.
     */
    ctx = (bkictx) context;
    offset = ctx->offset;
    if ( offset <= 0 ) return SS$_BADPARAM;
    ndxrec = ctx->cur;
    if ( offset >= ndxrec->gen.length ) return SS$_NOMOREFILES;
    /*
     * Look at record record at current offset, update offset used for
     * next read.
     */
    subrec = (bkrdr_recptr) &ndxrec->reloff[offset];
    if ( subrec->gen.type != BKSBREC_IXTXT ) return SS$_BUGCHECK;
    ctx->offset = offset + subrec->gen.length;
    /*
     * Copy fixed portion of ixtxt record to caller's arguments and initialize
     * variable portion to null values.
     */
    for ( i = 0; i < 7; i++ ) hdr[i] = subrec->ixtxt.ixhdr[i];
    hdr[7] = hdr[8] = 0;
    namlen = 0;
    name[0] = '\0';
    *desc = "";
    *value = 0;
    attr[0] = attr[1] = attr[2] = attr[3] = 0;
    /*
     * Scan text subrecords.
     */
    for ( i = sizeof(subrec->ixtxt); i < subrec->gen.length; i += sublen ) {
	trec = (struct text_rec *) &subrec->reloff[i];
	sublen = trec->reclen;
	switch ( trec->type ) {
	    case 1:
		break;
	    case 2:
	    case 3:
		/*
		 * Contcatenate strings.  Save attributes on first string only.
		 */
		j = sublen - sizeof(struct text_rec) + sizeof(trec->data);
		if ( namlen == 0 ) {
		    hdr[7] = trec->hor;
		    hdr[8] = trec->ver;
		    attr[0] = trec->fontno;
		    attr[1] = trec->x;
		    attr[2] = trec->y;
		}
		if ( (namlen + j) < 254 ) {
		    if ( namlen != 0 ) name[namlen++] = '\0';	/* fake word boundary */
		    memcpy ( &name[namlen], trec->data, j );
		    namlen += j;
		    name[namlen] = '\0';
		}
		attr[3] = namlen;
		break;

	    default:
		/*
		 * Assume this is description following name.
		 */
		*desc = &subrec->reloff[i];
		vptr = (long *) &subrec->reloff[i+strlen(*desc)+1];
		*value = *vptr;
		sublen = 0;
		i = subrec->gen.length;	/* force loop exit */
		break;
	}
    }
    return 1;
}