about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--archive.h6
-rw-r--r--concat.c55
-rw-r--r--html.c94
3 files changed, 150 insertions, 5 deletions
diff --git a/archive.h b/archive.h
index 65f8a84..136723b 100644
--- a/archive.h
+++ b/archive.h
@@ -209,3 +209,9 @@ int htmlThreadOpen(FILE *file, const struct Envelope *envelope);
 int htmlSubthreadOpen(FILE *file, struct List thread);
 int htmlSubthreadClose(FILE *file);
 int htmlThreadClose(FILE *file);
+int htmlIndexHead(FILE *file);
+int htmlIndexOpen(FILE *file);
+int htmlIndexThread(
+	FILE *file, const struct Envelope *envelope, struct List thread
+);
+int htmlIndexClose(FILE *file);
diff --git a/concat.c b/concat.c
index e75c857..8e165d2 100644
--- a/concat.c
+++ b/concat.c
@@ -213,5 +213,60 @@ void concatThreads(struct List threads, const struct Envelope *envelopes) {
 	}
 }
 
+struct Sort {
+	size_t index;
+	time_t updated;
+	time_t created;
+};
+
+static int compar(const void *_a, const void *_b) {
+	const struct Sort *a = _a;
+	const struct Sort *b = _b;
+	if (a->updated == b->updated) {
+		return (a->created > b->created) - (a->created < b->created);
+	} else {
+		return (a->updated > b->updated) - (a->updated < b->updated);
+	}
+}
+
 void concatIndex(struct List threads, const struct Envelope *envelopes) {
+	struct Sort *order = calloc(threads.len, sizeof(*order));
+	if (!order) err(EX_OSERR, "calloc");
+
+	for (size_t i = 0; i < threads.len; ++i) {
+		struct stat status;
+		const char *path = threadPath(envelopes[i].messageID, "html");
+		int error = stat(path, &status);
+		if (error) err(EX_DATAERR, "%s", path);
+
+		order[i].index = i;
+		order[i].created = envelopes[i].time;
+		order[i].updated = status.st_mtime;
+	}
+	qsort(order, threads.len, sizeof(*order), compar);
+
+	const char *path = "index.html";
+	FILE *file = fopen(path, "w");
+	if (!file) err(EX_CANTCREAT, "%s", path);
+
+	int error = htmlIndexHead(file);
+	if (error) err(EX_IOERR, "%s", path);
+
+	if (concatHead) {
+		error = concatFile(file, concatHead);
+		if (error) err(EX_IOERR, "%s", path);
+	}
+
+	error = htmlIndexOpen(file);
+	if (error) err(EX_IOERR, "%s", path);
+
+	for (size_t i = threads.len - 1; i < threads.len; --i) {
+		const struct Envelope *envelope = &envelopes[order[i].index];
+		struct List thread = dataCheck(threads.ptr[order[i].index], List).list;
+		error = htmlIndexThread(file, envelope, thread);
+		if (error) err(EX_IOERR, "%s", path);
+	}
+
+	error = htmlIndexClose(file) || fclose(file);
+	if (error) err(EX_IOERR, "%s", path);
 }
diff --git a/html.c b/html.c
index 0d1ae11..8f349e5 100644
--- a/html.c
+++ b/html.c
@@ -393,12 +393,9 @@ int htmlSubthreadClose(FILE *file) {
 	return templateRender(file, TEMPLATE(</details>), NULL, NULL);
 }
 
-int htmlThreadClose(FILE *file) {
+static int htmlFooter(FILE *file) {
 	const char *template = TEMPLATE(
-		</main>
-		<footer>
-			generated <time datetime="[time]">[time]</time>
-		</footer>
+		<footer>generated <time datetime="[time]">[time]</time></footer>
 	);
 	time_t now = time(NULL);
 	char time[sizeof("0000-00-00T00:00:00Z")];
@@ -409,3 +406,90 @@ int htmlThreadClose(FILE *file) {
 	};
 	return templateRender(file, template, vars, escapeXML);
 }
+
+int htmlThreadClose(FILE *file) {
+	return 0
+		|| templateRender(file, TEMPLATE(</main>), NULL, NULL)
+		|| htmlFooter(file);
+}
+
+int htmlIndexHead(FILE *file) {
+	const char *template = TEMPLATE(
+		<!DOCTYPE html>
+		<meta charset="utf-8">
+		<title>[title]</title>
+		<link rel="alternate" type="application/atom+xml" href="index.atom">
+	);
+	struct Variable vars[] = {
+		{ "title", htmlTitle },
+		{0},
+	};
+	return templateRender(file, template, vars, escapeXML);
+}
+
+int htmlIndexOpen(FILE *file) {
+	const char *template = TEMPLATE(
+		<header class="index">
+			<h1>[title]</h1>
+			<nav>
+				<ul>
+					<li><a href="index.atom">follow</a></li>
+				</ul>
+			</nav>
+		</header>
+		<main class="index">
+			<ol>
+	);
+	struct Variable vars[] = {
+		{ "title", htmlTitle },
+		{0},
+	};
+	return templateRender(file, template, vars, escapeXML);
+}
+
+static char *htmlIndexURL(const struct Envelope *envelope) {
+	struct Variable vars[] = {
+		{ "messageID", envelope->messageID },
+		{ "type", "html" },
+		{0},
+	};
+	return templateURL(PATH_THREAD, vars);
+}
+
+int htmlIndexThread(
+	FILE *file, const struct Envelope *envelope, struct List thread
+) {
+	const char *template = TEMPLATE(
+		<li>
+			<h2 class="subject"><a href="[url]">[subject]</a></h2>
+			<address class="from">[from]</address>
+			<time datetime="[utc]">[date]</time>
+			<data class="replies" value="[replies]">[replies] repl[ies]</data>
+		</li>
+	);
+	char *url = htmlIndexURL(envelope);
+	char utc[sizeof("0000-00-00T00:00:00Z")];
+	strftime(utc, sizeof(utc), "%FT%TZ", gmtime(&envelope->time));
+	size_t count = threadCount(thread) - 1;
+	char replies[32];
+	snprintf(replies, sizeof(replies), "%zu", count);
+	struct Variable vars[] = {
+		{ "url", url },
+		{ "subject", envelope->subject },
+		{ "from", addressName(envelope->from) },
+		{ "utc", utc },
+		{ "date", envelope->date },
+		{ "replies", replies },
+		{ "ies", (count == 1 ? "y" : "ies") },
+		{0},
+	};
+	int error = templateRender(file, template, vars, escapeXML);
+	free(url);
+	return error;
+}
+
+int htmlIndexClose(FILE *file) {
+	return 0
+		|| templateRender(file, TEMPLATE(</ol></main>), NULL, NULL)
+		|| htmlFooter(file);
+}