/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* $XConsortium: iltiff.c /main/6 1996/10/04 11:29:48 rcs $ */
/**---------------------------------------------------------------------
***	
***    (c)Copyright 1991 Hewlett-Packard Co.
***    
***                             RESTRICTED RIGHTS LEGEND
***    Use, duplication, or disclosure by the U.S. Government is subject to
***    restrictions as set forth in sub-paragraph (c)(1)(ii) of the Rights in
***    Technical Data and Computer Software clause in DFARS 252.227-7013.
***                             Hewlett-Packard Company
***                             3000 Hanover Street
***                             Palo Alto, CA 94304 U.S.A.
***    Rights for non-DOD U.S. Government Departments and Agencies are as set
***    forth in FAR 52.227-19(c)(1,2).
***
***-------------------------------------------------------------------*/

    /*  /ilc/iltiff.c : General TIFF file access functions, including
        those exposed in /ilc/iltiffint.h to other /ilc/iltiff*.c code.
    */

#include <stdlib.h>

#include "iltiffint.h"
#include "ilerrors.h"

#ifdef DTLIB
/* Used when this module is compiled by the DtHelp library build */
#include "GraphicsP.h"
#endif /* DTLIB */

    /*  Size in bytes of one item (# items = TIFF "length"), for each TIFF tag type.
        Also, # of entries of that type which fit in one long.
        Indexed by tag type = 1..IL_MAX_TAG_TYPE (0 entry unused).
    */
IL_PRIVATE int _ilTagTypeItemSizes [IL_MAX_TAG_TYPE+1] = {
    0,                          /* 0th entry unused; invalid tag type */
    1,                          /* IL_TAG_BYTE */
    1,                          /* IL_TAG_ASCII */
    2,                          /* IL_TAG_SHORT */
    4,                          /* IL_TAG_LONG */
    8 };                        /* IL_TAG_RATIONAL */

IL_PRIVATE int _ilTagTypeItemsThatFit [IL_MAX_TAG_TYPE+1] = {
    0,                          /* 0th entry unused; invalid tag type */
    4,                          /* IL_TAG_BYTE */
    4,                          /* IL_TAG_ASCII */
    2,                          /* IL_TAG_SHORT */
    1,                          /* IL_TAG_LONG */
    0 };                        /* IL_TAG_RATIONAL */

    /*  Structure of the first 8 bytes of a TIFF file: see ilConnectFile() */
typedef struct {
    ilByte              byteOrder[2];
    ilByte              version[2];
    ilByte              IFDHeadOffset[4];
    } ilTIFFHeader;


        /*  ------------------------- ilFreeFileList -------------------------------- */
        /*  Free the file list in the given file "*pFile".
            Note: ilFreeFileList() is also the Destroy() function for ilFile object.
        */

static void ilFreeFileList (
    ilFilePtr           pFile
    )
{
ilFileImagePtr pFileImage, pAltFileImage, pFileImageTemp;

        /*  Run thru the list of file images; for each: if a mask image destroy it
            (only one); if child images, destroy them - they are linked by pChild.
        */
    pFileImage = pFile->pFileImageHead;
    while (pFileImage) {

            /*  Destroy the mask image if present */
        pAltFileImage = (ilFileImagePtr)pFileImage->p.pMask;
        if (pAltFileImage)
            IL_FREE (pAltFileImage);

            /*  Destroy the child images if present.  They are linked
                by pNext, and each one can have a mask image.
            */
        pAltFileImage = (ilFileImagePtr)pFileImage->p.pChild;
        while (pAltFileImage) {
            if (pAltFileImage->p.pMask)
                IL_FREE (pAltFileImage->p.pMask);
            pFileImageTemp = pAltFileImage;
            pAltFileImage = (ilFileImagePtr)pAltFileImage->p.pNext;
            IL_FREE (pFileImageTemp);
            }

            /*  Destroy the main image */
        pFileImageTemp = pFileImage;
        pFileImage = (ilFileImagePtr)pFileImage->p.pNext;
        IL_FREE (pFileImageTemp);
        }

        /*  Make the list empty and declare it invalid.
        */
    pFile->pFileImageHead = pFile->pFileImageTail = (ilFileImagePtr)NULL;
    pFile->haveImageList = FALSE;
}


        /*  ----------------------- ilReadFileTags --------------------------------- */
        /*  Public function: see spec.
        */
