aboutsummaryrefslogtreecommitdiff
path: root/mktext.c
diff options
context:
space:
mode:
Diffstat (limited to 'mktext.c')
-rw-r--r--mktext.c116
1 files changed, 116 insertions, 0 deletions
diff --git a/mktext.c b/mktext.c
new file mode 100644
index 0000000..55af074
--- /dev/null
+++ b/mktext.c
@@ -0,0 +1,116 @@
+/* mktext -- resolve a lectionary's verse keys against a scripture corpus and
+ * write the LZSS-packed text clectio compiles in. This is what makes clectio
+ * bring-your-own-Bible: point it at any corpus formatted like ours (a 6-column
+ * TSV: Book, Abbrev, BookNum, Chapter, Verse, Text) and it produces the text for
+ * exactly the verses the calendar cites -- nothing more. The corpus must use the
+ * same (Vulgate) chapter/verse numbering as the keys.
+ *
+ * usage: mktext verses.keys corpus.tsv out.lz
+ * out.lz = [uint32 LE rawlen][LZSS-packed "chap:verse text\n" lines].
+ */
+#define LZ_PACK
+#include "lz.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+ char *key; /* "Book\tChap\tVerse" */
+ char *cv; /* "Chap:Verse" */
+ char *text; /* verse text */
+} Rec;
+
+static Rec *recs;
+static size_t nrec;
+
+static int cmp(const void *a, const void *b) {
+ return strcmp(((const Rec *)a)->key, ((const Rec *)b)->key);
+}
+
+static char *slurp(const char *path, size_t *len) {
+ FILE *f = fopen(path, "rb");
+ if (!f) { fprintf(stderr, "mktext: cannot open %s\n", path); exit(1); }
+ fseek(f, 0, SEEK_END);
+ long n = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ char *buf = malloc((size_t)n + 1);
+ if (fread(buf, 1, (size_t)n, f) != (size_t)n) { fprintf(stderr, "mktext: read error\n"); exit(1); }
+ buf[n] = 0;
+ fclose(f);
+ *len = (size_t)n;
+ return buf;
+}
+
+/* split a TSV line into fields in place; return field count (max 6). */
+static int fields(char *line, char *f[6]) {
+ int n = 0;
+ f[n++] = line;
+ for (char *p = line; *p && n < 6; p++)
+ if (*p == '\t') { *p = 0; f[n++] = p + 1; }
+ return n;
+}
+
+int main(int argc, char **argv) {
+ if (argc != 4) {
+ fprintf(stderr, "usage: mktext verses.keys corpus.tsv out.lz\n");
+ return 2;
+ }
+ size_t klen, tlen;
+ char *keys = slurp(argv[1], &klen);
+ char *tsv = slurp(argv[2], &tlen);
+
+ /* index the corpus by (Book\tChap\tVerse) */
+ size_t cap = 4096;
+ recs = malloc(cap * sizeof(Rec));
+ for (char *line = strtok(tsv, "\n"); line; line = strtok(NULL, "\n")) {
+ char *fl[6];
+ if (fields(line, fl) < 6)
+ continue;
+ if (nrec == cap) { cap *= 2; recs = realloc(recs, cap * sizeof(Rec)); }
+ size_t kl = strlen(fl[0]) + strlen(fl[3]) + strlen(fl[4]) + 3;
+ char *key = malloc(kl);
+ snprintf(key, kl, "%s\t%s\t%s", fl[0], fl[3], fl[4]);
+ size_t cl = strlen(fl[3]) + strlen(fl[4]) + 2;
+ char *cv = malloc(cl);
+ snprintf(cv, cl, "%s:%s", fl[3], fl[4]);
+ recs[nrec].key = key;
+ recs[nrec].cv = cv;
+ recs[nrec].text = fl[5];
+ nrec++;
+ }
+ qsort(recs, nrec, sizeof(Rec), cmp);
+
+ /* resolve each key line to "chap:verse text\n" */
+ size_t obuf = 1 << 20, olen = 0;
+ char *out = malloc(obuf);
+ size_t missing = 0, nkeys = 0;
+ for (char *line = strtok(keys, "\n"); line; line = strtok(NULL, "\n")) {
+ nkeys++;
+ Rec probe;
+ probe.key = line;
+ Rec *r = bsearch(&probe, recs, nrec, sizeof(Rec), cmp);
+ const char *cv = "0:0", *text = "";
+ if (r) { cv = r->cv; text = r->text; }
+ else { missing++; /* keep a placeholder so indices stay aligned */ }
+ size_t need = strlen(cv) + strlen(text) + 3;
+ while (olen + need > obuf) { obuf *= 2; out = realloc(out, obuf); }
+ olen += (size_t)snprintf(out + olen, need, "%s %s", cv, text);
+ out[olen++] = '\n';
+ }
+ if (missing)
+ fprintf(stderr, "mktext: warning: %zu of %zu verses not in corpus (blank)\n", missing, nkeys);
+
+ size_t plen;
+ unsigned char *packed = lz_pack((unsigned char *)out, olen, &plen);
+
+ FILE *of = fopen(argv[3], "wb");
+ if (!of) { fprintf(stderr, "mktext: cannot write %s\n", argv[3]); return 1; }
+ unsigned char hdr[4] = { (unsigned char)olen, (unsigned char)(olen >> 8),
+ (unsigned char)(olen >> 16), (unsigned char)(olen >> 24) };
+ fwrite(hdr, 1, 4, of);
+ fwrite(packed, 1, plen, of);
+ fclose(of);
+ fprintf(stderr, "mktext: %zu verses, %zu -> %zu bytes (%.1fx)\n",
+ nkeys, olen, plen, olen ? (double)olen / (double)plen : 0);
+ return 0;
+}