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 else // POSIX 79 { 80 import std.algorithm.iteration; 81 import std.array; 82 import std.ascii; 83 import std.conv : octal; 84 import std.file; 85 import std.process; 86 import std..string; 87 88 private alias toLower = std.ascii.toLower; 89 90 private string getPosixAppName(string appName) 91 { 92 string s = appName ? appName : getExecutableName(); 93 string s2; 94 foreach (c; s) 95 if (isAlphaNum(c)) 96 s2 ~= toLower(c); 97 else 98 if (!s2.endsWith('-')) 99 s2 ~= '-'; 100 return s2; 101 } 102 103 private struct XdgDir 104 { 105 string homeVarName; 106 string homeDefaultValue; 107 string dirsVarName; 108 string dirsDefaultValue; 109 110 string getHome() const 111 { 112 return environment.get(homeVarName, homeDefaultValue.expandTilde()); 113 } 114 115 string getAppHome(string appName) const 116 { 117 return getHome().buildPath(getPosixAppName(appName)); 118 } 119 120 string[] getDirs() const 121 { 122 string paths = environment.get(dirsVarName, dirsDefaultValue); 123 return [getHome()] ~ paths.split(pathSeparator); 124 } 125 126 string[] getAppDirs(string appName) const 127 { 128 return getDirs() 129 .map!(dir => dir.buildPath(getPosixAppName(appName))) 130 .array(); 131 } 132 133 } 134 135 immutable XdgDir _xdgData = XdgDir("XDG_DATA_HOME" , "~/.local/share", "XDG_DATA_DIRS" , "/usr/local/share/:/usr/share/"); 136 immutable XdgDir _xdgConfig = XdgDir("XDG_CONFIG_HOME", "~/.config" , "XDG_CONFIG_DIRS", "/etc/xdg"); 137 immutable XdgDir _xdgCache = XdgDir("XDG_CACHE_HOME" , "~/.cache" ); 138 139 /*private*/ string _getXdgAppDir(alias xdgDir)(string appName = null) 140 { 141 return xdgDir.getAppHome(appName); 142 } 143 144 /*private*/ string[] _getXdgAppDirs(alias xdgDir)(string appName = null) 145 { 146 return xdgDir.getAppDirs(appName); 147 } 148 149 alias getDataDir = _getXdgAppDir!_xdgData; /// 150 alias getConfigDir = _getXdgAppDir!_xdgConfig; /// 151 alias getCacheDir = _getXdgAppDir!_xdgCache; /// 152 153 alias getDataDirs = _getXdgAppDirs!_xdgData; /// 154 alias getConfigDirs = _getXdgAppDirs!_xdgConfig; /// 155 } 156 157 /// Get the base name of the current executable. 158 string getExecutableName() 159 { 160 import std.file; 161 return thisExePath().baseName(); 162 } 163 164 /*private*/ template _bindArgs(alias fun, CTArgs...) 165 { 166 auto _bindArgs(RTArgs...)(auto ref RTArgs rtArgs) 167 { 168 return fun(CTArgs, rtArgs); 169 } 170 }