xref: /reactos/dll/3rdparty/libtiff/tif_stream.cxx (revision 23373acb)
1 /*
2  * Copyright (c) 1988-1996 Sam Leffler
3  * Copyright (c) 1991-1996 Silicon Graphics, Inc.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and
6  * its documentation for any purpose is hereby granted without fee, provided
7  * that (i) the above copyright notices and this permission notice appear in
8  * all copies of the software and related documentation, and (ii) the names of
9  * Sam Leffler and Silicon Graphics may not be used in any advertising or
10  * publicity relating to the software without the specific, prior written
11  * permission of Sam Leffler and Silicon Graphics.
12  *
13  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
14  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
15  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
18  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
19  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
21  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22  * OF THIS SOFTWARE.
23  */
24 
25 /*
26  * TIFF Library UNIX-specific Routines.
27  */
28 #include "tiffiop.h"
29 #include <iostream>
30 
31 #ifndef __VMS
32 using namespace std;
33 #endif
34 
35 /*
36   ISO C++ uses a 'std::streamsize' type to define counts.  This makes
37   it similar to, (but perhaps not the same as) size_t.
38 
39   The std::ios::pos_type is used to represent stream positions as used
40   by tellg(), tellp(), seekg(), and seekp().  This makes it similar to
41   (but perhaps not the same as) 'off_t'.  The std::ios::streampos type
42   is used for character streams, but is documented to not be an
43   integral type anymore, so it should *not* be assigned to an integral
44   type.
45 
46   The std::ios::off_type is used to specify relative offsets needed by
47   the variants of seekg() and seekp() which accept a relative offset
48   argument.
49 
50   Useful prototype knowledge:
51 
52   Obtain read position
53     ios::pos_type basic_istream::tellg()
54 
55   Set read position
56     basic_istream& basic_istream::seekg(ios::pos_type)
57     basic_istream& basic_istream::seekg(ios::off_type, ios_base::seekdir)
58 
59   Read data
60     basic_istream& istream::read(char *str, streamsize count)
61 
62   Number of characters read in last unformatted read
63     streamsize istream::gcount();
64 
65   Obtain write position
66     ios::pos_type basic_ostream::tellp()
67 
68   Set write position
69     basic_ostream& basic_ostream::seekp(ios::pos_type)
70     basic_ostream& basic_ostream::seekp(ios::off_type, ios_base::seekdir)
71 
72   Write data
73     basic_ostream& ostream::write(const char *str, streamsize count)
74 */
75 
76 struct tiffis_data;
77 struct tiffos_data;
78 
79 extern "C" {
80 
81 	static tmsize_t _tiffosReadProc(thandle_t, void*, tmsize_t);
82 	static tmsize_t _tiffisReadProc(thandle_t fd, void* buf, tmsize_t size);
83 	static tmsize_t _tiffosWriteProc(thandle_t fd, void* buf, tmsize_t size);
84 	static tmsize_t _tiffisWriteProc(thandle_t, void*, tmsize_t);
85 	static uint64   _tiffosSeekProc(thandle_t fd, uint64 off, int whence);
86 	static uint64   _tiffisSeekProc(thandle_t fd, uint64 off, int whence);
87 	static uint64   _tiffosSizeProc(thandle_t fd);
88 	static uint64   _tiffisSizeProc(thandle_t fd);
89 	static int      _tiffosCloseProc(thandle_t fd);
90 	static int      _tiffisCloseProc(thandle_t fd);
91 	static int 	_tiffDummyMapProc(thandle_t , void** base, toff_t* size );
92 	static void     _tiffDummyUnmapProc(thandle_t , void* base, toff_t size );
93 	static TIFF*    _tiffStreamOpen(const char* name, const char* mode, void *fd);
94 
95 struct tiffis_data
96 {
97 	istream	*stream;
98         ios::pos_type start_pos;
99 };
100 
101 struct tiffos_data
102 {
103 	ostream	*stream;
104 	ios::pos_type start_pos;
105 };
106 
107 static tmsize_t
108 _tiffosReadProc(thandle_t, void*, tmsize_t)
109 {
110         return 0;
111 }
112 
113 static tmsize_t
114 _tiffisReadProc(thandle_t fd, void* buf, tmsize_t size)
115 {
116         tiffis_data	*data = reinterpret_cast<tiffis_data *>(fd);
117 
118         // Verify that type does not overflow.
119         streamsize request_size = size;
120         if (static_cast<tmsize_t>(request_size) != size)
121           return static_cast<tmsize_t>(-1);
122 
123         data->stream->read((char *) buf, request_size);
124 
125         return static_cast<tmsize_t>(data->stream->gcount());
126 }
127 
128 static tmsize_t
129 _tiffosWriteProc(thandle_t fd, void* buf, tmsize_t size)
130 {
131 	tiffos_data	*data = reinterpret_cast<tiffos_data *>(fd);
132 	ostream		*os = data->stream;
133 	ios::pos_type	pos = os->tellp();
134 
135         // Verify that type does not overflow.
136         streamsize request_size = size;
137         if (static_cast<tmsize_t>(request_size) != size)
138           return static_cast<tmsize_t>(-1);
139 
140 	os->write(reinterpret_cast<const char *>(buf), request_size);
141 
142 	return static_cast<tmsize_t>(os->tellp() - pos);
143 }
144 
145 static tmsize_t
146 _tiffisWriteProc(thandle_t, void*, tmsize_t)
147 {
148 	return 0;
149 }
150 
151 static uint64
152 _tiffosSeekProc(thandle_t fd, uint64 off, int whence)
153 {
154 	tiffos_data	*data = reinterpret_cast<tiffos_data *>(fd);
155 	ostream		*os = data->stream;
156 
157 	// if the stream has already failed, don't do anything
158 	if( os->fail() )
159 		return static_cast<uint64>(-1);
160 
161 	switch(whence) {
162 	case SEEK_SET:
163 		{
164 			// Compute 64-bit offset
165 			uint64 new_offset = static_cast<uint64>(data->start_pos) + off;
166 
167 			// Verify that value does not overflow
168 			ios::off_type offset = static_cast<ios::off_type>(new_offset);
169 			if (static_cast<uint64>(offset) != new_offset)
170 				return static_cast<uint64>(-1);
171 
172 			os->seekp(offset, ios::beg);
173 		break;
174 		}
175 	case SEEK_CUR:
176 		{
177 			// Verify that value does not overflow
178 			ios::off_type offset = static_cast<ios::off_type>(off);
179 			if (static_cast<uint64>(offset) != off)
180 				return static_cast<uint64>(-1);
181 
182 			os->seekp(offset, ios::cur);
183 			break;
184 		}
185 	case SEEK_END:
186 		{
187 			// Verify that value does not overflow
188 			ios::off_type offset = static_cast<ios::off_type>(off);
189 			if (static_cast<uint64>(offset) != off)
190 				return static_cast<uint64>(-1);
191 
192 			os->seekp(offset, ios::end);
193 			break;
194 		}
195 	}
196 
197 	// Attempt to workaround problems with seeking past the end of the
198 	// stream.  ofstream doesn't have a problem with this but
199 	// ostrstream/ostringstream does. In that situation, add intermediate
200 	// '\0' characters.
201 	if( os->fail() ) {
202 #ifdef __VMS
203 		int		old_state;
204 #else
205 		ios::iostate	old_state;
206 #endif
207 		ios::pos_type	origin;
208 
209 		old_state = os->rdstate();
210 		// reset the fail bit or else tellp() won't work below
211 		os->clear(os->rdstate() & ~ios::failbit);
212 		switch( whence ) {
213 			case SEEK_SET:
214                         default:
215 				origin = data->start_pos;
216 				break;
217 			case SEEK_CUR:
218 				origin = os->tellp();
219 				break;
220 			case SEEK_END:
221 				os->seekp(0, ios::end);
222 				origin = os->tellp();
223 				break;
224 		}
225 		// restore original stream state
226 		os->clear(old_state);
227 
228 		// only do something if desired seek position is valid
229 		if( (static_cast<uint64>(origin) + off) > static_cast<uint64>(data->start_pos) ) {
230 			uint64	num_fill;
231 
232 			// clear the fail bit
233 			os->clear(os->rdstate() & ~ios::failbit);
234 
235 			// extend the stream to the expected size
236 			os->seekp(0, ios::end);
237 			num_fill = (static_cast<uint64>(origin)) + off - os->tellp();
238 			for( uint64 i = 0; i < num_fill; i++ )
239 				os->put('\0');
240 
241 			// retry the seek
242 			os->seekp(static_cast<ios::off_type>(static_cast<uint64>(origin) + off), ios::beg);
243 		}
244 	}
245 
246 	return static_cast<uint64>(os->tellp());
247 }
248 
249 static uint64
250 _tiffisSeekProc(thandle_t fd, uint64 off, int whence)
251 {
252 	tiffis_data	*data = reinterpret_cast<tiffis_data *>(fd);
253 
254 	switch(whence) {
255 	case SEEK_SET:
256 		{
257 			// Compute 64-bit offset
258 			uint64 new_offset = static_cast<uint64>(data->start_pos) + off;
259 
260 			// Verify that value does not overflow
261 			ios::off_type offset = static_cast<ios::off_type>(new_offset);
262 			if (static_cast<uint64>(offset) != new_offset)
263 				return static_cast<uint64>(-1);
264 
265 			data->stream->seekg(offset, ios::beg);
266 			break;
267 		}
268 	case SEEK_CUR:
269 		{
270 			// Verify that value does not overflow
271 			ios::off_type offset = static_cast<ios::off_type>(off);
272 			if (static_cast<uint64>(offset) != off)
273 				return static_cast<uint64>(-1);
274 
275 			data->stream->seekg(offset, ios::cur);
276 			break;
277 		}
278 	case SEEK_END:
279 		{
280 			// Verify that value does not overflow
281 			ios::off_type offset = static_cast<ios::off_type>(off);
282 			if (static_cast<uint64>(offset) != off)
283 				return static_cast<uint64>(-1);
284 
285 			data->stream->seekg(offset, ios::end);
286 			break;
287 		}
288 	}
289 
290 	return (uint64) (data->stream->tellg() - data->start_pos);
291 }
292 
293 static uint64
294 _tiffosSizeProc(thandle_t fd)
295 {
296 	tiffos_data	*data = reinterpret_cast<tiffos_data *>(fd);
297 	ostream		*os = data->stream;
298 	ios::pos_type	pos = os->tellp();
299 	ios::pos_type	len;
300 
301 	os->seekp(0, ios::end);
302 	len = os->tellp();
303 	os->seekp(pos);
304 
305 	return (uint64) len;
306 }
307 
308 static uint64
309 _tiffisSizeProc(thandle_t fd)
310 {
311 	tiffis_data	*data = reinterpret_cast<tiffis_data *>(fd);
312 	ios::pos_type	pos = data->stream->tellg();
313 	ios::pos_type	len;
314 
315 	data->stream->seekg(0, ios::end);
316 	len = data->stream->tellg();
317 	data->stream->seekg(pos);
318 
319 	return (uint64) len;
320 }
321 
322 static int
323 _tiffosCloseProc(thandle_t fd)
324 {
325 	// Our stream was not allocated by us, so it shouldn't be closed by us.
326 	delete reinterpret_cast<tiffos_data *>(fd);
327 	return 0;
328 }
329 
330 static int
331 _tiffisCloseProc(thandle_t fd)
332 {
333 	// Our stream was not allocated by us, so it shouldn't be closed by us.
334 	delete reinterpret_cast<tiffis_data *>(fd);
335 	return 0;
336 }
337 
338 static int
339 _tiffDummyMapProc(thandle_t , void** base, toff_t* size )
340 {
341 	(void) base;
342 	(void) size;
343 	return (0);
344 }
345 
346 static void
347 _tiffDummyUnmapProc(thandle_t , void* base, toff_t size )
348 {
349 	(void) base;
350 	(void) size;
351 }
352 
353 /*
354  * Open a TIFF file descriptor for read/writing.
355  */
356 static TIFF*
357 _tiffStreamOpen(const char* name, const char* mode, void *fd)
358 {
359 	TIFF*	tif;
360 
361 	if( strchr(mode, 'w') ) {
362 		tiffos_data	*data = new tiffos_data;
363 		data->stream = reinterpret_cast<ostream *>(fd);
364 		data->start_pos = data->stream->tellp();
365 
366 		// Open for writing.
367 		tif = TIFFClientOpen(name, mode,
368 				reinterpret_cast<thandle_t>(data),
369 				_tiffosReadProc,
370                                 _tiffosWriteProc,
371 				_tiffosSeekProc,
372                                 _tiffosCloseProc,
373 				_tiffosSizeProc,
374 				_tiffDummyMapProc,
375                                 _tiffDummyUnmapProc);
376 		if (!tif) {
377 			delete data;
378 		}
379 	} else {
380 		tiffis_data	*data = new tiffis_data;
381 		data->stream = reinterpret_cast<istream *>(fd);
382 		data->start_pos = data->stream->tellg();
383 		// Open for reading.
384 		tif = TIFFClientOpen(name, mode,
385 				reinterpret_cast<thandle_t>(data),
386 				_tiffisReadProc,
387                                 _tiffisWriteProc,
388 				_tiffisSeekProc,
389                                 _tiffisCloseProc,
390 				_tiffisSizeProc,
391 				_tiffDummyMapProc,
392                                 _tiffDummyUnmapProc);
393 		if (!tif) {
394 			delete data;
395 		}
396 	}
397 
398 	return (tif);
399 }
400 
401 } /* extern "C" */
402 
403 TIFF*
404 TIFFStreamOpen(const char* name, ostream *os)
405 {
406 	// If os is either a ostrstream or ostringstream, and has no data
407 	// written to it yet, then tellp() will return -1 which will break us.
408 	// We workaround this by writing out a dummy character and
409 	// then seek back to the beginning.
410 	if( !os->fail() && static_cast<int>(os->tellp()) < 0 ) {
411 		*os << '\0';
412 		os->seekp(0);
413 	}
414 
415 	// NB: We don't support mapped files with streams so add 'm'
416 	return _tiffStreamOpen(name, "wm", os);
417 }
418 
419 TIFF*
420 TIFFStreamOpen(const char* name, istream *is)
421 {
422 	// NB: We don't support mapped files with streams so add 'm'
423 	return _tiffStreamOpen(name, "rm", is);
424 }
425 
426 /* vim: set ts=8 sts=8 sw=8 noet: */
427 /*
428  * Local Variables:
429  * mode: c
430  * c-basic-offset: 8
431  * fill-column: 78
432  * End:
433  */
434 
435