mirror of
https://github.com/simon987/antiword.git
synced 2025-04-10 13:06:41 +00:00
331 lines
7.7 KiB
C
331 lines
7.7 KiB
C
/*
|
|
* listlist.c
|
|
* Copyright (C) 2002,2003 A.J. van Os; Released under GPL
|
|
*
|
|
* Description:
|
|
* Build, read and destroy a list of Word list information
|
|
*
|
|
* Note:
|
|
* This list only exists when the Word document is saved by Word 8 or later
|
|
*/
|
|
|
|
#include "antiword.h"
|
|
|
|
/*
|
|
* Private structure to hide the way the information
|
|
* is stored from the rest of the program
|
|
*/
|
|
typedef struct list_desc_tag {
|
|
list_block_type tInfo;
|
|
ULONG ulListID;
|
|
USHORT usIstd;
|
|
UCHAR ucListLevel;
|
|
struct list_desc_tag *pNext;
|
|
} list_desc_type;
|
|
|
|
typedef struct list_value_tag {
|
|
USHORT usValue;
|
|
USHORT usListIndex;
|
|
UCHAR ucListLevel;
|
|
struct list_value_tag *pNext;
|
|
} list_value_type;
|
|
|
|
/* Variables needed to describe the LFO list (pllfo) */
|
|
static ULONG *aulLfoList = NULL;
|
|
static USHORT usLfoLen = 0;
|
|
/* Variables needed to write the List Information List */
|
|
static list_desc_type *pAnchor = NULL;
|
|
static list_desc_type *pBlockLast = NULL;
|
|
/* Variable needed for numbering new lists */
|
|
static list_value_type *pValues = NULL;
|
|
/* Variables needed for numbering old lists */
|
|
static int iOldListSeqNumber = 0;
|
|
static USHORT usOldListValue = 0;
|
|
|
|
|
|
/*
|
|
* vDestroyListInfoList - destroy the List Information List
|
|
*/
|
|
void
|
|
vDestroyListInfoList(void)
|
|
{
|
|
list_desc_type *pCurr, *pNext;
|
|
list_value_type *pValueCurr, *pValueNext;
|
|
|
|
DBG_MSG("vDestroyListInfoList");
|
|
|
|
/* Free the LFO list */
|
|
usLfoLen = 0;
|
|
aulLfoList = xfree(aulLfoList);
|
|
|
|
/* Free the List Information List */
|
|
pCurr = pAnchor;
|
|
while (pCurr != NULL) {
|
|
pNext = pCurr->pNext;
|
|
pCurr = xfree(pCurr);
|
|
pCurr = pNext;
|
|
}
|
|
pAnchor = NULL;
|
|
/* Reset all control variables */
|
|
pBlockLast = NULL;
|
|
|
|
/* Free the values list */
|
|
pValueCurr = pValues;
|
|
while (pValueCurr != NULL) {
|
|
pValueNext = pValueCurr->pNext;
|
|
pValueCurr = xfree(pValueCurr);
|
|
pValueCurr = pValueNext;
|
|
}
|
|
pValues = NULL;
|
|
/* Reset the values for the old lists */
|
|
iOldListSeqNumber = 0;
|
|
usOldListValue = 0;
|
|
} /* end of vDestroyListInfoList */
|
|
|
|
/*
|
|
* vBuildLfoList - build the LFO list (pllfo)
|
|
*/
|
|
void
|
|
vBuildLfoList(const UCHAR *aucBuffer, size_t tBufLen)
|
|
{
|
|
size_t tRecords;
|
|
int iIndex;
|
|
|
|
fail(aucBuffer == NULL);
|
|
|
|
if (tBufLen < 4) {
|
|
return;
|
|
}
|
|
tRecords = (size_t)ulGetLong(0, aucBuffer);
|
|
NO_DBG_DEC(tRecords);
|
|
if (4 + 16 * tRecords > tBufLen || tRecords >= 0x7fff) {
|
|
/* Just a sanity check */
|
|
DBG_DEC(tRecords);
|
|
DBG_DEC(4 + 16 * tRecords);
|
|
DBG_DEC(tBufLen);
|
|
return;
|
|
}
|
|
aulLfoList = xcalloc(tRecords, sizeof(ULONG));
|
|
for (iIndex = 0; iIndex < (int)tRecords; iIndex++) {
|
|
aulLfoList[iIndex] = ulGetLong(4 + 16 * iIndex, aucBuffer);
|
|
NO_DBG_HEX(aulLfoList[iIndex]);
|
|
}
|
|
usLfoLen = (USHORT)tRecords;
|
|
} /* end of vBuildLfoList */
|
|
|
|
/*
|
|
* vAdd2ListInfoList - add an element to the List Information list
|
|
*/
|
|
void
|
|
vAdd2ListInfoList(ULONG ulListID, USHORT usIstd, UCHAR ucListLevel,
|
|
const list_block_type *pListBlock)
|
|
{
|
|
list_desc_type *pListMember;
|
|
|
|
fail(pListBlock == NULL);
|
|
|
|
NO_DBG_HEX(ulListID);
|
|
NO_DBG_DEC(usIstd);
|
|
NO_DBG_DEC(ucListLevel);
|
|
NO_DBG_DEC(pListBlock->ulStartAt);
|
|
NO_DBG_DEC(pListBlock->bNoRestart);
|
|
NO_DBG_DEC(pListBlock->sLeftIndent);
|
|
NO_DBG_HEX(pListBlock->ucNFC);
|
|
NO_DBG_HEX(pListBlock->usListChar);
|
|
|
|
/* Create list member */
|
|
pListMember = xmalloc(sizeof(list_desc_type));
|
|
/* Fill the list member */
|
|
pListMember->tInfo = *pListBlock;
|
|
pListMember->ulListID = ulListID;
|
|
pListMember->usIstd = usIstd;
|
|
pListMember->ucListLevel = ucListLevel;
|
|
pListMember->pNext = NULL;
|
|
/* Correct the values where needed */
|
|
if (pListMember->tInfo.ulStartAt > 0xffff) {
|
|
DBG_DEC(pListMember->tInfo.ulStartAt);
|
|
pListMember->tInfo.ulStartAt = 1;
|
|
}
|
|
/* Add the new member to the list */
|
|
if (pAnchor == NULL) {
|
|
pAnchor = pListMember;
|
|
} else {
|
|
fail(pBlockLast == NULL);
|
|
pBlockLast->pNext = pListMember;
|
|
}
|
|
pBlockLast = pListMember;
|
|
} /* end of vAdd2ListInfoList */
|
|
|
|
/*
|
|
* Get a matching record from the List Information List
|
|
*
|
|
* Returns NULL if no matching records is found
|
|
*/
|
|
const list_block_type *
|
|
pGetListInfo(USHORT usListIndex, UCHAR ucListLevel)
|
|
{
|
|
list_desc_type *pCurr;
|
|
list_block_type *pNearMatch;
|
|
ULONG ulListID;
|
|
|
|
if (usListIndex == 0) {
|
|
return NULL;
|
|
}
|
|
if (usListIndex - 1 >= usLfoLen || ucListLevel > 8) {
|
|
DBG_DEC(usListIndex);
|
|
DBG_DEC(ucListLevel);
|
|
return NULL;
|
|
}
|
|
fail(aulLfoList == NULL);
|
|
ulListID = aulLfoList[usListIndex - 1];
|
|
NO_DBG_HEX(ulListID);
|
|
|
|
pNearMatch = NULL;
|
|
for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
|
|
if (pCurr->ulListID != ulListID) {
|
|
/* No match */
|
|
continue;
|
|
}
|
|
if (pCurr->ucListLevel == ucListLevel) {
|
|
/* Exact match */
|
|
return &pCurr->tInfo;
|
|
}
|
|
if (pCurr->ucListLevel == 0) {
|
|
/* Near match */
|
|
pNearMatch = &pCurr->tInfo;
|
|
}
|
|
}
|
|
/* No exact match, use a near match if any */
|
|
return pNearMatch;
|
|
} /* end of pGetListInfo */
|
|
|
|
/*
|
|
* Get a matching record from the List Information List
|
|
*
|
|
* Returns NULL if no matching records is found
|
|
*/
|
|
const list_block_type *
|
|
pGetListInfoByIstd(USHORT usIstd)
|
|
{
|
|
list_desc_type *pCurr;
|
|
|
|
if (usIstd == ISTD_INVALID || usIstd == STI_NIL || usIstd == STI_USER) {
|
|
return NULL;
|
|
}
|
|
|
|
for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
|
|
if (pCurr->usIstd == usIstd) {
|
|
return &pCurr->tInfo;
|
|
}
|
|
}
|
|
return NULL;
|
|
} /* end of pGetListInfoByIstd */
|
|
|
|
/*
|
|
* vRestartListValues - reset the less significant list levels
|
|
*/
|
|
static void
|
|
vRestartListValues(USHORT usListIndex, UCHAR ucListLevel)
|
|
{
|
|
list_value_type *pPrev, *pCurr, *pNext;
|
|
int iCounter;
|
|
|
|
iCounter = 0;
|
|
pPrev = NULL;
|
|
pCurr = pValues;
|
|
|
|
while (pCurr != NULL) {
|
|
if (pCurr->usListIndex != usListIndex ||
|
|
pCurr->ucListLevel <= ucListLevel) {
|
|
pPrev = pCurr;
|
|
pCurr = pCurr->pNext;
|
|
continue;
|
|
}
|
|
/* Reset the level by deleting the record */
|
|
pNext = pCurr->pNext;
|
|
if (pPrev == NULL) {
|
|
pValues = pNext;
|
|
} else {
|
|
pPrev->pNext = pNext;
|
|
}
|
|
DBG_DEC(pCurr->usListIndex);
|
|
DBG_DEC(pCurr->ucListLevel);
|
|
pCurr = xfree(pCurr);
|
|
pCurr = pNext;
|
|
iCounter++;
|
|
}
|
|
DBG_DEC_C(iCounter > 0, iCounter);
|
|
} /* end of vRestartListValues */
|
|
|
|
/*
|
|
* usGetListValue - Get the current value of the given list
|
|
*
|
|
* Returns the value of the given list
|
|
*/
|
|
USHORT
|
|
usGetListValue(int iListSeqNumber, int iWordVersion,
|
|
const style_block_type *pStyle)
|
|
{
|
|
list_value_type *pCurr;
|
|
USHORT usValue;
|
|
|
|
fail(iListSeqNumber < 0);
|
|
fail(iListSeqNumber < iOldListSeqNumber);
|
|
fail(iWordVersion < 0);
|
|
fail(pStyle == NULL);
|
|
|
|
if (iListSeqNumber <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (iWordVersion < 8) {
|
|
/* Old style list */
|
|
if (iListSeqNumber == iOldListSeqNumber ||
|
|
(iListSeqNumber == iOldListSeqNumber + 1 &&
|
|
eGetNumType(pStyle->ucNumLevel) == level_type_sequence)) {
|
|
if (!pStyle->bNumPause) {
|
|
usOldListValue++;
|
|
}
|
|
} else {
|
|
usOldListValue = pStyle->usStartAt;
|
|
}
|
|
iOldListSeqNumber = iListSeqNumber;
|
|
return usOldListValue;
|
|
}
|
|
|
|
/* New style list */
|
|
if (pStyle->usListIndex == 0 ||
|
|
pStyle->usListIndex - 1 >= usLfoLen ||
|
|
pStyle->ucListLevel > 8) {
|
|
/* Out of range; no need to search */
|
|
return 0;
|
|
}
|
|
|
|
for (pCurr = pValues; pCurr != NULL; pCurr = pCurr->pNext) {
|
|
if (pCurr->usListIndex == pStyle->usListIndex &&
|
|
pCurr->ucListLevel == pStyle->ucListLevel) {
|
|
/* Record found; increment and return the value */
|
|
pCurr->usValue++;
|
|
usValue = pCurr->usValue;
|
|
if (!pStyle->bNoRestart) {
|
|
vRestartListValues(pStyle->usListIndex,
|
|
pStyle->ucListLevel);
|
|
}
|
|
return usValue;
|
|
}
|
|
}
|
|
|
|
/* Record not found; create it and add it to the front of the list */
|
|
pCurr = xmalloc(sizeof(list_value_type));
|
|
pCurr->usValue = pStyle->usStartAt;
|
|
pCurr->usListIndex = pStyle->usListIndex;
|
|
pCurr->ucListLevel = pStyle->ucListLevel;
|
|
pCurr->pNext = pValues;
|
|
pValues = pCurr;
|
|
usValue = pCurr->usValue;
|
|
if (!pStyle->bNoRestart) {
|
|
vRestartListValues(pStyle->usListIndex, pStyle->ucListLevel);
|
|
}
|
|
return usValue;
|
|
} /* end of usGetListValue */
|