Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * datalist.c
 * Copyright (C) 2000-2002 A.J. van Os; Released under GPL
 *
 * Description:
 * Build, read and destroy a list of Word data blocks
 */

#include <stdlib.h>
#include <errno.h>
#include "antiword.h"

#if defined(__riscos)
#define EIO             42
#endif /* __riscos */


/*
 * Private structure to hide the way the information
 * is stored from the rest of the program
 */
typedef struct data_mem_tag {
        data_block_type         tInfo;
        struct data_mem_tag     *pNext;
} data_mem_type;

/* Variable to describe the start of the data block list */
static data_mem_type    *pAnchor = NULL;
/* Variable needed to read the data block list */
static data_mem_type    *pBlockLast = NULL;
/* Variable needed to read the data block list */
static data_mem_type    *pBlockCurrent = NULL;
static ULONG    ulBlockOffset = 0;
static size_t   tByteNext = 0;
/* Last block read */
static UCHAR    aucBlock[BIG_BLOCK_SIZE];


/*
 * vDestroyDataBlockList - destroy the data block list
 */
void
vDestroyDataBlockList(void)
{
        data_mem_type   *pCurr, *pNext;

        DBG_MSG("vDestroyDataBlockList");

        pCurr = pAnchor;
        while (pCurr != NULL) {
                pNext = pCurr->pNext;
                pCurr = xfree(pCurr);
                pCurr = pNext;
        }
        pAnchor = NULL;
        /* Reset all the control variables */
        pBlockLast = NULL;
        pBlockCurrent = NULL;
        ulBlockOffset = 0;
        tByteNext = 0;
} /* end of vDestroyDataBlockList */

/*
 * bAdd2DataBlockList - add an element to the data block list
 *
 * Returns TRUE when successful, otherwise FALSE
 */
BOOL
bAdd2DataBlockList(const data_block_type *pDataBlock)
{
        data_mem_type   *pListMember;

        fail(pDataBlock == NULL);
        fail(pDataBlock->ulFileOffset == FC_INVALID);
        fail(pDataBlock->ulDataPos == CP_INVALID);
        fail(pDataBlock->ulLength == 0);

        NO_DBG_MSG("bAdd2DataBlockList");
        NO_DBG_HEX(pDataBlock->ulFileOffset);
        NO_DBG_HEX(pDataBlock->ulDataPos);
        NO_DBG_HEX(pDataBlock->ulLength);

        if (pDataBlock->ulFileOffset == FC_INVALID ||
            pDataBlock->ulDataPos == CP_INVALID ||
            pDataBlock->ulLength == 0) {
                werr(0, "Software (datablock) error");
                return FALSE;
        }
        /* Check for continuous blocks */
        if (pBlockLast != NULL &&
            pBlockLast->tInfo.ulFileOffset +
             pBlockLast->tInfo.ulLength == pDataBlock->ulFileOffset &&
            pBlockLast->tInfo.ulDataPos +
             pBlockLast->tInfo.ulLength == pDataBlock->ulDataPos) {
                /* These are continous blocks */
                pBlockLast->tInfo.ulLength += pDataBlock->ulLength;
                return TRUE;
        }
        /* Make a new block */
        pListMember = xmalloc(sizeof(data_mem_type));
        /* Add the block to the data list */
        pListMember->tInfo = *pDataBlock;
        pListMember->pNext = NULL;
        if (pAnchor == NULL) {
                pAnchor = pListMember;
        } else {
                fail(pBlockLast == NULL);
                pBlockLast->pNext = pListMember;
        }
        pBlockLast = pListMember;
        return TRUE;
} /* end of bAdd2DataBlockList */

/*
 * ulGetDataOffset - get the offset in the data block list
 *
 * Get the fileoffset the current position in the data block list
 */
ULONG
ulGetDataOffset(FILE *pFile)
{
        return pBlockCurrent->tInfo.ulFileOffset + ulBlockOffset + tByteNext;
} /* end of ulGetDataOffset */