ilPtr ilReadFileTags (
    ilFileImage         fileImage,
    int                 nTagsInit,
    unsigned short     *pTagNumbers,
    ilFileTag         **ppTags,
    unsigned long       mustBeZero
    )
{
#define RFT_FATAL_ERROR(_error) {error = (_error);  goto RFTFatalError; }
ilFileImagePtr          pFileImage;
ilTIFFTagPtr            pFileTag, pFileTags;
ilError                 error;
ilBool                  tagFound;
unsigned long           readSize, mallocSize;
CARD32                  offset;
ilPtr                   pTagData, pTagAlloc;
ilFilePtr               pFile;
ilFileTag              *pTag, **ppTag;
int                     tagNumber, nFileTags, nItems, nTags, tagType, i;
ilBool         bigEndian;

    pFileImage = (ilFileImagePtr)fileImage;
    if (mustBeZero != 0) {
        pFileImage->context->error = IL_ERROR_PAR_NOT_ZERO;
        return (ilPtr)NULL;                                     /* EXIT */
        }
    if (nTagsInit <= 0) {
        pFileImage->context->error = IL_OK;
        return (ilPtr)NULL;                                     /* no tags; EXIT */
        }

    pFile = (ilFilePtr)pFileImage->p.file;
    bigEndian = pFile->bigEndian;

        /*  Allocate space and read in all the file tags to "*pFileTags".
            Flip the bytes of number, type and length, but not the data/offset.
        */
    pTagAlloc = (ilPtr)NULL;                    /* in case of fatal error */
    readSize = pFileImage->tagCount * IL_TAG_SIZE;
    pFileTags = (ilTIFFTagPtr)IL_MALLOC (readSize);
    if (!pFileTags)
        RFT_FATAL_ERROR (IL_ERROR_MALLOC)
    if (!IL_SEEK (pFile, pFileImage->tagOffset)
     || !IL_READ (pFile, readSize, pFileTags))
        RFT_FATAL_ERROR (IL_ERROR_FILE_IO)

    pFileTag = pFileTags;
    pTagData = (ilPtr)pFileTag;
    nFileTags = pFileImage->tagCount;
    while (nFileTags-- > 0) {
        IL_FLIP_SHORT (bigEndian, pTagData, pFileTag->number)
        pTagData += 2;
        IL_FLIP_SHORT (bigEndian, pTagData, tagType)

            /*  NOTE: declare fatal error if tag type out of range, BUT TIFF 6.0 says 
                we should ignore these tags; ALSO, there are new tag types for 6.0.
            */
        if ((tagType == 0) || (tagType > IL_MAX_TAG_TYPE))
            RFT_FATAL_ERROR (IL_ERROR_FILE_TAG_TYPE)
        pFileTag->type = tagType;
        pTagData += 2;
        IL_FLIP_LONG (bigEndian, pTagData, pFileTag->length)
        pTagData += 8;                      /* skip length and data */
        pFileTag++;
        }

        /*  For each of caller's tags: find *pTagNumber in the pFileTags list.
            If not found, set corresponding "ppTags" entry to null, else point it
            at the found entry in pFileTags list, add its data size (rounded up to 
            multiple of 4 - long-align), and the size of one ilFileTag, to "mallocSize".
        */
    mallocSize = 0;
    ppTag = ppTags;
    nTags = nTagsInit;
    while (nTags-- > 0) {
        tagNumber = *pTagNumbers++;
        tagFound = FALSE;
        pFileTag = pFileTags;
        nFileTags = pFileImage->tagCount;
        while (nFileTags-- > 0) {
            if (pFileTag->number == tagNumber) {
                tagFound = TRUE;
                break;
                }
            else pFileTag++;
            }
        if (tagFound) {
            mallocSize += (sizeof (ilFileTag) +
                (pFileTag->length * _ilTagTypeItemSizes [pFileTag->type] + 3)) & ~3;
            *ppTag++ = (ilFileTag *)pFileTag;
            }
        else *ppTag++ = (ilFileTag *)NULL;
        }

        /*  If no tags found; exit, else allocate "mallocSize" bytes, and for each found
            tag, point corresponding "ppTags" to spot in pTagAlloc that has the ilFileTag
            data, followed by the data itself, then long aligned.
                What is returned will look like:
                    ppTags [0] == null if tag not present, else ->:  
                                    tag data:                               byte offset
                                      number (same value as corresponding       0
                                              entry in pTagsNumbers)
                                      type                                      2
                                      nItems                                    4
                                      pItems (points to <items>, i.e. one       8
                                        long past this spot, or null if 
                                        "nItems" is zero (0).
                                      <items> - "nItems" occurrences of data   12
                                        depending on "type".
                    ppTags [1]->      <next tag, at offset 12 + ppTag[0]->nItems
                                        * size in bytes of one ppTag[0] item,
                                      as determined by "type".
        */
    if (mallocSize == 0) {
        IL_FREE (pFileTags);
        pFileImage->context->error = IL_OK;
        return (ilPtr)NULL;                                     /* EXIT */
        }
    pTagAlloc = (ilPtr)malloc (mallocSize);     /* caller uses free() to free this */
    if (!pTagAlloc)
        RFT_FATAL_ERROR (IL_ERROR_MALLOC)

    pTag = (ilFileTag *)pTagAlloc;
    nTags = nTagsInit;
    ppTag = ppTags;
    while (nTags-- > 0) {
        if (!*ppTag)
            ppTag++;                            /* tag not found, already null, next */
        else {                                  /* tag found */
            pFileTag = (ilTIFFTagPtr)*ppTag;    /* points to internal tag data */
            *ppTag++ = pTag;                    /* point to converted data, next tag */
            pTag->number = pFileTag->number;    /* copy from pFileTags to pTagAlloc */
            tagType = pTag->type = pFileTag->type;
            nItems = pTag->nItems = pFileTag->length;
            pTagData = (ilPtr)pTag + sizeof(ilFileTag);

            if (nItems == 0) {
                pTag->pItems = (ilPtr)NULL;
                pTag = (ilFileTag *)pTagData;   /* point pTag to spot for next tag */
                }
            else {
                pTag->pItems = pTagData;        /* data follows the tag */
                pTag = (ilFileTag *)(pTagData + /* next spot = past data, long-aligned */
                    ((nItems * _ilTagTypeItemSizes [tagType] + 3) & ~3));

                    /*  Byte flip the tag data to the spot pointed to by "pTagData",
                        i.e. right after the 12 bytes for the tag.  In TIFF, the data
                        is in the "offset" field tag if it fits in 4 bytes, else offset
                        points to the data in the file - seek and read it in that case.
                    */
                switch (tagType) {
                  case IL_TAG_BYTE:
                  case IL_TAG_ASCII:
                    if (nItems <= 4)
                        bcopy (pFileTag->data.chars, (char *)pTagData, nItems);
                    else {
                        IL_FLIP_LONG (bigEndian, pFileTag->data.chars, offset)
                        if (!IL_SEEK (pFile, offset) 
                         || !IL_READ (pFile, nItems, pTagData))
                            RFT_FATAL_ERROR (IL_ERROR_FILE_IO)
                        }
                    break;

                  case IL_TAG_SHORT:
                    if (nItems <= 2) {
                        for (i = 0; nItems > 0; nItems--, i += 2, pTagData += 2)
                            IL_FLIP_SHORT (bigEndian, pFileTag->data.chars + i,
                                           *((unsigned short *)pTagData))
                        }
                    else {
                        IL_FLIP_LONG (bigEndian, pFileTag->data.chars, offset)
                        if (!IL_SEEK (pFile, offset)
                         || !IL_READ (pFile, nItems<<1, pTagData))
                            RFT_FATAL_ERROR (IL_ERROR_FILE_IO)
                        for (; nItems > 0; nItems--, pTagData += 2)
                            IL_FLIP_SHORT (bigEndian, pTagData, *((unsigned short *)pTagData))
                        }
                    break;

                  case IL_TAG_LONG:
                    if (nItems == 1)
                        IL_FLIP_LONG (bigEndian, pFileTag->data.chars, *((INT32 *)pTagData))
                    else {
                        IL_FLIP_LONG (bigEndian, pFileTag->data.chars, offset)
                        if (!IL_SEEK (pFile, offset)
                         || !IL_READ (pFile, nItems<<2, pTagData))
                            RFT_FATAL_ERROR (IL_ERROR_FILE_IO)
                        for (; nItems > 0; nItems--, pTagData += 4)
                            IL_FLIP_LONG (bigEndian, pTagData, *((INT32 *)pTagData))
                        }
                    break;

                  case IL_TAG_RATIONAL:
                    IL_FLIP_LONG (bigEndian, pFileTag->data.chars, offset)
                    if (!IL_SEEK (pFile, offset) || !IL_READ (pFile, nItems<<3, pTagData))
                        RFT_FATAL_ERROR (IL_ERROR_FILE_IO)
                    while (nItems-- > 0) {
                        IL_FLIP_LONG (bigEndian, pTagData, *((INT32 *)pTagData))
                        pTagData += 4;
                        IL_FLIP_LONG (bigEndian, pTagData, *((INT32 *)pTagData))
                        pTagData += 4;
                        }
                    break;
                    }
                }   /* END nItems > 0 */
            }       /* END tag found */
        }           /* END each caller's tag */

        /*  Tags read successfully: free pFileTags (local buffer); return pTagAlloc.
        */
    IL_FREE (pFileTags);
    pFileImage->context->error = IL_OK;
    return pTagAlloc;                                           /* EXIT */

        /*  Fatal error: free pFileTags, pTagAlloc if not null, return error code, null.
        */
RFTFatalError:
    if (pFileTags)
        IL_FREE (pFileTags);
    if (pTagAlloc)
        free (pTagAlloc);
    pFileImage->context->error = error;
    return (ilPtr)NULL;                                         /* EXIT */
}


        /*  --------------------- ilAddImageToList --------------------------------- */
        /*  Called when forming file image list.  "pPrevFileImage" is a pointer to the
            previous non-mask image in the file; null if pFileImage first non-mask read.
            pFileImage->imageType indicates whether image being added is 
            child/mask/mainImage; if child or mask, pPrevFileImage must be non-null.
        */
