LCOV - code coverage report
Current view: top level - src - zipoutputstreambuf.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 85 86 98.8 %
Date: 2019-04-24 14:10:30 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-2019  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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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         243 : void writeZipCentralDirectory(std::ostream &os, FileEntry::vector_t& entries, std::string const& comment)
      57             : {
      58         486 :     ZipEndOfCentralDirectory eocd(comment);
      59         243 :     eocd.setOffset(os.tellp());  // start position
      60         243 :     eocd.setCount(entries.size());
      61             : 
      62         243 :     size_t central_directory_size(0);
      63      171708 :     for(auto it = entries.begin(); it != entries.end(); ++it)
      64             :     {
      65      171467 :         (*it)->write(os);
      66      171465 :         central_directory_size += (*it)->getHeaderSize();
      67             :     }
      68             : 
      69         241 :     eocd.setCentralDirectorySize(central_directory_size);
      70         241 :     eocd.write(os);
      71         239 : }
      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         243 : ZipOutputStreambuf::ZipOutputStreambuf(std::streambuf * outbuf)
      93         243 :     : DeflateOutputStreambuf(outbuf)
      94             :     //, m_zip_comment("") -- auto-init
      95             :     //, m_entries() -- auto-init
      96             :     //, m_compression_level(FileEntry::COMPRESSION_LEVEL_DEFAULT) -- auto-init
      97             :     //, m_open_entry(false) -- auto-init
      98             :     //, m_open(true) -- auto-init
      99             : {
     100         243 : }
     101             : 
     102             : 
     103             : /** \brief Clean up the buffer.
     104             :  *
     105             :  * This function cleans up this output buffer. In general this ensures
     106             :  * that the data still cached gets flushed.
     107             :  *
     108             :  * \warning
     109             :  * This function may gobble up some important exceptions. If you want
     110             :  * to make sure that the file is properly written, you must call the
     111             :  * finish() function (or the close() function) to fully terminate the
     112             :  * file. If these functions do not fail, then the output file is
     113             :  * considered valid and you can keep it. The finish() function can fail
     114             :  * because of a comment or a file which are too large, for example.
     115             :  */
     116         729 : ZipOutputStreambuf::~ZipOutputStreambuf()
     117             : {
     118             :     // avoid possible exceptions when writing the central directory
     119             :     try
     120             :     {
     121         243 :         finish();
     122             :     }
     123           1 :     catch(...)
     124             :     {
     125             :     }
     126         486 : }
     127             : 
     128             : 
     129             : /** \brief Close this buffer entry.
     130             :  *
     131             :  * Closes the current output buffer entry and positions the stream
     132             :  * write pointer at the beginning of the next entry.
     133             :  */
     134      171952 : void ZipOutputStreambuf::closeEntry()
     135             : {
     136      171952 :     if(!m_open_entry)
     137             :     {
     138         486 :         return;
     139             :     }
     140             : 
     141      171466 :     switch(m_compression_level)
     142             :     {
     143             :     case FileEntry::COMPRESSION_LEVEL_NONE:
     144       77735 :         overflow(); // flush
     145       77735 :         break;
     146             : 
     147             :     default:
     148       93731 :         closeStream();
     149       93731 :         break;
     150             : 
     151             :     }
     152             : 
     153      171466 :     updateEntryHeaderInfo();
     154      171466 :     setEntryClosedState();
     155             : }
     156             : 
     157             : 
     158             : /** \brief Close the output stream buffer.
     159             :  *
     160             :  * This function calls finish to make sure that any cached
     161             :  * data is saved and then close the stream buffer.
     162             :  */
     163         239 : void ZipOutputStreambuf::close()
     164             : {
     165         239 :     finish();
     166         239 : }
     167             : 
     168             : 
     169             : /** \brief Finish up an output stream buffer.
     170             :  *
     171             :  * Closes the current entry (if one is open), then writes the Zip
     172             :  * Central Directory Structure closing the ZipOutputStream. The
     173             :  * output stream (std::ostream) that the zip archive is being
     174             :  * written to is not closed.
     175             :  */
     176         724 : void ZipOutputStreambuf::finish()
     177             : {
     178         724 :     if(!m_open)
     179             :     {
     180         481 :         return;
     181             :     }
     182         243 :     m_open = false;
     183             : 
     184         482 :     std::ostream os(m_outbuf);
     185         243 :     closeEntry();
     186         243 :     writeZipCentralDirectory(os, m_entries, m_zip_comment);
     187             : }
     188             : 
     189             : 
     190             : /** \brief Start saving an entry in the output buffer.
     191             :  *
     192             :  * Opens the next entry in the zip archive and returns a const pointer to a
     193             :  * FileEntry object for the entry.
     194             :  *
     195             :  * If a previous entry was still open, the function calls closeEntry()
     196             :  * first.
     197             :  *
     198             :  * \param[in] entry  The entry to be saved and made current.
     199             :  */
     200      171467 : void ZipOutputStreambuf::putNextEntry(FileEntry::pointer_t entry)
     201             : {
     202      171467 :     closeEntry();
     203             : 
     204             :     // if the method is STORED force uncompressed data
     205      171467 :     if(entry->getMethod() == StorageMethod::STORED)
     206             :     {
     207             :         // force to "no compression" when the method is STORED
     208       76700 :         m_compression_level = FileEntry::COMPRESSION_LEVEL_NONE;
     209             :     }
     210             :     else
     211             :     {
     212             :         // get the user defined compression level
     213       94767 :         m_compression_level = entry->getLevel();
     214             :     }
     215      171467 :     m_overflown_bytes = 0;
     216      171467 :     switch(m_compression_level)
     217             :     {
     218             :     case FileEntry::COMPRESSION_LEVEL_NONE:
     219       77736 :         setp(&m_invec[0], &m_invec[0] + getBufferSize());
     220       77736 :         break;
     221             : 
     222             :     default:
     223       93731 :         init(m_compression_level);
     224       93731 :         break;
     225             : 
     226             :     }
     227             : 
     228      171467 :     m_entries.push_back(entry);
     229             : 
     230      342934 :     std::ostream os(m_outbuf);
     231             : 
     232             :     // Update entry header info
     233      171467 :     entry->setEntryOffset(os.tellp());
     234             :     /** \TODO
     235             :      * Rethink the design as we have to force a call to the correct
     236             :      * write() function?
     237             :      */
     238      171467 :     static_cast<ZipLocalEntry *>(entry.get())->ZipLocalEntry::write(os);
     239             : 
     240      171466 :     m_open_entry = true;
     241      171466 : }
     242             : 
     243             : 
     244             : /** \brief Set the archive comment.
     245             :  *
     246             :  * This function saves a global comment for the Zip archive.
     247             :  *
     248             :  * You may set it to an empty string which means that no comment
     249             :  * will be saved.
     250             :  *
     251             :  * The comment is saved when the first entry is saved so it
     252             :  * has to be put in there early on.
     253             :  *
     254             :  * \param[in] comment  The comment to save in the Zip archive.
     255             :  */
     256         243 : void ZipOutputStreambuf::setComment(std::string const& comment)
     257             : {
     258         243 :     m_zip_comment = comment;
     259         243 : }
     260             : 
     261             : 
     262             : //
     263             : // Protected and private methods
     264             : //
     265             : 
     266             : /** \brief Implementation of the overflow() function.
     267             :  *
     268             :  * When writing to a buffer, the overflow() function gets called when
     269             :  * there is no more room in the output buffer. The buffer is expected
     270             :  * to flush the data to disk and reset the buffer availability.
     271             :  *
     272             :  * \param[in] c  The character that made it all happen. Maybe EOF.
     273             :  *
     274             :  * \return EOF if the function fails, 0 otherwise.
     275             :  */
     276      745232 : int ZipOutputStreambuf::overflow(int c)
     277             : {
     278      745232 :     size_t const size(pptr() - pbase());
     279      745232 :     m_overflown_bytes += size;
     280      745232 :     switch(m_compression_level)
     281             :     {
     282             :     case FileEntry::COMPRESSION_LEVEL_NONE:
     283             :     {
     284             :         // Ok, we are STORED, so we handle it ourselves to avoid "side
     285             :         // effects" from zlib, which adds markers every now and then.
     286       84955 :         size_t const bc(m_outbuf->sputn(&m_invec[0], size));
     287       84955 :         if(size != bc)
     288             :         {
     289             :             // Without implementing our own stream in our test, this
     290             :             // cannot really be reached because it is all happening
     291             :             // inside the same loop in ZipFile::saveCollectionToArchive()
     292             :             throw IOException("ZipOutputStreambuf::overflow(): write to buffer failed."); // LCOV_EXCL_LINE
     293             :         }
     294       84955 :         setp(&m_invec[0], &m_invec[0] + getBufferSize());
     295             : 
     296       84955 :         if(c != EOF)
     297             :         {
     298        7220 :             *pptr() = c;
     299        7220 :             pbump(1);
     300             :         }
     301             : 
     302       84955 :         return 0;
     303             :     }
     304             : 
     305             :     default:
     306      660277 :         return DeflateOutputStreambuf::overflow(c);
     307             : 
     308             :     }
     309             : }
     310             : 
     311             : 
     312             : 
     313             : /** \brief Implement the sync() functionality.
     314             :  *
     315             :  * This virtual function is reimplemented to make sure that the system
     316             :  * does not run a default sync() function.
     317             :  *
     318             :  * This function calls the DeflateOutputStreambuf::sync() function which
     319             :  * returns -1 because it will not "synchronize" the input buffer.
     320             :  */
     321             : int ZipOutputStreambuf::sync() // LCOV_EXCL_LINE
     322             : {
     323             :     return DeflateOutputStreambuf::sync(); // LCOV_EXCL_LINE
     324             : }
     325             : 
     326             : 
     327             : 
     328             : /** \brief Mark the current entry as closed.
     329             :  *
     330             :  * After the putNextEntry() call and saving of the file content, the
     331             :  * closeEntry() function can be called to close the entry. The entry
     332             :  * is really closed when this setEntryClosedState() is called.
     333             :  */
     334      171466 : void ZipOutputStreambuf::setEntryClosedState()
     335             : {
     336      171466 :     m_open_entry = false;
     337             : 
     338             :     /** \FIXME
     339             :      * Update put pointers to trigger overflow on write. Overflow
     340             :      * should then return EOF while m_open_entry is false.
     341             :      */
     342      171466 : }
     343             : 
     344             : 
     345             : /** \brief Save the header information.
     346             :  *
     347             :  * This function saves parameters that are now available in the header
     348             :  * of the local entry.
     349             :  *
     350             :  * These parameters include:
     351             :  *
     352             :  * \li The uncompressed size of the entry
     353             :  * \li The compressed size of the entry
     354             :  * \li The CRC32 of the input file (before the compression)
     355             :  */
     356      171466 : void ZipOutputStreambuf::updateEntryHeaderInfo()
     357             : {
     358      171466 :     if(!m_open_entry)
     359             :     {
     360           0 :         return;
     361             :     }
     362             : 
     363      342932 :     std::ostream os(m_outbuf);
     364      171466 :     int const curr_pos(os.tellp());
     365             : 
     366             :     // update fields in m_entries.back()
     367      342932 :     FileEntry::pointer_t entry(m_entries.back());
     368      171466 :     entry->setSize(getSize());
     369      171466 :     entry->setCrc(getCrc32());
     370             :     /** \TODO
     371             :      * Rethink the design as we have to force a call to the correct
     372             :      * getHeaderSize() function?
     373             :      */
     374      171466 :     entry->setCompressedSize(curr_pos - entry->getEntryOffset() - static_cast<ZipLocalEntry *>(entry.get())->ZipLocalEntry::getHeaderSize());
     375             : 
     376             :     // write ZipLocalEntry header to header position
     377      171466 :     os.seekp(entry->getEntryOffset());
     378             :     /** \TODO
     379             :      * Rethink the design as we have to force a call to the correct write()
     380             :      * function?
     381             :      */
     382      171466 :     static_cast<ZipLocalEntry *>(entry.get())->ZipLocalEntry::write(os);
     383      171466 :     os.seekp(curr_pos);
     384             : }
     385             : 
     386             : 
     387           3 : } // zipios namespace
     388             : 
     389             : // Local Variables:
     390             : // mode: cpp
     391             : // indent-tabs-mode: nil
     392             : // c-basic-offset: 4
     393             : // tab-width: 4
     394             : // End:
     395             : 
     396             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12