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
_tiffosReadProc(thandle_t,void *,tmsize_t)108 _tiffosReadProc(thandle_t, void*, tmsize_t)
109 {
110 return 0;
111 }
112
113 static tmsize_t
_tiffisReadProc(thandle_t fd,void * buf,tmsize_t size)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
_tiffosWriteProc(thandle_t fd,void * buf,tmsize_t size)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
_tiffisWriteProc(thandle_t,void *,tmsize_t)146 _tiffisWriteProc(thandle_t, void*, tmsize_t)
147 {
148 return 0;
149 }
150
151 static uint64
_tiffosSeekProc(thandle_t fd,uint64 off,int whence)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
_tiffisSeekProc(thandle_t fd,uint64 off,int whence)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
_tiffosSizeProc(thandle_t fd)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
_tiffisSizeProc(thandle_t fd)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
_tiffosCloseProc(thandle_t fd)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
_tiffisCloseProc(thandle_t fd)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
_tiffDummyMapProc(thandle_t,void ** base,toff_t * size)339 _tiffDummyMapProc(thandle_t , void** base, toff_t* size )
340 {
341 (void) base;
342 (void) size;
343 return (0);
344 }
345
346 static void
_tiffDummyUnmapProc(thandle_t,void * base,toff_t size)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*
_tiffStreamOpen(const char * name,const char * mode,void * fd)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*
TIFFStreamOpen(const char * name,ostream * os)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*
TIFFStreamOpen(const char * name,istream * is)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