changeset 241:d9409b338f2f

Merge
author David Demelier <markand@malikania.fr>
date Thu, 11 Sep 2014 17:22:11 +0200
parents b97d75a78e22 (current diff) f6d9fdb5eeeb (diff)
children a9883eeb9757
files
diffstat 6 files changed, 624 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/C++/Tests/OptionParser/main.cpp	Thu Sep 11 17:21:51 2014 +0200
+++ b/C++/Tests/OptionParser/main.cpp	Thu Sep 11 17:22:11 2014 +0200
@@ -1,3 +1,21 @@
+/*
+ * main.cpp -- main test file for OptionParser
+ *
+ * Copyright (c) 2013, 2014 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 <gtest/gtest.h>
 
 #include <OptionParser.h>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C++/Tests/Xdg/CMakeLists.txt	Thu Sep 11 17:22:11 2014 +0200
@@ -0,0 +1,26 @@
+#
+# CMakeLists.txt -- tests for XDG
+#
+# Copyright (c) 2013, 2014 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.
+#
+
+set(
+	SOURCES
+	${code_SOURCE_DIR}/C++/Xdg.cpp
+	${code_SOURCE_DIR}/C++/Xdg.h
+	main.cpp
+)
+
+define_test(xdg "${SOURCES}")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C++/Tests/Xdg/main.cpp	Thu Sep 11 17:22:11 2014 +0200
@@ -0,0 +1,340 @@
+/*
+ * main.cpp -- main test file for XDG
+ *
+ * Copyright (c) 2013, 2014 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 <gtest/gtest.h>
+
+#include <Xdg.h>
+
+using namespace testing;
+
+namespace {
+
+std::string myhome;
+
+}
+
+TEST(HomeEmpty, config)
+{
+	ASSERT_TRUE(unsetenv("XDG_CONFIG_HOME") == 0);
+
+	try {
+		Xdg xdg;
+
+		ASSERT_EQ(myhome + "/.config", xdg.configHome());
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeEmpty, data)
+{
+	ASSERT_TRUE(unsetenv("XDG_DATA_HOME") == 0);
+
+	try {
+		Xdg xdg;
+
+		ASSERT_EQ(myhome + "/.local/share", xdg.dataHome());
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeEmpty, cache)
+{
+	ASSERT_TRUE(unsetenv("XDG_CACHE_HOME") == 0);
+
+	try {
+		Xdg xdg;
+
+		ASSERT_EQ(myhome + "/.cache", xdg.cacheHome());
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeEmpty, runtime)
+{
+	ASSERT_TRUE(unsetenv("XDG_RUNTIME_DIR") == 0);
+
+	try {
+		Xdg xdg;
+
+		try {
+			xdg.runtimeDir();
+
+			ASSERT_TRUE(false);
+		} catch (const std::exception &) { }
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeValid, config)
+{
+	ASSERT_TRUE(setenv("XDG_CONFIG_HOME", "/test/config", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		ASSERT_EQ("/test/config", xdg.configHome());
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeValid, data)
+{
+	ASSERT_TRUE(setenv("XDG_DATA_HOME", "/test/data", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		ASSERT_EQ("/test/data", xdg.dataHome());
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeValid, cache)
+{
+	ASSERT_TRUE(setenv("XDG_CACHE_HOME", "/test/cache", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		ASSERT_EQ("/test/cache", xdg.cacheHome());
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeValid, runtime)
+{
+	ASSERT_TRUE(setenv("XDG_RUNTIME_DIR", "/test/runtime", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		ASSERT_EQ("/test/runtime", xdg.runtimeDir());
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeInvalid, config)
+{
+	ASSERT_TRUE(setenv("XDG_CONFIG_HOME", "invalid", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		ASSERT_EQ(myhome + "/.config", xdg.configHome());
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeInvalid, data)
+{
+	ASSERT_TRUE(setenv("XDG_DATA_HOME", "invalid", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		ASSERT_EQ(myhome + "/.local/share", xdg.dataHome());
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeInvalid, cache)
+{
+	ASSERT_TRUE(setenv("XDG_CACHE_HOME", "invalid", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		ASSERT_EQ(myhome + "/.cache", xdg.cacheHome());
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(HomeInvalid, runtime)
+{
+	ASSERT_TRUE(setenv("XDG_RUNTIME_DIR", "invalid", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		try {
+			xdg.runtimeDir();
+
+			ASSERT_TRUE(false);
+		} catch (const std::exception &) { }
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(DirectoriesEmpty, config)
+{
+	ASSERT_TRUE(unsetenv("XDG_CONFIG_DIRS") == 0);
+
+	try {
+		Xdg xdg;
+
+		const auto &list = xdg.configDirs();
+
+		ASSERT_EQ((size_t)1, list.size());
+		ASSERT_EQ("/etc/xdg", list[0]);
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(DirectoriesEmpty, data)
+{
+	ASSERT_TRUE(unsetenv("XDG_DATA_DIRS") == 0);
+
+	try {
+		Xdg xdg;
+
+		const auto &list = xdg.dataDirs();
+
+		ASSERT_EQ((size_t)2, list.size());
+		ASSERT_EQ("/usr/local/share", list[0]);
+		ASSERT_EQ("/usr/share", list[1]);
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(DirectoriesValid, config)
+{
+	ASSERT_TRUE(setenv("XDG_CONFIG_DIRS", "/config1:/config2", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		const auto &list = xdg.configDirs();
+
+		ASSERT_EQ((size_t)2, list.size());
+		ASSERT_EQ("/config1", list[0]);
+		ASSERT_EQ("/config2", list[1]);
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(DirectoriesValid, data)
+{
+	ASSERT_TRUE(setenv("XDG_DATA_DIRS", "/data1:/data2", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		const auto &list = xdg.dataDirs();
+
+		ASSERT_EQ((size_t)2, list.size());
+		ASSERT_EQ("/data1", list[0]);
+		ASSERT_EQ("/data2", list[1]);
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(DirectoriesInvalid, config)
+{
+	ASSERT_TRUE(setenv("XDG_CONFIG_DIRS", "bad1:bad2", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		const auto &list = xdg.configDirs();
+
+		ASSERT_EQ((size_t)1, list.size());
+		ASSERT_EQ("/etc/xdg", list[0]);
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(DirectoriesInvalid, data)
+{
+	ASSERT_TRUE(setenv("XDG_DATA_DIRS", "bad1:bad2", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		const auto &list = xdg.dataDirs();
+
+		ASSERT_EQ((size_t)2, list.size());
+		ASSERT_EQ("/usr/local/share", list[0]);
+		ASSERT_EQ("/usr/share", list[1]);
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(DirectoriesMixed, config)
+{
+	ASSERT_TRUE(setenv("XDG_CONFIG_DIRS", "/config1:bad:/config2", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		const auto &list = xdg.configDirs();
+
+		ASSERT_EQ((size_t)2, list.size());
+		ASSERT_EQ("/config1", list[0]);
+		ASSERT_EQ("/config2", list[1]);
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+TEST(DirectoriesMixed, data)
+{
+	ASSERT_TRUE(setenv("XDG_DATA_DIRS", "/data1:bad:/data2", true) == 0);
+
+	try {
+		Xdg xdg;
+
+		const auto &list = xdg.dataDirs();
+
+		ASSERT_EQ((size_t)2, list.size());
+		ASSERT_EQ("/data1", list[0]);
+		ASSERT_EQ("/data2", list[1]);
+	} catch (const std::exception &) {
+		ASSERT_TRUE(false);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	auto home = getenv("HOME");
+
+	if (home == nullptr)
+		return 0;
+
+	myhome = home;
+	InitGoogleTest(&argc, argv);
+
+	return RUN_ALL_TESTS();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C++/Xdg.cpp	Thu Sep 11 17:22:11 2014 +0200
@@ -0,0 +1,125 @@
+/*
+ * Xdg.cpp -- XDG directory specifications
+ *
+ * Copyright (c) 2013, 2014 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 <cstdlib>
+#include <stdexcept>
+#include <sstream>
+
+#include "Xdg.h"
+
+namespace {
+
+bool isabsolute(const std::string &path)
+{
+	return path.length() > 0 && path[0] == '/';
+}
+
+std::vector<std::string> split(const std::string &arg)
+{
+	std::stringstream iss(arg);
+	std::string item;
+	std::vector<std::string> elems;
+
+	while (std::getline(iss, item, ':'))
+		if (isabsolute(item))
+			elems.push_back(item);
+
+	return elems;
+}
+
+std::string envOrHome(const std::string &var, const std::string &repl)
+{
+	auto value = getenv(var.c_str());
+
+	if (value == nullptr || !isabsolute(value)) {
+		auto home = getenv("HOME");
+
+		if (home == nullptr)
+			throw std::runtime_error("could not get home directory");
+
+		return std::string(home) + "/" + repl;
+	}
+
+	return value;
+}
+
+std::vector<std::string> listOrDefaults(const std::string &var, const std::vector<std::string> &list)
+{
+	auto value = getenv(var.c_str());
+
+	if (!value)
+		return list;
+
+	// No valid item at all? Use defaults
+	auto result = split(value);
+
+	return (result.size() == 0) ? list : result;
+}
+
+} // !namespace
+
+Xdg::Xdg()
+{
+	m_configHome	= envOrHome("XDG_CONFIG_HOME", ".config");
+	m_dataHome	= envOrHome("XDG_DATA_HOME", ".local/share");
+	m_cacheHome	= envOrHome("XDG_CACHE_HOME", ".cache");
+
+	m_configDirs	= listOrDefaults("XDG_CONFIG_DIRS", { "/etc/xdg" });
+	m_dataDirs	= listOrDefaults("XDG_DATA_DIRS", { "/usr/local/share", "/usr/share" });
+
+	/*
+	 * Runtime directory is a special case and does not have a replacement, the
+	 * application should manage this by itself.
+	 */
+	auto runtime = getenv("XDG_RUNTIME_DIR");
+	if (runtime && isabsolute(runtime))
+		m_runtimeDir = runtime;
+}
+
+const std::string &Xdg::configHome() const noexcept
+{
+	return m_configHome;
+}
+
+const std::string &Xdg::dataHome() const noexcept
+{
+	return m_dataHome;
+}
+
+const std::string &Xdg::cacheHome() const noexcept
+{
+	return m_cacheHome;
+}
+
+const std::string &Xdg::runtimeDir() const
+{
+	if (m_runtimeDir.size() == 0)
+		throw std::runtime_error("XDG_RUNTIME_DIR is not set");
+
+	return m_runtimeDir;
+}
+
+const Xdg::List &Xdg::configDirs() const noexcept
+{
+	return m_configDirs;
+}
+
+const Xdg::List &Xdg::dataDirs() const noexcept
+{
+	return m_dataDirs;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C++/Xdg.h	Thu Sep 11 17:22:11 2014 +0200
@@ -0,0 +1,110 @@
+/*
+ * Xdg.h -- XDG directory specifications
+ *
+ * Copyright (c) 2013, 2014 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 _XDG_H_
+#define _XDG_H_
+
+#include <vector>
+#include <string>
+
+#if defined(_WIN32)
+#  if defined(BUILDING_DLL)
+#    define EXPORT __declspec(dllexport)
+#  else
+#    define EXPORT __declspec(dllimport)
+#  endif
+#else
+#  define EXPORT
+#endif
+
+/**
+ * @class Xdg
+ * @brief XDG specifications
+ *
+ * Read and get XDG directories. This file contains exports thingies so it can
+ * compiles successfully on Windows but its usage is discouraged.
+ */
+class EXPORT Xdg {
+public:
+	using List	= std::vector<std::string>;
+
+private:
+	std::string	m_configHome;
+	std::string	m_dataHome;
+	std::string	m_cacheHome;
+	std::string	m_runtimeDir;
+	List		m_configDirs;
+	List		m_dataDirs;
+
+public:
+	/**
+	 * Open an xdg instance and load directories.
+	 *
+	 * @throw std::runtime_error on failures
+	 */
+	Xdg();
+
+	/**
+	 * Get the config directory. ${XDG_CONFIG_HOME} or ${HOME}/.config
+	 *
+	 * @return the config directory
+	 */
+	const std::string &configHome() const noexcept;
+
+	/**
+	 * Get the data directory. ${XDG_DATA_HOME} or ${HOME}/.local/share
+	 *
+	 * @return the data directory
+	 */
+	const std::string &dataHome() const noexcept;
+
+	/**
+	 * Get the cache directory. ${XDG_CACHE_HOME} or ${HOME}/.cache
+	 *
+	 * @return the cache directory
+	 */
+	const std::string &cacheHome() const noexcept;
+
+	/**
+	 * Get the runtime directory. ${XDG_RUNTIME_DIR} must be set,
+	 * if not, it throws an exception.
+	 *
+	 * The XDG standard says that application should handle XDG_RUNTIME_DIR by
+	 * themselves.
+	 *
+	 * @return the runtime directory
+	 * @throw std::runtime_error on error
+	 */
+	const std::string &runtimeDir() const;
+
+	/**
+	 * Get the standard config directories. ${XDG_CONFIG_DIRS} or { "/etc/xdg" }
+	 *
+	 * @return the list of config directories
+	 */
+	const List &configDirs() const noexcept;
+
+	/**
+	 * Get the data directories. ${XDG_DATA_DIRS} or { "/usr/local/share", "/usr/share" }
+	 *
+	 * @return the list of data directories
+	 */
+	const List &dataDirs() const noexcept;
+};
+
+#endif // !_XDG_H_
--- a/CMakeLists.txt	Thu Sep 11 17:21:51 2014 +0200
+++ b/CMakeLists.txt	Thu Sep 11 17:22:11 2014 +0200
@@ -68,6 +68,7 @@
 option(WITH_SOCKET "Enable sockets tests" On)
 option(WITH_UTF8 "Enable Utf8 functions tests" On)
 option(WITH_XMLPARSER "Enable XML tests" On)
+option(WITH_XDG "Enable XDG standard directories tests" On)
 
 if (WITH_DIRECTORY)
 	add_subdirectory(C++/Tests/Directory)
@@ -96,3 +97,7 @@
 if (WITH_PARSER)
 	add_subdirectory(C++/Tests/Parser)
 endif ()
+
+if (WITH_XDG)
+	add_subdirectory(C++/Tests/Xdg)
+endif ()