mirror of
https://github.com/simon987/antiword.git
synced 2025-04-10 13:06:41 +00:00
824 lines
22 KiB
C
824 lines
22 KiB
C
/*
|
|
* blocklist.c
|
|
* Copyright (C) 1998-2005 A.J. van Os; Released under GNU GPL
|
|
*
|
|
* Description:
|
|
* Build, read and destroy the lists 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;
|
|
|
|
typedef struct readinfo_tag {
|
|
list_mem_type *pBlockCurrent;
|
|
ULONG ulBlockOffset;
|
|
size_t tByteNext;
|
|
UCHAR aucBlock[BIG_BLOCK_SIZE];
|
|
} readinfo_type;
|
|
|
|
/* Variables to describe the start of the block lists */
|
|
static list_mem_type *pTextAnchor = NULL;
|
|
static list_mem_type *pFootnoteAnchor = NULL;
|
|
static list_mem_type *pHdrFtrAnchor = NULL;
|
|
static list_mem_type *pMacroAnchor = NULL;
|
|
static list_mem_type *pAnnotationAnchor = NULL;
|
|
static list_mem_type *pEndnoteAnchor = NULL;
|
|
static list_mem_type *pTextBoxAnchor = NULL;
|
|
static list_mem_type *pHdrTextBoxAnchor = NULL;
|
|
/* Variable needed to build the block list */
|
|
static list_mem_type *pBlockLast = NULL;
|
|
/* Variable needed to read the block lists */
|
|
static readinfo_type tOthers = { NULL, 0, 0, };
|
|
static readinfo_type tHdrFtr = { NULL, 0, 0, };
|
|
static readinfo_type tFootnote = { NULL, 0, 0, };
|
|
|
|
|
|
/*
|
|
* pFreeOneList - free a text block list
|
|
*
|
|
* Will always return NULL
|
|
*/
|
|
static list_mem_type *
|
|
pFreeOneList(list_mem_type *pAnchor)
|
|
{
|
|
list_mem_type *pCurr, *pNext;
|
|
|
|
pCurr = pAnchor;
|
|
while (pCurr != NULL) {
|
|
pNext = pCurr->pNext;
|
|
pCurr = xfree(pCurr);
|
|
pCurr = pNext;
|
|
}
|
|
return NULL;
|
|
} /* end of pFreeOneList */
|
|
|
|
/*
|
|
* vDestroyTextBlockList - destroy the text block lists
|
|
*/
|
|
void
|
|
vDestroyTextBlockList(void)
|
|
{
|
|
DBG_MSG("vDestroyTextBlockList");
|
|
|
|
/* Free the lists one by one */
|
|
pTextAnchor = pFreeOneList(pTextAnchor);
|
|
pFootnoteAnchor = pFreeOneList(pFootnoteAnchor);
|
|
pHdrFtrAnchor = pFreeOneList(pHdrFtrAnchor);
|
|
pMacroAnchor = pFreeOneList(pMacroAnchor);
|
|
pAnnotationAnchor = pFreeOneList(pAnnotationAnchor);
|
|
pEndnoteAnchor = pFreeOneList(pEndnoteAnchor);
|
|
pTextBoxAnchor = pFreeOneList(pTextBoxAnchor);
|
|
pHdrTextBoxAnchor = pFreeOneList(pHdrTextBoxAnchor);
|
|
/* Reset all the controle variables */
|
|
pBlockLast = NULL;
|
|
tOthers.pBlockCurrent = NULL;
|
|
tHdrFtr.pBlockCurrent = NULL;
|
|
tFootnote.pBlockCurrent = NULL;
|
|
} /* end of vDestroyTextBlockList */
|
|
|
|
/*
|
|
* bAdd2TextBlockList - add an element to the text block list
|
|
*
|
|
* returns: TRUE when successful, otherwise FALSE
|
|
*/
|
|
BOOL
|
|
bAdd2TextBlockList(const text_block_type *pTextBlock)
|
|
{
|
|
list_mem_type *pListMember;
|
|
|
|
fail(pTextBlock == NULL);
|
|
fail(pTextBlock->ulFileOffset == FC_INVALID);
|
|
fail(pTextBlock->ulCharPos == CP_INVALID);
|
|
fail(pTextBlock->ulLength == 0);
|
|
fail(pTextBlock->bUsesUnicode && odd(pTextBlock->ulLength));
|
|
|
|
NO_DBG_MSG("bAdd2TextBlockList");
|
|
NO_DBG_HEX(pTextBlock->ulFileOffset);
|
|
NO_DBG_HEX(pTextBlock->ulCharPos);
|
|
NO_DBG_HEX(pTextBlock->ulLength);
|
|
NO_DBG_DEC(pTextBlock->bUsesUnicode);
|
|
NO_DBG_DEC(pTextBlock->usPropMod);
|
|
|
|
if (pTextBlock->ulFileOffset == FC_INVALID ||
|
|
pTextBlock->ulCharPos == CP_INVALID ||
|
|
pTextBlock->ulLength == 0 ||
|
|
(pTextBlock->bUsesUnicode && odd(pTextBlock->ulLength))) {
|
|
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.ulFileOffset +
|
|
pBlockLast->tInfo.ulLength == pTextBlock->ulFileOffset &&
|
|
pBlockLast->tInfo.ulCharPos +
|
|
pBlockLast->tInfo.ulLength == pTextBlock->ulCharPos &&
|
|
pBlockLast->tInfo.bUsesUnicode == pTextBlock->bUsesUnicode &&
|
|
pBlockLast->tInfo.usPropMod == pTextBlock->usPropMod) {
|
|
/* These are continous blocks */
|
|
pBlockLast->tInfo.ulLength += pTextBlock->ulLength;
|
|
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 */
|
|
|
|
/*
|
|
* vSpitList - Split the list in two
|
|
*/
|
|
static void
|
|
vSpitList(list_mem_type **ppAnchorCurr, list_mem_type **ppAnchorNext,
|
|
ULONG ulListLen)
|
|
{
|
|
list_mem_type *pCurr;
|
|
long lCharsToGo, lBytesTooFar;
|
|
|
|
fail(ppAnchorCurr == NULL);
|
|
fail(ppAnchorNext == NULL);
|
|
fail(ulListLen > (ULONG)LONG_MAX);
|
|
|
|
pCurr = NULL;
|
|
lCharsToGo = (long)ulListLen;
|
|
lBytesTooFar = -1;
|
|
if (ulListLen != 0) {
|
|
DBG_DEC(ulListLen);
|
|
for (pCurr = *ppAnchorCurr;
|
|
pCurr != NULL;
|
|
pCurr = pCurr->pNext) {
|
|
NO_DBG_DEC(pCurr->tInfo.ulLength);
|
|
fail(pCurr->tInfo.ulLength == 0);
|
|
fail(pCurr->tInfo.ulLength > (ULONG)LONG_MAX);
|
|
if (pCurr->tInfo.bUsesUnicode) {
|
|
fail(odd(pCurr->tInfo.ulLength));
|
|
lCharsToGo -= (long)(pCurr->tInfo.ulLength / 2);
|
|
if (lCharsToGo < 0) {
|
|
lBytesTooFar = -2 * lCharsToGo;
|
|
}
|
|
} else {
|
|
lCharsToGo -= (long)pCurr->tInfo.ulLength;
|
|
if (lCharsToGo < 0) {
|
|
lBytesTooFar = -lCharsToGo;
|
|
}
|
|
}
|
|
if (lCharsToGo <= 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* Split the list */
|
|
if (ulListLen == 0) {
|
|
/* Current blocklist is empty */
|
|
*ppAnchorNext = *ppAnchorCurr;
|
|
*ppAnchorCurr = NULL;
|
|
} else if (pCurr == NULL) {
|
|
/* No blocks for the next list */
|
|
*ppAnchorNext = NULL;
|
|
} else if (lCharsToGo == 0) {
|
|
/* Move the integral number of blocks to the next list */
|
|
*ppAnchorNext = pCurr->pNext;
|
|
pCurr->pNext = NULL;
|
|
} else {
|
|
/* Split the part current block list, part next block list */
|
|
DBG_DEC(lBytesTooFar);
|
|
fail(lBytesTooFar <= 0);
|
|
*ppAnchorNext = xmalloc(sizeof(list_mem_type));
|
|
DBG_HEX(pCurr->tInfo.ulFileOffset);
|
|
(*ppAnchorNext)->tInfo.ulFileOffset =
|
|
pCurr->tInfo.ulFileOffset +
|
|
pCurr->tInfo.ulLength -
|
|
lBytesTooFar;
|
|
DBG_HEX((*ppAnchorNext)->tInfo.ulFileOffset);
|
|
DBG_HEX(pCurr->tInfo.ulCharPos);
|
|
(*ppAnchorNext)->tInfo.ulCharPos =
|
|
pCurr->tInfo.ulCharPos +
|
|
pCurr->tInfo.ulLength -
|
|
lBytesTooFar;
|
|
DBG_HEX((*ppAnchorNext)->tInfo.ulCharPos);
|
|
(*ppAnchorNext)->tInfo.ulLength = (ULONG)lBytesTooFar;
|
|
pCurr->tInfo.ulLength -= (ULONG)lBytesTooFar;
|
|
(*ppAnchorNext)->tInfo.bUsesUnicode = pCurr->tInfo.bUsesUnicode;
|
|
(*ppAnchorNext)->tInfo.usPropMod = pCurr->tInfo.usPropMod;
|
|
/* Move the integral number of blocks to the next list */
|
|
(*ppAnchorNext)->pNext = pCurr->pNext;
|
|
pCurr->pNext = NULL;
|
|
}
|
|
} /* end of vSpitList */
|
|
|
|
#if defined(DEBUG) || defined(__riscos)
|
|
/*
|
|
* ulComputeListLength - compute the length of a list
|
|
*
|
|
* returns the list length in characters
|
|
*/
|
|
static ULONG
|
|
ulComputeListLength(const list_mem_type *pAnchor)
|
|
{
|
|
const list_mem_type *pCurr;
|
|
ULONG ulTotal;
|
|
|
|
ulTotal = 0;
|
|
for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
|
|
fail(pCurr->tInfo.ulLength == 0);
|
|
if (pCurr->tInfo.bUsesUnicode) {
|
|
fail(odd(pCurr->tInfo.ulLength));
|
|
ulTotal += pCurr->tInfo.ulLength / 2;
|
|
} else {
|
|
ulTotal += pCurr->tInfo.ulLength;
|
|
}
|
|
}
|
|
return ulTotal;
|
|
} /* end of ulComputeListLength */
|
|
#endif /* DEBUG || __riscos */
|
|
|
|
#if defined(DEBUG)
|
|
/*
|
|
* vCheckList - check the number of bytes in a block list
|
|
*/
|
|
static void
|
|
vCheckList(const list_mem_type *pAnchor, ULONG ulListLen, char *szMsg)
|
|
{
|
|
ULONG ulTotal;
|
|
|
|
ulTotal = ulComputeListLength(pAnchor);
|
|
DBG_DEC(ulTotal);
|
|
if (ulTotal != ulListLen) {
|
|
DBG_DEC(ulListLen);
|
|
werr(1, szMsg);
|
|
}
|
|
} /* end of vCheckList */
|
|
#endif /* DEBUG */
|
|
|
|
/*
|
|
* bIsEmptyBox - check to see if the given text box is empty
|
|
*/
|
|
static BOOL
|
|
bIsEmptyBox(FILE *pFile, const list_mem_type *pAnchor)
|
|
{
|
|
const list_mem_type *pCurr;
|
|
size_t tIndex, tSize;
|
|
UCHAR *aucBuffer;
|
|
char cChar;
|
|
|
|
fail(pFile == NULL);
|
|
|
|
if (pAnchor == NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
aucBuffer = NULL;
|
|
for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
|
|
fail(pCurr->tInfo.ulLength == 0);
|
|
tSize = (size_t)pCurr->tInfo.ulLength;
|
|
#if defined(__dos) && !defined(__DJGPP__)
|
|
if (pCurr->tInfo.ulLength > 0xffffUL) {
|
|
tSize = 0xffff;
|
|
}
|
|
#endif /* __dos && !__DJGPP__ */
|
|
fail(aucBuffer != NULL);
|
|
aucBuffer = xmalloc(tSize);
|
|
if (!bReadBytes(aucBuffer, tSize,
|
|
pCurr->tInfo.ulFileOffset, pFile)) {
|
|
aucBuffer = xfree(aucBuffer);
|
|
return FALSE;
|
|
}
|
|
for (tIndex = 0; tIndex < tSize; tIndex++) {
|
|
cChar = (char)aucBuffer[tIndex];
|
|
switch (cChar) {
|
|
case '\0': case '\r': case '\n':
|
|
case '\f': case '\t': case '\v':
|
|
case ' ':
|
|
break;
|
|
default:
|
|
aucBuffer = xfree(aucBuffer);
|
|
return FALSE;
|
|
}
|
|
}
|
|
aucBuffer = xfree(aucBuffer);
|
|
}
|
|
fail(aucBuffer != NULL);
|
|
return TRUE;
|
|
} /* end of bIsEmptyBox */
|
|
|
|
/*
|
|
* vSplitBlockList - split the block list in the various parts
|
|
*
|
|
* Split the blocklist in a Text block list, a Footnote block list, a
|
|
* HeaderFooter block list, a Macro block list, an Annotation block list,
|
|
* an Endnote block list, a TextBox list and a HeaderTextBox list.
|
|
*
|
|
* NOTE:
|
|
* The various ul*Len input parameters are given in characters, but the
|
|
* length of the blocks are in bytes.
|
|
*/
|
|
void
|
|
vSplitBlockList(FILE *pFile, ULONG ulTextLen, ULONG ulFootnoteLen,
|
|
ULONG ulHdrFtrLen, ULONG ulMacroLen, ULONG ulAnnotationLen,
|
|
ULONG ulEndnoteLen, ULONG ulTextBoxLen, ULONG ulHdrTextBoxLen,
|
|
BOOL bMustExtend)
|
|
{
|
|
list_mem_type *apAnchors[8];
|
|
list_mem_type *pGarbageAnchor, *pCurr;
|
|
size_t tIndex;
|
|
|
|
DBG_MSG("vSplitBlockList");
|
|
|
|
pGarbageAnchor = NULL;
|
|
|
|
DBG_MSG_C(ulTextLen != 0, "Text block list");
|
|
vSpitList(&pTextAnchor, &pFootnoteAnchor, ulTextLen);
|
|
DBG_MSG_C(ulFootnoteLen != 0, "Footnote block list");
|
|
vSpitList(&pFootnoteAnchor, &pHdrFtrAnchor, ulFootnoteLen);
|
|
DBG_MSG_C(ulHdrFtrLen != 0, "Header/Footer block list");
|
|
vSpitList(&pHdrFtrAnchor, &pMacroAnchor, ulHdrFtrLen);
|
|
DBG_MSG_C(ulMacroLen != 0, "Macro block list");
|
|
vSpitList(&pMacroAnchor, &pAnnotationAnchor, ulMacroLen);
|
|
DBG_MSG_C(ulAnnotationLen != 0, "Annotation block list");
|
|
vSpitList(&pAnnotationAnchor, &pEndnoteAnchor, ulAnnotationLen);
|
|
DBG_MSG_C(ulEndnoteLen != 0, "Endnote block list");
|
|
vSpitList(&pEndnoteAnchor, &pTextBoxAnchor, ulEndnoteLen);
|
|
DBG_MSG_C(ulTextBoxLen != 0, "Textbox block list");
|
|
vSpitList(&pTextBoxAnchor, &pHdrTextBoxAnchor, ulTextBoxLen);
|
|
DBG_MSG_C(ulHdrTextBoxLen != 0, "HeaderTextbox block list");
|
|
vSpitList(&pHdrTextBoxAnchor, &pGarbageAnchor, ulHdrTextBoxLen);
|
|
|
|
/* Free the garbage block list, this should not be needed */
|
|
DBG_DEC_C(pGarbageAnchor != NULL, pGarbageAnchor->tInfo.ulLength);
|
|
pGarbageAnchor = pFreeOneList(pGarbageAnchor);
|
|
|
|
#if defined(DEBUG)
|
|
vCheckList(pTextAnchor, ulTextLen, "Software error (Text)");
|
|
vCheckList(pFootnoteAnchor, ulFootnoteLen, "Software error (Footnote)");
|
|
vCheckList(pHdrFtrAnchor, ulHdrFtrLen, "Software error (Hdr/Ftr)");
|
|
vCheckList(pMacroAnchor, ulMacroLen, "Software error (Macro)");
|
|
vCheckList(pAnnotationAnchor, ulAnnotationLen,
|
|
"Software error (Annotation)");
|
|
vCheckList(pEndnoteAnchor, ulEndnoteLen, "Software error (Endnote)");
|
|
vCheckList(pTextBoxAnchor, ulTextBoxLen, "Software error (TextBox)");
|
|
vCheckList(pHdrTextBoxAnchor, ulHdrTextBoxLen,
|
|
"Software error (HdrTextBox)");
|
|
#endif /* DEBUG */
|
|
|
|
/* Remove the list if the text box is empty */
|
|
if (bIsEmptyBox(pFile, pTextBoxAnchor)) {
|
|
pTextBoxAnchor = pFreeOneList(pTextBoxAnchor);
|
|
}
|
|
if (bIsEmptyBox(pFile, pHdrTextBoxAnchor)) {
|
|
pHdrTextBoxAnchor = pFreeOneList(pHdrTextBoxAnchor);
|
|
}
|
|
|
|
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] = pFootnoteAnchor;
|
|
apAnchors[2] = pHdrFtrAnchor;
|
|
apAnchors[3] = pMacroAnchor;
|
|
apAnchors[4] = pAnnotationAnchor;
|
|
apAnchors[5] = pEndnoteAnchor;
|
|
apAnchors[6] = pTextBoxAnchor;
|
|
apAnchors[7] = pHdrTextBoxAnchor;
|
|
|
|
for (tIndex = 0; tIndex < elementsof(apAnchors); tIndex++) {
|
|
for (pCurr = apAnchors[tIndex];
|
|
pCurr != NULL;
|
|
pCurr = pCurr->pNext) {
|
|
if (pCurr->pNext != NULL &&
|
|
pCurr->tInfo.ulLength % BIG_BLOCK_SIZE != 0) {
|
|
DBG_DEC(tIndex);
|
|
DBG_HEX(pCurr->tInfo.ulFileOffset);
|
|
DBG_HEX(pCurr->tInfo.ulCharPos);
|
|
DBG_DEC(pCurr->tInfo.ulLength);
|
|
pCurr->tInfo.ulLength /= BIG_BLOCK_SIZE;
|
|
pCurr->tInfo.ulLength++;
|
|
pCurr->tInfo.ulLength *= BIG_BLOCK_SIZE;
|
|
DBG_DEC(pCurr->tInfo.ulLength);
|
|
}
|
|
}
|
|
}
|
|
} /* end of vSplitBlockList */
|
|
|
|
#if defined(__riscos)
|
|
/*
|
|
* ulGetDocumentLength - get the total character length of the printable lists
|
|
*
|
|
* returns: The total number of characters
|
|
*/
|
|
ULONG
|
|
ulGetDocumentLength(void)
|
|
{
|
|
long ulTotal;
|
|
|
|
DBG_MSG("ulGetDocumentLength");
|
|
|
|
ulTotal = ulComputeListLength(pTextAnchor);
|
|
ulTotal += ulComputeListLength(pFootnoteAnchor);
|
|
ulTotal += ulComputeListLength(pEndnoteAnchor);
|
|
ulTotal += ulComputeListLength(pTextBoxAnchor);
|
|
ulTotal += ulComputeListLength(pHdrTextBoxAnchor);
|
|
DBG_DEC(ulTotal);
|
|
return ulTotal;
|
|
} /* end of ulGetDocumentLength */
|
|
#endif /* __riscos */
|
|
|
|
#if 0
|
|
/*
|
|
* bExistsHdrFtr - are there headers and/or footers?
|
|
*/
|
|
BOOL
|
|
bExistsHdrFtr(void)
|
|
{
|
|
return pHdrFtrAnchor != NULL &&
|
|
pHdrFtrAnchor->tInfo.ulLength != 0;
|
|
} /* end of bExistsHdrFtr */
|
|
#endif
|
|
|
|
/*
|
|
* bExistsTextBox - is there a text box?
|
|
*/
|
|
BOOL
|
|
bExistsTextBox(void)
|
|
{
|
|
return pTextBoxAnchor != NULL &&
|
|
pTextBoxAnchor->tInfo.ulLength != 0;
|
|
} /* end of bExistsTextBox */
|
|
|
|
/*
|
|
* bExistsHdrTextBox - is there a header text box?
|
|
*/
|
|
BOOL
|
|
bExistsHdrTextBox(void)
|
|
{
|
|
return pHdrTextBoxAnchor != NULL &&
|
|
pHdrTextBoxAnchor->tInfo.ulLength != 0;
|
|
} /* end of bExistsHdrTextBox */
|
|
|
|
/*
|
|
* usGetNextByte - get the next byte from the specified block list
|
|
*/
|
|
static USHORT
|
|
usGetNextByte(FILE *pFile, readinfo_type *pInfoCurrent, list_mem_type *pAnchor,
|
|
ULONG *pulFileOffset, ULONG *pulCharPos, USHORT *pusPropMod)
|
|
{
|
|
ULONG ulReadOff;
|
|
size_t tReadLen;
|
|
|
|
fail(pInfoCurrent == NULL);
|
|
|
|
if (pInfoCurrent->pBlockCurrent == NULL ||
|
|
pInfoCurrent->tByteNext >= sizeof(pInfoCurrent->aucBlock) ||
|
|
pInfoCurrent->ulBlockOffset + pInfoCurrent->tByteNext >=
|
|
pInfoCurrent->pBlockCurrent->tInfo.ulLength) {
|
|
if (pInfoCurrent->pBlockCurrent == NULL) {
|
|
/* First block, first part */
|
|
pInfoCurrent->pBlockCurrent = pAnchor;
|
|
pInfoCurrent->ulBlockOffset = 0;
|
|
} else if (pInfoCurrent->ulBlockOffset +
|
|
sizeof(pInfoCurrent->aucBlock) <
|
|
pInfoCurrent->pBlockCurrent->tInfo.ulLength) {
|
|
/* Same block, next part */
|
|
pInfoCurrent->ulBlockOffset +=
|
|
sizeof(pInfoCurrent->aucBlock);
|
|
} else {
|
|
/* Next block, first part */
|
|
pInfoCurrent->pBlockCurrent =
|
|
pInfoCurrent->pBlockCurrent->pNext;
|
|
pInfoCurrent->ulBlockOffset = 0;
|
|
}
|
|
if (pInfoCurrent->pBlockCurrent == NULL) {
|
|
/* Past the last part of the last block */
|
|
return (USHORT)EOF;
|
|
}
|
|
tReadLen = (size_t)
|
|
(pInfoCurrent->pBlockCurrent->tInfo.ulLength -
|
|
pInfoCurrent->ulBlockOffset);
|
|
if (tReadLen > sizeof(pInfoCurrent->aucBlock)) {
|
|
tReadLen = sizeof(pInfoCurrent->aucBlock);
|
|
}
|
|
ulReadOff = pInfoCurrent->pBlockCurrent->tInfo.ulFileOffset +
|
|
pInfoCurrent->ulBlockOffset;
|
|
if (!bReadBytes(pInfoCurrent->aucBlock,
|
|
tReadLen, ulReadOff, pFile)) {
|
|
/* Don't read from this list any longer */
|
|
pInfoCurrent->pBlockCurrent = NULL;
|
|
return (USHORT)EOF;
|
|
}
|
|
pInfoCurrent->tByteNext = 0;
|
|
}
|
|
if (pulFileOffset != NULL) {
|
|
*pulFileOffset =
|
|
pInfoCurrent->pBlockCurrent->tInfo.ulFileOffset +
|
|
pInfoCurrent->ulBlockOffset +
|
|
pInfoCurrent->tByteNext;
|
|
}
|
|
if (pulCharPos != NULL) {
|
|
*pulCharPos =
|
|
pInfoCurrent->pBlockCurrent->tInfo.ulCharPos +
|
|
pInfoCurrent->ulBlockOffset +
|
|
pInfoCurrent->tByteNext;
|
|
}
|
|
if (pusPropMod != NULL) {
|
|
*pusPropMod = pInfoCurrent->pBlockCurrent->tInfo.usPropMod;
|
|
}
|
|
return (USHORT)pInfoCurrent->aucBlock[pInfoCurrent->tByteNext++];
|
|
} /* end of usGetNextByte */
|
|
|
|
|
|
/*
|
|
* usGetNextChar - get the next character from the specified block list
|
|
*/
|
|
static USHORT
|
|
usGetNextChar(FILE *pFile, list_id_enum eListID,
|
|
ULONG *pulFileOffset, ULONG *pulCharPos, USHORT *pusPropMod)
|
|
{
|
|
readinfo_type *pReadinfo;
|
|
list_mem_type *pAnchor;
|
|
USHORT usLSB, usMSB;
|
|
|
|
switch (eListID) {
|
|
case text_list:
|
|
pReadinfo = &tOthers;
|
|
pAnchor = pTextAnchor;
|
|
break;
|
|
case footnote_list:
|
|
pReadinfo = &tFootnote;
|
|
pAnchor = pFootnoteAnchor;
|
|
break;
|
|
case hdrftr_list:
|
|
pReadinfo = &tHdrFtr;
|
|
pAnchor = pHdrFtrAnchor;
|
|
break;
|
|
case endnote_list:
|
|
pReadinfo = &tOthers;
|
|
pAnchor = pEndnoteAnchor;
|
|
break;
|
|
case textbox_list:
|
|
pReadinfo = &tOthers;
|
|
pAnchor = pTextBoxAnchor;
|
|
break;
|
|
case hdrtextbox_list:
|
|
pReadinfo = &tOthers;
|
|
pAnchor = pHdrTextBoxAnchor;
|
|
break;
|
|
default:
|
|
DBG_DEC(eListID);
|
|
return (USHORT)EOF;
|
|
}
|
|
|
|
usLSB = usGetNextByte(pFile, pReadinfo, pAnchor,
|
|
pulFileOffset, pulCharPos, pusPropMod);
|
|
if (usLSB == (USHORT)EOF) {
|
|
return (USHORT)EOF;
|
|
}
|
|
fail(pReadinfo->pBlockCurrent == NULL);
|
|
|
|
if (pReadinfo->pBlockCurrent->tInfo.bUsesUnicode) {
|
|
usMSB = usGetNextByte(pFile,
|
|
pReadinfo, pAnchor, NULL, NULL, NULL);
|
|
} else {
|
|
usMSB = 0x00;
|
|
}
|
|
if (usMSB == (USHORT)EOF) {
|
|
DBG_MSG("usGetNextChar: Unexpected EOF");
|
|
DBG_HEX_C(pulFileOffset != NULL, *pulFileOffset);
|
|
DBG_HEX_C(pulCharPos != NULL, *pulCharPos);
|
|
return (USHORT)EOF;
|
|
}
|
|
return (usMSB << 8) | usLSB;
|
|
} /* end of usGetNextChar */
|
|
|
|
/*
|
|
* usNextChar - get the next character from the given block list
|
|
*/
|
|
USHORT
|
|
usNextChar(FILE *pFile, list_id_enum eListID,
|
|
ULONG *pulFileOffset, ULONG *pulCharPos, USHORT *pusPropMod)
|
|
{
|
|
USHORT usRetVal;
|
|
|
|
fail(pFile == NULL);
|
|
|
|
usRetVal = usGetNextChar(pFile, eListID,
|
|
pulFileOffset, pulCharPos, pusPropMod);
|
|
if (usRetVal == (USHORT)EOF) {
|
|
if (pulFileOffset != NULL) {
|
|
*pulFileOffset = FC_INVALID;
|
|
}
|
|
if (pulCharPos != NULL) {
|
|
*pulCharPos = CP_INVALID;
|
|
}
|
|
if (pusPropMod != NULL) {
|
|
*pusPropMod = IGNORE_PROPMOD;
|
|
}
|
|
}
|
|
return usRetVal;
|
|
} /* end of usNextChar */
|
|
|
|
/*
|
|
* usToHdrFtrPosition - Go to a character position in header/foorter list
|
|
*
|
|
* Returns the character found on the specified character position
|
|
*/
|
|
USHORT
|
|
usToHdrFtrPosition(FILE *pFile, ULONG ulCharPos)
|
|
{
|
|
ULONG ulCharPosCurr;
|
|
USHORT usChar;
|
|
|
|
tHdrFtr.pBlockCurrent = NULL; /* To reset the header/footer list */
|
|
do {
|
|
usChar = usNextChar(pFile,
|
|
hdrftr_list, NULL, &ulCharPosCurr, NULL);
|
|
} while (usChar != (USHORT)EOF && ulCharPosCurr != ulCharPos);
|
|
return usChar;
|
|
} /* end of usToHdrFtrPosition */
|
|
|
|
/*
|
|
* usToFootnotePosition - Go to a character position in footnote list
|
|
*
|
|
* Returns the character found on the specified character position
|
|
*/
|
|
USHORT
|
|
usToFootnotePosition(FILE *pFile, ULONG ulCharPos)
|
|
{
|
|
ULONG ulCharPosCurr;
|
|
USHORT usChar;
|
|
|
|
tFootnote.pBlockCurrent = NULL; /* To reset the footnote list */
|
|
do {
|
|
usChar = usNextChar(pFile,
|
|
footnote_list, NULL, &ulCharPosCurr, NULL);
|
|
} while (usChar != (USHORT)EOF && ulCharPosCurr != ulCharPos);
|
|
return usChar;
|
|
} /* end of usToFootnotePosition */
|
|
|
|
/*
|
|
* Convert a character position to an offset in the file.
|
|
* Logical to physical offset.
|
|
*
|
|
* Returns: FC_INVALID: in case of error
|
|
* otherwise: the computed file offset
|
|
*/
|
|
ULONG
|
|
ulCharPos2FileOffsetX(ULONG ulCharPos, list_id_enum *peListID)
|
|
{
|
|
static list_id_enum eListIDs[8] = {
|
|
text_list, footnote_list, hdrftr_list,
|
|
macro_list, annotation_list, endnote_list,
|
|
textbox_list, hdrtextbox_list,
|
|
};
|
|
list_mem_type *apAnchors[8];
|
|
list_mem_type *pCurr;
|
|
list_id_enum eListGuess;
|
|
ULONG ulBestGuess;
|
|
size_t tIndex;
|
|
|
|
fail(peListID == NULL);
|
|
|
|
if (ulCharPos == CP_INVALID) {
|
|
*peListID = no_list;
|
|
return FC_INVALID;
|
|
}
|
|
|
|
apAnchors[0] = pTextAnchor;
|
|
apAnchors[1] = pFootnoteAnchor;
|
|
apAnchors[2] = pHdrFtrAnchor;
|
|
apAnchors[3] = pMacroAnchor;
|
|
apAnchors[4] = pAnnotationAnchor;
|
|
apAnchors[5] = pEndnoteAnchor;
|
|
apAnchors[6] = pTextBoxAnchor;
|
|
apAnchors[7] = pHdrTextBoxAnchor;
|
|
|
|
eListGuess = no_list; /* Best guess is no list */
|
|
ulBestGuess = FC_INVALID; /* Best guess is "file offset not found" */
|
|
|
|
for (tIndex = 0; tIndex < elementsof(apAnchors); tIndex++) {
|
|
for (pCurr = apAnchors[tIndex];
|
|
pCurr != NULL;
|
|
pCurr = pCurr->pNext) {
|
|
if (ulCharPos == pCurr->tInfo.ulCharPos +
|
|
pCurr->tInfo.ulLength &&
|
|
pCurr->pNext != NULL) {
|
|
/*
|
|
* The character position is one beyond this
|
|
* block, so we guess it's the first byte of
|
|
* the next block (if there is a next block)
|
|
*/
|
|
eListGuess= eListIDs[tIndex];
|
|
ulBestGuess = pCurr->pNext->tInfo.ulFileOffset;
|
|
}
|
|
|
|
if (ulCharPos < pCurr->tInfo.ulCharPos ||
|
|
ulCharPos >= pCurr->tInfo.ulCharPos +
|
|
pCurr->tInfo.ulLength) {
|
|
/* Character position is not in this block */
|
|
continue;
|
|
}
|
|
|
|
/* The character position is in the current block */
|
|
*peListID = eListIDs[tIndex];
|
|
return pCurr->tInfo.ulFileOffset +
|
|
ulCharPos - pCurr->tInfo.ulCharPos;
|
|
}
|
|
}
|
|
/* Passed beyond the end of the last list */
|
|
NO_DBG_HEX(ulCharPos);
|
|
NO_DBG_HEX(ulBestGuess);
|
|
*peListID = eListGuess;
|
|
return ulBestGuess;
|
|
} /* end of ulCharPos2FileOffsetX */
|
|
|
|
/*
|
|
* Convert a character position to an offset in the file.
|
|
* Logical to physical offset.
|
|
*
|
|
* Returns: FC_INVALID: in case of error
|
|
* otherwise: the computed file offset
|
|
*/
|
|
ULONG
|
|
ulCharPos2FileOffset(ULONG ulCharPos)
|
|
{
|
|
list_id_enum eListID;
|
|
|
|
return ulCharPos2FileOffsetX(ulCharPos, &eListID);
|
|
} /* end of ulCharPos2FileOffset */
|
|
|
|
/*
|
|
* Convert an offset in the header/footer list to a character position.
|
|
*
|
|
* Returns: CP_INVALID: in case of error
|
|
* otherwise: the computed character position
|
|
*/
|
|
ULONG
|
|
ulHdrFtrOffset2CharPos(ULONG ulHdrFtrOffset)
|
|
{
|
|
list_mem_type *pCurr;
|
|
ULONG ulOffset;
|
|
|
|
ulOffset = ulHdrFtrOffset;
|
|
for (pCurr = pHdrFtrAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
|
|
if (ulOffset >= pCurr->tInfo.ulLength) {
|
|
/* The offset is not in this block */
|
|
ulOffset -= pCurr->tInfo.ulLength;
|
|
continue;
|
|
}
|
|
return pCurr->tInfo.ulCharPos + ulOffset;
|
|
}
|
|
return CP_INVALID;
|
|
} /* end of ulHdrFtrOffset2CharPos */
|
|
|
|
/*
|
|
* Get the sequence number beloning to the given file offset
|
|
*
|
|
* Returns the sequence number
|
|
*/
|
|
ULONG
|
|
ulGetSeqNumber(ULONG ulFileOffset)
|
|
{
|
|
list_mem_type *pCurr;
|
|
ULONG ulSeq;
|
|
|
|
if (ulFileOffset == FC_INVALID) {
|
|
return FC_INVALID;
|
|
}
|
|
|
|
ulSeq = 0;
|
|
for (pCurr = pTextAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
|
|
if (ulFileOffset >= pCurr->tInfo.ulFileOffset &&
|
|
ulFileOffset < pCurr->tInfo.ulFileOffset +
|
|
pCurr->tInfo.ulLength) {
|
|
/* The file offset is within the current textblock */
|
|
return ulSeq + ulFileOffset - pCurr->tInfo.ulFileOffset;
|
|
}
|
|
ulSeq += pCurr->tInfo.ulLength;
|
|
}
|
|
return FC_INVALID;
|
|
} /* end of ulGetSeqNumber */
|