Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * wordole.c
 * Copyright (C) 1998-2004 A.J. van Os; Released under GPL
 *
 * Description:
 * Deal with the OLE internals of a MS Word file
 */

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

/* Private type for Property Set Storage entries */
typedef struct pps_entry_tag {
        ULONG   ulNext;
        ULONG   ulPrevious;
        ULONG   ulDir;
        ULONG   ulSB;
        ULONG   ulSize;
        int     iLevel;
        char    szName[32];
        UCHAR   ucType;
} pps_entry_type;

/* Show that a PPS number or index should not be used */
#define PPS_NUMBER_INVALID      0xffffffffUL


/* Macro to make sure all such statements will be identical */
#define FREE_ALL()              \
        do {\
                vDestroySmallBlockList();\
                aulRootList = xfree(aulRootList);\
                aulSbdList = xfree(aulSbdList);\
                aulBbdList = xfree(aulBbdList);\
                aulSBD = xfree(aulSBD);\
                aulBBD = xfree(aulBBD);\
        } while(0)


/*
 * ulReadLong - read four bytes from the given file and offset
 */
static ULONG
ulReadLong(FILE *pFile, ULONG ulOffset)
{
        UCHAR   aucBytes[4];

        fail(pFile == NULL);

        if (!bReadBytes(aucBytes, 4, ulOffset, pFile)) {
                werr(1, "Read long 0x%lx not possible", ulOffset);
        }
        return ulGetLong(0, aucBytes);
} /* end of ulReadLong */

/*
 * vName2String - turn the name into a proper string.
 */
static void
vName2String(char *szName, const UCHAR *aucBytes, size_t tNameSize)
{
        char    *pcChar;
        size_t  tIndex;

        fail(aucBytes == NULL || szName == NULL);

        if (tNameSize < 2) {
                szName[0] = '\0';
                return;
        }
        for (tIndex = 0, pcChar = szName;
             tIndex < 2 * tNameSize;
             tIndex += 2, pcChar++) {
                *pcChar = (char)aucBytes[tIndex];
        }
        szName[tNameSize - 1] = '\0';
} /* end of vName2String */

/*
 * tReadBlockIndices - read the Big/Small Block Depot indices
 *
 * Returns the number of indices read
 */
static size_t
tReadBlockIndices(FILE *pFile, ULONG *aulBlockDepot,
        size_t tMaxRec, ULONG ulOffset)
{
        size_t  tDone;
        int     iIndex;
        UCHAR   aucBytes[BIG_BLOCK_SIZE];

        fail(pFile == NULL || aulBlockDepot == NULL);
        fail(tMaxRec == 0);

        /* Read a big block with BBD or SBD indices */
        if (!bReadBytes(aucBytes, BIG_BLOCK_SIZE, ulOffset, pFile)) {
                werr(0, "Reading big block from 0x%lx is not possible",
                        ulOffset);
                return 0;
        }
        /* Split the big block into indices, an index is four bytes */
        tDone = min(tMaxRec, (size_t)BIG_BLOCK_SIZE / 4);
        for (iIndex = 0; iIndex < (int)tDone; iIndex++) {
                aulBlockDepot[iIndex] = ulGetLong(4 * iIndex, aucBytes);
                NO_DBG_DEC(aulBlockDepot[iIndex]);
        }
        return tDone;
} /* end of tReadBlockIndices */

/*
 * bGetBBD - get the Big Block Depot indices from the index-blocks
 */
static BOOL
bGetBBD(FILE *pFile, const ULONG *aulDepot, size_t tDepotLen,
        ULONG *aulBBD, size_t tBBDLen)
{
        ULONG   ulBegin;
        size_t  tToGo, tDone;
        int     iIndex;

        fail(pFile == NULL || aulDepot == NULL || aulBBD == NULL);

        DBG_MSG("bGetBBD");

        tToGo = tBBDLen;
        for (iIndex = 0; iIndex < (int)tDepotLen && tToGo != 0; iIndex++) {
                ulBegin = (aulDepot[iIndex] + 1) * BIG_BLOCK_SIZE;
                NO_DBG_HEX(ulBegin);
                tDone = tReadBlockIndices(pFile, aulBBD, tToGo, ulBegin);
                fail(tDone > tToGo);
                if (tDone == 0) {
                        return FALSE;
                }
                aulBBD += tDone;
                tToGo -= tDone;
        }
        return tToGo == 0;
} /* end of bGetBBD */

