summary refs log tree commit diff
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rw-r--r--bin/.gitignore4
-rw-r--r--bin/Makefile15
-rw-r--r--bin/README.718
-rw-r--r--bin/bri.c85
-rw-r--r--bin/fbatt.c123
-rw-r--r--bin/fbclock.c132
-rw-r--r--bin/html.mk1
-rw-r--r--bin/man1/bri.144
-rw-r--r--bin/man1/fbatt.134
-rw-r--r--bin/man1/fbclock.136
-rw-r--r--bin/man1/psfed.1166
-rw-r--r--bin/psfed.c577
12 files changed, 5 insertions, 1230 deletions
diff --git a/bin/.gitignore b/bin/.gitignore
index 02f0536f..5f3bf0c7 100644
--- a/bin/.gitignore
+++ b/bin/.gitignore
@@ -3,15 +3,12 @@
 beef
 bibsort
 bit
-bri
 c
 config.mk
 dehtml
 downgrade
 dtch
 ever
-fbatt
-fbclock
 freecell
 git-comment
 glitch
@@ -25,7 +22,6 @@ order
 pbd
 pngo
 psf2png
-psfed
 ptee
 relay
 scheme
diff --git a/bin/Makefile b/bin/Makefile
index 8564f59b..9fe60278 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -37,18 +37,12 @@ BSD += ever
 
 GAMES += freecell
 
-LINUX += bri
-LINUX += fbatt
-LINUX += fbclock
-LINUX += psfed
-
 TLS += downgrade
 TLS += relay
 
 MANS = ${BINS:%=man1/%.1}
 MANS.BSD = ${BSD:%=man1/%.1}
 MANS.GAMES = ${GAMES:%=man6/%.6}
-MANS.LINUX = ${LINUX:%=man1/%.1}
 MANS.TLS = ${TLS:%=man1/%.1}
 
 LDLIBS.downgrade = -ltls
@@ -78,12 +72,10 @@ bsd: ${BSD}
 
 games: ${GAMES}
 
-linux: ${LINUX}
-
 tls: ${TLS}
 
 IGNORE = *.o *.html
-IGNORE += ${BINS} ${BSD} ${GAMES} ${LINUX} ${TLS}
+IGNORE += ${BINS} ${BSD} ${GAMES} ${TLS}
 IGNORE += scheme.h tags htmltags
 
 .gitignore: Makefile
@@ -112,10 +104,6 @@ install-games: install-meta ${GAMES} ${MANS.GAMES}
 	install ${GAMES} ${PREFIX}/bin
 	install -m 644 ${MANS.GAMES} ${MANDIR}/man6
 
-install-linux: install-meta ${LINUX} ${MANS.LINUX}
-	install ${LINUX} ${PREFIX}/bin
-	install -m 644 ${MANS.LINUX} ${MANDIR}/man1
-
 install-tls: install-meta ${TLS} ${MANS.TLS}
 	install ${TLS} ${PREFIX}/bin
 	install -m 644 ${MANS.TLS} ${MANDIR}/man1
@@ -124,7 +112,6 @@ uninstall:
 	rm -f ${BINS:%=${PREFIX}/bin/%} ${MANS:%=${MANDIR}/%}
 	rm -f ${BSD:%=${PREFIX}/bin/%} ${MANS.BSD:%=${MANDIR}/%}
 	rm -f ${GAMES:%=${PREFIX}/bin/%} ${MANS.GAMES:%=${MANDIR}/%}
-	rm -f ${LINUX:%=${PREFIX}/bin/%} ${MANS.LINUX:%=${MANDIR}/%}
 	rm -f ${TLS:%=${PREFIX}/bin/%} ${MANS.TLS:%=${MANDIR}/%}
 
 .SUFFIXES: .pl
diff --git a/bin/README.7 b/bin/README.7
index f3407978..be4c6a31 100644
--- a/bin/README.7
+++ b/bin/README.7
@@ -1,4 +1,4 @@
-.Dd September 15, 2021
+.Dd September 22, 2021
 .Dt BIN 7
 .Os "Causal Agency"
 .
@@ -8,11 +8,9 @@
 .
 .Sh DESCRIPTION
 Various tools primarily targeting
-Darwin,
-.Fx
-and
-.Ox .
-Some tools target Linux.
+.Fx ,
+.Ox
+and macOS.
 .
 .Pp
 .Bl -tag -width "git-comment(1)" -compact
@@ -22,8 +20,6 @@ Befunge-93 interpreter
 reformat bibliography
 .It Xr bit 1
 calculator
-.It Xr bri 1
-backlight brightness control
 .It Xr c 1
 run C statements
 .It Xr dehtml 1
@@ -34,10 +30,6 @@ IRC features for all
 detached sessions
 .It Xr ever 1
 watch files
-.It Xr fbatt 1
-framebuffer battery indicator
-.It Xr fbclock 1
-framebuffer clock
 .It Xr freecell 6
 patience game
 .It Xr git-comment 1
@@ -62,8 +54,6 @@ macOS pasteboard daemon
 PNG optimizer
 .It Xr psf2png 1
 PSF2 to PNG renderer
-.It Xr psfed 1
-PSF2 font editor
 .It Xr ptee 1
 tee for PTYs
 .It Xr relay 1
