1 /* $Id: tif_stream.cxx,v 1.13 2015-05-28 01:50:22 bfriesen 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 (void) base; 344 (void) size; 345 return (0); 346 } 347 348 static void 349 _tiffDummyUnmapProc(thandle_t , void* base, toff_t size ) 350 { 351 (void) base; 352 (void) size; 353 } 354 355 /* 356 * Open a TIFF file descriptor for read/writing. 357 */ 358 static TIFF* 359 _tiffStreamOpen(const char* name, const char* mode, void *fd) 360 { 361 TIFF* tif; 362 363 if( strchr(mode, 'w') ) { 364 tiffos_data *data = new tiffos_data; 365 data->stream = reinterpret_cast<ostream *>(fd); 366 data->start_pos = data->stream->tellp(); 367 368 // Open for writing. 369 tif = TIFFClientOpen(name, mode, 370 reinterpret_cast<thandle_t>(data), 371 _tiffosReadProc, 372 _tiffosWriteProc, 373 _tiffosSeekProc, 374 _tiffosCloseProc, 375 _tiffosSizeProc, 376 _tiffDummyMapProc, 377 _tiffDummyUnmapProc); 378 } else { 379 tiffis_data *data = new tiffis_data; 380 data->stream = reinterpret_cast<istream *>(fd); 381 data->start_pos = data->stream->tellg(); 382 // Open for reading. 383 tif = TIFFClientOpen(name, mode, 384 reinterpret_cast<thandle_t>(data), 385 _tiffisReadProc, 386 _tiffisWriteProc, 387 _tiffisSeekProc, 388 _tiffisCloseProc, 389 _tiffisSizeProc, 390 _tiffDummyMapProc, 391 _tiffDummyUnmapProc); 392 } 393 394 return (tif); 395 } 396 397 } /* extern "C" */ 398 399 TIFF* 400 TIFFStreamOpen(const char* name, ostream *os) 401 { 402 // If os is either a ostrstream or ostringstream, and has no data 403 // written to it yet, then tellp() will return -1 which will break us. 404 // We workaround this by writing out a dummy character and 405 // then seek back to the beginning. 406 if( !os->fail() && static_cast<int>(os->tellp()) < 0 ) { 407 *os << '\0'; 408 os->seekp(0); 409 } 410 411 // NB: We don't support mapped files with streams so add 'm' 412 return _tiffStreamOpen(name, "wm", os); 413 } 414 415 TIFF* 416 TIFFStreamOpen(const char* name, istream *is) 417 { 418 // NB: We don't support mapped files with streams so add 'm' 419 return _tiffStreamOpen(name, "rm", is); 420 } 421 422 /* vim: set ts=8 sts=8 sw=8 noet: */ 423 /* 424 * Local Variables: 425 * mode: c 426 * c-basic-offset: 8 427 * fill-column: 78 428 * End: 429 */ 430 431