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

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions