/*
 * blocklist.c
 * Copyright (C) 1998-2001 A.J. van Os; Released under GPL
 *
 * Description:
 * Build, read and destroy a list of Word text blocks
 */

#include <stdlib.h>
#include "antiword.h"


/*
 * Private structure to hide the way the information
 * is stored from the rest of the program
 */
typedef struct list_mem_tag {
	text_block_type		tInfo;
	struct list_mem_tag	*pNext;
} list_mem_type;

/* Variables to describe the start of the five block lists */
static list_mem_type	*pTextAnchor = NULL;
static list_mem_type	*pFootAnchor = NULL;
static list_mem_type	*pUnused1Anchor = NULL;
static list_mem_type	*pEndAnchor = NULL;
static list_mem_type	*pUnused2Anchor = NULL;
/* Variable needed to build the block list */
static list_mem_type	*pBlockLast = NULL;
/* Variable needed to read the text block list */
static list_mem_type	*pTextBlockCurrent = NULL;
/* Variable needed to read the footnote block list */
static list_mem_type	*pFootBlockCurrent = NULL;
/* Variable needed to read the endnote block list */
static list_mem_type	*pEndBlockCurrent = NULL;
/* Last block read */
static unsigned char	aucBlock[BIG_BLOCK_SIZE];


/*
 * vDestroyTextBlockList - destroy the text block list
 */
void
vDestroyTextBlockList(void)
{
	list_mem_type	*apAnchor[5];
	list_mem_type	*pCurr, *pNext;
	int	iIndex;

	DBG_MSG("vDestroyTextBlockList");

	apAnchor[0] = pTextAnchor;
	apAnchor[1] = pFootAnchor;
	apAnchor[2] = pUnused1Anchor;
	apAnchor[3] = pEndAnchor;
	apAnchor[4] = pUnused2Anchor;

	for (iIndex = 0; iIndex < 5; iIndex++) {
		pCurr = apAnchor[iIndex];
		while (pCurr != NULL) {
			pNext = pCurr->pNext;
			pCurr = xfree(pCurr);
			pCurr = pNext;
		}
	}
	/* Show that there are no lists any more */
	pTextAnchor = NULL;
	pFootAnchor = NULL;
	pUnused1Anchor = NULL;
	pEndAnchor = NULL;
	pUnused2Anchor = NULL;
	/* Reset all the controle variables */
	pBlockLast = NULL;
	pTextBlockCurrent = NULL;
	pFootBlockCurrent = NULL;
	pEndBlockCurrent = NULL;
} /* end of vDestroyTextBlockList */

/*
 * bAdd2TextBlockList - add an element to the text block list
 *
 * returns: TRUE when successful, otherwise FALSE
 */
BOOL
bAdd2TextBlockList(text_block_type *pTextBlock)
{
	list_mem_type	*pListMember;

	fail(pTextBlock == NULL);
	fail(pTextBlock->lFileOffset < 0);
	fail(pTextBlock->lTextOffset < 0);
	fail(pTextBlock->lLength <= 0);
	fail(pTextBlock->bUsesUnicode && odd(pTextBlock->lLength));

	NO_DBG_MSG("bAdd2TextBlockList");
	NO_DBG_HEX(pTextBlock->lFileOffset);
	NO_DBG_HEX(pTextBlock->lTextOffset);
	NO_DBG_HEX(pTextBlock->lLength);
	NO_DBG_DEC(pTextBlock->bUsesUnicode);
	NO_DBG_DEC(pTextBlock->usPropMod);

	if (pTextBlock->lFileOffset < 0 ||
	    pTextBlock->lTextOffset < 0 ||
	    pTextBlock->lLength <= 0 ||
	    (pTextBlock->bUsesUnicode && odd(pTextBlock->lLength))) {
		werr(0, "Software (textblock) error");
		return FALSE;
	}
	/*
	 * Check for continuous blocks of the same character size and
	 * the same properties modifier
	 */
	if (pBlockLast != NULL &&
	    pBlockLast->tInfo.lFileOffset +
	     pBlockLast->tInfo.lLength == pTextBlock->lFileOffset &&
	    pBlockLast->tInfo.lTextOffset +
	     pBlockLast->tInfo.lLength == pTextBlock->lTextOffset &&
	    pBlockLast->tInfo.bUsesUnicode == pTextBlock->bUsesUnicode &&
	    pBlockLast->tInfo.usPropMod == pTextBlock->usPropMod) {
		/* These are continous blocks */
		pBlockLast->tInfo.lLength += pTextBlock->lLength;
		return TRUE;
	}
	/* Make a new block */
	pListMember = xmalloc(sizeof(list_mem_type));
	/* Add the block to the list */
	pListMember->tInfo = *pTextBlock;
	pListMember->pNext = NULL;
	if (pTextAnchor == NULL) {
		pTextAnchor = pListMember;
	} else {
		fail(pBlockLast == NULL);
		pBlockLast->pNext = pListMember;
	}
	pBlockLast = pListMember;
	return TRUE;
} /* end of bAdd2TextBlockList */


