瀏覽代碼

* Initial commit

* Implemented 'path' and 'directory' classes (including unit tests)
master
bergmann 4 年之前
當前提交
e88e0408e7
共有 29 個檔案被更改,包括 1451 行新增0 行删除
  1. +3
    -0
      .gitmodules
  2. +65
    -0
      CMakeLists.txt
  3. +3
    -0
      README.md
  4. +9
    -0
      cmake/cppfs-config.cmake
  5. +28
    -0
      cmake/cppfs-const.cmake
  6. +11
    -0
      cmake/cppfs-options.cmake
  7. +32
    -0
      cmake/cppfs-var.cmake
  8. +1
    -0
      cmake/modules
  9. +6
    -0
      include/cppfs.h
  10. +26
    -0
      include/cppfs/config.h
  11. +11
    -0
      include/cppfs/directory.h
  12. +125
    -0
      include/cppfs/directory/directory.h
  13. +81
    -0
      include/cppfs/directory/directory.inl
  14. +199
    -0
      include/cppfs/directory/directory.linux.inl
  15. +10
    -0
      include/cppfs/file.h
  16. +39
    -0
      include/cppfs/file/file.h
  17. +21
    -0
      include/cppfs/file/file.inl
  18. +21
    -0
      include/cppfs/file/file.linux.inl
  19. +7
    -0
      include/cppfs/misc.h
  20. +65
    -0
      include/cppfs/misc/misc.h
  21. +46
    -0
      include/cppfs/misc/misc.linux.inl
  22. +11
    -0
      include/cppfs/path.h
  23. +149
    -0
      include/cppfs/path/path.h
  24. +122
    -0
      include/cppfs/path/path.inl
  25. +118
    -0
      include/cppfs/path/path.linux.inl
  26. +35
    -0
      src/CMakeLists.txt
  27. +59
    -0
      test/CMakeLists.txt
  28. +36
    -0
      test/cppfs/directory_tests.cpp
  29. +112
    -0
      test/cppfs/path_tests.cpp

+ 3
- 0
.gitmodules 查看文件

@@ -0,0 +1,3 @@
[submodule "cmake/modules"]
path = cmake/modules
url = https://git.bergmann89.de/cpp/CMakeModules.git

+ 65
- 0
CMakeLists.txt 查看文件

@@ -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 )

+ 3
- 0
README.md 查看文件

@@ -0,0 +1,3 @@
# cppfs

Readme of the project.

+ 9
- 0
cmake/cppfs-config.cmake 查看文件

@@ -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")

+ 28
- 0
cmake/cppfs-const.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 )

+ 11
- 0
cmake/cppfs-options.cmake 查看文件

@@ -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 )

+ 32
- 0
cmake/cppfs-var.cmake 查看文件

@@ -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 ( )

+ 1
- 0
cmake/modules

@@ -0,0 +1 @@
Subproject commit 165bf7c7702ec184b46b046889b62ef4a63afccf

+ 6
- 0
include/cppfs.h 查看文件

@@ -0,0 +1,6 @@
#pragma once

#include <cppfs/config.h>
#include <cppfs/directory.h>
#include <cppfs/file.h>
#include <cppfs/path.h>

+ 26
- 0
include/cppfs/config.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

}

+ 11
- 0
include/cppfs/directory.h 查看文件

@@ -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

+ 125
- 0
include/cppfs/directory/directory.h 查看文件

@@ -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;
};

}

+ 81
- 0
include/cppfs/directory/directory.inl 查看文件

@@ -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); }

}

+ 199
- 0
include/cppfs/directory/directory.linux.inl 查看文件

@@ -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);
}

}

+ 10
- 0
include/cppfs/file.h 查看文件

@@ -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

+ 39
- 0
include/cppfs/file/file.h 查看文件

@@ -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;
};

}

+ 21
- 0
include/cppfs/file/file.inl 查看文件

@@ -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(); }

}

+ 21
- 0
include/cppfs/file/file.linux.inl 查看文件

@@ -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;
}

}

+ 7
- 0
include/cppfs/misc.h 查看文件

@@ -0,0 +1,7 @@
#pragma once

#include "misc/misc.h"

#if cppfs_os == cppfs_os_linux
#include "misc/misc.linux.inl"
#endif

+ 65
- 0
include/cppfs/misc/misc.h 查看文件

@@ -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
};

}

+ 46
- 0
include/cppfs/misc/misc.linux.inl 查看文件

@@ -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;
}

}

+ 11
- 0
include/cppfs/path.h 查看文件

@@ -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

+ 149
- 0
include/cppfs/path/path.h 查看文件

@@ -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;
};

}

+ 122
- 0
include/cppfs/path/path.inl 查看文件

@@ -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);
}
}

}

+ 118
- 0
include/cppfs/path/path.linux.inl 查看文件

@@ -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();
}

}

+ 35
- 0
src/CMakeLists.txt 查看文件

@@ -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 ( )

+ 59
- 0
test/CMakeLists.txt 查看文件

@@ -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 ( )

+ 36
- 0
test/cppfs/directory_tests.cpp 查看文件

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

+ 112
- 0
test/cppfs/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");
}

Loading…
取消
儲存