1/* $Id: tif_stream.cxx,v 1.6.2.1 2009-01-01 00:10:43 bfriesen Exp $ */
2
3/*
4 * Copyright (c) 1988-1996 Sam Leffler
5 * Copyright (c) 1991-1996 Silicon Graphics, Inc.
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and
8 * its documentation for any purpose is hereby granted without fee, provided
9 * that (i) the above copyright notices and this permission notice appear in
10 * all copies of the software and related documentation, and (ii) the names of
11 * Sam Leffler and Silicon Graphics may not be used in any advertising or
12 * publicity relating to the software without the specific, prior written
13 * permission of Sam Leffler and Silicon Graphics.
14 *
15 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
17 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
20 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
21 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
23 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24 * OF THIS SOFTWARE.
25 */
26
27/*
28 * TIFF Library UNIX-specific Routines.
29 */
30#include "tiffiop.h"
31#include <iostream>
32
33#ifndef __VMS
34using namespace std;
35#endif
36
37class tiffis_data
38{
39  public:
40
41	istream	*myIS;
42        long	myStreamStartPos;
43};
44
45class tiffos_data
46{
47  public:
48
49	ostream	*myOS;
50	long	myStreamStartPos;
51};
52
53static tsize_t
54_tiffosReadProc(thandle_t, tdata_t, tsize_t)
55{
56        return 0;
57}
58
59static tsize_t
60_tiffisReadProc(thandle_t fd, tdata_t buf, tsize_t size)
61{
62        tiffis_data	*data = (tiffis_data *)fd;
63
64        data->myIS->read((char *)buf, (int)size);
65
66        return data->myIS->gcount();
67}
68
69static tsize_t
70_tiffosWriteProc(thandle_t fd, tdata_t buf, tsize_t size)
71{
72	tiffos_data	*data = (tiffos_data *)fd;
73	ostream		*os = data->myOS;
74	int		pos = os->tellp();
75
76	os->write((const char *)buf, size);
77
78	return ((int)os->tellp()) - pos;
79}
80
81static tsize_t
82_tiffisWriteProc(thandle_t, tdata_t, tsize_t)
83{
84	return 0;
85}
86
87static toff_t
88_tiffosSeekProc(thandle_t fd, toff_t off, int whence)
89{
90	tiffos_data	*data = (tiffos_data *)fd;
91	ostream	*os = data->myOS;
92
93	// if the stream has already failed, don't do anything
94	if( os->fail() )
95		return os->tellp();
96
97	switch(whence) {
98	case SEEK_SET:
99	    os->seekp(data->myStreamStartPos + off, ios::beg);
100		break;
101	case SEEK_CUR:
102		os->seekp(off, ios::cur);
103		break;
104	case SEEK_END:
105		os->seekp(off, ios::end);
106		break;
107	}
108
109	// Attempt to workaround problems with seeking past the end of the
110	// stream.  ofstream doesn't have a problem with this but
111	// ostrstream/ostringstream does. In that situation, add intermediate
112	// '\0' characters.
113	if( os->fail() ) {
114#ifdef __VMS
115		int		old_state;
116#else
117		ios::iostate	old_state;
118#endif
119		toff_t		origin=0;
120
121		old_state = os->rdstate();
122		// reset the fail bit or else tellp() won't work below
123		os->clear(os->rdstate() & ~ios::failbit);
124		switch( whence ) {
125			case SEEK_SET:
126				origin = data->myStreamStartPos;
127				break;
128			case SEEK_CUR:
129				origin = os->tellp();
130				break;
131			case SEEK_END:
132				os->seekp(0, ios::end);
133				origin = os->tellp();
134				break;
135		}
136		// restore original stream state
137		os->clear(old_state);
138
139		// only do something if desired seek position is valid
140		if( origin + off > data->myStreamStartPos ) {
141			toff_t	num_fill;
142
143			// clear the fail bit
144			os->clear(os->rdstate() & ~ios::failbit);
145
146			// extend the stream to the expected size
147			os->seekp(0, ios::end);
148			num_fill = origin + off - (toff_t)os->tellp();
149			for( toff_t i = 0; i < num_fill; i++ )
150				os->put('\0');
151
152			// retry the seek
153			os->seekp(origin + off, ios::beg);
154		}
155	}
156
157	return os->tellp();
158}
159
160static toff_t
161_tiffisSeekProc(thandle_t fd, toff_t off, int whence)
162{
163	tiffis_data	*data = (tiffis_data *)fd;
164
165	switch(whence) {
166	case SEEK_SET:
167		data->myIS->seekg(data->myStreamStartPos + off, ios::beg);
168		break;
169	case SEEK_CUR:
170		data->myIS->seekg(off, ios::cur);
171		break;
172	case SEEK_END:
173		data->myIS->seekg(off, ios::end);
174		break;
175	}
176
177	return ((long)data->myIS->tellg()) - data->myStreamStartPos;
178}
179
180static toff_t
181_tiffosSizeProc(thandle_t fd)
182{
183	tiffos_data	*data = (tiffos_data *)fd;
184	ostream		*os = data->myOS;
185	toff_t		pos = os->tellp();
186	toff_t		len;
187
188	os->seekp(0, ios::end);
189	len = os->tellp();
190	os->seekp(pos);
191
192	return len;
193}
194
195static toff_t
196_tiffisSizeProc(thandle_t fd)
197{
198	tiffis_data	*data = (tiffis_data *)fd;
199	int		pos = data->myIS->tellg();
200	int		len;
201
202	data->myIS->seekg(0, ios::end);
203	len = data->myIS->tellg();
204	data->myIS->seekg(pos);
205
206	return len;
207}
208
209static int
210_tiffosCloseProc(thandle_t fd)
211{
212	// Our stream was not allocated by us, so it shouldn't be closed by us.
213	delete (tiffos_data *)fd;
214	return 0;
215}
216
217static int
218_tiffisCloseProc(thandle_t fd)
219{
220	// Our stream was not allocated by us, so it shouldn't be closed by us.
221	delete (tiffis_data *)fd;
222	return 0;
223}
224
225static int
226_tiffDummyMapProc(thandle_t , tdata_t* , toff_t* )
227{
228	return (0);
229}
230
231static void
232_tiffDummyUnmapProc(thandle_t , tdata_t , toff_t )
233{
234}
235
236/*
237 * Open a TIFF file descriptor for read/writing.
238 */
239static TIFF*
240_tiffStreamOpen(const char* name, const char* mode, void *fd)
241{
242	TIFF*	tif;
243
244	if( strchr(mode, 'w') ) {
245		tiffos_data	*data = new tiffos_data;
246		data->myOS = (ostream *)fd;
247		data->myStreamStartPos = data->myOS->tellp();
248
249		// Open for writing.
250		tif = TIFFClientOpen(name, mode,
251				(thandle_t) data,
252				_tiffosReadProc, _tiffosWriteProc,
253				_tiffosSeekProc, _tiffosCloseProc,
254				_tiffosSizeProc,
255				_tiffDummyMapProc, _tiffDummyUnmapProc);
256	} else {
257		tiffis_data	*data = new tiffis_data;
258		data->myIS = (istream *)fd;
259		data->myStreamStartPos = data->myIS->tellg();
260		// Open for reading.
261		tif = TIFFClientOpen(name, mode,
262				(thandle_t) data,
263				_tiffisReadProc, _tiffisWriteProc,
264				_tiffisSeekProc, _tiffisCloseProc,
265				_tiffisSizeProc,
266				_tiffDummyMapProc, _tiffDummyUnmapProc);
267	}
268
269	return (tif);
270}
271
272TIFF*
273TIFFStreamOpen(const char* name, ostream *os)
274{
275	// If os is either a ostrstream or ostringstream, and has no data
276	// written to it yet, then tellp() will return -1 which will break us.
277	// We workaround this by writing out a dummy character and
278	// then seek back to the beginning.
279	if( !os->fail() && (int)os->tellp() < 0 ) {
280		*os << '\0';
281		os->seekp(0);
282	}
283
284	// NB: We don't support mapped files with streams so add 'm'
285	return _tiffStreamOpen(name, "wm", os);
286}
287
288TIFF*
289TIFFStreamOpen(const char* name, istream *is)
290{
291	// NB: We don't support mapped files with streams so add 'm'
292	return _tiffStreamOpen(name, "rm", is);
293}
294
295/* vim: set ts=8 sts=8 sw=8 noet: */
296