diff --git a/bin/bri.c b/bin/bri.c
deleted file mode 100644
index 81df2995..00000000
--- a/bin/bri.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Copyright (C) 2017  C. 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
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <dirent.h>
-#include <err.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sysexits.h>
-#include <unistd.h>
-
-typedef unsigned uint;
-
-static const char *Class = "/sys/class/backlight";
-
-int main(int argc, char *argv[]) {
-	int error;
-
-	const char *input = (argc > 1) ? argv[1] : NULL;
-
-	error = chdir(Class);
-	if (error) err(EX_OSFILE, "%s", Class);
-
-	DIR *dir = opendir(".");
-	if (!dir) err(EX_OSFILE, "%s", Class);
-
-	struct dirent *entry;
-	while (NULL != (errno = 0, entry = readdir(dir))) {
-		if (entry->d_name[0] == '.') continue;
-
-		error = chdir(entry->d_name);
-		if (error) err(EX_OSFILE, "%s/%s", Class, entry->d_name);
-		break;
-	}
-	if (!entry) {
-		if (errno) err(EX_IOERR, "%s", Class);
-		errx(EX_CONFIG, "%s: empty", Class);
-	}
-
-	FILE *actual = fopen("actual_brightness", "r");
-	if (!actual) err(EX_OSFILE, "actual_brightness");
-
-	uint value;
-	int match = fscanf(actual, "%u", &value);
-	if (match == EOF) err(EX_IOERR, "actual_brightness");
-	if (match < 1) errx(EX_DATAERR, "actual_brightness");
-
-	if (!input) {
-		printf("%u\n", value);
-		return EX_OK;
-	}
-
-	if (input[0] == '+' || input[0] == '-') {
-		size_t count = strnlen(input, 16);
-		if (input[0] == '+') {
-			value += 16 * count;
-		} else {
-			value -= 16 * count;
-		}
-	} else {
-		value = strtoul(input, NULL, 0);
-	}
-
-	FILE *brightness = fopen("brightness", "w");
-	if (!brightness) err(EX_OSFILE, "brightness");
-
-	int size = fprintf(brightness, "%u", value);
-	if (size < 0) err(EX_IOERR, "brightness");
-
-	return EX_OK;
-}
diff --git a/bin/fbatt.c b/bin/fbatt.c
deleted file mode 100644
index 9feffe47..00000000
--- a/bin/fbatt.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/* Copyright (C) 2018  C. 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
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <dirent.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/fb.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sysexits.h>
-#include <unistd.h>
-
-#include "scheme.h"
-
-static const char *Class = "/sys/class/power_supply";
-
-static const uint32_t Right  = 5 * 8 + 1; // fbclock width.
-static const uint32_t Width  = 8;
-static const uint32_t Height = 16;
-
-int main() {
-	int error;
-
-	DIR *dir = opendir(Class);
-	if (!dir) err(EX_OSFILE, "%s", Class);
-
-	FILE *chargeFull = NULL;
-	FILE *chargeNow = NULL;
-
-	const struct dirent *entry;
-	while (NULL != (errno = 0, entry = readdir(dir))) {
-		if (entry->d_name[0] == '.') continue;
-
-		error = chdir(Class);
-		if (error) err(EX_OSFILE, "%s", Class);
-
-		error = chdir(entry->d_name);
-		if (error) err(EX_OSFILE, "%s/%s", Class, entry->d_name);
-
-		chargeFull = fopen("charge_full", "r");
-		chargeNow = fopen("charge_now", "r");
-		if (chargeFull && chargeNow) break;
-	}
-	if (!chargeFull || !chargeNow) {
-		if (errno) err(EX_OSFILE, "%s", Class);
-		errx(EX_CONFIG, "%s: empty", Class);
-	}
-	closedir(dir);
-
-	const char *path = getenv("FRAMEBUFFER");
-	if (!path) path = "/dev/fb0";
-
-	int fb = open(path, O_RDWR);
-	if (fb < 0) err(EX_OSFILE, "%s", path);
-
-	struct fb_var_screeninfo info;
-	error = ioctl(fb, FBIOGET_VSCREENINFO, &info);
-	if (error) err(EX_IOERR, "%s", path);
-
-	size_t size = 4 * info.xres * info.yres;
-	uint32_t *buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
-	if (buf == MAP_FAILED) err(EX_IOERR, "%s", path);
-
-	for (;;) {
-		int match;
-
-		rewind(chargeFull);
-		fflush(chargeFull);
-		uint32_t full;
-		match = fscanf(chargeFull, "%u", &full);
-		if (match == EOF) err(EX_IOERR, "charge_full");
-		if (match < 1) errx(EX_DATAERR, "charge_full");
-
-		rewind(chargeNow);
-		fflush(chargeNow);
-		uint32_t now;
-		match = fscanf(chargeNow, "%u", &now);
-		if (match == EOF) err(EX_IOERR, "charge_now");
-		if (match < 1) errx(EX_DATAERR, "charge_now");
-
-		uint32_t percent = 100 * now / full;
-		uint32_t height = 16 * now / full;
-
-		for (int i = 0; i < 60; ++i, sleep(1)) {
-			uint32_t left = info.xres - Right - Width;
-
-			for (uint32_t y = 0; y <= Height; ++y) {
-				buf[y * info.xres + left - 1] = DarkWhite;
-				buf[y * info.xres + left + Width] = DarkWhite;
-			}
-			for (uint32_t x = left; x < left + Width; ++x) {
-				buf[Height * info.xres + x] = DarkWhite;
-			}
-
-			for (uint32_t y = 0; y < Height; ++y) {
-				for (uint32_t x = left; x < left + Width; ++x) {
-					buf[y * info.xres + x] =
-						(Height - 1 - y > height) ? DarkBlack
-						: (percent <= 10) ? DarkRed
-						: (percent <= 30) ? DarkYellow
-						: LightBlack;
-				}
-			}
-		}
-	}
-}
diff --git a/bin/fbclock.c b/bin/fbclock.c
deleted file mode 100644
index ddc32db6..00000000
--- a/bin/fbclock.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/* Copyright (C) 2018  C. 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
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <assert.h>
-#include <err.h>
-#include <fcntl.h>
-#include <linux/fb.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sysexits.h>
-#include <time.h>
-#include <unistd.h>
-#include <zlib.h>
-
-#include "scheme.h"
-
-static const uint32_t PSF2Magic = 0x864AB572;
-struct PSF2Header {
-	uint32_t magic;
-	uint32_t version;
-	uint32_t headerSize;
-	uint32_t flags;
-	uint32_t glyphCount;
-	uint32_t glyphSize;
-	uint32_t glyphHeight;
-	uint32_t glyphWidth;
-};
-
-int main() {
-	size_t len;
-
-	const char *fontPath = getenv("FONT");
-	if (!fontPath) {
-		fontPath = "/usr/share/kbd/consolefonts/Lat2-Terminus16.psfu.gz";
-	}
-
-	gzFile font = gzopen(fontPath, "r");
-	if (!font) err(EX_NOINPUT, "%s", fontPath);
-
-	struct PSF2Header header;
-	len = gzfread(&header, sizeof(header), 1, font);
-	if (!len && gzeof(font)) errx(EX_DATAERR, "%s: missing header", fontPath);
-	if (!len) errx(EX_IOERR, "%s", gzerror(font, NULL));
-
-	if (header.magic != PSF2Magic) {
-		errx(
-			EX_DATAERR, "%s: invalid header magic %08X",
-			fontPath, header.magic
-		);
-	}
-	if (header.headerSize != sizeof(struct PSF2Header)) {
-		errx(
-			EX_DATAERR, "%s: weird header size %d",
-			fontPath, header.headerSize
-		);
-	}
-
-	uint8_t glyphs[128][header.glyphSize];
-	len = gzfread(glyphs, header.glyphSize, 128, font);
-	if (!len && gzeof(font)) errx(EX_DATAERR, "%s: missing glyphs", fontPath);
-	if (!len) errx(EX_IOERR, "%s", gzerror(font, NULL));
-
-	gzclose(font);
-
-	const char *fbPath = getenv("FRAMEBUFFER");
-	if (!fbPath) fbPath = "/dev/fb0";
-
-	int fb = open(fbPath, O_RDWR);
-	if (fb < 0) err(EX_OSFILE, "%s", fbPath);
-
-	struct fb_var_screeninfo info;
-	int error = ioctl(fb, FBIOGET_VSCREENINFO, &info);
-	if (error) err(EX_IOERR, "%s", fbPath);
-
-	size_t size = 4 * info.xres * info.yres;
-	uint32_t *buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
-	if (buf == MAP_FAILED) err(EX_IOERR, "%s", fbPath);
-
-	for (;;) {
-		time_t t = time(NULL);
-		if (t < 0) err(EX_OSERR, "time");
-		const struct tm *local = localtime(&t);
-		if (!local) err(EX_OSERR, "localtime");
-
-		char str[64];
-		len = strftime(str, sizeof(str), "%H:%M", local);
-		assert(len);
-
-		for (int i = 0; i < (60 - local->tm_sec); ++i, sleep(1)) {
-			uint32_t left = info.xres - header.glyphWidth * len;
-			uint32_t bottom = header.glyphHeight;
-
-			for (uint32_t y = 0; y < bottom; ++y) {
-				buf[y * info.xres + left - 1] = DarkWhite;
-			}
-			for (uint32_t x = left - 1; x < info.xres; ++x) {
-				buf[bottom * info.xres + x] = DarkWhite;
-			}
-
-			for (const char *s = str; *s; ++s) {
-				const uint8_t *glyph = glyphs[(unsigned)*s];
-				uint32_t stride = header.glyphSize / header.glyphHeight;
-				for (uint32_t y = 0; y < header.glyphHeight; ++y) {
-					for (uint32_t x = 0; x < header.glyphWidth; ++x) {
-						uint8_t bits = glyph[y * stride + x / 8];
-						uint8_t bit = bits >> (7 - x % 8) & 1;
-						buf[y * info.xres + left + x] = bit
-							? DarkWhite
-							: DarkBlack;
-					}
-				}
-				left += header.glyphWidth;
-			}
-		}
-	}
-}
diff --git a/bin/html.mk b/bin/html.mk
index 92baed46..c1f85595 100644
--- a/bin/html.mk
+++ b/bin/html.mk
@@ -4,7 +4,6 @@ HTMLS = index.html png.html
 HTMLS += ${BINS:=.html}
 HTMLS += ${BSD:=.html}
 HTMLS += ${GAMES:=.html}
-HTMLS += ${LINUX:=.html}
 HTMLS += ${TLS:=.html}
 
 html: ${HTMLS}
diff --git a/bin/man1/bri.1 b/bin/man1/bri.1
deleted file mode 100644
index 54a02322..00000000
--- a/bin/man1/bri.1
+++ /dev/null
@@ -1,44 +0,0 @@
-.Dd September 7, 2018
-.Dt BRI 1
-.Os
-.
-.Sh NAME
-.Nm bri
-.Nd backlight brightness control
-.
-.Sh SYNOPSIS
-.Nm
-.Op Ar brightness
-.Nm
-.Cm +
-.Nm
-.Cm -
-.
-.Sh DESCRIPTION
-.Nm
-controls the backlight brightness on Linux.
-.
-.Pp
-With no argument,
-the current brightness is printed.
-With a numerical
-.Ar brightness
-argument,
-the brightness is set.
-.
-.Pp
-The
-.Cm +
-and
-.Cm -
-arguments
-may be repeated any number of times
-and adjust the brightness
-in increments of 16.
-.
-.Sh FILES
-.Bl -tag
-.It Pa /sys/class/backlight
-Files under the first subdirectory found
-are used to control the backlight brightness.
-.El
diff --git a/bin/man1/fbatt.1 b/bin/man1/fbatt.1
deleted file mode 100644
index 2d30cba7..00000000
--- a/bin/man1/fbatt.1
+++ /dev/null
@@ -1,34 +0,0 @@
-.Dd September 7, 2018
-.Dt FBATT 1
-.Os
-.
-.Sh NAME
-.Nm fbatt
-.Nd framebuffer battery indicator
-.
-.Sh SYNOPSIS
-.Nm
-.
-.Sh DESCRIPTION
-.Nm
-displays a battery charge indicator
-in the top-right corner
-of the Linux framebuffer.
-.
-.Sh ENVIRONMENT
-.Bl -tag
-.It Ev FRAMEBUFFER
-The framebuffer device path.
-.El
-.
-.Sh FILES
-.Bl -tag
-.It Pa /dev/fb0
-The default framebuffer device path.
-.It Pa /sys/class/power_supply
-The first subdirectory containing
-.Pa charge_full
-and
-.Pa charge_now
-is used to read the battery charge.
-.El
diff --git a/bin/man1/fbclock.1 b/bin/man1/fbclock.1
deleted file mode 100644
index 3195eb42..00000000
--- a/bin/man1/fbclock.1
+++ /dev/null
@@ -1,36 +0,0 @@
-.Dd September 7, 2018
-.Dt FBCLOCK 1
-.Os
-.
-.Sh NAME
-.Nm fbclock
-.Nd framebuffer clock
-.
-.Sh SYNOPSIS
-.Nm
-.
-.Sh DESCRIPTION
-.Nm
-displays a clock
-in the top-right corner
-of the Linux framebuffer.
-.
-.Sh ENVIRONMENT
-.Bl -tag
-.It Ev FONT
-Path to gzipped PSF file.
-.
-.It Ev FRAMEBUFFER
-The framebuffer device path.
-.El
-.
-.Sh FILES
-.Bl -tag
-.It Pa /dev/fb0
-The default framebuffer device path.
-.It Pa /usr/share/kbd/consolefonts/Lat2-Terminus16.psfu.gz
-The default font path.
-.El
-.
-.Sh SEE ALSO
-.Xr setfont 8
diff --git a/bin/man1/psfed.1 b/bin/man1/psfed.1
deleted file mode 100644
index 3fbc4710..00000000
--- a/bin/man1/psfed.1
+++ /dev/null
@@ -1,166 +0,0 @@
-.Dd January 14, 2019
-.Dt PSFED 1
-.Os
-.
-.Sh NAME
-.Nm psfed
-.Nd PSF2 font editor
-.
-.Sh SYNOPSIS
-.Nm
-.Op Fl H Ar height
-.Op Fl g Ar glyphs
-.Op Fl h Ar height
-.Op Fl w Ar width
-.Ar file
-.
-.Sh DESCRIPTION
-.Nm
-is a PSF2 font editor
-for the Linux framebuffer.
-.
-.Pp
-The arguments are as follows:
-.
-.Bl -tag -width Ds
-.It Fl H Ar height
-Modify the height of an existing font.
-Only increasing the height is allowed.
-.
-.It Fl g Ar glyphs
-Set the number of glyphs in a new font.
-The default number of glyphs is 256.
-.
-.It Fl h Ar height
-Set the height of a new font.
-The default height is 16.
-.
-.It Fl w Ar width
-Set the width of a new font.
-The default width is 8.
-.El
-.
-.Ss Normal Mode
-In normal mode,
-each glyph is displayed in a grid.
-.
-.Pp
-.Bl -tag -width Ds -compact
-.It Ic q
-Quit.
-.Nm
-will ask for confirmation
-if the font has been modified
-since the last write.
-.
-.It Ic w
-Write font to
-.Ar file .
-.
-.It Ic - Ic +
-Adjust display scale.
-.
-.It Ic h Ic l
-Select previous/next glyph.
-.
-.It Ic k Ic j
-Select glyph in previous/next row.
-.
-.It Ic f
-Select glyph of next input character.
-.
-.It Ic '
-Return to previously selected glyph.
-.
-.It Ic y
-Copy selected glyph.
-.
-.It Ic e
-Edit selected glyph in
-.Sx Edit Mode .
-.
-.It Ic i
-Enter
-.Sx Preview Mode .
-.El
-.
-.Ss Edit Mode
-In edit mode,
-the selected glyph is displayed for editing
-surrounded by a checked border.
-The glyph is also displayed unscaled
-in the bottom-right corner.
-.
-.Pp
-.Bl -tag -width Ds -compact
-.It Ic ESC
-Return to
-.Sx Normal Mode .
-.
-.It Ic - Ic +
-Adjust display scale.
-.
-.It Ic g Ic G
-Toggle guide on selected column/row.
-.
-.It Ic h Ic l
-Select previous/next bit in row.
-.
-.It Ic k Ic j
-Select previous/next bit in column.
-.
-.It Ic SPACE
-Flip selected bit.
-.
-.It Ic r
-Invert glyph.
-.
-.It Ic H Ic L
-Move glyph left/right.
-.
-.It Ic K Ic J
-Move glyph up/down.
-.
-.It Ic p
-Paste the copied glyph.
-.
-.It Ic u
-Revert glyph to initial state.
-.El
-.
-.Ss Preview Mode
-In preview mode,
-arbitrary text may be entered
-for preview.
-Press
-.Ic ESC
-to return to
-.Sx Normal Mode .
-.
-.Sh ENVIRONMENT
-.Bl -tag -width FRAMEBUFFER
-.It Ev FRAMEBUFFER
-The framebuffer device path.
-The default path is
-.Pa /dev/fb0 .
-.El
-.
-.Sh SEE ALSO
-.Xr psfaddtable 1 ,
-.Xr psfgettable 1 ,
-.Xr psfstriptable 1 ,
-.Xr setfont 8
-.
-.Sh CAVEATS
-.Nm
-does not support Unicode tables.
-Use
-.Xr psfaddtable 1
-to add Unicode tables
-to fonts created by
-.Nm .
-.
-.Sh BUGS
-.Nm
-makes no attempt to convert header fields
-to and from little-endian format.
diff --git a/bin/psfed.c b/bin/psfed.c
deleted file mode 100644
index 4f46b500..00000000
--- a/bin/psfed.c
+++ /dev/null
@@ -1,577 +0,0 @@
-/* Copyright (C) 2018  C. 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
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/fb.h>
-#include <locale.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sysexits.h>
-#include <termios.h>
-#include <unistd.h>
-#include <wchar.h>
-
-static const wchar_t CP437[256] =
-	L"\0☺☻♥♦♣♠•◘○◙♂♀♪♫☼"
-	L"►◄↕‼¶§▬↨↑↓→←∟↔▲▼"
-	L" !\"#$%&'()*+,-./"
-	L"0123456789:;<=>?"
-	L"@ABCDEFGHIJKLMNO"
-	L"PQRSTUVWXYZ[\\]^_"
-	L"`abcdefghijklmno"
-	L"pqrstuvwxyz{|}~⌂"
-	L"ÇüéâäàåçêëèïîìÄÅ"
-	L"ÉæÆôöòûùÿÖÜ¢£¥₧ƒ"
-	L"áíóúñѪº¿⌐¬½¼¡«»"
-	L"░▒▓│┤╡╢╖╕╣║╗╝╜╛┐"
-	L"└┴┬├─┼╞╟╚╔╩╦╠═╬╧"
-	L"╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀"
-	L"αßΓπΣσµτΦΘΩδ∞φε∩"
-	L"≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\0";
-
-static struct {
-	uint32_t width;
-	uint32_t height;
-	uint32_t *buffer;
-	uint32_t background;
-} frame;
-
-static void frameClear(void) {
-	for (uint32_t i = 0; i < frame.width * frame.height; ++i) {
-		frame.buffer[i] = frame.background;
-	}
-}
-
-static void frameOpen(void) {
-	const char *dev = getenv("FRAMEBUFFER");
-	if (!dev) dev = "/dev/fb0";
-
-	int fd = open(dev, O_RDWR);
-	if (fd < 0) err(EX_OSFILE, "%s", dev);
-
-	struct fb_var_screeninfo info;
-	int error = ioctl(fd, FBIOGET_VSCREENINFO, &info);
-	if (error) err(EX_IOERR, "%s", dev);
-
-	frame.width = info.xres;
-	frame.height = 3 * info.yres / 4;
-	frame.buffer = mmap(
-		NULL, sizeof(*frame.buffer) * frame.width * frame.height,
-		PROT_READ | PROT_WRITE, MAP_SHARED,
-		fd, 0
-	);
-	if (frame.buffer == MAP_FAILED) err(EX_IOERR, "%s", dev);
-	close(fd);
-
-	frame.background = frame.buffer[0];
-	atexit(frameClear);
-}
-
-static const uint32_t Magic = 0x864AB572;
-static const uint32_t Version = 0;
-static const uint32_t FlagUnicode = 1 << 0;
-static uint32_t bytes(uint32_t bits) {
-	return (bits + 7) / 8;
-}
-
-static char *path;
-static struct {
-	uint32_t magic;
-	uint32_t version;
-	uint32_t size;
-	uint32_t flags;
-	struct {
-		uint32_t len;
-		uint32_t size;
-		uint32_t height;
-		uint32_t width;
-	} glyph;
-} header;
-static uint8_t *glyphs;
-
-static void fileRead(uint32_t newLen, uint32_t newWidth, uint32_t newHeight) {
-	FILE *file = fopen(path, "r");
-	if (file) {
-		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);
-
-	} else {
-		if (errno != ENOENT) err(EX_NOINPUT, "%s", path);
-		header.magic = Magic;
-		header.version = Version;
-		header.size = sizeof(header);
-		header.flags = 0;
-		header.glyph.len = newLen;
-		header.glyph.size = bytes(newWidth) * newHeight;
-		header.glyph.height = newHeight;
-		header.glyph.width = newWidth;
-	}
-
-	if (header.magic != Magic) {
-		errx(EX_DATAERR, "%s: invalid magic %08X", path, header.magic);
-	}
-	if (header.version != Version) {
-		errx(EX_DATAERR, "%s: unsupported version %u", path, header.version);
-	}
-	if (header.flags & FlagUnicode) {
-		errx(EX_DATAERR, "%s: unsupported unicode table", path);
-	}
-	if (header.flags) {
-		errx(EX_DATAERR, "%s: unsupported flags %08X", path, header.flags);
-	}
-
-	if (file && header.size > sizeof(header)) {
-		int error = fseek(file, header.size, SEEK_SET);
-		if (error) err(EX_IOERR, "%s", path);
-
-		warnx("%s: truncating long header", path);
-		header.size = sizeof(header);
-	}
-
-	glyphs = calloc(header.glyph.len, header.glyph.size);
-	if (!glyphs) err(EX_OSERR, "calloc");
-
-	if (file) {
-		size_t len = fread(glyphs, header.glyph.size, header.glyph.len, file);
-		if (ferror(file)) err(EX_IOERR, "%s", path);
-		if (len < header.glyph.len) {
-			errx(EX_DATAERR, "%s: truncated glyphs", path);
-		}
-		fclose(file);
-	}
-}
-
-static void fileWrite(void) {
-	FILE *file = fopen(path, "w");
-	if (!file) err(EX_CANTCREAT, "%s", path);
-
-	fwrite(&header, sizeof(header), 1, file);
-	if (ferror(file)) err(EX_IOERR, "%s", path);
-
-	fwrite(glyphs, header.glyph.size, header.glyph.len, file);
-	if (ferror(file)) err(EX_IOERR, "%s", path);
-
-	int error = fclose(file);
-	if (error) err(EX_IOERR, "%s", path);
-}
-
-static uint8_t *glyph(uint32_t index) {
-	return &glyphs[header.glyph.size * index];
-}
-static uint8_t *bitByte(uint32_t index, uint32_t x, uint32_t y) {
-	return &glyph(index)[bytes(header.glyph.width) * y + x / 8];
-}
-static uint8_t bitGet(uint32_t index, uint32_t x, uint32_t y) {
-	return *bitByte(index, x, y) >> (7 - x % 8) & 1;
-}
-static void bitFlip(uint32_t index, uint32_t x, uint32_t y) {
-	*bitByte(index, x, y) ^= 1 << (7 - x % 8);
-}
-static void bitSet(uint32_t index, uint32_t x, uint32_t y, uint8_t bit) {
-	*bitByte(index, x, y) &= ~(1 << (7 - x % 8));
-	*bitByte(index, x, y) |= bit << (7 - x % 8);
-}
-
-static void drawGlyph(
-	uint32_t destX, uint32_t destY, uint32_t scale, uint32_t index,
-	uint32_t selectX, uint32_t selectY, uint32_t guideX, uint32_t guideY
-) {
-	destX <<= scale;
-	destY <<= scale;
-
-	for (uint32_t y = 0; y < (header.glyph.height << scale); ++y) {
-		if (destY + y >= frame.height) break;
-		for (uint32_t x = 0; x < (header.glyph.width << scale); ++x) {
-			if (destX + x >= frame.width) break;
-
-			uint32_t glyphX = x >> scale;
-			uint32_t glyphY = y >> scale;
-			uint32_t fill = -bitGet(index, glyphX, glyphY);
-			if (selectX & 1 << glyphX && selectY & 1 << glyphY) fill ^= 0x77;
-			if (guideX & 1 << glyphX || guideY & 1 << glyphY) fill ^= 0x3300;
-
-			frame.buffer[frame.width * (destY + y) + destX + x] = fill;
-		}
-	}
-}
-
-static void drawBorder(uint32_t destX, uint32_t destY, uint32_t scale) {
-	destX <<= scale;
-	destY <<= scale;
-
-	for (uint32_t y = 0; y < destY; ++y) {
-		if (y >= frame.height) break;
-		uint32_t fill = -(y >> scale & 1) ^ 0x555555;
-		for (uint32_t x = 0; x < (uint32_t)(1 << scale); ++x) {
-			if (destX + x >= frame.width) break;
-			frame.buffer[frame.width * y + destX + x] = fill;
-		}
-	}
-
-	for (uint32_t x = 0; x < destX; ++x) {
-		if (x >= frame.width) break;
-		uint32_t fill = -(x >> scale & 1) ^ 0x555555;
-		for (uint32_t y = 0; y < (uint32_t)(1 << scale); ++y) {
-			if (destY + y >= frame.height) break;
-			frame.buffer[frame.width * (destY + y) + x] = fill;
-		}
-	}
-}
-
-enum { LF = '\n', Esc = '\33', Del = '\177' };
-
-static enum {
-	Normal,
-	Edit,
-	Preview,
-	Discard,
-} mode;
-
-static struct {
-	uint32_t scale;
-	uint32_t index;
-	bool modified;
-	bool to;
-	uint32_t from;
-} normal;
-
-static struct {
-	uint32_t scale;
-	uint32_t index;
-	uint32_t x;
-	uint32_t y;
-	uint32_t guideX;
-	uint32_t guideY;
-	uint8_t *undo;
-	uint8_t *copy;
-} edit = {
-	.scale = 4,
-};
-
-static const uint32_t NormalCols = 32;
-static void drawNormal(void) {
-	for (uint32_t i = 0; i < header.glyph.len; ++i) {
-		drawGlyph(
-			header.glyph.width * (i % NormalCols),
-			header.glyph.height * (i  / NormalCols),
-			normal.scale, i,
-			-(i == normal.index), -(i == normal.index), 0, 0
-		);
-	}
-}
-
-static void normalDec(uint32_t n) {
-	if (normal.index >= n) normal.index -= n;
-}
-static void normalInc(uint32_t n) {
-	if (normal.index + n < header.glyph.len) normal.index += n;
-}
-static void normalPrint(const char *prefix) {
-	if (normal.index <= 256) {
-		printf(
-			"%s: %02X '%lc'\n",
-			prefix, normal.index, (wint_t)CP437[normal.index]
-		);
-	} else {
-		printf("%s: %02X\n", prefix, normal.index);
-	}
-}
-
-static void inputNormal(char ch) {
-	if (normal.to) {
-		if (ch < header.glyph.len) normal.index = ch;
-		normalPrint("index");
-		normal.to = false;
-		return;
-	}
-
-	switch (ch) {
-		break; case 'q': {
-			if (!normal.modified) exit(EX_OK);
-			mode = Discard;
-		}
-		break; case 'w': {
-			fileWrite();
-			printf("write: %s\n", path);
-			normal.modified = false;
-		}
-		break; case '-': if (normal.scale) normal.scale--; frameClear();
-		break; case '+': normal.scale++;
-		break; case 'h': normalDec(1); normalPrint("index");
-		break; case 'l': normalInc(1); normalPrint("index");
-		break; case 'k': normalDec(NormalCols); normalPrint("index");
-		break; case 'j': normalInc(NormalCols); normalPrint("index");
-		break; case 'f': normal.from = normal.index; normal.to = true;
-		break; case 047: normal.index = normal.from; normalPrint("index");
-		break; case 'y': {
-			if (!edit.copy) edit.copy = malloc(header.glyph.size);
-			if (!edit.copy) err(EX_OSERR, "malloc");
-			memcpy(edit.copy, glyph(normal.index), header.glyph.size);
-			normalPrint("copy");
-		}
-		break; case 'e': {
-			normal.modified = true;
-			edit.index = normal.index;
-			if (!edit.undo) edit.undo = malloc(header.glyph.size);
-			if (!edit.undo) err(EX_OSERR, "malloc");
-			memcpy(edit.undo, glyph(edit.index), header.glyph.size);
-			mode = Edit;
-			frameClear();
-		}
-		break; case 'i': mode = Preview; frameClear();
-	}
-}
-
-static void drawEdit(void) {
-	drawGlyph(
-		0, 0, edit.scale, edit.index,
-		1 << edit.x, 1 << edit.y, edit.guideX, edit.guideY
-	);
-	drawBorder(header.glyph.width, header.glyph.height, edit.scale);
-	drawGlyph(
-		header.glyph.width << edit.scale,
-		header.glyph.height << edit.scale,
-		0, edit.index,
-		0, 0, 0, 0
-	);
-}
-
-static void inputEdit(char ch) {
-	switch (ch) {
-		break; case Esc: mode = Normal; frameClear();
-
-		break; case '-': if (edit.scale) edit.scale--; frameClear();
-		break; case '+': edit.scale++;
-		break; case 'g': edit.guideY ^= 1 << edit.y;
-		break; case 'G': edit.guideX ^= 1 << edit.x;
-
-		break; case 'h': if (edit.x) edit.x--;
-		break; case 'l': if (edit.x + 1 < header.glyph.width) edit.x++;
-		break; case 'k': if (edit.y) edit.y--;
-		break; case 'j': if (edit.y + 1 < header.glyph.height) edit.y++;
-		break; case ' ': bitFlip(edit.index, edit.x, edit.y);
-
-		break; case 'r': {
-			for (uint32_t y = 0; y < header.glyph.height; ++y) {
-				for (uint32_t x = 0; x < header.glyph.width; ++x) {
-					bitFlip(edit.index, x, y);
-				}
-			}
-		}
-
-		break; case 'H': {
-			for (uint32_t x = 0; x < header.glyph.width; ++x) {
-				for (uint32_t y = 0; y < header.glyph.height; ++y) {
-					if (x + 1 < header.glyph.width) {
-						bitSet(edit.index, x, y, bitGet(edit.index, x + 1, y));
-					} else {
-						bitSet(edit.index, x, y, 0);
-					}
-				}
-			}
-		}
-
-		break; case 'L': {
-			uint32_t width = header.glyph.width;
-			for (uint32_t x = width - 1; x < width; --x) {
-				for (uint32_t y = 0; y < header.glyph.height; ++y) {
-					if (x - 1 < width) {
-						bitSet(edit.index, x, y, bitGet(edit.index, x - 1, y));
-					} else {
-						bitSet(edit.index, x, y, 0);
-					}
-				}
-			}
-		}
-
-		break; case 'K': {
-			for (uint32_t y = 0; y < header.glyph.height; ++y) {
-				for (uint32_t x = 0; x < header.glyph.width; ++x) {
-					if (y + 1 < header.glyph.height) {
-						bitSet(edit.index, x, y, bitGet(edit.index, x, y + 1));
-					} else {
-						bitSet(edit.index, x, y, 0);
-					}
-				}
-			}
-		}
-
-		break; case 'J': {
-			uint32_t height = header.glyph.height;
-			for (uint32_t y = height - 1; y < height; --y) {
-				for (uint32_t x = 0; x < header.glyph.width; ++x) {
-					if (y - 1 < height) {
-						bitSet(edit.index, x, y, bitGet(edit.index, x, y - 1));
-					} else {
-						bitSet(edit.index, x, y, 0);
-					}
-				}
-			}
-		}
-
-		break; case 'p': {
-			if (!edit.copy) break;
-			memcpy(glyph(edit.index), edit.copy, header.glyph.size);
-		}
-		break; case 'u': {
-			if (!edit.undo) break;
-			memcpy(glyph(edit.index), edit.undo, header.glyph.size);
-		}
-	}
-}
-
-enum { PreviewRows = 8, PreviewCols = 64 };
-static struct {
-	uint32_t glyphs[PreviewRows * PreviewCols];
-	uint32_t index;
-} preview;
-
-static void drawPreview(void) {
-	for (uint32_t i = 0; i < PreviewRows * PreviewCols; ++i) {
-		drawGlyph(
-			header.glyph.width * (i % PreviewCols),
-			header.glyph.height * (i / PreviewCols),
-			0, preview.glyphs[i],
-			-(i == preview.index), -(i == preview.index), 0, 0
-		);
-	}
-}
-
-static void inputPreview(char ch) {
-	switch (ch) {
-		break; case Esc: mode = Normal; frameClear();
-		break; case Del: {
-			if (preview.index) preview.index--;
-			preview.glyphs[preview.index] = 0;
-		}
-		break; case LF: {
-			uint32_t tail = PreviewCols - (preview.index % PreviewCols);
-			memset(
-				&preview.glyphs[preview.index],
-				0, sizeof(preview.glyphs[0]) * tail
-			);
-			preview.index += tail;
-		}
-		break; default: preview.glyphs[preview.index++] = ch;
-	}
-	preview.index %= PreviewRows * PreviewCols;
-}
-
-static void drawDiscard(void) {
-	printf("discard modifications? ");
-	fflush(stdout);
-}
-
-static void inputDiscard(char ch) {
-	printf("%c\n", ch);
-	if (ch == 'Y' || ch == 'y') exit(EX_OK);
-	mode = Normal;
-}
-
-static void draw(void) {
-	switch (mode) {
-		break; case Normal: drawNormal();
-		break; case Edit: drawEdit();
-		break; case Preview: drawPreview();
-		break; case Discard: drawDiscard();
-	}
-}
-
-static void input(char ch) {
-	switch (mode) {
-		break; case Normal: inputNormal(ch);
-		break; case Edit: inputEdit(ch);
-		break; case Preview: inputPreview(ch);
-		break; case Discard: inputDiscard(ch);
-	}
-}
-
-static struct termios saveTerm;
-static void restoreTerm(void) {
-	tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm);
-}
-
-int main(int argc, char *argv[]) {
-	setlocale(LC_CTYPE, "");
-
-	uint32_t newLen = 256;
-	uint32_t newWidth = 8;
-	uint32_t newHeight = 16;
-	uint32_t setHeight = 0;
-
-	int opt;
-	while (0 < (opt = getopt(argc, argv, "H:g:h:w:"))) {
-		switch (opt) {
-			break; case 'H': setHeight = strtoul(optarg, NULL, 0);
-			break; case 'g': newLen = strtoul(optarg, NULL, 0);
-			break; case 'h': newHeight = strtoul(optarg, NULL, 0);
-			break; case 'w': newWidth = strtoul(optarg, NULL, 0);
-			break; default:  return EX_USAGE;
-		}
-	}
-	if (!newLen || !newWidth || !newHeight) return EX_USAGE;
-	if (optind == argc) return EX_USAGE;
-
-	path = strdup(argv[optind]);
-	fileRead(newLen, newWidth, newHeight);
-
-	if (setHeight) {
-		if (setHeight < header.glyph.height) {
-			errx(EX_CONFIG, "cannot decrease height");
-		}
-
-		uint32_t setSize = bytes(header.glyph.width) * setHeight;
-		uint8_t *setGlyphs = calloc(header.glyph.len, setSize);
-		for (uint32_t i = 0; i < header.glyph.len; ++i) {
-			memcpy(&setGlyphs[setSize * i], glyph(i), header.glyph.size);
-		}
-		free(glyphs);
-		glyphs = setGlyphs;
-
-		header.glyph.height = setHeight;
-		header.glyph.size = setSize;
-		normal.modified = true;
-	}
-
-	frameOpen();
-	frameClear();
-
-	int error = tcgetattr(STDIN_FILENO, &saveTerm);
-	if (error) err(EX_IOERR, "tcgetattr");
-	atexit(restoreTerm);
-
-	struct termios term = saveTerm;
-	term.c_lflag &= ~(ICANON | ECHO);
-	error = tcsetattr(STDIN_FILENO, TCSADRAIN, &term);
-	if (error) err(EX_IOERR, "tcsetattr");
-
-	for (;;) {
-		draw();
-		char ch;
-		ssize_t size = read(STDIN_FILENO, &ch, 1);
-		if (size < 0) err(EX_IOERR, "read");
-		if (!size) return EX_SOFTWARE;
-		input(ch);
-	}
-}