Home | History | Annotate | Download | only in libtiff
      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