/*
 * bGetSBD - get the Small Block Depot indices from the index-blocks
 */
static BOOL
bGetSBD(FILE *pFile, const ULONG *aulDepot, size_t tDepotLen,
        ULONG *aulSBD, size_t tSBDLen)
{
        ULONG   ulBegin;
        size_t  tToGo, tDone;
        int     iIndex;

        fail(pFile == NULL || aulDepot == NULL || aulSBD == NULL);

        DBG_MSG("bGetSBD");

        tToGo = tSBDLen;
        for (iIndex = 0; iIndex < (int)tDepotLen && tToGo != 0; iIndex++) {
                fail(aulDepot[iIndex] >= ULONG_MAX / BIG_BLOCK_SIZE);
                ulBegin = (aulDepot[iIndex] + 1) * BIG_BLOCK_SIZE;
                NO_DBG_HEX(ulBegin);
                tDone = tReadBlockIndices(pFile, aulSBD, tToGo, ulBegin);
                fail(tDone > tToGo);
                if (tDone == 0) {
                        return FALSE;
                }
                aulSBD += tDone;
                tToGo -= tDone;
        }
        return tToGo == 0;
} /* end of bGetSBD */

/*
 * vComputePPSlevels - compute the levels of the Property Set Storage entries
 */
static void
vComputePPSlevels(pps_entry_type *atPPSlist, pps_entry_type *pNode,
                        int iLevel, int iRecursionLevel)
{
        fail(atPPSlist == NULL || pNode == NULL);
        fail(iLevel < 0 || iRecursionLevel < 0);

        if (iRecursionLevel > 25) {
                /* This removes the possibility of an infinite recursion */
                DBG_DEC(iRecursionLevel);
                return;
        }
        if (pNode->iLevel <= iLevel) {
                /* Avoid entering a loop */
                DBG_DEC(iLevel);
                DBG_DEC(pNode->iLevel);
                return;
        }

        pNode->iLevel = iLevel;

        if (pNode->ulDir != PPS_NUMBER_INVALID) {
                vComputePPSlevels(atPPSlist,
                                &atPPSlist[pNode->ulDir],
                                iLevel + 1,
                                iRecursionLevel + 1);
        }
        if (pNode->ulNext != PPS_NUMBER_INVALID) {
                vComputePPSlevels(atPPSlist,
                                &atPPSlist[pNode->ulNext],
                                iLevel,
                                iRecursionLevel + 1);
        }
        if (pNode->ulPrevious != PPS_NUMBER_INVALID) {
                vComputePPSlevels(atPPSlist,
                                &atPPSlist[pNode->ulPrevious],
                                iLevel,
                                iRecursionLevel + 1);
        }
} /* end of vComputePPSlevels */

/*
 * bGetPPS - search the Property Set Storage for three sets
 *
 * Return TRUE if the WordDocument PPS is found
 */