/*
 * bSetDataOffset - set the offset in the data block list
 *
 * Make the given fileoffset the current position in the data block list
 */
BOOL
bSetDataOffset(FILE *pFile, ULONG ulFileOffset)
{
        data_mem_type   *pCurr;
        size_t  tReadLen;

        DBG_HEX(ulFileOffset);

        for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
                if (ulFileOffset < pCurr->tInfo.ulFileOffset ||
                    ulFileOffset >= pCurr->tInfo.ulFileOffset +
                     pCurr->tInfo.ulLength) {
                        /* The file offset is not in this block */
                        continue;
                }
                /* Compute the maximum number of bytes to read */
                tReadLen = (size_t)(pCurr->tInfo.ulFileOffset +
                                pCurr->tInfo.ulLength -
                                ulFileOffset);
                /* Compute the real number of bytes to read */
                if (tReadLen > sizeof(aucBlock)) {
                        tReadLen = sizeof(aucBlock);
                }
                /* Read the bytes */
                if (!bReadBytes(aucBlock, tReadLen, ulFileOffset, pFile)) {
                        return FALSE;
                }
                /* Set the control variables */
                pBlockCurrent = pCurr;
                ulBlockOffset = ulFileOffset - pCurr->tInfo.ulFileOffset;
                tByteNext = 0;
                return TRUE;
        }
        return FALSE;
} /* end of bSetDataOffset */

/*
 * iNextByte - get the next byte from the data block list
 */
int
iNextByte(FILE *pFile)
{
        ULONG   ulReadOff;
        size_t  tReadLen;

        fail(pBlockCurrent == NULL);

        if (tByteNext >= sizeof(aucBlock) ||
            ulBlockOffset + tByteNext >= pBlockCurrent->tInfo.ulLength) {
                if (ulBlockOffset + sizeof(aucBlock) <
                                        pBlockCurrent->tInfo.ulLength) {
                        /* Same block, next part */
                        ulBlockOffset += sizeof(aucBlock);
                } else {
                        /* Next block, first part */
                        pBlockCurrent = pBlockCurrent->pNext;
                        ulBlockOffset = 0;
                }
                if (pBlockCurrent == NULL) {
                        /* Past the last part of the last block */
                        errno = EIO;
                        return EOF;
                }
                tReadLen = (size_t)
                                (pBlockCurrent->tInfo.ulLength - ulBlockOffset);
                if (tReadLen > sizeof(aucBlock)) {
                        tReadLen = sizeof(aucBlock);
                }
                ulReadOff = pBlockCurrent->tInfo.ulFileOffset + ulBlockOffset;
                if (!bReadBytes(aucBlock, tReadLen, ulReadOff, pFile)) {
                        errno = EIO;
                        return EOF;
                }
                tByteNext = 0;
        }
        return (int)aucBlock[tByteNext++];
} /* end of iNextByte */

/*
 * usNextWord - get the next word from the data block list
 *
 * Read a two byte value in Little Endian order, that means MSB last
 *
 * All return values can be valid so errno is set in case of error
 */
USHORT
usNextWord(FILE *pFile)
{
        USHORT  usLSB, usMSB;

        usLSB = (USHORT)iNextByte(pFile);
        if (usLSB == (USHORT)EOF) {
                errno = EIO;
                return (USHORT)EOF;
        }
        usMSB = (USHORT)iNextByte(pFile);
        if (usMSB == (USHORT)EOF) {
                DBG_MSG("usNextWord: Unexpected EOF");
                errno = EIO;
                return (USHORT)EOF;
        }
        return (usMSB << 8) | usLSB;
} /* end of usNextWord */

/*
 * ulNextLong - get the next long from the data block list
 *
 * Read a four byte value in Little Endian order, that means MSW last
 *
 * All return values can be valid so errno is set in case of error
 */
