diff options
-rw-r--r-- | archive.h | 6 | ||||
-rw-r--r-- | concat.c | 55 | ||||
-rw-r--r-- | html.c | 94 |
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); +} |