git-trace

romele / git-trace public

62 commits · 1 branch · 1 tag · 1 contributor (top: Romele) · first commit 3d ago · last push 11m ago

src/util.c

#include "util.h"

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

void die(const char *msg)
{
    perror(msg);
    exit(1);
}

void *xmalloc(size_t n)
{
    void *p = malloc(n ? n : 1);
    if (!p)
        die("malloc");
    return p;
}

void *xrealloc(void *p, size_t n)
{
    p = realloc(p, n ? n : 1);
    if (!p)
        die("realloc");
    return p;
}

char *xstrdup(const char *s)
{
    size_t n = strlen(s) + 1;
    char *p = xmalloc(n);
    memcpy(p, s, n);
    return p;
}

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

int ends_with(const char *s, const char *suffix)
{
    size_t a = strlen(s), b = strlen(suffix);
    return a >= b && strcmp(s + a - b, suffix) == 0;
}

static int hexv(int c)
{
    if (c >= '0' && c <= '9') return c - '0';
    if (c >= 'a' && c <= 'f') return c - 'a' + 10;
    if (c >= 'A' && c <= 'F') return c - 'A' + 10;
    return -1;
}

char *url_decode(const char *s, size_t len)
{
    char *out = xmalloc(len + 1);
    size_t w = 0;
    for (size_t i = 0; i < len; i++) {
        if (s[i] == '%' && i + 2 < len && hexv(s[i+1]) >= 0 && hexv(s[i+2]) >= 0) {
            out[w++] = (char)(hexv(s[i+1]) * 16 + hexv(s[i+2]));
            i += 2;
        } else if (s[i] == '+') {
            out[w++] = ' ';
        } else {
            out[w++] = s[i];
        }
    }
    out[w] = '\0';
    return out;
}

char *html_escape(const char *s, size_t len)
{
    struct buf b;
    buf_init(&b);
    for (size_t i = 0; i < len; i++) {
        switch (s[i]) {
        case '<':  buf_puts(&b, "&lt;");   break;
        case '>':  buf_puts(&b, "&gt;");   break;
        case '&':  buf_puts(&b, "&amp;");  break;
        case '"':  buf_puts(&b, "&quot;"); break;
        default:   buf_put(&b, s + i, 1);
        }
    }
    if (!b.data)
        return xstrdup("");
    return b.data; /* buf_put already NUL-terminates */
}

void buf_init(struct buf *b)
{
    b->data = NULL;
    b->len = b->cap = 0;
}

static void buf_reserve(struct buf *b, size_t extra)
{
    if (b->len + extra + 1 <= b->cap)
        return;
    size_t nc = b->cap ? b->cap * 2 : 256;
    while (nc < b->len + extra + 1)
        nc *= 2;
    b->data = xrealloc(b->data, nc);
    b->cap = nc;
}

void buf_put(struct buf *b, const void *p, size_t n)
{
    buf_reserve(b, n);
    memcpy(b->data + b->len, p, n);
    b->len += n;
    b->data[b->len] = '\0';
}

void buf_puts(struct buf *b, const char *s)
{
    buf_put(b, s, strlen(s));
}

void buf_printf(struct buf *b, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    va_list ap2;
    va_copy(ap2, ap);
    int n = vsnprintf(NULL, 0, fmt, ap);
    va_end(ap);
    if (n < 0) { va_end(ap2); return; }
    buf_reserve(b, (size_t)n);
    vsnprintf(b->data + b->len, (size_t)n + 1, fmt, ap2);
    va_end(ap2);
    b->len += (size_t)n;
}

void buf_put_urlenc(struct buf *b, const char *s, int keep_slash)
{
    for (; *s; s++) {
        unsigned char c = (unsigned char)*s;
        if (isalnum(c) || c == '.' || c == '_' || c == '-' || c == '~' ||
            (keep_slash && c == '/'))
            buf_put(b, &c, 1);
        else
            buf_printf(b, "%%%02X", c);
    }
}

void buf_put_escaped(struct buf *b, const char *s, size_t n)
{
    char *e = html_escape(s, n);
    buf_puts(b, e);
    free(e);
}

void buf_free(struct buf *b)
{
    free(b->data);
    b->data = NULL;
    b->len = b->cap = 0;
}

const char *status_text(int code)
{
    switch (code) {
    case 200: return "OK";
    case 301: return "Moved Permanently";
    case 302: return "Found";
    case 400: return "Bad Request";
    case 401: return "Unauthorized";
    case 403: return "Forbidden";
    case 404: return "Not Found";
    case 405: return "Method Not Allowed";
    case 411: return "Length Required";
    case 413: return "Payload Too Large";
    case 500: return "Internal Server Error";
    case 502: return "Bad Gateway";
    default:  return "Status";
    }
}

/* recursive mkdir -p: each missing intermediate component is created */
int mkdir_p(const char *path)
{
    char tmp[PATH_MAX];
    size_t n = strlen(path);
    if (n == 0 || n >= sizeof tmp)
        return -1;
    memcpy(tmp, path, n + 1);
    for (char *p = tmp + 1; ; p++) {
        if (*p == '/' || *p == '\0') {
            char save = *p;
            *p = '\0';
            if (mkdir(tmp, 0755) != 0 && errno != EEXIST) {
                *p = save;
                return -1;
            }
            *p = save;
            if (save == '\0')
                return 0;
        }
    }
}

int gt_glob(const char *pat, const char *s)
{
    /* single-backtrack '*' matching — O(len(pat) * len(s)) worst
     * case; a recursive matcher is exponential on hostile patterns
     * like "*a*a*a*a*b". */
    const char *star_p = NULL, *star_s = NULL;
    while (*s) {
        if (*pat == '*') {
            star_p = ++pat;
            star_s = s;
        } else if (*pat == '?' || *pat == *s) {
            pat++;
            s++;
        } else if (star_p) {
            pat = star_p;
            s = ++star_s;
        } else {
            return 0;
        }
    }
    while (*pat == '*')
        pat++;
    return !*pat;
}

void gt_spawn_task_arg(const char *subcmd, const char *arg)
{
    pid_t p = fork();
    if (p != 0) {                           /* parent / caller */
        int st;
        if (p > 0)
            waitpid(p, &st, 0);             /* reap intermediate */
        return;
    }
    setsid();                               /* intermediate child */
    if (fork() != 0)
        _exit(0);                           /* grandchild -> init */
    char self[PATH_MAX];
    ssize_t l = readlink("/proc/self/exe", self, sizeof self - 1);
    if (l > 0) {
        self[l] = '\0';
        if (arg)
            execl(self, self, subcmd, arg, (char *)NULL);
        else
            execl(self, self, subcmd, (char *)NULL);
    }
    _exit(127);
}

void gt_spawn_task(const char *subcmd)
{
    gt_spawn_task_arg(subcmd, NULL);
}