/*
 * vSplitBlockList - split the block list in five parts
 *
 * Split the blocklist in a Text block list, a Footnote block list, a
 * Endnote block list and two Unused lists.
 *
 * NOTE:
 * The various l*Len input parameters are given in characters, but the
 * length of the blocks are in bytes.
 */
void
vSplitBlockList(long lTextLen, long lFootnoteLen, long lUnused1Len,
	long lEndnoteLen, long lUnused2Len, BOOL bMustExtend)
{
	list_mem_type	*apAnchors[5];
	list_mem_type	*pGarbageAnchor, *pCurr, *pNext;
	long		lCharsToGo, lBytesTooFar;
	int		iIndex;
#if defined(DEBUG)
	long		lTotal;
#endif /* DEBUG */

	DBG_MSG("vSplitBlockList");

/* Text block list */
	pCurr = NULL;
	lCharsToGo = lTextLen;
	lBytesTooFar = -1;
	if (lTextLen > 0) {
		DBG_MSG("Text block list");
		DBG_DEC(lTextLen);
		for (pCurr = pTextAnchor;
		     pCurr != NULL;
		     pCurr = pCurr->pNext) {
			NO_DBG_DEC(pCurr->tInfo.lLength);
			fail(pCurr->tInfo.lLength <= 0);
			if (pCurr->tInfo.bUsesUnicode) {
				fail(odd(pCurr->tInfo.lLength));
				lCharsToGo -= pCurr->tInfo.lLength / 2;
				if (lCharsToGo < 0) {
					lBytesTooFar = -2 * lCharsToGo;
				}
			} else {
				lCharsToGo -= pCurr->tInfo.lLength;
				if (lCharsToGo < 0) {
					lBytesTooFar = -lCharsToGo;
				}
			}
			if (lCharsToGo <= 0) {
				break;
			}
		}
	}
/* Split the list */
	if (lTextLen <= 0) {
		/* Empty text blocks list */
		pFootAnchor = pTextAnchor;
		pTextAnchor = NULL;
	} else if (pCurr == NULL) {
		/* No footnote blocks */
		pFootAnchor = NULL;
	} else if (lCharsToGo == 0) {
		/* Move the integral number of footnote blocks */
		pFootAnchor = pCurr->pNext;
		pCurr->pNext = NULL;
	} else {
		/* Split the part-text block, part-footnote block */
		DBG_DEC(lBytesTooFar);
		fail(lBytesTooFar <= 0);
		pFootAnchor = xmalloc(sizeof(list_mem_type));
		DBG_HEX(pCurr->tInfo.lFileOffset);
		pFootAnchor->tInfo.lFileOffset =
				pCurr->tInfo.lFileOffset +
				pCurr->tInfo.lLength -
				lBytesTooFar;
		DBG_HEX(pFootAnchor->tInfo.lFileOffset);
		DBG_HEX(pCurr->tInfo.lTextOffset);
		pFootAnchor->tInfo.lTextOffset =
				pCurr->tInfo.lTextOffset +
				pCurr->tInfo.lLength -
				lBytesTooFar;
		DBG_HEX(pFootAnchor->tInfo.lTextOffset);
		pFootAnchor->tInfo.lLength = lBytesTooFar;
		pCurr->tInfo.lLength -= lBytesTooFar;
		pFootAnchor->tInfo.bUsesUnicode = pCurr->tInfo.bUsesUnicode;
		/* Move the integral number of footnote blocks */
		pFootAnchor->pNext = pCurr->pNext;
		pCurr->pNext = NULL;
	}
/* Footnote block list */
	pCurr = NULL;
	lCharsToGo = lFootnoteLen;
	lBytesTooFar = -1;
	if (lFootnoteLen > 0) {
		DBG_MSG("Footnote block list");
		DBG_DEC(lFootnoteLen);
		for (pCurr = pFootAnchor;
		     pCurr != NULL;
		     pCurr = pCurr->pNext) {
			DBG_DEC(pCurr->tInfo.lLength);
			fail(pCurr->tInfo.lLength <= 0);
			if (pCurr->tInfo.bUsesUnicode) {
				fail(odd(pCurr->tInfo.lLength));
				lCharsToGo -= pCurr->tInfo.lLength / 2;
				if (lCharsToGo < 0) {
					lBytesTooFar = -2 * lCharsToGo;
				}
			} else {
				lCharsToGo -= pCurr->tInfo.lLength;
				if (lCharsToGo < 0) {
					lBytesTooFar = -lCharsToGo;
				}
			}
			if (lCharsToGo <= 0) {
				break;
			}
		}
	}
/* Split the list */
	if (lFootnoteLen <= 0) {
		/* Empty footnote list */
		pUnused1Anchor = pFootAnchor;
		pFootAnchor = NULL;
	} else if (pCurr == NULL) {
		/* No unused1 blocks */
		pUnused1Anchor = NULL;
	} else if (lCharsToGo == 0) {
		/* Move the integral number of unused1-list blocks */
		pUnused1Anchor = pCurr->pNext;
		pCurr->pNext = NULL;
	} else {
	  	/* Split the part-footnote block, part-unused1 block */
		DBG_DEC(lBytesTooFar);
		fail(lBytesTooFar <= 0);
		pUnused1Anchor = xmalloc(sizeof(list_mem_type));
		DBG_HEX(pCurr->tInfo.lFileOffset);
		pUnused1Anchor->tInfo.lFileOffset =
				pCurr->tInfo.lFileOffset +
				pCurr->tInfo.lLength -
				lBytesTooFar;
		DBG_HEX(pUnused1Anchor->tInfo.lFileOffset);
		DBG_HEX(pCurr->tInfo.lTextOffset);
		pUnused1Anchor->tInfo.lTextOffset =
				pCurr->tInfo.lTextOffset +
				pCurr->tInfo.lLength -
				lBytesTooFar;
		DBG_HEX(pUnused1Anchor->tInfo.lTextOffset);
		pUnused1Anchor->tInfo.lLength = lBytesTooFar;
		pCurr->tInfo.lLength -= lBytesTooFar;
		pUnused1Anchor->tInfo.bUsesUnicode =
				pCurr->tInfo.bUsesUnicode;
		/* Move the integral number of unused1 blocks */
		pUnused1Anchor->pNext = pCurr->pNext;
		pCurr->pNext = NULL;
	}
/* Unused1 block list */
	pCurr = NULL;
	lCharsToGo = lUnused1Len;
	lBytesTooFar = -1;
	if (lUnused1Len > 0) {
		DBG_MSG("Unused1 block list");
		DBG_DEC(lUnused1Len);
		for (pCurr = pUnused1Anchor;
		     pCurr != NULL;
		     pCurr = pCurr->pNext) {
			DBG_DEC(pCurr->tInfo.lLength);
			fail(pCurr->tInfo.lLength <= 0);
			if (pCurr->tInfo.bUsesUnicode) {
				fail(odd(pCurr->tInfo.lLength));
				lCharsToGo -= pCurr->tInfo.lLength / 2;
				if (lCharsToGo < 0) {
					lBytesTooFar = -2 * lCharsToGo;
				}
			} else {
				lCharsToGo -= pCurr->tInfo.lLength;
				if (lCharsToGo < 0) {
					lBytesTooFar = -lCharsToGo;
				}
			}
			if (lCharsToGo <= 0) {
				break;
			}
		}
	}
/* Split the list */
	if (lUnused1Len <= 0) {
		/* Empty unused1 list */
		pEndAnchor = pUnused1Anchor;
		pUnused1Anchor = NULL;
	} else if (pCurr == NULL) {
		/* No endnote blocks */
		pEndAnchor = NULL;
	} else if (lCharsToGo == 0) {
		/* Move the intergral number of endnote blocks */
		pEndAnchor = pCurr->pNext;
		pCurr->pNext = NULL;
	} else {
		/* Split the part-unused1-list block, part-endnote block */
		DBG_DEC(lBytesTooFar);
		fail(lBytesTooFar <= 0);
		pEndAnchor = xmalloc(sizeof(list_mem_type));
		DBG_HEX(pCurr->tInfo.lFileOffset);
		pEndAnchor->tInfo.lFileOffset =
				pCurr->tInfo.lFileOffset +
				pCurr->tInfo.lLength -
				lBytesTooFar;
		DBG_HEX(pEndAnchor->tInfo.lFileOffset);
		DBG_HEX(pCurr->tInfo.lTextOffset);
		pEndAnchor->tInfo.lTextOffset =
				pCurr->tInfo.lTextOffset +
				pCurr->tInfo.lLength -
				lBytesTooFar;
		DBG_HEX(pEndAnchor->tInfo.lTextOffset);
		pEndAnchor->tInfo.lLength = lBytesTooFar;
		pCurr->tInfo.lLength -= lBytesTooFar;
		pEndAnchor->tInfo.bUsesUnicode = pCurr->tInfo.bUsesUnicode;
		/* Move the integral number of endnote blocks */
		pEndAnchor->pNext = pCurr->pNext;
		pCurr->pNext = NULL;
	}
/* Endnote block list */
	pCurr = NULL;
	lCharsToGo = lEndnoteLen;
	lBytesTooFar = -1;
	if (lEndnoteLen > 0) {
		DBG_MSG("Endnote block list");
		DBG_DEC(lEndnoteLen);
		for (pCurr = pEndAnchor;
		     pCurr != NULL;
		     pCurr = pCurr->pNext) {
			DBG_DEC(pCurr->tInfo.lLength);
			fail(pCurr->tInfo.lLength <= 0);
			if (pCurr->tInfo.bUsesUnicode) {
				fail(odd(pCurr->tInfo.lLength));
				lCharsToGo -= pCurr->tInfo.lLength / 2;
				if (lCharsToGo <= 0) {
					lBytesTooFar = -2 * lCharsToGo;
				}
			} else {
				lCharsToGo -= pCurr->tInfo.lLength;
				if (lCharsToGo <= 0) {
					lBytesTooFar = -lCharsToGo;
				}
			}
			if (lCharsToGo <= 0) {
				break;
			}
		}
	}
/* Split the list */
	if (lEndnoteLen <= 0) {
		/* Empty endnote list */
		pUnused2Anchor = pEndAnchor;
		pEndAnchor = NULL;
	} else if (pCurr == NULL) {
		/* No unused2 blocks */
		pUnused2Anchor = NULL;
	} else if (lCharsToGo == 0) {
		/* Move the intergral number of unused2 blocks */
		pUnused2Anchor = pCurr->pNext;
		pCurr->pNext = NULL;
	} else {
		/* Split the part-endnote block, part-unused2 block */
		DBG_DEC(lBytesTooFar);
		fail(lBytesTooFar <= 0);
		pUnused2Anchor = xmalloc(sizeof(list_mem_type));
		DBG_HEX(pCurr->tInfo.lFileOffset);
		pUnused2Anchor->tInfo.lFileOffset =
				pCurr->tInfo.lFileOffset +
				pCurr->tInfo.lLength -
				lBytesTooFar;
		DBG_HEX(pUnused2Anchor->tInfo.lFileOffset);
		DBG_HEX(pCurr->tInfo.lTextOffset);
		pUnused2Anchor->tInfo.lTextOffset =
				pCurr->tInfo.lTextOffset +
				pCurr->tInfo.lLength -
				lBytesTooFar;
		DBG_HEX(pUnused2Anchor->tInfo.lTextOffset);
		pUnused2Anchor->tInfo.lLength = lBytesTooFar;
		pCurr->tInfo.lLength -= lBytesTooFar;
		pUnused2Anchor->tInfo.bUsesUnicode =
				pCurr->tInfo.bUsesUnicode;
		/* Move the integral number of unused2 blocks */
		pUnused2Anchor->pNext = pCurr->pNext;
		pCurr->pNext = NULL;
	}
/* Unused2 block list */
	pCurr = NULL;
	lCharsToGo = lUnused2Len;
	lBytesTooFar = -1;
	if (lUnused2Len > 0) {
		DBG_MSG("Unused2 block list");
		DBG_DEC(lUnused2Len);
		for (pCurr = pUnused2Anchor;
		     pCurr != NULL;
		     pCurr = pCurr->pNext) {
			DBG_DEC(pCurr->tInfo.lLength);
			fail(pCurr->tInfo.lLength <= 0);
			if (pCurr->tInfo.bUsesUnicode) {
				fail(odd(pCurr->tInfo.lLength));
				lCharsToGo -= pCurr->tInfo.lLength / 2;
				if (lCharsToGo < 0) {
					lBytesTooFar = -2 * lCharsToGo;
				}
			} else {
				lCharsToGo -= pCurr->tInfo.lLength;
				if (lCharsToGo < 0) {
					lBytesTooFar = -lCharsToGo;
				}
			}
			if (lCharsToGo <= 0) {
				break;
			}
		}
	}
/* Split the list */
	if (lUnused2Len <= 0) {
		/* Empty unused2 list */
		pGarbageAnchor = pUnused2Anchor;
		pUnused2Anchor = NULL;
	} else if (pCurr == NULL) {
		/* No garbage block list */
		pGarbageAnchor = NULL;
	} else if (lCharsToGo == 0) {
		/* Move the intergral number of garbage blocks */
		pGarbageAnchor = pCurr->pNext;
		pCurr->pNext = NULL;
	} else {
		/* Reduce the part-unused2 block */
		DBG_DEC(lBytesTooFar);
		fail(lBytesTooFar <= 0);
		pCurr->tInfo.lLength -= lBytesTooFar;
		/* Move the integral number of garbage blocks */
		pGarbageAnchor = pCurr->pNext;
		pCurr->pNext = NULL;
	}
/* Free the garbage block list, this should never be needed */
	pCurr = pGarbageAnchor;
	while (pCurr != NULL) {
		DBG_FIXME();
		DBG_HEX(pCurr->tInfo.lFileOffset);
		DBG_DEC(pCurr->tInfo.lLength);
		pNext = pCurr->pNext;
		pCurr = xfree(pCurr);
		pCurr = pNext;
	}

#if defined(DEBUG)
	/* Check the number of bytes in the block lists */
	lTotal = 0;
	for (pCurr = pTextAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
		NO_DBG_HEX(pCurr->tInfo.lFileOffset);
		NO_DBG_HEX(pCurr->tInfo.lTextOffset);
		NO_DBG_DEC(pCurr->tInfo.lLength);
		fail(pCurr->tInfo.lLength <= 0);
		if (pCurr->tInfo.bUsesUnicode) {
			fail(odd(pCurr->tInfo.lLength));
			lTotal += pCurr->tInfo.lLength / 2;
		} else {
			lTotal += pCurr->tInfo.lLength;
		}
	}
	DBG_DEC(lTotal);
	if (lTotal != lTextLen) {
		DBG_DEC(lTextLen);
		werr(1, "Software error (Text)");
	}
	lTotal = 0;
	for (pCurr = pFootAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
		DBG_HEX(pCurr->tInfo.lFileOffset);
		NO_DBG_HEX(pCurr->tInfo.lTextOffset);
		DBG_DEC(pCurr->tInfo.lLength);
		fail(pCurr->tInfo.lLength <= 0);
		if (pCurr->tInfo.bUsesUnicode) {
			fail(odd(pCurr->tInfo.lLength));
			lTotal += pCurr->tInfo.lLength / 2;
		} else {
			lTotal += pCurr->tInfo.lLength;
		}
	}
	DBG_DEC(lTotal);
	if (lTotal != lFootnoteLen) {
		DBG_DEC(lFootnoteLen);
		werr(1, "Software error (Footnotes)");
	}
	lTotal = 0;
	for (pCurr = pUnused1Anchor; pCurr != NULL; pCurr = pCurr->pNext) {
		DBG_HEX(pCurr->tInfo.lFileOffset);
		NO_DBG_HEX(pCurr->tInfo.lTextOffset);
		DBG_DEC(pCurr->tInfo.lLength);
		fail(pCurr->tInfo.lLength <= 0);
		if (pCurr->tInfo.bUsesUnicode) {
			fail(odd(pCurr->tInfo.lLength));
			lTotal += pCurr->tInfo.lLength / 2;
		} else {
			lTotal += pCurr->tInfo.lLength;
		}
	}
	DBG_DEC(lTotal);
	if (lTotal != lUnused1Len) {
		DBG_DEC(lUnused1Len);
		werr(1, "Software error (Unused1-list)");
	}
	lTotal = 0;
	for (pCurr = pEndAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
		DBG_HEX(pCurr->tInfo.lFileOffset);
		NO_DBG_HEX(pCurr->tInfo.lTextOffset);
		DBG_DEC(pCurr->tInfo.lLength);
		fail(pCurr->tInfo.lLength <= 0);
		if (pCurr->tInfo.bUsesUnicode) {
			fail(odd(pCurr->tInfo.lLength));
			lTotal += pCurr->tInfo.lLength / 2;
		} else {
			lTotal += pCurr->tInfo.lLength;
		}
	}
	DBG_DEC(lTotal);
	if (lTotal != lEndnoteLen) {
		DBG_DEC(lEndnoteLen);
		werr(1, "Software error (Endnotes)");
	}
	lTotal = 0;
	for (pCurr = pUnused2Anchor; pCurr != NULL; pCurr = pCurr->pNext) {
		DBG_HEX(pCurr->tInfo.lFileOffset);
		NO_DBG_HEX(pCurr->tInfo.lTextOffset);
		DBG_DEC(pCurr->tInfo.lLength);
		fail(pCurr->tInfo.lLength <= 0);
		if (pCurr->tInfo.bUsesUnicode) {
			fail(odd(pCurr->tInfo.lLength));
			lTotal += pCurr->tInfo.lLength / 2;
		} else {
			lTotal += pCurr->tInfo.lLength;
		}
	}
	DBG_DEC(lTotal);
	if (lTotal != lUnused2Len) {
		DBG_DEC(lUnused2Len);
		werr(1, "Software error (Unused2-list)");
	}
#endif /* DEBUG */

	if (!bMustExtend) {
		return;
	}
	/*
	 * All blocks (except the last one) must have a length that
	 * is a multiple of the Big Block Size
	 */

	apAnchors[0] = pTextAnchor;
	apAnchors[1] = pFootAnchor;
	apAnchors[2] = pUnused1Anchor;
	apAnchors[3] = pEndAnchor;
	apAnchors[4] = pUnused2Anchor;

	for (iIndex = 0; iIndex < 5; iIndex++) {
		for (pCurr = apAnchors[iIndex];
		     pCurr != NULL;
		     pCurr = pCurr->pNext) {
			if (pCurr->pNext != NULL &&
			    pCurr->tInfo.lLength % BIG_BLOCK_SIZE != 0) {
				DBG_HEX(pCurr->tInfo.lFileOffset);
				DBG_HEX(pCurr->tInfo.lTextOffset);
				DBG_DEC(pCurr->tInfo.lLength);
				pCurr->tInfo.lLength /= BIG_BLOCK_SIZE;
				pCurr->tInfo.lLength++;
				pCurr->tInfo.lLength *= BIG_BLOCK_SIZE;
				DBG_DEC(pCurr->tInfo.lLength);
			}
		}
	}
} /* end of vSplitBlockList */

