/* * Define routines for traversing bodypart sections within a bookreader * file. * * int bks_create_section_cursor ( void *bkf_ctx, void **cursor ); * int bks_delete_section_cursor ( void *cursor ); * int bks_seek_section ( void *cursor, int offset, int direction, * int *type, int *length, long *hdr[9] ); * int bks_read_section ( void *cursor, int *type, short *h_v, * unsigned char attr[4], int *length, char **data, int *is_last ); * * Author: David Jones * Date: 12-SEP-1995 */ #include #include #include #include #include "bookreader_recdef.h" #include "bookfile_io.h" #include "bookfile_section.h" /* validate prototypes */ struct section_context { void *bkf; /* bkf_open context for doing I/O */ bkrdr_recptr root; /* pointer to root part for file */ bkrdr_recptr cur; /* Currently open bodypart page */ bkrdr_recptr sec; /* current subrec within bodypart */ long sect_id; /* Currently positions section (-1) for none*/ int cur_alloc; /* Allocation size of cur */ int cur_offset; /* Offset of sec within cur */ int offset; /* Current read offset in sect */ }; typedef struct section_context *bksctx; int bks_create_section_cursor ( void *bkf, void **cursor ) { bksctx ctx; int status, page_length, length; /* * allocate context block. */ ctx = (bksctx) malloc ( sizeof(struct section_context) ); if ( !ctx ) return SS$_INSFMEM; ctx->bkf = bkf; ctx->cur_alloc = 0; ctx->sect_id = -1; ctx->offset = 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 ); *cursor = (void *) ctx; return status; } int bks_delete_section_cursor ( void *context ) { bksctx ctx; /* * Deallocate any structures and sub-structures allocated. */ ctx = (bksctx) context; if ( ctx->cur_alloc > 0 ) { free ( ctx->cur ); ctx->cur_alloc = 0; } free ( ctx ); return 1; } /****************************************************************************/ /* Scan starting at current position for section with matching ID. */ static int scan_for_section ( bksctx ctx, int id ) { int i; bkrdr_recptr cur, sec; cur = ctx->cur; for ( i = ctx->cur_offset; i < cur->gen.length; i += sec->gen.length ) { int kk; sec = (bkrdr_recptr) &cur->reloff[i]; #ifdef DEBUG printf(" [%d/%d] subrec type %d, length: %d id: ", i, cur->gen.length, sec->gen.type, sec->gen.length ); for (kk=0; kk < 9; kk++) printf("%d%s",sec->bodytext.bodhdr[kk], (kk < 8) ? " " : "\n" ); #endif if ( sec->gen.length < 0 ) return SS$_BUGCHECK; if ( sec->gen.type == BKSBREC_BODYTEXT ) { if ( sec->bodytext.sect_id == id ) break; } else if ( sec->gen.type == BKSBREC_FIGHOT ) { if ( sec->hotspot.target == id ) break; } else if ( sec->gen.type == BKSBREC_FIGURE ) { if ( sec->figure.sect == id ) break; } else if ( sec->gen.type == BKSBREC_EXTENSION ) { if ( sec->extension.sect == id ) break; } else { /* skip it unless current is one less */ if ( ctx->sect_id+1 == id ) break; } } ctx->cur_offset = i; ctx->sec = sec; if ( i >= cur->gen.length ) return SS$_BUGCHECK; /* not found */ ctx->sect_id = id; ctx->offset = sizeof(sec->bodytext); /* prepare for read_section() */ return 1; } /****************************************************************************/ /* Read page and intialize context for reading it's sections. */ static int load_bodypart ( bksctx ctx, int part_num ) { bkrdr_recptr cur, tmp; int status, tmp_len, page_length, sect_id; /* * Read page and validate type, function return pointer to I/O buffer. */ status = bkf_read_page ( ctx->bkf, part_num, &page_length, &tmp, &tmp_len ); if ( (status&1) == 0 ) return status; /* abort on read error */ if ( tmp->gen.type != BKREC_BODYPART ) return SS$_BUGCHECK; /* * Make copy of I/O buffer into cursor's private buffer, expanding it * if needed. */ if ( page_length > ctx->cur_alloc ) { if ( ctx->cur_alloc > 0 ) free ( ctx->cur ); ctx->cur_alloc = 0; cur = ctx->cur = (bkrdr_recptr) malloc ( page_length+15000); if ( !cur ) return SS$_INSFMEM; ctx->cur_alloc = page_length + 15000; } memcpy ( ctx->cur, tmp, page_length ); /* * Now position context for read_section calls. */ ctx->cur_offset = sizeof(cur->body); /* offset of 1st subrec */ ctx->offset = sizeof(cur->bodytext); /* offset subrec's 1st textrec*/ tmp_len = bkf_lookup_part_section ( ctx->bkf, part_num, §_id ); ctx->sect_id = sect_id; return status; } /****************************************************************************/ /* * Seek_section input args corresponde to fseek(): * direction = 0 -> offset is absolute. * direction = 1 -> offset is relative to current position. * direction = 2 -> offset is relative to end (unimplemented). * Seeking to a section prepares the section for subtext scans, seeking * to (cursor,0,1) resets context for the current cursor. */ int bks_seek_section ( void *cursor, int offset, int direction, int *type, int *length, long hdr[9] ) { bksctx ctx; bkrdr_recptr cur,sec; int status, pos, copy_size; ctx = (bksctx) cursor; #ifdef DEBUG printf("/bks/ seek_section entered, current sect=%d, alloc=%d, offset=%d/%d\n", ctx->sect_id, ctx->cur_alloc, ctx->cur_offset, ctx->offset ); #endif cur = ctx->cur; sec = ctx->sec; if ( (offset == 1) && (direction == 1) ) { /* * Do sequential seek to start of next subrecord in part. */ if ( ctx->sect_id < 0 ) return SS$_BADPARAM; /* no current position */ pos = ctx->cur_offset + sec->gen.length; if ( pos >= cur->gen.length ) { /* * Advance to next part. */ status = load_bodypart ( ctx, cur->body.nextpart ); if ( (status&1) == 0 ) return status; cur = ctx->cur; pos = sizeof(cur->body); } else { /* continue in same part */ ctx->cur_offset = pos; } ctx->offset = sizeof(sec->bodytext); sec = ctx->sec = (bkrdr_recptr) &cur->reloff[pos]; offset = ctx->sect_id; } else if ( direction == 1 ) { /* Convert relative position to absolute */ if ( ctx->sect_id < 0 ) return SS$_BADPARAM; /* no current position */ offset += ctx->sect_id; if ( offset <= 0 ) return SS$_BADPARAM; } else if ( (direction != 0) || (offset < 0) ) { return SS$_BADPARAM; } else if ( offset == ctx->sect_id ) { /* Redo current subrec */ ctx->offset = sizeof(sec->bodytext); } else if ( (offset-1) == ctx->sect_id && (ctx->sect_id >= 0) && (cur->gen.length > (ctx->cur_offset + sec->gen.length)) ) { /* * We want next section and it is likely in current part */ #ifdef DEBUG printf("/bks/ checking same part for section %d\n", offset ); #endif ctx->cur_offset += sec->gen.length; scan_for_section ( ctx, offset ); sec = ctx->sec; } if ( offset != ctx->sect_id ) { int part_num; /* * We still haven't found part, use section map to locate * part number containing the section. */ ctx->sect_id = -1; /* invalidate current position */ status = bkf_lookup_section ( ctx->bkf, offset, &part_num ); if ( (status&1) == 0 ) return status; /* not in map */ #ifdef DEBUG printf("/bks/ checking new part (%d) for section %d\n", part_num, offset ); #endif /* * Load page and seek to section. */ status = load_bodypart ( ctx, part_num ); if ( (status&1) == 0 ) return status; status = scan_for_section ( ctx, offset ); if ( (status&1) == 0 ) return status; sec = ctx->sec; } /* * return the section header to the caller. Call may specify null for * length and hdr arguments. */ *type = sec->gen.type; if ( length ) *length = sec->gen.length; if ( hdr ) { /* make it optional */ copy_size = sizeof(long)*9; if ( copy_size + sizeof(sec->gen) > sec->gen.length ) copy_size = sec->gen.length - sizeof(sec->gen); memcpy ( hdr, sec->bodytext.bodhdr, copy_size ); } return 1; } /************************************************************************/ /* Return next text record from current section. */ int bks_read_section ( void *cursor, int *type, short *h_v, unsigned char attr[4], int *length, char **data, int *is_last ) { bksctx ctx; bkrdr_recptr cur,sec; struct text_rec *txt; int status, i, txtlen; ctx = (bksctx) cursor; sec = ctx->sec; i = ctx->offset; #ifdef DEBUG printf("/bks/ reading section of type: %d, length: %d, offset: %d\n", sec->gen.type, sec->gen.length, i); #endif if ( i < sec->gen.length ) { /* return data */ txt = (struct text_rec *) &sec->reloff[i]; if ( sec->gen.type != BKSBREC_BODYTEXT || txt->type < 1 || txt->type > 3 ) { *type = 0; *length = txt->reclen; h_v[0] = h_v[1] = 0; attr[0] = attr[1] = attr[2] = attr[3] = 0; txtlen = sec->gen.length - i; /* rest of section */ *length = txtlen; *data = &sec->reloff[i]; } else { /* Extract text header */ *type = txt->type; txtlen = txt->reclen; h_v[0] = txt->hor; h_v[1] = txt->ver; attr[0] = txt->fontno; attr[1] = txt->x; attr[2] = txt->y; attr[3] = txt->data[0]; *length = txtlen - 9; /* subtact header */ *data = txt->data; } /* * Update offset for next call. */ ctx->offset = i + txtlen; if ( i > ctx->offset ) return SS$_BUGCHECK; if ( ctx->offset < sec->gen.length ) *is_last = 0; else { cur = ctx->cur; if ( cur->gen.length > (ctx->cur_offset + sec->gen.length)) *is_last = 1; else *is_last = 2; } return 1; } else { /* No more records */ cur = ctx->cur; if ( cur->gen.length > (ctx->cur_offset + sec->gen.length)) *is_last = 1; else *is_last = 2; return SS$_ENDOFFILE; } } int bks_seek_part ( void *cursor, int part_offset, int dir, int *sect_cnt ) { bksctx ctx; bkrdr_recptr cur,sec; int status, i; ctx = (bksctx) cursor; cur = ctx->cur; #ifdef DEBUG printf("/bks/ seek_part entered, current sect=%d, alloc=%d, offset=%d/%d\n", ctx->sect_id, ctx->cur_alloc, ctx->cur_offset, ctx->offset ); printf("/bks/ seek_part offset: %d dir: %d\n", part_offset, dir ); #endif if ( dir == 1 ) { /* * Relative seek, traverse links in part headers. */ if ( ctx->sect_id < 0 ) return SS$_BADPARAM; /* no current position */ while ( part_offset != 0 ) { if ( part_offset < 0 ) { status = load_bodypart ( ctx, ctx->cur->body.prevpart ); part_offset++; } else { part_offset--; status = load_bodypart ( ctx, ctx->cur->body.nextpart ); } if ( (status&1) == 0 ) return status; } } else if ( dir == 0 ) { status = load_bodypart ( ctx, part_offset ); if ( (status&1) == 0 ) return status; } if ( dir != 0 ) return SS$_BADPARAM; /* * scan the sub-records to determine the initial section id */ cur = ctx->cur; *sect_cnt = cur->body.sectcnt; ctx->sec = (bkrdr_recptr) &cur->reloff[sizeof(cur->body)]; return 1; } int bks_get_cursor_info ( void *cursor, int parts_info[4], /* 0-cur_part, 1-prev, 2-next, 3-reserved */ int sect_info[4] ) /* 0-first_sect, 1-sect_count, 2-cur_sect */ { bksctx ctx; bkrdr_recptr cur,sec; struct text_rec *txt; int status, i, txtlen; ctx = (bksctx) cursor; sec = ctx->sec; cur = ctx->cur; if ( !cur ) return SS$_ENDOFFILE; bkf_lookup_first_section ( ctx->bkf, ctx->sect_id, &parts_info[0], §_info[0] ); parts_info[1] = cur->body.prevpart; parts_info[2] = cur->body.nextpart; parts_info[3] = cur->body.unk1; sect_info[1] = cur->body.sectcnt; sect_info[2] = ctx->sect_id; sect_info[3] = ctx->offset; return 1; }