Mercurial > code
changeset 519:d6dad57e9e6b
Fs: import
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 01 Jun 2016 16:35:01 +0200 |
parents | 78f296a7b2e5 |
children | b698e591b43a |
files | CMakeLists.txt modules/fs/CMakeLists.txt modules/fs/doc/mainpage.cpp modules/fs/fs.cpp modules/fs/fs.hpp modules/fs/test/main.cpp |
diffstat | 6 files changed, 1151 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Wed Jun 01 16:26:20 2016 +0200 +++ b/CMakeLists.txt Wed Jun 01 16:35:01 2016 +0200 @@ -44,4 +44,5 @@ add_subdirectory(modules/base64) add_subdirectory(modules/dynlib) +add_subdirectory(modules/fs) add_subdirectory(modules/options)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/fs/CMakeLists.txt Wed Jun 01 16:35:01 2016 +0200 @@ -0,0 +1,32 @@ +# +# CMakeLists.txt -- code building for common code +# +# Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +# +# Create fake hierarchy for tests +# +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/root) +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/root/level-1) +file(WRITE ${CMAKE_BINARY_DIR}/root/file-1.txt "Hello") +file(WRITE ${CMAKE_BINARY_DIR}/root/level-1/file-2.txt "World") + +code_define_module( + NAME fs + SOURCES fs.cpp fs.hpp + LIBRARIES $<$<BOOL:${WIN32}>:shlwapi> + FLAGS DIRECTORY=\"${CMAKE_BINARY_DIR}/root/\" +) \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/fs/doc/mainpage.cpp Wed Jun 01 16:35:01 2016 +0200 @@ -0,0 +1,67 @@ +/** + * \mainpage + * + * Welcome to the fs library. + * + * ## Introduction + * + * Various free functions for filesystem operations. No dependencies. + * + * Including: + * + * - Directory manipulation, + * - Basic path management (clean, baseName, dirName), + * - Checking access to files, + * - Convenient stat wrapper. + * + * ## Operating system support + * + * - Linux, + * - All BSD variants, + * - Windows (must link to shlwapi library). + * + * ## Installation + * + * Just copy the two files fs.hpp and fs.cpp and add them to your project. + * + * ## Overview + * + * ### Reading a directory + * + * ````cpp + * #include <iostream> + * + * #include "fs.hpp" + * + * int main() + * { + * try { + * for (const fs::Entry &e : fs::readdir("jokes")) + * std::cout << "entry: " << e.name << std::endl; + * } catch (const std::exception &error) { + * std::cerr << error.what() << std::endl; + * } + * + * return 0; + * } + * ```` + * + * ### Creating a directory + * + * ````cpp + * #include <iostream> + * + * #include "fs.hpp" + * + * int main() + * { + * try { + * fs::mkdir("tmp"); + * } catch (const std::exception &error) { + * std::cerr << error.what() << std::endl; + * } + * + * return 0; + * } + * ```` + */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/fs/fs.cpp Wed Jun 01 16:35:01 2016 +0200 @@ -0,0 +1,493 @@ +/* + * fs.cpp -- filesystem operations + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if defined(_WIN32) +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include <algorithm> +#include <cassert> +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <sstream> +#include <stdexcept> + +#if defined(_WIN32) +# include <direct.h> +# include <Windows.h> +# include <Shlwapi.h> +#else +# include <sys/types.h> +# include <dirent.h> +# include <unistd.h> +#endif + +#include "fs.hpp" + +namespace fs { + +namespace { + +/* + * error. + * ------------------------------------------------------------------ + * + * Function to retrieve system error in Windows API. + */ +#if defined(_WIN32) + +std::string error() +{ + LPSTR error = nullptr; + std::string errmsg = "Unknown error"; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + nullptr, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&error, 0, nullptr); + + if (error) { + errmsg = std::string(error); + LocalFree(error); + } + + return errmsg; +} + +#endif + +/* + * hasAccess. + * ------------------------------------------------------------------ + * + * Check if we have access to the file specified, mode is the same used as std::fopen. + */ +bool hasAccess(const std::string &path, const std::string &mode) +{ + assert(mode.length() == 1); + assert(mode[0] == 'r' || mode[0] == 'w'); + + auto fp = std::fopen(path.c_str(), mode.c_str()); + + if (fp == nullptr) + return false; + + std::fclose(fp); + + return true; +} + +/* + * typeOf. + * ------------------------------------------------------------------ + * + * Get the type of the specified file. + * + * Use GetFileAttributesA on Windows and stat if available. + * + * Receives the object as predicate parameter and return true on success. + */ +#if defined(_WIN32) + +template <typename Predicate> +bool typeOf(const std::string &path, Predicate &&predicate) +{ + DWORD result = GetFileAttributesA(path.c_str()); + + if (result == INVALID_FILE_ATTRIBUTES) + return false; + + return predicate(result); +} + +#elif defined(FS_HAVE_STAT) + +template <typename Predicate> +bool typeOf(const std::string &path, Predicate &&predicate) noexcept +{ + struct stat st; + + if (::stat(path.c_str(), &st) < 0) + return false; + + return predicate(st); +} + +#else + +template <typename Predicate> +bool typeOf(const std::string &path, Predicate &&predicate) noexcept +{ + throw std::runtime_error(std::strerror(ENOSYS)); +} + +#endif + +} // !namespace + +/* + * clean. + * ------------------------------------------------------------------ + */ +std::string clean(std::string input) +{ + if (input.empty()) + return input; + + /* First, remove any duplicates */ + input.erase(std::unique(input.begin(), input.end(), [&] (char c1, char c2) { + return c1 == c2 && (c1 == '/' || c1 == '\\'); + }), input.end()); + + /* Add a trailing / or \\ */ + char c = input[input.length() - 1]; + + if (c != '/' && c != '\\') + input += separator(); + + /* Now converts all / to \\ for Windows and the opposite for Unix */ +#if defined(_WIN32) + std::replace(input.begin(), input.end(), '/', '\\'); +#else + std::replace(input.begin(), input.end(), '\\', '/'); +#endif + + return input; +} + +/* + * baseName. + * ------------------------------------------------------------------ + */ +std::string baseName(std::string path) +{ + auto pos = path.find_last_of("\\/"); + + if (pos != std::string::npos) + path = path.substr(pos + 1); + + return path; +} + +/* + * dirName. + * ------------------------------------------------------------------ + */ +std::string dirName(std::string path) +{ + auto pos = path.find_last_of("\\/"); + + if (pos == std::string::npos) + path = "."; + else + path = path.substr(0, pos); + + return path; +} + +/* + * isAbsolute. + * ------------------------------------------------------------------ + */ +bool isAbsolute(const std::string &path) noexcept +{ +#if defined(_WIN32) + return !isRelative(path); +#else + return path.size() > 0 && path[0] == '/'; +#endif +} + +/* + * isRelative. + * ------------------------------------------------------------------ + */ +bool isRelative(const std::string &path) noexcept +{ +#if defined(_WIN32) + return PathIsRelativeA(path.c_str()) == 1; +#else + return !isAbsolute(path); +#endif +} + +/* + * isReadable. + * ------------------------------------------------------------------ + */ +bool isReadable(const std::string &path) noexcept +{ + return hasAccess(path, "r"); +} + +/* + * isWritable. + * ------------------------------------------------------------------ + */ +bool isWritable(const std::string &path) noexcept +{ + return hasAccess(path, "w"); +} + +/* + * isFile. + * ------------------------------------------------------------------ + */ +bool isFile(const std::string &path) +{ + return typeOf(path, [] (const auto &object) { +#if defined(_WIN32) + return (object & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE; +#elif defined(FS_HAVE_STAT) + return S_ISREG(object.st_mode); +#endif + }); +} + +/* + * isDirectory. + * ------------------------------------------------------------------ + */ +bool isDirectory(const std::string &path) +{ + return typeOf(path, [] (const auto &object) { +#if defined(_WIN32) + return (object & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; +#elif defined(FS_HAVE_STAT) + return S_ISDIR(object.st_mode); +#endif + }); +} + +/* + * isSymlink. + * ------------------------------------------------------------------ + */ +bool isSymlink(const std::string &path) +{ + return typeOf(path, [] (const auto &object) { +#if defined(_WIN32) + return (object & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT; +#elif defined(FS_HAVE_STAT) + return S_ISLNK(object.st_mode); +#endif + }); +} + +/* + * stat. + * ------------------------------------------------------------------ + */ +#if defined(FS_HAVE_STAT) + +struct stat stat(const std::string &path) +{ + struct stat st; + + if (::stat(path.c_str(), &st) < 0) + throw std::runtime_error(std::strerror(errno)); + + return st; +} + +#endif + +/* + * exists. + * ------------------------------------------------------------------ + */ +bool exists(const std::string &path) noexcept +{ +#if defined(FS_HAVE_STAT) + struct stat st; + + return ::stat(path.c_str(), &st) == 0; +#else + return hasAccess(path, "r"); +#endif +} + +/* + * readdir. + * ------------------------------------------------------------------ + */ +std::vector<Entry> readdir(const std::string &path, int flags) +{ + std::vector<Entry> entries; + +#if defined(_WIN32) + std::ostringstream oss; + HANDLE handle; + WIN32_FIND_DATA fdata; + + oss << path << "\\*"; + handle = FindFirstFile(oss.str().c_str(), &fdata); + + if (handle == nullptr) + throw std::runtime_error(error()); + + do { + Entry entry; + + entry.name = fdata.cFileName; + + if (entry.name == "." && !(flags & Dot)) + continue; + if (entry.name == ".." && !(flags & DotDot)) + continue; + + switch (fdata.dwFileAttributes) { + case FILE_ATTRIBUTE_DIRECTORY: + entry.type = Entry::Dir; + break; + case FILE_ATTRIBUTE_NORMAL: + entry.type = Entry::File; + break; + case FILE_ATTRIBUTE_REPARSE_POINT: + entry.type = Entry::Link; + break; + default: + break; + } + + entries.push_back(std::move(entry)); + } while (FindNextFile(handle, &fdata) != 0); + + FindClose(handle); +#else + DIR *dp; + struct dirent *ent; + + if ((dp = opendir(path.c_str())) == nullptr) + throw std::runtime_error(std::strerror(errno)); + + while ((ent = readdir(dp)) != nullptr) { + Entry entry; + + entry.name = ent->d_name; + if (entry.name == "." && !(flags & Dot)) + continue; + if (entry.name == ".." && !(flags & DotDot)) + continue; + + switch (ent->d_type) { + case DT_DIR: + entry.type = Entry::Dir; + break; + case DT_REG: + entry.type = Entry::File; + break; + case DT_LNK: + entry.type = Entry::Link; + break; + default: + break; + } + + entries.push_back(std::move(entry)); + } + + closedir(dp); +#endif + + return entries; +} + +/* + * mkdir. + * ------------------------------------------------------------------ + */ +void mkdir(const std::string &path, int mode) +{ + std::string::size_type next = 0; + std::string part; + + for (;;) { + next = path.find_first_of("\\/", next); + part = path.substr(0, next); + + if (!part.empty()) { +#if defined(_WIN32) + (void)mode; + + if (::_mkdir(part.c_str()) < 0 && errno != EEXIST) + throw std::runtime_error(std::strerror(errno)); +#else + if (::mkdir(part.c_str(), mode) < 0 && errno != EEXIST) + throw std::runtime_error(std::strerror(errno)); +#endif + } + + if (next++ == std::string::npos) + break; + } +} + +/* + * rmdir. + * ------------------------------------------------------------------ + */ +void rmdir(const std::string &base) noexcept +{ + try { + for (const auto &entry : readdir(base)) { + std::string path = base + separator() + entry.name; + + if (entry.type == Entry::Dir) + rmdir(path); + else + remove(path.c_str()); + } + } catch (...) { + /* Silently discard to remove as much as possible */ + } + +#if defined(_WIN32) + RemoveDirectoryA(base.c_str()); +#else + remove(base.c_str()); +#endif +} + +/* + * cwd. + * ------------------------------------------------------------------ + */ +std::string cwd() +{ +#if defined(_WIN32) + char path[MAX_PATH]; + + if (!GetCurrentDirectoryA(sizeof (path), path)) + throw std::runtime_error("failed to get current working directory"); + + return path; +#else + char path[PATH_MAX]; + + if (getcwd(path, sizeof (path)) == nullptr) + throw std::runtime_error{std::strerror(errno)}; + + return path; +#endif +} + +} // !fs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/fs/fs.hpp Wed Jun 01 16:35:01 2016 +0200 @@ -0,0 +1,364 @@ +/* + * fs.hpp -- filesystem operations + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef FS_HPP +#define FS_HPP + +/** + * \file fs.hpp + * \brief Filesystem operations made easy. + */ + +/** + * \page filesystem Filesystem + * \brief Filesystem support + * + * The following options can be set by the user: + * + * - **FS_HAVE_STAT**: (bool) Set to true if sys/stat.h and stat function are available, automatically detected. + * - **FS_BUILDING_DLL**: (bool) Set to true if building this code as DLL, see also FS_EXPORT, + * - **FS_EXPORT**: (unspecified) Attribute to export symbols, __declspec(dllexport) on Windows if FS_BUILDING_DLL is set. + */ + +#if !defined(FS_HAVE_STAT) +# if defined(_WIN32) +# define FS_HAVE_STAT +# elif defined(__linux__) +# define FS_HAVE_STAT +# elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +# define FS_HAVE_STAT +# elif defined(__APPLE__) +# define FS_HAVE_STAT +# endif +#endif + +/** + * \brief DLL export. + */ +#if !defined(FS_EXPORT) +# if defined(FS_BUILDING_DLL) && defined(_WIN32) +# define FS_EXPORT __declspec(dllexport) +# else +# define FS_EXPORT +# endif +#endif + +#if defined(FS_HAVE_STAT) +# include <sys/stat.h> +#endif + +#include <regex> +#include <string> +#include <vector> + +/** + * \brief Filesystem namespace. + */ +namespace fs { + +/** + * \enum Flags + * \brief Flags for readdir. + */ +enum Flags { + Dot = (1 << 0), //!< if set, also lists "." + DotDot = (1 << 1) //!< if set, also lists ".." +}; + +/** + * \class Entry + * \brief Entry in the directory list. + */ +class Entry { +public: + /** + * \brief Describe the type of an entry + */ + enum Type : char { + Unknown, //!< File type is unknown, + File, //!< File is regular type, + Dir, //!< File is directory, + Link //!< File is link + }; + + std::string name; //!< name of entry (base name) + Type type{Unknown}; //!< type of file +}; + +/** + * Check if two entries are identical. + * + * \param e1 the first entry + * \param e2 the second entry + * \return true if they are identical + */ +inline bool operator==(const Entry &e1, const Entry &e2) noexcept +{ + return e1.name == e2.name && e1.type == e2.type; +} + +/** + * Check if two entries are different. + * + * \param e1 the first entry + * \param e2 the second entry + * \return true if they are different + */ +inline bool operator!=(const Entry &e1, const Entry &e2) noexcept +{ + return !(e1 == e2); +} + +/** + * Get the separator for that system. + * + * \return \ on Windows and / otherwise + */ +inline char separator() noexcept +{ +#if defined(_WIN32) + return '\\'; +#else + return '/'; +#endif +} + +/** + * Clean a path by removing any extra / or \ and add a trailing one. + * + * \param path the path + * \return the updated path + */ +FS_EXPORT std::string clean(std::string path); + +/** + * Get the base name from a path. + * + * Example, baseName("/etc/foo.conf") // foo.conf + * + * \param path the path + * \return the base name + */ +FS_EXPORT std::string baseName(std::string path); + +/** + * Get the parent directory from a path. + * + * Example, dirName("/etc/foo.conf") // /etc + * + * \param path the path + * \return the parent directory + */ +FS_EXPORT std::string dirName(std::string path); + +#if defined(FS_HAVE_STAT) + +/** + * Get stat information. + * + * \param path the path + * \return the stat information + * \throw std::runtime_error on failure + */ +FS_EXPORT struct stat stat(const std::string &path); + +#endif // !HAVE_STAT + +/** + * Check if a file exists. + * + * If HAVE_ACCESS is defined, the function access is used, otherwise stat is used. + * + * \param path the path to check + * \return true if the path exists + */ +FS_EXPORT bool exists(const std::string &path) noexcept; + +/** + * Check if the path is absolute. + * + * \param path the path + * \return true if the path is absolute + */ +FS_EXPORT bool isAbsolute(const std::string &path) noexcept; + +/** + * Check if the path is relative. + * + * \param path the path + * \return true if the path is absolute + */ +FS_EXPORT bool isRelative(const std::string &path) noexcept; + +/** + * Check if the file is readable. + * + * \param path the path + * \return true if has read access + */ +FS_EXPORT bool isReadable(const std::string &path) noexcept; + +/** + * Check if the file is writable. + * + * \param path the path + * \return true if has write access + */ +FS_EXPORT bool isWritable(const std::string &path) noexcept; + +/** + * Check if the file is a regular file. + * + * \param path the path + * \return true if it is a file and false if not or not readable + * \throw std::runtime_error if the operation is not supported + */ +FS_EXPORT bool isFile(const std::string &path); + +/** + * Check if the file is a directory. + * + * \param path the path + * \return true if it is a directory and false if not or not readable + * \throw std::runtime_error if the operation is not supported + */ +FS_EXPORT bool isDirectory(const std::string &path); + +/** + * Check if the file is a symbolic link. + * + * \param path the path + * \return true if it is a symbolic link and false if not or not readable + * \throw std::runtime_error if the operation is not supported + */ +FS_EXPORT bool isSymlink(const std::string &path); + +/** + * Read a directory and return a list of entries (not recursive). + * + * \param path the directory path + * \param flags the optional flags (see Flags) + * \return the list of entries + * \throw std::runtime_error on failure + */ +FS_EXPORT std::vector<Entry> readdir(const std::string &path, int flags = 0); + +/** + * Create a directory recursively. + * + * \param path the path + * \param mode the optional mode (not always supported) + * \throw std::runtime_error on failure + * \post all intermediate directories are created + */ +FS_EXPORT void mkdir(const std::string &path, int mode = 0700); + +/** + * Remove a directory recursively. + * + * If errors happens, they are silently discarded to remove as much as possible. + * + * \param path the path + */ +FS_EXPORT void rmdir(const std::string &path) noexcept; + +/** + * Search an item recursively. + * + * The predicate must have the following signature: + * void f(const std::string &base, const Entry &entry) + * + * Where: + * - base is the current parent directory in the tree + * - entry is the current entry + * + * \param base the base directory + * \param predicate the predicate + * \return the full path name to the file or empty string if never found + * \throw std::runtime_error on read errors + */ +template <typename Predicate> +std::string findIf(const std::string &base, Predicate &&predicate) +{ + /* + * Do not go deeply to the tree before testing all files in the current directory for performances reasons, we iterate + * this directory to search for the entry name and iterate again over all sub directories if not found. + */ + std::string path; + std::vector<Entry> entries = readdir(base); + + for (const auto &entry : entries) { + if (predicate(base, entry)) { + path = base + separator() + entry.name; + break; + } + } + + if (!path.empty()) + return path; + + for (const auto &entry : entries) { + if (entry.type != Entry::Dir) + continue; + + path = findIf(base + separator() + entry.name, std::forward<Predicate>(predicate)); + + if (!path.empty()) + break; + } + + return path; +} + +/** + * Find a file by name recursively. + * + * \param base the base directory + * \param name the file name + * \return the full path name to the file or empty string if never found + * \throw std::runtime_error on read errors + */ +inline std::string find(const std::string &base, const std::string &name) +{ + return findIf(base, [&] (const auto &, const auto &entry) { return entry.name == name; }); +} + +/** + * Overload by regular expression. + * + * \param base the base directory + * \param regex the regular expression + * \return the full path name to the file or empty string if never found + * \throw std::runtime_error on read errors + */ +inline std::string find(const std::string &base, const std::regex ®ex) +{ + return findIf(base, [&] (const auto &, const auto &entry) { return std::regex_match(entry.name, regex); }); +} + +/** + * Get the current working directory. + * + * \return the current working directory + * \throw std::runtime_error on failure + */ +FS_EXPORT std::string cwd(); + +} // !fs + +#endif // !FS_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/fs/test/main.cpp Wed Jun 01 16:35:01 2016 +0200 @@ -0,0 +1,194 @@ +/* + * main.cpp -- test directory + * + * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sstream> + +#include <gtest/gtest.h> + +#include <fs.hpp> + +TEST(Symbols, entry) +{ + return; + + FAIL() << "must not go here"; + + fs::Entry e1, e2; + + (void)(e1 == e2); + (void)(e1 != e2); +} + +TEST(Symbols, all) +{ + return; + + FAIL() << "must not go here"; + + (void)fs::separator(); + (void)fs::clean(""); + (void)fs::baseName(""); + (void)fs::dirName(""); + (void)fs::stat(""); + (void)fs::exists(""); + (void)fs::isAbsolute(""); + (void)fs::isRelative(""); + (void)fs::isReadable(""); + (void)fs::isWritable(""); + (void)fs::isFile(""); + (void)fs::isDirectory(""); + (void)fs::isSymlink(""); + (void)fs::readdir(""); + (void)fs::mkdir(""); + (void)fs::rmdir(""); + (void)fs::cwd(); +} + +TEST(Filter, withDot) +{ + try { + bool dot(false); + bool dotdot(false); + + for (const auto &entry : fs::readdir(".", fs::Dot)) { + if (entry.name == ".") + dot = true; + if (entry.name == "..") + dotdot = true; + } + + ASSERT_TRUE(dot); + ASSERT_FALSE(dotdot); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(Filter, withDotDot) +{ + try { + bool dot(false); + bool dotdot(false); + + for (const auto &entry : fs::readdir(".", fs::DotDot)) { + if (entry.name == ".") + dot = true; + if (entry.name == "..") + dotdot = true; + } + + ASSERT_FALSE(dot); + ASSERT_TRUE(dotdot); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(Filter, withBothDots) +{ + try { + bool dot(false); + bool dotdot(false); + + for (const auto &entry : fs::readdir(".", fs::Dot | fs::DotDot)) { + if (entry.name == ".") + dot = true; + if (entry.name == "..") + dotdot = true; + } + + ASSERT_TRUE(dot); + ASSERT_TRUE(dotdot); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(Filter, withoutDots) +{ + try { + bool dot(false); + bool dotdot(false); + + for (const auto &entry : fs::readdir(".")) { + if (entry.name == ".") + dot = true; + if (entry.name == "..") + dotdot = true; + } + + ASSERT_FALSE(dot); + ASSERT_FALSE(dotdot); + } catch (const std::exception &error) { + FAIL() << error.what(); + } +} + +TEST(Exists, yes) +{ + ASSERT_TRUE(fs::exists(DIRECTORY "file-1.txt")); +} + +TEST(Exists, no) +{ + ASSERT_FALSE(fs::exists(DIRECTORY "does not exists")); +} + +TEST(IsFile, yes) +{ + ASSERT_TRUE(fs::isFile(__FILE__)); +} + +TEST(IsFile, no) +{ + ASSERT_FALSE(fs::isFile(DIRECTORY)); +} + +TEST(IsDirectory, yes) +{ + ASSERT_TRUE(fs::isDirectory(DIRECTORY)); +} + +TEST(IsDirectory, no) +{ + ASSERT_FALSE(fs::isDirectory(__FILE__)); +} + +TEST(RemoveDirectory, basic) +{ + try { + std::string path = DIRECTORY "mkdir-test"; + + fs::mkdir(path); + + ASSERT_TRUE(fs::isDirectory(path)); + + fs::rmdir(path); + + ASSERT_FALSE(fs::exists(path)); + } catch (const std::exception &ex) { + FAIL() << ex.what(); + } +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}