skip to content
==> src/source_hash.c <==
1/* The file list (find), its order (LC_ALL=C sort), and the hash of "path\0content" each. */
2#include "mesh/source_hash.h"
3#include "sha256_stream.h"
4
5#include <dirent.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/stat.h>
10
11typedef struct {
12    char **v;
13    size_t n, cap;
14} list;
15
16static void add(list *l, const char *s) {
17    if (l->n == l->cap) {
18        l->cap = l->cap ? 2 * l->cap : 64;
19        char **nv = realloc(l->v, l->cap * sizeof *nv);
20        if (!nv) abort();
21        l->v = nv;
22    }
23    size_t k = strlen(s) + 1;
24    l->v[l->n] = malloc(k);
25    if (!l->v[l->n]) abort();
26    memcpy(l->v[l->n++], s, k);
27}
28
29static bool source_name(const char *n) {
30    size_t k = strlen(n);
31    return (k > 2 && (strcmp(n + k - 2, ".c") == 0 || strcmp(n + k - 2, ".h") == 0)) || strcmp(n, "sexc.json") == 0 ||
32           strcmp(n, "program.toml") == 0;
33}
34
35/* rel is relative to root; recurse into folders except .sexc. */
36static void walk(const char *root, const char *rel, list *out) {
37    char path[4096];
38    snprintf(path, sizeof path, "%s/%s", root, rel);
39    DIR *d = opendir(path);
40    if (!d) return;
41    struct dirent *e;
42    while ((e = readdir(d))) {
43        if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0 || strcmp(e->d_name, ".sexc") == 0) continue;
44        char r[4096], full[8192];
45        snprintf(r, sizeof r, "%s/%s", rel, e->d_name);
46        snprintf(full, sizeof full, "%s/%s", root, r);
47        struct stat st;
48        if (lstat(full, &st) != 0) continue;
49        if (S_ISDIR(st.st_mode)) walk(root, r, out);
50        else if (S_ISREG(st.st_mode) && source_name(e->d_name)) add(out, r);
51    }
52    closedir(d);
53}
54
55static int by_bytes(const void *a, const void *b) { return strcmp(*(char *const *)a, *(char *const *)b); }
56
57bool mesh_source_hash(const char *root, const char *const *dirs, size_t ndirs, char out[17]) {
58    list files = {0};
59    for (size_t i = 0; i < ndirs; i++) walk(root, dirs[i], &files);
60    if (files.n) qsort(files.v, files.n, sizeof *files.v, by_bytes);
61    sha256_stream s;
62    sha256_stream_init(&s);
63    bool ok = true;
64    for (size_t i = 0; i < files.n && ok; i++) {
65        sha256_stream_update(&s, files.v[i], strlen(files.v[i]) + 1);
66        char full[8192];
67        snprintf(full, sizeof full, "%s/%s", root, files.v[i]);
68        FILE *f = fopen(full, "rb");
69        if (!f) { ok = false; break; }
70        char buf[65536];
71        size_t n;
72        while ((n = fread(buf, 1, sizeof buf, f)) > 0) sha256_stream_update(&s, buf, n);
73        ok = !ferror(f);
74        fclose(f);
75    }
76    for (size_t i = 0; i < files.n; i++) free(files.v[i]);
77    free(files.v);
78    uint8_t d[32];
79    char hex[65];
80    sha256_stream_final(&s, d);
81    sha256_stream_hex(d, hex);
82    memcpy(out, hex, 16);
83    out[16] = 0;
84    return ok;
85}