// Display images inside a terminal // Copyright (C) 2023 JustKidding // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "util.hpp" #include "flags.hpp" #include "os.hpp" #include "process.hpp" #include "util/ptr.hpp" #include "util/socket.hpp" #include #include #include #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER < 0x10100000L # define EVP_MD_CTX_new EVP_MD_CTX_create # define EVP_MD_CTX_free EVP_MD_CTX_destroy #endif #ifdef ENABLE_TURBOBASE64 # ifdef WITH_SYSTEM_TURBOBASE64 # include # else # include "turbob64.h" # endif #endif #include #include namespace fs = std::filesystem; using njson = nlohmann::json; auto util::str_split(std::string_view str, std::string_view delim) -> std::vector { std::vector result; for (const auto word : ranges::views::split(str, delim)) { result.emplace_back(ranges::to(word)); } return result; } auto util::get_process_tree(int pid) -> std::vector { std::vector res; Process runner(pid); while (runner.pid > 1) { res.push_back(runner.pid); runner = Process(runner.ppid); } return res; } auto util::get_process_tree_v2(int pid) -> std::vector { std::vector tree; Process runner(pid); while (runner.pid > 1) { tree.push_back(runner); runner = Process(runner.ppid); } return tree; } auto util::get_cache_path() -> std::string { const auto home = os::getenv("HOME").value_or(util::temp_directory_path()); const auto cache_home = os::getenv("XDG_CACHE_HOME").value_or(fmt::format("{}/.cache", home)); return fmt::format("{}/ueberzugpp/", cache_home); } auto util::get_log_filename() -> std::string { const auto user = os::getenv("USER").value_or("NOUSER"); return fmt::format("ueberzugpp-{}.log", user); } auto util::get_socket_path(int pid) -> std::string { return fmt::format("{}/ueberzugpp-{}.socket", util::temp_directory_path().string(), pid); } void util::send_socket_message(const std::string_view msg, const std::string_view endpoint) { try { UnixSocket socket; socket.connect_to_endpoint(endpoint); socket.write(msg.data(), msg.size()); } catch (const std::system_error &err) { return; } } auto util::base64_encode(const unsigned char *input, size_t length) -> std::string { const size_t bufsize = 4 * ((length + 2) / 3) + 1; std::vector res(bufsize, 0); base64_encode_v2(input, length, reinterpret_cast(res.data())); return {res.data()}; } void util::base64_encode_v2(const unsigned char *input, size_t length, unsigned char *out) { #ifdef ENABLE_TURBOBASE64 tb64enc(input, length, out); #else EVP_EncodeBlock(out, input, static_cast(length)); #endif } auto util::get_b2_hash_ssl(const std::string_view str) -> std::string { std::stringstream sstream; const auto mdctx = c_unique_ptr{EVP_MD_CTX_new()}; #ifdef LIBRESSL_VERSION_NUMBER const auto *evp = EVP_sha256(); #else const auto *evp = EVP_blake2b512(); #endif auto digest = std::array(); EVP_DigestInit_ex(mdctx.get(), evp, nullptr); EVP_DigestUpdate(mdctx.get(), str.data(), str.size()); unsigned int digest_len = 0; EVP_DigestFinal_ex(mdctx.get(), digest.data(), &digest_len); sstream << std::hex << std::setw(2) << std::setfill('0'); for (unsigned int i = 0; i < digest_len; ++i) { sstream << static_cast(digest[i]); } return sstream.str(); } void util::move_cursor(int row, int col) { std::cout << "\033[" << row << ";" << col << "f" << std::flush; } void util::save_cursor_position() { std::cout << "\0337" << std::flush; } void util::restore_cursor_position() { std::cout << "\0338" << std::flush; } auto util::get_cache_file_save_location(const fs::path &path) -> std::string { return fmt::format("{}{}{}", get_cache_path(), get_b2_hash_ssl(path.string()), path.extension().string()); } void util::benchmark(const std::function &func) { using std::chrono::duration; using std::chrono::duration_cast; using std::chrono::high_resolution_clock; using std::chrono::milliseconds; const auto ti1 = high_resolution_clock::now(); func(); const auto ti2 = high_resolution_clock::now(); const duration ms_double = ti2 - ti1; std::cout << ms_double.count() << "ms\n"; } void util::send_command(const Flags &flags) { if (flags.cmd_action == "exit") { util::send_socket_message("EXIT", flags.cmd_socket); return; } if (flags.cmd_action == "remove") { const njson json = {{"action", "remove"}, {"identifier", flags.cmd_id}}; util::send_socket_message(json.dump(), flags.cmd_socket); return; } const njson json = {{"action", flags.cmd_action}, {"identifier", flags.cmd_id}, {"max_width", std::stoi(flags.cmd_max_width)}, {"max_height", std::stoi(flags.cmd_max_height)}, {"x", std::stoi(flags.cmd_x)}, {"y", std::stoi(flags.cmd_y)}, {"path", flags.cmd_file_path}}; util::send_socket_message(json.dump(), flags.cmd_socket); } void util::clear_terminal_area(int xcoord, int ycoord, int width, int height) { save_cursor_position(); const auto line_clear = std::string(width, ' '); for (int i = ycoord; i <= height + 2; ++i) { util::move_cursor(i, xcoord); std::cout << line_clear; } std::cout << std::flush; restore_cursor_position(); } auto util::generate_random_string(size_t length) -> std::string { constexpr auto chars = std::to_array({'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}); auto rng_dev = std::random_device(); auto rng = std::mt19937(rng_dev()); auto dist = std::uniform_int_distribution{{}, chars.size() - 1}; std::string result(length, 0); std::generate_n(std::begin(result), length, [&chars, &dist, &rng] { return chars[dist(rng)]; }); return result; } auto util::read_exif_rotation(const fs::path &path) -> std::optional { try { auto image = vips::VImage::new_from_file(path.c_str()); return image.get_int("orientation"); } catch (const vips::VError &) { return {}; } } auto util::round_up(int num_to_round, int multiple) -> int { if (multiple == 0) { return num_to_round; } int remainder = num_to_round % multiple; if (remainder == 0) { return num_to_round; } return num_to_round + multiple - remainder; } auto util::temp_directory_path() -> fs::path { return os::getenv("UEBERZUGPP_TMPDIR").value_or(fs::temp_directory_path()); }