changeset 82:5779d53ea67b

Irccd: merge directory/filesystem with libfs, #451 - Irccd.Directory constructor now fails if the given path is not a directory, no breaking changes as the constructor could already throw before, - Irccd.Directory.find now returns undefined if not found and not recursive, - More checks to avoid using functions on non Directory objects.
author David Demelier <markand@malikania.fr>
date Thu, 31 Mar 2016 13:27:05 +0200
parents 969f30acfb0e
children f1a3026027f1
files doc/html/api/module/Irccd.Directory/function/find.md doc/html/api/module/Irccd.Directory/method/find.md lib/irccd/CMakeSources.cmake lib/irccd/directory.cpp lib/irccd/directory.h lib/irccd/filesystem.cpp lib/irccd/filesystem.h lib/irccd/fs.cpp lib/irccd/fs.h lib/irccd/irccd.cpp lib/irccd/irccdctl.cpp lib/irccd/js-directory.cpp lib/irccd/js-file.cpp lib/irccd/path.cpp lib/irccd/plugin.cpp lib/irccd/system.cpp
diffstat 16 files changed, 768 insertions(+), 622 deletions(-) [+]
line wrap: on
line diff
--- a/doc/html/api/module/Irccd.Directory/function/find.md	Thu Mar 31 12:39:08 2016 +0200
+++ b/doc/html/api/module/Irccd.Directory/function/find.md	Thu Mar 31 13:27:05 2016 +0200
@@ -6,5 +6,7 @@
   - "**path**: the base path,"
   - "**pattern**: the regular expression or file name,"
   - "**recursive**: set to true to search recursively (Optional, default: false)."
-returns: "The path to the file or undefined on errors or not found."
+returns: "The path to the file or undefined if not found."
+throws:
+  - "Any exception on error."
 ---
--- a/doc/html/api/module/Irccd.Directory/method/find.md	Thu Mar 31 12:39:08 2016 +0200
+++ b/doc/html/api/module/Irccd.Directory/method/find.md	Thu Mar 31 13:27:05 2016 +0200
@@ -5,5 +5,7 @@
 arguments:
   - "**pattern**: the regular expression or file name,"
   - "**recursive**: set to true to search recursively (Optional, default: false)."
-returns: "The path to the file or undefined on errors or not found"
+returns: "The path to the file or undefined if not found."
+throws:
+  - "Any exception on error."
 ---
--- a/lib/irccd/CMakeSources.cmake	Thu Mar 31 12:39:08 2016 +0200
+++ b/lib/irccd/CMakeSources.cmake	Thu Mar 31 13:27:05 2016 +0200
@@ -32,9 +32,8 @@
 	${CMAKE_CURRENT_LIST_DIR}/cmd-watch.h
 	${CMAKE_CURRENT_LIST_DIR}/command.h
 	${CMAKE_CURRENT_LIST_DIR}/config.h
