diff --git a/3rdparty/xdgpaths.d b/3rdparty/xdgpaths.d deleted file mode 100644 index 83ca0e73..00000000 --- a/3rdparty/xdgpaths.d +++ /dev/null @@ -1,503 +0,0 @@ -/** - * Getting XDG base directories. - * Note: These functions are defined only on freedesktop systems. - * Authors: - * $(LINK2 https://github.com/FreeSlave, Roman Chistokhodov) - * Copyright: - * Roman Chistokhodov, 2016 - * License: - * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * See_Also: - * $(LINK2 https://specifications.freedesktop.org/basedir-spec/latest/index.html, XDG Base Directory Specification) - */ - -module xdgpaths; - -import isfreedesktop; - -version(D_Ddoc) -{ - /** - * Path to runtime user directory. - * Returns: User's runtime directory determined by $(B XDG_RUNTIME_DIR) environment variable. - * If directory does not exist it tries to create one with appropriate permissions. On fail returns an empty string. - */ - @trusted string xdgRuntimeDir() nothrow; - - /** - * The ordered set of non-empty base paths to search for data files, in descending order of preference. - * Params: - * subfolder = Subfolder which is appended to every path if not null. - * Returns: Data directories, without user's one and with no duplicates. - * Note: This function does not check if paths actually exist and appear to be directories. - * See_Also: $(D xdgAllDataDirs), $(D xdgDataHome) - */ - @trusted string[] xdgDataDirs(string subfolder = null) nothrow; - - /** - * The ordered set of non-empty base paths to search for data files, in descending order of preference. - * Params: - * subfolder = Subfolder which is appended to every path if not null. - * Returns: Data directories, including user's one if could be evaluated. - * Note: This function does not check if paths actually exist and appear to be directories. - * See_Also: $(D xdgDataDirs), $(D xdgDataHome) - */ - @trusted string[] xdgAllDataDirs(string subfolder = null) nothrow; - - /** - * The ordered set of non-empty base paths to search for configuration files, in descending order of preference. - * Params: - * subfolder = Subfolder which is appended to every path if not null. - * Returns: Config directories, without user's one and with no duplicates. - * Note: This function does not check if paths actually exist and appear to be directories. - * See_Also: $(D xdgAllConfigDirs), $(D xdgConfigHome) - */ - @trusted string[] xdgConfigDirs(string subfolder = null) nothrow; - - /** - * The ordered set of non-empty base paths to search for configuration files, in descending order of preference. - * Params: - * subfolder = Subfolder which is appended to every path if not null. - * Returns: Config directories, including user's one if could be evaluated. - * Note: This function does not check if paths actually exist and appear to be directories. - * See_Also: $(D xdgConfigDirs), $(D xdgConfigHome) - */ - @trusted string[] xdgAllConfigDirs(string subfolder = null) nothrow; - - /** - * The base directory relative to which user-specific data files should be stored. - * Returns: Path to user-specific data directory or empty string on error. - * Params: - * subfolder = Subfolder to append to determined path. - * shouldCreate = If path does not exist, create directory using 700 permissions (i.e. allow access only for current user). - * See_Also: $(D xdgAllDataDirs), $(D xdgDataDirs) - */ - @trusted string xdgDataHome(string subfolder = null, bool shouldCreate = false) nothrow; - - /** - * The base directory relative to which user-specific configuration files should be stored. - * Returns: Path to user-specific configuration directory or empty string on error. - * Params: - * subfolder = Subfolder to append to determined path. - * shouldCreate = If path does not exist, create directory using 700 permissions (i.e. allow access only for current user). - * See_Also: $(D xdgAllConfigDirs), $(D xdgConfigDirs) - */ - @trusted string xdgConfigHome(string subfolder = null, bool shouldCreate = false) nothrow; - - /** - * The base directory relative to which user-specific non-essential files should be stored. - * Returns: Path to user-specific cache directory or empty string on error. - * Params: - * subfolder = Subfolder to append to determined path. - * shouldCreate = If path does not exist, create directory using 700 permissions (i.e. allow access only for current user). - */ - @trusted string xdgCacheHome(string subfolder = null, bool shouldCreate = false) nothrow; -} - -static if (isFreedesktop) -{ - private { - import std.algorithm : splitter, map, filter, canFind; - import std.array; - import std.conv : octal; - import std.exception : collectException, enforce; - import std.file; - import std.path : buildPath, dirName; - import std.process : environment; - import std.string : toStringz; - - import core.sys.posix.unistd; - import core.sys.posix.sys.stat; - import core.sys.posix.sys.types; - import core.stdc.string; - import core.stdc.errno; - - static if (is(typeof({import std.string : fromStringz;}))) { - import std.string : fromStringz; - } else { //own fromStringz implementation for compatibility reasons - @system static pure inout(char)[] fromStringz(inout(char)* cString) { - return cString ? cString[0..strlen(cString)] : null; - } - } - - enum mode_t privateMode = octal!700; - } - - version(unittest) { - import std.algorithm : equal; - - private struct EnvGuard - { - this(string env) { - envVar = env; - envValue = environment.get(env); - } - - ~this() { - if (envValue is null) { - environment.remove(envVar); - } else { - environment[envVar] = envValue; - } - } - - string envVar; - string envValue; - } - } - - private string[] pathsFromEnvValue(string envValue, string subfolder = null) nothrow { - string[] result; - try { - foreach(path; splitter(envValue, ':').filter!(p => !p.empty).map!(p => buildPath(p, subfolder))) { - if (path[$-1] == '/') { - path = path[0..$-1]; - } - if (!result.canFind(path)) { - result ~= path; - } - } - } catch(Exception e) { - - } - return result; - } - - unittest - { - assert(pathsFromEnvValue("") == (string[]).init); - assert(pathsFromEnvValue(":") == (string[]).init); - assert(pathsFromEnvValue("::") == (string[]).init); - - assert(pathsFromEnvValue("path1:path2") == ["path1", "path2"]); - assert(pathsFromEnvValue("path1:") == ["path1"]); - assert(pathsFromEnvValue("path1/") == ["path1"]); - assert(pathsFromEnvValue("path1/:path1") == ["path1"]); - assert(pathsFromEnvValue("path2:path1:path2") == ["path2", "path1"]); - } - - private string[] pathsFromEnv(string envVar, string subfolder = null) nothrow { - string envValue; - collectException(environment.get(envVar), envValue); - return pathsFromEnvValue(envValue, subfolder); - } - - private bool ensureExists(string dir) nothrow - { - bool ok; - try { - ok = dir.exists; - if (!ok) { - mkdirRecurse(dir.dirName); - ok = mkdir(dir.toStringz, privateMode) == 0; - } else { - ok = dir.isDir; - } - } catch(Exception e) { - ok = false; - } - return ok; - } - - unittest - { - import std.file; - import std.stdio; - - string temp = tempDir(); - if (temp.length) { - string testDir = buildPath(temp, "xdgpaths-unittest-tempdir"); - string testFile = buildPath(testDir, "touched"); - string testSubDir = buildPath(testDir, "subdir"); - try { - mkdir(testDir); - File(testFile, "w"); - assert(!ensureExists(testFile)); - enforce(ensureExists(testSubDir)); - } catch(Exception e) { - - } finally { - collectException(rmdir(testSubDir)); - collectException(remove(testFile)); - collectException(rmdir(testDir)); - } - } - } - - private string xdgBaseDir(string envvar, string fallback, string subfolder = null, bool shouldCreate = false) nothrow { - string dir; - collectException(environment.get(envvar), dir); - if (dir.length == 0) { - string home; - collectException(environment.get("HOME"), home); - dir = home.length ? buildPath(home, fallback) : null; - } - - if (dir.length == 0) { - return null; - } - - if (shouldCreate) { - if (ensureExists(dir)) { - if (subfolder.length) { - string path = buildPath(dir, subfolder); - try { - if (!path.exists) { - mkdirRecurse(path); - } - return path; - } catch(Exception e) { - - } - } else { - return dir; - } - } - } else { - return buildPath(dir, subfolder); - } - return null; - } - - version(unittest) { - void testXdgBaseDir(string envVar, string fallback) { - auto homeGuard = EnvGuard("HOME"); - auto dataHomeGuard = EnvGuard(envVar); - - auto newHome = "/home/myuser"; - auto newDataHome = "/home/myuser/data"; - - environment[envVar] = newDataHome; - assert(xdgBaseDir(envVar, fallback) == newDataHome); - assert(xdgBaseDir(envVar, fallback, "applications") == buildPath(newDataHome, "applications")); - - environment.remove(envVar); - environment["HOME"] = newHome; - assert(xdgBaseDir(envVar, fallback) == buildPath(newHome, fallback)); - assert(xdgBaseDir(envVar, fallback, "icons") == buildPath(newHome, fallback, "icons")); - - environment.remove("HOME"); - assert(xdgBaseDir(envVar, fallback).empty); - assert(xdgBaseDir(envVar, fallback, "mime").empty); - } - } - - @trusted string[] xdgDataDirs(string subfolder = null) nothrow - { - auto result = pathsFromEnv("XDG_DATA_DIRS", subfolder); - if (result.length) { - return result; - } else { - return [buildPath("/usr/local/share", subfolder), buildPath("/usr/share", subfolder)]; - } - } - - /// - unittest - { - auto dataDirsGuard = EnvGuard("XDG_DATA_DIRS"); - - auto newDataDirs = ["/usr/local/data", "/usr/data"]; - - environment["XDG_DATA_DIRS"] = "/usr/local/data:/usr/data:/usr/local/data/:/usr/data/"; - assert(xdgDataDirs() == newDataDirs); - assert(equal(xdgDataDirs("applications"), newDataDirs.map!(p => buildPath(p, "applications")))); - - environment.remove("XDG_DATA_DIRS"); - assert(xdgDataDirs() == ["/usr/local/share", "/usr/share"]); - assert(equal(xdgDataDirs("icons"), ["/usr/local/share", "/usr/share"].map!(p => buildPath(p, "icons")))); - } - - @trusted string[] xdgAllDataDirs(string subfolder = null) nothrow - { - string dataHome = xdgDataHome(subfolder); - string[] dataDirs = xdgDataDirs(subfolder); - if (dataHome.length) { - return dataHome ~ dataDirs; - } else { - return dataDirs; - } - } - - /// - unittest - { - auto homeGuard = EnvGuard("HOME"); - auto dataHomeGuard = EnvGuard("XDG_DATA_HOME"); - auto dataDirsGuard = EnvGuard("XDG_DATA_DIRS"); - - auto newDataHome = "/home/myuser/data"; - auto newDataDirs = ["/usr/local/data", "/usr/data"]; - environment["XDG_DATA_HOME"] = newDataHome; - environment["XDG_DATA_DIRS"] = "/usr/local/data:/usr/data"; - - assert(xdgAllDataDirs() == newDataHome ~ newDataDirs); - - environment.remove("XDG_DATA_HOME"); - environment.remove("HOME"); - - assert(xdgAllDataDirs() == newDataDirs); - } - - @trusted string[] xdgConfigDirs(string subfolder = null) nothrow - { - auto result = pathsFromEnv("XDG_CONFIG_DIRS", subfolder); - if (result.length) { - return result; - } else { - return [buildPath("/etc/xdg", subfolder)]; - } - } - - /// - unittest - { - auto dataConfigGuard = EnvGuard("XDG_CONFIG_DIRS"); - - auto newConfigDirs = ["/usr/local/config", "/usr/config"]; - - environment["XDG_CONFIG_DIRS"] = "/usr/local/config:/usr/config"; - assert(xdgConfigDirs() == newConfigDirs); - assert(equal(xdgConfigDirs("menus"), newConfigDirs.map!(p => buildPath(p, "menus")))); - - environment.remove("XDG_CONFIG_DIRS"); - assert(xdgConfigDirs() == ["/etc/xdg"]); - assert(equal(xdgConfigDirs("autostart"), ["/etc/xdg"].map!(p => buildPath(p, "autostart")))); - } - - @trusted string[] xdgAllConfigDirs(string subfolder = null) nothrow - { - string configHome = xdgConfigHome(subfolder); - string[] configDirs = xdgConfigDirs(subfolder); - if (configHome.length) { - return configHome ~ configDirs; - } else { - return configDirs; - } - } - - /// - unittest - { - auto homeGuard = EnvGuard("HOME"); - auto configHomeGuard = EnvGuard("XDG_CONFIG_HOME"); - auto configDirsGuard = EnvGuard("XDG_CONFIG_DIRS"); - - auto newConfigHome = "/home/myuser/data"; - environment["XDG_CONFIG_HOME"] = newConfigHome; - auto newConfigDirs = ["/usr/local/data", "/usr/data"]; - environment["XDG_CONFIG_DIRS"] = "/usr/local/data:/usr/data"; - - assert(xdgAllConfigDirs() == newConfigHome ~ newConfigDirs); - - environment.remove("XDG_CONFIG_HOME"); - environment.remove("HOME"); - - assert(xdgAllConfigDirs() == newConfigDirs); - } - - @trusted string xdgDataHome(string subfolder = null, bool shouldCreate = false) nothrow { - return xdgBaseDir("XDG_DATA_HOME", ".local/share", subfolder, shouldCreate); - } - - unittest - { - testXdgBaseDir("XDG_DATA_HOME", ".local/share"); - } - - @trusted string xdgConfigHome(string subfolder = null, bool shouldCreate = false) nothrow { - return xdgBaseDir("XDG_CONFIG_HOME", ".config", subfolder, shouldCreate); - } - - unittest - { - testXdgBaseDir("XDG_CONFIG_HOME", ".config"); - } - - @trusted string xdgCacheHome(string subfolder = null, bool shouldCreate = false) nothrow { - return xdgBaseDir("XDG_CACHE_HOME", ".cache", subfolder, shouldCreate); - } - - unittest - { - testXdgBaseDir("XDG_CACHE_HOME", ".cache"); - } - - version(XdgPathsRuntimeDebug) { - private import std.stdio; - } - - @trusted string xdgRuntimeDir() nothrow // Do we need it on BSD systems? - { - import std.exception : assumeUnique; - import core.sys.posix.pwd; - - try { //one try to rule them all and for compatibility reasons - const uid_t uid = getuid(); - string runtime; - collectException(environment.get("XDG_RUNTIME_DIR"), runtime); - - if (!runtime.length) { - passwd* pw = getpwuid(uid); - - try { - if (pw && pw.pw_name) { - runtime = tempDir() ~ "/runtime-" ~ assumeUnique(fromStringz(pw.pw_name)); - - if (!(runtime.exists && runtime.isDir)) { - if (mkdir(runtime.toStringz, privateMode) != 0) { - version(XdgPathsRuntimeDebug) stderr.writefln("Failed to create runtime directory %s: %s", runtime, fromStringz(strerror(errno))); - return null; - } - } - } else { - version(XdgPathsRuntimeDebug) stderr.writeln("Failed to get user name to create runtime directory"); - return null; - } - } catch(Exception e) { - version(XdgPathsRuntimeDebug) collectException(stderr.writefln("Error when creating runtime directory: %s", e.msg)); - return null; - } - } - stat_t statbuf; - stat(runtime.toStringz, &statbuf); - if (statbuf.st_uid != uid) { - version(XdgPathsRuntimeDebug) collectException(stderr.writeln("Wrong ownership of runtime directory %s, %d instead of %d", runtime, statbuf.st_uid, uid)); - return null; - } - if ((statbuf.st_mode & octal!777) != privateMode) { - version(XdgPathsRuntimeDebug) collectException(stderr.writefln("Wrong permissions on runtime directory %s, %o instead of %o", runtime, statbuf.st_mode, privateMode)); - return null; - } - - return runtime; - } catch (Exception e) { - version(XdgPathsRuntimeDebug) collectException(stderr.writeln("Error when getting runtime directory: %s", e.msg)); - return null; - } - } - - version(xdgpathsFileTest) unittest - { - string runtimePath = buildPath(tempDir(), "xdgpaths-runtime-test"); - try { - collectException(std.file.rmdir(runtimePath)); - - if (mkdir(runtimePath.toStringz, privateMode) == 0) { - auto runtimeGuard = EnvGuard("XDG_RUNTIME_DIR"); - environment["XDG_RUNTIME_DIR"] = runtimePath; - assert(xdgRuntimeDir() == runtimePath); - - if (chmod(runtimePath.toStringz, octal!777) == 0) { - assert(xdgRuntimeDir() == string.init); - } - - std.file.rmdir(runtimePath); - } else { - version(XdgPathsRuntimeDebug) stderr.writeln(fromStringz(strerror(errno))); - } - } catch(Exception e) { - version(XdgPathsRuntimeDebug) stderr.writeln(e.msg); - } - } -}