#if defined(__riscos)
/*
 * lGetDocumentLength - get the combined character length of the three lists
 *
 * returns: The total number of characters
 */
long
lGetDocumentLength(void)
{
	list_mem_type	*apAnchors[3];
	list_mem_type	*pCurr;
	long		lTotal;
	int		iIndex;

	DBG_MSG("uiGetDocumentLength");

	apAnchors[0] = pTextAnchor;
	apAnchors[1] = pFootAnchor;
	apAnchors[2] = pEndAnchor;

	lTotal = 0;
	for (iIndex = 0; iIndex < 3; iIndex++) {
		for (pCurr = apAnchors[iIndex];
		     pCurr != NULL;
		     pCurr = pCurr->pNext) {
			fail(pCurr->tInfo.lLength <= 0);
			if (pCurr->tInfo.bUsesUnicode) {
				fail(odd(pCurr->tInfo.lLength));
				lTotal += pCurr->tInfo.lLength / 2;
			} else {
				lTotal += pCurr->tInfo.lLength;
			}
		}
	}
	DBG_DEC(lTotal);
	return lTotal;
} /* end of tGetDocumentLength */
#endif /* __riscos */

/*
 * usNextTextByte - get the next byte from the text block list
 */
static unsigned short
usNextTextByte(FILE *pFile,
	long *plFileOffset, long *plTextOffset, unsigned short *pusPropMod)
{
	static long	lBlockOffset = 0;
	static size_t	tByteNext = 0;
	long	lReadOff;
	size_t	tReadLen;

	if (pTextBlockCurrent == NULL ||
	    tByteNext >= sizeof(aucBlock) ||
	    lBlockOffset + (long)tByteNext >= pTextBlockCurrent->tInfo.lLength) {
		if (pTextBlockCurrent == NULL) {
			/* First block, first part */
			pTextBlockCurrent = pTextAnchor;
			lBlockOffset = 0;
		} else if (lBlockOffset + (long)sizeof(aucBlock) <
				pTextBlockCurrent->tInfo.lLength) {
			/* Same block, next part */
			lBlockOffset += (long)sizeof(aucBlock);
		} else {
			/* Next block, first part */
			pTextBlockCurrent = pTextBlockCurrent->pNext;
			lBlockOffset = 0;
		}
		if (pTextBlockCurrent == NULL) {
			/* Past the last part of the last block */
			return (unsigned short)EOF;
		}
		tReadLen = (size_t)
			(pTextBlockCurrent->tInfo.lLength - lBlockOffset);
		if (tReadLen > sizeof(aucBlock)) {
			tReadLen = sizeof(aucBlock);
		}
		lReadOff = pTextBlockCurrent->tInfo.lFileOffset +
				lBlockOffset;
		if (!bReadBytes(aucBlock, tReadLen, lReadOff, pFile)) {
			return (unsigned short)EOF;
		}
		tByteNext = 0;
	}
	if (plFileOffset != NULL) {
		*plFileOffset = pTextBlockCurrent->tInfo.lFileOffset +
			lBlockOffset + (long)tByteNext;
	}
	if (plTextOffset != NULL) {
		*plTextOffset = pTextBlockCurrent->tInfo.lTextOffset +
			lBlockOffset + (long)tByteNext;
	}
	if (pusPropMod != NULL) {
		*pusPropMod = pTextBlockCurrent->tInfo.usPropMod;
	}
	return aucBlock[tByteNext++];
} /* end of usNextTextByte */

