diff modules/fs/fs.cpp @ 519:d6dad57e9e6b

Fs: import
author David Demelier <markand@malikania.fr>
date Wed, 01 Jun 2016 16:35:01 +0200
parents
children ecf5fb9319da
line wrap: on
line diff
--- /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