// SPDX-License-Identifier: BSD-2-Clause // This code is part of the sfizz library and is licensed under a BSD 2-clause // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "NativeHelpers.h" #include #include #include #include #include #include #if defined(_WIN32) #include #include const fs::path& getUserDocumentsDirectory() { static const fs::path directory = []() -> fs::path { std::unique_ptr path(new WCHAR[32768]); if (SHGetFolderPathW(nullptr, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, path.get()) != S_OK) throw std::runtime_error("Cannot get the document directory."); return fs::path(path.get()); }(); return directory; } wchar_t *stringToWideChar(const char *str, int strCch) { unsigned strSize = MultiByteToWideChar(CP_UTF8, 0, str, strCch, nullptr, 0); if (strSize == 0) return {}; std::unique_ptr strW(new wchar_t[strSize]); if (MultiByteToWideChar(CP_UTF8, 0, str, strCch, strW.get(), strSize) == 0) return {}; return strW.release(); } char* stringToUTF8(const wchar_t *strW, int strWCch) { unsigned strSize = WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, nullptr, 0, nullptr, nullptr); if (strSize == 0) return {}; std::unique_ptr str(new char[strSize]); if (WideCharToMultiByte(CP_UTF8, 0, strW, strWCch, str.get(), strSize, nullptr, nullptr) == 0) return {}; return str.release(); } #elif defined(__APPLE__) // implemented in NativeHelpers.mm #else const fs::path& getUserDocumentsDirectory() { static const fs::path directory = []() -> fs::path { for (const XdgUserDirsEntry& ent : parseXdgUserDirs(getXdgConfigHome() / "user-dirs.dirs")) { if (ent.name == "XDG_DOCUMENTS_DIR") return ent.value; } return getUserHomeDirectory() / "Documents"; }(); return directory; } const fs::path& getUserHomeDirectory() { static const fs::path directory = []() -> fs::path { const char* home = getenv("HOME"); if (home && home[0] == '/') return fs::u8path(home); else throw std::runtime_error("Cannot get the home directory."); }(); return directory; } const fs::path& getXdgConfigHome() { static const fs::path directory = []() -> fs::path { const char* config = getenv("XDG_CONFIG_HOME"); if (config && config[0] == '/') return fs::u8path(config); else return getUserHomeDirectory() / ".config"; }(); return directory; } std::vector parseXdgUserDirs(const fs::path& userDirsPath) { // from user-dirs.dirs(5) // This file contains lines of the form `XDG_NAME_DIR=VALUE` // VALUE must be of the form "$HOME/Path" or "/Path". // Lines beginning with a # character are ignored. std::vector ents; const fs::path& home = getUserHomeDirectory(); fs::ifstream in(userDirsPath); std::string lineBuf; lineBuf.reserve(256); while (std::getline(in, lineBuf)) { absl::string_view line(lineBuf); line = absl::StripLeadingAsciiWhitespace(line); if (line.empty() || line.front() == '#') continue; size_t pos = line.find('='); if (pos == line.npos) continue; XdgUserDirsEntry ent; ent.name = std::string(line.substr(0, pos)); absl::string_view rawValue = line.substr(pos + 1); rawValue = absl::StripTrailingAsciiWhitespace(rawValue); if (rawValue.size() < 2 || rawValue.front() != '"' || rawValue.back() != '"') continue; rawValue.remove_prefix(1); rawValue.remove_suffix(1); if (!rawValue.empty() && rawValue.front() == '/') ent.value = fs::u8path(rawValue.begin(), rawValue.end()); else if (absl::StartsWith(rawValue, "$HOME")) { absl::string_view part = rawValue.substr(5); ent.value = home / fs::u8path(part.begin(), part.end()).relative_path(); } else continue; ents.push_back(std::move(ent)); } return ents; } #endif