static BOOL
bGetPPS(FILE *pFile,
        const ULONG *aulRootList, size_t tRootListLen, pps_info_type *pPPS)
{
        pps_entry_type  *atPPSlist;
        ULONG   ulBegin, ulOffset, ulTmp;
        size_t  tNbrOfPPS, tNameSize;
        int     iIndex, iStartBlock, iRootIndex;
        BOOL    bWord, bExcel;
        UCHAR   aucBytes[PROPERTY_SET_STORAGE_SIZE];

        fail(pFile == NULL || aulRootList == NULL || pPPS == NULL);

        DBG_MSG("bGetPPS");

        NO_DBG_DEC(tRootListLen);

        bWord = FALSE;
        bExcel = FALSE;
        (void)memset(pPPS, 0, sizeof(*pPPS));

        /* Read and store all the Property Set Storage entries */

        tNbrOfPPS = tRootListLen * BIG_BLOCK_SIZE / PROPERTY_SET_STORAGE_SIZE;
        atPPSlist = xcalloc(tNbrOfPPS, sizeof(pps_entry_type));
        iRootIndex = 0;

        for (iIndex = 0; iIndex < (int)tNbrOfPPS; iIndex++) {
                ulTmp = (ULONG)iIndex * PROPERTY_SET_STORAGE_SIZE;
                iStartBlock = (int)(ulTmp / BIG_BLOCK_SIZE);
                ulOffset = ulTmp % BIG_BLOCK_SIZE;
                ulBegin = (aulRootList[iStartBlock] + 1) * BIG_BLOCK_SIZE +
                                ulOffset;
                NO_DBG_HEX(ulBegin);
                if (!bReadBytes(aucBytes, PROPERTY_SET_STORAGE_SIZE,
                                                        ulBegin, pFile)) {
                        werr(0, "Reading PPS %d is not possible", iIndex);
                        atPPSlist = xfree(atPPSlist);
                        return FALSE;
                }
                tNameSize = (size_t)usGetWord(0x40, aucBytes);
                tNameSize = (tNameSize + 1) / 2;
                vName2String(atPPSlist[iIndex].szName, aucBytes, tNameSize);
                atPPSlist[iIndex].ucType = ucGetByte(0x42, aucBytes);
                if (atPPSlist[iIndex].ucType == 5) {
                        iRootIndex = iIndex;
                }
                atPPSlist[iIndex].ulPrevious = ulGetLong(0x44, aucBytes);
                atPPSlist[iIndex].ulNext = ulGetLong(0x48, aucBytes);
                atPPSlist[iIndex].ulDir = ulGetLong(0x4c, aucBytes);
                atPPSlist[iIndex].ulSB = ulGetLong(0x74, aucBytes);
                atPPSlist[iIndex].ulSize = ulGetLong(0x78, aucBytes);
                atPPSlist[iIndex].iLevel = INT_MAX;
                if ((atPPSlist[iIndex].ulPrevious >= (ULONG)tNbrOfPPS &&
                     atPPSlist[iIndex].ulPrevious != PPS_NUMBER_INVALID) ||
                    (atPPSlist[iIndex].ulNext >= (ULONG)tNbrOfPPS &&
                     atPPSlist[iIndex].ulNext != PPS_NUMBER_INVALID) ||
                    (atPPSlist[iIndex].ulDir >= (ULONG)tNbrOfPPS &&
                     atPPSlist[iIndex].ulDir != PPS_NUMBER_INVALID)) {
                        DBG_DEC(iIndex);
                        DBG_DEC(atPPSlist[iIndex].ulPrevious);
                        DBG_DEC(atPPSlist[iIndex].ulNext);
                        DBG_DEC(atPPSlist[iIndex].ulDir);
                        DBG_DEC(tNbrOfPPS);
                        werr(0, "The Property Set Storage is damaged");
                        atPPSlist = xfree(atPPSlist);
                        return FALSE;
                }
        }

#if 0 /* defined(DEBUG) */
        DBG_MSG("Before");
        for (iIndex = 0; iIndex < (int)tNbrOfPPS; iIndex++) {
                DBG_MSG(atPPSlist[iIndex].szName);
                DBG_HEX(atPPSlist[iIndex].ulDir);
                DBG_HEX(atPPSlist[iIndex].ulPrevious);
                DBG_HEX(atPPSlist[iIndex].ulNext);
                DBG_DEC(atPPSlist[iIndex].ulSB);
                DBG_HEX(atPPSlist[iIndex].ulSize);
                DBG_DEC(atPPSlist[iIndex].iLevel);
        }
#endif /* DEBUG */

        /* Add level information to each entry */
        vComputePPSlevels(atPPSlist, &atPPSlist[iRootIndex], 0, 0);

        /* Check the entries on level 1 for the required information */
        NO_DBG_MSG("After");
        for (iIndex = 0; iIndex < (int)tNbrOfPPS; iIndex++) {
#if 0 /* defined(DEBUG) */
                DBG_MSG(atPPSlist[iIndex].szName);
                DBG_HEX(atPPSlist[iIndex].ulDir);
                DBG_HEX(atPPSlist[iIndex].ulPrevious);
                DBG_HEX(atPPSlist[iIndex].ulNext);
                DBG_DEC(atPPSlist[iIndex].ulSB);
                DBG_HEX(atPPSlist[iIndex].ulSize);
                DBG_DEC(atPPSlist[iIndex].iLevel);
#endif /* DEBUG */
                if (atPPSlist[iIndex].iLevel != 1 ||
                    atPPSlist[iIndex].ucType != 2 ||
                    atPPSlist[iIndex].szName[0] == '\0' ||
                    atPPSlist[iIndex].ulSize == 0) {
                        /* This entry can be ignored */
                        continue;
                }
                if (pPPS->tWordDocument.ulSize == 0 &&
                    STREQ(atPPSlist[iIndex].szName, "WordDocument")) {
                        pPPS->tWordDocument.ulSB = atPPSlist[iIndex].ulSB;
                        pPPS->tWordDocument.ulSize = atPPSlist[iIndex].ulSize;
                        bWord = TRUE;
                } else if (pPPS->tData.ulSize == 0 &&
                           STREQ(atPPSlist[iIndex].szName, "Data")) {
                        pPPS->tData.ulSB = atPPSlist[iIndex].ulSB;
                        pPPS->tData.ulSize = atPPSlist[iIndex].ulSize;
                } else if (pPPS->t0Table.ulSize == 0 &&
                           STREQ(atPPSlist[iIndex].szName, "0Table")) {
                        pPPS->t0Table.ulSB = atPPSlist[iIndex].ulSB;
                        pPPS->t0Table.ulSize = atPPSlist[iIndex].ulSize;
                } else if (pPPS->t1Table.ulSize == 0 &&
                           STREQ(atPPSlist[iIndex].szName, "1Table")) {
                        pPPS->t1Table.ulSB = atPPSlist[iIndex].ulSB;
                        pPPS->t1Table.ulSize = atPPSlist[iIndex].ulSize;
                } else if (pPPS->tSummaryInfo.ulSize == 0 &&
                           STREQ(atPPSlist[iIndex].szName,
                                                "\005SummaryInformation")) {
                        pPPS->tSummaryInfo.ulSB = atPPSlist[iIndex].ulSB;
                        pPPS->tSummaryInfo.ulSize = atPPSlist[iIndex].ulSize;
                } else if (pPPS->tDocSummaryInfo.ulSize == 0 &&
                           STREQ(atPPSlist[iIndex].szName,
                                        "\005DocumentSummaryInformation")) {
                        pPPS->tDocSummaryInfo.ulSB = atPPSlist[iIndex].ulSB;
                        pPPS->tDocSummaryInfo.ulSize = atPPSlist[iIndex].ulSize;
                } else if (STREQ(atPPSlist[iIndex].szName, "Book") ||
                           STREQ(atPPSlist[iIndex].szName, "Workbook")) {
                        bExcel = TRUE;
                }
        }

        /* Free the space for the Property Set Storage entries */
        atPPSlist = xfree(atPPSlist);

        /* Draw your conclusions */
        if (bWord) {
                return TRUE;
        }

        if (bExcel) {
                werr(0, "Sorry, but this is an Excel spreadsheet");
        } else {
                werr(0, "This OLE file does not contain a Word document");
        }
        return FALSE;
} /* end of bGetPPS */

