Example #1
0
/*
 * Read the next TIFF directory from a file
 * and convert it to the internal format.
 * We read directories sequentially.
 */
static uint64
ReadDirectory(int fd, unsigned int ix, uint64 off)
{
	uint16 dircount;
	uint32 direntrysize;
	void* dirmem = NULL;
	uint64 nextdiroff = 0;
	uint32 n;
	uint8* dp;

	if (off == 0)			/* no more directories */
		goto done;
	if (_TIFF_lseek_f(fd, (_TIFF_off_t)off, SEEK_SET) != (_TIFF_off_t)off) {
		Fatal("Seek error accessing TIFF directory");
		goto done;
	}
	if (!bigtiff) {
		if (read(fd, (char*) &dircount, sizeof (uint16)) != sizeof (uint16)) {
			ReadError("directory count");
			goto done;
		}
		if (swabflag)
			TIFFSwabShort(&dircount);
		direntrysize = 12;
	} else {
		uint64 dircount64 = 0;
		if (read(fd, (char*) &dircount64, sizeof (uint64)) != sizeof (uint64)) {
			ReadError("directory count");
			goto done;
		}
		if (swabflag)
			TIFFSwabLong8(&dircount64);
		if (dircount64>0xFFFF) {
			Error("Sanity check on directory count failed");
			goto done;
		}
		dircount = (uint16)dircount64;
		direntrysize = 20;
	}
	dirmem = _TIFFmalloc(TIFFSafeMultiply(tmsize_t,dircount,direntrysize));
	if (dirmem == NULL) {
		Fatal("No space for TIFF directory");
		goto done;
	}
	n = read(fd, (char*) dirmem, dircount*direntrysize);
	if (n != dircount*direntrysize) {
		n /= direntrysize;
		Error(
#if defined(__WIN32__) && defined(_MSC_VER)
	    "Could only read %lu of %u entries in directory at offset %#I64x",
		      (unsigned long)n, dircount, (unsigned __int64) off);
#else
	    "Could only read %lu of %u entries in directory at offset %#llx",
		      (unsigned long)n, dircount, (unsigned long long) off);
#endif
		dircount = n;
		nextdiroff = 0;
	} else {
Example #2
0
TIFF*
TIFFClientOpen(
    const char* name, const char* mode,
    thandle_t clientdata,
    TIFFReadWriteProc readproc,
    TIFFReadWriteProc writeproc,
    TIFFSeekProc seekproc,
    TIFFCloseProc closeproc,
    TIFFSizeProc sizeproc,
    TIFFMapFileProc mapproc,
    TIFFUnmapFileProc unmapproc
)
{
    static const char module[] = "TIFFClientOpen";
    TIFF *tif;
    int m;
    const char* cp;

    /* The following are configuration checks. They should be redundant, but should not
     * compile to any actual code in an optimised release build anyway. If any of them
     * fail, (makefile-based or other) configuration is not correct */
    assert(sizeof(uint8)==1);
    assert(sizeof(int8)==1);
    assert(sizeof(uint16)==2);
    assert(sizeof(int16)==2);
    assert(sizeof(uint32)==4);
    assert(sizeof(int32)==4);
    assert(sizeof(uint64)==8);
    assert(sizeof(int64)==8);
    assert(sizeof(tmsize_t)==sizeof(void*));
    {
        union{
            uint8 a8[2];
            uint16 a16;
        } n;
        n.a8[0]=1;
        n.a8[1]=0;
        #ifdef WORDS_BIGENDIAN
        assert(n.a16==256);
        #else
        assert(n.a16==1);
        #endif
    }

    m = _TIFFgetMode(mode, module);
    if (m == -1)
        goto bad2;
    tif = (TIFF *)_TIFFmalloc((tmsize_t)(sizeof (TIFF) + strlen(name) + 1));
    if (tif == NULL) {
        TIFFErrorExt(clientdata, module, "%s: Out of memory (TIFF structure)", name);
        goto bad2;
    }
    _TIFFmemset(tif, 0, sizeof (*tif));
    tif->tif_name = (char *)tif + sizeof (TIFF);
    strcpy(tif->tif_name, name);
    tif->tif_mode = m &~ (O_CREAT|O_TRUNC);
    tif->tif_curdir = (uint16) -1;		/* non-existent directory */
    tif->tif_curoff = 0;
    tif->tif_curstrip = (uint32) -1;	/* invalid strip */
    tif->tif_row = (uint32) -1;		/* read/write pre-increment */
    tif->tif_clientdata = clientdata;
    if (!readproc || !writeproc || !seekproc || !closeproc || !sizeproc) {
        TIFFErrorExt(clientdata, module,
            "One of the client procedures is NULL pointer.");
        goto bad2;
    }
    tif->tif_readproc = readproc;
    tif->tif_writeproc = writeproc;
    tif->tif_seekproc = seekproc;
    tif->tif_closeproc = closeproc;
    tif->tif_sizeproc = sizeproc;
    if (mapproc)
        tif->tif_mapproc = mapproc;
    else
        tif->tif_mapproc = _tiffDummyMapProc;
    if (unmapproc)
        tif->tif_unmapproc = unmapproc;
    else
        tif->tif_unmapproc = _tiffDummyUnmapProc;
    _TIFFSetDefaultCompressionState(tif);    /* setup default state */
    /*
     * Default is to return data MSB2LSB and enable the
     * use of memory-mapped files and strip chopping when
     * a file is opened read-only.
     */
    tif->tif_flags = FILLORDER_MSB2LSB;
    if (m == O_RDONLY )
        tif->tif_flags |= TIFF_MAPPED;

    #ifdef STRIPCHOP_DEFAULT
    if (m == O_RDONLY || m == O_RDWR)
        tif->tif_flags |= STRIPCHOP_DEFAULT;
    #endif

    /*
     * Process library-specific flags in the open mode string.
     * The following flags may be used to control intrinsic library
     * behaviour that may or may not be desirable (usually for
     * compatibility with some application that claims to support
     * TIFF but only supports some braindead idea of what the
     * vendor thinks TIFF is):
     *
     * 'l' use little-endian byte order for creating a file
     * 'b' use big-endian byte order for creating a file
     * 'L' read/write information using LSB2MSB bit order
     * 'B' read/write information using MSB2LSB bit order
     * 'H' read/write information using host bit order
     * 'M' enable use of memory-mapped files when supported
     * 'm' disable use of memory-mapped files
     * 'C' enable strip chopping support when reading
     * 'c' disable strip chopping support
     * 'h' read TIFF header only, do not load the first IFD
     * '4' ClassicTIFF for creating a file (default)
     * '8' BigTIFF for creating a file
     *
     * The use of the 'l' and 'b' flags is strongly discouraged.
     * These flags are provided solely because numerous vendors,
     * typically on the PC, do not correctly support TIFF; they
     * only support the Intel little-endian byte order.  This
     * support is not configured by default because it supports
     * the violation of the TIFF spec that says that readers *MUST*
     * support both byte orders.  It is strongly recommended that
     * you not use this feature except to deal with busted apps
     * that write invalid TIFF.  And even in those cases you should
     * bang on the vendors to fix their software.
     *
     * The 'L', 'B', and 'H' flags are intended for applications
     * that can optimize operations on data by using a particular
     * bit order.  By default the library returns data in MSB2LSB
     * bit order for compatibiltiy with older versions of this
     * library.  Returning data in the bit order of the native cpu
     * makes the most sense but also requires applications to check
     * the value of the FillOrder tag; something they probably do
     * not do right now.
     *
     * The 'M' and 'm' flags are provided because some virtual memory
     * systems exhibit poor behaviour when large images are mapped.
     * These options permit clients to control the use of memory-mapped
     * files on a per-file basis.
     *
     * The 'C' and 'c' flags are provided because the library support
     * for chopping up large strips into multiple smaller strips is not
     * application-transparent and as such can cause problems.  The 'c'
     * option permits applications that only want to look at the tags,
     * for example, to get the unadulterated TIFF tag information.
     */
    for (cp = mode; *cp; cp++)
        switch (*cp) {
            case 'b':
                #ifndef WORDS_BIGENDIAN
                if (m&O_CREAT)
                    tif->tif_flags |= TIFF_SWAB;
                #endif
                break;
            case 'l':
                #ifdef WORDS_BIGENDIAN
                if ((m&O_CREAT))
                    tif->tif_flags |= TIFF_SWAB;
                #endif
                break;
            case 'B':
                tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
                    FILLORDER_MSB2LSB;
                break;
            case 'L':
                tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
                    FILLORDER_LSB2MSB;
                break;
            case 'H':
                tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) |
                    HOST_FILLORDER;
                break;
            case 'M':
                if (m == O_RDONLY)
                    tif->tif_flags |= TIFF_MAPPED;
                break;
            case 'm':
                if (m == O_RDONLY)
                    tif->tif_flags &= ~TIFF_MAPPED;
                break;
            case 'C':
                if (m == O_RDONLY)
                    tif->tif_flags |= TIFF_STRIPCHOP;
                break;
            case 'c':
                if (m == O_RDONLY)
                    tif->tif_flags &= ~TIFF_STRIPCHOP;
                break;
            case 'h':
                tif->tif_flags |= TIFF_HEADERONLY;
                break;
            case '8':
                if (m&O_CREAT)
                    tif->tif_flags |= TIFF_BIGTIFF;
                break;
        }
    /*
     * Read in TIFF header.
     */
    if ((m & O_TRUNC) ||
        !ReadOK(tif, &tif->tif_header, sizeof (TIFFHeaderClassic))) {
        if (tif->tif_mode == O_RDONLY) {
            TIFFErrorExt(tif->tif_clientdata, name,
                "Cannot read TIFF header");
            goto bad;
        }
        /*
         * Setup header and write.
         */
        #ifdef WORDS_BIGENDIAN
        tif->tif_header.common.tiff_magic = tif->tif_flags & TIFF_SWAB
            ? TIFF_LITTLEENDIAN : TIFF_BIGENDIAN;
        #else
        tif->tif_header.common.tiff_magic = tif->tif_flags & TIFF_SWAB
            ? TIFF_BIGENDIAN : TIFF_LITTLEENDIAN;
        #endif
        if (!(tif->tif_flags&TIFF_BIGTIFF))
        {
            tif->tif_header.common.tiff_version = TIFF_VERSION_CLASSIC;
            tif->tif_header.classic.tiff_diroff = 0;
            if (tif->tif_flags & TIFF_SWAB)
                TIFFSwabShort(&tif->tif_header.common.tiff_version);
            tif->tif_header_size = sizeof(TIFFHeaderClassic);
        }
        else
        {
            tif->tif_header.common.tiff_version = TIFF_VERSION_BIG;
            tif->tif_header.big.tiff_offsetsize = 8;
            tif->tif_header.big.tiff_unused = 0;
            tif->tif_header.big.tiff_diroff = 0;
            if (tif->tif_flags & TIFF_SWAB)
            {
                TIFFSwabShort(&tif->tif_header.common.tiff_version);
                TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize);
            }
            tif->tif_header_size = sizeof (TIFFHeaderBig);
        }
        /*
         * The doc for "fopen" for some STD_C_LIBs says that if you
         * open a file for modify ("+"), then you must fseek (or
         * fflush?) between any freads and fwrites.  This is not
         * necessary on most systems, but has been shown to be needed
         * on Solaris.
         */
        TIFFSeekFile( tif, 0, SEEK_SET );
        if (!WriteOK(tif, &tif->tif_header, (tmsize_t)(tif->tif_header_size))) {
            TIFFErrorExt(tif->tif_clientdata, name,
                "Error writing TIFF header");
            goto bad;
        }
        /*
         * Setup the byte order handling.
         */
        if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN) {
            #ifndef WORDS_BIGENDIAN
            tif->tif_flags |= TIFF_SWAB;
            #endif
        } else {
            #ifdef WORDS_BIGENDIAN
            tif->tif_flags |= TIFF_SWAB;
            #endif
        }
        /*
         * Setup default directory.
         */
        if (!TIFFDefaultDirectory(tif))
            goto bad;
        tif->tif_diroff = 0;
        tif->tif_dirlist = NULL;
        tif->tif_dirlistsize = 0;
        tif->tif_dirnumber = 0;
        return (tif);
    }
    /*
     * Setup the byte order handling.
     */
    if (tif->tif_header.common.tiff_magic != TIFF_BIGENDIAN &&
        tif->tif_header.common.tiff_magic != TIFF_LITTLEENDIAN
        #if MDI_SUPPORT
        &&
        #if HOST_BIGENDIAN
        tif->tif_header.common.tiff_magic != MDI_BIGENDIAN
        #else
        tif->tif_header.common.tiff_magic != MDI_LITTLEENDIAN
        #endif
        ) {
        TIFFErrorExt(tif->tif_clientdata, name,
            "Not a TIFF or MDI file, bad magic number %d (0x%x)",
        #else
        ) {
        TIFFErrorExt(tif->tif_clientdata, name,
            "Not a TIFF file, bad magic number %d (0x%x)",
        #endif
            tif->tif_header.common.tiff_magic,
            tif->tif_header.common.tiff_magic);
        goto bad;
    }
    if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN) {
        #ifndef WORDS_BIGENDIAN
        tif->tif_flags |= TIFF_SWAB;
        #endif
    } else {
        #ifdef WORDS_BIGENDIAN
        tif->tif_flags |= TIFF_SWAB;
        #endif
    }
    if (tif->tif_flags & TIFF_SWAB)
        TIFFSwabShort(&tif->tif_header.common.tiff_version);
    if ((tif->tif_header.common.tiff_version != TIFF_VERSION_CLASSIC)&&
        (tif->tif_header.common.tiff_version != TIFF_VERSION_BIG)) {
        TIFFErrorExt(tif->tif_clientdata, name,
            "Not a TIFF file, bad version number %d (0x%x)",
            tif->tif_header.common.tiff_version,
            tif->tif_header.common.tiff_version);
        goto bad;
    }
    if (tif->tif_header.common.tiff_version == TIFF_VERSION_CLASSIC)
    {
        if (tif->tif_flags & TIFF_SWAB)
            TIFFSwabLong(&tif->tif_header.classic.tiff_diroff);
        tif->tif_header_size = sizeof(TIFFHeaderClassic);
    }
    else
    {
        if (!ReadOK(tif, ((uint8*)(&tif->tif_header) + sizeof(TIFFHeaderClassic)), (sizeof(TIFFHeaderBig)-sizeof(TIFFHeaderClassic))))
        {
            TIFFErrorExt(tif->tif_clientdata, name,
                "Cannot read TIFF header");
            goto bad;
        }
        if (tif->tif_flags & TIFF_SWAB)
        {
            TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize);
            TIFFSwabLong8(&tif->tif_header.big.tiff_diroff);
        }
        if (tif->tif_header.big.tiff_offsetsize != 8)
        {
            TIFFErrorExt(tif->tif_clientdata, name,
                "Not a TIFF file, bad BigTIFF offsetsize %d (0x%x)",
                tif->tif_header.big.tiff_offsetsize,
                tif->tif_header.big.tiff_offsetsize);
            goto bad;
        }
        if (tif->tif_header.big.tiff_unused != 0)
        {
            TIFFErrorExt(tif->tif_clientdata, name,
                "Not a TIFF file, bad BigTIFF unused %d (0x%x)",
                tif->tif_header.big.tiff_unused,
                tif->tif_header.big.tiff_unused);
            goto bad;
        }
        tif->tif_header_size = sizeof(TIFFHeaderBig);
        tif->tif_flags |= TIFF_BIGTIFF;
    }
    tif->tif_flags |= TIFF_MYBUFFER;
    tif->tif_rawcp = tif->tif_rawdata = 0;
    tif->tif_rawdatasize = 0;
        tif->tif_rawdataoff = 0;
        tif->tif_rawdataloaded = 0;

    switch (mode[0]) {
        case 'r':
            if (!(tif->tif_flags&TIFF_BIGTIFF))
                tif->tif_nextdiroff = tif->tif_header.classic.tiff_diroff;
            else
                tif->tif_nextdiroff = tif->tif_header.big.tiff_diroff;
            /*
             * Try to use a memory-mapped file if the client
             * has not explicitly suppressed usage with the
             * 'm' flag in the open mode (see above).
             */
            if (tif->tif_flags & TIFF_MAPPED)
            {
                toff_t n;
                if (TIFFMapFileContents(tif,(void**)(&tif->tif_base),&n))
                {
                    tif->tif_size=(tmsize_t)n;
                    assert((toff_t)tif->tif_size==n);
                }
                else
                    tif->tif_flags &= ~TIFF_MAPPED;
            }
            /*
             * Sometimes we do not want to read the first directory (for example,
             * it may be broken) and want to proceed to other directories. I this
             * case we use the TIFF_HEADERONLY flag to open file and return
             * immediately after reading TIFF header.
             */
            if (tif->tif_flags & TIFF_HEADERONLY)
                return (tif);

            /*
             * Setup initial directory.
             */
            if (TIFFReadDirectory(tif)) {
                tif->tif_rawcc = (tmsize_t)-1;
                tif->tif_flags |= TIFF_BUFFERSETUP;
                return (tif);
            }
            break;
        case 'a':
            /*
             * New directories are automatically append
             * to the end of the directory chain when they
             * are written out (see TIFFWriteDirectory).
             */
            if (!TIFFDefaultDirectory(tif))
                goto bad;
            return (tif);
    }
