foxygit / TransTUI Log in
commits tags

/src/util.c · 5.08 KB

raw
#include "util.h"

#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <time.h>
#include <unistd.h>

void fmt_size(int64_t bytes, char *buf, size_t n)
{
    static const char *units[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
    double v = (double)bytes;
    size_t u = 0;
    while (v >= 1024.0 && u < 5) {
        v /= 1024.0;
        u++;
    }
    if (u == 0)
        snprintf(buf, n, "%.0f %s", v, units[u]);
    else
        snprintf(buf, n, "%.2f %s", v, units[u]);
}

void fmt_speed(int64_t bytes_per_sec, char *buf, size_t n)
{
    char tmp[32];
    fmt_size(bytes_per_sec, tmp, sizeof(tmp));
    snprintf(buf, n, "%s/s", tmp);
}

void fmt_eta(int seconds, char *buf, size_t n)
{
    if (seconds < 0) {
        snprintf(buf, n, "\xe2\x88\x9e"); /* infinity, unknown */
        return;
    }
    if (seconds == 0) {
        snprintf(buf, n, "klar");
        return;
    }
    int days = seconds / 86400;
    int hours = (seconds % 86400) / 3600;
    int mins = (seconds % 3600) / 60;
    int secs = seconds % 60;
    if (days > 0)
        snprintf(buf, n, "%dd %dh", days, hours);
    else if (hours > 0)
        snprintf(buf, n, "%dh %dm", hours, mins);
    else if (mins > 0)
        snprintf(buf, n, "%dm %ds", mins, secs);
    else
        snprintf(buf, n, "%ds", secs);
}

void fmt_ratio(double ratio, char *buf, size_t n)
{
    if (ratio < 0)
        snprintf(buf, n, "\xe2\x88\x9e");
    else
        snprintf(buf, n, "%.2f", ratio);
}

void fmt_time(long unix_time, char *buf, size_t n)
{
    if (unix_time <= 0) {
        snprintf(buf, n, "-");
        return;
    }
    time_t t = (time_t)unix_time;
    struct tm tmv;
    localtime_r(&t, &tmv);
    strftime(buf, n, "%Y-%m-%d %H:%M", &tmv);
}

static const char b64tab[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

size_t base64_encoded_size(size_t len)
{
    return ((len + 2) / 3) * 4;
}

long base64_encode(const unsigned char *data, size_t len, char *out, size_t outsize)
{
    size_t needed = base64_encoded_size(len);
    if (outsize < needed + 1)
        return -1;

    size_t i, o = 0;
    for (i = 0; i + 2 < len; i += 3) {
        uint32_t n = ((uint32_t)data[i] << 16) | ((uint32_t)data[i + 1] << 8) | data[i + 2];
        out[o++] = b64tab[(n >> 18) & 0x3F];
        out[o++] = b64tab[(n >> 12) & 0x3F];
        out[o++] = b64tab[(n >> 6) & 0x3F];
        out[o++] = b64tab[n & 0x3F];
    }
    size_t rem = len - i;
    if (rem == 1) {
        uint32_t n = (uint32_t)data[i] << 16;
        out[o++] = b64tab[(n >> 18) & 0x3F];
        out[o++] = b64tab[(n >> 12) & 0x3F];
        out[o++] = '=';
        out[o++] = '=';
    } else if (rem == 2) {
        uint32_t n = ((uint32_t)data[i] << 16) | ((uint32_t)data[i + 1] << 8);
        out[o++] = b64tab[(n >> 18) & 0x3F];
        out[o++] = b64tab[(n >> 12) & 0x3F];
        out[o++] = b64tab[(n >> 6) & 0x3F];
        out[o++] = '=';
    }
    out[o] = '\0';
    return (long)o;
}

unsigned char *read_file_all(const char *path, size_t *out_len)
{
    FILE *f = fopen(path, "rb");
    if (!f)
        return NULL;
    if (fseek(f, 0, SEEK_END) != 0) {
        fclose(f);
        return NULL;
    }
    long sz = ftell(f);
    if (sz < 0) {
        fclose(f);
        return NULL;
    }
    rewind(f);
    unsigned char *buf = malloc((size_t)sz > 0 ? (size_t)sz : 1);
    if (!buf) {
        fclose(f);
        return NULL;
    }
    size_t rd = fread(buf, 1, (size_t)sz, f);
    fclose(f);
    if (rd != (size_t)sz) {
        free(buf);
        return NULL;
    }
    if (out_len)
        *out_len = rd;
    return buf;
}

int str_ends_with(const char *s, const char *suffix)
{
    size_t ls = strlen(s), lsuf = strlen(suffix);
    if (lsuf > ls)
        return 0;
    return strcmp(s + (ls - lsuf), suffix) == 0;
}

int str_starts_with(const char *s, const char *prefix)
{
    return strncmp(s, prefix, strlen(prefix)) == 0;
}

int single_instance_lock(void)
{
    char path[512];
    const char *rundir = getenv("XDG_RUNTIME_DIR");
    if (rundir && rundir[0])
        snprintf(path, sizeof(path), "%s/transtui.lock", rundir);
    else
        snprintf(path, sizeof(path), "/tmp/transtui-%d.lock", (int)getuid());

    int fd = open(path, O_CREAT | O_RDWR, 0600);
    if (fd < 0)
        return -1;

    if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
        close(fd);
        return 0;
    }
    /* fd is deliberately leaked - it must stay open for the process's
     * lifetime for the lock to hold, and is closed by the kernel on exit
     * (normal or crash) either way. */
    return 1;
}

int str_ci_contains(const char *haystack, const char *needle)
{
    size_t nlen = strlen(needle);
    if (nlen == 0)
        return 1;
    size_t hlen = strlen(haystack);
    if (nlen > hlen)
        return 0;
    for (size_t i = 0; i + nlen <= hlen; i++) {
        size_t j = 0;
        for (; j < nlen; j++) {
            unsigned char a = (unsigned char)haystack[i + j];
            unsigned char b = (unsigned char)needle[j];
            if (tolower(a) != tolower(b))
                break;
        }
        if (j == nlen)
            return 1;
    }
    return 0;
}