summaryrefslogtreecommitdiff
path: root/src/util/util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/util.cpp')
-rw-r--r--src/util/util.cpp262
1 files changed, 262 insertions, 0 deletions
diff --git a/src/util/util.cpp b/src/util/util.cpp
new file mode 100644
index 0000000..2d3e99f
--- /dev/null
+++ b/src/util/util.cpp
@@ -0,0 +1,262 @@
+// 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 <https://www.gnu.org/licenses/>.
+
+#include "util.hpp"
+#include "flags.hpp"
+#include "os.hpp"
+#include "process.hpp"
+#include "util/ptr.hpp"
+#include "util/socket.hpp"
+
+#include <array>
+#include <iomanip>
+#include <iostream>
+#include <memory>
+#include <random>
+#include <sstream>
+
+#include <fmt/format.h>
+#include <nlohmann/json.hpp>
+#include <openssl/evp.h>
+#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 <turbobase64/turbob64.h>
+# else
+# include "turbob64.h"
+# endif
+#endif
+#include <range/v3/all.hpp>
+
+#include <vips/vips8>
+
+namespace fs = std::filesystem;
+using njson = nlohmann::json;
+
+auto util::str_split(std::string_view str, std::string_view delim) -> std::vector<std::string>
+{
+ std::vector<std::string> result;
+ for (const auto word : ranges::views::split(str, delim)) {
+ result.emplace_back(ranges::to<std::string>(word));
+ }
+ return result;
+}
+
+auto util::get_process_tree(int pid) -> std::vector<int>
+{
+ std::vector<int> 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<Process>
+{
+ std::vector<Process> 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<char> res(bufsize, 0);
+ base64_encode_v2(input, length, reinterpret_cast<unsigned char *>(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<int>(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, EVP_MD_CTX_free>{EVP_MD_CTX_new()};
+#ifdef LIBRESSL_VERSION_NUMBER
+ const auto *evp = EVP_sha256();
+#else
+ const auto *evp = EVP_blake2b512();
+#endif
+ auto digest = std::array<unsigned char, EVP_MAX_MD_SIZE>();
+
+ 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<int>(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<void(void)> &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<double, std::milli> 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<std::uint16_t>
+{
+ 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());
+}