Files

100 lines
3.9 KiB
C++

#include "prefix.h"
#include "system_exports.h"
#include <cctype>
#include <cstdlib>
#include <system_error>
#include <vector>
#include <stdexcept>
#include <string>
namespace raven {
NativePrefix::NativePrefix(std::filesystem::path root) : directory(std::move(root))
{
if (directory.empty())
throw std::runtime_error("native prefix requires a directory");
// The folders every Windows installation has, so that programs can put files in the places they expect.
for (const char *folder : {"windows", "windows/system32", "windows/syswow64", "Program Files/Common Files", "Program Files (x86)/Common Files",
"ProgramData/Microsoft/Windows/Start Menu/Programs",
"Users/Public/Desktop", "Users/Public/Documents"})
std::filesystem::create_directories(directory / "drive_c" / folder);
// The Fonts folder holds the installed fonts, as on Windows: links to the host's font files, made once when the folder is new.
const auto fonts = directory / "drive_c" / "windows" / "Fonts";
std::error_code error;
if (std::filesystem::create_directory(fonts, error)) {
std::vector<std::filesystem::path> roots{"/usr/share/fonts", "/usr/local/share/fonts"};
if (const char *home = std::getenv("HOME"); home && *home)
roots.push_back(std::filesystem::path(home) / ".local" / "share" / "fonts");
for (const auto &root : roots)
for (std::filesystem::recursive_directory_iterator walk(root, std::filesystem::directory_options::follow_directory_symlink, error), end;
walk != end; walk.increment(error)) {
auto extension = walk->path().extension().string();
for (auto &character : extension)
character = char(std::tolower(static_cast<unsigned char>(character)));
if ((extension == ".ttf" || extension == ".otf" || extension == ".ttc") && walk->is_regular_file(error))
std::filesystem::create_symlink(walk->path(), fonts / walk->path().filename(), error);
}
}
}
void NativePrefix::place_runtime_components(const std::filesystem::path &runtime) const
{
// The loader takes these libraries from beside the runtime whatever the prefix holds: the copies only answer programs that search for,
// read or hash them. A newer runtime refreshes them.
const auto windows = directory / "drive_c" / "windows";
for (const auto &[source, folder] : {std::pair{runtime, "system32"}, std::pair{runtime / "x86", "syswow64"}})
for (const auto name : runtime_components) {
std::error_code error;
const auto from = source / name;
if (std::filesystem::is_regular_file(from, error))
std::filesystem::copy_file(from, windows / folder / name, std::filesystem::copy_options::update_existing, error);
}
}
NativePrefix NativePrefix::from_environment()
{
if (const char *configured = std::getenv("RAVEN_PREFIX"); configured && *configured)
return NativePrefix(configured);
const char *home = std::getenv("HOME");
if (!home || !*home)
throw std::runtime_error("native prefix needs RAVEN_PREFIX or HOME");
return NativePrefix(std::filesystem::path(home) / ".local" / "share" / "raven" / "default");
}
std::optional<std::filesystem::path> NativePrefix::resolve(std::string_view windows_path) const
{
std::string text(windows_path);
for (auto &character : text)
if (character == '\\')
character = '/';
std::filesystem::path base = directory / "drive_c" / "windows";
if (text.size() >= 2 && text[1] == ':') {
const char drive = char(text[0] | 0x20);
if (drive < 'a' || drive > 'z')
return std::nullopt;
base = directory / (std::string("drive_") + drive);
text.erase(0, 2);
}
if (!text.empty() && text[0] == '/')
text.erase(0, 1);
if (text.empty())
return std::nullopt;
std::filesystem::path result = base;
size_t start = 0;
while (start <= text.size()) {
const size_t end = text.find('/', start);
const auto part = text.substr(start, end == text.npos ? text.npos : end - start);
if (part == "..")
return std::nullopt;
if (!part.empty() && part != ".")
result /= part;
if (end == text.npos)
break;
start = end + 1;
}
return result;
}
}