ULONG
ulNextLong(FILE *pFile)
{
        ULONG   ulLSW, ulMSW;

        ulLSW = (ULONG)usNextWord(pFile);
        if (ulLSW == (ULONG)EOF) {
                errno = EIO;
                return (ULONG)EOF;
        }
        ulMSW = (ULONG)usNextWord(pFile);
        if (ulMSW == (ULONG)EOF) {
                DBG_MSG("ulNextLong: Unexpected EOF");
                errno = EIO;
                return (ULONG)EOF;
        }
        return (ulMSW << 16) | ulLSW;
} /* end of ulNextLong */

/*
 * usNextWordBE - get the next two byte value
 *
 * Read a two byte value in Big Endian order, that means MSB first
 *
 * All return values can be valid so errno is set in case of error
 */
USHORT
usNextWordBE(FILE *pFile)
{
        USHORT usLSB, usMSB;

        usMSB = (USHORT)iNextByte(pFile);
        if (usMSB == (USHORT)EOF) {
                errno = EIO;
                return (USHORT)EOF;
        }
        usLSB = (USHORT)iNextByte(pFile);
        if (usLSB == (USHORT)EOF) {
                DBG_MSG("usNextWordBE: Unexpected EOF");
                errno = EIO;
                return (USHORT)EOF;
        }
        return (usMSB << 8) | usLSB;
} /* end of usNextWordBE */

/*
 * ulNextLongBE - get the next four byte value
 *
 * Read a four byte value in Big Endian order, that means MSW first
 *
 * All return values can be valid so errno is set in case of error
 */
ULONG
ulNextLongBE(FILE *pFile)
{
        ULONG   ulLSW, ulMSW;

        ulMSW = (ULONG)usNextWordBE(pFile);
        if (ulMSW == (ULONG)EOF) {
                errno = EIO;
                return (ULONG)EOF;
        }
        ulLSW = (ULONG)usNextWordBE(pFile);
        if (ulLSW == (ULONG)EOF) {
                DBG_MSG("ulNextLongBE: Unexpected EOF");
                errno = EIO;
                return (ULONG)EOF;
        }
        return (ulMSW << 16) | ulLSW;
} /* end of ulNextLongBE */

/*
 * tSkipBytes - skip over the given number of bytes
 *
 * Returns the number of skipped bytes
 */
size_t
tSkipBytes(FILE *pFile, size_t tToSkip)
{
        size_t  tToGo, tMaxMove, tMove;

        fail(pFile == NULL);
        fail(pBlockCurrent == NULL);

        tToGo = tToSkip;
        while (tToGo != 0) {
                /* Goto the end of the current block */
                tMaxMove = min(sizeof(aucBlock) - tByteNext,
                                (size_t)(pBlockCurrent->tInfo.ulLength -
                                ulBlockOffset - tByteNext));
                tMove = min(tMaxMove, tToGo);
                tByteNext += tMove;
                tToGo -= tMove;
                if (tToGo != 0) {
                        /* Goto the next block */
                        if (iNextByte(pFile) == EOF) {
                                return tToSkip - tToGo;
                        }
                        tToGo--;
                }
        }
        return tToSkip;
} /* end of tSkipBytes */

/*
 * Translate  a data position to an offset in the file.
 * Logical to physical offset.
 *
 * Returns:     FC_INVALID: in case of error
 *              otherwise: the computed file offset
 */
ULONG
ulDataPos2FileOffset(ULONG ulDataPos)
{
        data_mem_type   *pCurr;

        fail(ulDataPos == CP_INVALID);

        for (pCurr = pAnchor; pCurr != NULL; pCurr = pCurr->pNext) {
                if (ulDataPos < pCurr->tInfo.ulDataPos ||
                    ulDataPos >= pCurr->tInfo.ulDataPos +
                     pCurr->tInfo.ulLength) {
                        /* The data offset is not in this block, try the next */
                        continue;
                }
                /* The data offset is in the current block */
                return pCurr->tInfo.ulFileOffset +
                                ulDataPos -
                                pCurr->tInfo.ulDataPos;
        }
        /* Passed beyond the end of the list */
        DBG_HEX_C(ulDataPos != 0, ulDataPos);
        return FC_INVALID;
} /* end of ulDataPos2FileOffset */