~mika/kethel2

0abf87c2f3a4ad952050e1b4ed248728d21ea971 — mika a month ago 197aa6e
feature: aggregate
11 files changed, 200 insertions(+), 36 deletions(-)

M .gitignore
M Makefile
D contrib/mkthought
M include/stdhead.html
M include/stdnav.html
A kethel2/aggregate.c
R {mkpage => kethel2/mkpage}.c
D schemata.sql
A thoughts/data.tsv
A thoughts/index.html
A thoughts/item.html
M .gitignore => .gitignore +2 -2
@@ 2,6 2,6 @@
/mkpage.o
/out
/darkhttpd*
/tmp
/aggregate

/kethel.db
/stage.sql

M Makefile => Makefile +15 -16
@@ 3,18 3,9 @@ CFLAGS=-Wall -Wextra -Werror -pedantic -O2 -std=c99

DEVSERVER='https://raw.githubusercontent.com/emikulic/darkhttpd/refs/heads/master/darkhttpd.c'

.PHONY: all serve initdb commitdb
.PHONY: all serve 

all: mkpage out/index.html out/colophon.html

initdb:
	@echo "In the sql prompt, use '.read schemata.sql'"
	sqlite3 kethel.db

commitdb:
		@echo "In the sql prompt, use '.read stage.sql'"
		sqlite3 kethel.db
		rm stage.sql
all: mkpage aggregate out/index.html out/colophon.html out/thoughts.html

serve: all darkhttpd
	./darkhttpd out


@@ 23,16 14,24 @@ darkhttpd:
	curl $(DEVSERVER) > darkhttpd.c
	$(CC) -O2 darkhttpd.c -o darkhttpd

mkpage: mkpage.c
	$(CC) $(CFLAGS) -c mkpage.c -o mkpage.o
	$(CC) $(CFLAGS) mkpage.o -o mkpage
mkpage: kethel2/mkpage.c
	mkdir -p ./tmp
	$(CC) $(CFLAGS) -c kethel2/mkpage.c -o tmp/mkpage.o
	$(CC) $(CFLAGS) tmp/mkpage.o -o mkpage

# list of all pages

aggregate: kethel2/aggregate.c
	$(CC) $(CFLAGS) -c kethel2/aggregate.c -o tmp/aggregate.o
	$(CC) $(CFLAGS) tmp/aggregate.o -o aggregate

# list of all pages
out/index.html: index.html
	./mkpage index.html out/index.html

out/colophon.html: colophon.html
	./mkpage colophon.html out/colophon.html

out/thoughts/index.html: kethel.db
out/thoughts.html: thoughts/data.tsv thoughts/item.html thoughts/index.html
	mkdir -p ./tmp
	./aggregate thoughts/data.tsv thoughts/item.html thoughts/index.html tmp/thoughts.o.html
	./mkpage tmp/thoughts.o.html out/thoughts.html

D contrib/mkthought => contrib/mkthought +0 -8
@@ 1,8 0,0 @@
#!/usr/bin/env bash

echo -n "Title: "
TITLE=$(cat)
echo -n "Body: "
BODY=$(cat)

echo "INSERT INTO thoughts VALUES('$TITLE', '$BODY', '$(date "+%Y-%m-%d %H:%M:%S")');" >> ./stage.sql

M include/stdhead.html => include/stdhead.html +8 -1
@@ 1,3 1,10 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style> p { width: max(50vw, 60rem); } </style>
<style>
p { width: max(50vw, 60rem); }
blockquote { margin: 1em 0; }
figcaption {
  font-style: italic;
  margin-left: 1em;
}
</style>

M include/stdnav.html => include/stdnav.html +1 -1
@@ 4,7 4,7 @@
    <nav style="display: inline">
        <a href="/">root</a> |
        <a href="/blog/index.html">macroblog</a> |
        <a href="/thoughts/index.html">microblog</a> |
        <a href="/thoughts.html">microblog</a> |
        <a href="/review/index.html">media</a> |
    </nav>
</header>

A kethel2/aggregate.c => kethel2/aggregate.c +136 -0
@@ 0,0 1,136 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* read_to_string(const char* path) {
	FILE* f = fopen(path, "rb");
	if (f == NULL) {
		fprintf(stderr, "Failed to open %s in `rb` mode", path);
		return NULL;
	}
	fseek(f, 0, SEEK_END);
	unsigned long fileSize = ftell(f);
	char* data = malloc(fileSize);
	fseek(f, 0, SEEK_SET);
	if (fread(data, fileSize, 1, f) <= 0) {
		free(data);
		fclose(f);
		return NULL;
	}
	fclose(f);
	return data;
}

int strfind(const char* haystack, char needle) {
	int i = 1;
	while ((haystack[i]) != needle) {
		if (haystack[i] == '\0') return 0;
		i++;
	}
	return i;
}