/*
 * usNextFootByte - get the next byte from the footnote block list
 */
static unsigned short
usNextFootByte(FILE *pFile,
	long *plFileOffset, long *plTextOffset, unsigned short *pusPropMod)
{
	static long	lBlockOffset = 0;
	static size_t	tByteNext = 0;
	long	lReadOff;
	size_t	tReadLen;

	if (pFootBlockCurrent == NULL ||
	    tByteNext >= sizeof(aucBlock) ||
	    lBlockOffset + (long)tByteNext >= pFootBlockCurrent->tInfo.lLength) {
		if (pFootBlockCurrent == NULL) {
			/* First block, first part */
			pFootBlockCurrent = pFootAnchor;
			lBlockOffset = 0;
		} else if (lBlockOffset + (long)sizeof(aucBlock) <
				pFootBlockCurrent->tInfo.lLength) {
			/* Same block, next part */
			lBlockOffset += (long)sizeof(aucBlock);
		} else {
			/* Next block, first part */
			pFootBlockCurrent = pFootBlockCurrent->pNext;
			lBlockOffset = 0;
		}
		if (pFootBlockCurrent == NULL) {
			/* Past the last part of the last block */
			return (unsigned short)EOF;
		}
		tReadLen = (size_t)
			(pFootBlockCurrent->tInfo.lLength - lBlockOffset);
		if (tReadLen > sizeof(aucBlock)) {
			tReadLen = sizeof(aucBlock);
		}
		lReadOff = pFootBlockCurrent->tInfo.lFileOffset +
				lBlockOffset;
		if (!bReadBytes(aucBlock, tReadLen, lReadOff, pFile)) {
			return (unsigned short)EOF;
		}
		tByteNext = 0;
	}
	if (plFileOffset != NULL) {
		*plFileOffset = pFootBlockCurrent->tInfo.lFileOffset +
			lBlockOffset + (long)tByteNext;
	}
	if (plTextOffset != NULL) {
		*plTextOffset = pFootBlockCurrent->tInfo.lTextOffset +
			lBlockOffset + (long)tByteNext;
	}
	if (pusPropMod != NULL) {
		*pusPropMod = pFootBlockCurrent->tInfo.usPropMod;
	}
	return aucBlock[tByteNext++];
} /* end of usNextFootByte */