bad:
    tif->tif_mode = O_RDONLY;	/* XXX avoid flush */
        TIFFCleanup(tif);
bad2:
    return ((TIFF*)0);
}
Example #3
0
static void
dump(int fd, uint64 diroff)
{
	unsigned i;

	lseek(fd, (off_t) 0, 0);
	if (read(fd, (char*) &hdr, sizeof (TIFFHeaderCommon)) != sizeof (TIFFHeaderCommon))
		ReadError("TIFF header");
	if (hdr.common.tiff_magic != TIFF_BIGENDIAN
	    && hdr.common.tiff_magic != TIFF_LITTLEENDIAN &&
#if HOST_BIGENDIAN
	    /* MDI is sensitive to the host byte order, unlike TIFF */
	    MDI_BIGENDIAN != hdr.common.tiff_magic
#else
	    MDI_LITTLEENDIAN != hdr.common.tiff_magic
#endif
	   ) {
		Fatal("Not a TIFF or MDI file, bad magic number %u (%#x)",
		    hdr.common.tiff_magic, hdr.common.tiff_magic);
	}
	if (hdr.common.tiff_magic == TIFF_BIGENDIAN
	    || hdr.common.tiff_magic == MDI_BIGENDIAN)
		swabflag = !bigendian;
	else
		swabflag = bigendian;
	if (swabflag)
		TIFFSwabShort(&hdr.common.tiff_version);
	if (hdr.common.tiff_version==42)
	{
		if (read(fd, (char*) &hdr.classic.tiff_diroff, 4) != 4)
			ReadError("TIFF header");
		if (swabflag)
			TIFFSwabLong(&hdr.classic.tiff_diroff);
		printf("Magic: %#x <%s-endian> Version: %#x <%s>\n",
		    hdr.classic.tiff_magic,
		    hdr.classic.tiff_magic == TIFF_BIGENDIAN ? "big" : "little",
		    42,"ClassicTIFF");
		if (diroff == 0)
			diroff = hdr.classic.tiff_diroff;
	}
	else if (hdr.common.tiff_version==43)
	{
		if (read(fd, (char*) &hdr.big.tiff_offsetsize, 12) != 12)
			ReadError("TIFF header");
		if (swabflag)
		{
			TIFFSwabShort(&hdr.big.tiff_offsetsize);
			TIFFSwabShort(&hdr.big.tiff_unused);
			TIFFSwabLong8(&hdr.big.tiff_diroff);
		}
		printf("Magic: %#x <%s-endian> Version: %#x <%s>\n",
		    hdr.big.tiff_magic,
		    hdr.big.tiff_magic == TIFF_BIGENDIAN ? "big" : "little",
		    43,"BigTIFF");
		printf("OffsetSize: %#x Unused: %#x\n",
		    hdr.big.tiff_offsetsize,hdr.big.tiff_unused);
		if (diroff == 0)
			diroff = hdr.big.tiff_diroff;
		bigtiff = 1;
	}
	else
		Fatal("Not a TIFF file, bad version number %u (%#x)",
		    hdr.common.tiff_version, hdr.common.tiff_version);
	for (i = 0; diroff != 0; i++) {
		if (i > 0)
			putchar('\n');
		diroff = ReadDirectory(fd, i, diroff);
	}
}
Example #4
0
static void
dump(int fd, uint64 diroff)
{
	unsigned i, j;
	uint64* visited_diroff = NULL;
	unsigned int count_visited_dir = 0;

	_TIFF_lseek_f(fd, (_TIFF_off_t) 0, 0);
	if (read(fd, (char*) &hdr, sizeof (TIFFHeaderCommon)) != sizeof (TIFFHeaderCommon))
		ReadError("TIFF header");
	if (hdr.common.tiff_magic != TIFF_BIGENDIAN
	    && hdr.common.tiff_magic != TIFF_LITTLEENDIAN &&
#if HOST_BIGENDIAN
	    /* MDI is sensitive to the host byte order, unlike TIFF */
	    MDI_BIGENDIAN != hdr.common.tiff_magic
#else
	    MDI_LITTLEENDIAN != hdr.common.tiff_magic
#endif
	   ) {
		Fatal("Not a TIFF or MDI file, bad magic number %u (%#x)",
		    hdr.common.tiff_magic, hdr.common.tiff_magic);
	}
	if (hdr.common.tiff_magic == TIFF_BIGENDIAN
	    || hdr.common.tiff_magic == MDI_BIGENDIAN)
		swabflag = !bigendian;
	else
		swabflag = bigendian;
	if (swabflag)
		TIFFSwabShort(&hdr.common.tiff_version);
	if (hdr.common.tiff_version==42)
	{
		if (read(fd, (char*) &hdr.classic.tiff_diroff, 4) != 4)
			ReadError("TIFF header");
		if (swabflag)
			TIFFSwabLong(&hdr.classic.tiff_diroff);
		printf("Magic: %#x <%s-endian> Version: %#x <%s>\n",
		    hdr.classic.tiff_magic,
		    hdr.classic.tiff_magic == TIFF_BIGENDIAN ? "big" : "little",
		    42,"ClassicTIFF");
		if (diroff == 0)
			diroff = hdr.classic.tiff_diroff;
	}
	else if (hdr.common.tiff_version==43)
	{
		if (read(fd, (char*) &hdr.big.tiff_offsetsize, 12) != 12)
			ReadError("TIFF header");
		if (swabflag)
		{
			TIFFSwabShort(&hdr.big.tiff_offsetsize);
			TIFFSwabShort(&hdr.big.tiff_unused);
			TIFFSwabLong8(&hdr.big.tiff_diroff);
		}
		printf("Magic: %#x <%s-endian> Version: %#x <%s>\n",
		    hdr.big.tiff_magic,
		    hdr.big.tiff_magic == TIFF_BIGENDIAN ? "big" : "little",
		    43,"BigTIFF");
		printf("OffsetSize: %#x Unused: %#x\n",
		    hdr.big.tiff_offsetsize,hdr.big.tiff_unused);
		if (diroff == 0)
			diroff = hdr.big.tiff_diroff;
		bigtiff = 1;
	}
	else
		Fatal("Not a TIFF file, bad version number %u (%#x)",
		    hdr.common.tiff_version, hdr.common.tiff_version);
	for (i = 0; diroff != 0; i++) {
		for(j=0; j<count_visited_dir; j++)
		{
		    if( visited_diroff[j] == diroff )
		    {
			free(visited_diroff);
			Fatal("Cycle detected in chaining of TIFF directories!");
		    }
		}
                {
                    size_t alloc_size;
                    alloc_size=TIFFSafeMultiply(tmsize_t,(count_visited_dir + 1),
                                                sizeof(uint64));
                    if (alloc_size == 0)
                    {
                        if (visited_diroff)
                            free(visited_diroff);
                        visited_diroff = 0;
                    }
                    else
                    {
                        visited_diroff = (uint64*) realloc(visited_diroff,alloc_size);
                    }
                }
		if( !visited_diroff )
		    Fatal("Out of memory");
		visited_diroff[count_visited_dir] = diroff;
		count_visited_dir ++;

		if (i > 0)
			putchar('\n');
		diroff = ReadDirectory(fd, i, diroff);
	}
	if( visited_diroff )
	    free(visited_diroff);
}
Example #5
0
/*
 * Function to convert standard TIFF file to BigTIFF file.  Loop through all directories in
 * current file (including subdirectories linked via SubIFD arrays) and create corresponding
 * new directories with 64-bit arrays.  The old 32-bit directories are left in the file as
 * dead space.  The data for directory entries are reused by default.  Some directory entries
 * require new data with 64-bit offsets (e.g. stripe/tile offsets and SubIFD arrays).
 *
 * Returns 1 if successful, 0 if cannot convert to BigTIFF (TIFF_NOBIGTIFF set 
 * or 32-bit API called).
 */