-	${CMAKE_CURRENT_LIST_DIR}/directory.h
 	${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.h
-	${CMAKE_CURRENT_LIST_DIR}/filesystem.h
+	${CMAKE_CURRENT_LIST_DIR}/fs.h
 	${CMAKE_CURRENT_LIST_DIR}/ini.h
 	${CMAKE_CURRENT_LIST_DIR}/irccd.h
 	${CMAKE_CURRENT_LIST_DIR}/irccdctl.h
@@ -99,9 +98,8 @@
 	${CMAKE_CURRENT_LIST_DIR}/cmd-server-topic.cpp
 	${CMAKE_CURRENT_LIST_DIR}/cmd-watch.cpp
 	${CMAKE_CURRENT_LIST_DIR}/command.cpp
-	${CMAKE_CURRENT_LIST_DIR}/directory.cpp
 	${CMAKE_CURRENT_LIST_DIR}/elapsed-timer.cpp
-	${CMAKE_CURRENT_LIST_DIR}/filesystem.cpp
+	${CMAKE_CURRENT_LIST_DIR}/fs.cpp
 	${CMAKE_CURRENT_LIST_DIR}/ini.cpp
 	${CMAKE_CURRENT_LIST_DIR}/irccd.cpp
 	${CMAKE_CURRENT_LIST_DIR}/irccdctl.cpp
--- a/lib/irccd/directory.cpp	Thu Mar 31 12:39:08 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-/*
- * directory.cpp -- open and read directories
- *
- * 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.
- */
-
-#include <sstream>
-#include <stdexcept>
-
-#include "directory.h"
-
-#if defined(_WIN32)
-#  include <Windows.h>
-#else
-#  include <cstring>
-#  include <cerrno>
-
-#  include <sys/types.h>
-#  include <dirent.h>
-#endif
-
-namespace irccd {
-
-#if defined(_WIN32)
-
-namespace {
-
-std::string systemError()
-{
-	LPSTR error = nullptr;
-	std::string errmsg = "Unknown error";
-
-	FormatMessageA(
-		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-		NULL,
-		GetLastError(),
-		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-		(LPSTR)&error, 0, NULL);
-
-	if (error) {
-		errmsg = std::string(error);
-		LocalFree(error);
-	}
-
-	return errmsg;
-}
-
-} // !namespace
-
-void Directory::systemLoad(const std::string &path, int flags)
-{
-	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(systemError());
-
-	do {
-		DirectoryEntry entry;
-
-		entry.name = fdata.cFileName;
-		if (entry.name == "." && !(flags & Directory::Dot))
-			continue;
-		if (entry.name == ".." && !(flags & Directory::DotDot))
-			continue;
-
-		switch (fdata.dwFileAttributes) {
-		case FILE_ATTRIBUTE_DIRECTORY:
-			entry.type = DirectoryEntry::Dir;
-			break;
-		case FILE_ATTRIBUTE_NORMAL:
-			entry.type = DirectoryEntry::File;
-			break;
-		case FILE_ATTRIBUTE_REPARSE_POINT:
-			entry.type = DirectoryEntry::Link;
-			break;
-		default:
-			break;
-		}
-
-		m_list.push_back(entry);
-	} while (FindNextFile(handle, &fdata) != 0);
-
-	FindClose(handle);
-}
-
-#else
-
-void Directory::systemLoad(const std::string &path, int flags)
-{
-	DIR *dp;
-	struct dirent *ent;
-
-	if ((dp = opendir(path.c_str())) == nullptr)
-		throw std::runtime_error(std::strerror(errno));
-
-	while ((ent = readdir(dp)) != nullptr) {
-		DirectoryEntry entry;
-
-		entry.name = ent->d_name;
-		if (entry.name == "." && !(flags & Directory::Dot))
-			continue;
-		if (entry.name == ".." && !(flags & Directory::DotDot))
-			continue;
-
-		switch (ent->d_type) {
-		case DT_DIR:
-			entry.type = DirectoryEntry::Dir;
-			break;
-		case DT_REG:
-			entry.type = DirectoryEntry::File;
-			break;
-		case DT_LNK:
-			entry.type = DirectoryEntry::Link;
-			break;
-		default:
-			break;
-		}
-
-		m_list.push_back(entry);
-	}
-
-	closedir(dp);
-}
-
-#endif
-
-bool operator==(const DirectoryEntry &e1, const DirectoryEntry &e2)
-{
-	return e1.name == e2.name && e1.type == e2.type;
-}
-
-Directory::Directory()
-{
-}
-
-Directory::Directory(const std::string &path, int flags)
-{
-	systemLoad(path, flags);
-}
-
-Directory::List::iterator Directory::begin()
-{
-	return m_list.begin();
-}
-
-Directory::List::const_iterator Directory::cbegin() const
-{
-	return m_list.cbegin();
-}
-
-Directory::List::iterator Directory::end()
-{
-	return m_list.end();
-}
-
-Directory::List::const_iterator Directory::cend() const
-{
-	return m_list.cend();
-}
-
-int Directory::count() const
-{
-	return m_list.size();
-}
-
-bool operator==(const Directory &d1, const Directory &d2)
-{
-	return d1.m_list == d2.m_list;
-}
-
-} // !irccd
--- a/lib/irccd/directory.h	Thu Mar 31 12:39:08 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * directory.h -- open and read directories
- *
- * 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 _DIRECTORY_H_
-#define _DIRECTORY_H_
-
-#include <cstddef>
-#include <string>
-#include <vector>
-
-namespace irccd {
-
-/**
- * @class Entry
- * @brief entry in the directory list
- */
-class DirectoryEntry {
-public:
-	/**
-	 * @enum Type
-	 * @brief Describe the type of an entry
-	 */
-	enum Type {
-		Unknown		= 0,
-		File,
-		Dir,
-		Link
-	};
-
-	std::string	name;		//! name of entry (base name)
-	Type		type{Unknown};	//! type of file
-
-	friend bool operator==(const DirectoryEntry &e1, const DirectoryEntry &e2);
-};
-
-/**
- * @class Directory
- * @brief class to manipulate directories
- *
- * This class allow the user to iterate directories in a for range based
- * loop using iterators.
- */
-class Directory {
-public:
-	/**
-	 * @enum Flags
-	 * @brief optional flags to read directories
-	 */
-	enum Flags {
-		Dot	= (1 << 0),	//!< If set, lists "." too
-		DotDot	= (1 << 1)	//!< If set, lists ".." too
-	};
-
-	using List = std::vector<DirectoryEntry>;
-
-	// C++ Container compatibility
-	using value_type	= List::value_type;
-	using iterator		= List::iterator;
-	using const_iterator	= List::const_iterator;
-
-private:
-	List m_list;
-
-	void systemLoad(const std::string &path, int flags);
-
-public:
-	/**
-	 * Default constructor, does nothing.
-	 */
-	Directory();
-
-	/**
-	 * Open a directory and read all its content.
-	 * @param path the path
-	 * @param flags the optional flags
-	 */
-	Directory(const std::string &path, int flags = 0);
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~Directory() = default;
-
-	/**
-	 * Return an iterator the beginning.
-	 *
-	 * @return the iterator
-	 */
-	List::iterator begin();
-
-	/**
-	 * Return a const iterator the beginning.
-	 *
-	 * @return the iterator
-	 */
-	List::const_iterator cbegin() const;
-
-	/**
-	 * Return an iterator to past the end.
-	 *
-	 * @return the iterator
-	 */
-	List::iterator end();
-
-	/**
-	 * Return a const iterator to past the end.
-	 *
-	 * @return the iterator
-	 */
-	List::const_iterator cend() const;
-
-	/**
-	 * Get the number of entries in the directory.
-	 *
-	 * @return the number
-	 */
-	int count() const;
-
-	friend bool operator==(const Directory &d1, const Directory &d2);
-};
-
-} // !irccd
-
-#endif // !_DIRECTORY_H_
--- a/lib/irccd/filesystem.cpp	Thu Mar 31 12:39:08 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-/*
- * filesystem.cpp -- some file system operation
- *
- * 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.
- */
-
-#include <cerrno>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <stdexcept>
-#include <sstream>
-
-#include <irccd-config.h>
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-#  include <direct.h>
-#  include <shlwapi.h>
-#else
-#  include <sys/stat.h>
-#  include <climits>
-#  include <unistd.h>
-#  include <libgen.h>
-#endif
-
-#include "filesystem.h"
-
-namespace irccd {
-
-namespace fs {
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-const char Separator('\\');
-#else
-const char Separator('/');
-#endif
-
-std::string baseName(std::string path)
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	std::size_t pos;
-
-	pos = path.find_last_of('\\');
-	if (pos == std::string::npos)
-		pos = path.find_last_of('/');
-	if (pos == std::string::npos)
-		return path;
-
-	return path.substr(pos + 1);
-#else
-	return basename(&path[0]);
-#endif
-}
-
-std::string dirName(std::string path)
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	std::size_t pos;
-
-	pos = path.find_last_of('\\');
-	if (pos == std::string::npos)
-		pos = path.find_last_of('/');
-	if (pos == std::string::npos)
-		return path;
-
-	return path.substr(0, pos);
-#else
-	return dirname(&path[0]);
-#endif
-}
-
-bool isAbsolute(const std::string &path) noexcept
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	return !isRelative(path);
-#else
-	return path.size() > 0 && path[0] == '/';
-#endif
-}
-
-bool isRelative(const std::string &path) noexcept
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	return PathIsRelativeA(path.c_str());
-#else
-	return !isAbsolute(path);
-#endif
-}
-
-bool exists(const std::string &path)
-{
-#if defined(HAVE_ACCESS)
-	return access(path.c_str(), F_OK) == 0;
-#elif defined(HAVE_STAT)
-	struct stat st;
-
-	return (stat(path.c_str(), &st) == 0);
-#else
-	// worse fallback
-	std::FILE *file = std::fopen(path.c_str(), "r");
-	bool result;
-
-	if (file != nullptr) {
-		result = true;
-		std::fclose(file);
-	} else {
-		result = false;
-	}
-
-	return result;
-#endif
-}
-
-void mkdir(const std::string &dir, int mode)
-{
-	std::ostringstream oss;
-
-	oss << "mkdir: ";
-
-	for (std::size_t i = 0; i < dir.length(); ++i) {
-		if (dir[i] != '/' && dir[i] != '\\')
-			continue;
-
-		std::string part = dir.substr(0, i);
-		if (part.length() <= 0 || exists(part))
-			continue;
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-		if (::_mkdir(part.c_str()) < 0) {
-#else
-		if (::mkdir(part.c_str(), mode) < 0) {
-#endif
-			oss << part << ": " << std::strerror(errno);
-			throw std::runtime_error(oss.str());
-		}
-	}
-
-	// Last part
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	if (::_mkdir(dir.c_str()) < 0) {
-#else
-	if (::mkdir(dir.c_str(), mode) < 0) {
-#endif
-		oss << dir << ": " << std::strerror(errno);
-		throw std::runtime_error(oss.str());
-	}
-
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	// Windows's mkdir does not use mode.
-	(void)mode;
-#endif
-}
-
-std::string cwd()
-{
-#if defined(IRCCD_SYSTEM_WINDOWS)
-	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
-
-} // !irccd
--- a/lib/irccd/filesystem.h	Thu Mar 31 12:39:08 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * filesystem.h -- some file system operation
- *
- * 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 _IRCCD_FILESYSTEM_H_
-#define _IRCCD_FILESYSTEM_H_
-
-#include <string>
-
-namespace irccd {
-
-namespace fs {
-
-extern const char Separator;
-
-std::string baseName(std::string path);
-std::string dirName(std::string path);
-bool isAbsolute(const std::string &path) noexcept;
-bool isRelative(const std::string &path) noexcept;
-bool exists(const std::string &path);
-void mkdir(const std::string &dir, int mode = 0700);
-std::string cwd();
-
-} // !fs
-
-} // !irccd
-
-#endif // !_IRCCD_FILESYSTEM_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/fs.cpp	Thu Mar 31 13:27:05 2016 +0200
@@ -0,0 +1,383 @@
+/*
+ * 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.
+ */
+
+#include <algorithm>
+#include <cerrno>
+#include <cstdio>
+#include <cstring>
+#include <stdexcept>
+
+#if defined(_WIN32)
+#  include <direct.h>
+#  include <Windows.h>
+#  include <Shlwapi.h>
+#else
+#  include <sys/types.h>
+#  include <dirent.h>
+#endif
+
+#if defined(HAVE_ACCESS)
+#  include <unistd.h>
+#endif
+
+#include "fs.h"
+
+namespace irccd {
+
+namespace fs {
+
+namespace {
+
+#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
+
+bool can(const std::string &path, const std::string &mode)
+{
+	auto fp = std::fopen(path.c_str(), mode.c_str());
+	auto result = fp != nullptr;
+
+	std::fclose(fp);
+
+	return result;
+}
+
+#if defined(_WIN32)
+
+bool is(const std::string &path, DWORD flags)
+{
+	DWORD result = GetFileAttributes(path.c_str());
+
+	if (result == INVALID_FILE_ATTRIBUTES)
+		return false;
+
+	return result & flags;
+}
+
+#else
+
+template <typename Predicate>
+bool is(const std::string &path, Predicate &&predicate) noexcept
+{
+	struct stat st;
+
+	if (::stat(path.c_str(), &st) < 0)
+		return false;
+
+	return predicate(st);
+}
+
+#endif
+
+} // !namespace
+
+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;
+}
+
+std::string baseName(std::string path)
+{
+	auto pos = path.find_last_of("\\/");
+
+	if (pos != std::string::npos)
+		path = path.substr(0, pos + 1);
+
+	return path.substr(pos + 1);
+}
+
+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;
+}
+
+bool isAbsolute(const std::string &path) noexcept
+{
+#if defined(_WIN32)
+	return !isRelative(path);
+#else
+	return path.size() > 0 && path[0] == '/';
+#endif
+}
+
+bool isRelative(const std::string &path) noexcept
+{
+#if defined(_WIN32)
+	return PathIsRelativeA(path.c_str());
+#else
+	return !isAbsolute(path);
+#endif
+}
+
+bool isReadable(const std::string &path) noexcept
+{
+	return can(path, "r");
+}
+
+bool isWritable(const std::string &path) noexcept
+{
+	return can(path, "w");
+}
+
+bool isFile(const std::string &path) noexcept
+{
+#if defined(_WIN32)
+	return is(path, FILE_ATTRIBUTE_ARCHIVE);
+#else
+	return is(path, [] (const struct stat &st) { return S_ISREG(st.st_mode); });
+#endif
+}
+
+bool isDirectory(const std::string &path) noexcept
+{
+#if defined(_WIN32)
+	return is(path, FILE_ATTRIBUTE_DIRECTORY);
+#else
+	return is(path, [] (const struct stat &st) { return S_ISDIR(st.st_mode); });
+#endif
+}
+
+bool isSymlink(const std::string &path) noexcept
+{
+#if defined(_WIN32)
+	return is(path, FILE_ATTRIBUTE_REPARSE_POINT);
+#else
+	return is(path, [] (const struct stat &st) { return S_ISLNK(st.st_mode); });
+#endif
+}
+
+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;
+}
+
+bool exists(const std::string &path) noexcept
+{
+#if defined(HAVE_ACCESS)
+	return ::access(path.c_str(), F_OK) == 0;
+#else
+	struct stat st;
+
+	return ::stat(path.c_str(), &st) == 0;
+#endif
+}
+
+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;
+}
+
+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;
+	}
+}
+
+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
+				(void)::remove(path.c_str());
+		}
+	} catch (...) {
+		/* Silently discard to remove as much as possible */
+	}
+
+#if defined(_WIN32)
+	RemoveDirectory(base.c_str());
+#else
+	(void)::remove(base.c_str());
+#endif
+}
+
+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
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/fs.h	Thu Mar 31 13:27:05 2016 +0200
@@ -0,0 +1,320 @@
+/*
+ * fs.h -- 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 IRCCD_FS_H
+#define IRCCD_FS_H
+
+/**
+ * @file fs.h
+ * @brief Filesystem operations made easy.
+ *
+ * The following options can be set by the user:
+ *
+  * - **HAVE_ACCESS**: (bool) Set to true if unistd.h file and access(2) function are available.
+ *
+ * On Windows, you must link to shlwapi library.
+ */
+
+#include <sys/stat.h>
+
+#include <regex>
+#include <string>
+#include <vector>
+
+namespace irccd {
+
+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
+ */
+bool operator==(const Entry &e1, const Entry &e2) noexcept;
+
+/**
+ * Check if two entries are different.
+ *
+ * @param e1 the first entry
+ * @param e2 the second entry
+ * @return true if they are different
+ */
+bool operator!=(const Entry &e1, const Entry &e2) noexcept;
+
+/**
+ * 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
+ */
+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
+ */
+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
+ */
+std::string dirName(std::string path);
+
+/**
+ * Get stat information.
+ *
+ * @param path the path
+ * @return the stat information
+ * @throw std::runtime_error on failure
+ */
+struct stat stat(const std::string &path);
+
+/**
+ * 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
+ */
+bool exists(const std::string &path) noexcept;
+
+/**
+ * Check if the path is absolute.
+ *
+ * @param path the path
+ * @return true if the path is absolute
+ */
+bool isAbsolute(const std::string &path) noexcept;
+
+/**
+ * Check if the path is relative.
+ *
+ * @param path the path
+ * @return true if the path is absolute
+ */
+bool isRelative(const std::string &path) noexcept;
+
+/**
+ * Check if the file is readable.
+ *
+ * @param path the path
+ * @return true if has read access
+ */
+bool isReadable(const std::string &path) noexcept;
+
+/**
+ * Check if the file is writable.
+ *
+ * @param path the path
+ * @return true if has write access
+ */
+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
+ */
+bool isFile(const std::string &path) noexcept;
+
+/**
+ * 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
+ */
+bool isDirectory(const std::string &path) noexcept;
+
+/**
+ * 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
+ */
+bool isSymlink(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
+ */
+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
+ */
+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
+ */
+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()) {
+		for (const auto &entry : entries) {
+			if (entry.type == Entry::Dir) {
+				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 &regex)
+{
+	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
+ */
+std::string cwd();
+
+} // !fs
+
+} // !irccd
+
+#endif // !IRCCD_FS_H
--- a/lib/irccd/irccd.cpp	Thu Mar 31 12:39:08 2016 +0200
+++ b/lib/irccd/irccd.cpp	Thu Mar 31 13:27:05 2016 +0200
@@ -20,7 +20,7 @@
 #include <stdexcept>
 
 #include "irccd.h"
-#include "filesystem.h"
+#include "fs.h"
 #include "logger.h"
 #include "path.h"
 #include "util.h"
--- a/lib/irccd/irccdctl.cpp	Thu Mar 31 12:39:08 2016 +0200
+++ b/lib/irccd/irccdctl.cpp	Thu Mar 31 13:27:05 2016 +0200
@@ -26,7 +26,7 @@
 #include <irccd-config.h>
 
 #include "elapsed-timer.h"
-#include "filesystem.h"
+#include "fs.h"
 #include "ini.h"
 #include "irccdctl.h"
 #include "json.h"
--- a/lib/irccd/js-directory.cpp	Thu Mar 31 12:39:08 2016 +0200
+++ b/lib/irccd/js-directory.cpp	Thu Mar 31 13:27:05 2016 +0200
@@ -26,8 +26,7 @@
 
 #include <irccd-config.h>
 
-#include "directory.h"
-#include "filesystem.h"
+#include "fs.h"
 #include "js.h"
 #include "js-irccd.h"
 #include "path.h"
@@ -71,18 +70,18 @@
 	 * not directories to avoid going deeper recursively if the requested
 	 * file is in the current directory.
 	 */
-	Directory directory(base);
+	auto entries = fs::readdir(base);
 
-	for (const DirectoryEntry &entry : directory)
-		if (entry.type != DirectoryEntry::Dir && pred(entry.name))
+	for (const auto &entry : entries)
+		if (entry.type != fs::Entry::Dir && pred(entry.name))
 			return base + entry.name;
 
 	if (!recursive)
-		throw std::out_of_range("entry not found");
+		return "";
 
-	for (const DirectoryEntry &entry : directory) {
-		if (entry.type == DirectoryEntry::Dir) {
-			std::string next = base + entry.name + fs::Separator;
+	for (const auto &entry : entries) {
+		if (entry.type == fs::Entry::Dir) {
+			std::string next = base + entry.name + fs::separator();
 			std::string path = findPath(next, true, pred);
 
 			if (!path.empty())
@@ -166,26 +165,17 @@
  */
 duk::Ret remove(duk::ContextPtr ctx, const std::string &path, bool recursive)
 {
-	if (!recursive) {
-		::remove(path.c_str());
-	} else {
-		try {
-			Directory directory(path);
+	if (!fs::isDirectory(path))
+		duk::raise(ctx, SystemError(EINVAL, "not a directory"));
 
-			for (const DirectoryEntry &entry : directory) {
-				if (entry.type == DirectoryEntry::Dir) {
-					(void)remove(ctx, path + fs::Separator + entry.name, true);
-				} else {
-					std::string filename = path + fs::Separator + entry.name;
-
-					::remove(filename.c_str());
-				}
-			}
-
-			::remove(path.c_str());
-		} catch (const std::exception &) {
-			// TODO: put the error in a log.
-		}
+	if (!recursive) {
+#if defined(_WIN32)
+		::RemoveDirectory(path.c_str());
+#else
+		::remove(path.c_str());
+#endif
+	} else {
+		fs::rmdir(path.c_str());
 	}
 
 	return 0;
@@ -202,7 +192,9 @@
  *   - pattern, the regular expression or file name,
  *   - recursive, set to true to search recursively (default: false).
  * Returns:
- *   The path to the file or undefined on errors or not found
+ *   The path to the file or undefined if not found.
+ * Throws:
+ *   - Any exception on error.
  */
 duk::Ret methodFind(duk::ContextPtr ctx)
 {
@@ -253,24 +245,29 @@
 		return 0;
 
 	try {
-		Directory directory(duk::require<std::string>(ctx, 0), duk::optional<int>(ctx, 1, 0));
+		std::string path = duk::require<std::string>(ctx, 0);
+		std::int8_t flags = duk::optional<int>(ctx, 1, 0);
+
+		if (!fs::isDirectory(path))
+			duk::raise(ctx, SystemError(EINVAL, "not a directory"));
+
+		std::vector<fs::Entry> list = fs::readdir(path, flags);
 
 		duk::push(ctx, duk::This{});
 		duk::push(ctx, "count");
-		duk::push(ctx, directory.count());
+		duk::push(ctx, (int)list.size());
 		duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
 		duk::push(ctx, "path");
-		duk::push(ctx, duk::require<std::string>(ctx, 0));
+		duk::push(ctx, path);
 		duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
 		duk::push(ctx, "entries");
 		duk::push(ctx, duk::Array{});
 
-		int i = 0;
-		for (const DirectoryEntry &entry : directory) {
+		for (unsigned i = 0; i < list.size(); ++i) {
 			duk::push(ctx, duk::Object{});
-			duk::putProperty(ctx, -1, "name", entry.name);
-			duk::putProperty(ctx, -1, "type", static_cast<int>(entry.type));
-			duk::putProperty(ctx, -2, i++);
+			duk::putProperty(ctx, -1, "name", list[i].name);
+			duk::putProperty(ctx, -1, "type", static_cast<int>(list[i].type));
+			duk::putProperty(ctx, -2, (int)i);
 		}
 
 		duk::defineProperty(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE);
@@ -347,12 +344,12 @@
 };
 
 const duk::Map<int> constants{
-	{ "Dot",		static_cast<int>(Directory::Dot)		},
-	{ "DotDot",		static_cast<int>(Directory::DotDot)		},
-	{ "TypeUnknown",	static_cast<int>(DirectoryEntry::Unknown)	},
-	{ "TypeDir",		static_cast<int>(DirectoryEntry::Dir)		},
-	{ "TypeFile",		static_cast<int>(DirectoryEntry::File)		},
-	{ "TypeLink",		static_cast<int>(DirectoryEntry::Link)		}
+	{ "Dot",		static_cast<int>(fs::Dot)			},
+	{ "DotDot",		static_cast<int>(fs::DotDot)			},
+	{ "TypeUnknown",	static_cast<int>(fs::Entry::Unknown)		},
+	{ "TypeDir",		static_cast<int>(fs::Entry::Dir)		},
+	{ "TypeFile",		static_cast<int>(fs::Entry::File)		},
+	{ "TypeLink",		static_cast<int>(fs::Entry::Link)		}
 };
 
 } // !namespace
@@ -365,7 +362,7 @@
 	duk::push(ctx, duk::Function{constructor, 2});
 	duk::push(ctx, constants);
 	duk::push(ctx, functions);
-	duk::putProperty(ctx, -1, "separator", std::string{fs::Separator});
+	duk::putProperty(ctx, -1, "separator", std::string{fs::separator()});
 	duk::push(ctx, duk::Object{});
 	duk::push(ctx, methods);
 	duk::putProperty(ctx, -2, "prototype");
--- a/lib/irccd/js-file.cpp	Thu Mar 31 12:39:08 2016 +0200
+++ b/lib/irccd/js-file.cpp	Thu Mar 31 13:27:05 2016 +0200
@@ -26,13 +26,13 @@
 #  include <sys/stat.h>
 #endif
 
-#include "filesystem.h"
+#include "fs.h"
 #include "js-irccd.h"
 #include "js-file.h"
 
-#if defined(HAVE_STAT)
+namespace irccd {
 
-namespace irccd {
+#if defined(HAVE_STAT)
 
 /*
  * duk::TypeInfo specialization for struct stat
--- a/lib/irccd/path.cpp	Thu Mar 31 12:39:08 2016 +0200
+++ b/lib/irccd/path.cpp	Thu Mar 31 13:27:05 2016 +0200
@@ -56,7 +56,7 @@
 #  include "xdg.h"
 #endif
 
-#include "filesystem.h"
+#include "fs.h"
 #include "path.h"
 #include "system.h"
 #include "util.h"
@@ -185,7 +185,7 @@
 
 	return base + WITH_CONFDIR;
 #else
-	return fs::isAbsolute(WITH_CONFDIR) ? WITH_CONFDIR : std::string(PREFIX) + fs::Separator + WITH_CONFDIR;
+	return fs::isAbsolute(WITH_CONFDIR) ? WITH_CONFDIR : std::string(PREFIX) + fs::separator() + WITH_CONFDIR;
 #endif
 }
 
@@ -196,7 +196,7 @@
 
 	return base + WITH_DATADIR;
 #else
-	return fs::isAbsolute(WITH_DATADIR) ? WITH_CONFDIR : std::string(PREFIX) + fs::Separator + WITH_DATADIR;
+	return fs::isAbsolute(WITH_DATADIR) ? WITH_CONFDIR : std::string(PREFIX) + fs::separator() + WITH_DATADIR;
 #endif
 }
 
@@ -207,7 +207,7 @@
 
 	return base + WITH_CACHEDIR;
 #else
-	return fs::isAbsolute(WITH_CACHEDIR) ? WITH_CACHEDIR : std::string(PREFIX) + fs::Separator + WITH_CACHEDIR;
+	return fs::isAbsolute(WITH_CACHEDIR) ? WITH_CACHEDIR : std::string(PREFIX) + fs::separator() + WITH_CACHEDIR;
 #endif
 }
 
@@ -218,7 +218,7 @@
 
 	return base + WITH_PLUGINDIR;
 #else
-	return fs::isAbsolute(WITH_PLUGINDIR) ? WITH_PLUGINDIR : std::string(PREFIX) + fs::Separator + WITH_PLUGINDIR;
+	return fs::isAbsolute(WITH_PLUGINDIR) ? WITH_PLUGINDIR : std::string(PREFIX) + fs::separator() + WITH_PLUGINDIR;
 #endif
 }
 
@@ -415,7 +415,7 @@
 			std::string name = fs::baseName(argv0);
 
 			for (const auto &dir : util::split(sys::env("PATH"), std::string(1, Separator))) {
-				std::string path = dir + fs::Separator + name;
+				std::string path = dir + fs::separator() + name;
 
 				if (fs::exists(path)) {
 					base = path;
@@ -425,12 +425,12 @@
 
 			/* Not found in PATH? add dummy value */
 			if (base.empty())
-				base = std::string(".") + fs::Separator + WITH_BINDIR + fs::Separator + "dummy";
+				base = std::string(".") + fs::separator() + WITH_BINDIR + fs::separator() + "dummy";
 		}
 	}
 
 	/* Find bin/<progname> */
-	auto pos = base.rfind(std::string(WITH_BINDIR) + fs::Separator + fs::baseName(base));
+	auto pos = base.rfind(std::string(WITH_BINDIR) + fs::separator() + fs::baseName(base));
 
 	if (pos != std::string::npos)
 		base.erase(pos);
@@ -457,7 +457,7 @@
 	/* Add a trailing / or \\ */
 	char c = input[input.length() - 1];
 	if (c != '/' && c != '\\')
-		input += fs::Separator;
+		input += fs::separator();
 
 	/* Now converts all / to \\ for Windows and the opposite for Unix */
 #if defined(IRCCD_SYSTEM_WINDOWS)
--- a/lib/irccd/plugin.cpp	Thu Mar 31 12:39:08 2016 +0200
+++ b/lib/irccd/plugin.cpp	Thu Mar 31 13:27:05 2016 +0200
@@ -26,7 +26,7 @@
 #  include <cstring>
 #endif
 
-#include "filesystem.h"
+#include "fs.h"
 #include "js-directory.h"
 #include "js-elapsed-timer.h"
 #include "js-file.h"
--- a/lib/irccd/system.cpp	Thu Mar 31 12:39:08 2016 +0200
+++ b/lib/irccd/system.cpp	Thu Mar 31 13:27:05 2016 +0200
@@ -67,7 +67,7 @@
 #  include <pwd.h>
 #endif
 
-#include "filesystem.h"
+#include "fs.h"
 #include "logger.h"
 #include "system.h"
 #include "util.h"