/*
 * vGetBbdList - make a list of the places to find big blocks
 */
static void
vGetBbdList(FILE *pFile, int iNbr, ULONG *aulBbdList, ULONG ulOffset)
{
        int     iIndex;

        fail(pFile == NULL);
        fail(iNbr > 127);
        fail(aulBbdList == NULL);

        NO_DBG_DEC(iNbr);
        for (iIndex = 0; iIndex < iNbr; iIndex++) {
                aulBbdList[iIndex] =
                        ulReadLong(pFile, ulOffset + 4 * (ULONG)iIndex);
                NO_DBG_DEC(iIndex);
                NO_DBG_HEX(aulBbdList[iIndex]);
        }
} /* end of vGetBbdList */

/*
 * bGetDocumentText - make a list of the text blocks of a Word document
 *
 * Return TRUE when succesful, otherwise FALSE
 */
static BOOL
bGetDocumentText(FILE *pFile, const pps_info_type *pPPS,
        const ULONG *aulBBD, size_t tBBDLen,
        const ULONG *aulSBD, size_t tSBDLen,
        const UCHAR *aucHeader, int iWordVersion)
{
        ULONG   ulBeginOfText;
        ULONG   ulTextLen, ulFootnoteLen, ulEndnoteLen;
        ULONG   ulHdrFtrLen, ulMacroLen, ulAnnotationLen;
        ULONG   ulTextBoxLen, ulHdrTextBoxLen;
        UINT    uiQuickSaves;
        BOOL    bFarEastWord, bTemplate, bFastSaved, bEncrypted, bSuccess;
        USHORT  usIdent, usDocStatus;

        fail(pFile == NULL || pPPS == NULL);
        fail(aulBBD == NULL);
        fail(aulSBD == NULL);

        DBG_MSG("bGetDocumentText");

        /* Get the "magic number" from the header */
        usIdent = usGetWord(0x00, aucHeader);
        DBG_HEX(usIdent);
        bFarEastWord = usIdent == 0x8098 || usIdent == 0x8099 ||
                        usIdent == 0xa697 || usIdent == 0xa699;
        /* Get the status flags from the header */
        usDocStatus = usGetWord(0x0a, aucHeader);
        DBG_HEX(usDocStatus);
        bTemplate = (usDocStatus & BIT(0)) != 0;
        DBG_MSG_C(bTemplate, "This document is a Template");
        bFastSaved = (usDocStatus & BIT(2)) != 0;
        uiQuickSaves = (UINT)(usDocStatus & 0x00f0) >> 4;
        DBG_MSG_C(bFastSaved, "This document is Fast Saved");
        DBG_DEC_C(bFastSaved, uiQuickSaves);
        bEncrypted = (usDocStatus & BIT(8)) != 0;
        if (bEncrypted) {
                werr(0, "Encrypted documents are not supported");
                return FALSE;
        }

        /* Get length information */
        ulBeginOfText = ulGetLong(0x18, aucHeader);
        DBG_HEX(ulBeginOfText);
        switch (iWordVersion) {
        case 6:
        case 7:
                ulTextLen = ulGetLong(0x34, aucHeader);
                ulFootnoteLen = ulGetLong(0x38, aucHeader);
                ulHdrFtrLen = ulGetLong(0x3c, aucHeader);
                ulMacroLen = ulGetLong(0x40, aucHeader);
                ulAnnotationLen = ulGetLong(0x44, aucHeader);
                ulEndnoteLen = ulGetLong(0x48, aucHeader);
                ulTextBoxLen = ulGetLong(0x4c, aucHeader);
                ulHdrTextBoxLen = ulGetLong(0x50, aucHeader);
                break;
        case 8:
                ulTextLen = ulGetLong(0x4c, aucHeader);
                ulFootnoteLen = ulGetLong(0x50, aucHeader);
                ulHdrFtrLen = ulGetLong(0x54, aucHeader);
                ulMacroLen = ulGetLong(0x58, aucHeader);
                ulAnnotationLen = ulGetLong(0x5c, aucHeader);
                ulEndnoteLen = ulGetLong(0x60, aucHeader);
                ulTextBoxLen = ulGetLong(0x64, aucHeader);
                ulHdrTextBoxLen = ulGetLong(0x68, aucHeader);
                break;
        default:
                werr(0, "This version of Word is not supported");
                return FALSE;
        }
        DBG_DEC(ulTextLen);
        DBG_DEC(ulFootnoteLen);
        DBG_DEC(ulHdrFtrLen);
        DBG_DEC(ulMacroLen);
        DBG_DEC(ulAnnotationLen);
        DBG_DEC(ulEndnoteLen);
        DBG_DEC(ulTextBoxLen);
        DBG_DEC(ulHdrTextBoxLen);

        /* Make a list of the text blocks */
        switch (iWordVersion) {
        case 6:
        case 7:
                if (bFastSaved) {
                        bSuccess = bGet6DocumentText(pFile,
                                        bFarEastWord,
                                        pPPS->tWordDocument.ulSB,
                                        aulBBD, tBBDLen,
                                        aucHeader);
                } else {
                        bSuccess = bAddTextBlocks(ulBeginOfText,
                                ulTextLen +
                                ulFootnoteLen +
                                ulHdrFtrLen +
                                ulMacroLen + ulAnnotationLen +
                                ulEndnoteLen +
                                ulTextBoxLen + ulHdrTextBoxLen,
                                bFarEastWord,
                                IGNORE_PROPMOD,
                                pPPS->tWordDocument.ulSB,
                                aulBBD, tBBDLen);
                }
                break;
        case 8:
                bSuccess = bGet8DocumentText(pFile,
                                pPPS,
                                aulBBD, tBBDLen, aulSBD, tSBDLen,
                                aucHeader);
                break;
        default:
                werr(0, "This version of Word is not supported");
                bSuccess = FALSE;
                break;
        }

        if (bSuccess) {
                vSplitBlockList(pFile,
                                ulTextLen,
                                ulFootnoteLen,
                                ulHdrFtrLen,
                                ulMacroLen,
                                ulAnnotationLen,
                                ulEndnoteLen,
                                ulTextBoxLen,
                                ulHdrTextBoxLen,
                                !bFastSaved && iWordVersion == 8);
        } else {
                vDestroyTextBlockList();
                werr(0, "I can't find the text of this document");
        }
        return bSuccess;
} /* end of bGetDocumentText */