static int 
TIFFMakeBigTIFF(TIFF *tif)
{
	uint32	dirlink = TIFF_HEADER_DIROFF_S,
		diroff = tif->tif_header.s.tiff_diroff;
	toff_t	dirlinkB = TIFF_HEADER_DIROFF_B,
		diroffB;
	uint16	dircount, dirindex;
	uint64	dircountB;
	tsize_t	dirsize, dirsizeB;
	int	issubifd = 0;
	uint32	subifdcnt = 0;
	uint32	subifdlink;
	toff_t	subifdlinkB;
	char	*data, *dataB;
	TIFFDirEntry *dir, *dirB;

	/*
	 * Update flags and file header
	 */
	if (noBigTIFF(tif)) {
		TIFFErrorExt((tif)->tif_clientdata, 
			"TIFFCheckBigTIFF", "File > 2^32 and NO BigTIFF specified");
		return (0);
	}
	tif->tif_flags |= TIFF_ISBIGTIFF;
	tif->tif_header.b.tiff_version = TIFF_BIGTIFF_VERSION;
	tif->tif_header.b.tiff_offsize = 8;
	tif->tif_header.b.tiff_fill = 0;
	tif->tif_header.b.tiff_diroff = 0;
	if (tif->tif_flags & TIFF_SWAB) {
		TIFFSwabShort(&tif->tif_header.b.tiff_version);
		TIFFSwabShort(&tif->tif_header.b.tiff_offsize);
	}
	if (!SeekOK(tif, 0) ||
	    !WriteOK(tif, &tif->tif_header, sizeof(TIFFHeader))) {
		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, 
			"Error updating TIFF header (%d)", TIFFGetErrno(tif));
		return (0);
	}
	if (tif->tif_flags & TIFF_SWAB) {
		TIFFSwabShort(&tif->tif_header.b.tiff_version);
		TIFFSwabShort(&tif->tif_header.b.tiff_offsize);
	}

	/*
	 * Loop through all directories and rewrite as BigTIFF with 64-bit offsets.  This
	 * begins with main IFD chain but may divert to SubIFD arrays as needed.
	 */
	while (diroff != 0 && diroff != tif->tif_diroff) {
		if (!SeekOK(tif, diroff) ||
		    !ReadOK(tif, &dircount, sizeof(dircount))) {
			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, 
				"Error reading TIFF directory (%d)", TIFFGetErrno(tif));
			return (0);
		}
		if (tif->tif_flags & TIFF_SWAB)
			TIFFSwabShort(&dircount);
		dircountB = dircount;
		if (!(data = _TIFFmalloc(dirsize = dircount * TIFFDirEntryLenS)) ||
		    !(dataB = _TIFFmalloc(dirsizeB = dircount * TIFFDirEntryLenB))) {
			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, 
				"Error allocating space for directory");
			return (0);
		}
		if (!SeekOK(tif, diroff + sizeof(dircount)) ||
		    !ReadOK(tif, data, dirsize)) {
			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, 
				"Error reading TIFF directory (%d)", TIFFGetErrno(tif));
			goto error;
		}
		diroffB = tif->tif_dataoff;
		tif->tif_dataoff += sizeof(dircountB) + dirsizeB + sizeof(toff_t);

		for (dirindex = 0; dirindex < dircount; dirindex++) {
			dir = (TIFFDirEntry*) (data + dirindex * TIFFDirEntryLenS);
			dirB = (TIFFDirEntry*) (dataB + dirindex * TIFFDirEntryLenB);
			if (tif->tif_flags & TIFF_SWAB) {
				TIFFSwabShort(&dir->s.tdir_tag);
				TIFFSwabShort(&dir->s.tdir_type);
				TIFFSwabLong(&dir->s.tdir_count);
				TIFFSwabLong(&dir->s.tdir_offset);
			}
			dirB->b.tdir_tag = dir->s.tdir_tag;
			dirB->b.tdir_type = dir->s.tdir_type;
			dirB->b.tdir_count = dir->s.tdir_count;
			dirB->b.tdir_offset = 0;
			/*
			 * If data are in directory entry itself, copy data, else (data are pointed
			 * to by directory entry) copy pointer.  This is complicated by the fact that
			 * the old entry had 32-bits of space, and the new has 64-bits, so may have
			 * to read data pointed at by the old entry directly into the new entry.
			 */
			switch (dir->s.tdir_type) {
			case TIFF_UNDEFINED:
			case TIFF_BYTE:
			case TIFF_SBYTE:
			case TIFF_ASCII:
				if (dir->s.tdir_count <= sizeof(dir->s.tdir_offset))
					_TIFFmemcpy(&dirB->b.tdir_offset, &dir->s.tdir_offset, dir->s.tdir_count);
				else if (dir->s.tdir_count <= sizeof(dirB->b.tdir_count)) {
					TIFFSeekFile(tif, dir->s.tdir_offset, SEEK_SET);
					TIFFReadFile(tif, &dirB->b.tdir_offset, dir->s.tdir_count);
				} else
					dirB->b.tdir_offset = dir->s.tdir_offset;
				break;
			case TIFF_SHORT:
			case TIFF_SSHORT:
				if (dir->s.tdir_count <= sizeof(dir->s.tdir_offset) / sizeof(uint16))
					_TIFFmemcpy(&dirB->b.tdir_offset, &dir->s.tdir_offset, dir->s.tdir_count * sizeof(uint16));
				else if (dir->s.tdir_count <= sizeof(dirB->b.tdir_count) / sizeof(uint16)) {
					TIFFSeekFile(tif, dir->s.tdir_offset, SEEK_SET);
					TIFFReadFile(tif, &dirB->b.tdir_offset, dir->s.tdir_count * sizeof(uint16));
					if (tif->tif_flags & TIFF_SWAB)
						TIFFSwabArrayOfShort((uint16*) &dirB->b.tdir_offset, dir->s.tdir_count);
				} else
					dirB->b.tdir_offset = dir->s.tdir_offset;
				break;
			case TIFF_LONG:
			case TIFF_FLOAT:
			case TIFF_IFD:
				if (dir->s.tdir_count <= sizeof(dir->s.tdir_offset) / sizeof(uint32))
					_TIFFmemcpy(&dirB->b.tdir_offset, &dir->s.tdir_offset, dir->s.tdir_count * sizeof(uint32));
				else if (dir->s.tdir_count <= sizeof(dirB->b.tdir_count) / sizeof(uint32)) {
					TIFFSeekFile(tif, dir->s.tdir_offset, SEEK_SET);
					TIFFReadFile(tif, &dirB->b.tdir_offset, dir->s.tdir_count * sizeof(uint32));
					if (tif->tif_flags & TIFF_SWAB)
						TIFFSwabArrayOfLong((uint32*) &dirB->b.tdir_offset, dir->s.tdir_count);
				} else
					dirB->b.tdir_offset = dir->s.tdir_offset;
				break;
			case TIFF_RATIONAL:
			case TIFF_SRATIONAL:
				if (dir->s.tdir_count * 2 <= sizeof(dirB->b.tdir_offset) / sizeof(uint32)) {
					TIFFSeekFile(tif, dir->s.tdir_offset, SEEK_SET);
					TIFFReadFile(tif, &dirB->b.tdir_offset, dir->s.tdir_count * 2 * sizeof(uint32));
					if (tif->tif_flags & TIFF_SWAB)
						TIFFSwabArrayOfLong((uint32*) &dirB->b.tdir_offset, dir->s.tdir_count * 2);
				} else
					dirB->b.tdir_offset = dir->s.tdir_offset;
				break;
			default:
				dirB->b.tdir_offset = dir->s.tdir_offset;
				break;
			}
			/*
			 * Process special case of SUBIFD; must change data from 32-bit to 64-bit in 
			 * case new 64-bit directory offsets need to be added.
			 */
			switch (dirB->b.tdir_tag) {
			case TIFFTAG_SUBIFD:
				dirB->b.tdir_type = TIFF_IFD8;
				subifdcnt = dir->s.tdir_count;
				/*
				 * Set pointer to existing SubIFD array
				 */
				if (subifdcnt <= sizeof(dir->s.tdir_offset) / sizeof(uint32))
					subifdlink = diroff + sizeof(dircount) +
						(char*) &dir->s.tdir_offset - (char*) dir;
				else
					subifdlink = dir->s.tdir_offset;
				/*
				 * Initialize new SubIFD array, set pointer to it
				 */
				if (subifdcnt <= sizeof(dir->b.tdir_offset) / sizeof(toff_t)) {
					dir->b.tdir_offset = 0;
					subifdlinkB = diroffB + sizeof(dircountB) +
						(char*) &dir->b.tdir_offset - (char*) dirB;
				} else {
					toff_t *offB;

					if (!(offB = _TIFFmalloc(subifdcnt * sizeof(toff_t)))) {
						TIFFErrorExt(tif->tif_clientdata, tif->tif_name, 
							"Error allocating space for 64-bit SubIFDs");
						goto error;
					}
					_TIFFmemset(offB, 0, subifdcnt * sizeof(toff_t));
					TIFFWriteLong8Array(tif, dirB, offB);
					_TIFFfree(offB);
					subifdlinkB = dirB->b.tdir_offset;
					}
				break;
			}
			if (tif->tif_flags & TIFF_SWAB) {
				TIFFSwabShort(&dirB->b.tdir_tag);
				TIFFSwabShort(&dirB->b.tdir_type);
				TIFFSwabLong8(&dirB->b.tdir_count);
				TIFFSwabLong8(&dirB->b.tdir_offset);
			}
		}
		
		/*
		 * Write out new directory and chain to previous
		 */
		if (tif->tif_flags & TIFF_SWAB)
			TIFFSwabLong8(&dircountB);
		if (!SeekOK(tif, diroffB) ||
		    !WriteOK(tif, &dircountB, sizeof(dircountB)) ||
		    !SeekOK(tif, diroffB + sizeof(dircountB)) ||
		    !WriteOK(tif, dataB, dirsizeB)) {
			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, 
				"Error writing TIFF directory (%d)", TIFFGetErrno(tif));
			goto error;
		}

		/*
		 * If directory is SubIFD update array in host directory, else add to
		 * main directory chain
		 */
		if (tif->tif_nsubifd &&
			tif->tif_subifdoff == subifdlink)
			tif->tif_subifdoff = subifdlinkB;

		if (!issubifd && 
			dirlinkB == TIFF_HEADER_DIROFF_B)
			tif->tif_header.b.tiff_diroff = diroffB;
		if (tif->tif_flags & TIFF_SWAB)
			TIFFSwabLong8(&diroffB);
		if (!SeekOK(tif, (issubifd ? subifdlinkB++ : dirlinkB)) ||
		    !WriteOK(tif, &diroffB, sizeof(diroffB))) {
			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, 
				"Error writing directory link (%d)", TIFFGetErrno(tif));
			goto error;
		}

		if (issubifd)
			subifdcnt--;
		else {
			dirlink = diroff + sizeof(dircount) + dirsize;
			dirlinkB = diroffB + sizeof(dircountB) + dirsizeB;
		}
		issubifd = (subifdcnt > 0);

		if (!SeekOK(tif, (issubifd ? subifdlink++ : dirlink)) ||
		    !ReadOK(tif, &diroff, sizeof(diroff))) {
			TIFFErrorExt(tif->tif_clientdata, tif->tif_name, 
				"Error reading directory link (%d)", TIFFGetErrno(tif));
			goto error;
		}
		if (tif->tif_flags & TIFF_SWAB)
			TIFFSwabLong(&diroff);
		_TIFFfree(dataB);
		_TIFFfree(data);
	}

	/*
	 * Mark end of directory chain
	 */
	diroffB = 0;
	if (dirlinkB == TIFF_HEADER_DIROFF_B)
		tif->tif_header.b.tiff_diroff = diroffB;
	if (tif->tif_flags & TIFF_SWAB)
		TIFFSwabLong8(&diroffB);
	if (!SeekOK(tif, dirlinkB) ||
	    !WriteOK(tif, &diroffB, sizeof(diroffB))) {
		TIFFErrorExt(tif->tif_clientdata, tif->tif_name, 
			"Error writing directory link (%d)", TIFFGetErrno(tif));
		goto error;
	}

	return (1);

error:
	_TIFFfree(dataB);
	_TIFFfree(data);
	return (0);
}