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