#include "file_utils.h" #include "string_utils.h" #include #include #include #include #include #include #include #include #include #include namespace fs = ghc::filesystem; #ifndef PROCDIR #define PROCDIR "/proc" #endif std::string read_line(const std::string& filename) { std::string line; std::ifstream file(filename); if (file.fail()){ return line; } std::getline(file, line); return line; } std::string get_basename(const std::string&& path) { auto npos = path.find_last_of("/\\"); if (npos == std::string::npos) return path; if (npos < path.size() - 1) return path.substr(npos + 1); return path; } #ifdef __linux__ std::vector ls(const char* root, const char* prefix, LS_FLAGS flags) { std::vector list; struct dirent* dp; DIR* dirp = opendir(root); if (!dirp) { SPDLOG_ERROR("Error opening directory '{}': {}", root, strerror(errno)); return list; } while ((dp = readdir(dirp))) { if ((prefix && !starts_with(dp->d_name, prefix)) || !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; switch (dp->d_type) { case DT_LNK: { struct stat s; std::string path(root); if (path.back() != '/') path += "/"; path += dp->d_name; if (stat(path.c_str(), &s)) continue; if (((flags & LS_DIRS) && S_ISDIR(s.st_mode)) || ((flags & LS_FILES) && S_ISREG(s.st_mode))) { list.push_back(dp->d_name); } break; } case DT_DIR: if (flags & LS_DIRS) list.push_back(dp->d_name); break; case DT_REG: if (flags & LS_FILES) list.push_back(dp->d_name); break; } } closedir(dirp); return list; } bool file_exists(const std::string& path) { struct stat s; return !stat(path.c_str(), &s) && !S_ISDIR(s.st_mode); } bool dir_exists(const std::string& path) { struct stat s; return !stat(path.c_str(), &s) && S_ISDIR(s.st_mode); } std::string read_symlink(const char * link) { char result[PATH_MAX] {}; ssize_t count = readlink(link, result, PATH_MAX); return std::string(result, (count > 0) ? count : 0); } std::string read_symlink(const std::string&& link) { return read_symlink(link.c_str()); } std::string get_exe_path() { return read_symlink(PROCDIR "/self/exe"); } std::string get_wine_exe_name(bool keep_ext) { const std::string exe_path = get_exe_path(); if (!ends_with(exe_path, "wine-preloader") && !ends_with(exe_path, "wine64-preloader")) { return std::string(); } std::string line = read_line(PROCDIR "/self/comm"); // max 16 characters though if (ends_with(line, ".exe", true)) { auto dot = keep_ext ? std::string::npos : line.find_last_of('.'); return line.substr(0, dot); } std::ifstream cmdline(PROCDIR "/self/cmdline"); // Iterate over arguments (separated by NUL byte). while (std::getline(cmdline, line, '\0')) { auto n = std::string::npos; if (!line.empty() && ((n = line.find_last_of("/\\")) != std::string::npos) && n < line.size() - 1) // have at least one character { auto dot = keep_ext ? std::string::npos : line.find_last_of('.'); if (dot < n) dot = line.size(); return line.substr(n + 1, dot - n - 1); } else if (ends_with(line, ".exe", true)) { auto dot = keep_ext ? std::string::npos : line.find_last_of('.'); return line.substr(0, dot); } } return std::string(); } std::string get_home_dir() { std::string path; const char* p = getenv("HOME"); if (p) path = p; return path; } std::string get_data_dir() { const char* p = getenv("XDG_DATA_HOME"); if (p) return p; std::string path = get_home_dir(); if (!path.empty()) path += "/.local/share"; return path; } std::string get_config_dir() { const char* p = getenv("XDG_CONFIG_HOME"); if (p) return p; std::string path = get_home_dir(); if (!path.empty()) path += "/.config"; return path; } bool lib_loaded(const std::string& lib) { fs::path path(PROCDIR "/self/map_files/"); for (auto& p : fs::directory_iterator(path)) { auto file = p.path().string(); auto sym = read_symlink(file.c_str()); if (sym.find(lib) != std::string::npos) { return true; } } return false; } std::string remove_parentheses(const std::string& text) { // Remove parentheses and text between them std::regex pattern("\\([^)]*\\)"); return std::regex_replace(text, pattern, ""); } #endif // __linux__