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 zipios::FilePath.
24 : *
25 : * This file includes the zipios::FilePath implementation which makes it
26 : * a little easier to handle the stat() system call on any file for any
27 : * system.
28 : */
29 :
30 : #include "zipios/filepath.hpp"
31 :
32 : #include "zipios_common.hpp"
33 :
34 : #include <memory.h>
35 :
36 :
37 : namespace zipios
38 : {
39 :
40 :
41 : namespace
42 : {
43 :
44 :
45 : /** \brief Prune the trailing separator if present.
46 : *
47 : * This function is used to ensure that the FilePath does NOT end
48 : * with a separator.
49 : *
50 : * \warning
51 : * At this time the path is not canonicalized properly. We expect \p path
52 : * to not include double separators one after another. However, passing
53 : * such a path to the FilePath will keep it as is.
54 : *
55 : * \param[in] path The path to prune of one trailing separator.
56 : *
57 : * \return The path as is or without the last '/'.
58 : */
59 724347 : std::string pruneTrailingSeparator(std::string path)
60 : {
61 724347 : if(path.size() > 0)
62 : {
63 409962 : if(path[path.size() - 1] == g_separator)
64 : {
65 21009 : path.erase(path.size() - 1);
66 : }
67 : }
68 :
69 724347 : return path;
70 : }
71 :
72 :
73 : } // no name namespace
74 :
75 :
76 :
77 : /** \class FilePath
78 : * \brief Handle a file path and name and its statistics.
79 : *
80 : * The FilePath class represents a path to a file or directory name.
81 : * FilePath has member functions to check if the file path is a valid
82 : * file system entity, and to check what kind of file system entity
83 : * it is, e.g. is it a file, a directory, a pipe, etc.
84 : *
85 : * It also knows of the last modification time and size of the file.
86 : *
87 : * \warning
88 : * The information about a file is cached so at the time it gets used
89 : * the file on disk may have changed, it may even have been deleted.
90 : */
91 :
92 :
93 : /** \brief Initialize a FilePath object.
94 : *
95 : * The constructor saves the path and if \p check_exists is true, read
96 : * the file statistics, especially the st_mode.
97 : *
98 : * \param[in] path A string representation of the path.
99 : *
100 : * \sa exists()
101 : * \sa pruneTrailingSeparator()
102 : */
103 724010 : FilePath::FilePath(std::string const& path)
104 724010 : : m_path(pruneTrailingSeparator(path))
105 : //, m_stat() -- see below
106 : //, m_checked(false) -- auto-init
107 : //, m_exists(false) -- auto-init
108 : {
109 724010 : memset(&m_stat, 0, sizeof(m_stat));
110 724010 : }
111 :
112 :
113 : /** \brief Read the file mode.
114 : *
115 : * This function sets m_checked to true, stat()'s the path, to see if
116 : * it exists and to determine what type of file it is. All the query
117 : * functions call check() before they test a flag to make sure it
118 : * is set appropriately.
119 : *
120 : * This means stat()'ing is deferred until it becomes necessary. But also
121 : * it is cached meaning that if the file changes in between we get the
122 : * old flags.
123 : */
124 1088610 : void FilePath::check() const
125 : {
126 1088610 : if(!m_checked)
127 : {
128 75732 : m_checked = true;
129 :
130 : /** \TODO
131 : * Under MS-Windows, we need to use _wstat() to make it work in
132 : * Unicode (i.e. UTF-8 to wchar_t then call _wstat()...) Also we
133 : * want to use the 64 bit variant to make sure that we get a
134 : * valid size. Any other reference to the stat() command should
135 : * be replace by using a FilePath().
136 : *
137 : * See zipios/zipios-config.hpp.in
138 : */
139 75732 : memset(&m_stat, 0, sizeof(m_stat));
140 75732 : m_exists = stat(m_path.c_str(), &m_stat) == 0;
141 : }
142 1088610 : }
143 :
144 :
145 : /** \brief Replace the path with a new path.
146 : *
147 : * This function replaces the internal path of this FilePath with
148 : * the new specified path.
149 : *
150 : * \param[in] path The new path to save in this object.
151 : *
152 : * \return A reference to this object.
153 : */
154 337 : FilePath& FilePath::operator = (std::string const& path)
155 : {
156 337 : m_path = pruneTrailingSeparator(path);
157 337 : return *this;
158 : }
159 :
160 :
161 : /** \brief Retrieve the path.
162 : *
163 : * This operator can be used to retrieve a copy of the path.
164 : *
165 : * \return The m_path string returned as is (i.e. the whole path).
166 : */
167 105480626 : FilePath::operator std::string () const
168 : {
169 105480626 : return m_path;
170 : }
171 :
172 :
173 : /** \brief Append the a child name to this path.
174 : *
175 : * This function concatenates two FilePath objects and returns
176 : * another FilePath.
177 : *
178 : * A file separator is inserted between both names if appropriate.
179 : *
180 : * \warning
181 : * Note that the function allows you to append two full paths,
182 : * or even a relative path (left) to a full path (right), which
183 : * may result in a new path that is not quite sensible.
184 : *
185 : * \param[in] rhs The right hand side.
186 : */
187 21270 : FilePath FilePath::operator + (FilePath const& rhs) const
188 : {
189 21270 : if(m_path.empty())
190 : {
191 126 : return rhs;
192 : }
193 :
194 21144 : if(rhs.m_path.empty())
195 : {
196 1770 : return *this;
197 : }
198 :
199 19374 : if(rhs.m_path[0] == g_separator)
200 : {
201 2 : return m_path + rhs.m_path;
202 : }
203 :
204 19372 : return m_path + g_separator + rhs.m_path;
205 : }
206 :
207 :
208 : /** \brief Check whether two FilePath represent the same file.
209 : *
210 : * This function compares a FilePath object (this) and a C-string
211 : * to know whether the two are the same.
212 : *
213 : * A null pointer as the C-string is viewed as an empty string.
214 : *
215 : * \param[in] rhs The right hand side to compare with.
216 : *
217 : * \sa operator == (FilePath const& rhs);
218 : */
219 6 : bool FilePath::operator == (char const *rhs) const
220 : {
221 6 : return m_path == rhs;
222 : }
223 :
224 :
225 : /** \brief Check whether two FilePath represent the same file.
226 : *
227 : * This function compares a FilePath object (this) and a C-string
228 : * to know whether the two are the same.
229 : *
230 : * A null pointer as the C-string is viewed as an empty string.
231 : *
232 : * \param[in] lhs The left hand side to compare with.
233 : * \param[in] rhs The right hand side to compare with.
234 : *
235 : * \sa operator == (FilePath const& rhs);
236 : */
237 6 : bool operator == (char const *lhs, FilePath const& rhs)
238 : {
239 6 : return lhs == rhs.m_path;
240 : }
241 :
242 :
243 : /** \brief Check whether two FilePath represent the same file.
244 : *
245 : * This function compares a FilePath object (this) against
246 : * a string representing a path to know whether the two are
247 : * the equal.
248 : *
249 : * \param[in] rhs The right hand side to compare with.
250 : *
251 : * \sa operator == (FilePath const& rhs);
252 : */
253 6 : bool FilePath::operator == (std::string const& rhs) const
254 : {
255 6 : return m_path == rhs;
256 : }
257 :
258 :
259 : /** \brief Check whether two FilePath represent the same file.
260 : *
261 : * This function compares a FilePath object (this) against
262 : * a string representing a path to know whether the two are
263 : * the equal.
264 : *
265 : * \param[in] lhs The left hand side to compare with.
266 : * \param[in] rhs The right hand side to compare with.
267 : *
268 : * \sa operator == (FilePath const& rhs);
269 : */
270 6 : bool operator == (std::string const& lhs, FilePath const& rhs)
271 : {
272 6 : return lhs == rhs.m_path;
273 : }
274 :
275 :
276 : /** \brief Check whether two FilePath represent the same file.
277 : *
278 : * This function compares two FilePath objects (this and rhs)
279 : * to know whether the two are the same.
280 : *
281 : * \note
282 : * It is important to know that the compare is rather primitive.
283 : * The two paths must be equal character by character instead
284 : * of actually representing exactly the same file. Also relative
285 : * paths will likely be equal and these may not represent the
286 : * same file at all.
287 : *
288 : * \param[in] rhs The right hand side to compare with.
289 : *
290 : * \sa operator == (char const *rhs);
291 : * \sa operator == (std::string const& rhs);
292 : */
293 109048 : bool FilePath::operator == (FilePath const& rhs) const
294 : {
295 109048 : return m_path == rhs.m_path;
296 : }
297 :
298 :
299 : /** \brief Retrieve the basename.
300 : *
301 : * This function returns the filename part of the FilePath
302 : * object by pruning the path off.
303 : *
304 : * \return Return the basename of this FilePath filename.
305 : */
306 3466042 : std::string FilePath::filename() const
307 : {
308 3466042 : std::string::size_type const pos(m_path.find_last_of(g_separator));
309 3466042 : if(pos != std::string::npos)
310 : {
311 3457581 : return m_path.substr(pos + 1);
312 : }
313 :
314 8461 : return m_path;
315 : }
316 :
317 :
318 : /** \brief Get the length of the string.
319 : *
320 : * This function returns the length of the string used to
321 : * represent this FilePath path and filename.
322 : *
323 : * \return The length of the string representing this file path.
324 : *
325 : * \sa size()
326 : */
327 857385 : size_t FilePath::length() const
328 : {
329 857385 : return m_path.length();
330 : }
331 :
332 :
333 : /** \brief Get the length of the string.
334 : *
335 : * This function returns the length of the string used to
336 : * represent this FilePath path and filename.
337 : *
338 : * \note
339 : * This is an overloaded function that calls the length() function.
340 : * It is defined because the string represents an array of bytes
341 : * and as such the size() function may be used.
342 : *
343 : * \return The length of the string representing this file path.
344 : *
345 : * \sa length()
346 : */
347 27 : size_t FilePath::size() const
348 : {
349 27 : return length();
350 : }
351 :
352 :
353 : /** \brief Check whether the file exists.
354 : *
355 : * This function calls check() and then returns true if the file
356 : * exists on disk.
357 : *
358 : * \return true If the path is a valid file system entity.
359 : */
360 27 : bool FilePath::exists() const
361 : {
362 27 : check();
363 27 : return m_exists;
364 : }
365 :
366 :
367 : /** \brief Check whether the file is a regular file.
368 : *
369 : * This function returns true if the file exists and is a
370 : * regular file.
371 : *
372 : * \return true if the path is a regular file.
373 : */
374 75923 : bool FilePath::isRegular() const
375 : {
376 75923 : check();
377 75923 : return m_exists && S_ISREG(m_stat.st_mode);
378 : }
379 :
380 :
381 : /** \brief Check whether the file is a directory.
382 : *
383 : * This function returns true if the file exists and is a
384 : * directory.
385 : *
386 : * \return true if the path is a directory.
387 : */
388 862383 : bool FilePath::isDirectory() const
389 : {
390 862383 : check();
391 862383 : return m_exists && S_ISDIR(m_stat.st_mode);
392 : }
393 :
394 :
395 : /** \brief Check whether the file is a character special file.
396 : *
397 : * This function returns true if the file exists and is a
398 : * character special file.
399 : *
400 : * \return true if the path is character special (a character device file).
401 : */
402 27 : bool FilePath::isCharSpecial() const
403 : {
404 27 : check();
405 27 : return m_exists && S_ISCHR(m_stat.st_mode);
406 : }
407 :
408 :
409 : /** \brief Check whether the file is a block special file.
410 : *
411 : * This function returns true if the file exists and is a
412 : * block special file.
413 : *
414 : * \return true if the path is block special (a block device file).
415 : */
416 27 : bool FilePath::isBlockSpecial() const
417 : {
418 27 : check();
419 27 : return m_exists && S_ISBLK(m_stat.st_mode);
420 : }
421 :
422 :
423 : /** \brief Check whether the file is a socket.
424 : *
425 : * This function returns true if the file exists and is a
426 : * socket file.
427 : *
428 : * \return true if the path is a socket.
429 : */
430 27 : bool FilePath::isSocket() const
431 : {
432 27 : check();
433 27 : return m_exists && S_ISSOCK(m_stat.st_mode);
434 : }
435 :
436 :
437 : /** \brief Check whether the file is a pipe.
438 : *
439 : * This function returns true if the file exists and is a
440 : * pipe file.
441 : *
442 : * \return true if the path is a FIFO.
443 : */
444 27 : bool FilePath::isFifo() const
445 : {
446 27 : check();
447 27 : return m_exists && S_ISFIFO(m_stat.st_mode);
448 : }
449 :
450 :
451 : /** \brief Get the size of the file.
452 : *
453 : * This function returns the size of the file. The size may be a 64 bit
454 : * size on 64 bit systems.
455 : *
456 : * \note
457 : * If the file represents a directory, the size will be zero.
458 : *
459 : * \note
460 : * If the file is not considered valid, the size returned is zero.
461 : *
462 : * \warning
463 : * There is also a function called size() which actually checks the
464 : * length of the path and not the size of the file.
465 : *
466 : * \return The last modification as a Unix time.
467 : *
468 : * \sa size()
469 : */
470 74566 : size_t FilePath::fileSize() const
471 : {
472 74566 : check();
473 74566 : return m_stat.st_size;
474 : }
475 :
476 :
477 : /** \brief Get the last modification time of the file.
478 : *
479 : * This function returns the last modification time of the specified
480 : * file.
481 : *
482 : * \note
483 : * If the file is not considered valid, the time returned is zero.
484 : *
485 : * \return The last modification as a Unix time.
486 : */
487 75603 : std::time_t FilePath::lastModificationTime() const
488 : {
489 75603 : check();
490 75603 : return m_stat.st_mtime;
491 : }
492 :
493 :
494 : /** \brief Print out a FilePath.
495 : *
496 : * This function prints out the name of the file that this FilePath
497 : * represents.
498 : *
499 : * \param[in,out] os The output stream.
500 : * \param[in] path The path to print out.
501 : *
502 : * \return A copy of the \p os stream reference.
503 : */
504 579 : std::ostream& operator << (std::ostream& os, FilePath const& path)
505 : {
506 579 : os << static_cast<std::string>(path);
507 579 : return os;
508 : }
509 :
510 3 : } // namespace
511 :
512 : // Local Variables:
513 : // mode: cpp
514 : // indent-tabs-mode: nil
515 : // c-basic-offset: 4
516 : // tab-width: 4
517 : // End:
518 :
519 : // vim: ts=4 sw=4 et
|