static void ilAddImageToList (
    ilFilePtr                   pFile,
    ilFileImagePtr     pFileImage,
    ilFileImagePtr              pPrevFileImage
    )
{
    pFileImage->p.pNext = pFileImage->p.pPrev = 
        pFileImage->p.pChild = pFileImage->p.pMask = (ilFileImage)NULL;

    switch (pFileImage->imageType) {
      case childImage:
        if (pPrevFileImage->imageType == childImage)
             pPrevFileImage->p.pNext = (ilFileImage)pFileImage;
        else pPrevFileImage->p.pChild = (ilFileImage)pFileImage;
        break;

      case maskImage:
        pPrevFileImage->p.pMask = (ilFileImage)pFileImage;
        break;

        /*  Main image: if page # present, add to list in page order, else add to end */
      case mainImage:
        if (pFileImage->p.nPages < 0) {     /* no page #; add to end of list */
            if ((pFileImage->p.pPrev = (ilFileImage)pFile->pFileImageTail))
                pFileImage->p.pPrev->pNext = (ilFileImage)pFileImage;
            else pFile->pFileImageHead = pFileImage;
            }
        else {
                /*  Have page #.  If list is empty or page # <= that of first element in
                    list, add pFileImage to front of list; otherwise find place in list
                    based on page # and insert image in that place.
                */
            pPrevFileImage = pFile->pFileImageHead;
            if (!pPrevFileImage || (pFileImage->p.page <= pPrevFileImage->p.page)) {
                pFileImage->p.pNext = (ilFileImage)pPrevFileImage;  /* add to front */
                pFileImage->p.pPrev = (ilFileImage)NULL;
                pFile->pFileImageHead = pFileImage;
                }
            else {      /* Add after pPrevFileImage w/ highest page # <= image's page # */
                while (pPrevFileImage->p.pNext
                 && (pPrevFileImage->p.pNext->page <= pFileImage->p.page)) {
                    pPrevFileImage = (ilFileImagePtr)pPrevFileImage->p.pNext;
                    }
                pFileImage->p.pNext = pPrevFileImage->p.pNext;
                pFileImage->p.pPrev = (ilFileImage)pPrevFileImage;
                pPrevFileImage->p.pNext = (ilFileImage)pFileImage;
                }
            }

            /*  Point next in list, or tail if this image is last, back to this image */
        if (pFileImage->p.pNext)
            pFileImage->p.pNext->pPrev = (ilFileImage)pFileImage;
        else pFile->pFileImageTail = pFileImage;
        break;
        }
}


        /*  --------------------- ilReadTagsAndAddImageToList ------------------------ */
        /*  Look at the tag values in the given file image "*pFileImage", and if the tags
            indicate a supported image, add it to the list of file images in "*pFile".
            If image is not supported (e.g unknown compression type) the image is freed.
                ppPrevFileImage must point to the ptr to the previous image read from the 
            file, or null if this is the first image; used for child and mask images.
                Return IL_OK if image valid, even if not supported; an error means
            the image is malformed (e.g. missing required tag).
        */
