/* * 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 */