diff options
-rw-r--r-- | default.html | 45 | ||||
-rw-r--r-- | html.c | 263 |
2 files changed, 143 insertions, 165 deletions
diff --git a/default.html b/default.html index 36736a6..7300f7c 100644 --- a/default.html +++ b/default.html @@ -12,11 +12,9 @@ body { nav ul { padding: 0; list-style-type: none; - display: flex; - flex-wrap: wrap; } nav ul li { - margin-right: 1.5ch; + display: inline; } main.index ol { @@ -30,15 +28,12 @@ main.index h2 { font-size: 1em; margin: 0; } -main.index data.replies, main.index time { +main.index time, main.index data.replies { display: block; } main.index data.replies[value="0"] { display: none; } -main.index data.replies::before { - content: '+'; -} article.message header { background-color: gainsboro; @@ -55,41 +50,14 @@ article.message header nav ul { margin: 0; } -address { - display: inline; - font-style: inherit; -} -ul.recipient { +ul.address { margin: 0; padding: 0; list-style-type: none; - display: flex; - flex-wrap: wrap; -} -ul.recipient li:not(:last-child)::after { - content: ', '; - margin-right: 1ch; -} -ul.recipient li.group > address::after { - content: ': '; -} -ul.recipient li.group::after { - content: '; '; - margin-right: 1ch; -} -address.from::before { - content: 'From: '; -} -address.from { - margin-right: 1ch; -} -ul.to::before { - content: 'To: '; - margin-right: 1ch; + display: inline; } -ul.cc::before { - content: 'Cc: '; - margin-right: 1ch; +ul.address li { + display: inline; } pre { @@ -133,6 +101,7 @@ details.subthread summary { footer { margin: 1em 0; + font-size: x-small; text-align: center; color: gray; } diff --git a/html.c b/html.c index d55e5bf..4269871 100644 --- a/html.c +++ b/html.c @@ -28,6 +28,7 @@ #include <assert.h> #include <err.h> #include <regex.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -36,46 +37,64 @@ #include "archive.h" -static int htmlAddress(FILE *file, const char *class, struct Address addr) { +static char *htmlMailto(struct Address addr) { + struct Variable vars[] = { + { "mailbox", addr.mailbox }, + { "host", addr.host }, + {0}, + }; + return templateString("mailto:[mailbox]@[host]", vars, escapeURL); +} + +static int htmlAddress(FILE *file, struct Address addr, bool last) { + char *mailto = htmlMailto(addr); const char *template; if (addr.host) { - template = Q(<li><address class="[class]">[name]</address></li>); + template = Q( + <li><a href="[mailto]">[name]</a>[,]</li> + ); } else if (addr.mailbox) { - template = (const char *) { - Q(<li class="group">) - Q(<address class="[class]">[mailbox]</address>) - Q(<ul>) - }; + template = Q( + <li>[mailbox]: + <ul> + ); } else { - template = (const char *) { - Q(</ul>) - Q(</li>) - }; + template = Q( + </ul>; + </li> + ); } struct Variable vars[] = { - { "class", class }, + { "mailto", mailto }, { "name", addressName(addr) }, { "mailbox", addr.mailbox }, + { ",", (last ? "" : ", ") }, {0}, }; - return templateRender(file, template, vars, escapeXML); + int error = templateRender(file, template, vars, escapeXML); + free(mailto); + return error; } static int -htmlAddressList(FILE *file, const char *class, struct AddressList list) { +htmlAddressList(FILE *file, const char *name, struct AddressList list) { if (!list.len) return 0; - const char *template = Q(<ul class="recipient [class]">); + const char *template = Q( + <div class="[name]"> + [name]: + <ul class="address"> + ); struct Variable vars[] = { - { "class", class }, + { "name", name }, {0}, }; int error = templateRender(file, template, vars, escapeXML); if (error) return error; for (size_t i = 0; i < list.len; ++i) { - error = htmlAddress(file, class, list.addrs[i]); + error = htmlAddress(file, list.addrs[i], i == list.len - 1); if (error) return error; } - return templateRender(file, Q(</ul>), NULL, NULL); + return templateRender(file, Q(</ul></div>), NULL, NULL); } static char *htmlReply(const struct Envelope *envelope) { @@ -118,7 +137,7 @@ static char *htmlMbox(const char *messageID) { static int htmlNavItem(FILE *file, const char *name, const char *base, const char *url) { - const char *template = Q(<li><a href="[base][url]">[name]</a></li>); + const char *template = Q(<li><a href="[base][url]">[name]</a> </li>); struct Variable vars[] = { { "name", name }, { "base", base }, @@ -129,57 +148,51 @@ htmlNavItem(FILE *file, const char *name, const char *base, const char *url) { } int htmlMessageNav(FILE *file, const struct Envelope *envelope) { - int error = templateRender(file, Q(<nav><ul>), NULL, NULL); + char *mbox = htmlMbox(envelope->messageID); + int error = 0 + || templateRender(file, Q(<nav><ul>), NULL, NULL) + || htmlNavItem(file, "download", "", mbox); if (error) return error; + free(mbox); if (envelope->inReplyTo) { char *fragment = htmlFragment(envelope->inReplyTo); error = htmlNavItem(file, "parent", "", fragment); free(fragment); if (error) return error; } - char *mbox = htmlMbox(envelope->messageID); - error = htmlNavItem(file, "download", "", mbox); - free(mbox); - if (error) return error; return templateRender(file, Q(</ul></nav>), NULL, NULL); } -static const char *htmlUTC(time_t time) { - static char buf[sizeof("0000-00-00T00:00:00Z")]; - strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&time)); - return buf; -} - int htmlMessageOpen(FILE *file, const struct Envelope *envelope) { - const char *template = { - Q(<article class="message" id="[messageID]">) - Q(<header>) - Q(<h2 class="subject"><a href="[fragment]">[subject]</a></h2>) - Q(<address class="from">) - Q(<a href="[reply]">[from]</a>) - Q(</address>) - Q(<time datetime="[utc]">[date]</time>) - }; char *fragment = htmlFragment(envelope->messageID); char *reply = htmlReply(envelope); + const char *template = Q( + <article class="message" id="[messageID]"> + <header> + <h2 class="Subject"><a href="[fragment]">[subject]</a></h2> + <div class="From"> + From: <a href="[reply]">[from]</a> + <time datetime="[utc]">[date]</time> + </div> + ); struct Variable vars[] = { { "messageID", envelope->messageID }, { "fragment", fragment }, { "subject", envelope->subject }, { "reply", reply }, { "from", addressName(envelope->from) }, - { "utc", htmlUTC(envelope->time) }, + { "utc", iso8601(envelope->time).s }, { "date", envelope->date }, {0}, }; int error = 0 || templateRender(file, template, vars, escapeXML) - || htmlAddressList(file, "to", envelope->to) - || htmlAddressList(file, "cc", envelope->cc) + || htmlAddressList(file, "To", envelope->to) + || htmlAddressList(file, "Cc", envelope->cc) || htmlMessageNav(file, envelope) || templateRender(file, Q(</header>), NULL, NULL); - free(reply); free(fragment); + free(reply); return error; } @@ -350,12 +363,12 @@ int htmlAttachmentOpen(FILE *file) { int htmlAttachment( FILE *file, const struct BodyPart *part, const struct Variable *path ) { - const char *template = { - Q(<li><a href="[url]">[name][type][/][subtype]</a></li>) - }; char *url = templateString("../" PATH_ATTACHMENT, path, escapeURL); const char *name = paramGet(part->disposition.params, "filename"); if (!name) name = paramGet(part->params, "name"); + const char *template = Q( + <li><a href="[url]">[name][type][/][subtype]</a> </li> + ); struct Variable vars[] = { { "url", url }, { "name", (name ? name : "") }, @@ -387,20 +400,19 @@ static char *htmlThreadURL(const struct Envelope *envelope, const char *type) { } int htmlThreadHead(FILE *file, const struct Envelope *envelope) { - const char *template = { - Q(<!DOCTYPE html>) - Q(<meta charset="utf-8">) - Q(<meta name="generator" content="[generator]">) - Q(<title>[subject]</title>) - Q(<link rel="alternate" type="application/atom+xml" href="[atom]">) - Q(<link rel="alternate" type="application/mbox" href="[mbox]">) - }; char *atom = htmlThreadURL(envelope, "atom"); char *mbox = htmlThreadURL(envelope, "mbox"); + const char *template = Q( + <!DOCTYPE html> + <meta charset="utf-8"> + <meta name="generator" content="[generator]"> + <title>[subject]</title> + <link rel="alternate" type="application/atom+xml" href="[atom]"> + <link rel="alternate" type="application/mbox" href="[mbox]"> + ); struct Variable vars[] = { { "generator", GENERATOR_URL }, { "subject", envelope->subject }, - { "title", baseTitle }, { "atom", atom }, { "mbox", mbox }, {0}, @@ -412,21 +424,21 @@ int htmlThreadHead(FILE *file, const struct Envelope *envelope) { } int htmlThreadOpen(FILE *file, const struct Envelope *envelope) { - const char *template = { - Q(<header class="thread">) - Q(<h1>[subject]</h1>) - Q(<nav>) - Q(<ul>) - Q(<li><a href="../index.html">index</a></li>) - Q(<li><a href="[atom]">follow</a></li>) - Q(<li><a href="[mbox]">download</a></li>) - Q(</ul>) - Q(</nav>) - Q(</header>) - Q(<main class="thread">) - }; char *atom = htmlThreadURL(envelope, "atom"); char *mbox = htmlThreadURL(envelope, "mbox"); + const char *template = Q( + <header class="thread"> + <h1>[subject]</h1> + <nav> + <ul> + <li><a href="../index.html">index</a> </li> + <li><a href="[atom]">follow</a> </li> + <li><a href="[mbox]">download</a> </li> + </ul> + </nav> + </header> + <main class="thread"> + ); struct Variable vars[] = { { "subject", envelope->subject }, { "atom", atom }, @@ -439,8 +451,8 @@ int htmlThreadOpen(FILE *file, const struct Envelope *envelope) { return error; } -static size_t threadCount(struct List thread) { - size_t count = 0; +static uint32_t threadCount(struct List thread) { + uint32_t count = 0; for (size_t i = 0; i < thread.len; ++i) { if (thread.ptr[i].type == List) { count += threadCount(thread.ptr[i].list); @@ -451,41 +463,43 @@ static size_t threadCount(struct List thread) { return count; } -int htmlSubthreadOpen(FILE *file, struct List thread) { - const char *template = { - Q(<details class="subthread" open>) - Q(<summary>) - Q(<data class="replies" value="[replies]">[replies] repl[ies]</data>) - Q(</summary>) - }; - size_t count = threadCount(thread); - char replies[32]; - snprintf(replies, sizeof(replies), "%zu", count); +static int htmlReplies(FILE *file, uint32_t replies) { + const char *template = Q( + <data class="replies" value="[replies]">[replies] repl[ies]</data> + ); struct Variable vars[] = { - { "replies", replies }, - { "ies", (count > 1 ? "ies" : "y") }, + { "replies", u32(replies).s }, + { "ies", (replies != 1 ? "ies" : "y") }, {0}, }; return templateRender(file, template, vars, escapeXML); } +int htmlSubthreadOpen(FILE *file, struct List thread) { + const char *template = Q( + <details class="subthread" open> + <summary> + ); + return 0 + || templateRender(file, template, NULL, NULL) + || htmlReplies(file, threadCount(thread)) + || templateRender(file, Q(</summary>), NULL, NULL); +} + int htmlSubthreadClose(FILE *file) { return templateRender(file, Q(</details>), NULL, NULL); } static int htmlFooter(FILE *file) { - const char *template = { - Q(<footer>) - Q(<small>) - Q(<a href="[generator]">generated</a>) - " " - Q(<time datetime="[time]">[time]</time>) - Q(</small>) - Q(</footer>) - }; + const char *template = Q( + <footer> + <a href="[generator]">generated</a> + <time datetime="[time]">[time]</time> + </footer> + ); struct Variable vars[] = { { "generator", GENERATOR_URL }, - { "time", htmlUTC(time(NULL)) }, + { "time", iso8601(time(NULL)).s }, {0}, }; return templateRender(file, template, vars, escapeXML); @@ -498,13 +512,13 @@ int htmlThreadClose(FILE *file) { } int htmlIndexHead(FILE *file) { - const char *template = { - Q(<!DOCTYPE html>) - Q(<meta charset="utf-8">) - Q(<meta name="generator" content="[generator]">) - Q(<title>[title]</title>) - Q(<link rel="alternate" type="application/atom+xml" href="index.atom">) - }; + const char *template = Q( + <!DOCTYPE html> + <meta charset="utf-8"> + <meta name="generator" content="[generator]"> + <title>[title]</title> + <link rel="alternate" type="application/atom+xml" href="index.atom"> + ); struct Variable vars[] = { { "generator", GENERATOR_URL }, { "title", baseTitle }, @@ -528,23 +542,23 @@ static int htmlIndexNav(FILE *file) { } int htmlIndexOpen(FILE *file) { - const char *head = { - Q(<header class="index">) - Q(<h1>[title]</h1>) - }; + const char *head = Q( + <header class="index"> + <h1>[title]</h1> + ); + const char *tail = Q( + </header> + <main class="index"> + <ol> + ); struct Variable vars[] = { { "title", baseTitle }, {0}, }; - const char *tail = { - Q(</header>) - Q(<main class="index">) - Q(<ol>) - }; return 0 || templateRender(file, head, vars, escapeXML) || htmlIndexNav(file) - || templateRender(file, tail, NULL, NULL); + || templateRender(file, tail, vars, escapeXML); } static char *htmlIndexURL(const struct Envelope *envelope) { @@ -559,32 +573,27 @@ static char *htmlIndexURL(const struct Envelope *envelope) { int htmlIndexThread( FILE *file, const struct Envelope *envelope, struct List thread ) { - const char *template = { - Q(<li>) - Q(<h2 class="subject"><a href="[url]">[subject]</a></h2>) - Q(<address class="from">[from]</address>) - Q(<time datetime="[utc]">[date]</time>) - " " - Q(<data class="replies" value="[replies]">) - Q([replies] repl[ies]) - Q(</data>) - Q(</li>) - }; char *url = htmlIndexURL(envelope); - size_t count = threadCount(thread) - 1; - char replies[32]; - snprintf(replies, sizeof(replies), "%zu", count); + const char *template = Q( + <li> + <h2 class="Subject"><a href="[url]">[subject]</a></h2> + <div class="From"> + From: [from] + <time datetime="[utc]">[date]</time> + </div> + ); struct Variable vars[] = { { "url", url }, { "subject", envelope->subject }, { "from", addressName(envelope->from) }, - { "utc", htmlUTC(envelope->time) }, + { "utc", iso8601(envelope->time).s }, { "date", envelope->date }, - { "replies", replies }, - { "ies", (count == 1 ? "y" : "ies") }, {0}, }; - int error = templateRender(file, template, vars, escapeXML); + int error = 0 + || templateRender(file, template, vars, escapeXML) + || htmlReplies(file, threadCount(thread) - 1) + || templateRender(file, Q(</li>), NULL, NULL); free(url); return error; } |