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 111 _tiffosReadProc(thandle_t, void*, tmsize_t) 112 { 113 return 0; 114 } 115 116 static tmsize_t 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 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 149 _tiffisWriteProc(thandle_t, void*, tmsize_t) 150 { 151 return 0; 152 } 153 154 static uint64 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 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 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 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 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 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 342 _tiffDummyMapProc(thandle_t , void** base, toff_t* size ) 343 { 344 return (0); 345 } 346 347 static void 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* 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* 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* 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