/*
 * usNextEndByte - get the next byte from the endnote block list
 */
static unsigned short
usNextEndByte(FILE *pFile,
	long *plFileOffset, long *plTextOffset, unsigned short *pusPropMod)
{
	static long	lBlockOffset = 0;
	static size_t	tByteNext = 0;
	long	lReadOff;
	size_t	tReadLen;

	if (pEndBlockCurrent == NULL ||
	    tByteNext >= sizeof(aucBlock) ||
	    lBlockOffset + (long)tByteNext >= pEndBlockCurrent->tInfo.lLength) {
		if (pEndBlockCurrent == NULL) {
			/* First block, first part */
			pEndBlockCurrent = pEndAnchor;
			lBlockOffset = 0;
		} else if (lBlockOffset + (long)sizeof(aucBlock) <
				pEndBlockCurrent->tInfo.lLength) {
			/* Same block, next part */
			lBlockOffset += sizeof(aucBlock);
		} else {
			/* Next block, first part */
			pEndBlockCurrent = pEndBlockCurrent->pNext;
			lBlockOffset = 0;
		}
		if (pEndBlockCurrent == NULL) {
			/* Past the last part of the last block */
			return (unsigned short)EOF;
		}
		tReadLen = (size_t)
			(pEndBlockCurrent->tInfo.lLength - lBlockOffset);
		if (tReadLen > sizeof(aucBlock)) {
			tReadLen = sizeof(aucBlock);
		}
		lReadOff = pEndBlockCurrent->tInfo.lFileOffset +
				lBlockOffset;
		if (!bReadBytes(aucBlock, tReadLen, lReadOff, pFile)) {
			return (unsigned short)EOF;
		}
		tByteNext = 0;
	}
	if (plFileOffset != NULL) {
		*plFileOffset = pEndBlockCurrent->tInfo.lFileOffset +
			lBlockOffset + (long)tByteNext;
	}
	if (plTextOffset != NULL) {
		*plTextOffset = pEndBlockCurrent->tInfo.lTextOffset +
			lBlockOffset + (long)tByteNext;
	}
	if (pusPropMod != NULL) {
		*pusPropMod = pEndBlockCurrent->tInfo.usPropMod;
	}
	return aucBlock[tByteNext++];
} /* end of usNextEndByte */

