Commit Diff


commit - 964686ce0b77d06b8c954366db56e9a8d098b1d5
commit + ac42bb6c7f9f30028fbc63f623bf4bba99b725ad
blob - f1521270a295bd429b4e962e82efeea04b0b269d
blob + 7431a6328c8f7d85d30e234b2d9bf0a5d8b1b36d
--- site/Makefile
+++ site/Makefile
@@ -1,19 +1,70 @@
-.PHONY: all serve-www serve-gemini upload
+MANPAGES =	../gmid.1 \
+		../gg.1
 
-all: gmid.1.html gmid.1.txt
+PAGES =		index.gmi \
+		changelog.gmi \
+		contrib.gmi \
+		quickstart.gmi
 
-gmid.1.html: ../gmid.1
-	./mdoc2html.sh $? $@
+TITLE_index.gmi =	home
+TITLE_changelog.gmi =	changelog
+TITLE_contrib.gmi =	contrib
+TITLE_quickstart.gmi =	quickstart
 
-gmid.1.txt: ../gmid.1
-	MANWIDTH=72 man -Tutf8 -l $? | col -b > $@
+REPOLOGY_BANNER =	https://repology.org/badge/vertical-allrepos/gmid.svg
+REPOLOGY_URL =		https://repology.org/project/gmid/versions
 
+SUBST =		./subst GITHUB=https://github.com/omar-polo/gmid \
+			VERS=1.7.5 \
+			CURV=1.7 \
+			NEXTV=1.8
+
+SUBST_GEM =	${SUBST} MANEXT=txt  EXT=gmi  REPOLOGY=${REPOLOGY_URL}
+SUBST_WWW =	${SUBST} MANEXT=html EXT=html REPOLOGY=${REPOLOGY_BANNER}
+
+.PHONY: all dirs manpages serve-www serve-gemini upload clean titles
+
+all: dirs manpages pages
+	cp style.css www/
+	cp vim-screenshot.png www/
+	cp vim-screenshot.png gemini/
+
+dirs:
+	mkdir -p gemini www
+
+manpages:
+.for m in ${MANPAGES}
+	./mdoc2html.sh $m www/${m:T}.html
+	man -O width=65 -Tutf8 -l $m | col -b > gemini/${m:T}.txt
+.endfor
+
+pages:
+.for p in ${PAGES}
+	${MAKE} titles-gem | ./menu.pl $p gemini > gemini/$p
+	${SUBST_GEM} $p >> gemini/$p
+
+	${SUBST_WWW} TITLE=${TITLE_${p}:Q} header.html > www/${p:.gmi=.html}
+	${MAKE} titles-www | ./menu.pl "${p:.gmi=.html}" html >> www/${p:.gmi=.html}
+	${SUBST_WWW} $p | ./gem2html >> www/${p:.gmi=.html}
+	cat footer.html >> www/${p:.gmi=.html}
+.endfor
+
 serve-www:
-	python3 -m http.server 8888
+	python3 -m http.server --directory www 8888
 
 serve-gemini:
-	../gmid -p 1966 .
+	gmid -p 1966 ./gemini
 
 upload:
