LCOV - code coverage report
Current view: top level - src - zipoutputstreambuf.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 86 86 100.0 %
Date: 2015-04-12 Functions: 14 15 93.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   Zipios++ - a small C++ library that provides easy access to .zip files.
       3             : 
       4             :   Copyright (C) 2000-2007  Thomas Sondergaard
       5             :   Copyright (C) 2015  Made to Order Software Corporation
       6             : 
       7             :   This library is free software; you can redistribute it and/or
       8             :   modify it under the terms of the GNU Lesser General Public
       9             :   License as published by the Free Software Foundation; either
      10             :   version 2 of the License, or (at your option) any later version.
      11             : 
      12             :   This library is distributed in the hope that it will be useful,
      13             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :   Lesser General Public License for more details.
      16             : 
      17             :   You should have received a copy of the GNU Lesser General Public
      18             :   License along with this library; if not, write to the Free Software
      19             :   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
      20             : */
      21             : 
      22             : /** \file
      23             :  * \brief Implementation of the zipios::ZipOutputStreambuf class.
      24             :  *
      25             :  * This file includes the functions necessary to write data to a Zip
      26             :  * archive.
      27             :  */
      28             : 
      29             : #include "zipoutputstreambuf.hpp"
      30             : 
      31             : #include "zipios++/zipiosexceptions.hpp"
      32             : 
      33             : #include "ziplocalentry.hpp"
      34             : #include "zipendofcentraldirectory.hpp"
      35             : 
      36             : 
      37             : namespace zipios
      38             : {
      39             : 
      40             : 
      41             : namespace
      42             : {
      43             : 
      44             : 
      45             : /** \brief Help function used to write the central directory.
      46             :  *
      47             :  * When you create a Zip archive, it includes a central directory where
      48             :  * all the meta data about each file is saved. This function saves an
      49             :  * array of entries in an output stream to generate the Zip file
      50             :  * central directory.
      51             :  *
      52             :  * \param[in] os  The output stream.
      53             :  * \param[in] entries  The array of entries to save in this central directory.
      54             :  * \param[in] comment  The zip archive global comment.
      55             :  */
      56         256 : void writeZipCentralDirectory(std::ostream &os, FileEntry::vector_t& entries, std::string const& comment)
      57             : {
      58         256 :     ZipEndOfCentralDirectory eocd(comment);
      59         256 :     eocd.setOffset(os.tellp());  // start position
      60         256 :     eocd.setCount(entries.size());
      61             : 
      62         256 :     size_t central_directory_size(0);
      63      133320 :     for(auto it = entries.begin(); it != entries.end(); ++it)
      64             :     {
      65      133066 :         (*it)->write(os);
      66      133064 :         central_directory_size += (*it)->getHeaderSize();
      67             :     }
      68             : 
      69         254 :     eocd.setCentralDirectorySize(central_directory_size);
      70         258 :     eocd.write(os);
      71         252 : }
      72             : 
      73             : 
      74             : } // no name namespace
      75             : 
      76             : 
      77             : /** \class ZipOutputStreambuf
      78             :  * \brief Handle the output buffer of a zip archive.
      79             :  *
      80             :  * The ZipOutputStreambuf class is a zip archive output
      81             :  * streambuf filter.
      82             :  */
      83             : 
      84             : 
      85             : /** \brief Initialize a ZipOutputStreambuf object.
      86             :  *
      87             :  * Note that a new initialized ZipOutputStreambuf is not ready to
      88             :  * accept data, putNextEntry() must be invoked at least once first.
      89             :  *
      90             :  * \param[in] outbuf  The streambuf to use for output.
      91             :  */
      92         256 : ZipOutputStreambuf::ZipOutputStreambuf(std::streambuf * outbuf)
      93         256 :     : DeflateOutputStreambuf(outbuf)
      94             :     //, m_open_entry(false) -- auto-init
      95             :     //, m_open(true) -- auto-init
      96             : {
      97         256 : }
      98             : 
      99             : 
     100             : /** \brief Clean up the buffer.
     101             :  *
     102             :  * This function cleans up this output buffer. In general this ensures
     103             :  * that the data still cached gets flushed.
     104             :  *
     105             :  * \warning
     106             :  * This function may gobble up some important exceptions. If you want
     107             :  * to make sure that the file is properly written, you must call the
     108             :  * finish() function (or the close() function) to fully terminate the
     109             :  * file. If these functions do not fail, then the output file is
     110             :  * considered valid and you can keep it. The finish() function can fail
     111             :  * because of a comment or a file which are too large, for example.
     112             :  */
     113         768 : ZipOutputStreambuf::~ZipOutputStreambuf()
     114             : {
     115             :     // avoid possible exceptions when writing the central directory
     116             :     try
     117             :     {
     118         256 :         finish();
     119             :     }
     120           1 :     catch(...)
     121             :     {
     122             :     }
     123         512 : }
     124             : 
     125             : 
     126             : /** \brief Close this buffer entry.
     127             :  *
     128             :  * Closes the current output buffer entry and positions the stream
     129             :  * write pointer at the beginning of the next entry.
     130             :  */
     131      133577 : void ZipOutputStreambuf::closeEntry()
     132             : {
     133      133577 :     if(!m_open_entry)
     134             :     {
     135      134089 :         return;
     136             :     }
     137             : 
     138      133065 :     switch(m_compression_level)
     139             :     {
     140             :     case FileEntry::COMPRESSION_LEVEL_NONE:
     141       72765 :         overflow(); // flush
     142       72765 :         break;
     143             : 
     144             :     default:
     145       60300 :         closeStream();
     146       60300 :         break;
     147             : 
     148             :     }
     149             : 
     150      133065 :     updateEntryHeaderInfo();
     151      133065 :     setEntryClosedState();
     152             : }
     153             : 
     154             : 
     155             : /** \brief Close the output stream buffer.
     156             :  *
     157             :  * This function calls finish to make sure that any cached
     158             :  * data is saved and then close the stream buffer.
     159             :  */
     160         252 : void ZipOutputStreambuf::close()
     161             : {
     162         252 :     finish();
     163         252 : }
     164             : 
     165             : 
     166             : /** \brief Finish up an output stream buffer.
     167             :  *
     168             :  * Closes the current entry (if one is open), then writes the Zip
     169             :  * Central Directory Structure closing the ZipOutputStream. The
     170             :  * output stream (std::ostream) that the zip archive is being
     171             :  * written to is not closed.
     172             :  */
     173         763 : void ZipOutputStreambuf::finish()
     174             : {
     175         763 :     if(!m_open)
     176             :     {
     177        1266 :         return;
     178             :     }
     179         256 :     m_open = false;
     180             : 
     181         256 :     std::ostream os(m_outbuf);
     182         256 :     closeEntry();
     183         260 :     writeZipCentralDirectory(os, m_entries, m_zip_comment);
     184             : }
     185             : 
     186             : 
     187             : /** \brief Start saving an entry in the output buffer.
     188             :  *
     189             :  * Opens the next entry in the zip archive and returns a const pointer to a
     190             :  * FileEntry object for the entry.
     191             :  *
     192             :  * If a previous entry was still open, the function calls closeEntry()
     193             :  * first.
     194             :  *
     195             :  * \param[in] entry  The entry to be saved and made current.
     196             :  */
     197      133066 : void ZipOutputStreambuf::putNextEntry(FileEntry::pointer_t entry)
     198             : {
     199      133066 :     closeEntry();
     200             : 
     201             :     // if the method is STORED force uncompressed data
     202      133066 :     if(entry->getMethod() == StorageMethod::STORED)
     203             :     {
     204             :         // force to "no compression" when the method is STORED
     205       72062 :         m_compression_level = FileEntry::COMPRESSION_LEVEL_NONE;
     206             :     }
     207             :     else
     208             :     {
     209             :         // get the user defined compression level
     210       61004 :         m_compression_level = entry->getLevel();
     211             :     }
     212      133066 :     m_overflown_bytes = 0;
     213      133066 :     switch(m_compression_level)
     214             :     {
     215             :     case FileEntry::COMPRESSION_LEVEL_NONE:
     216       72766 :         setp(&m_invec[0], &m_invec[0] + getBufferSize());
     217       72766 :         break;
     218             : 
     219             :     default:
     220       60300 :         init(m_compression_level);
     221       60300 :         break;
     222             : 
     223             :     }
     224             : 
     225      133066 :     m_entries.push_back(entry);
     226             : 
     227      133066 :     std::ostream os(m_outbuf);
     228             : 
     229             :     // Update entry header info
     230      133066 :     entry->setEntryOffset(os.tellp());
     231             :     /** \TODO
     232             :      * Rethink the design as we have to force a call to the correct
     233             :      * write() function?
     234             :      */
     235      133066 :     static_cast<ZipLocalEntry *>(entry.get())->ZipLocalEntry::write(os);
     236             : 
     237      133066 :     m_open_entry = true;
     238      133065 : }
     239             : 
     240             : 
     241             : /** \brief Set the archive comment.
     242             :  *
     243             :  * This function saves a global comment for the Zip archive.
     244             :  *
     245             :  * You may set it to an empty string which means that no comment
     246             :  * will be saved.
     247             :  *
     248             :  * The comment is saved when the first entry is saved so it
     249             :  * has to be put in there early on.
     250             :  *
     251             :  * \param[in] comment  The comment to save in the Zip archive.
     252             :  */
     253         256 : void ZipOutputStreambuf::setComment(std::string const& comment)
     254             : {
     255         256 :     m_zip_comment = comment;
     256         256 : }
     257             : 
     258             : 
     259             : //
     260             : // Protected and private methods
     261             : //
     262             : 
     263             : /** \brief Implementation of the overflow() function.
     264             :  *
     265             :  * When writing to a buffer, the overflow() function gets called when
     266             :  * there is no more room in the output buffer. The buffer is expected
     267             :  * to flush the data to disk and reset the buffer availability.
     268             :  *
     269             :  * \param[in] c  The character that made it all happen. Maybe EOF.
     270             :  *
     271             :  * \return EOF if the function fails, 0 otherwise.
     272             :  */
     273      487183 : int ZipOutputStreambuf::overflow(int c)
     274             : {
     275      487183 :     size_t const size(pptr() - pbase());
     276      487183 :     m_overflown_bytes += size;
     277      487183 :     switch(m_compression_level)
     278             :     {
     279             :     case FileEntry::COMPRESSION_LEVEL_NONE:
     280             :     {
     281             :         // Ok, we are STORED, so we handle it ourselves to avoid "side
     282             :         // effects" from zlib, which adds markers every now and then.
     283       77822 :         size_t const bc(m_outbuf->sputn(&m_invec[0], size));
     284       77822 :         if(size != bc)
     285             :         {
     286             :             // Without implementing our own stream in our test, this
     287             :             // cannot really be reached because it is all happening
     288             :             // inside the same loop in ZipFile::saveCollectionToArchive()
     289             :             throw IOException("ZipOutputStreambuf::overflow(): write to buffer failed."); // LCOV_EXCL_LINE
     290             :         }
     291       77822 :         setp(&m_invec[0], &m_invec[0] + getBufferSize());
     292             : 
     293       77822 :         if(c != EOF)
     294             :         {
     295        5057 :             *pptr() = c;
     296        5057 :             pbump(1);
     297             :         }
     298             : 
     299       77822 :         return 0;
     300             :     }
     301             : 
     302             :     default:
     303      409361 :         return DeflateOutputStreambuf::overflow(c);
     304             : 
     305             :     }
     306             : }
     307             : 
     308             : 
     309             : 
     310             : /** \brief Implement the sync() functionality.
     311             :  *
     312             :  * This virtual function is reimplemented to make sure that the system
     313             :  * does not run a default sync() function.
     314             :  *
     315             :  * This function calls the DeflateOutputStreambuf::sync() function which
     316             :  * returns -1 because it will not "synchronize" the input buffer.
     317             :  */
     318             : int ZipOutputStreambuf::sync() // LCOV_EXCL_LINE
     319             : {
     320             :     return DeflateOutputStreambuf::sync(); // LCOV_EXCL_LINE
     321             : }
     322             : 
     323             : 
     324             : 
     325             : /** \brief Mark the current entry as closed.
     326             :  *
     327             :  * After the putNextEntry() call and saving of the file content, the
     328             :  * closeEntry() function can be called to close the entry. The entry
     329             :  * is really closed when this setEntryClosedState() is called.
     330             :  */
     331      133065 : void ZipOutputStreambuf::setEntryClosedState()
     332             : {
     333      133065 :     m_open_entry = false;
     334             : 
     335             :     /** \FIXME
     336             :      * Update put pointers to trigger overflow on write. Overflow
     337             :      * should then return EOF while m_open_entry is false.
     338             :      */
     339      133065 : }
     340             : 
     341             : 
     342             : /** \brief Save the header information.
     343             :  *
     344             :  * This function saves parameters that are now available in the header
     345             :  * of the local entry.
     346             :  *
     347             :  * These parameters include:
     348             :  *
     349             :  * \li The uncompressed size of the entry
     350             :  * \li The compressed size of the entry
     351             :  * \li The CRC32 of the input file (before the compression)
     352             :  */
     353      133065 : void ZipOutputStreambuf::updateEntryHeaderInfo()
     354             : {
     355      133065 :     if(!m_open_entry)
     356             :     {
     357      133065 :         return;
     358             :     }
     359             : 
     360      133065 :     std::ostream os(m_outbuf);
     361      133065 :     int const curr_pos(os.tellp());
     362             : 
     363             :     // update fields in m_entries.back()
     364      266130 :     FileEntry::pointer_t entry(m_entries.back());
     365      133065 :     entry->setSize(getSize());
     366      133065 :     entry->setCrc(getCrc32());
     367             :     /** \TODO
     368             :      * Rethink the design as we have to force a call to the correct
     369             :      * getHeaderSize() function?
     370             :      */
     371      133065 :     entry->setCompressedSize(curr_pos - entry->getEntryOffset() - static_cast<ZipLocalEntry *>(entry.get())->ZipLocalEntry::getHeaderSize());
     372             : 
     373             :     // write ZipLocalEntry header to header position
     374      133065 :     os.seekp(entry->getEntryOffset());
     375             :     /** \TODO
     376             :      * Rethink the design as we have to force a call to the correct write()
     377             :      * function?
     378             :      */
     379      133065 :     static_cast<ZipLocalEntry *>(entry.get())->ZipLocalEntry::write(os);
     380      266130 :     os.seekp(curr_pos);
     381             : }
     382             : 
     383             : 
     384           3 : } // zipios namespace
     385             : 
     386             : // Local Variables:
     387             : // mode: cpp
     388             : // indent-tabs-mode: nil
     389             : // c-basic-offset: 4
     390             : // tab-width: 4
     391             : // End:
     392             : 
     393             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.10