static ilError ilReadTagsAndAddImageToList (
    ilFilePtr                   pFile,
    ilFileImagePtr             *ppPrevFileImage,
    ilFileImagePtr     pFileImage
    )
{
    /*  Indexes into "tagNumbers" below. NOTE: order is important, as tags are checked
        in the below order, and some tags default to values which must be before them.
            Also NOTE: check for type other than short/long and # items done  below!
    */
#define NEW_SUBFILE_TYPE     0
#define IMAGE_WIDTH          1
#define IMAGE_LENGTH         2
#define RESOLUTION_UNIT      3
#define X_RESOLUTION         4          /* follow RESOLUTION_UNIT */
#define Y_RESOLUTION         5          /* follow RESOLUTION_UNIT */
#define ROWS_PER_STRIP       6          /* follow IMAGE_WIDTH */
#define SAMPLES_PER_PIXEL    7
#define PLANAR_CONFIGURATION 8          /* follow SAMPLES_PER_PIXEL */
#define BITS_PER_SAMPLE      9          /* follow SAMPLES_PER_PIXEL */
#define FILL_ORDER           10
#define GROUP_3_OPTIONS      11         /* follow FILL_ORDER */
#define GROUP_4_OPTIONS      12         /* follow FILL_ORDER */
#define COMPRESSION          13         /* follow GROUP_3/4_OPTIONS, FILL_ORDER */
#define PREDICTOR            14         /* follow COMPRESSION */
#define PHOTOMETRIC_INTERPRETATION 15   /* follow COMP, BITS_PS, SAMPLES_PP, NEW_SFT */
#define PAGE_NUMBER          16         /* follow NEW_SFT */
#define JPEG_PROC            17         /* follow COMPRESSION */
#define YCBCR_COEFFICIENTS   18         /* follow PHOTO_INT */
#define YCBCR_SUBSAMPLING    19         /* follow PHOTO_INT */
#define YCBCR_POSITIONING    20         /* follow PHOTO_INT */
#define REFERENCE_BLACK_WHITE 21        /* follow PHOTO_INT */
#define PRIVATE_0            22         /* follow PHOTO_INT */

static unsigned short tagNumbers [] = {
    IL_TAG_NEW_SUBFILE_TYPE,
    IL_TAG_IMAGE_WIDTH,
    IL_TAG_IMAGE_LENGTH,
    IL_TAG_RESOLUTION_UNIT,
    IL_TAG_X_RESOLUTION,
    IL_TAG_Y_RESOLUTION,
    IL_TAG_ROWS_PER_STRIP,
    IL_TAG_SAMPLES_PER_PIXEL,
    IL_TAG_PLANAR_CONFIGURATION,
    IL_TAG_BITS_PER_SAMPLE,
    IL_TAG_FILL_ORDER,
    IL_TAG_GROUP_3_OPTIONS,
    IL_TAG_GROUP_4_OPTIONS,
    IL_TAG_COMPRESSION,
    IL_TAG_PREDICTOR,
    IL_TAG_PHOTOMETRIC_INTERPRETATION,
    IL_TAG_PAGE_NUMBER,
    IL_TAG_JPEG_PROC,
    IL_TAG_YCBCR_COEFFICIENTS,
    IL_TAG_YCBCR_SUBSAMPLING,
    IL_TAG_YCBCR_POSITIONING,
    IL_TAG_REFERENCE_BLACK_WHITE,
    IL_TAG_PRIVATE_0 };

#define AI_NTAGS            (sizeof(tagNumbers) / sizeof (unsigned short))
#define AIL_FATAL_ERROR(_error) {error = (_error);  goto AILFatalError; }

ilFileImageRelation        imageType = mainImage;
ilFileTag                  *tag [AI_NTAGS];
ilBool                      present;
ilError                     error;
ilPtr                       pTagAlloc;
unsigned long      value;
int                         i, tagIndex, nBits, resolutionUnit, fillOrder = 0;
unsigned long               group3CompData;
unsigned long               group4CompData;
ilFileTag         *pTag;
ilYCbCrInfo                *pYCbCr = NULL;     /* null if not YCbCr ; else -> des...YCbCr */

        /*  Init/zero des and format in *pFileImage.
        */
    IL_INIT_IMAGE_DES (&pFileImage->p.des)
    IL_INIT_IMAGE_FORMAT (&pFileImage->p.format)

        /*  Read the above file tags; fatal error if error returned.
        */
    pTagAlloc = ilReadFileTags ((ilFileImage)pFileImage, AI_NTAGS, tagNumbers, tag, 0);
    if (!pTagAlloc)
        return (pFileImage->context->error) ? 
            pFileImage->context->error : IL_ERROR_FILE_MISSING_TAG;

        /*  Loop thru the tags; set "present" true if tag found, set "value" to integer
            value of tag (long or short allowed - exceptions checked explicitly - else
            fatal error).
                Fix bug #0169: ignore tags with # items <= 0.
        */
    for (tagIndex = 0; tagIndex < AI_NTAGS; tagIndex++) {
        if ((pTag = tag [tagIndex]) && (pTag->nItems > 0)) {
            if (pTag->type == IL_TAG_SHORT)
                value = *((unsigned short *)pTag->pItems);
            else if (pTag->type == IL_TAG_LONG)
/* compatibility problem with long and unsigned long data fields */
                value = *((CARD32 *)pTag->pItems);
            else if ((tagIndex != X_RESOLUTION) 
                  && (tagIndex != Y_RESOLUTION)
                  && (tagIndex != YCBCR_COEFFICIENTS)
                  && (tagIndex != REFERENCE_BLACK_WHITE))
                    AIL_FATAL_ERROR (IL_ERROR_FILE_MALFORMED_TAG);
            if (pTag->nItems != 1) {
                if ((tagIndex != BITS_PER_SAMPLE) 
                 && (tagIndex != PAGE_NUMBER)
                 && (tagIndex != YCBCR_COEFFICIENTS)
                 && (tagIndex != YCBCR_SUBSAMPLING)
                 && (tagIndex != REFERENCE_BLACK_WHITE)
                 && (tagIndex != PRIVATE_0))
                    AIL_FATAL_ERROR (IL_ERROR_FILE_MALFORMED_TAG);
                }
            present = TRUE;
            }
        else present = FALSE;

            /*  Parse the tags and fill in pFileImage->format, des.  Init des first.
                See page 8 in the TIFF v5.0 spec for info on each tag type.
            */
        switch (tagIndex) {
          case NEW_SUBFILE_TYPE:
            if (present && (value & 4)) imageType = maskImage;
            else if (present && (value & 1)) imageType = childImage;
            else imageType = mainImage;
            break;

          case IMAGE_WIDTH:
            if (!present) AIL_FATAL_ERROR (IL_ERROR_FILE_MISSING_TAG);
            pFileImage->p.width = value;
            break;

          case IMAGE_LENGTH:
            if (!present) AIL_FATAL_ERROR (IL_ERROR_FILE_MISSING_TAG);
            pFileImage->p.height = value;
            break;

                /*  Resolution returned as dpi * 2, or "0" if not present.  The TIFF
                    spec says should default to 300 dpi if not present, but we let
                    the caller make that assumption.
                */
          case RESOLUTION_UNIT:
            if (!present) 
                resolutionUnit = 2;             /* default to inches */
            else resolutionUnit = value;
            break;

          case X_RESOLUTION:
          case Y_RESOLUTION:
            { long      denom;
            if (!present)
                value = 0;
            else {
                if (pTag->type != IL_TAG_RATIONAL)
                    AIL_FATAL_ERROR (IL_ERROR_FILE_MALFORMED_TAG);
/* compatibility problem with long and unsigned long data fields */
                value = *((INT32 *)pTag->pItems);
                denom = *((INT32 *)pTag->pItems + 1);
                if (resolutionUnit == 3)            /* centimeters */
                    value = (denom == 0) ? 0 : (double)value / 2.54 / (double)denom + 0.5;
                else                                /* not centimeters: assume inches */
                    value = (denom == 0) ? 0 : (double)value / (double)denom + 0.5;
                }
            if (tagIndex == X_RESOLUTION)
                 pFileImage->p.xRes = value;
            else pFileImage->p.yRes = value;
            }
            break;

          case ROWS_PER_STRIP:
            if (!present || (((long)value) < 0) || (value > pFileImage->p.height))
                 pFileImage->p.stripHeight = pFileImage->p.height;
            else pFileImage->p.stripHeight = value;
            break;

          case SAMPLES_PER_PIXEL:
            if (!present) value = 1;
            if (value <= 0) 
                AIL_FATAL_ERROR (IL_ERROR_FILE_TAG_VALUE);
            if (value > IL_MAX_SAMPLES)
                goto UnsupportedImage;
            pFileImage->p.des.nSamplesPerPixel = value;
            break;

                /*  Planar configuration: default to pixels unless more than one
                    sample, and value is present and is 2.
                */
          case PLANAR_CONFIGURATION:
            pFileImage->p.format.sampleOrder = ((pFileImage->p.des.nSamplesPerPixel != 1) 
                && present && (value == 2)) ? IL_SAMPLE_PLANES : IL_SAMPLE_PIXELS;
            break;

                /*  bits per sample: default to 1, or to first item (in "value") if
                    fewer items than samples per pixel.
                */
          case BITS_PER_SAMPLE:
            for (i = 0; i < pFileImage->p.des.nSamplesPerPixel; i++)
                pFileImage->p.format.nBitsPerSample [i] = (!present || (pTag->nItems < 1)) ?
                    1 : ((i > pTag->nItems - 1) ? value : *((unsigned short *)pTag->pItems + i));
            break;

                /*  FillOrder: used for class F only (TIFF compression "3")
                */
          case FILL_ORDER:
            fillOrder = (present && (value == 2)) ? IL_LSB_FIRST : IL_MSB_FIRST;
            break;

                /*  Parse group 3 TIFF options, assumed to be 0 if not present.
                    As per TIFF spec, don't support if any unrecognized bits are not 0.
                    Note that "group3CompData" is set to 0 below if comp = 2 (TIFF G3).
                */
          case GROUP_3_OPTIONS:
            group3CompData = IL_G3M_EOL_MARKERS;    /* EOL present if not "TIFF" G3 */
            if (fillOrder == IL_LSB_FIRST)
                group3CompData |= IL_G3M_LSB_FIRST;
            if (present) {
                if (value & ~0x7)
                    goto UnsupportedImage;      /* non-zero unhandled bits; unsupported */
                if (value & 1)
                    group3CompData |= IL_G3M_2D;
                if (value & 2)
                    group3CompData |= IL_G3M_UNCOMPRESSED;
                if (!(value & 4))
                    group3CompData |= IL_G3M_EOL_UNALIGNED;
                }
            break;

                /*  Group 4: set LSB first if FillOrder tag specifies it.  If 
                    Group4Options tag defined and any bit other than zero set (bit 0
                    is undefined) than consider image unsupported.  Bit one (1) specifies
                    "uncompressed" mode which is not supported.
                */
          case GROUP_4_OPTIONS:
            group4CompData = 0;
            if (fillOrder == IL_LSB_FIRST)
                group4CompData |= IL_G4M_LSB_FIRST;
            if (present) {
                if (value & ~1)
                    goto UnsupportedImage;      /* non-zero unhandled bits; unsupported */
                }
            break;

          case COMPRESSION:
            if (!present) value = 1;
            switch (value) {
              case 1: pFileImage->p.des.compression = IL_UNCOMPRESSED; break;

                /*  Comp codes 2 and 3 are both Group 3 - the difference is only in
                    what *pCompData describes.  For code 2 ("TIFF" group 3), the comp
                    data is 0 - the bits were defined that way.  For code 3, use what
                    was deciphered/defaulted by the group 3 options tag.
                */
              case 2: 
                group3CompData = 0;     /* "TIFF" G3 - default options, set to 0 */
              case 3:                   /* "true" G3 */
                pFileImage->p.des.compression = IL_G3;
                pFileImage->p.des.compInfo.g3.flags = group3CompData;
                break;

              case 4: pFileImage->p.des.compression = IL_G4;         
                pFileImage->p.des.compInfo.g4.flags = group4CompData;
                break;
              case 5: pFileImage->p.des.compression = IL_LZW; break;
              case 6: 
                pFileImage->p.des.compression = IL_JPEG;
                pFileImage->p.des.compInfo.JPEG.reserved = IL_JPEGM_RAW;
                pFileImage->p.des.compInfo.JPEG.process = 0;
                break;
              case 32773: pFileImage->p.des.compression = IL_PACKBITS; break;
              default: goto UnsupportedImage;
                }
            break;

                /*  NOTE: predictive LZW not supported yet !!!!
                */
          case PREDICTOR:
            if (present && (value != 1) && (pFileImage->p.des.compression == IL_LZW))
                goto UnsupportedImage;
            break;

                /*  If no photometric interpration, default based on (assumed) image
                    type: 1 sample: 1 bit/pixel: bitonal, white=0 if fax comp, else 1;
                                  > 1 bit/pixel: gray, black=0.
                          3 samples: RGB; else error.
                */
          case PHOTOMETRIC_INTERPRETATION:
            pYCbCr = (ilYCbCrInfo *)NULL;       /* unless a YCbCr image */
            if (!present) {
                if (pFileImage->p.des.nSamplesPerPixel == 1) {
                    if (pFileImage->p.format.nBitsPerSample[0] == 1) {
                        pFileImage->p.des.type = IL_BITONAL;
                        switch (pFileImage->p.des.compression) {
                          case IL_G3: case IL_G4:
                            pFileImage->p.des.blackIsZero = FALSE;
                            break;
                          default:
                            pFileImage->p.des.blackIsZero = TRUE;
                            break;
                            }
                        }
                    else {
                        pFileImage->p.des.type = IL_GRAY;
                        pFileImage->p.des.blackIsZero = TRUE;
                        }
                    }
                else if (pFileImage->p.des.nSamplesPerPixel == 3)
                    pFileImage->p.des.type = IL_RGB;
                else AIL_FATAL_ERROR (IL_ERROR_FILE_MISSING_TAG);
                }
            else switch (value) {   /* photo interpretation present; handle it */

                    /*  Bitonal or gray; 0 = white is 0, else black is zero. */
              case 0:
              case 1: 
                if (pFileImage->p.des.nSamplesPerPixel != 1)
                    AIL_FATAL_ERROR (IL_ERROR_FILE_TAG_VALUE);
                pFileImage->p.des.blackIsZero = (value == 1);
                pFileImage->p.des.type = (pFileImage->p.format.nBitsPerSample[0] == 1) ?
                    IL_BITONAL : IL_GRAY;
                break;

                    /*  RGB. */
              case 2:
                if (pFileImage->p.des.nSamplesPerPixel != 3)
                    AIL_FATAL_ERROR (IL_ERROR_FILE_TAG_VALUE);
                pFileImage->p.des.type = IL_RGB;
                break;

                    /*  Palette images: max of 8 bits */
              case 3:
                if (pFileImage->p.des.nSamplesPerPixel != 1)
                    AIL_FATAL_ERROR (IL_ERROR_FILE_TAG_VALUE);
                if (pFileImage->p.format.nBitsPerSample[0] > 8)
                    goto UnsupportedImage;
                pFileImage->p.des.type = IL_PALETTE;
                break;

                    /*  Mask image: is bitonal */
              case 4:
                if (pFileImage->p.des.nSamplesPerPixel != 1)
                    AIL_FATAL_ERROR (IL_ERROR_FILE_TAG_VALUE);
                pFileImage->p.des.type = IL_BITONAL;
                pFileImage->p.des.blackIsZero = FALSE;
                imageType = maskImage;
                break;

                    /*  YCbCr: set pYCbCr to non-null */
              case 6:
                if (pFileImage->p.des.nSamplesPerPixel != 3)
                    AIL_FATAL_ERROR (IL_ERROR_FILE_TAG_VALUE);
                pFileImage->p.des.type = IL_YCBCR;
                pYCbCr = &pFileImage->p.des.typeInfo.YCbCr;     /* point to YCbCr data */
                break;

                    /*  Others not supported */
              default: goto UnsupportedImage;
                }
            break;

          case PAGE_NUMBER:
            if (present) {
                if ((pTag->nItems != 2) || (pTag->type != IL_TAG_SHORT))
                    AIL_FATAL_ERROR (IL_ERROR_FILE_MALFORMED_TAG);
                pFileImage->p.page = value;
                pFileImage->p.nPages = *((unsigned short *)pTag->pItems + 1);
                }
            else pFileImage->p.page = pFileImage->p.nPages = -1;
            break;

                /*  JPEG tags: ignore if not JPEG compression */
          case JPEG_PROC:
            if (pFileImage->p.des.compression == IL_JPEG) {
                if (!present) AIL_FATAL_ERROR (IL_ERROR_FILE_MISSING_TAG);
                if (value != 1)         /* only baseline sequential supported! */
                    goto UnsupportedImage;
                }
            break;

                /*  YCbCr tags: ignored if not YCbCr image */
          case YCBCR_COEFFICIENTS:
            if (pYCbCr) {
                if (!present) {
                    pYCbCr->lumaRed = 2990;
                    pYCbCr->lumaGreen = 5870;
                    pYCbCr->lumaBlue = 1140;
                    }
                else {
/* compatibility problem with long or unsigned long data fields */
                    INT32 *pLong = (INT32 *)pTag->pItems;
                    double          num, denom;
                    if ((pTag->nItems != 3) || (pTag->type != IL_TAG_RATIONAL))
                        AIL_FATAL_ERROR (IL_ERROR_FILE_MALFORMED_TAG);
                    num = *pLong++;  denom = *pLong++;
                    pYCbCr->lumaRed = num/denom * 10000;
                    num = *pLong++;  denom = *pLong++;
                    pYCbCr->lumaGreen = num/denom * 10000;
                    num = *pLong++;  denom = *pLong++;
                    pYCbCr->lumaBlue = num/denom * 10000;
                    }
                }
            break;

          case YCBCR_SUBSAMPLING:
            if (pYCbCr) {
                if (!present)
                    pYCbCr->sample[1].subsampleHoriz = pYCbCr->sample[1].subsampleVert = 2;
                else {
                    if ((pTag->nItems != 2) || (pTag->type != IL_TAG_SHORT))
                        AIL_FATAL_ERROR (IL_ERROR_FILE_MALFORMED_TAG);
                    pYCbCr->sample[1].subsampleHoriz = *((short *)pTag->pItems);
                    pYCbCr->sample[1].subsampleVert = *((short *)pTag->pItems + 1);
                    }
                        /* Set Y subsampling to (1,1); copy Cr to Cb: defined by TIFF */
                pYCbCr->sample[0].subsampleHoriz = pYCbCr->sample[0].subsampleVert = 1;
                pYCbCr->sample[2].subsampleHoriz = pYCbCr->sample[1].subsampleHoriz;
                pYCbCr->sample[2].subsampleVert = pYCbCr->sample[1].subsampleVert;
                }
            break;

                /*  Ignore positioning for now; set to zero (0) */
          case YCBCR_POSITIONING:
            if (pYCbCr)
                pYCbCr->positioning = 0;
            break;

            /*  ReferenceBlackWhite: if present, allow tag type to be "long" or "rational".
                "rational" is the correct tag type; "long" was written by Image 1 (the
                first Developer's Kit that supported JPEG), so support the old files.
            */
          case REFERENCE_BLACK_WHITE:
            if (pYCbCr) {
                ilYCbCrSampleInfo  *pSample = pYCbCr->sample;
                int                         i;
                if (!present) {
                    pSample->refBlack = 0; pSample->refWhite = 255; pSample++;
                    pSample->refBlack = 0; pSample->refWhite = 255; pSample++;
                    pSample->refBlack = 0; pSample->refWhite = 255; pSample++;
                    }
                else {
/* compatibility problem with long and unsigned long data fields */
                    INT32 *pLong = (INT32 *)pTag->pItems;
                    if (pTag->nItems != 6)
                        AIL_FATAL_ERROR (IL_ERROR_FILE_MALFORMED_TAG);
                    if (pTag->type == IL_TAG_LONG) {            /* old type */
                        for (i = 0; i < 3; i++, pSample++) {
                            pSample->refBlack = *pLong++; 
                            pSample->refWhite = *pLong++;
                            }
                        }
                    else if (pTag->type == IL_TAG_RATIONAL) {   /* correct TIFF 6.0 type */
                        long    num, denom;
                        for (i = 0; i < 3; i++, pSample++) {
                            num = *pLong++;
                            denom = *pLong++;
                            pSample->refBlack = (denom == 1) ? num :
                                (double)num / (double)denom + 0.5;
                            num = *pLong++;
                            denom = *pLong++;
                            pSample->refWhite = (denom == 1) ? num :
                                (double)num / (double)denom + 0.5;
                            }
                        }
                    else AIL_FATAL_ERROR (IL_ERROR_FILE_MALFORMED_TAG); /* bad tag type */
                    }
                }
            break;

                /*  HP IL private tag.  See /ilc/iltiffint.h for definition.
                    If "dithered palette" mask on, set levels and bit in flags in des.
                */
          case PRIVATE_0:
            if (present) {
                if (value & IL_TAG_P0_FLAG_DITHER_LEVELS) {
                    if (pFileImage->p.des.type == IL_PALETTE) {
                        short *pLevel = ((short *)pTag->pItems + 1);
                        if ((pTag->nItems < 4) || (pTag->type != IL_TAG_SHORT))
                            AIL_FATAL_ERROR (IL_ERROR_FILE_MALFORMED_TAG);
                        pFileImage->p.des.flags |= IL_DITHERED_PALETTE;
                        pFileImage->p.des.typeInfo.palette.levels[0] = *pLevel++;
                        pFileImage->p.des.typeInfo.palette.levels[1] = *pLevel++;
                        pFileImage->p.des.typeInfo.palette.levels[2] = *pLevel++;
                        }
                    }
                }
            break;

            }   /* END switch tag index */
        }       /* END each tag */

        /*  All the tags are parsed and valid so far.  Finish filling in *pFileImage;
            # levels is inferred from # bits; only supporting byte/sample for now.
        */
    for (i = 0; i < pFileImage->p.des.nSamplesPerPixel; i++) {
        nBits = pFileImage->p.format.nBitsPerSample [i];
        if (nBits <= 0)
            AIL_FATAL_ERROR (IL_ERROR_FILE_TAG_VALUE);      /* error; invalid # bits */
        if (nBits > 8)
            goto UnsupportedImage;                          /* not error, not supported */
        pFileImage->p.des.nLevelsPerSample [i] = 1 << nBits;
        }
    pFileImage->p.format.byteOrder = IL_MSB_FIRST;          /* arbitrary! */
    pFileImage->p.format.rowBitAlign = 8;                   /* by TIFF definition */

        /*  If mask image: must be bitonal or discard it. */
    if (imageType == maskImage) {
        ilFileImagePtr pPrevFileImage = *ppPrevFileImage;
        if (!pPrevFileImage                                     /* no image before it */
         || pPrevFileImage->p.pMask                             /* already has mask */
         || (pFileImage->p.des.nSamplesPerPixel != 1)
         || (pFileImage->p.format.nBitsPerSample[0] != 1))
            goto UnsupportedImage;                              /* reject image */
        }

        /*  If a child image and no previous image, make a main image and add it. */
    if ((imageType == childImage) && !*ppPrevFileImage)
        imageType = mainImage;

        /*  Add image to list, make previous image unless a mask image */
    pFileImage->imageType = imageType;                      /* child/mask/mainImage */
    ilAddImageToList (pFile, pFileImage, *ppPrevFileImage);
    if (imageType != maskImage)
        *ppPrevFileImage = pFileImage;

    free (pTagAlloc);                                       /* free() as client would */
    return IL_OK;                                           /* success; EXIT */

        /*  Error: free pTagAlloc, but not pFileImage - caller does that. Return error.
            Use free(), not IL_FREE(), to free pTagAlloc, just like caller should.
        */
AILFatalError:
    if (pTagAlloc)
        free (pTagAlloc);
    return error;

        /*  Image is not supported; not an error, but don't add to list - free image.
        */
UnsupportedImage:
    IL_FREE (pFileImage);
    return IL_OK;
}


        /*  ------------------------ ilListFileImages ------------------------------- */
        /*  Public function: see spec.
        */
