/// OS path utilities. /// Copyright: dd86k <dd@dax.moe> /// License: MIT /// Authors: $(LINK2 https://github.com/dd86k, dd86k) module os.path; version (Windows) { import core.sys.windows.windef : S_OK; import core.sys.windows.shlobj : CSIDL_PROFILE, CSIDL_LOCAL_APPDATA, CSIDL_APPDATA, SHGetFolderPathA, SHGetFolderPathW; import core.stdc.stdlib : malloc, free; import core.stdc.wchar_ : wcslen; import std.encoding : transcode; } else version (Posix) { import core.sys.posix.unistd : getuid, uid_t; import core.sys.posix.pwd : getpwuid, passwd; import core.stdc.string : strlen; } import std.process : environment; import std.path : dirSeparator, buildPath; import std.file : exists; // NOTE: As of Windows Vista, the SHGetSpecialFolderPathW function is a wrapper // for SHGetKnownFolderPath. The latter not defined in the shlobj module. /// Get the path to the current user's home folder. /// This does not verify if the path exists. /// Windows: Typically C:\\Users\\%USERNAME% /// Posix: Typically /home/$USERNAME /// Returns: Path or null on failure. string getHomeFolder() { version (Windows) { // 1. %USERPROFILE% if ("USERPROFILE" in environment) return environment["USERPROFILE"]; // 2. %HOMEDRIVE% and %HOMEPATH% if ("HOMEDRIVE" in environment && "HOMEPATH" in environment) return environment["HOMEDRIVE"] ~ environment["HOMEPATH"]; // 3. SHGetFolderPath wchar *buffer = cast(wchar*)malloc(1024); if (SHGetFolderPathW(null, CSIDL_PROFILE, null, 0, buffer) == S_OK) { string path; transcode(buffer[0..wcslen(buffer)], path); free(buffer); // since transcode allocated return path; } free(buffer); } else version (Posix) { // 1. $HOME if ("HOME" in environment) return environment["HOME"]; // 2. getpwuid+getuid uid_t uid = getuid(); if (uid >= 0) { passwd *wd = getpwuid(uid); if (wd) { return cast(immutable(char)[]) wd.pw_dir[0..strlen(wd.pw_dir)]; } } } return null; } /// Get the path to the current user data folder. /// This does not verify if the path exists. /// Windows: Typically C:\\Users\\%USERNAME%\\AppData\\Local /// Posix: Typically /home/$USERNAME/.config /// Returns: Path or null on failure. string getUserConfigFolder() { version (Windows) { // 1. %LOCALAPPDATA% if ("LOCALAPPDATA" in environment) return environment["LOCALAPPDATA"]; // 2. SHGetFolderPath wchar *buffer = cast(wchar*)malloc(1024); if (SHGetFolderPathW(null, CSIDL_LOCAL_APPDATA, null, 0, buffer) == S_OK) { string path; transcode(buffer[0..wcslen(buffer)], path); free(buffer); // transcode allocates return path; } free(buffer); } else version (Posix) { if (const(string) *xdg_config_home = "XDG_CONFIG_HOME" in environment) return *xdg_config_home; } // Fallback string base = getHomeFolder; if (base is null) return null; version (Windows) { return buildPath(base, "AppData", "Local"); } else version (Posix) { return buildPath(base, ".config"); } } /// Build the path for a given file name with the user home folder. /// This does not verify if the path exists. /// Windows: Typically C:\\Users\\%USERNAME%\\{filename} /// Posix: Typically /home/$USERNAME/{filename} /// Params: filename = Name of a file. /// Returns: Path or null on failure. string buildUserFile(string filename) { string base = getHomeFolder; if (base is null) return null; return buildPath(base, filename); } /// Build the path for a given file name and parent folder with the user data folder. /// This does not verify if the path exists. /// Windows: Typically C:\\Users\\%USERNAME%\\AppData\\Roaming\\{appname}\\{filename} /// Posix: Typically /home/$USERNAME/.config/{appname}/{filename} /// Params: /// appname = Name of the app. This acts as the parent folder. /// filename = Name of a file. /// Returns: Path or null on failure. string buildUserAppFile(string appname, string filename) { string base = getUserConfigFolder; if (base is null) return null; return buildPath(base, appname, filename); }