LCOV - code coverage report
Current view: top level - snapwebsites - file_content.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 59 1.7 %
Date: 2019-12-15 17:13:15 Functions: 2 11 18.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Snap Websites Servers -- handle file content (i.e. read all / write all)
       2             : // Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // This program is free software; you can redistribute it and/or modify
       5             : // it under the terms of the GNU General Public License as published by
       6             : // the Free Software Foundation; either version 2 of the License, or
       7             : // (at your option) any later version.
       8             : //
       9             : // This program is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             : // GNU General Public License for more details.
      13             : //
      14             : // You should have received a copy of the GNU General Public License
      15             : // along with this program; if not, write to the Free Software
      16             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      17             : 
      18             : // self
      19             : //
      20             : #include "snapwebsites/file_content.h"
      21             : 
      22             : // snapwebsites lib
      23             : //
      24             : #include "snapwebsites/log.h"
      25             : #include "snapwebsites/mkdir_p.h"
      26             : 
      27             : // C lib
      28             : //
      29             : #include <unistd.h>
      30             : 
      31             : // C++ lib
      32             : //
      33             : #include <fstream>
      34             : 
      35             : // include last
      36             : //
      37             : #include <snapdev/poison.h>
      38             : 
      39             : 
      40             : namespace snap
      41             : {
      42             : 
      43             : 
      44             : /** \brief Initialize a content file.
      45             :  *
      46             :  * The constructor initialize the file content object with a filename.
      47             :  * The filename is used by the read_all() and write_all() functions.
      48             :  *
      49             :  * If the file_content is setup to be a temporary file, then the
      50             :  * destructor also makes use of the filename to delete the file
      51             :  * at that time. By default a file is not marked as temporary.
      52             :  *
      53             :  * \exception file_content_exception_invalid_parameter
      54             :  * The \p filename parameter cannot be an empty string.
      55             :  *
      56             :  * \exception file_content_exception_io_error
      57             :  * The function checks whether all the folders exist. If not then the
      58             :  * file can't be create or read so there is no valid file_content()
      59             :  * possible with that path. This exception only occurs if the
      60             :  * \p create_missing_directories is true and the creation of any
      61             :  * of the directories fails.
      62             :  *
      63             :  * \param[in] filename  The name of the file to read and write.
      64             :  * \param[in] create_missing_directories  Whether to create missing directories
      65             :  *            as found in the path (see mkdir_p()).
      66             :  * \param[in] temporary  Whether the file is temporary.
      67             :  *
      68             :  * \sa mkdir_p()
      69             :  */
      70           0 : file_content::file_content(std::string const & filename, bool create_missing_directories, bool temporary)
      71             :     : f_filename(filename)
      72           0 :     , f_temporary(temporary)
      73             : {
      74           0 :     if(f_filename.empty())
      75             :     {
      76           0 :         throw file_content_exception_invalid_parameter("the filename of a file_content object cannot be the empty string");
      77             :     }
      78             : 
      79           0 :     if(create_missing_directories)
      80             :     {
      81           0 :         int const r(mkdir_p(QString::fromUtf8(f_filename.c_str()), true));
      82           0 :         if(r != 0)
      83             :         {
      84           0 :             throw file_content_exception_io_error("the full path to filename for a file_content object could not be created");
      85             :         }
      86             :     }
      87           0 : }
      88             : 
      89             : 
      90             : /** \brief Clean up as required.
      91             :  *
      92             :  * If the file_content was marked as temporary, then the destructor
      93             :  * deletes the file on disk before returning.
      94             :  *
      95             :  * If the file does not exist and it was marked as temporary, the
      96             :  * deletion will fail silently. Otherwise you get a warning in the
      97             :  * logs with more details about the error (i.e. permission, bad
      98             :  * filename, etc.)
      99             :  */
     100           0 : file_content::~file_content()
     101             : {
     102             :     // TODO: add support for temporary files, unlink() it here
     103           0 :     if(f_temporary)
     104             :     {
     105           0 :         if(unlink(f_filename.c_str()) != 0)
     106             :         {
     107           0 :             if(errno != ENOENT)
     108             :             {
     109           0 :                 int const e(errno);
     110           0 :                 SNAP_LOG_WARNING("could not delete file \"")(f_filename)("\", got errno: ")(e)(", ")(strerror(e))(".");
     111             :             }
     112             :         }
     113             :     }
     114           0 : }
     115             : 
     116             : 
     117             : /** \brief Get the filename.
     118             :  *
     119             :  * This function returns a copy of the filename used by this file_content
     120             :  * object. Note that the filename cannot be modified.
     121             :  *
     122             :  * \return A reference to this file content filename.
     123             :  */
     124           0 : std::string const & file_content::get_filename() const
     125             : {
     126           0 :     return f_filename;
     127             : }
     128             : 
     129             : 
     130             : /** \brief Check whether this file exists.
     131             :  *
     132             :  * This function checks whether the file exists on disk. If the read_all()
     133             :  * function returns false and this functio nreturns true, then you probably
     134             :  * do not have permission to read the file or it is a directory.
     135             :  *
     136             :  * \return true if the file exists and can be read.
     137             :  */
     138           0 : bool file_content::exists() const
     139             : {
     140           0 :     return access(f_filename.c_str(), R_OK) == 0;
     141             : }
     142             : 
     143             : 
     144             : /** \brief Read the entire file in a string.
     145             :  *
     146             :  * This function reads the entire file in memory and saves it in a
     147             :  * string. The file can be binary in which case remember that c_str()
     148             :  * and other similar function will not work right.
     149             :  *
     150             :  * The function may return false if the file could not be opened.
     151             :  *
     152             :  * \return true if the file could be read, false otherwise.
     153             :  *
     154             :  * \sa write_all()
     155             :  */
     156           0 : bool file_content::read_all()
     157             : {
     158             :     // try to open the file
     159             :     //
     160           0 :     std::ifstream in;
     161           0 :     in.open(f_filename, std::ios::in | std::ios::binary);
     162           0 :     if(!in.is_open())
     163             :     {
     164             :         // cannot open
     165           0 :         SNAP_LOG_WARNING("could not open \"")(f_filename)("\" for reading.");
     166           0 :         return false;
     167             :     }
     168             : 
     169             :     // get the size
     170             :     //
     171           0 :     in.seekg(0, std::ios::end);
     172           0 :     std::ifstream::pos_type const size(in.tellg());
     173           0 :     in.seekg(0, std::ios::beg);
     174             : 
     175           0 :     if(std::ifstream::pos_type(-1) == size)
     176             :     {
     177             :         // cannot get the size
     178           0 :         SNAP_LOG_WARNING("tellg() failed against \"")(f_filename)("\".");
     179           0 :         return false;
     180             :     }
     181             : 
     182             :     // resize the buffer accoringly
     183             :     //
     184             :     try
     185             :     {
     186           0 :         f_content.resize(size, '\0');
     187             :     }
     188           0 :     catch(std::bad_alloc const & e)
     189             :     {
     190           0 :         SNAP_LOG_WARNING("could not allocate ")(size)(" bytes to read \"")(f_filename)("\".");
     191           0 :         return false;
     192             :     }
     193             : 
     194             :     // read the data
     195             :     //
     196           0 :     in.read(&f_content[0], size);
     197           0 :     if(in.fail()) // note: eof() will be true so good() will return false
     198             :     {
     199           0 :         SNAP_LOG_WARNING("could not read ")(size)(" bytes from \"")(f_filename)("\".");
     200           0 :         return false;
     201             :     }
     202             : 
     203           0 :     return true;
     204             : }
     205             : 
     206             : 
     207             : /** \brief Write the content to the file.
     208             :  *
     209             :  * This function writes the file content data to the file. If a new
     210             :  * filename is specified, the content is saved there instead. Note
     211             :  * that the filename does not get modified, but it allows for creating
     212             :  * a backup before making changes and save the new file:
     213             :  *
     214             :  * \code
     215             :  *      file_content fc("filename.here");
     216             :  *      fc.read_all();
     217             :  *      fc.write_all("filename.bak");  // create backup
     218             :  *      std::string content(fc.get_content());
     219             :  *      ... // make changes on 'content'
     220             :  *      fc.set_content(content);
     221             :  *      fc.write_all();
     222             :  * \endcode
     223             :  *
     224             :  * \warning
     225             :  * If you marked the file_content object as managing a temporary file
     226             :  * and specify a filename here which is not exactly equal to the
     227             :  * filename passed to the constructor, then the file you are writing
     228             :  * now will not be deleted automatically.
     229             :  *
     230             :  * \param[in] filename  The name to use or an empty string (or nothing)
     231             :  *                      to use the filename defined in the constructor.
     232             :  *
     233             :  * \return true if the file was saved successfully.
     234             :  *
     235             :  * \sa set_content()
     236             :  * \sa read_all()
     237             :  */
     238           0 : bool file_content::write_all(std::string const & filename)
     239             : {
     240             :     // select filename
     241             :     //
     242           0 :     std::string const name(filename.empty() ? f_filename : filename);
     243             : 
     244             :     // try to open the file
     245             :     //
     246           0 :     std::ofstream out;
     247           0 :     out.open(name, std::ios::trunc | std::ios::out | std::ios::binary);
     248           0 :     if(!out.is_open())
     249             :     {
     250             :         // cannot open
     251           0 :         SNAP_LOG_WARNING("could not open \"")(name)("\" for writing.");
     252           0 :         return false;
     253             :     }
     254             : 
     255             :     // write the data
     256             :     //
     257           0 :     out.write(&f_content[0], f_content.length());
     258           0 :     if(out.fail()) // note: eof() will be true so good() will return false
     259             :     {
     260           0 :         SNAP_LOG_WARNING("could not write ")(f_content.length())(" bytes to \"")(name)("\".");
     261           0 :         return false;
     262             :     }
     263             : 
     264           0 :     return true;
     265             : }
     266             : 
     267             : 
     268             : /** \brief Change the content with \p new_content.
     269             :  *
     270             :  * This function replaces the current file content with new_content.
     271             :  *
     272             :  * If \p new_content is empty, then the file will become empty on a
     273             :  * write().
     274             :  *
     275             :  * \param[in] new_content  The new content of the file.
     276             :  *
     277             :  * \sa get_content()
     278             :  * \sa write()
     279             :  */
     280           0 : void file_content::set_content(std::string const & new_content)
     281             : {
     282           0 :     f_content = new_content;
     283           0 : }
     284             : 
     285             : 
     286             : /** \brief Get a constant reference to the content.
     287             :  *
     288             :  * This function gives you access to the existing content of the file.
     289             :  * The content is considered valid only if you called the read() function
     290             :  * first, although it is not mandatory (i.e. in case you are creating a
     291             :  * new file, then you do not need to call read() first.)
     292             :  *
     293             :  * \return A constant reference to this file content data.
     294             :  */
     295           0 : std::string const & file_content::get_content() const
     296             : {
     297           0 :     return f_content;
     298             : }
     299             : 
     300             : 
     301             : 
     302             : 
     303           6 : } // namespace snap
     304             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13