Commit Diff
Commit:
7d6d378a8d77a583c9b79f8ade1900108b773788
Date:
Wed Aug 24 09:11:58 2022
UTC
Message:
initial import
--- /dev/null
+++ Makefile
@@ -0,0 +1,31 @@
+ENV = MBLAZE=.mblaze OUTDIR='${OUTDIR}'
+MBLAZE_PAGER = cat
+MDIR = ${HOME}/Maildir/op/GoT
+OUTDIR = www
+
+.PHONY: all dirs assets clean
+
+all: .mblaze dirs assets
+ mlist '${MDIR}' | mthread -r | \
+ ${ENV} mscan -f '%R %I %i %16D <%16f> %128S' | \
+ ${ENV} ./mexp | ${ENV} ./mkindex > ${OUTDIR}/index.html
+
+dirs:
+ mkdir -p ${OUTDIR}/thread/
+
+assets: dirs got.png style.css
+ cp got.png ${OUTDIR}/got@2x.png
+ convert got.png -resize 200x200 ${OUTDIR}/got.png
+ convert got.png -resize 128x128 ${OUTDIR}/got-tiny@2x.png
+ convert got.png -resize 64x64 ${OUTDIR}/got-tiny.png
+ cp style.css ${OUTDIR}
+
+${OUTDIR}:
+ mkdir -p '${OUTDIR}'
+
+.mblaze:
+ mkdir -p .mblaze
+ touch .mblaze/seq
+
+clean:
+ rm -rf ${OUTDIR}
--- /dev/null
+++ foot.html
@@ -0,0 +1,18 @@
+ <footer>
+ <p>Game Of Trees Mail Archive</p>
+ <nav>
+ <ul>
+ <li>
+ <a href='index.html'>Index</a>
+ </li>
+ <li>
+ <a href='https://lists.openbsd.org/cgi-bin/mj_wwwusr?user=&passw=&func=lists-long-full&extra=gameoftrees'>Subscribe to the Mailing List</a>
+ </li>
+ <li>
+ <a href='https://gameoftrees.org/'>Game of Trees</a>
+ </li>
+ </ul>
+ </nav>
+ </footer>
+ </body>
+</html>
Binary files /dev/null and got.png differ
--- /dev/null
+++ head.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <link rel="stylesheet" href="/style.css" />
+ <title>TITLE</title>
+ </head>
+ <body>
--- /dev/null
+++ mexp
@@ -0,0 +1,111 @@
+#!/usr/bin/env perl
+
+use open ":std", ":encoding(UTF-8)";
+use utf8;
+use strict;
+use warnings;
+use v5.32;
+
+my $outdir = $ENV{'OUTDIR'};
+die 'Set $OUTDIR' unless defined $outdir;
+mkdir $outdir;
+
+my $hdr = do {
+ local $/ = undef;
+ open my $fh, "<", "head.html"
+ or die "can't open head.html: $!";
+ <$fh>;
+};
+
+my $foot = do {
+ local $/ = undef;
+ open my $fh, "<", "foot.html"
+ or die "can't open foot.html: $!";
+ <$fh>;
+};
+
+my $logo = <<EOF;
+<img srcset='/got-tiny.png, /got-tiny@2x.png 2x'
+ src='/got-tiny.png'
+ alt='"GOT", but the "O" is a cute, smiling sun' />
+EOF
+
+sub san {
+ my $str = shift;
+ $str =~ s/&/\&/g;
+ $str =~ s/</\</g;
+ $str =~ s/>/\>/g;
+ return $str;
+}
+
+my $tid;
+while (<>) {
+ chomp;
+ say; # continue the pipeline
+
+ m/^([^ ]+) <([^>]+)> (.+)(\d{4}-\d{2}-\d{2} \d{2}:\d{2}) <([^>]+)> (.*)/;
+ die "can't parse: $_" unless defined $1;
+ my ($fname, $mid, $indent, $date, $from, $subj) = ($1, $2, $3, $4, $5, $6);
+ $subj = san($subj);
+ $subj =~ s/\s+/ /g;
+ $subj =~ s/\s+$//;
+
+ $mid =~ s,_,__,g;
+ $mid =~ s,/,_,g;
+
+ chomp($mid);
+
+ next if -f "$outdir/$mid.html";
+
+ my $level = length($indent) - 1;
+ $level = 10 if $indent =~ m/\.\.\d{2}\.\./;
+
+ $tid = $mid if $level == 0;
+
+ my $dest = "$outdir/$mid.html";
+ open(my $fh, '>', "$dest") or die "can't open $dest: $!";
+
+ say $fh $hdr =~ s/TITLE/$subj/r;
+
+ open(my $mshow, "-|", "mshow", "-nNA", "text/plain", $fname)
+ or die "can't exec mshow: $!";
+
+ print $fh "<header class='mail-header'>";
+ print $fh "<p>";
+ print $fh $logo;
+ print $fh "<a href='/'>‹ Back to the index</a>";
+ print $fh " or ";
+ print $fh "<a href='/thread/$tid.html#$mid'>→ go to the thread</a>.";
+ print $fh "</p>";
+ print $fh "<dl>";
+ while (<$mshow>) {
+ chomp;
+ last if /^$/;
+ my ($h, $v) = m/^([-A-Za-z]+): (.*)/;
+ die "bogus line? $fname : $_" unless (defined $h and defined $v);
+
+ # drop the (1 day ago) string
+ $v =~ s/\(.*\)//g if ($h eq "Date");
+
+ print $fh "<dt>", san($h), ":</dt>";
+ print $fh "<dd>", san($v), "</dd>";
+ }
+ print $fh "</dl></header>";
+
+ my $body = do {
+ local $/ = undef;
+ <$mshow>;
+ };
+
+ print $fh "<pre>";
+ # print $fh san($_) while <>;
+ print $fh san($body // "");
+ print $fh "</pre>";
+
+ print $fh $foot;
+
+ close($mshow);
+ close($fh);
+
+ # exit(0);
+}
--- /dev/null
+++ mkindex
@@ -0,0 +1,145 @@
+#!/usr/bin/env perl
+
+use open ":std", ":encoding(UTF-8)";
+use utf8;
+use strict;
+use warnings;
+use v5.32;
+
+my $outdir = $ENV{'OUTDIR'};
+die 'Set $OUTDIR' unless defined $outdir;
+mkdir $outdir;
+
+my $tfh;
+
+my $logo = <<EOF;
+<img srcset='/got-tiny.png, /got-tiny@2x.png 2x'
+ src='/got-tiny.png'
+ alt='"GOT", but the "O" is a cute, smiling sun' />
+EOF
+
+sub san {
+ my $str = shift;
+ $str =~ s/&/\&/g;
+ $str =~ s/</\</g;
+ $str =~ s/>/\>/g;
+ return $str;
+}
+
+sub initpage {
+ my ($fh, $title) = @_;
+ open(my $hdr, '<', 'head.html')
+ or die "can't open head.html: $!";
+ while (<$hdr>) {
+ s/TITLE/$title/;
+ print $fh $_;
+ }
+}
+
+sub endpage {
+ my $fh = shift;
+ open(my $foot, '<', 'foot.html')
+ or die "can't open foot.html: $!";
+ print $fh $_ while <$foot>;
+}
+
+sub nextthread {
+ endthread() if defined($tfh);
+ my ($mid, $subj) = @_;
+ my $dest = "$outdir/thread/$mid.html";
+ open($tfh, '>', $dest) or die "can't open $dest: $!";
+ initpage($tfh, $subj);
+
+ print $tfh "<header class='mail-header'>";
+ print $tfh "<p>";
+ print $tfh $logo;
+ print $tfh "<a href='/'>‹ Back to the index</a>";
+ print $tfh "</p>";
+ print $tfh "<dl><dt>Thread:</dt><dd>$subj</dd>";
+ print $tfh "</header>\n";
+ print $tfh "<div class='thread'>\n";
+}
+
+sub endthread {
+ print $tfh "</div>\n";
+ endpage($tfh);
+ close($tfh);
+}
+
+sub entry_raw {
+ my ($fh, $mid, $level, $date, $from, $subj) = @_;
+
+ my $class = "";
+ $class = "reply indent-$level" unless $level == 0;
+ print $fh "<div id='$mid' class='mail $class'>";
+ print $fh "<p class='mail-meta'>";
+ print $fh "<time>$date</time> ";
+ print $fh "<span class='from'>$from</span>";
+ print $fh "<span class='colon'>:</span>";
+ if ($fh != $tfh && $level == 0) {
+ print $fh " (<a href='/thread/$mid.html'>thread</a>)";
+ }
+ print $fh "</p>";
+ print $fh "<p class='subject'>";
+ print $fh "<a href='/$mid.html'>$subj</a>";
+ print $fh "</p>";
+ print $fh "</div>\n";
+}
+
+sub entry {
+ entry_raw(\*STDOUT, @_);
+ entry_raw($tfh, @_);
+}
+
+initpage(\*STDOUT, "Game of Trees Mail Archive");
+print <<EOF;
+<header class='index-header'>
+ <a href="https://gameoftrees.org" target="_blank">
+ <img src='/got.png'
+ srcset='/got.png, /got@2x.png 2x'
+ alt='"GOT" where the "O" is a cute smiling sun.' />
+ </a>
+ <h1>Game of Trees Mail Archive</h1>
+</header>
+<main>
+EOF
+
+my $threads_seen = 0;
+while (<>) {
+ chomp;
+ m/^[^ ]+ <([^>]+)> (.+)(\d{4}-\d{2}-\d{2} \d{2}:\d{2}) <([^>]+)> (.*)/;
+ die "woops; $_\n" unless defined $1;
+
+ my ($mid, $indent, $date, $from, $subj) = ($1, $2, $3, $4, $5);
+ $from =~ s/\s+$//;
+ $from = san($from);
+ $subj = san($subj);
+
+ my $level = length($indent) - 1;
+ $level = 10 if $indent =~ m/\.\.\d{2}\.\./;
+
+ $mid =~ s,_,__,g;
+ $mid =~ s,/,_,g;
+
+ $subj =~ s/\s+/ /g;
+ $subj =~ s/\s+$//g;
+
+ my $new_thread = $indent eq " ";
+ say "</section> <hr />" if $threads_seen && $new_thread;
+ if ($new_thread) {
+ nextthread($mid, $subj);
+ say "<section class='thread'>";
+ }
+
+ $threads_seen = 1;
+
+ entry($mid, $level, $date, $from, $subj);
+}
+
+if ($threads_seen) {
+ say "</section>" ;
+ endthread();
+}
+
+say "</main>";
+endpage(\*STDOUT);
--- /dev/null
+++ style.css
@@ -0,0 +1,133 @@
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font: 14px sans-serif;
+}
+
+header.mail-header {
+ background-color: #ced7e0;
+ padding: 5px 10px;
+ margin: 10px 0;
+}
+
+header.mail-header img {
+ vertical-align: middle;
+}
+
+header dl {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 0;
+}
+
+header dt {
+ width: 70px;
+}
+
+header dd {
+ margin-left: auto;
+ width: calc(100% - 70px);
+ flex-grow: 2;
+}
+
+header dt, header dd {
+ padding: 3px 0;
+}
+
+header.index-header {
+ margin: 20px 0;
+ text-align: center;
+}
+
+h1 {
+ font-weight: normal;
+ font-size: 1.8rem;
+ margin: 0 0 1rem 0;
+}
+
+main {
+ padding: 5px;
+}
+
+pre {
+ font-family: monospace;
+ padding: 5px;
+ white-space: pre-wrap;
+}
+
+hr {
+ display: none;
+}
+
+.thread {
+ margin: 2rem 0;
+}
+
+.thread p {
+ margin: 0;
+}
+
+.thread a {
+ text-decoration: none;
+}
+
+.thread a:hover {
+ text-decoration: underline;
+}
+
+.mail {
+ /* font-family: monospace; */
+ margin: 0.5rem 0;
+ /* display: flex; */
+ /* flex-direction: row; */
+}
+
+.mail:target {
+ background-color: khaki;
+}
+
+.mail .mail-meta {
+ font-size: 13px;
+}
+
+.mail.reply .subject {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: pre;
+}
+
+.mail.indent-1 { padding-left: 10px; }
+.mail.indent-2 { padding-left: 20px; }
+.mail.indent-3 { padding-left: 30px; }
+.mail.indent-4 { padding-left: 40px; }
+.mail.indent-5 { padding-left: 50px; }
+.mail.indent-6 { padding-left: 60px; }
+.mail.indent-7 { padding-left: 70px; }
+.mail.indent-8 { padding-left: 80px; }
+.mail.indent-9 { padding-left: 90px; }
+.mail.indent-10 { padding-left: 100px; }
+
+
+footer {
+ margin-top: 70px;
+ border-top: 1px solid black;
+}
+
+footer > p {
+ text-align: center;
+}
+
+footer ul {
+ padding: 0;
+ text-align: center;
+}
+
+footer li {
+ display: inline-block;
+ padding: 8px;
+}
Omar Polo