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