* Implemented 'path' and 'directory' classes (including unit tests)master
| @@ -0,0 +1,3 @@ | |||
| [submodule "cmake/modules"] | |||
| path = cmake/modules | |||
| url = https://git.bergmann89.de/cpp/CMakeModules.git | |||
| @@ -0,0 +1,65 @@ | |||
| # Initialize CMake ################################################################################ | |||
| CMake_Minimum_Required ( VERSION 3.12.0 FATAL_ERROR ) | |||
| # Set CMAKE_BUILD_TYPE | |||
| If ( NOT CMAKE_BUILD_TYPE ) | |||
| Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE ) | |||
| EndIf ( NOT CMAKE_BUILD_TYPE ) | |||
| Set_Property ( CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel ) | |||
| # Set CMAKE_MODULE_PATH | |||
| If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) | |||
| Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} | |||
| "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) | |||
| EndIf ( ) | |||
| If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/cmake" ) | |||
| Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} | |||
| "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/cmake" ) | |||
| EndIf ( ) | |||
| # Project ######################################################################################### | |||
| Include ( GNUInstallDirs ) | |||
| Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cppfs-options.cmake ) | |||
| Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cppfs-const.cmake ) | |||
| Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cppfs-var.cmake ) | |||
| Project ( ${CPPFS_PROJECT_NAME} | |||
| DESCRIPTION "${CPPFS_PROJECT_DESCRIPTION}" | |||
| VERSION "${CPPFS_VERSION}" ) | |||
| Include ( CTest ) | |||
| Add_Compile_Options ( $<$<AND:$<CXX_COMPILER_ID:GNU>,$<CONFIG:DEBUG>>:-D_GLIBCXX_DEBUG> ) | |||
| # Subdirectories | |||
| Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) | |||
| Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) | |||
| # Install | |||
| If ( NOT CPPFS_HAS_EXPORT | |||
| OR NOT CPPFS_INSTALL_PACKAGE ) | |||
| Return ( ) | |||
| EndIf ( ) | |||
| Include ( CMakePackageConfigHelpers ) | |||
| Write_Basic_Package_Version_File ( "${CMAKE_CURRENT_BINARY_DIR}/cmake/cppfs-config-version.cmake" | |||
| VERSION ${CPPFS_VERSION} | |||
| COMPATIBILITY AnyNewerVersion ) | |||
| Configure_File ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cppfs-config.cmake" | |||
| "${CMAKE_CURRENT_BINARY_DIR}/cmake/cppfs-config.cmake" | |||
| @ONLY ) | |||
| Set ( ConfigPackageLocation "${CPPFS_INSTALL_DIR_SHARE}/cmake" ) | |||
| Install ( EXPORT | |||
| cppfs | |||
| NAMESPACE | |||
| cppfs:: | |||
| DESTINATION | |||
| ${ConfigPackageLocation} ) | |||
| Install ( FILES | |||
| "${CMAKE_CURRENT_BINARY_DIR}/cmake/cppfs-config.cmake" | |||
| "${CMAKE_CURRENT_BINARY_DIR}/cmake/cppfs-config-version.cmake" | |||
| DESTINATION | |||
| ${ConfigPackageLocation} | |||
| COMPONENT | |||
| Devel ) | |||
| @@ -0,0 +1,3 @@ | |||
| # cppfs | |||
| Readme of the project. | |||
| @@ -0,0 +1,9 @@ | |||
| # cppfs-config.cmake - package configuration file | |||
| # Include ( CMakeFindDependencyMacro ) | |||
| # Find_Dependency ( <dependency> ) | |||
| Include ( FindPackageHandleStandardArgs ) | |||
| Set ( ${CMAKE_FIND_PACKAGE_NAME}_CONFIG ${CMAKE_CURRENT_LIST_FILE} ) | |||
| Find_Package_Handle_Standard_Args ( cppfs CONFIG_MODE ) | |||
| Include ( "${CMAKE_CURRENT_LIST_DIR}/cppfs.cmake") | |||
| @@ -0,0 +1,28 @@ | |||
| # This file contains constant variables that are fixed to this project | |||
| # Version | |||
| Set ( CPPFS_VERSION_MAJOR 1 ) | |||
| Set ( CPPFS_VERSION_MINOR 0 ) | |||
| Set ( CPPFS_VERSION_PATCH 0 ) | |||
| Set ( CPPFS_VERSION_BUILD 0 ) | |||
| Set ( CPPFS_VERSION_HASH "" ) | |||
| Set ( CPPFS_VERSION_BEHIND 0 ) | |||
| Set ( CPPFS_VERSION_DIRTY 0 ) | |||
| # Names | |||
| Set ( CPPFS_PROJECT_NAME "cppfs" ) | |||
| Set ( CPPFS_PROJECT_DESCRIPTION "A simple interface library" ) | |||
| # Include generated variables for further usage | |||
| Include ( ${CMAKE_CURRENT_LIST_DIR}/cppfs-var.cmake ) | |||
| # Install directories | |||
| Set ( CPPFS_INSTALL_DIR_INCLUDE "${CMAKE_INSTALL_INCLUDEDIR}/${CPPFS_NAME}" ) | |||
| Set ( CPPFS_INSTALL_DIR_LIB "${CMAKE_INSTALL_LIBDIR}" ) | |||
| Set ( CPPFS_INSTALL_DIR_SHARE "${CMAKE_INSTALL_DATAROOTDIR}/${CPPFS_NAME}" ) | |||
| # C Standard | |||
| Set ( CMAKE_C_STANDARD 11 ) | |||
| Set ( CMAKE_CXX_STANDARD 17 ) | |||
| Set ( CMAKE_C_STANDARD_REQUIRED ON ) | |||
| Set ( CMAKE_CXX_STANDARD_REQUIRED ON ) | |||
| @@ -0,0 +1,11 @@ | |||
| # This file contains options that can be passed to the cmake command | |||
| Option ( CPPFS_INSTALL_HEADER | |||
| "Install headers of cppfs." | |||
| ON ) | |||
| Option ( CPPFS_INSTALL_PACKAGE | |||
| "Install the cmake package of cppfs." | |||
| ON ) | |||
| Option ( CPPFS_USE_GIT_VERSION | |||
| "Read the git tags to get the version of cppfs" | |||
| ON ) | |||
| @@ -0,0 +1,32 @@ | |||
| # This file contains generated variables that are needed for the project | |||
| # Git Version | |||
| If ( CPPFS_USE_GIT_VERSION ) | |||
| Include ( git_helper OPTIONAL RESULT_VARIABLE HAS_GIT_HELPER ) | |||
| If ( HAS_GIT_HELPER ) | |||
| GitGetVersion ( ${CMAKE_CURRENT_LIST_DIR}/.. | |||
| CPPFS_VERSION_MAJOR | |||
| CPPFS_VERSION_MINOR | |||
| CPPFS_VERSION_PATCH | |||
| CPPFS_VERSION_BUILD | |||
| CPPFS_VERSION_HASH | |||
| CPPFS_VERSION_BEHIND | |||
| CPPFS_VERSION_DIRTY ) | |||
| EndIf ( ) | |||
| EndIf ( ) | |||
| # Strings | |||
| Set ( CPPFS_VERSION_SHORT | |||
| "${CPPFS_VERSION_MAJOR}.${CPPFS_VERSION_MINOR}" ) | |||
| Set ( CPPFS_VERSION | |||
| "${CPPFS_VERSION_SHORT}.${CPPFS_VERSION_PATCH}.${CPPFS_VERSION_BUILD}" ) | |||
| Set ( CPPFS_VERSION_COMPLETE | |||
| "${CPPFS_VERSION}" ) | |||
| Set ( CPPFS_NAME | |||
| "${CPPFS_PROJECT_NAME}-${CPPFS_VERSION_SHORT}" ) | |||
| Set ( CPPFS_OUTPUTNAME | |||
| "${CPPFS_PROJECT_NAME}" ) | |||
| If ( CPPFS_VERSION_BEHIND ) | |||
| Set ( CPPFS_VERSION_COMPLETE | |||
| "${CPPFS_VERSION_COMPLETE}+${CPPFS_VERSION_BEHIND}" ) | |||
| EndIf ( ) | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 165bf7c7702ec184b46b046889b62ef4a63afccf | |||
| @@ -0,0 +1,6 @@ | |||
| #pragma once | |||
| #include <cppfs/config.h> | |||
| #include <cppfs/directory.h> | |||
| #include <cppfs/file.h> | |||
| #include <cppfs/path.h> | |||
| @@ -0,0 +1,26 @@ | |||
| #pragma once | |||
| #define cppfs_os_linux 1 | |||
| #define cppfs_os_windows 2 | |||
| #define cppfs_os_ios 3 | |||
| #if defined(__linux__) | |||
| #define cppfs_os cppfs_os_linux | |||
| #else | |||
| #error "Unknown or unsupported operation system!" | |||
| #endif | |||
| namespace cppfs | |||
| { | |||
| #if cppfs_os == cppfs_os_linux | |||
| struct constants | |||
| { | |||
| static constexpr char path_delimiter = '/'; | |||
| static constexpr const char * temp_dir = "/tmp"; | |||
| }; | |||
| using fd_handle = int; | |||
| #endif | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| #pragma once | |||
| #include <cppfs/config.h> | |||
| #include "directory/directory.h" | |||
| #include "directory/directory.inl" | |||
| #if cppfs_os == cppfs_os_linux | |||
| #include "directory/directory.linux.inl" | |||
| #endif | |||
| @@ -0,0 +1,125 @@ | |||
| #pragma once | |||
| #include <string> | |||
| #include <memory> | |||
| #include <iterator> | |||
| #if cppfs_os == cppfs_os_linux | |||
| #include <dirent.h> | |||
| #endif | |||
| #include "../path.h" | |||
| #include "../misc.h" | |||
| namespace cppfs | |||
| { | |||
| struct directory | |||
| { | |||
| public: | |||
| struct entry | |||
| { | |||
| public: | |||
| file_type type; | |||
| std::string name; | |||
| }; | |||
| struct iterator | |||
| : public std::iterator<std::forward_iterator_tag, const entry> | |||
| { | |||
| private: | |||
| const directory * _owner; | |||
| entry _entry; | |||
| public: | |||
| inline iterator( | |||
| const directory& p_owner, | |||
| bool p_end); | |||
| inline iterator( | |||
| iterator&& p_other); | |||
| inline iterator( | |||
| const iterator& p_other); | |||
| inline iterator& operator=(iterator&& p_other); | |||
| inline iterator& operator=(const iterator& p_other); | |||
| inline iterator& operator++(); | |||
| inline iterator operator++(int); | |||
| inline bool operator==(const iterator& p_other) const; | |||
| inline bool operator!=(const iterator& p_other) const; | |||
| inline reference operator* () const; | |||
| inline pointer operator->() const; | |||
| #if cppfs_os == cppfs_os_linux | |||
| private: | |||
| using dir_ptr_u = std::unique_ptr<DIR, decltype(&closedir)>; | |||
| dir_ptr_u _dir; | |||
| struct dirent * _dirent; | |||
| inline void next(); | |||
| inline long offset() const; | |||
| #endif | |||
| }; | |||
| public: | |||
| /** | |||
| * @brief Get the default file permissions for directories. | |||
| */ | |||
| static inline const file_permissions& default_permissions(); | |||
| private: | |||
| cppfs::path _path; | |||
| public: | |||
| /** | |||
| * @brief Constructor. | |||
| */ | |||
| template<typename... T_args> | |||
| inline directory(T_args&&... p_args); | |||
| /** | |||
| * @brief Get the path of the directory. | |||
| */ | |||
| inline const cppfs::path& path() const; | |||
| /** | |||
| * @brief Check if the directory exsists. | |||
| */ | |||
| inline bool exists() const; | |||
| /** | |||
| * @brief Create the directory. | |||
| */ | |||
| inline const directory& create( | |||
| bool p_parent, | |||
| const file_permissions& p_perm = default_permissions()) const; | |||
| /** | |||
| * @brief Remove the directory (and all it's content if the parameter is set to true) | |||
| */ | |||
| inline const directory& remove( | |||
| bool p_recursive) const; | |||
| /** | |||
| * @brief Create a begin iterator. | |||
| */ | |||
| inline iterator begin() const; | |||
| /** | |||
| * @brief Create a end iterator. | |||
| */ | |||
| inline iterator end() const; | |||
| private: | |||
| /** | |||
| * @brief Remove the directory if it's empty. | |||
| */ | |||
| inline void remove_dir() const; | |||
| }; | |||
| } | |||
| @@ -0,0 +1,81 @@ | |||
| #pragma once | |||
| #include "directory.h" | |||
| #include "../misc.h" | |||
| #include "../file.h" | |||
| namespace cppfs | |||
| { | |||
| /* directory */ | |||
| const file_permissions& directory::default_permissions() | |||
| { | |||
| static const file_permissions value({ | |||
| file_permission::user_read, | |||
| file_permission::user_write, | |||
| file_permission::user_execute, | |||
| file_permission::group_read, | |||
| file_permission::group_write, | |||
| file_permission::group_execute, | |||
| file_permission::others_read, | |||
| file_permission::others_execute, | |||
| }); | |||
| return value; | |||
| } | |||
| template<typename... T_args> | |||
| directory::directory(T_args&&... p_args) | |||
| : _path(std::forward<T_args>(p_args)...) | |||
| { } | |||
| const cppfs::path& directory::path() const | |||
| { return _path; } | |||
| bool directory::exists() const | |||
| { return _path.is_directory(); } | |||
| inline const directory& directory::remove( | |||
| bool p_recursive) const | |||
| { | |||
| if (p_recursive) | |||
| { | |||
| for (auto& e : *this) | |||
| { | |||
| auto p = path().join(e.name); | |||
| switch (e.type) | |||
| { | |||
| case file_type::directory: | |||
| directory(p).remove(true); | |||
| break; | |||
| case file_type::file: | |||
| case file_type::symbolic_link: | |||
| case file_type::hard_link: | |||
| case file_type::block_device: | |||
| case file_type::char_device: | |||
| case file_type::named_pipe: | |||
| case file_type::socket: | |||
| file(p).remove(); | |||
| break; | |||
| case file_type::unknown: | |||
| throw cppcore::exception("Unknown file type: " + p.str()); | |||
| } | |||
| } | |||
| } | |||
| remove_dir(); | |||
| return *this; | |||
| } | |||
| directory::iterator directory::begin() const | |||
| { return iterator(*this, false); } | |||
| directory::iterator directory::end() const | |||
| { return iterator(*this, true); } | |||
| } | |||
| @@ -0,0 +1,199 @@ | |||
| #pragma once | |||
| #include <sys/stat.h> | |||
| #include "directory.h" | |||
| namespace cppfs | |||
| { | |||
| /* directory::iterator */ | |||
| directory::iterator::iterator(const directory& p_owner, bool p_end) | |||
| : _owner (&p_owner) | |||
| , _entry () | |||
| , _dir (nullptr, &closedir) | |||
| , _dirent (nullptr) | |||
| { | |||
| if (!p_end) | |||
| { | |||
| assert(_owner); | |||
| auto s = _owner->path().str(); | |||
| _dir.reset(opendir(s.c_str())); | |||
| if (!_dir) | |||
| throw cppcore::error_exception("Unable to open directory", errno); | |||
| next(); | |||
| } | |||
| } | |||
| directory::iterator::iterator(iterator&& p_other) | |||
| : _owner (std::move(p_other)._owner) | |||
| , _entry (std::move(p_other)._entry) | |||
| , _dir (std::move(p_other)._dir) | |||
| , _dirent (std::move(p_other)._dirent) | |||
| { } | |||
| directory::iterator::iterator(const iterator& p_other) | |||
| : _owner (p_other._owner) | |||
| , _entry () | |||
| , _dir (nullptr, &closedir) | |||
| , _dirent (nullptr) | |||
| { this->operator=(p_other); } | |||
| directory::iterator& directory::iterator::operator=(iterator&& p_other) | |||
| { | |||
| _owner = std::move(p_other)._owner; | |||
| _entry = std::move(p_other)._entry; | |||
| _dir = std::move(p_other)._dir; | |||
| _dirent = std::move(p_other)._dirent; | |||
| return *this; | |||
| } | |||
| directory::iterator& directory::iterator::operator=(const iterator& p_other) | |||
| { | |||
| _owner = p_other._owner; | |||
| _entry = entry { }; | |||
| _dirent = nullptr; | |||
| _dir.reset(); | |||
| assert(_owner); | |||
| auto s = _owner->path().str(); | |||
| _dir.reset(opendir(s.c_str())); | |||
| if (!_dir) | |||
| throw cppcore::error_exception("Unable to open directory", errno); | |||
| seekdir(_dir.get(), p_other.offset()); | |||
| next(); | |||
| return *this; | |||
| } | |||
| directory::iterator& directory::iterator::operator++() | |||
| { | |||
| next(); | |||
| return *this; | |||
| } | |||
| directory::iterator directory::iterator::operator++(int) | |||
| { | |||
| auto it = *this; | |||
| next(); | |||
| return it; | |||
| } | |||
| bool directory::iterator::operator==(const iterator& p_other) const | |||
| { | |||
| return _owner | |||
| && p_other._owner | |||
| && _owner == p_other._owner | |||
| && ( (!_dirent && !p_other._dirent) | |||
| || offset() == p_other.offset()); | |||
| } | |||
| bool directory::iterator::operator!=(const iterator& p_other) const | |||
| { return !this->operator==(p_other); } | |||
| directory::iterator::reference directory::iterator::operator* () const | |||
| { | |||
| assert(_dirent); | |||
| return _entry; | |||
| } | |||
| directory::iterator::pointer directory::iterator::operator->() const | |||
| { | |||
| assert(_dirent); | |||
| return &_entry; | |||
| } | |||
| void directory::iterator::next() | |||
| { | |||
| do | |||
| { | |||
| _dirent = readdir(_dir.get()); | |||
| } | |||
| while ( | |||
| _dirent | |||
| && ( ( _dirent->d_name[0] == '.' | |||
| && _dirent->d_name[1] == '.' | |||
| && _dirent->d_name[2] == '\0') | |||
| || ( _dirent->d_name[0] == '.' | |||
| && _dirent->d_name[1] == '\0'))); | |||
| if (_dirent) | |||
| { | |||
| _entry.name = &_dirent->d_name[0]; | |||
| switch (_dirent->d_type) | |||
| { | |||
| case DT_BLK: _entry.type = file_type::block_device; break; | |||
| case DT_CHR: _entry.type = file_type::char_device; break; | |||
| case DT_DIR: _entry.type = file_type::directory; break; | |||
| case DT_FIFO: _entry.type = file_type::named_pipe; break; | |||
| case DT_LNK: _entry.type = file_type::symbolic_link; break; | |||
| case DT_REG: _entry.type = file_type::file; break; | |||
| case DT_SOCK: _entry.type = file_type::socket; break; | |||
| default: _entry.type = file_type::unknown; break; | |||
| } | |||
| } | |||
| } | |||
| long directory::iterator::offset() const | |||
| { | |||
| long ret = -1; | |||
| if (_dir) | |||
| { | |||
| ret = telldir(_dir.get()); | |||
| if (ret < 0) | |||
| throw cppcore::error_exception("Unable to get current position in directory", errno); | |||
| } | |||
| return ret; | |||
| } | |||
| /* directory */ | |||
| const directory& directory::create( | |||
| bool p_parent, | |||
| const file_permissions& p_perm) const | |||
| { | |||
| using namespace std::string_literals; | |||
| auto perm = file_permissions::to_unix(p_perm); | |||
| if (p_parent) | |||
| { | |||
| std::ostringstream os; | |||
| auto& parts = _path.parts(); | |||
| for (auto& p : parts) | |||
| { | |||
| os << '/' << p; | |||
| struct stat st; | |||
| auto s = os.str(); | |||
| if (!stat(s.c_str(), &st) && S_ISDIR(st.st_mode)) | |||
| continue; | |||
| if (mkdir(s.c_str(), perm)) | |||
| throw cppcore::error_exception("Unable to create directory: "s + s, errno); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| auto& s = _path.str(); | |||
| if (mkdir(s.c_str(), perm)) | |||
| throw cppcore::error_exception("Unable to create directory: "s + s, errno); | |||
| } | |||
| return *this; | |||
| } | |||
| void directory::remove_dir() const | |||
| { | |||
| using namespace std::string_literals; | |||
| auto& p = _path.str(); | |||
| if (rmdir(p.c_str())) | |||
| throw cppcore::error_exception("Unable to remove directory: "s + p, errno); | |||
| } | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| #pragma once | |||
| #include <cppfs/config.h> | |||
| #include "file/file.h" | |||
| #include "file/file.inl" | |||
| #if cppfs_os == cppfs_os_linux | |||
| #include "file/file.linux.inl" | |||
| #endif | |||
| @@ -0,0 +1,39 @@ | |||
| #pragma once | |||
| #include <string> | |||
| #include "../path.h" | |||
| #include "../misc.h" | |||
| namespace cppfs | |||
| { | |||
| struct file | |||
| { | |||
| private: | |||
| cppfs::path _path; | |||
| public: | |||
| /** | |||
| * @brief Constructor. | |||
| */ | |||
| template<typename... T_args> | |||
| inline file(T_args&&... p_args); | |||
| /** | |||
| * @brief Get the path of the file. | |||
| */ | |||
| inline const cppfs::path& path() const; | |||
| /** | |||
| * @brief Check if the file exsists. | |||
| */ | |||
| inline bool exists() const; | |||
| /** | |||
| * @brief Remove the file. | |||
| */ | |||
| inline const file& remove() const; | |||
| }; | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| #pragma once | |||
| #include "file.h" | |||
| namespace cppfs | |||
| { | |||
| /* file */ | |||
| template<typename... T_args> | |||
| file::file(T_args&&... p_args) | |||
| : _path(std::forward<T_args>(p_args)...) | |||
| { } | |||
| const cppfs::path& file::path() const | |||
| { return _path; } | |||
| bool file::exists() const | |||
| { return _path.is_file(); } | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| #pragma once | |||
| #include "file.h" | |||
| namespace cppfs | |||
| { | |||
| /* file */ | |||
| const file& file::remove() const | |||
| { | |||
| using namespace std::string_literals; | |||
| auto& s = _path.str(); | |||
| if (unlink(s.c_str())) | |||
| throw cppcore::error_exception("Unable to remove file: "s + _path.str(), errno); | |||
| return *this; | |||
| } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| #pragma once | |||
| #include "misc/misc.h" | |||
| #if cppfs_os == cppfs_os_linux | |||
| #include "misc/misc.linux.inl" | |||
| #endif | |||
| @@ -0,0 +1,65 @@ | |||
| #pragma once | |||
| #include <cppcore/misc/flags.h> | |||
| #if cppfs_os == cppfs_os_linux | |||
| #include <sys/stat.h> | |||
| #endif | |||
| namespace cppfs | |||
| { | |||
| enum class file_type | |||
| { | |||
| unknown = 0, | |||
| directory, | |||
| file, | |||
| symbolic_link, | |||
| hard_link, | |||
| block_device, | |||
| char_device, | |||
| named_pipe, | |||
| socket, | |||
| }; | |||
| enum class file_permission | |||
| { | |||
| unknown = 0, | |||
| user_read, | |||
| user_write, | |||
| user_execute, | |||
| group_read, | |||
| group_write, | |||
| group_execute, | |||
| others_read, | |||
| others_write, | |||
| others_execute, | |||
| }; | |||
| struct file_permissions | |||
| : public cppcore::shifted_flags<file_permission> | |||
| { | |||
| public: | |||
| using base_type = cppcore::shifted_flags<file_permission>; | |||
| public: | |||
| using base_type::base_type; | |||
| #if cppfs_os == cppfs_os_linux | |||
| public: | |||
| /** | |||
| * @brief Convert file permissions to unix permissions. | |||
| */ | |||
| static inline mode_t to_unix(const file_permissions& p_perm); | |||
| /** | |||
| * @brief Convert unix permissions to file permissions. | |||
| */ | |||
| static inline file_permissions from_unix(mode_t p_perm); | |||
| #endif | |||
| }; | |||
| } | |||
| @@ -0,0 +1,46 @@ | |||
| #pragma once | |||
| #include "misc.h" | |||
| namespace cppfs | |||
| { | |||
| mode_t file_permissions::to_unix(const file_permissions& p_perm) | |||
| { | |||
| mode_t perm = 0; | |||
| if (p_perm.is_set(file_permission::user_read)) perm |= S_IRUSR; | |||
| if (p_perm.is_set(file_permission::user_write)) perm |= S_IWUSR; | |||
| if (p_perm.is_set(file_permission::user_execute)) perm |= S_IXUSR; | |||
| if (p_perm.is_set(file_permission::group_read)) perm |= S_IRGRP; | |||
| if (p_perm.is_set(file_permission::group_write)) perm |= S_IWGRP; | |||
| if (p_perm.is_set(file_permission::group_execute)) perm |= S_IXGRP; | |||
| if (p_perm.is_set(file_permission::others_read)) perm |= S_IROTH; | |||
| if (p_perm.is_set(file_permission::others_write)) perm |= S_IWOTH; | |||
| if (p_perm.is_set(file_permission::others_execute)) perm |= S_IXOTH; | |||
| return perm; | |||
| } | |||
| file_permissions file_permissions::from_unix(mode_t p_perm) | |||
| { | |||
| file_permissions perm; | |||
| if (p_perm & S_IRUSR) perm.set(file_permission::user_read); | |||
| if (p_perm & S_IWUSR) perm.set(file_permission::user_write); | |||
| if (p_perm & S_IXUSR) perm.set(file_permission::user_execute); | |||
| if (p_perm & S_IRGRP) perm.set(file_permission::group_read); | |||
| if (p_perm & S_IWGRP) perm.set(file_permission::group_write); | |||
| if (p_perm & S_IXGRP) perm.set(file_permission::group_execute); | |||
| if (p_perm & S_IROTH) perm.set(file_permission::others_read); | |||
| if (p_perm & S_IWOTH) perm.set(file_permission::others_write); | |||
| if (p_perm & S_IXOTH) perm.set(file_permission::others_execute); | |||
| return perm; | |||
| } | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| #pragma once | |||
| #include <cppfs/config.h> | |||
| #include "path/path.h" | |||
| #include "path/path.inl" | |||
| #if cppfs_os == cppfs_os_linux | |||
| #include "path/path.linux.inl" | |||
| #endif | |||
| @@ -0,0 +1,149 @@ | |||
| #pragma once | |||
| #include <string> | |||
| #include <vector> | |||
| #include "../misc.h" | |||
| namespace cppfs | |||
| { | |||
| enum class path_status | |||
| { | |||
| none = 0, | |||
| absoulte, | |||
| relative, | |||
| }; | |||
| struct path | |||
| { | |||
| public: | |||
| using string_vector = std::vector<std::string>; | |||
| public: | |||
| /** | |||
| * @brief Get the path of the current working directory. | |||
| */ | |||
| static inline path current(); | |||
| private: | |||
| path_status _status; | |||
| mutable std::string _path; | |||
| mutable string_vector _parts; | |||
| public: | |||
| /** | |||
| * @brief Default construtor. | |||
| */ | |||
| inline path(); | |||
| /** | |||
| * @brief Value construtor. | |||
| */ | |||
| inline path(const std::string& p_path); | |||
| /** | |||
| * @brief Value construtor. | |||
| */ | |||
| inline path( | |||
| string_vector&& p_parts, | |||
| path_status p_status); | |||
| /** | |||
| * @brief Value construtor. | |||
| */ | |||
| inline path( | |||
| const string_vector& p_parts, | |||
| path_status p_status = path_status::none); | |||
| /** | |||
| * @brief Value construtor. | |||
| */ | |||
| inline path( | |||
| const std::initializer_list<std::string>& p_parts, | |||
| path_status p_status = path_status::none); | |||
| /** | |||
| * @brief Move constructor. | |||
| */ | |||
| inline path(path&&) = default; | |||
| /** | |||
| * @brief Copy constructor. | |||
| */ | |||
| inline path(const path&) = default; | |||
| /** | |||
| * @brief Move assignment constructor. | |||
| */ | |||
| inline path& operator=(path&&) = default; | |||
| /** | |||
| * @brief Copy assignment constructor. | |||
| */ | |||
| inline path& operator=(const path&) = default; | |||
| public: | |||
| /** | |||
| * @brief Get the path status. | |||
| */ | |||
| inline path_status status() const; | |||
| /** | |||
| * @brief Get the type of the path. | |||
| */ | |||
| inline file_type type() const; | |||
| /** | |||
| * @brief Get the stored path as string. | |||
| */ | |||
| inline const std::string& str() const; | |||
| /** | |||
| * @brief Get the parts of the path. | |||
| */ | |||
| inline const string_vector& parts() const; | |||
| /** | |||
| * @brief Returns true if the path exists. | |||
| */ | |||
| inline bool exsists() const; | |||
| /** | |||
| * @brief Returns true if the path is a normal file. | |||
| */ | |||
| inline bool is_file() const; | |||
| /** | |||
| * @brief Returns true if the path is a normal directory. | |||
| */ | |||
| inline bool is_directory() const; | |||
| /** | |||
| * @brief Returns the base path of the current stored path. | |||
| */ | |||
| inline path base() const; | |||
| /** | |||
| * @brief Returns the normalized path. | |||
| */ | |||
| inline path normalize() const; | |||
| /** | |||
| * @brief Returns file extension. | |||
| */ | |||
| inline std::string extension() const; | |||
| /** | |||
| * @brief Combines two paths. | |||
| */ | |||
| inline path join(const path& other) const; | |||
| private: | |||
| /** | |||
| * @brief Create a path from the parts vector. | |||
| */ | |||
| std::string make_path() const; | |||
| }; | |||
| } | |||
| @@ -0,0 +1,122 @@ | |||
| #pragma once | |||
| #include "path.h" | |||
| namespace cppfs | |||
| { | |||
| /* path */ | |||
| path::path() | |||
| : _status (path_status::absoulte) | |||
| , _path () | |||
| , _parts () | |||
| { } | |||
| path::path( | |||
| string_vector&& p_parts, | |||
| path_status p_status) | |||
| : _status (p_status) | |||
| , _path () | |||
| , _parts (std::move(p_parts)) | |||
| { } | |||
| path::path( | |||
| const string_vector& p_parts, | |||
| path_status p_status) | |||
| : _status (p_status) | |||
| , _path () | |||
| , _parts (p_parts) | |||
| { } | |||
| path::path( | |||
| const std::initializer_list<std::string>& p_parts, | |||
| path_status p_status) | |||
| : _status (p_status) | |||
| , _path () | |||
| , _parts (std::begin(p_parts), std::end(p_parts)) | |||
| { } | |||
| path_status path::status() const | |||
| { return _status; } | |||
| const std::string& path::str() const | |||
| { | |||
| if (_path.empty()) | |||
| _path = make_path(); | |||
| return _path; | |||
| } | |||
| const path::string_vector& path::parts() const | |||
| { return _parts; } | |||
| bool path::exsists() const | |||
| { return type() != file_type::unknown; } | |||
| bool path::is_file() const | |||
| { | |||
| auto t = type(); | |||
| return t != file_type::unknown | |||
| && t != file_type::directory; | |||
| } | |||
| bool path::is_directory() const | |||
| { return type() == file_type::directory; } | |||
| path path::base() const | |||
| { | |||
| auto ret = normalize(); | |||
| if (!ret._parts.empty()) | |||
| ret._parts.pop_back(); | |||
| return ret; | |||
| } | |||
| path path::normalize() const | |||
| { | |||
| string_vector parts; | |||
| if (_status != path_status::absoulte) | |||
| parts = current().parts(); | |||
| for (auto& p : _parts) | |||
| { | |||
| if (p == ".." && !parts.empty()) | |||
| parts.pop_back(); | |||
| else if (p != ".") | |||
| parts.emplace_back(p); | |||
| } | |||
| return path(std::move(parts), path_status::absoulte); | |||
| } | |||
| std::string path::extension() const | |||
| { | |||
| if (_parts.empty()) | |||
| return std::string(); | |||
| auto& s = _parts.back(); | |||
| auto p = s.find_last_of('.'); | |||
| if (p == std::string::npos || p == 0) | |||
| return std::string(); | |||
| return s.substr(p + 1); | |||
| } | |||
| path path::join(const path& other) const | |||
| { | |||
| switch (other._status) | |||
| { | |||
| case path_status::absoulte: | |||
| return other; | |||
| default: | |||
| auto parts = _parts; | |||
| for (auto& p : other.parts()) | |||
| parts.emplace_back(p); | |||
| return path(std::move(parts), _status); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,118 @@ | |||
| #pragma once | |||
| #include <sstream> | |||
| #include <errno.h> | |||
| #include <stdlib.h> | |||
| #include <unistd.h> | |||
| #include <linux/limits.h> | |||
| #include <cppcore/conversion/string.h> | |||
| #include <cppfs/config.h> | |||
| #include "path.h" | |||
| namespace cppfs | |||
| { | |||
| path path::current() | |||
| { | |||
| char buf[PATH_MAX + 1]; | |||
| if (!getcwd(&buf[0], sizeof(buf)-1)) | |||
| throw cppcore::error_exception(errno); | |||
| return path(&buf[0]); | |||
| } | |||
| path::path(const std::string& p_path) | |||
| : _status (path_status::none) | |||
| , _path (p_path) | |||
| , _parts () | |||
| { | |||
| if (!_path.empty()) | |||
| { | |||
| if (_path[0] == '.') | |||
| { | |||
| _status = path_status::relative; | |||
| } | |||
| else if (_path[0] == '/' || _path[0] == '\\') | |||
| { | |||
| _status = path_status::absoulte; | |||
| } | |||
| } | |||
| bool ok = cppcore::string_split( | |||
| _path, | |||
| [](char c) { | |||
| return c == '\\' | |||
| || c == '/'; | |||
| }, | |||
| [this](auto&& s){ | |||
| if (_parts.empty() && s.empty()) | |||
| return true; | |||
| if (s != ".") | |||
| _parts.emplace_back(std::move(s)); | |||
| return true; | |||
| }); | |||
| if (!ok) | |||
| throw cppcore::exception("Error while splitting path into parts"); | |||
| } | |||
| file_type path::type() const | |||
| { | |||
| struct stat st; | |||
| auto& s = str(); | |||
| if (stat(s.c_str(), &st)) | |||
| return file_type::unknown; | |||
| switch (st.st_mode & S_IFMT) | |||
| { | |||
| case S_IFSOCK: return file_type::socket; | |||
| case S_IFLNK: return file_type::symbolic_link; | |||
| case S_IFREG: return file_type::file; | |||
| case S_IFBLK: return file_type::block_device; | |||
| case S_IFDIR: return file_type::directory; | |||
| case S_IFCHR: return file_type::char_device; | |||
| case S_IFIFO: return file_type::named_pipe; | |||
| default: return file_type::unknown; | |||
| } | |||
| } | |||
| std::string path::make_path() const | |||
| { | |||
| std::ostringstream ss; | |||
| switch (_status) | |||
| { | |||
| case path_status::none: | |||
| break; | |||
| case path_status::absoulte: | |||
| ss << constants::path_delimiter; | |||
| break; | |||
| case path_status::relative: | |||
| ss << '.' << constants::path_delimiter; | |||
| break; | |||
| } | |||
| bool first = true; | |||
| for (auto& part : _parts) | |||
| { | |||
| if (first) | |||
| first = false; | |||
| else | |||
| ss << constants::path_delimiter; | |||
| ss << part; | |||
| } | |||
| return ss.str(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| # Initialize ###################################################################################### | |||
| Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) | |||
| Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) | |||
| Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) | |||
| FInd_Package ( cppcore REQUIRED ) | |||
| # Interface Library ############################################################################### | |||
| Set ( CPPFS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) | |||
| Add_Library ( cppfs INTERFACE ) | |||
| Target_Include_Directories ( cppfs | |||
| INTERFACE | |||
| $<BUILD_INTERFACE:${CPPFS_INCLUDE_DIR}> | |||
| $<INSTALL_INTERFACE:${CPPFS_INSTALL_DIR_INCLUDE}> ) | |||
| Target_Link_Libraries ( cppfs | |||
| INTERFACE | |||
| cppcore::cppcore ) | |||
| # Install ######################################################################################### | |||
| Set ( CPPFS_HAS_EXPORT False PARENT_SCOPE ) | |||
| # Header | |||
| If ( CPPFS_INSTALL_HEADER ) | |||
| Set ( CPPFS_HAS_EXPORT True PARENT_SCOPE ) | |||
| Install ( FILES ${CPPFS_INCLUDE_DIR}/cppfs.h | |||
| DESTINATION ${CPPFS_INSTALL_DIR_INCLUDE} ) | |||
| Install ( DIRECTORY ${CPPFS_INCLUDE_DIR}/cppfs | |||
| DESTINATION ${CPPFS_INSTALL_DIR_INCLUDE} ) | |||
| Install ( TARGETS cppfs | |||
| EXPORT cppfs | |||
| DESTINATION ${CPPFS_INSTALL_DIR_INCLUDE} ) | |||
| EndIf ( ) | |||
| @@ -0,0 +1,59 @@ | |||
| # Initialize ###################################################################################### | |||
| Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) | |||
| Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) | |||
| Include ( cmake_tests OPTIONAL RESULT_VARIABLE HAS_CMAKE_TESTS ) | |||
| Find_Package ( Sanitizers QUIET ) | |||
| # Test ############################################################################################ | |||
| Find_Package ( GTest ) | |||
| If ( NOT "${GTest_FOUND}" ) | |||
| Return ( ) | |||
| EndIf ( ) | |||
| File ( GLOB_RECURSE CPPFS_TEST_HEADER_FILES | |||
| ${CMAKE_CURRENT_SOURCE_DIR}/*.h ) | |||
| File ( GLOB_RECURSE CPPFS_TEST_INLINE_FILES | |||
| ${CMAKE_CURRENT_SOURCE_DIR}/*.inl ) | |||
| File ( GLOB_RECURSE CPPFS_TEST_SOURCE_FILES | |||
| RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} | |||
| ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) | |||
| ForEach ( FILE IN LISTS CPPFS_TEST_SOURCE_FILES ) | |||
| # add test | |||
| Get_Filename_Component ( TEST_DIR ${FILE} DIRECTORY ) | |||
| Get_Filename_Component ( TEST_NAME ${FILE} NAME_WE ) | |||
| Set ( TEST_NAME "${TEST_DIR}/${TEST_NAME}" ) | |||
| String ( REPLACE "\\" "-" TEST_NAME "${TEST_NAME}" ) | |||
| String ( REPLACE "/" "-" TEST_NAME "${TEST_NAME}" ) | |||
| String ( REPLACE "_" "-" TEST_NAME "${TEST_NAME}" ) | |||
| Set ( TEST_NAME "test-${TEST_NAME}" ) | |||
| Add_Executable ( ${TEST_NAME} | |||
| EXCLUDE_FROM_ALL | |||
| ${CPPFS_TEST_HEADER_FILES} | |||
| ${CPPFS_TEST_INLINE_FILES} | |||
| ${FILE} ) | |||
| Target_Link_Libraries ( ${TEST_NAME} | |||
| PUBLIC | |||
| cppfs | |||
| GTest::Main ) | |||
| # Sanitizers | |||
| If ( Sanitizers_FOUND ) | |||
| Add_Sanitizers ( ${TEST_NAME} ) | |||
| EndIf ( ) | |||
| # pedantic | |||
| If ( HAS_PEDANTIC ) | |||
| Pedantic_Apply_Flags_Target ( ${TEST_NAME} ALL ) | |||
| EndIf ( ) | |||
| # test | |||
| If ( HAS_CMAKE_TESTS ) | |||
| Add_CMake_Test ( NAME ${TEST_NAME} TARGET ${TEST_NAME} GROUP cppfs ) | |||
| Else ( ) | |||
| Add_Test ( NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) | |||
| EndIf ( ) | |||
| EndForEach ( ) | |||
| @@ -0,0 +1,36 @@ | |||
| #include <gtest/gtest.h> | |||
| #include <cppfs.h> | |||
| using namespace ::testing; | |||
| using namespace ::cppfs; | |||
| TEST(directory_tests, create) | |||
| { | |||
| EXPECT_ANY_THROW(directory("/fuu/bar/baz").create(false)); | |||
| EXPECT_FALSE(directory("/tmp/fuu/bar/baz").exists()); | |||
| directory("/tmp/fuu/bar/baz").create(true).remove(false); | |||
| } | |||
| TEST(directory_tests, exists) | |||
| { | |||
| EXPECT_TRUE (directory("./cppfs").exists()); | |||
| EXPECT_FALSE(directory("./cppfs/directory_tests.cpp").exists()); | |||
| } | |||
| TEST(directory_tests, iterator) | |||
| { | |||
| directory dir("./cppfs"); | |||
| std::vector<std::string> files; | |||
| std::transform(dir.begin(), dir.end(), std::back_inserter(files), [](auto& e) { return e.name; }); | |||
| std::sort(files.begin(), files.end()); | |||
| EXPECT_EQ( | |||
| files, | |||
| std::vector<std::string>({ | |||
| "directory_tests.cpp", | |||
| "path_tests.cpp", | |||
| })); | |||
| } | |||
| @@ -0,0 +1,112 @@ | |||
| #include <gtest/gtest.h> | |||
| #include <cppfs.h> | |||
| using namespace ::testing; | |||
| using namespace ::cppfs; | |||
| TEST(path_tests, ctor_string) | |||
| { | |||
| path p("some/test/path"); | |||
| EXPECT_EQ(p.status(), path_status::none); | |||
| EXPECT_EQ(p.parts(), path::string_vector({ "some", "test", "path" })); | |||
| } | |||
| TEST(path_tests, ctor_string_relative) | |||
| { | |||
| path p0("./some/test/path"); | |||
| EXPECT_EQ(p0.status(), path_status::relative); | |||
| EXPECT_EQ(p0.parts(), path::string_vector({ "some", "test", "path" })); | |||
| path p1("../some/test/path"); | |||
| EXPECT_EQ(p1.status(), path_status::relative); | |||
| EXPECT_EQ(p1.parts(), path::string_vector({ "..", "some", "test", "path" })); | |||
| } | |||
| TEST(path_tests, ctor_string_absolute) | |||
| { | |||
| path p("/some/test/path"); | |||
| EXPECT_EQ(p.status(), path_status::absoulte); | |||
| EXPECT_EQ(p.parts(), path::string_vector({ "some", "test", "path" })); | |||
| } | |||
| TEST(path_tests, ctor_parts) | |||
| { | |||
| EXPECT_EQ("some/test/path", path({ "some", "test", "path" }, path_status::none).str()); | |||
| EXPECT_EQ("/some/test/path", path({ "some", "test", "path" }, path_status::absoulte).str()); | |||
| EXPECT_EQ("./some/test/path", path({ "some", "test", "path" }, path_status::relative).str()); | |||
| } | |||
| TEST(path_tests, exsists) | |||
| { | |||
| EXPECT_TRUE (path("./cppfs") | |||
| .exsists()); | |||
| EXPECT_TRUE (path("./cppfs/path_tests.cpp") | |||
| .exsists()); | |||
| EXPECT_TRUE (path("./cppfs/directory_tests.cpp") | |||
| .exsists()); | |||
| EXPECT_FALSE(path("./cppfs/asd") | |||
| .exsists()); | |||
| } | |||
| TEST(path_tests, is_file) | |||
| { | |||
| EXPECT_FALSE(path("./cppfs") | |||
| .is_file()); | |||
| EXPECT_TRUE (path("./cppfs/path_tests.cpp") | |||
| .is_file()); | |||
| EXPECT_TRUE (path("./cppfs/directory_tests.cpp") | |||
| .is_file()); | |||
| EXPECT_FALSE(path("./cppfs/asd") | |||
| .is_file()); | |||
| } | |||
| TEST(path_tests, is_directory) | |||
| { | |||
| EXPECT_TRUE (path("./cppfs") | |||
| .is_directory()); | |||
| EXPECT_FALSE(path("./cppfs/path_tests.cpp") | |||
| .is_directory()); | |||
| EXPECT_FALSE(path("./cppfs/directory_tests.cpp") | |||
| .is_directory()); | |||
| EXPECT_FALSE(path("./cppfs/asd") | |||
| .is_directory()); | |||
| } | |||
| TEST(path_tests, base) | |||
| { | |||
| auto cwd = path::current().str(); | |||
| EXPECT_EQ(path("./cppfs/fuuu/bar").base().str(), cwd + "/cppfs/fuuu"); | |||
| EXPECT_EQ(path("/fuu/bar/../baz").base().str(), "/fuu"); | |||
| } | |||
| TEST(path_tests, normalize) | |||
| { | |||
| auto cwd = path::current().str(); | |||
| EXPECT_EQ(path("fuu/../bar/./biz/baz/..").normalize().str(), cwd + "/bar/biz"); | |||
| EXPECT_EQ(path("fuu/../bar/./biz/baz/..").normalize().str(), cwd + "/bar/biz"); | |||
| EXPECT_EQ(path("/fuu/../bar/./biz/baz/..").normalize().str(), "/bar/biz"); | |||
| } | |||
| TEST(path_tests, extension) | |||
| { | |||
| EXPECT_EQ(path("fuu/fuu").extension(), ""); | |||
| EXPECT_EQ(path("fuu/.fuu").extension(), ""); | |||
| EXPECT_EQ(path("fuu/fuu.bar").extension(), "bar"); | |||
| EXPECT_EQ(path("fuu/fuu.bar.biz").extension(), "biz"); | |||
| } | |||
| TEST(path_tests, join) | |||
| { | |||
| using namespace std::string_literals; | |||
| EXPECT_EQ(path("fuu/fuu").join("/bar"s).str(), "/bar"); | |||
| EXPECT_EQ(path("fuu/fuu").join("./bar"s).str(), "fuu/fuu/bar"); | |||
| EXPECT_EQ(path("fuu/fuu").join("bar"s).str(), "fuu/fuu/bar"); | |||
| } | |||