/*
 * vGetDocumentData - make a list of the data blocks of a Word document
 */
static void
vGetDocumentData(FILE *pFile, const pps_info_type *pPPS,
        const ULONG *aulBBD, size_t tBBDLen,
        const UCHAR *aucHeader, int iWordVersion)
{
        options_type    tOptions;
        ULONG   ulBeginOfText;
        BOOL    bFastSaved, bHasImages, bSuccess;
        USHORT  usDocStatus;

        fail(pFile == NULL);
        fail(pPPS == NULL);
        fail(aulBBD == NULL);

        /* Get the options */
        vGetOptions(&tOptions);

        /* Get the status flags from the header */
        usDocStatus = usGetWord(0x0a, aucHeader);
        DBG_HEX(usDocStatus);
        bFastSaved = (usDocStatus & BIT(2)) != 0;
        bHasImages = (usDocStatus & BIT(3)) != 0;

        if (!bHasImages ||
            tOptions.eConversionType == conversion_text ||
            tOptions.eConversionType == conversion_fmt_text ||
            tOptions.eConversionType == conversion_xml ||
            tOptions.eImageLevel == level_no_images) {
                /*
                 * No images in the document or text-only output or
                 * no images wanted, so no data blocks will be needed
                 */
                vDestroyDataBlockList();
                return;
        }

        /* Get length information */
        ulBeginOfText = ulGetLong(0x18, aucHeader);
        DBG_HEX(ulBeginOfText);

        /* Make a list of the data blocks */
        switch (iWordVersion) {
        case 6:
        case 7:
                /*
                 * The data blocks are in the text stream. The text stream
                 * is in "fast saved" format or "normal saved" format
                 */
                if (bFastSaved) {
                        bSuccess = bGet6DocumentData(pFile,
                                        pPPS->tWordDocument.ulSB,
                                        aulBBD, tBBDLen,
                                        aucHeader);
                } else {
                        bSuccess = bAddDataBlocks(ulBeginOfText,
                                        (ULONG)LONG_MAX,
                                        pPPS->tWordDocument.ulSB,
                                        aulBBD, tBBDLen);
                }
                break;
        case 8:
                /*
                 * The data blocks are in the data stream. The data stream
                 * is always in "normal saved" format
                 */
                bSuccess = bAddDataBlocks(0, (ULONG)LONG_MAX,
                                pPPS->tData.ulSB, aulBBD, tBBDLen);
                break;
        default:
                werr(0, "This version of Word is not supported");
                bSuccess = FALSE;
                break;
        }

        if (!bSuccess) {
                vDestroyDataBlockList();
                werr(0, "I can't find the data of this document");
        }
} /* end of vGetDocumentData */