/*
 * usNextTextChar - get the next character from the text block list
 */
static unsigned short
usNextTextChar(FILE *pFile,
	long *plFileOffset, long *plTextOffset, unsigned short *pusPropMod)
{
	unsigned short	usLSB, usMSB;

	usLSB = usNextTextByte(pFile, plFileOffset, plTextOffset, pusPropMod);
	if (usLSB == (unsigned short)EOF) {
		return (unsigned short)EOF;
	}
	if (pTextBlockCurrent->tInfo.bUsesUnicode) {
		usMSB = usNextTextByte(pFile, NULL, NULL, NULL);
	} else {
		usMSB = 0x00;
	}
	if (usMSB == (unsigned short)EOF) {
		DBG_MSG("usNextTextChar: Unexpected EOF");
		DBG_HEX_C(plFileOffset != NULL, *plFileOffset);
		DBG_HEX_C(plTextOffset != NULL, *plTextOffset);
		return (unsigned short)EOF;
	}
	return (usMSB << 8) | usLSB;
} /* end of usNextTextChar */

/*
 * usNextFootChar - get the next character from the footnote block list
 */
static unsigned short
usNextFootChar(FILE *pFile,
	long *plFileOffset, long *plTextOffset, unsigned short *pusPropMod)
{
	unsigned short	usLSB, usMSB;

	usLSB = usNextFootByte(pFile, plFileOffset, plTextOffset, pusPropMod);
	if (usLSB == (unsigned short)EOF) {
		return (unsigned short)EOF;
	}
	if (pFootBlockCurrent->tInfo.bUsesUnicode) {
		usMSB = usNextFootByte(pFile, NULL, NULL, NULL);
	} else {
		usMSB = 0x00;
	}
	if (usMSB == (unsigned short)EOF) {
		DBG_MSG("usNextFootChar: Unexpected EOF");
		DBG_HEX_C(plFileOffset != NULL, *plFileOffset);
		DBG_HEX_C(plTextOffset != NULL, *plTextOffset);
		return (unsigned short)EOF;
	}
	return (usMSB << 8) | usLSB;
} /* end of usNextFootChar */

