roentgen b75cab
/* $Id: tif_stream.cxx,v 1.11 2010-12-11 23:12:29 faxguy Exp $ */
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * Copyright (c) 1988-1996 Sam Leffler
roentgen b75cab
 * Copyright (c) 1991-1996 Silicon Graphics, Inc.
roentgen b75cab
 *
roentgen b75cab
 * Permission to use, copy, modify, distribute, and sell this software and 
roentgen b75cab
 * its documentation for any purpose is hereby granted without fee, provided
roentgen b75cab
 * that (i) the above copyright notices and this permission notice appear in
roentgen b75cab
 * all copies of the software and related documentation, and (ii) the names of
roentgen b75cab
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
roentgen b75cab
 * publicity relating to the software without the specific, prior written
roentgen b75cab
 * permission of Sam Leffler and Silicon Graphics.
roentgen b75cab
 * 
roentgen b75cab
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
roentgen b75cab
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
roentgen b75cab
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
roentgen b75cab
 * 
roentgen b75cab
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
roentgen b75cab
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
roentgen b75cab
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
roentgen b75cab
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
roentgen b75cab
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
roentgen b75cab
 * OF THIS SOFTWARE.
roentgen b75cab
 */
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * TIFF Library UNIX-specific Routines.
roentgen b75cab
 */
roentgen b75cab
#include "tiffiop.h"
roentgen b75cab
#include <iostream></iostream>
roentgen b75cab
roentgen b75cab
#ifndef __VMS
roentgen b75cab
using namespace std;
roentgen b75cab
#endif
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
  ISO C++ uses a 'std::streamsize' type to define counts.  This makes
roentgen b75cab
  it similar to, (but perhaps not the same as) size_t.
roentgen b75cab
roentgen b75cab
  The std::ios::pos_type is used to represent stream positions as used
roentgen b75cab
  by tellg(), tellp(), seekg(), and seekp().  This makes it similar to
roentgen b75cab
  (but perhaps not the same as) 'off_t'.  The std::ios::streampos type
roentgen b75cab
  is used for character streams, but is documented to not be an
roentgen b75cab
  integral type anymore, so it should *not* be assigned to an integral
roentgen b75cab
  type.
roentgen b75cab
roentgen b75cab
  The std::ios::off_type is used to specify relative offsets needed by
roentgen b75cab
  the variants of seekg() and seekp() which accept a relative offset
roentgen b75cab
  argument.
roentgen b75cab
roentgen b75cab
  Useful prototype knowledge:
roentgen b75cab
roentgen b75cab
  Obtain read position
roentgen b75cab
    ios::pos_type basic_istream::tellg()
roentgen b75cab
roentgen b75cab
  Set read position
roentgen b75cab
    basic_istream& basic_istream::seekg(ios::pos_type)
roentgen b75cab
    basic_istream& basic_istream::seekg(ios::off_type, ios_base::seekdir)
roentgen b75cab
roentgen b75cab
  Read data
roentgen b75cab
    basic_istream& istream::read(char *str, streamsize count)
roentgen b75cab
roentgen b75cab
  Number of characters read in last unformatted read
roentgen b75cab
    streamsize istream::gcount();
roentgen b75cab
roentgen b75cab
  Obtain write position
roentgen b75cab
    ios::pos_type basic_ostream::tellp()
roentgen b75cab
roentgen b75cab
  Set write position
roentgen b75cab
    basic_ostream& basic_ostream::seekp(ios::pos_type)
roentgen b75cab
    basic_ostream& basic_ostream::seekp(ios::off_type, ios_base::seekdir)
roentgen b75cab
roentgen b75cab
  Write data
roentgen b75cab
    basic_ostream& ostream::write(const char *str, streamsize count)
