changeset 188:0de84b31842b

Irccd: update fs module
author David Demelier <markand@malikania.fr>
date Tue, 31 May 2016 22:33:32 +0200
parents 7ef1aab52c1a
children bb70bb9e41eb
files lib/irccd/fs.cpp lib/irccd/fs.hpp
diffstat 2 files changed, 220 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/lib/irccd/fs.cpp	Tue May 31 22:29:33 2016 +0200
+++ b/lib/irccd/fs.cpp	Tue May 31 22:33:32 2016 +0200
@@ -16,7 +16,12 @@
  * 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>
@@ -41,6 +46,12 @@
 
 namespace {
 
+/*
+ * error.
+ * ------------------------------------------------------------------
+ *
+ * Function to retrieve system error in Windows API.
+ */
 #if defined(_WIN32)
 
 std::string error()
@@ -65,8 +76,17 @@
 
 #endif
 
-bool can(const std::string &path, const std::string &mode)
+/*
+ * 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)
@@ -77,22 +97,33 @@
 	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)
 
-bool is(const std::string &path, DWORD flags)
+template <typename Predicate>
+bool typeOf(const std::string &path, Predicate &&predicate)
 {
-	DWORD result = GetFileAttributes(path.c_str());
+	DWORD result = GetFileAttributesA(path.c_str());
 
 	if (result == INVALID_FILE_ATTRIBUTES)
 		return false;
 
-	return result & flags;
+	return predicate(result);
 }
 
-#else
+#elif defined(FS_HAVE_STAT)
 
 template <typename Predicate>
-bool is(const std::string &path, Predicate &&predicate) noexcept
+bool typeOf(const std::string &path, Predicate &&predicate) noexcept
 {
 	struct stat st;
 
@@ -102,26 +133,39 @@
 	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.
+	/* 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 \\.
+	/* 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.
+	/* Now converts all / to \\ for Windows and the opposite for Unix */
 #if defined(_WIN32)
 	std::replace(input.begin(), input.end(), '/', '\\');
 #else
@@ -131,6 +175,10 @@
 	return input;
 }
 
+/*
+ * baseName.
+ * ------------------------------------------------------------------
+ */
 std::string baseName(std::string path)
 {
 	auto pos = path.find_last_of("\\/");
@@ -141,11 +189,15 @@
 	return path;
 }
 
+/*
+ * dirName.
+ * ------------------------------------------------------------------
+ */
 std::string dirName(std::string path)
 {
 	auto pos = path.find_last_of("\\/");
 
-	if (pos == std::string::npos) 
+	if (pos == std::string::npos)
 		path = ".";
 	else
 		path = path.substr(0, pos);
@@ -153,6 +205,10 @@
 	return path;
 }
 
+/*
+ * isAbsolute.
+ * ------------------------------------------------------------------
+ */
 bool isAbsolute(const std::string &path) noexcept
 {
 #if defined(_WIN32)
@@ -162,51 +218,87 @@
 #endif
 }
 
+/*
+ * isRelative.
+ * ------------------------------------------------------------------
+ */
 bool isRelative(const std::string &path) noexcept
 {
 #if defined(_WIN32)
-	return PathIsRelativeA(path.c_str());
+	return PathIsRelativeA(path.c_str()) == 1;
 #else
 	return !isAbsolute(path);
 #endif
 }
 
+/*
+ * isReadable.
+ * ------------------------------------------------------------------
+ */
 bool isReadable(const std::string &path) noexcept
 {
-	return can(path, "r");
+	return hasAccess(path, "r");
+}
+
+/*
+ * isWritable.
+ * ------------------------------------------------------------------
+ */
+bool isWritable(const std::string &path) noexcept
+{
+	return hasAccess(path, "w");
 }
 
-bool isWritable(const std::string &path) noexcept
+/*
+ * isFile.
+ * ------------------------------------------------------------------
+ */
+bool isFile(const std::string &path)
 {
-	return can(path, "w");
+	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
+	});
 }
 