/*
 * usNextEndChar - get the next character from the endnote block list
 */
static unsigned short
usNextEndChar(FILE *pFile,
	long *plFileOffset, long *plTextOffset, unsigned short *pusPropMod)
{
	unsigned short	usLSB, usMSB;

	usLSB = usNextEndByte(pFile, plFileOffset, plTextOffset, pusPropMod);
	if (usLSB == (unsigned short)EOF) {
		return (unsigned short)EOF;
	}
	if (pEndBlockCurrent->tInfo.bUsesUnicode) {
		usMSB = usNextEndByte(pFile, NULL, NULL, NULL);
	} else {
		usMSB = 0x00;
	}
	if (usMSB == (unsigned short)EOF) {
		DBG_MSG("usNextEndChar: Unexpected EOF");
		DBG_HEX_C(plFileOffset != NULL, *plFileOffset);
		DBG_HEX_C(plTextOffset != NULL, *plTextOffset);
		return (unsigned short)EOF;
	}
	return (usMSB << 8) | usLSB;
} /* end of usNextEndChar */

/*
 * usNextChar - get the next character from the given block list
 */
unsigned short
usNextChar(FILE *pFile, list_id_enum eListID,
	long *plFileOffset, long *plTextOffset, unsigned short *pusPropMod)
{
	fail(pFile == NULL);

	switch (eListID) {
	case text_list:
		return usNextTextChar(pFile,
				plFileOffset, plTextOffset, pusPropMod);
	case footnote_list:
		return usNextFootChar(pFile,
				plFileOffset, plTextOffset, pusPropMod);
	case endnote_list:
		return usNextEndChar(pFile,
				plFileOffset, plTextOffset, pusPropMod);
	default:
		if (plFileOffset != NULL) {
			*plFileOffset = -1;
		}
		if (plTextOffset != NULL) {
			*plTextOffset = -1;
		}
		if (pusPropMod != NULL) {
			*pusPropMod = IGNORE_PROPMOD;
		}
		return (unsigned short)EOF;
	}
} /* end of usNextChar */

