summary refs log tree commit diff
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rw-r--r--bin/beef.c7
-rw-r--r--bin/bit.y3
-rw-r--r--bin/c11.l2
-rw-r--r--bin/dehtml.l7
-rw-r--r--bin/downgrade.c57
-rw-r--r--bin/dtch.c61
-rw-r--r--bin/ever.c23
-rw-r--r--bin/freecell.c5
-rw-r--r--bin/git-comment.pl2
-rw-r--r--bin/glitch.c694
-rw-r--r--bin/hilex.c29
-rw-r--r--bin/hilex.h2
-rw-r--r--bin/htagml.c23
-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.c29
-rw-r--r--bin/mtags.c11
-rw-r--r--bin/nudge.c15
-rw-r--r--bin/order.y11
-rw-r--r--bin/pbd.c45
-rw-r--r--bin/png.h5
-rw-r--r--bin/pngo.c85
-rw-r--r--bin/psf2png.c17
-rw-r--r--bin/ptee.c39
-rw-r--r--bin/qf.c25
-rw-r--r--bin/quick.c21
-rw-r--r--bin/relay.c45
-rw-r--r--bin/scheme.c7
-rw-r--r--bin/shotty.l15
-rw-r--r--bin/title.c19
-rw-r--r--bin/up.sh30
-rw-r--r--bin/when.y134
-rw-r--r--bin/xx.c15
35 files changed, 842 insertions, 697 deletions
diff --git a/bin/beef.c b/bin/beef.c
index b2579b73..31781753 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
@@ -19,7 +19,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #include <time.h>
 
 enum {
@@ -44,7 +43,7 @@ static long stack[StackLen];
 static size_t top = StackLen;
 
 static void push(long val) {
-	if (!top) errx(EX_SOFTWARE, "stack overflow");
+	if (!top) errx(1, "stack overflow");
 	stack[--top] = val;
 }
 static long pop(void) {
@@ -121,7 +120,7 @@ int main(int argc, char *argv[]) {
 	FILE *file = stdin;
 	if (argc > 1) {
 		file = fopen(argv[1], "r");
-		if (!file) err(EX_NOINPUT, "%s", argv[1]);
+		if (!file) err(1, "%s", argv[1]);
 	}
 
 	int y = 0;
diff --git a/bin/bit.y b/bin/bit.y
index ab310492..33f5f940 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
@@ -22,7 +22,6 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sysexits.h>
 
 #define MASK(b) ((1ULL << (b)) - 1)
 
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..b6aa4eb8 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
@@ -47,7 +47,6 @@ enum Token {
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
-#include <sysexits.h>
 #include <unistd.h>
 #include <wchar.h>
 
@@ -107,7 +106,7 @@ int main(int argc, char *argv[]) {
 	for (int opt; 0 < (opt = getopt(argc, argv, "s"));) {
 		switch (opt) {
 			break; case 's': collapse = true;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 	argc -= optind;
@@ -116,7 +115,7 @@ int main(int argc, char *argv[]) {
 	if (!argc) argc++;
 	for (int i = 0; i < argc; ++i) {
 		yyin = (argv[i] ? fopen(argv[i], "r") : stdin);
-		if (!yyin) err(EX_NOINPUT, "%s", argv[i]);
+		if (!yyin) err(1, "%s", argv[i]);
 
 		bool space = true;
 		bool discard = false;
diff --git a/bin/downgrade.c b/bin/downgrade.c
index d4c5b598..0d76c787 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
@@ -22,7 +22,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #include <tls.h>
 #include <unistd.h>
 
@@ -40,7 +39,7 @@ static void clientWrite(const char *ptr, size_t len) {
 	while (len) {
 		ssize_t ret = tls_write(client, ptr, len);
 		if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue;
-		if (ret < 0) errx(EX_IOERR, "tls_write: %s", tls_error(client));
+		if (ret < 0) errx(1, "tls_write: %s", tls_error(client));
 		ptr += ret;
 		len -= ret;
 	}
@@ -77,11 +76,11 @@ static void push(struct Message msg) {
 	dst->id = strdup(msg.id);
 	dst->nick = strdup(msg.nick);
 	dst->chan = strdup(msg.chan);
-	if (!dst->id || !dst->nick || !dst->chan) err(EX_OSERR, "strdup");
+	if (!dst->id || !dst->nick || !dst->chan) err(1, "strdup");
 	dst->mesg = NULL;
 	if (msg.mesg) {
 		dst->mesg = strdup(msg.mesg);
-		if (!dst->mesg) err(EX_OSERR, "strdup");
+		if (!dst->mesg) err(1, "strdup");
 	}
 }
 
@@ -103,11 +102,11 @@ static void handle(char *ptr) {
 	if (!strcmp(cmd, "CAP")) {
 		strsep(&ptr, " ");
 		char *sub = strsep(&ptr, " ");
-		if (!sub) errx(EX_PROTOCOL, "CAP without subcommand");
+		if (!sub) errx(1, "CAP without subcommand");
 		if (!strcmp(sub, "NAK")) {
-			errx(EX_CONFIG, "server does not support %s", ptr);
+			errx(1, "server does not support %s", ptr);
 		} else if (!strcmp(sub, "ACK")) {
-			if (!ptr) errx(EX_PROTOCOL, "CAP ACK without caps");
+			if (!ptr) errx(1, "CAP ACK without caps");
 			if (*ptr == ':') ptr++;
 			if (!strcmp(ptr, "sasl")) format("AUTHENTICATE EXTERNAL\r\n");
 		}
@@ -116,13 +115,13 @@ static void handle(char *ptr) {
 	} else if (!strcmp(cmd, "433")) {
 		strsep(&ptr, " ");
 		char *nick = strsep(&ptr, " ");
-		if (!nick) errx(EX_PROTOCOL, "ERR_NICKNAMEINUSE missing nick");
+		if (!nick) errx(1, "ERR_NICKNAMEINUSE missing nick");
 		format("NICK %s_\r\n", nick);
 	} else if (!strcmp(cmd, "001")) {
 		if (join) format("JOIN %s\r\n", join);
 	} else if (!strcmp(cmd, "005")) {
 		char *self = strsep(&ptr, " ");
-		if (!self) errx(EX_PROTOCOL, "RPL_ISUPPORT missing nick");
+		if (!self) errx(1, "RPL_ISUPPORT missing nick");
 		while (ptr && *ptr != ':') {
 			char *tok = strsep(&ptr, " ");
 			char *key = strsep(&tok, "=");
@@ -132,16 +131,16 @@ static void handle(char *ptr) {
 		}
 	} else if (!strcmp(cmd, "INVITE") && invite) {
 		strsep(&ptr, " ");
-		if (!ptr) errx(EX_PROTOCOL, "INVITE missing channel");
+		if (!ptr) errx(1, "INVITE missing channel");
 		if (*ptr == ':') ptr++;
 		format("JOIN %s\r\n", ptr);
 	} else if (!strcmp(cmd, "PING")) {
-		if (!ptr) errx(EX_PROTOCOL, "PING missing parameter");
+		if (!ptr) errx(1, "PING missing parameter");
 		format("PONG %s\r\n", ptr);
 	} else if (!strcmp(cmd, "ERROR")) {
-		if (!ptr) errx(EX_PROTOCOL, "ERROR missing parameter");
+		if (!ptr) errx(1, "ERROR missing parameter");
 		if (*ptr == ':') ptr++;
-		errx(EX_UNAVAILABLE, "%s", ptr);
+		errx(1, "%s", ptr);
 	}
 
 	if (
@@ -149,13 +148,13 @@ static void handle(char *ptr) {
 		strcmp(cmd, "NOTICE") &&
 		strcmp(cmd, "TAGMSG")
 	) return;
-	if (!origin) errx(EX_PROTOCOL, "%s missing origin", cmd);
+	if (!origin) errx(1, "%s missing origin", cmd);
 
 	struct Message msg = {
 		.nick = strsep(&origin, "!"),
 		.chan = strsep(&ptr, " "),
 	};
-	if (!msg.chan) errx(EX_PROTOCOL, "%s missing target", cmd);
+	if (!msg.chan) errx(1, "%s missing target", cmd);
 	if (msg.chan[0] == ':') msg.chan++;
 	if (msg.chan[0] != '#') return;
 	if (strcmp(cmd, "TAGMSG")) msg.mesg = (*ptr == ':' ? &ptr[1] : ptr);
@@ -263,7 +262,7 @@ static void quit(int sig) {
 	(void)sig;
 	format("QUIT\r\n");
 	tls_close(client);
-	exit(EX_OK);
+	exit(0);
 }
 
 int main(int argc, char *argv[]) {
@@ -282,44 +281,44 @@ int main(int argc, char *argv[]) {
 			break; case 'n': nick = optarg;
 			break; case 'p': port = optarg;
 			break; case 'v': verbose = true;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
-	if (optind == argc) errx(EX_USAGE, "host required");
+	if (optind == argc) errx(1, "host required");
 	host = argv[optind];
 
 	client = tls_client();
-	if (!client) errx(EX_SOFTWARE, "tls_client");
+	if (!client) errx(1, "tls_client");
 
 	struct tls_config *config = tls_config_new();
-	if (!config) errx(EX_SOFTWARE, "tls_config_new");
+	if (!config) errx(1, "tls_config_new");
 
 	if (cert) {
 		if (!priv) priv = cert;
 		int error = tls_config_set_keypair_file(config, cert, priv);
-		if (error) errx(EX_NOINPUT, "%s: %s", cert, tls_config_error(config));
+		if (error) errx(1, "%s: %s", cert, tls_config_error(config));
 	}
 
 	int error = tls_configure(client, config);
-	if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client));
+	if (error) errx(1, "tls_configure: %s", tls_error(client));
 
 	error = tls_connect(client, host, port);
-	if (error) errx(EX_UNAVAILABLE, "tls_connect: %s", tls_error(client));
+	if (error) errx(1, "tls_connect: %s", tls_error(client));
 
 	do {
 		error = tls_handshake(client);
 	} while (error == TLS_WANT_POLLIN || error == TLS_WANT_POLLOUT);
-	if (error) errx(EX_PROTOCOL, "tls_handshake: %s", tls_error(client));
+	if (error) errx(1, "tls_handshake: %s", tls_error(client));
 	tls_config_clear_keys(config);
 
 #ifdef __OpenBSD__
 	error = pledge("stdio", NULL);
-	if (error) err(EX_OSERR, "pledge");
+	if (error) err(1, "pledge");
 #endif
 
 #ifdef __FreeBSD__
 	error = caph_enter() || caph_limit_stdio();
-	if (error) err(EX_OSERR, "caph_enter");
+	if (error) err(1, "caph_enter");
 #endif
 
 	signal(SIGHUP, quit);
@@ -342,8 +341,8 @@ int main(int argc, char *argv[]) {
 	for (;;) {
 		ssize_t n = tls_read(client, &buf[len], sizeof(buf) - len);
 		if (n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT) continue;
-		if (n < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client));
-		if (!n) errx(EX_UNAVAILABLE, "disconnected");
+		if (n < 0) errx(1, "tls_read: %s", tls_error(client));
+		if (!n) errx(1, "disconnected");
 		len += n;
 
 		char *ptr = buf;
diff --git a/bin/dtch.c b/bin/dtch.c
index 2aea53ae..55b33910 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
@@ -28,7 +28,6 @@
 #include <sys/stat.h>
 #include <sys/un.h>
 #include <sys/wait.h>
-#include <sysexits.h>
 #include <termios.h>
 #include <unistd.h>
 
@@ -91,18 +90,18 @@ static void handler(int sig) {
 static void detach(int server, bool sink, char *argv[]) {
 	int pty;
 	pid_t pid = forkpty(&pty, NULL, NULL, NULL);
-	if (pid < 0) err(EX_OSERR, "forkpty");
+	if (pid < 0) err(1, "forkpty");
 
 	if (!pid) {
 		execvp(argv[0], argv);
-		err(EX_NOINPUT, "%s", argv[0]);
+		err(127, "%s", argv[0]);
 	}
 
 	signal(SIGINT, handler);
 	signal(SIGTERM, handler);
 
 	int error = listen(server, 0);
-	if (error) err(EX_OSERR, "listen");
+	if (error) err(1, "listen");
 
 	struct pollfd fds[] = {
 		{ .events = POLLIN, .fd = server },
@@ -111,7 +110,7 @@ static void detach(int server, bool sink, char *argv[]) {
 	while (0 < poll(fds, (sink ? 2 : 1), -1)) {
 		if (fds[0].revents) {
 			int client = accept(server, NULL, NULL);
-			if (client < 0) err(EX_IOERR, "accept");
+			if (client < 0) err(1, "accept");
 
 			ssize_t len = sendfd(client, pty);
 			if (len < 0) warn("sendfd");
@@ -125,18 +124,18 @@ static void detach(int server, bool sink, char *argv[]) {
 		if (fds[1].revents) {
 			char buf[4096];
 			ssize_t len = read(pty, buf, sizeof(buf));
-			if (len < 0) err(EX_IOERR, "read");
+			if (len < 0) err(1, "read");
 		}
 
 		int status;
 		pid_t dead = waitpid(pid, &status, WNOHANG);
-		if (dead < 0) err(EX_OSERR, "waitpid");
+		if (dead < 0) err(1, "waitpid");
 		if (dead) {
 			unlink(addr.sun_path);
 			exit(WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status));
 		}
 	}
-	err(EX_IOERR, "poll");
+	err(1, "poll");
 }
 
 static struct termios saveTerm;
@@ -154,28 +153,28 @@ static void attach(int client) {
 	int error;
 
 	int pty = recvfd(client);
-	if (pty < 0) err(EX_IOERR, "recvfd");
+	if (pty < 0) err(1, "recvfd");
 	warnx("attached");
 
 	struct winsize window;
 	error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
-	if (error) err(EX_IOERR, "ioctl");
+	if (error) err(1, "ioctl");
 
 	struct winsize redraw = { .ws_row = 1, .ws_col = 1 };
 	error = ioctl(pty, TIOCSWINSZ, &redraw);
-	if (error) err(EX_IOERR, "ioctl");
+	if (error) err(1, "ioctl");
 
 	error = ioctl(pty, TIOCSWINSZ, &window);
-	if (error) err(EX_IOERR, "ioctl");
+	if (error) err(1, "ioctl");
 
 	error = tcgetattr(STDIN_FILENO, &saveTerm);
-	if (error) err(EX_IOERR, "tcgetattr");
+	if (error) err(1, "tcgetattr");
 	atexit(restoreTerm);
 
 	struct termios raw = saveTerm;
 	cfmakeraw(&raw);
 	error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw);
-	if (error) err(EX_IOERR, "tcsetattr");
+	if (error) err(1, "tcsetattr");
 
 	signal(SIGWINCH, nop);
 
@@ -187,35 +186,35 @@ static void attach(int client) {
 	for (;;) {
 		int nfds = poll(fds, 2, -1);
 		if (nfds < 0) {
-			if (errno != EINTR) err(EX_IOERR, "poll");
+			if (errno != EINTR) err(1, "poll");
 
 			error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
-			if (error) err(EX_IOERR, "ioctl");
+			if (error) err(1, "ioctl");
 
 			error = ioctl(pty, TIOCSWINSZ, &window);
-			if (error) err(EX_IOERR, "ioctl");
+			if (error) err(1, "ioctl");
 
 			continue;
 		}
 
 		if (fds[0].revents) {
 			ssize_t len = read(STDIN_FILENO, buf, sizeof(buf));
-			if (len < 0) err(EX_IOERR, "read");
+			if (len < 0) err(1, "read");
 			if (!len) break;
 
 			if (len == 1 && buf[0] == CTRL('Q')) break;
 
 			len = write(pty, buf, len);
-			if (len < 0) err(EX_IOERR, "write");
+			if (len < 0) err(1, "write");
 		}
 
 		if (fds[1].revents) {
 			ssize_t len = read(pty, buf, sizeof(buf));
-			if (len < 0) err(EX_IOERR, "read");
+			if (len < 0) err(1, "read");
 			if (!len) break;
 
 			len = write(STDOUT_FILENO, buf, len);
-			if (len < 0) err(EX_IOERR, "write");
+			if (len < 0) err(1, "write");
 		}
 	}
 }
@@ -231,41 +230,41 @@ int main(int argc, char *argv[]) {
 		switch (opt) {
 			break; case 'a': atch = true;
 			break; case 's': sink = true;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
-	if (optind == argc) errx(EX_USAGE, "no session name");
+	if (optind == argc) errx(1, "no session name");
 	const char *name = argv[optind++];
 
 	if (optind == argc) {
 		argv[--optind] = getenv("SHELL");
-		if (!argv[optind]) errx(EX_CONFIG, "SHELL unset");
+		if (!argv[optind]) errx(1, "SHELL unset");
 	}
 
 	const char *home = getenv("HOME");
-	if (!home) errx(EX_CONFIG, "HOME unset");
+	if (!home) errx(1, "HOME unset");
 
 	int fd = open(home, 0);
-	if (fd < 0) err(EX_CANTCREAT, "%s", home);
+	if (fd < 0) err(1, "%s", home);
 
 	error = mkdirat(fd, ".dtch", 0700);
-	if (error && errno != EEXIST) err(EX_CANTCREAT, "%s/.dtch", home);
+	if (error && errno != EEXIST) err(1, "%s/.dtch", home);
 
 	close(fd);
 
 	int sock = socket(PF_UNIX, SOCK_STREAM, 0);
-	if (sock < 0) err(EX_OSERR, "socket");
+	if (sock < 0) err(1, "socket");
 	fcntl(sock, F_SETFD, FD_CLOEXEC);
 
 	snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/.dtch/%s", home, name);
 
 	if (atch) {
 		error = connect(sock, (struct sockaddr *)&addr, SUN_LEN(&addr));
-		if (error) err(EX_NOINPUT, "%s", addr.sun_path);
+		if (error) err(1, "%s", addr.sun_path);
 		attach(sock);
 	} else {
 		error = bind(sock, (struct sockaddr *)&addr, SUN_LEN(&addr));
-		if (error) err(EX_CANTCREAT, "%s", addr.sun_path);
+		if (error) err(1, "%s", addr.sun_path);
 		detach(sock, sink, &argv[optind]);
 	}
 }
diff --git a/bin/ever.c b/bin/ever.c
index f983912b..24575617 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
@@ -22,12 +22,11 @@
 #include <stdlib.h>
 #include <sys/event.h>
 #include <sys/wait.h>
-#include <sysexits.h>
 #include <unistd.h>
 
 static int watch(int kq, char *path) {
 	int fd = open(path, O_CLOEXEC);
-	if (fd < 0) err(EX_NOINPUT, "%s", path);
+	if (fd < 0) err(1, "%s", path);
 
 	struct kevent event;
 	EV_SET(
@@ -40,7 +39,7 @@ static int watch(int kq, char *path) {
 		path
 	);
 	int nevents = kevent(kq, &event, 1, NULL, 0, NULL);
-	if (nevents < 0) err(EX_OSERR, "kevent");
+	if (nevents < 0) err(1, "kevent");
 
 	return fd;
 }
@@ -48,17 +47,17 @@ static int watch(int kq, char *path) {
 static bool quiet;
 static void exec(int fd, char *const argv[]) {
 	pid_t pid = fork();
-	if (pid < 0) err(EX_OSERR, "fork");
+	if (pid < 0) err(1, "fork");
 
 	if (!pid) {
 		dup2(fd, STDIN_FILENO);
 		execvp(*argv, argv);
-		err(EX_NOINPUT, "%s", *argv);
+		err(127, "%s", *argv);
 	}
 
 	int status;
 	pid = wait(&status);
-	if (pid < 0) err(EX_OSERR, "wait");
+	if (pid < 0) err(1, "wait");
 
 	if (quiet) return;
 	if (WIFEXITED(status)) {
@@ -77,15 +76,15 @@ int main(int argc, char *argv[]) {
 		switch (opt) {
 			break; case 'i': input = true;
 			break; case 'q': quiet = true;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 	argc -= optind;
 	argv += optind;
-	if (argc < 2) return EX_USAGE;
+	if (argc < 2) return 1;
 
 	int kq = kqueue();
-	if (kq < 0) err(EX_OSERR, "kqueue");
+	if (kq < 0) err(1, "kqueue");
 
 	int i;
 	for (i = 0; i < argc - 1; ++i) {
@@ -103,7 +102,7 @@ int main(int argc, char *argv[]) {
 	for (;;) {
 		struct kevent event;
 		int nevents = kevent(kq, NULL, 0, &event, 1, NULL);
-		if (nevents < 0) err(EX_OSERR, "kevent");
+		if (nevents < 0) err(1, "kevent");
 
 		if (event.fflags & NOTE_DELETE) {
 			close(event.ident);
@@ -111,7 +110,7 @@ int main(int argc, char *argv[]) {
 			event.ident = watch(kq, (char *)event.udata);
 		} else if (input) {
 			off_t off = lseek(event.ident, 0, SEEK_SET);
-			if (off < 0) err(EX_IOERR, "lseek");
+			if (off < 0) err(1, "lseek");
 		}
 
 		exec((input ? event.ident : STDIN_FILENO), &argv[i]);
diff --git a/bin/freecell.c b/bin/freecell.c
index 11bed1c0..0110ecfe 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
@@ -22,7 +22,6 @@
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sysexits.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -367,7 +366,7 @@ int main(int argc, char *argv[]) {
 		switch (opt) {
 			break; case 'd': delay = strtoul(optarg, NULL, 10);
 			break; case 'n': game = strtoul(optarg, NULL, 10);
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 	curse();
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..4eec2c49 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,297 +14,339 @@
  * 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>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #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(1, "%s", path);
+	if (!n) errx(1, "%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(1, "%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)) {
-		errx(EX_DATAERR, "%s: invalid signature", path);
+static void sigRead(void) {
+	uint8_t sig[sizeof(Sig)];
+	pngRead(sig, sizeof(sig), "signature");
+	if (memcmp(sig, Sig, sizeof(sig))) {
+		errx(1, "%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(
+		1, "%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(1, "%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(
+			1, "%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(
+			1, "%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
+			1, "%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(
+			1, "%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(1, "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(1, "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(1, "%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(1, "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(1, "%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
+			1, "%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(1, "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(1, "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 +361,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 +372,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(1, "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,148 +433,172 @@ 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;
-		}
+	for (enum Filter i = None; i < FilterCap; ++i) {
+		free(filter[i]);
 	}
 }
 
-static void zeroX(void) {
-	for (uint32_t y = 0; y < header.height; ++y) {
-		memset(lines[y]->data, 0, pixelSize());
-	}
-}
-
-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) {
 		path = inPath;
 		file = fopen(path, "r");
-		if (!file) err(EX_NOINPUT, "%s", path);
+		if (!file) err(1, "%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(1, "%s: expected IHDR, found %s", path, ihdr.type);
+	}
+	headerRead(ihdr);
+	if (header.interlace != Progressive) {
+		errx(1, "%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(1, "%s", buf);
+		} else {
+			file = fopen(path, "w");
+			if (!file) err(1, "%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 (error) err(1, "%s", path);
+
+	if (outPath && outPath == inPath) {
+		error = rename(buf, outPath);
+		if (error) err(1, "%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(1, "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 1;
 		}
 	}
 
-	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..81485ab2 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
@@ -23,7 +23,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/wait.h>
-#include <sysexits.h>
 #include <unistd.h>
 
 #include "hilex.h"
@@ -53,7 +52,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 },
 };
 
@@ -61,14 +60,14 @@ static const struct Lexer *parseLexer(const char *name) {
 	for (size_t i = 0; i < ARRAY_LEN(Lexers); ++i) {
 		if (!strcmp(name, Lexers[i].name)) return Lexers[i].lexer;
 	}
-	errx(EX_USAGE, "unknown lexer %s", name);
+	errx(1, "unknown lexer %s", name);
 }
 
 static void ungets(const char *str, FILE *file) {
 	size_t len = strlen(str);
 	for (size_t i = len-1; i < len; --i) {
 		int ch = ungetc(str[i], file);
-		if (ch == EOF) errx(EX_IOERR, "cannot push back string");
+		if (ch == EOF) errx(1, "cannot push back string");
 	}
 }
 
@@ -134,16 +133,16 @@ static void ansiHeader(const char *opts[]) {
 
 	int rw[2];
 	int error = pipe(rw);
-	if (error) err(EX_OSERR, "pipe");
+	if (error) err(1, "pipe");
 
 	pid_t pid = fork();
-	if (pid < 0) err(EX_OSERR, "fork");
+	if (pid < 0) err(1, "fork");
 	if (!pid) {
 		dup2(rw[0], STDIN_FILENO);
 		close(rw[0]);
 		close(rw[1]);
 		execl(shell, shell, "-c", pager, NULL);
-		err(EX_CONFIG, "%s", shell);
+		err(127, "%s", shell);
 	}
 	dup2(rw[1], STDOUT_FILENO);
 	close(rw[0]);
@@ -152,7 +151,7 @@ static void ansiHeader(const char *opts[]) {
 
 #ifdef __OpenBSD__
 	error = pledge("stdio", NULL);
-	if (error) err(EX_OSERR, "pledge");
+	if (error) err(1, "pledge");
 #endif
 }
 
@@ -330,7 +329,7 @@ static const struct Formatter *parseFormatter(const char *name) {
 	for (size_t i = 0; i < ARRAY_LEN(Formatters); ++i) {
 		if (!strcmp(name, Formatters[i].name)) return &Formatters[i];
 	}
-	errx(EX_USAGE, "unknown formatter %s", name);
+	errx(1, "unknown formatter %s", name);
 }
 
 static char *const OptionKeys[OptionCap + 1] = {
@@ -356,12 +355,12 @@ int main(int argc, char *argv[]) {
 				while (*optarg) {
 					char *val;
 					int key = getsubopt(&optarg, OptionKeys, &val);
-					if (key < 0) errx(EX_USAGE, "no such option %s", val);
+					if (key < 0) errx(1, "no such option %s", val);
 					opts[key] = (val ? val : "");
 				}
 			}
 			break; case 't': text = true;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 
@@ -370,7 +369,7 @@ int main(int argc, char *argv[]) {
 	if (optind < argc) {
 		path = argv[optind];
 		file = fopen(path, "r");
-		if (!file) err(EX_NOINPUT, "%s", path);
+		if (!file) err(1, "%s", path);
 		pager = isatty(STDOUT_FILENO);
 	}
 
@@ -381,7 +380,7 @@ int main(int argc, char *argv[]) {
 	} else {
 		error = pledge("stdio", NULL);
 	}
-	if (error) err(EX_OSERR, "pledge");
+	if (error) err(1, "pledge");
 #endif
 
 	if (!name) {
@@ -394,7 +393,7 @@ int main(int argc, char *argv[]) {
 	if (!opts[Title]) opts[Title] = name;
 	if (!lexer) lexer = matchLexer(name, file);
 	if (!lexer && text) lexer = &LexText;
-	if (!lexer) errx(EX_USAGE, "cannot infer lexer for %s", name);
+	if (!lexer) errx(1, "cannot infer lexer for %s", name);
 
 	*lexer->in = file;
 	if (formatter->header) formatter->header(opts);
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/htagml.c b/bin/htagml.c
index 1f547be6..c35cdb15 100644
--- a/bin/htagml.c
+++ b/bin/htagml.c
@@ -20,12 +20,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #include <unistd.h>
 
 static char *deregex(const char *patt) {
 	char *buf = malloc(strlen(patt) + 1);
-	if (!buf) err(EX_OSERR, "malloc");
+	if (!buf) err(1, "malloc");
 	char *ptr = buf;
 	if (*patt == '^') patt++;
 	for (; *patt; ++patt) {
@@ -94,21 +93,21 @@ int main(int argc, char *argv[]) {
 			break; case 'm': main = true;
 			break; case 'p': pre = true;
 			break; case 'x': index = true;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
-	if (optind == argc) errx(EX_USAGE, "name required");
+	if (optind == argc) errx(1, "name required");
 	const char *name = argv[optind];
 
 	FILE *file = fopen(name, "r");
-	if (!file) err(EX_NOINPUT, "%s", name);
+	if (!file) err(1, "%s", name);
 
 	FILE *tagsFile = fopen(tagsPath, "r");
-	if (!tagsFile) err(EX_NOINPUT, "%s", tagsPath);
+	if (!tagsFile) err(1, "%s", tagsPath);
 
 #ifdef __OpenBSD__
 	int error = pledge("stdio", NULL);
-	if (error) err(EX_OSERR, "pledge");
+	if (error) err(1, "pledge");
 #endif
 
 	size_t len = 0;
@@ -119,7 +118,7 @@ int main(int argc, char *argv[]) {
 		char *str;
 		size_t len;
 	} *tags = malloc(cap * sizeof(*tags));
-	if (!tags) err(EX_OSERR, "malloc");
+	if (!tags) err(1, "malloc");
 
 	char *buf = NULL;
 	size_t bufCap = 0;
@@ -128,15 +127,15 @@ int main(int argc, char *argv[]) {
 		char *tag = strsep(&line, "\t");
 		char *file = strsep(&line, "\t");
 		char *def = strsep(&line, "\n");
-		if (!tag || !file || !def) errx(EX_DATAERR, "malformed tags file");
+		if (!tag || !file || !def) errx(1, "malformed tags file");
 
 		if (strcmp(file, name)) continue;
 		if (len == cap) {
 			tags = realloc(tags, (cap *= 2) * sizeof(*tags));
-			if (!tags) err(EX_OSERR, "realloc");
+			if (!tags) err(1, "realloc");
 		}
 		tags[len].tag = strdup(tag);
-		if (!tags[len].tag) err(EX_OSERR, "strdup");
+		if (!tags[len].tag) err(1, "strdup");
 
 		tags[len].num = 0;
 		if (def[0] == '/' || def[0] == '?') {
@@ -184,7 +183,7 @@ int main(int argc, char *argv[]) {
 		if (pipe) {
 			ssize_t len = getline(&buf, &bufCap, stdin);
 			if (len < 0) {
-				errx(EX_DATAERR, "missing line %d on standard input", num);
+				errx(1, "missing line %d on standard input", num);
 			}
 		}
 		if (!tag) {
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..75517159 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
@@ -19,7 +19,6 @@
 #include <stdlib.h>
 #include <sys/ioctl.h>
 #include <sys/wait.h>
-#include <sysexits.h>
 #include <termios.h>
 #include <unistd.h>
 
@@ -46,31 +45,31 @@ int main(int argc, char *argv[]) {
 	for (int opt; 0 < (opt = getopt(argc, argv, "r:"));) {
 		switch (opt) {
 			break; case 'r': baudRate = strtoul(optarg, NULL, 10);
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
-	if (argc - optind < 1) return EX_USAGE;
+	if (argc - optind < 1) return 1;
 
 	error = tcgetattr(STDIN_FILENO, &saveTerm);
-	if (error) err(EX_IOERR, "tcgetattr");
+	if (error) err(1, "tcgetattr");
 	atexit(restoreTerm);
 
 	struct termios raw = saveTerm;
 	cfmakeraw(&raw);
 	error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw);
-	if (error) err(EX_IOERR, "tcsetattr");
+	if (error) err(1, "tcsetattr");
 
 	struct winsize window;
 	error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
-	if (error) err(EX_IOERR, "TIOCGWINSZ");
+	if (error) err(1, "TIOCGWINSZ");
 
 	int pty;
 	pid_t pid = forkpty(&pty, NULL, NULL, &window);
-	if (pid < 0) err(EX_OSERR, "forkpty");
+	if (pid < 0) err(1, "forkpty");
 
 	if (!pid) {
 		execvp(argv[optind], &argv[optind]);
-		err(EX_NOINPUT, "%s", argv[optind]);
+		err(1, "%s", argv[optind]);
 	}
 
 	byte c;
@@ -81,22 +80,22 @@ int main(int argc, char *argv[]) {
 	while (usleep(8 * 1000000 / baudRate), 0 < poll(fds, 2, -1)) {
 		if (fds[0].revents) {
 			ssize_t size = read(STDIN_FILENO, &c, 1);
-			if (size < 0) err(EX_IOERR, "read(%d)", STDIN_FILENO);
+			if (size < 0) err(1, "read(%d)", STDIN_FILENO);
 			size = write(pty, &c, 1);
-			if (size < 0) err(EX_IOERR, "write(%d)", pty);
+			if (size < 0) err(1, "write(%d)", pty);
 		}
 
 		if (fds[1].revents) {
 			ssize_t size = read(pty, &c, 1);
-			if (size < 0) err(EX_IOERR, "read(%d)", pty);
+			if (size < 0) err(1, "read(%d)", pty);
 			if (!size) break;
 			size = write(STDOUT_FILENO, &c, 1);
-			if (size < 0) err(EX_IOERR, "write(%d)", STDOUT_FILENO);
+			if (size < 0) err(1, "write(%d)", STDOUT_FILENO);
 		}
 	}
 
 	int status;
 	pid_t dead = waitpid(pid, &status, 0);
-	if (dead < 0) err(EX_OSERR, "waitpid");
-	return WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE;
+	if (dead < 0) err(1, "waitpid");
+	return WIFEXITED(status) ? WEXITSTATUS(status) : 1;
 }
diff --git a/bin/mtags.c b/bin/mtags.c
index 11cd9c8a..5c42f343 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
@@ -21,7 +21,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #include <unistd.h>
 
 static void escape(FILE *file, const char *str, size_t len) {
@@ -41,16 +40,16 @@ int main(int argc, char *argv[]) {
 		switch (opt) {
 			break; case 'a': append = true;
 			break; case 'f': path = optarg;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 
 	FILE *tags = fopen(path, (append ? "a" : "w"));
-	if (!tags) err(EX_CANTCREAT, "%s", path);
+	if (!tags) err(1, "%s", path);
 
 #ifdef __OpenBSD__
 	error = pledge("stdio rpath", NULL);
-	if (error) err(EX_OSERR, "pledge");
+	if (error) err(1, "pledge");
 #endif
 
 	regex_t makeFile, makeLine;
@@ -87,7 +86,7 @@ int main(int argc, char *argv[]) {
 		}
 
 		FILE *file = fopen(argv[i], "r");
-		if (!file) err(EX_NOINPUT, "%s", argv[i]);
+		if (!file) err(1, "%s", argv[i]);
 
 		while (0 < getline(&buf, &cap, file)) {
 			regmatch_t match[2];
diff --git a/bin/nudge.c b/bin/nudge.c
index 108d2459..c6247b87 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
@@ -18,7 +18,6 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sysexits.h>
 #include <termios.h>
 #include <unistd.h>
 
@@ -39,21 +38,21 @@ int main(int argc, char *argv[]) {
 			break; case 'n': count = atoi(optarg);
 			break; case 's': shake = atoi(optarg);
 			break; case 't': delay = atoi(optarg) * 1000;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 
 	int tty = open(path, O_RDWR);
-	if (tty < 0) err(EX_OSFILE, "%s", path);
+	if (tty < 0) err(1, "%s", path);
 
 	struct termios save;
 	int error = tcgetattr(tty, &save);
-	if (error) err(EX_IOERR, "tcgetattr");
+	if (error) err(1, "tcgetattr");
 
 	struct termios raw = save;
 	cfmakeraw(&raw);
 	error = tcsetattr(tty, TCSAFLUSH, &raw);
-	if (error) err(EX_IOERR, "tcsetattr");
+	if (error) err(1, "tcsetattr");
 
 	char buf[256];
 	dprintf(tty, "\33[13t");
@@ -64,8 +63,8 @@ int main(int argc, char *argv[]) {
 	int n = sscanf(buf, "\33[3;%d;%dt", &x, &y);
 
 	error = tcsetattr(tty, TCSANOW, &save);
-	if (error) err(EX_IOERR, "tcsetattr");
-	if (n < 2) return EX_CONFIG;
+	if (error) err(1, "tcsetattr");
+	if (n < 2) return 1;
 
 	dprintf(tty, "\33[5t");
 	for (int i = 0; i < count; ++i) {
diff --git a/bin/order.y b/bin/order.y
index c2e87971..05be9838 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
@@ -22,7 +22,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 
 #define YYSTYPE char *
 
@@ -32,7 +31,7 @@ static char *fmt(const char *format, ...) {
 	va_start(ap, format);
 	vasprintf(&str, format, ap);
 	va_end(ap);
-	if (!str) err(EX_OSERR, "vasprintf");
+	if (!str) err(1, "vasprintf");
 	return str;
 }
 
@@ -179,17 +178,17 @@ static int yylex(void) {
 }
 
 static void yyerror(const char *str) {
-	errx(EX_DATAERR, "%s", str);
+	errx(1, "%s", str);
 }
 
 int main(int argc, char *argv[]) {
 	for (int i = 1; i < argc; ++i) {
 		in = fmemopen(argv[i], strlen(argv[i]), "r");
-		if (!in) err(EX_OSERR, "fmemopen");
+		if (!in) err(1, "fmemopen");
 		yyparse();
 		fclose(in);
 	}
-	if (argc > 1) return EX_OK;
+	if (argc > 1) return 0;
 	in = stdin;
 	yyparse();
 }
diff --git a/bin/pbd.c b/bin/pbd.c
index 2fd401ae..8375fc38 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
@@ -24,27 +24,26 @@
 #include <string.h>
 #include <sys/socket.h>
 #include <sys/wait.h>
-#include <sysexits.h>
 #include <unistd.h>
 
 typedef unsigned char byte;
 
 static void spawn(const char *cmd, const char *arg, int dest, int src) {
 	pid_t pid = fork();
-	if (pid < 0) err(EX_OSERR, "fork");
+	if (pid < 0) err(1, "fork");
 
 	if (pid) {
 		int status;
 		pid_t dead = waitpid(pid, &status, 0);
-		if (dead < 0) err(EX_OSERR, "waitpid(%d)", pid);
+		if (dead < 0) err(1, "waitpid(%d)", pid);
 		if (status) warnx("%s: status %d", cmd, status);
 
 	} else {
 		int fd = dup2(src, dest);
-		if (fd < 0) err(EX_OSERR, "dup2");
+		if (fd < 0) err(1, "dup2");
 
 		execlp(cmd, cmd, arg, NULL);
-		err(EX_UNAVAILABLE, "%s", cmd);
+		err(127, "%s", cmd);
 	}
 }
 
@@ -52,10 +51,10 @@ static int pbd(void) {
 	int error;
 
 	int server = socket(PF_INET, SOCK_STREAM, 0);
-	if (server < 0) err(EX_OSERR, "socket");
+	if (server < 0) err(1, "socket");
 
 	error = fcntl(server, F_SETFD, FD_CLOEXEC);
-	if (error) err(EX_IOERR, "fcntl");
+	if (error) err(1, "fcntl");
 
 	struct sockaddr_in addr = {
 		.sin_family = AF_INET,
@@ -63,17 +62,17 @@ static int pbd(void) {
 		.sin_addr = { .s_addr = htonl(0x7F000001) },
 	};
 	error = bind(server, (struct sockaddr *)&addr, sizeof(addr));
-	if (error) err(EX_UNAVAILABLE, "bind");
+	if (error) err(1, "bind");
 
 	error = listen(server, 0);
-	if (error) err(EX_UNAVAILABLE, "listen");
+	if (error) err(1, "listen");
 
 	for (;;) {
 		int client = accept(server, NULL, NULL);
-		if (client < 0) err(EX_IOERR, "accept");
+		if (client < 0) err(1, "accept");
 
 		error = fcntl(client, F_SETFD, FD_CLOEXEC);
-		if (error) err(EX_IOERR, "fcntl");
+		if (error) err(1, "fcntl");
 
 		char c = 0;
 		ssize_t size = read(client, &c, 1);
@@ -91,7 +90,7 @@ static int pbd(void) {
 
 static int pbdClient(char c) {
 	int client = socket(PF_INET, SOCK_STREAM, 0);
-	if (client < 0) err(EX_OSERR, "socket");
+	if (client < 0) err(1, "socket");
 
 	struct sockaddr_in addr = {
 		.sin_family = AF_INET,
@@ -99,10 +98,10 @@ static int pbdClient(char c) {
 		.sin_addr = { .s_addr = htonl(0x7F000001) },
 	};
 	int error = connect(client, (struct sockaddr *)&addr, sizeof(addr));
-	if (error) err(EX_UNAVAILABLE, "connect");
+	if (error) err(1, "connect");
 
 	ssize_t size = write(client, &c, 1);
-	if (size < 0) err(EX_IOERR, "write");
+	if (size < 0) err(1, "write");
 
 	return client;
 }
@@ -112,29 +111,29 @@ static void copy(int out, int in) {
 	ssize_t readSize;
 	while (0 < (readSize = read(in, buf, sizeof(buf)))) {
 		ssize_t writeSize = write(out, buf, readSize);
-		if (writeSize < 0) err(EX_IOERR, "write(%d)", out);
+		if (writeSize < 0) err(1, "write(%d)", out);
 	}
-	if (readSize < 0) err(EX_IOERR, "read(%d)", in);
+	if (readSize < 0) err(1, "read(%d)", in);
 }
 
 static int pbcopy(void) {
 	int client = pbdClient('c');
 	copy(client, STDIN_FILENO);
-	return EX_OK;
+	return 0;
 }
 
 static int pbpaste(void) {
 	int client = pbdClient('p');
 	copy(STDOUT_FILENO, client);
-	return EX_OK;
+	return 0;
 }
 
 static int open1(const char *url) {
-	if (!url) return EX_USAGE;
+	if (!url) return 1;
 	int client = pbdClient('o');
 	ssize_t size = write(client, url, strlen(url));
-	if (size < 0) err(EX_IOERR, "write");
-	return EX_OK;
+	if (size < 0) err(1, "write");
+	return 0;
 }
 
 int main(int argc, char *argv[]) {
@@ -144,7 +143,7 @@ int main(int argc, char *argv[]) {
 			case 'o': return open1(optarg);
 			case 'p': return pbpaste();
 			case 's': return pbd();
-			default:  return EX_USAGE;
+			default:  return 1;
 		}
 	}
 	return pbd();
diff --git a/bin/png.h b/bin/png.h
index 15b6d13d..ec884395 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
@@ -18,7 +18,6 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sysexits.h>
 
 static inline uint32_t pngCRCTable(uint8_t n) {
 	static uint32_t table[256];
@@ -35,7 +34,7 @@ static inline uint32_t pngCRCTable(uint8_t n) {
 static uint32_t pngCRC;
 
 static inline void pngWrite(FILE *file, const uint8_t *ptr, uint32_t len) {
-	if (!fwrite(ptr, len, 1, file)) err(EX_IOERR, "pngWrite");
+	if (!fwrite(ptr, len, 1, file)) err(1, "pngWrite");
 	for (uint32_t i = 0; i < len; ++i) {
 		pngCRC = pngCRCTable(pngCRC ^ ptr[i]) ^ (pngCRC >> 8);
 	}
diff --git a/bin/pngo.c b/bin/pngo.c
index ba90889a..e2ad3837 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
@@ -22,7 +22,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #include <unistd.h>
 #include <zlib.h>
 
@@ -35,14 +34,14 @@ static uint32_t crc;
 
 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);
+	if (!n && ferror(file)) err(1, "%s", path);
+	if (!n) errx(1, "%s: missing %s", path, desc);
 	crc = crc32(crc, ptr, len);
 }
 
 static void pngWrite(const void *ptr, size_t len) {
 	size_t n = fwrite(ptr, len, 1, file);
-	if (!n) err(EX_IOERR, "%s", path);
+	if (!n) err(1, "%s", path);
 	crc = crc32(crc, ptr, len);
 }
 
@@ -52,7 +51,7 @@ 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);
+		errx(1, "%s: invalid signature", path);
 	}
 }
 
@@ -96,10 +95,7 @@ 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
-	);
+	errx(1, "%s: expected CRC32 %08X, found %08X", path, expect, actual);
 }
 
 static void crcWrite(void) {
@@ -108,7 +104,7 @@ static void crcWrite(void) {
 
 static void chunkSkip(struct Chunk chunk) {
 	if (!(chunk.type[0] & 0x20)) {
-		errx(EX_CONFIG, "%s: unsupported critical chunk %s", path, chunk.type);
+		errx(1, "%s: unsupported critical chunk %s", path, chunk.type);
 	}
 	uint8_t buf[4096];
 	while (chunk.len > sizeof(buf)) {
@@ -181,7 +177,7 @@ static void headerPrint(void) {
 static void headerRead(struct Chunk chunk) {
 	if (chunk.len != HeaderLen) {
 		errx(
-			EX_DATAERR, "%s: expected %s length %" PRIu32 ", found %" PRIu32,
+			1, "%s: expected %s length %" PRIu32 ", found %" PRIu32,
 			path, chunk.type, (uint32_t)HeaderLen, chunk.len
 		);
 	}
@@ -195,8 +191,8 @@ static void headerRead(struct Chunk chunk) {
 	crcRead();
 	recalc();
 
-	if (!header.width) errx(EX_DATAERR, "%s: invalid width 0", path);
-	if (!header.height) errx(EX_DATAERR, "%s: invalid height 0", path);
+	if (!header.width) errx(1, "%s: invalid width 0", path);
+	if (!header.height) errx(1, "%s: invalid height 0", path);
 	static const struct {
 		uint8_t color;
 		uint8_t depth;
@@ -228,28 +224,21 @@ static void headerRead(struct Chunk chunk) {
 	}
 	if (!valid) {
 		errx(
-			EX_DATAERR,
-			"%s: invalid color type %" PRIu8 " and bit depth %" PRIu8,
+			1, "%s: invalid color type %" PRIu8 " and bit depth %" PRIu8,
 			path, header.color, header.depth
 		);
 	}
 	if (header.compression != Deflate) {
 		errx(
-			EX_DATAERR, "%s: invalid compression method %" PRIu8,
+			1, "%s: invalid compression method %" PRIu8,
 			path, header.compression
 		);
 	}
 	if (header.filter != Adaptive) {
-		errx(
-			EX_DATAERR, "%s: invalid filter method %" PRIu8,
-			path, header.filter
-		);
+		errx(1, "%s: invalid filter method %" PRIu8, path, header.filter);
 	}
 	if (header.interlace > Adam7) {
-		errx(
-			EX_DATAERR, "%s: invalid interlace method %" PRIu8,
-			path, header.interlace
-		);
+		errx(1, "%s: invalid interlace method %" PRIu8, path, header.interlace);
 	}
 
 	if (verbose) headerPrint();
@@ -331,16 +320,13 @@ static void transCompact(void) {
 static void palRead(struct Chunk chunk) {
 	if (chunk.len % 3) {
 		errx(
-			EX_DATAERR, "%s: %s length %" PRIu32 " not divisible by 3",
+			1, "%s: %s length %" PRIu32 " not divisible by 3",
 			path, chunk.type, chunk.len
 		);
 	}
 	pal.len = chunk.len / 3;
 	if (pal.len > 256) {
-		errx(
-			EX_DATAERR, "%s: %s length %" PRIu32 " > 256",
-			path, chunk.type, pal.len
-		);
+		errx(1, "%s: %s length %" PRIu32 " > 256", path, chunk.type, pal.len);
 	}
 	pngRead(pal.rgb, chunk.len, "palette data");
 	crcRead();
@@ -362,10 +348,7 @@ static void palWrite(void) {
 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
-		);
+		errx(1, "%s: %s length %" PRIu32 " > 256", path, chunk.type, trans.len);
 	}
 	pngRead(trans.a, chunk.len, "transparency data");
 	crcRead();
@@ -388,7 +371,7 @@ static uint8_t *data;
 
 static void dataAlloc(void) {
 	data = malloc(dataLen);
-	if (!data) err(EX_OSERR, "malloc");
+	if (!data) err(1, "malloc");
 }
 
 static const char *humanize(size_t n) {
@@ -408,15 +391,15 @@ 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, "inflateInit: %s", stream.msg);
+	if (error != Z_OK) errx(1, "inflateInit: %s", stream.msg);
 
 	for (;;) {
 		if (strcmp(chunk.type, "IDAT")) {
-			errx(EX_DATAERR, "%s: missing IDAT chunk", path);
+			errx(1, "%s: missing IDAT chunk", path);
 		}
 
 		uint8_t *idat = malloc(chunk.len);
-		if (!idat) err(EX_OSERR, "malloc");
+		if (!idat) err(1, "malloc");
 
 		pngRead(idat, chunk.len, "image data");
 		crcRead();
@@ -428,7 +411,7 @@ static void dataRead(struct Chunk chunk) {
 
 		if (error == Z_STREAM_END) break;
 		if (error != Z_OK) {
-			errx(EX_DATAERR, "%s: inflate: %s", path, stream.msg);
+			errx(1, "%s: inflate: %s", path, stream.msg);
 		}
 
 		chunk = chunkRead();
@@ -436,7 +419,7 @@ static void dataRead(struct Chunk chunk) {
 	inflateEnd(&stream);
 	if ((size_t)stream.total_out != dataLen) {
 		errx(
-			EX_DATAERR, "%s: expected data length %zu, found %zu",
+			1, "%s: expected data length %zu, found %zu",
 			path, dataLen, (size_t)stream.total_out
 		);
 	}
@@ -461,11 +444,11 @@ static void dataWrite(void) {
 	int error = deflateInit2(
 		&stream, Z_BEST_COMPRESSION, Z_DEFLATED, 15, 8, Z_FILTERED
 	);
-	if (error != Z_OK) errx(EX_SOFTWARE, "deflateInit2: %s", stream.msg);
+	if (error != Z_OK) errx(1, "deflateInit2: %s", stream.msg);
 
 	uLong bound = deflateBound(&stream, dataLen);
 	uint8_t *buf = malloc(bound);
-	if (!buf) err(EX_OSERR, "malloc");
+	if (!buf) err(1, "malloc");
 
 	stream.next_out = buf;
 	stream.avail_out = bound;
@@ -566,7 +549,7 @@ 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");
+		if (!filter[i]) err(1, "malloc");
 	}
 	for (uint32_t y = header.height-1; y < header.height; --y) {
 		uint32_t heuristic[FilterCap] = {0};
@@ -838,7 +821,7 @@ static void optimize(const char *inPath, const char *outPath) {
 	if (inPath) {
 		path = inPath;
 		file = fopen(path, "r");
-		if (!file) err(EX_NOINPUT, "%s", path);
+		if (!file) err(1, "%s", path);
 	} else {
 		path = "stdin";
 		file = stdin;
@@ -847,11 +830,11 @@ static void optimize(const char *inPath, const char *outPath) {
 	sigRead();
 	struct Chunk ihdr = chunkRead();
 	if (strcmp(ihdr.type, "IHDR")) {
-		errx(EX_DATAERR, "%s: expected IHDR, found %s", path, ihdr.type);
+		errx(1, "%s: expected IHDR, found %s", path, ihdr.type);
 	}
 	headerRead(ihdr);
 	if (header.interlace != Progressive) {
-		errx(EX_CONFIG, "%s: unsupported interlacing", path);
+		errx(1, "%s: unsupported interlacing", path);
 	}
 
 	palClear();
@@ -888,10 +871,10 @@ static void optimize(const char *inPath, const char *outPath) {
 		if (outPath == inPath) {
 			snprintf(buf, sizeof(buf), "%so", outPath);
 			file = fopen(buf, "wx");
-			if (!file) err(EX_CANTCREAT, "%s", buf);
+			if (!file) err(1, "%s", buf);
 		} else {
 			file = fopen(path, "w");
-			if (!file) err(EX_CANTCREAT, "%s", outPath);
+			if (!file) err(1, "%s", outPath);
 		}
 	} else {
 		path = "stdout";
@@ -907,11 +890,11 @@ static void optimize(const char *inPath, const char *outPath) {
 	dataWrite();
 	free(data);
 	int error = fclose(file);
-	if (error) err(EX_IOERR, "%s", path);
+	if (error) err(1, "%s", path);
 
 	if (outPath && outPath == inPath) {
 		error = rename(buf, outPath);
-		if (error) err(EX_CANTCREAT, "%s", outPath);
+		if (error) err(1, "%s", outPath);
 	}
 }
 
@@ -927,7 +910,7 @@ int main(int argc, char *argv[]) {
 			break; case 'g': discardColor = true;
 			break; case 'o': outPath = optarg;
 			break; case 'v': verbose = true;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 
diff --git a/bin/psf2png.c b/bin/psf2png.c
index 1aaa8635..aeb975b3 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
@@ -19,7 +19,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #include <unistd.h>
 
 #include "png.h"
@@ -37,17 +36,17 @@ int main(int argc, char *argv[]) {
 			break; case 'c': cols = strtoul(optarg, NULL, 0);
 			break; case 'f': fg = strtoul(optarg, NULL, 16);
 			break; case 's': str = optarg;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 	if (!cols && str) cols = strlen(str);
-	if (!cols) return EX_USAGE;
+	if (!cols) return 1;
 
 	const char *path = NULL;
 	if (optind < argc) path = argv[optind];
 	
 	FILE *file = path ? fopen(path, "r") : stdin;
-	if (!file) err(EX_NOINPUT, "%s", path);
+	if (!file) err(1, "%s", path);
 	if (!path) path = "(stdin)";
 
 	struct {
@@ -63,15 +62,15 @@ int main(int argc, char *argv[]) {
 		} glyph;
 	} header;
 	size_t len = fread(&header, sizeof(header), 1, file);
-	if (ferror(file)) err(EX_IOERR, "%s", path);
-	if (len < 1) errx(EX_DATAERR, "%s: truncated header", path);
+	if (ferror(file)) err(1, "%s", path);
+	if (len < 1) errx(1, "%s: truncated header", path);
 
 	uint32_t widthBytes = (header.glyph.width + 7) / 8;
 	uint8_t glyphs[header.glyph.len][header.glyph.height][widthBytes];
 	len = fread(glyphs, header.glyph.size, header.glyph.len, file);
-	if (ferror(file)) err(EX_IOERR, "%s", path);
+	if (ferror(file)) err(1, "%s", path);
 	if (len < header.glyph.len) {
-		errx(EX_DATAERR, "%s: truncated glyphs", path);
+		errx(1, "%s: truncated glyphs", path);
 	}
 	fclose(file);
 
diff --git a/bin/ptee.c b/bin/ptee.c
index 8374bd8f..c4749d62 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
@@ -24,7 +24,6 @@
 #include <sys/ioctl.h>
 #include <sys/time.h>
 #include <sys/wait.h>
-#include <sysexits.h>
 #include <termios.h>
 #include <unistd.h>
 
@@ -52,35 +51,35 @@ int main(int argc, char *argv[]) {
 	for (int opt; 0 < (opt = getopt(argc, argv, "t:"));) {
 		switch (opt) {
 			break; case 't': timer = atoi(optarg);
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 	argc -= optind;
 	argv += optind;
 
-	if (argc < 1) return EX_USAGE;
-	if (isatty(STDOUT_FILENO)) errx(EX_USAGE, "stdout is not redirected");
+	if (argc < 1) return 1;
+	if (isatty(STDOUT_FILENO)) errx(1, "stdout is not redirected");
 
 	int error = tcgetattr(STDIN_FILENO, &saveTerm);
-	if (error) err(EX_IOERR, "tcgetattr");
+	if (error) err(1, "tcgetattr");
 	atexit(restoreTerm);
 
 	struct termios raw = saveTerm;
 	cfmakeraw(&raw);
 	error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw);
-	if (error) err(EX_IOERR, "tcsetattr");
+	if (error) err(1, "tcsetattr");
 
 	struct winsize window;
 	error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
-	if (error) err(EX_IOERR, "ioctl");
+	if (error) err(1, "ioctl");
 
 	int pty;
 	pid_t pid = forkpty(&pty, NULL, NULL, &window);
-	if (pid < 0) err(EX_OSERR, "forkpty");
+	if (pid < 0) err(1, "forkpty");
 
 	if (!pid) {
 		execvp(argv[0], argv);
-		err(EX_NOINPUT, "%s", argv[0]);
+		err(1, "%s", argv[0]);
 	}
 
 	if (timer) {
@@ -103,17 +102,17 @@ int main(int argc, char *argv[]) {
 	};
 	for (;;) {
 		int nfds = poll(fds, 2, -1);
-		if (nfds < 0 && errno != EINTR) err(EX_IOERR, "poll");
+		if (nfds < 0 && errno != EINTR) err(1, "poll");
 
 		if (nfds < 0) {
 			ssize_t wlen = write(STDOUT_FILENO, mc, sizeof(mc) - 1);
-			if (wlen < 0) err(EX_IOERR, "write");
+			if (wlen < 0) err(1, "write");
 			continue;
 		}
 
 		if (fds[0].revents & POLLIN) {
 			ssize_t rlen = read(STDIN_FILENO, buf, sizeof(buf));
-			if (rlen < 0) err(EX_IOERR, "read");
+			if (rlen < 0) err(1, "read");
 
 			if (rlen == 1 && buf[0] == CTRL('Q')) {
 				stop ^= true;
@@ -122,30 +121,30 @@ int main(int argc, char *argv[]) {
 
 			if (rlen == 1 && buf[0] == CTRL('S')) {
 				ssize_t wlen = write(STDOUT_FILENO, mc, sizeof(mc) - 1);
-				if (wlen < 0) err(EX_IOERR, "write");
+				if (wlen < 0) err(1, "write");
 				continue;
 			}
 
 			ssize_t wlen = write(pty, buf, rlen);
-			if (wlen < 0) err(EX_IOERR, "write");
+			if (wlen < 0) err(1, "write");
 		}
 
 		if (fds[1].revents & POLLIN) {
 			ssize_t rlen = read(pty, buf, sizeof(buf));
-			if (rlen < 0) err(EX_IOERR, "read");
+			if (rlen < 0) err(1, "read");
 
 			ssize_t wlen = write(STDIN_FILENO, buf, rlen);
-			if (wlen < 0) err(EX_IOERR, "write");
+			if (wlen < 0) err(1, "write");
 
 			if (!stop) {
 				wlen = write(STDOUT_FILENO, buf, rlen);
-				if (wlen < 0) err(EX_IOERR, "write");
+				if (wlen < 0) err(1, "write");
 			}
 		}
 
 		int status;
 		pid_t dead = waitpid(pid, &status, WNOHANG);
-		if (dead < 0) err(EX_OSERR, "waitpid");
-		if (dead) return WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE;
+		if (dead < 0) err(1, "waitpid");
+		if (dead) return WIFEXITED(status) ? WEXITSTATUS(status) : 1;
 	}
 }
diff --git a/bin/qf.c b/bin/qf.c
index 1fbf48b9..afa7eced 100644
--- a/bin/qf.c
+++ b/bin/qf.c
@@ -24,7 +24,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/wait.h>
-#include <sysexits.h>
 #include <unistd.h>
 
 enum Type {
@@ -51,7 +50,7 @@ static void push(struct Line line) {
 	if (lines.len == lines.cap) {
 		lines.cap = (lines.cap ? lines.cap * 2 : 256);
 		lines.ptr = realloc(lines.ptr, sizeof(*lines.ptr) * lines.cap);
-		if (!lines.ptr) err(EX_OSERR, "realloc");
+		if (!lines.ptr) err(1, "realloc");
 	}
 	lines.ptr[lines.len++] = line;
 }
@@ -176,15 +175,15 @@ static void edit(struct Line line) {
 	const char *editor = getenv("EDITOR");
 	if (!editor) editor = "vi";
 	pid_t pid = fork();
-	if (pid < 0) err(EX_OSERR, "fork");
+	if (pid < 0) err(1, "fork");
 	if (!pid) {
 		dup2(STDERR_FILENO, STDIN_FILENO);
 		execlp(editor, editor, cmd, line.path, NULL);
-		err(EX_CONFIG, "%s", editor);
+		err(127, "%s", editor);
 	}
 	int status;
 	pid = waitpid(pid, &status, 0);
-	if (pid < 0) err(EX_OSERR, "waitpid");
+	if (pid < 0) err(1, "waitpid");
 }
 
 static void toPrev(enum Type type) {
@@ -228,7 +227,7 @@ static void input(void) {
 			break; case 'n': toNext(Match);
 			break; case 'q': {
 				endwin();
-				exit(EX_OK);
+				exit(0);
 			}
 			break; case 'r': clearok(stdscr, true);
 		}
@@ -238,7 +237,7 @@ static void input(void) {
 }
 
 int main(int argc, char *argv[]) {
-	if (isatty(STDIN_FILENO)) errx(EX_USAGE, "no input");
+	if (isatty(STDIN_FILENO)) errx(1, "no input");
 	if (argc > 1) {
 		pattern = argv[1];
 		int flags = REG_EXTENDED | REG_ICASE;
@@ -249,7 +248,7 @@ int main(int argc, char *argv[]) {
 			}
 		}
 		int error = regcomp(&regex, pattern, flags);
-		if (error) errx(EX_USAGE, "invalid pattern");
+		if (error) errx(1, "invalid pattern");
 	}
 	curse();
 	draw();
@@ -260,14 +259,14 @@ int main(int argc, char *argv[]) {
 	size_t len = 0;
 	size_t cap = 4096;
 	char *buf = malloc(cap);
-	if (!buf) err(EX_OSERR, "malloc");
+	if (!buf) err(1, "malloc");
 	while (poll(fds, (reading ? 2 : 1), -1)) {
 		if (fds[0].revents) {
 			input();
 		}
 		if (reading && fds[1].revents) {
 			ssize_t n = read(fds[1].fd, &buf[len], cap - len);
-			if (n < 0) err(EX_IOERR, "read");
+			if (n < 0) err(1, "read");
 			if (!n) reading = false;
 			len += n;
 			char *ptr = buf;
@@ -277,7 +276,7 @@ int main(int argc, char *argv[]) {
 				ptr = &nl[1]
 			) {
 				struct Line line = { .text = strndup(ptr, nl - ptr) };
-				if (!line.text) err(EX_OSERR, "strndup");
+				if (!line.text) err(1, "strndup");
 				parse(line);
 			}
 			len -= ptr - buf;
@@ -285,10 +284,10 @@ int main(int argc, char *argv[]) {
 			if (len == cap) {
 				cap *= 2;
 				buf = realloc(buf, cap);
-				if (!buf) err(EX_OSERR, "realloc");
+				if (!buf) err(1, "realloc");
 			}
 		}
 		draw();
 	}
-	err(EX_IOERR, "poll");
+	err(1, "poll");
 }
diff --git a/bin/quick.c b/bin/quick.c
index edf35a3b..96f29eb0 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
@@ -26,13 +26,12 @@
 #include <strings.h>
 #include <sys/socket.h>
 #include <sys/wait.h>
-#include <sysexits.h>
 #include <unistd.h>
 
 static void request(int sock, char *argv[]) {
 	struct pollfd pfd = { .fd = sock, .events = POLLIN };
 	int nfds = poll(&pfd, 1, -1);
-	if (nfds < 0) err(EX_OSERR, "poll");
+	if (nfds < 0) err(1, "poll");
 
 	char buf[4096];
 	ssize_t len = recv(sock, buf, sizeof(buf)-1, MSG_PEEK);
@@ -89,7 +88,7 @@ static void request(int sock, char *argv[]) {
 
 	dprintf(sock, "HTTP/1.1 200 OK\nConnection: close\n");
 	pid_t pid = fork();
-	if (pid < 0) err(EX_OSERR, "fork");
+	if (pid < 0) err(1, "fork");
 	if (!pid) {
 		dup2(sock, STDIN_FILENO);
 		dup2(sock, STDOUT_FILENO);
@@ -100,7 +99,7 @@ static void request(int sock, char *argv[]) {
 
 	int status;
 	pid = wait(&status);
-	if (pid < 0) err(EX_OSERR, "wait");
+	if (pid < 0) err(1, "wait");
 	if (WIFEXITED(status) && WEXITSTATUS(status)) {
 		warnx("%s exited %d", argv[0], WEXITSTATUS(status));
 	} else if (WIFSIGNALED(status)) {
@@ -113,13 +112,13 @@ int main(int argc, char *argv[]) {
 	for (int opt; 0 < (opt = getopt(argc, argv, "p:"));) {
 		switch (opt) {
 			break; case 'p': port = atoi(optarg);
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
-	if (optind == argc) errx(EX_USAGE, "script required");
+	if (optind == argc) errx(1, "script required");
 
 	int server = socket(AF_INET, SOCK_STREAM, 0);
-	if (server < 0) err(EX_OSERR, "socket");
+	if (server < 0) err(1, "socket");
 	fcntl(server, F_SETFD, FD_CLOEXEC);
 
 	int on = 1;
@@ -135,7 +134,7 @@ int main(int argc, char *argv[]) {
 		|| bind(server, (struct sockaddr *)&addr, addrlen)
 		|| getsockname(server, (struct sockaddr *)&addr, &addrlen)
 		|| listen(server, -1);
-	if (error) err(EX_UNAVAILABLE, "%hd", port);
+	if (error) err(1, "%hd", port);
 
 	char host[NI_MAXHOST], serv[NI_MAXSERV];
 	error = getnameinfo(
@@ -143,7 +142,7 @@ int main(int argc, char *argv[]) {
 		host, sizeof(host), serv, sizeof(serv),
 		NI_NOFQDN | NI_NUMERICSERV
 	);
-	if (error) errx(EX_UNAVAILABLE, "getnameinfo: %s", gai_strerror(error));
+	if (error) errx(1, "getnameinfo: %s", gai_strerror(error));
 	printf("http://%s:%s/\n", host, serv);
 	fflush(stdout);
 
@@ -159,5 +158,5 @@ int main(int argc, char *argv[]) {
 		fcntl(sock, F_SETFD, FD_CLOEXEC);
 		request(sock, &argv[optind]);
 	}
-	err(EX_IOERR, "accept");
+	err(1, "accept");
 }
diff --git a/bin/relay.c b/bin/relay.c
index aa7913c9..a1a134d4 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
@@ -35,7 +35,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
-#include <sysexits.h>
 #include <tls.h>
 #include <unistd.h>
 
@@ -47,7 +46,7 @@ static void clientWrite(struct tls *client, const char *ptr, size_t len) {
 	while (len) {
 		ssize_t ret = tls_write(client, ptr, len);
 		if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue;
-		if (ret < 0) errx(EX_IOERR, "tls_write: %s", tls_error(client));
+		if (ret < 0) errx(1, "tls_write: %s", tls_error(client));
 		ptr += ret;
 		len -= ret;
 	}
@@ -59,7 +58,7 @@ static void clientFormat(struct tls *client, const char *format, ...) {
 	va_start(ap, format);
 	int len = vsnprintf(buf, sizeof(buf), format, ap);
 	va_end(ap);
-	if ((size_t)len > sizeof(buf) - 1) errx(EX_DATAERR, "message too large");
+	if ((size_t)len > sizeof(buf) - 1) errx(1, "message too large");
 	clientWrite(client, buf, len);
 }
 
@@ -67,7 +66,7 @@ static void clientHandle(struct tls *client, const char *chan, char *line) {
 	char *prefix = NULL;
 	if (line[0] == ':') {
 		prefix = strsep(&line, " ") + 1;
-		if (!line) errx(EX_PROTOCOL, "unexpected eol");
+		if (!line) errx(1, "unexpected eol");
 	}
 
 	char *command = strsep(&line, " ");
@@ -78,14 +77,14 @@ static void clientHandle(struct tls *client, const char *chan, char *line) {
 	}
 	if (strcmp(command, "PRIVMSG") && strcmp(command, "NOTICE")) return;
 
-	if (!prefix) errx(EX_PROTOCOL, "message without prefix");
+	if (!prefix) errx(1, "message without prefix");
 	char *nick = strsep(&prefix, "!");
 
-	if (!line) errx(EX_PROTOCOL, "message without destination");
+	if (!line) errx(1, "message without destination");
 	char *dest = strsep(&line, " ");
 	if (strcmp(dest, chan)) return;
 
-	if (!line || line[0] != ':') errx(EX_PROTOCOL, "message without message");
+	if (!line || line[0] != ':') errx(1, "message without message");
 	line = &line[1];
 
 	if (!strncmp(line, "\1ACTION ", 8)) {
@@ -102,14 +101,14 @@ static void clientHandle(struct tls *client, const char *chan, char *line) {
 #ifdef __FreeBSD__
 static void limit(int fd, const cap_rights_t *rights) {
 	int error = cap_rights_limit(fd, rights);
-	if (error) err(EX_OSERR, "cap_rights_limit");
+	if (error) err(1, "cap_rights_limit");
 }
 #endif
 
 int main(int argc, char *argv[]) {
 	int error;
 
-	if (argc < 5) return EX_USAGE;
+	if (argc < 5) return 1;
 	const char *host = argv[1];
 	const char *port = argv[2];
 	const char *nick = argv[3];
@@ -119,18 +118,18 @@ int main(int argc, char *argv[]) {
 	signal(SIGPIPE, SIG_IGN);
 
 	struct tls_config *config = tls_config_new();
-	if (!config) errx(EX_SOFTWARE, "tls_config_new");
+	if (!config) errx(1, "tls_config_new");
 
 	error = tls_config_set_ciphers(config, "compat");
 	if (error) {
-		errx(EX_SOFTWARE, "tls_config_set_ciphers: %s", tls_config_error(config));
+		errx(1, "tls_config_set_ciphers: %s", tls_config_error(config));
 	}
 
 	struct tls *client = tls_client();
-	if (!client) errx(EX_SOFTWARE, "tls_client");
+	if (!client) errx(1, "tls_client");
 
 	error = tls_configure(client, config);
-	if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client));
+	if (error) errx(1, "tls_configure: %s", tls_error(client));
 	tls_config_free(config);
 
 	struct addrinfo *head;
@@ -140,12 +139,12 @@ int main(int argc, char *argv[]) {
 		.ai_protocol = IPPROTO_TCP,
 	};
 	error = getaddrinfo(host, port, &hints, &head);
-	if (error) errx(EX_NOHOST, "getaddrinfo: %s", gai_strerror(error));
+	if (error) errx(1, "getaddrinfo: %s", gai_strerror(error));
 
 	int sock = -1;
 	for (struct addrinfo *ai = head; ai; ai = ai->ai_next) {
 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-		if (sock < 0) err(EX_OSERR, "socket");
+		if (sock < 0) err(1, "socket");
 
 		error = connect(sock, ai->ai_addr, ai->ai_addrlen);
 		if (!error) break;
@@ -153,15 +152,15 @@ int main(int argc, char *argv[]) {
 		close(sock);
 		sock = -1;
 	}
-	if (sock < 0) err(EX_UNAVAILABLE, "connect");
+	if (sock < 0) err(1, "connect");
 	freeaddrinfo(head);
 
 	error = tls_connect_socket(client, sock, host);
-	if (error) errx(EX_PROTOCOL, "tls_connect: %s", tls_error(client));
+	if (error) errx(1, "tls_connect: %s", tls_error(client));
 
 #ifdef __FreeBSD__
 	error = cap_enter();
-	if (error) err(EX_OSERR, "cap_enter");
+	if (error) err(1, "cap_enter");
 
 	cap_rights_t rights;
 	cap_rights_init(&rights, CAP_WRITE);
@@ -190,7 +189,7 @@ int main(int argc, char *argv[]) {
 	while (0 < poll(fds, 2, -1)) {
 		if (fds[0].revents) {
 			ssize_t len = getline(&input, &cap, stdin);
-			if (len < 0) err(EX_IOERR, "getline");
+			if (len < 0) err(1, "getline");
 			input[len - 1] = '\0';
 			clientFormat(client, "NOTICE %s :%s\r\n", chan, input);
 		}
@@ -198,8 +197,8 @@ int main(int argc, char *argv[]) {
 
 		ssize_t read = tls_read(client, &buf[len], sizeof(buf) - len);
 		if (read == TLS_WANT_POLLIN || read == TLS_WANT_POLLOUT) continue;
-		if (read < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client));
-		if (!read) return EX_UNAVAILABLE;
+		if (read < 0) errx(1, "tls_read: %s", tls_error(client));
+		if (!read) return 1;
 		len += read;
 
 		char *crlf;
@@ -214,5 +213,5 @@ int main(int argc, char *argv[]) {
 		len -= line - buf;
 		memmove(buf, line, len);
 	}
-	err(EX_IOERR, "poll");
+	err(1, "poll");
 }
diff --git a/bin/scheme.c b/bin/scheme.c
index 33fd8b60..82539ba2 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
@@ -19,7 +19,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #include <unistd.h>
 
 #include "png.h"
@@ -263,14 +262,14 @@ int main(int argc, char *argv[]) {
 			break; case 'm': output = outputMintty;
 			break; case 'p': {
 				uint p = strtoul(optarg, NULL, 0);
-				if (p >= SchemeLen) return EX_USAGE;
+				if (p >= SchemeLen) return 1;
 				hsv = &scheme[p];
 				len = 1;
 			}
 			break; case 's': output = outputCSS;
 			break; case 't': len = SchemeLen;
 			break; case 'x': output = outputRGB;
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 
diff --git a/bin/shotty.l b/bin/shotty.l
index ff78d241..b6d54eee 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
@@ -26,7 +26,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
-#include <sysexits.h>
 #include <unistd.h>
 #include <wchar.h>
 
@@ -181,13 +180,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
@@ -554,25 +553,25 @@ int main(int argc, char *argv[]) {
 			break; case 'n': hide = true;
 			break; case 's': size = true;
 			break; case 'w': cols = atoi(optarg);
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 	if (optind < argc) {
 		yyin = fopen(argv[optind], "r");
-		if (!yyin) err(EX_NOINPUT, "%s", argv[optind]);
+		if (!yyin) err(1, "%s", argv[optind]);
 	}
 
 	if (size) {
 		struct winsize win;
 		int error = ioctl(STDERR_FILENO, TIOCGWINSZ, &win);
-		if (error) err(EX_IOERR, "ioctl");
+		if (error) err(1, "ioctl");
 		cols = win.ws_col;
 		rows = win.ws_row;
 	}
 	scr.bot = rows;
 
 	cells = calloc(cols * rows, sizeof(*cells));
-	if (!cells) err(EX_OSERR, "calloc");
+	if (!cells) err(1, "calloc");
 	erase(cell(0, 0), cell(rows-1, cols));
 
 	bool mc = false;
diff --git a/bin/title.c b/bin/title.c
index 82f89d95..f40f5c87 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
@@ -22,7 +22,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sysexits.h>
 #include <unistd.h>
 #include <wchar.h>
 
@@ -33,7 +32,7 @@ static regex_t regex(const char *pattern, int flags) {
 
 	char buf[256];
 	regerror(error, &regex, buf, sizeof(buf));
-	errx(EX_SOFTWARE, "regcomp: %s: %s", buf, pattern);
+	errx(1, "regcomp: %s: %s", buf, pattern);
 }
 
 static const struct Entity {
@@ -128,7 +127,7 @@ static CURLcode fetchTitle(const char *url) {
 	char *dest;
 	curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &dest);
 	dest = strdup(dest);
-	if (!dest) err(EX_OSERR, "strdup");
+	if (!dest) err(1, "strdup");
 
 	code = curl_easy_setopt(curl, CURLOPT_URL, dest);
 	if (code) return code;
@@ -149,10 +148,10 @@ int main(int argc, char *argv[]) {
 	setlinebuf(stdout);
 
 	CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
-	if (code) errx(EX_OSERR, "curl_global_init: %s", curl_easy_strerror(code));
+	if (code) errx(1, "curl_global_init: %s", curl_easy_strerror(code));
 
 	curl = curl_easy_init();
-	if (!curl) errx(EX_SOFTWARE, "curl_easy_init");
+	if (!curl) errx(1, "curl_easy_init");
 
 	static char error[CURL_ERROR_SIZE];
 	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
@@ -180,14 +179,14 @@ int main(int argc, char *argv[]) {
 				excludeRegex = regex(optarg, REG_NOSUB);
 			}
 			break; case 'v': curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
-			break; default:  return EX_USAGE;
+			break; default:  return 1;
 		}
 	}
 
 	if (optind < argc) {
 		code = fetchTitle(argv[optind]);
-		if (!code) return EX_OK;
-		errx(EX_DATAERR, "curl_easy_perform: %s", error);
+		if (!code) return 0;
+		errx(1, "curl_easy_perform: %s", error);
 	}
 
 	char *buf = NULL;
@@ -207,5 +206,5 @@ int main(int argc, char *argv[]) {
 			ptr[match.rm_eo] = ' ';
 		}
 	}
-	if (ferror(stdin)) err(EX_IOERR, "getline");
+	if (ferror(stdin)) err(1, "getline");
 }
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..1d3795ad 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,10 +18,12 @@
 
 #include <ctype.h>
 #include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <strings.h>
-#include <sysexits.h>
 #include <time.h>
 
 static void yyerror(const char *str);
@@ -30,12 +32,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 };
@@ -43,14 +46,14 @@ static const struct tm Week = { .tm_mday = 7 };
 static struct tm normalize(struct tm date) {
 	time_t time = timegm(&date);
 	struct tm *norm = gmtime(&time);
-	if (!norm) err(EX_OSERR, "gmtime");
+	if (!norm) err(1, "gmtime");
 	return *norm;
 }
 
 static struct tm today(void) {
 	time_t now = time(NULL);
 	struct tm *local = localtime(&now);
-	if (!local) err(EX_OSERR, "localtime");
+	if (!local) err(1, "localtime");
 	struct tm date = {
 		.tm_year = local->tm_year,
 		.tm_mon = local->tm_mon,
@@ -117,7 +120,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 +136,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(1, "realloc");
+	}
+	dates.ptr[dates.len] = date;
+	dates.ptr[dates.len].tm_zone = strdup(name);
+	if (!dates.ptr[dates.len].tm_zone) err(1, "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,7 +202,8 @@ static void printScalar(struct tm scalar) {
 
 %}
 
-%token Number Month Day
+%token Name Number Month Day
+%right '='
 %left '+' '-'
 %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(1, "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(1, "%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 0;
+		}
 	}
 
 	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..89966a38 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
@@ -19,7 +19,6 @@
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sysexits.h>
 #include <unistd.h>
 
 typedef unsigned char byte;
@@ -105,7 +104,7 @@ static void undump(FILE *file) {
 	while (0 < (match = fscanf(file, " %hhx", &c))) {
 		printf("%c", c);
 	}
-	if (!match) errx(EX_DATAERR, "invalid input");
+	if (!match) errx(1, "invalid input");
 }
 
 int main(int argc, char *argv[]) {
@@ -122,21 +121,21 @@ int main(int argc, char *argv[]) {
 			break; case 'r': reverse = true;
 			break; case 's': options.offset ^= true;
 			break; case 'z': options.skip ^= true;
-			break; default: return EX_USAGE;
+			break; default: return 1;
 		}
 	}
 	if (argc > optind) path = argv[optind];
-	if (!options.cols) return EX_USAGE;
+	if (!options.cols) return 1;
 
 	FILE *file = path ? fopen(path, "r") : stdin;
-	if (!file) err(EX_NOINPUT, "%s", path);
+	if (!file) err(1, "%s", path);
 
 	if (reverse) {
 		undump(file);
 	} else {
 		dump(file);
 	}
-	if (ferror(file)) err(EX_IOERR, "%s", path);
+	if (ferror(file)) err(1, "%s", path);
 
-	return EX_OK;
+	return 0;
 }