# HG changeset patch # User David Demelier # Date 1508842340 -7200 # Node ID 16b9ebfd3f08da8d50a17c2f08ccd39e6a315aa6 # Parent 928a40398decea0d89d977cd8ec2c4878e403436 Irccd: get rid of fs.hpp, closes #594 diff -r 928a40398dec -r 16b9ebfd3f08 MIGRATING.md --- a/MIGRATING.md Mon Oct 23 21:30:17 2017 +0200 +++ b/MIGRATING.md Tue Oct 24 12:52:20 2017 +0200 @@ -32,3 +32,7 @@ - The method ElapsedTimer.reset has been removed, just use `start` instead when you want to accumulate time. + +#### Module Directory + + - The property `Directory.count` has been removed. diff -r 928a40398dec -r 16b9ebfd3f08 cmake/function/IrccdDefineTest.cmake --- a/cmake/function/IrccdDefineTest.cmake Mon Oct 23 21:30:17 2017 +0200 +++ b/cmake/function/IrccdDefineTest.cmake Tue Oct 24 12:52:20 2017 +0200 @@ -74,6 +74,7 @@ CMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}" CMAKE_CURRENT_BINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}" CMAKE_CURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}" + TESTS_BINARY_DIR="${tests_BINARY_DIR}" SOURCEDIR="${CMAKE_CURRENT_SOURCE_DIR}" BINARYDIR="${CMAKE_CURRENT_BINARY_DIR}" IRCCD_TESTS_DIRECTORY="${CMAKE_BINARY_DIR}/tests" diff -r 928a40398dec -r 16b9ebfd3f08 irccdctl/main.cpp --- a/irccdctl/main.cpp Mon Oct 23 21:30:17 2017 +0200 +++ b/irccdctl/main.cpp Tue Oct 24 12:52:20 2017 +0200 @@ -24,7 +24,6 @@ #include "alias.hpp" #include "cli.hpp" #include "client.hpp" -#include "fs.hpp" #include "ini.hpp" #include "irccdctl.hpp" #include "logger.hpp" diff -r 928a40398dec -r 16b9ebfd3f08 libcommon/CMakeLists.txt --- a/libcommon/CMakeLists.txt Mon Oct 23 21:30:17 2017 +0200 +++ b/libcommon/CMakeLists.txt Tue Oct 24 12:52:20 2017 +0200 @@ -22,7 +22,6 @@ set( HEADERS - ${libcommon_SOURCE_DIR}/irccd/fs.hpp ${libcommon_SOURCE_DIR}/irccd/ini.hpp ${libcommon_SOURCE_DIR}/irccd/logger.hpp ${libcommon_SOURCE_DIR}/irccd/net.hpp @@ -35,7 +34,6 @@ set( SOURCES - ${libcommon_SOURCE_DIR}/irccd/fs.cpp ${libcommon_SOURCE_DIR}/irccd/ini.cpp ${libcommon_SOURCE_DIR}/irccd/logger.cpp ${libcommon_SOURCE_DIR}/irccd/options.cpp diff -r 928a40398dec -r 16b9ebfd3f08 libcommon/irccd/fs.cpp --- a/libcommon/irccd/fs.cpp Mon Oct 23 21:30:17 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,448 +0,0 @@ -/* - * fs.cpp -- filesystem operations - * - * Copyright (c) 2013-2017 David Demelier - * - * 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) -# if !defined(_CRT_SECURE_NO_WARNINGS) -# define _CRT_SECURE_NO_WARNINGS -# endif -# if !defined(WIN32_LEAN_AND_MEAN) -# define WIN32_LEAN_AND_MEAN -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include - -#if defined(_WIN32) -# include -# include -# include -#else -# include -# include -# include -# include -#endif - -#include "fs.hpp" - -namespace irccd { - -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 -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 -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 -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; -} - -/* - * 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 - }); -} - -/* - * 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 - -/* - * readdir. - * ------------------------------------------------------------------ - */ -std::vector readdir(const std::string &path, int flags) -{ - std::vector 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 -} - -} // !irccd - -} // !fs diff -r 928a40398dec -r 16b9ebfd3f08 libcommon/irccd/fs.hpp --- a/libcommon/irccd/fs.hpp Mon Oct 23 21:30:17 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,277 +0,0 @@ -/* - * fs.hpp -- filesystem operations - * - * Copyright (c) 2013-2017 David Demelier - * - * 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 IRCCD_FS_HPP -#define IRCCD_FS_HPP - -/** - * \file fs.hpp - * \brief Filesystem operations made easy. - */ - -/** - * \cond FS_HIDDEN_SYMBOLS - */ - -#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 - -/** - * \endcond - */ - -#if defined(FS_HAVE_STAT) -# include -#endif - -#include -#include -#include - -#include "sysconfig.hpp" - -namespace irccd { - -/** - * \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 ".." -}; - -/** - * \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 - */ -IRCCD_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 - */ -IRCCD_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 - */ -IRCCD_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 - */ -IRCCD_EXPORT struct stat stat(const std::string &path); - -#endif // !HAVE_STAT - -/** - * Check if the file is readable. - * - * \param path the path - * \return true if has read access - */ -IRCCD_EXPORT bool isReadable(const std::string &path) noexcept; - -/** - * Check if the file is writable. - * - * \param path the path - * \return true if has write access - */ -IRCCD_EXPORT bool isWritable(const std::string &path) noexcept; - -/** - * 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 - */ -IRCCD_EXPORT std::vector readdir(const std::string &path, int flags = 0); - -/** - * 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 -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 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)); - - 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); }); -} - -} // !fs - -} // !irccd - -#endif // !IRCCD_FS_HPP diff -r 928a40398dec -r 16b9ebfd3f08 libcommon/irccd/system.cpp --- a/libcommon/irccd/system.cpp Mon Oct 23 21:30:17 2017 +0200 +++ b/libcommon/irccd/system.cpp Tue Oct 24 12:52:20 2017 +0200 @@ -100,7 +100,6 @@ # include #endif -#include "fs.hpp" #include "logger.hpp" #include "system.hpp" #include "util.hpp" diff -r 928a40398dec -r 16b9ebfd3f08 libcommon/irccd/util.hpp --- a/libcommon/irccd/util.hpp Mon Oct 23 21:30:17 2017 +0200 +++ b/libcommon/irccd/util.hpp Tue Oct 24 12:52:20 2017 +0200 @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -328,7 +329,7 @@ * \return true if integer */ IRCCD_EXPORT bool is_int(const std::string& value, int base = 10) noexcept; - + /** * Check if the string is real. * @@ -848,6 +849,101 @@ } // !poller +namespace fs { + +/** + * Get the base name from a path. + * + * Example, baseName("/etc/foo.conf") // foo.conf + * + * \param path the path + * \return the base name + */ +inline std::string base_name(const std::string& path) +{ + return boost::filesystem::path(path).filename().string(); +} + +/** + * Get the parent directory from a path. + * + * Example, dirName("/etc/foo.conf") // /etc + * + * \param path the path + * \return the parent directory + */ +inline std::string dir_name(std::string path) +{ + return boost::filesystem::path(path).parent_path().string(); +} + +/** + * Search an item recursively. + * + * The predicate must have the following signature: + * void f(const boost::filesystem::directory_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 + * \param recursive true to do recursive search + * \return the full path name to the file or empty string if never found + * \throw std::runtime_error on read errors + */ +template +std::string find_if(const std::string& base, bool recursive, Predicate&& predicate) +{ + auto find = [&] (auto it) -> std::string { + for (const auto& entry : it) + if (predicate(entry)) + return entry.path().string(); + + return ""; + }; + + if (recursive) + return find(boost::filesystem::recursive_directory_iterator(base)); + + return find(boost::filesystem::directory_iterator(base)); +} + +/** + * Find a file by name recursively. + * + * \param base the base directory + * \param name the file name + * \param recursive true to do recursive search + * \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, bool recursive = false) +{ + return find_if(base, recursive, [&] (const auto& entry) { + return entry.path().filename().string() == name; + }); +} + +/** + * Overload by regular expression. + * + * \param base the base directory + * \param regex the regular expression + * \param recursive true to do recursive search + * \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& regex, bool recursive = false) +{ + return find_if(base, recursive, [&] (const auto& entry) { + return std::regex_match(entry.path().filename().string(), regex); + }); +} + +} // !fs + } // !util } // !irccd diff -r 928a40398dec -r 16b9ebfd3f08 libirccd-js/irccd/js_directory_module.cpp --- a/libirccd-js/irccd/js_directory_module.cpp Mon Oct 23 21:30:17 2017 +0200 +++ b/libirccd-js/irccd/js_directory_module.cpp Tue Oct 24 12:52:20 2017 +0200 @@ -27,12 +27,13 @@ #include #include "duktape.hpp" -#include "fs.hpp" #include "js_directory_module.hpp" #include "js_irccd_module.hpp" #include "js_plugin.hpp" #include "sysconfig.hpp" +namespace fs = boost::filesystem; + namespace irccd { namespace { @@ -56,68 +57,6 @@ } /* - * Find an entry recursively (or not) in a directory using a predicate which can - * be used to test for regular expression, equality. - * - * Do not use this function directly, use: - * - * - find_name - * - find_regex - */ -template -std::string find_path(const std::string& base, bool recursive, Pred&& pred) -{ - /* - * For performance reason, we first iterate over all entries that are - * not directories to avoid going deeper recursively if the requested - * file is in the current directory. - */ - auto entries = fs::readdir(base); - - for (const auto& entry : entries) - if (entry.type != fs::Entry::Dir && pred(entry.name)) - return base + entry.name; - - if (!recursive) - return ""; - - for (const auto& entry : entries) { - if (entry.type == fs::Entry::Dir) { - std::string next = base + entry.name + fs::separator(); - std::string path = find_path(next, true, pred); - - if (!path.empty()) - return path; - } - } - - return ""; -} - -/* - * Helper for finding by equality. - */ -std::string find_name(std::string base, const std::string& pattern, bool recursive) -{ - return find_path(base, recursive, [&] (const auto& entryname) -> bool { - return pattern == entryname; - }); -} - -/* - * Helper for finding by regular expression - */ -std::string find_regex(const std::string& base, std::string pattern, bool recursive) -{ - std::regex regexp(pattern, std::regex::ECMAScript); - std::smatch smatch; - - return find_path(base, recursive, [&] (const auto& entryname) -> bool { - return std::regex_match(entryname, smatch, regexp); - }); -} - -/* * Generic find function for: * * - Directory.find @@ -132,7 +71,7 @@ std::string path; if (duk_is_string(ctx, pattern_index)) - path = find_name(base, duk_get_string(ctx, pattern_index), recursive); + path = util::fs::find(base, dukx_get_std_string(ctx, pattern_index), recursive); else { // Check if it's a valid RegExp object. duk_get_global_string(ctx, "RegExp"); @@ -144,7 +83,7 @@ auto pattern = duk_to_string(ctx, -1); duk_pop(ctx); - path = find_regex(base, pattern, recursive); + path = util::fs::find(base, std::regex(pattern), recursive); } else duk_error(ctx, DUK_ERR_TYPE_ERROR, "pattern must be a string or a regex expression"); } @@ -230,14 +169,13 @@ */ /* - * Function: irccd.Directory(path, flags) [constructor] + * Function: Irccd.Directory(path) [constructor] * -------------------------------------------------------- * * Opens and read the directory at the specified path. * * Arguments: * - path, the path to the directory, - * - flags, the optional flags (default: 0). * Throws: * - Any exception on error */ @@ -248,33 +186,32 @@ try { auto path = duk_require_string(ctx, 0); - auto flags = duk_get_uint(ctx, 1); if (!boost::filesystem::is_directory(path)) dukx_throw(ctx, system_error(EINVAL, "not a directory")); - auto list = fs::readdir(path, flags); + duk_push_this(ctx); - duk_push_this(ctx); - duk_push_string(ctx, "count"); - duk_push_int(ctx, list.size()); - duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - duk_push_string(ctx, "path"); - dukx_push_std_string(ctx, path); - duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + // 'entries' property. duk_push_string(ctx, "entries"); duk_push_array(ctx); - for (unsigned i = 0; i < list.size(); ++i) { + unsigned i = 0; + for (const auto& entry : boost::filesystem::directory_iterator(path)) { duk_push_object(ctx); - dukx_push_std_string(ctx, list[i].name); + dukx_push_std_string(ctx, entry.path().filename().string()); duk_put_prop_string(ctx, -2, "name"); - duk_push_int(ctx, list[i].type); + duk_push_int(ctx, entry.status().type()); duk_put_prop_string(ctx, -2, "type"); - duk_put_prop_index(ctx, -2, i); + duk_put_prop_index(ctx, -2, i++); } duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + + // 'path' property. + duk_push_string(ctx, "path"); + dukx_push_std_string(ctx, path); + duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); } catch (const std::exception& ex) { dukx_throw(ctx, system_error(errno, ex.what())); } @@ -348,13 +285,15 @@ }; const duk_number_list_entry constants[] = { - { "Dot", static_cast(fs::Dot) }, - { "DotDot", static_cast(fs::DotDot) }, - { "TypeUnknown", static_cast(fs::Entry::Unknown) }, - { "TypeDir", static_cast(fs::Entry::Dir) }, - { "TypeFile", static_cast(fs::Entry::File) }, - { "TypeLink", static_cast(fs::Entry::Link) }, - { nullptr, 0 } + { "TypeFile", static_cast(fs::regular_file) }, + { "TypeDir", static_cast(fs::directory_file) }, + { "TypeLink", static_cast(fs::symlink_file) }, + { "TypeBlock", static_cast(fs::block_file) }, + { "TypeCharacter", static_cast(fs::character_file) }, + { "TypeFifo", static_cast(fs::fifo_file) }, + { "TypeSocket", static_cast(fs::socket_file) }, + { "TypeUnknown", static_cast(fs::type_unknown) }, + { nullptr, 0 } }; } // !namespace @@ -372,8 +311,15 @@ duk_push_c_function(plugin->context(), constructor, 2); duk_put_number_list(plugin->context(), -1, constants); duk_put_function_list(plugin->context(), -1, functions); - dukx_push_std_string(plugin->context(), std::string{fs::separator()}); + +#if defined(IRCCD_SYSTEM_WINDOWS) + duk_push_string(plugin->context(), "\\"); +#else + duk_push_string(plugin->context(), "/"); +#endif + duk_put_prop_string(plugin->context(), -2, "separator"); + duk_push_object(plugin->context()); duk_put_function_list(plugin->context(), -1, methods); duk_put_prop_string(plugin->context(), -2, "prototype"); diff -r 928a40398dec -r 16b9ebfd3f08 libirccd-js/irccd/js_file_module.cpp --- a/libirccd-js/irccd/js_file_module.cpp Mon Oct 23 21:30:17 2017 +0200 +++ b/libirccd-js/irccd/js_file_module.cpp Tue Oct 24 12:52:20 2017 +0200 @@ -31,7 +31,6 @@ # include #endif -#include #include #include "js_file_module.hpp" @@ -153,7 +152,7 @@ */ duk_ret_t method_basename(duk_context* ctx) { - dukx_push_std_string(ctx, fs::baseName(self(ctx)->path())); + dukx_push_std_string(ctx, util::fs::base_name(self(ctx)->path())); return 1; } @@ -182,7 +181,7 @@ */ duk_ret_t method_dirname(duk_context* ctx) { - dukx_push_std_string(ctx, fs::dirName(self(ctx)->path())); + dukx_push_std_string(ctx, util::fs::dir_name(self(ctx)->path())); return 1; } @@ -519,7 +518,7 @@ */ duk_ret_t function_basename(duk_context* ctx) { - dukx_push_std_string(ctx, fs::baseName(duk_require_string(ctx, 0))); + dukx_push_std_string(ctx, util::fs::base_name(duk_require_string(ctx, 0))); return 1; } @@ -537,7 +536,7 @@ */ duk_ret_t function_dirname(duk_context* ctx) { - dukx_push_std_string(ctx, fs::dirName(duk_require_string(ctx, 0))); + dukx_push_std_string(ctx, util::fs::dir_name(duk_require_string(ctx, 0))); return 1; } diff -r 928a40398dec -r 16b9ebfd3f08 libirccd-js/irccd/js_plugin.cpp --- a/libirccd-js/irccd/js_plugin.cpp Mon Oct 23 21:30:17 2017 +0200 +++ b/libirccd-js/irccd/js_plugin.cpp Tue Oct 24 12:52:20 2017 +0200 @@ -26,7 +26,6 @@ # include #endif -#include "fs.hpp" #include "irccd.hpp" #include "logger.hpp" #include "js_plugin_module.hpp" diff -r 928a40398dec -r 16b9ebfd3f08 libirccd-test/irccd/js_test.hpp --- a/libirccd-test/irccd/js_test.hpp Mon Oct 23 21:30:17 2017 +0200 +++ b/libirccd-test/irccd/js_test.hpp Tue Oct 24 12:52:20 2017 +0200 @@ -70,6 +70,10 @@ add(); plugin_->on_load(irccd_); + + // Add some CMake variables. + duk_push_string(plugin_->context(), TESTS_BINARY_DIR); + duk_put_global_string(plugin_->context(), "TESTS_BINARY_DIR"); } }; diff -r 928a40398dec -r 16b9ebfd3f08 libirccd/irccd/config.cpp --- a/libirccd/irccd/config.cpp Mon Oct 23 21:30:17 2017 +0200 +++ b/libirccd/irccd/config.cpp Tue Oct 24 12:52:20 2017 +0200 @@ -21,7 +21,6 @@ #include #include "config.hpp" -#include "fs.hpp" #include "irccd.hpp" #include "logger.hpp" #include "plugin.hpp" diff -r 928a40398dec -r 16b9ebfd3f08 tests/CMakeLists.txt --- a/tests/CMakeLists.txt Mon Oct 23 21:30:17 2017 +0200 +++ b/tests/CMakeLists.txt Tue Oct 24 12:52:20 2017 +0200 @@ -20,6 +20,13 @@ project(tests) if (WITH_TESTS) + # Create some hierarchy. + file(MAKE_DIRECTORY ${tests_BINARY_DIR}/root) + file(MAKE_DIRECTORY ${tests_BINARY_DIR}/root/level-a) + file(MAKE_DIRECTORY ${tests_BINARY_DIR}/root/level-a/level-1) + file(WRITE ${tests_BINARY_DIR}/root/file-1.txt "\n") + file(WRITE ${tests_BINARY_DIR}/root/level-a/level-1/file-2.txt) + add_subdirectory(cmd-plugin-config) # add_subdirectory(cmd-plugin-info) # add_subdirectory(cmd-plugin-list) @@ -63,6 +70,7 @@ if (HAVE_JS) add_subdirectory(js) add_subdirectory(js-elapsedtimer) + add_subdirectory(js-directory) add_subdirectory(js-file) add_subdirectory(js-irccd) add_subdirectory(js-logger) diff -r 928a40398dec -r 16b9ebfd3f08 tests/js-directory/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/js-directory/CMakeLists.txt Tue Oct 24 12:52:20 2017 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2017 David Demelier +# +# 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. +# + +irccd_define_test( + NAME js-directory + SOURCES main.cpp + LIBRARIES libirccd-js +) diff -r 928a40398dec -r 16b9ebfd3f08 tests/js-directory/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/js-directory/main.cpp Tue Oct 24 12:52:20 2017 +0200 @@ -0,0 +1,51 @@ +/* + * main.cpp -- test Irccd.Directory API + * + * Copyright (c) 2013-2017 David Demelier + * + * 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. + */ + +#define BOOST_TEST_MODULE "Directory Javascript API" +#include + +#include + +#include + +namespace irccd { + +class directory_test : public js_test { +public: +}; + +BOOST_FIXTURE_TEST_SUITE(js_directory_suite, directory_test) + +BOOST_AUTO_TEST_CASE(constructor) +{ + const std::string script( + "d = new Irccd.Directory(TESTS_BINARY_DIR + \"/level-1\");" + "p = d.path;" + "e = d.entries;" + ); + + if (duk_peval_string(plugin_->context(), script.c_str()) != 0) + throw dukx_exception(plugin_->context(), -1); + + duk_get_global_string(plugin_->context(), "l"); + BOOST_TEST(duk_get_int(plugin_->context(), -1) == 2); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // !irccd diff -r 928a40398dec -r 16b9ebfd3f08 tests/js/main.cpp --- a/tests/js/main.cpp Mon Oct 23 21:30:17 2017 +0200 +++ b/tests/js/main.cpp Tue Oct 24 12:52:20 2017 +0200 @@ -20,7 +20,8 @@ #include #include -#include + +#include namespace irccd { @@ -56,7 +57,7 @@ dukx_peval_file(ctx_, SOURCEDIR "/syntax-error.js"); } catch (const Exception& ex) { BOOST_REQUIRE_EQUAL("SyntaxError", ex.name); - BOOST_REQUIRE_EQUAL("syntax-error.js", fs::baseName(ex.fileName)); + BOOST_REQUIRE_EQUAL("syntax-error.js", util::fs::base_name(ex.fileName)); BOOST_REQUIRE_EQUAL(6, ex.lineNumber); BOOST_REQUIRE_EQUAL("empty expression not allowed (line 6)", ex.message); } diff -r 928a40398dec -r 16b9ebfd3f08 tests/util/main.cpp --- a/tests/util/main.cpp Mon Oct 23 21:30:17 2017 +0200 +++ b/tests/util/main.cpp Tue Oct 24 12:52:20 2017 +0200 @@ -271,7 +271,7 @@ std::string result = util::strip(value); BOOST_REQUIRE_EQUAL("", result); -} +} BOOST_AUTO_TEST_SUITE_END() @@ -460,4 +460,58 @@ BOOST_AUTO_TEST_SUITE_END() +/* + * util::fs::find function (name) + * ------------------------------------------------------------------ + */ + +BOOST_AUTO_TEST_SUITE(fs_find_name) + +BOOST_AUTO_TEST_CASE(not_recursive) +{ + auto file1 = util::fs::find(CMAKE_CURRENT_BINARY_DIR "/root", "file-1.txt", false); + auto file2 = util::fs::find(CMAKE_CURRENT_BINARY_DIR "/root", "file-2.txt", false); + + BOOST_TEST(file1.find("file-1.txt") != std::string::npos); + BOOST_TEST(file2.empty()); +} + +BOOST_AUTO_TEST_CASE(recursive) +{ + auto file1 = util::fs::find(CMAKE_CURRENT_BINARY_DIR "/root", "file-1.txt", true); + auto file2 = util::fs::find(CMAKE_CURRENT_BINARY_DIR "/root", "file-2.txt", true); + + BOOST_TEST(file1.find("file-1.txt") != std::string::npos); + BOOST_TEST(file2.find("file-2.txt") != std::string::npos); +} + +BOOST_AUTO_TEST_SUITE_END() + +/* + * util::fs::find function (regex) + * ------------------------------------------------------------------ + */ + +BOOST_AUTO_TEST_SUITE(fs_find_regex) + +BOOST_AUTO_TEST_CASE(not_recursive) +{ + const std::regex regex("file-[12]\\.txt"); + + auto file = util::fs::find(TESTS_BINARY_DIR "/root", regex, false); + + BOOST_TEST(file.find("file-1.txt") != std::string::npos); +} + +BOOST_AUTO_TEST_CASE(recursive) +{ + const std::regex regex("file-[12]\\.txt"); + + auto file = util::fs::find(TESTS_BINARY_DIR "/root/level-a", regex, true); + + BOOST_TEST(file.find("file-2.txt") != std::string::npos); +} + +BOOST_AUTO_TEST_SUITE_END() + } // !irccd