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