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
|