1 /** 2 * OS-specific paths. 3 * 4 * getConfigDir - roaming, for configuration 5 * getDataDir - roaming, for user data 6 * getCacheDir - local 7 * 8 * License: 9 * This Source Code Form is subject to the terms of 10 * the Mozilla Public License, v. 2.0. If a copy of 11 * the MPL was not distributed with this file, You 12 * can obtain one at http://mozilla.org/MPL/2.0/. 13 * 14 * Authors: 15 * Vladimir Panteleev <ae@cy.md> 16 * 17 * References: 18 * https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html 19 */ 20 21 module ae.sys.paths; 22 23 import std.path; 24 25 version (Windows) 26 { 27 import core.stdc.wctype; 28 29 import std.exception; 30 import std.file; 31 import std.utf; 32 33 import ae.sys.windows.imports; 34 mixin(importWin32!q{shlobj}); 35 mixin(importWin32!q{objidl}); 36 mixin(importWin32!q{windef}); 37 mixin(importWin32!q{winbase}); 38 39 private string getShellPath(int csidl) 40 { 41 LPITEMIDLIST pidl; 42 SHGetSpecialFolderLocation(null, csidl, &pidl); 43 scope(exit) 44 { 45 IMalloc aMalloc; 46 SHGetMalloc(&aMalloc); 47 aMalloc.Free(pidl); 48 } 49 50 auto path = new wchar[MAX_PATH]; 51 if (!SHGetPathFromIDListW(pidl, path.ptr)) 52 return null; 53 path.length = wcslen(path.ptr); 54 55 return toUTF8(path); 56 } 57 58 /*private*/ string _getAppDir(int csidl, string appName = null) 59 { 60 return getShellPath(csidl) ~ `\` ~ (appName ? appName : getExecutableName()); 61 } 62 63 /*private*/ string[] _getAppDirs(int csidl, string appName = null) 64 { 65 return [thisExePath.dirName(), _getAppDir(csidl, appName)]; 66 } 67 68 alias getLocalAppProfile = _bindArgs!(_getAppDir, CSIDL_LOCAL_APPDATA); /// 69 alias getRoamingAppProfile = _bindArgs!(_getAppDir, CSIDL_APPDATA); /// 70 71 alias getConfigDir = getRoamingAppProfile; /// 72 alias getDataDir = getRoamingAppProfile; /// 73 alias getCacheDir = getLocalAppProfile; /// 74 75 alias getConfigDirs = _bindArgs!(_getAppDir, CSIDL_LOCAL_APPDATA); /// 76 alias getDataDirs = _bindArgs!(_getAppDir, CSIDL_LOCAL_APPDATA); /// 77 78 string getHomeDir() { return getShellPath(CSIDL_PROFILE); } 79 } 80 else // POSIX 81 { 82 import std.algorithm.iteration; 83 import std.array; 84 import std.ascii; 85 import std.conv : octal; 86 import std.file; 87 import std.process; 88 import std.string; 89 90 private alias toLower = std.ascii.toLower; 91 92 private string getPosixAppName(string appName) 93 { 94 string s = appName ? appName : getExecutableName(); 95 string s2; 96 foreach (c; s) 97 if (isAlphaNum(c)) 98 s2 ~= toLower(c); 99 else 100 if (!s2.endsWith('-')) 101 s2 ~= '-'; 102 return s2; 103 } 104 105 private struct XdgDir 106 { 107 string homeVarName; 108 string homeDefaultValue; 109 string dirsVarName; 110 string dirsDefaultValue; 111 112 string getHome() const 113 { 114 return environment.get(homeVarName, homeDefaultValue.expandTilde()); 115 } 116 117 string getAppHome(string appName) const 118 { 119 return getHome().buildPath(getPosixAppName(appName)); 120 } 121 122 string[] getDirs() const 123 { 124 string paths = environment.get(dirsVarName, dirsDefaultValue); 125 return [getHome()] ~ paths.split(pathSeparator); 126 } 127 128 string[] getAppDirs(string appName) const 129 { 130 return getDirs() 131 .map!(dir => dir.buildPath(getPosixAppName(appName))) 132 .array(); 133 } 134 135 } 136 137 immutable XdgDir _xdgData = XdgDir("XDG_DATA_HOME" , "~/.local/share", "XDG_DATA_DIRS" , "/usr/local/share/:/usr/share/"); 138 immutable XdgDir _xdgConfig = XdgDir("XDG_CONFIG_HOME", "~/.config" , "XDG_CONFIG_DIRS", "/etc/xdg"); 139 immutable XdgDir _xdgCache = XdgDir("XDG_CACHE_HOME" , "~/.cache" ); 140 141 /*private*/ string _getXdgAppDir(alias xdgDir)(string appName = null) 142 { 143 return xdgDir.getAppHome(appName); 144 } 145 146 /*private*/ string[] _getXdgAppDirs(alias xdgDir)(string appName = null) 147 { 148 return xdgDir.getAppDirs(appName); 149 } 150 151 alias getDataDir = _getXdgAppDir!_xdgData; /// 152 alias getConfigDir = _getXdgAppDir!_xdgConfig; /// 153 alias getCacheDir = _getXdgAppDir!_xdgCache; /// 154 155 alias getDataDirs = _getXdgAppDirs!_xdgData; /// 156 alias getConfigDirs = _getXdgAppDirs!_xdgConfig; /// 157 158 string getHomeDir() { return environment.get("HOME"); } 159 } 160 161 /// Get the base name of the current executable. 162 string getExecutableName() 163 { 164 import std.file; 165 return thisExePath().baseName(); 166 } 167 168 /*private*/ template _bindArgs(alias fun, CTArgs...) 169 { 170 auto _bindArgs(RTArgs...)(auto ref RTArgs rtArgs) 171 { 172 return fun(CTArgs, rtArgs); 173 } 174 }