-	rsync --delete -a . op:sites/gmid.omarpolo.com
-	rsync --delete -a . op:gemini/gmid.omarpolo.com
+	rsync --delete -a www/ op:sites/gmid.omarpolo.com
+	rsync --delete -a gemini/ op:gemini/gmid.omarpolo.com
+
+titles-gem:
+.for p in ${PAGES}
+	@printf "%s %s\n" "${p}" ${TITLE_${p}:Q}
+.endfor
+
+titles-www:
+.for p in ${PAGES}
+	@printf "%s %s\n" "${p:.gmi=.html}" ${TITLE_${p}:Q}
+.endfor
blob - b505e2ab00a63ed0957a8915891166cdeccd02b2 (mode 644)
blob + /dev/null
--- site/contrib.html
+++ /dev/null
@@ -1,232 +0,0 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <title>gmid | contrib</title>
-    <meta charset="utf8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1" />
-    <style>
-    body {
-      font-family: monospace;
-      font-size: 14px;
-      max-width: 780px;
-      margin: 0 auto;
-      padding: 20px;
-      padding-bottom: 80px;
-    }
-
-    h1::before {
-      content: "# ";
-    }
-
-    h2 {
-      margin-top: 40px;
-    }
-
-    h2::before {
-      content: "## ";
-    }
-
-    h3::before {
-      content: "### ";
-    }
-
-    blockquote {
-      margin: 0;
-      padding: 0;
-    }
-
-    blockquote::before {
-      content: "> ";
-    }
-
-    blockquote p {
-      font-style: italic;
-      display: inline;
-    }
-
-    p.link::before {
-      content: "→ ";
-    }
-
-    strong::before { content: "*" }
-    strong::after  { content: "*" }
-
-    hr {
-      border: 0;
-      height: 1px;
-      background-color: #222;
-      width: 100%;
-      display: block;
-      margin: 2em auto;
-    }
-
-    img {
-      border-radius: 5px;
-    }
-
-    pre {
-      overflow: auto;
-      padding: 1rem;
-      background-color: #f0f0f0;
-      border-radius: 3px;
-    }
-
-    pre.banner {
-      display: flex;
-      flex-direction: row;
-      justify-content: center;
-    }
-
-    code, kbd {
-      color: #9d109d;
-    }
-
-    img {
-      display: block;
-      margin: 0 auto;
-      max-width: 100%;
-    }
-
-    @media (prefers-color-scheme: dark) {
-      body {
-        background-color: #222;
-        color: white;
-      }
-
-      a {
-        color: aqua;
-      }
-
-      hr {
-        background-color: #ddd;
-      }
-
-      pre {
-        background-color: #353535;
-      }
-
-      code, kbd {
-        color: #ff4cff;
-      }
-    }
-
-    @media (max-width: 400px) {
-      pre.banner { font-size: 9px; }
-    }
-
-    @media (max-width: 500px) {
-      pre.banner { font-size: 10px; }
-    }
-    </style>
-  </head>
-  <body>
-    <header>
-      <nav>
-        <a href="index.html">Home</a> |
-        contrib |
-        <a href="quickstart.html">Quickstart</a> |
-        <a href="gmid.1.html">docs</a>
-      </nav>
-    </header>
-    <h1>contrib</h1>
-    <p>
-      This directory is for additional contributed files which may be
-      useful.
-    </p>
-    <p>Contents:</p>
-    <ul>
-      <li><a href="#dockerfile">Dockerfile</a></li>
-      <li><a href="#gencert">gencert</a></li>
-      <li><a href="#openbsd-rc">OpenBSD rc file</a></li>
-      <li><a href="#systemd-unit-file">Systemd unit file</a></li>
-      <li><a href="#renew-certs">renew certificates automatically</a></li>
-      <li><a href="#vim-syntax-files">Vim syntax files</a></li>
-    </ul>
-    <hr />
-    <h2 id="dockerfile">Dockerfile</h2>
-    <p>
-      <a href="https://git.omarpolo.com/gmid/tree/contrib/Dockerfile">contrib/Dockerfile</a>
-      is a simple Dockerfile.  The resulting image is a classic alpine
-      linux image with a statically linked gmid installed as
-      <code>/bin/gmid</code>.
-    </p>
-    <p>To build the image:</p>
-    <pre># docker build -f contrib/Dockerfile -t gmid .</pre>
-    <p>and then run it with something along the lines of:</p>
-    <pre># docker run --rm -it -p 1965:1965      \
-        -v gmid.conf:/etc/gmid.conf:ro  \
-        -v path/to/keys:/tls:ro         \
-        -v /var/gemini:/var/gemini:ro   \
-        gmid -c /etc/gmid.conf</pre>
-    <h2 id="gencert">gencert</h2>
-    <p>
-      <a href="https://git.omarpolo.com/gmid/tree/contrib/gencert">contrib/gencert</a>
-      is a simple script to generate self-signed certificates.
-    </p>
-    <h2 id="openbsd-rc">OpenBSD rc file</h2>
-    <p>
-      <a href="https://git.omarpolo.com/gmid/tree/contrib/gmid">contrib/gmid</a>
-      is a sample service file for OpenBSD <code>rc(8)</code>.
-      To install it:
-    </p>
-    <pre># cp contrib/gmid /etc/rc.d</pre>
-    <p>
-      then the usual
-      <code>rcctl [start|stop|enable|restart] gmid</code>
-      are available.
-    </p>
-    <h2 id="systemd-unit-file">Systemd unit file</h2>
-    <p>
-      <a href="https://git.omarpolo.com/gmid/tree/contrib/gmid.service">contrib/gmid.service</a>
-      is a simple service file for
-      systemd.  To install it:
-    </p>
-    <pre># cp contrib/gmid.service /lib/systemd/system/gmid.service</pre>
-    <p>
-      then the usual
-      <code>systemctl [status|start|enable|stop] gmid</code>
-      commands can be used to manage the server.
-    </p>
-    <p>Some things to keep in mind:</p>
-    <ul>
-      <li>
-        the <code>ExecStart</code> path may depend on the installation.
-      </li>
-      <li>
-        a <code>gmid</code> user needs to be created for e.g. with:
-        <pre># useradd --system --no-create-home -s /bin/nologin -c "gmid Gemini server" gmid</pre>
-      </li>
-      <li>
-        logs can be inspected with <code>journalctl(1)</code>:
-        <pre># journalctl -t gmid</pre>
-      </li>
-    </ul>
-    <h2 id="renew-certs">renew certificates automatically</h2>
-    <p>
-      <strong>NB:</strong> this script requires features that are
-      currently available only in the master branch.
-    </p>
-    <p>
-      <a href="https://git.omarpolo.com/gmid/tree/contrib/renew-certs">contrib/renew-certs</a>
-      is a script meant to be run in a crontab that watch for
-      certificate expiration.  It can optionally renew the certs and
-      restart gmid too.
-    </p>
-    <p>Read the documentation with: <code>perldoc renew-certs</code>.</p>
-    <h2 id="vim-syntax-files">Vim syntax files</h2>
-    <p>
-      <a href="https://git.omarpolo.com/gmid/tree/contrib/vim">contrib/vim</a>
-      contains a syntax highlighting for vim.  To install it, just
-      copy the files to <code>~/.vim</code>
-      or <code>/usr/share/vim/vimfiles</code>, e.g.
-    </p>
-    <pre>$ mkdir -p ~/.vim
-$ cp -R contrib/vim/* ~/.vim/</pre>
-    <p>To enable Syntastic checker, add to your vimrc:</p>
-    <pre>let g:syntastic_gmid_checkers = ['gmid']</pre>
-    <p>The end result is something like this:</p>
-    <a href="vim-screenshot.png">
-      <img src="vim-screenshot.png" alt="Screenshot of vim editing gmid.conf with syntax highlighting" />
-    </a>
-  </body>
-</html>
blob - /dev/null
blob + 2ab5c0d1fc7b0e6c12fb00bf177037f64450298c (mode 644)
--- /dev/null
+++ site/footer.html
@@ -0,0 +1,2 @@
+    </body>
+</html>
blob - /dev/null
blob + b9f03d2884972722553225322c70853137453486 (mode 755)
--- /dev/null
+++ site/gem2html
@@ -0,0 +1,114 @@
+#!/usr/bin/env perl
+#
+# Copyright (c) 2022 Omar Polo <op@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use v5.10;
+use strict;
+use warnings;
+
+my $in_pre = 0;
+my $in_list = 0;
+
+while (<>) {
+	chomp;
+	if ($in_pre && m/^```/) {
+		$in_pre = 0;
+		say "</pre>";
+	} elsif (!$in_pre && m/^```/) {
+		if ($in_list) {
+			$in_list = 0;
+			say "</ul>";
+		}
+		$in_pre = 1;
+		print "<pre>";
+	} elsif ($in_pre) {
+		say san($_);
+	} elsif ($in_list && m/^$/) {
+		say "</ul>";
+		$in_list = 0;
+	} elsif (m/^\*\s+(.*)/) { # NB: at least one space
+		if (!$in_list) {
+			$in_list = "item";
+			say "<ul>";
+		} elsif ($in_list eq "link") {
+			$in_list = "item";
+			say "</ul>";
+			say "<ul>";
+		}
+		output("li", $1);
+	} elsif (m/^=>\s*([^\s]*)\s*(.*)$/) {
+		my $href = $1;
+		my $alt = $2;
+
+		# special case: images
+		if ($1 =~ /\.(png|jpg|svg)$/) {
+			if ($in_list) {
+				say "</ul>";
+				$in_list = 0;
+			}
+			say "<img src='$href' alt='$alt' />";
+			next;
+		}
+
+		if (!$in_list) {
+			$in_list = "link";
+			say "<ul class='link-list'>";
+		} elsif ($in_list eq "item") {
+			$in_list = "link";
+			say "</ul>";
+			say "<ul class='link-list'>";
+		}
+		$_ = $alt || $href;
+		say "<li><a href='$href'>". san() ."</a></li>";
+	} elsif (m/^###\s*(.*)$/) {
+		output("h3", $1);
+	} elsif (m/^##\s*(.*)$/) {
+		output("h2", $1);
+	} elsif (m/^#\s*(.*)$/) {
+		output("h1", $1);
+	} elsif (m/^>\s*(.*)$/) {
+		output("blockquote", $1);
+	} else {
+		output("p", $_);
+	}
+}
+
+sub san {
+	s/&/\&amp;/g;
+	s/</\&lt;/g;
+	s/>/\&gt;/g;
+	return $_;
+}
+
+sub output {
+	my ($tn, $content) = @_;
+
+	if (!$in_list && $tn eq "li") {
+		$in_list = 1;
+		say "<ul>";
+	}
+
+	if ($in_list && $tn ne "li") {
+		$in_list = 0;
+		say "</ul>";
+	}
+
+	if ($tn eq "p" && $content eq "") {
+		return;
+	}
+
+	$_ = $content;
+	say "<$tn>". san() ."</$tn>";
+}
blob - b216a5e07a4ab30d183cc145fb704566514f9f5b (mode 644)
blob + /dev/null
--- site/index.html
+++ /dev/null
@@ -1,377 +0,0 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <title>gmid</title>
-    <meta charset="utf8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <style>
-    body {
-      font-family: monospace;
-      font-size: 14px;
-      max-width: 780px;
-      margin: 0 auto;
-      padding: 20px;
-      padding-bottom: 80px;
-    }
-
-    h1::before {
-      content: "# ";
-    }
-
-    h2 {
-      margin-top: 40px;
-    }
-
-    h2::before {
-      content: "## ";
-    }
-
-    h3::before {
-      content: "### ";
-    }
-
-    blockquote {
-      margin: 0;
-      padding: 0;
-    }
-
-    blockquote::before {
-      content: "> ";
-    }
-
-    blockquote p {
-      font-style: italic;
-      display: inline;
-    }
-
-    p.link::before {
-      content: "→ ";
-    }
-
-    strong::before { content: "*" }
-    strong::after  { content: "*" }
-
-    hr {
-      border: 0;
-      height: 1px;
-      background-color: #222;
-      width: 100%;
-      display: block;
-      margin: 2em auto;
-    }
-
-    img {
-      border-radius: 5px;
-    }
-
-    pre {
-      overflow: auto;
-      padding: 1rem;
-      background-color: #f0f0f0;
-      border-radius: 3px;
-    }
-
-    pre.banner {
-      display: flex;
-      flex-direction: row;
-      justify-content: center;
-    }
-
-    code, kbd {
-      color: #9d109d;
-    }
-
-    img {
-      display: block;
-      margin: 0 auto;
-      max-width: 100%;
-    }
-
-    @media (prefers-color-scheme: dark) {
-      body {
-        background-color: #222;
-        color: white;
-      }
-
-      a {
-        color: aqua;
-      }
-
-      hr {
-        background-color: #ddd;
-      }
-
-      pre {
-        background-color: #353535;
-      }
-
-      code, kbd {
-        color: #ff4cff;
-      }
-    }
-
-    @media (max-width: 400px) {
-      pre.banner { font-size: 9px; }
-    }
-
-    @media (max-width: 500px) {
-      pre.banner { font-size: 10px; }
-    }
-    </style>
-  </head>
-  <body>
-    <header>
-      <nav>
-        Home |
-        <a href="contrib.html">contrib</a> |
-        <a href="quickstart.html">Quickstart</a> |
-        <a href="gmid.1.html">docs</a>
-      </nav>
-    </header>
-    <h1>gmid</h1>
-    <blockquote>
-      <p>A Gemini server</p>
-    </blockquote>
-    <h2>Features</h2>
-    <ul>
-      <li>sandboxed by default on OpenBSD, FreeBSD and Linux</li>
-      <li>able to reload the configuration on-the-fly without loosing connections</li>
-      <li>punycode and IRI support</li>
-      <li>reverse proxying</li>
-      <li>CGI and FastCGI support</li>
-      <li>virtual hosts and per-location rules</li>
-      <li>low memory footprint</li>
-      <li>event-based asynchronous I/O model</li>
-      <li>rich configuration file</li>
-    </ul>
-    <h2>Install</h2>
-    <p>Some distros provide a package — thanks to the maintainers!</p>
-    <p>
-      <a href="https://repology.org/project/gmid/versions">
-        <img src="https://repology.org/badge/vertical-allrepos/gmid.svg" alt="Packaging status">
-      </a>
-    </p>
-    <p>Source code and precompiled binaries for linux are available:</p>
-    <ul>
-      <li>
-        <a href="https://github.com/omar-polo/gmid/releases/download/1.7.5/gmid-1.7.5.tar.gz">gmid-1.7.5.tar.gz</a>
-      </li>
-    </ul>
-    <ul>
-      <li>
-        <a href="https://git.omarpolo.com/gmid/">git repository</a>
-      </li>
-      <li>
-        <a href="gemini://git.omarpolo.com/gmid.git/">git repository via Gemini</a>
-      </li>
-      <li>
-        <a href="https://github.com/omar-polo/gmid">GitHub mirror</a>
-      </li>
-    </ul>
-    <ul>
-      <li>
-        <a href="https://github.com/omar-polo/gmid/releases/download/1.7.5/gmid.linux.aarch64">gmid.linux.aarch64</a>
-      </li>
-      <li>
-        <a href="https://github.com/omar-polo/gmid/releases/download/1.7.5/gmid.linux.amd64">gmid.linux.amd64</a>
-      </li>
-    </ul>
-    <p>
-      When in doubt, compile from source: it’s easy and takes less
-      than a minute on a raspberry pi 3.  The dependencies are:
-    </p>
-    <ul>
-      <li>libevent</li>
-      <li>OpenSSL/LibreSSL</li>
-      <li>libtls (from either LibreSSL or LibreTLS)</li>
-      <li>yacc or GNU bison</li>
-    </ul>
-    <p>Once all the dependencies are installed, building is as easy as:</p>
-    <pre>$ curl -LO https://github.com/omar-polo/gmid/releases/download/1.7.5/gmid-1.7.5.tar.gz
-$ tar xzvf gmid-1.7.5.tar.gz
-$ cd gmid-1.7.5
-$ ./configure
-$ make
-$ sudo make install # eventually</pre>
-    <p>
-      A SHA256 file is available.  However, that only checks for
-      accidental corruption: you can use signify (SHA256.sig and the
-      public key gmid-1.7.pub) or GPG.  The hash of the signify public
-      key is also included in the SHA256 file and thus signed with my
-      GPG.  The signify public key for the next release ‘gmid-1.8.pub’
-      is also included.
-    </p>
-    <ul>
-      <li>
-        <a href="https://github.com/omar-polo/gmid/releases/download/1.7.5/SHA256">SHA256</a>
-      </li>
-      <li>
-        <a href="https://github.com/omar-polo/gmid/releases/download/1.7.5/SHA256.gpg">SHA256.gpg</a>
-      </li>
-      <li>
-        <a href="https://github.com/omar-polo/gmid/releases/download/1.7.5/SHA256.sig">SHA256.sig</a>
-      </li>
-    </ul>
-    <p>To verify the signatures with signify(1)</p>
-    <pre>$ signify -C -p gmid-1.7.pub -x SHA256.sig
-Signature Verified
-gmid-1.7.pub: OK
-gmid-1.7.5.tar.gz: OK
-gmid-1.8.pub: OK
-gmid.linux.aarch64: OK
-gmid.linux.amd64: OK</pre>
-    <h2>Changelog for the last versions</h2>
-    <p>1.7.5 “Space-dye Vest” fifth bugfix release — Released October 15, 2021</p>
-    <p>This version includes the following bugfix:</p>
-    <ul>
-      <li>don't die when a connection is closed before being accepted by gmid (i.e. handle <code>ECONNRESET</code>)</li>
-    </ul>
-    <hr />
-    <p>1.7.4 “Space-dye Vest”  fourth bugfix release — Released September 24, 2021</p>
-    <p>This version includes the following bugfix:</p>
-    <ul>
-      <li>
-        fix a possible out-of-bound access when handling a request for
-        a non-existent file in the root directory of a vhost that's
-        matched by the cgi option
-      </li>
-    </ul>
-    <p>and the relative regression test.</p>
-    <hr />
-    <p>1.7.3 “Space-dye Vest” third bugfix release — Released September 18, 2021</p>
-    <h3>Improvements</h3>
-    <ul>
-      <li>follows symlinks</li>
-      <li>improved documentation and added key generation example (thanks! Anna)</li>
-    </ul>
-    <h3>Bugfix</h3>
-    <ul>
-      <li>fix syslog logging on FreeBSD.  Reported by Karl Jeacle, thanks!</li>
-      <li>don't crash if <code>-c</code> is missing in configtest mode (<code>-n</code>).  Reported by heph, thanks!</li>
-      <li>allow fstat64 on linux (needed by glibc on aarch64).  Reported by pine, thanks!</li>
-    </ul>
-    <hr />
-    <p>1.7.2 “Space-dye Vest” second bugfix release — Released July 19, 2021</p>
-    <p>This version includes the following bugfix:</p>
-    <ul>
-      <li>
-        An un-initialized field in the configless code path leads to a
-        crash on the first request.
-      </li>
-    </ul>
-    <p>and the relative regression test.</p>
-    <hr />
-    <p>1.7.1 “Space-dye Vest” bugfix release — Released July 11, 2021</p>
-    <p>This version includes two bugfixes:</p>
-    <ul>
-      <li>use <code>${MAKE}</code> to recursively call make.</li>
-      <li>
-        fix the misleading example in the manpage: macros name may not
-        be reserved words.
-      </li>
-    </ul>
-    <hr />
-    <p>1.7 “Space-dye Vest” — Released July 10, 2021</p>
-    <p>
-      Starting from this version gmid doesn't depend on lex anymore,
-      but yacc is still needed.
-    </p>
-    <h3>New Features</h3>
-    <ul>
-      <li>initial fastcgi support!  (it&#8217;s still young!)</li>
-      <li>
-        added user-defined macros, either via <code>-Dname=val</code> or
-        directly in the configuration file.
-      </li>
-      <li>new <code>include</code> keyword to load additional configuration files.</li>
-      <li>new <code>env</code> rule to define environment vars for CGI scripts.</li>
-      <li>new <code>alias</code> rule to define hostname aliases for a server.</li>
-      <li>allow <code>root</code> to be specified per-location block.</li>
-      <li>pidfile support with the new <code>-P</code> cli flag.</li>
-      <li>
-        define <code>TLS_VERSION</code>, <code>TLS_CIPHER</code> and
-        <code>TLS_CIPHER_STRENGTH</code> for CGI scripts.
-      </li>
-    </ul>
-    <h3>Improvements</h3>
-    <ul>
-      <li>
-        remove limits on the number of virtual hosts and location
-        blocks that can be defined.
-      </li>
-      <li>print the datetime when logging to stderr.</li>
-      <li>
-        use <code>text&#47;x-patch</code> for <code>.patch</code> and <code>.diff</code> files.
-      </li>
-      <li>sort the auto index alphabetically.</li>
-      <li>various improvements to the log management.</li>
-      <li>drop the dependency on lex.</li>
-      <li>
-        added <code>--help</code> as synonym of <code>-h</code> and
-        <code>-V</code>&#47;<code>--version</code> to print the version.
-      </li>
-      <li>
-        c-like handling of strings in the configuration file: when
-        two or more strings are next to each-others, are
-        automatically joined into a single string.  This is
-        particularly useful with <code>$</code>-macros.
-      </li>
-    </ul>
-    <h3>Bug fixes</h3>
-    <ul>
-      <li>
-        correctly handle CGI scripts that replies with the maxium
-        header length allowed.
-      </li>
-      <li>fixed the <code>static</code> target.</li>
-      <li>
-        fixed recursive mkdirs for configless mode (i.e. create
-        <code>~&#47;.local&#47;share&#47;gmid</code>)
-      </li>
-      <li>
-        logs sent to syslog now have proper priority (before every
-        message ended up as LOG_CRIT).  Found by Anna
-        &#8220;CyberTailor&#8221;, thanks!
-      </li>
-      <li>
-        ensure <code>%p</code> (path) is always absolute in
-        <code>block return</code> rules.
-      </li>
-      <li>
-        fix automatic certificate generation, it caused problems on
-        some adroid devices.  Found by Gnuserland, thanks!
-      </li>
-      <li>document the <code>log</code> rule.</li>
-      <li>
-        the seccomp filter was reworked and now it&#8217;s known to
-        work properly on a vast range of architectures (to be more
-        specific: all the architectures supported by alpine linux),
-        see github issue #4.  Prompted and tested by @begss, thanks!
-      </li>
-      <li>
-        various improvements to the configure script, notified and
-        fixed by Anna &#8220;CyberTailor&#8221;, thanks!
-      </li>
-      <li>added a timeout to the regression tests.</li>
-    </ul>
-    <h3>Breaking changes</h3>
-    <ul>
-      <li>
-        if duplicate rules are found in the configuration file, an
-        error is now raised instead of silently using only the last
-        value.
-      </li>
-      <li>
-        (sort of) <code>gg</code> moved to <code>regress</code> as
-        it's only used in the regression suite.
-      </li>
-      <li>
-        (notice) the <code>mime "mime-type" "extension"</code> rule was deprecated and
-        replaced by the new <code>map "mime-type" to-ext "extension"</code>.  The
-        <code>mime</code> rule will be removed in a future version
-        because its syntax is incompatible with the new string
-        auto-concat mechanism.
-      </li>
-    </ul>
-  </body>
-</html>
blob - /dev/null
blob + 6b63898cfe346113a3c9361b179ddeccdfba557b (mode 644)
--- /dev/null
+++ site/header.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <title>TITLE | gmid</title>
+        <meta charset="utf8" />
+        <meta name="viewport" content="width=device-width, initial-scale=1" />
+        <link rel="stylesheet" href="style.css" />
+    </head>
+    <body>
blob - 739e414e1ee54b7ad842ea9f13cb3e936dc3675c (mode 644)
blob + /dev/null
--- site/quickstart.html
+++ /dev/null
@@ -1,291 +0,0 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <title>gmid quickstart</title>
-    <meta charset="utf8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1" />
-    <style>
-    body {
-      font-family: monospace;
-      font-size: 14px;
-      max-width: 780px;
-      margin: 0 auto;
-      padding: 20px;
-      padding-bottom: 80px;
-    }
-
-    h1::before {
-      content: "# ";
-    }
-
-    h2 {
-      margin-top: 40px;
-    }
-
-    h2::before {
-      content: "## ";
-    }
-
-    h3::before {
-      content: "### ";
-    }
-
-    blockquote {
-      margin: 0;
-      padding: 0;
-    }
-
-    blockquote::before {
-      content: "> ";
-    }
-
-    blockquote p {
-      font-style: italic;
-      display: inline;
-    }
-
-    p.link::before {
-      content: "→ ";
-    }
-
-    strong::before { content: "*" }
-    strong::after  { content: "*" }
-
-    hr {
-      border: 0;
-      height: 1px;
-      background-color: #222;
-      width: 100%;
-      display: block;
-      margin: 2em auto;
-    }
-
-    img {
-      border-radius: 5px;
-    }
-
-    pre {
-      overflow: auto;
-      padding: 1rem;
-      background-color: #f0f0f0;
-      border-radius: 3px;
-    }
-
-    pre.banner {
-      display: flex;
-      flex-direction: row;
-      justify-content: center;
-    }
-
-    code, kbd {
-      color: #9d109d;
-    }
-
-    img {
-      display: block;
-      margin: 0 auto;
-      max-width: 100%;
-    }
-
-    @media (prefers-color-scheme: dark) {
-      body {
-        background-color: #222;
-        color: white;
-      }
-
-      a {
-        color: aqua;
-      }
-
-      hr {
-        background-color: #ddd;
-      }
-
-      pre {
-        background-color: #353535;
-      }
-
-      code, kbd {
-        color: #ff4cff;
-      }
-    }
-
-    @media (max-width: 400px) {
-      pre.banner { font-size: 9px; }
-    }
-
-    @media (max-width: 500px) {
-      pre.banner { font-size: 10px; }
-    }
-    </style>
-  </head>
-  <body>
-    <header>
-      <nav>
-        <a href="/">Home</a> |
-        <a href="contrib.html">contrib</a> |
-        Quickstart |
-        <a href="gmid.1.html">docs</a>
-      </nav>
-    </header>
-    <h1>gmid quickstart</h1>
-    <p>gmid can be run in two different “modes”:</p>
-    <dl>
-      <dt>configless:</dt>
-      <dd>
-        a quick way to serve a directory tree from the shell, useful
-        for testing a capsule before uploading it
-      </dd>
-      <dt>daemon mode:</dt>
-      <dd>
-        gmid reads the configuration file and runs in the background
-      </dd>
-    </dl>
-    <p>To run gmid in the “configless” mode, just type:</p>
-    <pre>$ gmid path/to/dir</pre>
-    <p>
-      gmid will then generate a certificate inside ~/.local/share/gmid
-      and serve the given directory locally.
-    </p>
-    <h2>Setting up a capsule with gmid</h2>
-    <p>
-      To host a Gemini capsule you need to run gmid in “daemon”
-      mode, and so a configuration file is needed. The format of the
-      configuration file is described in the manpage and is quite
-      flexible, but something like the following should be enough to
-      start:
-    </p>
-    <pre># /etc/gmid.conf
-
-server "example.com" {
-	cert "/etc/ssl/example.com.pem"
-	key  "/etc/ssl/private/example.com/key"
-
-	# path to the root directory of your capsule
-	root "/var/gemini/example.com"
-}</pre>
-    <p>
-      A certificate is needed for the capsule.  Generate one for
-      e.g. using
-      <a href="https://git.omarpolo.com/gmid/tree/contrib/gencert">contrib/gencert</a>:
-    </p>
-    <pre>$ ./contrib/gencert example.com
-Generating a 4096 bit RSA private key
-.................................................++++
-..........++++
-writing new private key to './example.com.key'
------
-
-Generated files:
-        ./example.com.pem : certificate
-        ./example.com.key : private key</pre>
-    <p>
-      Move ‘example.com.pem’ and ‘example.com.key’ to a safe place and
-      double check that the ‘cert’ and ‘key’ options in the
-      configuration points to these files.
-    </p>
-    <p>For example, save them in ‘/etc/ssl/’ (as root)</p>
-    <pre># mkdir -p /etc/ssl/private
-# chown 700 /etc/ssl/private
-# mv example.com.pem /etc/ssl/
-# mv example.com.key /etc/ssl/private/</pre>
-    <p>
-      Make sure that the ‘cert’ and ‘key’ options in the configuration
-      file points to these files.
-    </p>
-    <p>Then running gmid is as easy as</p>
-    <pre>$ gmid -c /etc/gmid.conf</pre>
-    <p>Congratulations, your capsule is online!</p>
-    <h2>Securing your gmid installation</h2>
-    <p>
-      gmid employs various techniques to prevent the damage caused by
-      bugs, but some steps needs to be done manually.
-    </p>
-    <p>
-      If gmid was installed from your distribution package manager,
-      chance are that it already does all of this and is also
-      providing a service to run gmid automatically (e.g. a rc script,
-      a systemd unit file …)  Otherwise, it’s heavily suggested to
-      create at least a dedicated user.
-    </p>
-    <h3>A dedicated user</h3>
-    <p>
-      Ideally, gmid should be started as root and drop privileges to a
-      local user.  This way, the certificates can be readable only by
-      root.  For example, on GNU/linux systems a ‘gmid’ user can be
-      created with:
-    </p>
-    <pre># useradd --system --no-create-home -s /bin/nologin -c "gmid Gemini server" gmid</pre>
-    <p>
-      Please consult your OS documentation for more information on the
-      matter.
-    </p>
-    <p>
-      The configuration then needs to be adjusted to include the
-      ‘user’ directive at the top:
-    </p>
-    <pre># /etc/gmid.conf
-user "gmid"
-
-server "example.com" { … }</pre>
-    <p>
-      gmid then needs to be started with root privileges, but will
-      then switch to the provided user automatically.  If by accident
-      the ‘user’ option is omitted and gmid is running as root, it
-      will complain loudly in the logs.
-    </p>
-    <h3>chroot</h3>
-    <p>
-      It’s a common practice for system daemons to chroot themselves
-      into a directory.  From here on I’ll assume /var/gemini, but it
-      can be any directory.
-    </p>
-    <p>
-      A chroot on UNIX-like OS is an operation that changes the
-      “apparent” root directory (i.e. the “/”) from the current
-      process and its child.  Think of it like imprisoning a process
-      into a directory and never letting it escape until it
-      terminates.
-    </p>
-    <p>
-      Using a chroot may complicate the use of CGI scripts, because
-      then all the dependencies of the scripts (sh, perl, libraries…)
-      need to be installed inside the chroot too.  For this very
-      reason gmid supports FastCGI.
-    </p>
-    <p>
-      The chroot feature requires a dedicate user, see the previous
-      section.
-    </p>
-    <p>
-      To chroot gmid inside a directory, use the ‘chroot’ directive in
-      the configuration file:
-    </p>
-    <pre># /etc/gmid.conf
-
-user "gmid"
-
-# the given directory, /var/gemini in this case, must exists.
-chroot "/var/gemini"</pre>
-    <p>
-      Note that once ‘chroot’ is in place, every ‘root’ directive is
-      implicitly relative to the chroot, but ‘cert’ and ‘key’ aren’t!
-    </p>
-    <p>For example, given the following configuration:</p>
-    <pre># /etc/gmid.conf
-
-user "gmid"
-chroot "/var/gemini"
-
-server "example.com" {
-	cert "/etc/ssl/example.com.pem"
-	key  "/etc/ssl/example.com.key"
-	root "/example.com"
-}</pre>
-    <p>
-      The certificate and the key path are the specified ones, but the
-      root directory of the virtual host is actually
-      “/var/gemini/example.com/”.
-    </p>
-  </body>
-</html>
blob - /dev/null
blob + 6c65eea57739000765980931c725e96c7a6d3eed (mode 755)
--- /dev/null
+++ site/menu.pl
@@ -0,0 +1,39 @@
+#!/usr/bin/env perl
+
+use v5.10;
+use strict;
+use warnings;
+
+my $page = shift or die 'missing page';
+my $outtype = shift or die 'missing output type';
+my @pages = ();
+
+while (<>) {
+	chomp;
+	@pages = (@pages, $_);
+}
+
+my $did = 0;
+for (@pages) {
+	my ($href, $text) = m/^([^\s]*)\s*(.*)$/;
+
+	if ($outtype eq 'gemini') {
+		if ($href ne $page) {
+			say "=> $href $text";
+		}
+	} else {
+		if (!$did) {
+			$did = 1;
+		} else {
+			print "| ";
+		}
+
+		if ($href eq $page) {
+			print "$text ";
+		} else {
+			print "<a href='$href'>$text</a> ";
+		}
+	}
+}
+
+say "";
blob - /dev/null
blob + 026d3c3f1e93d4b85c5571f10f7ceb7c147dcd93 (mode 644)
--- /dev/null
+++ site/style.css
@@ -0,0 +1,116 @@
+body {
+  font-family: monospace;
+  font-size: 14px;
+  max-width: 780px;
+  margin: 0 auto;
+  padding: 20px;
+  padding-bottom: 80px;
+}
+
+h1::before {
+  content: "# ";
+}
+
+h2 {
+  margin-top: 40px;
+}
+
+h2::before {
+  content: "## ";
+}
+
+h3::before {
+  content: "### ";
+}
+
+blockquote {
+  margin: 0;
+  padding: 0;
+}
+
+blockquote::before {
+  content: "> ";
+}
+
+blockquote p {
+  font-style: italic;
+  display: inline;
+}
+
+p.link::before {
+  content: "→ ";
+}
+
+strong::before { content: "*" }
+strong::after  { content: "*" }
+
+hr {
+  border: 0;
+  height: 1px;
+  background-color: #222;
+  width: 100%;
+  display: block;
+  margin: 2em auto;
+}
+
+ul.link-list {
+  list-style: disclosure-closed;
+}
+
+img {
+  border-radius: 5px;
+}
+
+pre {
+  overflow: auto;
+  padding: 1rem;
+  background-color: #f0f0f0;
+  border-radius: 3px;
+}
+
+pre.banner {
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+}
+
+code, kbd {
+  color: #9d109d;
+}
+
+img {
+  display: block;
+  margin: 0 auto;
+  max-width: 100%;
+}
+
+@media (prefers-color-scheme: dark) {
+  body {
+background-color: #222;
+color: white;
+  }
+
+  a {
+color: aqua;
+  }
+
+  hr {
+background-color: #ddd;
+  }
+
+  pre {
+background-color: #353535;
+  }
+
+  code, kbd {
+color: #ff4cff;
+  }
+}
+
+@media (max-width: 400px) {
+  pre.banner { font-size: 9px; }
+}
+
+@media (max-width: 500px) {
+  pre.banner { font-size: 10px; }
+}
blob - /dev/null
blob + c8f63c0b97b5684486521a5d9f44c75de64107f0 (mode 755)
--- /dev/null
+++ site/subst
@@ -0,0 +1,46 @@
+#!/usr/bin/env perl
+#
+# Copyright (c) 2022 Omar Polo <op@omarpolo.com>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# subst -- filter that replaces key=values pairs
+#
+# usage: subst key1=val1 [... keyn=valn] [files...]
+#
+# Use `--' before the first file if it may contain an = in its name.
+# If no files are given, read and substitute from standard input.
+
+use v5.10;
+use strict;
+use warnings;
+
+my @args = ();
+
+while (1) {
+	$_ = shift;
+	last if !$_;
+
+	if ($_ eq '--') {
+		last;
+	} elsif ($_ !~ m/=/) {
+		unshift @ARGV, $_;
+		last;
+	}
+
+	my ($match, $repl) = m/^([^\s]*)=(.*)$/;
+	push @args, "-e", "s,$match,$repl,g";
+}
+
+# OK, shelling out to sed is a bit lame...
+exec "sed", @args, @ARGV;