Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

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