-bool isFile(const std::string &path) noexcept
+/*
+ * isDirectory.
+ * ------------------------------------------------------------------
+ */
+bool isDirectory(const std::string &path)
 {
+	return typeOf(path, [] (const auto &object) {
 #if defined(_WIN32)
-	return is(path, FILE_ATTRIBUTE_ARCHIVE);
-#else
-	return is(path, [] (const struct stat &st) { return S_ISREG(st.st_mode); });
+		return (object & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
+#elif defined(FS_HAVE_STAT)
+		return S_ISDIR(object.st_mode);
 #endif
+	});
 }
 
-bool isDirectory(const std::string &path) noexcept
+/*
+ * isSymlink.
+ * ------------------------------------------------------------------
+ */
+bool isSymlink(const std::string &path)
 {
+	return typeOf(path, [] (const auto &object) {
 #if defined(_WIN32)
-	return is(path, FILE_ATTRIBUTE_DIRECTORY);
-#else
-	return is(path, [] (const struct stat &st) { return S_ISDIR(st.st_mode); });
+		return (object & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT;
+#elif defined(FS_HAVE_STAT)
+		return S_ISLNK(object.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
-}
+/*
+ * stat.
+ * ------------------------------------------------------------------
+ */
+#if defined(FS_HAVE_STAT)
 
 struct stat stat(const std::string &path)
 {
@@ -218,17 +310,27 @@
 	return st;
 }
 
+#endif
+
+/*
+ * exists.
+ * ------------------------------------------------------------------
+ */
 bool exists(const std::string &path) noexcept
 {
-#if defined(HAVE_ACCESS)
-	return ::access(path.c_str(), F_OK) == 0;
-#else
+#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;
@@ -242,7 +344,7 @@
 	handle = FindFirstFile(oss.str().c_str(), &fdata);
 
 	if (handle == nullptr)
-		throw std::runtime_error{error()};
+		throw std::runtime_error(error());
 
 	do {
 		Entry entry;
@@ -311,6 +413,10 @@
 	return entries;
 }
 
+/*
+ * mkdir.
+ * ------------------------------------------------------------------
+ */
 void mkdir(const std::string &path, int mode)
 {
 	std::string::size_type next = 0;
@@ -337,6 +443,10 @@
 	}
 }
 
+/*
+ * rmdir.
+ * ------------------------------------------------------------------
+ */
 void rmdir(const std::string &base) noexcept
 {
 	try {
@@ -346,25 +456,29 @@
 			if (entry.type == Entry::Dir)
 				rmdir(path);
 			else
-				(void)::remove(path.c_str());
+				remove(path.c_str());
 		}
 	} catch (...) {
 	}
 
 #if defined(_WIN32)
-	RemoveDirectory(base.c_str());
+	RemoveDirectoryA(base.c_str());
 #else
-	(void)::remove(base.c_str());
+	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"};
+		throw std::runtime_error("failed to get current working directory");
 
 	return path;
 #else
--- a/lib/irccd/fs.hpp	Tue May 31 22:29:33 2016 +0200
+++ b/lib/irccd/fs.hpp	Tue May 31 22:33:32 2016 +0200
@@ -22,15 +22,32 @@
 /**
  * \file fs.hpp
  * \brief Filesystem operations made easy.
+ */
+
+/**
+ * \page filesystem Filesystem
+ * \brief Filesystem support
  *
  * 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.
+ *   - **FS_HAVE_STAT**: (bool) Set to true if sys/stat.h and stat function are available, automatically detected.
  */
 
-#include <sys/stat.h>
+#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
+
+#if defined(FS_HAVE_STAT)
+#  include <sys/stat.h>
+#endif
 
 #include <regex>
 #include <string>
@@ -40,6 +57,9 @@
 
 namespace irccd {
 
+/**
+ * \brief Filesystem namespace.
+ */
 namespace fs {
 
 /**
@@ -72,6 +92,30 @@
 };
 
 /**
+ * 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
@@ -113,6 +157,8 @@
  */
 IRCCD_EXPORT std::string dirName(std::string path);
 
+#if defined(FS_HAVE_STAT)
+
 /**
  * Get stat information.
  *
@@ -122,6 +168,8 @@
  */
 IRCCD_EXPORT struct stat stat(const std::string &path);
 
+#endif // !HAVE_STAT
+
 /**
  * Check if a file exists.
  *
@@ -169,24 +217,27 @@
  *
  * \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
  */
-IRCCD_EXPORT bool isFile(const std::string &path) noexcept;
+IRCCD_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
  */
-IRCCD_EXPORT bool isDirectory(const std::string &path) noexcept;
+IRCCD_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
  */
-IRCCD_EXPORT bool isSymlink(const std::string &path) noexcept;
+IRCCD_EXPORT bool isSymlink(const std::string &path);
 
 /**
  * Read a directory and return a list of entries (not recursive).
@@ -249,15 +300,17 @@
 		}
 	}
 
-	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())
+		return path;
 
-				if (!path.empty())
-					break;
-			}
-		}
+	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;