git-trace

romele / git-trace public

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

src/gitx.c

#include "gitx.h"

#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

#define CAP_MAX (16 * 1024 * 1024)

int git_ref_ok(const char *s)
{
    size_t n = strlen(s);
    if (n == 0 || n > 255)
        return 0;
    if (s[0] == '-' || s[0] == '/' || s[0] == '.')
        return 0;
    if (strstr(s, ".."))
        return 0;
    for (size_t i = 0; i < n; i++) {
        unsigned char c = (unsigned char)s[i];
        if (!(isalnum(c) || c == '.' || c == '_' || c == '-' || c == '/'))
            return 0;
    }
    return 1;
}

int git_path_ok(const char *s)
{
    size_t n = strlen(s);
    if (n == 0)
        return 1;                  /* empty = tree root */
    if (n > 1024 || s[0] == '-' || s[0] == '/')
        return 0;
    if (strstr(s, ".."))
        return 0;
    for (size_t i = 0; i < n; i++) {
        unsigned char c = (unsigned char)s[i];
        if (!(isalnum(c) || c == '.' || c == '_' || c == '-' || c == '/' ||
              c == ' ' || c == '+' || c == '(' || c == ')' || c == '@'))
            return 0;
    }
    return 1;
}

int git_pat_ok(const char *s)
{
    size_t n = strlen(s);
    if (n == 0 || n > 255)
        return 0;
    if (s[0] == '-' || s[0] == '/' || s[0] == '.')
        return 0;
    if (strstr(s, ".."))
        return 0;
    for (size_t i = 0; i < n; i++) {
        unsigned char c = (unsigned char)s[i];
        if (!(isalnum(c) || c == '.' || c == '_' || c == '-' ||
              c == '/' || c == '*' || c == '?'))
            return 0;
    }
    return 1;
}

static int capture_impl(const char *repo_dir, const char *const envs[],
                        const char *const argv[], struct buf *out)
{
    int pfd[2];
    if (pipe(pfd) < 0)
        return -1;

    pid_t pid = fork();
    if (pid < 0) {
        close(pfd[0]);
        close(pfd[1]);
        return -1;
    }
    if (pid == 0) {
        dup2(pfd[1], 1);
        close(pfd[0]);
        close(pfd[1]);
        int devnull = open("/dev/null", 1);
        if (devnull >= 0)
            dup2(devnull, 2);
        if (chdir(repo_dir) != 0)
            _exit(127);
        if (envs)
            for (int i = 0; envs[i]; i++)
                putenv((char *)envs[i]);
        execvp("git", (char *const *)argv);
        _exit(127);
    }

    close(pfd[1]);
    int overflow = 0;
    char tmp[16384];
    for (;;) {
        ssize_t n = read(pfd[0], tmp, sizeof tmp);
        if (n <= 0)
            break;
        if (out->len + (size_t)n > CAP_MAX) {
            overflow = 1;
            kill(pid, SIGKILL);
            break;
        }
        buf_put(out, tmp, (size_t)n);
    }
    close(pfd[0]);

    int st = 0;
    waitpid(pid, &st, 0);
    if (overflow)
        return -2;
    return WIFEXITED(st) ? WEXITSTATUS(st) : -1;
}

int git_capture(const char *repo_dir, const char *const argv[],
                struct buf *out)
{
    return capture_impl(repo_dir, NULL, argv, out);
}

int git_capture_env(const char *repo_dir, const char *const envs[],
                    const char *const argv[], struct buf *out)
{
    return capture_impl(repo_dir, envs, argv, out);
}

char *git_default_branch(const char *repo_dir)
{
    struct buf b;
    buf_init(&b);
    const char *argv[] = { "git", "symbolic-ref", "--short", "-q",
                           "HEAD", NULL };
    if (git_capture(repo_dir, argv, &b) != 0 || b.len == 0) {
        buf_free(&b);
        return NULL;
    }
    char *nl = strchr(b.data, '\n');
    if (nl)
        *nl = '\0';
    return b.data;
}