* Fixed bug: Copy directory iterator caused SEGFAULT and wrong listingsmaster
| @@ -21,20 +21,33 @@ namespace cppfs | |||||
| { | { | ||||
| public: | public: | ||||
| file_type type; | file_type type; | ||||
| std::string name; | |||||
| cppfs::path path; | |||||
| public: | |||||
| static inline int compare(const entry& lhv, const entry& rhv); | |||||
| public: | |||||
| inline friend bool operator==(const entry& lhv, const entry& rhv); | |||||
| inline friend bool operator!=(const entry& lhv, const entry& rhv); | |||||
| inline friend bool operator<=(const entry& lhv, const entry& rhv); | |||||
| inline friend bool operator>=(const entry& lhv, const entry& rhv); | |||||
| inline friend bool operator< (const entry& lhv, const entry& rhv); | |||||
| inline friend bool operator> (const entry& lhv, const entry& rhv); | |||||
| }; | }; | ||||
| struct iterator | struct iterator | ||||
| : public std::iterator<std::forward_iterator_tag, const entry> | : public std::iterator<std::forward_iterator_tag, const entry> | ||||
| { | { | ||||
| private: | private: | ||||
| const directory * _owner; | |||||
| entry _entry; | |||||
| cppfs::path _path; | |||||
| entry _entry; | |||||
| public: | public: | ||||
| inline iterator() = default; | |||||
| inline iterator( | inline iterator( | ||||
| const directory& p_owner, | const directory& p_owner, | ||||
| bool p_end); | |||||
| bool p_end); | |||||
| inline iterator( | inline iterator( | ||||
| iterator&& p_other); | iterator&& p_other); | ||||
| @@ -58,8 +71,9 @@ namespace cppfs | |||||
| private: | private: | ||||
| using dir_ptr_u = std::unique_ptr<DIR, decltype(&closedir)>; | using dir_ptr_u = std::unique_ptr<DIR, decltype(&closedir)>; | ||||
| dir_ptr_u _dir; | |||||
| struct dirent * _dirent; | |||||
| dir_ptr_u _dir { nullptr, &closedir }; | |||||
| long _pos { 0 }; | |||||
| struct dirent * _dirent { nullptr }; | |||||
| inline void next(); | inline void next(); | ||||
| inline long offset() const; | inline long offset() const; | ||||
| @@ -1,5 +1,7 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <cppcore/misc/compare.h> | |||||
| #include "directory.h" | #include "directory.h" | ||||
| #include "../misc.h" | #include "../misc.h" | ||||
| #include "../file.h" | #include "../file.h" | ||||
| @@ -7,6 +9,80 @@ | |||||
| namespace cppfs | namespace cppfs | ||||
| { | { | ||||
| /* directory::entry */ | |||||
| int directory::entry::compare(const entry& lhv, const entry& rhv) | |||||
| { | |||||
| cppcore::op_invariant_string_compare str_cmp; | |||||
| auto lhp = lhv.path.parts(); | |||||
| auto rhp = rhv.path.parts(); | |||||
| auto lhi = lhp.begin(); | |||||
| auto rhi = rhp.begin(); | |||||
| auto lhc = lhp.size(); | |||||
| auto rhc = rhp.size(); | |||||
| auto lhd = lhv.type == file_type::directory ? 0 : 1; | |||||
| auto rhd = rhv.type == file_type::directory ? 0 : 1; | |||||
| /* check parts of the path (except the last one) */ | |||||
| while (lhc > 0 && rhc > 0) | |||||
| { | |||||
| auto lhx = lhc == 1 ? lhd : 0; | |||||
| auto rhx = rhc == 1 ? rhd : 0; | |||||
| /* check type (directory or file) */ | |||||
| if (lhx < rhx) | |||||
| return -1; | |||||
| if (lhx > rhx) | |||||
| return 1; | |||||
| /* check name */ | |||||
| auto cmp = str_cmp(*lhi, *rhi); | |||||
| if (cmp != 0) | |||||
| return cmp; | |||||
| ++lhi; | |||||
| ++rhi; | |||||
| --lhc; | |||||
| --rhc; | |||||
| } | |||||
| /* check length of the path */ | |||||
| if (lhc < rhc) | |||||
| return -1; | |||||
| if (lhc > rhc) | |||||
| return 1; | |||||
| return 0; | |||||
| } | |||||
| bool operator==(const directory::entry& lhv, const directory::entry& rhv) | |||||
| { return directory::entry::compare(lhv, rhv) == 0; } | |||||
| bool operator!=(const directory::entry& lhv, const directory::entry& rhv) | |||||
| { return directory::entry::compare(lhv, rhv) != 0; } | |||||
| bool operator<=(const directory::entry& lhv, const directory::entry& rhv) | |||||
| { return directory::entry::compare(lhv, rhv) <= 0; } | |||||
| bool operator>=(const directory::entry& lhv, const directory::entry& rhv) | |||||
| { return directory::entry::compare(lhv, rhv) >= 0; } | |||||
| bool operator< (const directory::entry& lhv, const directory::entry& rhv) | |||||
| { return directory::entry::compare(lhv, rhv) < 0; } | |||||
| bool operator> (const directory::entry& lhv, const directory::entry& rhv) | |||||
| { return directory::entry::compare(lhv, rhv) > 0; } | |||||
| /* directory */ | /* directory */ | ||||
| template<typename... T_args> | template<typename... T_args> | ||||
| @@ -27,7 +103,7 @@ namespace cppfs | |||||
| { | { | ||||
| for (auto& e : *this) | for (auto& e : *this) | ||||
| { | { | ||||
| auto p = path().join(e.name); | |||||
| auto p = path().join(e.path); | |||||
| switch (e.type) | switch (e.type) | ||||
| { | { | ||||
| case file_type::directory: | case file_type::directory: | ||||
| @@ -10,15 +10,15 @@ namespace cppfs | |||||
| /* directory::iterator */ | /* directory::iterator */ | ||||
| directory::iterator::iterator(const directory& p_owner, bool p_end) | directory::iterator::iterator(const directory& p_owner, bool p_end) | ||||
| : _owner (&p_owner) | |||||
| : _path (p_owner.path()) | |||||
| , _entry () | , _entry () | ||||
| , _dir (nullptr, &closedir) | , _dir (nullptr, &closedir) | ||||
| , _pos (-1) | |||||
| , _dirent (nullptr) | , _dirent (nullptr) | ||||
| { | { | ||||
| if (!p_end) | if (!p_end) | ||||
| { | { | ||||
| assert(_owner); | |||||
| auto s = _owner->path().str(); | |||||
| auto s = _path.str(); | |||||
| _dir.reset(opendir(s.c_str())); | _dir.reset(opendir(s.c_str())); | ||||
| if (!_dir) | if (!_dir) | ||||
| throw cppcore::error_exception("Unable to open directory", errno); | throw cppcore::error_exception("Unable to open directory", errno); | ||||
| @@ -27,44 +27,58 @@ namespace cppfs | |||||
| } | } | ||||
| directory::iterator::iterator(iterator&& p_other) | directory::iterator::iterator(iterator&& p_other) | ||||
| : _owner (std::move(p_other)._owner) | |||||
| : _path (std::move(p_other)._path) | |||||
| , _entry (std::move(p_other)._entry) | , _entry (std::move(p_other)._entry) | ||||
| , _dir (std::move(p_other)._dir) | , _dir (std::move(p_other)._dir) | ||||
| , _pos (std::move(p_other)._pos) | |||||
| , _dirent (std::move(p_other)._dirent) | , _dirent (std::move(p_other)._dirent) | ||||
| { } | |||||
| { | |||||
| p_other._dir.reset(); | |||||
| p_other._pos = -1; | |||||
| p_other._dirent = nullptr; | |||||
| } | |||||
| directory::iterator::iterator(const iterator& p_other) | directory::iterator::iterator(const iterator& p_other) | ||||
| : _owner (p_other._owner) | |||||
| : _path (p_other._path) | |||||
| , _entry () | , _entry () | ||||
| , _dir (nullptr, &closedir) | , _dir (nullptr, &closedir) | ||||
| , _pos (-1) | |||||
| , _dirent (nullptr) | , _dirent (nullptr) | ||||
| { this->operator=(p_other); } | { this->operator=(p_other); } | ||||
| directory::iterator& directory::iterator::operator=(iterator&& p_other) | directory::iterator& directory::iterator::operator=(iterator&& p_other) | ||||
| { | { | ||||
| _owner = std::move(p_other)._owner; | |||||
| _path = std::move(p_other)._path; | |||||
| _entry = std::move(p_other)._entry; | _entry = std::move(p_other)._entry; | ||||
| _dir = std::move(p_other)._dir; | _dir = std::move(p_other)._dir; | ||||
| _pos = std::move(p_other)._pos; | |||||
| _dirent = std::move(p_other)._dirent; | _dirent = std::move(p_other)._dirent; | ||||
| p_other._dir.reset(); | |||||
| p_other._pos = -1; | |||||
| p_other._dirent = nullptr; | |||||
| return *this; | return *this; | ||||
| } | } | ||||
| directory::iterator& directory::iterator::operator=(const iterator& p_other) | directory::iterator& directory::iterator::operator=(const iterator& p_other) | ||||
| { | { | ||||
| _owner = p_other._owner; | |||||
| _path = p_other._path; | |||||
| _pos = p_other._pos; | |||||
| _entry = entry { }; | _entry = entry { }; | ||||
| _dirent = nullptr; | _dirent = nullptr; | ||||
| _dir.reset(); | _dir.reset(); | ||||
| assert(_owner); | |||||
| auto s = _owner->path().str(); | |||||
| auto s = _path.str(); | |||||
| _dir.reset(opendir(s.c_str())); | _dir.reset(opendir(s.c_str())); | ||||
| if (!_dir) | if (!_dir) | ||||
| throw cppcore::error_exception("Unable to open directory", errno); | throw cppcore::error_exception("Unable to open directory", errno); | ||||
| seekdir(_dir.get(), p_other.offset()); | |||||
| next(); | |||||
| if (_dir && _pos >= 0) | |||||
| { | |||||
| seekdir(_dir.get(), _pos); | |||||
| next(); | |||||
| } | |||||
| return *this; | return *this; | ||||
| } | } | ||||
| @@ -84,9 +98,7 @@ namespace cppfs | |||||
| bool directory::iterator::operator==(const iterator& p_other) const | bool directory::iterator::operator==(const iterator& p_other) const | ||||
| { | { | ||||
| return _owner | |||||
| && p_other._owner | |||||
| && _owner == p_other._owner | |||||
| return _path == p_other._path | |||||
| && ( (!_dirent && !p_other._dirent) | && ( (!_dirent && !p_other._dirent) | ||||
| || offset() == p_other.offset()); | || offset() == p_other.offset()); | ||||
| } | } | ||||
| @@ -110,6 +122,7 @@ namespace cppfs | |||||
| { | { | ||||
| do | do | ||||
| { | { | ||||
| _pos = offset(); | |||||
| _dirent = readdir(_dir.get()); | _dirent = readdir(_dir.get()); | ||||
| } | } | ||||
| while ( | while ( | ||||
| @@ -122,7 +135,7 @@ namespace cppfs | |||||
| if (_dirent) | if (_dirent) | ||||
| { | { | ||||
| _entry.name = &_dirent->d_name[0]; | |||||
| _entry.path = _path.join(std::string(&_dirent->d_name[0])); | |||||
| switch (_dirent->d_type) | switch (_dirent->d_type) | ||||
| { | { | ||||
| case DT_BLK: _entry.type = file_type::block_device; break; | case DT_BLK: _entry.type = file_type::block_device; break; | ||||
| @@ -139,6 +139,20 @@ namespace cppfs | |||||
| */ | */ | ||||
| inline path join(const path& other) const; | inline path join(const path& other) const; | ||||
| public: | |||||
| inline friend bool operator< (const path& lhv, const path& rhv); | |||||
| inline friend bool operator<=(const path& lhv, const path& rhv); | |||||
| inline friend bool operator==(const path& lhv, const path& rhv); | |||||
| inline friend bool operator!=(const path& lhv, const path& rhv); | |||||
| inline friend bool operator>=(const path& lhv, const path& rhv); | |||||
| inline friend bool operator> (const path& lhv, const path& rhv); | |||||
| public: | |||||
| /** | |||||
| * @brief Compare two paths. | |||||
| */ | |||||
| static inline int compare(const path& lhv, const path& rhv); | |||||
| private: | private: | ||||
| /** | /** | ||||
| * @brief Create a path from the parts vector. | * @brief Create a path from the parts vector. | ||||
| @@ -119,4 +119,40 @@ namespace cppfs | |||||
| } | } | ||||
| } | } | ||||
| bool operator< (const path& lhv, const path& rhv) | |||||
| { return path::compare(lhv, rhv) < 0; } | |||||
| bool operator<=(const path& lhv, const path& rhv) | |||||
| { return path::compare(lhv, rhv) <= 0; } | |||||
| bool operator==(const path& lhv, const path& rhv) | |||||
| { return path::compare(lhv, rhv) == 0; } | |||||
| bool operator!=(const path& lhv, const path& rhv) | |||||
| { return path::compare(lhv, rhv) != 0; } | |||||
| bool operator>=(const path& lhv, const path& rhv) | |||||
| { return path::compare(lhv, rhv) >= 0; } | |||||
| bool operator> (const path& lhv, const path& rhv) | |||||
| { return path::compare(lhv, rhv) > 0; } | |||||
| int path::compare(const path& lhv, const path& rhv) | |||||
| { | |||||
| auto& lhp = lhv.parts(); | |||||
| auto& rhp = rhv.parts(); | |||||
| auto lht = lhv._status == path_status::absoulte ? 0 : 1; | |||||
| auto rht = rhv._status == path_status::absoulte ? 0 : 1; | |||||
| return | |||||
| lht < rht ? -1 : | |||||
| lht > rht ? 1 : | |||||
| lhp < rhp ? -1 : | |||||
| rhp < rhp ? 1 : | |||||
| 0; | |||||
| } | |||||
| } | } | ||||
| @@ -24,7 +24,7 @@ TEST(directory_tests, iterator) | |||||
| directory dir("./cppfs"); | directory dir("./cppfs"); | ||||
| std::vector<std::string> files; | std::vector<std::string> files; | ||||
| std::transform(dir.begin(), dir.end(), std::back_inserter(files), [](auto& e) { return e.name; }); | |||||
| std::transform(dir.begin(), dir.end(), std::back_inserter(files), [](auto& e) { return e.path.parts().back(); }); | |||||
| std::sort(files.begin(), files.end()); | std::sort(files.begin(), files.end()); | ||||
| EXPECT_EQ( | EXPECT_EQ( | ||||
| @@ -35,3 +35,73 @@ TEST(directory_tests, iterator) | |||||
| "path_tests.cpp", | "path_tests.cpp", | ||||
| })); | })); | ||||
| } | } | ||||
| TEST(directory_tests, entry_compare) | |||||
| { | |||||
| using namespace std::string_literals; | |||||
| EXPECT_EQ(-1, directory::entry::compare( | |||||
| directory::entry { file_type::directory, "/some/test/aaa"s }, | |||||
| directory::entry { file_type::directory, "/Some/Test/ZzZ"s })); | |||||
| EXPECT_EQ(-1, directory::entry::compare( | |||||
| directory::entry { file_type::directory, "/some/test"s }, | |||||
| directory::entry { file_type::directory, "/Some/Test/ZzZ"s })); | |||||
| EXPECT_EQ(-1, directory::entry::compare( | |||||
| directory::entry { file_type::directory, "/some/test/ZZZ"s }, | |||||
| directory::entry { file_type::file, "/Some/Test/AAA"s })); | |||||
| EXPECT_EQ(-1, directory::entry::compare( | |||||
| directory::entry { file_type::file, "/Some/aaa/AAA"s }, | |||||
| directory::entry { file_type::directory, "/some/bbb"s })); | |||||
| EXPECT_EQ(-1, directory::entry::compare( | |||||
| directory::entry { file_type::file, "/some/test/aAa"s }, | |||||
| directory::entry { file_type::file, "/Some/Test/ZzZ"s })); | |||||
| EXPECT_EQ(-1, directory::entry::compare( | |||||
| directory::entry { file_type::file, "/Some/Test/ZzZ"s }, | |||||
| directory::entry { file_type::file, "/some/test"s })); | |||||
| EXPECT_EQ(-1, directory::entry::compare( | |||||
| directory::entry { file_type::file, "/folder/file"s }, | |||||
| directory::entry { file_type::file, "/file"s })); | |||||
| EXPECT_EQ(0, directory::entry::compare( | |||||
| directory::entry { file_type::file, "/some/test/path"s }, | |||||
| directory::entry { file_type::file, "/Some/Test/paTh"s })); | |||||
| EXPECT_EQ(1, directory::entry::compare( | |||||
| directory::entry { file_type::directory, "/Some/Test/ZzZ"s }, | |||||
| directory::entry { file_type::directory, "/some/test/aaa"s })); | |||||
| EXPECT_EQ(1, directory::entry::compare( | |||||
| directory::entry { file_type::directory, "/Some/Test/ZzZ"s }, | |||||
| directory::entry { file_type::directory, "/some/test"s })); | |||||
| EXPECT_EQ(1, directory::entry::compare( | |||||
| directory::entry { file_type::file, "/Some/Test/AAA"s }, | |||||
| directory::entry { file_type::directory, "/some/test/ZZZ"s })); | |||||
| EXPECT_EQ(1, directory::entry::compare( | |||||
| directory::entry { file_type::directory, "/some/bbb"s }, | |||||
| directory::entry { file_type::file, "/Some/aaa/AAA"s })); | |||||
| EXPECT_EQ(1, directory::entry::compare( | |||||
| directory::entry { file_type::file, "/Some/Test/ZzZ"s }, | |||||
| directory::entry { file_type::file, "/some/test/aAa"s })); | |||||
| EXPECT_EQ(1, directory::entry::compare( | |||||
| directory::entry { file_type::file, "/some/test"s }, | |||||
| directory::entry { file_type::file, "/Some/Test/ZzZ"s })); | |||||
| EXPECT_EQ(1, directory::entry::compare( | |||||
| directory::entry { file_type::file, "/file"s }, | |||||
| directory::entry { file_type::file, "/folder/file"s })); | |||||
| } | |||||