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, "<"); break; case '>': buf_puts(&b, ">"); break; case '&': buf_puts(&b, "&"); break; case '"': buf_puts(&b, """); 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); }