roentgen b75cab
*/
roentgen b75cab
roentgen b75cab
struct tiffis_data;
roentgen b75cab
struct tiffos_data;
roentgen b75cab
roentgen b75cab
extern "C" {
roentgen b75cab
roentgen b75cab
	static tmsize_t _tiffosReadProc(thandle_t, void*, tmsize_t);
roentgen b75cab
	static tmsize_t _tiffisReadProc(thandle_t fd, void* buf, tmsize_t size);
roentgen b75cab
	static tmsize_t _tiffosWriteProc(thandle_t fd, void* buf, tmsize_t size);
roentgen b75cab
	static tmsize_t _tiffisWriteProc(thandle_t, void*, tmsize_t);
roentgen b75cab
	static uint64   _tiffosSeekProc(thandle_t fd, uint64 off, int whence);
roentgen b75cab
	static uint64   _tiffisSeekProc(thandle_t fd, uint64 off, int whence);
roentgen b75cab
	static uint64   _tiffosSizeProc(thandle_t fd);
roentgen b75cab
	static uint64   _tiffisSizeProc(thandle_t fd);
roentgen b75cab
	static int      _tiffosCloseProc(thandle_t fd);
roentgen b75cab
	static int      _tiffisCloseProc(thandle_t fd);
roentgen b75cab
	static int 	_tiffDummyMapProc(thandle_t , void** base, toff_t* size );
roentgen b75cab
	static void     _tiffDummyUnmapProc(thandle_t , void* base, toff_t size );
roentgen b75cab
	static TIFF*    _tiffStreamOpen(const char* name, const char* mode, void *fd);
roentgen b75cab
roentgen b75cab
struct tiffis_data
roentgen b75cab
{
roentgen b75cab
	istream	*stream;
roentgen b75cab
        ios::pos_type start_pos;
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
struct tiffos_data
roentgen b75cab
{
roentgen b75cab
	ostream	*stream;
roentgen b75cab
	ios::pos_type start_pos;
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
static tmsize_t
roentgen b75cab
_tiffosReadProc(thandle_t, void*, tmsize_t)
roentgen b75cab
{
roentgen b75cab
        return 0;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static tmsize_t
roentgen b75cab
_tiffisReadProc(thandle_t fd, void* buf, tmsize_t size)
roentgen b75cab
{
roentgen b75cab
        tiffis_data	*data = reinterpret_cast<tiffis_data *="">(fd);</tiffis_data>
roentgen b75cab
roentgen b75cab
        // Verify that type does not overflow.
roentgen b75cab
        streamsize request_size = size;
roentgen b75cab
        if (static_cast<tmsize_t>(request_size) != size)</tmsize_t>
roentgen b75cab
          return static_cast<tmsize_t>(-1);</tmsize_t>
roentgen b75cab
roentgen b75cab
        data->stream->read((char *) buf, request_size);
roentgen b75cab
roentgen b75cab
        return static_cast<tmsize_t>(data->stream->gcount());</tmsize_t>
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static tmsize_t
roentgen b75cab
_tiffosWriteProc(thandle_t fd, void* buf, tmsize_t size)
roentgen b75cab
{
roentgen b75cab
	tiffos_data	*data = reinterpret_cast<tiffos_data *="">(fd);</tiffos_data>
roentgen b75cab
	ostream		*os = data->stream;
roentgen b75cab
	ios::pos_type	pos = os->tellp();
roentgen b75cab
roentgen b75cab
        // Verify that type does not overflow.
roentgen b75cab
        streamsize request_size = size;
roentgen b75cab
        if (static_cast<tmsize_t>(request_size) != size)</tmsize_t>
roentgen b75cab
          return static_cast<tmsize_t>(-1);</tmsize_t>
roentgen b75cab
roentgen b75cab
	os->write(reinterpret_cast<const *="" char="">(buf), request_size);</const>
roentgen b75cab
roentgen b75cab
	return static_cast<tmsize_t>(os->tellp() - pos);</tmsize_t>
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static tmsize_t
roentgen b75cab
_tiffisWriteProc(thandle_t, void*, tmsize_t)
roentgen b75cab
{
roentgen b75cab
	return 0;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static uint64
roentgen b75cab
_tiffosSeekProc(thandle_t fd, uint64 off, int whence)
roentgen b75cab
{
roentgen b75cab
	tiffos_data	*data = reinterpret_cast<tiffos_data *="">(fd);</tiffos_data>
roentgen b75cab
	ostream		*os = data->stream;
roentgen b75cab
roentgen b75cab
	// if the stream has already failed, don't do anything
roentgen b75cab
	if( os->fail() )
roentgen b75cab
		return static_cast<uint64>(-1);</uint64>
roentgen b75cab
roentgen b75cab
	switch(whence) {
roentgen b75cab
	case SEEK_SET:
roentgen b75cab
		{
roentgen b75cab
			// Compute 64-bit offset
roentgen b75cab
			uint64 new_offset = static_cast<uint64>(data->start_pos) + off;</uint64>
roentgen b75cab
roentgen b75cab
			// Verify that value does not overflow
roentgen b75cab
			ios::off_type offset = static_cast<ios::off_type>(new_offset);</ios::off_type>
roentgen b75cab
			if (static_cast<uint64>(offset) != new_offset)</uint64>
roentgen b75cab
				return static_cast<uint64>(-1);</uint64>
roentgen b75cab
			
roentgen b75cab
			os->seekp(offset, ios::beg);
roentgen b75cab
		break;
roentgen b75cab
		}
roentgen b75cab
	case SEEK_CUR:
roentgen b75cab
		{
roentgen b75cab
			// Verify that value does not overflow
roentgen b75cab
			ios::off_type offset = static_cast<ios::off_type>(off);</ios::off_type>
roentgen b75cab
			if (static_cast<uint64>(offset) != off)</uint64>
roentgen b75cab
				return static_cast<uint64>(-1);</uint64>
roentgen b75cab
roentgen b75cab
			os->seekp(offset, ios::cur);
roentgen b75cab
			break;
roentgen b75cab
		}
roentgen b75cab
	case SEEK_END:
roentgen b75cab
		{
roentgen b75cab
			// Verify that value does not overflow
roentgen b75cab
			ios::off_type offset = static_cast<ios::off_type>(off);</ios::off_type>
roentgen b75cab
			if (static_cast<uint64>(offset) != off)</uint64>
roentgen b75cab
				return static_cast<uint64>(-1);</uint64>
roentgen b75cab
roentgen b75cab
			os->seekp(offset, ios::end);
roentgen b75cab
			break;
roentgen b75cab
		}
roentgen b75cab
	}
roentgen b75cab
roentgen b75cab
	// Attempt to workaround problems with seeking past the end of the
roentgen b75cab
	// stream.  ofstream doesn't have a problem with this but
roentgen b75cab
	// ostrstream/ostringstream does. In that situation, add intermediate
roentgen b75cab
	// '\0' characters.
roentgen b75cab
	if( os->fail() ) {
roentgen b75cab
#ifdef __VMS
roentgen b75cab
		int		old_state;
roentgen b75cab
#else
roentgen b75cab
		ios::iostate	old_state;
roentgen b75cab
#endif
roentgen b75cab
		ios::pos_type	origin;
roentgen b75cab
roentgen b75cab
		old_state = os->rdstate();
roentgen b75cab
		// reset the fail bit or else tellp() won't work below
roentgen b75cab
		os->clear(os->rdstate() & ~ios::failbit);
roentgen b75cab
		switch( whence ) {
roentgen b75cab
			case SEEK_SET:
roentgen b75cab
                        default:
roentgen b75cab
				origin = data->start_pos;
roentgen b75cab
				break;
roentgen b75cab
			case SEEK_CUR:
roentgen b75cab
				origin = os->tellp();
roentgen b75cab
				break;
roentgen b75cab
			case SEEK_END:
roentgen b75cab
				os->seekp(0, ios::end);
roentgen b75cab
				origin = os->tellp();
roentgen b75cab
				break;
roentgen b75cab
		}
roentgen b75cab
		// restore original stream state
roentgen b75cab
		os->clear(old_state);	
roentgen b75cab
roentgen b75cab
		// only do something if desired seek position is valid
roentgen b75cab
		if( (static_cast<uint64>(origin) + off) > static_cast<uint64>(data->start_pos) ) {</uint64></uint64>
roentgen b75cab
			uint64	num_fill;
roentgen b75cab
roentgen b75cab
			// clear the fail bit 
roentgen b75cab
			os->clear(os->rdstate() & ~ios::failbit);
roentgen b75cab
roentgen b75cab
			// extend the stream to the expected size
roentgen b75cab
			os->seekp(0, ios::end);
roentgen b75cab
			num_fill = (static_cast<uint64>(origin)) + off - os->tellp();</uint64>
roentgen b75cab
			for( uint64 i = 0; i < num_fill; i++ )
roentgen b75cab
				os->put('\0');
roentgen b75cab
roentgen b75cab
			// retry the seek
roentgen b75cab
			os->seekp(static_cast<ios::off_type>(static_cast<uint64>(origin) + off), ios::beg);</uint64></ios::off_type>
roentgen b75cab
		}
roentgen b75cab
	}
roentgen b75cab
roentgen b75cab
	return static_cast<uint64>(os->tellp());</uint64>
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static uint64
roentgen b75cab
_tiffisSeekProc(thandle_t fd, uint64 off, int whence)
roentgen b75cab
{
roentgen b75cab
	tiffis_data	*data = reinterpret_cast<tiffis_data *="">(fd);</tiffis_data>
roentgen b75cab
roentgen b75cab
	switch(whence) {
roentgen b75cab
	case SEEK_SET:
roentgen b75cab
		{
roentgen b75cab
			// Compute 64-bit offset
roentgen b75cab
			uint64 new_offset = static_cast<uint64>(data->start_pos) + off;</uint64>
roentgen b75cab
			
roentgen b75cab
			// Verify that value does not overflow
roentgen b75cab
			ios::off_type offset = static_cast<ios::off_type>(new_offset);</ios::off_type>
roentgen b75cab
			if (static_cast<uint64>(offset) != new_offset)</uint64>
roentgen b75cab
				return static_cast<uint64>(-1);</uint64>
roentgen b75cab
roentgen b75cab
			data->stream->seekg(offset, ios::beg);
roentgen b75cab
			break;
roentgen b75cab
		}
roentgen b75cab
	case SEEK_CUR:
roentgen b75cab
		{
roentgen b75cab
			// Verify that value does not overflow
roentgen b75cab
			ios::off_type offset = static_cast<ios::off_type>(off);</ios::off_type>
roentgen b75cab
			if (static_cast<uint64>(offset) != off)</uint64>
roentgen b75cab
				return static_cast<uint64>(-1);</uint64>
roentgen b75cab
roentgen b75cab
			data->stream->seekg(offset, ios::cur);
roentgen b75cab
			break;
roentgen b75cab
		}
roentgen b75cab
	case SEEK_END:
roentgen b75cab
		{
roentgen b75cab
			// Verify that value does not overflow
roentgen b75cab
			ios::off_type offset = static_cast<ios::off_type>(off);</ios::off_type>
roentgen b75cab
			if (static_cast<uint64>(offset) != off)</uint64>
roentgen b75cab
				return static_cast<uint64>(-1);</uint64>
roentgen b75cab
roentgen b75cab
			data->stream->seekg(offset, ios::end);
roentgen b75cab
			break;
roentgen b75cab
		}
roentgen b75cab
	}
roentgen b75cab
roentgen b75cab
	return (uint64) (data->stream->tellg() - data->start_pos);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static uint64
roentgen b75cab
_tiffosSizeProc(thandle_t fd)
roentgen b75cab
{
roentgen b75cab
	tiffos_data	*data = reinterpret_cast<tiffos_data *="">(fd);</tiffos_data>
roentgen b75cab
	ostream		*os = data->stream;
roentgen b75cab
	ios::pos_type	pos = os->tellp();
roentgen b75cab
	ios::pos_type	len;
roentgen b75cab
roentgen b75cab
	os->seekp(0, ios::end);
roentgen b75cab
	len = os->tellp();
roentgen b75cab
	os->seekp(pos);
roentgen b75cab
roentgen b75cab
	return (uint64) len;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static uint64
roentgen b75cab
_tiffisSizeProc(thandle_t fd)
roentgen b75cab
{
roentgen b75cab
	tiffis_data	*data = reinterpret_cast<tiffis_data *="">(fd);</tiffis_data>
roentgen b75cab
	ios::pos_type	pos = data->stream->tellg();
roentgen b75cab
	ios::pos_type	len;
roentgen b75cab
roentgen b75cab
	data->stream->seekg(0, ios::end);
roentgen b75cab
	len = data->stream->tellg();
roentgen b75cab
	data->stream->seekg(pos);
roentgen b75cab
roentgen b75cab
	return (uint64) len;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static int
roentgen b75cab
_tiffosCloseProc(thandle_t fd)
roentgen b75cab
{
roentgen b75cab
	// Our stream was not allocated by us, so it shouldn't be closed by us.
roentgen b75cab
	delete reinterpret_cast<tiffos_data *="">(fd);</tiffos_data>
roentgen b75cab
	return 0;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static int
roentgen b75cab
_tiffisCloseProc(thandle_t fd)
roentgen b75cab
{
roentgen b75cab
	// Our stream was not allocated by us, so it shouldn't be closed by us.
roentgen b75cab
	delete reinterpret_cast<tiffis_data *="">(fd);</tiffis_data>
roentgen b75cab
	return 0;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static int
roentgen b75cab
_tiffDummyMapProc(thandle_t , void** base, toff_t* size )
roentgen b75cab
{
roentgen b75cab
	return (0);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static void
roentgen b75cab
_tiffDummyUnmapProc(thandle_t , void* base, toff_t size )
roentgen b75cab
{
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * Open a TIFF file descriptor for read/writing.
roentgen b75cab
 */
roentgen b75cab
static TIFF*
roentgen b75cab
_tiffStreamOpen(const char* name, const char* mode, void *fd)
roentgen b75cab
{
roentgen b75cab
	TIFF*	tif;
roentgen b75cab
roentgen b75cab
	if( strchr(mode, 'w') ) {
roentgen b75cab
		tiffos_data	*data = new tiffos_data;
roentgen b75cab
		data->stream = reinterpret_cast<ostream *="">(fd);</ostream>
roentgen b75cab
		data->start_pos = data->stream->tellp();
roentgen b75cab
roentgen b75cab
		// Open for writing.
roentgen b75cab
		tif = TIFFClientOpen(name, mode,
roentgen b75cab
				reinterpret_cast<thandle_t>(data),</thandle_t>
roentgen b75cab
				_tiffosReadProc,
roentgen b75cab
                                _tiffosWriteProc,
roentgen b75cab
				_tiffosSeekProc,
roentgen b75cab
                                _tiffosCloseProc,
roentgen b75cab
				_tiffosSizeProc,
roentgen b75cab
				_tiffDummyMapProc,
roentgen b75cab
                                _tiffDummyUnmapProc);
roentgen b75cab
	} else {
roentgen b75cab
		tiffis_data	*data = new tiffis_data;
roentgen b75cab
		data->stream = reinterpret_cast<istream *="">(fd);</istream>
roentgen b75cab
		data->start_pos = data->stream->tellg();
roentgen b75cab
		// Open for reading.
roentgen b75cab
		tif = TIFFClientOpen(name, mode,
roentgen b75cab
				reinterpret_cast<thandle_t>(data),</thandle_t>
roentgen b75cab
				_tiffisReadProc,
roentgen b75cab
                                _tiffisWriteProc,
roentgen b75cab
				_tiffisSeekProc,
roentgen b75cab
                                _tiffisCloseProc,
roentgen b75cab
				_tiffisSizeProc,
roentgen b75cab
				_tiffDummyMapProc,
roentgen b75cab
                                _tiffDummyUnmapProc);
roentgen b75cab
	}
roentgen b75cab
roentgen b75cab
	return (tif);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
} /* extern "C" */
roentgen b75cab
roentgen b75cab
TIFF*
roentgen b75cab
TIFFStreamOpen(const char* name, ostream *os)
roentgen b75cab
{
roentgen b75cab
	// If os is either a ostrstream or ostringstream, and has no data
roentgen b75cab
	// written to it yet, then tellp() will return -1 which will break us.
roentgen b75cab
	// We workaround this by writing out a dummy character and
roentgen b75cab
	// then seek back to the beginning.
roentgen b75cab
	if( !os->fail() && static_cast<int>(os->tellp()) < 0 ) {</int>
roentgen b75cab
		*os << '\0';
roentgen b75cab
		os->seekp(0);
roentgen b75cab
	}
roentgen b75cab
roentgen b75cab
	// NB: We don't support mapped files with streams so add 'm'
roentgen b75cab
	return _tiffStreamOpen(name, "wm", os);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
TIFF*
roentgen b75cab
TIFFStreamOpen(const char* name, istream *is)
roentgen b75cab
{
roentgen b75cab
	// NB: We don't support mapped files with streams so add 'm'
roentgen b75cab
	return _tiffStreamOpen(name, "rm", is);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
/* vim: set ts=8 sts=8 sw=8 noet: */
roentgen b75cab
/*
roentgen b75cab
  Local Variables:
roentgen b75cab
  mode: c
roentgen b75cab
  indent-tabs-mode: true
roentgen b75cab
  c-basic-offset: 8
roentgen b75cab
  End:
roentgen b75cab
*/