* 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"); | |||||
| } | |||||