git repos / sago_web_git

src/util.cpp

browsing at commit = 9881d02ac31dadb3794f4728eb6cef4afe5721d3

raw · blame · history

#include "util.h"

#include <algorithm>
#include <array>
#include <cstdio>
#include <cstdlib>

namespace util {

namespace {
constexpr int MIN_OID_LEN = 4;
constexpr int MAX_OID_LEN = 40;
} // namespace

bool is_hex_oid(const std::string &s) {
    if (s.length() < MIN_OID_LEN || s.length() > MAX_OID_LEN)
        return false;
    for (char c : s) {
        const bool is_digit = c >= '0' && c <= '9';
        const bool is_lower_hex = c >= 'a' && c <= 'f';
        if (!is_digit && !is_lower_hex)
            return false;
    }
    return true;
}

std::string human_size(uint64_t bytes) {
    constexpr uint64_t unit = 1024;
    const std::array<const char *, 5> suffixes{"B", "KiB", "MiB", "GiB", "TiB"};
    if (bytes < unit)
        return std::to_string(bytes) + " B";

    double value = static_cast<double>(bytes);
    size_t i = 0;
    while (value >= unit && i + 1 < suffixes.size()) {
        value /= unit;
        ++i;
    }
    std::array<char, 32> buf{};
    std::snprintf(buf.data(), buf.size(), "%.1f %s", value, suffixes[i]);
    return std::string(buf.data());
}

std::string format_time(std::time_t t) {
    std::tm tm{};
    gmtime_r(&t, &tm);
    std::array<char, 32> buf{};
    std::strftime(buf.data(), buf.size(), "%Y-%m-%d %H:%M", &tm);
    return std::string(buf.data());
}

std::string basename(const std::string &path) {
    const size_t pos = path.find_last_of('/');
    if (pos == std::string::npos)
        return path;
    return path.substr(pos + 1);
}

std::string parent_path(const std::string &path) {
    const size_t pos = path.find_last_of('/');
    if (pos == std::string::npos)
        return "";
    return path.substr(0, pos);
}

std::string trim_slashes(const std::string &path) {
    size_t start = 0;
    size_t end = path.length();
    while (start < end && path[start] == '/')
        ++start;
    while (end > start && path[end - 1] == '/')
        --end;
    return path.substr(start, end - start);
}

std::string sanitize_filename(const std::string &name) {
    std::string out = name;
    for (char &c : out) {
        const bool is_digit = c >= '0' && c <= '9';
        const bool is_upper = c >= 'A' && c <= 'Z';
        const bool is_lower = c >= 'a' && c <= 'z';
        const bool is_safe_punct = c == '.' || c == '_' || c == '-';
        if (!is_digit && !is_upper && !is_lower && !is_safe_punct)
            c = '_';
    }
    return out;
}

bool is_markdown_path(const std::string &path) {
    const size_t dot = path.find_last_of('.');
    if (dot == std::string::npos)
        return false;
    std::string ext = path.substr(dot + 1);
    std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
    return ext == "md" || ext == "markdown";
}

std::string image_mime_type(const std::string &path) {
    const size_t dot = path.find_last_of('.');
    if (dot == std::string::npos)
        return "";
    std::string ext = path.substr(dot + 1);
    std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
    if (ext == "png")
        return "image/png";
    if (ext == "jpg" || ext == "jpeg")
        return "image/jpeg";
    if (ext == "gif")
        return "image/gif";
    if (ext == "webp")
        return "image/webp";
    if (ext == "svg")
        return "image/svg+xml";
    if (ext == "bmp")
        return "image/bmp";
    if (ext == "ico")
        return "image/x-icon";
    if (ext == "avif")
        return "image/avif";
    return "";
}

bool show_emails() {
    static const bool value = [] {
        const char *flag = std::getenv("SHOW_EMAILS");
        return flag && std::string(flag) == "1";
    }();
    return value;
}

} // namespace util