ilFileImage ilListFileImages (
    ilFile              file,
    unsigned long       mustBeZero
    )
{
ilError                 error;
long                    imageSequenceNumber;
ilByte                  fourBytes [4];
ilFileOffset            IFDOffset;
ilFileImagePtr          pFileImage, pPrevFileImage;
ilTIFFHeader            header;
ilFilePtr      pFile;
ilBool         bigEndian;

        /*  Check if image list already built - if so, return ptr to it.
        */
    pFile = (ilFilePtr)file;
    if (mustBeZero != 0) {
        pFile->o.p.context->error = IL_ERROR_PAR_NOT_ZERO;
        return (ilFileImage)NULL;                               /* EXIT */
        }
    if (pFile->haveImageList) {
        pFile->o.p.context->error = IL_OK;
        return (ilFileImage)pFile->pFileImageHead;              /* EXIT */
        }

    ilFreeFileList (pFile);                 /* remove potential invalid image list */

        /*  Don't have image list - read file and build the list.  When done,
            pFile->IFDTailPtrOffset points to the "next IFD" ptr which is null, i.e.
            the next IFD ptr for the last image in the file.  For a file with no images
            that would be the ptr in the file header.
                First read header to get ptr to first image in file.
        */
#ifdef DTLIB
    /* Used when this module is compiled by the DtHelp library build */
    if (_DtGrSeek (pFile->stream, pFile->offset, 0)) {
#else
    if (fseek (pFile->stream, pFile->offset, 0)) {
#endif /* DTLIB */
        pFile->o.p.context->error = IL_ERROR_FILE_IO;
        return (ilFileImage)NULL;
        }
#ifdef DTLIB
    /* Used when this module is compiled by the DtHelp library build */
    if (!_DtGrRead ((char *)&header, sizeof (header), 1, pFile->stream)) {
#else
    if (!fread ((char *)&header, sizeof (header), 1, pFile->stream)) {
#endif /* DTLIB */
        pFile->o.p.context->error = IL_ERROR_FILE_IO;
        return (ilFileImage)NULL;
        }
    IL_FLIP_LONG (pFile->bigEndian, header.IFDHeadOffset, IFDOffset);

    pFile->haveImageList = TRUE;
    pFile->IFDTailPtrOffset = 4;            /* point to first IFD ptr in header */
    imageSequenceNumber = 0;
    bigEndian = pFile->bigEndian;
    pPrevFileImage = (ilFileImagePtr)NULL;

        /*  Overview: read the tag count, allocate room for tags (pBeginTagBuffer) and
            read the unflipped tags into it.  Allocate space for converted tags 
            (pFile->pTags) and copy/flip unflipped tags into it.
            After the tags comes the offset to next IFD; read and flip it.
        */
    while (IFDOffset) {
        imageSequenceNumber++;
        pFileImage = (ilFileImagePtr)IL_MALLOC_ZERO (sizeof (ilFileImageRec));
        if (!pFileImage) {
            error = IL_ERROR_MALLOC;
            goto LFIFatalError;
            }

        pFileImage->p.file = (ilFile)pFile;
        pFileImage->context = pFile->o.p.context;

            /*  Read and flip the tagCount, @IFDOffset.  Point tagOffset at next location.
            */
        if (!IL_SEEK (pFile, IFDOffset)
         || !IL_READ (pFile, 2, fourBytes)) {
            error = IL_ERROR_FILE_IO;
            goto LFIFatalError;
            }
        IL_FLIP_SHORT (bigEndian, fourBytes, pFileImage->tagCount);
        pFileImage->tagOffset = IFDOffset + 2;

            /*  Add file image to list, if supported.  An error is returned only if
                the file image is malformed; not if it is just unsupported.
            */
        if ((error = ilReadTagsAndAddImageToList (pFile, &pPrevFileImage, pFileImage)))
            goto LFIFatalError;             /* fatal error returned; EXIT */

            /*  Read and flip offset to next IFD, at end of tags; save in pFile. */
        IFDOffset += pFileImage->tagCount * IL_TAG_SIZE + 2;
        pFile->IFDTailPtrOffset = IFDOffset;
        if (!IL_SEEK (pFile, IFDOffset)
         || !IL_READ (pFile, 4, fourBytes)) {
            error = IL_ERROR_FILE_IO;
            goto LFIFatalError;
            }
        IL_FLIP_LONG (bigEndian, fourBytes, IFDOffset);
        }   /* END one IFD */

        /*  Images accessed successfully; return ok; */
    pFile->o.p.context->error = IL_OK;
    return (ilFileImage)pFile->pFileImageHead;                      /* EXIT */

        /*  Fatal error: dispose file list and pFileImage if non-null, return "error",
            and sequence # of image in file that provoked the error.
        */
LFIFatalError:
    if (pFileImage)
        IL_FREE (pFileImage);
    ilFreeFileList (pFile);
    pFile->o.p.context->error = error;
    pFile->o.p.context->errorInfo = imageSequenceNumber;
    return (ilFileImage)NULL;
}


        /*  ------------------------ ilConnectFile -------------------------------- */
        /*  Public function: see spec.
            Locate a TIFF file at "offset" within "file", and return a handle used
            to access this file, or return null and an error code.
        */
ilFile ilConnectFile (
    ilContext           context,
#ifdef DTLIB
    /* Used when this module is compiled by the DtHelp library build */
    _DtGrStream         *stream,
#else
    FILE                *stream,
#endif /* DTLIB */
    long                offset,
    unsigned long       mustBeZero
    )
{
ilBool                  bigEndian;
ilFilePtr               pFile;
ilTIFFHeader            header;

    if (mustBeZero != 0) {
        context->error = IL_ERROR_PAR_NOT_ZERO;
        return (ilPipe)NULL;
        }

        /*  Read what should be a TIFF file header at "offset" within "file".
            Verify that byte order is "MM" (bigEndian) or "II" (!bigEndian) or error.
            Verify the answer to life, the universe, and everything ...
        */
#ifdef DTLIB
    /* Used when this module is compiled by the DtHelp library build */
    if (_DtGrSeek (stream, offset, 0)) {   /* nonzero means error for fseek */
#else
    if (fseek (stream, offset, 0)) {       /* nonzero means error for fseek */
#endif /* DTLIB */
        context->error = IL_ERROR_FILE_IO;
        return (ilFile)NULL;
        }
#ifdef DTLIB
    /* Used when this module is compiled by the DtHelp library build */
    if (!_DtGrRead ((char *)&header, sizeof (header), 1, stream)) {
#else
    if (!fread ((char *)&header, sizeof (header), 1, stream)) {
#endif /* DTLIB */
        context->error = IL_ERROR_FILE_IO;
        return (ilFile)NULL;
        }
    if ((header.byteOrder[0] == 'M') && (header.byteOrder[1] == 'M'))
        bigEndian = TRUE;
    else if ((header.byteOrder[0] == 'I') && (header.byteOrder[1] == 'I'))
        bigEndian = FALSE;
    else {
        context->error = IL_ERROR_FILE_NOT_TIFF;
        return (ilFile)NULL;
        }
    if (!(bigEndian && (header.version[0] == 0) && (header.version[1] == 42))
     && !(!bigEndian && (header.version[0] == 42) && (header.version[1] == 0))) {
        context->error = IL_ERROR_FILE_NOT_TIFF;
        return (ilFile)NULL;
        }

        /*  File seems to be a valid TIFF file.  Create an ilFileRec object, init it
            and return ptr to it.
        */
    pFile = (ilFilePtr)_ilCreateObject (context, IL_FILE, ilFreeFileList, sizeof(ilFileRec));
    if (!pFile)
        return (ilFile)NULL;                        /* context->error already set */

    pFile->stream = stream;
    pFile->offset = offset;
    pFile->bigEndian = bigEndian;
    pFile->pFileImageHead = pFile->pFileImageTail = (ilFileImagePtr)NULL;
    pFile->haveImageList = FALSE;

    context->error = IL_OK;
    return (ilFile)pFile;
}

