#include "tools/cabana/utils/util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #endif #include "common/util.h" static const std::thread::id main_thread_id = std::this_thread::get_id(); static std::mutex main_thread_queue_mutex; static std::vector> main_thread_queue; bool utils::isMainThread() { return std::this_thread::get_id() == main_thread_id; } void utils::runOnMainThread(std::function fn) { if (isMainThread()) { fn(); } else { std::lock_guard lk(main_thread_queue_mutex); main_thread_queue.push_back(std::move(fn)); } } void utils::drainMainThreadQueue() { std::vector> fns; { std::lock_guard lk(main_thread_queue_mutex); fns.swap(main_thread_queue); } for (auto &fn : fns) fn(); } void SegmentTree::build(int n, const std::function &y) { size = n; tree.resize(4 * size); if (size > 0) { build_tree(y, 1, 0, size - 1); } } void SegmentTree::build_tree(const std::function &y, int n, int left, int right) { if (left == right) { tree[n] = {y(left), y(left)}; } else { const int mid = (left + right) >> 1; build_tree(y, 2 * n, left, mid); build_tree(y, 2 * n + 1, mid + 1, right); tree[n] = {std::min(tree[2 * n].first, tree[2 * n + 1].first), std::max(tree[2 * n].second, tree[2 * n + 1].second)}; } } std::pair SegmentTree::get_minmax(int n, int left, int right, int range_left, int range_right) const { if (range_left > right || range_right < left) return {std::numeric_limits::max(), std::numeric_limits::lowest()}; if (range_left <= left && range_right >= right) return tree[n]; int mid = (left + right) >> 1; auto l = get_minmax(2 * n, left, mid, range_left, range_right); auto r = get_minmax(2 * n + 1, mid + 1, right, range_left, range_right); return {std::min(l.first, r.first), std::max(l.second, r.second)}; } UnixSignalHandler::UnixSignalHandler(std::function on_signal) { if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sig_fd)) { fprintf(stderr, "Couldn't create TERM socketpair\n"); abort(); } waiter = std::thread([this, on_signal = std::move(on_signal)]() { int tmp = 0; while (::read(sig_fd[1], &tmp, sizeof(tmp)) < 0) { if (errno != EINTR) return; } if (shutting_down.load()) return; on_signal(); }); std::signal(SIGINT, signalHandler); std::signal(SIGTERM, UnixSignalHandler::signalHandler); } UnixSignalHandler::~UnixSignalHandler() { shutting_down.store(true); int dummy = 0; (void)!::write(sig_fd[0], &dummy, sizeof(dummy)); if (waiter.joinable()) waiter.join(); ::close(sig_fd[0]); ::close(sig_fd[1]); } void UnixSignalHandler::signalHandler(int s) { (void)!::write(sig_fd[0], &s, sizeof(s)); } ValidState validateName(std::string &input) { std::replace(input.begin(), input.end(), ' ', '_'); if (input.empty()) return ValidState::Intermediate; for (const unsigned char c : input) { if (!std::isalnum(c) && c != '_') return ValidState::Invalid; } return ValidState::Acceptable; } ValidState validateNodes(const std::string &input) { if (input.empty()) return ValidState::Intermediate; bool need_word = true; for (const unsigned char c : input) { if (std::isalnum(c) || c == '_') { need_word = false; } else if (c == ',' && !need_word) { need_word = true; } else { return ValidState::Invalid; } } return need_word ? ValidState::Intermediate : ValidState::Acceptable; } ValidState validateNonWhitespace(const std::string &input) { if (input.empty()) return ValidState::Intermediate; for (const unsigned char c : input) { if (std::isspace(c)) return ValidState::Invalid; } return ValidState::Acceptable; } ValidState validateIpAddress(const std::string &input) { if (input.empty()) return ValidState::Intermediate; int dots = 0; int value = 0; bool has_digit = false; for (const unsigned char c : input) { if (std::isdigit(c)) { value = has_digit ? value * 10 + (c - '0') : (c - '0'); if (value > 255) return ValidState::Invalid; has_digit = true; } else if (c == '.') { if (!has_digit || dots >= 3) return ValidState::Invalid; ++dots; has_digit = false; value = 0; } else { return ValidState::Invalid; } } return (dots == 3 && has_digit) ? ValidState::Acceptable : ValidState::Intermediate; } ValidState validateDouble(const std::string &input) { if (input.empty()) return ValidState::Intermediate; if (input.find_first_of("xXpP") != std::string::npos) { return ValidState::Invalid; } const char *start = input.c_str(); char *end = nullptr; const double value = std::strtod(start, &end); if (end == start) { if (input == "-" || input == "+" || input == "." || input == "-." || input == "+.") { return ValidState::Intermediate; } return ValidState::Invalid; } if (*end == '\0') { return std::isfinite(value) ? ValidState::Acceptable : ValidState::Invalid; } for (const char *p = end; *p; ++p) { const char c = *p; if (!(c == 'e' || c == 'E' || c == '+' || c == '-' || c == '.' || (c >= '0' && c <= '9'))) { return ValidState::Invalid; } } return ValidState::Intermediate; } extern const unsigned char bootstrap_icons_svg[]; extern const size_t bootstrap_icons_svg_len; static std::unordered_map load_bootstrap_icons() { std::unordered_map icons; const std::string content(reinterpret_cast(bootstrap_icons_svg), bootstrap_icons_svg_len); const std::string sym_open = ""); icons[id] = std::move(svg_str); } } pos = end; } return icons; } namespace utils { std::string homePath() { const char *home = ::getenv("HOME"); return home ? home : ""; } std::filesystem::path configPath() { #ifdef __APPLE__ return std::filesystem::path(homePath()) / "Library/Preferences"; #else const char *xdg = ::getenv("XDG_CONFIG_HOME"); return (xdg && xdg[0]) ? std::filesystem::path(xdg) : std::filesystem::path(homePath()) / ".config"; #endif } #ifdef __APPLE__ static const char *clipboard_read_cmds[] = {"pbpaste"}; static const char *clipboard_write_cmds[] = {"pbcopy"}; #else static const char *clipboard_read_cmds[] = {"wl-paste --no-newline 2>/dev/null", "xclip -selection clipboard -o 2>/dev/null", "xsel -ob 2>/dev/null"}; static const char *clipboard_write_cmds[] = {"wl-copy 2>/dev/null", "xclip -selection clipboard 2>/dev/null", "xsel -ib 2>/dev/null"}; #endif bool getClipboardText(std::string *text) { text->clear(); bool has_tool = false; for (const char *cmd : clipboard_read_cmds) { FILE *f = ::popen(cmd, "r"); if (!f) continue; std::string out; char buf[4096]; for (size_t n; (n = ::fread(buf, 1, sizeof(buf), f)) > 0;) out.append(buf, n); int status = ::pclose(f); if (status == 0) { *text = std::move(out); return true; } has_tool |= WIFEXITED(status) && WEXITSTATUS(status) != 127; } return has_tool; } bool setClipboardText(const std::string &text) { std::signal(SIGPIPE, SIG_IGN); for (const char *cmd : clipboard_write_cmds) { FILE *f = ::popen(cmd, "w"); if (!f) continue; size_t written = ::fwrite(text.data(), 1, text.size(), f); if (::pclose(f) == 0 && written == text.size()) return true; } return false; } std::string bootstrapSvg(const std::string &id) { static auto icons = load_bootstrap_icons(); auto it = icons.find(id); return it != icons.end() ? it->second : std::string(); } } int num_decimals(double num) { char buf[32]; snprintf(buf, sizeof(buf), "%g", num); const char *dot = strpbrk(buf, ".,"); return dot ? (int)strlen(dot + 1) : 0; } std::filesystem::path executableDir() { #ifdef __APPLE__ char buf[PATH_MAX]; uint32_t size = sizeof(buf); if (_NSGetExecutablePath(buf, &size) != 0) return {}; std::error_code ec; auto path = std::filesystem::canonical(buf, ec); return (ec ? std::filesystem::path(buf) : path).parent_path(); #else return std::filesystem::path(util::readlink("/proc/self/exe")).parent_path(); #endif }