void write_item_data(char* restrict row, const char* restrict template, FILE* out) {
	char* tok = strtok(row, "\t");
	// TODO: generalise this
	const char* pref = "thought-";
	char* label = malloc(strlen(pref) + strlen(tok) + 2);
	sprintf(label, "%s%s", pref, tok);
	char* data[8];
	data[0] = label;
	int i = 1;
	tok = strtok(NULL, "\t");
	while (tok != NULL) {
		data[i] = malloc(strlen(tok) + 1);
		strcpy(data[i], tok);
		tok = strtok(NULL, "\t");
		i++;
	}
	int dataItems = i;
	int lineSize = strfind(template, '\n');
	int bufSize = lineSize + 1;
	char* templateLine = malloc(bufSize);
	strncpy(templateLine, template, lineSize);
	int nextLine = lineSize;

	const char magic[] = "<!-- ANCHOR";
	if (strncmp(templateLine, magic, sizeof(magic) - 1) != 0) {
		fprintf(stderr, "ERR: Invalid template provided!\n");
		fprintf(stderr, "<template>:%i: %s\n", 1, templateLine);
		return;
	}
	char* tagName = (templateLine + sizeof(magic));
	tagName[strfind(tagName, ' ')] = '\0';
	fprintf(out, "<%s id=\"%s\">\n", tagName, data[0]);
	fflush(out);
	int needlePos = strfind(template + nextLine, '\n');
	while (needlePos != 0) {
		if (bufSize <= needlePos) {
			templateLine = realloc(templateLine, needlePos + 1);
		}
		strncpy(templateLine, template+nextLine+1, needlePos);
		templateLine[needlePos] = '\0';
		if (strncmp(templateLine, "<!-- PUT", strlen("<!-- PUT") - 1) == 0) {
			// double digits are soo last year
			int offset = strlen("<!-- PUT ");
			unsigned char idx = templateLine[offset] - 0x30;
			fputs(data[idx], out);
		} else {
			fputs(templateLine, out);
		}
		nextLine += needlePos;
		needlePos = strfind(template + nextLine, '\n');
	}

	free(templateLine);
	for (int i = 0; i < dataItems; i++) {
		free(data[i]);
	}

	return;
}

/// aggregate - aggregates csv files into index.html, based on
///   1. the first csv element DESC. i.e. id #1 is the first, id #7 the latest
///   2. a schema, usually a <p> tag with replacers for csv items with idx > 0
int main(int argc, char* argv[]) {
	if (argc <= 4) {
		fputs("usage: aggregate <data.csv> <item.html> <index.html> <output.html>\n", stderr);
		return -1;
	}

	FILE* data = fopen(argv[1], "r");
	char* itemTemplate = read_to_string(argv[2]);
	FILE* index = fopen(argv[3], "r");
	FILE* output = fopen(argv[4], "w");
	char buf[512];
	while (fgets(buf, sizeof(buf), index) != NULL) {
		if (strncmp(buf, "<!-- PUT", 7) == 0) {
			break;
		} else {
			fputs(buf, output);
		}
	}

	if (fgets(buf, sizeof(buf), data) == NULL) {
		fprintf(stderr, "could not read tsv header, bailing");
		fclose(data);
		free(itemTemplate);
		return -1;
	}

	while (fgets(buf, sizeof(buf), data) != NULL) {
		write_item_data(buf, itemTemplate, output);
	}

	while (fgets(buf, sizeof(buf), index) != NULL) {
		fputs(buf, output);
	}

	fclose(data);
	fclose(index);
	fclose(output);
	free(itemTemplate);

	return 0;
}

R mkpage.c => kethel2/mkpage.c +11 -3
@@ 7,17 7,18 @@ int main(int argc, char* argv[]) {
    if (argc <= 2) {
        fprintf(stderr, "Usage: ./mkpage <input> <output>\n");
    }
    FILE* input  = fopen(argv[1], "r");
    FILE* input  = fopen(argv[1], "rb");
    if (input == NULL) {
        fprintf(stderr, "mkpage: fopen: %s does not exist!\n", argv[1]);
        return -1;
    }
    FILE* output = fopen(argv[2], "w");
    if (output == NULL) {
        fprintf(stderr, "mkpage: fopen: could not open %s for writing\n", argv[1]);
        fprintf(stderr, "mkpage: fopen: could not open %s for writing\n", argv[2]);
        fclose(input);
        return -1;
    }

    uintptr_t lineno = 0;
    char buf[256]; // ! NOTE: lines longer than 255 chars do not process correctly



@@ 49,7 50,14 @@ int main(int argc, char* argv[]) {
                long file_size = ftell(load);
                fseek(load, 0, SEEK_SET);
                char* readBuf = malloc(file_size);
                fread(readBuf, file_size, 1, load);
                size_t bytesRead = fread(readBuf, file_size, 1, load);
		if (bytesRead <= 0) {
			fputs("Failed to read from LOADed file\n", stderr);
			fclose(load);
			fclose(input);
			fclose(output);
			return -1;
		}
                fputs(readBuf, output);
                free(readBuf);
                fclose(load);

D schemata.sql => schemata.sql +0 -5
@@ 1,5 0,0 @@
CREATE TABLE Thoughts (
    title VARCHAR(64),
    body TEXT,
    date TEXT
);

A thoughts/data.tsv => thoughts/data.tsv +2 -0
@@ 0,0 1,2 @@
rowid	BODY	AUTHOR	DATE
0	why are lecture halls always at an insane angle, while if you're at a concert (or other theatre show for that matter) the seats barely climb up ,, big lecture hall please contact me,	mika	2026-09-14 17:40:58

A thoughts/index.html => thoughts/index.html +15 -0
@@ 0,0 1,15 @@
<!DOCTYPE html>
<html lang="en">
    <head>
<!--LOAD include/stdhead.html -->
        <title>microblog - m1kadev.nl</title>
    </head>

    <body>
<!--LOAD include/stdnav.html -->
        <main>
<!-- PUT -->
        </main>
<!--LOAD include/stdfooter.html -->
    </body>
</html>

A thoughts/item.html => thoughts/item.html +10 -0
@@ 0,0 1,10 @@
<!-- ANCHOR figure -->
<blockquote>
<!-- PUT 1 -->
</blockquote>
<figcaption>
<!-- PUT 2 -->
, at
<!-- PUT 3 -->
</figcaption>
</figure>