// Copyright 2015 The Kyua Authors. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of Google Inc. nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "utils/fs/directory.hpp" extern "C" { #include #include } #include #include #include "utils/format/macros.hpp" #include "utils/fs/exceptions.hpp" #include "utils/fs/path.hpp" #include "utils/noncopyable.hpp" #include "utils/sanity.hpp" #include "utils/text/operations.ipp" namespace detail = utils::fs::detail; namespace fs = utils::fs; namespace text = utils::text; /// Constructs a new directory entry. /// /// \param name_ Name of the directory entry. fs::directory_entry::directory_entry(const std::string& name_) : name(name_) { } /// Checks if two directory entries are equal. /// /// \param other The entry to compare to. /// /// \return True if the two entries are equal; false otherwise. bool fs::directory_entry::operator==(const directory_entry& other) const { return name == other.name; } /// Checks if two directory entries are different. /// /// \param other The entry to compare to. /// /// \return True if the two entries are different; false otherwise. bool fs::directory_entry::operator!=(const directory_entry& other) const { return !(*this == other); } /// Checks if this entry sorts before another entry. /// /// \param other The entry to compare to. /// /// \return True if this entry sorts before the other entry; false otherwise. bool fs::directory_entry::operator<(const directory_entry& other) const { return name < other.name; } /// Formats a directory entry. /// /// \param output Stream into which to inject the formatted entry. /// \param entry The entry to format. /// /// \return A reference to output. std::ostream& fs::operator<<(std::ostream& output, const directory_entry& entry) { output << F("directory_entry{name=%s}") % text::quote(entry.name, '\''); return output; } /// Internal implementation details for the directory_iterator. /// /// In order to support multiple concurrent iterators over the same directory /// object, this class is the one that performs all directory-level accesses. /// In particular, even if it may seem surprising, this is the class that /// handles the DIR object for the directory. /// /// Note that iterators implemented by this class do not rely on the container /// directory class at all. This should not be relied on for object lifecycle /// purposes. struct utils::fs::detail::directory_iterator::impl : utils::noncopyable { /// Path of the directory accessed by this iterator. const fs::path _path; /// Raw pointer to the system representation of the directory. /// /// We also use this to determine if the iterator is valid (at the end) or /// not. A null pointer means an invalid iterator. ::DIR* _dirp; /// Raw representation of the system directory entry. /// /// We need to keep this at the class level so that we can use the /// readdir_r(3) function. ::dirent _dirent; /// Custom representation of the directory entry. /// /// This is separate from _dirent because this is the type we return to the /// user. We must keep this as a pointer so that we can support the common /// operators (* and ->) over iterators. std::auto_ptr< directory_entry > _entry; /// Constructs an iterator pointing to the "end" of the directory. impl(void) : _path("invalid-directory-entry"), _dirp(NULL) { } /// Constructs a new iterator to start scanning a directory. /// /// \param path The directory that will be scanned. /// /// \throw system_error If there is a problem opening the directory. explicit impl(const path& path) : _path(path) { DIR* dirp = ::opendir(_path.c_str()); if (dirp == NULL) { const int original_errno = errno; throw fs::system_error(F("opendir(%s) failed") % _path, original_errno); } _dirp = dirp; // Initialize our first directory entry. Note that this may actually // close the directory we just opened if the directory happens to be // empty -- but directories are never empty because they at least have // '.' and '..' entries. next(); } /// Destructor. /// /// This closes the directory if still open. ~impl(void) { if (_dirp != NULL) close(); } /// Closes the directory and invalidates the iterator. void close(void) { PRE(_dirp != NULL); if (::closedir(_dirp) == -1) { UNREACHABLE_MSG("Invalid dirp provided to closedir(3)"); } _dirp = NULL; } /// Advances the directory entry to the next one. /// /// It is possible to use this function on a new directory_entry object to /// initialize the first entry. /// /// \throw system_error If the call to readdir_r fails. void next(void) { ::dirent* result; if (::readdir_r(_dirp, &_dirent, &result) == -1) { const int original_errno = errno; throw fs::system_error(F("readdir_r(%s) failed") % _path, original_errno); } if (result == NULL) { _entry.reset(NULL); close(); } else { _entry.reset(new directory_entry(_dirent.d_name)); } } }; /// Constructs a new directory iterator. /// /// \param pimpl The constructed internal implementation structure to use. detail::directory_iterator::directory_iterator(std::shared_ptr< impl > pimpl) : _pimpl(pimpl) { } /// Destructor. detail::directory_iterator::~directory_iterator(void) { } /// Creates a new directory iterator for a directory. /// /// \return The directory iterator. Note that the result may be invalid. /// /// \throw system_error If opening the directory or reading its first entry /// fails. detail::directory_iterator detail::directory_iterator::new_begin(const path& path) { return directory_iterator(std::shared_ptr< impl >(new impl(path))); } /// Creates a new invalid directory iterator. /// /// \return The invalid directory iterator. detail::directory_iterator detail::directory_iterator::new_end(void) { return directory_iterator(std::shared_ptr< impl >(new impl())); } /// Checks if two iterators are equal. /// /// We consider two iterators to be equal if both of them are invalid or, /// otherwise, if they have the exact same internal representation (as given by /// equality of the pimpl pointers). /// /// \param other The object to compare to. /// /// \return True if the two iterators are equal; false otherwise. bool detail::directory_iterator::operator==(const directory_iterator& other) const { return (_pimpl->_dirp == NULL && other._pimpl->_dirp == NULL) || _pimpl == other._pimpl; } /// Checks if two iterators are different. /// /// \param other The object to compare to. /// /// \return True if the two iterators are different; false otherwise. bool detail::directory_iterator::operator!=(const directory_iterator& other) const { return !(*this == other); } /// Moves the iterator one element forward. /// /// \return A reference to the iterator. /// /// \throw system_error If advancing the iterator fails. detail::directory_iterator& detail::directory_iterator::operator++(void) { _pimpl->next(); return *this; } /// Dereferences the iterator to its contents. /// /// \return A reference to the directory entry pointed to by the iterator. const fs::directory_entry& detail::directory_iterator::operator*(void) const { PRE(_pimpl->_entry.get() != NULL); return *_pimpl->_entry; } /// Dereferences the iterator to its contents. /// /// \return A pointer to the directory entry pointed to by the iterator. const fs::directory_entry* detail::directory_iterator::operator->(void) const { PRE(_pimpl->_entry.get() != NULL); return _pimpl->_entry.get(); } /// Internal implementation details for the directory. struct utils::fs::directory::impl : utils::noncopyable { /// Path to the directory to scan. fs::path _path; /// Constructs a new directory. /// /// \param path_ Path to the directory to scan. impl(const fs::path& path_) : _path(path_) { } }; /// Constructs a new directory. /// /// \param path_ Path to the directory to scan. fs::directory::directory(const path& path_) : _pimpl(new impl(path_)) { } /// Returns an iterator to start scanning the directory. /// /// \return An iterator on the directory. /// /// \throw system_error If the directory cannot be opened to obtain its first /// entry. fs::directory::const_iterator fs::directory::begin(void) const { return const_iterator::new_begin(_pimpl->_path); } /// Returns an invalid iterator to check for the end of an scan. /// /// \return An invalid iterator. fs::directory::const_iterator fs::directory::end(void) const { return const_iterator::new_end(); }