==> 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}