/*
 * iInitDocumentOLE - initialize an OLE document
 *
 * Returns the version of Word that made the document or -1
 */
int
iInitDocumentOLE(FILE *pFile, long lFilesize)
{
        pps_info_type   PPS_info;
        ULONG   *aulBBD, *aulSBD;
        ULONG   *aulRootList, *aulBbdList, *aulSbdList;
        ULONG   ulBdbListStart, ulAdditionalBBDlist;
        ULONG   ulRootStartblock, ulSbdStartblock, ulSBLstartblock;
        ULONG   ulStart, ulTmp;
        long    lMaxBlock;
        size_t  tBBDLen, tSBDLen, tNumBbdBlocks, tRootListLen;
        int     iWordVersion, iIndex, iToGo;
        BOOL    bSuccess;
        USHORT  usIdent, usDocStatus;
        UCHAR   aucHeader[HEADER_SIZE];

        fail(pFile == NULL);

        lMaxBlock = lFilesize / BIG_BLOCK_SIZE - 2;
        DBG_DEC(lMaxBlock);
        if (lMaxBlock < 1) {
                return -1;
        }
        tBBDLen = (size_t)(lMaxBlock + 1);
        tNumBbdBlocks = (size_t)ulReadLong(pFile, 0x2c);
        DBG_DEC(tNumBbdBlocks);
        ulRootStartblock = ulReadLong(pFile, 0x30);
        DBG_DEC(ulRootStartblock);
        ulSbdStartblock = ulReadLong(pFile, 0x3c);
        DBG_DEC(ulSbdStartblock);
        ulAdditionalBBDlist = ulReadLong(pFile, 0x44);
        DBG_HEX(ulAdditionalBBDlist);
        ulSBLstartblock = ulReadLong(pFile,
                        (ulRootStartblock + 1) * BIG_BLOCK_SIZE + 0x74);
        DBG_DEC(ulSBLstartblock);
        tSBDLen = (size_t)(ulReadLong(pFile,
                        (ulRootStartblock + 1) * BIG_BLOCK_SIZE + 0x78) /
                        SMALL_BLOCK_SIZE);
        /* All to be xcalloc-ed pointers to NULL */
        aulRootList = NULL;
        aulSbdList = NULL;
        aulBbdList = NULL;
        aulSBD = NULL;
        aulBBD = NULL;
/* Big Block Depot */
        aulBbdList = xcalloc(tNumBbdBlocks, sizeof(ULONG));
        aulBBD = xcalloc(tBBDLen, sizeof(ULONG));
        iToGo = (int)tNumBbdBlocks;
        vGetBbdList(pFile, min(iToGo, 109),  aulBbdList, 0x4c);
        ulStart = 109;
        iToGo -= 109;
        while (ulAdditionalBBDlist != END_OF_CHAIN && iToGo > 0) {
                ulBdbListStart = (ulAdditionalBBDlist + 1) * BIG_BLOCK_SIZE;
                vGetBbdList(pFile, min(iToGo, 127),
                                        aulBbdList + ulStart, ulBdbListStart);
                ulAdditionalBBDlist = ulReadLong(pFile,
                                        ulBdbListStart + 4 * 127);
                DBG_DEC(ulAdditionalBBDlist);
                DBG_HEX(ulAdditionalBBDlist);
                ulStart += 127;
                iToGo -= 127;
        }
        if (!bGetBBD(pFile, aulBbdList, tNumBbdBlocks, aulBBD, tBBDLen)) {
                FREE_ALL();
                return -1;
        }
        aulBbdList = xfree(aulBbdList);
/* Small Block Depot */
        aulSbdList = xcalloc(tBBDLen, sizeof(ULONG));
        aulSBD = xcalloc(tSBDLen, sizeof(ULONG));
        for (iIndex = 0, ulTmp = ulSbdStartblock;
             iIndex < (int)tBBDLen && ulTmp != END_OF_CHAIN;
             iIndex++, ulTmp = aulBBD[ulTmp]) {
                if (ulTmp >= (ULONG)tBBDLen) {
                        DBG_DEC(ulTmp);
                        DBG_DEC(tBBDLen);
                        werr(1, "The Big Block Depot is damaged");
                }
                aulSbdList[iIndex] = ulTmp;
                NO_DBG_HEX(aulSbdList[iIndex]);
        }
        if (!bGetSBD(pFile, aulSbdList, tBBDLen, aulSBD, tSBDLen)) {
                FREE_ALL();
                return -1;
        }
        aulSbdList = xfree(aulSbdList);
/* Root list */
        for (tRootListLen = 0, ulTmp = ulRootStartblock;
             tRootListLen < tBBDLen && ulTmp != END_OF_CHAIN;
             tRootListLen++, ulTmp = aulBBD[ulTmp]) {
                if (ulTmp >= (ULONG)tBBDLen) {
                        DBG_DEC(ulTmp);
                        DBG_DEC(tBBDLen);
                        werr(1, "The Big Block Depot is damaged");
                }
        }
        if (tRootListLen == 0) {
                werr(0, "No Rootlist found");
                FREE_ALL();
                return -1;
        }
        aulRootList = xcalloc(tRootListLen, sizeof(ULONG));
        for (iIndex = 0, ulTmp = ulRootStartblock;
             iIndex < (int)tBBDLen && ulTmp != END_OF_CHAIN;
             iIndex++, ulTmp = aulBBD[ulTmp]) {
                if (ulTmp >= (ULONG)tBBDLen) {
                        DBG_DEC(ulTmp);
                        DBG_DEC(tBBDLen);
                        werr(1, "The Big Block Depot is damaged");
                }
                aulRootList[iIndex] = ulTmp;
                NO_DBG_DEC(aulRootList[iIndex]);
        }
        fail(tRootListLen != (size_t)iIndex);
        bSuccess = bGetPPS(pFile, aulRootList, tRootListLen, &PPS_info);
        aulRootList = xfree(aulRootList);
        if (!bSuccess) {
                FREE_ALL();
                return -1;
        }
/* Small block list */
        if (!bCreateSmallBlockList(ulSBLstartblock, aulBBD, tBBDLen)) {
                FREE_ALL();
                return -1;
        }

        if (PPS_info.tWordDocument.ulSize < MIN_SIZE_FOR_BBD_USE) {
                DBG_DEC(PPS_info.tWordDocument.ulSize);
                FREE_ALL();
                werr(0, "I'm afraid the text stream of this file "
                        "is too small to handle.");
                return -1;
        }
        /* Read the headerblock */
        if (!bReadBuffer(pFile, PPS_info.tWordDocument.ulSB,
                        aulBBD, tBBDLen, BIG_BLOCK_SIZE,
                        aucHeader, 0, HEADER_SIZE)) {
                FREE_ALL();
                return -1;
        }
        usIdent = usGetWord(0x00, aucHeader);
        DBG_HEX(usIdent);
        fail(usIdent != 0x8098 &&       /* Word 7 for oriental languages */
             usIdent != 0x8099 &&       /* Word 7 for oriental languages */
             usIdent != 0xa5dc &&       /* Word 6 & 7 */
             usIdent != 0xa5ec &&       /* Word 7 & 97 & 98 */
             usIdent != 0xa697 &&       /* Word 7 for oriental languages */
             usIdent != 0xa699);        /* Word 7 for oriental languages */
        iWordVersion = iGetVersionNumber(aucHeader);
        if (iWordVersion < 6) {
                FREE_ALL();
                werr(0, "This file is from a version of Word before Word 6.");
                return -1;
        }

        /* Get the status flags from the header */
        usDocStatus = usGetWord(0x0a, aucHeader);
        if (usDocStatus & BIT(9)) {
                PPS_info.tTable = PPS_info.t1Table;
        } else {
                PPS_info.tTable = PPS_info.t0Table;
        }
        /* Clean the entries that should not be used */
        memset(&PPS_info.t0Table, 0, sizeof(PPS_info.t0Table));
        memset(&PPS_info.t1Table, 0, sizeof(PPS_info.t1Table));

        bSuccess = bGetDocumentText(pFile, &PPS_info,
                        aulBBD, tBBDLen, aulSBD, tSBDLen,
                        aucHeader, iWordVersion);
        if (bSuccess) {
                vGetDocumentData(pFile, &PPS_info,
                        aulBBD, tBBDLen, aucHeader, iWordVersion);
                vGetPropertyInfo(pFile, &PPS_info,
                        aulBBD, tBBDLen, aulSBD, tSBDLen,
                        aucHeader, iWordVersion);
                vSetDefaultTabWidth(pFile, &PPS_info,
                        aulBBD, tBBDLen, aulSBD, tSBDLen,
                        aucHeader, iWordVersion);
                vGetNotesInfo(pFile, &PPS_info,
                        aulBBD, tBBDLen, aulSBD, tSBDLen,
                        aucHeader, iWordVersion);
        }
        FREE_ALL();
        return bSuccess ? iWordVersion : -1;
} /* end of iInitDocumentOLE */