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