summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--TOUR.722
-rw-r--r--agpl.c3
-rw-r--r--bin/beef.c2
-rw-r--r--bin/bit.y2
-rw-r--r--bin/c11.l2
-rw-r--r--bin/dehtml.l2
-rw-r--r--bin/downgrade.c2
-rw-r--r--bin/dtch.c2
-rw-r--r--bin/ever.c2
-rw-r--r--bin/freecell.c2
-rw-r--r--bin/git-comment.pl2
-rw-r--r--bin/glitch.c687
-rw-r--r--bin/hilex.c4
-rw-r--r--bin/hilex.h2
-rw-r--r--bin/make.l2
-rw-r--r--bin/man1/up.118
-rw-r--r--bin/man1/when.134
-rw-r--r--bin/mdoc.l2
-rw-r--r--bin/modem.c2
-rw-r--r--bin/mtags.c2
-rw-r--r--bin/nudge.c2
-rw-r--r--bin/order.y2
-rw-r--r--bin/pbd.c2
-rw-r--r--bin/png.h2
-rw-r--r--bin/pngo.c2
-rw-r--r--bin/psf2png.c2
-rw-r--r--bin/ptee.c2
-rw-r--r--bin/quick.c2
-rw-r--r--bin/relay.c2
-rw-r--r--bin/scheme.c2
-rw-r--r--bin/sh.l14
-rw-r--r--bin/shotty.l6
-rw-r--r--bin/title.c2
-rw-r--r--bin/up.sh30
-rw-r--r--bin/when.y130
-rw-r--r--bin/xx.c2
-rw-r--r--doc/pdf/.gitignore1
-rw-r--r--doc/pdf/Makefile31
-rw-r--r--doc/zlib/adler32.32
-rw-r--r--doc/zlib/adler32_combine.32
-rw-r--r--doc/zlib/compress.32
-rw-r--r--doc/zlib/compressBound.32
-rw-r--r--doc/zlib/crc32.32
-rw-r--r--doc/zlib/crc32_combine.32
-rw-r--r--doc/zlib/deflate.32
-rw-r--r--doc/zlib/deflateBound.32
-rw-r--r--doc/zlib/deflateCopy.32
-rw-r--r--doc/zlib/deflateEnd.32
-rw-r--r--doc/zlib/deflateGetDictionary.32
-rw-r--r--doc/zlib/deflateInit.32
-rw-r--r--doc/zlib/deflateInit2.32
-rw-r--r--doc/zlib/deflateParams.32
-rw-r--r--doc/zlib/deflatePending.32
-rw-r--r--doc/zlib/deflatePrime.32
-rw-r--r--doc/zlib/deflateReset.32
-rw-r--r--doc/zlib/deflateSetDictionary.32
-rw-r--r--doc/zlib/deflateSetHeader.32
-rw-r--r--doc/zlib/deflateTune.32
-rw-r--r--doc/zlib/gzbuffer.32
-rw-r--r--doc/zlib/gzclose.32
-rw-r--r--doc/zlib/gzdirect.32
-rw-r--r--doc/zlib/gzeof.32
-rw-r--r--doc/zlib/gzerror.32
-rw-r--r--doc/zlib/gzflush.32
-rw-r--r--doc/zlib/gzfread.32
-rw-r--r--doc/zlib/gzfwrite.32
-rw-r--r--doc/zlib/gzgetc.32
-rw-r--r--doc/zlib/gzgets.32
-rw-r--r--doc/zlib/gzoffset.32
-rw-r--r--doc/zlib/gzopen.32
-rw-r--r--doc/zlib/gzprintf.32
-rw-r--r--doc/zlib/gzputc.32
-rw-r--r--doc/zlib/gzputs.32
-rw-r--r--doc/zlib/gzread.32
-rw-r--r--doc/zlib/gzseek.32
-rw-r--r--doc/zlib/gzsetparams.32
-rw-r--r--doc/zlib/gzungetc.32
-rw-r--r--doc/zlib/gzwrite.32
-rw-r--r--doc/zlib/inflate.32
-rw-r--r--doc/zlib/inflateBack.32
-rw-r--r--doc/zlib/inflateBackEnd.32
-rw-r--r--doc/zlib/inflateBackInit.32
-rw-r--r--doc/zlib/inflateCopy.32
-rw-r--r--doc/zlib/inflateEnd.32
-rw-r--r--doc/zlib/inflateGetDictionary.32
-rw-r--r--doc/zlib/inflateGetHeader.32
-rw-r--r--doc/zlib/inflateInit.32
-rw-r--r--doc/zlib/inflateInit2.32
-rw-r--r--doc/zlib/inflateMark.32
-rw-r--r--doc/zlib/inflatePrime.32
-rw-r--r--doc/zlib/inflateReset.32
-rw-r--r--doc/zlib/inflateSetDictionary.32
-rw-r--r--doc/zlib/inflateSync.32
-rw-r--r--doc/zlib/uncompress.32
-rw-r--r--doc/zlib/zlibCompileFlags.32
-rw-r--r--doc/zlib/zlibVersion.32
-rw-r--r--gpl.c3
-rw-r--r--home/.config/X/resources2
-rw-r--r--home/.config/git/config9
-rw-r--r--home/.ssh/config3
-rw-r--r--home/.xsession7
-rw-r--r--txt/books.txt18
-rw-r--r--txt/shows.txt1
-rw-r--r--txt/tweets.txt32
-rw-r--r--www/causal.agency/.gitignore3
-rw-r--r--www/causal.agency/Makefile11
-rw-r--r--www/causal.agency/alpha.html92
-rw-r--r--www/causal.agency/index.713
-rw-r--r--www/causal.agency/lands.html176
-rw-r--r--www/causal.agency/style.css5
-rw-r--r--www/git.causal.agency/.gitignore1
-rw-r--r--www/git.causal.agency/Makefile11
-rw-r--r--www/git.causal.agency/filter.c10
-rw-r--r--www/git.causal.agency/index.781
-rw-r--r--www/temp.causal.agency/up.c2
-rw-r--r--www/text.causal.agency/037-care.762
-rw-r--r--www/text.causal.agency/039-apologies.781
-rw-r--r--www/text.causal.agency/040-sound-memory.7165
-rw-r--r--www/text.causal.agency/041-albums-2022.7185
-rw-r--r--www/text.causal.agency/042-comfort-music.762
-rw-r--r--www/text.causal.agency/043-little-blessings.778
-rw-r--r--www/text.causal.agency/Makefile11
-rw-r--r--www/text.causal.agency/feed.sh1
123 files changed, 1694 insertions, 579 deletions
diff --git a/TOUR.7 b/TOUR.7
deleted file mode 100644
index 8466f524..00000000
--- a/TOUR.7
+++ /dev/null
@@ -1,22 +0,0 @@
-.Dd March 16, 2022
-.Dt TOUR 7
-.Os "Causal Agency"
-.
-.Sh NAME
-.Nm tour
-.Nd things that are here
-.
-.Sh FILES
-.Bl -tag -width Ds
-.It Pa home/
-.Dq dotfiles
-.It Pa bin/
-utilities of varying utility
-.It Pa doc/rfc/
-scripts for reading IETF RFCs
-.It Pa doc/zlib/
-zlib documentation ported to
-.Xr mdoc 7
-.It Pa www/
-sources for causal.agency sites
-.El
diff --git a/agpl.c b/agpl.c
index 58a88fc4..e7682757 100644
--- a/agpl.c
+++ b/agpl.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022  June McEnroe <june@causal.agency>
+/* Copyright (C) 2024  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -17,4 +17,3 @@
 #include <err.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sysexits.h>
diff --git a/bin/beef.c b/bin/beef.c
index b2579b73..556f3088 100644
--- a/bin/beef.c
+++ b/bin/beef.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2019  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/bit.y b/bin/bit.y
index ab310492..1119bce6 100644
--- a/bin/bit.y
+++ b/bin/bit.y
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2019  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/c11.l b/bin/c11.l
index a4b8c25a..b1f0b960 100644
--- a/bin/c11.l
+++ b/bin/c11.l
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/dehtml.l b/bin/dehtml.l
index 3f2de592..799f0926 100644
--- a/bin/dehtml.l
+++ b/bin/dehtml.l
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/downgrade.c b/bin/downgrade.c
index d4c5b598..31019714 100644
--- a/bin/downgrade.c
+++ b/bin/downgrade.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/dtch.c b/bin/dtch.c
index 2aea53ae..026493dd 100644
--- a/bin/dtch.c
+++ b/bin/dtch.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2017-2019  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2017-2019  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/ever.c b/bin/ever.c
index f983912b..f8ff943b 100644
--- a/bin/ever.c
+++ b/bin/ever.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2017  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2017  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/freecell.c b/bin/freecell.c
index 11bed1c0..fbc0fe22 100644
--- a/bin/freecell.c
+++ b/bin/freecell.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019, 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2019, 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/git-comment.pl b/bin/git-comment.pl
index 5100941f..5352702d 100644
--- a/bin/git-comment.pl
+++ b/bin/git-comment.pl
@@ -1,5 +1,5 @@
 #!/usr/bin/env perl
-# Copyright (C) 2021  C. McEnroe <june@causal.agency>
+# Copyright (C) 2021  June McEnroe <june@causal.agency>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/glitch.c b/bin/glitch.c
index 9747f35a..d0c926f9 100644
--- a/bin/glitch.c
+++ b/bin/glitch.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2018  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2018, 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -14,8 +14,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <arpa/inet.h>
 #include <err.h>
+#include <inttypes.h>
+#include <limits.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -25,286 +26,328 @@
 #include <unistd.h>
 #include <zlib.h>
 
-#define PACKED __attribute__((packed))
-
-#define CRC_INIT (crc32(0, Z_NULL, 0))
+#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
 
 static const char *path;
 static FILE *file;
 static uint32_t crc;
 
-static void readExpect(void *ptr, size_t size, const char *expect) {
-	fread(ptr, size, 1, file);
-	if (ferror(file)) err(EX_IOERR, "%s", path);
-	if (feof(file)) errx(EX_DATAERR, "%s: missing %s", path, expect);
-	crc = crc32(crc, ptr, size);
+static void pngRead(void *ptr, size_t len, const char *desc) {
+	size_t n = fread(ptr, len, 1, file);
+	if (!n && ferror(file)) err(EX_IOERR, "%s", path);
+	if (!n) errx(EX_DATAERR, "%s: missing %s", path, desc);
+	crc = crc32(crc, ptr, len);
 }
 
-static void writeExpect(const void *ptr, size_t size) {
-	fwrite(ptr, size, 1, file);
-	if (ferror(file)) err(EX_IOERR, "%s", path);
-	crc = crc32(crc, ptr, size);
+static void pngWrite(const void *ptr, size_t len) {
+	size_t n = fwrite(ptr, len, 1, file);
+	if (!n) err(EX_IOERR, "%s", path);
+	crc = crc32(crc, ptr, len);
 }
 
-static const uint8_t Signature[8] = "\x89PNG\r\n\x1A\n";
+static const uint8_t Sig[8] = "\x89PNG\r\n\x1A\n";
 
-static void readSignature(void) {
-	uint8_t signature[8];
-	readExpect(signature, 8, "signature");
-	if (0 != memcmp(signature, Signature, 8)) {
+static void sigRead(void) {
+	uint8_t sig[sizeof(Sig)];
+	pngRead(sig, sizeof(sig), "signature");
+	if (memcmp(sig, Sig, sizeof(sig))) {
 		errx(EX_DATAERR, "%s: invalid signature", path);
 	}
 }
 
-static void writeSignature(void) {
-	writeExpect(Signature, sizeof(Signature));
+static void sigWrite(void) {
+	pngWrite(Sig, sizeof(Sig));
 }
 
-struct PACKED Chunk {
-	uint32_t size;
-	char type[4];
-};
+static uint32_t u32Read(const char *desc) {
+	uint8_t b[4];
+	pngRead(b, sizeof(b), desc);
+	return (uint32_t)b[0] << 24 | (uint32_t)b[1] << 16
+		| (uint32_t)b[2] << 8 | (uint32_t)b[3];
+}
 
-static const char *typeStr(struct Chunk chunk) {
-	static char buf[5];
-	memcpy(buf, chunk.type, 4);
-	return buf;
+static void u32Write(uint32_t x) {
+	uint8_t b[4] = { x >> 24 & 0xFF, x >> 16 & 0xFF, x >> 8 & 0xFF, x & 0xFF };
+	pngWrite(b, sizeof(b));
 }
 
-static struct Chunk readChunk(void) {
+struct Chunk {
+	uint32_t len;
+	char type[5];
+};
+
+static struct Chunk chunkRead(void) {
 	struct Chunk chunk;
-	readExpect(&chunk, sizeof(chunk), "chunk");
-	chunk.size = ntohl(chunk.size);
-	crc = crc32(CRC_INIT, (Byte *)chunk.type, sizeof(chunk.type));
+	chunk.len = u32Read("chunk length");
+	crc = crc32(0, Z_NULL, 0);
+	pngRead(chunk.type, 4, "chunk type");
+	chunk.type[4] = 0;
 	return chunk;
 }
 
-static void writeChunk(struct Chunk chunk) {
-	chunk.size = htonl(chunk.size);
-	writeExpect(&chunk, sizeof(chunk));
-	crc = crc32(CRC_INIT, (Byte *)chunk.type, sizeof(chunk.type));
+static void chunkWrite(struct Chunk chunk) {
+	u32Write(chunk.len);
+	crc = crc32(0, Z_NULL, 0);
+	pngWrite(chunk.type, 4);
 }
 
-static void readCrc(void) {
-	uint32_t expected = crc;
-	uint32_t found;
-	readExpect(&found, sizeof(found), "CRC32");
-	found = ntohl(found);
-	if (found != expected) {
-		errx(
-			EX_DATAERR, "%s: expected CRC32 %08X, found %08X",
-			path, expected, found
-		);
-	}
+static void crcRead(void) {
+	uint32_t expect = crc;
+	uint32_t actual = u32Read("CRC32");
+	if (actual == expect) return;
+	errx(
+		EX_DATAERR, "%s: expected CRC32 %08X, found %08X",
+		path, expect, actual
+	);
 }
 
-static void writeCrc(void) {
-	uint32_t net = htonl(crc);
-	writeExpect(&net, sizeof(net));
+static void crcWrite(void) {
+	u32Write(crc);
 }
 
-static void skipChunk(struct Chunk chunk) {
-	uint8_t discard[chunk.size];
-	readExpect(discard, sizeof(discard), "chunk data");
-	readCrc();
+static void chunkSkip(struct Chunk chunk) {
+	if (!(chunk.type[0] & 0x20)) {
+		errx(EX_CONFIG, "%s: unsupported critical chunk %s", path, chunk.type);
+	}
+	uint8_t buf[4096];
+	while (chunk.len > sizeof(buf)) {
+		pngRead(buf, sizeof(buf), "chunk data");
+		chunk.len -= sizeof(buf);
+	}
+	if (chunk.len) pngRead(buf, chunk.len, "chunk data");
+	crcRead();
 }
 
-static struct PACKED {
+enum Color {
+	Grayscale = 0,
+	Truecolor = 2,
+	Indexed = 3,
+	GrayscaleAlpha = 4,
+	TruecolorAlpha = 6,
+};
+enum Compression {
+	Deflate,
+};
+enum FilterMethod {
+	Adaptive,
+};
+enum Interlace {
+	Progressive,
+	Adam7,
+};
+
+enum { HeaderLen = 13 };
+static struct {
 	uint32_t width;
 	uint32_t height;
 	uint8_t depth;
-	enum PACKED {
-		Grayscale      = 0,
-		Truecolor      = 2,
-		Indexed        = 3,
-		GrayscaleAlpha = 4,
-		TruecolorAlpha = 6,
-	} color;
+	uint8_t color;
 	uint8_t compression;
 	uint8_t filter;
 	uint8_t interlace;
 } header;
-_Static_assert(13 == sizeof(header), "header size");
 
-static size_t pixelBits(void) {
+static size_t pixelLen;
+static size_t lineLen;
+static size_t dataLen;
+
+static void recalc(void) {
+	size_t pixelBits = header.depth;
 	switch (header.color) {
-		case Grayscale:      return 1 * header.depth;
-		case Truecolor:      return 3 * header.depth;
-		case Indexed:        return 1 * header.depth;
-		case GrayscaleAlpha: return 2 * header.depth;
-		case TruecolorAlpha: return 4 * header.depth;
-		default: abort();
+		break; case GrayscaleAlpha: pixelBits *= 2;
+		break; case Truecolor: pixelBits *= 3;
+		break; case TruecolorAlpha: pixelBits *= 4;
 	}
+	pixelLen = (pixelBits + 7) / 8;
+	lineLen = (header.width * pixelBits + 7) / 8;
+	dataLen = (1 + lineLen) * header.height;
 }
 
-static size_t pixelSize(void) {
-	return (pixelBits() + 7) / 8;
+static void headerRead(struct Chunk chunk) {
+	if (chunk.len != HeaderLen) {
+		errx(
+			EX_DATAERR, "%s: expected %s length %" PRIu32 ", found %" PRIu32,
+			path, chunk.type, (uint32_t)HeaderLen, chunk.len
+		);
+	}
+	header.width = u32Read("header width");
+	header.height = u32Read("header height");
+	pngRead(&header.depth, 1, "header depth");
+	pngRead(&header.color, 1, "header color");
+	pngRead(&header.compression, 1, "header compression");
+	pngRead(&header.filter, 1, "header filter");
+	pngRead(&header.interlace, 1, "header interlace");
+	crcRead();
+	recalc();
+}
+
+static void headerWrite(void) {
+	struct Chunk ihdr = { HeaderLen, "IHDR" };
+	chunkWrite(ihdr);
+	u32Write(header.width);
+	u32Write(header.height);
+	pngWrite(&header.depth, 1);
+	pngWrite(&header.color, 1);
+	pngWrite(&header.compression, 1);
+	pngWrite(&header.filter, 1);
+	pngWrite(&header.interlace, 1);
+	crcWrite();
 }
 
-static size_t lineSize(void) {
-	return (header.width * pixelBits() + 7) / 8;
-}
+static struct {
+	uint32_t len;
+	uint8_t rgb[256][3];
+} pal;
 
-static size_t dataSize(void) {
-	return (1 + lineSize()) * header.height;
+static struct {
+	uint32_t len;
+	uint8_t a[256];
+} trans;
+
+static void palClear(void) {
+	pal.len = 0;
+	trans.len = 0;
 }
 
-static void readHeader(void) {
-	struct Chunk ihdr = readChunk();
-	if (0 != memcmp(ihdr.type, "IHDR", 4)) {
-		errx(EX_DATAERR, "%s: expected IHDR, found %s", path, typeStr(ihdr));
+static void palRead(struct Chunk chunk) {
+	if (chunk.len % 3) {
+		errx(
+			EX_DATAERR, "%s: %s length %" PRIu32 " not divisible by 3",
+			path, chunk.type, chunk.len
+		);
 	}
-	if (ihdr.size != sizeof(header)) {
+	pal.len = chunk.len / 3;
+	if (pal.len > 256) {
 		errx(
-			EX_DATAERR, "%s: expected IHDR size %zu, found %u",
-			path, sizeof(header), ihdr.size
+			EX_DATAERR, "%s: %s length %" PRIu32 " > 256",
+			path, chunk.type, pal.len
 		);
 	}
-	readExpect(&header, sizeof(header), "header");
-	readCrc();
-	header.width = ntohl(header.width);
-	header.height = ntohl(header.height);
-	if (!header.width) errx(EX_DATAERR, "%s: invalid width 0", path);
-	if (!header.height) errx(EX_DATAERR, "%s: invalid height 0", path);
+	pngRead(pal.rgb, chunk.len, "palette data");
+	crcRead();
 }
 
-static void writeHeader(void) {
-	struct Chunk ihdr = { .size = sizeof(header), .type = "IHDR" };
-	writeChunk(ihdr);
-	header.width = htonl(header.width);
-	header.height = htonl(header.height);
-	writeExpect(&header, sizeof(header));
-	writeCrc();
-	header.width = ntohl(header.width);
-	header.height = ntohl(header.height);
+static void palWrite(void) {
+	struct Chunk plte = { 3 * pal.len, "PLTE" };
+	chunkWrite(plte);
+	pngWrite(pal.rgb, plte.len);
+	crcWrite();
 }
 
-static struct {
-	uint32_t len;
-	uint8_t entries[256][3];
-} palette;
-
-static void readPalette(void) {
-	struct Chunk chunk;
-	for (;;) {
-		chunk = readChunk();
-		if (0 == memcmp(chunk.type, "PLTE", 4)) break;
-		skipChunk(chunk);
+static void transRead(struct Chunk chunk) {
+	trans.len = chunk.len;
+	if (trans.len > 256) {
+		errx(
+			EX_DATAERR, "%s: %s length %" PRIu32 " > 256",
+			path, chunk.type, trans.len
+		);
 	}
-	palette.len = chunk.size / 3;
-	readExpect(palette.entries, chunk.size, "palette data");
-	readCrc();
+	pngRead(trans.a, chunk.len, "transparency data");
+	crcRead();
 }
 
-static void writePalette(void) {
-	struct Chunk plte = { .size = 3 * palette.len, .type = "PLTE" };
-	writeChunk(plte);
-	writeExpect(palette.entries, plte.size);
-	writeCrc();
+static void transWrite(void) {
+	struct Chunk trns = { trans.len, "tRNS" };
+	chunkWrite(trns);
+	pngWrite(trans.a, trns.len);
+	crcWrite();
 }
 
 static uint8_t *data;
 
-static void readData(void) {
-	data = malloc(dataSize());
-	if (!data) err(EX_OSERR, "malloc(%zu)", dataSize());
+static void dataAlloc(void) {
+	data = malloc(dataLen);
+	if (!data) err(EX_OSERR, "malloc");
+}
 
-	struct z_stream_s stream = { .next_out = data, .avail_out = dataSize() };
+static void dataRead(struct Chunk chunk) {
+	z_stream stream = { .next_out = data, .avail_out = dataLen };
 	int error = inflateInit(&stream);
-	if (error != Z_OK) errx(EX_SOFTWARE, "%s: inflateInit: %s", path, stream.msg);
+	if (error != Z_OK) errx(EX_SOFTWARE, "inflateInit: %s", stream.msg);
 
 	for (;;) {
-		struct Chunk chunk = readChunk();
-		if (0 == memcmp(chunk.type, "IDAT", 4)) {
-			uint8_t *idat = malloc(chunk.size);
-			if (!idat) err(EX_OSERR, "malloc");
-
-			readExpect(idat, chunk.size, "image data");
-			readCrc();
+		if (strcmp(chunk.type, "IDAT")) {
+			errx(EX_DATAERR, "%s: missing IDAT chunk", path);
+		}
 
-			stream.next_in = idat;
-			stream.avail_in = chunk.size;
-			int error = inflate(&stream, Z_SYNC_FLUSH);
-			free(idat);
+		uint8_t *idat = malloc(chunk.len);
+		if (!idat) err(EX_OSERR, "malloc");
 
-			if (error == Z_STREAM_END) break;
-			if (error != Z_OK) errx(EX_DATAERR, "%s: inflate: %s", path, stream.msg);
+		pngRead(idat, chunk.len, "image data");
+		crcRead();
+		
+		stream.next_in = idat;
+		stream.avail_in = chunk.len;
+		error = inflate(&stream, Z_SYNC_FLUSH);
+		free(idat);
 
-		} else if (0 == memcmp(chunk.type, "IEND", 4)) {
-			errx(EX_DATAERR, "%s: missing IDAT chunk", path);
-		} else {
-			skipChunk(chunk);
+		if (error == Z_STREAM_END) break;
+		if (error != Z_OK) {
+			errx(EX_DATAERR, "%s: inflate: %s", path, stream.msg);
 		}
-	}
 
+		chunk = chunkRead();
+	}
 	inflateEnd(&stream);
-	if ((size_t)stream.total_out != dataSize()) {
+	if ((size_t)stream.total_out != dataLen) {
 		errx(
-			EX_DATAERR, "%s: expected data size %zu, found %zu",
-			path, dataSize(), (size_t)stream.total_out
+			EX_DATAERR, "%s: expected data length %zu, found %zu",
+			path, dataLen, (size_t)stream.total_out
 		);
 	}
 }
 
-static void writeData(void) {
-	uLong size = compressBound(dataSize());
-	uint8_t *deflate = malloc(size);
-	if (!deflate) err(EX_OSERR, "malloc");
+static void dataWrite(void) {
+	z_stream stream = {
+		.next_in = data,
+		.avail_in = dataLen,
+	};
+	int error = deflateInit2(
+		&stream, Z_BEST_COMPRESSION, Z_DEFLATED, 15, 8, Z_FILTERED
+	);
+	if (error != Z_OK) errx(EX_SOFTWARE, "deflateInit2: %s", stream.msg);
 
-	int error = compress2(deflate, &size, data, dataSize(), Z_BEST_SPEED);
-	if (error != Z_OK) errx(EX_SOFTWARE, "%s: compress2: %d", path, error);
+	uLong bound = deflateBound(&stream, dataLen);
+	uint8_t *buf = malloc(bound);
+	if (!buf) err(EX_OSERR, "malloc");
 
-	struct Chunk idat = { .size = size, .type = "IDAT" };
-	writeChunk(idat);
-	writeExpect(deflate, size);
-	writeCrc();
+	stream.next_out = buf;
+	stream.avail_out = bound;
+	deflate(&stream, Z_FINISH);
+	deflateEnd(&stream);
 
-	free(deflate);
-}
+	struct Chunk idat = { stream.total_out, "IDAT" };
+	chunkWrite(idat);
+	pngWrite(buf, stream.total_out);
+	crcWrite();
+	free(buf);
 
-static void writeEnd(void) {
-	struct Chunk iend = { .size = 0, .type = "IEND" };
-	writeChunk(iend);
-	writeCrc();
+	struct Chunk iend = { 0, "IEND" };
+	chunkWrite(iend);
+	crcWrite();
 }
 
-enum PACKED Filter {
+enum Filter {
 	None,
 	Sub,
 	Up,
 	Average,
 	Paeth,
-	FilterCount,
+	FilterCap,
 };
 
-static struct {
-	bool brokenPaeth;
-	bool filt;
-	bool recon;
-	uint8_t declareFilter;
-	uint8_t applyFilter;
-	enum Filter declareFilters[255];
-	enum Filter applyFilters[255];
-	bool invert;
-	bool mirror;
-	bool zeroX;
-	bool zeroY;
-} options;
-
 struct Bytes {
-	uint8_t x;
-	uint8_t a;
-	uint8_t b;
-	uint8_t c;
+	uint8_t x, a, b, c;
 };
 
+static bool brokenPaeth;
 static uint8_t paethPredictor(struct Bytes f) {
 	int32_t p = (int32_t)f.a + (int32_t)f.b - (int32_t)f.c;
-	int32_t pa = abs(p - (int32_t)f.a);
-	int32_t pb = abs(p - (int32_t)f.b);
-	int32_t pc = abs(p - (int32_t)f.c);
+	int32_t pa = labs(p - (int32_t)f.a);
+	int32_t pb = labs(p - (int32_t)f.b);
+	int32_t pc = labs(p - (int32_t)f.c);
 	if (pa <= pb && pa <= pc) return f.a;
-	if (options.brokenPaeth) {
+	if (brokenPaeth) {
 		if (pb < pc) return f.b;
 	} else {
 		if (pb <= pc) return f.b;
@@ -319,7 +362,7 @@ static uint8_t recon(enum Filter type, struct Bytes f) {
 		case Up:      return f.x + f.b;
 		case Average: return f.x + ((uint32_t)f.a + (uint32_t)f.b) / 2;
 		case Paeth:   return f.x + paethPredictor(f);
-		default:      abort();
+		default: abort();
 	}
 }
 
@@ -330,59 +373,59 @@ static uint8_t filt(enum Filter type, struct Bytes f) {
 		case Up:      return f.x - f.b;
 		case Average: return f.x - ((uint32_t)f.a + (uint32_t)f.b) / 2;
 		case Paeth:   return f.x - paethPredictor(f);
-		default:      abort();
+		default: abort();
 	}
 }
 
-static struct Line {
-	enum Filter type;
-	uint8_t data[];
-} **lines;
-
-static void scanlines(void) {
-	lines = calloc(header.height, sizeof(*lines));
-	if (!lines) err(EX_OSERR, "calloc(%u, %zu)", header.height, sizeof(*lines));
-
-	size_t stride = 1 + lineSize();
-	for (uint32_t y = 0; y < header.height; ++y) {
-		lines[y] = (struct Line *)&data[y * stride];
-		if (lines[y]->type >= FilterCount) {
-			errx(EX_DATAERR, "%s: invalid filter type %hhu", path, lines[y]->type);
-		}
-	}
+static uint8_t *lineType(uint32_t y) {
+	return &data[y * (1 + lineLen)];
+}
+static uint8_t *lineData(uint32_t y) {
+	return 1 + lineType(y);
 }
 
 static struct Bytes origBytes(uint32_t y, size_t i) {
-	bool a = (i >= pixelSize()), b = (y > 0), c = (a && b);
+	bool a = (i >= pixelLen), b = (y > 0), c = (a && b);
 	return (struct Bytes) {
-		.x = lines[y]->data[i],
-		.a = a ? lines[y]->data[i - pixelSize()] : 0,
-		.b = b ? lines[y - 1]->data[i] : 0,
-		.c = c ? lines[y - 1]->data[i - pixelSize()] : 0,
+		.x = lineData(y)[i],
+		.a = (a ? lineData(y)[i-pixelLen] : 0),
+		.b = (b ? lineData(y-1)[i] : 0),
+		.c = (c ? lineData(y-1)[i-pixelLen] : 0),
 	};
 }
 
-static void reconData(void) {
+static bool reconFilter;
+static void dataRecon(void) {
 	for (uint32_t y = 0; y < header.height; ++y) {
-		for (size_t i = 0; i < lineSize(); ++i) {
-			if (options.filt) {
-				lines[y]->data[i] = filt(lines[y]->type, origBytes(y, i));
+		for (size_t i = 0; i < lineLen; ++i) {
+			if (reconFilter) {
+				lineData(y)[i] = filt(*lineType(y), origBytes(y, i));
 			} else {
-				lines[y]->data[i] = recon(lines[y]->type, origBytes(y, i));
+				lineData(y)[i] = recon(*lineType(y), origBytes(y, i));
 			}
 		}
-		lines[y]->type = None;
+		*lineType(y) = None;
 	}
 }
 
-static void filterData(void) {
-	for (uint32_t y = header.height - 1; y < header.height; --y) {
-		uint8_t filter[FilterCount][lineSize()];
-		uint32_t heuristic[FilterCount] = {0};
+static bool filterRecon;
+static size_t applyFilter;
+static enum Filter applyFilters[256];
+static size_t declFilter;
+static enum Filter declFilters[256];
+
+static void dataFilter(void) {
+	uint8_t *filter[FilterCap];
+	for (enum Filter i = None; i < FilterCap; ++i) {
+		filter[i] = malloc(lineLen);
+		if (!filter[i]) err(EX_OSERR, "malloc");
+	}
+	for (uint32_t y = header.height-1; y < header.height; --y) {
+		uint32_t heuristic[FilterCap] = {0};
 		enum Filter minType = None;
-		for (enum Filter type = None; type < FilterCount; ++type) {
-			for (size_t i = 0; i < lineSize(); ++i) {
-				if (options.recon) {
+		for (enum Filter type = None; type < FilterCap; ++type) {
+			for (size_t i = 0; i < lineLen; ++i) {
+				if (filterRecon) {
 					filter[type][i] = recon(type, origBytes(y, i));
 				} else {
 					filter[type][i] = filt(type, origBytes(y, i));
@@ -391,49 +434,26 @@ static void filterData(void) {
 			}
 			if (heuristic[type] < heuristic[minType]) minType = type;
 		}
-
-		if (options.declareFilter) {
-			lines[y]->type = options.declareFilters[y % options.declareFilter];
+		if (declFilter) {
+			*lineType(y) = declFilters[y % declFilter];
 		} else {
-			lines[y]->type = minType;
+			*lineType(y) = minType;
 		}
-
-		if (options.applyFilter) {
-			enum Filter type = options.applyFilters[y % options.applyFilter];
-			memcpy(lines[y]->data, filter[type], lineSize());
+		if (applyFilter) {
+			memcpy(lineData(y), filter[applyFilters[y % applyFilter]], lineLen);
 		} else {
-			memcpy(lines[y]->data, filter[minType], lineSize());
+			memcpy(lineData(y), filter[minType], lineLen);
 		}
 	}
-}
-
-static void invert(void) {
-	for (uint32_t y = 0; y < header.height; ++y) {
-		for (size_t i = 0; i < lineSize(); ++i) {
-			lines[y]->data[i] ^= 0xFF;
-		}
-	}
-}
-
-static void mirror(void) {
-	for (uint32_t y = 0; y < header.height; ++y) {
-		for (size_t i = 0, j = lineSize() - 1; i < j; ++i, --j) {
-			uint8_t t = lines[y]->data[i];
-			lines[y]->data[i] = lines[y]->data[j];
-			lines[y]->data[j] = t;
-		}
-	}
-}
-
-static void zeroX(void) {
-	for (uint32_t y = 0; y < header.height; ++y) {
-		memset(lines[y]->data, 0, pixelSize());
+	for (enum Filter i = None; i < FilterCap; ++i) {
+		free(filter[i]);
 	}
 }
 
-static void zeroY(void) {
-	memset(lines[0]->data, 0, lineSize());
-}
+static bool invertData;
+static bool mirrorData;
+static bool zeroX;
+static bool zeroY;
 
 static void glitch(const char *inPath, const char *outPath) {
 	if (inPath) {
@@ -441,98 +461,145 @@ static void glitch(const char *inPath, const char *outPath) {
 		file = fopen(path, "r");
 		if (!file) err(EX_NOINPUT, "%s", path);
 	} else {
-		path = "(stdin)";
+		path = "stdin";
 		file = stdin;
 	}
 
-	readSignature();
-	readHeader();
-	if (header.color == Indexed) readPalette();
-	readData();
+	sigRead();
+	struct Chunk ihdr = chunkRead();
+	if (strcmp(ihdr.type, "IHDR")) {
+		errx(EX_DATAERR, "%s: expected IHDR, found %s", path, ihdr.type);
+	}
+	headerRead(ihdr);
+	if (header.interlace != Progressive) {
+		errx(EX_CONFIG, "%s: unsupported interlacing", path);
+	}
+
+	palClear();
+	dataAlloc();
+	for (;;) {
+		struct Chunk chunk = chunkRead();
+		if (!strcmp(chunk.type, "PLTE")) {
+			palRead(chunk);
+		} else if (!strcmp(chunk.type, "tRNS")) {
+			transRead(chunk);
+		} else if (!strcmp(chunk.type, "IDAT")) {
+			dataRead(chunk);
+		} else if (!strcmp(chunk.type, "IEND")) {
+			break;
+		} else {
+			chunkSkip(chunk);
+		}
+	}
 	fclose(file);
 
-	scanlines();
-	reconData();
-	filterData();
-	if (options.invert) invert();
-	if (options.mirror) mirror();
-	if (options.zeroX) zeroX();
-	if (options.zeroY) zeroY();
-	free(lines);
+	dataRecon();
+	dataFilter();
 
+	if (invertData) {
+		for (uint32_t y = 0; y < header.height; ++y) {
+			for (size_t i = 0; i < lineLen; ++i) {
+				lineData(y)[i] ^= 0xFF;
+			}
+		}
+	}
+	if (mirrorData) {
+		for (uint32_t y = 0; y < header.height; ++y) {
+			for (size_t i = 0, j = lineLen-1; i < j; ++i, --j) {
+				uint8_t x = lineData(y)[i];
+				lineData(y)[i] = lineData(y)[j];
+				lineData(y)[j] = x;
+			}
+		}
+	}
+	if (zeroX) {
+		for (uint32_t y = 0; y < header.height; ++y) {
+			memset(lineData(y), 0, pixelLen);
+		}
+	}
+	if (zeroY) {
+		memset(lineData(0), 0, lineLen);
+	}
+
+	char buf[PATH_MAX];
 	if (outPath) {
 		path = outPath;
-		file = fopen(path, "w");
-		if (!file) err(EX_CANTCREAT, "%s", path);
+		if (outPath == inPath) {
+			snprintf(buf, sizeof(buf), "%sg", outPath);
+			file = fopen(buf, "wx");
+			if (!file) err(EX_CANTCREAT, "%s", buf);
+		} else {
+			file = fopen(path, "w");
+			if (!file) err(EX_CANTCREAT, "%s", outPath);
+		}
 	} else {
-		path = "(stdout)";
+		path = "stdout";
 		file = stdout;
 	}
 
-	writeSignature();
-	writeHeader();
-	if (header.color == Indexed) writePalette();
-	writeData();
-	writeEnd();
+	sigWrite();
+	headerWrite();
+	if (header.color == Indexed) {
+		palWrite();
+		if (trans.len) transWrite();
+	}
+	dataWrite();
 	free(data);
-
 	int error = fclose(file);
 	if (error) err(EX_IOERR, "%s", path);
+
+	if (outPath && outPath == inPath) {
+		error = rename(buf, outPath);
+		if (error) err(EX_CANTCREAT, "%s", outPath);
+	}
 }
 
-static enum Filter parseFilter(const char *s) {
-	switch (s[0]) {
+static enum Filter parseFilter(const char *str) {
+	switch (str[0]) {
 		case 'N': case 'n': return None;
 		case 'S': case 's': return Sub;
 		case 'U': case 'u': return Up;
 		case 'A': case 'a': return Average;
 		case 'P': case 'p': return Paeth;
-		default: errx(EX_USAGE, "invalid filter type %s", s);
+		default: errx(EX_USAGE, "invalid filter type %s", str);
 	}
 }
 
-static uint8_t parseFilters(enum Filter *filters, const char *s) {
-	uint8_t len = 0;
-	do {
-		filters[len++] = parseFilter(s);
-		s = strchr(s, ',');
-	} while (s++);
+static size_t parseFilters(enum Filter *filters, char *str) {
+	size_t len = 0;
+	while (str) {
+		char *filt = strsep(&str, ",");
+		filters[len++] = parseFilter(filt);
+	}
 	return len;
 }
 
 int main(int argc, char *argv[]) {
 	bool stdio = false;
-	char *output = NULL;
+	char *outPath = NULL;
 
-	int opt;
-	while (0 < (opt = getopt(argc, argv, "a:cd:fimo:prxy"))) {
+	for (int opt; 0 < (opt = getopt(argc, argv, "a:cd:fimo:prxy"));) {
 		switch (opt) {
-			break; case 'a':
-				options.applyFilter = parseFilters(options.applyFilters, optarg);
+			break; case 'a': applyFilter = parseFilters(applyFilters, optarg);
 			break; case 'c': stdio = true;
-			break; case 'd':
-				options.declareFilter = parseFilters(options.declareFilters, optarg);
-			break; case 'f': options.filt = true;
-			break; case 'i': options.invert = true;
-			break; case 'm': options.mirror = true;
-			break; case 'o': output = optarg;
-			break; case 'p': options.brokenPaeth = true;
-			break; case 'r': options.recon = true;
-			break; case 'x': options.zeroX = true;
-			break; case 'y': options.zeroY = true;
-			break; default: return EX_USAGE;
+			break; case 'd': declFilter = parseFilters(declFilters, optarg);
+			break; case 'f': reconFilter = true;
+			break; case 'i': invertData = true;
+			break; case 'm': mirrorData = true;
+			break; case 'o': outPath = optarg;
+			break; case 'p': brokenPaeth = true;
+			break; case 'r': filterRecon = true;
+			break; case 'x': zeroX = true;
+			break; case 'y': zeroY = true;
+			break; default:  return EX_USAGE;
 		}
 	}
 
-	if (argc - optind == 1 && (output || stdio)) {
-		glitch(argv[optind], output);
-	} else if (optind < argc) {
+	if (optind < argc) {
 		for (int i = optind; i < argc; ++i) {
-			glitch(argv[i], argv[i]);
+			glitch(argv[i], (stdio ? NULL : outPath ? outPath : argv[i]));
 		}
 	} else {
-		glitch(NULL, output);
+		glitch(NULL, outPath);
 	}
-
-	return EX_OK;
 }
diff --git a/bin/hilex.c b/bin/hilex.c
index 0bcc5c7f..7d7b3f2d 100644
--- a/bin/hilex.c
+++ b/bin/hilex.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -53,7 +53,7 @@ static const struct {
 	{ &LexC, "c", "[.][chlmy]$", NULL },
 	{ &LexMake, "make", "[.](mk|am)$|^Makefile$", NULL },
 	{ &LexMdoc, "mdoc", "[.][1-9]$", "^[.]Dd" },
-	{ &LexSh, "sh", "[.]sh$|^[.](profile|shrc)$", "^#![ ]?/bin/sh" },
+	{ &LexSh, "sh", "[.]sh$|^[.](profile|shrc)$", "^#![ ]?/bin/k?sh" },
 	{ &LexText, "text", "[.]txt$", NULL },
 };
 
diff --git a/bin/hilex.h b/bin/hilex.h
index 882b5f95..b57fc8cc 100644
--- a/bin/hilex.h
+++ b/bin/hilex.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/make.l b/bin/make.l
index ae1be2f5..6296716d 100644
--- a/bin/make.l
+++ b/bin/make.l
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/man1/up.1 b/bin/man1/up.1
index 2240b99a..aece79bd 100644
--- a/bin/man1/up.1
+++ b/bin/man1/up.1
@@ -1,4 +1,4 @@
-.Dd June 21, 2021
+.Dd July 26, 2022
 .Dt UP 1
 .Os
 .
@@ -8,15 +8,9 @@
 .
 .Sh SYNOPSIS
 .Nm
-.Op Fl h
-.Op Ar file
-.
-.Nm
-.Fl c | t
-.Ar command
-.
-.Nm
-.Fl s
+.Op Fl c | h | s | t
+.Op Fl w Ar warn
+.Op Ar file | command
 .
 .Sh DESCRIPTION
 .Nm
@@ -65,6 +59,10 @@ Run a command with
 and
 .Xr shotty 1
 to produce an HTML file for upload.
+.It Fl w Ar warn
+Create an HTML redirect with
+.Ar warn
+in its title.
 .El
 .
 .Pp
diff --git a/bin/man1/when.1 b/bin/man1/when.1
index 0b473573..3f2735f7 100644
--- a/bin/man1/when.1
+++ b/bin/man1/when.1
@@ -1,4 +1,4 @@
-.Dd July 24, 2019
+.Dd September 19, 2022
 .Dt WHEN 1
 .Os
 .
@@ -9,6 +9,8 @@
 .Sh SYNOPSIS
 .Nm
 .Op Ar expr
+.Nm
+.Cm -
 .
 .Sh DESCRIPTION
 .Nm
@@ -18,24 +20,32 @@ If no
 is given,
 expressions are read
 from standard input.
+If
+.Cm -
+is given,
+the intervals between each named date
+and today are printed.
 .
 .Pp
 The grammar is as follows:
 .Bl -tag -width Ds
 .It Sy \&.
 Today's date.
+The empty expression is equivalent.
+.
+.It Ar name Op Sy = Ar date
+A named date.
+Names are alphanumeric including underscores.
 .
 .It Ar month Ar date Op Ar year
 A full date,
 or a date in the current year.
-.Ar month
-must be at least three letters.
+Months can be abbreviated to three letters.
 .
 .It Ar day
 A day of the week
 in the current week.
-.Ar day
-must be at least three letters.
+Days can be abbreviated to three letters.
 .
 .It Sy < Ar date
 The date one week before.
@@ -65,6 +75,14 @@ A number of months.
 A number of years.
 .El
 .
+.Sh FILES
+The file
+.Pa $XDG_CONFIG_HOME/when/dates
+or
+.Pa ~/.config/when/dates
+is read before any other expressions,
+if it exists.
+.
 .Sh EXAMPLES
 .Bl -tag -width "Dec 25 - ."
 .It Ic Dec 25 - \&.
@@ -74,3 +92,9 @@ The date next Friday.
 .It Ic \&. + 2w
 Your last day at work.
 .El
+.Pp
+Checking a milestone:
+.Bd -literal -offset indent
+$ echo 'hrt = oct 15 2021' >> ~/.config/when/dates
+$ when -hrt
+.Ed
diff --git a/bin/mdoc.l b/bin/mdoc.l
index f29b6ceb..b6deacbe 100644
--- a/bin/mdoc.l
+++ b/bin/mdoc.l
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/modem.c b/bin/modem.c
index 2133ae08..4392e071 100644
--- a/bin/modem.c
+++ b/bin/modem.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2018  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2018  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/mtags.c b/bin/mtags.c
index 11cd9c8a..5c1a057e 100644
--- a/bin/mtags.c
+++ b/bin/mtags.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/nudge.c b/bin/nudge.c
index 108d2459..8ae916eb 100644
--- a/bin/nudge.c
+++ b/bin/nudge.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/order.y b/bin/order.y
index c2e87971..b3cbf2df 100644
--- a/bin/order.y
+++ b/bin/order.y
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2019  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/pbd.c b/bin/pbd.c
index 2fd401ae..9f47b63e 100644
--- a/bin/pbd.c
+++ b/bin/pbd.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2017  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2017  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/png.h b/bin/png.h
index 15b6d13d..0df4699b 100644
--- a/bin/png.h
+++ b/bin/png.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2018  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2018  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/pngo.c b/bin/pngo.c
index ba90889a..eb51ccc2 100644
--- a/bin/pngo.c
+++ b/bin/pngo.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2018, 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2018, 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/psf2png.c b/bin/psf2png.c
index 1aaa8635..c36238a0 100644
--- a/bin/psf2png.c
+++ b/bin/psf2png.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2018  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2018  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/ptee.c b/bin/ptee.c
index 8374bd8f..52350a21 100644
--- a/bin/ptee.c
+++ b/bin/ptee.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2019  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/quick.c b/bin/quick.c
index edf35a3b..d814873d 100644
--- a/bin/quick.c
+++ b/bin/quick.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/relay.c b/bin/relay.c
index aa7913c9..fd799462 100644
--- a/bin/relay.c
+++ b/bin/relay.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2019  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/scheme.c b/bin/scheme.c
index 33fd8b60..2bae8f82 100644
--- a/bin/scheme.c
+++ b/bin/scheme.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2018, 2019  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2018, 2019  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/sh.l b/bin/sh.l
index 9664b009..8f0f7723 100644
--- a/bin/sh.l
+++ b/bin/sh.l
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -36,7 +36,7 @@ static int pop(void) {
 }
 %}
 
-%s Param Command Arith Backtick
+%s Param Command Arith Backtick Subshell
 %x DQuote HereDocDel HereDoc HereDocLit
 
 word [[:alnum:]_.-]+
@@ -51,7 +51,7 @@ reserved [!{}]|else|do|elif|for|done|fi|then|until|while|if|case|esac
 
 "\\". { return Escape; }
 
-<INITIAL,DQuote,HereDoc,Param,Command,Arith>{
+<INITIAL,DQuote,HereDoc,Param,Command,Arith,Subshell>{
 	"$"[*@#?$!0-9-] |
 	"$"[_[:alpha:][_[:alnum:]]* |
 	"${"[#]?{param}"}" {
@@ -73,6 +73,10 @@ reserved [!{}]|else|do|elif|for|done|fi|then|until|while|if|case|esac
 		BEGIN(push(Backtick));
 		return Subst;
 	}
+	"(" {
+		BEGIN(push(Subshell));
+		return Normal;
+	}
 }
 <Param>"}" |
 <Command>")" |
@@ -81,6 +85,10 @@ reserved [!{}]|else|do|elif|for|done|fi|then|until|while|if|case|esac
 	BEGIN(pop());
 	return Subst;
 }
+<Subshell>")" {
+	BEGIN(pop());
+	return Normal;
+}
 
 "\n" {
 	first = true;
diff --git a/bin/shotty.l b/bin/shotty.l
index ff78d241..dcac43ec 100644
--- a/bin/shotty.l
+++ b/bin/shotty.l
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019, 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2019, 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -181,13 +181,13 @@ ESC \x1B
 		| (wchar_t)(yytext[1] & 0x3F);
 	return Data;
 }
-[\xE0-\xEF][\x80-\xBF]{2} {
+[\xE0-\xEF]([\x80-\xBF]{2}) {
 	ch = (wchar_t)(yytext[0] & 0x0F) << 12
 		| (wchar_t)(yytext[1] & 0x3F) << 6
 		| (wchar_t)(yytext[2] & 0x3F);
 	return Data;
 }
-[\xF0-\xF7][\x80-\xBF]{3} {
+[\xF0-\xF7]([\x80-\xBF]{3}) {
 	ch = (wchar_t)(yytext[0] & 0x07) << 18
 		| (wchar_t)(yytext[1] & 0x3F) << 12
 		| (wchar_t)(yytext[2] & 0x3F) << 6
diff --git a/bin/title.c b/bin/title.c
index 82f89d95..47ff720a 100644
--- a/bin/title.c
+++ b/bin/title.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2019  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/bin/up.sh b/bin/up.sh
index cebc29c4..6305b1ee 100644
--- a/bin/up.sh
+++ b/bin/up.sh
@@ -4,21 +4,32 @@ set -eu
 readonly Host='temp.causal.agency'
 readonly Root='/var/www'
 
+temp=
+temp() {
+	temp=$(mktemp -d)
+	trap 'rm -r "$temp"' EXIT
+}
+
+warn=
 upload() {
 	src=$1
 	ext=${src##*.}
-	ts=$(date +'%s')
-	rand=$(openssl rand -hex 4)
-	url=$(printf '%s/%x%s.%s' "$Host" "$ts" "$rand" "$ext")
+	name=$(printf '%x%s' "$(date +%s)" "$(openssl rand -hex 4)")
+	url="${Host}/${name}.${ext}"
 	scp -q "$src" "${Host}:${Root}/${url}"
+	if test -n "$warn"; then
+		test -n "$temp" || temp
+		cat >"${temp}/warn.html" <<-EOF
+			<!DOCTYPE html>
+			<title>${warn}</title>
+			<meta http-equiv="refresh" content="0;url=${name}.${ext}">
+		EOF
+		url="${Host}/${name}.html"
+		scp -q "${temp}/warn.html" "${Host}:${Root}/${url}"
+	fi
 	echo "https://${url}"
 }
 
-temp() {
-	temp=$(mktemp -d)
-	trap 'rm -r "$temp"' EXIT
-}
-
 uploadText() {
 	temp
 	cat >"${temp}/input.txt"
@@ -64,12 +75,13 @@ uploadTerminal() {
 	upload "${temp}/term.html"
 }
 
-while getopts 'chst' opt; do
+while getopts 'chstw:' opt; do
 	case $opt in
 		(c) fn=uploadCommand;;
 		(h) fn=uploadHilex;;
 		(s) fn=uploadScreen;;
 		(t) fn=uploadTerminal;;
+		(w) warn=$OPTARG;;
 		(?) exit 1;;
 	esac
 done
diff --git a/bin/when.y b/bin/when.y
index 64859da7..46651ebb 100644
--- a/bin/when.y
+++ b/bin/when.y
@@ -1,4 +1,4 @@
-/* Copyright (C) 2019  June McEnroe <june@causal.agency>
+/* Copyright (C) 2019, 2022  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -18,6 +18,9 @@
 
 #include <ctype.h>
 #include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <strings.h>
@@ -30,12 +33,13 @@ static int yylex(void);
 #define YYSTYPE struct tm
 
 static const char *Days[7] = {
-	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
+	"Sunday", "Monday", "Tuesday", "Wednesday",
+	"Thursday", "Friday", "Saturday",
 };
 
 static const char *Months[12] = {
-	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
-	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+	"January", "February", "March", "April", "May", "June",
+	"July", "August", "September", "October", "November", "December",
 };
 
 static const struct tm Week = { .tm_mday = 7 };
@@ -117,7 +121,10 @@ static struct tm dateDiff(struct tm a, struct tm b) {
 		.tm_mon = a.tm_mon - b.tm_mon,
 		.tm_mday = a.tm_mday - b.tm_mday,
 	};
-	if (a.tm_mon < b.tm_mon) {
+	if (
+		a.tm_mon < b.tm_mon ||
+		(a.tm_mon == b.tm_mon && a.tm_mday < b.tm_mday)
+	) {
 		diff.tm_year--;
 		diff.tm_mon += 12;
 	}
@@ -130,15 +137,50 @@ static struct tm dateDiff(struct tm a, struct tm b) {
 	return diff;
 }
 
+static struct {
+	size_t cap, len;
+	struct tm *ptr;
+} dates;
+
+static struct tm getDate(const char *name) {
+	for (size_t i = 0; i < dates.len; ++i) {
+		if (!strcmp(dates.ptr[i].tm_zone, name)) return dates.ptr[i];
+	}
+	return (struct tm) {0};
+}
+
+static void setDate(const char *name, struct tm date) {
+	for (size_t i = 0; i < dates.len; ++i) {
+		if (strcmp(dates.ptr[i].tm_zone, name)) continue;
+		char *tm_zone = dates.ptr[i].tm_zone;
+		dates.ptr[i] = date;
+		dates.ptr[i].tm_zone = tm_zone;
+		return;
+	}
+	if (dates.len == dates.cap) {
+		dates.cap = (dates.cap ? dates.cap * 2 : 8);
+		dates.ptr = realloc(dates.ptr, sizeof(*dates.ptr) * dates.cap);
+		if (!dates.ptr) err(EX_OSERR, "realloc");
+	}
+	dates.ptr[dates.len] = date;
+	dates.ptr[dates.len].tm_zone = strdup(name);
+	if (!dates.ptr[dates.len].tm_zone) err(EX_OSERR, "strdup");
+	dates.len++;
+}
+
+static bool silent;
+
 static void printDate(struct tm date) {
+	if (silent) return;
 	printf(
-		"%s %s %d %d\n",
+		"%.3s %.3s %d %d\n",
 		Days[date.tm_wday], Months[date.tm_mon],
 		date.tm_mday, 1900 + date.tm_year
 	);
 }
 
 static void printScalar(struct tm scalar) {
+	if (silent) return;
 	if (scalar.tm_year) printf("%dy ", scalar.tm_year);
 	if (scalar.tm_mon) printf("%dm ", scalar.tm_mon);
 	if (scalar.tm_mday % 7) {
@@ -161,9 +203,9 @@ static void printScalar(struct tm scalar) {
 
 %}
 
-%token Number Month Day
+%token Name Number Month Day
 %left '+' '-'
-%right '<' '>'
+%right '=' '<' '>'
 
 %%
 
@@ -174,6 +216,8 @@ expr:
 
 date:
 	dateLit
+	| Name { $$ = getDate($1.tm_zone); free($1.tm_zone); }
+	| Name '=' date { setDate($1.tm_zone, $3); free($1.tm_zone); $$ = $3; }
 	| '(' date ')' { $$ = $2; }
 	| '<' date { $$ = dateSub($2, Week); }
 	| '>' date { $$ = dateAdd($2, Week); }
@@ -223,35 +267,77 @@ static int yylex(void) {
 		return Number;
 	}
 
-	for (int i = 0; i < 7; ++i) {
-		if (strncasecmp(input, Days[i], 3)) continue;
-		while (isalpha(*input)) input++;
-		yylval.tm_wday = i;
-		return Day;
+	size_t len;
+	for (len = 0; isalnum(input[len]) || input[len] == '_'; ++len);
+
+	if (len >= 3) {
+		for (int i = 0; i < 7; ++i) {
+			if (strncasecmp(input, Days[i], len)) continue;
+			yylval.tm_wday = i;
+			input += len;
+			return Day;
+		}
+
+		for (int i = 0; i < 12; ++i) {
+			if (strncasecmp(input, Months[i], len)) continue;
+			yylval.tm_mon = i;
+			input += len;
+			return Month;
+		}
 	}
 
-	for (int i = 0; i < 12; ++i) {
-		if (strncasecmp(input, Months[i], 3)) continue;
-		while (isalpha(*input)) input++;
-		yylval.tm_mon = i;
-		return Month;
+	if (len && (len != 1 || !strchr("dwmy", *input))) {
+		yylval.tm_zone = strndup(input, len);
+		if (!yylval.tm_zone) err(EX_OSERR, "strndup");
+		input += len;
+		return Name;
 	}
 
 	return *input++;
 }
 
 int main(int argc, char *argv[]) {
+	size_t cap = 0;
+	char *line = NULL;
+
+	char path[PATH_MAX];
+	const char *configHome = getenv("XDG_CONFIG_HOME");
+	if (configHome) {
+		snprintf(path, sizeof(path), "%s/when/dates", configHome);
+	} else {
+		snprintf(path, sizeof(path), "%s/.config/when/dates", getenv("HOME"));
+	}
+
+	FILE *file = fopen(path, "r");
+	if (file) {
+		silent = true;
+		while (0 < getline(&line, &cap, file)) {
+			input = line;
+			yyparse();
+		}
+		fclose(file);
+		silent = false;
+	} else if (errno != ENOENT) {
+		err(EX_CONFIG, "%s", path);
+	}
+
 	if (argc > 1) {
-		input = argv[1];
-		return yyparse();
+		if (strcmp(argv[1], "-")) {
+			input = argv[1];
+			return yyparse();
+		} else {
+			for (size_t i = 0; i < dates.len; ++i) {
+				printf("%s: ", dates.ptr[i].tm_zone);
+				printScalar(dateDiff(today(), dates.ptr[i]));
+			}
+			return EX_OK;
+		}
 	}
 
 	struct tm date = today();
 	printDate(date);
 	printf("\n");
 
-	char *line = NULL;
-	size_t cap = 0;
 	while (0 < getline(&line, &cap, stdin)) {
 		if (line[0] == '\n') continue;
 
diff --git a/bin/xx.c b/bin/xx.c
index 6d04f2f5..39d7ec07 100644
--- a/bin/xx.c
+++ b/bin/xx.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2017  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2017  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/doc/pdf/.gitignore b/doc/pdf/.gitignore
deleted file mode 100644
index a1363379..00000000
--- a/doc/pdf/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.pdf
diff --git a/doc/pdf/Makefile b/doc/pdf/Makefile
deleted file mode 100644
index 7afbdcf2..00000000
--- a/doc/pdf/Makefile
+++ /dev/null
@@ -1,31 +0,0 @@
-PDFS += abi.pdf
-PDFS += c11.pdf
-PDFS += elf.pdf
-PDFS += intel-64-opt.pdf
-PDFS += intel-64-sdm-vol-1.pdf
-PDFS += intel-64-sdm-vol-2.pdf
-PDFS += intel-64-sdm-vol-3.pdf
-PDFS += intel-64-sdm-vol-4.pdf
-PDFS += multiboot.pdf
-
-ELF = https://refspecs.linuxbase.org/elf
-INTEL = https://software.intel.com/sites/default/files/managed
-
-URL.abi.pdf = ${ELF}/x86_64-abi-0.99.pdf
-URL.c11.pdf = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
-URL.elf.pdf = ${ELF}/elf.pdf
-URL.intel-64-opt.pdf = ${INTEL}/9e/bc/64-ia-32-architectures-optimization-manual.pdf
-URL.intel-64-sdm-vol-1.pdf = ${INTEL}/a4/60/253665-sdm-vol-1.pdf
-URL.intel-64-sdm-vol-2.pdf = ${INTEL}/a4/60/325383-sdm-vol-2abcd.pdf
-URL.intel-64-sdm-vol-3.pdf = ${INTEL}/a4/60/325384-sdm-vol-3abcd.pdf
-URL.intel-64-sdm-vol-4.pdf = ${INTEL}/22/0d/335592-sdm-vol-4.pdf
-URL.multiboot.pdf = https://www.gnu.org/software/grub/manual/multiboot/multiboot.pdf
-
-all: ${PDFS}
-
-${PDFS}:
-	curl -Lf -o $@ ${URL.$@}
-	chmod a-w $@
-
-clean:
-	rm -f ${PDFS}
diff --git a/doc/zlib/adler32.3 b/doc/zlib/adler32.3
index d713d952..c58a34e7 100644
--- a/doc/zlib/adler32.3
+++ b/doc/zlib/adler32.3
@@ -58,7 +58,7 @@ if (adler != original_adler) error();
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/adler32_combine.3 b/doc/zlib/adler32_combine.3
index 861f235b..55e801e9 100644
--- a/doc/zlib/adler32_combine.3
+++ b/doc/zlib/adler32_combine.3
@@ -56,7 +56,7 @@ the result has no meaning or utility.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/compress.3 b/doc/zlib/compress.3
index 22b229ee..16445e2f 100644
--- a/doc/zlib/compress.3
+++ b/doc/zlib/compress.3
@@ -77,7 +77,7 @@ parameter is invalid.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/compressBound.3 b/doc/zlib/compressBound.3
index 5800e2ba..d61891eb 100644
--- a/doc/zlib/compressBound.3
+++ b/doc/zlib/compressBound.3
@@ -37,7 +37,7 @@ call to allocate the destination buffer.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/crc32.3 b/doc/zlib/crc32.3
index 3c9cc8c4..a42df2af 100644
--- a/doc/zlib/crc32.3
+++ b/doc/zlib/crc32.3
@@ -59,7 +59,7 @@ if (crc != original_crc) error();
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/crc32_combine.3 b/doc/zlib/crc32_combine.3
index 2f79f623..b25da679 100644
--- a/doc/zlib/crc32_combine.3
+++ b/doc/zlib/crc32_combine.3
@@ -47,7 +47,7 @@ and
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflate.3 b/doc/zlib/deflate.3
index 7df313ee..be182d96 100644
--- a/doc/zlib/deflate.3
+++ b/doc/zlib/deflate.3
@@ -363,7 +363,7 @@ to continue compressing.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateBound.3 b/doc/zlib/deflateBound.3
index 63e80246..be97494c 100644
--- a/doc/zlib/deflateBound.3
+++ b/doc/zlib/deflateBound.3
@@ -64,7 +64,7 @@ are used.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateCopy.3 b/doc/zlib/deflateCopy.3
index f30d6301..f20e0a9e 100644
--- a/doc/zlib/deflateCopy.3
+++ b/doc/zlib/deflateCopy.3
@@ -59,7 +59,7 @@ in both source and destination.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateEnd.3 b/doc/zlib/deflateEnd.3
index e24259a3..0abaabe1 100644
--- a/doc/zlib/deflateEnd.3
+++ b/doc/zlib/deflateEnd.3
@@ -43,7 +43,7 @@ may be set but then points to a static string
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateGetDictionary.3 b/doc/zlib/deflateGetDictionary.3
index 403f6d10..b9dabfe2 100644
--- a/doc/zlib/deflateGetDictionary.3
+++ b/doc/zlib/deflateGetDictionary.3
@@ -72,7 +72,7 @@ if the stream state is inconsistent.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateInit.3 b/doc/zlib/deflateInit.3
index a893dd91..52179883 100644
--- a/doc/zlib/deflateInit.3
+++ b/doc/zlib/deflateInit.3
@@ -171,7 +171,7 @@ if there is no error message.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateInit2.3 b/doc/zlib/deflateInit2.3
index 6a581ef8..a7d68b99 100644
--- a/doc/zlib/deflateInit2.3
+++ b/doc/zlib/deflateInit2.3
@@ -220,7 +220,7 @@ is set to null if there is no error message.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateParams.3 b/doc/zlib/deflateParams.3
index 8e770d4e..9eb5ca16 100644
--- a/doc/zlib/deflateParams.3
+++ b/doc/zlib/deflateParams.3
@@ -116,7 +116,7 @@ with more output space.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflatePending.3 b/doc/zlib/deflatePending.3
index 1ce40fc2..35fa6d38 100644
--- a/doc/zlib/deflatePending.3
+++ b/doc/zlib/deflatePending.3
@@ -49,7 +49,7 @@ if the source stream state was inconsistent.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflatePrime.3 b/doc/zlib/deflatePrime.3
index 639e715a..10a2924b 100644
--- a/doc/zlib/deflatePrime.3
+++ b/doc/zlib/deflatePrime.3
@@ -57,7 +57,7 @@ if the source stream state was inconsistent.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateReset.3 b/doc/zlib/deflateReset.3
index 7309ac15..1a18c507 100644
--- a/doc/zlib/deflateReset.3
+++ b/doc/zlib/deflateReset.3
@@ -50,7 +50,7 @@ being
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateSetDictionary.3 b/doc/zlib/deflateSetDictionary.3
index c2c9d7c2..3e66d3cf 100644
--- a/doc/zlib/deflateSetDictionary.3
+++ b/doc/zlib/deflateSetDictionary.3
@@ -135,7 +135,7 @@ for raw deflate
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateSetHeader.3 b/doc/zlib/deflateSetHeader.3
index 6fec645c..03d4f4d3 100644
--- a/doc/zlib/deflateSetHeader.3
+++ b/doc/zlib/deflateSetHeader.3
@@ -173,7 +173,7 @@ if the source stream state was inconsistent.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/deflateTune.3 b/doc/zlib/deflateTune.3
index 7269dec0..ea4dd915 100644
--- a/doc/zlib/deflateTune.3
+++ b/doc/zlib/deflateTune.3
@@ -63,7 +63,7 @@ for an invalid deflate stream.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzbuffer.3 b/doc/zlib/gzbuffer.3
index de7c706a..92438c48 100644
--- a/doc/zlib/gzbuffer.3
+++ b/doc/zlib/gzbuffer.3
@@ -52,7 +52,7 @@ such as being called too late.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzclose.3 b/doc/zlib/gzclose.3
index 77eae11e..bfcc583e 100644
--- a/doc/zlib/gzclose.3
+++ b/doc/zlib/gzclose.3
@@ -90,7 +90,7 @@ on success.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzdirect.3 b/doc/zlib/gzdirect.3
index 8fa26aae..640fd4c5 100644
--- a/doc/zlib/gzdirect.3
+++ b/doc/zlib/gzdirect.3
@@ -78,7 +78,7 @@ which may not be desired.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzeof.3 b/doc/zlib/gzeof.3
index 26c415fe..ba823aa6 100644
--- a/doc/zlib/gzeof.3
+++ b/doc/zlib/gzeof.3
@@ -56,7 +56,7 @@ end of file was detected.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzerror.3 b/doc/zlib/gzerror.3
index 13dcddd4..a9e175fc 100644
--- a/doc/zlib/gzerror.3
+++ b/doc/zlib/gzerror.3
@@ -68,7 +68,7 @@ that is being written concurrently.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzflush.3 b/doc/zlib/gzflush.3
index b93c03e7..476f7c09 100644
--- a/doc/zlib/gzflush.3
+++ b/doc/zlib/gzflush.3
@@ -66,7 +66,7 @@ see function
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzfread.3 b/doc/zlib/gzfread.3
index 66231cc3..7bf57fc5 100644
--- a/doc/zlib/gzfread.3
+++ b/doc/zlib/gzfread.3
@@ -100,7 +100,7 @@ and the error state is set to
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzfwrite.3 b/doc/zlib/gzfwrite.3
index 38383a33..6835db3a 100644
--- a/doc/zlib/gzfwrite.3
+++ b/doc/zlib/gzfwrite.3
@@ -68,7 +68,7 @@ and the error state is set to
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzgetc.3 b/doc/zlib/gzgetc.3
index 93a90edd..db9143ec 100644
--- a/doc/zlib/gzgetc.3
+++ b/doc/zlib/gzgetc.3
@@ -48,7 +48,7 @@ or error.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzgets.3 b/doc/zlib/gzgets.3
index 2a329e9e..c1435b39 100644
--- a/doc/zlib/gzgets.3
+++ b/doc/zlib/gzgets.3
@@ -60,7 +60,7 @@ are indeterminate.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzoffset.3 b/doc/zlib/gzoffset.3
index cbb78a77..b03c557e 100644
--- a/doc/zlib/gzoffset.3
+++ b/doc/zlib/gzoffset.3
@@ -44,7 +44,7 @@ returns -1.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzopen.3 b/doc/zlib/gzopen.3
index e3cb4cbd..9da647e1 100644
--- a/doc/zlib/gzopen.3
+++ b/doc/zlib/gzopen.3
@@ -254,7 +254,7 @@ for the routine
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzprintf.3 b/doc/zlib/gzprintf.3
index 26961f34..a2a241a2 100644
--- a/doc/zlib/gzprintf.3
+++ b/doc/zlib/gzprintf.3
@@ -64,7 +64,7 @@ This can be determined using
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzputc.3 b/doc/zlib/gzputc.3
index 161e5631..66897b5e 100644
--- a/doc/zlib/gzputc.3
+++ b/doc/zlib/gzputc.3
@@ -36,7 +36,7 @@ or -1 in case of error.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzputs.3 b/doc/zlib/gzputs.3
index f5d1fd84..71833ab2 100644
--- a/doc/zlib/gzputs.3
+++ b/doc/zlib/gzputs.3
@@ -34,7 +34,7 @@ or -1 in case of error.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzread.3 b/doc/zlib/gzread.3
index 84439eaa..4118eca7 100644
--- a/doc/zlib/gzread.3
+++ b/doc/zlib/gzread.3
@@ -108,7 +108,7 @@ and the error state is set to
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzseek.3 b/doc/zlib/gzseek.3
index cd85fd4c..a14b2db6 100644
--- a/doc/zlib/gzseek.3
+++ b/doc/zlib/gzseek.3
@@ -101,7 +101,7 @@ would be before the current position.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzsetparams.3 b/doc/zlib/gzsetparams.3
index ff544d23..f6ff9ed7 100644
--- a/doc/zlib/gzsetparams.3
+++ b/doc/zlib/gzsetparams.3
@@ -44,7 +44,7 @@ if there is a memory allocation error.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzungetc.3 b/doc/zlib/gzungetc.3
index 90cdafc7..fbe9371c 100644
--- a/doc/zlib/gzungetc.3
+++ b/doc/zlib/gzungetc.3
@@ -60,7 +60,7 @@ or -1 on failure.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/gzwrite.3 b/doc/zlib/gzwrite.3
index 606d89f4..73407ef5 100644
--- a/doc/zlib/gzwrite.3
+++ b/doc/zlib/gzwrite.3
@@ -32,7 +32,7 @@ or 0 in case of error.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflate.3 b/doc/zlib/inflate.3
index ca90c270..255e0f84 100644
--- a/doc/zlib/inflate.3
+++ b/doc/zlib/inflate.3
@@ -391,7 +391,7 @@ is to be attempted.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateBack.3 b/doc/zlib/inflateBack.3
index 59d5f8cb..fcda7452 100644
--- a/doc/zlib/inflateBack.3
+++ b/doc/zlib/inflateBack.3
@@ -278,7 +278,7 @@ cannot return
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateBackEnd.3 b/doc/zlib/inflateBackEnd.3
index eeb88636..39fbea8f 100644
--- a/doc/zlib/inflateBackEnd.3
+++ b/doc/zlib/inflateBackEnd.3
@@ -36,7 +36,7 @@ if the stream state was inconsistent.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateBackInit.3 b/doc/zlib/inflateBackInit.3
index 483edda5..d029542e 100644
--- a/doc/zlib/inflateBackInit.3
+++ b/doc/zlib/inflateBackInit.3
@@ -77,7 +77,7 @@ does not match the version of the header file.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateCopy.3 b/doc/zlib/inflateCopy.3
index 53b30edf..167b879b 100644
--- a/doc/zlib/inflateCopy.3
+++ b/doc/zlib/inflateCopy.3
@@ -52,7 +52,7 @@ in both source and destination.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateEnd.3 b/doc/zlib/inflateEnd.3
index 9b18226b..54945b50 100644
--- a/doc/zlib/inflateEnd.3
+++ b/doc/zlib/inflateEnd.3
@@ -37,7 +37,7 @@ if the stream state was inconsistent.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateGetDictionary.3 b/doc/zlib/inflateGetDictionary.3
index e70ee736..9290850c 100644
--- a/doc/zlib/inflateGetDictionary.3
+++ b/doc/zlib/inflateGetDictionary.3
@@ -62,7 +62,7 @@ if the stream state is inconsistent.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateGetHeader.3 b/doc/zlib/inflateGetHeader.3
index f77670f2..57f7c443 100644
--- a/doc/zlib/inflateGetHeader.3
+++ b/doc/zlib/inflateGetHeader.3
@@ -163,7 +163,7 @@ if the source stream state was inconsistent.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateInit.3 b/doc/zlib/inflateInit.3
index 186b058a..66a1d4f7 100644
--- a/doc/zlib/inflateInit.3
+++ b/doc/zlib/inflateInit.3
@@ -94,7 +94,7 @@ is set to null if there is no error message.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateInit2.3 b/doc/zlib/inflateInit2.3
index a630f12a..5b8b49ac 100644
--- a/doc/zlib/inflateInit2.3
+++ b/doc/zlib/inflateInit2.3
@@ -174,7 +174,7 @@ is set to null if there is no error message.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateMark.3 b/doc/zlib/inflateMark.3
index 2d15993d..90e2ee0b 100644
--- a/doc/zlib/inflateMark.3
+++ b/doc/zlib/inflateMark.3
@@ -81,7 +81,7 @@ or -65536 if the provided source stream state was inconsistent.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflatePrime.3 b/doc/zlib/inflatePrime.3
index c89dc2c5..66953665 100644
--- a/doc/zlib/inflatePrime.3
+++ b/doc/zlib/inflatePrime.3
@@ -66,7 +66,7 @@ if the source stream state was inconsistent.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateReset.3 b/doc/zlib/inflateReset.3
index a8d2e219..53c4ffe2 100644
--- a/doc/zlib/inflateReset.3
+++ b/doc/zlib/inflateReset.3
@@ -74,7 +74,7 @@ parameter is invalid.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateSetDictionary.3 b/doc/zlib/inflateSetDictionary.3
index 0e3c60c7..291c97e8 100644
--- a/doc/zlib/inflateSetDictionary.3
+++ b/doc/zlib/inflateSetDictionary.3
@@ -78,7 +78,7 @@ doesn't match the expected one
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/inflateSync.3 b/doc/zlib/inflateSync.3
index 35264ddd..56d3ca28 100644
--- a/doc/zlib/inflateSync.3
+++ b/doc/zlib/inflateSync.3
@@ -65,7 +65,7 @@ until success or the end of the input data.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/uncompress.3 b/doc/zlib/uncompress.3
index d951da9b..1047ad91 100644
--- a/doc/zlib/uncompress.3
+++ b/doc/zlib/uncompress.3
@@ -85,7 +85,7 @@ with the uncompressed data up to that point.
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/zlibCompileFlags.3 b/doc/zlib/zlibCompileFlags.3
index 465195c2..59cc24a8 100644
--- a/doc/zlib/zlibCompileFlags.3
+++ b/doc/zlib/zlibCompileFlags.3
@@ -156,7 +156,7 @@ not secure!
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/doc/zlib/zlibVersion.3 b/doc/zlib/zlibVersion.3
index 35a9854b..04377527 100644
--- a/doc/zlib/zlibVersion.3
+++ b/doc/zlib/zlibVersion.3
@@ -38,7 +38,7 @@ and
 This manual page was converted from
 .In zlib.h
 to mdoc format by
-.An C. McEnroe Aq Mt june@causal.agency .
+.An June McEnroe Aq Mt june@causal.agency .
 .
 .Sh AUTHORS
 .An Jean-loup Gailly Aq Mt jloup@gzip.org
diff --git a/gpl.c b/gpl.c
index 8634449b..8ff4916d 100644
--- a/gpl.c
+++ b/gpl.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2022  June McEnroe <june@causal.agency>
+/* Copyright (C) 2024  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,4 +17,3 @@
 #include <err.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sysexits.h>
diff --git a/home/.config/X/resources b/home/.config/X/resources
index cfedf60b..f4603cd9 100644
--- a/home/.config/X/resources
+++ b/home/.config/X/resources
@@ -26,7 +26,7 @@ XTerm*VT100*translations: #override \n\
 	<Btn4Down>: scroll-back(1,line,m) \n\
 	<Btn5Down>: scroll-forw(1,line,m)
 
-XTerm*faceName: Go Mono:size=11
+XTerm*faceName: Go Mono:size=12
 XTerm*internalBorder: 6
 XTerm*colorBDMode: true
 XTerm*scrollBar: false
diff --git a/home/.config/git/config b/home/.config/git/config
index 1a6d5ce1..c990de2c 100644
--- a/home/.config/git/config
+++ b/home/.config/git/config
@@ -2,6 +2,9 @@
 	name = June McEnroe
 	email = june@causal.agency
 
+[branch]
+	sort = committerdate
+
 [commit]
 	verbose = true
 
@@ -12,6 +15,9 @@
 [merge]
 	conflictStyle = diff3
 
+[push]
+	autoSetupRemote = true
+
 [pull]
 	rebase = true
 
@@ -21,5 +27,8 @@
 [pretty]
 	log = %Cred%h %Creset%s%C(yellow)%d %Cgreen(%ar) %Cblue<%aN>
 
+[alias]
+	forgive = blame
+
 [include]
 	path = ./private
diff --git a/home/.ssh/config b/home/.ssh/config
index b2035a81..f579ae9f 100644
--- a/home/.ssh/config
+++ b/home/.ssh/config
@@ -1,9 +1,10 @@
 IgnoreUnknown Include
 Include config_private
 
+AddKeysToAgent yes
 SendEnv LANG LC_*
 
-Host monday beastie puffy toaster tux progynova
+Host tuesday beastie puffy toaster tux progynova
 	HostName %h.local
 	ForwardAgent yes
 	RemoteForward 7062 127.0.0.1:7062
diff --git a/home/.xsession b/home/.xsession
index a51d52f2..1e05126c 100644
--- a/home/.xsession
+++ b/home/.xsession
@@ -4,8 +4,11 @@ export LC_CTYPE=en_US.UTF-8
 xset r rate 175 m 5/4 0
 xmodmap ~/.config/X/modmap
 xrdb -load ~/.config/X/resources
-xsetroot -bitmap /usr/X11R6/include/X11/bitmaps/xsnow \
-	-bg rgb:14/13/0E -fg rgb:7A/49/55 
+
+fg=998D6B
+command -v scheme && fg=$(scheme -p $(jot -r 1 1 8))
+xsetroot -bitmap /usr/X11R6/include/X11/bitmaps/escherknot \
+	-bg '#14130E' -fg "#${fg}"
 
 xterm -name clock -geometry 14x1-0+0 -sl 0 -e clock &
 exec cwm -c ~/.config/cwm/cwmrc
diff --git a/txt/books.txt b/txt/books.txt
index 2c424761..7ebae70c 100644
--- a/txt/books.txt
+++ b/txt/books.txt
@@ -1,5 +1,23 @@
+[ 2023 ]
+
+  7. ★★☆ Alix E. Harrow, Starling House
+  6. ★★☆ Alix E. Harrow, A Mirror Mended
+  5. ★★★ Alix E. Harrow, A Spindle Splintered
+  4. ★★☆ Alyson Greaves, The Sisters of Dorley (ch. 1-15)
+  3. ★☆☆ Nnedi Okorafor, Noor
+  2. ★★☆ Nnedi Okorafor, Remote Control
+  1. ★★☆ Becky Chambers, A Prayer for the Crown-Shy
+
 [ 2022 ]
 
+ 15. ★★★ Becky Chambers, The Long Way To a Small Angry Planet
+ 14. ★★★ ed. Tristan Taormino, Take Me There
+ 13. ★★★★ Becky Chambers, A Closed and Common Orbit
+ 12. ★★☆ Sybil Lamb, I've Got a Time Bomb
+ 11. ☆☆☆ Ruth Ozeki, The Book of Form and Emptiness
+ 10. ★☆☆ Sally Rooney, Conversations With Friends
+  9. ★☆☆ Sally Rooney, Normal People
+  8. ★★★ Andrea Stewart, The Bone Shard Emperor
   7. ★★☆ ed. Gwen Benaway, Maiden, Mother, Crone
   6. ★★★ Andrea Stewart, The Bone Shard Daughter
   5. ★☆☆ Madeline Miller, Circe
diff --git a/txt/shows.txt b/txt/shows.txt
index a2998e23..2abacf5b 100644
--- a/txt/shows.txt
+++ b/txt/shows.txt
@@ -1,3 +1,4 @@
+2022-12-18 (SAT) LINGUA IGNOTA
 2022-06-04 (MAI) Honeydrip, MAGELLA, BACKXWASH
 2020-01-23 (La Sala Rossa) Secondsight, BIG|BRAVE
 2019-12-10 (Casa del Popolo) meth, Street Sects
diff --git a/txt/tweets.txt b/txt/tweets.txt
deleted file mode 100644
index 240351ec..00000000
--- a/txt/tweets.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-triangle forum
-https://twitter.com/g0m/status/963890156477603841
-
-damn ya ass fat what's ya pronouns?
-https://twitter.com/msbigmilk/status/1291218055939448833
-
-afab
-https://twitter.com/ErisGael/status/1257524779046907904
-
-chaos emeralds
-https://twitter.com/ErisGael/status/1190147736244490240
-
-might as well salivate
-https://twitter.com/prawn_meat/status/857444039833944064
-
-notifications
-https://twitter.com/WTMMP/status/1259694226595610629
-
-all robot & computers
-https://twitter.com/cryptcrier/status/1238339418160680960
-
-the wet world
-https://twitter.com/TragicAllyHere/status/1137187876507140098
-
-a purchase?
-https://twitter.com/pant_leg/status/1027693563604230144
-
-influencer
-https://twitter.com/witchpuppy/status/974690828257054721
-
--Wint-conversion
-https://twitter.com/jckarter/status/967802665080995840
diff --git a/www/causal.agency/.gitignore b/www/causal.agency/.gitignore
index 7935a3c1..b00b1c3c 100644
--- a/www/causal.agency/.gitignore
+++ b/www/causal.agency/.gitignore
@@ -1,3 +1,4 @@
-*.html
+index.html
+leveler.html
 scheme.css
 scheme.png
diff --git a/www/causal.agency/Makefile b/www/causal.agency/Makefile
index 75849db0..8c74f8f1 100644
--- a/www/causal.agency/Makefile
+++ b/www/causal.agency/Makefile
@@ -1,11 +1,14 @@
 WEBROOT = /var/www/causal.agency
 
-FILES = index.html style.css scheme.css scheme.png
+GEN = index.html scheme.css scheme.png
+FILES = ${GEN} style.css alpha.html lands.html
 
 all: ${FILES}
 
-index.html: index.7
-	mandoc -T html -O style=style.css index.7 > index.html
+.SUFFIXES: .7 .html
+
+.7.html:
+	mandoc -T html -O style=style.css $< > $@
 
 scheme.css:
 	scheme -st > scheme.css
@@ -17,4 +20,4 @@ install: ${FILES}
 	install -C -m 644 ${FILES} ${WEBROOT}
 
 clean:
-	rm -f index.html scheme.css scheme.png
+	rm -f ${GEN}
diff --git a/www/causal.agency/alpha.html b/www/causal.agency/alpha.html
new file mode 100644
index 00000000..0d83f530
--- /dev/null
+++ b/www/causal.agency/alpha.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>all 26 letters of the alphabet RANKED</title>
+<style>
+body, button { font-size: 200%; text-align: center; }
+button { margin: 1em; padding: 1ch; }
+button#shuffle { font-size: 100%; }
+</style>
+
+which letter do you like more?
+<p>
+<button id="a">A</button>
+<button id="b">B</button>
+<p>
+<details>
+<summary>current ranking</summary>
+<p>
+<span id="ranking">ABCDEFGHIJKLMNOPQRSTUVWXYZ</span>
+<p>
+<button id="shuffle">reshuffle</button>
+</details>
+
+<script>
+let buttonA = document.getElementById("a");
+let buttonB = document.getElementById("b");
+let ranking = document.getElementById("ranking");
+
+let alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
+let rand = (bound) => Math.floor(Math.random() * bound);
+function shuffle() {
+	for (let i = alpha.length - 1; i > 0; --i) {
+		let j = rand(i + 1);
+		let x = alpha[i];
+		alpha[i] = alpha[j];
+		alpha[j] = x;
+	}
+}
+if (localStorage.getItem("alpha")) {
+	alpha = localStorage.getItem("alpha").split("");
+} else {
+	shuffle();
+}
+
+let index = 0;
+let even = true;
+function choose(o) {
+	if (o == "b") {
+		let x = alpha[index];
+		alpha[index] = alpha[index + 1];
+		alpha[index + 1] = x;
+	}
+	index += 2;
+	if (index > alpha.length - 2) {
+		even = !even;
+		index = (even ? 0 : 1);
+	}
+	update();
+}
+
+document.onkeydown = function(event) {
+	if (event.key.toUpperCase() == alpha[index]) {
+		choose("a");
+	} else if (event.key.toUpperCase() == alpha[index + 1]) {
+		choose("b");
+	}
+}
+
+function update() {
+	localStorage.setItem("alpha", alpha.join(""));
+	ranking.innerText = alpha.join("");
+	let a = buttonA;
+	let b = buttonB;
+	if (rand(2)) {
+		a = buttonB;
+		b = buttonA;
+	}
+	let lc = (c) => c;
+	if (rand(2)) lc = (c) => c.toLowerCase();
+	a.innerText = lc(alpha[index]);
+	b.innerText = lc(alpha[index + 1]);
+	a.onclick = () => choose("a");
+	b.onclick = () => choose("b");
+}
+update();
+
+document.getElementById("shuffle").onclick = function() {
+	if (confirm("Are you SURE you want to throw away all your hard work?")) {
+		shuffle();
+		update();
+	}
+}
+</script>
diff --git a/www/causal.agency/index.7 b/www/causal.agency/index.7
index c270f477..d29c1a6b 100644
--- a/www/causal.agency/index.7
+++ b/www/causal.agency/index.7
@@ -1,10 +1,10 @@
-.Dd November  3, 2021
+.Dd November 28, 2023
 .Dt CAUSAL.AGENCY 7
 .Os "Causal Agency"
 .
 .Sh NAME
 .Nm june
-.Nd computer enthusiast (her)
+.Nd computer enthusiast (she/her)
 .
 .Sh SYNOPSIS
 .Nm mail
@@ -42,7 +42,7 @@ a cosy IRC client
 a full-text search IRC logger
 .It Lk https://git.causal.agency/scooper/about scooper
 a web interface for litterbox
-.It Lk https://git.causal.agency/catsit/about catsit
+.It Lk https://git.causal.agency/kitd/about kitd
 a process supervisor
 .It Lk https://git.causal.agency/imbox/about "imbox & git-fetch-email"
 a tool to pull patches out of IMAP
@@ -64,4 +64,11 @@ an earthy terminal colour scheme
 .El
 .
 .Sh SEE ALSO
+.Bl -bullet
+.It
 .Lk /bin/ bin
+.It
+.Lk lands.html "Magic lands quiz"
+.It
+.Lk alpha.html "alphabet ranking game"
+.El
diff --git a/www/causal.agency/lands.html b/www/causal.agency/lands.html
new file mode 100644
index 00000000..7aaadd80
--- /dev/null
+++ b/www/causal.agency/lands.html
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<title>Lands Quiz</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<style>
+html { font: 14pt sans-serif; line-height: 1.5em; }
+body { padding: 1em 1ch; max-width: 78ch; margin: auto; }
+h1 { text-align: center; }
+h2 { margin-top: 0; }
+button { font-size: 100%; padding: 0.5em 1ch; }
+img { max-width: 100%; }
+div.cols { display: grid; grid-template-columns: 1fr 1fr; gap: 2ch; }
+</style>
+
+<h1 id="loading">Loading...</h1>
+<h1 id="error" hidden>Failed to load cards :(</h1>
+
+<div id="game" hidden>
+<h1>Magic Lands Quiz</h1>
+<p>Try to guess the colours of mana each land produces!</p>
+<div class="cols">
+	<div>
+		<img id="back" src="https://backs.scryfall.io/normal/0/a/0aeebaf5-8c7d-4636-9e82-8c27447861f7.jpg">
+		<a id="link" target="_blank">
+			<img id="image1" hidden>
+			<img id="image2" hidden>
+		</a>
+	</div>
+	<div>
+		<h2 id="name"></h2>
+		<input type="checkbox" id="w"> <label for="w">White</label><br>
+		<input type="checkbox" id="u"> <label for="u">Blue</label><br>
+		<input type="checkbox" id="b"> <label for="b">Black</label><br>
+		<input type="checkbox" id="r"> <label for="r">Red</label><br>
+		<input type="checkbox" id="g"> <label for="g">Green</label><br>
+		<p><button id="submit">Submit</button></p>
+		<h3>Score: <span id="score">0</span>/<span id="total">0</span></h3>
+	</div>
+</div>
+</div>
+
+<script>
+function shuffle(arr) {
+	let rand = (bound) => Math.floor(Math.random() * bound);
+	for (let i = arr.length-1; i > 0; --i) {
+		let j = rand(i+1);
+		let x = arr[i];
+		arr[i] = arr[j];
+		arr[j] = x;
+	}
+}
+
+const CardBack =
+"https://backs.scryfall.io/normal/0/a/0aeebaf5-8c7d-4636-9e82-8c27447861f7.jpg";
+
+function hideCard() {
+	document.getElementById("back").hidden = false;
+	document.getElementById("image1").hidden = true;
+	document.getElementById("image2").hidden = true;
+}
+
+function showCard(card) {
+	document.getElementById("back").hidden = true;
+	document.getElementById("link").href = card.scryfall_uri;
+	let image1 = document.getElementById("image1");
+	let image2 = document.getElementById("image2");
+	if (card.card_faces) {
+		image1.src = card.card_faces[0].image_uris.normal;
+		image2.src = card.card_faces[1].image_uris.normal;
+		image1.hidden = false;
+		image2.hidden = false;
+	} else {
+		image1.src = card.image_uris.normal;
+		image1.hidden = false;
+	}
+}
+
+function resetChecks() {
+	for (let c of "wubrg") {
+		let input = document.getElementById(c);
+		input.checked = false;
+		input.disabled = false;
+		input.labels[0].style.fontWeight = "normal";
+	}
+}
+
+function checkChecks(card) {
+	let score = 0;
+	let total = 0;
+	let checked = 0;
+	for (let c of "wubrg") {
+		let input = document.getElementById(c);
+		let produced = card.produced_mana.includes(c.toUpperCase());
+		if (produced) {
+			total++;
+			input.labels[0].style.fontWeight = "bold";
+			if (input.checked) score++;
+		}
+		if (input.checked) checked++;
+		input.disabled = true;
+	}
+	if (checked > total) score -= (checked - total);
+	if (score < 0) score = 0;
+	return { score: score, total: total };
+}
+
+document.onkeydown = function(event) {
+	for (let c of "wubrg") {
+		if (event.key == c) {
+			let input = document.getElementById(c);
+			if (!input.disabled) input.checked ^= true;
+		}
+	}
+	if (event.key == "Enter") {
+		document.getElementById("submit").click();
+	}
+}
+
+let score = 0;
+let total = 0;
+let cards = [];
+let card = null;
+
+function nextCard() {
+	hideCard();
+	resetChecks();
+	card = cards.shift();
+	document.getElementById("name").innerText = card.name;
+}
+
+document.getElementById("submit").onclick = function() {
+	if (card) {
+		let { score: cardScore, total: cardTotal } = checkChecks(card);
+		total += cardTotal;
+		score += cardScore;
+		document.getElementById("score").innerText = score;
+		document.getElementById("total").innerText = total;
+		showCard(card);
+		card = null;
+		if (cards.length) {
+			this.innerText = "Next card";
+		} else {
+			this.disabled = true;
+			this.innerText = "No more cards";
+		}
+	} else {
+		nextCard();
+		this.innerText = "Submit";
+	}
+}
+
+function loadCards(resp) {
+	let loading = document.getElementById("loading");
+	let error = document.getElementById("error");
+	let game = document.getElementById("game");
+	if (resp.status != 200) {
+		loading.hidden = true;
+		error.hidden = false;
+	}
+	resp.json().then((json) => {
+		cards.push(...json.data);
+		if (json.has_more) {
+			setTimeout(() => fetch(json.next_page).then(loadCards), 50);
+		} else {
+			loading.hidden = true;
+			game.hidden = false;
+			shuffle(cards);
+			nextCard();
+		}
+	});
+}
+
+const Search =
+"https://api.scryfall.com/cards/search?q=t:land+id>=2+produces>=2+produces!=wubrg";
+fetch(Search).then(loadCards);
+
+</script>
diff --git a/www/causal.agency/style.css b/www/causal.agency/style.css
index 368d8da1..ee218533 100644
--- a/www/causal.agency/style.css
+++ b/www/causal.agency/style.css
@@ -11,6 +11,11 @@ dl.Bl-diag > dt { font-weight: bold; }
 code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn,
 code.Cd { font-weight: bold; font-family: inherit; }
 
+div.head, div.foot { display: flex; justify-content: space-between; }
+.head-ltitle, .foot-left { flex: 1; }
+.head-vol, .foot-date { flex: 0 1 auto; text-align: center; }
+.head-rtitle, .foot-os { flex: 1; text-align: right; }
+
 html { font-family: monospace; line-height: 1.25em; }
 body { max-width: 80ch; margin: 1em auto; padding: 0 1ch; }
 table { border-collapse: collapse; }
diff --git a/www/git.causal.agency/.gitignore b/www/git.causal.agency/.gitignore
index 25e26cc8..eaed8039 100644
--- a/www/git.causal.agency/.gitignore
+++ b/www/git.causal.agency/.gitignore
@@ -1,3 +1,4 @@
+*.html
 about-filter
 compress
 ctags
diff --git a/www/git.causal.agency/Makefile b/www/git.causal.agency/Makefile
index f05d4a4a..86b9f3eb 100644
--- a/www/git.causal.agency/Makefile
+++ b/www/git.causal.agency/Makefile
@@ -2,6 +2,7 @@ PREFIX = /var/www
 CONFDIR = ${PREFIX}/conf
 DATADIR = ${PREFIX}/cgit
 BINDIR = ${PREFIX}/bin
+WEBROOT = ${PREIFX}/git.causal.agency
 
 CFLAGS += -Wall -Wextra
 LDFLAGS = -static -pie
@@ -17,7 +18,9 @@ BINS += mtags
 BINS += owner-filter
 BINS += source-filter
 
-all: ${BINS}
+HTMLS = index.html
+
+all: ${BINS} ${HTMLS}
 
 compress ctags mandoc:
 	${MAKE} -C /usr/src/usr.bin/$@ LDFLAGS='${LDFLAGS}'
@@ -35,12 +38,16 @@ hilex htagml mtags:
 about-filter email-filter owner-filter source-filter: filter
 	ln -f filter $@
 
+index.html: index.7
+	mandoc -Thtml -Ostyle=https://causal.agency/style.css index.7 >index.html
+
 install: cgitrc custom.css ${BINS}
 	install -m 644 cgitrc ${CONFDIR}
 	install -m 644 custom.css ${DATADIR}
 	install -d -o www -g daemon ${PREFIX}/cache/cgit
 	install -d -m 1700 -o www -g daemon ${PREFIX}/tmp
 	install -s ${BINS} ${BINDIR}
+	install -m 644 ${HTMLS} ${WEBROOT}
 
 clean:
-	rm -f compress filter ${BINS}
+	rm -f compress filter ${BINS} ${HTMLS}
diff --git a/www/git.causal.agency/filter.c b/www/git.causal.agency/filter.c
index 9ed9ee17..7c7e9320 100644
--- a/www/git.causal.agency/filter.c
+++ b/www/git.causal.agency/filter.c
@@ -32,12 +32,8 @@ static int email(void) {
 	size_t cap = 0;
 	char *buf = NULL;
 	if (getline(&buf, &cap, stdin) < 0) err(1, "getline");
-	long x = 1;
-	for (char *ch = buf; *ch && *ch != ' '; ++ch) {
-		x *= *ch;
-	}
-	if (buf[0] == 'C' && x == 1251729952200L) {
-		printf("C.%s", buf + strcspn(buf, " "));
+	if (buf[0] == 'C' && !strncmp(&buf[strcspn(buf, " ")], " McEnroe", 8)) {
+		printf("June%s", &buf[strcspn(buf, " ")]);
 	} else {
 		printf("%s", buf);
 	}
@@ -143,6 +139,7 @@ static int source(int argc, char *argv[]) {
 }
 
 int main(int argc, char *argv[]) {
+#ifdef __OpenBSD__
 	int error;
 	switch (getprogname()[0]) {
 		break; case 'a': error = pledge("stdio exec", NULL);
@@ -150,6 +147,7 @@ int main(int argc, char *argv[]) {
 		break; default:  error = pledge("stdio", NULL);
 	}
 	if (error) err(1, "pledge");
+#endif
 	switch (getprogname()[0]) {
 		case 'a': return about(argc, argv);
 		case 'e': return email();
diff --git a/www/git.causal.agency/index.7 b/www/git.causal.agency/index.7
new file mode 100644
index 00000000..58a40dfe
--- /dev/null
+++ b/www/git.causal.agency/index.7
@@ -0,0 +1,81 @@
+.Dd January 12, 2024
+.Dt GIT.CAUSAL.AGENCY 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm causal agency
+.Nd \(dqI think some people from the Gentoo project are behind this.\(dq
+.
+.Sh DESCRIPTION
+basically cgit (awful software)
+getting hammered by web crawlers
+keeps making my machine crash.
+this static page will be here
+until I can find a better solution.
+clone urls and tarball urls are still functional.
+.
+.Bl -tag
+.It src \(em dontfiles
+.Dl git clone https://git.causal.agency/src
+.It ascii.town
+.Bl -tag
+.It torus \(em collaborative ASCII art
+.Dl git clone https://git.causal.agency/torus
+.It play \(em some games for SSH
+.Dl git clone https://git.causal.agency/play
+.El
+.It email
+.Bl -tag
+.It imbox \(em IMAP to mbox
+.Dl git clone https://git.causal.agency/imbox
+.It bubger \(em IMAP archive generator
+.Dl git clone https://git.causal.agency/bubger
+.It notemap \(em notemap
+.Dl git clone https://git.causal.agency/notemap
+.El
+.It forks
+.Bl -tag
+.It shulker \(em Discord to vanilla Minecraft bridge
+.Dl git clone https://git.causal.agency/shulker
+.It cgit-pink \(em web frontend for git
+.Dl git clone https://git.causal.agency/cgit-pink
+.It dash \(em patched shell with cmake build
+.Dl git clone https://git.causal.agency/dash
+.El
+.It games
+.Bl -tag
+.It wep \(em Windows Entertainment Pack recreations
+.Dl git clone https://git.causal.agency/wep
+.It cards \(em CARDS.DLL loader for SDL
+.Dl git clone https://git.causal.agency/cards
+.El
+.It irc
+.Bl -tag
+.It scooper \(em web interface for litterbox
+.Dl git clone https://git.causal.agency/scooper
+.It litterbox \(em IRC logger
+.Dl git clone https://git.causal.agency/litterbox
+.It pounce \(em IRC bouncer
+.Dl git clone https://git.causal.agency/pounce
+.It catgirl \(em IRC client
+.Dl git clone https://git.causal.agency/catgirl
+.El
+.It ports
+.Bl -tag
+.It jorts \(em my own ports tree for macOS
+.Dl git clone https://git.causal.agency/jorts
+.It exman \(em manuals for other systems
+.Dl git clone https://git.causal.agency/exman
+.It libretls \(em libtls for OpenSSL
+.Dl git clone https://git.causal.agency/libretls
+.It ports \(em Fx and Ox ports for this software
+.Dl git clone https://git.causal.agency/ports
+.El
+.It system
+.Bl -tag
+.It kitd \(em process supervisor for OpenBSD
+.Dl git clone https://git.causal.agency/kitd
+.It catsit \(em (deprecated) process supervisor
+.Dl git clone https://git.causal.agency/catsit
+.El
+.El
diff --git a/www/temp.causal.agency/up.c b/www/temp.causal.agency/up.c
index 4b83b564..561a8901 100644
--- a/www/temp.causal.agency/up.c
+++ b/www/temp.causal.agency/up.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/www/text.causal.agency/037-care.7 b/www/text.causal.agency/037-care.7
index 3fffd778..052a4727 100644
--- a/www/text.causal.agency/037-care.7
+++ b/www/text.causal.agency/037-care.7
@@ -27,6 +27,10 @@ Prices obviously change,
 by which I mean they inevitably go up,
 but I'm gonna give the amounts I paid
 in 2021\(en2022.
+Also if you want more details
+about any of this
+please email me.
+I will be happy to tell you all about it.
 .
 .Ss Medication
 I get HRT through
@@ -65,21 +69,27 @@ for my current prescription
 on the public plan.
 .
 .Ss Hair removal
-I've gotten laser hair removal treatments
-at Dermamode with Audrey.
-They're very nice there.
-The initial consultation was over the phone.
-I paid $1350 for 6 treatments,
-in two installments
-at the first and second appointments,
-plus the ~$200 tax on that
-before the first appointment,
-I think.
-It hasn't gone well for me so far,
-but I'm hoping it will
-now that my testosterone levels
-have dropped.
-I will report back.
+I tried laser hair removal,
+for longer than I should have.
+It was a waste of time and money.
+Do not believe any arguments about
+its convenience over electrolysis.
+.
+.Pp
+I've started getting electrolysis done
+with Dimi.
+Again,
+feel free to email me for contact info.
+He is very good and can do long sessions.
+I really don't find it very painful,
+which I think is partly my own pain tolerance
+and partly good equipment and skill.
+I've also found that taking acetaminophen beforehand
+and dressing warmly to keep my body relaxed help.
+I've paid $85 for hour-long sessions
+and $160 for two-hour sessions.
+I'm still early in treatment,
+but I'm really happy with the results so far!
 .
 .Ss Sex & name change
 The form for this is
@@ -101,7 +111,8 @@ so I made a donation to P10.
 .
 .Pp
 I paid $144 to file mine
-but it's now $148.
+but it's now FREE
+the first time you do it.
 Also $17 to mail it.
 Surprisingly,
 I got an acknowledgment letter
@@ -121,10 +132,12 @@ a week later.
 It takes another 30 days
 to get the certificate of change,
 after which you can
-order a new birth certificate.
-In all it took about 3 months
+order a new birth certificate
+and RAMQ will (slowly) send you a form
+to get a new card.
+In all it took about 4 months
 from when I mailed the application
-to receiving the certificate of change.
+to having ID with my name on it.
 .
 .Ss Therapy
 I'm not seeking therapy
@@ -134,6 +147,17 @@ that's aware of it.
 I'll update this
 if I find one.
 .
+.Ss Piercings
+Ok I know this isn't trans-specific
+but at least for me getting piercings
+was gender-affirming.
+Cuz I got nipple piercings lol.
+Anyway,
+I went to Mauve.
+They're super nice,
+really know what they're doing,
+and their website has lots of info.
+.
 .Sh AUTHORS
 .An june Aq Mt june@causal.agency
 .
diff --git a/www/text.causal.agency/039-apologies.7 b/www/text.causal.agency/039-apologies.7
new file mode 100644
index 00000000..1b15076a
--- /dev/null
+++ b/www/text.causal.agency/039-apologies.7
@@ -0,0 +1,81 @@
+.Dd September 19, 2022
+.Dt APOLOGIES 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Apologies
+.Nd making them
+.
+.Sh DESCRIPTION
+Apologies are very important to me.
+Unfortunately
+I've only recently realized
+how valuable they are.
+I've tried to think about
+what makes a good apology,
+since it's not something
+I was ever taught.
+This is the advice
+I came up with for myself,
+on how to apologize.
+.
+.Bl -enum
+.It
+Make the apology.
+This is the most important part.
+If you feel guilty
+for something you've done,
+or think you might have hurt someone,
+apologize.
+Even if they don't need an apology,
+saying sorry won't hurt.
+And start with that.
+Literally say
+.Dq I'm sorry .
+Sometimes people forget that.
+.Pp
+On the other side,
+if you've been hurt by someone,
+and you trust them,
+let them know.
+Give them a chance to apologize.
+People don't always realize
+they've made a mistake.
+.
+.It
+Explain what you did wrong.
+I think it's important
+for the other person
+to know you understand
+how you've messed up.
+Really think about this!
+It's what will help you learn.
+If you know you've hurt someone
+but you're not sure why,
+you can try asking them.
+Take their answer seriously.
+.
+.It
+Don't make excuses.
+Do not talk about yourself.
+Don't even mention
+how you were feeling stressed that day,
+or whatever.
+It's not relevant.
+We all make mistakes,
+we all have bad days.
+.
+.It
+Commit to doing better.
+Try to learn from your mistakes.
+Say it won't happen again.
+Literally say
+.Dq I won't do that again .
+And then try your hardest to make that true.
+An apology is a commitment,
+not something you're done with
+once you've said it.
+.El
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/040-sound-memory.7 b/www/text.causal.agency/040-sound-memory.7
new file mode 100644
index 00000000..c995de08
--- /dev/null
+++ b/www/text.causal.agency/040-sound-memory.7
@@ -0,0 +1,165 @@
+.Dd November 14, 2022
+.Dt SOUND-MEMORY 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Sound Memory
+.Nd associations
+.
+.Sh DESCRIPTION
+.Ss Talking Heads \(em "Remain In Light"
+The first time I gave this album a serious listen
+was when I was going for several-hour walks
+at 4 in the morning in,
+I think,
+fall 2020.
+I would stay up all night,
+go out walking at 4am
+for a couple hours,
+come home,
+eat
+.Dq breakfast
+and go to sleep.
+I listened to this album
+walking on completely empty
+big city streets
+in the dark.
+.
+.Ss Buffy Sainte-Marie \(em Up Where We Belong
+I started listening to this album
+after hearing it many mornings
+walking into the cafe on my block
+back in 2019.
+I could tell Vincent was working
+if I heard this when I opened the door.
+.
+.Ss Molasses \(em Trilogie: Toil & Peaceful Life
+I listened to this when I had 8am classes
+in CEGEP.
+In particular my first semester philosophy course,
+which was in the forum.
+I usually got there even earlier
+because of how the bus schedules worked out.
+There was another girl in my class,
+who I always sat next to,
+who also got there early,
+but we never spoke outside of class.
+.
+.Ss Arcade Fire \(em Funeral
+This album just feels like walking outside
+in fresh snow in early winter,
+you know?
+.
+.Ss Molasses \(em Trouble at Jinx Hotel
+I listened to this when I was looking for an apartment.
+I specifically remember listening to it
+walking down Clark toward my new place
+to pick up my keys.
+.
+.Ss Arcade Fire \(em Neon Bible
+The song
+.Dq "No Cars Go"
+is strongly associated for me
+with my earliest gender feelings.
+It's how I date when I first
+started to feel like something was wrong.
+The Suburbs was released in 2010,
+so I was probably listening to Neon Bible
+in 2011.
+Ten years between that
+and coming out.
+.
+.Ss "Do Make Say Think" \(em "You You're a History In Rust"
+I remember hearing
+.Dq "A Tender History In Rust"
+for the first time
+at the office of my first job.
+Me and my coworkers stayed late,
+probably on a Friday night,
+drinking free tech startup booze.
+.
+.Ss mewithoutYou \(em It's All Crazy! It's All False! It's All a Dream! It's Alright
+I exclusively listened to this album
+on a high school trip to Europe.
+Every morning when we got on the bus,
+I heard
+.Dq Every Thought a Thought of You
+and every night before bed
+I listened to
+.Dq The King Beetle on a Coconut Estate .
+.
+.Ss Arcade Fire \(em The Suburbs
+I listened to this album a tonne
+when I was playing
+Minecraft and Urban Terror
+with my online friends
+while I was in high school.
+In particular I remember
+a backyard shed World of Padman map
+and the apartments Minecraft world.
+.
+.Ss Arcade Fire \(em Reflektor
+I associate
+.Dq Afterlife
+with the walk between Laurier metro
+and my first job,
+in the winter.
+Must've just been how the timing worked out
+with my commute at the time.
+.
+.Ss Swans \(em To Be Kind
+I listened to this on one of my playthroughs
+of Half-Life 2.
+In particular I associate
+.Dq Bring the Sun / Toussaint L'Ouverture
+with the Water Hazard chapter.
+.
+.Ss Wrekmeister Harmonies \(em Light Falls
+For a while I put this on whenever I
+left my apartment to go somewhere
+and it was already dark,
+so probably winter.
+.
+.Ss St. Vincent \(em MASSEDUCTION
+This,
+along with the next one,
+I think were all I listened to
+on a family vacation
+to Quebec City and New Brunswick
+some years ago.
+.
+.Ss SOPHIE \(em Oil of Every Pearl's Un-Insides
+Many hours on the road
+on that family vacation.
+Two albums on repeat.
+.
+.Ss Julia Holter \(em Aviary
+This is another album
+I listened to when I was taking
+walks at 4am.
+I wasn't in a good place.
+Yet.
+.
+.Ss Beep Test \(em Laugh Track
+A tape from the first act
+at one of my favourite shows
+I've ever been to,
+at La Sotterenea
+in Suoni 2019.
+I wish I had been out already.
+.
+.Ss The Armed \(em Only Love
+The third of the albums I listened to
+on those dark walks.
+I listened to it loud,
+this album's mixing needs it.
+.
+.Ss Eliza Kavtion \(em The Rez That Summer
+A favourite local artist.
+I remember vividly the first time
+I heard her play,
+opening for Wrekmeister Harmonies
+at La Vitrola in 2018.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/041-albums-2022.7 b/www/text.causal.agency/041-albums-2022.7
new file mode 100644
index 00000000..48bd3c3d
--- /dev/null
+++ b/www/text.causal.agency/041-albums-2022.7
@@ -0,0 +1,185 @@
+.Dd December 21, 2022
+.Dt ALBUMS-2022 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm albums 2022
+.Nd review
+.
+.Sh DESCRIPTION
+it's the year-end review
+of albums I listened to.
+same as last year,
+I added any albums I got into
+this year to a playlist.
+I've actually done that
+every year since 2018.
+maybe I'll review
+those old playlists some time.
+.
+.Ss ZHAOZE \(em SUMMER INSECTS TALK ABOUT ICE (2021)
+it's a five-and-a-half-minute album!
+you can loop it however long you want.
+it's really lovely.
+.Pp
+favourite track:
+ON HORSEBACK, TO FARAWAY
+.
+.Ss KATE BUSH \(em HOUNDS OF LOVE (1985)
+first of all I do not watch that one show.
+I've known that track for a while actually.
+I mean I probably first heard the CHROMATICS cover.
+but anyway,
+I think someone mentioned this album
+on IRC at just the right time
+and I put it on.
+the second half really shines tbh.
+love a concept album.
+.Pp
+favourite tracks:
+RUNNING UP THAT HILL,
+HOUNDS OF LOVE,
+AND DREAM OF SHEEP,
+WATCHING YOU WITHOUT ME,
+THE MORNING FOG.
+.
+.Ss GODSPEED YOU! BLACK EMPEROR \(em ALL LIGHTS FUCKED ON THE HAIRY AMP DROOLING (1994)
+didn't expect to hear this probably ever.
+still wild that it finally got uploaded.
+and to be honest I'm a little mad
+that it's actually good.
+like yeah it's not a godspeed album
+but it holds up as a tape on its own.
+it's the kind of shit I listen to.
+also can't believe some people
+still thought it was fake.
+like have you not heard
+any other efrim menuck projects?
+.Pp
+favourite tracks:
+$13.13,
+DIMINISHING SHINE,
+DADMOMDADDY,
+333 FRAMES PER SECOND,
+ALL ANGELS GONE.
+.
+.Ss BLACK DRESSES \(em FORGET YOUR OWN FACE (2022)
+woops I think I only listened to this like twice.
+will need to revisit it later for sure.
+I'll like it.
+.
+.Ss BACKXWASH \(em I LIE HERE BURIED WITH MY RINGS AND MY DRESSES (2021)
+only got into this album
+after hearing it live this summer.
+was the first show I went to in years
+and it was really fucking good.
+gotta listen to this shit loud.
+sampling godspeed for a beat fucks.
+honestly back to back bangers.
+.Pp
+favourite tracks:
+I LIE HERE BURIED WITH MY RINGS AND MY DRESSES,
+TERROR PACKETS,
+SONG OF SINNERS,
+BURN TO ASHES.
+.
+.Ss PHILIP GLASS ENSEMBLE \(em EINSTEIN ON THE BEACH (1979)
+actually just the knee plays
+because I can't be bothered
+listening to all of it.
+and I'm embarrassed by how much
+I enjoy this avant-garde bullshit.
+like ok just sing repeating numbers at me
+and my brain is happy.
+what is this?
+my kink?
+anyway I also have kind of an obsession
+with the
+.Dq story of love
+in knee 5.
+I fucking hate it.
+but it's delivered so well.
+and that violin though!
+.Pp
+favourite tracks:
+KNEE 1,
+KNEE 5.
+.
+.Ss KANYE WEST \(em YEEZUS (2013)
+ok look I listened to this
+before recent events.
+what the fuck.
+it's a really good album though?
+pretty sure I listened to it
+because bound 2 kept getting in my head,
+because of that minecraft parody parody
+wayne did ages ago.
+.Pp
+favourite tracks:
+BLACK SKINHEAD,
+HOLD MY LIQUOR,
+BLOOD ON THE LEAVES,
+BOUND 2.
+.
+.Ss FLYING RACCOON SUIT \(em AFTERGLOW (2021)
+I've listened to the whole album
+a few times
+but I'm mostly just here
+for the title track.
+this also happened to be
+dropped in IRC at just the right time.
+good ska-punk-type shit.
+and I like lisps ok.
+.Pp
+favourite track:
+AFTERGLOW.
+.
+.Ss RAMSHACKLE GLORY \(em LIVE THE DREAM (2011)
+one of those albums
+I don't know why I took so long
+to get to.
+I've been listening to johnny hobo
+since I was like in high school.
+ramshackle is a little more hopeful
+and I love that.
+your heart is a muscle the size of your fist.
+keep on loving.
+keep on fighting.
+.Pp
+favourite tracks:
+WE ARE ALL COMPOST IN TRAINING,
+NEVER COMING HOME,
+YOUR HEART IS A MUSCLE THE SIZE OF YOUR FIST.
+.
+.Ss LES RALLIZES D\('ENUD\('ES \(em THE OZ TAPES (2022)
+a pleasant surprise in someone's playlist.
+lately I've been listening to this
+in the metro to or from electrolysis.
+it's good listening for that.
+bold to have two versions
+of the same 24-minute song
+on the same release.
+.Pp
+favourite tracks:
+A SHADOW ON OUR JOY,
+THE LAST ONE_1970 (ver.2).
+.
+.Ss LINGUA IGNOTA \(em SINNER GET READY (2021)
+another I'm only getting into
+after hearing it live.
+just last sunday actually.
+was a good show.
+people will go wild
+to hear a cover live for real.
+.Pp
+favourite tracks:
+I WHO BEND THE TALL GRASSES,
+PENNSYLVANIA FURNACE,
+PERPETUAL FLAME OF CENTRALIA.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+I started writing this
+before I saw LINGUA IGNOTA.
+good thing I waited.
diff --git a/www/text.causal.agency/042-comfort-music.7 b/www/text.causal.agency/042-comfort-music.7
new file mode 100644
index 00000000..445e04c3
--- /dev/null
+++ b/www/text.causal.agency/042-comfort-music.7
@@ -0,0 +1,62 @@
+.Dd February 23, 2024
+.Dt COMFORT-MUSIC 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm comfort music
+.Nd feel better
+.
+.Sh DESCRIPTION
+it's been a while.
+and I'm on almost no sleep
+and haven't eaten a real meal
+since noon.
+which is a state I've written
+at least a couple posts in before,
+so what better time
+to return to what has apparently
+become this blog's format:
+lists of some music I like.
+.
+.Pp
+this is a list of music that comforts me.
+.
+.Bl -bullet
+.It
+knee play 5, from einstein on the beach.
+I like the organ and the counting and the cadence of the story.
+.It
+low \(em words.
+and I'm tired.
+.It
+godspeed you! black emperor \(em storm.
+this is like my original comfort music.
+been listening to it since I was teenage.
+the grooves are worn deep in my mind.
+.It
+set fire to flames \(em love song for 15 ontario (w/ singing police car).
+I like how it ends.
+.It
+va, from the beginner's guide.
+I think that's the whole point.
+though maybe it's too sad
+to be truly comforting.
+.It
+undertale, from undertale.
+what can I say?
+.It
+wrekmeister harmonies \(em covered in blood from invisible wounds.
+I find quite a bit of the album comforting really.
+I'm picking this one because I like the cadence
+of the lyrics.
+.It
+lingua ignota \(em pennsylvania furnace and perpetual flame of centralia.
+these are really my go to in recent times.
+I like waiting for the next line.
+.El
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+I don't think I've said anything
+very interesting here.
diff --git a/www/text.causal.agency/043-little-blessings.7 b/www/text.causal.agency/043-little-blessings.7
new file mode 100644
index 00000000..957c6289
--- /dev/null
+++ b/www/text.causal.agency/043-little-blessings.7
@@ -0,0 +1,78 @@
+.Dd March 24, 2024
+.Dt LITTLE-BLESSINGS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm little blessings
+.Nd life's
+.
+.Sh DESCRIPTION
+today I went out to go around.
+run some errands and do some shopping.
+along the way I was given
+several of life's little blessings.
+.
+.Pp
+while walking on ste-cath
+between berri and complexe desjardins,
+there was a somewhat disheveled man
+walking in the same direction and singing.
+he had a beautiful voice.
+he was singing a sad song in french,
+and he sung it well and enunciated every word.
+.
+.Pp
+in the mcdonald's at complexe desjardins,
+while waiting for my order,
+there were what appeared to be
+a teenager and her younger brother,
+who must have been
+looking at the display of
+current happy meal toys.
+the teenager was playing smash or pass,
+to the amusement of the younger one.
+they got ice cream
+and ate it across the room from me downstairs.
+.
+.Pp
+later,
+taking the 24 home from atwater
+carrying my new vacuum cleaner,
+the bus got lost.
+I think the driver missed the stop
+and tried to compensate
+by turning north onto peel
+and stopping there.
+but then he had to keep going up peel.
+he turned right onto docteur-penfield,
+which just brings you further up the mountain.
+when it met des pins,
+he turned left and pulled over,
+asking for guidance over the radio.
+we got moving again,
+back towards peel.
+that's how I ended up
+on a 24
+.Dq sherbrooke
+east,
+facing west on des pins.
+it was actually quite scenic.
+and amusing.
+I was in no rush.
+.
+.Pp
+after getting back onto sherbrooke,
+the bus had to take another detour,
+this one planned.
+so my ride on the 24,
+which normally only drives on sherbrooke,
+ended up going on peel,
+docteur-penfield,
+des pins,
+de bleury,
+ren\('e-l\('evesque
+and saint-laurent.
+it was a very exciting bus trip.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/Makefile b/www/text.causal.agency/Makefile
index c5555274..a8683a20 100644
--- a/www/text.causal.agency/Makefile
+++ b/www/text.causal.agency/Makefile
@@ -38,15 +38,24 @@ TXTS += 035-addendum-2021.txt
 TXTS += 036-compassion.txt
 TXTS += 037-care.txt
 TXTS += 038-agency.txt
+TXTS += 039-apologies.txt
+TXTS += 040-sound-memory.txt
+TXTS += 041-albums-2022.txt
+TXTS += 042-comfort-music.txt
+TXTS += 043-little-blessings.txt
 
 all: colb ${TXTS}
 
-.SUFFIXES: .7 .txt
+.SUFFIXES: .7 .fmt .txt
 
 .7.txt:
 	mandoc -T utf8 $< | ./colb > $@
 	touch -m -r $< $@
 
+.fmt.txt:
+	fmt $< | sed '1,/^$$/d' > $@
+	touch -m -r $< $@
+
 feed.atom: feed.sh colb ${TXTS}
 	sh feed.sh > feed.atom
 
diff --git a/www/text.causal.agency/feed.sh b/www/text.causal.agency/feed.sh
index 668046ef..71bbf662 100644
--- a/www/text.causal.agency/feed.sh
+++ b/www/text.causal.agency/feed.sh
@@ -27,6 +27,7 @@ set -- *.txt
 shift $(( $# - 20 ))
 for txt; do
 	entry="${txt%.txt}.7"
+	test -f "$entry" || entry="${txt%.txt}.fmt"
 	date=$(grep '^[.]Dd' "$entry" | cut -c 5-)
 	title=$(grep -m 1 '^[.]Nm' "$entry" | cut -c 5- | encode)
 	summary=$(grep '^[.]Nd' "$entry" | cut -c 5- | encode)