[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]
/*
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ssdef.h>

#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, &sect_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], &sect_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;
}