#if 0
/*
 * Translate the start from the begin of the text to the block that contains
 * this offset.
 * Logical offset to block pointer.
 *
 * Returns: block pointer or NULL
 */
static text_block_type *
pTextOffset2Block(long lTextOffset)
{
	list_mem_type	*apAnchors[3];
	list_mem_type	*pCurr;
	int		iIndex;

	apAnchors[0] = pTextAnchor;
	apAnchors[1] = pFootAnchor;
	apAnchors[2] = pEndAnchor;

	for (iIndex = 0; iIndex < 3; iIndex++) {
		for (pCurr = apAnchors[iIndex];
		     pCurr != NULL;
		     pCurr = pCurr->pNext) {
			if (lTextOffset >= pCurr->tInfo.lTextOffset &&
			    lTextOffset < pCurr->tInfo.lTextOffset +
			     pCurr->tInfo.lLength) {
				/* The textoffset is in the current block */
				return &pCurr->tInfo;
			}
		}
	}
	/* Passed beyond the end of the last list */
	return NULL;
} /* end of pTextOffset2Block */
#endif

/*
 * Translate the start from the begin of the text to an offset in the file.
 * Logical to physical offset.
 *
 * Returns:	 -1: in case of error
 *		>=0: the computed file offset
 */
long
lTextOffset2FileOffset(long lTextOffset)
{
	list_mem_type	*apAnchors[3];
	list_mem_type	*pCurr;
	long		lBestGuess;
	int		iIndex;

	apAnchors[0] = pTextAnchor;
	apAnchors[1] = pFootAnchor;
	apAnchors[2] = pEndAnchor;

	lBestGuess = -1;	/* Best guess is "fileoffset not found" */

	for (iIndex = 0; iIndex < 3; iIndex++) {
		for (pCurr = apAnchors[iIndex];
		     pCurr != NULL;
		     pCurr = pCurr->pNext) {
			if (lTextOffset == pCurr->tInfo.lTextOffset +
			     pCurr->tInfo.lLength &&
			    pCurr->pNext != NULL) {
				/*
				 * The textoffset is one beyond this block,
				 * so we guess it's the first byte of the next
				 * block (if there is a next block)
				 */
				lBestGuess = pCurr->pNext->tInfo.lFileOffset;
			}

			if (lTextOffset < pCurr->tInfo.lTextOffset ||
			    lTextOffset >= pCurr->tInfo.lTextOffset +
			     pCurr->tInfo.lLength) {
				/* The textoffset is not in this block */
				continue;
			}

			/* The textoffset is in the current block */
			return pCurr->tInfo.lFileOffset +
				lTextOffset - pCurr->tInfo.lTextOffset;
		}
	}
	/* Passed beyond the end of the last list */
	NO_DBG_HEX(lTextOffset);
	NO_DBG_HEX(lBestGuess);
	return lBestGuess;
} /* end of lTextOffset2FileOffset */

#if 0
/*
 * Get the properties modifier for the given logical offset
 *
 * Returns: the properties modifier or IGNORE_PROPMOD
 */
unsigned short
pTextOffset2PropMod(long lTextOffset)
{
	text_block_type	*pTextBlock;

	pTextBlock = pTextOffset2Block(lTextOffset);
	if (pTextBlock == NULL) {
		return IGNORE_PROPMOD;
	}
	return pTextBlock->usPropMod;
} /* end of pTextOffset2PropMod */
#endif
