/* * 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 #include #include #include #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; }