summary refs log tree commit diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore46
-rw-r--r--TOUR.729
-rw-r--r--agpl.c20
-rw-r--r--bin/.gitignore37
-rw-r--r--bin/LICENSE661
-rw-r--r--bin/Makefile153
-rw-r--r--bin/README.787
-rw-r--r--bin/beef.c141
-rw-r--r--bin/bibsort.pl68
-rw-r--r--bin/bit.y202
-rw-r--r--bin/bri.c85
-rw-r--r--bin/c.sh121
-rw-r--r--bin/c11.l149
-rw-r--r--bin/dash/.gitignore34
-rw-r--r--bin/dash/COPYING (renamed from COPYING)0
-rw-r--r--bin/dash/ChangeLog (renamed from ChangeLog)0
-rw-r--r--bin/dash/ChangeLog.O (renamed from ChangeLog.O)0
-rw-r--r--bin/dash/Makefile.am (renamed from Makefile.am)0
-rw-r--r--bin/dash/Makefile.in776
-rw-r--r--bin/dash/aclocal.m41132
-rwxr-xr-xbin/dash/autogen.sh (renamed from autogen.sh)2
-rwxr-xr-xbin/dash/compile348
-rw-r--r--bin/dash/config.h.in205
-rwxr-xr-xbin/dash/configure6450
-rw-r--r--bin/dash/configure.ac (renamed from configure.ac)9
-rwxr-xr-xbin/dash/depcomp791
-rwxr-xr-xbin/dash/install-sh541
-rwxr-xr-xbin/dash/missing215
-rw-r--r--bin/dash/src/.gitignore (renamed from src/.gitignore)0
-rw-r--r--bin/dash/src/Makefile.am (renamed from src/Makefile.am)20
-rw-r--r--bin/dash/src/Makefile.in834
-rw-r--r--bin/dash/src/TOUR (renamed from src/TOUR)0
-rw-r--r--bin/dash/src/alias.c (renamed from src/alias.c)0
-rw-r--r--bin/dash/src/alias.h (renamed from src/alias.h)0
-rw-r--r--bin/dash/src/arith_yacc.c (renamed from src/arith_yacc.c)0
-rw-r--r--bin/dash/src/arith_yacc.h (renamed from src/arith_yacc.h)0
-rw-r--r--bin/dash/src/arith_yylex.c (renamed from src/arith_yylex.c)0
-rw-r--r--bin/dash/src/bltin/bltin.h (renamed from src/bltin/bltin.h)0
-rw-r--r--bin/dash/src/bltin/echo.1 (renamed from src/bltin/echo.1)0
-rw-r--r--bin/dash/src/bltin/printf.1 (renamed from src/bltin/printf.1)0
-rw-r--r--bin/dash/src/bltin/printf.c (renamed from src/bltin/printf.c)0
-rw-r--r--bin/dash/src/bltin/test.1 (renamed from src/bltin/test.1)0
-rw-r--r--bin/dash/src/bltin/test.c (renamed from src/bltin/test.c)0
-rw-r--r--bin/dash/src/bltin/times.c (renamed from src/bltin/times.c)0
-rw-r--r--bin/dash/src/builtins.def.in (renamed from src/builtins.def.in)0
-rw-r--r--bin/dash/src/cd.c (renamed from src/cd.c)0
-rw-r--r--bin/dash/src/cd.h (renamed from src/cd.h)0
-rw-r--r--bin/dash/src/dash.1 (renamed from src/dash.1)6
-rw-r--r--bin/dash/src/error.c (renamed from src/error.c)0
-rw-r--r--bin/dash/src/error.h (renamed from src/error.h)0
-rw-r--r--bin/dash/src/eval.c (renamed from src/eval.c)0
-rw-r--r--bin/dash/src/eval.h (renamed from src/eval.h)0
-rw-r--r--bin/dash/src/exec.c (renamed from src/exec.c)0
-rw-r--r--bin/dash/src/exec.h (renamed from src/exec.h)0
-rw-r--r--bin/dash/src/expand.c (renamed from src/expand.c)0
-rw-r--r--bin/dash/src/expand.h (renamed from src/expand.h)0
-rw-r--r--bin/dash/src/funcs/cmv (renamed from src/funcs/cmv)0
-rw-r--r--bin/dash/src/funcs/dirs (renamed from src/funcs/dirs)0
-rw-r--r--bin/dash/src/funcs/kill (renamed from src/funcs/kill)0
-rw-r--r--bin/dash/src/funcs/login (renamed from src/funcs/login)0
-rw-r--r--bin/dash/src/funcs/newgrp (renamed from src/funcs/newgrp)0
-rw-r--r--bin/dash/src/funcs/popd (renamed from src/funcs/popd)0
-rw-r--r--bin/dash/src/funcs/pushd (renamed from src/funcs/pushd)0
-rw-r--r--bin/dash/src/funcs/suspend (renamed from src/funcs/suspend)0
-rw-r--r--bin/dash/src/histedit.c (renamed from src/histedit.c)10
-rw-r--r--bin/dash/src/init.h (renamed from src/init.h)0
-rw-r--r--bin/dash/src/input.c (renamed from src/input.c)6
-rw-r--r--bin/dash/src/input.h (renamed from src/input.h)0
-rw-r--r--bin/dash/src/jobs.c (renamed from src/jobs.c)4
-rw-r--r--bin/dash/src/jobs.h (renamed from src/jobs.h)0
-rw-r--r--bin/dash/src/machdep.h (renamed from src/machdep.h)0
-rw-r--r--bin/dash/src/mail.c (renamed from src/mail.c)0
-rw-r--r--bin/dash/src/mail.h (renamed from src/mail.h)0
-rw-r--r--bin/dash/src/main.c (renamed from src/main.c)0
-rw-r--r--bin/dash/src/main.h (renamed from src/main.h)0
-rw-r--r--bin/dash/src/memalloc.c (renamed from src/memalloc.c)0
-rw-r--r--bin/dash/src/memalloc.h (renamed from src/memalloc.h)0
-rw-r--r--bin/dash/src/miscbltin.c (renamed from src/miscbltin.c)0
-rw-r--r--bin/dash/src/miscbltin.h (renamed from src/miscbltin.h)0
-rw-r--r--bin/dash/src/mkbuiltins (renamed from src/mkbuiltins)0
-rw-r--r--bin/dash/src/mkinit.c (renamed from src/mkinit.c)0
-rw-r--r--bin/dash/src/mknodes.c (renamed from src/mknodes.c)0
-rw-r--r--bin/dash/src/mksignames.c (renamed from src/mksignames.c)0
-rw-r--r--bin/dash/src/mksyntax.c (renamed from src/mksyntax.c)0
-rw-r--r--bin/dash/src/mktokens (renamed from src/mktokens)0
-rw-r--r--bin/dash/src/myhistedit.h (renamed from src/myhistedit.h)0
-rw-r--r--bin/dash/src/mystring.c (renamed from src/mystring.c)0
-rw-r--r--bin/dash/src/mystring.h (renamed from src/mystring.h)0
-rw-r--r--bin/dash/src/nodes.c.pat (renamed from src/nodes.c.pat)0
-rw-r--r--bin/dash/src/nodetypes (renamed from src/nodetypes)0
-rw-r--r--bin/dash/src/options.c (renamed from src/options.c)0
-rw-r--r--bin/dash/src/options.h (renamed from src/options.h)0
-rw-r--r--bin/dash/src/output.c (renamed from src/output.c)0
-rw-r--r--bin/dash/src/output.h (renamed from src/output.h)0
-rw-r--r--bin/dash/src/parser.c (renamed from src/parser.c)92
-rw-r--r--bin/dash/src/parser.h (renamed from src/parser.h)1
-rw-r--r--bin/dash/src/redir.c (renamed from src/redir.c)0
-rw-r--r--bin/dash/src/redir.h (renamed from src/redir.h)0
-rw-r--r--bin/dash/src/shell.h (renamed from src/shell.h)0
-rw-r--r--bin/dash/src/show.c (renamed from src/show.c)0
-rw-r--r--bin/dash/src/show.h (renamed from src/show.h)0
-rw-r--r--bin/dash/src/system.c (renamed from src/system.c)0
-rw-r--r--bin/dash/src/system.h (renamed from src/system.h)0
-rw-r--r--bin/dash/src/trap.c (renamed from src/trap.c)0
-rw-r--r--bin/dash/src/trap.h (renamed from src/trap.h)0
-rw-r--r--bin/dash/src/var.c (renamed from src/var.c)2
-rw-r--r--bin/dash/src/var.h (renamed from src/var.h)6
-rw-r--r--bin/dtch.c271
-rw-r--r--bin/ever.c119
-rw-r--r--bin/fbatt.c123
-rw-r--r--bin/fbclock.c132
-rw-r--r--bin/freecell.c388
-rw-r--r--bin/glitch.c538
-rw-r--r--bin/hilex.c391
-rw-r--r--bin/hilex.h50
-rw-r--r--bin/htagml.c207
-rw-r--r--bin/html.mk45
-rw-r--r--bin/html.sh65
-rw-r--r--bin/make.l132
-rw-r--r--bin/man1/beef.191
-rw-r--r--bin/man1/bibsort.140
-rw-r--r--bin/man1/bit.155
-rw-r--r--bin/man1/bri.144
-rw-r--r--bin/man1/c.145
-rw-r--r--bin/man1/dtch.167
-rw-r--r--bin/man1/ever.151
-rw-r--r--bin/man1/fbatt.134
-rw-r--r--bin/man1/fbclock.136
-rw-r--r--bin/man1/glitch.177
-rw-r--r--bin/man1/hilex.1218
-rw-r--r--bin/man1/htagml.169
-rw-r--r--bin/man1/modem.131
-rw-r--r--bin/man1/mtags.176
-rw-r--r--bin/man1/nudge.144
-rw-r--r--bin/man1/order.138
-rw-r--r--bin/man1/pbd.166
-rw-r--r--bin/man1/pngo.156
-rw-r--r--bin/man1/psf2png.153
-rw-r--r--bin/man1/psfed.1166
-rw-r--r--bin/man1/ptee.140
-rw-r--r--bin/man1/relay.148
-rw-r--r--bin/man1/scheme.159
-rw-r--r--bin/man1/shotty.192
-rw-r--r--bin/man1/sup.148
-rw-r--r--bin/man1/title.151
-rw-r--r--bin/man1/typer.174
-rw-r--r--bin/man1/up.180
-rw-r--r--bin/man1/when.176
-rw-r--r--bin/man1/xx.168
-rw-r--r--bin/man3/png.390
-rw-r--r--bin/man6/freecell.650
-rw-r--r--bin/mdoc.l65
-rw-r--r--bin/modem.c102
-rw-r--r--bin/mtags.c99
-rw-r--r--bin/nudge.c78
-rw-r--r--bin/order.y195
-rw-r--r--bin/pbd.c151
-rw-r--r--bin/png.h108
-rw-r--r--bin/pngo.c812
-rw-r--r--bin/psf2png.c107
-rw-r--r--bin/psfed.c577
-rw-r--r--bin/ptee.c116
-rw-r--r--bin/relay.c218
-rw-r--r--bin/scheme.c278
-rw-r--r--bin/sh.l178
-rw-r--r--bin/shotty.c648
-rw-r--r--bin/sup.sh169
-rw-r--r--bin/title.c211
-rw-r--r--bin/typer.c197
-rw-r--r--bin/up.sh80
-rw-r--r--bin/when.y255
-rw-r--r--bin/xx.c142
-rw-r--r--doc/pdf/.gitignore1
-rw-r--r--doc/pdf/Makefile31
-rw-r--r--doc/rfc/.gitignore2
-rw-r--r--doc/rfc/Makefile38
-rw-r--r--doc/rfc/rfc.153
-rw-r--r--doc/rfc/rfc.in19
-rw-r--r--doc/rfc/rfctags.pl21
-rw-r--r--doc/zlib/Makefile87
-rw-r--r--doc/zlib/adler32.365
-rw-r--r--doc/zlib/adler32_combine.363
-rw-r--r--doc/zlib/compress.384
-rw-r--r--doc/zlib/compressBound.344
-rw-r--r--doc/zlib/crc32.366
-rw-r--r--doc/zlib/crc32_combine.354
-rw-r--r--doc/zlib/deflate.3370
-rw-r--r--doc/zlib/deflateBound.371
-rw-r--r--doc/zlib/deflateCopy.366
-rw-r--r--doc/zlib/deflateEnd.350
-rw-r--r--doc/zlib/deflateGetDictionary.379
-rw-r--r--doc/zlib/deflateInit.3178
-rw-r--r--doc/zlib/deflateInit2.3227
-rw-r--r--doc/zlib/deflateParams.3123
-rw-r--r--doc/zlib/deflatePending.356
-rw-r--r--doc/zlib/deflatePrime.364
-rw-r--r--doc/zlib/deflateReset.357
-rw-r--r--doc/zlib/deflateSetDictionary.3142
-rw-r--r--doc/zlib/deflateSetHeader.3180
-rw-r--r--doc/zlib/deflateTune.370
-rw-r--r--doc/zlib/gzbuffer.359
-rw-r--r--doc/zlib/gzclose.397
-rw-r--r--doc/zlib/gzdirect.385
-rw-r--r--doc/zlib/gzeof.363
-rw-r--r--doc/zlib/gzerror.375
-rw-r--r--doc/zlib/gzflush.373
-rw-r--r--doc/zlib/gzfread.3107
-rw-r--r--doc/zlib/gzfwrite.375
-rw-r--r--doc/zlib/gzgetc.355
-rw-r--r--doc/zlib/gzgets.367
-rw-r--r--doc/zlib/gzoffset.351
-rw-r--r--doc/zlib/gzopen.3261
-rw-r--r--doc/zlib/gzprintf.371
-rw-r--r--doc/zlib/gzputc.343
-rw-r--r--doc/zlib/gzputs.341
-rw-r--r--doc/zlib/gzread.3115
-rw-r--r--doc/zlib/gzseek.3108
-rw-r--r--doc/zlib/gzsetparams.351
-rw-r--r--doc/zlib/gzungetc.367
-rw-r--r--doc/zlib/gzwrite.339
-rw-r--r--doc/zlib/inflate.3398
-rw-r--r--doc/zlib/inflateBack.3285
-rw-r--r--doc/zlib/inflateBackEnd.343
-rw-r--r--doc/zlib/inflateBackInit.384
-rw-r--r--doc/zlib/inflateCopy.359
-rw-r--r--doc/zlib/inflateEnd.344
-rw-r--r--doc/zlib/inflateGetDictionary.369
-rw-r--r--doc/zlib/inflateGetHeader.3170
-rw-r--r--doc/zlib/inflateInit.3101
-rw-r--r--doc/zlib/inflateInit2.3181
-rw-r--r--doc/zlib/inflateMark.388
-rw-r--r--doc/zlib/inflatePrime.373
-rw-r--r--doc/zlib/inflateReset.381
-rw-r--r--doc/zlib/inflateSetDictionary.385
-rw-r--r--doc/zlib/inflateSync.372
-rw-r--r--doc/zlib/uncompress.392
-rw-r--r--doc/zlib/zlibCompileFlags.3163
-rw-r--r--doc/zlib/zlibVersion.345
-rw-r--r--etc/CodeQWERTY.bundle/Contents/Info.plist19
-rw-r--r--etc/CodeQWERTY.bundle/Contents/Resources/CodeQWERTY.keylayout1178
-rw-r--r--etc/Dark.terminal1652
-rw-r--r--etc/Go-Mono-Bold-Italic.ttfbin0 -> 176832 bytes
-rw-r--r--etc/Go-Mono-Bold.ttfbin0 -> 168340 bytes
-rw-r--r--etc/Go-Mono-Italic.ttfbin0 -> 173548 bytes
-rw-r--r--etc/Go-Mono.ttfbin0 -> 164200 bytes
-rw-r--r--etc/README.Go-Mono36
-rw-r--r--etc/code.map20
-rw-r--r--etc/psf/.gitignore2
-rw-r--r--etc/psf/Makefile24
-rw-r--r--etc/psf/default.u259
-rw-r--r--etc/psf/sans6x10.psfbin0 -> 2592 bytes
-rw-r--r--etc/psf/sans6x12.psfbin0 -> 3104 bytes
-rw-r--r--etc/psf/sans6x8.psfbin0 -> 2080 bytes
-rw-r--r--etc/wsconsctl.conf26
-rw-r--r--gpl.c20
-rw-r--r--home/.config/X/modmap16
-rw-r--r--home/.config/X/resources49
-rw-r--r--home/.config/cwm/cwmrc87
-rw-r--r--home/.config/git/config24
-rw-r--r--home/.config/git/ignore2
-rw-r--r--home/.config/htop/htoprc29
-rw-r--r--home/.editrc1
-rw-r--r--home/.gdbinit1
-rw-r--r--home/.hushlogin0
-rw-r--r--home/.inputrc1
-rw-r--r--home/.lldbinit1
-rwxr-xr-xhome/.local/bin/aes7
-rwxr-xr-xhome/.local/bin/clock17
-rwxr-xr-xhome/.local/bin/def47
-rwxr-xr-xhome/.local/bin/deg6
-rwxr-xr-xhome/.local/bin/git-password7
-rwxr-xr-xhome/.local/bin/mdate2
-rwxr-xr-xhome/.local/bin/nasd9
-rwxr-xr-xhome/.local/bin/notify-send9
-rwxr-xr-xhome/.local/bin/np7
-rwxr-xr-xhome/.local/bin/open19
-rwxr-xr-xhome/.local/bin/pbcopy11
-rwxr-xr-xhome/.local/bin/pbpaste11
-rwxr-xr-xhome/.local/bin/versions9
-rw-r--r--home/.profile23
-rw-r--r--home/.shrc63
-rw-r--r--home/.ssh/config18
-rw-r--r--home/.xsession12
-rw-r--r--install.sh54
-rw-r--r--link.sh23
-rw-r--r--port/caesar/.gitignore2
-rw-r--r--port/caesar/Makefile19
-rw-r--r--port/caesar/caesar.673
-rw-r--r--port/caesar/caesar.c159
-rw-r--r--port/caesar/rot13.sh33
-rw-r--r--port/cgram/.gitignore1
-rw-r--r--port/cgram/Makefile17
-rw-r--r--port/cgram/cgram.665
-rw-r--r--port/cgram/cgram.c344
-rw-r--r--port/cgram/pathnames.h30
-rw-r--r--port/ddate/.gitignore1
-rw-r--r--port/ddate/Makefile15
-rw-r--r--port/ddate/ddate.1117
-rw-r--r--port/ddate/ddate.c399
-rw-r--r--port/file2c/.gitignore1
-rw-r--r--port/file2c/Makefile15
-rw-r--r--port/file2c/file2c.175
-rw-r--r--port/file2c/file2c.c92
-rw-r--r--port/wcwidth/.gitignore3
-rw-r--r--port/wcwidth/COPYRIGHT190
-rw-r--r--port/wcwidth/Makefile27
-rw-r--r--port/wcwidth/nonspacing.h89
-rw-r--r--port/wcwidth/wcfix.in7
-rw-r--r--port/wcwidth/wcswidth.c8
-rw-r--r--port/wcwidth/wcwidth.c29
-rw-r--r--port/wcwidth/wide.h65
-rw-r--r--prune.sh7
-rw-r--r--txt/.notemap3
-rw-r--r--txt/books.txt129
-rw-r--r--txt/music.txt285
-rw-r--r--txt/plan.726
-rw-r--r--txt/shows.txt32
-rw-r--r--txt/trouble-at-jinx-hotel.txt236
-rw-r--r--txt/tweets.txt32
-rw-r--r--www/causal.agency/.gitignore3
-rw-r--r--www/causal.agency/Makefile20
-rw-r--r--www/causal.agency/index.770
-rw-r--r--www/causal.agency/style.css23
-rw-r--r--www/git.causal.agency/.gitignore6
-rw-r--r--www/git.causal.agency/Makefile20
-rw-r--r--www/git.causal.agency/about-filter.sh15
-rw-r--r--www/git.causal.agency/cgit/.gitignore13
-rw-r--r--www/git.causal.agency/cgit/.mailmap10
-rw-r--r--www/git.causal.agency/cgit/AUTHORS13
-rw-r--r--www/git.causal.agency/cgit/COPYING339
-rw-r--r--www/git.causal.agency/cgit/Makefile170
-rw-r--r--www/git.causal.agency/cgit/README86
-rw-r--r--www/git.causal.agency/cgit/cache.c475
-rw-r--r--www/git.causal.agency/cgit/cache.h37
-rw-r--r--www/git.causal.agency/cgit/cgit.c1105
-rw-r--r--www/git.causal.agency/cgit/cgit.css877
-rw-r--r--www/git.causal.agency/cgit/cgit.h397
-rw-r--r--www/git.causal.agency/cgit/cgit.mk114
-rw-r--r--www/git.causal.agency/cgit/cgit.pngbin0 -> 1366 bytes
-rw-r--r--www/git.causal.agency/cgit/cgitrc.5.txt977
-rw-r--r--www/git.causal.agency/cgit/cmd.c208
-rw-r--r--www/git.causal.agency/cgit/cmd.h16
-rw-r--r--www/git.causal.agency/cgit/configfile.c90
-rw-r--r--www/git.causal.agency/cgit/configfile.h10
-rwxr-xr-xwww/git.causal.agency/cgit/contrib/hooks/post-receive.agefile19
-rw-r--r--www/git.causal.agency/cgit/favicon.icobin0 -> 1078 bytes
-rw-r--r--www/git.causal.agency/cgit/filter.c222
-rwxr-xr-xwww/git.causal.agency/cgit/filters/about-formatting.sh27
-rwxr-xr-xwww/git.causal.agency/cgit/filters/commit-links.sh28
-rwxr-xr-xwww/git.causal.agency/cgit/filters/email-gravatar.py36
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/man2html4
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/md2html304
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/rst2html2
-rwxr-xr-xwww/git.causal.agency/cgit/filters/html-converters/txt2html4
-rwxr-xr-xwww/git.causal.agency/cgit/filters/syntax-highlighting.py55
-rwxr-xr-xwww/git.causal.agency/cgit/filters/syntax-highlighting.sh121
-rwxr-xr-xwww/git.causal.agency/cgit/gen-version.sh20
-rw-r--r--www/git.causal.agency/cgit/html.c344
-rw-r--r--www/git.causal.agency/cgit/html.h37
-rw-r--r--www/git.causal.agency/cgit/parsing.c223
-rw-r--r--www/git.causal.agency/cgit/robots.txt4
-rw-r--r--www/git.causal.agency/cgit/scan-tree.c270
-rw-r--r--www/git.causal.agency/cgit/scan-tree.h2
-rw-r--r--www/git.causal.agency/cgit/shared.c579
-rw-r--r--www/git.causal.agency/cgit/tests/.gitignore2
-rw-r--r--www/git.causal.agency/cgit/tests/Makefile17
-rwxr-xr-xwww/git.causal.agency/cgit/tests/filters/dump.sh4
-rwxr-xr-xwww/git.causal.agency/cgit/tests/setup.sh161
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh45
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0010-validate-html.sh40
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0020-validate-cache.sh78
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0101-index.sh17
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0102-summary.sh25
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0103-log.sh24
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0104-tree.sh32
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0105-commit.sh36
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0106-diff.sh19
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0107-snapshot.sh205
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0108-patch.sh62
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0109-gitconfig.sh48
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0110-rawdiff.sh42
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0111-filter.sh43
-rwxr-xr-xwww/git.causal.agency/cgit/tests/valgrind/bin/cgit12
-rw-r--r--www/git.causal.agency/cgit/ui-atom.c158
-rw-r--r--www/git.causal.agency/cgit/ui-atom.h6
-rw-r--r--www/git.causal.agency/cgit/ui-blame.c306
-rw-r--r--www/git.causal.agency/cgit/ui-blame.h6
-rw-r--r--www/git.causal.agency/cgit/ui-blob.c182
-rw-r--r--www/git.causal.agency/cgit/ui-blob.h8
-rw-r--r--www/git.causal.agency/cgit/ui-clone.c126
-rw-r--r--www/git.causal.agency/cgit/ui-clone.h8
-rw-r--r--www/git.causal.agency/cgit/ui-commit.c148
-rw-r--r--www/git.causal.agency/cgit/ui-commit.h6
-rw-r--r--www/git.causal.agency/cgit/ui-diff.c505
-rw-r--r--www/git.causal.agency/cgit/ui-diff.h15
-rw-r--r--www/git.causal.agency/cgit/ui-log.c555
-rw-r--r--www/git.causal.agency/cgit/ui-log.h9
-rw-r--r--www/git.causal.agency/cgit/ui-patch.c98
-rw-r--r--www/git.causal.agency/cgit/ui-patch.h7
-rw-r--r--www/git.causal.agency/cgit/ui-plain.c207
-rw-r--r--www/git.causal.agency/cgit/ui-plain.h6
-rw-r--r--www/git.causal.agency/cgit/ui-refs.c219
-rw-r--r--www/git.causal.agency/cgit/ui-refs.h8
-rw-r--r--www/git.causal.agency/cgit/ui-repolist.c381
-rw-r--r--www/git.causal.agency/cgit/ui-repolist.h7
-rw-r--r--www/git.causal.agency/cgit/ui-shared.c1241
-rw-r--r--www/git.causal.agency/cgit/ui-shared.h87
-rw-r--r--www/git.causal.agency/cgit/ui-snapshot.c319
-rw-r--r--www/git.causal.agency/cgit/ui-snapshot.h7
-rw-r--r--www/git.causal.agency/cgit/ui-ssdiff.c420
-rw-r--r--www/git.causal.agency/cgit/ui-ssdiff.h25
-rw-r--r--www/git.causal.agency/cgit/ui-stats.c426
-rw-r--r--www/git.causal.agency/cgit/ui-stats.h28
-rw-r--r--www/git.causal.agency/cgit/ui-summary.c148
-rw-r--r--www/git.causal.agency/cgit/ui-summary.h7
-rw-r--r--www/git.causal.agency/cgit/ui-tag.c120
-rw-r--r--www/git.causal.agency/cgit/ui-tag.h6
-rw-r--r--www/git.causal.agency/cgit/ui-tree.c411
-rw-r--r--www/git.causal.agency/cgit/ui-tree.h6
-rw-r--r--www/git.causal.agency/cgitrc29
-rw-r--r--www/git.causal.agency/custom.css86
-rw-r--r--www/git.causal.agency/owner-filter.sh6
-rw-r--r--www/git.causal.agency/source-filter.sh25
-rw-r--r--www/temp.causal.agency/.gitignore1
-rw-r--r--www/temp.causal.agency/Makefile16
-rw-r--r--www/temp.causal.agency/up.c156
-rw-r--r--www/text.causal.agency/.gitignore3
-rw-r--r--www/text.causal.agency/001-make.7159
-rw-r--r--www/text.causal.agency/002-writing-mdoc.7138
-rw-r--r--www/text.causal.agency/003-pleasant-c.7120
-rw-r--r--www/text.causal.agency/004-uloc.764
-rw-r--r--www/text.causal.agency/005-testing-c.773
-rw-r--r--www/text.causal.agency/006-some-libs.796
-rw-r--r--www/text.causal.agency/007-cgit-setup.7271
-rw-r--r--www/text.causal.agency/008-how-irc.7193
-rw-r--r--www/text.causal.agency/009-casual-update.7127
-rw-r--r--www/text.causal.agency/010-irc-suite.7409
-rw-r--r--www/text.causal.agency/011-libretls.7220
-rw-r--r--www/text.causal.agency/012-inability.739
-rw-r--r--www/text.causal.agency/013-hot-tips.7156
-rw-r--r--www/text.causal.agency/014-using-vi.7135
-rw-r--r--www/text.causal.agency/015-reusing-tags.7155
-rw-r--r--www/text.causal.agency/016-using-openbsd.7505
-rw-r--r--www/text.causal.agency/017-unpasswords.7153
-rw-r--r--www/text.causal.agency/018-operating-systems.786
-rw-r--r--www/text.causal.agency/019-mailing-list.7286
-rw-r--r--www/text.causal.agency/020-c-style.7172
-rw-r--r--www/text.causal.agency/021-time-machine.7144
-rw-r--r--www/text.causal.agency/022-swans-are-dead.7164
-rw-r--r--www/text.causal.agency/023-sparse-checkout.7144
-rw-r--r--www/text.causal.agency/024-seprintf.7137
-rw-r--r--www/text.causal.agency/Makefile49
-rw-r--r--www/text.causal.agency/feed.sh55
-rw-r--r--www/text.causal.agency/igp.149
-rw-r--r--www/text.causal.agency/igp.c143
455 files changed, 54384 insertions, 96 deletions
diff --git a/.gitignore b/.gitignore
index e349901a..3bcf7b3f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,42 +1,4 @@
-# .gitignore for dash
-
-# generated by autogen.sh
-Makefile.in
-/aclocal.m4
-/autom4te.cache/
-/compile
-/config.h.in
-/configure
-/depcomp
-/install-sh
-/missing
-
-# generated by configure
-Makefile
-.deps
-.dirstamp
-/config.cache
-/config.h
-/config.log
-/config.status
-/stamp-h1
-
-# generated by make
-/src/token_vars.h
-
-# Apple debug symbol bundles
-*.dSYM/
-
-# backups and patch artefacts
-*~
-*.bak
-*.orig
-*.rej
-
-# OS generated files
-.DS_Store
-.DS_Store?
-._*
-.Spotlight*
-.Trash*
-*[Tt]humbs.db
+/build
+/clone
+/git
+/trash
diff --git a/TOUR.7 b/TOUR.7
new file mode 100644
index 00000000..2e77ffaa
--- /dev/null
+++ b/TOUR.7
@@ -0,0 +1,29 @@
+.Dd December 28, 2020
+.Dt TOUR 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm tour
+.Nd things that are here
+.
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa home/
+.Dq dotfiles
+.It Pa bin/
+utilities of varying utility
+.It Pa bin/dash/
+dash patched for interactive use
+.It Pa doc/rfc/
+scripts for reading IETF RFCs
+.It Pa doc/zlib/
+zlib documentation ported to
+.Xr mdoc 7
+.It Pa www/
+sources for causal.agency sites
+.It Pa www/git.causal.agency/cgit/
+patched cgit
+.It Pa port/wcwidth/
+.Xr wcwidth 3
+replacement for macOS
+.El
diff --git a/agpl.c b/agpl.c
new file mode 100644
index 00000000..929730c1
--- /dev/null
+++ b/agpl.c
@@ -0,0 +1,20 @@
+/* Copyright (C) 2021  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 <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
diff --git a/bin/.gitignore b/bin/.gitignore
new file mode 100644
index 00000000..be0a8004
--- /dev/null
+++ b/bin/.gitignore
@@ -0,0 +1,37 @@
+*.html
+*.o
+beef
+bibsort
+bit
+bri
+c
+config.mk
+dtch
+ever
+fbatt
+fbclock
+freecell
+glitch
+hilex
+htagml
+htmltags
+modem
+mtags
+nudge
+order
+pbd
+pngo
+psf2png
+psfed
+ptee
+relay
+scheme
+scheme.h
+shotty
+sup
+tags
+title
+typer
+up
+when
+xx
diff --git a/bin/LICENSE b/bin/LICENSE
new file mode 100644
index 00000000..dba13ed2
--- /dev/null
+++ b/bin/LICENSE
@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/bin/Makefile b/bin/Makefile
new file mode 100644
index 00000000..14ad94eb
--- /dev/null
+++ b/bin/Makefile
@@ -0,0 +1,153 @@
+PREFIX ?= ~/.local
+MANDIR ?= ${PREFIX}/share/man
+
+LIBS_PREFIX ?= /usr/local
+CFLAGS += -I${LIBS_PREFIX}/include
+LDFLAGS += -L${LIBS_PREFIX}/lib
+
+CFLAGS += -Wall -Wextra -Wpedantic -Wno-gnu-case-range
+
+BINS += beef
+BINS += bibsort
+BINS += bit
+BINS += c
+BINS += dtch
+BINS += glitch
+BINS += hilex
+BINS += htagml
+BINS += modem
+BINS += mtags
+BINS += nudge
+BINS += order
+BINS += pbd
+BINS += pngo
+BINS += psf2png
+BINS += ptee
+BINS += scheme
+BINS += shotty
+BINS += sup
+BINS += title
+BINS += up
+BINS += when
+BINS += xx
+
+BSD += ever
+
+GAMES += freecell
+
+LINUX += bri
+LINUX += fbatt
+LINUX += fbclock
+LINUX += psfed
+
+TLS += relay
+TLS += typer
+
+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.dtch = -lutil
+LDLIBS.fbclock = -lz
+LDLIBS.freecell = -lcurses
+LDLIBS.glitch = -lz
+LDLIBS.modem = -lutil
+LDLIBS.pngo = -lz
+LDLIBS.ptee = -lutil
+LDLIBS.relay = -ltls
+LDLIBS.scheme = -lm
+LDLIBS.title = -lcurl
+LDLIBS.typer = -ltls
+
+ALL ?= meta any
+
+-include config.mk
+
+all: ${ALL}
+
+meta: .gitignore tags
+
+any: ${BINS}
+
+bsd: ${BSD}
+
+games: ${GAMES}
+
+linux: ${LINUX}
+
+tls: ${TLS}
+
+IGNORE = *.o *.html
+IGNORE += ${BINS} ${BSD} ${GAMES} ${LINUX} ${TLS}
+IGNORE += scheme.h tags htmltags
+
+.gitignore: Makefile
+	echo config.mk '${IGNORE}' | tr ' ' '\n' | sort > $@
+
+tags: *.[chly]
+	ctags -w *.[chly]
+
+clean:
+	rm -f ${IGNORE}
+
+install: ${ALL:%=install-%}
+
+install-meta:
+	install -d ${PREFIX}/bin ${MANDIR}/man1
+
+install-any: install-meta ${BINS} ${MANS}
+	install ${BINS} ${PREFIX}/bin
+	install -m 644 ${MANS} ${MANDIR}/man1
+
+install-bsd: install-meta ${BSD} ${MANS.BSD}
+	install ${BSD} ${PREFIX}/bin
+	install -m 644 ${MANS.BSD} ${MANDIR}/man1
+
+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
+
+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
+
+.c:
+	${CC} ${CFLAGS} ${LDFLAGS} $< ${LDLIBS.$@} -o $@
+
+.o:
+	${CC} ${LDFLAGS} $< ${LDLIBS.$@} -o $@
+
+.pl:
+	cp -f $< $@
+	chmod a+x $@
+
+OBJS.hilex = c11.o hilex.o make.o mdoc.o sh.o
+
+hilex: ${OBJS.hilex}
+	${CC} ${LDFLAGS} ${OBJS.$@} ${LDLIBS.$@} -o $@
+
+${OBJS.hilex}: hilex.h
+
+fbatt.o fbclock.o: scheme.h
+
+psf2png.o scheme.o: png.h
+
+scheme.h: scheme
+	./scheme -c > $@
+
+include html.mk
diff --git a/bin/README.7 b/bin/README.7
new file mode 100644
index 00000000..0ff46eab
--- /dev/null
+++ b/bin/README.7
@@ -0,0 +1,87 @@
+.Dd April 26, 2021
+.Dt BIN 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm bin
+.Nd various utilities
+.
+.Sh DESCRIPTION
+Various tools primarily targeting
+Darwin,
+.Fx
+and
+.Ox .
+Some tools target Linux.
+.
+.Pp
+.Bl -tag -width "fbclock(1)" -compact
+.It Xr beef 1
+Befunge-93 interpreter
+.It Xr bibsort 1
+reformat bibliography
+.It Xr bit 1
+calculator
+.It Xr bri 1
+backlight brightness control
+.It Xr c 1
+run C statements
+.It Xr dtch 1
+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 glitch 1
+PNG glitcher
+.It Xr hilex 1
+syntax highlighter
+.It Xr htagml 1
+tags HTMLizer
+.It Xr modem 1
+fixed baud rate wrapper
+.It Xr mtags 1
+miscellaneous tags
+.It Xr nudge 1
+terminal vibrator
+.It Xr order 1
+operator precedence
+.It Xr pbd 1
+macOS pasteboard daemon
+.It Xr pngo 1
+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
+IRC relay bot
+.It Xr scheme 1
+color scheme
+.It Xr shotty 1
+terminal capture
+.It Xr sup 1
+single-use passwords
+.It Xr title 1
+page titles
+.It Xr up 1
+upload file
+.It Xr when 1
+date calculator
+.It Xr xx 1
+hexdump
+.El
+.
+.Pp
+One piece of reused code.
+.Pp
+.Bl -tag -width "png(3)" -compact
+.It Xr png 3
+basic PNG output
+.El
diff --git a/bin/beef.c b/bin/beef.c
new file mode 100644
index 00000000..b2579b73
--- /dev/null
+++ b/bin/beef.c
@@ -0,0 +1,141 @@
+/* Copyright (C) 2019  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 <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <time.h>
+
+enum {
+	Cols = 80,
+	Rows = 25,
+};
+static char page[Rows][Cols];
+
+static char get(int y, int x) {
+	if (y < 0 || y >= Rows) return 0;
+	if (x < 0 || x >= Cols) return 0;
+	return page[y][x];
+}
+static void put(int y, int x, char v) {
+	if (y < 0 || y >= Rows) return;
+	if (x < 0 || x >= Cols) return;
+	page[y][x] = v;
+}
+
+enum { StackLen = 1024 };
+static long stack[StackLen];
+static size_t top = StackLen;
+
+static void push(long val) {
+	if (!top) errx(EX_SOFTWARE, "stack overflow");
+	stack[--top] = val;
+}
+static long pop(void) {
+	if (top == StackLen) return 0;
+	return stack[top++];
+}
+
+static struct {
+	int y, x;
+	int dy, dx;
+} pc = { .dx = 1 };
+
+static void inc(void) {
+	pc.y += pc.dy;
+	pc.x += pc.dx;
+	if (pc.y == -1) pc.y += Rows;
+	if (pc.x == -1) pc.x += Cols;
+	if (pc.y == Rows) pc.y -= Rows;
+	if (pc.x == Cols) pc.x -= Cols;
+}
+
+static bool string;
+
+static bool step(void) {
+	char ch = page[pc.y][pc.x];
+
+	if (ch == '"') {
+		string ^= true;
+	} else if (string) {
+		push(ch);
+		inc();
+		return true;
+	}
+
+	if (ch == '?') ch = "><^v"[rand() % 4];
+
+	long x, y, v;
+	switch (ch) {
+		break; case '+': push(pop() + pop());
+		break; case '-': y = pop(); x = pop(); push(x - y);
+		break; case '*': push(pop() * pop());
+		break; case '/': y = pop(); x = pop(); push(x / y);
+		break; case '%': y = pop(); x = pop(); push(x % y);
+		break; case '!': push(!pop());
+		break; case '`': y = pop(); x = pop(); push(x > y);
+		break; case '>': pc.dy = 0; pc.dx = +1;
+		break; case '<': pc.dy = 0; pc.dx = -1;
+		break; case '^': pc.dy = -1; pc.dx = 0;
+		break; case 'v': pc.dy = +1; pc.dx = 0;
+		break; case '_': pc.dy = 0; pc.dx = (pop() ? -1 : +1);
+		break; case '|': pc.dx = 0; pc.dy = (pop() ? -1 : +1);
+		break; case ':': x = pop(); push(x); push(x);
+		break; case '\\': y = pop(); x = pop(); push(y); push(x);
+		break; case '$': pop();
+		break; case '.': printf("%ld ", pop()); fflush(stdout);
+		break; case ',': printf("%c", (char)pop()); fflush(stdout);
+		break; case '#': inc();
+		break; case 'g': y = pop(); x = pop(); push(get(y, x));
+		break; case 'p': y = pop(); x = pop(); v = pop(); put(y, x, v);
+		break; case '&': x = EOF; scanf("%ld", &x); push(x);
+		break; case '~': push(getchar());
+		break; case '@': return false;
+		break; default:  if (ch >= '0' && ch <= '9') push(ch - '0');
+	}
+
+	inc();
+	return true;
+}
+
+int main(int argc, char *argv[]) {
+	srand(time(NULL));
+	memset(page, ' ', sizeof(page));
+
+	FILE *file = stdin;
+	if (argc > 1) {
+		file = fopen(argv[1], "r");
+		if (!file) err(EX_NOINPUT, "%s", argv[1]);
+	}
+
+	int y = 0;
+	char *line = NULL;
+	size_t cap = 0;
+	while (y < Rows && 0 < getline(&line, &cap, file)) {
+		for (int x = 0; x < Cols; ++x) {
+			if (line[x] == '\n' || !line[x]) break;
+			page[y][x] = line[x];
+		}
+		y++;
+	}
+	free(line);
+
+	while (step());
+	return pop();
+}
diff --git a/bin/bibsort.pl b/bin/bibsort.pl
new file mode 100644
index 00000000..0d132c19
--- /dev/null
+++ b/bin/bibsort.pl
@@ -0,0 +1,68 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+while (<>) {
+	print;
+	last if /^[.]Sh STANDARDS$/;
+}
+
+my ($ref, @refs);
+while (<>) {
+	next if /^[.](Bl|It|$)/;
+	last if /^[.]El$/;
+	if (/^[.]Rs$/) {
+		$ref = {};
+	} elsif (/^[.]%(.) (.*)/) {
+		$ref->{$1} = [] unless $ref->{$1};
+		push @{$ref->{$1}}, $2;
+	} elsif (/^[.]Re$/) {
+		push @refs, $ref;
+	} else {
+		print;
+	}
+}
+
+sub byLast {
+	my ($af, $al) = split /\s(\S+)(,.*)?$/, $a;
+	my ($bf, $bl) = split /\s(\S+)(,.*)?$/, $b;
+	($al // $af) cmp ($bl // $bf) || $af cmp $bf;
+}
+
+foreach $ref (@refs) {
+	@{$ref->{A}} = sort byLast @{$ref->{A}};
+	@{$ref->{Q}} = sort @{$ref->{Q}} if $ref->{Q};
+	if ($ref->{N} && $ref->{N}[0] =~ /RFC/) {
+		$ref->{R} = $ref->{N};
+		delete $ref->{N};
+	}
+	if ($ref->{R} && $ref->{R}[0] =~ /RFC (\d+)/ && !$ref->{U}) {
+		$ref->{U} = ["https://tools.ietf.org/html/rfc${1}"];
+	}
+}
+
+sub byAuthor {
+	local ($a, $b) = ($a->{A}[0], $b->{A}[0]);
+	byLast();
+}
+
+{
+	local ($,, $\) = (' ', "\n");
+	print '.Bl', '-item';
+	foreach $ref (sort byAuthor @refs) {
+		print '.It';
+		print '.Rs';
+		foreach my $key (qw(A T B I J R N V U P Q C D O)) {
+			next unless $ref->{$key};
+			foreach (@{$ref->{$key}}) {
+				print ".%${key}", $_;
+			}
+		}
+		print '.Re';
+	}
+	print '.El';
+}
+
+while (<>) {
+	print;
+}
diff --git a/bin/bit.y b/bin/bit.y
new file mode 100644
index 00000000..ab310492
--- /dev/null
+++ b/bin/bit.y
@@ -0,0 +1,202 @@
+/* Copyright (C) 2019  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 <ctype.h>
+#include <err.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+
+#define MASK(b) ((1ULL << (b)) - 1)
+
+#define YYSTYPE uint64_t
+
+static int yylex(void);
+static void yyerror(const char *str);
+static void print(uint64_t val);
+
+static uint64_t vars[128];
+
+%}
+
+%token Int Var
+
+%left '$'
+%right '='
+%left '|'
+%left '^'
+%left '&'
+%left Shl Shr Sar
+%left '+' '-'
+%left '*' '/' '%'
+%right '~'
+%left 'K' 'M' 'G' 'T'
+
+%%
+
+stmt:
+	| stmt expr '\n' { print(vars['_'] = $2); printf("\n"); }
+	| stmt expr ',' { print(vars['_'] = $2); }
+	| stmt '\n'
+	;
+
+expr:
+	Int
+	| Var { $$ = vars[$1]; }
+	| '(' expr ')' { $$ = $2; }
+	| expr 'K' { $$ = $1 << 10; }
+	| expr 'M' { $$ = $1 << 20; }
+	| expr 'G' { $$ = $1 << 30; }
+	| expr 'T' { $$ = $1 << 40; }
+	| '~' expr { $$ = ~$2; }
+	| '&' expr %prec '~' { $$ = MASK($2); }
+	| '+' expr %prec '~' { $$ = +$2; }
+	| '-' expr %prec '~' { $$ = -$2; }
+	| expr '*' expr { $$ = $1 * $3; }
+	| expr '/' expr { $$ = $1 / $3; }
+	| expr '%' expr { $$ = $1 % $3; }
+	| expr '+' expr { $$ = $1 + $3; }
+	| expr '-' expr { $$ = $1 - $3; }
+	| expr Shl expr { $$ = $1 << $3; }
+	| expr Shr expr { $$ = $1 >> $3; }
+	| expr Sar expr { $$ = (int64_t)$1 >> $3; }
+	| expr '&' expr { $$ = $1 & $3; }
+	| expr '^' expr { $$ = $1 ^ $3; }
+	| expr '|' expr { $$ = $1 | $3; }
+	| Var '=' expr { $$ = vars[$1] = $3; }
+	| expr '$' { $$ = $1; }
+	;
+
+%%
+
+static int lexInt(uint64_t base) {
+	yylval = 0;
+	for (int ch; EOF != (ch = getchar());) {
+		uint64_t digit = base;
+		if (ch == '_') {
+			continue;
+		} else if (isdigit(ch)) {
+			digit = ch - '0';
+		} else if (isxdigit(ch)) {
+			digit = 0xA + toupper(ch) - 'A';
+		}
+		if (digit >= base) {
+			ungetc(ch, stdin);
+			return Int;
+		}
+		yylval *= base;
+		yylval += digit;
+	}
+	return Int;
+}
+
+static int yylex(void) {
+	int ch;
+	while (isblank(ch = getchar()));
+	if (ch == '\'') {
+		yylval = 0;
+		while (EOF != (ch = getchar()) && ch != '\'') {
+			yylval <<= 8;
+			yylval |= ch;
+		}
+		return Int;
+	} else if (ch == '0') {
+		ch = getchar();
+		if (ch == 'b') {
+			return lexInt(2);
+		} else if (ch == 'x') {
+			return lexInt(16);
+		} else {
+			ungetc(ch, stdin);
+			return lexInt(8);
+		}
+	} else if (isdigit(ch)) {
+		ungetc(ch, stdin);
+		return lexInt(10);
+	} else if (ch == '_' || islower(ch)) {
+		yylval = ch;
+		return Var;
+	} else if (ch == '<') {
+		char ne = getchar();
+		if (ne == '<') {
+			return Shl;
+		} else {
+			ungetc(ne, stdin);
+			return ch;
+		}
+	} else if (ch == '-' || ch == '>') {
+		char ne = getchar();
+		if (ne == '>') {
+			return (ch == '-' ? Sar : Shr);
+		} else {
+			ungetc(ne, stdin);
+			return ch;
+		}
+	} else {
+		return ch;
+	}
+}
+
+static void yyerror(const char *str) {
+	warnx("%s", str);
+}
+
+static const char *Codes[128] = {
+	"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
+	"BS",  "HT",  "NL",  "VT",  "NP",  "CR",  "SO",  "SI",
+	"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
+	"CAN", "EM",  "SUB", "ESC", "FS",  "GS",  "RS",  "US",
+	[127] = "DEL",
+};
+
+static void print(uint64_t val) {
+	int bits = val > UINT32_MAX ? 64
+		: val > UINT16_MAX ? 32
+		: val > UINT8_MAX ? 16
+		: 8;
+	printf("0x%0*"PRIX64" %"PRId64"", bits >> 2, val, (int64_t)val);
+	if (bits == 8) {
+		char bin[9] = {0};
+		for (int i = 0; i < 8; ++i) {
+			bin[i] = '0' + (val >> (7 - i) & 1);
+		}
+		printf(" %#"PRIo64" 0b%s", val, bin);
+	}
+	if (val < 128) {
+		if (isprint(val)) printf(" '%c'", (char)val);
+		if (Codes[val]) printf(" %s", Codes[val]);
+	}
+	if (val) {
+		if (!(val & MASK(40))) {
+			printf(" %"PRIu64"T", val >> 40);
+		} else if (!(val & MASK(30))) {
+			printf(" %"PRIu64"G", val >> 30);
+		} else if (!(val & MASK(20))) {
+			printf(" %"PRIu64"M", val >> 20);
+		} else if (!(val & MASK(10))) {
+			printf(" %"PRIu64"K", val >> 10);
+		}
+	}
+	printf("\n");
+}
+
+int main(void) {
+	while (yyparse());
+}
diff --git a/bin/bri.c b/bin/bri.c
new file mode 100644
index 00000000..81df2995
--- /dev/null
+++ b/bin/bri.c
@@ -0,0 +1,85 @@
+/* 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/c.sh b/bin/c.sh
new file mode 100644
index 00000000..ff059437
--- /dev/null
+++ b/bin/c.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+set -eu
+
+temp=$(mktemp -d)
+trap 'rm -r "${temp}"' EXIT
+
+exec 3>>"${temp}/run.c"
+
+cat >&3 <<EOF
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <unistd.h>
+EOF
+
+expr=
+type=
+while getopts 'e:i:t' opt; do
+	case "${opt}" in
+		(e) expr=$OPTARG;;
+		(i) echo "#include <${OPTARG}>" >&3;;
+		(t) type=1;;
+		(?) exit 1;;
+	esac
+done
+shift $((OPTIND - 1))
+
+cat >&3 <<EOF
+int main(int argc, char *argv[]) {
+	(void)argc;
+	(void)argv;
+	$*;
+EOF
+
+if [ -n "${type}" ]; then
+	cat >&3 <<EOF
+	printf(
+		_Generic(
+			${expr},
+			char: "(char) ",
+			char *: "(char *) ",
+			const char *: "(const char *) ",
+			wchar_t *: "(wchar_t *) ",
+			const wchar_t *: "(const wchar_t *) ",
+			signed char: "(signed char) ",
+			short: "(short) ",
+			int: "(int) ",
+			long: "(long) ",
+			long long: "(long long) ",
+			unsigned char: "(unsigned char) ",
+			unsigned short: "(unsigned short) ",
+			unsigned int: "(unsigned int) ",
+			unsigned long: "(unsigned long) ",
+			unsigned long long: "(unsigned long long) ",
+			float: "(float) ",
+			double: "(double) ",
+			long double: "(long double) ",
+			default: "(void *) "
+		)
+	);
+EOF
+fi
+
+if [ -n "${expr}" ]; then
+	cat >&3 <<EOF
+	printf(
+		_Generic(
+			${expr},
+			char: "%c\n",
+			char *: "%s\n",
+			const char *: "%s\n",
+			wchar_t *: "%ls\n",
+			const wchar_t *: "%ls\n",
+			signed char: "%hhd\n",
+			short: "%hd\n",
+			int: "%d\n",
+			long: "%ld\n",
+			long long: "%lld\n",
+			unsigned char: "%hhu\n",
+			unsigned short: "%hu\n",
+			unsigned int: "%u\n",
+			unsigned long: "%lu\n",
+			unsigned long long: "%llu\n",
+			float: "%g\n",
+			double: "%g\n",
+			long double: "%Lg\n",
+			default: "%p\n"
+		),
+		${expr}
+	);
+EOF
+fi
+
+if [ $# -eq 0 -a -z "${expr}" ]; then
+	cat >&3
+fi
+
+echo '}' >&3
+
+cat >"${temp}/Makefile" <<EOF
+CFLAGS += -Wall -Wextra -Wpedantic
+EOF
+
+make -s -C "${temp}" run
+"${temp}/run"
diff --git a/bin/c11.l b/bin/c11.l
new file mode 100644
index 00000000..b9490f2e
--- /dev/null
+++ b/bin/c11.l
@@ -0,0 +1,149 @@
+/* Copyright (C) 2020  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/>.
+ */
+
+%option prefix="c11"
+%option noyywrap
+
+%{
+#include "hilex.h"
+%}
+
+%s MacroLine MacroInclude
+%x CharLiteral StringLiteral
+
+ident [_[:alpha:]][_[:alnum:]]*
+width "*"|[0-9]+
+
+%%
+	static int pop = INITIAL;
+
+[[:blank:]]+ { return Normal; }
+
+^"%"[%{}]? {
+	BEGIN(pop = MacroLine);
+	return Macro;
+}
+
+([-+*/%&|^=!<>]|"<<"|">>")"="? |
+[=~.?:]|"["|"]"|"++"|"--"|"&&"|"||"|"->" |
+sizeof|(_A|alignof) {
+	return Operator;
+}
+
+([1-9][0-9]*|"0"[0-7]*|"0x"[[:xdigit:]]+)([ulUL]{0,3}) |
+([0-9]+("."[0-9]*)?|[0-9]*"."[0-9]+)([eE][+-]?[0-9]+)?[flFL]? |
+"0x"[[:xdigit:]]*("."[[:xdigit:]]*)?([pP][+-]?[0-9]+)[flFL]? {
+	return Number;
+}
+
+auto|break|case|const|continue|default|do|else|enum|extern|for|goto|if|inline |
+register|restrict|return|static|struct|switch|typedef|union|volatile|while |
+(_A|a)lignas|_Atomic|_Generic|(_N|n)oreturn|(_S|s)tatic_assert |
+(_T|t)hread_local {
+	return Keyword;
+}
+
+^"#"[[:blank:]]*(include|import) {
+	BEGIN(pop = MacroInclude);
+	return Macro;
+}
+^"#"[[:blank:]]*{ident} {
+	BEGIN(pop = MacroLine);
+	return Macro;
+}
+<MacroInclude>"<"[^>]+">" {
+	return String;
+}
+<MacroLine,MacroInclude>{
+	"\n" {
+		BEGIN(pop = INITIAL);
+		return Normal;
+	}
+	"\\\n" { return Macro; }
+	{ident} { return Macro; }
+}
+
+{ident} { return Ident; }
+
+"//"([^\n]|"\\\n")* |
+"/*"([^*]|"*"[^/])*"*"+"/" {
+	return Comment;
+}
+
+[LUu]?"'"/[^\\] {
+	BEGIN(CharLiteral);
+	yymore();
+}
+[LUu]?"'" {
+	BEGIN(CharLiteral);
+	return String;
+}
+([LU]|u8?)?"\""/[^\\%] {
+	BEGIN(StringLiteral);
+	yymore();
+}
+([LU]|u8?)?"\"" {
+	BEGIN(StringLiteral);
+	return String;
+}
+
+<CharLiteral,StringLiteral>{
+	"\\\n" |
+	"\\"[''""?\\abfnrtv] |
+	"\\"([0-7]{1,3}) |
+	"\\x"([[:xdigit:]]{2}) |
+	"\\u"([[:xdigit:]]{4}) |
+	"\\U"([[:xdigit:]]{8}) {
+		return Escape;
+	}
+}
+<StringLiteral>{
+	"%%" |
+	"%"[EO]?[ABCDFGHIMRSTUVWXYZabcdeghjmnprtuwxyz] |
+	"%"[ #+-0]*{width}?("."{width})?([Lhjltz]|hh|ll)?[AEFGXacdefginopsux] {
+		return Format;
+	}
+}
+
+<CharLiteral>{
+	[^\\'']*"'" {
+		BEGIN(pop);
+		return String;
+	}
+	[^\\'']+|. { return String; }
+}
+<StringLiteral>{
+	[^%\\""]*"\"" {
+		BEGIN(pop);
+		return String;
+	}
+	[^%\\""]+|. { return String; }
+}
+
+<MacroLine,MacroInclude>. {
+	return Macro;
+}
+
+.|\n { return Normal; }
+
+%{
+	(void)yyunput;
+	(void)input;
+%}
+
+%%
+
+const struct Lexer LexC = { yylex, &yyin, &yytext };
diff --git a/bin/dash/.gitignore b/bin/dash/.gitignore
new file mode 100644
index 00000000..63fccdc3
--- /dev/null
+++ b/bin/dash/.gitignore
@@ -0,0 +1,34 @@
+# .gitignore for dash
+
+# generated by autogen.sh
+/autom4te.cache/
+
+# generated by configure
+Makefile
+.deps
+.dirstamp
+/config.cache
+/config.h
+/config.log
+/config.status
+/stamp-h1
+
+# generated by make
+/src/token_vars.h
+
+# Apple debug symbol bundles
+*.dSYM/
+
+# backups and patch artefacts
+*~
+*.bak
+*.orig
+*.rej
+
+# OS generated files
+.DS_Store
+.DS_Store?
+._*
+.Spotlight*
+.Trash*
+*[Tt]humbs.db
diff --git a/COPYING b/bin/dash/COPYING
index 37f8189c..37f8189c 100644
--- a/COPYING
+++ b/bin/dash/COPYING
diff --git a/ChangeLog b/bin/dash/ChangeLog
index 406e20c0..406e20c0 100644
--- a/ChangeLog
+++ b/bin/dash/ChangeLog
diff --git a/ChangeLog.O b/bin/dash/ChangeLog.O
index dfdb2cec..dfdb2cec 100644
--- a/ChangeLog.O
+++ b/bin/dash/ChangeLog.O
diff --git a/Makefile.am b/bin/dash/Makefile.am
index af437a64..af437a64 100644
--- a/Makefile.am
+++ b/bin/dash/Makefile.am
diff --git a/bin/dash/Makefile.in b/bin/dash/Makefile.in
new file mode 100644
index 00000000..49084bfd
--- /dev/null
+++ b/bin/dash/Makefile.in
@@ -0,0 +1,776 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = .
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
+	$(am__configure_deps) $(am__DIST_COMMON)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	cscope distdir distdir-am dist dist-all distcheck
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \
+	config.h.in
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+CSCOPE = cscope
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in COPYING \
+	ChangeLog compile install-sh missing
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+  if test -d "$(distdir)"; then \
+    find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+      && rm -rf "$(distdir)" \
+      || { sleep 5 && rm -rf "$(distdir)"; }; \
+  else :; fi
+am__post_remove_distdir = $(am__remove_distdir)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+DIST_TARGETS = dist-gzip
+# Exists only to be overridden by the user if desired.
+AM_DISTCHECK_DVI_TARGET = dvi
+distuninstallcheck_listfiles = find . -type f -print
+am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
+  | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = src
+all: config.h
+	$(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+am--refresh: Makefile
+	@:
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
+	      $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    echo ' $(SHELL) ./config.status'; \
+	    $(SHELL) ./config.status;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	$(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	$(am__cd) $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+	@test -f $@ || rm -f stamp-h1
+	@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+	@rm -f stamp-h1
+	cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in:  $(am__configure_deps) 
+	($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+	rm -f stamp-h1
+	touch $@
+
+distclean-hdr:
+	-rm -f config.h stamp-h1
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscope: cscope.files
+	test ! -s cscope.files \
+	  || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
+clean-cscope:
+	-rm -f cscope.files
+cscope.files: clean-cscope cscopelist
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+	-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
+
+distdir: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+	$(am__remove_distdir)
+	test -d "$(distdir)" || mkdir "$(distdir)"
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+	-test -n "$(am__skip_mode_fix)" \
+	|| find "$(distdir)" -type d ! -perm -755 \
+		-exec chmod u+rwx,go+rx {} \; -o \
+	  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+	  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+	  ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+	|| chmod -R a+r "$(distdir)"
+dist-gzip: distdir
+	tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
+	$(am__post_remove_distdir)
+
+dist-bzip2: distdir
+	tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
+	$(am__post_remove_distdir)
+
+dist-lzip: distdir
+	tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
+	$(am__post_remove_distdir)
+
+dist-xz: distdir
+	tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
+	$(am__post_remove_distdir)
+
+dist-zstd: distdir
+	tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst
+	$(am__post_remove_distdir)
+
+dist-tarZ: distdir
+	@echo WARNING: "Support for distribution archives compressed with" \
+		       "legacy program 'compress' is deprecated." >&2
+	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
+	tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+	$(am__post_remove_distdir)
+
+dist-shar: distdir
+	@echo WARNING: "Support for shar distribution archives is" \
+	               "deprecated." >&2
+	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
+	shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
+	$(am__post_remove_distdir)
+
+dist-zip: distdir
+	-rm -f $(distdir).zip
+	zip -rq $(distdir).zip $(distdir)
+	$(am__post_remove_distdir)
+
+dist dist-all:
+	$(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
+	$(am__post_remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration.  Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+	case '$(DIST_ARCHIVES)' in \
+	*.tar.gz*) \
+	  eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
+	*.tar.bz2*) \
+	  bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+	*.tar.lz*) \
+	  lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
+	*.tar.xz*) \
+	  xz -dc $(distdir).tar.xz | $(am__untar) ;;\
+	*.tar.Z*) \
+	  uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+	*.shar.gz*) \
+	  eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
+	*.zip*) \
+	  unzip $(distdir).zip ;;\
+	*.tar.zst*) \
+	  zstd -dc $(distdir).tar.zst | $(am__untar) ;;\
+	esac
+	chmod -R a-w $(distdir)
+	chmod u+w $(distdir)
+	mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
+	chmod a-w $(distdir)
+	test -d $(distdir)/_build || exit 0; \
+	dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+	  && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+	  && am__cwd=`pwd` \
+	  && $(am__cd) $(distdir)/_build/sub \
+	  && ../../configure \
+	    $(AM_DISTCHECK_CONFIGURE_FLAGS) \
+	    $(DISTCHECK_CONFIGURE_FLAGS) \
+	    --srcdir=../.. --prefix="$$dc_install_base" \
+	  && $(MAKE) $(AM_MAKEFLAGS) \
+	  && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \
+	  && $(MAKE) $(AM_MAKEFLAGS) check \
+	  && $(MAKE) $(AM_MAKEFLAGS) install \
+	  && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+	  && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+	  && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+	        distuninstallcheck \
+	  && chmod -R a-w "$$dc_install_base" \
+	  && ({ \
+	       (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+	            distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+	      } || { rm -rf "$$dc_destdir"; exit 1; }) \
+	  && rm -rf "$$dc_destdir" \
+	  && $(MAKE) $(AM_MAKEFLAGS) dist \
+	  && rm -rf $(DIST_ARCHIVES) \
+	  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+	  && cd "$$am__cwd" \
+	  || exit 1
+	$(am__post_remove_distdir)
+	@(echo "$(distdir) archives ready for distribution: "; \
+	  list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+	  sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+	@test -n '$(distuninstallcheck_dir)' || { \
+	  echo 'ERROR: trying to run $@ with an empty' \
+	       '$$(distuninstallcheck_dir)' >&2; \
+	  exit 1; \
+	}; \
+	$(am__cd) '$(distuninstallcheck_dir)' || { \
+	  echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
+	  exit 1; \
+	}; \
+	test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
+	   || { echo "ERROR: files left after uninstall:" ; \
+	        if test -n "$(DESTDIR)"; then \
+	          echo "  (check DESTDIR support)"; \
+	        fi ; \
+	        $(distuninstallcheck_listfiles) ; \
+	        exit 1; } >&2
+distcleancheck: distclean
+	@if test '$(srcdir)' = . ; then \
+	  echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+	  exit 1 ; \
+	fi
+	@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+	  || { echo "ERROR: files left in build directory after distclean:" ; \
+	       $(distcleancheck_listfiles) ; \
+	       exit 1; } >&2
+check-am: all-am
+check: check-recursive
+all-am: Makefile config.h
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
+	-rm -rf $(top_srcdir)/autom4te.cache
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) all install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+	am--refresh check check-am clean clean-cscope clean-generic \
+	cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \
+	dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \
+	dist-zstd distcheck distclean distclean-generic distclean-hdr \
+	distclean-tags distcleancheck distdir distuninstallcheck dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-data install-data-am install-dvi install-dvi-am \
+	install-exec install-exec-am install-html install-html-am \
+	install-info install-info-am install-man install-pdf \
+	install-pdf-am install-ps install-ps-am install-strip \
+	installcheck installcheck-am installdirs installdirs-am \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/bin/dash/aclocal.m4 b/bin/dash/aclocal.m4
new file mode 100644
index 00000000..6715ee0e
--- /dev/null
+++ b/bin/dash/aclocal.m4
@@ -0,0 +1,1132 @@
+# generated automatically by aclocal 1.16.3 -*- Autoconf -*-
+
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
+[m4_warning([this file was generated for autoconf 2.69.
+You have another version of autoconf.  It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically 'autoreconf'.])])
+
+# Copyright (C) 2002-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.16'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version.  Point them to the right macro.
+m4_if([$1], [1.16.3], [],
+      [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too.  Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.16.3])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to '$srcdir/foo'.  In other projects, it is set to
+# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory.  The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run.  This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+#    fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+#    fails if $ac_aux_dir is absolute,
+#    fails when called from a subdirectory in a VPATH build with
+#          a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir.  In an in-source build this is usually
+# harmless because $srcdir is '.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
+#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+#   MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH.  The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+])
+
+# AM_CONDITIONAL                                            -*- Autoconf -*-
+
+# Copyright (C) 1997-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ([2.52])dnl
+ m4_if([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
+       [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+  AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+
+# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+m4_if([$1], [CC],   [depcc="$CC"   am_compiler_list=],
+      [$1], [CXX],  [depcc="$CXX"  am_compiler_list=],
+      [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+      [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
+      [$1], [UPC],  [depcc="$UPC"  am_compiler_list=],
+      [$1], [GCJ],  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                    [depcc="$$1"   am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+               [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_$1_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+  fi
+  am__universal=false
+  m4_case([$1], [CC],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac],
+    [CXX],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac])
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok '-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_$1_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE([dependency-tracking], [dnl
+AS_HELP_STRING(
+  [--enable-dependency-tracking],
+  [do not reject slow dependency extractors])
+AS_HELP_STRING(
+  [--disable-dependency-tracking],
+  [speeds up one-time build])])
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+AC_SUBST([am__nodep])dnl
+_AM_SUBST_NOTMAKE([am__nodep])dnl
+])
+
+# Generate code to set up dependency tracking.              -*- Autoconf -*-
+
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+  # Older Autoconf quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  # TODO: see whether this extra hack can be removed once we start
+  # requiring Autoconf 2.70 or later.
+  AS_CASE([$CONFIG_FILES],
+          [*\'*], [eval set x "$CONFIG_FILES"],
+          [*], [set x $CONFIG_FILES])
+  shift
+  # Used to flag and report bootstrapping failures.
+  am_rc=0
+  for am_mf
+  do
+    # Strip MF so we end up with the name of the file.
+    am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile which includes
+    # dependency-tracking related rules and includes.
+    # Grep'ing the whole file directly is not great: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+      || continue
+    am_dirpart=`AS_DIRNAME(["$am_mf"])`
+    am_filepart=`AS_BASENAME(["$am_mf"])`
+    AM_RUN_LOG([cd "$am_dirpart" \
+      && sed -e '/# am--include-marker/d' "$am_filepart" \
+        | $MAKE -f - am--depfiles]) || am_rc=$?
+  done
+  if test $am_rc -ne 0; then
+    AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
+    for automatic dependency tracking.  If GNU make was not used, consider
+    re-running the configure script with MAKE="gmake" (or whatever is
+    necessary).  You can also try re-running configure with the
+    '--disable-dependency-tracking' option to at least be able to build
+    the package (albeit without support for automatic dependency tracking).])
+  fi
+  AS_UNSET([am_dirpart])
+  AS_UNSET([am_filepart])
+  AS_UNSET([am_mf])
+  AS_UNSET([am_rc])
+  rm -f conftest-deps.mk
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking is enabled.
+# This creates each '.Po' and '.Plo' makefile fragment that we'll need in
+# order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+     [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+     [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])])
+
+# Do all the work for Automake.                             -*- Autoconf -*-
+
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This macro actually does too much.  Some checks are only needed if
+# your package does certain things.  But this isn't really a big deal.
+
+dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
+m4_define([AC_PROG_CC],
+m4_defn([AC_PROG_CC])
+[_AM_PROG_CC_C_O
+])
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out.  PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition.  After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.65])dnl
+dnl Autoconf wants to disallow AM_ names.  We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[AC_DIAGNOSE([obsolete],
+             [$0: two- and three-arguments forms are deprecated.])
+m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(
+  m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
+  [ok:ok],,
+  [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
+ AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
+AM_MISSING_PROG([AUTOCONF], [autoconf])
+AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
+AM_MISSING_PROG([AUTOHEADER], [autoheader])
+AM_MISSING_PROG([MAKEINFO], [makeinfo])
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+# For better backward compatibility.  To be removed once Automake 1.9.x
+# dies out for good.  For more background, see:
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
+# We need awk for the "check" target (and possibly the TAP driver).  The
+# system "awk" is bad on some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+	      [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+			     [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+		  [_AM_DEPENDENCIES([CC])],
+		  [m4_define([AC_PROG_CC],
+			     m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+		  [_AM_DEPENDENCIES([CXX])],
+		  [m4_define([AC_PROG_CXX],
+			     m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+		  [_AM_DEPENDENCIES([OBJC])],
+		  [m4_define([AC_PROG_OBJC],
+			     m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
+		  [_AM_DEPENDENCIES([OBJCXX])],
+		  [m4_define([AC_PROG_OBJCXX],
+			     m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
+])
+AC_REQUIRE([AM_SILENT_RULES])dnl
+dnl The testsuite driver may need to know about EXEEXT, so add the
+dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen.  This
+dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+  [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes.  So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+  cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present.  This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message.  This
+can help us improve future automake versions.
+
+END
+  if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+    echo 'Configuration will proceed anyway, since you have set the' >&2
+    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+    echo >&2
+  else
+    cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+    AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
+  fi
+fi
+dnl The trailing newline in this macro's definition is deliberate, for
+dnl backward compatibility and to allow trailing 'dnl'-style comments
+dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
+])
+
+dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated.  The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+AC_SUBST([install_sh])])
+
+# Copyright (C) 2003-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot.  For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Check to see how 'make' treats includes.	            -*- Autoconf -*-
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check whether make has an 'include' directive that can support all
+# the idioms we need for our automatic dependency tracking code.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive])
+cat > confinc.mk << 'END'
+am__doit:
+	@echo this is the am__doit target >confinc.out
+.PHONY: am__doit
+END
+am__include="#"
+am__quote=
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+  AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out])
+  AS_CASE([$?:`cat confinc.out 2>/dev/null`],
+      ['0:this is the am__doit target'],
+      [AS_CASE([$s],
+          [BSD], [am__include='.include' am__quote='"'],
+          [am__include='include' am__quote=''])])
+  if test "$am__include" != "#"; then
+    _am_result="yes ($s style)"
+    break
+  fi
+done
+rm -f confinc.* confmf.*
+AC_MSG_RESULT([${_am_result}])
+AC_SUBST([am__include])])
+AC_SUBST([am__quote])])
+
+# Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
+
+# Copyright (C) 1997-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it is modern enough.
+# If it is, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+  MISSING="\${SHELL} '$am_aux_dir/missing'"
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
+else
+  am_missing_run=
+  AC_MSG_WARN(['missing' script is too old or missing])
+fi
+])
+
+# Helper functions for option handling.                     -*- Autoconf -*-
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# --------------------
+# Set option NAME.  Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_PROG_CC_C_O
+# ---------------
+# Like AC_PROG_CC_C_O, but changed for automake.  We rewrite AC_PROG_CC
+# to automatically call this.
+AC_DEFUN([_AM_PROG_CC_C_O],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+AC_LANG_PUSH([C])dnl
+AC_CACHE_CHECK(
+  [whether $CC understands -c and -o together],
+  [am_cv_prog_cc_c_o],
+  [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
+  # Make sure it works both with $CC and with simple cc.
+  # Following AC_PROG_CC_C_O, we do the test twice because some
+  # compilers refuse to overwrite an existing .o file with -o,
+  # though they will create one.
+  am_cv_prog_cc_c_o=yes
+  for am_i in 1 2; do
+    if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
+         && test -f conftest2.$ac_objext; then
+      : OK
+    else
+      am_cv_prog_cc_c_o=no
+      break
+    fi
+  done
+  rm -f core conftest*
+  unset am_i])
+if test "$am_cv_prog_cc_c_o" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+AC_LANG_POP([C])])
+
+# For backward compatibility.
+AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_RUN_LOG(COMMAND)
+# -------------------
+# Run COMMAND, save the exit status in ac_status, and log it.
+# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
+AC_DEFUN([AM_RUN_LOG],
+[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+   ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   (exit $ac_status); }])
+
+# Check to make sure that the build environment is sane.    -*- Autoconf -*-
+
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[[\\\"\#\$\&\'\`$am_lf]]*)
+    AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+  *[[\\\"\#\$\&\'\`$am_lf\ \	]]*)
+    AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   am_has_slept=no
+   for am_try in 1 2; do
+     echo "timestamp, slept: $am_has_slept" > conftest.file
+     set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+     if test "$[*]" = "X"; then
+	# -L didn't work.
+	set X `ls -t "$srcdir/configure" conftest.file`
+     fi
+     if test "$[*]" != "X $srcdir/configure conftest.file" \
+	&& test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+	# If neither matched, then we have a broken ls.  This can happen
+	# if, for instance, CONFIG_SHELL is bash and it inherits a
+	# broken ls alias from the environment.  This has actually
+	# happened.  Such a system could not be considered "sane".
+	AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+  alias in your environment])
+     fi
+     if test "$[2]" = conftest.file || test $am_try -eq 2; then
+       break
+     fi
+     # Just in case.
+     sleep 1
+     am_has_slept=yes
+   done
+   test "$[2]" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT([yes])
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+  ( sleep 1 ) &
+  am_sleep_pid=$!
+fi
+AC_CONFIG_COMMANDS_PRE(
+  [AC_MSG_CHECKING([that generated files are newer than configure])
+   if test -n "$am_sleep_pid"; then
+     # Hide warnings about reused PIDs.
+     wait $am_sleep_pid 2>/dev/null
+   fi
+   AC_MSG_RESULT([done])])
+rm -f conftest.file
+])
+
+# Copyright (C) 2009-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_SILENT_RULES([DEFAULT])
+# --------------------------
+# Enable less verbose build rules; with the default set to DEFAULT
+# ("yes" being less verbose, "no" or empty being verbose).
+AC_DEFUN([AM_SILENT_RULES],
+[AC_ARG_ENABLE([silent-rules], [dnl
+AS_HELP_STRING(
+  [--enable-silent-rules],
+  [less verbose build output (undo: "make V=1")])
+AS_HELP_STRING(
+  [--disable-silent-rules],
+  [verbose build output (undo: "make V=0")])dnl
+])
+case $enable_silent_rules in @%:@ (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+dnl
+dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
+dnl do not support nested variable expansions.
+dnl See automake bug#9928 and bug#10237.
+am_make=${MAKE-make}
+AC_CACHE_CHECK([whether $am_make supports nested variables],
+   [am_cv_make_support_nested_variables],
+   [if AS_ECHO([['TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi])
+if test $am_cv_make_support_nested_variables = yes; then
+  dnl Using '$V' instead of '$(V)' breaks IRIX make.
+  AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AC_SUBST([AM_V])dnl
+AM_SUBST_NOTMAKE([AM_V])dnl
+AC_SUBST([AM_DEFAULT_V])dnl
+AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor 'install' (even GNU) is that you can't
+# specify the program used to strip binaries.  This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in "make install-strip", and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
+if test "$cross_compiling" != no; then
+  AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# --------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball.                            -*- Autoconf -*-
+
+# Copyright (C) 2004-2020 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of 'v7', 'ustar', or 'pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+#     tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+#     $(am__untar) < result.tar
+#
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AC_SUBST([AMTAR], ['$${TAR-tar}'])
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+
+m4_if([$1], [v7],
+  [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
+
+  [m4_case([$1],
+    [ustar],
+     [# The POSIX 1988 'ustar' format is defined with fixed-size fields.
+      # There is notably a 21 bits limit for the UID and the GID.  In fact,
+      # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
+      # and bug#13588).
+      am_max_uid=2097151 # 2^21 - 1
+      am_max_gid=$am_max_uid
+      # The $UID and $GID variables are not portable, so we need to resort
+      # to the POSIX-mandated id(1) utility.  Errors in the 'id' calls
+      # below are definitely unexpected, so allow the users to see them
+      # (that is, avoid stderr redirection).
+      am_uid=`id -u || echo unknown`
+      am_gid=`id -g || echo unknown`
+      AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
+      if test $am_uid -le $am_max_uid; then
+         AC_MSG_RESULT([yes])
+      else
+         AC_MSG_RESULT([no])
+         _am_tools=none
+      fi
+      AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
+      if test $am_gid -le $am_max_gid; then
+         AC_MSG_RESULT([yes])
+      else
+        AC_MSG_RESULT([no])
+        _am_tools=none
+      fi],
+
+  [pax],
+    [],
+
+  [m4_fatal([Unknown tar format])])
+
+  AC_MSG_CHECKING([how to create a $1 tar archive])
+
+  # Go ahead even if we have the value already cached.  We do so because we
+  # need to set the values for the 'am__tar' and 'am__untar' variables.
+  _am_tools=${am_cv_prog_tar_$1-$_am_tools}
+
+  for _am_tool in $_am_tools; do
+    case $_am_tool in
+    gnutar)
+      for _am_tar in tar gnutar gtar; do
+        AM_RUN_LOG([$_am_tar --version]) && break
+      done
+      am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+      am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+      am__untar="$_am_tar -xf -"
+      ;;
+    plaintar)
+      # Must skip GNU tar: if it does not support --format= it doesn't create
+      # ustar tarball either.
+      (tar --version) >/dev/null 2>&1 && continue
+      am__tar='tar chf - "$$tardir"'
+      am__tar_='tar chf - "$tardir"'
+      am__untar='tar xf -'
+      ;;
+    pax)
+      am__tar='pax -L -x $1 -w "$$tardir"'
+      am__tar_='pax -L -x $1 -w "$tardir"'
+      am__untar='pax -r'
+      ;;
+    cpio)
+      am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+      am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+      am__untar='cpio -i -H $1 -d'
+      ;;
+    none)
+      am__tar=false
+      am__tar_=false
+      am__untar=false
+      ;;
+    esac
+
+    # If the value was cached, stop now.  We just wanted to have am__tar
+    # and am__untar set.
+    test -n "${am_cv_prog_tar_$1}" && break
+
+    # tar/untar a dummy directory, and stop if the command works.
+    rm -rf conftest.dir
+    mkdir conftest.dir
+    echo GrepMe > conftest.dir/file
+    AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+    rm -rf conftest.dir
+    if test -s conftest.tar; then
+      AM_RUN_LOG([$am__untar <conftest.tar])
+      AM_RUN_LOG([cat conftest.dir/file])
+      grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+    fi
+  done
+  rm -rf conftest.dir
+
+  AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+  AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
diff --git a/autogen.sh b/bin/dash/autogen.sh
index 9879c53e..5e9006ca 100755
--- a/autogen.sh
+++ b/bin/dash/autogen.sh
@@ -2,5 +2,5 @@
 
 aclocal \
 && autoheader \
-&& automake --add-missing \
+&& automake --add-missing --copy \
 && autoconf
diff --git a/bin/dash/compile b/bin/dash/compile
new file mode 100755
index 00000000..23fcba01
--- /dev/null
+++ b/bin/dash/compile
@@ -0,0 +1,348 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand '-c -o'.
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+nl='
+'
+
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent tools from complaining about whitespace usage.
+IFS=" ""	$nl"
+
+file_conv=
+
+# func_file_conv build_file lazy
+# Convert a $build file to $host form and store it in $file
+# Currently only supports Windows hosts. If the determined conversion
+# type is listed in (the comma separated) LAZY, no conversion will
+# take place.
+func_file_conv ()
+{
+  file=$1
+  case $file in
+    / | /[!/]*) # absolute file, and not a UNC file
+      if test -z "$file_conv"; then
+	# lazily determine how to convert abs files
+	case `uname -s` in
+	  MINGW*)
+	    file_conv=mingw
+	    ;;
+	  CYGWIN* | MSYS*)
+	    file_conv=cygwin
+	    ;;
+	  *)
+	    file_conv=wine
+	    ;;
+	esac
+      fi
+      case $file_conv/,$2, in
+	*,$file_conv,*)
+	  ;;
+	mingw/*)
+	  file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
+	  ;;
+	cygwin/* | msys/*)
+	  file=`cygpath -m "$file" || echo "$file"`
+	  ;;
+	wine/*)
+	  file=`winepath -w "$file" || echo "$file"`
+	  ;;
+      esac
+      ;;
+  esac
+}
+
+# func_cl_dashL linkdir
+# Make cl look for libraries in LINKDIR
+func_cl_dashL ()
+{
+  func_file_conv "$1"
+  if test -z "$lib_path"; then
+    lib_path=$file
+  else
+    lib_path="$lib_path;$file"
+  fi
+  linker_opts="$linker_opts -LIBPATH:$file"
+}
+
+# func_cl_dashl library
+# Do a library search-path lookup for cl
+func_cl_dashl ()
+{
+  lib=$1
+  found=no
+  save_IFS=$IFS
+  IFS=';'
+  for dir in $lib_path $LIB
+  do
+    IFS=$save_IFS
+    if $shared && test -f "$dir/$lib.dll.lib"; then
+      found=yes
+      lib=$dir/$lib.dll.lib
+      break
+    fi
+    if test -f "$dir/$lib.lib"; then
+      found=yes
+      lib=$dir/$lib.lib
+      break
+    fi
+    if test -f "$dir/lib$lib.a"; then
+      found=yes
+      lib=$dir/lib$lib.a
+      break
+    fi
+  done
+  IFS=$save_IFS
+
+  if test "$found" != yes; then
+    lib=$lib.lib
+  fi
+}
+
+# func_cl_wrapper cl arg...
+# Adjust compile command to suit cl
+func_cl_wrapper ()
+{
+  # Assume a capable shell
+  lib_path=
+  shared=:
+  linker_opts=
+  for arg
+  do
+    if test -n "$eat"; then
+      eat=
+    else
+      case $1 in
+	-o)
+	  # configure might choose to run compile as 'compile cc -o foo foo.c'.
+	  eat=1
+	  case $2 in
+	    *.o | *.[oO][bB][jJ])
+	      func_file_conv "$2"
+	      set x "$@" -Fo"$file"
+	      shift
+	      ;;
+	    *)
+	      func_file_conv "$2"
+	      set x "$@" -Fe"$file"
+	      shift
+	      ;;
+	  esac
+	  ;;
+	-I)
+	  eat=1
+	  func_file_conv "$2" mingw
+	  set x "$@" -I"$file"
+	  shift
+	  ;;
+	-I*)
+	  func_file_conv "${1#-I}" mingw
+	  set x "$@" -I"$file"
+	  shift
+	  ;;
+	-l)
+	  eat=1
+	  func_cl_dashl "$2"
+	  set x "$@" "$lib"
+	  shift
+	  ;;
+	-l*)
+	  func_cl_dashl "${1#-l}"
+	  set x "$@" "$lib"
+	  shift
+	  ;;
+	-L)
+	  eat=1
+	  func_cl_dashL "$2"
+	  ;;
+	-L*)
+	  func_cl_dashL "${1#-L}"
+	  ;;
+	-static)
+	  shared=false
+	  ;;
+	-Wl,*)
+	  arg=${1#-Wl,}
+	  save_ifs="$IFS"; IFS=','
+	  for flag in $arg; do
+	    IFS="$save_ifs"
+	    linker_opts="$linker_opts $flag"
+	  done
+	  IFS="$save_ifs"
+	  ;;
+	-Xlinker)
+	  eat=1
+	  linker_opts="$linker_opts $2"
+	  ;;
+	-*)
+	  set x "$@" "$1"
+	  shift
+	  ;;
+	*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
+	  func_file_conv "$1"
+	  set x "$@" -Tp"$file"
+	  shift
+	  ;;
+	*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
+	  func_file_conv "$1" mingw
+	  set x "$@" "$file"
+	  shift
+	  ;;
+	*)
+	  set x "$@" "$1"
+	  shift
+	  ;;
+      esac
+    fi
+    shift
+  done
+  if test -n "$linker_opts"; then
+    linker_opts="-link$linker_opts"
+  fi
+  exec "$@" $linker_opts
+  exit 1
+}
+
+eat=
+
+case $1 in
+  '')
+     echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand '-c -o'.
+Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file 'INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "compile $scriptversion"
+    exit $?
+    ;;
+  cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
+  icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
+    func_cl_wrapper "$@"      # Doesn't return...
+    ;;
+esac
+
+ofile=
+cfile=
+
+for arg
+do
+  if test -n "$eat"; then
+    eat=
+  else
+    case $1 in
+      -o)
+	# configure might choose to run compile as 'compile cc -o foo foo.c'.
+	# So we strip '-o arg' only if arg is an object.
+	eat=1
+	case $2 in
+	  *.o | *.obj)
+	    ofile=$2
+	    ;;
+	  *)
+	    set x "$@" -o "$2"
+	    shift
+	    ;;
+	esac
+	;;
+      *.c)
+	cfile=$1
+	set x "$@" "$1"
+	shift
+	;;
+      *)
+	set x "$@" "$1"
+	shift
+	;;
+    esac
+  fi
+  shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+  # If no '-o' option was seen then we might have been invoked from a
+  # pattern rule where we don't need one.  That is ok -- this is a
+  # normal compilation that the losing compiler can handle.  If no
+  # '.c' file was seen then we are probably linking.  That is also
+  # ok.
+  exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use '[/\\:.-]' here to ensure that we don't use the same name
+# that we are using for the .o file.  Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
+while true; do
+  if mkdir "$lockdir" >/dev/null 2>&1; then
+    break
+  fi
+  sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+  test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+  test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/bin/dash/config.h.in b/bin/dash/config.h.in
new file mode 100644
index 00000000..6fb0b3fb
--- /dev/null
+++ b/bin/dash/config.h.in
@@ -0,0 +1,205 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if __attribute__((__alias__())) is supported */
+#undef HAVE_ALIAS_ATTRIBUTE
+
+/* Define to 1 if you have the <alloca.h> header file. */
+#undef HAVE_ALLOCA_H
+
+/* Define to 1 if you have the `bsearch' function. */
+#undef HAVE_BSEARCH
+
+/* Define to 1 if you have the declaration of `isblank', and to 0 if you
+   don't. */
+#undef HAVE_DECL_ISBLANK
+
+/* Define to 1 if you have the `faccessat' function. */
+#undef HAVE_FACCESSAT
+
+/* Define to 1 if you have the `fnmatch' function. */
+#undef HAVE_FNMATCH
+
+/* Define to 1 if you have the `getpwnam' function. */
+#undef HAVE_GETPWNAM
+
+/* Define to 1 if you have the `getrlimit' function. */
+#undef HAVE_GETRLIMIT
+
+/* Define to 1 if you have the `glob' function. */
+#undef HAVE_GLOB
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `isalpha' function. */
+#undef HAVE_ISALPHA
+
+/* Define to 1 if you have the `killpg' function. */
+#undef HAVE_KILLPG
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `mempcpy' function. */
+#undef HAVE_MEMPCPY
+
+/* Define to 1 if you have the <paths.h> header file. */
+#undef HAVE_PATHS_H
+
+/* Define to 1 if you have the `sigsetmask' function. */
+#undef HAVE_SIGSETMASK
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `stpcpy' function. */
+#undef HAVE_STPCPY
+
+/* Define to 1 if you have the `strchrnul' function. */
+#undef HAVE_STRCHRNUL
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strsignal' function. */
+#undef HAVE_STRSIGNAL
+
+/* Define to 1 if you have the `strtod' function. */
+#undef HAVE_STRTOD
+
+/* Define to 1 if you have the `strtoimax' function. */
+#undef HAVE_STRTOIMAX
+
+/* Define to 1 if you have the `strtoumax' function. */
+#undef HAVE_STRTOUMAX
+
+/* Define if your `struct stat' has `st_mtim' */
+#undef HAVE_ST_MTIM
+
+/* Define to 1 if you have the `sysconf' function. */
+#undef HAVE_SYSCONF
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define if your faccessat tells root all files are executable */
+#undef HAVE_TRADITIONAL_FACCESSAT
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `_el_fn_sh_complete' function. */
+#undef HAVE__EL_FN_SH_COMPLETE
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to printf format string for intmax_t */
+#undef PRIdMAX
+
+/* The size of `intmax_t', as computed by sizeof. */
+#undef SIZEOF_INTMAX_T
+
+/* The size of `long long int', as computed by sizeof. */
+#undef SIZEOF_LONG_LONG_INT
+
+/* Define if you build with -DSMALL */
+#undef SMALL
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Version number of package */
+#undef VERSION
+
+/* Define if you build with -DWITH_LINENO */
+#undef WITH_LINENO
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to system shell path */
+#undef _PATH_BSHELL
+
+/* Define to devnull device node path */
+#undef _PATH_DEVNULL
+
+/* Define to tty device node path */
+#undef _PATH_TTY
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* 64-bit operations are the same as 32-bit */
+#undef dirent64
+
+/* 64-bit operations are the same as 32-bit */
+#undef fstat64
+
+/* 64-bit operations are the same as 32-bit */
+#undef lstat64
+
+/* 64-bit operations are the same as 32-bit */
+#undef open64
+
+/* 64-bit operations are the same as 32-bit */
+#undef readdir64
+
+/* klibc has bsd_signal instead of signal */
+#undef signal
+
+/* 64-bit operations are the same as 32-bit */
+#undef stat64
diff --git a/bin/dash/configure b/bin/dash/configure
new file mode 100755
index 00000000..83889b3b
--- /dev/null
+++ b/bin/dash/configure
@@ -0,0 +1,6450 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for dash 0.5.11.2.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+	 /*)
+	   for as_base in sh bash ksh sh5; do
+	     # Try only shells that exist, to save several forks.
+	     as_shell=$as_dir/$as_base
+	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+		    { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+		   if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+	   done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+	      { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='dash'
+PACKAGE_TARNAME='dash'
+PACKAGE_VERSION='0.5.11.2'
+PACKAGE_STRING='dash 0.5.11.2'
+PACKAGE_BUGREPORT=''
+PACKAGE_URL=''
+
+ac_unique_file="src/main.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIBOBJS
+CC_FOR_BUILD
+EGREP
+GREP
+CPP
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+am__nodep
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+AM_DEFAULT_V
+AM_V
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL
+am__quote'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_silent_rules
+enable_dependency_tracking
+enable_static
+enable_fnmatch
+enable_glob
+enable_test_workaround
+with_libedit
+enable_lineno
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
+		datadir sysconfdir sharedstatedir localstatedir includedir \
+		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+		libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_myself" : 'X\(//\)[^/]' \| \
+	 X"$as_myself" : 'X\(//\)$' \| \
+	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+	pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures dash 0.5.11.2 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/dash]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of dash 0.5.11.2:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-silent-rules   less verbose build output (undo: "make V=1")
+  --disable-silent-rules  verbose build output (undo: "make V=0")
+  --enable-dependency-tracking
+                          do not reject slow dependency extractors
+  --disable-dependency-tracking
+                          speeds up one-time build
+  --enable-static         Build statical linked program
+  --enable-fnmatch        Use fnmatch(3) from libc
+  --enable-glob           Use glob(3) from libc
+  --enable-test-workaround
+                          Guard against faccessat(2) that tells root all files
+                          are executable
+  --disable-lineno        Disable LINENO support
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --without-libedit       Compile without libedit support
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+dash configure 0.5.11.2
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
+# ---------------------------------------------
+# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
+# accordingly.
+ac_fn_c_check_decl ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  as_decl_name=`echo $2|sed 's/ *(.*//'`
+  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
+$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+#ifndef $as_decl_name
+#ifdef __cplusplus
+  (void) $as_decl_use;
+#else
+  (void) $as_decl_name;
+#endif
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_decl
+
+# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+# --------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_c_compute_int ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if test "$cross_compiling" = yes; then
+    # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_lo=0 ac_mid=0
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid; break
+else
+  as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+			if test $ac_lo -le $ac_mid; then
+			  ac_lo= ac_hi=
+			  break
+			fi
+			as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=-1 ac_mid=-1
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_lo=$ac_mid; break
+else
+  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+			if test $ac_mid -le $ac_hi; then
+			  ac_lo= ac_hi=
+			  break
+			fi
+			as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid
+else
+  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+  FILE *f = fopen ("conftest.val", "w");
+  if (! f)
+    return 1;
+  if (($2) < 0)
+    {
+      long int i = longval ();
+      if (i != ($2))
+	return 1;
+      fprintf (f, "%ld", i);
+    }
+  else
+    {
+      unsigned long int i = ulongval ();
+      if (i != ($2))
+	return 1;
+      fprintf (f, "%lu", i);
+    }
+  /* Do not output a trailing newline, as this causes \r\n confusion
+     on some platforms.  */
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+  ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+  fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_compute_int
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by dash $as_me 0.5.11.2, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+	"s/'\''/'\''\\\\'\'''\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=\$$ac_var
+	case $ac_val in
+	*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+	esac
+	$as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	# differences in whitespace do not lead to failure.
+	ac_old_val_w=`echo x $ac_old_val`
+	ac_new_val_w=`echo x $ac_new_val`
+	if test "$ac_old_val_w" != "$ac_new_val_w"; then
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  ac_cache_corrupted=:
+	else
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  eval $ac_var=\$ac_old_val
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+am__api_version='1.16'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+  ./ | .// | /[cC]/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    rm -rf conftest.one conftest.two conftest.dir
+	    echo one > conftest.one
+	    echo two > conftest.two
+	    mkdir conftest.dir
+	    if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+	      test -s conftest.one && test -s conftest.two &&
+	      test -s conftest.dir/conftest.one &&
+	      test -s conftest.dir/conftest.two
+	    then
+	      ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+	      break 3
+	    fi
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+
+  done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[\\\"\#\$\&\'\`$am_lf]*)
+    as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+  *[\\\"\#\$\&\'\`$am_lf\ \	]*)
+    as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   am_has_slept=no
+   for am_try in 1 2; do
+     echo "timestamp, slept: $am_has_slept" > conftest.file
+     set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+     if test "$*" = "X"; then
+	# -L didn't work.
+	set X `ls -t "$srcdir/configure" conftest.file`
+     fi
+     if test "$*" != "X $srcdir/configure conftest.file" \
+	&& test "$*" != "X conftest.file $srcdir/configure"; then
+
+	# If neither matched, then we have a broken ls.  This can happen
+	# if, for instance, CONFIG_SHELL is bash and it inherits a
+	# broken ls alias from the environment.  This has actually
+	# happened.  Such a system could not be considered "sane".
+	as_fn_error $? "ls -t appears to fail.  Make sure there is not a broken
+  alias in your environment" "$LINENO" 5
+     fi
+     if test "$2" = conftest.file || test $am_try -eq 2; then
+       break
+     fi
+     # Just in case.
+     sleep 1
+     am_has_slept=yes
+   done
+   test "$2" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+  ( sleep 1 ) &
+  am_sleep_pid=$!
+fi
+
+rm -f conftest.file
+
+test "$program_prefix" != NONE &&
+  program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+
+if test x"${MISSING+set}" != xset; then
+  MISSING="\${SHELL} '$am_aux_dir/missing'"
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
+else
+  am_missing_run=
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+  if ${ac_cv_path_mkdir+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in mkdir gmkdir; do
+	 for ac_exec_ext in '' $ac_executable_extensions; do
+	   as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
+	   case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+	     'mkdir (GNU coreutils) '* | \
+	     'mkdir (coreutils) '* | \
+	     'mkdir (fileutils) '4.1*)
+	       ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+	       break 3;;
+	   esac
+	 done
+       done
+  done
+IFS=$as_save_IFS
+
+fi
+
+  test -d ./--version && rmdir ./--version
+  if test "${ac_cv_path_mkdir+set}" = set; then
+    MKDIR_P="$ac_cv_path_mkdir -p"
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for MKDIR_P within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    MKDIR_P="$ac_install_sh -d"
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+	@echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+  *@@@%%%=?*=@@@%%%*)
+    eval ac_cv_prog_make_${ac_make}_set=yes;;
+  *)
+    eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+  SET_MAKE=
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+  enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=1;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+    AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  am__isrc=' -I$(srcdir)'
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='dash'
+ VERSION='0.5.11.2'
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# For better backward compatibility.  To be removed once Automake 1.9.x
+# dies out for good.  For more background, see:
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+mkdir_p='$(MKDIR_P)'
+
+# We need awk for the "check" target (and possibly the TAP driver).  The
+# system "awk" is bad on some platforms.
+# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AMTAR='$${TAR-tar}'
+
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar  pax cpio none'
+
+am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
+
+
+
+
+
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes.  So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+  cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present.  This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message.  This
+can help us improve future automake versions.
+
+END
+  if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+    echo 'Configuration will proceed anyway, since you have set the' >&2
+    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+    echo >&2
+  else
+    cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+    as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
+  fi
+fi
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+  enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=0;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+    AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+	then :; else
+	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	fi
+	# We set ac_cv_exeext here because the later test for it is not
+	# safe: cross compilers may not add the suffix if given an `-o'
+	# argument, so we may need to know it at that point already.
+	# Even if this section looks crufty: it has the advantage of
+	# actually working.
+	break;;
+    * )
+	break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+	 CFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+	-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
+$as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
+if ${am_cv_prog_cc_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+  # Make sure it works both with $CC and with simple cc.
+  # Following AC_PROG_CC_C_O, we do the test twice because some
+  # compilers refuse to overwrite an existing .o file with -o,
+  # though they will create one.
+  am_cv_prog_cc_c_o=yes
+  for am_i in 1 2; do
+    if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
+   ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } \
+         && test -f conftest2.$ac_objext; then
+      : OK
+    else
+      am_cv_prog_cc_c_o=no
+      break
+    fi
+  done
+  rm -f core conftest*
+  unset am_i
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
+$as_echo "$am_cv_prog_cc_c_o" >&6; }
+if test "$am_cv_prog_cc_c_o" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
+$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; }
+cat > confinc.mk << 'END'
+am__doit:
+	@echo this is the am__doit target >confinc.out
+.PHONY: am__doit
+END
+am__include="#"
+am__quote=
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+  { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
+   (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }
+  case $?:`cat confinc.out 2>/dev/null` in #(
+  '0:this is the am__doit target') :
+    case $s in #(
+  BSD) :
+    am__include='.include' am__quote='"' ;; #(
+  *) :
+    am__include='include' am__quote='' ;;
+esac ;; #(
+  *) :
+     ;;
+esac
+  if test "$am__include" != "#"; then
+    _am_result="yes ($s style)"
+    break
+  fi
+done
+rm -f confinc.* confmf.*
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
+$as_echo "${_am_result}" >&6; }
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+  enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+  AMDEP_TRUE=
+  AMDEP_FALSE='#'
+else
+  AMDEP_TRUE='#'
+  AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC"   am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CC_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  am__universal=false
+  case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok '-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CC_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+  am__fastdepCC_TRUE=
+  am__fastdepCC_FALSE='#'
+else
+  am__fastdepCC_TRUE='#'
+  am__fastdepCC_FALSE=
+fi
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if ${ac_cv_prog_CPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+  ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default"
+if test "x$ac_cv_header_minix_config_h" = xyes; then :
+  MINIX=yes
+else
+  MINIX=
+fi
+
+
+  if test "$MINIX" = yes; then
+
+$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h
+
+
+$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h
+
+
+$as_echo "#define _MINIX 1" >>confdefs.h
+
+  fi
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5
+$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; }
+if ${ac_cv_safe_to_define___extensions__+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#         define __EXTENSIONS__ 1
+          $ac_includes_default
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_safe_to_define___extensions__=yes
+else
+  ac_cv_safe_to_define___extensions__=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5
+$as_echo "$ac_cv_safe_to_define___extensions__" >&6; }
+  test $ac_cv_safe_to_define___extensions__ = yes &&
+    $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h
+
+  $as_echo "#define _ALL_SOURCE 1" >>confdefs.h
+
+  $as_echo "#define _GNU_SOURCE 1" >>confdefs.h
+
+  $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
+
+  $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for build system compiler" >&5
+$as_echo_n "checking for build system compiler... " >&6; }
+if test "$cross_compiling" = yes; then
+	CC_FOR_BUILD=${CC_FOR_BUILD-cc}
+else
+	CC_FOR_BUILD=${CC}
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${CC_FOR_BUILD}" >&5
+$as_echo "${CC_FOR_BUILD}" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((__alias__()))" >&5
+$as_echo_n "checking for __attribute__((__alias__()))... " >&6; }
+dash_cv_have_attribute_alias=no
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+void t() {}
+                                 void a() __attribute__((__alias__("t")));
+int
+main ()
+{
+a();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  dash_cv_have_attribute_alias=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dash_cv_have_attribute_alias" >&5
+$as_echo "$dash_cv_have_attribute_alias" >&6; }
+if test "x$dash_cv_have_attribute_alias" = xyes; then
+
+$as_echo "#define HAVE_ALIAS_ATTRIBUTE 1" >>confdefs.h
+
+fi
+
+# Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+  enableval=$enable_static;
+fi
+
+if test "$enable_static" = "yes"; then
+	export LDFLAGS="-static -Wl,--fatal-warnings"
+fi
+
+# Check whether --enable-fnmatch was given.
+if test "${enable_fnmatch+set}" = set; then :
+  enableval=$enable_fnmatch;
+fi
+
+# Check whether --enable-glob was given.
+if test "${enable_glob+set}" = set; then :
+  enableval=$enable_glob;
+fi
+
+
+
+for ac_header in alloca.h paths.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ac_fn_c_check_decl "$LINENO" "_PATH_BSHELL" "ac_cv_have_decl__PATH_BSHELL" "
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+"
+if test "x$ac_cv_have_decl__PATH_BSHELL" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define _PATH_BSHELL "/bin/sh"
+_ACEOF
+
+fi
+
+ac_fn_c_check_decl "$LINENO" "_PATH_DEVNULL" "ac_cv_have_decl__PATH_DEVNULL" "
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+"
+if test "x$ac_cv_have_decl__PATH_DEVNULL" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DEVNULL "/dev/null"
+_ACEOF
+
+fi
+
+ac_fn_c_check_decl "$LINENO" "_PATH_TTY" "ac_cv_have_decl__PATH_TTY" "
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+"
+if test "x$ac_cv_have_decl__PATH_TTY" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define _PATH_TTY "/dev/tty"
+_ACEOF
+
+fi
+
+
+ac_fn_c_check_decl "$LINENO" "isblank" "ac_cv_have_decl_isblank" "#include <ctype.h>
+"
+if test "x$ac_cv_have_decl_isblank" = xyes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_ISBLANK $ac_have_decl
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of intmax_t" >&5
+$as_echo_n "checking size of intmax_t... " >&6; }
+if ${ac_cv_sizeof_intmax_t+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (intmax_t))" "ac_cv_sizeof_intmax_t"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_intmax_t" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (intmax_t)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_intmax_t=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_intmax_t" >&5
+$as_echo "$ac_cv_sizeof_intmax_t" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_INTMAX_T $ac_cv_sizeof_intmax_t
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long int" >&5
+$as_echo_n "checking size of long long int... " >&6; }
+if ${ac_cv_sizeof_long_long_int+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long int))" "ac_cv_sizeof_long_long_int"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_long_long_int" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long long int)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_long_long_int=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long_int" >&5
+$as_echo "$ac_cv_sizeof_long_long_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_LONG_LONG_INT $ac_cv_sizeof_long_long_int
+_ACEOF
+
+
+
+if test "x$ac_cv_sizeof_intmax_t" = "x$ac_cv_sizeof_long_long_int"; then
+  intmax_fstr="lld"
+else
+  intmax_fstr="jd"
+fi
+
+ac_fn_c_check_decl "$LINENO" "PRIdMAX" "ac_cv_have_decl_PRIdMAX" "
+#include <inttypes.h>
+
+"
+if test "x$ac_cv_have_decl_PRIdMAX" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define PRIdMAX "$intmax_fstr"
+_ACEOF
+
+fi
+
+
+for ac_func in bsearch faccessat getpwnam getrlimit isalpha killpg \
+	       mempcpy \
+	       sigsetmask stpcpy strchrnul strsignal strtod strtoimax \
+	       strtoumax sysconf
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+# Check whether --enable-test-workaround was given.
+if test "${enable_test_workaround+set}" = set; then :
+  enableval=$enable_test_workaround;
+else
+  enable_test_workaround=auto
+fi
+
+
+if test "enable_test_workaround" = "auto" &&
+   test "$ac_cv_func_faccessat" = yes; then
+	case `uname -s 2>/dev/null` in
+	GNU/kFreeBSD | \
+	FreeBSD)
+		enable_test_workaround=yes
+	esac
+fi
+if test "$enable_test_workaround" = "yes"; then
+
+$as_echo "#define HAVE_TRADITIONAL_FACCESSAT 1" >>confdefs.h
+
+fi
+
+if test "$enable_fnmatch" = yes; then
+	use_fnmatch=
+	for ac_func in fnmatch
+do :
+  ac_fn_c_check_func "$LINENO" "fnmatch" "ac_cv_func_fnmatch"
+if test "x$ac_cv_func_fnmatch" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_FNMATCH 1
+_ACEOF
+ use_fnmatch=yes
+fi
+done
+
+fi
+
+if test "$use_fnmatch" = yes && test "$enable_glob" = yes; then
+	for ac_func in glob
+do :
+  ac_fn_c_check_func "$LINENO" "glob" "ac_cv_func_glob"
+if test "x$ac_cv_func_glob" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GLOB 1
+_ACEOF
+
+fi
+done
+
+fi
+
+ac_fn_c_check_func "$LINENO" "signal" "ac_cv_func_signal"
+if test "x$ac_cv_func_signal" = xyes; then :
+
+fi
+
+if test "$ac_cv_func_signal" != yes; then
+	ac_fn_c_check_func "$LINENO" "bsd_signal" "ac_cv_func_bsd_signal"
+if test "x$ac_cv_func_bsd_signal" = xyes; then :
+
+$as_echo "#define signal bsd_signal" >>confdefs.h
+
+fi
+
+fi
+
+ac_fn_c_check_func "$LINENO" "stat64" "ac_cv_func_stat64"
+if test "x$ac_cv_func_stat64" = xyes; then :
+
+else
+
+
+$as_echo "#define fstat64 fstat" >>confdefs.h
+
+
+$as_echo "#define lstat64 lstat" >>confdefs.h
+
+
+$as_echo "#define stat64 stat" >>confdefs.h
+
+
+fi
+
+
+ac_fn_c_check_func "$LINENO" "open64" "ac_cv_func_open64"
+if test "x$ac_cv_func_open64" = xyes; then :
+
+else
+
+
+$as_echo "#define open64 open" >>confdefs.h
+
+
+$as_echo "#define readdir64 readdir" >>confdefs.h
+
+
+$as_echo "#define dirent64 dirent" >>confdefs.h
+
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stat::st_mtim" >&5
+$as_echo_n "checking for stat::st_mtim... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+int
+main ()
+{
+struct stat foo; return sizeof(foo.st_mtim.tv_sec)
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  have_st_mtim=yes
+else
+  have_st_mtim=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_st_mtim" >&5
+$as_echo "$have_st_mtim" >&6; }
+if test "$have_st_mtim" = "yes"; then
+
+$as_echo "#define HAVE_ST_MTIM 1" >>confdefs.h
+
+fi
+
+
+# Check whether --with-libedit was given.
+if test "${with_libedit+set}" = set; then :
+  withval=$with_libedit;
+fi
+
+use_libedit=
+if test "$with_libedit" != "no"; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for history_init in -ledit" >&5
+$as_echo_n "checking for history_init in -ledit... " >&6; }
+if ${ac_cv_lib_edit_history_init+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ledit  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char history_init ();
+int
+main ()
+{
+return history_init ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_edit_history_init=yes
+else
+  ac_cv_lib_edit_history_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_edit_history_init" >&5
+$as_echo "$ac_cv_lib_edit_history_init" >&6; }
+if test "x$ac_cv_lib_edit_history_init" = xyes; then :
+
+		ac_fn_c_check_header_mongrel "$LINENO" "histedit.h" "ac_cv_header_histedit_h" "$ac_includes_default"
+if test "x$ac_cv_header_histedit_h" = xyes; then :
+  use_libedit="yes"
+else
+  as_fn_error $? "Can't find required header files." "$LINENO" 5
+fi
+
+
+else
+
+		as_fn_error $? "Can't find libedit." "$LINENO" 5
+fi
+
+fi
+if test "$use_libedit" != "yes"; then
+
+$as_echo "#define SMALL 1" >>confdefs.h
+
+else
+	export LIBS="$LIBS -ledit"
+	for ac_func in _el_fn_sh_complete
+do :
+  ac_fn_c_check_func "$LINENO" "_el_fn_sh_complete" "ac_cv_func__el_fn_sh_complete"
+if test "x$ac_cv_func__el_fn_sh_complete" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE__EL_FN_SH_COMPLETE 1
+_ACEOF
+
+fi
+done
+
+fi
+# Check whether --enable-lineno was given.
+if test "${enable_lineno+set}" = set; then :
+  enableval=$enable_lineno;
+fi
+
+if test "$enable_lineno" != "no"; then
+
+$as_echo "#define WITH_LINENO 1" >>confdefs.h
+
+fi
+ac_config_files="$ac_config_files Makefile src/Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+	cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+	  mv -f confcache "$cache_file"$$ &&
+	  mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+	  mv -f confcache "$cache_file" ;;
+	esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5
+$as_echo_n "checking that generated files are newer than configure... " >&6; }
+   if test -n "$am_sleep_pid"; then
+     # Hide warnings about reused PIDs.
+     wait $am_sleep_pid 2>/dev/null
+   fi
+   { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
+$as_echo "done" >&6; }
+ if test -n "$EXEEXT"; then
+  am__EXEEXT_TRUE=
+  am__EXEEXT_FALSE='#'
+else
+  am__EXEEXT_TRUE='#'
+  am__EXEEXT_FALSE=
+fi
+
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+  as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+  as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by dash $as_me 0.5.11.2, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+dash config.status 0.5.11.2
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+    "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = ""
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=[	 ]*/{
+h
+s///
+s/^/:/
+s/[	 ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[	 ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[	 ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[	 ]*#[	 ]*define[	 ][	 ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
+	 # because $ac_f cannot contain `:'.
+	 test -f "$ac_f" ||
+	   case $ac_f in
+	   [\\/$]*) false;;
+	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+	   esac ||
+	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+	  $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	`' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+  ac_MKDIR_P=$MKDIR_P
+  case $MKDIR_P in
+  [\\/$]* | ?:[\\/]* ) ;;
+  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+	|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$_am_arg" : 'X\(//\)[^/]' \| \
+	 X"$_am_arg" : 'X\(//\)$' \| \
+	 X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+  :C)  { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+  esac
+
+
+  case $ac_file$ac_mode in
+    "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+  # Older Autoconf quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  # TODO: see whether this extra hack can be removed once we start
+  # requiring Autoconf 2.70 or later.
+  case $CONFIG_FILES in #(
+  *\'*) :
+    eval set x "$CONFIG_FILES" ;; #(
+  *) :
+    set x $CONFIG_FILES ;; #(
+  *) :
+     ;;
+esac
+  shift
+  # Used to flag and report bootstrapping failures.
+  am_rc=0
+  for am_mf
+  do
+    # Strip MF so we end up with the name of the file.
+    am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile which includes
+    # dependency-tracking related rules and includes.
+    # Grep'ing the whole file directly is not great: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+      || continue
+    am_dirpart=`$as_dirname -- "$am_mf" ||
+$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$am_mf" : 'X\(//\)[^/]' \| \
+	 X"$am_mf" : 'X\(//\)$' \| \
+	 X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$am_mf" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+    am_filepart=`$as_basename -- "$am_mf" ||
+$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$am_mf" : 'X\(//\)$' \| \
+	 X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$am_mf" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+    { echo "$as_me:$LINENO: cd "$am_dirpart" \
+      && sed -e '/# am--include-marker/d' "$am_filepart" \
+        | $MAKE -f - am--depfiles" >&5
+   (cd "$am_dirpart" \
+      && sed -e '/# am--include-marker/d' "$am_filepart" \
+        | $MAKE -f - am--depfiles) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } || am_rc=$?
+  done
+  if test $am_rc -ne 0; then
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "Something went wrong bootstrapping makefile fragments
+    for automatic dependency tracking.  If GNU make was not used, consider
+    re-running the configure script with MAKE=\"gmake\" (or whatever is
+    necessary).  You can also try re-running configure with the
+    '--disable-dependency-tracking' option to at least be able to build
+    the package (albeit without support for automatic dependency tracking).
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+  { am_dirpart=; unset am_dirpart;}
+  { am_filepart=; unset am_filepart;}
+  { am_mf=; unset am_mf;}
+  { am_rc=; unset am_rc;}
+  rm -f conftest-deps.mk
+}
+ ;;
+
+  esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/configure.ac b/bin/dash/configure.ac
index d06b9044..dc96b748 100644
--- a/configure.ac
+++ b/bin/dash/configure.ac
@@ -169,18 +169,21 @@ if test "$have_st_mtim" = "yes"; then
 		[Define if your `struct stat' has `st_mtim'])
 fi
 
-AC_ARG_WITH(libedit, AS_HELP_STRING(--with-libedit, [Compile with libedit support]))
+AC_ARG_WITH(libedit,
+	AS_HELP_STRING(--without-libedit, [Compile without libedit support]))
 use_libedit=
-if test "$with_libedit" = "yes"; then
+if test "$with_libedit" != "no"; then
 	AC_CHECK_LIB(edit, history_init, [
 		AC_CHECK_HEADER([histedit.h], [use_libedit="yes"],
 				AC_MSG_ERROR(
-					[Can't find required header files.]))])
+					[Can't find required header files.]))], [
+		AC_MSG_ERROR([Can't find libedit.])])
 fi
 if test "$use_libedit" != "yes"; then
 	AC_DEFINE([SMALL], 1, [Define if you build with -DSMALL])
 else
 	export LIBS="$LIBS -ledit"
+	AC_CHECK_FUNCS(_el_fn_sh_complete)
 fi
 AC_ARG_ENABLE(lineno, AS_HELP_STRING(--disable-lineno, \
 				     [Disable LINENO support]))
diff --git a/bin/dash/depcomp b/bin/dash/depcomp
new file mode 100755
index 00000000..6b391623
--- /dev/null
+++ b/bin/dash/depcomp
@@ -0,0 +1,791 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, 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 General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+  '')
+    echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+    exit 1;
+    ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+  depmode     Dependency tracking mode.
+  source      Source file read by 'PROGRAMS ARGS'.
+  object      Object file output by 'PROGRAMS ARGS'.
+  DEPDIR      directory where to store dependencies.
+  depfile     Dependency file to output.
+  tmpdepfile  Temporary file to use when outputting dependencies.
+  libtool     Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "depcomp $scriptversion"
+    exit $?
+    ;;
+esac
+
+# Get the directory component of the given path, and save it in the
+# global variables '$dir'.  Note that this directory component will
+# be either empty or ending with a '/' character.  This is deliberate.
+set_dir_from ()
+{
+  case $1 in
+    */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
+      *) dir=;;
+  esac
+}
+
+# Get the suffix-stripped basename of the given path, and save it the
+# global variable '$base'.
+set_base_from ()
+{
+  base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
+}
+
+# If no dependency file was actually created by the compiler invocation,
+# we still have to create a dummy depfile, to avoid errors with the
+# Makefile "include basename.Plo" scheme.
+make_dummy_depfile ()
+{
+  echo "#dummy" > "$depfile"
+}
+
+# Factor out some common post-processing of the generated depfile.
+# Requires the auxiliary global variable '$tmpdepfile' to be set.
+aix_post_process_depfile ()
+{
+  # If the compiler actually managed to produce a dependency file,
+  # post-process it.
+  if test -f "$tmpdepfile"; then
+    # Each line is of the form 'foo.o: dependency.h'.
+    # Do two passes, one to just change these to
+    #   $object: dependency.h
+    # and one to simply output
+    #   dependency.h:
+    # which is needed to avoid the deleted-header problem.
+    { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
+      sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
+    } > "$depfile"
+    rm -f "$tmpdepfile"
+  else
+    make_dummy_depfile
+  fi
+}
+
+# A tabulation character.
+tab='	'
+# A newline character.
+nl='
+'
+# Character ranges might be problematic outside the C locale.
+# These definitions help.
+upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
+lower=abcdefghijklmnopqrstuvwxyz
+digits=0123456789
+alpha=${upper}${lower}
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+  echo "depcomp: Variables source, object and depmode must be set" 1>&2
+  exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+  sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Avoid interferences from the environment.
+gccflag= dashmflag=
+
+# Some modes work just like other modes, but use different flags.  We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write.  Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+  # HP compiler uses -M and no extra arg.
+  gccflag=-M
+  depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+  # This is just like dashmstdout with a different argument.
+  dashmflag=-xM
+  depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+  # This is just like msvisualcpp but w/o cygpath translation.
+  # Just convert the backslash-escaped backslashes to single forward
+  # slashes to satisfy depend.m4
+  cygpath_u='sed s,\\\\,/,g'
+  depmode=msvisualcpp
+fi
+
+if test "$depmode" = msvc7msys; then
+  # This is just like msvc7 but w/o cygpath translation.
+  # Just convert the backslash-escaped backslashes to single forward
+  # slashes to satisfy depend.m4
+  cygpath_u='sed s,\\\\,/,g'
+  depmode=msvc7
+fi
+
+if test "$depmode" = xlc; then
+  # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
+  gccflag=-qmakedep=gcc,-MF
+  depmode=gcc
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff.  Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am.  Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+  for arg
+  do
+    case $arg in
+    -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+    *)  set fnord "$@" "$arg" ;;
+    esac
+    shift # fnord
+    shift # $arg
+  done
+  "$@"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  mv "$tmpdepfile" "$depfile"
+  ;;
+
+gcc)
+## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
+## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
+## (see the conditional assignment to $gccflag above).
+## There are various ways to get dependency output from gcc.  Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+##   up in a subdir.  Having to rename by hand is ugly.
+##   (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+##   -MM, not -M (despite what the docs say).  Also, it might not be
+##   supported by the other compilers which use the 'gcc' depmode.
+## - Using -M directly means running the compiler twice (even worse
+##   than renaming).
+  if test -z "$gccflag"; then
+    gccflag=-MD,
+  fi
+  "$@" -Wp,"$gccflag$tmpdepfile"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  # The second -e expression handles DOS-style file names with drive
+  # letters.
+  sed -e 's/^[^:]*: / /' \
+      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the "deleted header file" problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header).  We avoid this by adding
+## dummy dependencies for each header file.  Too bad gcc doesn't do
+## this for us directly.
+## Some versions of gcc put a space before the ':'.  On the theory
+## that the space means something, we add a space to the output as
+## well.  hp depmode also adds that space, but also prefixes the VPATH
+## to the object.  Take care to not repeat it in the output.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+  tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+sgi)
+  if test "$libtool" = yes; then
+    "$@" "-Wp,-MDupdate,$tmpdepfile"
+  else
+    "$@" -MDupdate "$tmpdepfile"
+  fi
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+
+  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
+    echo "$object : \\" > "$depfile"
+    # Clip off the initial element (the dependent).  Don't try to be
+    # clever and replace this with sed code, as IRIX sed won't handle
+    # lines with more than a fixed number of characters (4096 in
+    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
+    # the IRIX cc adds comments like '#:fec' to the end of the
+    # dependency line.
+    tr ' ' "$nl" < "$tmpdepfile" \
+      | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
+      | tr "$nl" ' ' >> "$depfile"
+    echo >> "$depfile"
+    # The second pass generates a dummy entry for each header file.
+    tr ' ' "$nl" < "$tmpdepfile" \
+      | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+      >> "$depfile"
+  else
+    make_dummy_depfile
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+xlc)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+aix)
+  # The C for AIX Compiler uses -M and outputs the dependencies
+  # in a .u file.  In older versions, this file always lives in the
+  # current directory.  Also, the AIX compiler puts '$object:' at the
+  # start of each line; $object doesn't have directory information.
+  # Version 6 uses the directory in both cases.
+  set_dir_from "$object"
+  set_base_from "$object"
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$base.u
+    tmpdepfile3=$dir.libs/$base.u
+    "$@" -Wc,-M
+  else
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$dir$base.u
+    tmpdepfile3=$dir$base.u
+    "$@" -M
+  fi
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  aix_post_process_depfile
+  ;;
+
+tcc)
+  # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
+  # FIXME: That version still under development at the moment of writing.
+  #        Make that this statement remains true also for stable, released
+  #        versions.
+  # It will wrap lines (doesn't matter whether long or short) with a
+  # trailing '\', as in:
+  #
+  #   foo.o : \
+  #    foo.c \
+  #    foo.h \
+  #
+  # It will put a trailing '\' even on the last line, and will use leading
+  # spaces rather than leading tabs (at least since its commit 0394caf7
+  # "Emit spaces for -MD").
+  "$@" -MD -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
+  # We have to change lines of the first kind to '$object: \'.
+  sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
+  # And for each line of the second kind, we have to emit a 'dep.h:'
+  # dummy dependency, to avoid the deleted-header problem.
+  sed -n -e 's|^  *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+## The order of this option in the case statement is important, since the
+## shell code in configure will try each of these formats in the order
+## listed in this file.  A plain '-MD' option would be understood by many
+## compilers, so we must ensure this comes after the gcc and icc options.
+pgcc)
+  # Portland's C compiler understands '-MD'.
+  # Will always output deps to 'file.d' where file is the root name of the
+  # source file under compilation, even if file resides in a subdirectory.
+  # The object file name does not affect the name of the '.d' file.
+  # pgcc 10.2 will output
+  #    foo.o: sub/foo.c sub/foo.h
+  # and will wrap long lines using '\' :
+  #    foo.o: sub/foo.c ... \
+  #     sub/foo.h ... \
+  #     ...
+  set_dir_from "$object"
+  # Use the source, not the object, to determine the base name, since
+  # that's sadly what pgcc will do too.
+  set_base_from "$source"
+  tmpdepfile=$base.d
+
+  # For projects that build the same source file twice into different object
+  # files, the pgcc approach of using the *source* file root name can cause
+  # problems in parallel builds.  Use a locking strategy to avoid stomping on
+  # the same $tmpdepfile.
+  lockdir=$base.d-lock
+  trap "
+    echo '$0: caught signal, cleaning up...' >&2
+    rmdir '$lockdir'
+    exit 1
+  " 1 2 13 15
+  numtries=100
+  i=$numtries
+  while test $i -gt 0; do
+    # mkdir is a portable test-and-set.
+    if mkdir "$lockdir" 2>/dev/null; then
+      # This process acquired the lock.
+      "$@" -MD
+      stat=$?
+      # Release the lock.
+      rmdir "$lockdir"
+      break
+    else
+      # If the lock is being held by a different process, wait
+      # until the winning process is done or we timeout.
+      while test -d "$lockdir" && test $i -gt 0; do
+        sleep 1
+        i=`expr $i - 1`
+      done
+    fi
+    i=`expr $i - 1`
+  done
+  trap - 1 2 13 15
+  if test $i -le 0; then
+    echo "$0: failed to acquire lock after $numtries attempts" >&2
+    echo "$0: check lockdir '$lockdir'" >&2
+    exit 1
+  fi
+
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each line is of the form `foo.o: dependent.h',
+  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+  # Do two passes, one to just change these to
+  # `$object: dependent.h' and one to simply `dependent.h:'.
+  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp2)
+  # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+  # compilers, which have integrated preprocessors.  The correct option
+  # to use with these is +Maked; it writes dependencies to a file named
+  # 'foo.d', which lands next to the object file, wherever that
+  # happens to be.
+  # Much of this is similar to the tru64 case; see comments there.
+  set_dir_from  "$object"
+  set_base_from "$object"
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir.libs/$base.d
+    "$@" -Wc,+Maked
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    "$@" +Maked
+  fi
+  stat=$?
+  if test $stat -ne 0; then
+     rm -f "$tmpdepfile1" "$tmpdepfile2"
+     exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  if test -f "$tmpdepfile"; then
+    sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
+    # Add 'dependent.h:' lines.
+    sed -ne '2,${
+               s/^ *//
+               s/ \\*$//
+               s/$/:/
+               p
+             }' "$tmpdepfile" >> "$depfile"
+  else
+    make_dummy_depfile
+  fi
+  rm -f "$tmpdepfile" "$tmpdepfile2"
+  ;;
+
+tru64)
+  # The Tru64 compiler uses -MD to generate dependencies as a side
+  # effect.  'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
+  # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+  # dependencies in 'foo.d' instead, so we check for that too.
+  # Subdirectories are respected.
+  set_dir_from  "$object"
+  set_base_from "$object"
+
+  if test "$libtool" = yes; then
+    # Libtool generates 2 separate objects for the 2 libraries.  These
+    # two compilations output dependencies in $dir.libs/$base.o.d and
+    # in $dir$base.o.d.  We have to check for both files, because
+    # one of the two compilations can be disabled.  We should prefer
+    # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+    # automatically cleaned when .libs/ is deleted, while ignoring
+    # the former would cause a distcleancheck panic.
+    tmpdepfile1=$dir$base.o.d          # libtool 1.5
+    tmpdepfile2=$dir.libs/$base.o.d    # Likewise.
+    tmpdepfile3=$dir.libs/$base.d      # Compaq CCC V6.2-504
+    "$@" -Wc,-MD
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    tmpdepfile3=$dir$base.d
+    "$@" -MD
+  fi
+
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  # Same post-processing that is required for AIX mode.
+  aix_post_process_depfile
+  ;;
+
+msvc7)
+  if test "$libtool" = yes; then
+    showIncludes=-Wc,-showIncludes
+  else
+    showIncludes=-showIncludes
+  fi
+  "$@" $showIncludes > "$tmpdepfile"
+  stat=$?
+  grep -v '^Note: including file: ' "$tmpdepfile"
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  # The first sed program below extracts the file names and escapes
+  # backslashes for cygpath.  The second sed program outputs the file
+  # name when reading, but also accumulates all include files in the
+  # hold buffer in order to output them again at the end.  This only
+  # works with sed implementations that can handle large buffers.
+  sed < "$tmpdepfile" -n '
+/^Note: including file:  *\(.*\)/ {
+  s//\1/
+  s/\\/\\\\/g
+  p
+}' | $cygpath_u | sort -u | sed -n '
+s/ /\\ /g
+s/\(.*\)/'"$tab"'\1 \\/p
+s/.\(.*\) \\/\1:/
+H
+$ {
+  s/.*/'"$tab"'/
+  G
+  p
+}' >> "$depfile"
+  echo >> "$depfile" # make sure the fragment doesn't end with a backslash
+  rm -f "$tmpdepfile"
+  ;;
+
+msvc7msys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+#nosideeffect)
+  # This comment above is used by automake to tell side-effect
+  # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout, regardless of -o.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove '-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  test -z "$dashmflag" && dashmflag=-M
+  # Require at least two characters before searching for ':'
+  # in the target name.  This is to cope with DOS-style filenames:
+  # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
+  "$@" $dashmflag |
+    sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this sed invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+dashXmstdout)
+  # This case only exists to satisfy depend.m4.  It is never actually
+  # run, as this mode is specially recognized in the preamble.
+  exit 1
+  ;;
+
+makedepend)
+  "$@" || exit $?
+  # Remove any Libtool call
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+  # X makedepend
+  shift
+  cleared=no eat=no
+  for arg
+  do
+    case $cleared in
+    no)
+      set ""; shift
+      cleared=yes ;;
+    esac
+    if test $eat = yes; then
+      eat=no
+      continue
+    fi
+    case "$arg" in
+    -D*|-I*)
+      set fnord "$@" "$arg"; shift ;;
+    # Strip any option that makedepend may not understand.  Remove
+    # the object too, otherwise makedepend will parse it as a source file.
+    -arch)
+      eat=yes ;;
+    -*|$object)
+      ;;
+    *)
+      set fnord "$@" "$arg"; shift ;;
+    esac
+  done
+  obj_suffix=`echo "$object" | sed 's/^.*\././'`
+  touch "$tmpdepfile"
+  ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+  rm -f "$depfile"
+  # makedepend may prepend the VPATH from the source file name to the object.
+  # No need to regex-escape $object, excess matching of '.' is harmless.
+  sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process the last invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed '1,2d' "$tmpdepfile" \
+    | tr ' ' "$nl" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile" "$tmpdepfile".bak
+  ;;
+
+cpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove '-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  "$@" -E \
+    | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+             -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+    | sed '$ s: \\$::' > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  cat < "$tmpdepfile" >> "$depfile"
+  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvisualcpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  IFS=" "
+  for arg
+  do
+    case "$arg" in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+        set fnord "$@"
+        shift
+        shift
+        ;;
+    *)
+        set fnord "$@" "$arg"
+        shift
+        shift
+        ;;
+    esac
+  done
+  "$@" -E 2>/dev/null |
+  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
+  echo "$tab" >> "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvcmsys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+none)
+  exec "$@"
+  ;;
+
+*)
+  echo "Unknown depmode $depmode" 1>&2
+  exit 1
+  ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/bin/dash/install-sh b/bin/dash/install-sh
new file mode 100755
index 00000000..ec298b53
--- /dev/null
+++ b/bin/dash/install-sh
@@ -0,0 +1,541 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2020-11-14.01; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# 'make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+tab='	'
+nl='
+'
+IFS=" $tab$nl"
+
+# Set DOITPROG to "echo" to test this script.
+
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+# Create dirs (including intermediate dirs) using mode 755.
+# This is like GNU 'install' as of coreutils 8.32 (2020).
+mkdir_umask=22
+
+backupsuffix=
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+is_target_a_directory=possibly
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -p            pass -p to $cpprog.
+  -s            $stripprog installed files.
+  -S SUFFIX     attempt to back up existing files, with suffix SUFFIX.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+
+By default, rm is invoked with -f; when overridden with RMPROG,
+it's up to you to specify -f if you want it.
+
+If -S is not specified, no backups are attempted.
+
+Email bug reports to bug-automake@gnu.org.
+Automake home page: https://www.gnu.org/software/automake/
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
+
+    -o) chowncmd="$chownprog $2"
+        shift;;
+
+    -p) cpprog="$cpprog -p";;
+
+    -s) stripcmd=$stripprog;;
+
+    -S) backupsuffix="$2"
+        shift;;
+
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
+
+    -T) is_target_a_directory=never;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --) shift
+        break;;
+
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call 'install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names problematic for 'test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+    # Don't chown directories that already exist.
+    if test $dstdir_status = 0; then
+      chowncmd=""
+    fi
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename.
+    if test -d "$dst"; then
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
+      fi
+      dstdir=$dst
+      dstbase=`basename "$src"`
+      case $dst in
+	*/) dst=$dst$dstbase;;
+	*)  dst=$dst/$dstbase;;
+      esac
+      dstdir_status=0
+    else
+      dstdir=`dirname "$dst"`
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  case $dstdir in
+    */) dstdirslash=$dstdir;;
+    *)  dstdirslash=$dstdir/;;
+  esac
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+	# The $RANDOM variable is not portable (e.g., dash).  Use it
+	# here however when possible just to lower collision chance.
+	tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
+	trap '
+	  ret=$?
+	  rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
+	  exit $ret
+	' 0
+
+	# Because "mkdir -p" follows existing symlinks and we likely work
+	# directly in world-writeable /tmp, make sure that the '$tmpdir'
+	# directory is successfully created first before we actually test
+	# 'mkdir -p'.
+	if (umask $mkdir_umask &&
+	    $mkdirprog $mkdir_mode "$tmpdir" &&
+	    exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+	then
+	  if test -z "$dir_arg" || {
+	       # Check for POSIX incompatibilities with -m.
+	       # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+	       # other-writable bit of parent directory when it shouldn't.
+	       # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+	       test_tmpdir="$tmpdir/a"
+	       ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+	       case $ls_ld_tmpdir in
+		 d????-?r-*) different_mode=700;;
+		 d????-?--*) different_mode=755;;
+		 *) false;;
+	       esac &&
+	       $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+		 ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+		 test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+	       }
+	     }
+	  then posix_mkdir=:
+	  fi
+	  rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+	else
+	  # Remove any dirs left behind by ancient mkdir implementations.
+	  rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+	fi
+	trap '' 0;;
+    esac
+
+    if
+      $posix_mkdir && (
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
+      esac
+
+      oIFS=$IFS
+      IFS=/
+      set -f
+      set fnord $dstdir
+      shift
+      set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask $mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=${dstdirslash}_inst.$$_
+    rmtmp=${dstdirslash}_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask &&
+     { test -z "$stripcmd" || {
+	 # Create $dsttmp read-write so that cp doesn't create it read-only,
+	 # which would cause strip to fail.
+	 if test -z "$doit"; then
+	   : >"$dsttmp" # No need to fork-exec 'touch'.
+	 else
+	   $doit touch "$dsttmp"
+	 fi
+       }
+     } &&
+     $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       set +f &&
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # If $backupsuffix is set, and the file being installed
+      # already exists, attempt a backup.  Don't worry if it fails,
+      # e.g., if mv doesn't support -f.
+      if test -n "$backupsuffix" && test -f "$dst"; then
+        $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
+      fi
+
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/bin/dash/missing b/bin/dash/missing
new file mode 100755
index 00000000..8d0eaad2
--- /dev/null
+++ b/bin/dash/missing
@@ -0,0 +1,215 @@
+#! /bin/sh
+# Common wrapper for a few potentially missing GNU programs.
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, 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 General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+  echo 1>&2 "Try '$0 --help' for more information"
+  exit 1
+fi
+
+case $1 in
+
+  --is-lightweight)
+    # Used by our autoconf macros to check whether the available missing
+    # script is modern enough.
+    exit 0
+    ;;
+
+  --run)
+    # Back-compat with the calling convention used by older automake.
+    shift
+    ;;
+
+  -h|--h|--he|--hel|--help)
+    echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
+to PROGRAM being missing or too old.
+
+Options:
+  -h, --help      display this help and exit
+  -v, --version   output version information and exit
+
+Supported PROGRAM values:
+  aclocal   autoconf  autoheader   autom4te  automake  makeinfo
+  bison     yacc      flex         lex       help2man
+
+Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
+'g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+    exit $?
+    ;;
+
+  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+    echo "missing $scriptversion (GNU Automake)"
+    exit $?
+    ;;
+
+  -*)
+    echo 1>&2 "$0: unknown '$1' option"
+    echo 1>&2 "Try '$0 --help' for more information"
+    exit 1
+    ;;
+
+esac
+
+# Run the given program, remember its exit status.
+"$@"; st=$?
+
+# If it succeeded, we are done.
+test $st -eq 0 && exit 0
+
+# Also exit now if we it failed (or wasn't found), and '--version' was
+# passed; such an option is passed most likely to detect whether the
+# program is present and works.
+case $2 in --version|--help) exit $st;; esac
+
+# Exit code 63 means version mismatch.  This often happens when the user
+# tries to use an ancient version of a tool on a file that requires a
+# minimum version.
+if test $st -eq 63; then
+  msg="probably too old"
+elif test $st -eq 127; then
+  # Program was missing.
+  msg="missing on your system"
+else
+  # Program was found and executed, but failed.  Give up.
+  exit $st
+fi
+
+perl_URL=https://www.perl.org/
+flex_URL=https://github.com/westes/flex
+gnu_software_URL=https://www.gnu.org/software
+
+program_details ()
+{
+  case $1 in
+    aclocal|automake)
+      echo "The '$1' program is part of the GNU Automake package:"
+      echo "<$gnu_software_URL/automake>"
+      echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
+      echo "<$gnu_software_URL/autoconf>"
+      echo "<$gnu_software_URL/m4/>"
+      echo "<$perl_URL>"
+      ;;
+    autoconf|autom4te|autoheader)
+      echo "The '$1' program is part of the GNU Autoconf package:"
+      echo "<$gnu_software_URL/autoconf/>"
+      echo "It also requires GNU m4 and Perl in order to run:"
+      echo "<$gnu_software_URL/m4/>"
+      echo "<$perl_URL>"
+      ;;
+  esac
+}
+
+give_advice ()
+{
+  # Normalize program name to check for.
+  normalized_program=`echo "$1" | sed '
+    s/^gnu-//; t
+    s/^gnu//; t
+    s/^g//; t'`
+
+  printf '%s\n' "'$1' is $msg."
+
+  configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
+  case $normalized_program in
+    autoconf*)
+      echo "You should only need it if you modified 'configure.ac',"
+      echo "or m4 files included by it."
+      program_details 'autoconf'
+      ;;
+    autoheader*)
+      echo "You should only need it if you modified 'acconfig.h' or"
+      echo "$configure_deps."
+      program_details 'autoheader'
+      ;;
+    automake*)
+      echo "You should only need it if you modified 'Makefile.am' or"
+      echo "$configure_deps."
+      program_details 'automake'
+      ;;
+    aclocal*)
+      echo "You should only need it if you modified 'acinclude.m4' or"
+      echo "$configure_deps."
+      program_details 'aclocal'
+      ;;
+   autom4te*)
+      echo "You might have modified some maintainer files that require"
+      echo "the 'autom4te' program to be rebuilt."
+      program_details 'autom4te'
+      ;;
+    bison*|yacc*)
+      echo "You should only need it if you modified a '.y' file."
+      echo "You may want to install the GNU Bison package:"
+      echo "<$gnu_software_URL/bison/>"
+      ;;
+    lex*|flex*)
+      echo "You should only need it if you modified a '.l' file."
+      echo "You may want to install the Fast Lexical Analyzer package:"
+      echo "<$flex_URL>"
+      ;;
+    help2man*)
+      echo "You should only need it if you modified a dependency" \
+           "of a man page."
+      echo "You may want to install the GNU Help2man package:"
+      echo "<$gnu_software_URL/help2man/>"
+    ;;
+    makeinfo*)
+      echo "You should only need it if you modified a '.texi' file, or"
+      echo "any other file indirectly affecting the aspect of the manual."
+      echo "You might want to install the Texinfo package:"
+      echo "<$gnu_software_URL/texinfo/>"
+      echo "The spurious makeinfo call might also be the consequence of"
+      echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
+      echo "want to install GNU make:"
+      echo "<$gnu_software_URL/make/>"
+      ;;
+    *)
+      echo "You might have modified some files without having the proper"
+      echo "tools for further handling them.  Check the 'README' file, it"
+      echo "often tells you about the needed prerequisites for installing"
+      echo "this package.  You may also peek at any GNU archive site, in"
+      echo "case some other package contains this missing '$1' program."
+      ;;
+  esac
+}
+
+give_advice "$1" | sed -e '1s/^/WARNING: /' \
+                       -e '2,$s/^/         /' >&2
+
+# Propagate the correct exit status (expected to be 127 for a program
+# not found, 63 for a program that failed due to version mismatch).
+exit $st
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/src/.gitignore b/bin/dash/src/.gitignore
index 644eccb8..644eccb8 100644
--- a/src/.gitignore
+++ b/bin/dash/src/.gitignore
diff --git a/src/Makefile.am b/bin/dash/src/Makefile.am
index 17324653..fb8229f1 100644
--- a/src/Makefile.am
+++ b/bin/dash/src/Makefile.am
@@ -34,7 +34,7 @@ HELPERS = mkinit mksyntax mknodes mksignames
 
 BUILT_SOURCES = builtins.h nodes.h syntax.h token.h token_vars.h
 CLEANFILES = \
-	$(BUILT_SOURCES) $(patsubst %.o,%.c,$(dash_LDADD)) \
+	$(BUILT_SOURCES) $(dash_LDADD:%.o=%.c) \
 	$(HELPERS) builtins.def
 
 man_MANS = dash.1
@@ -44,27 +44,27 @@ EXTRA_DIST = \
 	mknodes.c nodetypes nodes.c.pat mksyntax.c mksignames.c
 
 token.h token_vars.h: mktokens
-	$(AM_V_GEN)$(SHELL) $^
+	$(AM_V_GEN)$(SHELL) mktokens
 
 builtins.def: builtins.def.in $(top_builddir)/config.h
-	$(AM_V_CC)$(COMPILE) -E -x c -o $@ $<
+	$(AM_V_CC)$(COMPILE) -E -x c -o $@ builtins.def.in
 
 builtins.c builtins.h: mkbuiltins builtins.def
-	$(AM_V_GEN)$(SHELL) $^
+	$(AM_V_GEN)$(SHELL) mkbuiltins builtins.def
 
 init.c: mkinit $(dash_CFILES)
-	$(AM_V_GEN)./$^
+	$(AM_V_GEN)./mkinit $(dash_CFILES)
 
 nodes.c nodes.h: mknodes nodetypes nodes.c.pat
-	$(AM_V_GEN)./$^
+	$(AM_V_GEN)./mknodes nodetypes nodes.c.pat
 
 syntax.c syntax.h: mksyntax
-	$(AM_V_GEN)./$^
+	$(AM_V_GEN)./mksyntax
 
 signames.c: mksignames
-	$(AM_V_GEN)./$^
+	$(AM_V_GEN)./mksignames
 
 mksyntax: token.h
 
-$(HELPERS): %: %.c
-	$(AM_V_CC)$(COMPILE_FOR_BUILD) -o $@ $<
+$(HELPERS):
+	$(AM_V_CC)$(COMPILE_FOR_BUILD) -o $@ $@.c
diff --git a/bin/dash/src/Makefile.in b/bin/dash/src/Makefile.in
new file mode 100644
index 00000000..ae25f565
--- /dev/null
+++ b/bin/dash/src/Makefile.in
@@ -0,0 +1,834 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+bin_PROGRAMS = dash$(EXEEXT)
+subdir = src
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"
+PROGRAMS = $(bin_PROGRAMS)
+am__dirstamp = $(am__leading_dot)dirstamp
+am__objects_1 = alias.$(OBJEXT) arith_yacc.$(OBJEXT) \
+	arith_yylex.$(OBJEXT) cd.$(OBJEXT) error.$(OBJEXT) \
+	eval.$(OBJEXT) exec.$(OBJEXT) expand.$(OBJEXT) \
+	histedit.$(OBJEXT) input.$(OBJEXT) jobs.$(OBJEXT) \
+	mail.$(OBJEXT) main.$(OBJEXT) memalloc.$(OBJEXT) \
+	miscbltin.$(OBJEXT) mystring.$(OBJEXT) options.$(OBJEXT) \
+	parser.$(OBJEXT) redir.$(OBJEXT) show.$(OBJEXT) trap.$(OBJEXT) \
+	output.$(OBJEXT) bltin/printf.$(OBJEXT) system.$(OBJEXT) \
+	bltin/test.$(OBJEXT) bltin/times.$(OBJEXT) var.$(OBJEXT)
+am_dash_OBJECTS = $(am__objects_1)
+dash_OBJECTS = $(am_dash_OBJECTS)
+dash_DEPENDENCIES = builtins.o init.o nodes.o signames.o syntax.o
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/alias.Po ./$(DEPDIR)/arith_yacc.Po \
+	./$(DEPDIR)/arith_yylex.Po ./$(DEPDIR)/cd.Po \
+	./$(DEPDIR)/error.Po ./$(DEPDIR)/eval.Po ./$(DEPDIR)/exec.Po \
+	./$(DEPDIR)/expand.Po ./$(DEPDIR)/histedit.Po \
+	./$(DEPDIR)/input.Po ./$(DEPDIR)/jobs.Po ./$(DEPDIR)/mail.Po \
+	./$(DEPDIR)/main.Po ./$(DEPDIR)/memalloc.Po \
+	./$(DEPDIR)/miscbltin.Po ./$(DEPDIR)/mystring.Po \
+	./$(DEPDIR)/options.Po ./$(DEPDIR)/output.Po \
+	./$(DEPDIR)/parser.Po ./$(DEPDIR)/redir.Po ./$(DEPDIR)/show.Po \
+	./$(DEPDIR)/system.Po ./$(DEPDIR)/trap.Po ./$(DEPDIR)/var.Po \
+	bltin/$(DEPDIR)/printf.Po bltin/$(DEPDIR)/test.Po \
+	bltin/$(DEPDIR)/times.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(dash_SOURCES)
+DIST_SOURCES = $(dash_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+man1dir = $(mandir)/man1
+NROFF = nroff
+MANS = $(man_MANS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_YFLAGS = -d
+COMMON_CFLAGS = -Wall
+COMMON_CPPFLAGS = \
+	-DBSD=1 -DSHELL
+
+AM_CFLAGS = $(COMMON_CFLAGS)
+AM_CPPFLAGS = -include $(top_builddir)/config.h $(COMMON_CPPFLAGS)
+AM_CFLAGS_FOR_BUILD = -g -O2 $(COMMON_CFLAGS) 
+AM_CPPFLAGS_FOR_BUILD = $(COMMON_CPPFLAGS)
+COMPILE_FOR_BUILD = \
+	$(CC_FOR_BUILD) $(DEFAULT_INCLUDES) $(AM_CPPFLAGS_FOR_BUILD) \
+	$(CPPFLAGS_FOR_BUILD) \
+	$(AM_CFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) 
+
+dash_CFILES = \
+	alias.c arith_yacc.c arith_yylex.c cd.c error.c eval.c exec.c expand.c \
+	histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \
+	mystring.c options.c parser.c redir.c show.c trap.c output.c \
+	bltin/printf.c system.c bltin/test.c bltin/times.c var.c
+
+dash_SOURCES = \
+	$(dash_CFILES) \
+	alias.h arith_yacc.h bltin/bltin.h cd.h error.h eval.h exec.h \
+	expand.h \
+	init.h input.h jobs.h machdep.h mail.h main.h memalloc.h miscbltin.h \
+	myhistedit.h mystring.h options.h output.h parser.h redir.h shell.h \
+	show.h system.h trap.h var.h
+
+dash_LDADD = builtins.o init.o nodes.o signames.o syntax.o
+HELPERS = mkinit mksyntax mknodes mksignames
+BUILT_SOURCES = builtins.h nodes.h syntax.h token.h token_vars.h
+CLEANFILES = \
+	$(BUILT_SOURCES) $(dash_LDADD:%.o=%.c) \
+	$(HELPERS) builtins.def
+
+man_MANS = dash.1
+EXTRA_DIST = \
+	$(man_MANS) \
+	mktokens mkbuiltins builtins.def.in mkinit.c \
+	mknodes.c nodetypes nodes.c.pat mksyntax.c mksignames.c
+
+all: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+	fi; \
+	for p in $$list; do echo "$$p $$p"; done | \
+	sed 's/$(EXEEXT)$$//' | \
+	while read p p1; do if test -f $$p \
+	  ; then echo "$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n;h' \
+	    -e 's|.*|.|' \
+	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+	sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
+	    else { print "f", $$3 "/" $$4, $$1; } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	    test -z "$$files" || { \
+	      echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+	      $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+	    } \
+	; done
+
+uninstall-binPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+	      -e 's/$$/$(EXEEXT)/' \
+	`; \
+	test -n "$$list" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+	-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+bltin/$(am__dirstamp):
+	@$(MKDIR_P) bltin
+	@: > bltin/$(am__dirstamp)
+bltin/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) bltin/$(DEPDIR)
+	@: > bltin/$(DEPDIR)/$(am__dirstamp)
+bltin/printf.$(OBJEXT): bltin/$(am__dirstamp) \
+	bltin/$(DEPDIR)/$(am__dirstamp)
+bltin/test.$(OBJEXT): bltin/$(am__dirstamp) \
+	bltin/$(DEPDIR)/$(am__dirstamp)
+bltin/times.$(OBJEXT): bltin/$(am__dirstamp) \
+	bltin/$(DEPDIR)/$(am__dirstamp)
+
+dash$(EXEEXT): $(dash_OBJECTS) $(dash_DEPENDENCIES) $(EXTRA_dash_DEPENDENCIES) 
+	@rm -f dash$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(dash_OBJECTS) $(dash_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+	-rm -f bltin/*.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alias.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arith_yacc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arith_yylex.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eval.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exec.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expand.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/histedit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jobs.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memalloc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/miscbltin.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mystring.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/redir.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/show.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/system.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/var.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@bltin/$(DEPDIR)/printf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@bltin/$(DEPDIR)/test.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@bltin/$(DEPDIR)/times.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+	@$(MKDIR_P) $(@D)
+	@echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+install-man1: $(man_MANS)
+	@$(NORMAL_INSTALL)
+	@list1=''; \
+	list2='$(man_MANS)'; \
+	test -n "$(man1dir)" \
+	  && test -n "`echo $$list1$$list2`" \
+	  || exit 0; \
+	echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \
+	$(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \
+	{ for i in $$list1; do echo "$$i"; done;  \
+	if test -n "$$list2"; then \
+	  for i in $$list2; do echo "$$i"; done \
+	    | sed -n '/\.1[a-z]*$$/p'; \
+	fi; \
+	} | while read p; do \
+	  if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; echo "$$p"; \
+	done | \
+	sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+	sed 'N;N;s,\n, ,g' | { \
+	list=; while read file base inst; do \
+	  if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+	    echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+	    $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+	  fi; \
+	done; \
+	for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+	while read files; do \
+	  test -z "$$files" || { \
+	    echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+	    $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+	done; }
+
+uninstall-man1:
+	@$(NORMAL_UNINSTALL)
+	@list=''; test -n "$(man1dir)" || exit 0; \
+	files=`{ for i in $$list; do echo "$$i"; done; \
+	l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+	  sed -n '/\.1[a-z]*$$/p'; \
+	} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+	dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(PROGRAMS) $(MANS)
+installdirs:
+	for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+	-rm -f bltin/$(DEPDIR)/$(am__dirstamp)
+	-rm -f bltin/$(am__dirstamp)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
+
+distclean: distclean-am
+		-rm -f ./$(DEPDIR)/alias.Po
+	-rm -f ./$(DEPDIR)/arith_yacc.Po
+	-rm -f ./$(DEPDIR)/arith_yylex.Po
+	-rm -f ./$(DEPDIR)/cd.Po
+	-rm -f ./$(DEPDIR)/error.Po
+	-rm -f ./$(DEPDIR)/eval.Po
+	-rm -f ./$(DEPDIR)/exec.Po
+	-rm -f ./$(DEPDIR)/expand.Po
+	-rm -f ./$(DEPDIR)/histedit.Po
+	-rm -f ./$(DEPDIR)/input.Po
+	-rm -f ./$(DEPDIR)/jobs.Po
+	-rm -f ./$(DEPDIR)/mail.Po
+	-rm -f ./$(DEPDIR)/main.Po
+	-rm -f ./$(DEPDIR)/memalloc.Po
+	-rm -f ./$(DEPDIR)/miscbltin.Po
+	-rm -f ./$(DEPDIR)/mystring.Po
+	-rm -f ./$(DEPDIR)/options.Po
+	-rm -f ./$(DEPDIR)/output.Po
+	-rm -f ./$(DEPDIR)/parser.Po
+	-rm -f ./$(DEPDIR)/redir.Po
+	-rm -f ./$(DEPDIR)/show.Po
+	-rm -f ./$(DEPDIR)/system.Po
+	-rm -f ./$(DEPDIR)/trap.Po
+	-rm -f ./$(DEPDIR)/var.Po
+	-rm -f bltin/$(DEPDIR)/printf.Po
+	-rm -f bltin/$(DEPDIR)/test.Po
+	-rm -f bltin/$(DEPDIR)/times.Po
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man1
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+		-rm -f ./$(DEPDIR)/alias.Po
+	-rm -f ./$(DEPDIR)/arith_yacc.Po
+	-rm -f ./$(DEPDIR)/arith_yylex.Po
+	-rm -f ./$(DEPDIR)/cd.Po
+	-rm -f ./$(DEPDIR)/error.Po
+	-rm -f ./$(DEPDIR)/eval.Po
+	-rm -f ./$(DEPDIR)/exec.Po
+	-rm -f ./$(DEPDIR)/expand.Po
+	-rm -f ./$(DEPDIR)/histedit.Po
+	-rm -f ./$(DEPDIR)/input.Po
+	-rm -f ./$(DEPDIR)/jobs.Po
+	-rm -f ./$(DEPDIR)/mail.Po
+	-rm -f ./$(DEPDIR)/main.Po
+	-rm -f ./$(DEPDIR)/memalloc.Po
+	-rm -f ./$(DEPDIR)/miscbltin.Po
+	-rm -f ./$(DEPDIR)/mystring.Po
+	-rm -f ./$(DEPDIR)/options.Po
+	-rm -f ./$(DEPDIR)/output.Po
+	-rm -f ./$(DEPDIR)/parser.Po
+	-rm -f ./$(DEPDIR)/redir.Po
+	-rm -f ./$(DEPDIR)/show.Po
+	-rm -f ./$(DEPDIR)/system.Po
+	-rm -f ./$(DEPDIR)/trap.Po
+	-rm -f ./$(DEPDIR)/var.Po
+	-rm -f bltin/$(DEPDIR)/printf.Po
+	-rm -f bltin/$(DEPDIR)/test.Po
+	-rm -f bltin/$(DEPDIR)/times.Po
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-man
+
+uninstall-man: uninstall-man1
+
+.MAKE: all check install install-am install-exec install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+	clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \
+	distclean distclean-compile distclean-generic distclean-tags \
+	distdir dvi dvi-am html html-am info info-am install \
+	install-am install-binPROGRAMS install-data install-data-am \
+	install-dvi install-dvi-am install-exec install-exec-am \
+	install-html install-html-am install-info install-info-am \
+	install-man install-man1 install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip installcheck installcheck-am \
+	installdirs maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+	ps ps-am tags tags-am uninstall uninstall-am \
+	uninstall-binPROGRAMS uninstall-man uninstall-man1
+
+.PRECIOUS: Makefile
+
+
+token.h token_vars.h: mktokens
+	$(AM_V_GEN)$(SHELL) mktokens
+
+builtins.def: builtins.def.in $(top_builddir)/config.h
+	$(AM_V_CC)$(COMPILE) -E -x c -o $@ builtins.def.in
+
+builtins.c builtins.h: mkbuiltins builtins.def
+	$(AM_V_GEN)$(SHELL) mkbuiltins builtins.def
+
+init.c: mkinit $(dash_CFILES)
+	$(AM_V_GEN)./mkinit $(dash_CFILES)
+
+nodes.c nodes.h: mknodes nodetypes nodes.c.pat
+	$(AM_V_GEN)./mknodes nodetypes nodes.c.pat
+
+syntax.c syntax.h: mksyntax
+	$(AM_V_GEN)./mksyntax
+
+signames.c: mksignames
+	$(AM_V_GEN)./mksignames
+
+mksyntax: token.h
+
+$(HELPERS):
+	$(AM_V_CC)$(COMPILE_FOR_BUILD) -o $@ $@.c
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/TOUR b/bin/dash/src/TOUR
index e30836e1..e30836e1 100644
--- a/src/TOUR
+++ b/bin/dash/src/TOUR
diff --git a/src/alias.c b/bin/dash/src/alias.c
index daeacbb8..daeacbb8 100644
--- a/src/alias.c
+++ b/bin/dash/src/alias.c
diff --git a/src/alias.h b/bin/dash/src/alias.h
index fb841d64..fb841d64 100644
--- a/src/alias.h
+++ b/bin/dash/src/alias.h
diff --git a/src/arith_yacc.c b/bin/dash/src/arith_yacc.c
index 1a087c32..1a087c32 100644
--- a/src/arith_yacc.c
+++ b/bin/dash/src/arith_yacc.c
diff --git a/src/arith_yacc.h b/bin/dash/src/arith_yacc.h
index ff34d524..ff34d524 100644
--- a/src/arith_yacc.h
+++ b/bin/dash/src/arith_yacc.h
diff --git a/src/arith_yylex.c b/bin/dash/src/arith_yylex.c
index ec5b5b25..ec5b5b25 100644
--- a/src/arith_yylex.c
+++ b/bin/dash/src/arith_yylex.c
diff --git a/src/bltin/bltin.h b/bin/dash/src/bltin/bltin.h
index f5ac06f2..f5ac06f2 100644
--- a/src/bltin/bltin.h
+++ b/bin/dash/src/bltin/bltin.h
diff --git a/src/bltin/echo.1 b/bin/dash/src/bltin/echo.1
index fbc7fb43..fbc7fb43 100644
--- a/src/bltin/echo.1
+++ b/bin/dash/src/bltin/echo.1
diff --git a/src/bltin/printf.1 b/bin/dash/src/bltin/printf.1
index 38731732..38731732 100644
--- a/src/bltin/printf.1
+++ b/bin/dash/src/bltin/printf.1
diff --git a/src/bltin/printf.c b/bin/dash/src/bltin/printf.c
index 7785735b..7785735b 100644
--- a/src/bltin/printf.c
+++ b/bin/dash/src/bltin/printf.c
diff --git a/src/bltin/test.1 b/bin/dash/src/bltin/test.1
index 42435fb3..42435fb3 100644
--- a/src/bltin/test.1
+++ b/bin/dash/src/bltin/test.1
diff --git a/src/bltin/test.c b/bin/dash/src/bltin/test.c
index c7fc479d..c7fc479d 100644
--- a/src/bltin/test.c
+++ b/bin/dash/src/bltin/test.c
diff --git a/src/bltin/times.c b/bin/dash/src/bltin/times.c
index 1166a68e..1166a68e 100644
--- a/src/bltin/times.c
+++ b/bin/dash/src/bltin/times.c
diff --git a/src/builtins.def.in b/bin/dash/src/builtins.def.in
index 95e420cc..95e420cc 100644
--- a/src/builtins.def.in
+++ b/bin/dash/src/builtins.def.in
diff --git a/src/cd.c b/bin/dash/src/cd.c
index 1ef1dc56..1ef1dc56 100644
--- a/src/cd.c
+++ b/bin/dash/src/cd.c
diff --git a/src/cd.h b/bin/dash/src/cd.h
index 87631619..87631619 100644
--- a/src/cd.h
+++ b/bin/dash/src/cd.h
diff --git a/src/dash.1 b/bin/dash/src/dash.1
index 32f6ac0d..9bc51cd6 100644
--- a/src/dash.1
+++ b/bin/dash/src/dash.1
@@ -32,7 +32,7 @@
 .\"
 .\"	@(#)sh.1	8.6 (Berkeley) 5/4/95
 .\"
-.Dd January 19, 2003
+.Dd December 22, 2020
 .Os
 .Dt DASH 1
 .Sh NAME
@@ -2291,6 +2291,10 @@ The secondary prompt string, which defaults to
 Output before each line when execution trace (set -x) is enabled,
 defaults to
 .Dq +\  .
+.It Ev RPS1
+The primary right prompt string.
+.It Ev RPS2
+The secondary right prompt string.
 .It Ev IFS
 Input Field Separators.
 This is normally set to
diff --git a/src/error.c b/bin/dash/src/error.c
index 728ff885..728ff885 100644
--- a/src/error.c
+++ b/bin/dash/src/error.c
diff --git a/src/error.h b/bin/dash/src/error.h
index 94e30a27..94e30a27 100644
--- a/src/error.h
+++ b/bin/dash/src/error.h
diff --git a/src/eval.c b/bin/dash/src/eval.c
index d4190f95..d4190f95 100644
--- a/src/eval.c
+++ b/bin/dash/src/eval.c
diff --git a/src/eval.h b/bin/dash/src/eval.h
index 63e7d865..63e7d865 100644
--- a/src/eval.h
+++ b/bin/dash/src/eval.h
diff --git a/src/exec.c b/bin/dash/src/exec.c
index 87354d49..87354d49 100644
--- a/src/exec.c
+++ b/bin/dash/src/exec.c
diff --git a/src/exec.h b/bin/dash/src/exec.h
index 423b07e6..423b07e6 100644
--- a/src/exec.h
+++ b/bin/dash/src/exec.h
diff --git a/src/expand.c b/bin/dash/src/expand.c
index 1730670e..1730670e 100644
--- a/src/expand.c
+++ b/bin/dash/src/expand.c
diff --git a/src/expand.h b/bin/dash/src/expand.h
index c44b8481..c44b8481 100644
--- a/src/expand.h
+++ b/bin/dash/src/expand.h
diff --git a/src/funcs/cmv b/bin/dash/src/funcs/cmv
index 91a67c53..91a67c53 100644
--- a/src/funcs/cmv
+++ b/bin/dash/src/funcs/cmv
diff --git a/src/funcs/dirs b/bin/dash/src/funcs/dirs
index 5f6ce635..5f6ce635 100644
--- a/src/funcs/dirs
+++ b/bin/dash/src/funcs/dirs
diff --git a/src/funcs/kill b/bin/dash/src/funcs/kill
index c5df95f5..c5df95f5 100644
--- a/src/funcs/kill
+++ b/bin/dash/src/funcs/kill
diff --git a/src/funcs/login b/bin/dash/src/funcs/login
index 215e5352..215e5352 100644
--- a/src/funcs/login
+++ b/bin/dash/src/funcs/login
diff --git a/src/funcs/newgrp b/bin/dash/src/funcs/newgrp
index ec0e7e5a..ec0e7e5a 100644
--- a/src/funcs/newgrp
+++ b/bin/dash/src/funcs/newgrp
diff --git a/src/funcs/popd b/bin/dash/src/funcs/popd
index 7bccf50d..7bccf50d 100644
--- a/src/funcs/popd
+++ b/bin/dash/src/funcs/popd
diff --git a/src/funcs/pushd b/bin/dash/src/funcs/pushd
index 19ac8e08..19ac8e08 100644
--- a/src/funcs/pushd
+++ b/bin/dash/src/funcs/pushd
diff --git a/src/funcs/suspend b/bin/dash/src/funcs/suspend
index 44844678..44844678 100644
--- a/src/funcs/suspend
+++ b/bin/dash/src/funcs/suspend
diff --git a/src/histedit.c b/bin/dash/src/histedit.c
index f5c90aba..1b7ad238 100644
--- a/src/histedit.c
+++ b/bin/dash/src/histedit.c
@@ -116,6 +116,14 @@ histedit(void)
 				if (hist)
 					el_set(el, EL_HIST, history, hist);
 				el_set(el, EL_PROMPT, getprompt);
+				el_set(el, EL_RPROMPT, getrprompt);
+#ifdef HAVE__EL_FN_SH_COMPLETE
+				el_set(el, EL_ADDFN, "sh-complete", "Filename completion",
+					_el_fn_sh_complete);
+#else
+				el_set(el, EL_ADDFN, "sh-complete", "Filename completion",
+					_el_fn_complete);
+#endif
 			} else {
 bad:
 				out2str("sh: can't initialize editing\n");
@@ -133,6 +141,7 @@ bad:
 			else if (Eflag)
 				el_set(el, EL_EDITOR, "emacs");
 			el_source(el, NULL);
+			el_set(el, EL_BIND, "^I", "sh-complete", NULL);
 		}
 	} else {
 		INTOFF;
@@ -160,6 +169,7 @@ sethistsize(const char *hs)
 		   (histsize = atoi(hs)) < 0)
 			histsize = 100;
 		history(hist, &he, H_SETSIZE, histsize);
+		history(hist, &he, H_SETUNIQUE, 1);
 	}
 }
 
diff --git a/src/init.h b/bin/dash/src/init.h
index d56fb28e..d56fb28e 100644
--- a/src/init.h
+++ b/bin/dash/src/init.h
diff --git a/src/input.c b/bin/dash/src/input.c
index 17544e78..4167bd18 100644
--- a/src/input.c
+++ b/bin/dash/src/input.c
@@ -152,12 +152,8 @@ retry:
 		static const char *rl_cp;
 		static int el_len;
 
-		if (rl_cp == NULL) {
-			struct stackmark smark;
-			pushstackmark(&smark, stackblocksize());
+		if (rl_cp == NULL)
 			rl_cp = el_gets(el, &el_len);
-			popstackmark(&smark);
-		}
 		if (rl_cp == NULL)
 			nr = 0;
 		else {
diff --git a/src/input.h b/bin/dash/src/input.h
index 8acc6e9f..8acc6e9f 100644
--- a/src/input.h
+++ b/bin/dash/src/input.h
diff --git a/src/jobs.c b/bin/dash/src/jobs.c
index f30313be..8fb9d4b2 100644
--- a/src/jobs.c
+++ b/bin/dash/src/jobs.c
@@ -1207,12 +1207,12 @@ stoppedjobs(void)
 	int retval;
 
 	retval = 0;
-	if (job_warning)
+	if (job_warning > 1)
 		goto out;
 	jp = curjob;
 	if (jp && jp->state == JOBSTOPPED) {
 		out2str("You have stopped jobs.\n");
-		job_warning = 2;
+		job_warning++;
 		retval++;
 	}
 
diff --git a/src/jobs.h b/bin/dash/src/jobs.h
index 6ac6c56d..6ac6c56d 100644
--- a/src/jobs.h
+++ b/bin/dash/src/jobs.h
diff --git a/src/machdep.h b/bin/dash/src/machdep.h
index f2ff0ad8..f2ff0ad8 100644
--- a/src/machdep.h
+++ b/bin/dash/src/machdep.h
diff --git a/src/mail.c b/bin/dash/src/mail.c
index 8eacb2d0..8eacb2d0 100644
--- a/src/mail.c
+++ b/bin/dash/src/mail.c
diff --git a/src/mail.h b/bin/dash/src/mail.h
index 3c6b21d2..3c6b21d2 100644
--- a/src/mail.h
+++ b/bin/dash/src/mail.h
diff --git a/src/main.c b/bin/dash/src/main.c
index 7a285346..7a285346 100644
--- a/src/main.c
+++ b/bin/dash/src/main.c
diff --git a/src/main.h b/bin/dash/src/main.h
index 19e49835..19e49835 100644
--- a/src/main.h
+++ b/bin/dash/src/main.h
diff --git a/src/memalloc.c b/bin/dash/src/memalloc.c
index 60637da1..60637da1 100644
--- a/src/memalloc.c
+++ b/bin/dash/src/memalloc.c
diff --git a/src/memalloc.h b/bin/dash/src/memalloc.h
index b9adf764..b9adf764 100644
--- a/src/memalloc.h
+++ b/bin/dash/src/memalloc.h
diff --git a/src/miscbltin.c b/bin/dash/src/miscbltin.c
index 5ccbbcb8..5ccbbcb8 100644
--- a/src/miscbltin.c
+++ b/bin/dash/src/miscbltin.c
diff --git a/src/miscbltin.h b/bin/dash/src/miscbltin.h
index dd9a8d1c..dd9a8d1c 100644
--- a/src/miscbltin.h
+++ b/bin/dash/src/miscbltin.h
diff --git a/src/mkbuiltins b/bin/dash/src/mkbuiltins
index f1f25932..f1f25932 100644
--- a/src/mkbuiltins
+++ b/bin/dash/src/mkbuiltins
diff --git a/src/mkinit.c b/bin/dash/src/mkinit.c
index 9025862e..9025862e 100644
--- a/src/mkinit.c
+++ b/bin/dash/src/mkinit.c
diff --git a/src/mknodes.c b/bin/dash/src/mknodes.c
index 1903a605..1903a605 100644
--- a/src/mknodes.c
+++ b/bin/dash/src/mknodes.c
diff --git a/src/mksignames.c b/bin/dash/src/mksignames.c
index a832eab9..a832eab9 100644
--- a/src/mksignames.c
+++ b/bin/dash/src/mksignames.c
diff --git a/src/mksyntax.c b/bin/dash/src/mksyntax.c
index a23c18ca..a23c18ca 100644
--- a/src/mksyntax.c
+++ b/bin/dash/src/mksyntax.c
diff --git a/src/mktokens b/bin/dash/src/mktokens
index 78055be8..78055be8 100644
--- a/src/mktokens
+++ b/bin/dash/src/mktokens
diff --git a/src/myhistedit.h b/bin/dash/src/myhistedit.h
index 22e5c438..22e5c438 100644
--- a/src/myhistedit.h
+++ b/bin/dash/src/myhistedit.h
diff --git a/src/mystring.c b/bin/dash/src/mystring.c
index de624b89..de624b89 100644
--- a/src/mystring.c
+++ b/bin/dash/src/mystring.c
diff --git a/src/mystring.h b/bin/dash/src/mystring.h
index 083ea98c..083ea98c 100644
--- a/src/mystring.h
+++ b/bin/dash/src/mystring.h
diff --git a/src/nodes.c.pat b/bin/dash/src/nodes.c.pat
index 9125bc73..9125bc73 100644
--- a/src/nodes.c.pat
+++ b/bin/dash/src/nodes.c.pat
diff --git a/src/nodetypes b/bin/dash/src/nodetypes
index ceaf478c..ceaf478c 100644
--- a/src/nodetypes
+++ b/bin/dash/src/nodetypes
diff --git a/src/options.c b/bin/dash/src/options.c
index a46c23b9..a46c23b9 100644
--- a/src/options.c
+++ b/bin/dash/src/options.c
diff --git a/src/options.h b/bin/dash/src/options.h
index 975fe339..975fe339 100644
--- a/src/options.h
+++ b/bin/dash/src/options.h
diff --git a/src/output.c b/bin/dash/src/output.c
index e9ee9b4d..e9ee9b4d 100644
--- a/src/output.c
+++ b/bin/dash/src/output.c
diff --git a/src/output.h b/bin/dash/src/output.h
index c43d4937..c43d4937 100644
--- a/src/output.h
+++ b/bin/dash/src/output.h
diff --git a/src/parser.c b/bin/dash/src/parser.c
index a47022e0..499f7f8e 100644
--- a/src/parser.c
+++ b/bin/dash/src/parser.c
@@ -107,6 +107,10 @@ struct heredoc *heredoc;
 int quoteflag;			/* set if (part of) last token was quoted */
 
 
+static char *promptcache;
+static char *rpromptcache;
+
+
 STATIC union node *list(int);
 STATIC union node *andor(void);
 STATIC union node *pipeline(void);
@@ -1541,27 +1545,6 @@ synerror(const char *msg)
 	/* NOTREACHED */
 }
 
-STATIC void
-setprompt(int which)
-{
-	struct stackmark smark;
-	int show;
-
-	needprompt = 0;
-	whichprompt = which;
-
-#ifdef SMALL
-	show = 1;
-#else
-	show = !el;
-#endif
-	if (show) {
-		pushstackmark(&smark, stackblocksize());
-		out2str(getprompt(NULL));
-		popstackmark(&smark);
-	}
-}
-
 const char *
 expandstr(const char *ps)
 {
@@ -1611,31 +1594,78 @@ out:
 	return result;
 }
 
-/*
- * called by editline -- any expansions to the prompt
- *    should be added here.
- */
-const char *
-getprompt(void *unused)
+STATIC void
+setprompt(int which)
 {
-	const char *prompt;
+	struct stackmark smark;
+	const char *prompt, *rprompt, *nl;
+	int show;
+
+	needprompt = 0;
+	whichprompt = which;
 
 	switch (whichprompt) {
 	default:
 #ifdef DEBUG
-		return "<internal prompt error>";
+		prompt = "<internal prompt error>";
+		rprompt = prompt;
+		break;
 #endif
 	case 0:
-		return nullstr;
+		prompt = nullstr;
+		rprompt = nullstr;
+		break;
 	case 1:
 		prompt = ps1val();
+		rprompt = rps1val();
 		break;
 	case 2:
 		prompt = ps2val();
+		rprompt = rps2val();
 		break;
 	}
 
-	return expandstr(prompt);
+#ifdef SMALL
+	show = 1;
+#else
+	show = !el;
+#endif
+	pushstackmark(&smark, stackblocksize());
+	if (show) {
+		out2str(expandstr(prompt));
+	} else {
+		free(promptcache);
+		free(rpromptcache);
+		promptcache = savestr(expandstr(prompt));
+		rpromptcache = savestr(expandstr(rprompt));
+
+		nl = strrchr(promptcache, '\n');
+		if (nl)
+			outmem(promptcache, &nl[1] - promptcache, out2);
+	}
+	popstackmark(&smark);
+}
+
+/*
+ * called by editline -- return the cached prompt expansion.
+ */
+const char *
+getprompt(void *unused)
+{
+	const char *nl;
+
+	nl = strrchr(promptcache, '\n');
+
+	if (nl)
+		return &nl[1];
+	else
+		return promptcache;
+}
+
+const char *
+getrprompt(void *unused)
+{
+	return rpromptcache;
 }
 
 const char *const *
diff --git a/src/parser.h b/bin/dash/src/parser.h
index 524ac1c7..6597faa8 100644
--- a/src/parser.h
+++ b/bin/dash/src/parser.h
@@ -86,6 +86,7 @@ int isassignment(const char *p);
 union node *parsecmd(int);
 void fixredir(union node *, const char *, int);
 const char *getprompt(void *);
+const char *getrprompt(void *);
 const char *const *findkwd(const char *);
 char *endofname(const char *);
 const char *expandstr(const char *);
diff --git a/src/redir.c b/bin/dash/src/redir.c
index 895140c3..895140c3 100644
--- a/src/redir.c
+++ b/bin/dash/src/redir.c
diff --git a/src/redir.h b/bin/dash/src/redir.h
index 1cf27616..1cf27616 100644
--- a/src/redir.h
+++ b/bin/dash/src/redir.h
diff --git a/src/shell.h b/bin/dash/src/shell.h
index 98edc8be..98edc8be 100644
--- a/src/shell.h
+++ b/bin/dash/src/shell.h
diff --git a/src/show.c b/bin/dash/src/show.c
index 4a049e93..4a049e93 100644
--- a/src/show.c
+++ b/bin/dash/src/show.c
diff --git a/src/show.h b/bin/dash/src/show.h
index d0ccac79..d0ccac79 100644
--- a/src/show.h
+++ b/bin/dash/src/show.h
diff --git a/src/system.c b/bin/dash/src/system.c
index 844a6410..844a6410 100644
--- a/src/system.c
+++ b/bin/dash/src/system.c
diff --git a/src/system.h b/bin/dash/src/system.h
index 007952c5..007952c5 100644
--- a/src/system.h
+++ b/bin/dash/src/system.h
diff --git a/src/trap.c b/bin/dash/src/trap.c
index cd84814f..cd84814f 100644
--- a/src/trap.c
+++ b/bin/dash/src/trap.c
diff --git a/src/trap.h b/bin/dash/src/trap.h
index beaf6605..beaf6605 100644
--- a/src/trap.h
+++ b/bin/dash/src/trap.h
diff --git a/src/var.c b/bin/dash/src/var.c
index ef9c2bde..ca9504ef 100644
--- a/src/var.c
+++ b/bin/dash/src/var.c
@@ -93,6 +93,8 @@ struct var varinit[] = {
 	{ 0,	VSTRFIXED|VTEXTFIXED,		"PS1=$ ",	0 },
 	{ 0,	VSTRFIXED|VTEXTFIXED,		"PS2=> ",	0 },
 	{ 0,	VSTRFIXED|VTEXTFIXED,		"PS4=+ ",	0 },
+	{ 0,	VSTRFIXED|VTEXTFIXED|VUNSET,	"RPS1\0",	0 },
+	{ 0,	VSTRFIXED|VTEXTFIXED|VUNSET,	"RPS2\0",	0 },
 	{ 0,	VSTRFIXED|VTEXTFIXED,		defoptindvar,	getoptsreset },
 #ifdef WITH_LINENO
 	{ 0,	VSTRFIXED|VTEXTFIXED,		linenovar,	0 },
diff --git a/src/var.h b/bin/dash/src/var.h
index aa7575a7..1d1d87a5 100644
--- a/src/var.h
+++ b/bin/dash/src/var.h
@@ -87,7 +87,9 @@ extern struct var varinit[];
 #define vps1 (&vpath)[1]
 #define vps2 (&vps1)[1]
 #define vps4 (&vps2)[1]
-#define voptind (&vps4)[1]
+#define vrps1 (&vps4)[1]
+#define vrps2 (&vrps1)[1]
+#define voptind (&vrps2)[1]
 #ifdef WITH_LINENO
 #define vlineno (&voptind)[1]
 #endif
@@ -122,6 +124,8 @@ extern char linenovar[];
 #define ps1val()	(vps1.text + 4)
 #define ps2val()	(vps2.text + 4)
 #define ps4val()	(vps4.text + 4)
+#define rps1val()	(vrps1.text + 5)
+#define rps2val()	(vrps2.text + 5)
 #define optindval()	(voptind.text + 7)
 #define linenoval()	(vlineno.text + 7)
 #ifndef SMALL
diff --git a/bin/dtch.c b/bin/dtch.c
new file mode 100644
index 00000000..2aea53ae
--- /dev/null
+++ b/bin/dtch.c
@@ -0,0 +1,271 @@
+/* Copyright (C) 2017-2019  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 <poll.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sysexits.h>
+#include <termios.h>
+#include <unistd.h>
+
+#if defined __FreeBSD__
+#include <libutil.h>
+#elif defined __linux__
+#include <pty.h>
+#else
+#include <util.h>
+#endif
+
+static char _;
+static struct iovec iov = { .iov_base = &_, .iov_len = 1 };
+
+static ssize_t sendfd(int sock, int fd) {
+	size_t len = CMSG_SPACE(sizeof(int));
+	char buf[len];
+	struct msghdr msg = {
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+		.msg_control = buf,
+		.msg_controllen = len,
+	};
+
+	struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+	cmsg->cmsg_level = SOL_SOCKET;
+	cmsg->cmsg_type = SCM_RIGHTS;
+	*(int *)CMSG_DATA(cmsg) = fd;
+
+	return sendmsg(sock, &msg, 0);
+}
+
+static int recvfd(int sock) {
+	size_t len = CMSG_SPACE(sizeof(int));
+	char buf[len];
+	struct msghdr msg = {
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+		.msg_control = buf,
+		.msg_controllen = len,
+	};
+	if (0 > recvmsg(sock, &msg, 0)) return -1;
+
+	struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+	if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS) {
+		errno = ENOMSG;
+		return -1;
+	}
+	return *(int *)CMSG_DATA(cmsg);
+}
+
+static struct sockaddr_un addr = { .sun_family = AF_UNIX };
+
+static void handler(int sig) {
+	unlink(addr.sun_path);
+	_exit(-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) {
+		execvp(argv[0], argv);
+		err(EX_NOINPUT, "%s", argv[0]);
+	}
+
+	signal(SIGINT, handler);
+	signal(SIGTERM, handler);
+
+	int error = listen(server, 0);
+	if (error) err(EX_OSERR, "listen");
+
+	struct pollfd fds[] = {
+		{ .events = POLLIN, .fd = server },
+		{ .events = POLLIN, .fd = pty },
+	};
+	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");
+
+			ssize_t len = sendfd(client, pty);
+			if (len < 0) warn("sendfd");
+
+			len = recv(client, &_, sizeof(_), 0);
+			if (len < 0) warn("recv");
+
+			close(client);
+		}
+
+		if (fds[1].revents) {
+			char buf[4096];
+			ssize_t len = read(pty, buf, sizeof(buf));
+			if (len < 0) err(EX_IOERR, "read");
+		}
+
+		int status;
+		pid_t dead = waitpid(pid, &status, WNOHANG);
+		if (dead < 0) err(EX_OSERR, "waitpid");
+		if (dead) {
+			unlink(addr.sun_path);
+			exit(WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status));
+		}
+	}
+	err(EX_IOERR, "poll");
+}
+
+static struct termios saveTerm;
+static void restoreTerm(void) {
+	tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm);
+	fprintf(stderr, "\33c"); // RIS
+	warnx("detached");
+}
+
+static void nop(int sig) {
+	(void)sig;
+}
+
+static void attach(int client) {
+	int error;
+
+	int pty = recvfd(client);
+	if (pty < 0) err(EX_IOERR, "recvfd");
+	warnx("attached");
+
+	struct winsize window;
+	error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
+	if (error) err(EX_IOERR, "ioctl");
+
+	struct winsize redraw = { .ws_row = 1, .ws_col = 1 };
+	error = ioctl(pty, TIOCSWINSZ, &redraw);
+	if (error) err(EX_IOERR, "ioctl");
+
+	error = ioctl(pty, TIOCSWINSZ, &window);
+	if (error) err(EX_IOERR, "ioctl");
+
+	error = tcgetattr(STDIN_FILENO, &saveTerm);
+	if (error) err(EX_IOERR, "tcgetattr");
+	atexit(restoreTerm);
+
+	struct termios raw = saveTerm;
+	cfmakeraw(&raw);
+	error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw);
+	if (error) err(EX_IOERR, "tcsetattr");
+
+	signal(SIGWINCH, nop);
+
+	char buf[4096];
+	struct pollfd fds[] = {
+		{ .events = POLLIN, .fd = STDIN_FILENO },
+		{ .events = POLLIN, .fd = pty },
+	};
+	for (;;) {
+		int nfds = poll(fds, 2, -1);
+		if (nfds < 0) {
+			if (errno != EINTR) err(EX_IOERR, "poll");
+
+			error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
+			if (error) err(EX_IOERR, "ioctl");
+
+			error = ioctl(pty, TIOCSWINSZ, &window);
+			if (error) err(EX_IOERR, "ioctl");
+
+			continue;
+		}
+
+		if (fds[0].revents) {
+			ssize_t len = read(STDIN_FILENO, buf, sizeof(buf));
+			if (len < 0) err(EX_IOERR, "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 (fds[1].revents) {
+			ssize_t len = read(pty, buf, sizeof(buf));
+			if (len < 0) err(EX_IOERR, "read");
+			if (!len) break;
+
+			len = write(STDOUT_FILENO, buf, len);
+			if (len < 0) err(EX_IOERR, "write");
+		}
+	}
+}
+
+int main(int argc, char *argv[]) {
+	int error;
+
+	bool atch = false;
+	bool sink = false;
+
+	int opt;
+	while (0 < (opt = getopt(argc, argv, "as"))) {
+		switch (opt) {
+			break; case 'a': atch = true;
+			break; case 's': sink = true;
+			break; default:  return EX_USAGE;
+		}
+	}
+	if (optind == argc) errx(EX_USAGE, "no session name");
+	const char *name = argv[optind++];
+
+	if (optind == argc) {
+		argv[--optind] = getenv("SHELL");
+		if (!argv[optind]) errx(EX_CONFIG, "SHELL unset");
+	}
+
+	const char *home = getenv("HOME");
+	if (!home) errx(EX_CONFIG, "HOME unset");
+
+	int fd = open(home, 0);
+	if (fd < 0) err(EX_CANTCREAT, "%s", home);
+
+	error = mkdirat(fd, ".dtch", 0700);
+	if (error && errno != EEXIST) err(EX_CANTCREAT, "%s/.dtch", home);
+
+	close(fd);
+
+	int sock = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) err(EX_OSERR, "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);
+		attach(sock);
+	} else {
+		error = bind(sock, (struct sockaddr *)&addr, SUN_LEN(&addr));
+		if (error) err(EX_CANTCREAT, "%s", addr.sun_path);
+		detach(sock, sink, &argv[optind]);
+	}
+}
diff --git a/bin/ever.c b/bin/ever.c
new file mode 100644
index 00000000..f983912b
--- /dev/null
+++ b/bin/ever.c
@@ -0,0 +1,119 @@
+/* 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 <sys/types.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#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);
+
+	struct kevent event;
+	EV_SET(
+		&event,
+		fd,
+		EVFILT_VNODE,
+		EV_ADD | EV_CLEAR,
+		NOTE_WRITE | NOTE_DELETE,
+		0,
+		path
+	);
+	int nevents = kevent(kq, &event, 1, NULL, 0, NULL);
+	if (nevents < 0) err(EX_OSERR, "kevent");
+
+	return fd;
+}
+
+static bool quiet;
+static void exec(int fd, char *const argv[]) {
+	pid_t pid = fork();
+	if (pid < 0) err(EX_OSERR, "fork");
+
+	if (!pid) {
+		dup2(fd, STDIN_FILENO);
+		execvp(*argv, argv);
+		err(EX_NOINPUT, "%s", *argv);
+	}
+
+	int status;
+	pid = wait(&status);
+	if (pid < 0) err(EX_OSERR, "wait");
+
+	if (quiet) return;
+	if (WIFEXITED(status)) {
+		warnx("exit %d\n", WEXITSTATUS(status));
+	} else if (WIFSIGNALED(status)) {
+		warnx("signal %d\n", WTERMSIG(status));
+	} else {
+		warnx("status %d\n", status);
+	}
+}
+
+int main(int argc, char *argv[]) {
+	bool input = false;
+
+	for (int opt; 0 < (opt = getopt(argc, argv, "iq"));) {
+		switch (opt) {
+			break; case 'i': input = true;
+			break; case 'q': quiet = true;
+			break; default:  return EX_USAGE;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc < 2) return EX_USAGE;
+
+	int kq = kqueue();
+	if (kq < 0) err(EX_OSERR, "kqueue");
+
+	int i;
+	for (i = 0; i < argc - 1; ++i) {
+		if (argv[i][0] == '-') {
+			i++;
+			break;
+		}
+		watch(kq, argv[i]);
+	}
+
+	if (!input) {
+		exec(STDIN_FILENO, &argv[i]);
+	}
+
+	for (;;) {
+		struct kevent event;
+		int nevents = kevent(kq, NULL, 0, &event, 1, NULL);
+		if (nevents < 0) err(EX_OSERR, "kevent");
+
+		if (event.fflags & NOTE_DELETE) {
+			close(event.ident);
+			sleep(1);
+			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");
+		}
+
+		exec((input ? event.ident : STDIN_FILENO), &argv[i]);
+	}
+}
diff --git a/bin/fbatt.c b/bin/fbatt.c
new file mode 100644
index 00000000..9feffe47
--- /dev/null
+++ b/bin/fbatt.c
@@ -0,0 +1,123 @@
+/* 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
new file mode 100644
index 00000000..ddc32db6
--- /dev/null
+++ b/bin/fbclock.c
@@ -0,0 +1,132 @@
+/* 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/freecell.c b/bin/freecell.c
new file mode 100644
index 00000000..11bed1c0
--- /dev/null
+++ b/bin/freecell.c
@@ -0,0 +1,388 @@
+/* Copyright (C) 2019, 2021  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 <ctype.h>
+#include <curses.h>
+#include <err.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <time.h>
+#include <unistd.h>
+
+typedef unsigned uint;
+typedef unsigned char byte;
+
+typedef byte Card;
+enum {
+	A = 1,
+	J = 11,
+	Q = 12,
+	K = 13,
+	Rank = 0x0F,
+	Suit = 0x30,
+	Color = 0x10,
+	Club = 0x00,
+	Diamond = 0x10,
+	Spade = 0x20,
+	Heart = 0x30,
+};
+
+enum { StackCap = 52 };
+struct Stack {
+	byte len;
+	Card cards[StackCap];
+};
+static void push(struct Stack *stack, Card card) {
+	assert(stack->len < StackCap);
+	stack->cards[stack->len++] = card;
+}
+static Card pop(struct Stack *stack) {
+	if (!stack->len) return 0;
+	return stack->cards[--stack->len];
+}
+static Card peek(struct Stack *stack) {
+	if (!stack->len) return 0;
+	return stack->cards[stack->len-1];
+}
+
+enum {
+	Foundation,
+	Cell = Foundation + 4,
+	Tableau = Cell + 4,
+	Stacks = Tableau + 8,
+};
+static struct Stack stacks[Stacks];
+
+struct Move {
+	byte dst;
+	byte src;
+};
+
+enum { QCap = 16 };
+static struct {
+	struct Move moves[QCap];
+	uint r, w, u;
+} q;
+static void enq(byte dst, byte src) {
+	q.moves[q.w % QCap].dst = dst;
+	q.moves[q.w % QCap].src = src;
+	q.w++;
+}
+static void deq(void) {
+	struct Move move = q.moves[q.r++ % QCap];
+	push(&stacks[move.dst], pop(&stacks[move.src]));
+}
+static void undo(void) {
+	uint len = q.w - q.u;
+	if (!len || len > QCap) return;
+	for (uint i = len-1; i < len; --i) {
+		struct Move move = q.moves[(q.u+i) % QCap];
+		push(&stacks[move.src], pop(&stacks[move.dst]));
+	}
+	q.r = q.w = q.u;
+}
+
+// https://rosettacode.org/wiki/Deal_cards_for_FreeCell
+static uint lcgState;
+static uint lcg(void) {
+	lcgState = (214013 * lcgState + 2531011) % (1 << 31);
+	return lcgState >> 16;
+}
+static void deal(uint game) {
+	lcgState = game;
+	struct Stack deck = {0};
+	for (Card i = A; i <= K; ++i) {
+		push(&deck, Club | i);
+		push(&deck, Diamond | i);
+		push(&deck, Heart | i);
+		push(&deck, Spade | i);
+	}
+	for (uint stack = 0; deck.len; ++stack) {
+		uint i = lcg() % deck.len;
+		Card card = deck.cards[i];
+		deck.cards[i] = deck.cards[--deck.len];
+		push(&stacks[Tableau + stack%8], card);
+	}
+}
+
+static bool win(void) {
+	for (uint i = Foundation; i < Cell; ++i) {
+		if (stacks[i].len != 13) return false;
+	}
+	return true;
+}
+
+static bool valid(uint dst, Card card) {
+	Card top = peek(&stacks[dst]);
+	if (dst < Cell) {
+		if (!top) return (card & Rank) == A;
+		return (card & Suit) == (top & Suit)
+			&& (card & Rank) == (top & Rank) + 1;
+	}
+	if (!top) return true;
+	if (dst >= Tableau) {
+		return (card & Color) != (top & Color)
+			&& (card & Rank) == (top & Rank) - 1;
+	}
+	return false;
+}
+
+static void autoEnq(void) {
+	Card min[] = { K, K };
+	for (uint i = Cell; i < Stacks; ++i) {
+		for (uint j = 0; j < stacks[i].len; ++j) {
+			Card card = stacks[i].cards[j];
+			if ((card & Rank) < min[!!(card & Color)]) {
+				min[!!(card & Color)] = card & Rank;
+			}
+		}
+	}
+	for (uint src = Cell; src < Stacks; ++src) {
+		Card card = peek(&stacks[src]);
+		if (!card) continue;
+		if (min[!(card & Color)] < (card & Rank)-1) continue;
+		for (uint dst = Foundation; dst < Cell; ++dst) {
+			if (valid(dst, card)) {
+				enq(dst, src);
+				return;
+			}
+		}
+	}
+}
+
+static void moveSingle(uint dst, uint src) {
+	if (!valid(dst, peek(&stacks[src]))) return;
+	q.u = q.w;
+	enq(dst, src);
+}
+
+static uint freeCells(uint cells[static 4]) {
+	uint len = 0;
+	for (uint i = Cell; i < Tableau; ++i) {
+		if (!stacks[i].len) cells[len++] = i;
+	}
+	return len;
+}
+
+static uint moveDepth(uint src) {
+	struct Stack stack = stacks[src];
+	if (stack.len < 2) return stack.len;
+	uint n = 1;
+	for (uint i = stack.len-2; i < stack.len; --i, ++n) {
+		if ((stack.cards[i] & Color) == (stack.cards[i+1] & Color)) break;
+		if ((stack.cards[i] & Rank) != (stack.cards[i+1] & Rank) + 1) break;
+	}
+	return n;
+}
+
+static void moveColumn(uint dst, uint src) {
+	uint depth;
+	uint cells[4];
+	uint free = freeCells(cells);
+	for (depth = moveDepth(src); depth; --depth) {
+		if (free < depth-1) continue;
+		if (valid(dst, stacks[src].cards[stacks[src].len-depth])) break;
+	}
+	if (depth < 2 || dst < Tableau) {
+		moveSingle(dst, src);
+		return;
+	}
+	q.u = q.w;
+	for (uint i = 0; i < depth-1; ++i) {
+		enq(cells[i], src);
+	}
+	enq(dst, src);
+	for (uint i = depth-2; i < depth-1; --i) {
+		enq(dst, cells[i]);
+	}
+}
+
+static void curse(void) {
+	setlocale(LC_CTYPE, "");
+	initscr();
+	cbreak();
+	noecho();
+	curs_set(0);
+	start_color();
+	use_default_colors();
+	init_pair(1, COLOR_BLACK, COLOR_WHITE);
+	init_pair(2, COLOR_RED, COLOR_WHITE);
+	init_pair(3, COLOR_GREEN, -1);
+}
+
+static void drawCard(bool hi, int y, int x, Card card) {
+	if (!card) return;
+	move(y, x);
+	attr_set(hi ? A_REVERSE : A_NORMAL, (card & Color) ? 2 : 1, NULL);
+	switch (card & Suit) {
+		break; case Club: addstr("\u2663");
+		break; case Diamond: addstr("\u2666");
+		break; case Spade: addstr("\u2660");
+		break; case Heart: addstr("\u2665");
+		break; default:;
+	}
+	switch (card & Rank) {
+		break; case A: addstr(" A");
+		break; case 10: addstr("10");
+		break; case J: addstr(" J");
+		break; case Q: addstr(" Q");
+		break; case K: addstr(" K");
+		break; default: {
+			addch(' ');
+			addch('0' + (card & Rank));
+		}
+	}
+	attr_set(A_NORMAL, 0, NULL);
+}
+
+static void drawStack(bool hi, int y, int x, const struct Stack *stack) {
+	for (uint i = 0; i < stack->len; ++i) {
+		drawCard(hi && i == stack->len-1, y++, x, stack->cards[i]);
+	}
+}
+
+enum {
+	Padding = 1,
+	CardWidth = 3,
+	CardHeight = 1,
+	CellX = Padding,
+	CellY = 2*CardHeight,
+	FoundationX = CellX + 4*(CardWidth+Padding),
+	FoundationY = CellY,
+	TableauX = CellX,
+	TableauY = CellY + 2*CardHeight,
+};
+
+static uint game;
+static uint srcStack = Stacks;
+
+static void draw(void) {
+	erase();
+	static char buf[256];
+	if (!buf[0]) snprintf(buf, sizeof(buf), "Game #%u", game);
+	attr_set(A_NORMAL, 3, NULL);
+	mvaddstr(0, Padding, buf);
+	for (uint i = 0; i < Stacks; ++i) {
+		int y, x;
+		char key;
+		if (i < Cell) {
+			y = FoundationY;
+			x = FoundationX + (3-(i-Foundation)) * (CardWidth+Padding);
+			key = '_';
+		} else if (i < Tableau) {
+			y = CellY;
+			x = CellX + (i-Cell) * (CardWidth+Padding);
+			key = '1' + i-Cell;
+		} else {
+			y = TableauY;
+			x = TableauX + (i-Tableau) * (CardWidth+Padding);
+			key = "QWERASDF"[i-Tableau];
+		}
+		if (i < Tableau) {
+			mvaddch(y, x+1, COLOR_PAIR(3) | key);
+		} else {
+			mvaddch(y + 8*CardHeight, x+1, COLOR_PAIR(3) | key);
+		}
+		if (i < Cell) {
+			drawCard(false, y, x, peek(&stacks[i]));
+		} else {
+			drawStack(i == srcStack, y, x, &stacks[i]);
+		}
+	}
+}
+
+static void input(void) {
+	char ch = getch();
+	uint stack = Stacks;
+	switch (tolower(ch)) {
+		break; case '\33': srcStack = Stacks;
+		break; case 'u': case '\b': case '\177': undo();
+		break; case '1': case '!': stack = Cell+0;
+		break; case '2': case '@': stack = Cell+1;
+		break; case '3': case '#': stack = Cell+2;
+		break; case '4': case '$': stack = Cell+3;
+		break; case '_': case ' ': stack = Foundation;
+		break; case 'q': stack = Tableau+0;
+		break; case 'w': stack = Tableau+1;
+		break; case 'e': stack = Tableau+2;
+		break; case 'r': stack = Tableau+3;
+		break; case 'a': stack = Tableau+4;
+		break; case 's': stack = Tableau+5;
+		break; case 'd': stack = Tableau+6;
+		break; case 'f': stack = Tableau+7;
+	}
+	if (stack == Stacks) return;
+
+	if (srcStack < Stacks) {
+		Card card = peek(&stacks[srcStack]);
+		if (stack == Foundation) {
+			for (; stack < Cell; ++stack) {
+				if (valid(stack, card)) break;
+			}
+			if (stack == Cell) return;
+		}
+		if (stack == srcStack) {
+			for (stack = Cell; stack < Stacks; ++stack) {
+				if (!stacks[stack].len) break;
+			}
+			if (stack == Stacks) return;
+		}
+		if (isupper(ch)) {
+			moveSingle(stack, srcStack);
+		} else {
+			moveColumn(stack, srcStack);
+		}
+		srcStack = Stacks;
+
+	} else if (stack >= Cell && stacks[stack].len) {
+		srcStack = stack;
+	}
+}
+
+static void status(void) {
+	printf("Game #%u %s!\n", game, win() ? "win" : "lose");
+}
+
+int main(int argc, char *argv[]) {
+	game = 1 + time(NULL) % 32000;
+	uint delay = 50;
+	for (int opt; 0 < (opt = getopt(argc, argv, "d:n:"));) {
+		switch (opt) {
+			break; case 'd': delay = strtoul(optarg, NULL, 10);
+			break; case 'n': game = strtoul(optarg, NULL, 10);
+			break; default:  return EX_USAGE;
+		}
+	}
+	curse();
+	deal(game);
+	atexit(status);
+	while (!win()) {
+		while (q.r < q.w) {
+			deq();
+			draw();
+			refresh();
+			usleep(delay * 1000);
+			if (q.r == q.w) autoEnq();
+		}
+		draw();
+		input();
+	}
+	endwin();
+}
diff --git a/bin/glitch.c b/bin/glitch.c
new file mode 100644
index 00000000..9747f35a
--- /dev/null
+++ b/bin/glitch.c
@@ -0,0 +1,538 @@
+/* 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 <arpa/inet.h>
+#include <err.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))
+
+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 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 const uint8_t Signature[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 writeSignature(void) {
+	writeExpect(Signature, sizeof(Signature));
+}
+
+struct PACKED Chunk {
+	uint32_t size;
+	char type[4];
+};
+
+static const char *typeStr(struct Chunk chunk) {
+	static char buf[5];
+	memcpy(buf, chunk.type, 4);
+	return buf;
+}
+
+static struct Chunk readChunk(void) {
+	struct Chunk chunk;
+	readExpect(&chunk, sizeof(chunk), "chunk");
+	chunk.size = ntohl(chunk.size);
+	crc = crc32(CRC_INIT, (Byte *)chunk.type, sizeof(chunk.type));
+	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 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 writeCrc(void) {
+	uint32_t net = htonl(crc);
+	writeExpect(&net, sizeof(net));
+}
+
+static void skipChunk(struct Chunk chunk) {
+	uint8_t discard[chunk.size];
+	readExpect(discard, sizeof(discard), "chunk data");
+	readCrc();
+}
+
+static struct PACKED {
+	uint32_t width;
+	uint32_t height;
+	uint8_t depth;
+	enum PACKED {
+		Grayscale      = 0,
+		Truecolor      = 2,
+		Indexed        = 3,
+		GrayscaleAlpha = 4,
+		TruecolorAlpha = 6,
+	} color;
+	uint8_t compression;
+	uint8_t filter;
+	uint8_t interlace;
+} header;
+_Static_assert(13 == sizeof(header), "header size");
+
+static size_t pixelBits(void) {
+	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();
+	}
+}
+
+static size_t pixelSize(void) {
+	return (pixelBits() + 7) / 8;
+}
+
+static size_t lineSize(void) {
+	return (header.width * pixelBits() + 7) / 8;
+}
+
+static size_t dataSize(void) {
+	return (1 + lineSize()) * header.height;
+}
+
+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));
+	}
+	if (ihdr.size != sizeof(header)) {
+		errx(
+			EX_DATAERR, "%s: expected IHDR size %zu, found %u",
+			path, sizeof(header), ihdr.size
+		);
+	}
+	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);
+}
+
+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 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);
+	}
+	palette.len = chunk.size / 3;
+	readExpect(palette.entries, chunk.size, "palette data");
+	readCrc();
+}
+
+static void writePalette(void) {
+	struct Chunk plte = { .size = 3 * palette.len, .type = "PLTE" };
+	writeChunk(plte);
+	writeExpect(palette.entries, plte.size);
+	writeCrc();
+}
+
+static uint8_t *data;
+
+static void readData(void) {
+	data = malloc(dataSize());
+	if (!data) err(EX_OSERR, "malloc(%zu)", dataSize());
+
+	struct z_stream_s stream = { .next_out = data, .avail_out = dataSize() };
+	int error = inflateInit(&stream);
+	if (error != Z_OK) errx(EX_SOFTWARE, "%s: inflateInit: %s", path, 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();
+
+			stream.next_in = idat;
+			stream.avail_in = chunk.size;
+			int error = inflate(&stream, Z_SYNC_FLUSH);
+			free(idat);
+
+			if (error == Z_STREAM_END) break;
+			if (error != Z_OK) errx(EX_DATAERR, "%s: inflate: %s", path, stream.msg);
+
+		} else if (0 == memcmp(chunk.type, "IEND", 4)) {
+			errx(EX_DATAERR, "%s: missing IDAT chunk", path);
+		} else {
+			skipChunk(chunk);
+		}
+	}
+
+	inflateEnd(&stream);
+	if ((size_t)stream.total_out != dataSize()) {
+		errx(
+			EX_DATAERR, "%s: expected data size %zu, found %zu",
+			path, dataSize(), (size_t)stream.total_out
+		);
+	}
+}
+
+static void writeData(void) {
+	uLong size = compressBound(dataSize());
+	uint8_t *deflate = malloc(size);
+	if (!deflate) err(EX_OSERR, "malloc");
+
+	int error = compress2(deflate, &size, data, dataSize(), Z_BEST_SPEED);
+	if (error != Z_OK) errx(EX_SOFTWARE, "%s: compress2: %d", path, error);
+
+	struct Chunk idat = { .size = size, .type = "IDAT" };
+	writeChunk(idat);
+	writeExpect(deflate, size);
+	writeCrc();
+
+	free(deflate);
+}
+
+static void writeEnd(void) {
+	struct Chunk iend = { .size = 0, .type = "IEND" };
+	writeChunk(iend);
+	writeCrc();
+}
+
+enum PACKED Filter {
+	None,
+	Sub,
+	Up,
+	Average,
+	Paeth,
+	FilterCount,
+};
+
+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;
+};
+
+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);
+	if (pa <= pb && pa <= pc) return f.a;
+	if (options.brokenPaeth) {
+		if (pb < pc) return f.b;
+	} else {
+		if (pb <= pc) return f.b;
+	}
+	return f.c;
+}
+
+static uint8_t recon(enum Filter type, struct Bytes f) {
+	switch (type) {
+		case None:    return f.x;
+		case Sub:     return f.x + f.a;
+		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();
+	}
+}
+
+static uint8_t filt(enum Filter type, struct Bytes f) {
+	switch (type) {
+		case None:    return f.x;
+		case Sub:     return f.x - f.a;
+		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();
+	}
+}
+
+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 struct Bytes origBytes(uint32_t y, size_t i) {
+	bool a = (i >= pixelSize()), 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,
+	};
+}
+
+static void reconData(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));
+			} else {
+				lines[y]->data[i] = recon(lines[y]->type, origBytes(y, i));
+			}
+		}
+		lines[y]->type = 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};
+		enum Filter minType = None;
+		for (enum Filter type = None; type < FilterCount; ++type) {
+			for (size_t i = 0; i < lineSize(); ++i) {
+				if (options.recon) {
+					filter[type][i] = recon(type, origBytes(y, i));
+				} else {
+					filter[type][i] = filt(type, origBytes(y, i));
+				}
+				heuristic[type] += abs((int8_t)filter[type][i]);
+			}
+			if (heuristic[type] < heuristic[minType]) minType = type;
+		}
+
+		if (options.declareFilter) {
+			lines[y]->type = options.declareFilters[y % options.declareFilter];
+		} else {
+			lines[y]->type = minType;
+		}
+
+		if (options.applyFilter) {
+			enum Filter type = options.applyFilters[y % options.applyFilter];
+			memcpy(lines[y]->data, filter[type], lineSize());
+		} else {
+			memcpy(lines[y]->data, filter[minType], lineSize());
+		}
+	}
+}
+
+static void invert(void) {
+	for (uint32_t y = 0; y < header.height; ++y) {
+		for (size_t i = 0; i < lineSize(); ++i) {
+			lines[y]->data[i] ^= 0xFF;
+		}
+	}
+}
+
+static void mirror(void) {
+	for (uint32_t y = 0; y < header.height; ++y) {
+		for (size_t i = 0, j = lineSize() - 1; i < j; ++i, --j) {
+			uint8_t t = lines[y]->data[i];
+			lines[y]->data[i] = lines[y]->data[j];
+			lines[y]->data[j] = t;
+		}
+	}
+}
+
+static void zeroX(void) {
+	for (uint32_t y = 0; y < header.height; ++y) {
+		memset(lines[y]->data, 0, pixelSize());
+	}
+}
+
+static void zeroY(void) {
+	memset(lines[0]->data, 0, lineSize());
+}
+
+static void glitch(const char *inPath, const char *outPath) {
+	if (inPath) {
+		path = inPath;
+		file = fopen(path, "r");
+		if (!file) err(EX_NOINPUT, "%s", path);
+	} else {
+		path = "(stdin)";
+		file = stdin;
+	}
+
+	readSignature();
+	readHeader();
+	if (header.color == Indexed) readPalette();
+	readData();
+	fclose(file);
+
+	scanlines();
+	reconData();
+	filterData();
+	if (options.invert) invert();
+	if (options.mirror) mirror();
+	if (options.zeroX) zeroX();
+	if (options.zeroY) zeroY();
+	free(lines);
+
+	if (outPath) {
+		path = outPath;
+		file = fopen(path, "w");
+		if (!file) err(EX_CANTCREAT, "%s", path);
+	} else {
+		path = "(stdout)";
+		file = stdout;
+	}
+
+	writeSignature();
+	writeHeader();
+	if (header.color == Indexed) writePalette();
+	writeData();
+	writeEnd();
+	free(data);
+
+	int error = fclose(file);
+	if (error) err(EX_IOERR, "%s", path);
+}
+
+static enum Filter parseFilter(const char *s) {
+	switch (s[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);
+	}
+}
+
+static uint8_t parseFilters(enum Filter *filters, const char *s) {
+	uint8_t len = 0;
+	do {
+		filters[len++] = parseFilter(s);
+		s = strchr(s, ',');
+	} while (s++);
+	return len;
+}
+
+int main(int argc, char *argv[]) {
+	bool stdio = false;
+	char *output = NULL;
+
+	int opt;
+	while (0 < (opt = getopt(argc, argv, "a:cd:fimo:prxy"))) {
+		switch (opt) {
+			break; case 'a':
+				options.applyFilter = parseFilters(options.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;
+		}
+	}
+
+	if (argc - optind == 1 && (output || stdio)) {
+		glitch(argv[optind], output);
+	} else if (optind < argc) {
+		for (int i = optind; i < argc; ++i) {
+			glitch(argv[i], argv[i]);
+		}
+	} else {
+		glitch(NULL, output);
+	}
+
+	return EX_OK;
+}
diff --git a/bin/hilex.c b/bin/hilex.c
new file mode 100644
index 00000000..91a5e889
--- /dev/null
+++ b/bin/hilex.c
@@ -0,0 +1,391 @@
+/* Copyright (C) 2020  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 <ctype.h>
+#include <err.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "hilex.h"
+
+#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
+
+static const char *Class[] = {
+#define X(class) [class] = #class,
+	ENUM_CLASS
+#undef X
+};
+
+static FILE *yyin;
+static char *yytext;
+static int yylex(void) {
+	static size_t cap = 0;
+	return (getline(&yytext, &cap, yyin) < 0 ? None : Normal);
+}
+static const struct Lexer LexText = { yylex, &yyin, &yytext };
+
+static const struct {
+	const struct Lexer *lexer;
+	const char *name;
+	const char *namePatt;
+	const char *linePatt;
+} Lexers[] = {
+	{ &LexC, "c", "[.][chlmy]$", NULL },
+	{ &LexMake, "make", "[.](mk|am)$|^Makefile$", NULL },
+	{ &LexMdoc, "mdoc", "[.][1-9]$", "^[.]Dd" },
+	{ &LexSh, "sh", "[.]sh$|^[.](profile|shrc)$", "^#![ ]?/bin/sh" },
+	{ &LexText, "text", "[.]txt$", NULL },
+};
+
+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);
+}
+
+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");
+	}
+}
+
+static const struct Lexer *matchLexer(const char *name, FILE *file) {
+	char buf[256];
+	regex_t regex;
+	for (size_t i = 0; i < ARRAY_LEN(Lexers); ++i) {
+		int error = regcomp(
+			&regex, Lexers[i].namePatt, REG_EXTENDED | REG_NOSUB
+		);
+		assert(!error);
+		error = regexec(&regex, name, 0, NULL, 0);
+		regfree(&regex);
+		if (!error) return Lexers[i].lexer;
+	}
+	char *line = fgets(buf, sizeof(buf), file);
+	if (!line) return NULL;
+	for (size_t i = 0; i < ARRAY_LEN(Lexers); ++i) {
+		if (!Lexers[i].linePatt) continue;
+		int error = regcomp(
+			&regex, Lexers[i].linePatt, REG_EXTENDED | REG_NOSUB
+		);
+		assert(!error);
+		error = regexec(&regex, line, 0, NULL, 0);
+		regfree(&regex);
+		if (!error) {
+			ungets(line, file);
+			return Lexers[i].lexer;
+		}
+	}
+	ungets(line, file);
+	return NULL;
+}
+
+#define ENUM_OPTION \
+	X(Document, "document") \
+	X(Inline, "inline") \
+	X(Monospace, "monospace") \
+	X(Pre, "pre") \
+	X(Style, "style") \
+	X(Tab, "tab") \
+	X(Title, "title")
+
+enum Option {
+#define X(option, key) option,
+	ENUM_OPTION
+#undef X
+	OptionCap,
+};
+
+typedef void Header(const char *opts[]);
+typedef void Output(const char *opts[], enum Class class, const char *text);
+
+static bool pager;
+static void ansiHeader(const char *opts[]) {
+	(void)opts;
+	if (!pager) return;
+	const char *shell = getenv("SHELL");
+	const char *pager = getenv("PAGER");
+	if (!shell) shell = "/bin/sh";
+	if (!pager) pager = "less";
+	setenv("LESS", "FRX", 0);
+
+	int rw[2];
+	int error = pipe(rw);
+	if (error) err(EX_OSERR, "pipe");
+
+	pid_t pid = fork();
+	if (pid < 0) err(EX_OSERR, "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);
+	}
+	dup2(rw[1], STDOUT_FILENO);
+	close(rw[0]);
+	close(rw[1]);
+	setlinebuf(stdout);
+}
+
+static void ansiFooter(const char *opts[]) {
+	(void)opts;
+	if (!pager) return;
+	int status;
+	fclose(stdout);
+	wait(&status);
+}
+
+static const char *SGR[ClassCap] = {
+	[Keyword] = "37",
+	[Macro]   = "32",
+	[Comment] = "34",
+	[String]  = "36",
+	[Format]  = "36;1;96",
+	[Subst]   = "33",
+};
+
+static void ansiFormat(const char *opts[], enum Class class, const char *text) {
+	(void)opts;
+	if (!SGR[class]) {
+		printf("%s", text);
+		return;
+	}
+	// Set color on each line for piping to less -R:
+	for (const char *nl; (nl = strchr(text, '\n')); text = &nl[1]) {
+		printf("\33[%sm%.*s\33[m\n", SGR[class], (int)(nl - text), text);
+	}
+	if (*text) printf("\33[%sm%s\33[m", SGR[class], text);
+}
+
+static void
+debugFormat(const char *opts[], enum Class class, const char *text) {
+	if (class != Normal) {
+		printf("%s(", Class[class]);
+		ansiFormat(opts, class, text);
+		printf(")");
+	} else {
+		printf("%s", text);
+	}
+}
+
+static const char *IRC[ClassCap] = {
+	[Keyword] = "\00315",
+	[Macro]   = "\0033",
+	[Comment] = "\0032",
+	[String]  = "\00310",
+	[Format]  = "\00311",
+	[Subst]   = "\0037",
+};
+
+static void ircHeader(const char *opts[]) {
+	if (opts[Monospace]) printf("\21");
+}
+
+static const char *stop(const char *text) {
+	return (*text == ',' || isdigit(*text) ? "\2\2" : "");
+}
+
+static void ircFormat(const char *opts[], enum Class class, const char *text) {
+	for (const char *nl; (nl = strchr(text, '\n')); text = &nl[1]) {
+		if (IRC[class]) printf("%s%s", IRC[class], stop(text));
+		printf("%.*s\n", (int)(nl - text), text);
+		if (opts[Monospace]) printf("\21");
+	}
+	if (*text) {
+		if (IRC[class]) {
+			printf("%s%s%s\17", IRC[class], stop(text), text);
+			if (opts[Monospace]) printf("\21");
+		} else {
+			printf("%s", text);
+		}
+	}
+}
+
+static void htmlEscape(const char *text) {
+	while (*text) {
+		switch (*text) {
+			break; case '"': text++; printf("&quot;");
+			break; case '&': text++; printf("&amp;");
+			break; case '<': text++; printf("&lt;");
+		}
+		size_t len = strcspn(text, "\"&<");
+		if (len) fwrite(text, len, 1, stdout);
+		text += len;
+	}
+}
+
+static const char *Styles[ClassCap] = {
+	[Keyword] = "color: dimgray;",
+	[Macro]   = "color: green;",
+	[Comment] = "color: navy;",
+	[String]  = "color: teal;",
+	[Format]  = "color: teal; font-weight: bold;",
+	[Subst]   = "color: olive;",
+};
+
+static void styleTabSize(const char *tab) {
+	printf("-moz-tab-size: ");
+	htmlEscape(tab);
+	printf("; tab-size: ");
+	htmlEscape(tab);
+	printf(";");
+}
+
+static void htmlHeader(const char *opts[]) {
+	if (!opts[Document]) goto body;
+
+	printf("<!DOCTYPE html>\n<title>");
+	if (opts[Title]) htmlEscape(opts[Title]);
+	printf("</title>\n");
+
+	if (opts[Style]) {
+		printf("<link rel=\"stylesheet\" href=\"");
+		htmlEscape(opts[Style]);
+		printf("\">\n");
+	} else if (!opts[Inline]) {
+		printf("<style>\n");
+		if (opts[Tab]) {
+			printf("pre.hilex { ");
+			styleTabSize(opts[Tab]);
+			printf(" }\n");
+		}
+		for (enum Class class = 0; class < ClassCap; ++class) {
+			if (!Styles[class]) continue;
+			printf("pre.hilex .%.2s { %s }\n", Class[class], Styles[class]);
+		}
+		printf("</style>\n");
+	}
+
+body:
+	if ((opts[Document] || opts[Pre]) && opts[Inline] && opts[Tab]) {
+		printf("<pre class=\"hilex\" style=\"");
+		styleTabSize(opts[Tab]);
+		printf("\">");
+	} else if (opts[Document] || opts[Pre]) {
+		printf("<pre class=\"hilex\">");
+	}
+}
+
+static void htmlFooter(const char *opts[]) {
+	if (opts[Document] || opts[Pre]) printf("</pre>");
+	if (opts[Document]) printf("\n");
+}
+
+static void htmlFormat(const char *opts[], enum Class class, const char *text) {
+	if (class != Normal) {
+		if (opts[Inline]) {
+			printf("<span style=\"%s\">", Styles[class] ? Styles[class] : "");
+		} else {
+			printf("<span class=\"%.2s\">", Class[class]);
+		}
+		htmlEscape(text);
+		printf("</span>");
+	} else {
+		htmlEscape(text);
+	}
+}
+
+static const struct Formatter {
+	const char *name;
+	Header *header;
+	Output *format;
+	Header *footer;
+} Formatters[] = {
+	{ "ansi", ansiHeader, ansiFormat, ansiFooter },
+	{ "debug", NULL, debugFormat, NULL },
+	{ "html", htmlHeader, htmlFormat, htmlFooter },
+	{ "irc", ircHeader, ircFormat, NULL },
+};
+
+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);
+}
+
+static char *const OptionKeys[OptionCap + 1] = {
+#define X(option, key) [option] = key,
+	ENUM_OPTION
+#undef X
+	NULL,
+};
+
+int main(int argc, char *argv[]) {
+	bool text = false;
+	const char *name = NULL;
+	const struct Lexer *lexer = NULL;
+	const struct Formatter *formatter = &Formatters[0];
+	const char *opts[OptionCap] = {0};
+
+	for (int opt; 0 < (opt = getopt(argc, argv, "f:l:n:o:t"));) {
+		switch (opt) {
+			break; case 'f': formatter = parseFormatter(optarg);
+			break; case 'l': lexer = parseLexer(optarg);
+			break; case 'n': name = optarg;
+			break; case 'o': {
+				while (*optarg) {
+					char *val;
+					int key = getsubopt(&optarg, OptionKeys, &val);
+					if (key < 0) errx(EX_USAGE, "no such option %s", val);
+					opts[key] = (val ? val : "");
+				}
+			}
+			break; case 't': text = true;
+			break; default:  return EX_USAGE;
+		}
+	}
+
+	const char *path = "(stdin)";
+	FILE *file = stdin;
+	if (optind < argc) {
+		path = argv[optind];
+		file = fopen(path, "r");
+		if (!file) err(EX_NOINPUT, "%s", path);
+		pager = isatty(STDOUT_FILENO);
+	}
+
+	if (!name) {
+		if (NULL != (name = strrchr(path, '/'))) {
+			name++;
+		} else {
+			name = path;
+		}
+	}
+	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);
+
+	*lexer->in = file;
+	if (formatter->header) formatter->header(opts);
+	for (enum Class class; None != (class = lexer->lex());) {
+		assert(class < ClassCap);
+		formatter->format(opts, class, *lexer->text);
+	}
+	if (formatter->footer) formatter->footer(opts);
+}
diff --git a/bin/hilex.h b/bin/hilex.h
new file mode 100644
index 00000000..882b5f95
--- /dev/null
+++ b/bin/hilex.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2020  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 <stdio.h>
+
+#define ENUM_CLASS \
+	X(None) \
+	X(Normal) \
+	X(Operator) \
+	X(Number) \
+	X(Keyword) \
+	X(Ident) \
+	X(Macro) \
+	X(Comment) \
+	X(String) \
+	X(Escape) \
+	X(Format) \
+	X(Subst)
+
+enum Class {
+#define X(class) class,
+	ENUM_CLASS
+#undef X
+	ClassCap,
+};
+
+typedef int Lex(void);
+struct Lexer {
+	Lex *lex;
+	FILE **in;
+	char **text;
+};
+
+extern const struct Lexer LexC;
+extern const struct Lexer LexMake;
+extern const struct Lexer LexMdoc;
+extern const struct Lexer LexSh;
diff --git a/bin/htagml.c b/bin/htagml.c
new file mode 100644
index 00000000..eb5128d1
--- /dev/null
+++ b/bin/htagml.c
@@ -0,0 +1,207 @@
+/* Copyright (C) 2021  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 <ctype.h>
+#include <err.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static char *nomagic(const char *pattern) {
+	char *buf = malloc(2 * strlen(pattern) + 1);
+	if (!buf) err(EX_OSERR, "malloc");
+	char *ptr = buf;
+	for (const char *ch = pattern; *ch; ++ch) {
+		if (strchr(".[*", *ch)) *ptr++ = '\\';
+		*ptr++ = *ch;
+	}
+	*ptr = '\0';
+	return buf;
+}
+
+static size_t escape(bool esc, const char *ptr, size_t len) {
+	if (!esc) {
+		fwrite(ptr, len, 1, stdout);
+		return len;
+	}
+	for (size_t i = 0; i < len; ++i) {
+		switch (ptr[i]) {
+			break; case '&': printf("&amp;");
+			break; case '<': printf("&lt;");
+			break; case '"': printf("&quot;");
+			break; default:  putchar(ptr[i]);
+		}
+	}
+	return len;
+}
+
+static void id(const char *tag) {
+	for (const char *ch = tag; *ch; ++ch) {
+		if (isalnum(*ch) || strchr("-._", *ch)) {
+			putchar(*ch);
+		} else {
+			putchar('_');
+		}
+	}
+}
+
+static char *hstrstr(const char *haystack, const char *needle) {
+	while (haystack) {
+		char *elem = strchr(haystack, '<');
+		char *match = strstr(haystack, needle);
+		if (!match) return NULL;
+		if (!elem || match < elem) return match;
+		haystack = strchr(elem, '>');
+	}
+	return NULL;
+}
+
+int main(int argc, char *argv[]) {
+	bool pre = false;
+	bool pipe = false;
+	bool index = false;
+	const char *tagsFile = "tags";
+	for (int opt; 0 < (opt = getopt(argc, argv, "f:ipx"));) {
+		switch (opt) {
+			break; case 'f': tagsFile = optarg;
+			break; case 'i': pipe = true;
+			break; case 'p': pre = true;
+			break; case 'x': index = true;
+			break; default:  return EX_USAGE;
+		}
+	}
+	if (optind == argc) errx(EX_USAGE, "name required");
+	const char *name = argv[optind];
+
+	FILE *file = fopen(tagsFile, "r");
+	if (!file) err(EX_NOINPUT, "%s", tagsFile);
+
+	size_t len = 0;
+	size_t cap = 256;
+	struct Tag {
+		char *tag;
+		int num;
+		regex_t regex;
+	} *tags = malloc(cap * sizeof(*tags));
+	if (!tags) err(EX_OSERR, "malloc");
+
+	char *buf = NULL;
+	size_t bufCap = 0;
+	while (0 < getline(&buf, &bufCap, file)) {
+		char *line = buf;
+		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 (strcmp(file, name)) continue;
+		if (len == cap) {
+			tags = realloc(tags, (cap *= 2) * sizeof(*tags));
+			if (!tags) err(EX_OSERR, "realloc");
+		}
+		tags[len].tag = strdup(tag);
+		if (!tags[len].tag) err(EX_OSERR, "strdup");
+
+		tags[len].num = 0;
+		if (def[0] == '/' || def[0] == '?') {
+			def++;
+			def[strlen(def)-1] = '\0';
+			char *search = nomagic(def);
+			int error = regcomp(
+				&tags[len].regex, search, REG_NEWLINE | REG_NOSUB
+			);
+			free(search);
+			if (error) {
+				warnx("invalid regex for tag %s: %s", tag, def);
+				continue;
+			}
+		} else {
+			tags[len].num = strtol(def, &def, 10);
+			if (*def) {
+				warnx("invalid line number for tag %s: %s", tag, def);
+				continue;
+			}
+		}
+		len++;
+	}
+	fclose(file);
+
+	file = fopen(name, "r");
+	if (!file) err(EX_NOINPUT, "%s", name);
+
+	int num = 0;
+	printf(pre ? "<pre>" : index ? "<ul class=\"index\">\n" : "");
+	while (0 < getline(&buf, &bufCap, file) && ++num) {
+		struct Tag *tag = NULL;
+		for (size_t i = 0; i < len; ++i) {
+			if (tags[i].num) {
+				if (num != tags[i].num) continue;
+			} else {
+				if (regexec(&tags[i].regex, buf, 0, NULL, 0)) continue;
+			}
+			tag = &tags[i];
+			tag->num = num;
+			break;
+		}
+		if (index) {
+			if (!tag) continue;
+			printf("<li><a class=\"tag\" href=\"#");
+			id(tag->tag);
+			printf("\">");
+			escape(true, tag->tag, strlen(tag->tag));
+			printf("</a></li>\n");
+			continue;
+		}
+		if (pipe) {
+			ssize_t len = getline(&buf, &bufCap, stdin);
+			if (len < 0) {
+				errx(EX_DATAERR, "missing line %d on standard input", num);
+			}
+		}
+		if (!tag) {
+			escape(!pipe, buf, strlen(buf));
+			continue;
+		}
+
+		size_t mlen = strlen(tag->tag);
+		char *match = (pipe ? hstrstr : strstr)(buf, tag->tag);
+		while (match > buf && isalnum(match[-1])) {
+			match = (pipe ? hstrstr : strstr)(&match[mlen], tag->tag);
+		}
+		if (!match && tag->tag[0] == 'M') {
+			mlen = 4;
+			match = (pipe ? hstrstr : strstr)(buf, "main");
+		}
+		if (!match) {
+			mlen = strlen(buf) - 1;
+			match = buf;
+		}
+		escape(!pipe, buf, match - buf);
+		printf("<a class=\"tag\" id=\"");
+		id(tag->tag);
+		printf("\" href=\"#");
+		id(tag->tag);
+		printf("\">");
+		match += escape(!pipe, match, mlen);
+		printf("</a>");
+		escape(!pipe, match, strlen(match));
+	}
+	printf(pre ? "</pre>" : index ? "</ul>\n" : "");
+}
diff --git a/bin/html.mk b/bin/html.mk
new file mode 100644
index 00000000..15e5257a
--- /dev/null
+++ b/bin/html.mk
@@ -0,0 +1,45 @@
+WEBROOT ?= /usr/local/www/causal.agency
+
+HTMLS = index.html png.html
+HTMLS += ${BINS:=.html}
+HTMLS += ${BSD:=.html}
+HTMLS += ${GAMES:=.html}
+HTMLS += ${LINUX:=.html}
+HTMLS += ${TLS:=.html}
+
+html: ${HTMLS}
+	@true
+
+install-html: ${HTMLS}
+	install -d ${WEBROOT}/bin
+	install -C -m 644 ${HTMLS} ${WEBROOT}/bin
+
+${HTMLS}: html.sh scheme hilex htagml htmltags
+
+htmltags: *.[chly] mtags Makefile html.mk *.sh
+	rm -f $@
+	for f in *.[chly]; do ctags -aw -f $@ $$f; done
+	./mtags -a -f $@ Makefile html.mk *.sh
+
+index.html: README.7 Makefile html.mk html.sh
+	sh html.sh README.7 Makefile html.mk html.sh > $@
+
+.SUFFIXES: .html
+
+.c.html:
+	sh html.sh man1/${<:.c=.1} $< > $@
+
+.h.html:
+	sh html.sh man3/${<:.h=.3} $< > $@
+
+.y.html:
+	sh html.sh man1/${<:.y=.1} $< > $@
+
+.sh.html:
+	sh html.sh man1/${<:.sh=.1} $< > $@
+
+.pl.html:
+	sh html.sh man1/${<:.pl=.1} $< > $@
+
+freecell.html: freecell.c man6/freecell.6
+	sh html.sh man6/freecell.6 freecell.c > $@
diff --git a/bin/html.sh b/bin/html.sh
new file mode 100644
index 00000000..d76fcc8c
--- /dev/null
+++ b/bin/html.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+set -eu
+
+readonly GitURL='https://git.causal.agency/src/tree/bin'
+
+man=$1
+shift
+title=${man##*/}
+title=${title%.[1-9]}
+
+cat <<EOF
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>${title}</title>
+<style>
+html { line-height: 1.25em; font-family: monospace; }
+body { max-width: 80ch; margin: 1em auto; padding: 0 1ch; }
+
+table.head, table.foot { width: 100%; }
+td.head-rtitle, td.foot-os { text-align: right; }
+td.head-vol { text-align: center; }
+div.Pp { margin: 1ex 0ex; }
+div.Nd, div.Bf, div.Op { display: inline; }
+span.Pa, span.Ad { font-style: italic; }
+span.Ms { font-weight: bold; }
+dl.Bl-diag > dt { font-weight: bold; }
+code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn,
+code.Cd { font-weight: bold; font-family: inherit; }
+
+table { border-collapse: collapse; }
+table.Nm code.Nm { padding-right: 1ch; }
+table.foot { margin-top: 1em; }
+
+ul.index { padding: 0; }
+ul.index li { display: inline; list-style-type: none; }
+pre { -moz-tab-size: 4; tab-size: 4; }
+
+$(./scheme -st)
+html { background-color: var(--ansi16); color: var(--ansi17); }
+a { color: var(--ansi4); }
+a:visited { color: var(--ansi5); }
+a.permalink, a.tag { color: var(--ansi3); text-decoration: none; }
+a.permalink > code:target, *:target > a.permalink,
+a.tag:target { color: var(--ansi11); }
+pre .Ke { color: var(--ansi7); }
+pre .Ma { color: var(--ansi2); }
+pre .Co { color: var(--ansi4); }
+pre .St { color: var(--ansi6); }
+pre .Fo { color: var(--ansi14); }
+pre .Su { color: var(--ansi1); }
+</style>
+EOF
+
+opts=fragment
+[ "${man}" = "README.7" ] && opts=${opts},man=%N.html
+mandoc -T html -O ${opts} "${man}"
+
+for src; do
+	cat <<-EOF
+	<p>
+	<a href="${GitURL}/${src}">${src} in git</a>
+	EOF
+	./htagml -x -f htmltags "${src}"
+	./hilex -t -f html "${src}" | ./htagml -ip -f htmltags "${src}"
+done
diff --git a/bin/make.l b/bin/make.l
new file mode 100644
index 00000000..027fefa3
--- /dev/null
+++ b/bin/make.l
@@ -0,0 +1,132 @@
+/* Copyright (C) 2020  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/>.
+ */
+
+%option prefix="make"
+%option noyywrap
+
+%{
+#include "hilex.h"
+%}
+
+%s Assign Preproc
+%x Variable Shell
+
+ident [._[:alnum:]]+
+assign [+?:!]?=
+target [-._/?*\[\][:alnum:]]+
+operator [:!]|::
+
+%%
+	static int pop = INITIAL;
+	static int depth = 0;
+
+^"\t"+ {
+	BEGIN(pop = Shell);
+	return Normal;
+}
+<Shell>{
+	"\n" {
+		BEGIN(pop = INITIAL);
+		return Normal;
+	}
+	"\\\n" { return Normal; }
+	[^\\\n$]+|. { return Normal; }
+}
+
+[[:blank:]]+ { return Normal; }
+
+{operator} { return Operator; }
+
+"."(PHONY|PRECIOUS|SUFFIXES)/{operator}? {
+	return Keyword;
+}
+
+{target}/{operator} { return Ident; }
+
+^"."{ident} |
+^"-"?include {
+	BEGIN(pop = Preproc);
+	return Macro;
+}
+<Preproc>{
+	"\n" {
+		BEGIN(pop = INITIAL);
+		return Normal;
+	}
+	"\\\n""\t"? { return Normal; }
+
+	"\""[^""]*"\"" |
+	"<"[^>]*">" {
+		return String;
+	}
+
+	[!<>=]"="?|"||"|"&&" { return Operator; }
+	[0-9]+|"0x"[[:xdigit:]]+ { return Number; }
+	defined|make|empty|exists|target|commands|in { return Keyword; }
+}
+
+^{ident}/[[:blank:]]*{assign} {
+	return Ident;
+}
+
+{assign} {
+	BEGIN(pop = Assign);
+	return Operator;
+}
+<Assign>{
+	"\n" {
+		BEGIN(pop = INITIAL);
+		return Normal;
+	}
+	"\\\n""\t"? { return Escape; }
+	[^\\$[:space:]]+|. { return String; }
+}
+
+{target} { return Ident; }
+
+"#"([^\\\n]|"\\"[^\n]|"\\\n")* { return Comment; }
+
+<*>{
+	"$"("{"|"(")/[^$] {
+		depth++;
+		BEGIN(Variable);
+		yymore();
+	}
+	"$"("{"|"(") {
+		depth++;
+		BEGIN(Variable);
+		return Subst;
+	}
+	"$". { return Subst; }
+}
+<Variable>{
+	[^${}()]*"}"|")" {
+		if (!--depth) BEGIN(pop);
+		return Subst;
+	}
+	[^${}()]+ { return Subst; }
+}
+
+.|\n { return Normal; }
+
+%{
+	(void)yyunput;
+	(void)input;
+%}
+
+%%
+
+const struct Lexer LexMake = { yylex, &yyin, &yytext };
diff --git a/bin/man1/beef.1 b/bin/man1/beef.1
new file mode 100644
index 00000000..ea52cfa0
--- /dev/null
+++ b/bin/man1/beef.1
@@ -0,0 +1,91 @@
+.Dd August 28, 2019
+.Dt BEEF 1
+.Os
+.
+.Sh NAME
+.Nm beef
+.Nd Befunge-93 interpreter
+.
+.Sh SYNOPSIS
+.Nm
+.Op Ar file
+.
+.Sh DESCRIPTION
+.Nm
+is a Befunge-93 interpreter.
+If no
+.Ar file
+is provided,
+the program is read from standard input.
+.
+.Ss Befunge-93 Command Summary
+.Bl -tag -width "0-9" -compact
+.It \(dq
+toggle string mode
+.It 0-9
+push value
+.It +
+add
+.It -
+subtract
+.It *
+multiply
+.It /
+divide
+.It %
+modulo
+.It !
+not
+.It `
+greater than
+.It >
+right
+.It <
+left
+.It ^
+up
+.It v
+down
+.It ?
+random
+.It _
+horizontal (left) if
+.It |
+vertical (up) if
+.It :
+duplicate
+.It \e
+swap
+.It $
+drop
+.It .
+output integer
+.It ,
+output ASCII
+.It #
+bridge
+.It g
+get (y, x)
+.It p
+put (y, x) = v
+.It &
+input integer
+.It ~
+input ASCII
+.It @
+exit
+.El
+.
+.Sh EXIT STATUS
+.Nm
+exits with the top value left on the stack,
+or 0 if the stack is left empty.
+.
+.Sh STANDARDS
+.Rs
+.%A Chris Pressey
+.%Q Cat's Eye Technologies
+.%T Befunge-93
+.%D September, 1993
+.%U https://github.com/catseye/Befunge-93/blob/master/doc/Befunge-93.markdown
+.Re
diff --git a/bin/man1/bibsort.1 b/bin/man1/bibsort.1
new file mode 100644
index 00000000..07ed91ef
--- /dev/null
+++ b/bin/man1/bibsort.1
@@ -0,0 +1,40 @@
+.Dd February 16, 2021
+.Dt BIBSORT 1
+.Os
+.
+.Sh NAME
+.Nm bibsort
+.Nd reformat bibliography
+.
+.Sh SYNOPSIS
+.Nm
+.Op Ar file
+.
+.Sh DESCRIPTION
+.Nm
+reformats on standard output
+the
+.Em STANDARDS
+section of the
+.Xr mdoc 7
+manual page
+.Ar file
+or standard input.
+Bibliographic references
+are sorted by author last names,
+and formatted in an item list
+with macro lines appearing
+in the order they are formatted by
+.Xr mandoc 1 .
+Additionally,
+.Ic \&%N
+macros referencing RFC numbers
+are rewritten to
+.Ic \&%R
+macros
+and missing
+.Ic \&%U
+macros are added for RFCs.
+.
+.Sh EXAMPLES
+.Dl :%!bibsort
diff --git a/bin/man1/bit.1 b/bin/man1/bit.1
new file mode 100644
index 00000000..b91a10e1
--- /dev/null
+++ b/bin/man1/bit.1
@@ -0,0 +1,55 @@
+.Dd December 30, 2020
+.Dt BIT 1
+.Os
+.
+.Sh NAME
+.Nm bit
+.Nd a calculator
+.
+.Sh SYNOPSIS
+.Nm
+.
+.Sh DESCRIPTION
+.Nm
+is an integer calculator.
+Its syntax resembles that of C expressions,
+with the following changes:
+.
+.Bl -bullet
+.It
+Underscores are allowed in integer literals.
+.It
+The
+.Sy 0b
+prefix is used for binary literals.
+.It
+The
+.Sy ->
+operator is used for arithmetic shift.
+.It
+The unary
+.Sy &
+operator is equivalent to
+.Sy (1 << x) - 1 .
+.It
+The postfix operators
+.Sy K ,
+.Sy M ,
+.Sy G ,
+.Sy T
+are used as constant multipliers.
+.It
+The postfix operator
+.Sy $
+is of lowest precedence and is equivalent to
+wrapping the preceding expression in parentheses.
+.It
+Single-letter (lower case) variables
+can be assigned.
+The variable
+.Sy _
+stores the previous result.
+.El
+.
+.Sh SEE ALSO
+.Xr operator 7
diff --git a/bin/man1/bri.1 b/bin/man1/bri.1
new file mode 100644
index 00000000..54a02322
--- /dev/null
+++ b/bin/man1/bri.1
@@ -0,0 +1,44 @@
+.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/c.1 b/bin/man1/c.1
new file mode 100644
index 00000000..97384ebe
--- /dev/null
+++ b/bin/man1/c.1
@@ -0,0 +1,45 @@
+.Dd January  9, 2021
+.Dt C 1
+.Os
+.
+.Sh NAME
+.Nm c
+.Nd run C
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl t
+.Op Fl e Ar expr
+.Op Fl i Ar include
+.Op Ar stmts ...
+.
+.Sh DESCRIPTION
+The
+.Nm
+utility compiles and runs
+C statements wrapped in
+.Fn main
+with common includes.
+If no
+.Ar expr
+or
+.Ar stmts
+are provided,
+statements are read from standard input.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl e Ar expr
+Print the result of the C expression
+.Ar expr
+after executing
+.Ar stmts .
+.It Fl i Ar include
+Add the include file
+.Ar include .
+.It Fl t
+With
+.Fl e ,
+print the type of the expression.
+.El
diff --git a/bin/man1/dtch.1 b/bin/man1/dtch.1
new file mode 100644
index 00000000..e27713e1
--- /dev/null
+++ b/bin/man1/dtch.1
@@ -0,0 +1,67 @@
+.Dd August 12, 2019
+.Dt DTCH 1
+.Os
+.
+.Sh NAME
+.Nm dtch
+.Nd detached sessions
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl s
+.Ar name
+.Op Ar command ...
+.Nm
+.Fl a
+.Ar name
+.
+.Sh DESCRIPTION
+.Nm
+spawns a
+.Ar command
+in a detachable session.
+If no
+.Ar command
+is given,
+the value of
+.Ev SHELL
+is used.
+The
+.Nm
+process
+should be run as a background job
+or with
+.Xr nohup 1 .
+.
+.Pp
+To attach to an existing session,
+pass the
+.Fl a
+flag.
+To detach from the session,
+type
+.Ic ^Q .
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Attach to an existing session.
+.It Fl s
+Sink the output of
+.Ar command
+while detached.
+.El
+.
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa ~/.dtch
+Directory of UNIX-domain sockets
+for each session.
+.El
+.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+dtch foo vim &
+dtch -a foo
+.Ed
diff --git a/bin/man1/ever.1 b/bin/man1/ever.1
new file mode 100644
index 00000000..8cdab99b
--- /dev/null
+++ b/bin/man1/ever.1
@@ -0,0 +1,51 @@
+.Dd February 24, 2021
+.Dt EVER 1
+.Os
+.
+.Sh NAME
+.Nm ever
+.Nd watch files
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl iq
+.Ar
+.Ar command
+.Nm
+.Op Fl i
+.Ar
+.Fl -
+.Ar command
+.Op Ar argument ...
+.
+.Sh DESCRIPTION
+.Nm
+executes the
+.Ar command
+whenever
+.Ar file
+is modified.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl i
+Attach the
+.Ar file
+which was modified
+to the standard input of
+.Ar command .
+.It Fl q
+Suppress exit status output.
+.El
+.
+.Sh EXAMPLES
+.Dl ever ever.c make
+.Dl ever when.y ever.c -- make when ever
+.Dl ever -i ever.1 mandoc
+.
+.Sh CAVEATS
+.Nm
+does not support Linux
+since it uses
+.Xr kqueue 2 .
diff --git a/bin/man1/fbatt.1 b/bin/man1/fbatt.1
new file mode 100644
index 00000000..2d30cba7
--- /dev/null
+++ b/bin/man1/fbatt.1
@@ -0,0 +1,34 @@
+.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
new file mode 100644
index 00000000..3195eb42
--- /dev/null
+++ b/bin/man1/fbclock.1
@@ -0,0 +1,36 @@
+.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/glitch.1 b/bin/man1/glitch.1
new file mode 100644
index 00000000..6562c4dc
--- /dev/null
+++ b/bin/man1/glitch.1
@@ -0,0 +1,77 @@
+.Dd September 7, 2018
+.Dt GLITCH 1
+.Os
+.
+.Sh NAME
+.Nm glitch
+.Nd PNG glitcher
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl cfimprxy
+.Op Fl a Ar filters
+.Op Fl d Ar filters
+.Op Fl o Ar file
+.Op Ar
+.
+.Sh DESCRIPTION
+.Nm
+misinterprets PNG files
+according to the options given
+to create natural glitch effects.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl a Ar filters
+Apply a pattern of comma-separated filters.
+Filters are
+.Cm none ,
+.Cm sub ,
+.Cm up ,
+.Cm average ,
+.Cm paeth .
+.
+.It Fl c
+Write to standard output.
+.
+.It Fl d Ar filters
+Declare a pattern of comma-separated filters.
+See
+.Fl a
+for list of filters.
+.
+.It Fl f
+Apply filtering in place of reconstruction.
+.
+.It Fl i
+Invert image data after filtering.
+.
+.It Fl m
+Mirror scanlines after filtering.
+.
+.It Fl o Ar file
+Write to
+.Ar file .
+.
+.It Fl p
+Use a broken Paeth predictor function.
+.
+.It Fl r
+Apply reconstruction in place of filtering.
+.
+.It Fl x
+Zero first pixel of each scanline after filtering.
+.
+.It Fl y
+Zero first scanline after filtering.
+.El
+.
+.Sh EXAMPLES
+.Dl glitch -m -a sub -d sub
+.
+.Sh SEE ALSO
+.Xr pngo 1
+.
+.Sh BUGS
+More wanted.
diff --git a/bin/man1/hilex.1 b/bin/man1/hilex.1
new file mode 100644
index 00000000..80b3155b
--- /dev/null
+++ b/bin/man1/hilex.1
@@ -0,0 +1,218 @@
+.Dd January 20, 2021
+.Dt HILEX 1
+.Os
+.
+.Sh NAME
+.Nm hilex
+.Nd syntax highlighter
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl t
+.Op Fl f Ar format
+.Op Fl l Ar lexer
+.Op Fl n Ar name
+.Op Fl o Ar opts
+.Op Ar file
+.
+.Sh DESCRIPTION
+The
+.Nm
+utility
+syntax highlights
+the contents of
+.Ar file
+or standard input
+and formats it on standard output.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width "-f format"
+.It Fl f Ar format
+Set the output format.
+See
+.Sx Output Formats .
+The default format is
+.Cm ansi .
+.
+.It Fl l Ar lexer
+Set the input lexer.
+See
+.Sx Input Lexers .
+The default input lexer is inferred from
+.Ar name
+or the first line of input.
+.
+.It Fl n Ar name
+Set the name used to infer the input lexer.
+The default is the final component of
+.Ar file .
+.
+.It Fl o Ar opts
+Set output format options.
+.Ar opts
+is a comma-separated list of options.
+Options for each output format are documented in
+.Sx Output Formats .
+.
+.It Fl t
+Default to the
+.Cm text
+input lexer if one cannot be inferred.
+.El
+.
+.Ss Output Formats
+.Bl -tag -width Ds
+.It Cm ansi
+Output ANSI terminal control sequences.
+If standard output is a terminal
+and standard input is not being read,
+output is piped to
+.Ev PAGER
+with
+.Ev LESS=FRX
+if it is not already set.
+.
+.It Cm html
+Output HTML
+.Sy span
+elements
+with the following classes:
+.Pp
+.Bl -hang -width "\&Op" -compact
+.It Sy \&Op
+operators
+.It Sy \&Nu
+numbers
+.It Sy \&Ke
+keywords
+.It Sy \&Id
+identifiers
+.It Sy \&Ma
+macros
+.It Sy \&Co
+comments
+.It Sy \&St
+strings
+.It Sy \&Es
+character escapes
+.It Sy \&Fo
+format strings
+.It Sy \&Su
+variable substitutions
+.El
+.Pp
+The options are as follows:
+.Bl -tag -width "title=..."
+.It Cm document
+Output an HTML document containing a
+.Sy pre
+element.
+.It Cm inline
+Output inline style attributes
+rather than classes.
+.It Cm pre
+Wrap the output in a
+.Sy pre
+element with the class
+.Sy hilex .
+.It Cm style Ns = Ns Ar url
+With
+.Cm document ,
+use the external stylesheet
+.Ar url .
+If unset,
+default styles are included in a
+.Sy style
+element.
+.It Cm tab Ns = Ns Ar n
+With
+.Cm document ,
+.Cm inline
+or
+.Cm pre ,
+set the
+.Sy tab-size
+property to
+.Ar n .
+.It Cm title Ns = Ns Ar ...
+With
+.Cm document ,
+set the
+.Sy title
+element text.
+The default title is the same as
+.Ar name .
+.El
+.
+.It Cm irc
+Output IRC formatting codes.
+The options are as follows:
+.Bl -tag -width "monospace"
+.It Cm monospace
+Use the IRCCloud monospace formatting code.
+.El
+.El
+.
+.Ss Input Lexers
+.Bl -tag -width Ds
+.It Cm c
+The C11 language,
+with minimal support for
+.Xr lex 1 ,
+.Xr yacc 1
+and Objective-C input.
+Inferred for
+.Pa *.[chlmy]
+files.
+.
+.It Cm make
+BSD
+.Xr make 1 .
+Inferred for
+.Pa Makefile ,
+.Pa *.mk
+and
+.Pa *.am
+files.
+.
+.It Cm mdoc
+The
+.Xr mdoc 7
+language.
+Inferred for
+.Pa *.[1-9]
+files
+and files starting with
+.Dq .Dd .
+.
+.It Cm sh
+POSIX
+.Xr sh 1 .
+Since lexical analysis of
+the shell command language
+is effectively impossible,
+this is best-effort only.
+Inferred for
+.Pa *.sh ,
+.Pa .profile ,
+.Pa .shrc
+files
+and files starting with
+.Dq #!/bin/sh .
+.
+.It Cm text
+Plain text.
+Inferred for
+.Pa *.txt
+files.
+.El
+.
+.Sh ENVIRONMENT
+.Bl -tag -width "PAGER"
+.It Ev PAGER
+The command to pipe ANSI output to
+if standard output is a terminal.
+The default is
+.Ev PAGER=less .
+.El
diff --git a/bin/man1/htagml.1 b/bin/man1/htagml.1
new file mode 100644
index 00000000..97e8f3f2
--- /dev/null
+++ b/bin/man1/htagml.1
@@ -0,0 +1,69 @@
+.Dd January 12, 2021
+.Dt HTAGML 1
+.Os
+.
+.Sh NAME
+.Nm htagml
+.Nd format tagged file as HTML
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl ip | x
+.Op Fl f Ar tagsfile
+.Ar file
+.
+.Sh DESCRIPTION
+The
+.Nm
+utility formats a file tagged with
+.Xr ctags 1
+as HTML.
+Tags are output as fragment hyperlinks
+with the class
+.Qq tag .
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl f Ar tagsfile
+Read the tag descriptions from a file called
+.Ar tagsfile .
+The default behavior is
+to read them from a file called
+.Pa tags .
+.It Fl i
+Assume
+.Ar file
+has been pre-formatted
+on standard input,
+such as by a syntax highlighter.
+Only tag hyperlinks are added.
+.It Fl p
+Wrap the output in a
+.Sy pre
+element.
+.It Fl x
+Instead produce an index of tags
+ordered by their occurrence in
+.Ar file .
+The index is formatted as a
+.Sy ul
+element with the class
+.Qq index .
+.El
+.
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa tags
+default input tags file
+.El
+.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+ctags htagml.c && htagml htagml.c
+hilex -f html htagml.c | htagml -i htagml.c
+.Ed
+.
+.Sh SEE ALSO
+.Xr ctags 1 ,
+.Xr hilex 1
diff --git a/bin/man1/modem.1 b/bin/man1/modem.1
new file mode 100644
index 00000000..a4bbc3f1
--- /dev/null
+++ b/bin/man1/modem.1
@@ -0,0 +1,31 @@
+.Dd December  8, 2020
+.Dt MODEM 1
+.Os
+.
+.Sh NAME
+.Nm modem
+.Nd fixed baud rate wrapper
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl r Ar rate
+.Ar command ...
+.
+.Sh DESCRIPTION
+.Nm
+runs the
+.Ar command
+in a new PTY
+with a fixed baud rate.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl r Ar rate
+Set the baud rate.
+The default is 19200.
+.El
+.
+.Sh BUGS
+Window size changes are not propagated
+to the child PTY.
diff --git a/bin/man1/mtags.1 b/bin/man1/mtags.1
new file mode 100644
index 00000000..57856ba0
--- /dev/null
+++ b/bin/man1/mtags.1
@@ -0,0 +1,76 @@
+.Dd January 20, 2021
+.Dt MTAGS 1
+.Os
+.
+.Sh NAME
+.Nm mtags
+.Nd miscellaneous tags
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl f Ar tagsfile
+.Ar
+.
+.Sh DESCRIPTION
+The
+.Nm
+utility
+makes a
+.Pa tags
+file for
+.Xr ex 1
+from the specified
+.Xr make 1 ,
+.Xr mdoc 7
+.Xr sh 1
+sources.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Append to
+.Pa tags
+file.
+.It Fl f Ar tagsfile
+Place the tag descriptions
+in a file called
+.Ar tagsfile .
+The default behaviour is
+to place them in a file called
+.Pa tags .
+.El
+.
+.Pp
+Files whose names are
+.Pa Makefile
+or end in
+.Pa .mk
+are assumed to be
+.Xr make 1
+files.
+Files whose names end in
+.Pa .[1-9]
+are assumed to be
+.Xr mdoc 7
+files.
+Files whose names are
+.Pa .profile ,
+.Pa .shrc
+or end in
+.Pa .sh
+are assumed to be
+.Xr sh 1
+files.
+.
+.Sh FILES
+.Bl -tag -width Ds
+.It Pa tags
+default output tags file
+.El
+.
+.Sh SEE ALSO
+.Xr ctags 1 ,
+.Xr ex 1 ,
+.Xr vi 1
diff --git a/bin/man1/nudge.1 b/bin/man1/nudge.1
new file mode 100644
index 00000000..3ca4294a
--- /dev/null
+++ b/bin/man1/nudge.1
@@ -0,0 +1,44 @@
+.Dd September  4, 2020
+.Dt NUDGE 1
+.Os
+.
+.Sh NAME
+.Nm nudge
+.Nd terminal vibrator
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar file
+.Op Fl n Ar count
+.Op Fl s Ar shake
+.Op Fl t Ar delay
+.
+.Sh DESCRIPTION
+The
+.Nm
+utility
+nudges the terminal.
+An
+.Xr xterm 1
+compatible terminal is required.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl f Ar file
+Open the terminal named by
+.Ar file .
+The default is
+.Pa /dev/tty .
+.It Fl n Ar count
+Set the number of times
+to nudge the terminal.
+The default is 2.
+.It Fl s Ar shake
+Set the shake intensity in pixels.
+The default is 10.
+.It Fl t Ar delay
+Set the interval between shakes
+in milliseconds.
+The default is 20.
+.El
diff --git a/bin/man1/order.1 b/bin/man1/order.1
new file mode 100644
index 00000000..89fcbda5
--- /dev/null
+++ b/bin/man1/order.1
@@ -0,0 +1,38 @@
+.Dd July 18, 2020
+.Dt ORDER 1
+.Os
+.
+.Sh NAME
+.Nm order
+.Nd operator precedence
+.
+.Sh SYNOPSIS
+.Nm
+.Op Ar expr ...
+.
+.Sh DESCRIPTION
+.Nm
+parses C expressions
+and prints them with parentheses
+according to the precedence rules in
+.Xr operator 7 .
+If no
+.Ar expr
+are given,
+an expression is read
+from standard input.
+.
+.Sh EXAMPLES
+.Bd -literal
+$ order 'a & b << 1'
+(a & (b << 1))
+.Ed
+.
+.Sh SEE ALSO
+.Xr operator 7
+.
+.Sh CAVEATS
+.Nm
+does not support the
+.Sy (type)
+operator.
diff --git a/bin/man1/pbd.1 b/bin/man1/pbd.1
new file mode 100644
index 00000000..f0665891
--- /dev/null
+++ b/bin/man1/pbd.1
@@ -0,0 +1,66 @@
+.Dd February  9, 2021
+.Dt PBD 1
+.Os
+.
+.Sh NAME
+.Nm pbd
+.Nd macOS pasteboard daemon
+.
+.Sh SYNOPSIS
+.Nm Op Fl s | c | p | o Ar url
+.
+.Sh DESCRIPTION
+.Nm
+is a daemon which pipes into macOS
+.Xr pbcopy 1 ,
+from
+.Xr pbpaste 1
+and invokes
+.Xr open 1
+in response to messages
+sent over TCP port 7062.
+.
+.Pp
+The socket can be forwarded through
+.Xr ssh 1
+and the flags can be used remotely
+to access the local pasteboard
+and open URLs.
+.
+.Pp
+Forwarding can be configured with:
+.Pp
+.Dl RemoteForward 7062 127.0.0.1:7062
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Behave as
+.Xr pbcopy 1 .
+.It Fl o Ar url
+Behave as
+.Xr open 1 .
+.It Fl p
+Behave as
+.Xr pbpaste 1 .
+.It Fl s
+Run the server.
+This is the default.
+.El
+.Pp
+ACAB.
+.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+pbd &
+ssh -R 7062:127.0.0.1:7062 tux.local
+pbd -p
+.Ed
+.
+.Sh SEE ALSO
+.Xr open 1 ,
+.Xr pbcopy 1 ,
+.Xr pbpaste 1 ,
+.Xr ssh 1 ,
+.Xr ssh_config 5
diff --git a/bin/man1/pngo.1 b/bin/man1/pngo.1
new file mode 100644
index 00000000..cec13160
--- /dev/null
+++ b/bin/man1/pngo.1
@@ -0,0 +1,56 @@
+.Dd September 17, 2018
+.Dt PNGO 1
+.Os
+.
+.Sh NAME
+.Nm pngo
+.Nd PNG optimizer
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl cv
+.Op Fl o Ar file
+.Op Ar
+.
+.Sh DESCRIPTION
+.Nm
+optimizes PNG files for size.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Write to standard output.
+.It Fl o Ar file
+Write to
+.Ar file .
+.It Fl v
+Output PNG header information.
+.El
+.
+.Pp
+.Nm
+performs the following optimizations:
+.Bl -bullet
+.It
+Discard ancillary chunks.
+.It
+Discard unnecessary alpha channel.
+.It
+Convert unnecessary truecolor to grayscale.
+.It
+Palletize color and alpha if possible.
+.It
+Reduce bit depth if possible.
+.It
+Apply a simple filter heuristic.
+.It
+Apply zlib's best compresion.
+.El
+.
+.Sh SEE ALSO
+.Xr glitch 1
+.
+.Sh CAVEATS
+.Nm
+does not support interlaced PNGs.
diff --git a/bin/man1/psf2png.1 b/bin/man1/psf2png.1
new file mode 100644
index 00000000..db74c6e2
--- /dev/null
+++ b/bin/man1/psf2png.1
@@ -0,0 +1,53 @@
+.Dd September 28, 2018
+.Dt PSF2PNG 1
+.Os
+.
+.Sh NAME
+.Nm psf2png
+.Nd PSF2 to PNG renderer
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl b Ar bg
+.Op Fl c Ar cols
+.Op Fl f Ar fg
+.Op Fl s Ar str
+.Op Ar file
+.
+.Sh DESCRIPTION
+.Nm
+renders the PSF2 font
+.Ar file
+or standard input
+to PNG
+on standard output.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl b Ar bg
+Use
+.Ar bg
+(hexadecimal RGB)
+as background color.
+The default background color is black.
+.It Fl c Ar cols
+Arrange glyphs in
+.Ar cols
+columns.
+The default number of columns is 32.
+.It Fl f Ar fg
+Use
+.Ar fg
+(hexadecimal RGB)
+as foreground color.
+The default foreground color is white.
+.It Fl s Ar str
+Render glyphs for string
+.Ar str
+rather than all glyphs.
+.El
+.
+.Sh SEE ALSO
+.Xr pngo 1 ,
+.Xr psfed 1
diff --git a/bin/man1/psfed.1 b/bin/man1/psfed.1
new file mode 100644
index 00000000..3fbc4710
--- /dev/null
+++ b/bin/man1/psfed.1
@@ -0,0 +1,166 @@
+.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/man1/ptee.1 b/bin/man1/ptee.1
new file mode 100644
index 00000000..04f9cdac
--- /dev/null
+++ b/bin/man1/ptee.1
@@ -0,0 +1,40 @@
+.Dd July 17, 2019
+.Dt PTEE 1
+.Os
+.
+.Sh NAME
+.Nm ptee
+.Nd tee for PTYs
+.
+.Sh SYNOPSIS
+.Nm
+.Ar command ...
+.Cm >
+.Ar file
+.
+.Sh DESCRIPTION
+.Nm
+runs
+.Ar command
+in a new PTY
+which is mirrored to
+the current PTY
+and standard output.
+Standard output must be redirected
+to a file or pipe.
+.
+.Pp
+Type
+.Ic ^S
+to toggle writing to standard output.
+Type
+.Ic ^Q
+to write the media copy sequence for
+.Xr shotty 1 .
+.
+.Sh SEE ALSO
+.Xr tee 1
+.
+.Sh BUGS
+Window size changes are not propagated
+to the child PTY.
diff --git a/bin/man1/relay.1 b/bin/man1/relay.1
new file mode 100644
index 00000000..402c4726
--- /dev/null
+++ b/bin/man1/relay.1
@@ -0,0 +1,48 @@
+.Dd April 28, 2019
+.Dt RELAY 1
+.Os
+.
+.Sh NAME
+.Nm relay
+.Nd IRC relay bot
+.
+.Sh SYNOPSIS
+.Nm
+.Ar host
+.Ar port
+.Ar nick
+.Ar chan
+.
+.Sh DESCRIPTION
+.Nm
+is one half of an IRC relay pair.
+It connects to
+.Ar host Ns : Ns Ar port
+over TLS
+as
+.Ar nick
+and joins
+.Ar chan .
+.
+.Pp
+.Nm
+outputs messages from
+.Ar chan
+to standard output
+and sends messages to
+.Ar chan
+from standard input.
+Two
+.Nm
+processes can be connected with
+.Xr mkfifo 1 .
+.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+mkfifo a b
+relay a.example.com 6697 relay '#example' <>a >b
+relay b.example.com 6697 relay '#example' <>b >a
+.Ed
+.
+.Sh SEE ALSO
+.Xr mkfifo 1
diff --git a/bin/man1/scheme.1 b/bin/man1/scheme.1
new file mode 100644
index 00000000..9f72d945
--- /dev/null
+++ b/bin/man1/scheme.1
@@ -0,0 +1,59 @@
+.Dd February  6, 2021
+.Dt SCHEME 1
+.Os
+.
+.Sh NAME
+.Nm scheme
+.Nd color scheme
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl Xacghilmstx
+.Op Fl p Ar n
+.
+.Sh DESCRIPTION
+.Nm
+generates a color scheme
+and outputs it in a number of formats.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl X
+Output Xresources for
+.Xr xterm 1 .
+.It Fl a
+Generate the 16 ANSI colors.
+This is the default.
+.It Fl c
+Output a C enum.
+.It Fl g
+Output a swatch PNG.
+.It Fl h
+Output floating point HSV.
+.It Fl i
+Swap black and white.
+.It Fl l
+Output Linux console OSC sequences.
+.It Fl m
+Output a
+.Xr mintty 1
+theme.
+Use with
+.Fl t .
+.It Fl p Ar n
+Generate only the color
+.Ar n .
+.It Fl s
+Output CSS
+for classes named
+.Sy fg Ns Ar n
+and
+.Sy bg Ns Ar n .
+.It Fl t
+Generate the 16 ANSI colors as well as
+background, foreground, bold, selection and cursor colors.
+.It Fl x
+Output hexadecimal RGB.
+This is the default.
+.El
diff --git a/bin/man1/shotty.1 b/bin/man1/shotty.1
new file mode 100644
index 00000000..d5eaa780
--- /dev/null
+++ b/bin/man1/shotty.1
@@ -0,0 +1,92 @@
+.Dd November 25, 2019
+.Dt SHOTTY 1
+.Os
+.
+.Sh NAME
+.Nm shotty
+.Nd terminal capture
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl Bdns
+.Op Fl b Ar bg
+.Op Fl f Ar fg
+.Op Fl h Ar rows
+.Op Fl w Ar cols
+.Op Ar file
+.
+.Sh DESCRIPTION
+.Nm
+interprets terminal output from
+.Ar file
+or standard input
+and produces HTML
+.Sy <pre>
+on standard output.
+.
+.Pp
+Terminal output
+can be captured with
+.Xr ptee 1 .
+.Nm
+targets compatibility with
+.Ev TERM Ns = Ns Cm xterm
+and
+.Ev TERM Ns = Ns Cm xterm-256color
+as used by
+.Xr ncurses 3 .
+A snapshot of the terminal
+is output each time
+a media copy sequence occurs,
+or once at the end of the capture.
+.
+.Pp
+HTML output uses the classes
+.Sy bg Ns Va n
+and
+.Sy fg Ns Va n ,
+and inline styles for
+bold, italic and underline.
+CSS for colors can be generated with
+.Xr scheme 1 .
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width "-w cols"
+.It Fl B
+Replace bold with bright colors.
+.
+.It Fl b Ar bg
+Set the default background color.
+The default value is 0 (black).
+.
+.It Fl d
+Output the terminal state
+following each control sequence.
+.
+.It Fl f Ar fg
+Set the default foreground color.
+The default value is 7 (white).
+.
+.It Fl h Ar rows
+Set the terminal height.
+The default value is 24.
+.
+.It Fl n
+Do not show the cursor.
+.
+.It Fl s
+Set the terminal size
+from the current terminal size.
+.
+.It Fl w Ar cols
+Set the terminal width.
+The default value is 80.
+.El
+.
+.Sh EXAMPLES
+.Dl ptee htop | shotty -s > htop.html
+.
+.Sh SEE ALSO
+.Xr ptee 1 ,
+.Xr scheme 1
diff --git a/bin/man1/sup.1 b/bin/man1/sup.1
new file mode 100644
index 00000000..4b266e06
--- /dev/null
+++ b/bin/man1/sup.1
@@ -0,0 +1,48 @@
+.Dd February 21, 2021
+.Dt SUP 1
+.Os
+.
+.Sh NAME
+.Nm sup
+.Nd single-use password
+.
+.Sh SYNOPSIS
+.Nm
+.Ar service
+.Op Ar email
+.
+.Sh DESCRIPTION
+The
+.Nm
+utility
+sets a random single-use password
+for a service using the
+.Dq forgot password
+or
+.Dq password reset
+flow.
+The password is copied to the clipboard
+and the service login page is opened.
+For passwordless services
+with email-based authentication,
+the emailed login link is opened.
+.
+.Pp
+The following services are supported:
+.Cm asciinema ,
+.Cm discogs ,
+.Cm freebsdbugzilla ,
+.Cm liberapay ,
+.Cm lobsters ,
+.Cm tildenews .
+.
+.Pp
+The
+.Nm
+utility requires
+.Xr curl 1 ,
+.Xr git-fetch-email 1 ,
+.Xr openssl 1 ,
+.Xr pbcopy 1
+and
+.Xr open 1 .
diff --git a/bin/man1/title.1 b/bin/man1/title.1
new file mode 100644
index 00000000..43ecc5e2
--- /dev/null
+++ b/bin/man1/title.1
@@ -0,0 +1,51 @@
+.Dd September 10, 2019
+.Dt TITLE 1
+.Os
+.
+.Sh NAME
+.Nm title
+.Nd page titles
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl x Ar pattern
+.Op Ar url
+.
+.Sh DESCRIPTION
+.Nm
+fetches HTML page titles
+over HTTP and HTTPS.
+.Nm
+scans standard input for URLs
+and writes their titles to standard output.
+If a
+.Ar url
+argument is given,
+.Nm
+exits after fetching its title.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl x Ar pattern
+Exclude URLs matching
+.Ar pattern ,
+which is a modern regular expression.
+See
+.Xr re_format 7 .
+.It Fl v
+Enable
+.Xr libcurl 3
+verbose output.
+.El
+.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+mkfifo snarf titles
+relay irc.example.org 6697 snarf '#example' <>titles >snarf
+title <snarf >titles
+.Ed
+.
+.Sh SEE ALSO
+.Xr relay 1
diff --git a/bin/man1/typer.1 b/bin/man1/typer.1
new file mode 100644
index 00000000..dd1108d1
--- /dev/null
+++ b/bin/man1/typer.1
@@ -0,0 +1,74 @@
+.Dd April 28, 2021
+.Dt TYPER 1
+.Os
+.
+.Sh NAME
+.Nm typer
+.Nd type all day
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl CPRv
+.Op Fl c Ar cert
+.Op Fl n Ar nick
+.Op Fl p Ar port
+.Op Fl u Ar user
+.Ar host
+.Ar chan
+.
+.Sh DESCRIPTION
+.Nm
+is an IRC bot
+that types all day long.
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl C
+Copy
+.Nm .
+Type whenever anyone else is typing.
+.It Fl P
+Request the
+.Sy causal.agency/passive
+vendor-specific capability.
+.It Fl R
+Reverse
+.Nm .
+Send other users' typing indicators
+as regular messages for all to see.
+.It Fl c Ar cert
+Use the TLS client certificate
+and private key loaded from
+.Ar cert .
+.It Fl n Ar nick
+Set the nickname.
+The default is
+.Nm .
+.It Fl p Ar port
+Connect to
+.Ar port .
+The default is 6697.
+.It Fl u Ar user
+Set the username.
+The default is
+.Nm .
+.It Fl v
+Log IRC protocol to standard error.
+.It Ar host
+Connect to
+.Ar host .
+.It Ar chan
+Type in the channel
+.Ar chan .
+.El
+.
+.Sh STANDARDS
+.Bl -item
+.It
+.Rs
+.%A MuffinMedic
+.%A James Wheare
+.%T IRCv3 typing client tag
+.%I IRCv3 Working Group
+.%U https://ircv3.net/specs/client-tags/typing
+.Re
+.El
diff --git a/bin/man1/up.1 b/bin/man1/up.1
new file mode 100644
index 00000000..189020de
--- /dev/null
+++ b/bin/man1/up.1
@@ -0,0 +1,80 @@
+.Dd February  7, 2021
+.Dt UP 1
+.Os
+.
+.Sh NAME
+.Nm up
+.Nd upload file
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl h
+.Op Ar file
+.
+.Nm
+.Fl c | t
+.Ar command ...
+.
+.Nm
+.Fl s
+.
+.Sh DESCRIPTION
+.Nm
+uploads a file
+to temp.causal.agency with
+.Xr scp 1 .
+If no
+.Ar file
+is provided,
+standard input is read
+and uploaded as text.
+.
+.Pp
+The destination file name
+is chosen using
+.Xr date 1
+and
+.Xr openssl 1
+.Cm rand .
+The URL of the uploaded file is printed
+and copied to the pasteboard with
+.Xr pbcopy 1
+if available.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Run a command
+to produce a text file for upload.
+.It Fl h
+Use
+.Xr hilex 1
+to produce an HTML file for upload.
+.It Fl s
+Use
+.Xr screencapture 1
+or
+.Xr scrot 1
+to produce a PNG file for upload.
+The file is optimized by
+.Xr pngo 1
+if available.
+.It Fl t
+Run a command with
+.Xr ptee 1
+and
+.Xr shotty 1
+to produce an HTML file for upload.
+.El
+.
+.Pp
+Any arguments after
+.Ql \-\-
+are passed to
+.Xr hilex 1
+and
+.Xr screencapture 1
+or
+.Xr scrot 1 ,
+respectively.
diff --git a/bin/man1/when.1 b/bin/man1/when.1
new file mode 100644
index 00000000..0b473573
--- /dev/null
+++ b/bin/man1/when.1
@@ -0,0 +1,76 @@
+.Dd July 24, 2019
+.Dt WHEN 1
+.Os
+.
+.Sh NAME
+.Nm when
+.Nd date calculator
+.
+.Sh SYNOPSIS
+.Nm
+.Op Ar expr
+.
+.Sh DESCRIPTION
+.Nm
+is a date calculator.
+If no
+.Ar expr
+is given,
+expressions are read
+from standard input.
+.
+.Pp
+The grammar is as follows:
+.Bl -tag -width Ds
+.It Sy \&.
+Today's date.
+.
+.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.
+.
+.It Ar day
+A day of the week
+in the current week.
+.Ar day
+must be at least three letters.
+.
+.It Sy < Ar date
+The date one week before.
+.
+.It Sy > Ar date
+The date one week after.
+.
+.It Ar date Sy + Ar interval
+The date after some interval.
+.
+.It Ar date Sy - Ar interval
+The date before some interval.
+.
+.It Ar date Sy - Ar date
+The interval between two dates.
+.
+.It Ar num Sy d
+A number of days.
+.
+.It Ar num Sy w
+A number of weeks.
+.
+.It Ar num Sy m
+A number of months.
+.
+.It Ar num Sy y
+A number of years.
+.El
+.
+.Sh EXAMPLES
+.Bl -tag -width "Dec 25 - ."
+.It Ic Dec 25 - \&.
+How long until Christmas.
+.It Ic >Fri
+The date next Friday.
+.It Ic \&. + 2w
+Your last day at work.
+.El
diff --git a/bin/man1/xx.1 b/bin/man1/xx.1
new file mode 100644
index 00000000..d38789a7
--- /dev/null
+++ b/bin/man1/xx.1
@@ -0,0 +1,68 @@
+.Dd September 7, 2018
+.Dt XX 1
+.Os
+.
+.Sh NAME
+.Nm xx
+.Nd hexdump
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl arsz
+.Op Fl c Ar cols
+.Op Fl g Ar group
+.Op Fl p Ar count
+.Op Ar file
+.
+.Sh DESCRIPTION
+.Nm
+dumps the contents of a
+.Ar file
+or standard input
+in hexadecimal format.
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Toggle ASCII output.
+.
+.It Fl c Ar cols
+Output
+.Ar cols
+bytes per line.
+The default
+.Ar cols
+is 16.
+.
+.It Fl g Ar group
+Output extra space after every
+.Ar group
+bytes.
+The default
+.Ar group
+is 8.
+.
+.It Fl p Ar count
+Output a blank line after every
+.Ar count
+bytes.
+.Ar count
+must be a multiple of
+.Ar cols .
+.
+.It Fl r
+Reverse hexdump.
+Read hexadecimal input
+and write byte output.
+.
+.It Fl s
+Toggle offset output.
+.
+.It Fl z
+Skip output of lines containing only zeros.
+.El
+.
+.Sh SEE ALSO
+.Xr hexdump 1 ,
+.Xr xxd 1
diff --git a/bin/man3/png.3 b/bin/man3/png.3
new file mode 100644
index 00000000..accffbd7
--- /dev/null
+++ b/bin/man3/png.3
@@ -0,0 +1,90 @@
+.Dd July 25, 2019
+.Dt PNG 3
+.Os
+.
+.Sh NAME
+.Nm png
+.Nd basic PNG output
+.
+.Sh SYNOPSIS
+.In png.h
+.
+.Ft void
+.Fo pngHead
+.Fa "FILE *file"
+.Fa "uint32_t width"
+.Fa "uint32_t height"
+.Fa "uint8_t depth"
+.Fa "uint8_t color"
+.Fc
+.
+.Ft void
+.Fn pngPalette "FILE *file" "const uint8_t *pal" "uint32_t len"
+.
+.Ft void
+.Fn pngData "FILE *file" "const uint8_t *data" "uint32_t len"
+.
+.Ft void
+.Fn pngTail "FILE *file"
+.
+.Sh DESCRIPTION
+The
+.Fn pngHead
+function
+writes the
+.Sy IHDR
+chunk to
+.Fa file .
+The
+.Fa color
+parameter can be one of
+.Dv PNGGrayscale ,
+.Dv PNGTruecolor
+optionally
+.Em or Ns 'ed
+with
+.Dv PNGAlpha ,
+or
+.Dv PNGIndexed .
+.
+.Pp
+The
+.Fn pngPalette
+function
+writes the
+.Sy PLTE
+chunk to
+.Fa file .
+.
+.Pp
+The
+.Fn pngData
+function
+writes the
+.Sy IDAT
+chunk to
+.Fa file
+without compression.
+The constants
+.Dv PNGNone ,
+.Dv PNGSub ,
+.Dv PNGUp ,
+.Dv PNGAverage ,
+.Dv PNGPaeth
+are defined
+for use in PNG data.
+.
+.Pp
+The
+.Fn pngTail
+function
+writes the
+.Sy IEND
+chunk to
+.Fa file .
+.
+.Sh ERRORS
+Any errors from writing to
+.Fa file
+are handled by calling
+.Xr err 3 .
diff --git a/bin/man6/freecell.6 b/bin/man6/freecell.6
new file mode 100644
index 00000000..0e485a16
--- /dev/null
+++ b/bin/man6/freecell.6
@@ -0,0 +1,50 @@
+.Dd April 17, 2021
+.Dt FREECELL 6
+.Os
+.
+.Sh NAME
+.Nm freecell
+.Nd patience game
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar delay
+.Op Fl n Ar game
+.
+.Sh DESCRIPTION
+.Nm
+is a terminal FreeCell patience game.
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl d Ar delay
+Set the delay in milliseconds
+between queued moves.
+The default is 50.
+.It Fl n Ar game
+Select the game number to play.
+.El
+.
+.Pp
+Moves are performed
+by typing a sequence of two keys.
+To automatically move a card
+to a free cell,
+type the same key twice.
+The keys are as follows:
+.Bl -tag -width Ds
+.It Ic Escape
+Cancel a pending move.
+.It Ic u , Backspace
+Undo the previous move.
+.It Ic 1 , 2 , 3 , 4
+Select the cells.
+.It Ic q , w , e , r , a , s , d , f
+Select the tableau cascades.
+.It Ic _ , Space
+Manually move
+the selected card
+to the foundations.
+.It Ic Shift
+Move a single card
+to an empty tableau cascade.
+.El
diff --git a/bin/mdoc.l b/bin/mdoc.l
new file mode 100644
index 00000000..a31b6a2e
--- /dev/null
+++ b/bin/mdoc.l
@@ -0,0 +1,65 @@
+/* Copyright (C) 2020  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/>.
+ */
+
+%option prefix="mdoc"
+%option noyywrap
+
+%{
+#include "hilex.h"
+%}
+
+%s MacroLine
+
+%%
+
+[[:blank:]]+ { return Normal; }
+
+^"." {
+	BEGIN(MacroLine);
+	return Keyword;
+}
+
+^".\\\"".* { return Comment; }
+
+<MacroLine>{
+	"\n" {
+		BEGIN(0);
+		return Normal;
+	}
+
+	%[ABCDIJNOPQRTUV]|A[cdnopqrt]|B[cdfkloqtx]|Br[coq]|Bsx|C[dm]|D[1bcdloqtvx] |
+	E[cdfklmnorsvx]|F[acdlnortx]|Hf|I[cnt]|L[bikp]|M[st]|N[dmosx]|O[copstx] |
+	P[acfopq]|Q[cloq]|R[esv]|S[chmoqstxy]|T[an]|U[dx]|V[at]|X[cor] {
+		return Keyword;
+	}
+
+	"\""([^""]|"\\\"")*"\"" { return String; }
+}
+
+"\\"(.|"("..|"["[^]]*"]") { return String; }
+
+[^.\\""[:space:]]+ { return Normal; }
+
+.|\n { return Normal; }
+
+%{
+	(void)yyunput;
+	(void)input;
+%}
+
+%%
+
+const struct Lexer LexMdoc = { yylex, &yyin, &yytext };
diff --git a/bin/modem.c b/bin/modem.c
new file mode 100644
index 00000000..2133ae08
--- /dev/null
+++ b/bin/modem.c
@@ -0,0 +1,102 @@
+/* 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 <poll.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sysexits.h>
+#include <termios.h>
+#include <unistd.h>
+
+#if defined __FreeBSD__
+#include <libutil.h>
+#elif defined __linux__
+#include <pty.h>
+#else
+#include <util.h>
+#endif
+
+typedef unsigned uint;
+typedef unsigned char byte;
+
+static struct termios saveTerm;
+static void restoreTerm(void) {
+	tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm);
+}
+
+int main(int argc, char *argv[]) {
+	int error;
+
+	uint baudRate = 19200;
+	for (int opt; 0 < (opt = getopt(argc, argv, "r:"));) {
+		switch (opt) {
+			break; case 'r': baudRate = strtoul(optarg, NULL, 10);
+			break; default:  return EX_USAGE;
+		}
+	}
+	if (argc - optind < 1) return EX_USAGE;
+
+	error = tcgetattr(STDIN_FILENO, &saveTerm);
+	if (error) err(EX_IOERR, "tcgetattr");
+	atexit(restoreTerm);
+
+	struct termios raw = saveTerm;
+	cfmakeraw(&raw);
+	error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw);
+	if (error) err(EX_IOERR, "tcsetattr");
+
+	struct winsize window;
+	error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
+	if (error) err(EX_IOERR, "TIOCGWINSZ");
+
+	int pty;
+	pid_t pid = forkpty(&pty, NULL, NULL, &window);
+	if (pid < 0) err(EX_OSERR, "forkpty");
+
+	if (!pid) {
+		execvp(argv[optind], &argv[optind]);
+		err(EX_NOINPUT, "%s", argv[optind]);
+	}
+
+	byte c;
+	struct pollfd fds[2] = {
+		{ .events = POLLIN, .fd = STDIN_FILENO },
+		{ .events = POLLIN, .fd = pty },
+	};
+	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);
+			size = write(pty, &c, 1);
+			if (size < 0) err(EX_IOERR, "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) break;
+			size = write(STDOUT_FILENO, &c, 1);
+			if (size < 0) err(EX_IOERR, "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;
+}
diff --git a/bin/mtags.c b/bin/mtags.c
new file mode 100644
index 00000000..1ebfbac5
--- /dev/null
+++ b/bin/mtags.c
@@ -0,0 +1,99 @@
+/* Copyright (C) 2021  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 <regex.h>
+#include <stdbool.h>
+#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) {
+	for (size_t i = 0; i < len; ++i) {
+		if (str[i] == '\\' || str[i] == '/') {
+			putc('\\', file);
+		}
+		putc(str[i], file);
+	}
+}
+
+int main(int argc, char *argv[]) {
+	bool append = false;
+	const char *path = "tags";
+	for (int opt; 0 < (opt = getopt(argc, argv, "af:"));) {
+		switch (opt) {
+			break; case 'a': append = true;
+			break; case 'f': path = optarg;
+			break; default:  return EX_USAGE;
+		}
+	}
+
+	FILE *tags = fopen(path, (append ? "a" : "w"));
+	if (!tags) err(EX_CANTCREAT, "%s", path);
+
+	regex_t makeFile, makeLine;
+	regex_t mdocFile, mdocLine;
+	regex_t shFile, shLine;
+	int error = 0
+		|| regcomp(&makeFile, "(^|/)Makefile|[.]mk$", REG_EXTENDED | REG_NOSUB)
+		|| regcomp(
+			&makeLine,
+			"^([.][^:$A-Z][^:$[:space:]]*|[^.:$][^:$[:space:]]*):",
+			REG_EXTENDED
+		)
+		|| regcomp(&mdocFile, "[.][1-9]$", REG_EXTENDED | REG_NOSUB)
+		|| regcomp(&mdocLine, "^[.]S[hs] ([^\t\n]+)", REG_EXTENDED)
+		|| regcomp(
+			&shFile, "(^|/)[.](profile|shrc)|[.]sh$", REG_EXTENDED | REG_NOSUB
+		)
+		|| regcomp(&shLine, "^([_[:alnum:]]+)[[:blank:]]*[(][)]", REG_EXTENDED);
+	assert(!error);
+
+	size_t cap = 0;
+	char *buf = NULL;
+	for (int i = optind; i < argc; ++i) {
+		const regex_t *regex;
+		if (!regexec(&makeFile, argv[i], 0, NULL, 0)) {
+			regex = &makeLine;
+		} else if (!regexec(&mdocFile, argv[i], 0, NULL, 0)) {
+			regex = &mdocLine;
+		} else if (!regexec(&shFile, argv[i], 0, NULL, 0)) {
+			regex = &shLine;
+		} else {
+			warnx("skipping unknown file type %s", argv[i]);
+			continue;
+		}
+
+		FILE *file = fopen(argv[i], "r");
+		if (!file) err(EX_NOINPUT, "%s", argv[i]);
+
+		while (0 < getline(&buf, &cap, file)) {
+			regmatch_t match[2];
+			if (regexec(regex, buf, 2, match, 0)) continue;
+			fprintf(
+				tags, "%.*s\t%s\t/^",
+				(int)(match[1].rm_eo - match[1].rm_so), &buf[match[1].rm_so],
+				argv[i]
+			);
+			escape(tags, buf, match[0].rm_eo);
+			fprintf(tags, "/\n");
+		}
+		fclose(file);
+	}
+}
diff --git a/bin/nudge.c b/bin/nudge.c
new file mode 100644
index 00000000..108d2459
--- /dev/null
+++ b/bin/nudge.c
@@ -0,0 +1,78 @@
+/* Copyright (C) 2020  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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <termios.h>
+#include <unistd.h>
+
+static int shake = 10;
+static int delay = 20000;
+static int count = 2;
+
+static void move(int tty, int x, int y) {
+	dprintf(tty, "\33[3;%d;%dt", x, y);
+	usleep(delay);
+}
+
+int main(int argc, char *argv[]) {
+	const char *path = "/dev/tty";
+	for (int opt; 0 < (opt = getopt(argc, argv, "f:n:s:t:"));) {
+		switch (opt) {
+			break; case 'f': path = optarg;
+			break; case 'n': count = atoi(optarg);
+			break; case 's': shake = atoi(optarg);
+			break; case 't': delay = atoi(optarg) * 1000;
+			break; default:  return EX_USAGE;
+		}
+	}
+
+	int tty = open(path, O_RDWR);
+	if (tty < 0) err(EX_OSFILE, "%s", path);
+
+	struct termios save;
+	int error = tcgetattr(tty, &save);
+	if (error) err(EX_IOERR, "tcgetattr");
+
+	struct termios raw = save;
+	cfmakeraw(&raw);
+	error = tcsetattr(tty, TCSAFLUSH, &raw);
+	if (error) err(EX_IOERR, "tcsetattr");
+
+	char buf[256];
+	dprintf(tty, "\33[13t");
+	ssize_t len = read(tty, buf, sizeof(buf) - 1);
+	buf[(len < 0 ? 0 : len)] = '\0';
+
+	int x, y;
+	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;
+
+	dprintf(tty, "\33[5t");
+	for (int i = 0; i < count; ++i) {
+		move(tty, x - shake, y);
+		move(tty, x, y + shake);
+		move(tty, x + shake, y);
+		move(tty, x, y - shake);
+		move(tty, x, y);
+	}
+}
diff --git a/bin/order.y b/bin/order.y
new file mode 100644
index 00000000..c2e87971
--- /dev/null
+++ b/bin/order.y
@@ -0,0 +1,195 @@
+/* Copyright (C) 2019  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 <ctype.h>
+#include <err.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#define YYSTYPE char *
+
+static char *fmt(const char *format, ...) {
+	char *str = NULL;
+	va_list ap;
+	va_start(ap, format);
+	vasprintf(&str, format, ap);
+	va_end(ap);
+	if (!str) err(EX_OSERR, "vasprintf");
+	return str;
+}
+
+static int yylex(void);
+static void yyerror(const char *str);
+
+%}
+
+%token Ident
+
+%left ','
+%right '=' MulAss DivAss ModAss AddAss SubAss ShlAss ShrAss AndAss XorAss OrAss
+%right '?' ':'
+%left Or
+%left And
+%left '|'
+%left '^'
+%left '&'
+%left Eq Ne
+%left '<' Le '>' Ge
+%left Shl Shr
+%left '+' '-'
+%left '*' '/' '%'
+%right '!' '~' Inc Dec Sizeof
+%left '(' ')' '[' ']' Arr '.'
+
+%%
+
+start:
+	expr { printf("%s\n", $1); }
+	;
+
+expr:
+	Ident
+	| '(' expr ')' { $$ = $2; }
+	| expr '[' expr ']' { $$ = fmt("(%s[%s])", $1, $3); }
+	| expr Arr Ident { $$ = fmt("(%s->%s)", $1, $3); }
+	| expr '.' Ident { $$ = fmt("(%s.%s)", $1, $3); }
+	| '!' expr { $$ = fmt("(!%s)", $2); }
+	| '~' expr { $$ = fmt("(~%s)", $2); }
+	| Inc expr { $$ = fmt("(++%s)", $2); }
+	| Dec expr { $$ = fmt("(--%s)", $2); }
+	| expr Inc { $$ = fmt("(%s++)", $1); }
+	| expr Dec { $$ = fmt("(%s--)", $1); }
+	| '+' expr %prec '!' { $$ = fmt("(+%s)", $2); }
+	| '-' expr %prec '!' { $$ = fmt("(-%s)", $2); }
+	| '*' expr %prec '!' { $$ = fmt("(*%s)", $2); }
+	| '&' expr %prec '!' { $$ = fmt("(&%s)", $2); }
+	| Sizeof expr { $$ = fmt("(sizeof %s)", $2); }
+	| expr '*' expr { $$ = fmt("(%s * %s)", $1, $3); }
+	| expr '/' expr { $$ = fmt("(%s / %s)", $1, $3); }
+	| expr '%' expr { $$ = fmt("(%s %% %s)", $1, $3); }
+	| expr '+' expr { $$ = fmt("(%s + %s)", $1, $3); }
+	| expr '-' expr { $$ = fmt("(%s - %s)", $1, $3); }
+	| expr Shl expr { $$ = fmt("(%s << %s)", $1, $3); }
+	| expr Shr expr { $$ = fmt("(%s >> %s)", $1, $3); }
+	| expr '<' expr { $$ = fmt("(%s < %s)", $1, $3); }
+	| expr Le expr { $$ = fmt("(%s <= %s)", $1, $3); }
+	| expr '>' expr { $$ = fmt("(%s > %s)", $1, $3); }
+	| expr Ge expr { $$ = fmt("(%s >= %s)", $1, $3); }
+	| expr Eq expr { $$ = fmt("(%s == %s)", $1, $3); }
+	| expr Ne expr { $$ = fmt("(%s != %s)", $1, $3); }
+	| expr '&' expr { $$ = fmt("(%s & %s)", $1, $3); }
+	| expr '^' expr { $$ = fmt("(%s ^ %s)", $1, $3); }
+	| expr '|' expr { $$ = fmt("(%s | %s)", $1, $3); }
+	| expr And expr { $$ = fmt("(%s && %s)", $1, $3); }
+	| expr Or expr { $$ = fmt("(%s || %s)", $1, $3); }
+	| expr '?' expr ':' expr { $$ = fmt("(%s ? %s : %s)", $1, $3, $5); }
+	| expr ass expr %prec '=' { $$ = fmt("(%s %s %s)", $1, $2, $3); }
+	| expr ',' expr { $$ = fmt("(%s, %s)", $1, $3); }
+	;
+
+ass:
+	'=' { $$ = "="; }
+	| MulAss { $$ = "*="; }
+	| DivAss { $$ = "/="; }
+	| ModAss { $$ = "%="; }
+	| AddAss { $$ = "+="; }
+	| SubAss { $$ = "-="; }
+	| ShlAss { $$ = "<<="; }
+	| ShrAss { $$ = ">>="; }
+	| AndAss { $$ = "&="; }
+	| XorAss { $$ = "^="; }
+	| OrAss { $$ = "|="; }
+	;
+
+%%
+
+#define T(a, b) ((int)(a) << 8 | (int)(b))
+
+static FILE *in;
+
+static int yylex(void) {
+	char ch;
+	while (isspace(ch = getc(in)));
+
+	if (isalnum(ch)) {
+		char ident[64] = { ch, '\0' };
+		for (size_t i = 1; i < sizeof(ident) - 1; ++i) {
+			ch = getc(in);
+			if (!isalnum(ch) && ch != '_') break;
+			ident[i] = ch;
+		}
+		ungetc(ch, in);
+		if (!strcmp(ident, "sizeof")) return Sizeof;
+		yylval = fmt("%s", ident);
+		return Ident;
+	}
+
+	char ne = getc(in);
+	switch (T(ch, ne)) {
+		case T('-', '>'): return Arr;
+		case T('+', '+'): return Inc;
+		case T('-', '-'): return Dec;
+		case T('<', '='): return Le;
+		case T('>', '='): return Ge;
+		case T('=', '='): return Eq;
+		case T('!', '='): return Ne;
+		case T('&', '&'): return And;
+		case T('|', '|'): return Or;
+		case T('*', '='): return MulAss;
+		case T('/', '='): return DivAss;
+		case T('%', '='): return ModAss;
+		case T('+', '='): return AddAss;
+		case T('-', '='): return SubAss;
+		case T('&', '='): return AndAss;
+		case T('^', '='): return XorAss;
+		case T('|', '='): return OrAss;
+		case T('<', '<'): {
+			if ('=' == (ne = getc(in))) return ShlAss;
+			ungetc(ne, in);
+			return Shl;
+		}
+		case T('>', '>'): {
+			if ('=' == (ne = getc(in))) return ShrAss;
+			ungetc(ne, in);
+			return Shr;
+		}
+		default: {
+			ungetc(ne, in);
+			return ch;
+		}
+	}
+}
+
+static void yyerror(const char *str) {
+	errx(EX_DATAERR, "%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");
+		yyparse();
+		fclose(in);
+	}
+	if (argc > 1) return EX_OK;
+	in = stdin;
+	yyparse();
+}
diff --git a/bin/pbd.c b/bin/pbd.c
new file mode 100644
index 00000000..2fd401ae
--- /dev/null
+++ b/bin/pbd.c
@@ -0,0 +1,151 @@
+/* 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 <arpa/inet.h>
+#include <err.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#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) {
+		int status;
+		pid_t dead = waitpid(pid, &status, 0);
+		if (dead < 0) err(EX_OSERR, "waitpid(%d)", pid);
+		if (status) warnx("%s: status %d", cmd, status);
+
+	} else {
+		int fd = dup2(src, dest);
+		if (fd < 0) err(EX_OSERR, "dup2");
+
+		execlp(cmd, cmd, arg, NULL);
+		err(EX_UNAVAILABLE, "%s", cmd);
+	}
+}
+
+static int pbd(void) {
+	int error;
+
+	int server = socket(PF_INET, SOCK_STREAM, 0);
+	if (server < 0) err(EX_OSERR, "socket");
+
+	error = fcntl(server, F_SETFD, FD_CLOEXEC);
+	if (error) err(EX_IOERR, "fcntl");
+
+	struct sockaddr_in addr = {
+		.sin_family = AF_INET,
+		.sin_port = htons(7062),
+		.sin_addr = { .s_addr = htonl(0x7F000001) },
+	};
+	error = bind(server, (struct sockaddr *)&addr, sizeof(addr));
+	if (error) err(EX_UNAVAILABLE, "bind");
+
+	error = listen(server, 0);
+	if (error) err(EX_UNAVAILABLE, "listen");
+
+	for (;;) {
+		int client = accept(server, NULL, NULL);
+		if (client < 0) err(EX_IOERR, "accept");
+
+		error = fcntl(client, F_SETFD, FD_CLOEXEC);
+		if (error) err(EX_IOERR, "fcntl");
+
+		char c = 0;
+		ssize_t size = read(client, &c, 1);
+		if (size < 0) warn("read");
+
+		switch (c) {
+			break; case 'p': spawn("pbpaste", NULL, STDOUT_FILENO, client);
+			break; case 'c': spawn("pbcopy", NULL, STDIN_FILENO, client);
+			break; case 'o': spawn("xargs", "open", STDIN_FILENO, client);
+		}
+
+		close(client);
+	}
+}
+
+static int pbdClient(char c) {
+	int client = socket(PF_INET, SOCK_STREAM, 0);
+	if (client < 0) err(EX_OSERR, "socket");
+
+	struct sockaddr_in addr = {
+		.sin_family = AF_INET,
+		.sin_port = htons(7062),
+		.sin_addr = { .s_addr = htonl(0x7F000001) },
+	};
+	int error = connect(client, (struct sockaddr *)&addr, sizeof(addr));
+	if (error) err(EX_UNAVAILABLE, "connect");
+
+	ssize_t size = write(client, &c, 1);
+	if (size < 0) err(EX_IOERR, "write");
+
+	return client;
+}
+
+static void copy(int out, int in) {
+	byte buf[4096];
+	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 (readSize < 0) err(EX_IOERR, "read(%d)", in);
+}
+
+static int pbcopy(void) {
+	int client = pbdClient('c');
+	copy(client, STDIN_FILENO);
+	return EX_OK;
+}
+
+static int pbpaste(void) {
+	int client = pbdClient('p');
+	copy(STDOUT_FILENO, client);
+	return EX_OK;
+}
+
+static int open1(const char *url) {
+	if (!url) return EX_USAGE;
+	int client = pbdClient('o');
+	ssize_t size = write(client, url, strlen(url));
+	if (size < 0) err(EX_IOERR, "write");
+	return EX_OK;
+}
+
+int main(int argc, char *argv[]) {
+	for (int opt; 0 < (opt = getopt(argc, argv, "co:ps"));) {
+		switch (opt) {
+			case 'c': return pbcopy();
+			case 'o': return open1(optarg);
+			case 'p': return pbpaste();
+			case 's': return pbd();
+			default:  return EX_USAGE;
+		}
+	}
+	return pbd();
+}
diff --git a/bin/png.h b/bin/png.h
new file mode 100644
index 00000000..15b6d13d
--- /dev/null
+++ b/bin/png.h
@@ -0,0 +1,108 @@
+/* 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+
+static inline uint32_t pngCRCTable(uint8_t n) {
+	static uint32_t table[256];
+	if (table[1]) return table[n];
+	for (int i = 0; i < 256; ++i) {
+		table[i] = i;
+		for (int j = 0; j < 8; ++j) {
+			table[i] = (table[i] >> 1) ^ (table[i] & 1 ? 0xEDB88320 : 0);
+		}
+	}
+	return table[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");
+	for (uint32_t i = 0; i < len; ++i) {
+		pngCRC = pngCRCTable(pngCRC ^ ptr[i]) ^ (pngCRC >> 8);
+	}
+}
+static inline void pngInt32(FILE *file, uint32_t n) {
+	pngWrite(file, (uint8_t []) { n >> 24, n >> 16, n >> 8, n }, 4);
+}
+static inline void pngChunk(FILE *file, char type[static 4], uint32_t len) {
+	pngInt32(file, len);
+	pngCRC = ~0;
+	pngWrite(file, (uint8_t *)type, 4);
+}
+
+enum {
+	PNGGrayscale,
+	PNGTruecolor = 2,
+	PNGIndexed,
+	PNGAlpha,
+};
+
+static inline void pngHead(
+	FILE *file, uint32_t width, uint32_t height, uint8_t depth, uint8_t color
+) {
+	pngWrite(file, (uint8_t *)"\x89PNG\r\n\x1A\n", 8);
+	pngChunk(file, "IHDR", 13);
+	pngInt32(file, width);
+	pngInt32(file, height);
+	pngWrite(file, &depth, 1);
+	pngWrite(file, &color, 1);
+	pngWrite(file, (uint8_t []) { 0, 0, 0 }, 3);
+	pngInt32(file, ~pngCRC);
+}
+
+static inline void pngPalette(FILE *file, const uint8_t *pal, uint32_t len) {
+	pngChunk(file, "PLTE", len);
+	pngWrite(file, pal, len);
+	pngInt32(file, ~pngCRC);
+}
+
+enum {
+	PNGNone,
+	PNGSub,
+	PNGUp,
+	PNGAverage,
+	PNGPaeth,
+};
+
+static inline void pngData(FILE *file, const uint8_t *data, uint32_t len) {
+	uint32_t adler1 = 1, adler2 = 0;
+	for (uint32_t i = 0; i < len; ++i) {
+		adler1 = (adler1 + data[i]) % 65521;
+		adler2 = (adler1 + adler2) % 65521;
+	}
+	uint32_t zlen = 2 + 5 * ((len + 0xFFFE) / 0xFFFF) + len + 4;
+	pngChunk(file, "IDAT", zlen);
+	pngWrite(file, (uint8_t []) { 0x08, 0x1D }, 2);
+	for (; len > 0xFFFF; data += 0xFFFF, len -= 0xFFFF) {
+		pngWrite(file, (uint8_t []) { 0x00, 0xFF, 0xFF, 0x00, 0x00 }, 5);
+		pngWrite(file, data, 0xFFFF);
+	}
+	pngWrite(file, (uint8_t []) { 0x01, len, len >> 8, ~len, ~len >> 8 }, 5);
+	pngWrite(file, data, len);
+	pngInt32(file, adler2 << 16 | adler1);
+	pngInt32(file, ~pngCRC);
+}
+
+static inline void pngTail(FILE *file) {
+	pngChunk(file, "IEND", 0);
+	pngInt32(file, ~pngCRC);
+}
diff --git a/bin/pngo.c b/bin/pngo.c
new file mode 100644
index 00000000..322cb1ba
--- /dev/null
+++ b/bin/pngo.c
@@ -0,0 +1,812 @@
+/* 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 <arpa/inet.h>
+#include <err.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 PAIR(a, b) ((uint16_t)(a) << 8 | (uint16_t)(b))
+
+#define CRC_INIT (crc32(0, Z_NULL, 0))
+
+static bool verbose;
+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 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 const uint8_t Signature[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 writeSignature(void) {
+	writeExpect(Signature, sizeof(Signature));
+}
+
+struct PACKED Chunk {
+	uint32_t size;
+	char type[4];
+};
+
+static const char *typeStr(struct Chunk chunk) {
+	static char buf[5];
+	memcpy(buf, chunk.type, 4);
+	return buf;
+}
+
+static struct Chunk readChunk(void) {
+	struct Chunk chunk;
+	readExpect(&chunk, sizeof(chunk), "chunk");
+	chunk.size = ntohl(chunk.size);
+	crc = crc32(CRC_INIT, (Byte *)chunk.type, sizeof(chunk.type));
+	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 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 writeCrc(void) {
+	uint32_t net = htonl(crc);
+	writeExpect(&net, sizeof(net));
+}
+
+static void skipChunk(struct Chunk chunk) {
+	if (!(chunk.type[0] & 0x20)) {
+		errx(EX_CONFIG, "%s: unsupported critical chunk %s", path, typeStr(chunk));
+	}
+	uint8_t discard[4096];
+	while (chunk.size > sizeof(discard)) {
+		readExpect(discard, sizeof(discard), "chunk data");
+		chunk.size -= sizeof(discard);
+	}
+	if (chunk.size) readExpect(discard, chunk.size, "chunk data");
+	readCrc();
+}
+
+static struct PACKED {
+	uint32_t width;
+	uint32_t height;
+	uint8_t depth;
+	enum PACKED {
+		Grayscale      = 0,
+		Truecolor      = 2,
+		Indexed        = 3,
+		GrayscaleAlpha = 4,
+		TruecolorAlpha = 6,
+	} color;
+	enum PACKED { Deflate } compression;
+	enum PACKED { Adaptive } filter;
+	enum PACKED { Progressive, Adam7 } interlace;
+} header;
+_Static_assert(13 == sizeof(header), "header size");
+
+static size_t pixelBits(void) {
+	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();
+	}
+}
+
+static size_t pixelSize(void) {
+	return (pixelBits() + 7) / 8;
+}
+
+static size_t lineSize(void) {
+	return (header.width * pixelBits() + 7) / 8;
+}
+
+static size_t dataSize(void) {
+	return (1 + lineSize()) * header.height;
+}
+
+static const char *ColorStr[] = {
+	[Grayscale] = "grayscale",
+	[Truecolor] = "truecolor",
+	[Indexed] = "indexed",
+	[GrayscaleAlpha] = "grayscale alpha",
+	[TruecolorAlpha] = "truecolor alpha",
+};
+static void printHeader(void) {
+	fprintf(
+		stderr,
+		"%s: %ux%u %hhu-bit %s\n",
+		path,
+		header.width, header.height,
+		header.depth, ColorStr[header.color]
+	);
+}
+
+static void readHeader(struct Chunk chunk) {
+	if (chunk.size != sizeof(header)) {
+		errx(
+			EX_DATAERR, "%s: expected IHDR size %zu, found %u",
+			path, sizeof(header), chunk.size
+		);
+	}
+	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);
+	switch (PAIR(header.color, header.depth)) {
+		case PAIR(Grayscale, 1):
+		case PAIR(Grayscale, 2):
+		case PAIR(Grayscale, 4):
+		case PAIR(Grayscale, 8):
+		case PAIR(Grayscale, 16):
+		case PAIR(Truecolor, 8):
+		case PAIR(Truecolor, 16):
+		case PAIR(Indexed, 1):
+		case PAIR(Indexed, 2):
+		case PAIR(Indexed, 4):
+		case PAIR(Indexed, 8):
+		case PAIR(GrayscaleAlpha, 8):
+		case PAIR(GrayscaleAlpha, 16):
+		case PAIR(TruecolorAlpha, 8):
+		case PAIR(TruecolorAlpha, 16):
+			break;
+		default:
+			errx(
+				EX_DATAERR, "%s: invalid color type %hhu and bit depth %hhu",
+				path, header.color, header.depth
+			);
+	}
+	if (header.compression != Deflate) {
+		errx(
+			EX_DATAERR, "%s: invalid compression method %hhu",
+			path, header.compression
+		);
+	}
+	if (header.filter != Adaptive) {
+		errx(EX_DATAERR, "%s: invalid filter method %hhu", path, header.filter);
+	}
+	if (header.interlace > Adam7) {
+		errx(EX_DATAERR, "%s: invalid interlace method %hhu", path, header.interlace);
+	}
+
+	if (verbose) printHeader();
+}
+
+static void writeHeader(void) {
+	if (verbose) printHeader();
+
+	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 struct {
+	uint32_t len;
+	uint8_t entries[256][3];
+} palette;
+
+static struct {
+	uint32_t len;
+	uint8_t alpha[256];
+} trans;
+
+static void paletteClear(void) {
+	palette.len = 0;
+	trans.len = 0;
+}
+
+static uint32_t paletteIndex(bool alpha, const uint8_t *rgba) {
+	uint32_t i;
+	for (i = 0; i < palette.len; ++i) {
+		if (alpha && i < trans.len && trans.alpha[i] != rgba[3]) continue;
+		if (0 == memcmp(palette.entries[i], rgba, 3)) break;
+	}
+	return i;
+}
+
+static bool paletteAdd(bool alpha, const uint8_t *rgba) {
+	uint32_t i = paletteIndex(alpha, rgba);
+	if (i < palette.len) return true;
+	if (i == 256) return false;
+	memcpy(palette.entries[i], rgba, 3);
+	palette.len++;
+	if (alpha) {
+		trans.alpha[i] = rgba[3];
+		trans.len++;
+	}
+	return true;
+}
+
+static void transCompact(void) {
+	uint32_t i;
+	for (i = 0; i < trans.len; ++i) {
+		if (trans.alpha[i] == 0xFF) break;
+	}
+	if (i == trans.len) return;
+
+	for (uint32_t j = i + 1; j < trans.len; ++j) {
+		if (trans.alpha[j] == 0xFF) continue;
+
+		uint8_t alpha = trans.alpha[i];
+		trans.alpha[i] = trans.alpha[j];
+		trans.alpha[j] = alpha;
+
+		uint8_t rgb[3];
+		memcpy(rgb, palette.entries[i], 3);
+		memcpy(palette.entries[i], palette.entries[j], 3);
+		memcpy(palette.entries[j], rgb, 3);
+
+		i++;
+	}
+	trans.len = i;
+}
+
+static void readPalette(struct Chunk chunk) {
+	if (chunk.size % 3) {
+		errx(EX_DATAERR, "%s: PLTE size %u not divisible by 3", path, chunk.size);
+	}
+
+	palette.len = chunk.size / 3;
+	if (palette.len > 256) {
+		errx(EX_DATAERR, "%s: PLTE length %u > 256", path, palette.len);
+	}
+
+	readExpect(palette.entries, chunk.size, "palette data");
+	readCrc();
+
+	if (verbose) fprintf(stderr, "%s: palette length %u\n", path, palette.len);
+}
+
+static void writePalette(void) {
+	if (verbose) fprintf(stderr, "%s: palette length %u\n", path, palette.len);
+	struct Chunk plte = { .size = 3 * palette.len, .type = "PLTE" };
+	writeChunk(plte);
+	writeExpect(palette.entries, plte.size);
+	writeCrc();
+}
+
+static void readTrans(struct Chunk chunk) {
+	trans.len = chunk.size;
+	if (trans.len > 256) {
+		errx(EX_DATAERR, "%s: tRNS length %u > 256", path, trans.len);
+	}
+	readExpect(trans.alpha, chunk.size, "transparency alpha");
+	readCrc();
+	if (verbose) fprintf(stderr, "%s: transparency length %u\n", path, trans.len);
+}
+
+static void writeTrans(void) {
+	if (verbose) fprintf(stderr, "%s: transparency length %u\n", path, trans.len);
+	struct Chunk trns = { .size = trans.len, .type = "tRNS" };
+	writeChunk(trns);
+	writeExpect(trans.alpha, trns.size);
+	writeCrc();
+}
+
+static uint8_t *data;
+
+static void allocData(void) {
+	data = malloc(dataSize());
+	if (!data) err(EX_OSERR, "malloc(%zu)", dataSize());
+}
+
+static void readData(struct Chunk chunk) {
+	if (verbose) fprintf(stderr, "%s: data size %zu\n", path, dataSize());
+
+	struct z_stream_s stream = { .next_out = data, .avail_out = dataSize() };
+	int error = inflateInit(&stream);
+	if (error != Z_OK) errx(EX_SOFTWARE, "%s: inflateInit: %s", path, stream.msg);
+
+	for (;;) {
+		if (0 != memcmp(chunk.type, "IDAT", 4)) {
+			errx(EX_DATAERR, "%s: missing IDAT chunk", path);
+		}
+
+		uint8_t *idat = malloc(chunk.size);
+		if (!idat) err(EX_OSERR, "malloc");
+
+		readExpect(idat, chunk.size, "image data");
+		readCrc();
+
+		stream.next_in = idat;
+		stream.avail_in = chunk.size;
+		int error = inflate(&stream, Z_SYNC_FLUSH);
+		free(idat);
+
+		if (error == Z_STREAM_END) break;
+		if (error != Z_OK) {
+			errx(EX_DATAERR, "%s: inflate: %s", path, stream.msg);
+		}
+
+		chunk = readChunk();
+	}
+
+	inflateEnd(&stream);
+	if ((size_t)stream.total_out != dataSize()) {
+		errx(
+			EX_DATAERR, "%s: expected data size %zu, found %zu",
+			path, dataSize(), (size_t)stream.total_out
+		);
+	}
+
+	if (verbose) {
+		fprintf(
+			stderr, "%s: deflate size %zu\n", path, (size_t)stream.total_in
+		);
+	}
+}
+
+static void writeData(void) {
+	if (verbose) fprintf(stderr, "%s: data size %zu\n", path, dataSize());
+
+	uLong size = compressBound(dataSize());
+	uint8_t *deflate = malloc(size);
+	if (!deflate) err(EX_OSERR, "malloc");
+
+	int error = compress2(deflate, &size, data, dataSize(), Z_BEST_COMPRESSION);
+	if (error != Z_OK) errx(EX_SOFTWARE, "%s: compress2: %d", path, error);
+
+	struct Chunk idat = { .size = size, .type = "IDAT" };
+	writeChunk(idat);
+	writeExpect(deflate, size);
+	writeCrc();
+
+	free(deflate);
+
+	if (verbose) fprintf(stderr, "%s: deflate size %lu\n", path, size);
+}
+
+static void writeEnd(void) {
+	struct Chunk iend = { .size = 0, .type = "IEND" };
+	writeChunk(iend);
+	writeCrc();
+}
+
+enum PACKED Filter {
+	None,
+	Sub,
+	Up,
+	Average,
+	Paeth,
+	FilterCount,
+};
+
+struct Bytes {
+	uint8_t x;
+	uint8_t a;
+	uint8_t b;
+	uint8_t c;
+};
+
+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);
+	if (pa <= pb && pa <= pc) return f.a;
+	if (pb <= pc) return f.b;
+	return f.c;
+}
+
+static uint8_t recon(enum Filter type, struct Bytes f) {
+	switch (type) {
+		case None:    return f.x;
+		case Sub:     return f.x + f.a;
+		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();
+	}
+}
+
+static uint8_t filt(enum Filter type, struct Bytes f) {
+	switch (type) {
+		case None:    return f.x;
+		case Sub:     return f.x - f.a;
+		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();
+	}
+}
+
+static struct Line {
+	enum Filter type;
+	uint8_t data[];
+} **lines;
+
+static void allocLines(void) {
+	lines = calloc(header.height, sizeof(*lines));
+	if (!lines) err(EX_OSERR, "calloc(%u, %zu)", header.height, sizeof(*lines));
+}
+
+static void scanlines(void) {
+	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 struct Bytes origBytes(uint32_t y, size_t i) {
+	bool a = (i >= pixelSize()), 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,
+	};
+}
+
+static void reconData(void) {
+	for (uint32_t y = 0; y < header.height; ++y) {
+		for (size_t i = 0; i < lineSize(); ++i) {
+			lines[y]->data[i] =
+				recon(lines[y]->type, origBytes(y, i));
+		}
+		lines[y]->type = None;
+	}
+}
+
+static void filterData(void) {
+	if (header.color == Indexed || header.depth < 8) return;
+	for (uint32_t y = header.height - 1; y < header.height; --y) {
+		uint8_t filter[FilterCount][lineSize()];
+		uint32_t heuristic[FilterCount] = {0};
+		enum Filter minType = None;
+		for (enum Filter type = None; type < FilterCount; ++type) {
+			for (size_t i = 0; i < lineSize(); ++i) {
+				filter[type][i] = filt(type, origBytes(y, i));
+				heuristic[type] += abs((int8_t)filter[type][i]);
+			}
+			if (heuristic[type] < heuristic[minType]) minType = type;
+		}
+		lines[y]->type = minType;
+		memcpy(lines[y]->data, filter[minType], lineSize());
+	}
+}
+
+static void discardAlpha(void) {
+	if (header.color != GrayscaleAlpha && header.color != TruecolorAlpha) return;
+	size_t sampleSize = header.depth / 8;
+	size_t colorSize = pixelSize() - sampleSize;
+	for (uint32_t y = 0; y < header.height; ++y) {
+		for (uint32_t x = 0; x < header.width; ++x) {
+			for (size_t i = 0; i < sampleSize; ++i) {
+				if (lines[y]->data[x * pixelSize() + colorSize + i] != 0xFF) return;
+			}
+		}
+	}
+
+	uint8_t *ptr = data;
+	for (uint32_t y = 0; y < header.height; ++y) {
+		*ptr++ = lines[y]->type;
+		for (uint32_t x = 0; x < header.width; ++x) {
+			memmove(ptr, &lines[y]->data[x * pixelSize()], colorSize);
+			ptr += colorSize;
+		}
+	}
+	header.color = (header.color == GrayscaleAlpha) ? Grayscale : Truecolor;
+	scanlines();
+}
+
+static void discardColor(void) {
+	if (header.color != Truecolor && header.color != TruecolorAlpha) return;
+	size_t sampleSize = header.depth / 8;
+	for (uint32_t y = 0; y < header.height; ++y) {
+		for (uint32_t x = 0; x < header.width; ++x) {
+			uint8_t *r = &lines[y]->data[x * pixelSize()];
+			uint8_t *g = r + sampleSize;
+			uint8_t *b = g + sampleSize;
+			if (0 != memcmp(r, g, sampleSize)) return;
+			if (0 != memcmp(g, b, sampleSize)) return;
+		}
+	}
+
+	uint8_t *ptr = data;
+	for (uint32_t y = 0; y < header.height; ++y) {
+		*ptr++ = lines[y]->type;
+		for (uint32_t x = 0; x < header.width; ++x) {
+			uint8_t *pixel = &lines[y]->data[x * pixelSize()];
+			memmove(ptr, pixel, sampleSize);
+			ptr += sampleSize;
+			if (header.color == TruecolorAlpha) {
+				memmove(ptr, pixel + 3 * sampleSize, sampleSize);
+				ptr += sampleSize;
+			}
+		}
+	}
+	header.color = (header.color == Truecolor) ? Grayscale : GrayscaleAlpha;
+	scanlines();
+}
+
+static void indexColor(void) {
+	if (header.color != Truecolor && header.color != TruecolorAlpha) return;
+	if (header.depth != 8) return;
+	bool alpha = (header.color == TruecolorAlpha);
+	for (uint32_t y = 0; y < header.height; ++y) {
+		for (uint32_t x = 0; x < header.width; ++x) {
+			if (!paletteAdd(alpha, &lines[y]->data[x * pixelSize()])) return;
+		}
+	}
+	transCompact();
+
+	uint8_t *ptr = data;
+	for (uint32_t y = 0; y < header.height; ++y) {
+		*ptr++ = lines[y]->type;
+		for (uint32_t x = 0; x < header.width; ++x) {
+			*ptr++ = paletteIndex(alpha, &lines[y]->data[x * pixelSize()]);
+		}
+	}
+	header.color = Indexed;
+	scanlines();
+}
+
+static void reduceDepth8(void) {
+	if (header.color != Grayscale && header.color != Indexed) return;
+	if (header.depth != 8) return;
+	if (header.color == Grayscale) {
+		for (uint32_t y = 0; y < header.height; ++y) {
+			for (size_t i = 0; i < lineSize(); ++i) {
+				uint8_t a = lines[y]->data[i];
+				if ((a >> 4) != (a & 0x0F)) return;
+			}
+		}
+	} else if (palette.len > 16) {
+		return;
+	}
+
+	uint8_t *ptr = data;
+	for (uint32_t y = 0; y < header.height; ++y) {
+		*ptr++ = lines[y]->type;
+		for (size_t i = 0; i < lineSize(); i += 2) {
+			uint8_t iByte = lines[y]->data[i];
+			uint8_t jByte = (i + 1 < lineSize()) ? lines[y]->data[i + 1] : 0;
+			uint8_t a = iByte & 0x0F;
+			uint8_t b = jByte & 0x0F;
+			*ptr++ = a << 4 | b;
+		}
+	}
+	header.depth = 4;
+	scanlines();
+}
+
+static void reduceDepth4(void) {
+	if (header.depth != 4) return;
+	if (header.color == Grayscale) {
+		for (uint32_t y = 0; y < header.height; ++y) {
+			for (size_t i = 0; i < lineSize(); ++i) {
+				uint8_t a = lines[y]->data[i] >> 4;
+				uint8_t b = lines[y]->data[i] & 0x0F;
+				if ((a >> 2) != (a & 0x03)) return;
+				if ((b >> 2) != (b & 0x03)) return;
+			}
+		}
+	} else if (palette.len > 4) {
+		return;
+	}
+
+	uint8_t *ptr = data;
+	for (uint32_t y = 0; y < header.height; ++y) {
+		*ptr++ = lines[y]->type;
+		for (size_t i = 0; i < lineSize(); i += 2) {
+			uint8_t iByte = lines[y]->data[i];
+			uint8_t jByte = (i + 1 < lineSize()) ? lines[y]->data[i + 1] : 0;
+			uint8_t a = iByte >> 4 & 0x03, b = iByte & 0x03;
+			uint8_t c = jByte >> 4 & 0x03, d = jByte & 0x03;
+			*ptr++ = a << 6 | b << 4 | c << 2 | d;
+		}
+	}
+	header.depth = 2;
+	scanlines();
+}
+
+static void reduceDepth2(void) {
+	if (header.depth != 2) return;
+	if (header.color == Grayscale) {
+		for (uint32_t y = 0; y < header.height; ++y) {
+			for (size_t i = 0; i < lineSize(); ++i) {
+				uint8_t a = lines[y]->data[i] >> 6;
+				uint8_t b = lines[y]->data[i] >> 4 & 0x03;
+				uint8_t c = lines[y]->data[i] >> 2 & 0x03;
+				uint8_t d = lines[y]->data[i] & 0x03;
+				if ((a >> 1) != (a & 0x01)) return;
+				if ((b >> 1) != (b & 0x01)) return;
+				if ((c >> 1) != (c & 0x01)) return;
+				if ((d >> 1) != (d & 0x01)) return;
+			}
+		}
+	} else if (palette.len > 2) {
+		return;
+	}
+
+	uint8_t *ptr = data;
+	for (uint32_t y = 0; y < header.height; ++y) {
+		*ptr++ = lines[y]->type;
+		for (size_t i = 0; i < lineSize(); i += 2) {
+			uint8_t iByte = lines[y]->data[i];
+			uint8_t jByte = (i + 1 < lineSize()) ? lines[y]->data[i + 1] : 0;
+			uint8_t a = iByte >> 6 & 0x01, b = iByte >> 4 & 0x01;
+			uint8_t c = iByte >> 2 & 0x01, d = iByte & 0x01;
+			uint8_t e = jByte >> 6 & 0x01, f = jByte >> 4 & 0x01;
+			uint8_t g = jByte >> 2 & 0x01, h = jByte & 0x01;
+			*ptr++ = a << 7 | b << 6 | c << 5 | d << 4 | e << 3 | f << 2 | g << 1 | h;
+		}
+	}
+	header.depth = 1;
+	scanlines();
+}
+
+static void reduceDepth(void) {
+	reduceDepth8();
+	reduceDepth4();
+	reduceDepth2();
+}
+
+static void optimize(const char *inPath, const char *outPath) {
+	if (inPath) {
+		path = inPath;
+		file = fopen(path, "r");
+		if (!file) err(EX_NOINPUT, "%s", path);
+	} else {
+		path = "(stdin)";
+		file = stdin;
+	}
+
+	readSignature();
+	struct Chunk ihdr = readChunk();
+	if (0 != memcmp(ihdr.type, "IHDR", 4)) {
+		errx(EX_DATAERR, "%s: expected IHDR, found %s", path, typeStr(ihdr));
+	}
+	readHeader(ihdr);
+	if (header.interlace != Progressive) {
+		errx(
+			EX_CONFIG, "%s: unsupported interlace method %hhu",
+			path, header.interlace
+		);
+	}
+
+	paletteClear();
+	allocData();
+	for (;;) {
+		struct Chunk chunk = readChunk();
+		if (0 == memcmp(chunk.type, "PLTE", 4)) {
+			readPalette(chunk);
+		} else if (0 == memcmp(chunk.type, "tRNS", 4)) {
+			readTrans(chunk);
+		} else if (0 == memcmp(chunk.type, "IDAT", 4)) {
+			readData(chunk);
+		} else if (0 != memcmp(chunk.type, "IEND", 4)) {
+			skipChunk(chunk);
+		} else {
+			break;
+		}
+	}
+
+	fclose(file);
+
+	allocLines();
+	scanlines();
+	reconData();
+
+	discardAlpha();
+	discardColor();
+	indexColor();
+	reduceDepth();
+	filterData();
+	free(lines);
+
+	if (outPath) {
+		path = outPath;
+		file = fopen(path, "w");
+		if (!file) err(EX_CANTCREAT, "%s", path);
+	} else {
+		path = "(stdout)";
+		file = stdout;
+	}
+
+	writeSignature();
+	writeHeader();
+	if (header.color == Indexed) {
+		writePalette();
+		if (trans.len) writeTrans();
+	}
+	writeData();
+	writeEnd();
+	free(data);
+
+	int error = fclose(file);
+	if (error) err(EX_IOERR, "%s", path);
+}
+
+int main(int argc, char *argv[]) {
+	bool stdio = false;
+	char *output = NULL;
+
+	int opt;
+	while (0 < (opt = getopt(argc, argv, "co:v"))) {
+		switch (opt) {
+			break; case 'c': stdio = true;
+			break; case 'o': output = optarg;
+			break; case 'v': verbose = true;
+			break; default: return EX_USAGE;
+		}
+	}
+
+	if (argc - optind == 1 && (output || stdio)) {
+		optimize(argv[optind], output);
+	} else if (optind < argc) {
+		for (int i = optind; i < argc; ++i) {
+			optimize(argv[i], argv[i]);
+		}
+	} else {
+		optimize(NULL, output);
+	}
+
+	return EX_OK;
+}
diff --git a/bin/psf2png.c b/bin/psf2png.c
new file mode 100644
index 00000000..1aaa8635
--- /dev/null
+++ b/bin/psf2png.c
@@ -0,0 +1,107 @@
+/* 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "png.h"
+
+int main(int argc, char *argv[]) {
+	uint32_t cols = 32;
+	const char *str = NULL;
+	uint32_t fg = 0xFFFFFF;
+	uint32_t bg = 0x000000;
+
+	int opt;
+	while (0 < (opt = getopt(argc, argv, "b:c:f:s:"))) {
+		switch (opt) {
+			break; case 'b': bg = strtoul(optarg, NULL, 16);
+			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;
+		}
+	}
+	if (!cols && str) cols = strlen(str);
+	if (!cols) return EX_USAGE;
+
+	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 (!path) path = "(stdin)";
+
+	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;
+	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);
+
+	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 (len < header.glyph.len) {
+		errx(EX_DATAERR, "%s: truncated glyphs", path);
+	}
+	fclose(file);
+
+	uint32_t count = (str ? strlen(str) : header.glyph.len);
+	uint32_t width = header.glyph.width * cols;
+	uint32_t rows = (count + cols - 1) / cols;
+	uint32_t height = header.glyph.height * rows;
+
+	pngHead(stdout, width, height, 8, PNGIndexed);
+	uint8_t pal[] = {
+		bg >> 16, bg >> 8, bg,
+		fg >> 16, fg >> 8, fg,
+	};
+	pngPalette(stdout, pal, sizeof(pal));
+
+	uint8_t data[height][1 + width];
+	memset(data, PNGNone, sizeof(data));
+
+	for (uint32_t i = 0; i < count; ++i) {
+		uint32_t row = header.glyph.height * (i / cols);
+		uint32_t col = 1 + header.glyph.width * (i % cols);
+		uint32_t g = (str ? str[i] : i);
+		for (uint32_t y = 0; y < header.glyph.height; ++y) {
+			for (uint32_t x = 0; x < header.glyph.width; ++x) {
+				uint8_t bit = glyphs[g][y][x / 8] >> (7 - x % 8) & 1;
+				data[row + y][col + x] = bit;
+			}
+		}
+	}
+
+	pngData(stdout, (uint8_t *)data, sizeof(data));
+	pngTail(stdout);
+}
diff --git a/bin/psfed.c b/bin/psfed.c
new file mode 100644
index 00000000..4f46b500
--- /dev/null
+++ b/bin/psfed.c
@@ -0,0 +1,577 @@
+/* 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);
+	}
+}
diff --git a/bin/ptee.c b/bin/ptee.c
new file mode 100644
index 00000000..6a9a16b4
--- /dev/null
+++ b/bin/ptee.c
@@ -0,0 +1,116 @@
+/* Copyright (C) 2019  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 <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sysexits.h>
+#include <termios.h>
+#include <unistd.h>
+
+#if defined __FreeBSD__
+#include <libutil.h>
+#elif defined __linux__
+#include <pty.h>
+#else
+#include <util.h>
+#endif
+
+typedef unsigned char byte;
+
+static struct termios saveTerm;
+static void restoreTerm(void) {
+	tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm);
+}
+
+int main(int argc, char *argv[]) {
+	if (argc < 2) return EX_USAGE;
+	if (isatty(STDOUT_FILENO)) errx(EX_USAGE, "stdout is not redirected");
+
+	int error = tcgetattr(STDIN_FILENO, &saveTerm);
+	if (error) err(EX_IOERR, "tcgetattr");
+	atexit(restoreTerm);
+
+	struct termios raw = saveTerm;
+	cfmakeraw(&raw);
+	error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw);
+	if (error) err(EX_IOERR, "tcsetattr");
+
+	struct winsize window;
+	error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
+	if (error) err(EX_IOERR, "ioctl");
+
+	int pty;
+	pid_t pid = forkpty(&pty, NULL, NULL, &window);
+	if (pid < 0) err(EX_OSERR, "forkpty");
+
+	if (!pid) {
+		execvp(argv[1], &argv[1]);
+		err(EX_NOINPUT, "%s", argv[1]);
+	}
+
+	bool stop = false;
+
+	byte buf[4096];
+	struct pollfd fds[2] = {
+		{ .events = POLLIN, .fd = STDIN_FILENO },
+		{ .events = POLLIN, .fd = pty },
+	};
+	while (0 < poll(fds, 2, -1)) {
+		if (fds[0].revents & POLLIN) {
+			ssize_t rlen = read(STDIN_FILENO, buf, sizeof(buf));
+			if (rlen < 0) err(EX_IOERR, "read");
+
+			if (rlen == 1 && buf[0] == CTRL('S')) {
+				stop ^= true;
+				continue;
+			}
+
+			if (rlen == 1 && buf[0] == CTRL('Q')) {
+				char dump[] = "\x1B[10i";
+				ssize_t wlen = write(STDOUT_FILENO, dump, sizeof(dump) - 1);
+				if (wlen < 0) err(EX_IOERR, "write");
+				continue;
+			}
+
+			ssize_t wlen = write(pty, buf, rlen);
+			if (wlen < 0) err(EX_IOERR, "write");
+		}
+
+		if (fds[1].revents & POLLIN) {
+			ssize_t rlen = read(pty, buf, sizeof(buf));
+			if (rlen < 0) err(EX_IOERR, "read");
+
+			ssize_t wlen = write(STDIN_FILENO, buf, rlen);
+			if (wlen < 0) err(EX_IOERR, "write");
+
+			if (!stop) {
+				wlen = write(STDOUT_FILENO, buf, rlen);
+				if (wlen < 0) err(EX_IOERR, "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;
+	}
+	err(EX_IOERR, "poll");
+}
diff --git a/bin/relay.c b/bin/relay.c
new file mode 100644
index 00000000..aa7913c9
--- /dev/null
+++ b/bin/relay.c
@@ -0,0 +1,218 @@
+/* Copyright (C) 2019  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/>.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with LibreSSL (or a modified version of that library),
+ * containing parts covered by the terms of the OpenSSL License and the
+ * original SSLeay license, the licensors of this Program grant you
+ * additional permission to convey the resulting work. Corresponding
+ * Source for a non-source form of such a combination shall include the
+ * source code for the parts of LibreSSL used as well as that of the
+ * covered work.
+ */
+
+#include <err.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sysexits.h>
+#include <tls.h>
+#include <unistd.h>
+
+#ifdef __FreeBSD__
+#include <sys/capsicum.h>
+#endif
+
+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));
+		ptr += ret;
+		len -= ret;
+	}
+}
+
+static void clientFormat(struct tls *client, const char *format, ...) {
+	char buf[1024];
+	va_list ap;
+	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");
+	clientWrite(client, buf, len);
+}
+
+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");
+	}
+
+	char *command = strsep(&line, " ");
+	if (!strcmp(command, "001") || !strcmp(command, "INVITE")) {
+		clientFormat(client, "JOIN :%s\r\n", chan);
+	} else if (!strcmp(command, "PING")) {
+		clientFormat(client, "PONG %s\r\n", line);
+	}
+	if (strcmp(command, "PRIVMSG") && strcmp(command, "NOTICE")) return;
+
+	if (!prefix) errx(EX_PROTOCOL, "message without prefix");
+	char *nick = strsep(&prefix, "!");
+
+	if (!line) errx(EX_PROTOCOL, "message without destination");
+	char *dest = strsep(&line, " ");
+	if (strcmp(dest, chan)) return;
+
+	if (!line || line[0] != ':') errx(EX_PROTOCOL, "message without message");
+	line = &line[1];
+
+	if (!strncmp(line, "\1ACTION ", 8)) {
+		line = &line[8];
+		size_t len = strcspn(line, "\1");
+		printf("* %c\u200C%s %.*s\n", nick[0], &nick[1], (int)len, line);
+	} else if (command[0] == 'N') {
+		printf("-%c\u200C%s- %s\n", nick[0], &nick[1], line);
+	} else {
+		printf("<%c\u200C%s> %s\n", nick[0], &nick[1], 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");
+}
+#endif
+
+int main(int argc, char *argv[]) {
+	int error;
+
+	if (argc < 5) return EX_USAGE;
+	const char *host = argv[1];
+	const char *port = argv[2];
+	const char *nick = argv[3];
+	const char *chan = argv[4];
+
+	setlinebuf(stdout);
+	signal(SIGPIPE, SIG_IGN);
+
+	struct tls_config *config = tls_config_new();
+	if (!config) errx(EX_SOFTWARE, "tls_config_new");
+
+	error = tls_config_set_ciphers(config, "compat");
+	if (error) {
+		errx(EX_SOFTWARE, "tls_config_set_ciphers: %s", tls_config_error(config));
+	}
+
+	struct tls *client = tls_client();
+	if (!client) errx(EX_SOFTWARE, "tls_client");
+
+	error = tls_configure(client, config);
+	if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client));
+	tls_config_free(config);
+
+	struct addrinfo *head;
+	struct addrinfo hints = {
+		.ai_family = AF_UNSPEC,
+		.ai_socktype = SOCK_STREAM,
+		.ai_protocol = IPPROTO_TCP,
+	};
+	error = getaddrinfo(host, port, &hints, &head);
+	if (error) errx(EX_NOHOST, "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");
+
+		error = connect(sock, ai->ai_addr, ai->ai_addrlen);
+		if (!error) break;
+
+		close(sock);
+		sock = -1;
+	}
+	if (sock < 0) err(EX_UNAVAILABLE, "connect");
+	freeaddrinfo(head);
+
+	error = tls_connect_socket(client, sock, host);
+	if (error) errx(EX_PROTOCOL, "tls_connect: %s", tls_error(client));
+
+#ifdef __FreeBSD__
+	error = cap_enter();
+	if (error) err(EX_OSERR, "cap_enter");
+
+	cap_rights_t rights;
+	cap_rights_init(&rights, CAP_WRITE);
+	limit(STDOUT_FILENO, &rights);
+	limit(STDERR_FILENO, &rights);
+
+	cap_rights_init(&rights, CAP_EVENT, CAP_READ);
+	limit(STDIN_FILENO, &rights);
+
+	cap_rights_set(&rights, CAP_WRITE);
+	limit(sock, &rights);
+#endif
+
+	clientFormat(client, "NICK :%s\r\nUSER %s 0 * :%s\r\n", nick, nick, nick);
+
+	char *input = NULL;
+	size_t cap = 0;
+
+	char buf[4096];
+	size_t len = 0;
+
+	struct pollfd fds[2] = {
+		{ .events = POLLIN, .fd = STDIN_FILENO },
+		{ .events = POLLIN, .fd = sock },
+	};
+	while (0 < poll(fds, 2, -1)) {
+		if (fds[0].revents) {
+			ssize_t len = getline(&input, &cap, stdin);
+			if (len < 0) err(EX_IOERR, "getline");
+			input[len - 1] = '\0';
+			clientFormat(client, "NOTICE %s :%s\r\n", chan, input);
+		}
+		if (!fds[1].revents) continue;
+
+		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;
+		len += read;
+
+		char *crlf;
+		char *line = buf;
+		for (;;) {
+			crlf = memmem(line, &buf[len] - line, "\r\n", 2);
+			if (!crlf) break;
+			crlf[0] = '\0';
+			clientHandle(client, chan, line);
+			line = &crlf[2];
+		}
+		len -= line - buf;
+		memmove(buf, line, len);
+	}
+	err(EX_IOERR, "poll");
+}
diff --git a/bin/scheme.c b/bin/scheme.c
new file mode 100644
index 00000000..33fd8b60
--- /dev/null
+++ b/bin/scheme.c
@@ -0,0 +1,278 @@
+/* Copyright (C) 2018, 2019  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 <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "png.h"
+
+typedef unsigned uint;
+typedef unsigned char byte;
+
+struct HSV {
+	double h, s, v;
+};
+
+struct RGB {
+	byte r, g, b;
+};
+
+static struct RGB convert(struct HSV o) {
+	double c = o.v * o.s;
+	double h = o.h / 60.0;
+	double x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0));
+	double m = o.v - c;
+	double r = m, g = m, b = m;
+	if      (h <= 1.0) { r += c; g += x; }
+	else if (h <= 2.0) { r += x; g += c; }
+	else if (h <= 3.0) { g += c; b += x; }
+	else if (h <= 4.0) { g += x; b += c; }
+	else if (h <= 5.0) { r += x; b += c; }
+	else if (h <= 6.0) { r += c; b += x; }
+	return (struct RGB) { r * 255.0, g * 255.0, b * 255.0 };
+}
+
+static const struct HSV
+R = {   0.0, 1.0, 1.0 },
+Y = {  60.0, 1.0, 1.0 },
+G = { 120.0, 1.0, 1.0 },
+C = { 180.0, 1.0, 1.0 },
+B = { 240.0, 1.0, 1.0 },
+M = { 300.0, 1.0, 1.0 };
+
+static struct HSV x(struct HSV o, double hd, double sf, double vf) {
+	return (struct HSV) {
+		fmod(o.h + hd, 360.0),
+		fmin(o.s * sf, 1.0),
+		fmin(o.v * vf, 1.0),
+	};
+}
+
+enum {
+	Black, Red, Green, Yellow, Blue, Magenta, Cyan, White,
+	Dark = 0,
+	Light = 8,
+	Background = 16,
+	Foreground,
+	Bold,
+	Selection,
+	Cursor,
+	SchemeLen,
+};
+static struct HSV scheme[SchemeLen];
+static struct HSV *dark = &scheme[Dark];
+static struct HSV *light = &scheme[Light];
+
+static void generate(void) {
+	light[Black]   = x(R, +45.0, 0.3, 0.3);
+	light[Red]     = x(R, +10.0, 0.9, 0.8);
+	light[Green]   = x(G, -55.0, 0.8, 0.6);
+	light[Yellow]  = x(Y, -20.0, 0.8, 0.8);
+	light[Blue]    = x(B, -55.0, 0.4, 0.5);
+	light[Magenta] = x(M, +45.0, 0.4, 0.6);
+	light[Cyan]    = x(C, -60.0, 0.3, 0.6);
+	light[White]   = x(R, +45.0, 0.3, 0.8);
+
+	dark[Black] = x(light[Black], 0.0, 1.0, 0.3);
+	dark[White] = x(light[White], 0.0, 1.0, 0.75);
+	for (uint i = Red; i < White; ++i) {
+		dark[i] = x(light[i], 0.0, 1.0, 0.8);
+	}
+
+	scheme[Background] = x(dark[Black],  0.0, 1.0, 0.9);
+	scheme[Foreground] = x(light[White], 0.0, 1.0, 0.9);
+	scheme[Bold]       = x(light[White], 0.0, 1.0, 1.0);
+	scheme[Selection]  = x(light[Red], +10.0, 1.0, 0.8);
+	scheme[Cursor]     = x(dark[White],  0.0, 1.0, 0.8);
+}
+
+static void swap(struct HSV *a, struct HSV *b) {
+	struct HSV c = *a;
+	*a = *b;
+	*b = c;
+}
+
+static void invert(void) {
+	swap(&dark[Black], &light[White]);
+	swap(&dark[White], &light[Black]);
+}
+
+typedef void OutputFn(const struct HSV *hsv, uint len);
+
+static void outputHSV(const struct HSV *hsv, uint len) {
+	for (uint i = 0; i < len; ++i) {
+		printf("%g,%g,%g\n", hsv[i].h, hsv[i].s, hsv[i].v);
+	}
+}
+
+#define FORMAT_RGB "%02hhX%02hhX%02hhX"
+
+static void outputRGB(const struct HSV *hsv, uint len) {
+	for (uint i = 0; i < len; ++i) {
+		struct RGB rgb = convert(hsv[i]);
+		printf(FORMAT_RGB "\n", rgb.r, rgb.g, rgb.b);
+	}
+}
+
+static void outputLinux(const struct HSV *hsv, uint len) {
+	for (uint i = 0; i < len; ++i) {
+		struct RGB rgb = convert(hsv[i]);
+		printf("\x1B]P%X" FORMAT_RGB, i, rgb.r, rgb.g, rgb.b);
+	}
+}
+
+static const char *Enum[SchemeLen] = {
+	"DarkBlack", "DarkRed", "DarkGreen", "DarkYellow",
+	"DarkBlue", "DarkMagenta", "DarkCyan", "DarkWhite",
+	"LightBlack", "LightRed", "LightGreen", "LightYellow",
+	"LightBlue", "LightMagenta", "LightCyan", "LightWhite",
+	"Background", "Foreground", "Bold", "Selection", "Cursor",
+};
+
+static void outputEnum(const struct HSV *hsv, uint len) {
+	printf("enum {\n");
+	for (uint i = 0; i < len; ++i) {
+		struct RGB rgb = convert(hsv[i]);
+		printf("\t%s = 0x" FORMAT_RGB ",\n", Enum[i], rgb.r, rgb.g, rgb.b);
+	}
+	printf("};\n");
+}
+
+#define FORMAT_X "rgb:%02hhX/%02hhX/%02hhX"
+
+static const char *Resources[SchemeLen] = {
+	[Background] = "background",
+	[Foreground] = "foreground",
+	[Bold] = "colorBD",
+	[Selection] = "highlightColor",
+	[Cursor] = "cursorColor",
+};
+
+static void outputXTerm(const struct HSV *hsv, uint len) {
+	for (uint i = 0; i < len; ++i) {
+		struct RGB rgb = convert(hsv[i]);
+		if (Resources[i]) {
+			printf("XTerm*%s: " FORMAT_X "\n", Resources[i], rgb.r, rgb.g, rgb.b);
+		} else {
+			printf("XTerm*color%u: " FORMAT_X "\n", i, rgb.r, rgb.g, rgb.b);
+		}
+	}
+}
+
+static const char *Mintty[SchemeLen] = {
+	"Black", "Red", "Green", "Yellow",
+	"Blue", "Magenta", "Cyan", "White",
+	"BoldBlack", "BoldRed", "BoldGreen", "BoldYellow",
+	"BoldBlue", "BoldMagenta", "BoldCyan", "BoldWhite",
+	[Background] = "BackgroundColour",
+	[Foreground] = "ForegroundColour",
+	[Cursor]     = "CursorColour",
+};
+
+static void outputMintty(const struct HSV *hsv, uint len) {
+	for (uint i = 0; i < len; ++i) {
+		if (!Mintty[i]) continue;
+		struct RGB rgb = convert(hsv[i]);
+		printf("%s=%hhu,%hhu,%hhu\n", Mintty[i], rgb.r, rgb.g, rgb.b);
+	}
+}
+
+static void outputCSS(const struct HSV *hsv, uint len) {
+	printf(":root {\n");
+	for (uint i = 0; i < len; ++i) {
+		struct RGB rgb = convert(hsv[i]);
+		printf("\t--ansi%u: #" FORMAT_RGB ";\n", i, rgb.r, rgb.g, rgb.b);
+	}
+	printf("}\n");
+	for (uint i = 0; i < len; ++i) {
+		printf(
+			".fg%u { color: var(--ansi%u); }\n"
+			".bg%u { background-color: var(--ansi%u); }\n",
+			i, i, i, i
+		);
+	}
+}
+
+enum {
+	SwatchWidth = 64,
+	SwatchHeight = 64,
+	SwatchCols = 8,
+};
+
+static void outputPNG(const struct HSV *hsv, uint len) {
+	uint rows = (len + SwatchCols - 1) / SwatchCols;
+	uint width = SwatchWidth * SwatchCols;
+	uint height = SwatchHeight * rows;
+	pngHead(stdout, width, height, 8, PNGIndexed);
+
+	struct RGB pal[len];
+	for (uint i = 0; i < len; ++i) {
+		pal[i] = convert(hsv[i]);
+	}
+	pngPalette(stdout, (byte *)pal, sizeof(pal));
+
+	byte data[height][1 + width];
+	memset(data, 0, sizeof(data));
+	for (uint y = 0; y < height; ++y) {
+		data[y][0] = (y % SwatchHeight ? PNGUp : PNGSub);
+	}
+	for (uint i = 0; i < len; ++i) {
+		uint y = SwatchHeight * (i / SwatchCols);
+		uint x = SwatchWidth * (i % SwatchCols);
+		data[y][1 + x] = (x ? 1 : i);
+	}
+	pngData(stdout, (byte *)data, sizeof(data));
+	pngTail(stdout);
+}
+
+int main(int argc, char *argv[]) {
+	generate();
+
+	OutputFn *output = outputRGB;
+	const struct HSV *hsv = scheme;
+	uint len = 16;
+
+	int opt;
+	while (0 < (opt = getopt(argc, argv, "Xacghilmp:stx"))) {
+		switch (opt) {
+			break; case 'X': output = outputXTerm;
+			break; case 'a': len = 16;
+			break; case 'c': output = outputEnum;
+			break; case 'g': output = outputPNG;
+			break; case 'h': output = outputHSV;
+			break; case 'i': invert();
+			break; case 'l': output = outputLinux;
+			break; case 'm': output = outputMintty;
+			break; case 'p': {
+				uint p = strtoul(optarg, NULL, 0);
+				if (p >= SchemeLen) return EX_USAGE;
+				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;
+		}
+	}
+
+	output(hsv, len);
+}
diff --git a/bin/sh.l b/bin/sh.l
new file mode 100644
index 00000000..b27ecebc
--- /dev/null
+++ b/bin/sh.l
@@ -0,0 +1,178 @@
+/* Copyright (C) 2021  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/>.
+ */
+
+%option prefix="sh"
+%option noyywrap
+
+%{
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include "hilex.h"
+
+enum { Cap = 64 };
+static int len = 1;
+static int stack[Cap];
+static int push(int val) {
+	if (len < Cap) stack[len++] = val;
+	return val;
+}
+static int pop(void) {
+	if (len > 1) len--;
+	return stack[len-1];
+}
+%}
+
+%s Param Command Arith Backtick
+%x DQuote HereDocDel HereDoc HereDocLit
+
+word [[:alnum:]_.-]+
+param [^:=?+%#{}-]+
+reserved [!{}]|else|do|elif|for|done|fi|then|until|while|if|case|esac
+
+%%
+	static bool first;
+	static char *delimiter;
+
+[[:blank:]]+ { return Normal; }
+
+"\\". { return Escape; }
+
+<INITIAL,DQuote,HereDoc,Param,Command,Arith>{
+	"$"[*@#?$!0-9-] |
+	"$"[_[:alpha:][_[:alnum:]]* |
+	"${"[#]?{param}"}" {
+		return Subst;
+	}
+	"${"{param} {
+		BEGIN(push(Param));
+		return Subst;
+	}
+	"$(" {
+		BEGIN(push(Command));
+		return Subst;
+	}
+	"$((" {
+		BEGIN(push(Arith));
+		return Subst;
+	}
+	"`" {
+		BEGIN(push(Backtick));
+		return Subst;
+	}
+}
+<Param>"}" |
+<Command>")" |
+<Arith>"))" |
+<Backtick>"`" {
+	BEGIN(pop());
+	return Subst;
+}
+
+"\n" {
+	first = true;
+	return Normal;
+}
+[&();|]|"&&"|";;"|"||" {
+	first = true;
+	return Operator;
+}
+[0-9]?([<>]"&"?|">|"|">>"|"<>") {
+	return Operator;
+}
+
+{reserved} {
+	if (first) {
+		first = false;
+		return Keyword;
+	}
+	return Normal;
+}
+
+{word}/[[:blank:]]*"()" { return Ident; }
+
+[0-9]?("<<"|"<<-") {
+	BEGIN(push(HereDocDel));
+	return Operator;
+}
+<HereDocDel>{
+	[[:blank:]]+ { return Normal; }
+	{word} {
+		delimiter = strdup(yytext);
+		assert(delimiter);
+		BEGIN(pop(), push(HereDoc));
+		return Ident;
+	}
+	"'"{word}"'" {
+		delimiter = strndup(&yytext[1], strlen(yytext)-2);
+		assert(delimiter);
+		BEGIN(pop(), push(HereDocLit));
+		return Ident;
+	}
+}
+<HereDoc,HereDocLit>{
+	^"\t"*{word} {
+		if (strcmp(&yytext[strspn(yytext, "\t")], delimiter)) REJECT;
+		free(delimiter);
+		BEGIN(pop());
+		return Ident;
+	}
+}
+<HereDoc>{
+	[^$`\n]+ { return String; }
+	.|\n { return String; }
+}
+<HereDocLit>{
+	.*\n { return String; }
+}
+
+"'"[^'']*"'" { return String; }
+
+"\""/[^$`\\] {
+	BEGIN(push(DQuote));
+	yymore();
+}
+"\"" {
+	BEGIN(push(DQuote));
+	return String;
+}
+
+<DQuote>{
+	[^\\$`""]*"\"" {
+		BEGIN(pop());
+		return String;
+	}
+	"\\"[$`""\\\n] { return Escape; }
+	[^\\$`""]+|. { return String; }
+}
+
+<INITIAL,Command,Backtick,Arith>"#".* { return Comment; }
+
+{word} {
+	first = false;
+	return Normal;
+}
+
+.|\n { return Normal; }
+
+%{
+	(void)yyunput;
+	(void)input;
+%}
+
+%%
+
+const struct Lexer LexSh = { yylex, &yyin, &yytext };
diff --git a/bin/shotty.c b/bin/shotty.c
new file mode 100644
index 00000000..83b00313
--- /dev/null
+++ b/bin/shotty.c
@@ -0,0 +1,648 @@
+/* Copyright (C) 2019  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 <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+
+#define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit
+
+typedef unsigned uint;
+
+enum {
+	NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL,
+	BS, HT, NL, VT, NP, CR, SO, SI,
+	DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB,
+	CAN, EM, SUB, ESC, FS, GS, RS, US,
+	DEL = 0x7F,
+};
+
+enum Attr {
+	BIT(Bold),
+	BIT(Dim),
+	BIT(Italic),
+	BIT(Underline),
+	BIT(Blink),
+	BIT(Reverse),
+};
+
+struct Style {
+	enum Attr attr;
+	int bg, fg;
+};
+
+struct Cell {
+	struct Style style;
+	wchar_t ch;
+};
+
+static uint rows = 24, cols = 80;
+static struct Cell *cells;
+
+static struct Cell *cell(uint y, uint x) {
+	assert(y <= rows);
+	assert(x <= cols);
+	assert(y * cols + x <= rows * cols);
+	return &cells[y * cols + x];
+}
+
+static uint y, x;
+static struct Style style = { .bg = -1, .fg = -1 };
+
+static struct {
+	uint y, x;
+} save;
+
+enum { ParamCap = 16 };
+static struct {
+	uint s[ParamCap];
+	uint n, i;
+} param;
+
+static uint p(uint i, uint z) {
+	return (i < param.n ? param.s[i] : z);
+}
+
+static uint min(uint a, uint b) {
+	return (a < b ? a : b);
+}
+
+#define _ch ch __attribute__((unused))
+typedef void Action(wchar_t ch);
+
+static void nop(wchar_t _ch) {
+}
+
+static void csi(wchar_t _ch) {
+	memset(&param, 0, sizeof(param));
+}
+
+static void csiSep(wchar_t _ch) {
+	if (param.n == ParamCap) return;
+	if (!param.n) param.n++;
+	param.n++;
+	param.i++;
+}
+
+static void csiDigit(wchar_t ch) {
+	param.s[param.i] *= 10;
+	param.s[param.i] += ch - L'0';
+	if (!param.n) param.n++;
+}
+
+static void bs(wchar_t _ch)  { if (x) x--; }
+static void ht(wchar_t _ch)  { x = min(x - x % 8 + 8, cols - 1); }
+static void cr(wchar_t _ch)  { x = 0; }
+static void cuu(wchar_t _ch) { y -= min(p(0, 1), y); }
+static void cud(wchar_t _ch) { y  = min(y + p(0, 1), rows - 1); }
+static void cuf(wchar_t _ch) { x  = min(x + p(0, 1), cols - 1); }
+static void cub(wchar_t _ch) { x -= min(p(0, 1), x); }
+static void cnl(wchar_t _ch) { x = 0; cud(0); }
+static void cpl(wchar_t _ch) { x = 0; cuu(0); }
+static void cha(wchar_t _ch) { x = min(p(0, 1) - 1, cols - 1); }
+static void vpa(wchar_t _ch) { y = min(p(0, 1) - 1, rows - 1); }
+static void cup(wchar_t _ch) {
+	y = min(p(0, 1) - 1, rows - 1);
+	x = min(p(1, 1) - 1, cols - 1);
+}
+static void decsc(wchar_t _ch) {
+	save.y = y;
+	save.x = x;
+}
+static void decrc(wchar_t _ch) {
+	y = save.y;
+	x = save.x;
+}
+
+static void move(struct Cell *dst, struct Cell *src, size_t len) {
+	memmove(dst, src, sizeof(*dst) * len);
+}
+
+static void erase(struct Cell *at, struct Cell *to) {
+	for (; at < to; ++at) {
+		at->style = style;
+		at->ch = L' ';
+	}
+}
+
+static void ed(wchar_t _ch) {
+	erase(
+		(p(0, 0) == 0 ? cell(y, x) : cell(0, 0)),
+		(p(0, 0) == 1 ? cell(y, x) : cell(rows - 1, cols))
+	);
+}
+static void el(wchar_t _ch) {
+	erase(
+		(p(0, 0) == 0 ? cell(y, x) : cell(y, 0)),
+		(p(0, 0) == 1 ? cell(y, x) : cell(y, cols))
+	);
+}
+static void ech(wchar_t _ch) {
+	erase(cell(y, x), cell(y, min(x + p(0, 1), cols)));
+}
+
+static void dch(wchar_t _ch) {
+	uint n = min(p(0, 1), cols - x);
+	move(cell(y, x), cell(y, x + n), cols - x - n);
+	erase(cell(y, cols - n), cell(y, cols));
+}
+static void ich(wchar_t _ch) {
+	uint n = min(p(0, 1), cols - x);
+	move(cell(y, x + n), cell(y, x), cols - x - n);
+	erase(cell(y, x), cell(y, x + n));
+}
+
+static struct {
+	uint top, bot;
+} scroll;
+
+static void scrollUp(uint top, uint n) {
+	n = min(n, scroll.bot - top);
+	move(cell(top, 0), cell(top + n, 0), cols * (scroll.bot - top - n));
+	erase(cell(scroll.bot - n, 0), cell(scroll.bot, 0));
+}
+
+static void scrollDown(uint top, uint n) {
+	n = min(n, scroll.bot - top);
+	move(cell(top + n, 0), cell(top, 0), cols * (scroll.bot - top - n));
+	erase(cell(top, 0), cell(top + n, 0));
+}
+
+static void decstbm(wchar_t _ch) {
+	scroll.bot = min(p(1, rows), rows);
+	scroll.top = min(p(0, 1) - 1, scroll.bot);
+}
+
+static void su(wchar_t _ch) { scrollUp(scroll.top, p(0, 1)); }
+static void sd(wchar_t _ch) { scrollDown(scroll.top, p(0, 1)); }
+static void dl(wchar_t _ch) { scrollUp(min(y, scroll.bot), p(0, 1)); }
+static void il(wchar_t _ch) { scrollDown(min(y, scroll.bot), p(0, 1)); }
+
+static void nl(wchar_t _ch) {
+	if (y + 1 == scroll.bot) {
+		scrollUp(scroll.top, 1);
+	} else {
+		y = min(y + 1, rows - 1);
+	}
+}
+static void ri(wchar_t _ch) {
+	if (y == scroll.top) {
+		scrollDown(scroll.top, 1);
+	} else {
+		if (y) y--;
+	}
+}
+
+static enum Mode {
+	BIT(Insert),
+	BIT(Wrap),
+	BIT(Cursor),
+} mode = Wrap | Cursor;
+
+static enum Mode paramMode(void) {
+	enum Mode mode = 0;
+	for (uint i = 0; i < param.n; ++i) {
+		switch (param.s[i]) {
+			break; case 4: mode |= Insert;
+			break; default: warnx("unhandled SM/RM %u", param.s[i]);
+		}
+	}
+	return mode;
+}
+
+static enum Mode paramDECMode(void) {
+	enum Mode mode = 0;
+	for (uint i = 0; i < param.n; ++i) {
+		switch (param.s[i]) {
+			break; case 1: // DECCKM
+			break; case 7: mode |= Wrap;
+			break; case 12: // "Start Blinking Cursor"
+			break; case 25: mode |= Cursor;
+			break; default: {
+				if (param.s[i] < 1000) {
+					warnx("unhandled DECSET/DECRST %u", param.s[i]);
+				}
+			}
+		}
+	}
+	return mode;
+}
+
+static void sm(wchar_t _ch) { mode |= paramMode(); }
+static void rm(wchar_t _ch) { mode &= ~paramMode(); }
+static void decset(wchar_t _ch) { mode |= paramDECMode(); }
+static void decrst(wchar_t _ch) { mode &= ~paramDECMode(); }
+
+enum {
+	Reset,
+	SetBold,
+	SetDim,
+	SetItalic,
+	SetUnderline,
+	SetBlink,
+	SetReverse = 7,
+
+	UnsetBoldDim = 22,
+	UnsetItalic,
+	UnsetUnderline,
+	UnsetBlink,
+	UnsetReverse = 27,
+
+	SetFg0 = 30,
+	SetFg7 = 37,
+	SetFg,
+	ResetFg,
+	SetBg0 = 40,
+	SetBg7 = 47,
+	SetBg,
+	ResetBg,
+
+	SetFg8 = 90,
+	SetFgF = 97,
+	SetBg8 = 100,
+	SetBgF = 107,
+
+	Color256 = 5,
+};
+
+static void sgr(wchar_t _ch) {
+	uint n = param.i + 1;
+	for (uint i = 0; i < n; ++i) {
+		switch (param.s[i]) {
+			break; case Reset: style = (struct Style) { .bg = -1, .fg = -1 };
+
+			break; case SetBold:      style.attr |= Bold; style.attr &= ~Dim;
+			break; case SetDim:       style.attr |= Dim; style.attr &= ~Bold;
+			break; case SetItalic:    style.attr |= Italic;
+			break; case SetUnderline: style.attr |= Underline;
+			break; case SetBlink:     style.attr |= Blink;
+			break; case SetReverse:   style.attr |= Reverse;
+
+			break; case UnsetBoldDim:   style.attr &= ~(Bold | Dim);
+			break; case UnsetItalic:    style.attr &= ~Italic;
+			break; case UnsetUnderline: style.attr &= ~Underline;
+			break; case UnsetBlink:     style.attr &= ~Blink;
+			break; case UnsetReverse:   style.attr &= ~Reverse;
+
+			break; case SetFg: {
+				if (++i < n && param.s[i] == Color256) {
+					if (++i < n) style.fg = param.s[i];
+				}
+			}
+			break; case SetBg: {
+				if (++i < n && param.s[i] == Color256) {
+					if (++i < n) style.bg = param.s[i];
+				}
+			}
+
+			break; case ResetFg: style.fg = -1;
+			break; case ResetBg: style.bg = -1;
+
+			break; default: {
+				uint p = param.s[i];
+				if (p >= SetFg0 && p <= SetFg7) {
+					style.fg = p - SetFg0;
+				} else if (p >= SetBg0 && p <= SetBg7) {
+					style.bg = p - SetBg0;
+				} else if (p >= SetFg8 && p <= SetFgF) {
+					style.fg = 8 + p - SetFg8;
+				} else if (p >= SetBg8 && p <= SetBgF) {
+					style.bg = 8 + p - SetBg8;
+				} else {
+					warnx("unhandled SGR %u", p);
+				}
+			}
+		}
+	}
+}
+
+static enum {
+	USASCII,
+	DECSpecial,
+} charset;
+
+static void usascii(wchar_t _ch) { charset = USASCII; }
+static void decSpecial(wchar_t _ch) { charset = DECSpecial; }
+
+static const wchar_t AltCharset[128] = {
+	['`'] = L'◆', ['a'] = L'▒', ['f'] = L'°', ['g'] = L'±', ['i'] = L'␋',
+	['j'] = L'┘', ['k'] = L'┐', ['l'] = L'┌', ['m'] = L'└', ['n'] = L'┼',
+	['o'] = L'⎺', ['p'] = L'⎻', ['q'] = L'─', ['r'] = L'⎼', ['s'] = L'⎽',
+	['t'] = L'├', ['u'] = L'┤', ['v'] = L'┴', ['w'] = L'┬', ['x'] = L'│',
+	['y'] = L'≤', ['z'] = L'≥', ['{'] = L'π', ['|'] = L'≠', ['}'] = L'£',
+	['~'] = L'·',
+};
+
+static void add(wchar_t ch) {
+	if (charset == DECSpecial && ch < 128 && AltCharset[ch]) {
+		ch = AltCharset[ch];
+	}
+
+	int width = wcwidth(ch);
+	if (width < 0) {
+		warnx("unhandled \\u%02X", ch);
+		return;
+	}
+
+	if (mode & Insert) {
+		uint n = min(width, cols - x);
+		move(cell(y, x + n), cell(y, x), cols - x - n);
+	}
+	if (mode & Wrap && x + width > cols) {
+		cr(0);
+		nl(0);
+	}
+
+	cell(y, x)->style = style;
+	cell(y, x)->ch = ch;
+	for (int i = 1; i < width && x + i < cols; ++i) {
+		cell(y, x + i)->style = style;
+		cell(y, x + i)->ch = L'\0';
+	}
+	x = min(x + width, (mode & Wrap ? cols : cols - 1));
+}
+
+static void html(void);
+static void mc(wchar_t _ch) {
+	if (p(0, 0) == 10) {
+		html();
+	} else {
+		warnx("unhandled CSI %u MC", p(0, 0));
+	}
+}
+
+static enum {
+	Data,
+	Esc,
+	G0,
+	CSI,
+	CSILt,
+	CSIEq,
+	CSIGt,
+	CSIQm,
+	CSIInter,
+	OSC,
+	OSCEsc,
+} state;
+
+static void escDefault(wchar_t ch) {
+	warnx("unhandled ESC %lc", ch);
+}
+
+static void g0Default(wchar_t ch) {
+	warnx("unhandled G0 %lc", ch);
+	charset = USASCII;
+}
+
+static void csiInter(wchar_t ch) {
+	switch (state) {
+		break; case CSI: warnx("unhandled CSI %lc ...", ch);
+		break; case CSILt: warnx("unhandled CSI < %lc ...", ch);
+		break; case CSIEq: warnx("unhandled CSI = %lc ...", ch);
+		break; case CSIGt: warnx("unhandled CSI > %lc ...", ch);
+		break; case CSIQm: warnx("unhandled CSI ? %lc ...", ch);
+		break; default: abort();
+	}
+}
+
+static void csiFinal(wchar_t ch) {
+	switch (state) {
+		break; case CSI: warnx("unhandled CSI %lc", ch);
+		break; case CSILt: warnx("unhandled CSI < %lc", ch);
+		break; case CSIEq: warnx("unhandled CSI = %lc", ch);
+		break; case CSIGt: warnx("unhandled CSI > %lc", ch);
+		break; case CSIQm: warnx("unhandled CSI ? %lc", ch);
+		break; case CSIInter: warnx("unhandled CSI ... %lc", ch);
+		break; default: abort();
+	}
+}
+
+#define S(s) break; case s: switch (ch)
+#define A(c, a, s) break; case c: a(ch); state = s
+#define D(a, s) break; default: a(ch); state = s
+static void update(wchar_t ch) {
+	switch (state) {
+		default: abort();
+
+		S(Data) {
+			A(BEL, nop, Data);
+			A(BS,  bs,  Data);
+			A(HT,  ht,  Data);
+			A(NL,  nl,  Data);
+			A(CR,  cr,  Data);
+			A(ESC, nop, Esc);
+			D(add, Data);
+		}
+
+		S(Esc) {
+			A('(', nop, G0);
+			A('7', decsc, Data);
+			A('8', decrc, Data);
+			A('=', nop, Data);
+			A('>', nop, Data);
+			A('M', ri,  Data);
+			A('[', csi, CSI);
+			A(']', nop, OSC);
+			D(escDefault, Data);
+		}
+		S(G0) {
+			A('0', decSpecial, Data);
+			A('B', usascii, Data);
+			D(g0Default, Data);
+		}
+
+		S(CSI) {
+			A(' ' ... '/', csiInter, CSIInter);
+			A('0' ... '9', csiDigit, CSI);
+			A(':', nop, CSI);
+			A(';', csiSep, CSI);
+			A('<', nop, CSILt);
+			A('=', nop, CSIEq);
+			A('>', nop, CSIGt);
+			A('?', nop, CSIQm);
+			A('@', ich, Data);
+			A('A', cuu, Data);
+			A('B', cud, Data);
+			A('C', cuf, Data);
+			A('D', cub, Data);
+			A('E', cnl, Data);
+			A('F', cpl, Data);
+			A('G', cha, Data);
+			A('H', cup, Data);
+			A('J', ed,  Data);
+			A('K', el,  Data);
+			A('L', il,  Data);
+			A('M', dl,  Data);
+			A('P', dch, Data);
+			A('S', su,  Data);
+			A('T', sd,  Data);
+			A('X', ech, Data);
+			A('d', vpa, Data);
+			A('h', sm,  Data);
+			A('i', mc,  Data);
+			A('l', rm,  Data);
+			A('m', sgr, Data);
+			A('r', decstbm, Data);
+			A('t', nop, Data);
+			D(csiFinal, Data);
+		}
+
+		S(CSILt ... CSIGt) {
+			A(' ' ... '/', csiInter, CSIInter);
+			A('0' ... '9', csiDigit, state);
+			A(':', nop, state);
+			A(';', csiSep, state);
+			D(csiFinal, Data);
+		}
+
+		S(CSIQm) {
+			A(' ' ... '/', csiInter, CSIInter);
+			A('0' ... '9', csiDigit, CSIQm);
+			A(':', nop, CSIQm);
+			A(';', csiSep, CSIQm);
+			A('h', decset, Data);
+			A('l', decrst, Data);
+			D(csiFinal, Data);
+		}
+
+		S(CSIInter) {
+			D(csiFinal, Data);
+		}
+
+		S(OSC) {
+			A(BEL, nop, Data);
+			A(ESC, nop, OSCEsc);
+			D(nop, OSC);
+		}
+		S(OSCEsc) {
+			A('\\', nop, Data);
+			D(nop, OSC);
+		}
+	}
+}
+
+static bool bright;
+static int defaultBg = 0;
+static int defaultFg = 7;
+
+static void span(const struct Style *prev, const struct Cell *cell) {
+	struct Style style = cell->style;
+	if (!prev || memcmp(prev, &style, sizeof(*prev))) {
+		if (prev) printf("</span>");
+		if (style.bg < 0) style.bg = defaultBg;
+		if (style.fg < 0) style.fg = defaultFg;
+		if (bright && style.attr & Bold) {
+			if (style.fg < 8) style.fg += 8;
+			style.attr ^= Bold;
+		}
+		printf(
+			"<span style=\"%s%s%s\" class=\"bg%u fg%u\">",
+			(style.attr & Bold ? "font-weight:bold;" : ""),
+			(style.attr & Italic ? "font-style:italic;" : ""),
+			(style.attr & Underline ? "text-decoration:underline;" : ""),
+			(style.attr & Reverse ? style.fg : style.bg),
+			(style.attr & Reverse ? style.bg : style.fg)
+		);
+	}
+	switch (cell->ch) {
+		break; case '&': printf("&amp;");
+		break; case '<': printf("&lt;");
+		break; case '>': printf("&gt;");
+		break; default:  printf("%lc", (wint_t)cell->ch);
+	}
+}
+
+static bool mediaCopy;
+static void html(void) {
+	mediaCopy = true;
+	if (mode & Cursor) cell(y, x)->style.attr ^= Reverse;
+	printf(
+		"<pre style=\"width: %uch;\" class=\"bg%u fg%u\">",
+		cols, defaultBg, defaultFg
+	);
+	for (uint y = 0; y < rows; ++y) {
+		for (uint x = 0; x < cols; ++x) {
+			if (!cell(y, x)->ch) continue;
+			span(x ? &cell(y, x - 1)->style : NULL, cell(y, x));
+		}
+		printf("</span>\n");
+	}
+	printf("</pre>\n");
+	if (mode & Cursor) cell(y, x)->style.attr ^= Reverse;
+}
+
+int main(int argc, char *argv[]) {
+	setlocale(LC_CTYPE, "");
+
+	bool debug = false;
+	bool size = false;
+	bool hide = false;
+
+	int opt;
+	while (0 < (opt = getopt(argc, argv, "Bb:df:h:nsw:"))) {
+		switch (opt) {
+			break; case 'B': bright = true;
+			break; case 'b': defaultBg = strtol(optarg, NULL, 0);
+			break; case 'd': debug = true;
+			break; case 'f': defaultFg = strtol(optarg, NULL, 0);
+			break; case 'h': rows = strtoul(optarg, NULL, 0);
+			break; case 'n': hide = true;
+			break; case 's': size = true;
+			break; case 'w': cols = strtoul(optarg, NULL, 0);
+			break; default:  return EX_USAGE;
+		}
+	}
+
+	FILE *file = stdin;
+	if (optind < argc) {
+		file = fopen(argv[optind], "r");
+		if (!file) err(EX_NOINPUT, "%s", argv[optind]);
+	}
+
+	if (size) {
+		struct winsize window;
+		int error = ioctl(STDERR_FILENO, TIOCGWINSZ, &window);
+		if (error) err(EX_IOERR, "ioctl");
+		rows = window.ws_row;
+		cols = window.ws_col;
+	}
+	scroll.bot = rows;
+
+	cells = calloc(rows * cols, sizeof(*cells));
+	if (!cells) err(EX_OSERR, "calloc");
+	erase(cell(0, 0), cell(rows - 1, cols));
+
+	wint_t ch;
+	while (WEOF != (ch = getwc(file))) {
+		uint prev = state;
+		update(ch);
+		if (debug && state != prev && state == Data) html();
+	}
+	if (ferror(file)) err(EX_IOERR, "getwc");
+
+	if (!mediaCopy) {
+		if (hide) mode &= ~Cursor;
+		html();
+	}
+}
diff --git a/bin/sup.sh b/bin/sup.sh
new file mode 100644
index 00000000..b7f28bc7
--- /dev/null
+++ b/bin/sup.sh
@@ -0,0 +1,169 @@
+#!/bin/sh
+set -eu
+
+service=$1
+email=${2:-$(git config fetchemail.imapUser)}
+
+generate() {
+	openssl rand -base64 33
+}
+copy() {
+	printf '%s' "$1" | pbcopy
+}
+
+asciinema() {
+	echo 'Fetching CSRF token...'
+	jar=$(mktemp -t sup)
+	trap 'rm "${jar}"' EXIT
+	csrf=$(
+		curl -Ss -c "${jar}" 'https://asciinema.org/login/new' |
+		sed -n 's/.*name="_csrf_token".*value="\([^"]*\)".*/\1/p'
+	)
+	echo 'Submitting form...'
+	curl -Ss -X POST -b "${jar}" \
+		-F "_csrf_token=${csrf}" -F "login[email]=${email}" \
+		'https://asciinema.org/login' \
+		>/dev/null
+	echo 'Waiting for email...'
+	url=$(
+		git fetch-email -i -M Trash \
+			-F 'hello@asciinema.org' -T "${email}" \
+			-S 'Login to asciinema.org' |
+		grep -m 1 '^https://asciinema\.org/session/new'
+	)
+	open "${url}"
+}
+
+bugzilla() {
+	echo 'Fetching CSRF token...'
+	csrf=$(
+		curl -Ss "${bugzillaBase}/" |
+		sed -n '
+			/name="token"/N
+			s/.*name="token"[[:space:]]*value="\([^"]*\)".*/\1/p
+		' | head -n 1
+	)
+	echo 'Submitting form...'
+	curl -Ss -X POST \
+		-F "loginname=${email}" -F "token=${csrf}" -F 'a=reqpw' \
+		"${bugzillaBase}/token.cgi" \
+		>/dev/null
+	echo 'Waiting for email...'
+	token=$(
+		git fetch-email -i -M Trash \
+			-F "${bugzillaFrom}" -T "${email}" \
+			-S 'Bugzilla Change Password Request' |
+		sed -n 's/.*t=3D\([^&]*\).*/\1/p' |
+		head -n 1
+	)
+	password=$(generate)
+	echo 'Setting password...'
+	curl -Ss -X POST \
+		-F "t=${token}" -F 'a=chgpw' \
+		-F "password=${password}" -F "matchpassword=${password}" \
+		"${bugzillaBase}/token.cgi" \
+		>/dev/null
+	copy "${password}"
+	open "${bugzillaBase}/"
+}
+
+freebsdbugzilla() {
+	bugzillaBase='https://bugs.freebsd.org/bugzilla'
+	bugzillaFrom='bugzilla-noreply@freebsd.org'
+	bugzilla
+}
+
+discogs() {
+	echo 'Submitting form...'
+	curl -Ss -X POST \
+		-F "email=${email}" -F 'Action.EmailResetInstructions=submit' \
+		'https://www.discogs.com/users/forgot_password' \
+		>/dev/null
+	echo 'Waiting for email...'
+	url=$(
+		git fetch-email -i -M Trash \
+			-F 'noreply@discogs.com' -T "${email}" \
+			-S 'Discogs Account Password Reset Instructions' |
+		sed -n 's/^To proceed, follow the instructions here: \(.*\)/\1/p'
+	)
+	echo 'Fetching token...'
+	token=$(curl -ISs --url "${url}" | sed -n 's/.*[?]token=\([^&]*\).*/\1/p')
+	password=$(generate)
+	echo 'Setting password...'
+	curl -Ss -X POST \
+		-F "token=${token}" \
+		-F "password0=${password}" -F "password1=${password}" \
+		-F 'Action.ChangePassword=submit' \
+		'https://www.discogs.com/users/forgot_password' \
+		>/dev/null
+	copy "${password}"
+	open 'https://discogs.com/login'
+}
+
+liberapay() {
+	echo 'Fetching CSRF token...'
+	csrf=$(
+		curl -Ss 'https://liberapay.com/sign-in' |
+		sed -n 's/.*name="csrf_token".*value="\([^"]*\)".*/\1/p'
+	)
+	echo 'Submitting form...'
+	curl -Ss -X POST \
+		-b "csrf_token=${csrf}" -F "csrf_token=${csrf}" \
+		-F "log-in.id=${email}" \
+		'https://liberapay.com/sign-in' \
+		>/dev/null
+	echo 'Waiting for email...'
+	url=$(
+		git fetch-email -i -M Trash \
+			-F 'support@liberapay.com' -T "${email}" \
+			-S 'Log in to Liberapay' |
+		grep -m 1 '^https://liberapay\.com/'
+	)
+	open "${url}"
+}
+
+lobsters() {
+	: ${lobstersBase:=https://lobste.rs}
+	: ${lobstersFrom:=nobody@lobste.rs}
+	echo 'Fetching CSRF token...'
+	csrf=$(
+		curl -Ss "${lobstersBase}/login/forgot_password" |
+		sed -n 's/.*name="authenticity_token" value="\([^"]*\)".*/\1/p'
+	)
+	echo 'Submitting form...'
+	curl -Ss -X POST \
+		-F "authenticity_token=${csrf}" \
+		-F "email=${email}" -F 'commit=submit' \
+		"${lobstersBase}/login/reset_password" \
+		>/dev/null
+	echo 'Waiting for email...'
+	token=$(
+		git fetch-email -i -M Trash \
+			-F "${lobstersFrom}" -T "${email}" \
+			-S 'Reset your password' |
+		sed -n 's|^https://.*[?]token=\(.*\)|\1|p'
+	)
+	echo 'Fetching CSRF token...'
+	csrf=$(
+		curl -Ss "${lobstersBase}/login/set_new_password?token=${token}" |
+		sed -n 's/.*name="authenticity_token" value="\([^"]*\)".*/\1/p'
+	)
+	password=$(generate)
+	echo 'Setting password...'
+	curl -Ss -X POST \
+		-F "authenticity_token=${csrf}" -F "token=${token}" \
+		-F "password=${password}" -F "password_confirmation=${password}" \
+		-F 'commit=submit' \
+		"${lobstersBase}/login/set_new_password" \
+		>/dev/null
+	copy "${password}"
+	open "${lobstersBase}/login"
+}
+
+tildenews() {
+	lobstersBase='https://tilde.news'
+	lobstersFrom='nobody@tilde.news'
+	lobsters
+}
+
+$service
diff --git a/bin/title.c b/bin/title.c
new file mode 100644
index 00000000..82f89d95
--- /dev/null
+++ b/bin/title.c
@@ -0,0 +1,211 @@
+/* Copyright (C) 2019  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 <curl/curl.h>
+#include <err.h>
+#include <locale.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+
+static regex_t regex(const char *pattern, int flags) {
+	regex_t regex;
+	int error = regcomp(&regex, pattern, REG_EXTENDED | flags);
+	if (!error) return regex;
+
+	char buf[256];
+	regerror(error, &regex, buf, sizeof(buf));
+	errx(EX_SOFTWARE, "regcomp: %s: %s", buf, pattern);
+}
+
+static const struct Entity {
+	wchar_t ch;
+	const char *name;
+} Entities[] = {
+	{ L'"', "&quot;" },
+	{ L'&', "&amp;" },
+	{ L'<', "&lt;" },
+	{ L'>', "&gt;" },
+	{ L'␤', "&#10;" },
+};
+
+static wchar_t entity(const char *name) {
+	for (size_t i = 0; i < sizeof(Entities) / sizeof(Entities[0]); ++i) {
+		struct Entity entity = Entities[i];
+		if (strncmp(name, entity.name, strlen(entity.name))) continue;
+		return entity.ch;
+	}
+	if (!strncmp(name, "&#x", 3)) return strtoul(&name[3], NULL, 16);
+	if (!strncmp(name, "&#", 2)) return strtoul(&name[2], NULL, 10);
+	return 0;
+}
+
+static const char EntityPattern[] = {
+	"[[:space:]]+|&([[:alpha:]]+|#([[:digit:]]+|x[[:xdigit:]]+));"
+};
+static regex_t EntityRegex;
+
+static void showTitle(const char *title) {
+	regmatch_t match = {0};
+	for (; *title; title += match.rm_eo) {
+		if (regexec(&EntityRegex, title, 1, &match, 0)) break;
+		if (title[match.rm_so] != '&') {
+			printf("%.*s ", (int)match.rm_so, title);
+			continue;
+		}
+		wchar_t ch = entity(&title[match.rm_so]);
+		if (ch) {
+			printf("%.*s%lc", (int)match.rm_so, title, (wint_t)ch);
+		} else {
+			printf("%.*s", (int)match.rm_eo, title);
+		}
+	}
+	printf("%s\n", title);
+}
+
+static CURL *curl;
+static bool title;
+static struct {
+	char buf[64 * 1024];
+	size_t len;
+} body;
+
+// HE COMES
+static const char TitlePattern[] = "<title>([^<]*)</title>";
+static regex_t TitleRegex;
+
+static size_t handleBody(char *buf, size_t size, size_t nitems, void *user) {
+	(void)user;
+	size_t len = size * nitems;
+	size_t cap = sizeof(body.buf) - body.len - 1;
+	size_t new = (len < cap ? len : cap);
+	if (title || !new) return len;
+
+	memcpy(&body.buf[body.len], buf, new);
+	body.len += new;
+	body.buf[body.len] = '\0';
+
+	regmatch_t match[2];
+	if (regexec(&TitleRegex, body.buf, 2, match, 0)) return len;
+	body.buf[match[1].rm_eo] = '\0';
+	showTitle(&body.buf[match[1].rm_so]);
+	title = true;
+
+	return len;
+}
+
+static CURLcode fetchTitle(const char *url) {
+	CURLcode code = curl_easy_setopt(curl, CURLOPT_URL, url);
+	if (code) return code;
+
+	curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
+	code = curl_easy_perform(curl);
+	if (code) return code;
+
+	char *type;
+	code = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &type);
+	if (code) return code;
+	if (!type || strncmp(type, "text/html", 9)) return CURLE_OK;
+
+	char *dest;
+	curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &dest);
+	dest = strdup(dest);
+	if (!dest) err(EX_OSERR, "strdup");
+
+	code = curl_easy_setopt(curl, CURLOPT_URL, dest);
+	if (code) return code;
+	free(dest);
+
+	body.len = 0;
+	title = false;
+	curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
+	code = curl_easy_perform(curl);
+	return code;
+}
+
+int main(int argc, char *argv[]) {
+	EntityRegex = regex(EntityPattern, 0);
+	TitleRegex = regex(TitlePattern, REG_ICASE);
+
+	setlocale(LC_CTYPE, "");
+	setlinebuf(stdout);
+
+	CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
+	if (code) errx(EX_OSERR, "curl_global_init: %s", curl_easy_strerror(code));
+
+	curl = curl_easy_init();
+	if (!curl) errx(EX_SOFTWARE, "curl_easy_init");
+
+	static char error[CURL_ERROR_SIZE];
+	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
+
+	curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+	curl_easy_setopt(
+		curl, CURLOPT_USERAGENT,
+		"curl/7.54.0 facebookexternalhit/1.1 Twitterbot/1.0"
+	);
+	curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
+	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+	curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 3L);
+	curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
+
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handleBody);
+
+	bool exclude = false;
+	regex_t excludeRegex;
+
+	int opt;
+	while (0 < (opt = getopt(argc, argv, "x:v"))) {
+		switch (opt) {
+			break; case 'x': {
+				exclude = true;
+				excludeRegex = regex(optarg, REG_NOSUB);
+			}
+			break; case 'v': curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+			break; default:  return EX_USAGE;
+		}
+	}
+
+	if (optind < argc) {
+		code = fetchTitle(argv[optind]);
+		if (!code) return EX_OK;
+		errx(EX_DATAERR, "curl_easy_perform: %s", error);
+	}
+
+	char *buf = NULL;
+	size_t cap = 0;
+
+	regex_t urlRegex = regex("https?://([^[:space:]>\"()]|[(][^)]*[)])+", 0);
+	while (0 < getline(&buf, &cap, stdin)) {
+		regmatch_t match = {0};
+		for (char *ptr = buf; *ptr; ptr += match.rm_eo) {
+			if (regexec(&urlRegex, ptr, 1, &match, 0)) break;
+			ptr[match.rm_eo] = '\0';
+			const char *url = &ptr[match.rm_so];
+			if (!exclude || regexec(&excludeRegex, url, 0, NULL, 0)) {
+				code = fetchTitle(url);
+				if (code) warnx("curl_easy_perform: %s", error);
+			}
+			ptr[match.rm_eo] = ' ';
+		}
+	}
+	if (ferror(stdin)) err(EX_IOERR, "getline");
+}
diff --git a/bin/typer.c b/bin/typer.c
new file mode 100644
index 00000000..76eab784
--- /dev/null
+++ b/bin/typer.c
@@ -0,0 +1,197 @@
+/* Copyright (C) 2021  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/>.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this Program, or any covered work, by linking or
+ * combining it with LibreSSL (or a modified version of that library),
+ * containing parts covered by the terms of the OpenSSL License and the
+ * original SSLeay license, the licensors of this Program grant you
+ * additional permission to convey the resulting work. Corresponding
+ * Source for a non-source form of such a combination shall include the
+ * source code for the parts of LibreSSL used as well as that of the
+ * covered work.
+ */
+
+#include <err.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sysexits.h>
+#include <tls.h>
+#include <unistd.h>
+
+static bool verbose;
+static struct tls *client;
+static const char *chan;
+
+static void clientWrite(const char *ptr, size_t len) {
+	if (verbose) printf("%.*s", (int)len, ptr);
+	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));
+		ptr += ret;
+		len -= ret;
+	}
+}
+
+static void format(const char *format, ...) {
+	char buf[1024];
+	va_list ap;
+	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");
+	clientWrite(buf, len);
+}
+
+static bool joined;
+static bool reverse;
+static bool copy;
+
+static void handle(char *line) {
+	char *tags = NULL;
+	char *origin = NULL;
+	if (line && line[0] == '@') tags = 1 + strsep(&line, " ");
+	if (line && line[0] == ':') origin = 1 + strsep(&line, " ");
+	char *cmd = strsep(&line, " ");
+	if (!cmd) return;
+	if (!strcmp(cmd, "CAP")) {
+		char *param = strsep(&cmd, " ");
+		if (!param) errx(EX_PROTOCOL, "CAP missing parameter");
+		if (!strcmp(param, "NAK")) {
+			errx(EX_CONFIG, "server does not support message-tags");
+		}
+		format("CAP END\r\n");
+	} else if (!strcmp(cmd, "001")) {
+		format("JOIN %s\r\n", chan);
+		joined = true;
+	} else if (!strcmp(cmd, "PING")) {
+		format("PONG %s\r\n", line);
+	} else if (copy && !strcmp(cmd, "TAGMSG") && tags) {
+		if (strstr(tags, "typing=")) {
+			format("@%s TAGMSG %s\r\n", tags, chan);
+		}
+	} else if (reverse && !strcmp(cmd, "TAGMSG") && tags && origin) {
+		char *nick = strsep(&origin, "!");
+		if (strstr(tags, "typing=active")) {
+			format("PRIVMSG %s :\u2328\uFE0F %s is typing!\r\n", chan, nick);
+		} else if (strstr(tags, "typing=paused")) {
+			format("PRIVMSG %s :\U0001F914 %s is thinking!\r\n", chan, nick);
+		} else if (strstr(tags, "typing=done")) {
+			format("PRIVMSG %s :\u270B %s stopped typing!\r\n", chan, nick);
+		}
+	}
+}
+
+static void timer(int sig) {
+	(void)sig;
+	if (!joined) return;
+	const char *status = (arc4random_uniform(4) ? "active" : "done");
+	format(
+		"@+typing=%s;+draft/typing=%s TAGMSG %s\r\n",
+		status, status, chan
+	);
+}
+
+int main(int argc, char *argv[]) {
+	const char *host = NULL;
+	const char *port = "6697";
+	const char *cert = NULL;
+	const char *nick = "typer";
+	const char *user = "typer";
+	bool passive = false;
+
+	for (int opt; 0 < (opt = getopt(argc, argv, "CPRc:n:p:u:v"));) {
+		switch (opt) {
+			break; case 'C': copy = true;
+			break; case 'P': passive = true;
+			break; case 'R': reverse = true;
+			break; case 'c': cert = optarg;
+			break; case 'n': nick = optarg;
+			break; case 'p': port = optarg;
+			break; case 'u': user = optarg;
+			break; case 'v': verbose = true;
+			break; default:  return EX_USAGE;
+		}
+	}
+	if (argc - optind < 2) errx(EX_USAGE, "host and chan required");
+	host = argv[optind];
+	chan = argv[optind + 1];
+
+	client = tls_client();
+	if (!client) errx(EX_SOFTWARE, "tls_client");
+
+	struct tls_config *config = tls_config_new();
+	if (!config) errx(EX_SOFTWARE, "tls_config_new");
+
+	if (cert) {
+		int error = tls_config_set_keypair_file(config, cert, cert);
+		if (error) errx(EX_CONFIG, "%s: %s", cert, tls_config_error(config));
+	}
+
+	int error = tls_configure(client, config);
+	if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client));
+	tls_config_free(config);
+
+	error = tls_connect(client, host, port);
+	if (error) errx(EX_UNAVAILABLE, "tls_connect: %s", tls_error(client));
+
+	format(
+		"CAP REQ :message-tags%s\r\n"
+		"NICK %s\r\n"
+		"USER %s 0 * :typer\r\n",
+		(passive ? " causal.agency/passive" : ""),
+		nick, user
+	);
+
+	if (!copy && !reverse) {
+		signal(SIGALRM, timer);
+		struct itimerval itimer = {
+			.it_interval.tv_sec = 5,
+			.it_value.tv_sec = 5
+		};
+		error = setitimer(ITIMER_REAL, &itimer, NULL);
+		if (error) err(EX_OSERR, "setitimer");
+	}
+
+	size_t len = 0;
+	char buf[4096];
+	for (;;) {
+		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) errx(EX_UNAVAILABLE, "server disconnected");
+		len += read;
+
+		char *crlf;
+		char *line = buf;
+		for (;;) {
+			crlf = memmem(line, &buf[len] - line, "\r\n", 2);
+			if (!crlf) break;
+			crlf[0] = '\0';
+			if (verbose) printf("%s\n", line);
+			handle(line);
+			line = &crlf[2];
+		}
+		len -= line - buf;
+		memmove(buf, line, len);
+	}
+}
diff --git a/bin/up.sh b/bin/up.sh
new file mode 100644
index 00000000..f55213d4
--- /dev/null
+++ b/bin/up.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+set -eu
+
+readonly Host='temp.causal.agency'
+
+upload() {
+	local src ext ts rand url
+	src=$1
+	ext=${src##*.}
+	ts=$(date +'%s')
+	rand=$(openssl rand -hex 4)
+	url=$(printf '%s/%x%s.%s' "$Host" "$ts" "$rand" "$ext")
+	scp -q "$src" "${Host}:/usr/local/www/${url}"
+	echo "https://${url}"
+}
+
+temp() {
+	temp=$(mktemp -d)
+	trap "rm -r '$temp'" EXIT
+}
+
+uploadText() {
+	temp
+	cat > "${temp}/input.txt"
+	upload "${temp}/input.txt"
+}
+
+uploadCommand() {
+	temp
+	echo "$ $*" > "${temp}/exec.txt"
+	"$@" >> "${temp}/exec.txt" 2>&1 || true
+	upload "${temp}/exec.txt"
+}
+
+uploadHilex() {
+	temp
+	hilex -f html -o document,tab=4 "$@" > "${temp}/hilex.html"
+	upload "${temp}/hilex.html"
+}
+
+uploadScreen() {
+	temp
+	if type screencapture >/dev/null; then
+		screencapture -i "$@" "${temp}/capture.png"
+	else
+		scrot -s "$@" "${temp}/capture.png"
+	fi
+	pngo "${temp}/capture.png" || true
+	upload "${temp}/capture.png"
+}
+
+uploadTerminal() {
+	temp
+	cat > "${temp}/term.html" <<-EOF
+	<!DOCTYPE html>
+	<title>${1}</title>
+	<style>
+	$(scheme -s)
+	</style>
+	EOF
+	ptee "$@" | shotty -Bs >> "${temp}/term.html"
+	upload "${temp}/term.html"
+}
+
+while getopts 'chst' opt; do
+	case "$opt" in
+		(c) fn=uploadCommand;;
+		(h) fn=uploadHilex;;
+		(s) fn=uploadScreen;;
+		(t) fn=uploadTerminal;;
+		(?) exit 1;;
+	esac
+done
+shift $((OPTIND - 1))
+[ $# -eq 0 ] && : ${fn:=uploadText}
+: ${fn:=upload}
+
+url=$($fn "$@")
+printf '%s' "$url" | pbcopy || true
+echo "$url"
diff --git a/bin/when.y b/bin/when.y
new file mode 100644
index 00000000..d647d193
--- /dev/null
+++ b/bin/when.y
@@ -0,0 +1,255 @@
+/* Copyright (C) 2019  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 <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <time.h>
+
+static void yyerror(const char *str);
+static int yylex(void);
+
+#define YYSTYPE struct tm
+
+static const char *Days[7] = {
+	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
+};
+
+static const char *Months[12] = {
+	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+};
+
+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");
+	return *norm;
+}
+
+static struct tm today(void) {
+	time_t now = time(NULL);
+	struct tm *local = localtime(&now);
+	if (!local) err(EX_OSERR, "localtime");
+	struct tm date = {
+		.tm_year = local->tm_year,
+		.tm_mon = local->tm_mon,
+		.tm_mday = local->tm_mday,
+	};
+	return normalize(date);
+}
+
+static struct tm monthDay(int month, int day) {
+	struct tm date = today();
+	date.tm_mon = month;
+	date.tm_mday = day;
+	return normalize(date);
+}
+
+static struct tm monthDayYear(int month, int day, int year) {
+	struct tm date = today();
+	date.tm_mon = month;
+	date.tm_mday = day;
+	date.tm_year = year - 1900;
+	return normalize(date);
+}
+
+static struct tm weekDay(int day) {
+	struct tm date = today();
+	date.tm_mday += day - date.tm_wday;
+	return normalize(date);
+}
+
+static struct tm scalarAdd(struct tm a, struct tm b) {
+	a.tm_mday += b.tm_mday;
+	a.tm_mon += b.tm_mon;
+	a.tm_year += b.tm_year;
+	return a;
+}
+
+static struct tm scalarSub(struct tm a, struct tm b) {
+	a.tm_mday -= b.tm_mday;
+	a.tm_mon -= b.tm_mon;
+	a.tm_year -= b.tm_year;
+	return a;
+}
+
+static struct tm dateAdd(struct tm date, struct tm scalar) {
+	return normalize(scalarAdd(date, scalar));
+}
+
+static struct tm dateSub(struct tm date, struct tm scalar) {
+	return normalize(scalarSub(date, scalar));
+}
+
+static struct tm dateDiff(struct tm a, struct tm b) {
+	struct tm diff = {
+		.tm_year = a.tm_year - b.tm_year,
+		.tm_mon = a.tm_mon - b.tm_mon,
+		.tm_mday = a.tm_mday - b.tm_mday,
+	};
+	if (a.tm_mon < b.tm_mon) {
+		diff.tm_year--;
+		diff.tm_mon += 12;
+	}
+	if (a.tm_mday < b.tm_mday) {
+		diff.tm_mon--;
+		diff.tm_mday = 0;
+		while (dateAdd(b, diff).tm_mday != a.tm_mday) diff.tm_mday++;
+	}
+	time_t atime = timegm(&a), btime = timegm(&b);
+	diff.tm_yday = (atime - btime) / 24 / 60 / 60;
+	return diff;
+}
+
+static void printDate(struct tm date) {
+	printf(
+		"%s %s %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 (scalar.tm_year) printf("%dy ", scalar.tm_year);
+	if (scalar.tm_mon) printf("%dm ", scalar.tm_mon);
+	if (scalar.tm_mday % 7) {
+		printf("%dd ", scalar.tm_mday);
+	} else if (scalar.tm_mday) {
+		printf("%dw ", scalar.tm_mday / 7);
+	}
+	if (scalar.tm_yday && scalar.tm_mon) {
+		if (scalar.tm_yday % 7 == 0) {
+			printf("(%dw) ", scalar.tm_yday / 7);
+		}
+		printf("(%dd) ", scalar.tm_yday);
+	}
+	printf("\n");
+}
+
+%}
+
+%token Number Month Day
+%left '+' '-'
+%right '<' '>'
+
+%%
+
+expr:
+	date { printDate($1); }
+	| scalar { printScalar($1); }
+	;
+
+date:
+	dateLit
+	| '(' date ')' { $$ = $2; }
+	| '<' date { $$ = dateSub($2, Week); }
+	| '>' date { $$ = dateAdd($2, Week); }
+	| date '+' scalar { $$ = dateAdd($1, $3); }
+	| date '-' scalar { $$ = dateSub($1, $3); }
+	;
+
+scalar:
+	scalarLit
+	| '(' scalar ')' { $$ = $2; }
+	| scalar '+' scalar { $$ = scalarAdd($1, $3); }
+	| scalar '-' scalar { $$ = scalarSub($1, $3); }
+	| date '-' date { $$ = dateDiff($1, $3); }
+	;
+
+dateLit:
+	{ $$ = today(); }
+	| '.' { $$ = today(); }
+	| Month Number { $$ = monthDay($1.tm_mon, $2.tm_sec); }
+	| Month Number Number { $$ = monthDayYear($1.tm_mon, $2.tm_sec, $3.tm_sec); }
+	| Day { $$ = weekDay($1.tm_wday); }
+	;
+
+scalarLit:
+	Number 'd' { $$ = (struct tm) { .tm_mday = $1.tm_sec }; }
+	| Number 'w' { $$ = (struct tm) { .tm_mday = 7 * $1.tm_sec }; }
+	| Number 'm' { $$ = (struct tm) { .tm_mon = $1.tm_sec }; }
+	| Number 'y' { $$ = (struct tm) { .tm_year = $1.tm_sec }; }
+	;
+
+%%
+
+static void yyerror(const char *str) {
+	warnx("%s", str);
+}
+
+static const char *input;
+
+static int yylex(void) {
+	while (isspace(*input)) input++;
+	if (!*input) return EOF;
+
+	if (isdigit(*input)) {
+		char *rest;
+		yylval.tm_sec = strtol(input, &rest, 10);
+		input = rest;
+		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;
+	}
+
+	for (int i = 0; i < 12; ++i) {
+		if (strncasecmp(input, Months[i], 3)) continue;
+		while (isalpha(*input)) input++;
+		yylval.tm_mon = i;
+		return Month;
+	}
+
+	return *input++;
+}
+
+int main(int argc, char *argv[]) {
+	if (argc > 1) {
+		input = argv[1];
+		return yyparse();
+	}
+
+	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;
+
+		if (today().tm_mday != date.tm_mday) {
+			warnx("the date has changed");
+			date = today();
+		}
+
+		input = line;
+		yyparse();
+		printf("\n");
+	}
+}
diff --git a/bin/xx.c b/bin/xx.c
new file mode 100644
index 00000000..6d04f2f5
--- /dev/null
+++ b/bin/xx.c
@@ -0,0 +1,142 @@
+/* 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 <ctype.h>
+#include <err.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+typedef unsigned char byte;
+
+static bool zero(const byte *ptr, size_t size) {
+	for (size_t i = 0; i < size; ++i) {
+		if (ptr[i]) return false;
+	}
+	return true;
+}
+
+static struct {
+	size_t cols;
+	size_t group;
+	size_t blank;
+	bool ascii;
+	bool offset;
+	bool skip;
+} options = { 16, 8, 0, true, true, false };
+
+static void dump(FILE *file) {
+	bool skip = false;
+
+	byte buf[options.cols];
+	size_t offset = 0;
+	for (
+		size_t size;
+		(size = fread(buf, 1, sizeof(buf), file));
+		offset += size
+	) {
+		if (options.skip) {
+			if (zero(buf, size)) {
+				if (!skip) printf("*\n");
+				skip = true;
+				continue;
+			} else {
+				skip = false;
+			}
+		}
+
+		if (options.blank) {
+			if (offset && offset % options.blank == 0) {
+				printf("\n");
+			}
+		}
+
+		if (options.offset) {
+			printf("%08zX:  ", offset);
+		}
+
+		for (size_t i = 0; i < sizeof(buf); ++i) {
+			if (options.group) {
+				if (i && !(i % options.group)) {
+					printf(" ");
+				}
+			}
+			if (i < size) {
+				printf("%02hhX ", buf[i]);
+			} else {
+				printf("   ");
+			}
+		}
+
+		if (options.ascii) {
+			printf(" ");
+			for (size_t i = 0; i < size; ++i) {
+				if (options.group) {
+					if (i && !(i % options.group)) {
+						printf(" ");
+					}
+				}
+				printf("%c", isprint(buf[i]) ? buf[i] : '.');
+			}
+		}
+
+		printf("\n");
+	}
+}
+
+static void undump(FILE *file) {
+	byte c;
+	int match;
+	while (0 < (match = fscanf(file, " %hhx", &c))) {
+		printf("%c", c);
+	}
+	if (!match) errx(EX_DATAERR, "invalid input");
+}
+
+int main(int argc, char *argv[]) {
+	bool reverse = false;
+	const char *path = NULL;
+
+	int opt;
+	while (0 < (opt = getopt(argc, argv, "ac:g:p:rsz"))) {
+		switch (opt) {
+			break; case 'a': options.ascii ^= true;
+			break; case 'c': options.cols = strtoul(optarg, NULL, 0);
+			break; case 'g': options.group = strtoul(optarg, NULL, 0);
+			break; case 'p': options.blank = strtoul(optarg, NULL, 0);
+			break; case 'r': reverse = true;
+			break; case 's': options.offset ^= true;
+			break; case 'z': options.skip ^= true;
+			break; default: return EX_USAGE;
+		}
+	}
+	if (argc > optind) path = argv[optind];
+	if (!options.cols) return EX_USAGE;
+
+	FILE *file = path ? fopen(path, "r") : stdin;
+	if (!file) err(EX_NOINPUT, "%s", path);
+
+	if (reverse) {
+		undump(file);
+	} else {
+		dump(file);
+	}
+	if (ferror(file)) err(EX_IOERR, "%s", path);
+
+	return EX_OK;
+}
diff --git a/doc/pdf/.gitignore b/doc/pdf/.gitignore
new file mode 100644
index 00000000..a1363379
--- /dev/null
+++ b/doc/pdf/.gitignore
@@ -0,0 +1 @@
+*.pdf
diff --git a/doc/pdf/Makefile b/doc/pdf/Makefile
new file mode 100644
index 00000000..7afbdcf2
--- /dev/null
+++ b/doc/pdf/Makefile
@@ -0,0 +1,31 @@
+PDFS += abi.pdf
+PDFS += c11.pdf
+PDFS += elf.pdf
+PDFS += intel-64-opt.pdf
+PDFS += intel-64-sdm-vol-1.pdf
+PDFS += intel-64-sdm-vol-2.pdf
+PDFS += intel-64-sdm-vol-3.pdf
+PDFS += intel-64-sdm-vol-4.pdf
+PDFS += multiboot.pdf
+
+ELF = https://refspecs.linuxbase.org/elf
+INTEL = https://software.intel.com/sites/default/files/managed
+
+URL.abi.pdf = ${ELF}/x86_64-abi-0.99.pdf
+URL.c11.pdf = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
+URL.elf.pdf = ${ELF}/elf.pdf
+URL.intel-64-opt.pdf = ${INTEL}/9e/bc/64-ia-32-architectures-optimization-manual.pdf
+URL.intel-64-sdm-vol-1.pdf = ${INTEL}/a4/60/253665-sdm-vol-1.pdf
+URL.intel-64-sdm-vol-2.pdf = ${INTEL}/a4/60/325383-sdm-vol-2abcd.pdf
+URL.intel-64-sdm-vol-3.pdf = ${INTEL}/a4/60/325384-sdm-vol-3abcd.pdf
+URL.intel-64-sdm-vol-4.pdf = ${INTEL}/22/0d/335592-sdm-vol-4.pdf
+URL.multiboot.pdf = https://www.gnu.org/software/grub/manual/multiboot/multiboot.pdf
+
+all: ${PDFS}
+
+${PDFS}:
+	curl -Lf -o $@ ${URL.$@}
+	chmod a-w $@
+
+clean:
+	rm -f ${PDFS}
diff --git a/doc/rfc/.gitignore b/doc/rfc/.gitignore
new file mode 100644
index 00000000..cc3245d4
--- /dev/null
+++ b/doc/rfc/.gitignore
@@ -0,0 +1,2 @@
+rfc
+rfctags
diff --git a/doc/rfc/Makefile b/doc/rfc/Makefile
new file mode 100644
index 00000000..31e238a0
--- /dev/null
+++ b/doc/rfc/Makefile
@@ -0,0 +1,38 @@
+PREFIX ?= ~/.local
+MANDIR ?= ${PREFIX}/share/man
+
+MODULE = ftp.rfc-editor.org::rfcs-text-only
+RFCS = ${MODULE}/rfc-index.txt ${MODULE}/'rfc[1-9]*.txt'
+
+all: rfc rfctags
+
+.SUFFIXES: .in .pl
+
+.in:
+	sed 's|%%PREFIX%%|${PREFIX}|g' $< > $@
+	chmod a+x $@
+
+.pl:
+	cp -f $< $@
+	chmod a+x $@
+
+clean:
+	rm -f rfc rfctags
+
+install: rfc rfctags rfc.1
+	install -d ${PREFIX}/bin ${MANDIR}/man1
+	install rfc rfctags ${PREFIX}/bin
+	install -m 644 rfc.1 ${MANDIR}/man1
+	ln -fs rfc.1 ${MANDIR}/man1/rfctags.1
+
+sync:
+	install -d ${PREFIX}/share
+	rsync -ptz ${RFCS} ${PREFIX}/share/rfc
+
+compress:
+	find ${PREFIX}/share/rfc -name '*.txt' | xargs gzip -9f
+
+uninstall:
+	rm -f ${PREFIX}/bin/rfc ${PREFIX}/bin/rfctags
+	rm -f ${MANDIR}/man1/rfc.1 ${MANDIR}/man1/rfctags.1
+	rm -fr ${PREFIX}/share/rfc
diff --git a/doc/rfc/rfc.1 b/doc/rfc/rfc.1
new file mode 100644
index 00000000..ece5a901
--- /dev/null
+++ b/doc/rfc/rfc.1
@@ -0,0 +1,53 @@
+.Dd January 18, 2021
+.Dt RFC 1
+.Os
+.
+.Sh NAME
+.Nm rfc ,
+.Nm rfctags
+.Nd view IETF RFCs
+.
+.Sh SYNOPSIS
+.Nm rfc
+.Op Ar number
+.Nm rfctags
+.Op Ar
+.
+.Sh DESCRIPTION
+The
+.Nm rfc
+utility displays
+an IETF RFC by number,
+or the RFC index if no number is specified.
+The RFC is displayed in the
+.Ev PAGER
+with a tags file generated by
+.Nm rfctags .
+.
+.Pp
+The
+.Nm rfctags
+utility generates tags
+for RFC text file
+section numbers,
+section names
+and bracketed references.
+.
+.Sh ENVIRONMENT
+.Bl -tag -width Ds
+.It Ev PAGER
+The program used to display RFCs.
+It must accept the
+.Fl T
+flag for specifying
+the path of the tags file.
+The default is
+.Ev PAGER=less .
+.El
+.
+.Sh SEE ALSO
+.Xr ctags 1 ,
+.Xr less 1
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/doc/rfc/rfc.in b/doc/rfc/rfc.in
new file mode 100644
index 00000000..16081c83
--- /dev/null
+++ b/doc/rfc/rfc.in
@@ -0,0 +1,19 @@
+#!/bin/sh
+set -eu
+
+mktemp='mktemp -t rfc'
+[ "$(uname)" = 'OpenBSD' ] && mktemp="${mktemp}.XXXXXXXXXX"
+
+rfc=%%PREFIX%%/share/rfc/"rfc${1:--index}.txt"
+tags=$($mktemp)
+trap 'rm "${tags}"' EXIT
+
+if test -f "${rfc}.gz"; then
+	txt=$($mktemp)
+	trap 'rm "${txt}" "${tags}"' EXIT
+	gunzip -c "${rfc}.gz" >"${txt}"
+	rfc=$txt
+fi
+
+%%PREFIX%%/bin/rfctags "${rfc}" >"${tags}"
+${PAGER:-less} -T "${tags}" "${rfc}"
diff --git a/doc/rfc/rfctags.pl b/doc/rfc/rfctags.pl
new file mode 100644
index 00000000..05173d00
--- /dev/null
+++ b/doc/rfc/rfctags.pl
@@ -0,0 +1,21 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use open ':encoding(ISO-8859-1)';
+
+($,, $\) = ("\t", "\n");
+while (<>) {
+	chomp;
+	# Section headings
+	if (/^([\d.]+|[A-Z][.])\s+([^\t]+)?/) {
+		print $1, $ARGV, $.;
+		print $2, $ARGV, $. if $2;
+		print $1, $ARGV, $. if $1 =~ /^([\d.]+)[.]$/;
+	}
+	# References
+	if (/^\s*(\[[\w-]+\])\s{2,}/) {
+		print $1, $ARGV, $.;
+		print "\\$1", $ARGV, $.; # vim ^] prepends \ to [
+	}
+	close ARGV if eof;
+}
diff --git a/doc/zlib/Makefile b/doc/zlib/Makefile
new file mode 100644
index 00000000..6cfd4a42
--- /dev/null
+++ b/doc/zlib/Makefile
@@ -0,0 +1,87 @@
+PREFIX ?= ~/.local
+MANDIR ?= ${PREFIX}/share/man
+
+MAN += adler32.3
+MAN += adler32_combine.3
+MAN += compress.3
+MAN += compressBound.3
+MAN += crc32.3
+MAN += crc32_combine.3
+MAN += deflate.3
+MAN += deflateBound.3
+MAN += deflateCopy.3
+MAN += deflateEnd.3
+MAN += deflateGetDictionary.3
+MAN += deflateInit.3
+MAN += deflateInit2.3
+MAN += deflateParams.3
+MAN += deflatePending.3
+MAN += deflatePrime.3
+MAN += deflateReset.3
+MAN += deflateSetDictionary.3
+MAN += deflateSetHeader.3
+MAN += deflateTune.3
+MAN += gzbuffer.3
+MAN += gzclose.3
+MAN += gzdirect.3
+MAN += gzeof.3
+MAN += gzerror.3
+MAN += gzflush.3
+MAN += gzfread.3
+MAN += gzfwrite.3
+MAN += gzgetc.3
+MAN += gzgets.3
+MAN += gzoffset.3
+MAN += gzopen.3
+MAN += gzprintf.3
+MAN += gzputc.3
+MAN += gzputs.3
+MAN += gzread.3
+MAN += gzseek.3
+MAN += gzsetparams.3
+MAN += gzungetc.3
+MAN += gzwrite.3
+MAN += inflate.3
+MAN += inflateBack.3
+MAN += inflateBackEnd.3
+MAN += inflateBackInit.3
+MAN += inflateCopy.3
+MAN += inflateEnd.3
+MAN += inflateGetDictionary.3
+MAN += inflateGetHeader.3
+MAN += inflateInit.3
+MAN += inflateInit2.3
+MAN += inflateMark.3
+MAN += inflatePrime.3
+MAN += inflateReset.3
+MAN += inflateSetDictionary.3
+MAN += inflateSync.3
+MAN += uncompress.3
+MAN += zlibCompileFlags.3
+MAN += zlibVersion.3
+
+MLINKS += adler32.3 adler32_z.3
+MLINKS += compress.3 compress2.3
+MLINKS += crc32.3 crc32_z.3
+MLINKS += gzclose.3 gzclose_r.3
+MLINKS += gzclose.3 gzclose_w.3
+MLINKS += gzerror.3 gzclearerr.3
+MLINKS += gzopen.3 gzdopen.3
+MLINKS += gzseek.3 gzrewind.3
+MLINKS += gzseek.3 gztell.3
+MLINKS += inflateReset.3 inflateReset2.3
+MLINKS += uncompress.3 uncompress2.3
+
+lint:
+	mandoc -T lint ${MAN} | grep -v 'referenced manual not found'
+
+install:
+	install -d ${MANDIR}/man3
+	install -m 644 ${MAN} ${MANDIR}/man3
+	set -- ${MLINKS}; while [ -n "$$*" ]; do \
+		ln -fs $$1 ${MANDIR}/man3/$$2; shift 2; done
+
+uninstall:
+	rm -f ${MAN:%=${MANDIR}/man3/%}
+	set -- ${MLINKS}; while [ -n "$$*" ]; do \
+		rm -f ${MANDIR}/man3/$$2; shift 2; done
diff --git a/doc/zlib/adler32.3 b/doc/zlib/adler32.3
new file mode 100644
index 00000000..d713d952
--- /dev/null
+++ b/doc/zlib/adler32.3
@@ -0,0 +1,65 @@
+.Dd January 15, 2017
+.Dt ADLER32 3
+.Os
+.
+.Sh NAME
+.Nm adler32 ,
+.Nm adler32_z
+.Nd update Adler-32 checksum
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft uLong
+.Fn adler32 "uLong adler" "const Bytef *buf" "uInt len"
+.Ft uLong
+.Fn adler32_z "uLong adler" "const Bytef *buf" "z_size_t len"
+.
+.Sh DESCRIPTION
+Update a running Adler-32 checksum with the bytes
+.Fa "buf[0..len-1]"
+and return the updated checksum.
+If
+.Fa buf
+is
+.Dv Z_NULL ,
+this function returns
+the required initial value for the checksum.
+.
+.Pp
+An Adler-32 checksum is almost as reliable as a CRC-32
+but can be computed much faster.
+.
+.Pp
+.Fn adler32_z
+is the same as
+.Fn adler32 ,
+but with a
+.Vt size_t
+length.
+.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+uLong adler = adler32(0L, Z_NULL, 0);
+
+while (read_buffer(buffer, length) != EOF) {
+	adler = adler32(adler, buffer, length);
+}
+if (adler != original_adler) error();
+.Ed
+.
+.Sh SEE ALSO
+.Xr adler32_combine 3 ,
+.Xr crc32 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/adler32_combine.3 b/doc/zlib/adler32_combine.3
new file mode 100644
index 00000000..861f235b
--- /dev/null
+++ b/doc/zlib/adler32_combine.3
@@ -0,0 +1,63 @@
+.Dd January 15, 2017
+.Dt ADLER32_COMBINE 3
+.Os
+.
+.Sh NAME
+.Nm adler32_combine
+.Nd combine Adler-32 checksums
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft uLong
+.Fn adler32_combine "uLong adler1" "uLong adler2" "z_off_t len2"
+.
+.Sh DESCRIPTION
+Combine two Adler-32 checksums into one.
+For two sequences of bytes,
+.Va seq1
+and
+.Va seq2
+with lengths
+.Va len1
+and
+.Va len2 ,
+Adler-32 checksums were calculated for each,
+.Va adler1
+and
+.Va adler2 .
+.Fn adler32_combine
+returns the Adler-32 checksum of
+.Va seq1
+and
+.Va seq2
+concatenated,
+requiring only
+.Fa adler1 ,
+.Fa adler2 ,
+and
+.Fa len2 .
+Note that the
+.Vt z_off_t
+type
+.Pq like Vt off_t
+is a signed integer.
+If
+.Fa len2
+is negative,
+the result has no meaning or utility.
+.
+.Sh SEE ALSO
+.Xr adler32 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/compress.3 b/doc/zlib/compress.3
new file mode 100644
index 00000000..22b229ee
--- /dev/null
+++ b/doc/zlib/compress.3
@@ -0,0 +1,84 @@
+.Dd January 15, 2017
+.Dt COMPRESS 3
+.Os
+.
+.Sh NAME
+.Nm compress ,
+.Nm compress2
+.Nd compress source buffer into destination buffer
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.
+.Ft int
+.Fo compress
+.Fa "Bytef *dest"
+.Fa "uLongf *destLen"
+.Fa "const Bytef *source"
+.Fa "uLong sourceLen"
+.Fc
+.
+.Ft int
+.Fo compress2
+.Fa "Bytef *dest"
+.Fa "uLongf *destLen"
+.Fa "const Bytef *source"
+.Fa "uLong sourceLen"
+.Fa "int level"
+.Fc
+.
+.Sh DESCRIPTION
+Compresses the source buffer into the destination buffer.
+.Fa sourceLen
+is the byte length of the source buffer.
+Upon entry,
+.Fa destLen
+is the total size of the destination buffer,
+which must be at least the value returned by
+.Fn compressBound sourceLen .
+Upon exit,
+.Fa destLen
+is the actual size of the compressed data.
+.
+.Pp
+.Fn compress
+is equivalent to
+.Fn compress2
+with a
+.Fa level
+parameter of
+.Dv Z_DEFAULT_COMPRESSION .
+.
+.Sh RETURN VALUES
+.Fn compress
+and
+.Fn compress2
+return
+.Dv Z_OK
+on success,
+.Dv Z_MEM_ERROR
+if there was not enough memory,
+.Dv Z_BUF_ERROR
+if there was not enough room in the output buffer,
+.Dv Z_STREAM_ERROR
+if the
+.Fa level
+parameter is invalid.
+.
+.Sh SEE ALSO
+.Xr compressBound 3 ,
+.Xr deflateInit 3 ,
+.Xr uncompress 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/compressBound.3 b/doc/zlib/compressBound.3
new file mode 100644
index 00000000..5800e2ba
--- /dev/null
+++ b/doc/zlib/compressBound.3
@@ -0,0 +1,44 @@
+.Dd January 15, 2017
+.Dt COMPRESSBOUND 3
+.Os
+.
+.Sh NAME
+.Nm compressBound
+.Nd compressed size upper bound
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft uLong
+.Fn compressBound "uLong sourceLen"
+.
+.Sh DESCRIPTION
+.Fn compressBound
+returns an upper bound on the compressed size after
+.Xr compress 3
+or
+.Xr compress2 3
+on
+.Fa sourceLen
+bytes.
+It would be used before a
+.Xr compress 3
+or
+.Xr compress2 3
+call to allocate the destination buffer.
+.
+.Sh SEE ALSO
+.Xr compress 3 ,
+.Xr deflateBound 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/crc32.3 b/doc/zlib/crc32.3
new file mode 100644
index 00000000..3c9cc8c4
--- /dev/null
+++ b/doc/zlib/crc32.3
@@ -0,0 +1,66 @@
+.Dd January 15, 2017
+.Dt CRC32 3
+.Os
+.
+.Sh NAME
+.Nm crc32 ,
+.Nm crc32_z
+.Nd update CRC-32 checksum
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft uLong
+.Fn crc32 "uLong crc" "const Bytef *buf" "uInt len"
+.Ft uLong
+.Fn crc32_z "uLong crc" "const Bytef *buf" "z_size_t len"
+.
+.Sh DESCRIPTION
+Update a running CRC-32 with the bytes
+.Fa "buf[0..len-1]"
+and return the updated CRC-32.
+If
+.Fa buf
+is
+.Dv Z_NULL ,
+this function returns
+the required initial value for the CRC.
+Pre- and post-conditioning
+(one's complement)
+is performed within this function
+so it shouldn't be done
+by the application.
+.
+.Pp
+.Fn crc32_z
+is the same as
+.Fn crc32 ,
+but with a
+.Vt size_t
+length.
+.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+uLong crc = crc32(0L, Z_NULL, 0);
+
+while (read_buffer(buffer, length) != EOF) {
+	crc = crc32(crc, buffer, length);
+}
+if (crc != original_crc) error();
+.Ed
+.
+.Sh SEE ALSO
+.Xr adler32 3 ,
+.Xr crc32_combine 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/crc32_combine.3 b/doc/zlib/crc32_combine.3
new file mode 100644
index 00000000..2f79f623
--- /dev/null
+++ b/doc/zlib/crc32_combine.3
@@ -0,0 +1,54 @@
+.Dd January 15, 2017
+.Dt CRC32_COMBINE 3
+.Os
+.
+.Sh NAME
+.Nm crc32_combine
+.Nd combine CRC-32 checksums
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft uLong
+.Fn crc32_combine "uLong crc1" "uLong crc2" "z_off_t len2"
+.
+.Sh DESCRIPTION
+Combine two CRC-32 check values into one.
+For two sequences of bytes,
+.Va seq1
+and
+.Va seq2
+with lengths
+.Va len1
+and
+.Va len2 ,
+CRC-32 check values were calculated for each,
+.Va crc1
+and
+.Va crc2 .
+.Fn crc32_combine
+returns the CRC-32 check value of
+.Va seq1
+and
+.Va seq2
+concatenated,
+requiring only
+.Fa crc1 ,
+.Fa crc2 ,
+and
+.Fa len2 .
+.
+.Sh SEE ALSO
+.Xr crc32 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflate.3 b/doc/zlib/deflate.3
new file mode 100644
index 00000000..7df313ee
--- /dev/null
+++ b/doc/zlib/deflate.3
@@ -0,0 +1,370 @@
+.Dd January 15, 2017
+.Dt DEFLATE 3
+.Os
+.
+.Sh NAME
+.Nm deflate
+.Nd deflate compression
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn deflate "z_streamp strm" "int flush"
+.
+.Sh DESCRIPTION
+.Fn deflate
+compresses as much data as possible,
+and stops when the input buffer becomes empty
+or the output buffer becomes full.
+It may introduce some output latency
+(reading input without producing any output)
+except when forced the flush.
+.
+.Pp
+The detailed semantics are as follows.
+.Fn deflate
+performs one or both of the following actions:
+.
+.Bl -dash
+.It
+Compress more input starting at
+.Fa next_in
+and update
+.Fa next_in
+and
+.Fa avail_in
+accordingly.
+If not all input can be processed
+(because there is not enough room in the output buffer),
+.Fa next_in
+and
+.Fa avail_in
+are updated
+and processing will resume at this point
+for the next call of
+.Fn deflate .
+.
+.It
+Generate more output starting at
+.Fa next_out
+and update
+.Fa next_out
+and
+.Fa avail_out
+accordingly.
+This action is forced if the parameter
+.Fa flush
+is non-zero.
+Forcing flush frequently degrades the compression ratio,
+so this parameter should be set only when necessary.
+Some output may be provided even if
+.Fa flush
+is zero.
+.El
+.
+.Pp
+Before the call of
+.Fn deflate ,
+the application should ensure that
+at least one of the actions is possible,
+by providing more input
+and/or consuming more output,
+and updating
+.Fa avail_in
+or
+.Fa avail_out
+accordingly;
+.Fa avail_out
+should never be zero before the call.
+The application can consume the compressed output
+when it wants,
+for example when the output buffer is full
+.Po
+.Fa avail_out
+== 0
+.Pc ,
+or after each call of
+.Fn deflate .
+If
+.Fn deflate
+returns
+.Dv Z_OK
+and with zero
+.Fa avail_out ,
+it must be called again after making room in the output buffer
+because there might be more output pending.
+See
+.Xr deflatePending 3 ,
+which can be used if desired to determine
+whether or not there is more output in that case.
+.
+.Pp
+Normally the parameter
+.Fa flush
+is set to
+.Dv Z_NO_FLUSH ,
+which allows
+.Fn deflate
+to decide how much data to accumulate before producing output,
+in order to maximize compression.
+.
+.Pp
+If the parameter
+.Fa flush
+is set to
+.Dv Z_SYNC_FLUSH ,
+all pending output is flushed to the output buffer
+and the output is aligned on a byte boundary,
+so that the decompressor can get all input data available so far.
+.Po
+In particular
+.Fa avail_in
+is zero after the call if enough output space
+has been provided before the call.
+.Pc \&
+Flushing may degrade compression for some compression algorithms
+and so it should be used only when necessary.
+This completes the current deflate block
+and follows it with an empty stored block
+that is three bits plus filler bits to the next byte,
+followed by four bytes
+(00 00 ff ff).
+.
+.Pp
+If
+.Fa flush
+is set to
+.Dv Z_PARTIAL_FLUSH ,
+all pending output is flushed to the output buffer,
+but the output is not aligned to a byte boundary.
+All of the input data so far will be available to the decompressor,
+as for
+.Dv Z_SYNC_FLUSH .
+This completes the current deflate block
+and follows it with an empty fixed codes block
+that is 10 bits long.
+This assures that enough bytes are output
+in order for the decompressor to finish the block
+before the empty fixed codes block.
+.
+.Pp
+If
+.Fa flush
+is set to
+.Dv Z_BLOCK ,
+a deflate block is completed and emitted,
+as for
+.Dv Z_SYNC_FLUSH ,
+but the output is not aligned on a byte boundary,
+and up to seven bits of the current block
+are held to be written as the next byte
+after the next deflate block is completed.
+In this case,
+the decompressor may not be provided enough bits
+at this point in order to complete decompression
+of the data provided so far to the compressor.
+It may need to wait for the next block to be emitted.
+This is for advanced applications
+that need to control the emission of deflate blocks.
+.
+.Pp
+If
+.Fa flush
+is set to
+.Dv Z_FULL_FLUSH ,
+all output is flushed as with
+.Dv Z_SYNC_FLUSH ,
+and the compression state is reset
+so that decompression can restart from this point
+if previous compressed data has been damaged
+or if random access is desired.
+Using
+.Dv Z_FULL_FLUSH
+too often can seriously degrade compression.
+.
+.Pp
+If
+.Fn deflate
+returns with
+.Fa avail_out
+== 0,
+this function must be called again
+with the same value of the
+.Fa flush
+parameter
+and more output space
+.Po
+updated
+.Fa avail_out
+.Pc ,
+until the flush is complete
+.Po
+.Fn deflate
+returns with non-zero
+.Fa avail_out
+.Pc .
+In the case of a
+.Dv Z_FULL_FLUSH
+or
+.Dv Z_SYNC_FLUSH ,
+make sure that
+.Fa avail_out
+is greater than six
+to avoid repeated flush markers
+due to
+.Fa avail_out
+== 0
+on return.
+.
+.Pp
+If the parameter
+.Fa flush
+is set to
+.Dv Z_FINISH ,
+pending input is processed,
+pending output is flushed and
+.Fn deflate
+returns with
+.Dv Z_STREAM_END
+if there was enough output space.
+If
+.Fn deflate
+returns with
+.Dv Z_OK
+or
+.Dv Z_BUF_ERROR ,
+this function must be called again with
+.Dv Z_FINISH
+and more output space
+.Pq updated Fa avail_out
+but no more input data,
+until it returns with
+.Dv Z_STREAM_END
+or an error.
+After
+.Fn deflate
+has returned
+.Dv Z_STREAM_END ,
+the only possible operations on the stream are
+.Xr deflateReset 3
+or
+.Xr deflateEnd 3 .
+.
+.Pp
+.Dv Z_FINISH
+can be used in the first
+.Fn deflate
+call after
+.Xr deflateInit 3
+if all the compression is to be done in a single step.
+In order to complete in one call,
+.Fa avail_out
+must be at least the value returned by
+.Xr deflateBound 3 .
+Then
+.Fn deflate
+is guaranteed to return
+.Dv Z_STREAM_END .
+If not enough output space is provided,
+.Fn deflate
+will not return
+.Dv Z_STREAM_END ,
+and it must be called again as described above.
+.
+.Pp
+.Fn deflate
+sets
+.Fa strm->adler
+to the Adler-32 checksum
+of all input read so far
+.Po
+that is,
+.Fa total_in
+bytes
+.Pc .
+If a gzip stream is being generated,
+then
+.Fa strm->adler
+will be the CRC-32 checksum of the input read so far.
+See
+.Xr deflateInit2 3 .
+.
+.Pp
+.Fn deflate
+may update
+.Fa strm->data_type
+if it can make a good guess
+about the input data type
+.Po
+.Dv Z_BINARY
+or
+.Dv Z_TEXT
+.Pc .
+If in doubt,
+the date is considered binary.
+This field is only for information purposes
+and does not affect the compression algorithm in any manner.
+.
+.Sh RETURN VALUES
+.Fn deflate
+returns
+.Dv Z_OK
+if some progress has been made
+(more input processed or more output produced),
+.Dv Z_STREAM_END
+if all input has been consumed
+and all output has been produced
+.Po
+only when
+.Fa flush
+is set to
+.Dv Z_FINISH
+.Pc ,
+.Dv Z_STREAM_ERROR
+if the stream state was inconsistent
+.Po
+for example if
+.Fa next_in
+or
+.Fa next_out
+was
+.Dv Z_NULL
+or the state was inadvertently written over
+by the application
+.Pc ,
+or
+.Dv Z_BUF_ERROR
+if no progress is possible
+.Po
+for example
+.Fa avail_in
+or
+.Fa avail_out
+was zero
+.Pc .
+Note that
+.Dv Z_BUF_ERROR
+is not fatal,
+and
+.Fn deflate
+can be called again with more input and more output space
+to continue compressing.
+.
+.Sh SEE ALSO
+.Xr deflateEnd 3 ,
+.Xr deflateInit 3 ,
+.Xr deflatePending 3 ,
+.Xr inflate 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateBound.3 b/doc/zlib/deflateBound.3
new file mode 100644
index 00000000..63e80246
--- /dev/null
+++ b/doc/zlib/deflateBound.3
@@ -0,0 +1,71 @@
+.Dd January 15, 2017
+.Dt DEFLATEBOUND 3
+.Os
+.
+.Sh NAME
+.Nm deflateBound
+.Nd compressed size upper bound
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft uLong
+.Fn deflateBound "z_streamp strm" "uLong sourceLen"
+.
+.Sh DESCRIPTION
+.Fn deflateBound
+returns an upper bound
+on the compressed size
+after deflation of
+.Fa sourceLen
+bytes.
+It must be called after
+.Xr deflateInit 3
+or
+.Xr deflateInit2 3 ,
+and after
+.Xr deflateSetHeader 3 ,
+if used.
+This would be used
+to allocate an output buffer
+for deflation in a single pass,
+and so would be called before
+.Xr deflate 3 .
+If that first
+.Fn deflate
+call is provided the
+.Fa sourceLen
+input bytes,
+an output buffer allocated
+to the size returned by
+.Fn deflateBound ,
+and the flush value
+.Dv Z_FINISH ,
+then
+.Fn deflate
+is guaranteed to return
+.Dv Z_STREAM_END .
+Note that it is possible
+for the compressed size
+to be larger than the value returned by
+.Fn deflateBound
+if flush options other than
+.Dv Z_FINISH
+or
+.Dv Z_NO_FLUSH
+are used.
+.
+.Sh SEE ALSO
+.Xr compressBound 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateCopy.3 b/doc/zlib/deflateCopy.3
new file mode 100644
index 00000000..f30d6301
--- /dev/null
+++ b/doc/zlib/deflateCopy.3
@@ -0,0 +1,66 @@
+.Dd January 15, 2017
+.Dt DEFLATECOPY 3
+.Os
+.
+.Sh NAME
+.Nm deflateCopy
+.Nd copy deflate stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn deflateCopy "z_streamp dest" "z_streamp source"
+.
+.Sh DESCRIPTION
+Sets the destination stream
+as a complete copy of the source stream.
+.
+.Pp
+This function can be useful when
+several compression strategies will be tried,
+for example when there are several ways of
+pre-processing the input data with a filter.
+The streams that will be discarded
+should then be freed by calling
+.Xr deflateEnd 3 .
+Note that
+.Fn deflateCopy
+duplicates the internal compression state
+which can be quite large,
+so this strategy is slow
+and can consume lots of memory.
+.
+.Sh RETURN VALUES
+.Fn deflateCopy
+returns
+.Dv Z_OK
+if success,
+.Dv Z_MEM_ERROR
+if there was not enough memory,
+.Dv Z_STREAM_ERROR
+if the source stream state was inconsistent
+.Po
+such as
+.Fa zalloc
+being
+.Dv Z_NULL
+.Pc .
+.Fa msg
+is left unchanged
+in both source and destination.
+.
+.Sh SEE ALSO
+.Xr deflateInit 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateEnd.3 b/doc/zlib/deflateEnd.3
new file mode 100644
index 00000000..e24259a3
--- /dev/null
+++ b/doc/zlib/deflateEnd.3
@@ -0,0 +1,50 @@
+.Dd January 15, 2017
+.Dt DEFLATEEND 3
+.Os
+.
+.Sh NAME
+.Nm deflateEnd
+.Nd free deflate stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn deflateEnd "z_streamp strm"
+.
+.Sh DESCRIPTION
+All dynamically allocated data structures
+for this stream are freed.
+This function discards any unprocessed input
+and does not flush any pending output.
+.
+.Sh RETURN VALUES
+.Fn deflateEnd
+returns
+.Dv Z_OK
+if success,
+.Dv Z_STREAM_ERROR
+if the stream state was inconsistent,
+.Dv Z_DATA_ERROR
+if the stream was freed prematurely
+(some input or output was discarded).
+In the error case,
+.Fa msg
+may be set but then points to a static string
+(which must not be deallocated).
+.
+.Sh SEE ALSO
+.Xr deflate 3 ,
+.Xr deflateInit 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateGetDictionary.3 b/doc/zlib/deflateGetDictionary.3
new file mode 100644
index 00000000..403f6d10
--- /dev/null
+++ b/doc/zlib/deflateGetDictionary.3
@@ -0,0 +1,79 @@
+.Dd January 15, 2017
+.Dt DEFLATEGETDICTIONARY 3
+.Os
+.
+.Sh NAME
+.Nm deflateGetDictionary
+.Nd deflate sliding dictionary
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fo deflateGetDictionary
+.Fa "z_streamp strm"
+.Fa "Bytef *dictionary"
+.Fa "uInt *dictLength"
+.Fc
+.
+.Sh DESCRIPTION
+Returns the sliding dictionary
+being maintained by deflate.
+.Fa dictLength
+is set to the number of bytes in the dictionary,
+and that many bytes are copied to
+.Fa dictionary .
+.Fa dictionary
+must have enough space,
+where 32768 bytes is always enough.
+If
+.Fn deflateGetDictionary
+is called with
+.Fa dictionary
+equal to
+.Dv Z_NULL ,
+then only the dictionary length is returned,
+and nothing is copied.
+Similarly,
+if
+.Fa dictLength
+is
+.Dv Z_NULL ,
+then it is not set.
+.
+.Pp
+.Fn deflateGetDictionary
+may return a length less than the window size,
+even when more than the window size in input
+has been provided.
+It may return up to 258 bytes less in that case,
+due to how zlib's implementation of deflate
+manages the sliding window and lookahead for matches,
+where matches can be up to 258 bytes long.
+If the application needs the last window-size bytes of input,
+then that would need to be saved by the application
+outside of zlib.
+.
+.Sh RETURN VALUES
+.Fn deflateGetDictionary
+returns
+.Dv Z_OK
+on success,
+or
+.Dv Z_STREAM_ERROR
+if the stream state is inconsistent.
+.
+.Sh SEE ALSO
+.Xr deflateSetDictionary 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateInit.3 b/doc/zlib/deflateInit.3
new file mode 100644
index 00000000..a893dd91
--- /dev/null
+++ b/doc/zlib/deflateInit.3
@@ -0,0 +1,178 @@
+.Dd January 15, 2017
+.Dt DEFLATEINIT 3
+.Os
+.
+.Sh NAME
+.Nm deflateInit
+.Nd initialize deflate stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.
+.Ft typedef voidpf
+.Fo (*alloc_func)
+.Fa "voidpf opaque"
+.Fa "uInt items"
+.Fa "uInt size"
+.Fc
+.
+.Ft typedef void
+.Fo (*free_func)
+.Fa "voidpf opaque"
+.Fa "voidpf address"
+.Fc
+.
+.Bd -literal
+typedef struct z_stream_s {
+	z_const Bytef *next_in;
+	uInt     avail_in;
+	uLong    total_in;
+
+	Bytef    *next_out;
+	uInt     avail_out;
+	uLong    total_out;
+
+	z_const char *msg;
+	struct internal_state FAR *state;
+
+	alloc_func zalloc;
+	free_func  zfree;
+	voidpf     opaque;
+
+	int     data_type;
+	uLong   adler;
+	uLong   reserved;
+} z_stream;
+.Ed
+.
+.Pp
+.Vt typedef z_stream FAR *z_streamp;
+.
+.Ft int
+.Fn deflateInit "z_streamp strm" "int level"
+.
+.Sh DESCRIPTION
+Initializes the internal stream state for compression.
+The fields
+.Fa zalloc ,
+.Fa zfree
+and
+.Fa opaque
+must be initialized before by the caller.
+If
+.Fa zalloc
+and
+.Fa zfree
+are set to
+.Dv Z_NULL ,
+.Fn deflateInit
+updates them to use default allocation functions.
+.Fn deflateInit
+does not perform any compression:
+this will be done by
+.Xr deflate 3 .
+.
+.Pp
+The compression
+.Fa level
+must be
+.Dv Z_DEFAULT_COMPRESSION ,
+or between 0 and 9:
+1 gives best speed,
+9 gives best compression,
+0 gives no compression at all
+(the input data is simply copied a block at a time).
+.Dv Z_DEFAULT_COMPRESSION
+requests a default compromise between speed and compression
+(currently equivalent to level 6).
+.
+.Pp
+The fields of
+.Vt z_stream
+are as follows:
+.
+.Bl -tag -width "data_type"
+.It Fa next_in
+next input byte
+.It Fa avail_in
+number of bytes available at
+.Fa next_in
+.It Fa total_in
+total number of input bytes read so far
+.It Fa next_out
+next output byte will go here
+.It Fa avail_out
+remaining free space at
+.Fa next_out
+.It Fa total_out
+total number of bytes output so far
+.It Fa msg
+last error message,
+.Dv NULL
+if no error
+.It Fa state
+not visible by applications
+.It Fa zalloc
+used to allocate the internal state
+.It Fa zfree
+used to free the internal state
+.It Fa opaque
+private data object passed to
+.Fa zalloc
+and
+.Fa zfree
+.It data_type
+best guess about the data type:
+binary or text for
+.Xr deflate 3 ,
+or the decoding state for
+.Xr inflate 3
+.It adler
+Adler-32 or CRC-32 value of the uncompressed data
+.It reserved
+reserved for future use
+.El
+.
+.Sh RETURN VALUES
+.Fn deflateInit
+returns
+.Dv Z_OK
+if success,
+.Dv Z_MEM_ERROR
+if there was not enough memory,
+.Dv Z_STREAM_ERROR
+if
+.Fa level
+is not a valid compression level,
+or
+.Dv Z_VERSION_ERROR
+if the zlib library version
+.Pq Xr zlibVersion 3
+is incompatible with the version assumed by the caller
+.Pq Dv ZLIB_VERSION .
+.Fa msg
+is set to null
+if there is no error message.
+.
+.Sh SEE ALSO
+.Xr deflate 3 ,
+.Xr deflateCopy 3 ,
+.Xr deflateEnd 3 ,
+.Xr deflateInit2 3 ,
+.Xr deflatePrime 3 ,
+.Xr deflateReset 3 ,
+.Xr deflateSetDictionary 3 ,
+.Xr deflateTune 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateInit2.3 b/doc/zlib/deflateInit2.3
new file mode 100644
index 00000000..6a581ef8
--- /dev/null
+++ b/doc/zlib/deflateInit2.3
@@ -0,0 +1,227 @@
+.Dd January 15, 2017
+.Dt DEFLATEINIT2 3
+.Os
+.
+.Sh NAME
+.Nm deflateInit2
+.Nd deflate compression options
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fo deflateInit2
+.Fa "z_streamp strm"
+.Fa "int level"
+.Fa "int method"
+.Fa "int windowBits"
+.Fa "int memLevel"
+.Fa "int strategy"
+.Fc
+.
+.Sh DESCRIPTION
+This is another version of
+.Xr deflateInit 3
+with more compression options.
+The fields
+.Fa next_in ,
+.Fa zalloc ,
+.Fa zfree
+and
+.Fa opaque
+must be initialized before by the caller.
+.
+.Pp
+The
+.Fa method
+parameter is the compression method.
+It must be
+.Dv Z_DEFLATED
+in this version of the library.
+.
+.Pp
+The
+.Fa windowBits
+parameter is the base two logarithm
+of the window size
+(the size of the history buffer).
+It should be in the range 8..15
+for this version of the library.
+Larger values of this parameter
+result in better compression
+at the expense of memory usage.
+The default value is 15 if
+.Xr deflateInit 3
+is used instead.
+.
+.Pp
+For the current implementation of
+.Xr deflate 3 ,
+a
+.Fa windowBits
+value of 8
+(a window size of 256 bytes)
+is not supported.
+As a result,
+a request for 8
+will result in 9
+(a 512-byte window).
+In that case,
+providing 8 to
+.Xr inflateInit2 3
+will result in an error
+when the zlib header with 9
+is checked against the initialization of
+.Xr inflate 3 .
+The remedy is to not use 8 with
+.Fn deflateInit2
+with this initialization,
+or at least in that case use 9 with
+.Xr inflateInit2 3 .
+.
+.Pp
+.Fa windowBits
+can also be -8..-15 for raw deflate.
+In this case,
+.Fa -windowBits
+determines the window size.
+.Xr deflate 3
+will then generate raw deflate data
+with no zlib header or trailer,
+and will not compute a check value.
+.
+.Pp
+.Fa windowBits
+can also be greater than 15
+for optional gzip encoding.
+Add 16 to
+.Fa windowBits
+to write a simple gzip header and trailer
+around the compressed data
+instead of a zlib wrapper.
+The gzip header will have
+no file name,
+no extra data,
+no comment,
+no modification time (set to zero),
+no header CRC,
+and the operating system will be set
+to the appropriate value,
+if the operating system was determined at compile time.
+If a gzip stream is being written,
+.Fa strm->adler
+is a CRC-32 instead of an Adler-32.
+.
+.Pp
+For raw deflate or gzip encoding,
+a request for a 256-byte window
+is rejected as invalid,
+since only the zlib header provides
+a means of transmitting the window size
+to the decompressor.
+.
+.Pp
+The
+.Fa memLevel
+parameter specifies how much memory should be allocated
+for the internal compression state.
+.Fa memLevel=1
+uses minimum memory
+but is slow and reduces compression ratio;
+.Fa memLevel=9
+uses maximum memory for optimal speed.
+The default value is 8.
+See
+.In zconf.h
+for total memory usage
+as a function of
+.Fa windowBits
+and
+.Fa memLevel .
+.
+.Pp
+The
+.Fa strategy
+parameter is used to tune the compression algorithm.
+Use the value
+.Dv Z_DEFAULT_STRATEGY
+for normal data,
+.Dv Z_FILTERED
+for data produced by a filter
+(or predictor),
+.Dv Z_HUFFMAN_ONLY
+to force Huffman encoding only
+(no string match),
+or
+.Dv Z_RLE
+to limit match distances to one
+(run-length encoding).
+Filtered data consists mostly of small values
+with a somewhat random distribution.
+In this case,
+the compression algorithm
+is tuned to compress them better.
+The effect of
+.Dv Z_FILTERED
+is to force more Huffman coding
+and less string matching;
+it is somewhat intermediate between
+.Dv Z_DEFAULT_STRATEGY
+and
+.Dv Z_HUFFMAN_ONLY .
+.Dv Z_RLE
+is designed to be almost as fast as
+.Dv Z_HUFFMAN_ONLY ,
+but give better compression for PNG image data.
+The
+.Fa strategy
+parameter only affects the compression ratio
+but not the correctness of the compressed output
+even if it is not set appropriately.
+.Dv Z_FIXED
+prevents the use of dynamic Huffman codes,
+allowing for a simpler decoder
+for special applications.
+.
+.Pp
+.Fn deflateInit2
+does not perform any compression:
+this will be done by
+.Xr deflate 3 .
+.
+.Sh RETURN VALUES
+.Fn deflateInit2
+returns
+.Dv Z_OK
+if success,
+.Dv Z_MEM_ERROR
+if there was not enough memory,
+.Dv Z_STREAM_ERROR
+if any parameter is invalid
+(such as invalid method),
+or
+.Dv Z_VERSION_ERROR
+if the zlib library version
+.Pq Xr zlibVersion 3
+is incompatible with the version assumed by the caller
+.Pq Dv ZLIB_VERSION .
+.Fa msg
+is set to null if there is no error message.
+.
+.Sh SEE ALSO
+.Xr deflate 3 ,
+.Xr deflateInit 3 ,
+.Xr deflateParams 3 ,
+.Xr deflateSetHeader 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateParams.3 b/doc/zlib/deflateParams.3
new file mode 100644
index 00000000..8e770d4e
--- /dev/null
+++ b/doc/zlib/deflateParams.3
@@ -0,0 +1,123 @@
+.Dd January 15, 2017
+.Dt DEFLATEPARAMS 3
+.Os
+.
+.Sh NAME
+.Nm deflateParams
+.Nd update compression level and strategy
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn deflateParams "z_streamp strm" "int level" "int strategy"
+.
+.Sh DESCRIPTION
+Dynamically update the compression level
+and compression strategy.
+The interpretation of
+.Fa level
+and
+.Fa strategy
+is as in
+.Xr deflateInit2 3 .
+This can be used to switch between compression
+and straight copy of the input data,
+or to switch to a different kind of input data
+requiring a different strategy.
+If the compression approach
+(which is a function of the level)
+or the strategy is changed,
+and if any input has been consumed
+in a previous
+.Xr deflate 3
+call,
+then the input available so far is compressed
+with the old level and strategy using
+.Fn deflate strm Z_BLOCK .
+There are three approaches
+for the compression levels
+0, 1..3, and 4..9 respectively.
+The new level and strategy
+will take effect at the next call of
+.Xr deflate 3 .
+.
+.Pp
+If a
+.Fn deflate strm Z_BLOCK
+is performed by
+.Fn deflateParams ,
+and it does not have enough output space to complete,
+then the parameter change will not take effect.
+In this case,
+.Fn deflateParams
+can be called again
+with the same parameters
+and more output space
+to try again.
+.
+.Pp
+In order to assure a change in the parameters
+on the first try,
+the deflate stream should be flushed using
+.Xr deflate 3
+with
+.Dv Z_BLOCK
+or other flush request until
+.Fa strm.avail_out
+is not zero,
+before calling
+.Fn deflateParams .
+Then no more input data
+should be provided before the
+.Fn deflateParams
+call.
+If this is done,
+the old level and strategy
+will be applied
+to the data compressed before
+.Fn deflateParams ,
+and the new level and strategy
+will be applied
+to the data compressed after
+.Fn deflateParams .
+.
+.Sh RETURN VALUES
+.Fn deflateParams
+returns
+.Dv Z_OK
+on success,
+.Dv Z_STREAM_ERROR
+if the source stream state was inconsistent
+or if a parameter was invalid,
+or
+.Dv Z_BUF_ERROR
+if there was not enough output space
+to complete the compression
+of the available input data
+before a change in the strategy or approach.
+Note that in the case of a
+.Dv Z_BUF_ERROR ,
+the parameters are not changed.
+A return value of
+.Dv Z_BUF_ERROR
+is not fatal,
+in which case
+.Fn deflateParams
+can be retried
+with more output space.
+.
+.Sh SEE ALSO
+.Xr deflateInit2 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflatePending.3 b/doc/zlib/deflatePending.3
new file mode 100644
index 00000000..1ce40fc2
--- /dev/null
+++ b/doc/zlib/deflatePending.3
@@ -0,0 +1,56 @@
+.Dd January 15, 2017
+.Dt DEFLATEPENDING 3
+.Os
+.
+.Sh NAME
+.Nm deflatePending
+.Nd pending deflate output
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn deflatePending "z_streamp strm" "unsigned *pending" "int *bits"
+.
+.Sh DESCRIPTION
+.Fn deflatePending
+returns the number of bytes and bits
+of output that have been generated,
+but not yet provided in the available output.
+The bytes not provided would be due to
+the available output space having been consumed.
+The number of bits of output not provided
+are between 0 and 7,
+where they await more bits to join them
+in order to fill out a full byte.
+If
+.Fa pending
+or
+.Fa bits
+are
+.Dv Z_NULL ,
+then those values are not set.
+.
+.Sh RETURN VALUES
+.Fn deflatePending
+returns
+.Dv Z_OK
+if success,
+or
+.Dv Z_STREAM_ERROR
+if the source stream state was inconsistent.
+.
+.Sh SEE ALSO
+.Xr deflate 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflatePrime.3 b/doc/zlib/deflatePrime.3
new file mode 100644
index 00000000..639e715a
--- /dev/null
+++ b/doc/zlib/deflatePrime.3
@@ -0,0 +1,64 @@
+.Dd January 15, 2017
+.Dt DEFLATEPRIME 3
+.Os
+.
+.Sh NAME
+.Nm deflatePrime
+.Nd insert bits in deflate stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn deflatePrime "z_streamp strm" "int bits" "int value"
+.
+.Sh DESCRIPTION
+.Fn deflatePrime
+inserts bits in the deflate output stream.
+The intent is that this function
+is used to start off the deflate output
+with the bits leftover
+from a previous deflate stream
+when appending to it.
+As such,
+this function can only be used for raw deflate,
+and must be used before the first
+.Xr deflate 3
+call
+after a
+.Xr deflateInit2 3
+or
+.Xr deflateReset 3 .
+.Fa bits
+must be less than or equal to 16,
+and that many of the least significant bits of
+.Fa value
+will be inserted in the output.
+.
+.Sh RETURN VALUES
+.Fn deflatePrime
+returns
+.Dv Z_OK
+if success,
+.Dv Z_BUF_ERROR
+if there was not enough room
+in the internal buffer
+to insert the bits,
+or
+.Dv Z_STREAM_ERROR
+if the source stream state was inconsistent.
+.
+.Sh SEE ALSO
+.Xr deflateInit2 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateReset.3 b/doc/zlib/deflateReset.3
new file mode 100644
index 00000000..7309ac15
--- /dev/null
+++ b/doc/zlib/deflateReset.3
@@ -0,0 +1,57 @@
+.Dd January 15, 2017
+.Dt DEFLATERESET 3
+.Os
+.
+.Sh NAME
+.Nm deflateReset
+.Nd reset deflate stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn deflateReset "z_streamp strm"
+.
+.Sh DESCRIPTION
+This function is equivalent to
+.Xr deflateEnd 3
+followed by
+.Xr deflateInit 3 ,
+but does not free and reallocate
+the internal compression state.
+The stream will leave the compression level
+and any other attributes
+that may have been set unchanged.
+.
+.Sh RETURN VALUES
+.Fn deflateReset
+returns
+.Dv Z_OK
+if success,
+or
+.Dv Z_STREAM_ERROR
+if the source stream state was inconsistent
+.Po
+such as
+.Fa zalloc
+or
+.Fa state
+being
+.Dv Z_NULL
+.Pc .
+.
+.Sh SEE ALSO
+.Xr deflateEnd 3 ,
+.Xr deflateInit 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateSetDictionary.3 b/doc/zlib/deflateSetDictionary.3
new file mode 100644
index 00000000..c2c9d7c2
--- /dev/null
+++ b/doc/zlib/deflateSetDictionary.3
@@ -0,0 +1,142 @@
+.Dd January 15, 2017
+.Dt DEFLATESETDICTIONARY 3
+.Os
+.
+.Sh NAME
+.Nm deflateSetDictionary
+.Nd initialize compression dictionary
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fo deflateSetDictionary
+.Fa "z_streamp strm"
+.Fa "const Bytef *dictionary"
+.Fa "uInt dictLength"
+.Fc
+.
+.Sh DESCRIPTION
+.Fn deflateSetDictionary
+initializes the compression dictionary
+from the given byte sequence
+without producing any compressed output.
+When using the zlib format,
+this function must be called immediately after
+.Xr deflateInit 3 ,
+.Xr deflateInit2 3 ,
+or
+.Xr deflateReset 3 ,
+and before any call of
+.Xr deflate 3 .
+When doing raw deflate,
+this function must be called
+either before any call of
+.Xr deflate 3 ,
+or immediately after the completion of a deflate block,
+i.e. after all input has been consumed
+and all output has been delivered
+when using any of the flush options
+.Dv Z_BLOCK ,
+.Dv Z_PARTIAL_FLUSH ,
+.Dv Z_SYNC_FLUSH ,
+or
+.Dv Z_FULL_FLUSH .
+The compressor and decompressor
+must use exactly the same dictionary
+.Po
+see
+.Xr inflateSetDictionary 3
+.Pc .
+.
+.Pp
+The dictionary should consist of strings
+(byte sequences)
+that are likely to be encountered later
+in the data to be compressed,
+with the most commonly used strings
+preferably put towards the end of the dictionary.
+Using a dictionary is most useful
+when the data to be compressed is short
+and can be predicted with good accuracy;
+the data can then be compressed better than
+with the default empty dictionary.
+.
+.Pp
+Depending on the size of
+the compression data structures selected by
+.Xr deflateInit 3
+or
+.Xr deflateInit2 3 ,
+a part of the dictionary may in effect be discarded,
+for example if the dictionary is larger
+than the window size provided in
+.Xr deflateInit 3
+or
+.Xr deflateInit2 3 .
+Thus the strings most likely to be useful
+should be put at the end of the dictionary,
+not at the front.
+In addition,
+the current implementation of deflate
+will use at most the window size minus 262 bytes
+of the provided dictionary.
+.
+.Pp
+Upon return of this function,
+.Fa strm->adler
+is set to the Adler-32 value
+of the dictionary;
+the decompressor may later use this value
+to determine which dictionary has been used
+by the compressor.
+(The Adler-32 value applies to the whole dictionary
+even if only a subset of the dictionary
+is actually used by the compressor.)
+If a raw deflate was requested,
+then the Adler-32 value is not computed and
+.Fa strm->adler
+is not set.
+.
+.Pp
+.Fn deflateSetDictionary
+does not perform any compression:
+this will be done by
+.Xr deflate 3 .
+.
+.Sh RETURN VALUES
+.Fn deflateSetDictionary
+returns
+.Dv Z_OK
+if success,
+or
+.Dv Z_STREAM_ERROR
+if a parameter is invalid
+.Po
+e.g. dictionary being
+.Dv Z_NULL
+.Pc
+or the stream state is inconsistent
+.Po
+for example if
+.Xr deflate 3
+has already been called for this stream
+or if not at a block boundary
+for raw deflate
+.Pc .
+.
+.Sh SEE ALSO
+.Xr deflateGetDictionary 3 ,
+.Xr inflateSetDictionary 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateSetHeader.3 b/doc/zlib/deflateSetHeader.3
new file mode 100644
index 00000000..6fec645c
--- /dev/null
+++ b/doc/zlib/deflateSetHeader.3
@@ -0,0 +1,180 @@
+.Dd January 15, 2017
+.Dt DEFLATESETHEADER 3
+.Os
+.
+.Sh NAME
+.Nm deflateSetHeader
+.Nd set gzip header
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.
+.Bd -literal
+typedef struct gz_header_s {
+	int     text;
+	uLong   time;
+	int     xflags;
+	int     os;
+	Bytef   *extra;
+	uInt    extra_len;
+	uInt    extra_max;
+	Bytef   *name;
+	uInt    name_max;
+	Bytef   *comment;
+	uInt    comm_max;
+	int     hcrc;
+	int     done;
+} gz_header;
+.Ed
+.
+.Pp
+.Vt typedef gz_header FAR *gz_headerp;
+.
+.Ft int
+.Fn deflateSetHeader "z_streamp strm" "gz_headerp head"
+.
+.Sh DESCRIPTION
+.Fn deflateSetHeader
+provides gzip header information
+for when a gzip stream
+is requested by
+.Xr deflateInit2 3 .
+.Fn deflateSetHeader
+may be called after
+.Xr deflateInit2 3
+or
+.Xr deflateReset 3
+and before the first call of
+.Xr deflate 3 .
+The
+text,
+time,
+OS,
+extra field,
+name,
+and comment
+information in the provided
+.Vt gz_header
+structure
+are written to the gzip header
+.Po
+.Fa xflag
+is ignored \(em
+the extra flags are set
+according to the compression level
+.Pc .
+The caller must assure that,
+if not
+.Dv Z_NULL ,
+.Fa name
+and
+.Fa comment
+are terminated with a zero byte,
+and that if
+.Fa extra
+is not
+.Dv Z_NULL ,
+that
+.Fa extra_len
+bytes are available there.
+If
+.Fa hcrc
+is true,
+a gzip header CRC is included.
+Note that the current versions
+of the command-line version of
+.Xr gzip 1
+(up through version 1.3.x)
+do not support header CRCs,
+and will report that it is a
+"multi-part gzip file"
+and give up.
+.
+.Pp
+If
+.Fn deflateSetHeader
+is not used,
+the default gzip header has
+text false,
+the time set to zero,
+and OS set to 255,
+with no extra, name, or comment fields.
+The gzip header is returned
+to the default state by
+.Xr deflateReset 3 .
+.
+.Pp
+The fields of
+.Vt gz_header
+are as follows:
+.
+.Bl -tag -width "extra_len"
+.It Fa text
+true if compressed data believed to be text
+.It Fa time
+modification time
+.It Fa xflags
+extra flags
+(not used when writing a gzip file)
+.It Fa os
+operating system
+.It Fa extra
+pointer to extra field or
+.Dv Z_NULL
+if none
+.It Fa extra_len
+extra field length
+.Po
+valid if
+.Fa extra
+!=
+.Dv Z_NULL
+.Pc
+.It Fa extra_max
+space at extra
+(only when reading header)
+.It Fa name
+pointer to zero-terminated file name or
+.Dv Z_NULL
+.It Fa name_max
+space at
+.Fa name
+(only when reading header)
+.It Fa comment
+pointer to zero-terminated comment or
+.Dv Z_NULL
+.It Fa comm_max
+space at comment
+(only when reading header)
+.It Fa hcrc
+true if there was or will be a header CRC
+.It Fa done
+true when done reading gzip header
+(not used when writing a gzip file)
+.El
+.
+.Sh RETURN VALUES
+.Fn deflateSetHeader
+returns
+.Dv Z_OK
+if success,
+or
+.Dv Z_STREAM_ERROR
+if the source stream state was inconsistent.
+.
+.Sh SEE ALSO
+.Xr gzip 1 ,
+.Xr deflateInit2 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/deflateTune.3 b/doc/zlib/deflateTune.3
new file mode 100644
index 00000000..7269dec0
--- /dev/null
+++ b/doc/zlib/deflateTune.3
@@ -0,0 +1,70 @@
+.Dd January 15, 2017
+.Dt DEFLATETUNE 3
+.Os
+.
+.Sh NAME
+.Nm deflateTune
+.Nd fine tune compression parameters
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fo deflateTune
+.Fa "z_streamp strm"
+.Fa "int good_length"
+.Fa "int max_lazy"
+.Fa "int nice_length"
+.Fa "int max_chain"
+.Fc
+.
+.Sh DESCRIPTION
+Fine tune deflate's internal compression parameters.
+This should only be used
+by someone who understands the algorithm
+used by zlib's deflate
+for searching for the best matching string,
+and even then only by the most fanatic optimizer
+trying to squeeze out the last compressed bit
+for their specific input data.
+Read the
+.Pa deflate.c
+source code for the meaning of the
+.Fa max_lazy ,
+.Fa good_length ,
+.Fa nice_length ,
+and
+.Fa max_chain
+parameters.
+.
+.Pp
+.Fn deflateTune
+can be called after
+.Xr deflateInit 3
+or
+.Xr deflateInit2 3 .
+.
+.Sh RETURN VALUES
+.Fn deflateTune
+returns
+.Dv Z_OK
+on success,
+or
+.Dv Z_STREAM_ERROR
+for an invalid deflate stream.
+.
+.Sh SEE ALSO
+.Xr deflateInit 3 ,
+.Xr deflateInit2 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzbuffer.3 b/doc/zlib/gzbuffer.3
new file mode 100644
index 00000000..de7c706a
--- /dev/null
+++ b/doc/zlib/gzbuffer.3
@@ -0,0 +1,59 @@
+.Dd January 15, 2017
+.Dt GZBUFFER 3
+.Os
+.
+.Sh NAME
+.Nm gzbuffer
+.Nd set buffer size
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzbuffer "gzFile file" "unsigned size"
+.
+.Sh DESCRIPTION
+Set the internal buffer size
+used by this library's functions.
+The default buffer size is 8192 bytes.
+This function must be called after
+.Xr gzopen 3
+or
+.Xr gzdopen 3 ,
+and before any other calls
+that read or write the file.
+The buffer memory allocation
+is always deferred to the first read or write.
+Three times that size in buffer space is allocated.
+A larger buffer size of,
+for example,
+64K or 128K bytes
+will noticeably increase the speed
+of decompression (reading).
+.
+.Pp
+The new buffer size also affects
+the maximum length for
+.Xr gzprintf 3 .
+.
+.Sh RETURN VALUES
+.Fn gzbuffer
+returns 0 on success,
+or -1 on failure,
+such as being called too late.
+.
+.Sh SEE ALSO
+.Xr gzopen 3 ,
+.Xr gzprintf 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzclose.3 b/doc/zlib/gzclose.3
new file mode 100644
index 00000000..77eae11e
--- /dev/null
+++ b/doc/zlib/gzclose.3
@@ -0,0 +1,97 @@
+.Dd January 15, 2017
+.Dt GZCLOSE 3
+.Os
+.
+.Sh NAME
+.Nm gzclose ,
+.Nm gzclose_r ,
+.Nm gzclose_w
+.Nd close compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzclose "gzFile file"
+.Ft int
+.Fn gzclose_r "gzFile file"
+.Ft int
+.Fn gzclose_w "gzFile file"
+.
+.Sh DESCRIPTION
+Flushes all pending output if necessary,
+closes the compressed file
+and deallocates the (de)compression state.
+Note that once
+.Fa file
+is closed,
+you cannot call
+.Xr gzerror 3
+with
+.Fa file ,
+since its structures
+have been deallocated.
+.Fn gzclose
+must not be called more than once
+on the same file,
+just as
+.Xr free 3
+must not be called more than once
+on the same allocation.
+.
+.Pp
+.Fn gzclose_r
+and
+.Fn gzclose_w
+are the same as
+.Fn gzclose ,
+but
+.Fn gzclose_r
+is only for use when reading,
+and
+.Fn gzclose_w
+is only for use when writing or appending.
+The advantage to using these instead of
+.Fn gzclose
+is that they avoid linking in
+zlib compression or decompression code
+that is not used when only reading
+or only writing respectively.
+If
+.Fn gzclose
+is used,
+then both compression and decompression code
+will be included in the application
+when linking to a static zlib library.
+.
+.Sh RETURN VALUES
+.Fn gzclose
+will return
+.Dv Z_STREAM_ERROR
+if
+.Fa file
+is not valid,
+.Dv Z_ERRNO
+on a file operator error,
+.Dv Z_MEM_ERROR
+if out of memory,
+.Dv Z_BUF_ERROR
+if the last read ended in the middle of a gzip stream,
+or
+.Dv Z_OK
+on success.
+.
+.Sh SEE ALSO
+.Xr gzopen 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzdirect.3 b/doc/zlib/gzdirect.3
new file mode 100644
index 00000000..8fa26aae
--- /dev/null
+++ b/doc/zlib/gzdirect.3
@@ -0,0 +1,85 @@
+.Dd January 15, 2017
+.Dt GZDIRECT 3
+.Os
+.
+.Sh NAME
+.Nm gzdirect
+.Nd check direct copy
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzdirect "gzFile file"
+.
+.Sh DESCRIPTION
+Returns true (1) if
+.Fa file
+is being copied directly while reading,
+or false (0) if
+.Fa file
+is a gzip stream being decompressed.
+.
+.Pp
+If the input file is empty,
+.Fn gzdirect
+will return true,
+since the input does not contain a gzip stream.
+.
+.Pp
+If
+.Fn gzdirect
+is used immediately after
+.Xr gzopen 3
+or
+.Xr gzdopen 3
+it will cause buffers to be allocated
+to allow reading the file
+to determine if it is a gzip file.
+Therefore if
+.Xr gzbuffer 3
+is used,
+it should be called before
+.Fn gzdirect .
+.
+.Pp
+When writing,
+.Fn gzdirect
+returns true (1)
+if transparent writing was requested
+.Po
+.Dq wT
+for the
+.Xr gzopen 3
+mode
+.Pc ,
+or false (0) otherwise.
+.Po
+Note:
+.Fn gzdirect
+is not needed when writing.
+Transparent writing
+must be explicitly requested,
+so the application already knows the answer.
+When linking statically,
+using
+.Fn gzdirect
+will include all of the zlib code
+for gzip file reading and decompression,
+which may not be desired.
+.Pc
+.
+.Sh SEE ALSO
+.Xr gzopen 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzeof.3 b/doc/zlib/gzeof.3
new file mode 100644
index 00000000..26c415fe
--- /dev/null
+++ b/doc/zlib/gzeof.3
@@ -0,0 +1,63 @@
+.Dd January 15, 2017
+.Dt GZEOF 3
+.Os
+.
+.Sh NAME
+.Nm gzeof
+.Nd check end-of-file indicator
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzeof "gzFile file"
+.
+.Sh DESCRIPTION
+Returns true (1)
+if the end-of-file indicator
+has been set while reading,
+false (0) otherwise.
+Note that the end-of-file indicator
+is set only if the read
+tried to go past the end of the input,
+but came up short.
+Therefore,
+just like
+.Xr feof 3 ,
+.Fn gzeof
+may return false
+even if there is no more data to read,
+in the event that the last read request
+was for the exact number of bytes
+remaining in the input file.
+This will happen if the input file size
+is an exact multiple of the buffer size.
+.
+.Pp
+If
+.Fn gzeof
+returns true,
+then the read functions
+will return no more data,
+unless the end-of-file indicator
+is reset by
+.Xr gzclearerr 3
+and the input file
+has grown since the previous
+end of file was detected.
+.
+.Sh SEE ALSO
+.Xr gzerror 3 ,
+.Xr gzread 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzerror.3 b/doc/zlib/gzerror.3
new file mode 100644
index 00000000..13dcddd4
--- /dev/null
+++ b/doc/zlib/gzerror.3
@@ -0,0 +1,75 @@
+.Dd January 15, 2017
+.Dt GZERROR 3
+.Os
+.
+.Sh NAME
+.Nm gzerror ,
+.Nm gzclearerr
+.Nd check and reset compressed file error
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft const char *
+.Fn gzerror "gzFile file" "int *errnum"
+.Ft void
+.Fn gzclearerr "gzFile file"
+.
+.Sh DESCRIPTION
+.Fn gzerror
+returns the error message for the last error
+which occured on the given compressed file.
+.Fa errnum
+is set to the zlib error number.
+If an error occurred in the file system
+and not in the compression library,
+.Fa errnum
+is set to
+.Dv Z_ERRNO
+and the application may consult
+.Va errno
+to get the exact error code.
+.
+.Pp
+The application must not modify the returned string.
+Future calls to this function
+may invalidate the previously returned string.
+If
+.Fa file
+is closed,
+then the string previously returned by
+.Fn gzerror
+will no longer be available.
+.
+.Pp
+.Fn gzerror
+should be used to distinguish errors from end-of-file
+for those functions that do not distinguish those cases
+in their return values.
+.
+.Pp
+.Fn gzclearerr
+clears the error and end-of-file for
+.Fa file .
+This is analogous to the
+.Xr clearerr 3
+function in stdio.
+This is useful for continuing to read a gzip file
+that is being written concurrently.
+.
+.Sh SEE ALSO
+.Xr gzeof 3 ,
+.Xr gzread 3 ,
+.Xr gzwrite 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzflush.3 b/doc/zlib/gzflush.3
new file mode 100644
index 00000000..b93c03e7
--- /dev/null
+++ b/doc/zlib/gzflush.3
@@ -0,0 +1,73 @@
+.Dd January 15, 2017
+.Dt GZFLUSH 3
+.Os
+.
+.Sh NAME
+.Nm gzflush
+.Nd flush output to compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzflush "gzFile file" "int flush"
+.
+.Sh DESCRIPTION
+Flushes all pending output
+into the compressed file.
+The parameter
+.Fa flush
+is as in the
+.Xr deflate 3
+function.
+.Fn gzflush
+is only permitted when writing.
+.
+.Pp
+If the
+.Fa flush
+parameter is
+.Dv Z_FINISH ,
+the remaining data is written
+and the gzip stream
+is completed in the output.
+If
+.Xr gzwrite 3
+is called again,
+a new gzip stream
+will be started in the output.
+.Xr gzread 3
+is able to read
+such concatenated gzip streams.
+.
+.Pp
+.Fn gzflush
+should be called only when strictly necessary
+because it will degrade compression
+if called too often.
+.
+.Sh RETURN VALUES
+The return value
+is the zlib error number
+.Po
+see function
+.Xr gzerror 3
+.Pc .
+.
+.Sh SEE ALSO
+.Xr deflate 3 ,
+.Xr gzerror 3 ,
+.Xr gzread 3 ,
+.Xr gzwrite 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzfread.3 b/doc/zlib/gzfread.3
new file mode 100644
index 00000000..66231cc3
--- /dev/null
+++ b/doc/zlib/gzfread.3
@@ -0,0 +1,107 @@
+.Dd January 15, 2017
+.Dt GZFREAD 3
+.Os
+.
+.Sh NAME
+.Nm gzfread
+.Nd read from compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft z_size_t
+.Fn gzfread "voidp buf" "z_size_t size" "z_size_t nitems" "gzFile file"
+.
+.Sh DESCRIPTION
+Read up to
+.Fa nitems
+of size
+.Fa size
+from
+.Fa file
+to
+.Fa buf ,
+otherwise operating as
+.Xr gzread 3
+does.
+This duplicates the interface of stdio's
+.Xr fread 3 ,
+with
+.Vt size_t
+request and return types.
+If the library defines
+.Vt size_t ,
+then
+.Vt z_size_t
+is identical to
+.Vt size_t .
+If not,
+then
+.Vt z_size_t
+is an unsigned integer type
+that can contain a pointer.
+.
+.Pp
+In the event that the end of file is reached
+and only a partial item is available at the end,
+i.e. the remaining uncompressed data length
+is not a multiple of
+.Fa size ,
+then the file partial item
+is nevertheless read into
+.Fa buf
+and the end-of-file flag is set.
+The length of the partial item read
+is not provided,
+but could be inferred from the result of
+.Xr gztell 3 .
+This behavior is the same as the behavior of
+.Xr fread 3
+implementations in common libraries,
+but it prevents the direct use of
+.Fn gzfread
+to read a concurrently written file,
+reseting and retrying on end-of-file,
+when
+.Fa size
+is not 1.
+.
+.Sh RETURN VALUES
+.Fn gzfread
+returns the number of full items read of size
+.Fa size ,
+or zero if the end of the file was reached
+and a full item could not be read,
+or if there was an error.
+.Xr gzerror 3
+must be consulted if zero is returned
+in order to determine if there was an error.
+If the multiplication of
+.Fa size
+and
+.Fa nitems
+overflows,
+i.e. the product does not fit in
+.Vt z_size_t ,
+then nothing is read,
+zero is returned,
+and the error state is set to
+.Dv Z_STREAM_ERROR .
+.
+.Sh SEE ALSO
+.Xr gzeof 3 ,
+.Xr gzerror 3 ,
+.Xr gzopen 3 ,
+.Xr gzread 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzfwrite.3 b/doc/zlib/gzfwrite.3
new file mode 100644
index 00000000..38383a33
--- /dev/null
+++ b/doc/zlib/gzfwrite.3
@@ -0,0 +1,75 @@
+.Dd January 15, 2017
+.Dt GZFWRITE 3
+.Os
+.
+.Sh NAME
+.Nm gzfwrite
+.Nd write to compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft z_size_t
+.Fn gzfwrite "voidpc buf" "z_size_t size" "z_size_t nitems" "gzFile file"
+.
+.Sh DESCRIPTION
+.Fn gzfwrite
+writes
+.Fa nitems
+items of size
+.Fa size
+from
+.Fa buf
+to
+.Fa file ,
+duplicating the interface of stdio's
+.Xr fwrite 3 ,
+with
+.Vt size_t
+request and return types.
+If the library defines
+.Vt size_t ,
+then
+.Vt z_size_t
+is identical to
+.Vt size_t .
+If not,
+then
+.Vt z_size_t
+is an unsigned integer type
+that can contain a pointer.
+.
+.Sh RETURN VALUES
+.Fn gzfwrite
+returns the number of full items
+written of size
+.Fa size ,
+or zero if there was an error.
+If the multiplication of
+.Fa size
+and
+.Fa nitems
+overflows,
+i.e. the product does not fit in a
+.Vt z_size_t ,
+then nothing is written,
+zero is returned,
+and the error state is set to
+.Dv Z_STREAM_ERROR .
+.
+.Sh SEE ALSO
+.Xr gzerror 3 ,
+.Xr gzopen 3 ,
+.Xr gzwrite 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzgetc.3 b/doc/zlib/gzgetc.3
new file mode 100644
index 00000000..93a90edd
--- /dev/null
+++ b/doc/zlib/gzgetc.3
@@ -0,0 +1,55 @@
+.Dd January 15, 2017
+.Dt GZGETC 3
+.Os
+.
+.Sh NAME
+.Nm gzgetc
+.Nd get character from compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzgetc "gzFile file"
+.
+.Sh DESCRIPTION
+Reads one byte from the compressed file.
+This is implemented as a macro for speed.
+As such,
+it does not do all of the checking
+the other functions do.
+I.e.\&
+it does not check to see if
+.Fa file
+is
+.Dv NULL ,
+nor whether the structure
+.Fa file
+points to has been clobbered or not.
+.
+.Sh RETURN VALUES
+.Fn gzgetc
+returns the byte
+or -1 in case of
+end of file
+or error.
+.
+.Sh SEE ALSO
+.Xr gzeof 3 ,
+.Xr gzerror 3 ,
+.Xr gzgets 3 ,
+.Xr gzopen 3 ,
+.Xr gzread 3 ,
+.Xr gzungetc 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzgets.3 b/doc/zlib/gzgets.3
new file mode 100644
index 00000000..2a329e9e
--- /dev/null
+++ b/doc/zlib/gzgets.3
@@ -0,0 +1,67 @@
+.Dd January 15, 2017
+.Dt GZGETS 3
+.Os
+.
+.Sh NAME
+.Nm gzgets
+.Nd read line from compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft char *
+.Fn gzgets "gzFile file" "char *buf" "int len"
+.
+.Sh DESCRIPTION
+Reads bytes from the compressed file
+until
+.Fa len-1
+characters are read,
+or a newline character
+is read and transferred to
+.Fa buf ,
+or an end-of-file condition
+is encountered.
+If any characters are read or if
+.Fa len
+== 1,
+the string is terminated
+with a null character.
+If no characters are read
+due to an end-of-file or
+.Fa len
+< 1,
+then the buffer is left untouched.
+.
+.Sh RETURN VALUES
+.Fn gzgets
+returns
+.Fa buf
+which is a null-terminated string,
+or it returns
+.Dv NULL
+for end-of-file
+or in case of error.
+If there was an error,
+the contents at
+.Fa buf
+are indeterminate.
+.
+.Sh SEE ALSO
+.Xr gzeof 3 ,
+.Xr gzerror 3 ,
+.Xr gzgetc 3 ,
+.Xr gzopen 3 ,
+.Xr gzread 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzoffset.3 b/doc/zlib/gzoffset.3
new file mode 100644
index 00000000..cbb78a77
--- /dev/null
+++ b/doc/zlib/gzoffset.3
@@ -0,0 +1,51 @@
+.Dd January 15, 2017
+.Dt GZOFFSET 3
+.Os
+.
+.Sh NAME
+.Nm gzoffset
+.Nd offset in compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft z_off_t
+.Fn gzoffset "gzFile file"
+.
+.Sh DESCRIPTION
+Returns the current offset
+in the file being read or written.
+This offset includes
+the count of bytes
+that precede the gzip stream,
+for example when appending
+or when using
+.Xr gzdopen 3
+for reading.
+When reading,
+the offset does not include
+as yet unused buffered input.
+This information can be used
+for a progress indicator.
+.
+.Sh RETURN VALUES
+On error,
+.Fn gzoffset
+returns -1.
+.
+.Sh SEE ALSO
+.Xr gzerror 3 ,
+.Xr gzopen 3 ,
+.Xr gzseek 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzopen.3 b/doc/zlib/gzopen.3
new file mode 100644
index 00000000..e3cb4cbd
--- /dev/null
+++ b/doc/zlib/gzopen.3
@@ -0,0 +1,261 @@
+.Dd January 15, 2017
+.Dt GZOPEN 3
+.Os
+.
+.Sh NAME
+.Nm gzopen ,
+.Nm gzdopen
+.Nd open gzip file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft gzFile
+.Fn gzopen "const char *path" "const char *mode"
+.Ft gzFile
+.Fn gzdopen "int fd" "const char *mode"
+.
+.Sh DESCRIPTION
+Opens a gzip (.gz) file
+for reading or writing.
+The
+.Fa mode
+parameter is as in
+.Xr fopen 3
+.Po
+.Dq rb
+or
+.Dq wb
+.Pc
+but can also include a compression level
+.Pq Dq wb9
+or a strategy:
+.Sq f
+for filtered data as in
+.Dq wb6f ,
+.Sq h
+for Huffman-only compression as in
+.Dq wb1h ,
+.Sq R
+for run-length encoding as in
+.Dq wb1R ,
+or
+.Sq F
+for fixed code compression as in
+.Dq wb9F .
+.Po
+See the description of
+.Xr deflateInit2 3
+for more information about the
+.Fa strategy
+parameter.
+.Pc \&
+.Sq T
+will request transparent writing or appending
+with no compression
+and not using the gzip format.
+.
+.Pp
+.Dq a
+can be used instead of
+.Dq w
+to request that the gzip stream
+that will be written
+be appended to the file.
+.Dq +
+will result in an error,
+since reading and writing
+to the same gzip file
+is not supported.
+The addition of
+.Dq x
+when writing will create the file exclusively,
+which fails if the file already exists.
+On systems that support it,
+the addition of
+.Dq e
+when reading or writing
+will set the flag to close the file on an
+.Xr execve 2
+call.
+.
+.Pp
+These functions,
+as well as
+.Xr gzip 1 ,
+will read and decode
+a sequence of gzip streams in a file.
+The append function of
+.Fn gzopen
+can be used to create such a file.
+.Po
+Also see
+.Xr gzflush 3
+for another way to do this.
+.Pc \&
+When appending,
+.Fn gzopen
+does not test whether the file begins with a gzip stream,
+nor does it look for the end of the gzip streams
+to begin appending.
+.Fn gzopen
+will simply append a gzip stream
+to the existing file.
+.
+.Pp
+.Fn gzopen
+can be used to read a file which is not in gzip format;
+in this case
+.Xr gzread 3
+will directly read from the file without decompression.
+When reading,
+this will be detected automatically
+by looking for the magic two-byte gzip header.
+.
+.Pp
+.Fn gzdopen
+associates at
+.Vt gzFile
+with the file descriptor
+.Fa fd .
+File descriptors
+are obtained from calls like
+.Xr open 2 ,
+.Xr dup 2 ,
+.Xr creat 2 ,
+.Xr pipe 2
+or
+.Xr fileno 3
+.Po
+if the file has been previously opened with
+.Xr fopen 3
+.Pc .
+The
+.Fa mode
+parameter is as in
+.Fn gzopen .
+.
+.Pp
+The next call of
+.Xr gzclose 3
+on the returned
+.Vt gzFile
+will also close the file descriptor
+.Fa fd ,
+just like
+.Xr fclose 3 .
+If you want to keep
+.Fa fd
+open,
+use
+.Li "fd = dup(fd_keep); gz = gzdopen(fd, mode)" .
+The duplicated descriptor should be saved
+to avoid a leak,
+since
+.Fn gzdopen
+does not close
+.Fa fd
+if it fails.
+If you are using
+.Xr fileno 3
+to get the file descriptor from a
+.Vt FILE * ,
+then you will have to use
+.Xr dup 2
+to avoid double-closing
+the file descriptor.
+Both
+.Xr gzclose 3
+and
+.Xr flcose 3
+will close the associated file descriptor,
+so they need to have different file descriptors.
+.
+.Sh RETURN VALUES
+.Fn gzopen
+and
+.Fn gzdopen
+return
+.Dv NULL
+if the file could not be opened,
+if there was insufficient memory
+to allocate the
+.Vt gzFile
+state,
+or if an invalid
+.Fa mode
+was specified
+.Po
+an
+.Sq r ,
+.Sq w ,
+or
+.Sq a
+was not provided,
+or
+.Sq +
+was provided
+.Pc ,
+or if
+.Fa fd
+is -1.
+The file descriptor
+is not used until the next
+gz* read, write, seek, or close operation,
+so
+.Fn gzdopen
+will not detect if
+.Fa fd
+is invalid
+.Po
+unless
+.Fa fd
+is -1
+.Pc .
+.Va errno
+can be checked
+to determine if the reason
+.Fn gzopen
+failed was that the file
+could not be opened.
+.
+.Sh ERRORS
+The
+.Fn gzopen
+function may fail and set
+.Va errno
+for any of the errors specified
+for the routine
+.Xr fopen 3 .
+.
+.Sh SEE ALSO
+.Xr deflateInit2 3 ,
+.Xr fopen 3 ,
+.Xr gzbuffer 3 ,
+.Xr gzclose 3 ,
+.Xr gzdirect 3 ,
+.Xr gzeof 3 ,
+.Xr gzerror 3 ,
+.Xr gzflush 3 ,
+.Xr gzgetc 3 ,
+.Xr gzgets 3 ,
+.Xr gzoffset 3 ,
+.Xr gzprintf 3 ,
+.Xr gzputc 3 ,
+.Xr gzputs 3 ,
+.Xr gzread 3 ,
+.Xr gzseek 3 ,
+.Xr gzsetparams 3 ,
+.Xr gzwrite 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzprintf.3 b/doc/zlib/gzprintf.3
new file mode 100644
index 00000000..26961f34
--- /dev/null
+++ b/doc/zlib/gzprintf.3
@@ -0,0 +1,71 @@
+.Dd January 15, 2017
+.Dt GZPRINTF 3
+.Os
+.
+.Sh NAME
+.Nm gzprintf
+.Nd format output to compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzprintf "gzFile file" "const char *format" "..."
+.
+.Sh DESCRIPTION
+Converts, formats, and writes the arguments
+to the compressed file
+under control of the format string,
+as in
+.Xr fprintf 3 .
+.
+.Sh RETURN VALUES
+.Fn gzprintf
+returns the number of
+uncompressed bytes actually written,
+or a negative zlib error code
+in case of error.
+The number of uncompressed bytes written
+is limited to 8191,
+or one less than the buffer size given to
+.Xr gzbuffer 3 .
+The caller should assure that
+this limit is not exceeded.
+If it is exceeded,
+then
+.Fn gzprintf
+will return an error (0)
+with nothing written.
+In this case,
+there may also be a buffer overflow
+with unpredictable consequences,
+which is possibly only if zlib
+was compiled with the insecure functions
+.Xr sprintf 3
+or
+.Xr vsprintf 3
+because the secure
+.Xr snprintf 3
+or
+.Xr vsnprintf 3
+functions
+were not available.
+This can be determined using
+.Xr zlibCompileFlags 3 .
+.
+.Sh SEE ALSO
+.Xr fprintf 3 ,
+.Xr gzerror 3 ,
+.Xr gzopen 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzputc.3 b/doc/zlib/gzputc.3
new file mode 100644
index 00000000..161e5631
--- /dev/null
+++ b/doc/zlib/gzputc.3
@@ -0,0 +1,43 @@
+.Dd January 15, 2017
+.Dt GZPUTC 3
+.Os
+.
+.Sh NAME
+.Nm gzputc
+.Nd write character to compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzputc "gzFile file" "int c"
+.
+.Sh DESCRIPTION
+Writes
+.Fa c ,
+converted to an
+.Vt unsigned char ,
+into the compressed file.
+.
+.Sh RETURN VALUES
+.Fn gzputc
+returns the value that was written,
+or -1 in case of error.
+.
+.Sh SEE ALSO
+.Xr gzerror 3 ,
+.Xr gzopen 3 ,
+.Xr gzputs 3 ,
+.Xr gzwrite 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzputs.3 b/doc/zlib/gzputs.3
new file mode 100644
index 00000000..f5d1fd84
--- /dev/null
+++ b/doc/zlib/gzputs.3
@@ -0,0 +1,41 @@
+.Dd January 15, 2017
+.Dt GZPUTS 3
+.Os
+.
+.Sh NAME
+.Nm gzputs
+.Nd write string to compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzputs "gzFile file" "const char *s"
+.
+.Sh DESCRIPTION
+Writes the given null-terminated string
+to the compressed file,
+excluding the terminating null character.
+.
+.Sh RETURN VALUES
+.Fn gzputs
+returns the number of characters written,
+or -1 in case of error.
+.
+.Sh SEE ALSO
+.Xr gzerror 3 ,
+.Xr gzopen 3 ,
+.Xr gzputc 3 ,
+.Xr gzwrite 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzread.3 b/doc/zlib/gzread.3
new file mode 100644
index 00000000..84439eaa
--- /dev/null
+++ b/doc/zlib/gzread.3
@@ -0,0 +1,115 @@
+.Dd January 15, 2017
+.Dt GZREAD 3
+.Os
+.
+.Sh NAME
+.Nm gzread
+.Nd read from compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzread "gzFile file" "voidp buf" "unsigned len"
+.
+.Sh DESCRIPTION
+Reads the given number of uncompressed bytes
+from the compressed file.
+If the input file
+is not in gzip format,
+.Fn gzread
+copies the given number of bytes
+into the buffer directly from the file.
+.
+.Pp
+After reaching the end of a gzip stream
+in the input,
+.Fn gzread
+will continue to read,
+looking for another gzip stream.
+Any number of gzip streams
+may be concatenated in the input file,
+and will all be decompressed by
+.Fn gzread .
+If something other than a gzip stream
+is encountered after a gzip stream,
+that remaining trailing garbage is ignored
+(and no error is returned).
+.
+.Pp
+.Fn gzread
+can be used to read a gzip file
+that is being concurrently written.
+Upon reaching the end of the input,
+.Fn gzread
+will return with the available data.
+If the error code returned by
+.Xr gzerror 3
+is
+.Dv Z_OK
+or
+.Dv Z_BUF_ERROR ,
+then
+.Xr gzclearerr 3
+can be used
+to clear the end of file indicator
+in order to permit
+.Fn gzread
+to be tried again.
+.Dv Z_OK
+indicates that a gzip stream was completed
+on the last
+.Fn gzread .
+.Dv Z_BUF_ERROR
+indicates that the input file
+ended in the middle of a gzip stream.
+Note that
+.Fn gzread
+does not return -1
+in the event of an incomplete gzip stream.
+This error is deferred until
+.Xr gzclose 3 ,
+which will return
+.Dv Z_BUF_ERROR
+if the last
+.Fn gzread
+ended in the middle of a gzip stream.
+Alternatively,
+.Xr gzerror 3
+can be used before
+.Xr gzclose 3
+to detect this case.
+.
+.Sh RETURN VALUES
+.Fn gzread
+returns the number of uncompressed bytes actually read,
+less than
+.Fa len
+for end of file,
+or -1 for error.
+If
+.Fa len
+is too large to fit in an
+.Vt int ,
+then nothing is read,
+-1 is returned,
+and the error state is set to
+.Dv Z_STREAM_ERROR .
+.
+.Sh SEE ALSO
+.Xr gzeof 3 ,
+.Xr gzerror 3 ,
+.Xr gzfread 3 ,
+.Xr gzopen 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzseek.3 b/doc/zlib/gzseek.3
new file mode 100644
index 00000000..cd85fd4c
--- /dev/null
+++ b/doc/zlib/gzseek.3
@@ -0,0 +1,108 @@
+.Dd January 15, 2017
+.Dt GZSEEK 3
+.Os
+.
+.Sh NAME
+.Nm gzseek ,
+.Nm gzrewind ,
+.Nm gztell
+.Nd seek compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft z_off_t
+.Fn gzseek "gzFile file" "z_off_t offset" "int whence"
+.Ft int
+.Fn gzrewind "gzFile file"
+.Ft z_off_t
+.Fn gztell "gzFile file"
+.
+.Sh DESCRIPTION
+Sets the starting position
+for the next
+.Xr gzread 3
+or
+.Xr gzwrite 3
+on the given compressed file.
+The
+.Fa offset
+represents a number of bytes
+in the uncompressed data stream.
+The
+.Fa whence
+parameter
+is defined as in
+.Xr lseek 2 ;
+the value
+.Dv SEEK_END
+is not supported.
+.
+.Pp
+If the file is opened for reading,
+this function is emulated
+but can be extremely slow.
+If the file is opened for writing,
+only forward seeks are supported;
+.Fn gzseek
+then compresses a sequence of zeroes
+up to the new starting position.
+.
+.Pp
+.Fn gzrewind
+rewinds the given file.
+This function is supported
+only for reading.
+.
+.Pp
+.Fn gzrewind file
+is equivalent to
+.Li (int) Ns Fn gzseek file 0L SEEK_SET .
+.
+.Pp
+.Fn gztell
+returns the starting position
+for the next
+.Xr gzread 3
+or
+.Xr gzwrite 3
+on the given compressed file.
+This position represents a number of bytes
+in the uncompressed data stream,
+and is zero when starting,
+even if appending or reading
+a gzip stream from the middle of a file using
+.Xr gzdopen 3 .
+.
+.Pp
+.Fn gztell file
+is equivalent to
+.Fn gzseek file 0L SEEK_CUR .
+.
+.Sh RETURN VALUES
+.Fn gzseek
+returns the resulting offset location
+as measured in bytes
+from the beginning of the uncompressed stream,
+or -1 in case of error,
+in particular if the file
+is opened for writing
+and the new starting position
+would be before the current position.
+.
+.Sh SEE ALSO
+.Xr gzerror 3 ,
+.Xr gzoffset 3 ,
+.Xr gzopen 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzsetparams.3 b/doc/zlib/gzsetparams.3
new file mode 100644
index 00000000..ff544d23
--- /dev/null
+++ b/doc/zlib/gzsetparams.3
@@ -0,0 +1,51 @@
+.Dd January 15, 2017
+.Dt GZSETPARAMS 3
+.Os
+.
+.Sh NAME
+.Nm gzsetparams
+.Nd set compression level and strategy
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzsetparams "gzFile file" "int level" "int strategy"
+.
+.Sh DESCRIPTION
+Dynamically update the compression level or strategy.
+See the description of
+.Xr deflateInit2 3
+for the meaning
+of these parameters.
+Previously provided data is flushed
+before the parameter change.
+.
+.Sh RETURN VALUES
+.Fn gzsetparams
+returns
+.Dv Z_OK
+if success,
+.Dv Z_STREAM_ERROR
+if the file was not opened for writing,
+.Dv Z_ERRNO
+if there is an error writing the flushed data,
+or
+.Dv Z_MEM_ERROR
+if there is a memory allocation error.
+.
+.Sh SEE ALSO
+.Xr deflateInit2 3 ,
+.Xr gzopen 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzungetc.3 b/doc/zlib/gzungetc.3
new file mode 100644
index 00000000..90cdafc7
--- /dev/null
+++ b/doc/zlib/gzungetc.3
@@ -0,0 +1,67 @@
+.Dd January 15, 2017
+.Dt GZUNGETC 3
+.Os
+.
+.Sh NAME
+.Nm gzungetc
+.Nd un-get character from compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzungetc "int c" "gzFile file"
+.
+.Sh DESCRIPTION
+Push one character back onto the stream
+to be read as the first character
+on the next read.
+At least one character of push-back
+is allowed.
+.Fn gzungetc
+will fail if
+.Fa c
+is -1,
+and may fail if a character
+has been pushed
+but not read yet.
+If
+.Fn gzungetc
+is used immediately after
+.Xr gzopen 3
+or
+.Xr gzdopen 3 ,
+at least the output buffer size
+of pushed characters is allowed.
+.Po
+See
+.Xr gzbuffer 3 .
+.Pc \&
+The pushed character will be discarded
+if the stream is repositioned with
+.Xr gzseek 3
+or
+.Xr gzrewind 3 .
+.
+.Sh RETURN VALUES
+.Fn gzungetc
+returns the character pushed,
+or -1 on failure.
+.
+.Sh SEE ALSO
+.Xr gzbuffer 3 ,
+.Xr gzerror 3 ,
+.Xr gzgetc 3 ,
+.Xr gzopen 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/gzwrite.3 b/doc/zlib/gzwrite.3
new file mode 100644
index 00000000..606d89f4
--- /dev/null
+++ b/doc/zlib/gzwrite.3
@@ -0,0 +1,39 @@
+.Dd January 15, 2017
+.Dt GZWRITE 3
+.Os
+.
+.Sh NAME
+.Nm gzwrite
+.Nd write to compressed file
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn gzwrite "gzFile file" "voidpc buf" "unsigned len"
+.
+.Sh DESCRIPTION
+Writes the given number of uncompressed bytes
+into the compressed file.
+.
+.Sh RETURN VALUES
+.Fn gzwrite
+returns the number of uncompressed bytes written
+or 0 in case of error.
+.
+.Sh SEE ALSO
+.Xr gzerror 3 ,
+.Xr gzfwrite 3 ,
+.Xr gzopen 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflate.3 b/doc/zlib/inflate.3
new file mode 100644
index 00000000..ca90c270
--- /dev/null
+++ b/doc/zlib/inflate.3
@@ -0,0 +1,398 @@
+.Dd January 15, 2017
+.Dt INFLATE 3
+.Os
+.
+.Sh NAME
+.Nm inflate
+.Nd deflate decompression
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn inflate "z_streamp strm" "int flush"
+.
+.Sh DESCRIPTION
+.Fn inflate
+decompresses as much data as possible,
+and stops when the input buffer becomes empty
+or the output buffer becomes full.
+It may introduce some output latency
+(reading input without producing any output)
+except when forced to flush.
+.
+.Pp
+The detailed semantics are as follows.
+.Fn inflate
+performs one or both of the following actions:
+.
+.Bl -dash
+.It
+Decompress more input starting at
+.Fa next_in
+and update
+.Fa next_in
+and
+.Fa avail_in
+accordingly.
+If not all input can be processed
+(because there is not enough room in the output buffer),
+then
+.Fa next_in
+and
+.Fa avail_in
+are updated accordingly,
+and processing will resume at this point
+for the next call of
+.Fn inflate .
+.
+.It
+Generate more output starting at
+.Fa next_out
+and update
+.Fa next_out
+and
+.Fa avail_out
+accordingly.
+.Fn inflate
+provides as much output as possible,
+until there is no more input data
+or no more space in the output buffer
+.Po
+see below about the
+.Fa flush
+parameter
+.Pc .
+.El
+.
+.Pp
+Before the call of
+.Fn inflate ,
+the application should ensure that
+at least one of the actions is possible,
+by providing more input
+and/or consuming more output,
+and updating the
+.Fa next_*
+and
+.Fa avail_*
+values accordingly.
+If the caller of
+.Fn inflate
+does not provide both available input
+and available output space,
+it is possible that there will be no progress made.
+The application can consume the uncompressed output
+when it wants,
+for example when the output buffer is full
+.Po
+.Fa avail_out
+== 0
+.Pc ,
+or after each call of
+.Fn inflate .
+If
+.Fn inflate
+returns
+.Dv Z_OK
+and with zero
+.Fa avail)out ,
+it must be called again after making room
+in the output buffer
+because there might be more output pending.
+.
+.Pp
+The
+.Fa flush
+parameter of
+.Fn inflate
+can be
+.Dv Z_NO_FLUSH ,
+.Dv Z_SYNC_FLUSH ,
+.Dv Z_FINISH ,
+.Dv Z_BLOCK ,
+or
+.Dv Z_TREES .
+.Dv Z_SYNC_FLUSH
+requests that
+.Fn inflate
+flush as much output as possible
+to the output buffer.
+.Dv Z_BLOCK
+requests that
+.Fn inflate
+stop if and when it gets to the next deflate block boundary.
+When decoding the zlib or gzip format,
+this will cause
+.Fn inflate
+to return immediately after the header
+and before the first block.
+When doing a raw inflate,
+.Fn inflate
+will go ahead and process the first block,
+and will return when it gets to the end of that block,
+or when it runs out of data.
+.
+.Pp
+The
+.Dv Z_BLOCK
+option assists in appending to
+or combining deflate streams.
+To assist in this,
+on return
+.Fn inflate
+always sets
+.Fa strm->data_type
+to the number of unused bits
+in the last byte taken from
+.Fa strm->next_in ,
+plus 64 if
+.Fn inflate
+is currently decoding the last block in the deflate stream,
+plus 128 if
+.Fn inflate
+returned immediately after decoding an end-of-block code
+or decoding the complete header up to
+just before the first byte of the deflate stream.
+The end-of-block will not be indicated
+until all of the uncompressed data
+from that block has been written to
+.Fa strm->next_out .
+The number of unused bits may in general be greater than seven,
+except when bit 7 of
+.Fa data_type
+is set,
+in which case the number of unused bits
+will be less than eight.
+.Fa data_type
+is set as noted here every time
+.Fn inflate
+returns for all flush options,
+and so can be used to determine
+the amount of currently consumed input in bits.
+.
+.Pp
+The
+.Dv Z_TREES
+option behaves as
+.Dv Z_BLOCK
+does,
+but it also returns
+when the end of each deflate block header is reached,
+before any actual data in that block is decoded.
+This allows the caller to determine
+the length of the deflate block header
+for later use in random access
+within a deflate block.
+256 is added to the value of
+.Fa strm->data_type
+when
+.Fn inflate
+returns immediately after reaching
+the end of the deflate block header.
+.
+.Pp
+.Fn inflate
+should normally be called until it returns
+.Dv Z_STREAM_END
+or an error.
+However if all decompression is to be performed
+in a single step
+.Po
+a single call of
+.Fn inflate
+.Pc ,
+the parameter
+.Fa flush
+should be set to
+.Dv Z_FINISH .
+In this case all pending input is processed
+and all pending output is flushed;
+.Fa avail_out
+must be large enough to hold all of
+the uncompressed data for the operation to complete.
+(The size of the uncompressed data
+may have been saved by the compressor for this purpose.)
+The use of
+.Dv Z_FINISH
+is not required to perform inflation in one step.
+However it may be used to inform
+.Fn inflate
+that a faster approach can be used for the single
+.Fn inflate
+call.
+.Dv Z_FINISH also informs
+.Fn inflate
+to not maintain a sliding window
+if the stream completes,
+which reduces
+.Fn inflate Ap s
+memory footprint.
+If the stream does not complete,
+either because not all of the stream is provided
+or not enough output space is provided,
+then a sliding window will be allocated and
+.Fn inflate
+can be called again to continue the operation as if
+.Dv Z_NO_FLUSH
+had been used.
+.
+.Pp
+In this implementation,
+.Fn inflate
+always flushes as much output as possible
+to the output buffer,
+and always uses the faster approach
+on the first call.
+So the effects of the
+.Fa flush
+parameter in this implementation
+are on the return value of
+.Fn inflate
+as noted below,
+when
+.Fn inflate
+returns early when
+.Dv Z_BLOCK
+or
+.Dv Z_TREES
+is used,
+and when
+.Fn inflate
+avoids the allocation of memory
+for a sliding window when
+.Dv Z_FINISH
+is used.
+.
+.Pp
+If a preset dictionary is needed after this call
+.Po
+see
+.Xr inflateSetDictionary 3
+.Pc ,
+.Fn inflate
+sets
+.Fa strm->adler
+to the Adler-32 checksum of the dictionary
+chosen by the compressor
+and returns
+.Dv Z_NEED_DICT ;
+otherwise it sets
+.Fa strm->adler
+to the Adler-32 checksum
+of all output produced so far
+.Po
+that is,
+.Fa total_out
+bytes
+.Pc
+and returns
+.Dv Z_OK ,
+.Dv Z_STREAM_END
+or an error code
+as described in
+.Sx RETURN VALUES .
+At the end of the stream,
+.Fn inflate
+checks that its computed Adler-32 checksum
+is equal to that saved by the compressor
+and returns
+.Dv Z_STREAM_END
+only if the checksum is correct.
+.
+.Pp
+.Fn inflate
+can decompress and check
+either zlib-wrapped or gzip-wrapped
+deflate data.
+The header type is detected automatically,
+if requested when initializing with
+.Xr inflateInit2 3 .
+Any information contained in the gzip header
+is not retained unless
+.Xr inflateGetHeader 3
+is used.
+When processing gzip-wrapped deflate data,
+.Fa strm->adler32
+is set to the CRC-32
+of the output produced so far.
+The CRC-32 is checked against the gzip trailer,
+as is the uncompressed length,
+modulo 2^32.
+.
+.Sh RETURN VALUES
+.Fn inflate
+returns
+.Dv Z_OK
+if some progress has been made
+(more input processed or more output produced),
+.Dv Z_STREAM_END
+if the end of the compressed data has been reached
+and all uncompressed output has been produced,
+.Dv Z_NEED_DICT
+if a preset dictionary is needed at this point,
+.Dv Z_DATA_ERROR
+if the input data was corrupted
+.Po
+input stream not conforming to the zlib format
+or incorrect check value,
+in which case
+.Fa strm->msg
+points to a string with a more specific error
+.Pc ,
+.Dv Z_STREAM_ERROR
+if the stream structure was inconsistent
+.Po
+for example
+.Fa next_in
+or
+.Fa next_out
+was
+.Dv Z_NULL ,
+or the state was inadvertently written over
+by the application
+.Pc ,
+.Dv Z_MEM_ERROR
+if there was not enough memory,
+.Dv Z_BUF_ERROR
+if no progress was possible
+or if there was not enough room
+in the output buffer when
+.Dv Z_FINISH
+is used.
+Note that
+.Dv Z_BUF_ERROR
+is not fatal,
+and
+.Fn inflate
+can be called again with more input
+and more output space
+to continue decompressing.
+If
+.Dv Z_DATA_ERROR
+is returned,
+the application may then call
+.Xr inflateSync 3
+to look for a good compression block
+if a partial recovery of the data
+is to be attempted.
+.
+.Sh SEE ALSO
+.Xr deflate 3 ,
+.Xr inflateBack 3 ,
+.Xr inflateEnd 3 ,
+.Xr inflateInit 3 ,
+.Xr inflateMark 3 ,
+.Xr inflateSync 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateBack.3 b/doc/zlib/inflateBack.3
new file mode 100644
index 00000000..59d5f8cb
--- /dev/null
+++ b/doc/zlib/inflateBack.3
@@ -0,0 +1,285 @@
+.Dd January 15, 2017
+.Dt INFLATEBACK 3
+.Os
+.
+.Sh NAME
+.Nm inflateBack
+.Nd inflate call-back interface
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.
+.Ft typedef unsigned
+.Fo (*in_func)
+.Fa "void FAR *"
+.Fa "z_const unsigned char FAR * FAR *"
+.Fc
+.
+.Ft typedef int
+.Fo (*out_func)
+.Fa "void FAR *"
+.Fa "unsigned char FAR *"
+.Fa "unsigned"
+.Fc
+.
+.Ft int
+.Fo inflateBack
+.Fa "z_streamp strm"
+.Fa "in_func in"
+.Fa "void FAR *in_desc"
+.Fa "out_func out"
+.Fa "void FAR *out_desc"
+.Fc
+.
+.Sh DESCRIPTION
+.Fn inflateBack
+does a raw inflate
+with a single call
+using a call-back interface
+for input and output.
+This is potentially more efficient than
+.Xr inflate 3
+for file I/O applications,
+in that it avoids copying between the output
+and the sliding window
+by simply making the window itself the output buffer.
+.Xr inflate 3
+can be faster on modern CPUs
+when used with large buffers.
+.Fn inflateBack
+trusts the application to not change
+the output buffer passed by the output function,
+at least until
+.Fn inflateBack
+returns.
+.
+.Pp
+.Xr inflateBackInit 3
+must be called first
+to allocate the internal state
+and to initialize the state
+with the user-provided window buffer.
+.Fn inflateBack
+may then be used multiple times
+to inflate a complete,
+raw deflate stream
+with each call.
+.Xr inflateBackEnd 3
+is then called to free the allocated state.
+.
+.Pp
+A raw deflate stream
+is one with no zlib or gzip header or trailer.
+This routine would normally be used
+in a utility that reads zip or gzip files
+and write out uncompressed files.
+The utility would decode the header
+and process the trailer on its own,
+hence this routine expects only
+the raw deflate stream to decompress.
+This is different from the default behaviour of
+.Xr inflate 3 ,
+which expects a zlib header and trailer
+around the deflate stream.
+.
+.Pp
+.Fn inflateBack
+uses two subroutines
+supplied by the caller
+that are then called by
+.Fn inflateBack
+for input and output.
+.Fn inflateBack
+calls those routines
+until it reads a complete deflate stream
+and writes out all of the uncompressed data,
+or until it encounters an error.
+The function's parameters and return types
+are defined above in the
+.Vt in_func
+and
+.Vt out_func
+typedefs.
+.Fn inflateBack
+will call
+.Fn in in_desc &buf
+which should return
+the number of bytes of provided input,
+and a pointer to that input in
+.Fa buf .
+If there is no input available,
+.Fn in
+must return zero \(em
+.Fa buf
+is ignored in that case \(em
+and
+.Fn inflateBack
+will return a buffer error.
+.Fn inflateBack
+will call
+.Fn out out_desc buf len
+to write the uncompressed data
+.Fa buf[0..len-1] .
+.Fn out
+should return zero on success,
+or non-zero on failure.
+If
+.Fn out
+returns non-zero,
+.Fn inflateBack
+will return with an error.
+Neither
+.Fn in
+nor
+.Fn out
+are permitted to change
+the contents of the window provided to
+.Xr inflateBackInit 3 ,
+which is also the buffer that
+.Fn out
+uses to write from.
+The length written by
+.Fn out
+will be at most the window size.
+Any non-zero amount of input
+may be provided by
+.Fn in .
+.
+.Pp
+For convenience,
+.Fn inflateBack
+can be provided input on the first call
+by setting
+.Fa strm->next_in
+and
+.Fa strm->avail_in .
+If that input is exhausted,
+then
+.Fn in
+will be called.
+Therefore
+.Fa strm->next_in
+must be initialized before calling
+.Fn inflateBack .
+If
+.Fa strm->next_in
+is
+.Dv Z_NULL ,
+then
+.Fn in
+will be called immediately for input.
+If
+.Fa strm->next_in
+is not
+.Dv Z_NULL ,
+then
+.Fa strm->avail_in
+must also be initialized,
+and then if
+.Fa strm->avail_in
+is not zero,
+input will initially be taken from
+.Fa "strm->next_in[0 .. strm->avail_in - 1]" .
+.
+.Pp
+The
+.Fa in_desc
+and
+.Fa out_desc
+parameters of
+.Fn inflateBack
+is passed as the first parameter of
+.Fn in
+and
+.Fn out
+respectively when they are called.
+These descriptors can be optionally used
+to pass any information that the caller-supplied
+.Fn in
+and
+.Fn out
+functions need to do their job.
+.
+.Sh RETURN VALUES
+On return,
+.Fn inflateBack
+will set
+.Fa strm->next_in
+and
+.Fa strm->avail_in
+to pass back any unused input
+that was provided by the last
+.Fn in
+call.
+The return values of
+.Fn inflateBack
+can be
+.Dv Z_STREAM_END
+on success,
+.Dv Z_BUF_ERROR
+if
+.Fn in
+or
+.Fn out
+returned an error,
+.Dv Z_DATA_ERROR
+if there was a format error
+in the deflate stream
+.Po
+in which case
+.Fa strm->msg
+is set to indicate the nature of the error
+.Pc ,
+or
+.Dv Z_STREAM_ERROR
+if the stream was not properly initialized.
+In the case of
+.Dv Z_BUF_ERROR ,
+an input or output error can be distinguished using
+.Fa strm->next_in
+which will be
+.Dv Z_NULL
+only if
+.Fn in
+returned an error.
+If
+.Fa strm->next_in
+is not
+.Dv Z_NULL ,
+then the
+.Dv Z_BUF_ERROR
+was due to
+.Fn out
+returning non-zero.
+.Po
+.Fn in
+will always be called before
+.Fn out ,
+so
+.Fa strm->next_in
+is assured to be defined if
+.Fa out
+returns non-zero.
+.Pc \&
+Note that
+.Fn inflateBack
+cannot return
+.Dv Z_OK .
+.
+.Sh SEE ALSO
+.Xr inflate 3 ,
+.Xr inflateBackEnd 3 ,
+.Xr inflateBackInit 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateBackEnd.3 b/doc/zlib/inflateBackEnd.3
new file mode 100644
index 00000000..eeb88636
--- /dev/null
+++ b/doc/zlib/inflateBackEnd.3
@@ -0,0 +1,43 @@
+.Dd January 15, 2017
+.Dt INFLATEBACKEND 3
+.Os
+.
+.Sh NAME
+.Nm inflateBackEnd
+.Nd free inflateBack stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn inflateBackEnd "z_streamp strm"
+.
+.Sh DESCRIPTION
+All memory allocated by
+.Xr inflateBackInit 3
+is freed.
+.
+.Sh RETURN VALUES
+.Fn inflateBackEnd
+returns
+.Dv Z_OK
+on success,
+or
+.Dv Z_STREAM_ERROR
+if the stream state was inconsistent.
+.
+.Sh SEE ALSO
+.Xr inflateBack 3 ,
+.Xr inflateBackInit 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateBackInit.3 b/doc/zlib/inflateBackInit.3
new file mode 100644
index 00000000..483edda5
--- /dev/null
+++ b/doc/zlib/inflateBackInit.3
@@ -0,0 +1,84 @@
+.Dd January 15, 2017
+.Dt INFLATEBACKINIT 3
+.Os
+.
+.Sh NAME
+.Nm inflateBackInit
+.Nd initialize inflateBack stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fo inflateBackInit
+.Fa "z_streamp strm"
+.Fa "int windowBits"
+.Fa "unsigned char FAR *window"
+.Fc
+.
+.Sh DESCRIPTION
+Initialize the internal stream state
+for decompression using
+.Xr inflateBack 3
+calls.
+The fields
+.Fa zalloc ,
+.Fa zfree
+and
+.Fa opaque
+in
+.Fa strm
+must be initialized before the call.
+If
+.Fa zalloc
+and
+.Fa zfree
+are
+.Dv Z_NULL ,
+then the default
+library-derived memory allocation routines are used.
+.Fa windowBits
+is the base two logarithm of the window size,
+in the range 8..15.
+.Fa window
+is a caller supplied buffer of that size.
+Except for special applications
+where it is assured that deflate
+was used with small window sizes,
+.Fa windowBits
+must be 15
+and a 32K byte
+.Fa window
+must be supplied
+to be able to decompress general deflate streams.
+.
+.Sh RETURN VALUES
+.Fn inflateBackInit
+will return
+.Dv Z_OK
+on success,
+.Dv Z_STREAM_ERROR
+if any of the parameters are invalid,
+.Dv Z_MEM_ERROR
+if the internal state could not be allocated,
+or
+.Dv Z_VERSION_ERROR
+if the version of the library
+does not match the version of the header file.
+.
+.Sh SEE ALSO
+.Xr inflateBack 3 ,
+.Xr inflateBackEnd 3 ,
+.Xr inflateInit 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateCopy.3 b/doc/zlib/inflateCopy.3
new file mode 100644
index 00000000..53b30edf
--- /dev/null
+++ b/doc/zlib/inflateCopy.3
@@ -0,0 +1,59 @@
+.Dd January 15, 2017
+.Dt INFLATECOPY 3
+.Os
+.
+.Sh NAME
+.Nm inflateCopy
+.Nd copy inflate stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn inflateCopy "z_streamp dest" "z_streamp source"
+.
+.Sh DESCRIPTION
+Sets the destination stream
+as a complete copy of the source stream.
+.
+.Pp
+This function can be useful
+when randomly accessing a large stream.
+The first pass through the stream
+can periodically record the inflate state,
+allowing restarting inflate at those points
+when randomly accessing the stream.
+.
+.Sh RETURN VALUES
+.Fn inflateCopy
+returns
+.Dv Z_OK
+if success,
+.Dv Z_MEM_ERROR
+if there was not enough memory,
+.Dv Z_STREAM_ERROR
+if the source stream state was inconsistent
+.Po
+such as
+.Fa zalloc
+being
+.Dv Z_NULL
+.Pc .
+.Fa msg
+is left unchanged
+in both source and destination.
+.
+.Sh SEE ALSO
+.Xr inflateInit 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateEnd.3 b/doc/zlib/inflateEnd.3
new file mode 100644
index 00000000..9b18226b
--- /dev/null
+++ b/doc/zlib/inflateEnd.3
@@ -0,0 +1,44 @@
+.Dd January 15, 2017
+.Dt INFLATEEND 3
+.Os
+.
+.Sh NAME
+.Nm inflateEnd
+.Nd free inflate stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn inflateEnd "z_streamp strm"
+.
+.Sh DESCRIPTION
+All dynamically allocated data structures
+for this stream are feed.
+This function discards any unprocessed input
+and does not flush any pending output.
+.
+.Sh RETURN VALUES
+.Fn inflateEnd
+returns
+.Dv Z_OK
+if success,
+or
+.Dv Z_STREAM_ERROR
+if the stream state was inconsistent.
+.
+.Sh SEE ALSO
+.Xr inflate 3 ,
+.Xr inflateInit 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateGetDictionary.3 b/doc/zlib/inflateGetDictionary.3
new file mode 100644
index 00000000..e70ee736
--- /dev/null
+++ b/doc/zlib/inflateGetDictionary.3
@@ -0,0 +1,69 @@
+.Dd January 15, 2017
+.Dt INFLATEGETDICTIONARY 3
+.Os
+.
+.Sh NAME
+.Nm inflateGetDictionary
+.Nd inflate sliding dictionary
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fo inflateGetDictionary
+.Fa "z_streamp strm"
+.Fa "Bytef *dictionary"
+.Fa "uInt *dictLength"
+.Fc
+.
+.Sh DESCRIPTION
+Returns the sliding dictionary
+being maintained by
+.Xr inflate 3 .
+.Fa dictLength
+is set to the number of bytes
+in the dictionary,
+and that many bytes are copied to
+.Fa dictionary .
+.Fa dictionary
+must have enough space,
+where 32768 bytes is always enough.
+If
+.Fn inflateGetDictionary
+is called with
+.Fa dictionary
+equal to
+.Dv Z_NULL ,
+then only the dictionary length is returned,
+and nothing is copied.
+Similarly,
+if
+.Fa dictLength
+is
+.Dv Z_NULL ,
+then it is not set.
+.
+.Sh RETURN VALUES
+.Fn inflateGetDictionary
+returns
+.Dv Z_OK
+on success,
+or
+.Dv Z_STREAM_ERROR
+if the stream state is inconsistent.
+.
+.Sh SEE ALSO
+.Xr deflateSetDictionary 3 ,
+.Xr inflateSetDictionary 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateGetHeader.3 b/doc/zlib/inflateGetHeader.3
new file mode 100644
index 00000000..f77670f2
--- /dev/null
+++ b/doc/zlib/inflateGetHeader.3
@@ -0,0 +1,170 @@
+.Dd January 15, 2017
+.Dt INFLATEGETHEADER 3
+.Os
+.
+.Sh NAME
+.Nm inflateGetHeader
+.Nd get gzip header
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn inflateGetHeader "z_streamp strm" "gz_headerp head"
+.
+.Sh DESCRIPTION
+.Fn inflateGetHeader
+requests that gzip header information
+be stored in the provided
+.Vt gz_header
+structure.
+.Fn inflateGetHeader
+may be called after
+.Xr inflateInit2 3
+or
+.Xr inflateReset 3 ,
+and before the first call of
+.Xr inflate 3 .
+As
+.Xr inflate 3
+processes the gzip stream,
+.Fa head->done
+is zero until the header is completed,
+at which time
+.Fa head->done
+is set to one.
+If a zlib stream is being decoded,
+then
+.Fa head->done
+is set to -1 to indicate that
+there will be no gzip header information forthcoming.
+Note that
+.Dv Z_BLOCK
+or
+.Dv Z_TREES
+can be used to force
+.Xr inflate 3
+to return immediately after
+header processing is complete
+and before any actual data is decompressed.
+.
+.Pp
+The
+.Fa text ,
+.Fa time ,
+.Fa xflags ,
+and
+.Fa os
+fields are filled in with the gzip header contents.
+.Fa hcrc
+is set to true if there is a header CRC.
+.Po
+The header CRC was valid if
+.Fa done
+is set to one.
+.Pc \&
+If
+.Fa extra
+is not
+.Dv Z_NULL ,
+then
+.Fa extra_max
+contains the maximum number of bytes to write to
+.Fa extra .
+Once
+.Fa done
+is true,
+.Fa extra_len
+contains the actual extra field length,
+and
+.Fa extra
+contains the extra field,
+or that field truncated if
+.Fa extra_max
+is less than
+.Fa extra_len .
+If
+.Fa name
+is not
+.Dv Z_NULL ,
+then up to
+.Fa name_max
+characters are written there,
+terminated with a zero
+unless the length is greater than
+.Fa name_max .
+If
+.Fa comment
+is not
+.Dv Z_NULL ,
+then up to
+.Fa comm_max
+characters are written there,
+terminated with a zero
+unless the length is greater than
+.Fa comm_max .
+When any of
+.Fa extra ,
+.Fa name ,
+or
+.Fa comment
+are not
+.Dv Z_NULL
+and the respective field
+is not present in the header,
+then that field is set to
+.Dv Z_NULL
+to signal its absence.
+This allows the use of
+.Xr deflateSetHeader 3
+with the returned structure
+to duplicate the header.
+However if those fields are set to allocated memory,
+then the application will need to
+save those pointers elsewhere
+so that they can be eventually feed.
+.
+.Pp
+If
+.Fn inflateGetHeader
+is not used,
+then the header information is simply discarded.
+The header is always checked for validity,
+including the header CRC if present.
+.Xr inflateReset 3
+will reset the process to discard the header information.
+The application would need to call
+.Fn inflateGetHeader
+again to retrieve the header from the next gzip stream.
+.
+.Pp
+The
+.Vt gz_headerp
+type is documented in
+.Xr deflateSetHeader 3 .
+.
+.Sh RETURN VALUES
+.Fn inflateGetHeader
+returns
+.Dv Z_OK
+if success,
+or
+.Dv Z_STREAM_ERROR
+if the source stream state was inconsistent.
+.
+.Sh SEE ALSO
+.Xr gzip 1 ,
+.Xr deflateSetHeader 3 ,
+.Xr inflateInit2 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateInit.3 b/doc/zlib/inflateInit.3
new file mode 100644
index 00000000..186b058a
--- /dev/null
+++ b/doc/zlib/inflateInit.3
@@ -0,0 +1,101 @@
+.Dd January 15, 2017
+.Dt INFLATEINIT 3
+.Os
+.
+.Sh NAME
+.Nm inflateInit
+.Nd initialize inflate stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn inflateInit "z_streamp strm"
+.
+.Sh DESCRIPTION
+Initializes the internal stream state for decompression.
+The fields
+.Fa next_in ,
+.Fa avail_in ,
+.Fa zalloc ,
+.Fa zfree
+and
+.Fa opaque
+must be initialized before by the caller.
+In the current version of
+.Fn inflateInit ,
+the provided input is not read or consumed.
+The allocation of a sliding window
+will be deferred to the first call of
+.Xr inflate 3
+(if the decompression does not complete on the first call).
+If
+.Fa zalloc
+and
+.Fa zfree
+are set to
+.Dv Z_NULL ,
+.Fn inflateInit
+updates them to use default allocation functions.
+.
+.Pp
+.Fn inflateInit
+does not perform any decompression.
+Actual decompression will be done by
+.Xr inflate 3 .
+So
+.Fa next_in ,
+.Fa avail_in ,
+.Fa next_out
+and
+.Fa avail_out
+are unused and unchanged.
+The current implementation of
+.Fn inflateInit
+does not process any header information \(em
+that is deferred until
+.Xr inflate 3
+is called.
+.
+.Pp
+The
+.Vt z_streamp
+type is documented in
+.Xr deflateInit 3 .
+.
+.Sh RETURN VALUES
+.Fn inflateInit
+returns
+.Dv Z_OK
+if success,
+.Dv Z_MEM_ERROR
+if there was not enough memory,
+.Dv Z_VERSION_ERROR
+if the zlib library version
+is incompatible with the version assumed by the caller,
+or
+.Dv Z_STREAM_ERROR
+if the parameters are invalid,
+such as a null pointer to the structure.
+.Fa msg
+is set to null if there is no error message.
+.
+.Sh SEE ALSO
+.Xr inflate 3 ,
+.Xr inflateBackInit 3 ,
+.Xr inflateCopy 3 ,
+.Xr inflateEnd 3 ,
+.Xr inflateInit2 3 ,
+.Xr inflateSetDictionary 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateInit2.3 b/doc/zlib/inflateInit2.3
new file mode 100644
index 00000000..a630f12a
--- /dev/null
+++ b/doc/zlib/inflateInit2.3
@@ -0,0 +1,181 @@
+.Dd January 15, 2017
+.Dt INFLATEINIT2 3
+.Os
+.
+.Sh NAME
+.Nm inflateInit2
+.Nd inflate compression options
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn inflateInit2 "z_streamp strm" "int windowBits"
+.
+.Sh DESCRIPTION
+This is another version of
+.Xr inflateInit 3
+with an extra parameter.
+The fields
+.Fa next_in ,
+.Fa avail_in ,
+.Fa zalloc ,
+.Fa zfree
+and
+.Fa opaque
+must be initialized before by the caller.
+.
+.Pp
+The
+.Fa windowBits
+parameter is the base two logarithm
+of the maximum window size
+(the size of the history buffer).
+It should be in the range 8..15
+for this version of the library.
+The default value is 15 if
+.Xr inflateInit 3
+is used instead.
+.Fa windowBits
+must be greater than or equal to the
+.Fa windowBits
+value provided to
+.Xr deflateInit2 3
+while compressing,
+or it must be equal to 15 if
+.Xr deflateInit2 3
+was not used.
+If a compressed stream with a larger window size
+is given as input,
+.Xr inflate 3
+will return with the error code
+.Dv Z_DATA_ERROR
+instead of trying to allocate a larger window.
+.
+.Pp
+.Fa windowBits
+can also be zero
+to request that
+.Xr inflate 3
+use the window size
+in the zlib header
+of the compressed stream.
+.
+.Pp
+.Fa windowBits
+can also be -8..-15
+for raw inflate.
+In this case,
+.Fa -windowBits
+determines the window size.
+.Xr inflate 3
+will then process raw deflate data,
+not looking for a zlib or gzip header,
+not generating a check value,
+and not looking for any check values
+for comparison at the end of the stream.
+This is for use with other formats
+that use the deflate compressed data format
+such as zip.
+Those formats provide their own check values.
+If a custom format is developed
+using the raw deflate format for compressed data,
+it is recommended that a check value
+such as an Adler-32 or a CRC-32
+be applied to the uncompressed data
+as is done in the zlib, gzip and zip formats.
+For most applications,
+the zlib format should be used as is.
+Note that comments above on the use in
+.Xr deflateInit2 3
+applies to the magnitude of
+.Fa windowBits .
+.
+.Pp
+.Fa windowBits
+can also be greater than 15
+for optional gzip decoding.
+Add 32 to
+.Fa windowBits
+to enable zlib and gzip decoding
+with automatic header detection,
+or add 16 to decode only the gzip format
+.Po
+the zlib format will return a
+.Dv Z_DATA_ERROR
+.Pc .
+If a gzip stream is being decoded,
+.Fa strm->adler
+is a CRC-32 instead of an Adler-32.
+Unlike the
+.Xr gunzip 1
+utility and
+.Xr gzread 3 ,
+.Xr inflate 3
+will not automatically decode
+concatenated gzip streams.
+.Xr inflate 3
+will return
+.Dv Z_STREAM_END
+at the end of the gzip stream.
+The state would need to be reset
+to continue decoding a subsequent gzip stream.
+.
+.Pp
+.Fn inflateInit2
+does not perform any decompression
+apart from possibly reading the zlib header if present:
+actual decompression will be done by
+.Xr inflate 3 .
+.Po
+So
+.Fa next_in
+and
+.Fa avail_in
+may be modified,
+but
+.Fa next_out
+and
+.Fa avail_out
+are unused and unchanged.
+.Pc \&
+The current implementation of
+.Fn inflateInit2
+does not process any header information \(em
+that is deferred until
+.Xr inflate 3
+is called.
+.
+.Sh RETURN VALUES
+.Fn inflateInit2
+returns
+.Dv Z_OK
+if success,
+.Dv Z_MEM_ERROR
+if there was not enough memory,
+.Dv Z_VERSION_ERROR
+if the zlib library version
+is incompatible with the version assumed by the caller,
+or
+.Dv Z_STREAM_ERROR
+if the parameters are invalid,
+such as a null pointer to the structure.
+.Fa msg
+is set to null if there is no error message.
+.
+.Sh SEE ALSO
+.Xr deflateInit2 3 ,
+.Xr inflateInit 3 ,
+.Xr inflatePrime 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateMark.3 b/doc/zlib/inflateMark.3
new file mode 100644
index 00000000..2d15993d
--- /dev/null
+++ b/doc/zlib/inflateMark.3
@@ -0,0 +1,88 @@
+.Dd January 15, 2017
+.Dt INFLATEMARK 3
+.Os
+.
+.Sh NAME
+.Nm inflateMark
+.Nd mark location for random access
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft long
+.Fn inflateMark "z_streamp strm"
+.
+.Sh DESCRIPTION
+This function returns two values,
+one in the lower 16 bits of the return value,
+and the other in the remaining upper bits,
+obtained by shifting the return value down 16 bits.
+If the upper value is -1
+and the lower value is zero,
+then
+.Xr inflate 3
+is currently decoding information outside of a block.
+If the upper value is -1
+and the lower value is non-zero,
+then
+.Xr inflate 3
+is in the middle of a stored block,
+with the lower value equaling
+the number of bytes from the input remaining to copy.
+If the upper value is not -1,
+then it is the number of bits
+back from the current bit position
+in the input of the code
+(literal of length/distance pair)
+currently being processed.
+In that case the lower value
+is the number of bytes
+already emitted for that code.
+.
+.Pp
+A code is being processed if
+.Xr inflate 3
+is waiting for more input to complete
+decoding of the code,
+or if it has completed decoding
+but is waiting for more output space
+to write the literal or match data.
+.
+.Pp
+.Fn inflateMark
+is used to mark locations in the input data
+for random access,
+which may be at bit positions,
+and to note those cases where
+the output of a code may span boundaries
+of random access blocks.
+The current location in the input stream
+can be determined from
+.Fa avail_in
+and
+.Fa data_type
+as noted in the description for the
+.Dv Z_BLOCK
+.Fa flush
+parameter for
+.Xr inflate 3 .
+.
+.Sh RETURN VALUES
+.Fn inflateMark
+returns the value noted above,
+or -65536 if the provided source stream state was inconsistent.
+.
+.Sh SEE ALSO
+.Xr inflate 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflatePrime.3 b/doc/zlib/inflatePrime.3
new file mode 100644
index 00000000..c89dc2c5
--- /dev/null
+++ b/doc/zlib/inflatePrime.3
@@ -0,0 +1,73 @@
+.Dd January 15, 2017
+.Dt INFLATEPRIME 3
+.Os
+.
+.Sh NAME
+.Nm inflatePrime
+.Nd insert bits in inflate stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn inflatePrime "z_streamp strm" "int bits" "int value"
+.
+.Sh DESCRIPTION
+This function inserts bits
+in the inflate input stream.
+The intent is that this function
+is used to start inflating
+at a bit position
+in the middle of a byte.
+The provided bits will be used
+before any bytes are used from
+.Fa next_in .
+This function should only be used with raw inflate,
+and should be used before the first
+.Xr inflate 3
+call after
+.Xr inflateInit2 3
+or
+.Xr inflateReset 3 .
+.Fa bits
+must be less than or equal to 16,
+and that many of the least significant bits of
+.Fa value
+will be inserted in the input.
+.
+.Pp
+If
+.Fa bits
+is negative,
+then the input stream bit buffer is emptied.
+Then
+.Fn inflatePrime
+can be called again
+to put bits in the buffer.
+This is used to clear out bits leftover
+after feeding inflate a block description
+prior to feeding inflate codes.
+.
+.Sh RETURN VALUES
+.Fn inflatePrime
+returns
+.Dv Z_OK
+if success,
+or
+.Dv Z_STREAM_ERROR
+if the source stream state was inconsistent.
+.
+.Sh SEE ALSO
+.Xr inflateInit2 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateReset.3 b/doc/zlib/inflateReset.3
new file mode 100644
index 00000000..a8d2e219
--- /dev/null
+++ b/doc/zlib/inflateReset.3
@@ -0,0 +1,81 @@
+.Dd January 15, 2017
+.Dt INFLATERESET 3
+.Os
+.
+.Sh NAME
+.Nm inflateReset ,
+.Nm inflateReset2
+.Nd reset inflate stream
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn inflateReset "z_streamp strm"
+.Ft int
+.Fn inflateReset2 "z_streamp strm" "int windowBits"
+.
+.Sh DESCRIPTION
+This function is equivalent to
+.Xr inflateEnd 3
+followed by
+.Xr inflateInit 3 ,
+but does not free and reallocate
+the internal decompression state.
+The stream will keep attributes
+that may have been set by
+.Xr inflateInit2 3 .
+.
+.Pp
+.Fn inflateReset2
+is the same as
+.Fn inflateReset ,
+but it also permits changing
+the wrap and window size requests.
+The
+.Fa windowBits
+parameter is interpreted the same as it is for
+.Xr inflateInit2 3 .
+If the window size is changed,
+then the memory allocated for the window is freed,
+and the window will be reallocated by
+.Xr inflate 3
+if needed.
+.
+.Sh RETURN VALUES
+.Fn inflateReset
+and
+.Fn inflateReset2
+return
+.Dv Z_OK
+if success,
+or
+.Dv Z_STREAM_ERROR
+if the source stream state was inconsistent
+.Po
+such as
+.Fa zalloc
+or
+.Fa state
+being
+.Dv Z_NULL
+.Pc ,
+or if the
+.Fa windowBits
+parameter is invalid.
+.
+.Sh SEE ALSO
+.Xr inflateEnd 3 ,
+.Xr inflateInit 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateSetDictionary.3 b/doc/zlib/inflateSetDictionary.3
new file mode 100644
index 00000000..0e3c60c7
--- /dev/null
+++ b/doc/zlib/inflateSetDictionary.3
@@ -0,0 +1,85 @@
+.Dd January 15, 2017
+.Dt INFLATESETDICTIONARY 3
+.Os
+.
+.Sh NAME
+.Nm inflateSetDictionary
+.Nd initialize decompression dictionary
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fo inflateSetDictionary
+.Fa "z_streamp strm"
+.Fa "const Bytef *dictionary"
+.Fa "uInt dictLength"
+.Fc
+.
+.Sh DESCRIPTION
+Initializes the decompression dictionary
+from the given uncompressed byte sequence.
+This function must be called
+immediately after a call of
+.Xr inflate 3 ,
+if that call returned
+.Dv Z_NEED_DICT .
+The dictionary chosen by the compressor
+can be determined from the Adler-32 value
+returned by that call of
+.Xr inflate 3 .
+The compressor and decompressor
+must use exactly the same dictionary
+.Po
+see
+.Xr deflateSetDictionary 3
+.Pc .
+For raw inflate,
+this function can be called at any time
+to set the dictionary.
+If the provided dictionary
+is smaller than the window
+and there is already data in the window,
+then the provided dictionary
+will amend what's there.
+The application must insure that the dictionary
+that was used for compression is provided.
+.
+.Pp
+.Fn inflateSetDictionary
+does not perform any decompression:
+this will be done by subsequent calls of
+.Xr inflate 3 .
+.
+.Sh RETURN VALUES
+.Fn inflateSetDictionary
+returns
+.Dv Z_OK
+if success,
+.Dv Z_STREAM_ERROR
+if a parameter is invalid
+.Po
+e.g. dictionary being
+.Dv Z_NULL
+.Pc
+or the stream state is inconsistent,
+.Dv Z_DATA_ERROR
+if the given dictionary
+doesn't match the expected one
+(incorrect Adler-32 value).
+.
+.Sh SEE ALSO
+.Xr deflateGetDictionary 3 ,
+.Xr inflateGetDictionary 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/inflateSync.3 b/doc/zlib/inflateSync.3
new file mode 100644
index 00000000..35264ddd
--- /dev/null
+++ b/doc/zlib/inflateSync.3
@@ -0,0 +1,72 @@
+.Dd January 15, 2017
+.Dt INFLATESYNC 3
+.Os
+.
+.Sh NAME
+.Nm inflateSync
+.Nd skip invalid data
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft int
+.Fn inflateSync "z_streamp strm"
+.
+.Sh DESCRIPTION
+Skips invalid compressed data
+until a possible full flush point
+.Po
+see
+.Xr deflate 3
+for the description of
+.Dv Z_FULL_FLUSH
+.Pc
+can be found,
+or until all available input is skipped.
+No output is provided.
+.
+.Pp
+.Fn inflateSync
+searches for a 00 00 FF FF pattern
+in the compressed data.
+All full flush points have this pattern,
+but not all occurrences of this pattern
+are full flush points.
+.
+.Sh RETURN VALUES
+.Fn inflateSync
+returns
+.Dv Z_OK
+if a possible full flush point has been found,
+.Dv Z_BUF_ERROR
+if no more input was provided,
+.Dv Z_DATA_ERROR
+if no flush point has been found,
+or
+.Dv Z_STREAM_ERROR
+if the stream structure was inconsistent.
+In the success case,
+the application may save the current value of
+.Fa total_in
+which indicates where valid compressed data was found.
+In the error case,
+the application may repeatedly call
+.Fn inflateSync ,
+providing more input each time,
+until success or the end of the input data.
+.
+.Sh SEE ALSO
+.Xr deflate 3 ,
+.Xr inflate 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/uncompress.3 b/doc/zlib/uncompress.3
new file mode 100644
index 00000000..d951da9b
--- /dev/null
+++ b/doc/zlib/uncompress.3
@@ -0,0 +1,92 @@
+.Dd January 15, 2017
+.Dt UNCOMPRESS 3
+.Os
+.
+.Sh NAME
+.Nm uncompress ,
+.Nm uncompress2
+.Nd decompress source buffer into destination buffer
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.
+.Ft int
+.Fo uncompress
+.Fa "Bytef *dest"
+.Fa "uLongf *destLen"
+.Fa "const Bytef *source"
+.Fa "uLong sourceLen"
+.Fc
+.
+.Ft int
+.Fo uncompress2
+.Fa "Bytef *dest"
+.Fa "uLongf *destLen"
+.Fa "const Bytef *source"
+.Fa "uLong *sourceLen"
+.Fc
+.
+.Sh DESCRIPTION
+Decompresses the source buffer into the destination buffer.
+.Fa sourceLen
+is the byte length of the source buffer.
+Upon entry,
+.Fa destLen
+is the total size of the destination buffer,
+which must be large enough to hold the entire uncompressed data.
+.Po
+The size of the uncompressed data
+must have been saved previously by the compressor
+and transmitted to the decompressor
+by some mechanism outside the scope of this compression library.
+.Pc \&
+Upon exit,
+.Fa destLen
+is the actual size of the uncompressed data.
+.
+.Pp
+.Fn uncompress2
+is the same as
+.Fn uncompress ,
+except that
+.Fa sourceLen
+is a pointer,
+where the length of the source is
+.Fa *sourceLen .
+On return,
+.Fa *sourceLen
+is the number of source bytes consumed.
+.
+.Sh RETURN VALUES
+.Fn uncompress
+returns
+.Dv Z_OK
+if success,
+.Dv Z_MEM_ERROR
+if there was not enough memory,
+.Dv Z_BUF_ERROR
+if there was not enough room in the output buffer,
+or
+.Dv Z_DATA_ERROR
+if the input data was corrupted or incomplete.
+In the case where there is not enough room,
+.Fn uncompress
+will fill the output buffer
+with the uncompressed data up to that point.
+.
+.Sh SEE ALSO
+.Xr compress 3 ,
+.Xr inflate 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/zlibCompileFlags.3 b/doc/zlib/zlibCompileFlags.3
new file mode 100644
index 00000000..465195c2
--- /dev/null
+++ b/doc/zlib/zlibCompileFlags.3
@@ -0,0 +1,163 @@
+.Dd January 15, 2017
+.Dt ZLIBCOMPILEFLAGS 3
+.Os
+.
+.Sh NAME
+.Nm zlibCompileFlags
+.Nd zlib compile-time options
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Ft uLong
+.Fn zlibCompileFlags void
+.
+.Sh DESCRIPTION
+Return flags indicating compile-time options.
+.
+.Ss Type sizes
+Two bits each,
+00 = 16 bits,
+01 = 32 bits,
+10 = 64 bits,
+11 = other.
+.
+.Pp
+.Bl -tag -width Ds -compact
+.It 1.0
+size of
+.Vt uInt
+.It 3.2
+size of
+.Vt uLong
+.It 5.4
+size of
+.Vt voidpf
+(pointer)
+.It 7.6
+size of
+.Vt z_off_t
+.El
+.
+.Ss Compiler, assembler, and debug options
+.Bl -tag -width Ds -compact
+.It 8
+.Dv ZLIB_DEBUG
+.It 9
+.Dv ASMV
+or
+.Dv ASMINF
+\(em
+use ASM code
+.It 10
+.Dv ZLIB_WINAPI
+\(em
+exported functions use the WINAPI calling convention
+.It 11
+0 (reserved)
+.El
+.
+.Ss One-time table building
+Smaller code,
+but not thread-safe if true.
+.
+.Pp
+.Bl -tag -width Ds -compact
+.It 12
+.Dv BUILDFIXED
+\(em
+build static block decoding tables when needed
+.It 13
+.Dv DYNAMIC_CRC_TABLE
+\(em
+build CRC calculation tables when needed
+.It 14,15
+0 (reserved)
+.El
+.
+.Ss Library content
+Indicates missing functionality.
+.
+.Pp
+.Bl -tag -width Ds -compact
+.It 16
+.Dv NO_GZCOMPRESS
+\(em
+gz* functions cannot compress
+(to avoid linking deflate code when not needed)
+.It 17
+.Dv NO_GZIP
+\(em
+.Xr deflate 3
+can't write gzip streams,
+and
+.Xr inflate 3
+can't detect and decode gzip streams
+(to avoid linking crc code)
+.It 18-19
+0 (reserved)
+.El
+.
+.Ss Operation variations
+Changes in library functionality.
+.
+.Pp
+.Bl -tag -width Ds -compact
+.It 20
+.Dv PKZIP_BUG_WORKAROUND
+\(em
+slightly more permissive
+.Xr inflate 3
+.It 21
+.Dv FASTEST
+\(em
+deflate algorithm with only one,
+lowest compression level
+.It 22,23
+0 (reserved)
+.El
+.
+.Ss sprintf variant used by gzprintf
+Zero is best.
+.
+.Pp
+.Bl -tag -width Ds -compact
+.It 24
+0 = vs*,
+1 = s*
+\(em
+1 means limited to 20 arguments after the format
+.It 25
+0 = *nprintf,
+1 = *printf
+\(em
+1 means
+.Xr gzprintf 3
+not secure!
+.It 26
+0 = returns value,
+1 = void
+\(em
+1 means inferred string length returned
+.El
+.
+.Ss Remainder
+.Bl -tag -width Ds -compact
+.It 27-31
+0 (reserved)
+.El
+.
+.Sh SEE ALSO
+.Xr zlibVersion 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/doc/zlib/zlibVersion.3 b/doc/zlib/zlibVersion.3
new file mode 100644
index 00000000..35a9854b
--- /dev/null
+++ b/doc/zlib/zlibVersion.3
@@ -0,0 +1,45 @@
+.Dd January 15, 2017
+.Dt ZLIBVERSION 3
+.Os
+.
+.Sh NAME
+.Nm zlibVersion
+.Nd check zlib version
+.
+.Sh LIBRARY
+.Lb libz
+.
+.Sh SYNOPSIS
+.In zlib.h
+.Fd #define ZLIB_VERSION \(dq1.2.11\(dq
+.Ft "const char *"
+.Fn zlibVersion void
+.
+.Sh DESCRIPTION
+The application can compare
+.Fn zlibVersion
+and
+.Dv ZLIB_VERSION
+for consistency.
+If the first character differs,
+the library code actually used
+is not compatible with the
+.In zlib.h
+header file used by the application.
+This check is automatically made by
+.Xr deflateInit 3
+and
+.Xr inflateInit 3 .
+.
+.Sh SEE ALSO
+.Xr zlibCompileFlags 3
+.
+.Sh HISTORY
+This manual page was converted from
+.In zlib.h
+to mdoc format by
+.An C. McEnroe Aq Mt june@causal.agency .
+.
+.Sh AUTHORS
+.An Jean-loup Gailly Aq Mt jloup@gzip.org
+.An Mark Adler Aq Mt madler@alumni.caltech.edu
diff --git a/etc/CodeQWERTY.bundle/Contents/Info.plist b/etc/CodeQWERTY.bundle/Contents/Info.plist
new file mode 100644
index 00000000..f78351e8
--- /dev/null
+++ b/etc/CodeQWERTY.bundle/Contents/Info.plist
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+	<dict>
+		<key>CFBundleIdentifier</key>
+		<string>agency.causal.keyboardlayout.code</string>
+		<key>CFBundleName</key>
+		<string>Code QWERTY</string>
+		<key>CFBundleVersion</key>
+		<string>1.0</string>
+		<key>KLInfo_Code QWERTY</key>
+		<dict>
+			<key>TISInputSourceID</key>
+			<string>agency.causal.keyboardlayout.code.qwerty</string>
+			<key>TISIntendedLanguage</key>
+			<string>en-CA</string>
+		</dict>
+	</dict>
+</plist>
diff --git a/etc/CodeQWERTY.bundle/Contents/Resources/CodeQWERTY.keylayout b/etc/CodeQWERTY.bundle/Contents/Resources/CodeQWERTY.keylayout
new file mode 100644
index 00000000..393a86dd
--- /dev/null
+++ b/etc/CodeQWERTY.bundle/Contents/Resources/CodeQWERTY.keylayout
@@ -0,0 +1,1178 @@
+<?xml version="1.1" encoding="UTF-8"?>
+<!DOCTYPE keyboard PUBLIC "" "file://localhost/System/Library/DTDs/KeyboardLayout.dtd">
+<keyboard group="0" id="5069" maxout="1" name="Code QWERTY">
+    <layouts>
+        <layout first="0" last="17" mapSet="16c" modifiers="f4"/>
+        <layout first="18" last="18" mapSet="994" modifiers="f4"/>
+        <layout first="21" last="23" mapSet="994" modifiers="f4"/>
+        <layout first="30" last="30" mapSet="994" modifiers="f4"/>
+        <layout first="194" last="194" mapSet="994" modifiers="f4"/>
+        <layout first="197" last="197" mapSet="994" modifiers="f4"/>
+        <layout first="200" last="201" mapSet="994" modifiers="f4"/>
+        <layout first="206" last="207" mapSet="994" modifiers="f4"/>
+    </layouts>
+    <modifierMap defaultIndex="7" id="f4">
+        <keyMapSelect mapIndex="8">
+            <modifier keys="command?"/>
+        </keyMapSelect>
+        <keyMapSelect mapIndex="0">
+            <modifier keys="anyShift? caps? command"/>
+        </keyMapSelect>
+        <keyMapSelect mapIndex="9">
+            <modifier keys="anyShift caps?"/>
+        </keyMapSelect>
+        <keyMapSelect mapIndex="2">
+            <modifier keys="caps"/>
+        </keyMapSelect>
+        <keyMapSelect mapIndex="3">
+            <modifier keys="anyOption"/>
+        </keyMapSelect>
+        <keyMapSelect mapIndex="4">
+            <modifier keys="anyShift caps? anyOption command?"/>
+        </keyMapSelect>
+        <keyMapSelect mapIndex="5">
+            <modifier keys="caps anyOption"/>
+        </keyMapSelect>
+        <keyMapSelect mapIndex="6">
+            <modifier keys="caps? anyOption command"/>
+        </keyMapSelect>
+        <keyMapSelect mapIndex="7">
+            <modifier keys="anyShift caps? option? command? control"/>
+            <modifier keys="shift? caps? anyOption command? control"/>
+            <modifier keys="caps? anyOption? command? control"/>
+        </keyMapSelect>
+    </modifierMap>
+    <keyMapSet id="16c">
+        <keyMap index="0">
+            <key action="13" code="0"/>
+            <key code="1" output="s"/>
+            <key code="2" output="d"/>
+            <key code="3" output="f"/>
+            <key code="4" output="h"/>
+            <key code="5" output="g"/>
+            <key code="6" output="z"/>
+            <key code="7" output="x"/>
+            <key code="8" output="c"/>
+            <key code="9" output="v"/>
+            <key code="10" output="§"/>
+            <key code="11" output="b"/>
+            <key code="12" output="q"/>
+            <key code="13" output="w"/>
+            <key action="14" code="14"/>
+            <key code="15" output="r"/>
+            <key action="19" code="16"/>
+            <key code="17" output="t"/>
+            <key code="18" output="1"/>
+            <key code="19" output="2"/>
+            <key code="20" output="3"/>
+            <key code="21" output="4"/>
+            <key code="22" output="6"/>
+            <key code="23" output="5"/>
+            <key code="24" output="="/>
+            <key code="25" output="9"/>
+            <key code="26" output="7"/>
+            <key code="27" output="-"/>
+            <key code="28" output="8"/>
+            <key code="29" output="0"/>
+            <key code="30" output="]"/>
+            <key action="17" code="31"/>
+            <key action="18" code="32"/>
+            <key code="33" output="["/>
+            <key action="15" code="34"/>
+            <key code="35" output="p"/>
+            <key code="36" output="&#x000D;"/>
+            <key code="37" output="l"/>
+            <key code="38" output="j"/>
+            <key code="39" output="&#x0027;"/>
+            <key code="40" output="k"/>
+            <key code="41" output=";"/>
+            <key code="42" output="\"/>
+            <key code="43" output=","/>
+            <key code="44" output="/"/>
+            <key action="16" code="45"/>
+            <key code="46" output="m"/>
+            <key code="47" output="."/>
+            <key code="48" output="&#x0009;"/>
+            <key action="5" code="49"/>
+            <key code="50" output="`"/>
+            <key code="51" output="&#x0008;"/>
+            <key code="52" output="&#x0003;"/>
+            <key code="53" output="&#x001B;"/>
+            <key code="64" output="&#x0010;"/>
+            <key code="65" output="."/>
+            <key code="66" output="&#x001D;"/>
+            <key code="67" output="*"/>
+            <key code="69" output="+"/>
+            <key code="70" output="&#x001C;"/>
+            <key code="71" output="&#x001B;"/>
+            <key code="72" output="&#x001F;"/>
+            <key code="75" output="/"/>
+            <key code="76" output="&#x0003;"/>
+            <key code="77" output="&#x001E;"/>
+            <key code="78" output="-"/>
+            <key code="79" output="&#x0010;"/>
+            <key code="80" output="&#x0010;"/>
+            <key code="81" output="="/>
+            <key code="82" output="0"/>
+            <key code="83" output="1"/>
+            <key code="84" output="2"/>
+            <key code="85" output="3"/>
+            <key code="86" output="4"/>
+            <key code="87" output="5"/>
+            <key code="88" output="6"/>
+            <key code="89" output="7"/>
+            <key code="91" output="8"/>
+            <key code="92" output="9"/>
+            <key code="96" output="&#x0010;"/>
+            <key code="97" output="&#x0010;"/>
+            <key code="98" output="&#x0010;"/>
+            <key code="99" output="&#x0010;"/>
+            <key code="100" output="&#x0010;"/>
+            <key code="101" output="&#x0010;"/>
+            <key code="102" output="&#x0010;"/>
+            <key code="103" output="&#x0010;"/>
+            <key code="104" output="&#x0010;"/>
+            <key code="105" output="&#x0010;"/>
+            <key code="106" output="&#x0010;"/>
+            <key code="107" output="&#x0010;"/>
+            <key code="108" output="&#x0010;"/>
+            <key code="109" output="&#x0010;"/>
+            <key code="110" output="&#x0010;"/>
+            <key code="111" output="&#x0010;"/>
+            <key code="112" output="&#x0010;"/>
+            <key code="113" output="&#x0010;"/>
+            <key code="114" output="&#x0005;"/>
+            <key code="115" output="&#x0001;"/>
+            <key code="116" output="&#x000B;"/>
+            <key code="117" output="&#x007F;"/>
+            <key code="118" output="&#x0010;"/>
+            <key code="119" output="&#x0004;"/>
+            <key code="120" output="&#x0010;"/>
+            <key code="121" output="&#x000C;"/>
+            <key code="122" output="&#x0010;"/>
+            <key code="123" output="&#x001C;"/>
+            <key code="124" output="&#x001D;"/>
+            <key code="125" output="&#x001F;"/>
+            <key code="126" output="&#x001E;"/>
+        </keyMap>
+        <keyMap index="1">
+            <key action="6" code="0"/>
+            <key code="1" output="S"/>
+            <key code="2" output="D"/>
+            <key code="3" output="F"/>
+            <key code="4" output="H"/>
+            <key code="5" output="G"/>
+            <key code="6" output="Z"/>
+            <key code="7" output="X"/>
+            <key code="8" output="C"/>
+            <key code="9" output="V"/>
+            <key code="10" output="±"/>
+            <key code="11" output="B"/>
+            <key code="12" output="Q"/>
+            <key code="13" output="W"/>
+            <key action="7" code="14"/>
+            <key code="15" output="R"/>
+            <key action="12" code="16"/>
+            <key code="17" output="T"/>
+            <key code="18" output="!"/>
+            <key code="19" output="@"/>
+            <key code="20" output="#"/>
+            <key code="21" output="$"/>
+            <key code="22" output="^"/>
+            <key code="23" output="%"/>
+            <key code="24" output="+"/>
+            <key code="25" output="("/>
+            <key code="26" output="&#x0026;"/>
+            <key code="27" output="_"/>
+            <key code="28" output="*"/>
+            <key code="29" output=")"/>
+            <key code="30" output="}"/>
+            <key action="10" code="31"/>
+            <key action="11" code="32"/>
+            <key code="33" output="{"/>
+            <key action="8" code="34"/>
+            <key code="35" output="P"/>
+            <key code="36" output="&#x000D;"/>
+            <key code="37" output="L"/>
+            <key code="38" output="J"/>
+            <key code="39" output="&#x0022;"/>
+            <key code="40" output="K"/>
+            <key code="41" output=":"/>
+            <key code="42" output="|"/>
+            <key code="43" output="&#x003C;"/>
+            <key code="44" output="?"/>
+            <key action="9" code="45"/>
+            <key code="46" output="M"/>
+            <key code="47" output="&#x003E;"/>
+            <key code="48" output="&#x0009;"/>
+            <key action="5" code="49"/>
+            <key code="50" output="~"/>
+            <key code="51" output="&#x0008;"/>
+            <key code="52" output="&#x0003;"/>
+            <key code="53" output="&#x001B;"/>
+            <key code="64" output="&#x0010;"/>
+            <key code="65" output="."/>
+            <key code="66" output="*"/>
+            <key code="67" output="*"/>
+            <key code="69" output="+"/>
+            <key code="70" output="+"/>
+            <key code="71" output="&#x001B;"/>
+            <key code="72" output="="/>
+            <key code="75" output="/"/>
+            <key code="76" output="&#x0003;"/>
+            <key code="77" output="/"/>
+            <key code="78" output="-"/>
+            <key code="79" output="&#x0010;"/>
+            <key code="80" output="&#x0010;"/>
+            <key code="81" output="="/>
+            <key code="82" output="0"/>
+            <key code="83" output="1"/>
+            <key code="84" output="2"/>
+            <key code="85" output="3"/>
+            <key code="86" output="4"/>
+            <key code="87" output="5"/>
+            <key code="88" output="6"/>
+            <key code="89" output="7"/>
+            <key code="91" output="8"/>
+            <key code="92" output="9"/>
+            <key code="96" output="&#x0010;"/>
+            <key code="97" output="&#x0010;"/>
+            <key code="98" output="&#x0010;"/>
+            <key code="99" output="&#x0010;"/>
+            <key code="100" output="&#x0010;"/>
+            <key code="101" output="&#x0010;"/>
+            <key code="102" output="&#x0010;"/>
+            <key code="103" output="&#x0010;"/>
+            <key code="104" output="&#x0010;"/>
+            <key code="105" output="&#x0010;"/>
+            <key code="106" output="&#x0010;"/>
+            <key code="107" output="&#x0010;"/>
+            <key code="108" output="&#x0010;"/>
+            <key code="109" output="&#x0010;"/>
+            <key code="110" output="&#x0010;"/>
+            <key code="111" output="&#x0010;"/>
+            <key code="112" output="&#x0010;"/>
+            <key code="113" output="&#x0010;"/>
+            <key code="114" output="&#x0005;"/>
+            <key code="115" output="&#x0001;"/>
+            <key code="116" output="&#x000B;"/>
+            <key code="117" output="&#x007F;"/>
+            <key code="118" output="&#x0010;"/>
+            <key code="119" output="&#x0004;"/>
+            <key code="120" output="&#x0010;"/>
+            <key code="121" output="&#x000C;"/>
+            <key code="122" output="&#x0010;"/>
+            <key code="123" output="&#x001C;"/>
+            <key code="124" output="&#x001D;"/>
+            <key code="125" output="&#x001F;"/>
+            <key code="126" output="&#x001E;"/>
+        </keyMap>
+        <keyMap index="2">
+            <key action="6" code="0"/>
+            <key code="1" output="S"/>
+            <key code="2" output="D"/>
+            <key code="3" output="F"/>
+            <key code="4" output="H"/>
+            <key code="5" output="G"/>
+            <key code="6" output="Z"/>
+            <key code="7" output="X"/>
+            <key code="8" output="C"/>
+            <key code="9" output="V"/>
+            <key code="10" output="§"/>
+            <key code="11" output="B"/>
+            <key code="12" output="Q"/>
+            <key code="13" output="W"/>
+            <key action="7" code="14"/>
+            <key code="15" output="R"/>
+            <key action="12" code="16"/>
+            <key code="17" output="T"/>
+            <key code="18" output="1"/>
+            <key code="19" output="2"/>
+            <key code="20" output="3"/>
+            <key code="21" output="4"/>
+            <key code="22" output="6"/>
+            <key code="23" output="5"/>
+            <key code="24" output="="/>
+            <key code="25" output="9"/>
+            <key code="26" output="7"/>
+            <key code="27" output="-"/>
+            <key code="28" output="8"/>
+            <key code="29" output="0"/>
+            <key code="30" output="]"/>
+            <key action="10" code="31"/>
+            <key action="11" code="32"/>
+            <key code="33" output="["/>
+            <key action="8" code="34"/>
+            <key code="35" output="P"/>
+            <key code="36" output="&#x000D;"/>
+            <key code="37" output="L"/>
+            <key code="38" output="J"/>
+            <key code="39" output="&#x0027;"/>
+            <key code="40" output="K"/>
+            <key code="41" output=";"/>
+            <key code="42" output="\"/>
+            <key code="43" output=","/>
+            <key code="44" output="/"/>
+            <key action="9" code="45"/>
+            <key code="46" output="M"/>
+            <key code="47" output="."/>
+            <key code="48" output="&#x0009;"/>
+            <key action="5" code="49"/>
+            <key code="50" output="`"/>
+            <key code="51" output="&#x0008;"/>
+            <key code="52" output="&#x0003;"/>
+            <key code="53" output="&#x001B;"/>
+            <key code="64" output="&#x0010;"/>
+            <key code="65" output="."/>
+            <key code="66" output="&#x001D;"/>
+            <key code="67" output="*"/>
+            <key code="69" output="+"/>
+            <key code="70" output="&#x001C;"/>
+            <key code="71" output="&#x001B;"/>
+            <key code="72" output="&#x001F;"/>
+            <key code="75" output="/"/>
+            <key code="76" output="&#x0003;"/>
+            <key code="77" output="&#x001E;"/>
+            <key code="78" output="-"/>
+            <key code="79" output="&#x0010;"/>
+            <key code="80" output="&#x0010;"/>
+            <key code="81" output="="/>
+            <key code="82" output="0"/>
+            <key code="83" output="1"/>
+            <key code="84" output="2"/>
+            <key code="85" output="3"/>
+            <key code="86" output="4"/>
+            <key code="87" output="5"/>
+            <key code="88" output="6"/>
+            <key code="89" output="7"/>
+            <key code="91" output="8"/>
+            <key code="92" output="9"/>
+            <key code="96" output="&#x0010;"/>
+            <key code="97" output="&#x0010;"/>
+            <key code="98" output="&#x0010;"/>
+            <key code="99" output="&#x0010;"/>
+            <key code="100" output="&#x0010;"/>
+            <key code="101" output="&#x0010;"/>
+            <key code="102" output="&#x0010;"/>
+            <key code="103" output="&#x0010;"/>
+            <key code="104" output="&#x0010;"/>
+            <key code="105" output="&#x0010;"/>
+            <key code="106" output="&#x0010;"/>
+            <key code="107" output="&#x0010;"/>
+            <key code="108" output="&#x0010;"/>
+            <key code="109" output="&#x0010;"/>
+            <key code="110" output="&#x0010;"/>
+            <key code="111" output="&#x0010;"/>
+            <key code="112" output="&#x0010;"/>
+            <key code="113" output="&#x0010;"/>
+            <key code="114" output="&#x0005;"/>
+            <key code="115" output="&#x0001;"/>
+            <key code="116" output="&#x000B;"/>
+            <key code="117" output="&#x007F;"/>
+            <key code="118" output="&#x0010;"/>
+            <key code="119" output="&#x0004;"/>
+            <key code="120" output="&#x0010;"/>
+            <key code="121" output="&#x000C;"/>
+            <key code="122" output="&#x0010;"/>
+            <key code="123" output="&#x001C;"/>
+            <key code="124" output="&#x001D;"/>
+            <key code="125" output="&#x001F;"/>
+            <key code="126" output="&#x001E;"/>
+        </keyMap>
+        <keyMap index="3">
+            <key code="0" output="å"/>
+            <key code="1" output="ß"/>
+            <key code="2" output="∂"/>
+            <key code="3" output="ƒ"/>
+            <key code="4" output="˙"/>
+            <key code="5" output="©"/>
+            <key code="6" output="Ω"/>
+            <key code="7" output="≈"/>
+            <key code="8" output="ç"/>
+            <key code="9" output="√"/>
+            <key code="10" output="§"/>
+            <key code="11" output="∫"/>
+            <key code="12" output="œ"/>
+            <key code="13" output="∑"/>
+            <key action="0" code="14"/>
+            <key code="15" output="®"/>
+            <key code="16" output="¥"/>
+            <key code="17" output="†"/>
+            <key code="18" output="¡"/>
+            <key code="19" output="™"/>
+            <key code="20" output="£"/>
+            <key code="21" output="¢"/>
+            <key code="22" output="§"/>
+            <key code="23" output="∞"/>
+            <key code="24" output="≠"/>
+            <key code="25" output="ª"/>
+            <key code="26" output="¶"/>
+            <key code="27" output="–"/>
+            <key code="28" output="•"/>
+            <key code="29" output="º"/>
+            <key code="30" output="‘"/>
+            <key code="31" output="ø"/>
+            <key action="3" code="32"/>
+            <key code="33" output="“"/>
+            <key action="2" code="34"/>
+            <key code="35" output="π"/>
+            <key code="36" output="&#x000D;"/>
+            <key code="37" output="¬"/>
+            <key code="38" output="∆"/>
+            <key code="39" output="æ"/>
+            <key code="40" output="˚"/>
+            <key code="41" output="…"/>
+            <key code="42" output="«"/>
+            <key code="43" output="≤"/>
+            <key code="44" output="÷"/>
+            <key action="4" code="45"/>
+            <key code="46" output="µ"/>
+            <key code="47" output="≥"/>
+            <key code="48" output="&#x0009;"/>
+            <key code="49" output=" "/>
+            <key action="1" code="50"/>
+            <key code="51" output="&#x0008;"/>
+            <key code="52" output="&#x0003;"/>
+            <key code="53" output="&#x001B;"/>
+            <key code="64" output="&#x0010;"/>
+            <key code="65" output="."/>
+            <key code="66" output="&#x001D;"/>
+            <key code="67" output="*"/>
+            <key code="69" output="+"/>
+            <key code="70" output="&#x001C;"/>
+            <key code="71" output="&#x001B;"/>
+            <key code="72" output="&#x001F;"/>
+            <key code="75" output="/"/>
+            <key code="76" output="&#x0003;"/>
+            <key code="77" output="&#x001E;"/>
+            <key code="78" output="-"/>
+            <key code="79" output="&#x0010;"/>
+            <key code="80" output="&#x0010;"/>
+            <key code="81" output="="/>
+            <key code="82" output="0"/>
+            <key code="83" output="1"/>
+            <key code="84" output="2"/>
+            <key code="85" output="3"/>
+            <key code="86" output="4"/>
+            <key code="87" output="5"/>
+            <key code="88" output="6"/>
+            <key code="89" output="7"/>
+            <key code="91" output="8"/>
+            <key code="92" output="9"/>
+            <key code="96" output="&#x0010;"/>
+            <key code="97" output="&#x0010;"/>
+            <key code="98" output="&#x0010;"/>
+            <key code="99" output="&#x0010;"/>
+            <key code="100" output="&#x0010;"/>
+            <key code="101" output="&#x0010;"/>
+            <key code="102" output="&#x0010;"/>
+            <key code="103" output="&#x0010;"/>
+            <key code="104" output="&#x0010;"/>
+            <key code="105" output="&#x0010;"/>
+            <key code="106" output="&#x0010;"/>
+            <key code="107" output="&#x0010;"/>
+            <key code="108" output="&#x0010;"/>
+            <key code="109" output="&#x0010;"/>
+            <key code="110" output="&#x0010;"/>
+            <key code="111" output="&#x0010;"/>
+            <key code="112" output="&#x0010;"/>
+            <key code="113" output="&#x0010;"/>
+            <key code="114" output="&#x0005;"/>
+            <key code="115" output="&#x0001;"/>
+            <key code="116" output="&#x000B;"/>
+            <key code="117" output="&#x007F;"/>
+            <key code="118" output="&#x0010;"/>
+            <key code="119" output="&#x0004;"/>
+            <key code="120" output="&#x0010;"/>
+            <key code="121" output="&#x000C;"/>
+            <key code="122" output="&#x0010;"/>
+            <key code="123" output="&#x001C;"/>
+            <key code="124" output="&#x001D;"/>
+            <key code="125" output="&#x001F;"/>
+            <key code="126" output="&#x001E;"/>
+        </keyMap>
+        <keyMap index="4">
+            <key code="0" output="Å"/>
+            <key code="1" output="Í"/>
+            <key code="2" output="Î"/>
+            <key code="3" output="Ï"/>
+            <key code="4" output="Ó"/>
+            <key code="5" output="˝"/>
+            <key code="6" output="¸"/>
+            <key code="7" output="˛"/>
+            <key code="8" output="Ç"/>
+            <key code="9" output="◊"/>
+            <key code="10" output="±"/>
+            <key code="11" output="ı"/>
+            <key code="12" output="Œ"/>
+            <key code="13" output="„"/>
+            <key code="14" output="´"/>
+            <key code="15" output="‰"/>
+            <key code="16" output="Á"/>
+            <key code="17" output="ˇ"/>
+            <key code="18" output="⁄"/>
+            <key code="19" output="€"/>
+            <key code="20" output="‹"/>
+            <key code="21" output="›"/>
+            <key code="22" output="fl"/>
+            <key code="23" output="fi"/>
+            <key code="24" output="±"/>
+            <key code="25" output="·"/>
+            <key code="26" output="‡"/>
+            <key code="27" output="—"/>
+            <key code="28" output="°"/>
+            <key code="29" output="‚"/>
+            <key code="30" output="’"/>
+            <key code="31" output="Ø"/>
+            <key code="32" output="¨"/>
+            <key code="33" output="”"/>
+            <key code="34" output="ˆ"/>
+            <key code="35" output="∏"/>
+            <key code="36" output="&#x000D;"/>
+            <key code="37" output="Ò"/>
+            <key code="38" output="Ô"/>
+            <key code="39" output="Æ"/>
+            <key code="40" output=""/>
+            <key code="41" output="Ú"/>
+            <key code="42" output="»"/>
+            <key code="43" output="¯"/>
+            <key code="44" output="¿"/>
+            <key code="45" output="˜"/>
+            <key code="46" output="Â"/>
+            <key code="47" output="˘"/>
+            <key code="48" output="&#x0009;"/>
+            <key code="49" output=" "/>
+            <key code="50" output="`"/>
+            <key code="51" output="&#x0008;"/>
+            <key code="52" output="&#x0003;"/>
+            <key code="53" output="&#x001B;"/>
+            <key code="64" output="&#x0010;"/>
+            <key code="65" output="."/>
+            <key code="66" output="*"/>
+            <key code="67" output="*"/>
+            <key code="69" output="+"/>
+            <key code="70" output="+"/>
+            <key code="71" output="&#x001B;"/>
+            <key code="72" output="="/>
+            <key code="75" output="/"/>
+            <key code="76" output="&#x0003;"/>
+            <key code="77" output="/"/>
+            <key code="78" output="-"/>
+            <key code="79" output="&#x0010;"/>
+            <key code="80" output="&#x0010;"/>
+            <key code="81" output="="/>
+            <key code="82" output="0"/>
+            <key code="83" output="1"/>
+            <key code="84" output="2"/>
+            <key code="85" output="3"/>
+            <key code="86" output="4"/>
+            <key code="87" output="5"/>
+            <key code="88" output="6"/>
+            <key code="89" output="7"/>
+            <key code="91" output="8"/>
+            <key code="92" output="9"/>
+            <key code="96" output="&#x0010;"/>
+            <key code="97" output="&#x0010;"/>
+            <key code="98" output="&#x0010;"/>
+            <key code="99" output="&#x0010;"/>
+            <key code="100" output="&#x0010;"/>
+            <key code="101" output="&#x0010;"/>
+            <key code="102" output="&#x0010;"/>
+            <key code="103" output="&#x0010;"/>
+            <key code="104" output="&#x0010;"/>
+            <key code="105" output="&#x0010;"/>
+            <key code="106" output="&#x0010;"/>
+            <key code="107" output="&#x0010;"/>
+            <key code="108" output="&#x0010;"/>
+            <key code="109" output="&#x0010;"/>
+            <key code="110" output="&#x0010;"/>
+            <key code="111" output="&#x0010;"/>
+            <key code="112" output="&#x0010;"/>
+            <key code="113" output="&#x0010;"/>
+            <key code="114" output="&#x0005;"/>
+            <key code="115" output="&#x0001;"/>
+            <key code="116" output="&#x000B;"/>
+            <key code="117" output="&#x007F;"/>
+            <key code="118" output="&#x0010;"/>
+            <key code="119" output="&#x0004;"/>
+            <key code="120" output="&#x0010;"/>
+            <key code="121" output="&#x000C;"/>
+            <key code="122" output="&#x0010;"/>
+            <key code="123" output="&#x001C;"/>
+            <key code="124" output="&#x001D;"/>
+            <key code="125" output="&#x001F;"/>
+            <key code="126" output="&#x001E;"/>
+        </keyMap>
+        <keyMap index="5">
+            <key code="0" output="Å"/>
+            <key code="1" output="Í"/>
+            <key code="2" output="Î"/>
+            <key code="3" output="Ï"/>
+            <key code="4" output="Ó"/>
+            <key code="5" output="©"/>
+            <key code="6" output="Ω"/>
+            <key code="7" output="≈"/>
+            <key code="8" output="Ç"/>
+            <key code="9" output="√"/>
+            <key code="10" output="§"/>
+            <key code="11" output="ı"/>
+            <key code="12" output="Œ"/>
+            <key code="13" output="∑"/>
+            <key code="14" output="´"/>
+            <key code="15" output="®"/>
+            <key code="16" output="Á"/>
+            <key code="17" output="†"/>
+            <key code="18" output="¡"/>
+            <key code="19" output="™"/>
+            <key code="20" output="£"/>
+            <key code="21" output="¢"/>
+            <key code="22" output="§"/>
+            <key code="23" output="∞"/>
+            <key code="24" output="≠"/>
+            <key code="25" output="ª"/>
+            <key code="26" output="¶"/>
+            <key code="27" output="–"/>
+            <key code="28" output="•"/>
+            <key code="29" output="º"/>
+            <key code="30" output="‘"/>
+            <key code="31" output="Ø"/>
+            <key code="32" output="¨"/>
+            <key code="33" output="“"/>
+            <key code="34" output="ˆ"/>
+            <key code="35" output="∏"/>
+            <key code="36" output="&#x000D;"/>
+            <key code="37" output="Ò"/>
+            <key code="38" output="Ô"/>
+            <key code="39" output="Æ"/>
+            <key code="40" output="˚"/>
+            <key code="41" output="…"/>
+            <key code="42" output="«"/>
+            <key code="43" output="≤"/>
+            <key code="44" output="÷"/>
+            <key code="45" output="˜"/>
+            <key code="46" output="Â"/>
+            <key code="47" output="≥"/>
+            <key code="48" output="&#x0009;"/>
+            <key code="49" output=" "/>
+            <key code="50" output="`"/>
+            <key code="51" output="&#x0008;"/>
+            <key code="52" output="&#x0003;"/>
+            <key code="53" output="&#x001B;"/>
+            <key code="64" output="&#x0010;"/>
+            <key code="65" output="."/>
+            <key code="66" output="&#x001D;"/>
+            <key code="67" output="*"/>
+            <key code="69" output="+"/>
+            <key code="70" output="&#x001C;"/>
+            <key code="71" output="&#x001B;"/>
+            <key code="72" output="&#x001F;"/>
+            <key code="75" output="/"/>
+            <key code="76" output="&#x0003;"/>
+            <key code="77" output="&#x001E;"/>
+            <key code="78" output="-"/>
+            <key code="79" output="&#x0010;"/>
+            <key code="80" output="&#x0010;"/>
+            <key code="81" output="="/>
+            <key code="82" output="0"/>
+            <key code="83" output="1"/>
+            <key code="84" output="2"/>
+            <key code="85" output="3"/>
+            <key code="86" output="4"/>
+            <key code="87" output="5"/>
+            <key code="88" output="6"/>
+            <key code="89" output="7"/>
+            <key code="91" output="8"/>
+            <key code="92" output="9"/>
+            <key code="96" output="&#x0010;"/>
+            <key code="97" output="&#x0010;"/>
+            <key code="98" output="&#x0010;"/>
+            <key code="99" output="&#x0010;"/>
+            <key code="100" output="&#x0010;"/>
+            <key code="101" output="&#x0010;"/>
+            <key code="102" output="&#x0010;"/>
+            <key code="103" output="&#x0010;"/>
+            <key code="104" output="&#x0010;"/>
+            <key code="105" output="&#x0010;"/>
+            <key code="106" output="&#x0010;"/>
+            <key code="107" output="&#x0010;"/>
+            <key code="108" output="&#x0010;"/>
+            <key code="109" output="&#x0010;"/>
+            <key code="110" output="&#x0010;"/>
+            <key code="111" output="&#x0010;"/>
+            <key code="112" output="&#x0010;"/>
+            <key code="113" output="&#x0010;"/>
+            <key code="114" output="&#x0005;"/>
+            <key code="115" output="&#x0001;"/>
+            <key code="116" output="&#x000B;"/>
+            <key code="117" output="&#x007F;"/>
+            <key code="118" output="&#x0010;"/>
+            <key code="119" output="&#x0004;"/>
+            <key code="120" output="&#x0010;"/>
+            <key code="121" output="&#x000C;"/>
+            <key code="122" output="&#x0010;"/>
+            <key code="123" output="&#x001C;"/>
+            <key code="124" output="&#x001D;"/>
+            <key code="125" output="&#x001F;"/>
+            <key code="126" output="&#x001E;"/>
+        </keyMap>
+        <keyMap index="6">
+            <key code="0" output="å"/>
+            <key code="1" output="ß"/>
+            <key code="2" output="∂"/>
+            <key code="3" output="ƒ"/>
+            <key code="4" output="˙"/>
+            <key code="5" output="©"/>
+            <key code="6" output="Ω"/>
+            <key code="7" output="≈"/>
+            <key code="8" output="ç"/>
+            <key code="9" output="√"/>
+            <key code="10" output="§"/>
+            <key code="11" output="∫"/>
+            <key code="12" output="œ"/>
+            <key code="13" output="∑"/>
+            <key code="14" output="´"/>
+            <key code="15" output="®"/>
+            <key code="16" output="¥"/>
+            <key code="17" output="†"/>
+            <key code="18" output="¡"/>
+            <key code="19" output="™"/>
+            <key code="20" output="£"/>
+            <key code="21" output="¢"/>
+            <key code="22" output="§"/>
+            <key code="23" output="∞"/>
+            <key code="24" output="≠"/>
+            <key code="25" output="ª"/>
+            <key code="26" output="¶"/>
+            <key code="27" output="–"/>
+            <key code="28" output="•"/>
+            <key code="29" output="º"/>
+            <key code="30" output="‘"/>
+            <key code="31" output="ø"/>
+            <key code="32" output="¨"/>
+            <key code="33" output="“"/>
+            <key code="34" output="^"/>
+            <key code="35" output="π"/>
+            <key code="36" output="&#x000D;"/>
+            <key code="37" output="¬"/>
+            <key code="38" output="∆"/>
+            <key code="39" output="æ"/>
+            <key code="40" output="˚"/>
+            <key code="41" output="…"/>
+            <key code="42" output="«"/>
+            <key code="43" output="≤"/>
+            <key code="44" output="÷"/>
+            <key code="45" output="~"/>
+            <key code="46" output="µ"/>
+            <key code="47" output="≥"/>
+            <key code="48" output="&#x0009;"/>
+            <key code="49" output=" "/>
+            <key code="50" output="`"/>
+            <key code="51" output="&#x0008;"/>
+            <key code="52" output="&#x0003;"/>
+            <key code="53" output="&#x001B;"/>
+            <key code="64" output="&#x0010;"/>
+            <key code="65" output="."/>
+            <key code="66" output="&#x001D;"/>
+            <key code="67" output="*"/>
+            <key code="69" output="+"/>
+            <key code="70" output="&#x001C;"/>
+            <key code="71" output="&#x001B;"/>
+            <key code="72" output="&#x001F;"/>
+            <key code="75" output="/"/>
+            <key code="76" output="&#x0003;"/>
+            <key code="77" output="&#x001E;"/>
+            <key code="78" output="-"/>
+            <key code="79" output="&#x0010;"/>
+            <key code="80" output="&#x0010;"/>
+            <key code="81" output="="/>
+            <key code="82" output="0"/>
+            <key code="83" output="1"/>
+            <key code="84" output="2"/>
+            <key code="85" output="3"/>
+            <key code="86" output="4"/>
+            <key code="87" output="5"/>
+            <key code="88" output="6"/>
+            <key code="89" output="7"/>
+            <key code="91" output="8"/>
+            <key code="92" output="9"/>
+            <key code="96" output="&#x0010;"/>
+            <key code="97" output="&#x0010;"/>
+            <key code="98" output="&#x0010;"/>
+            <key code="99" output="&#x0010;"/>
+            <key code="100" output="&#x0010;"/>
+            <key code="101" output="&#x0010;"/>
+            <key code="102" output="&#x0010;"/>
+            <key code="103" output="&#x0010;"/>
+            <key code="104" output="&#x0010;"/>
+            <key code="105" output="&#x0010;"/>
+            <key code="106" output="&#x0010;"/>
+            <key code="107" output="&#x0010;"/>
+            <key code="108" output="&#x0010;"/>
+            <key code="109" output="&#x0010;"/>
+            <key code="110" output="&#x0010;"/>
+            <key code="111" output="&#x0010;"/>
+            <key code="112" output="&#x0010;"/>
+            <key code="113" output="&#x0010;"/>
+            <key code="114" output="&#x0005;"/>
+            <key code="115" output="&#x0001;"/>
+            <key code="116" output="&#x000B;"/>
+            <key code="117" output="&#x007F;"/>
+            <key code="118" output="&#x0010;"/>
+            <key code="119" output="&#x0004;"/>
+            <key code="120" output="&#x0010;"/>
+            <key code="121" output="&#x000C;"/>
+            <key code="122" output="&#x0010;"/>
+            <key code="123" output="&#x001C;"/>
+            <key code="124" output="&#x001D;"/>
+            <key code="125" output="&#x001F;"/>
+            <key code="126" output="&#x001E;"/>
+        </keyMap>
+        <keyMap index="7">
+            <key code="0" output="&#x0001;"/>
+            <key code="1" output="&#x0013;"/>
+            <key code="2" output="&#x0004;"/>
+            <key code="3" output="&#x0006;"/>
+            <key code="4" output="&#x0008;"/>
+            <key code="5" output="&#x0007;"/>
+            <key code="6" output="&#x001A;"/>
+            <key code="7" output="&#x0018;"/>
+            <key code="8" output="&#x0003;"/>
+            <key code="9" output="&#x0016;"/>
+            <key code="10" output="0"/>
+            <key code="11" output="&#x0002;"/>
+            <key code="12" output="&#x0011;"/>
+            <key code="13" output="&#x0017;"/>
+            <key code="14" output="&#x0005;"/>
+            <key code="15" output="&#x0012;"/>
+            <key code="16" output="&#x0019;"/>
+            <key code="17" output="&#x0014;"/>
+            <key code="18" output="1"/>
+            <key code="19" output="2"/>
+            <key code="20" output="3"/>
+            <key code="21" output="4"/>
+            <key code="22" output="6"/>
+            <key code="23" output="5"/>
+            <key code="24" output="="/>
+            <key code="25" output="9"/>
+            <key code="26" output="7"/>
+            <key code="27" output="&#x001F;"/>
+            <key code="28" output="8"/>
+            <key code="29" output="0"/>
+            <key code="30" output="&#x001D;"/>
+            <key code="31" output="&#x000F;"/>
+            <key code="32" output="&#x0015;"/>
+            <key code="33" output="&#x001B;"/>
+            <key code="34" output="&#x0009;"/>
+            <key code="35" output="&#x0010;"/>
+            <key code="36" output="&#x000D;"/>
+            <key code="37" output="&#x000C;"/>
+            <key code="38" output="&#x000A;"/>
+            <key code="39" output="&#x0027;"/>
+            <key code="40" output="&#x000B;"/>
+            <key code="41" output=";"/>
+            <key code="42" output="&#x001C;"/>
+            <key code="43" output=","/>
+            <key code="44" output="/"/>
+            <key code="45" output="&#x000E;"/>
+            <key code="46" output="&#x000D;"/>
+            <key code="47" output="."/>
+            <key code="48" output="&#x0009;"/>
+            <key action="5" code="49"/>
+            <key code="50" output="`"/>
+            <key code="51" output="&#x0008;"/>
+            <key code="52" output="&#x0003;"/>
+            <key code="53" output="&#x001B;"/>
+            <key code="64" output="&#x0010;"/>
+            <key code="65" output="."/>
+            <key code="66" output="&#x001D;"/>
+            <key code="67" output="*"/>
+            <key code="69" output="+"/>
+            <key code="70" output="&#x001C;"/>
+            <key code="71" output="&#x001B;"/>
+            <key code="72" output="&#x001F;"/>
+            <key code="75" output="/"/>
+            <key code="76" output="&#x0003;"/>
+            <key code="77" output="&#x001E;"/>
+            <key code="78" output="-"/>
+            <key code="79" output="&#x0010;"/>
+            <key code="80" output="&#x0010;"/>
+            <key code="81" output="="/>
+            <key code="82" output="0"/>
+            <key code="83" output="1"/>
+            <key code="84" output="2"/>
+            <key code="85" output="3"/>
+            <key code="86" output="4"/>
+            <key code="87" output="5"/>
+            <key code="88" output="6"/>
+            <key code="89" output="7"/>
+            <key code="91" output="8"/>
+            <key code="92" output="9"/>
+            <key code="96" output="&#x0010;"/>
+            <key code="97" output="&#x0010;"/>
+            <key code="98" output="&#x0010;"/>
+            <key code="99" output="&#x0010;"/>
+            <key code="100" output="&#x0010;"/>
+            <key code="101" output="&#x0010;"/>
+            <key code="102" output="&#x0010;"/>
+            <key code="103" output="&#x0010;"/>
+            <key code="104" output="&#x0010;"/>
+            <key code="105" output="&#x0010;"/>
+            <key code="106" output="&#x0010;"/>
+            <key code="107" output="&#x0010;"/>
+            <key code="108" output="&#x0010;"/>
+            <key code="109" output="&#x0010;"/>
+            <key code="110" output="&#x0010;"/>
+            <key code="111" output="&#x0010;"/>
+            <key code="112" output="&#x0010;"/>
+            <key code="113" output="&#x0010;"/>
+            <key code="114" output="&#x0005;"/>
+            <key code="115" output="&#x0001;"/>
+            <key code="116" output="&#x000B;"/>
+            <key code="117" output="&#x007F;"/>
+            <key code="118" output="&#x0010;"/>
+            <key code="119" output="&#x0004;"/>
+            <key code="120" output="&#x0010;"/>
+            <key code="121" output="&#x000C;"/>
+            <key code="122" output="&#x0010;"/>
+            <key code="123" output="&#x001C;"/>
+            <key code="124" output="&#x001D;"/>
+            <key code="125" output="&#x001F;"/>
+            <key code="126" output="&#x001E;"/>
+        </keyMap>
+        <keyMap index="8" baseMapSet="16c" baseIndex="0">
+            <key code="18" output="!"/>
+            <key code="19" output="@"/>
+            <key code="20" output="#"/>
+            <key code="21" output="$"/>
+            <key code="22" output="^"/>
+            <key code="23" output="%"/>
+            <key code="25" output="("/>
+            <key code="26" output="&#x0026;"/>
+            <key code="27" output="_"/>
+            <key code="28" output="*"/>
+            <key code="29" output=")"/>
+            <key code="30" output="}"/>
+            <key code="33" output="{"/>
+            <key code="42" output="|"/>
+        </keyMap>
+        <keyMap index="9" baseMapSet="16c" baseIndex="1">
+            <key code="18" output="1"/>
+            <key code="19" output="2"/>
+            <key code="20" output="3"/>
+            <key code="21" output="4"/>
+            <key code="22" output="6"/>
+            <key code="23" output="5"/>
+            <key code="25" output="9"/>
+            <key code="26" output="7"/>
+            <key code="27" output="-"/>
+            <key code="28" output="8"/>
+            <key code="29" output="0"/>
+            <key code="30" output="]"/>
+            <key code="33" output="["/>
+            <key code="42" output="\"/>
+        </keyMap>
+    </keyMapSet>
+    <keyMapSet id="994">
+        <keyMap baseIndex="0" baseMapSet="16c" index="0">
+            <key code="24" output="^"/>
+            <key code="30" output="["/>
+            <key code="33" output="@"/>
+            <key code="39" output=":"/>
+            <key code="42" output="]"/>
+            <key code="93" output="¥"/>
+            <key code="94" output="_"/>
+            <key code="95" output=","/>
+            <key action="5" code="102"/>
+            <key action="5" code="104"/>
+        </keyMap>
+        <keyMap baseIndex="1" baseMapSet="16c" index="1">
+            <key code="19" output="&#x0022;"/>
+            <key code="22" output="&#x0026;"/>
+            <key code="24" output="~"/>
+            <key code="25" output=")"/>
+            <key code="26" output="&#x0027;"/>
+            <key code="27" output="="/>
+            <key code="28" output="("/>
+            <key code="29" output="0"/>
+            <key code="30" output="{"/>
+            <key code="33" output="`"/>
+            <key code="39" output="*"/>
+            <key code="41" output="+"/>
+            <key code="42" output="}"/>
+            <key code="93" output="|"/>
+            <key code="94" output="_"/>
+            <key code="95" output=","/>
+            <key action="5" code="102"/>
+            <key action="5" code="104"/>
+        </keyMap>
+        <keyMap baseIndex="2" baseMapSet="16c" index="2">
+            <key code="24" output="^"/>
+            <key code="30" output="["/>
+            <key code="33" output="@"/>
+            <key code="39" output=":"/>
+            <key code="42" output="]"/>
+            <key code="93" output="¥"/>
+            <key code="94" output="_"/>
+            <key code="95" output=","/>
+            <key action="5" code="102"/>
+            <key action="5" code="104"/>
+        </keyMap>
+        <keyMap baseIndex="3" baseMapSet="16c" index="3">
+            <key code="93" output="\"/>
+            <key action="1" code="94"/>
+            <key code="95" output=","/>
+            <key action="5" code="102"/>
+            <key action="5" code="104"/>
+        </keyMap>
+        <keyMap baseIndex="4" baseMapSet="16c" index="4">
+            <key code="93" output="|"/>
+            <key code="94" output="`"/>
+            <key code="95" output=","/>
+            <key action="5" code="102"/>
+            <key action="5" code="104"/>
+        </keyMap>
+        <keyMap baseIndex="5" baseMapSet="16c" index="5">
+            <key code="93" output="\"/>
+            <key code="94" output="`"/>
+            <key code="95" output=","/>
+            <key action="5" code="102"/>
+            <key action="5" code="104"/>
+        </keyMap>
+        <keyMap baseIndex="6" baseMapSet="16c" index="6">
+            <key code="93" output="\"/>
+            <key code="94" output="_"/>
+            <key code="95" output=","/>
+            <key action="5" code="102"/>
+            <key action="5" code="104"/>
+        </keyMap>
+        <keyMap baseIndex="7" baseMapSet="16c" index="7">
+            <key code="93" output="|"/>
+            <key code="94" output="_"/>
+            <key code="95" output=","/>
+            <key action="5" code="102"/>
+            <key action="5" code="104"/>
+        </keyMap>
+    </keyMapSet>
+    <actions>
+        <action id="0">
+            <when next="s1" state="none"/>
+        </action>
+        <action id="1">
+            <when next="s2" state="none"/>
+        </action>
+        <action id="10">
+            <when output="O" state="none"/>
+            <when output="Ó" state="s1"/>
+            <when output="Ò" state="s2"/>
+            <when output="Ô" state="s3"/>
+            <when output="Ö" state="s4"/>
+            <when output="Õ" state="s5"/>
+        </action>
+        <action id="11">
+            <when output="U" state="none"/>
+            <when output="Ú" state="s1"/>
+            <when output="Ù" state="s2"/>
+            <when output="Û" state="s3"/>
+            <when output="Ü" state="s4"/>
+        </action>
+        <action id="12">
+            <when output="Y" state="none"/>
+            <when output="Ÿ" state="s4"/>
+        </action>
+        <action id="13">
+            <when output="a" state="none"/>
+            <when output="á" state="s1"/>
+            <when output="à" state="s2"/>
+            <when output="â" state="s3"/>
+            <when output="ä" state="s4"/>
+            <when output="ã" state="s5"/>
+        </action>
+        <action id="14">
+            <when output="e" state="none"/>
+            <when output="é" state="s1"/>
+            <when output="è" state="s2"/>
+            <when output="ê" state="s3"/>
+            <when output="ë" state="s4"/>
+        </action>
+        <action id="15">
+            <when output="i" state="none"/>
+            <when output="í" state="s1"/>
+            <when output="ì" state="s2"/>
+            <when output="î" state="s3"/>
+            <when output="ï" state="s4"/>
+        </action>
+        <action id="16">
+            <when output="n" state="none"/>
+            <when output="ñ" state="s5"/>
+        </action>
+        <action id="17">
+            <when output="o" state="none"/>
+            <when output="ó" state="s1"/>
+            <when output="ò" state="s2"/>
+            <when output="ô" state="s3"/>
+            <when output="ö" state="s4"/>
+            <when output="õ" state="s5"/>
+        </action>
+        <action id="18">
+            <when output="u" state="none"/>
+            <when output="ú" state="s1"/>
+            <when output="ù" state="s2"/>
+            <when output="û" state="s3"/>
+            <when output="ü" state="s4"/>
+        </action>
+        <action id="19">
+            <when output="y" state="none"/>
+            <when output="ÿ" state="s4"/>
+        </action>
+        <action id="2">
+            <when next="s3" state="none"/>
+        </action>
+        <action id="3">
+            <when next="s4" state="none"/>
+        </action>
+        <action id="4">
+            <when next="s5" state="none"/>
+        </action>
+        <action id="5">
+            <when output=" " state="none"/>
+            <when output="´" state="s1"/>
+            <when output="`" state="s2"/>
+            <when output="ˆ" state="s3"/>
+            <when output="¨" state="s4"/>
+            <when output="˜" state="s5"/>
+        </action>
+        <action id="6">
+            <when output="A" state="none"/>
+            <when output="Á" state="s1"/>
+            <when output="À" state="s2"/>
+            <when output="Â" state="s3"/>
+            <when output="Ä" state="s4"/>
+            <when output="Ã" state="s5"/>
+        </action>
+        <action id="7">
+            <when output="E" state="none"/>
+            <when output="É" state="s1"/>
+            <when output="È" state="s2"/>
+            <when output="Ê" state="s3"/>
+            <when output="Ë" state="s4"/>
+        </action>
+        <action id="8">
+            <when output="I" state="none"/>
+            <when output="Í" state="s1"/>
+            <when output="Ì" state="s2"/>
+            <when output="Î" state="s3"/>
+            <when output="Ï" state="s4"/>
+        </action>
+        <action id="9">
+            <when output="N" state="none"/>
+            <when output="Ñ" state="s5"/>
+        </action>
+    </actions>
+    <terminators>
+        <when output="´" state="s1"/>
+        <when output="`" state="s2"/>
+        <when output="ˆ" state="s3"/>
+        <when output="¨" state="s4"/>
+        <when output="˜" state="s5"/>
+    </terminators>
+</keyboard>
diff --git a/etc/Dark.terminal b/etc/Dark.terminal
new file mode 100644
index 00000000..75184f05
--- /dev/null
+++ b/etc/Dark.terminal
@@ -0,0 +1,1652 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ANSIBlackColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECswLjA4NjQxNDgyMTQ1IDAuMDgyNjczMjQ0MTggMC4wNjE3
+	NjQxODgxMSAxTxApMC4wNjg1NjU0ODc4NiAwLjA2NjU1MDExMzI2IDAuMDUzMTg5MzQ0
+	NwAQAYACgAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50
+	clJHQiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAA
+	AAAAAPbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAU
+	YmtwdAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1u
+	ZAAAAlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAA
+	A/gAAAAUbWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwA
+	AAgMYlRSQwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1Q
+	YWNrYXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAA
+	AAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAA
+	AAAAAAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABja
+	WFlaIAAAAAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMu
+	Y2gAAAAAAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2
+	LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVD
+	IDYxOTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlv
+	biBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25k
+	aXRpb24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3
+	AAAAAAATpP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf
+	521lYXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1
+	cnYAAAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4A
+	YwBoAG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDg
+	AOUA6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwB
+	gwGLAZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJU
+	Al0CZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oD
+	ZgNyA34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSo
+	BLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicG
+	NwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4
+	CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsK
+	EQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxc
+	DHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4P
+	CQ8lD0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHo
+	EgcSJhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIV
+	NBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihiv
+	GNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHsc
+	oxzMHPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDE
+	IPAhHCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTgl
+	aCWXJccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1
+	KmgqmyrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ov
+	kS/HL/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUT
+	NU01hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87
+	LTtrO6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFq
+	QaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVI
+	S0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9J
+	T5NP3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW
+	91dEV5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69
+	Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhn
+	PWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/R
+	cCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5
+	KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKS
+	gvSDV4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOM
+	yo0xjZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cK
+	l3WX4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobai
+	JqKWowajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1E
+	rbiuLa6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5
+	SrnCuju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVL
+	xcjGRsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7S
+	P9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p
+	36/gNuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77Ibt
+	Ee2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn
+	+3f8B/yY/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9y
+	U3BhY2WiIyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEA
+	GgAkACkAMgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANUBAQEDAQUBBwEOARMBGQEb
+	AR0BHw1rDXANew2EDZENlA2hDaoNrw23AAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAA
+	AAAADbo=
+	</data>
+	<key>ANSIBlueColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjIzNzg3NzEzMDUgMC4zODQ0ODYzMTc2IDAuNDAxODE3
+	NTYwMiAxTxAnMC4xODUxNzgwMTE3IDAuMzEyNjgzODIwNyAwLjMyNzM1MzE0OTcAEAGA
+	AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig
+	WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2
+	1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA
+	AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU
+	AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA
+	FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU
+	UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
+	ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA
+	EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA
+	AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA
+	AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA
+	AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg
+	RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2
+	Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g
+	SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u
+	IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA
+	E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
+	AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA
+	AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
+	AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA
+	8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS
+	AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC
+	cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+
+	A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE
+	0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ
+	BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I
+	MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9
+	ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M
+	pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B
+	D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
+	RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4
+	FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ
+	IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1
+	HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
+	SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH
+	JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq
+	zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+
+	MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1
+	wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq
+	O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C
+	MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX
+	SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q
+	J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
+	V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff
+	s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp
+	aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw
+	4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn
+	ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD
+	uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
+	jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY
+	TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG
+	o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u
+	oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7
+	urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG
+	w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE
+	08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
+	veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o
+	7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8
+	mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl
+	oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp
+	ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN
+	Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21
+	</data>
+	<key>ANSIBrightBlackColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjI5ODc1MjYwNTkgMC4yNzU0NDMxMzY3IDAuMjA4Mjg5
+	OTIxMyAxTxAnMC4yMzI1NTg4MTY3IDAuMjEzOTgzMDU4OSAwLjE1NzM3MjYyMzcAEAGA
+	AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig
+	WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2
+	1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA
+	AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU
+	AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA
+	FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU
+	UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
+	ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA
+	EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA
+	AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA
+	AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA
+	AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg
+	RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2
+	Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g
+	SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u
+	IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA
+	E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
+	AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA
+	AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
+	AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA
+	8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS
+	AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC
+	cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+
+	A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE
+	0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ
+	BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I
+	MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9
+	ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M
+	pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B
+	D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
+	RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4
+	FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ
+	IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1
+	HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
+	SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH
+	JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq
+	zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+
+	MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1
+	wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq
+	O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C
+	MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX
+	SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q
+	J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
+	V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff
+	s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp
+	aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw
+	4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn
+	ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD
+	uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
+	jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY
+	TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG
+	o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u
+	oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7
+	urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG
+	w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE
+	08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
+	veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o
+	7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8
+	mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl
+	oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp
+	ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN
+	Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21
+	</data>
+	<key>ANSIBrightBlueColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjI5OTI5MDg5NTUgMC40ODI3MTYzODE1IDAuNDk2MTAy
+	ODA5OSAxTxAnMC4yMzg5NDMwNzAyIDAuNDA5NTA4Mzc3MyAwLjQyMDQxODk3NzcAEAGA
+	AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig
+	WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2
+	1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA
+	AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU
+	AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA
+	FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU
+	UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
+	ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA
+	EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA
+	AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA
+	AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA
+	AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg
+	RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2
+	Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g
+	SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u
+	IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA
+	E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
+	AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA
+	AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
+	AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA
+	8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS
+	AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC
+	cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+
+	A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE
+	0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ
+	BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I
+	MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9
+	ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M
+	pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B
+	D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
+	RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4
+	FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ
+	IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1
+	HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
+	SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH
+	JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq
+	zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+
+	MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1
+	wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq
+	O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C
+	MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX
+	SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q
+	J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
+	V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff
+	s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp
+	aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw
+	4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn
+	ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD
+	uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
+	jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY
+	TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG
+	o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u
+	oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7
+	urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG
+	w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE
+	08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
+	veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o
+	7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8
+	mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl
+	oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp
+	ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN
+	Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21
+	</data>
+	<key>ANSIBrightCyanColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjQxODI5MzY1NDkgMC41OTkyNjI5NTI4IDAuNDIxMTYz
+	NDY5NiAxTxAnMC4zNDk1MDUyMTU5IDAuNTM3MzI0Nzg2MiAwLjM0NjU0MzYzOTkAEAGA
+	AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig
+	WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2
+	1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA
+	AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU
+	AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA
+	FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU
+	UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
+	ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA
+	EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA
+	AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA
+	AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA
+	AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg
+	RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2
+	Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g
+	SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u
+	IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA
+	E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
+	AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA
+	AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
+	AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA
+	8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS
+	AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC
+	cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+
+	A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE
+	0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ
+	BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I
+	MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9
+	ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M
+	pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B
+	D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
+	RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4
+	FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ
+	IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1
+	HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
+	SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH
+	JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq
+	zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+
+	MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1
+	wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq
+	O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C
+	MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX
+	SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q
+	J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
+	V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff
+	s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp
+	aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw
+	4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn
+	ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD
+	uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
+	jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY
+	TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG
+	o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u
+	oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7
+	urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG
+	w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE
+	08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
+	veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o
+	7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8
+	mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl
+	oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp
+	ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN
+	Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21
+	</data>
+	<key>ANSIBrightGreenColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjU1NjU1MTQ1NjUgMC42MDA4OTg1NjM5IDAuMTE2MjQx
+	OTY5MiAxTxAoMC40ODUyMTc3NTAxIDAuNTQxMTE2ODkzMyAwLjA5MjAwNzkyMDE1ABAB
+	gAKABdMYGREaGxxUTlNJRFVOU0lDQxAHgAOABE8RDEgAAAxITGlubwIQAABtbnRyUkdC
+	IFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA
+	9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0
+	AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAAC
+	VAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAA
+	ABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxi
+	VFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2th
+	cmQgQ29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAA
+	ABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAA
+	AAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVog
+	AAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAA
+	AAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4x
+	IERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5
+	NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGlu
+	IElFQzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlv
+	biBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAA
+	ABOk/gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVh
+	cwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAA
+	AAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgA
+	bQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDr
+	APAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsB
+	kgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn
+	AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3ID
+	fgOKA5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTE
+	BNME4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgG
+	WQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgf
+	CDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicK
+	PQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyO
+	DKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUP
+	QQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxIm
+	EkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYV
+	eBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6
+	GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc
+	9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEc
+	IUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZcl
+	xyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqb
+	Ks8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv
+	/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWH
+	NcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7
+	qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHu
+	QjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI
+	10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d
+	UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RX
+	klfgWC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19h
+	X7NgBWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn
+	6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CG
+	cOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl5
+	53pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INX
+	g7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGN
+	mI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfg
+	mEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopaj
+	BqN2o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4t
+	rqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6
+	O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZG
+	xsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHT
+	RNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A2
+	4L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7Zzu
+	KO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH
+	/Jj9Kf26/kv+3P9t///SHyAhIlokY2xhc3NuYW1lWCRjbGFzc2VzXE5TQ29sb3JTcGFj
+	ZaIjJFxOU0NvbG9yU3BhY2VYTlNPYmplY3TSHyAmJ1dOU0NvbG9yoiYkAAgAEQAaACQA
+	KQAyADcASQBMAFEAUwBaAGAAawB4AH4AiwCgAKcA0gD9AP8BAQEDAQoBDwEVARcBGQEb
+	DWcNbA13DYANjQ2QDZ0Npg2rDbMAAAAAAAACAQAAAAAAAAAoAAAAAAAAAAAAAAAAAAAN
+	tg==
+	</data>
+	<key>ANSIBrightMagentaColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjYwMDY0NDIzMDggMC4zNTgyNDcxNjA5IDAuNDE4NDg3
+	MzEwNCAxTxAnMC41MjUyNzc3OTM0IDAuMjc3OTIxMzc4NiAwLjM0MzkxMTY3NzYAEAGA
+	AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig
+	WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2
+	1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA
+	AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU
+	AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA
+	FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU
+	UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
+	ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA
+	EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA
+	AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA
+	AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA
+	AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg
+	RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2
+	Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g
+	SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u
+	IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA
+	E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
+	AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA
+	AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
+	AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA
+	8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS
+	AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC
+	cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+
+	A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE
+	0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ
+	BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I
+	MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9
+	ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M
+	pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B
+	D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
+	RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4
+	FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ
+	IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1
+	HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
+	SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH
+	JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq
+	zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+
+	MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1
+	wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq
+	O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C
+	MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX
+	SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q
+	J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
+	V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff
+	s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp
+	aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw
+	4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn
+	ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD
+	uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
+	jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY
+	TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG
+	o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u
+	oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7
+	urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG
+	w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE
+	08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
+	veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o
+	7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8
+	mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl
+	oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp
+	ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN
+	Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21
+	</data>
+	<key>ANSIBrightRedColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECkwLjgwMDg5NjQ2NTggMC4xOTc5Nzc1NDI5IDAuMDc4OTg3
+	MTU4ODMgMU8QKDAuNzQ2NjI0MTEyMSAwLjExOTYyMTgwNTggMC4wNjg5MzI5ODc3NQAQ
+	AYACgAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50clJH
+	QiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAA
+	APbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtw
+	dAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAA
+	AlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gA
+	AAAUbWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgM
+	YlRSQwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNr
+	YXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAA
+	AAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAA
+	AAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFla
+	IAAAAAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gA
+	AAAAAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIu
+	MSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVDIDYx
+	OTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBp
+	biBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRp
+	b24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAA
+	AAATpP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521l
+	YXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYA
+	AAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBo
+	AG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA
+	6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGL
+	AZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0C
+	ZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNy
+	A34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYE
+	xATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZI
+	BlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsI
+	HwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQon
+	Cj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUM
+	jgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8l
+	D0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcS
+	JhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVW
+	FXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY
+	+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzM
+	HPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAh
+	HCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWX
+	Jccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1Kmgq
+	myrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/H
+	L/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01
+	hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtr
+	O6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB
+	7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iR
+	SNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP
+	3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dE
+	V5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9f
+	YV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeT
+	Z+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtw
+	hnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJ
+	eed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSD
+	V4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0x
+	jZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX
+	4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKW
+	owajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1Erbiu
+	La6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnC
+	uju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjG
+	RsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB
+	00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/g
+	NuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c
+	7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8
+	B/yY/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3Bh
+	Y2WiIyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEAGgAk
+	ACkAMgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANMA/gEAAQIBBAELARABFgEYARoB
+	HA1oDW0NeA2BDY4NkQ2eDacNrA20AAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAA
+	Dbc=
+	</data>
+	<key>ANSIBrightWhiteColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjgwMTk4NTU2MTggMC43MzU3NzAwNDY3IDAuNTU1MDUz
+	NTMyMSAxTxAnMC43NTY0MTcxNTUzIDAuNjg1MjI5MjQxOCAwLjQ4MjY3MjM5MzMAEAGA
+	AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig
+	WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2
+	1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA
+	AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU
+	AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA
+	FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU
+	UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
+	ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA
+	EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA
+	AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA
+	AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA
+	AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg
+	RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2
+	Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g
+	SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u
+	IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA
+	E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
+	AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA
+	AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
+	AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA
+	8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS
+	AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC
+	cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+
+	A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE
+	0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ
+	BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I
+	MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9
+	ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M
+	pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B
+	D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
+	RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4
+	FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ
+	IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1
+	HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
+	SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH
+	JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq
+	zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+
+	MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1
+	wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq
+	O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C
+	MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX
+	SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q
+	J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
+	V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff
+	s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp
+	aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw
+	4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn
+	ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD
+	uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
+	jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY
+	TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG
+	o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u
+	oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7
+	urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG
+	w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE
+	08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
+	veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o
+	7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8
+	mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl
+	oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp
+	ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN
+	Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21
+	</data>
+	<key>ANSIBrightYellowColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjgwMTIwNzQyMzIgMC41ODM2NjYxNDU4IDAuMTU3Mjcw
+	OTk3OCAxTxAnMC43NTIwNzE5NzY3IDAuNTE1MzE4NzUxMyAwLjEyMjE5NTczNTYAEAGA
+	AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig
+	WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2
+	1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA
+	AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU
+	AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA
+	FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU
+	UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
+	ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA
+	EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA
+	AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA
+	AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA
+	AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg
+	RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2
+	Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g
+	SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u
+	IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA
+	E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
+	AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA
+	AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
+	AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA
+	8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS
+	AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC
+	cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+
+	A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE
+	0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ
+	BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I
+	MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9
+	ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M
+	pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B
+	D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
+	RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4
+	FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ
+	IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1
+	HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
+	SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH
+	JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq
+	zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+
+	MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1
+	wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq
+	O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C
+	MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX
+	SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q
+	J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
+	V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff
+	s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp
+	aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw
+	4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn
+	ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD
+	uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
+	jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY
+	TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG
+	o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u
+	oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7
+	urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG
+	w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE
+	08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
+	veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o
+	7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8
+	mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl
+	oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp
+	ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN
+	Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21
+	</data>
+	<key>ANSICyanColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECcwLjMzMzEyNjMwNjUgMC40NzcwOTI5ODEzIDAuMzMyMDk3
+	ODg4IDFPECcwLjI2ODExMDA5NjUgMC40MDc4NTk0NDQ2IDAuMjYyOTM4MDIyNgAQAYAC
+	gAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50clJHQiBY
+	WVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAAAPbW
+	AAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtwdAAA
+	AgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAAAlQA
+	AABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gAAAAU
+	bWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgMYlRS
+	QwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNrYXJk
+	IENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAS
+	c1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAAAAAA
+	AAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAA
+	AAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAA
+	AAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIuMSBE
+	ZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVDIDYxOTY2
+	LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJ
+	RUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24g
+	aW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAAAAAT
+	pP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521lYXMA
+	AAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYAAAAA
+	AAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBoAG0A
+	cgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA6wDw
+	APYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGLAZIB
+	mgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJx
+	AnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNyA34D
+	igOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYExATT
+	BOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZIBlkG
+	agZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsIHwgy
+	CEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQonCj0K
+	VApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUMjgyn
+	DMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8lD0EP
+	Xg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcSJhJF
+	EmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVWFXgV
+	mxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY+hkg
+	GUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzMHPUd
+	Hh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAhHCFI
+	IXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl
+	9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1KmgqmyrP
+	KwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/HL/4w
+	NTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXC
+	Nf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtrO6o7
+	6DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB7kIw
+	QnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iRSNdJ
+	HUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP3VAn
+	UHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dEV5JX
+	4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9fYV+z
+	YAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeTZ+lo
+	P2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtwhnDg
+	cTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJeed6
+	RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6
+	hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0xjZiN
+	/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX4JhM
+	mLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKWowaj
+	dqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1ErbiuLa6h
+	rxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnCuju6
+	tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjGRsbD
+	x0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TT
+	xtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/gNuC9
+	4UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c7iju
+	tO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8B/yY
+	/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3BhY2Wi
+	IyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEAGgAkACkA
+	MgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANEA+wD9AP8BAQEIAQ0BEwEVARcBGQ1l
+	DWoNdQ1+DYsNjg2bDaQNqQ2xAAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAADbQ=
+	</data>
+	<key>ANSIGreenColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECkwLjQ0NjkyMDM5NDkgMC40NzgzODYyMjMzIDAuMDkzNTM1
+	OTM3MzcgMU8QKDAuMzcyNzA2MDU1NiAwLjQxMDYwNDExOTMgMC4wNzU3MTQ2MzI4NwAQ
+	AYACgAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50clJH
+	QiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAA
+	APbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtw
+	dAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAA
+	AlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gA
+	AAAUbWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgM
+	YlRSQwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNr
+	YXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAA
+	AAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAA
+	AAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFla
+	IAAAAAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gA
+	AAAAAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIu
+	MSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVDIDYx
+	OTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBp
+	biBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRp
+	b24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAA
+	AAATpP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521l
+	YXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYA
+	AAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBo
+	AG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA
+	6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGL
+	AZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0C
+	ZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNy
+	A34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYE
+	xATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZI
+	BlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsI
+	HwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQon
+	Cj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUM
+	jgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8l
+	D0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcS
+	JhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVW
+	FXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY
+	+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzM
+	HPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAh
+	HCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWX
+	Jccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1Kmgq
+	myrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/H
+	L/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01
+	hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtr
+	O6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB
+	7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iR
+	SNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP
+	3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dE
+	V5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9f
+	YV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeT
+	Z+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtw
+	hnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJ
+	eed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSD
+	V4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0x
+	jZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX
+	4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKW
+	owajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1Erbiu
+	La6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnC
+	uju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjG
+	RsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB
+	00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/g
+	NuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c
+	7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8
+	B/yY/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3Bh
+	Y2WiIyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEAGgAk
+	ACkAMgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANMA/gEAAQIBBAELARABFgEYARoB
+	HA1oDW0NeA2BDY4NkQ2eDacNrA20AAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAA
+	Dbc=
+	</data>
+	<key>ANSIMagentaColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjQ3ODEyOTQ0NjUgMC4yODgyOTk1NjA1IDAuMzMyMzUx
+	ODMzNiAxTxAnMC4zOTg3NjkxNDAyIDAuMjE3NjQ0NzUxMSAwLjI2MzEzMjE4NDcAEAGA
+	AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig
+	WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2
+	1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA
+	AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU
+	AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA
+	FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU
+	UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
+	ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA
+	EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA
+	AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA
+	AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA
+	AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg
+	RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2
+	Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g
+	SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u
+	IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA
+	E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
+	AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA
+	AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
+	AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA
+	8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS
+	AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC
+	cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+
+	A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE
+	0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ
+	BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I
+	MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9
+	ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M
+	pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B
+	D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
+	RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4
+	FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ
+	IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1
+	HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
+	SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH
+	JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq
+	zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+
+	MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1
+	wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq
+	O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C
+	MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX
+	SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q
+	J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
+	V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff
+	s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp
+	aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw
+	4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn
+	ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD
+	uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
+	jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY
+	TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG
+	o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u
+	oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7
+	urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG
+	w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE
+	08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
+	veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o
+	7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8
+	mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl
+	oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp
+	ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN
+	Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21
+	</data>
+	<key>ANSIRedColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECkwLjYzODQyMzk3OTMgMC4xNTYyMTk5NTkzIDAuMDYzNTUz
+	ODE3NTcgMU8QKTAuNTYzOTM3NzgzMiAwLjA5NTk2MjQ3OTcxIDAuMDU3NzQ0NjQ0NTgA
+	EAGAAoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJS
+	R0IgWFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAA
+	AAD21gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJr
+	cHQAAAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQA
+	AAJUAAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4
+	AAAAFG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAI
+	DGJUUkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFj
+	a2FyZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAA
+	AAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAA
+	AAAAAAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZ
+	WiAAAAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNo
+	AAAAAAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0y
+	LjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2
+	MTk2Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24g
+	aW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0
+	aW9uIGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAA
+	AAAAE6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dt
+	ZWFzAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2
+	AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMA
+	aABtAHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADl
+	AOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMB
+	iwGSAZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJd
+	AmcCcQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YD
+	cgN+A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2
+	BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcG
+	SAZZBmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgL
+	CB8IMghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEK
+	Jwo9ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1
+	DI4MpwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkP
+	JQ9BD14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIH
+	EiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQV
+	VhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjV
+	GPoZIBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMc
+	zBz1HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDw
+	IRwhSCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgl
+	lyXHJfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpo
+	KpsqzysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Ev
+	xy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVN
+	NYc1wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07
+	azuqO+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs
+	Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtI
+	kUjXSR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+T
+	T91QJ1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdX
+	RFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8P
+	X2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1n
+	k2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XAr
+	cIZw4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5
+	iXnnekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0
+	g1eDuoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqN
+	MY2Yjf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1
+	l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiai
+	lqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24
+	ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5
+	wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XI
+	xkbGw8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/S
+	wdNE08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v
+	4DbgveFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHt
+	nO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3
+	/Af8mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNw
+	YWNloiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoA
+	JAApADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDTAP8BAQEDAQUBDAERARcBGQEb
+	AR0NaQ1uDXkNgg2PDZINnw2oDa0NtQAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAA
+	AA24
+	</data>
+	<key>ANSIWhiteColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjYwMTQyMTM1NjIgMC41NTQ3NzkxNzE5IDAuNDIwMzc2
+	ODM3MyAxTxAmMC41MzAyMTU0NDIyIDAuNDg0NTIxOTI1NCAwLjM0NjA2NTQwMgAQAYAC
+	gAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50clJHQiBY
+	WVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAAAPbW
+	AAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtwdAAA
+	AgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAAAlQA
+	AABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gAAAAU
+	bWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgMYlRS
+	QwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNrYXJk
+	IENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAS
+	c1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAAAAAA
+	AAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAA
+	AAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAA
+	AAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIuMSBE
+	ZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVDIDYxOTY2
+	LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJ
+	RUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24g
+	aW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAAAAAT
+	pP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521lYXMA
+	AAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYAAAAA
+	AAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBoAG0A
+	cgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA6wDw
+	APYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGLAZIB
+	mgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJx
+	AnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNyA34D
+	igOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYExATT
+	BOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZIBlkG
+	agZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsIHwgy
+	CEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQonCj0K
+	VApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUMjgyn
+	DMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8lD0EP
+	Xg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcSJhJF
+	EmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVWFXgV
+	mxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY+hkg
+	GUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzMHPUd
+	Hh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAhHCFI
+	IXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl
+	9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1KmgqmyrP
+	KwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/HL/4w
+	NTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXC
+	Nf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtrO6o7
+	6DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB7kIw
+	QnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iRSNdJ
+	HUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP3VAn
+	UHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dEV5JX
+	4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9fYV+z
+	YAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeTZ+lo
+	P2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtwhnDg
+	cTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJeed6
+	RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6
+	hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0xjZiN
+	/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX4JhM
+	mLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKWowaj
+	dqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1ErbiuLa6h
+	rxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnCuju6
+	tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjGRsbD
+	x0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TT
+	xtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/gNuC9
+	4UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c7iju
+	tO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8B/yY
+	/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3BhY2Wi
+	IyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEAGgAkACkA
+	MgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANIA+wD9AP8BAQEIAQ0BEwEVARcBGQ1l
+	DWoNdQ1+DYsNjg2bDaQNqQ2xAAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAADbQ=
+	</data>
+	<key>ANSIYellowColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjYzOTQxMzA1ODggMC40NjU0MDE4MjgzIDAuMTI1ODc5
+	NzM0OCAxTxAoMC41Njg4Njk1OTA4IDAuMzkyMjI2OTY0MiAwLjA5ODMyNTU2NTQ2ABAB
+	gAKABdMYGREaGxxUTlNJRFVOU0lDQxAHgAOABE8RDEgAAAxITGlubwIQAABtbnRyUkdC
+	IFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA
+	9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0
+	AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAAC
+	VAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAA
+	ABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxi
+	VFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2th
+	cmQgQ29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAA
+	ABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAA
+	AAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVog
+	AAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAA
+	AAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4x
+	IERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5
+	NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGlu
+	IElFQzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlv
+	biBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAA
+	ABOk/gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVh
+	cwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAA
+	AAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgA
+	bQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDr
+	APAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsB
+	kgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn
+	AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3ID
+	fgOKA5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTE
+	BNME4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgG
+	WQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgf
+	CDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicK
+	PQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyO
+	DKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUP
+	QQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxIm
+	EkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYV
+	eBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6
+	GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc
+	9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEc
+	IUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZcl
+	xyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqb
+	Ks8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv
+	/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWH
+	NcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7
+	qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHu
+	QjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI
+	10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/d
+	UCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RX
+	klfgWC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19h
+	X7NgBWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn
+	6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CG
+	cOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl5
+	53pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INX
+	g7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGN
+	mI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfg
+	mEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopaj
+	BqN2o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4t
+	rqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6
+	O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZG
+	xsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHT
+	RNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A2
+	4L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7Zzu
+	KO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH
+	/Jj9Kf26/kv+3P9t///SHyAhIlokY2xhc3NuYW1lWCRjbGFzc2VzXE5TQ29sb3JTcGFj
+	ZaIjJFxOU0NvbG9yU3BhY2VYTlNPYmplY3TSHyAmJ1dOU0NvbG9yoiYkAAgAEQAaACQA
+	KQAyADcASQBMAFEAUwBaAGAAawB4AH4AiwCgAKcA0gD9AP8BAQEDAQoBDwEVARcBGQEb
+	DWcNbA13DYANjQ2QDZ0Npg2rDbMAAAAAAAACAQAAAAAAAAAoAAAAAAAAAAAAAAAAAAAN
+	tg==
+	</data>
+	<key>BackgroundColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECswLjA3ODU3MTg5MzI3IDAuMDc0ODI3NTQ0MzkgMC4wNTM5
+	MTM1MTEzNCAxTxApMC4wNjM1NDA2MzAwNCAwLjA2MTU1OTQwODkgMC4wNDg0NzkxODQ1
+	MQAQAYACgAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50
+	clJHQiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAA
+	AAAAAPbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAU
+	YmtwdAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1u
+	ZAAAAlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAA
+	A/gAAAAUbWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwA
+	AAgMYlRSQwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1Q
+	YWNrYXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAA
+	AAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAA
+	AAAAAAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABja
+	WFlaIAAAAAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMu
+	Y2gAAAAAAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2
+	LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVD
+	IDYxOTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlv
+	biBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25k
+	aXRpb24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3
+	AAAAAAATpP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf
+	521lYXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1
+	cnYAAAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4A
+	YwBoAG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDg
+	AOUA6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwB
+	gwGLAZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJU
+	Al0CZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oD
+	ZgNyA34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSo
+	BLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicG
+	NwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4
+	CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsK
+	EQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxc
+	DHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4P
+	CQ8lD0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHo
+	EgcSJhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIV
+	NBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihiv
+	GNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHsc
+	oxzMHPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDE
+	IPAhHCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTgl
+	aCWXJccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1
+	KmgqmyrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ov
+	kS/HL/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUT
+	NU01hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87
+	LTtrO6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFq
+	QaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVI
+	S0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9J
+	T5NP3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW
+	91dEV5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69
+	Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhn
+	PWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/R
+	cCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5
+	KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKS
+	gvSDV4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOM
+	yo0xjZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cK
+	l3WX4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobai
+	JqKWowajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1E
+	rbiuLa6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5
+	SrnCuju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVL
+	xcjGRsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7S
+	P9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p
+	36/gNuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77Ibt
+	Ee2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn
+	+3f8B/yY/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9y
+	U3BhY2WiIyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEA
+	GgAkACkAMgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANUBAQEDAQUBBwEOARMBGQEb
+	AR0BHw1rDXANew2EDZENlA2hDaoNrw23AAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAA
+	AAAADbo=
+	</data>
+	<key>Bell</key>
+	<false/>
+	<key>BellBounceCritical</key>
+	<false/>
+	<key>CursorColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjQ3OTUyNTg2NDEgMC40NDQ2OTg0ODI4IDAuMzMxMzI2
+	MjQ2MyAxTxAnMC40MDMyNjEwNjU1IDAuMzcxNzA4MDk1MSAwLjI2MjQ2MDc2ODIAEAGA
+	AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig
+	WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2
+	1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA
+	AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU
+	AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA
+	FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU
+	UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
+	ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA
+	EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA
+	AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA
+	AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA
+	AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg
+	RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2
+	Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g
+	SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u
+	IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA
+	E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
+	AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA
+	AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
+	AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA
+	8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS
+	AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC
+	cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+
+	A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE
+	0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ
+	BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I
+	MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9
+	ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M
+	pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B
+	D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
+	RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4
+	FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ
+	IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1
+	HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
+	SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH
+	JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq
+	zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+
+	MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1
+	wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq
+	O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C
+	MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX
+	SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q
+	J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
+	V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff
+	s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp
+	aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw
+	4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn
+	ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD
+	uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
+	jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY
+	TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG
+	o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u
+	oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7
+	urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG
+	w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE
+	08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
+	veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o
+	7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8
+	mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl
+	oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp
+	ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN
+	Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21
+	</data>
+	<key>DisableANSIColor</key>
+	<false/>
+	<key>Font</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGkCwwVFlUkbnVsbNQNDg8QERIT
+	FFZOU1NpemVYTlNmRmxhZ3NWTlNOYW1lViRjbGFzcyNAJgAAAAAAABAQgAKAA1ZHb01v
+	bm/SFxgZGlokY2xhc3NuYW1lWCRjbGFzc2VzVk5TRm9udKIZG1hOU09iamVjdAgRGiQp
+	MjdJTFFTWF5nbnd+hY6QkpSboKu0u74AAAAAAAABAQAAAAAAAAAcAAAAAAAAAAAAAAAA
+	AAAAxw==
+	</data>
+	<key>FontAntialias</key>
+	<true/>
+	<key>FontHeightSpacing</key>
+	<integer>1</integer>
+	<key>FontWidthSpacing</key>
+	<integer>1</integer>
+	<key>ProfileCurrentVersion</key>
+	<real>2.0699999999999998</real>
+	<key>SelectionColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECkwLjYzNzg1MDc2MTQgMC4yNTM4NjgzNzEyIDAuMDYwOTk5
+	MzExNTEgMU8QKDAuNTY0MDcwNzAxNiAwLjE4NDY3ODM2MDggMC4wNTY1MzkxODUzNQAQ
+	AYACgAXTGBkRGhscVE5TSURVTlNJQ0MQB4ADgARPEQxIAAAMSExpbm8CEAAAbW50clJH
+	QiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAAA
+	APbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtw
+	dAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAA
+	AlQAAABwZG1kZAAAAsQAAACIdnVlZAAAA0wAAACGdmlldwAAA9QAAAAkbHVtaQAAA/gA
+	AAAUbWVhcwAABAwAAAAkdGVjaAAABDAAAAAMclRSQwAABDwAAAgMZ1RSQwAABDwAAAgM
+	YlRSQwAABDwAAAgMdGV4dAAAAABDb3B5cmlnaHQgKGMpIDE5OTggSGV3bGV0dC1QYWNr
+	YXJkIENvbXBhbnkAAGRlc2MAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAA
+	AAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAADzUQABAAAAARbMWFlaIAAAAAAAAAAA
+	AAAAAAAAAABYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFla
+	IAAAAAAAACSgAAAPhAAAts9kZXNjAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gA
+	AAAAAAAAAAAAABZJRUMgaHR0cDovL3d3dy5pZWMuY2gAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAuSUVDIDYxOTY2LTIu
+	MSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAuSUVDIDYx
+	OTY2LTIuMSBEZWZhdWx0IFJHQiBjb2xvdXIgc3BhY2UgLSBzUkdCAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAGRlc2MAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBp
+	biBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRp
+	b24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2aWV3AAAA
+	AAATpP4AFF8uABDPFAAD7cwABBMLAANcngAAAAFYWVogAAAAAABMCVYAUAAAAFcf521l
+	YXMAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKPAAAAAnNpZyAAAAAAQ1JUIGN1cnYA
+	AAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBo
+	AG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA
+	6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGL
+	AZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0C
+	ZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNy
+	A34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYE
+	xATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZI
+	BlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsI
+	HwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQon
+	Cj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUM
+	jgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8l
+	D0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcS
+	JhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVW
+	FXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY
+	+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzM
+	HPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAh
+	HCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWX
+	Jccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1Kmgq
+	myrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/H
+	L/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01
+	hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtr
+	O6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB
+	7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iR
+	SNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP
+	3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dE
+	V5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9f
+	YV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeT
+	Z+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtw
+	hnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJ
+	eed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSD
+	V4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0x
+	jZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX
+	4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKW
+	owajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1Erbiu
+	La6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnC
+	uju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjG
+	RsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB
+	00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/g
+	NuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c
+	7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8
+	B/yY/Sn9uv5L/tz/bf//0h8gISJaJGNsYXNzbmFtZVgkY2xhc3Nlc1xOU0NvbG9yU3Bh
+	Y2WiIyRcTlNDb2xvclNwYWNlWE5TT2JqZWN00h8gJidXTlNDb2xvcqImJAAIABEAGgAk
+	ACkAMgA3AEkATABRAFMAWgBgAGsAeAB+AIsAoACnANMA/gEAAQIBBAELARABFgEYARoB
+	HA1oDW0NeA2BDY4NkQ2eDacNrA20AAAAAAAAAgEAAAAAAAAAKAAAAAAAAAAAAAAAAAAA
+	Dbc=
+	</data>
+	<key>ShowActiveProcessInTabTitle</key>
+	<false/>
+	<key>ShowActiveProcessInTitle</key>
+	<false/>
+	<key>ShowActivityIndicatorInTab</key>
+	<false/>
+	<key>ShowCommandKeyInTitle</key>
+	<false/>
+	<key>ShowComponentsWhenTabHasCustomTitle</key>
+	<true/>
+	<key>ShowDimensionsInTitle</key>
+	<false/>
+	<key>ShowRepresentedURLInTabTitle</key>
+	<false/>
+	<key>ShowRepresentedURLInTitle</key>
+	<false/>
+	<key>ShowRepresentedURLPathInTitle</key>
+	<false/>
+	<key>ShowShellCommandInTitle</key>
+	<false/>
+	<key>ShowTTYNameInTitle</key>
+	<false/>
+	<key>ShowWindowSettingsNameInTitle</key>
+	<false/>
+	<key>TerminalType</key>
+	<string>xterm</string>
+	<key>TextBoldColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECgwLjgwMTk4NTU2MTggMC43MzU3NzAwNDY3IDAuNTU1MDUz
+	NTMyMSAxTxAnMC43NTY0MTcxNTUzIDAuNjg1MjI5MjQxOCAwLjQ4MjY3MjM5MzMAEAGA
+	AoAF0xgZERobHFROU0lEVU5TSUNDEAeAA4AETxEMSAAADEhMaW5vAhAAAG1udHJSR0Ig
+	WFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAAAAAAAAD2
+	1gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQA
+	AAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJU
+	AAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAA
+	FG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJU
+	UkMAAAQ8AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
+	ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAA
+	EnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAA
+	AAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeFAAAY2lhZWiAA
+	AAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAA
+	AAAAAAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEg
+	RGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2MTk2
+	Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24gaW4g
+	SUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9u
+	IGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAA
+	E6T+ABRfLgAQzxQAA+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
+	AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAA
+	AAAABAAAAAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
+	AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA
+	8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4BdQF8AYMBiwGS
+	AZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQCHQImAi8COAJBAksCVAJdAmcC
+	cQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+
+	A4oDlgOiA64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE
+	0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZ
+	BmoGewaMBp0GrwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8I
+	MghGCFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEKJwo9
+	ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwqDEMMXAx1DI4M
+	pwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9B
+	D14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
+	RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4
+	FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZ
+	IBlFGWsZkRm3Gd0aBBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1
+	HR4dRx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
+	SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4JWgllyXH
+	JfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsq
+	zysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9aL5Evxy/+
+	MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1
+	wjX9Njc2cjauNuk3JDdgN5w31zgUOFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuq
+	O+g8JzxlPKQ84z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5C
+	MEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjX
+	SR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+TT91Q
+	J1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
+	V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhdyV4aXmxevV8PX2Ff
+	s2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBklGTpZT1lkmXnZj1mkmboZz1nk2fp
+	aD9olmjsaUNpmmnxakhqn2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw
+	4HE6cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnn
+	ekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eD
+	uoQdhICE44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
+	jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+XCpd1l+CY
+	TJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBpoNihR6G2oiailqMG
+	o3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2u
+	oa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5wro7
+	urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbG
+	w8dBx7/IPci8yTrJuco4yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE
+	08bUSdTL1U7V0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
+	veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHtnO4o
+	7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8
+	mP0p/br+S/7c/23//9IfICEiWiRjbGFzc25hbWVYJGNsYXNzZXNcTlNDb2xvclNwYWNl
+	oiMkXE5TQ29sb3JTcGFjZVhOU09iamVjdNIfICYnV05TQ29sb3KiJiQACAARABoAJAAp
+	ADIANwBJAEwAUQBTAFoAYABrAHgAfgCLAKAApwDSAPwA/gEAAQIBCQEOARQBFgEYARoN
+	Zg1rDXYNfw2MDY8NnA2lDaoNsgAAAAAAAAIBAAAAAAAAACgAAAAAAAAAAAAAAAAAAA21
+	</data>
+	<key>TextColor</key>
+	<data>
+	YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
+	AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGmCwwXHR4lVSRudWxs1Q0ODxAR
+	EhMUFRZcTlNDb21wb25lbnRzVU5TUkdCXE5TQ29sb3JTcGFjZV8QEk5TQ3VzdG9tQ29s
+	b3JTcGFjZVYkY2xhc3NPECYwLjcxOTQwMjEzNDQgMC42NjA5ODgxNTIgMC41MDEzMjYy
+	NjMgMU8QJzAuNjYwODU1MjkzMyAwLjYwMDE1MjY3MTMgMC40MjY4MTE1NzU5ABABgAKA
+	BdMYGREaGxxUTlNJRFVOU0lDQxAHgAOABE8RDEgAAAxITGlubwIQAABtbnRyUkdCIFhZ
+	WiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA9tYA
+	AQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0AAAC
+	BAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAACVAAA
+	AHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAAABRt
+	ZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxiVFJD
+	AAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQg
+	Q29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAABJz
+	UkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAA
+	AAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAA
+	AAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAA
+	AAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERl
+	ZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5NjYt
+	Mi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAAAAAA
+	AAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElF
+	QzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBp
+	biBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAAABOk
+	/gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAA
+	AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAA
+	AAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQBy
+	AHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDrAPAA
+	9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsBkgGa
+	AaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJnAnEC
+	egKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOK
+	A5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME
+	4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZq
+	BnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDII
+	RghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpU
+	CmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcM
+	wAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9e
+	D3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUS
+	ZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWb
+	Fb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZ
+	RRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0e
+	HUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUgh
+	dSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3
+	JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8r
+	Ais2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1
+	MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1
+	/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvo
+	PCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHuQjBC
+	ckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI10kd
+	SWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQ
+	cVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfg
+	WC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7Ng
+	BWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/
+	aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBx
+	OnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl553pG
+	eqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INXg7qE
+	HYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/
+	jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyY
+	uJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2
+	o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGv
+	Fq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1
+	uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPH
+	Qce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG
+	1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3h
+	ROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO60
+	70DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9
+	Kf26/kv+3P9t///SHyAhIlokY2xhc3NuYW1lWCRjbGFzc2VzXE5TQ29sb3JTcGFjZaIj
+	JFxOU0NvbG9yU3BhY2VYTlNPYmplY3TSHyAmJ1dOU0NvbG9yoiYkAAgAEQAaACQAKQAy
+	ADcASQBMAFEAUwBaAGAAawB4AH4AiwCgAKcA0AD6APwA/gEAAQcBDAESARQBFgEYDWQN
+	aQ10DX0Nig2NDZoNow2oDbAAAAAAAAACAQAAAAAAAAAoAAAAAAAAAAAAAAAAAAANsw==
+	</data>
+	<key>UseBoldFonts</key>
+	<false/>
+	<key>UseBrightBold</key>
+	<true/>
+	<key>VisualBell</key>
+	<false/>
+	<key>VisualBellOnlyWhenMuted</key>
+	<false/>
+	<key>WindowTitle</key>
+	<string>Terminal</string>
+	<key>name</key>
+	<string>Dark</string>
+	<key>noWarnProcesses</key>
+	<array>
+		<dict>
+			<key>ProcessName</key>
+			<string>screen</string>
+		</dict>
+		<dict>
+			<key>ProcessName</key>
+			<string>tmux</string>
+		</dict>
+		<dict>
+			<key>ProcessName</key>
+			<string>atch</string>
+		</dict>
+	</array>
+	<key>rowCount</key>
+	<integer>25</integer>
+	<key>shellExitAction</key>
+	<integer>1</integer>
+	<key>type</key>
+	<string>Window Settings</string>
+	<key>useOptionAsMetaKey</key>
+	<false/>
+</dict>
+</plist>
diff --git a/etc/Go-Mono-Bold-Italic.ttf b/etc/Go-Mono-Bold-Italic.ttf
new file mode 100644
index 00000000..c138a9e1
--- /dev/null
+++ b/etc/Go-Mono-Bold-Italic.ttf
Binary files differdiff --git a/etc/Go-Mono-Bold.ttf b/etc/Go-Mono-Bold.ttf
new file mode 100644
index 00000000..551da07f
--- /dev/null
+++ b/etc/Go-Mono-Bold.ttf
Binary files differdiff --git a/etc/Go-Mono-Italic.ttf b/etc/Go-Mono-Italic.ttf
new file mode 100644
index 00000000..22d4390e
--- /dev/null
+++ b/etc/Go-Mono-Italic.ttf
Binary files differdiff --git a/etc/Go-Mono.ttf b/etc/Go-Mono.ttf
new file mode 100644
index 00000000..71e30123
--- /dev/null
+++ b/etc/Go-Mono.ttf
Binary files differdiff --git a/etc/README.Go-Mono b/etc/README.Go-Mono
new file mode 100644
index 00000000..7043c362
--- /dev/null
+++ b/etc/README.Go-Mono
@@ -0,0 +1,36 @@
+These fonts were created by the Bigelow & Holmes foundry specifically for the
+Go project. See https://blog.golang.org/go-fonts for details.
+
+They are licensed under the same open source license as the rest of the Go
+project's software:
+
+Copyright (c) 2016 Bigelow & Holmes Inc.. All rights reserved.
+
+Distribution of this font is governed by the following license. If you do not
+agree to this license, including the disclaimer, do not distribute or modify
+this font.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+	* Redistributions of source code must retain the above copyright notice,
+	  this list of conditions and the following disclaimer.
+
+	* Redistributions in binary form must reproduce the above copyright notice,
+	  this list of conditions and the following disclaimer in the documentation
+	  and/or other materials provided with the distribution.
+
+	* Neither the name of Google Inc. nor the names of its contributors may be
+	  used to endorse or promote products derived from this software without
+	  specific prior written permission.
+
+DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/etc/code.map b/etc/code.map
new file mode 100644
index 00000000..a3749b8d
--- /dev/null
+++ b/etc/code.map
@@ -0,0 +1,20 @@
+include "/usr/share/kbd/keymaps/i386/qwerty/us.map.gz"
+
+keycode 2 = exclam one
+keycode 3 = at two
+keycode 4 = numbersign three
+keycode 5 = dollar four
+keycode 6 = percent five
+keycode 7 = asciicircum six
+keycode 8 = ampersand seven
+keycode 9 = asterisk eight
+keycode 10 = parenleft nine
+keycode 11 = parenright zero
+keycode 12 = underscore minus
+keycode 26 = braceleft bracketleft
+keycode 27 = braceright bracketright
+keycode 43 = bar backslash
+keycode 58 = Escape
+
+keycode 100 = Compose
+keycode 125 = Escape
diff --git a/etc/psf/.gitignore b/etc/psf/.gitignore
new file mode 100644
index 00000000..446e6b46
--- /dev/null
+++ b/etc/psf/.gitignore
@@ -0,0 +1,2 @@
+*.png
+*.psfu
diff --git a/etc/psf/Makefile b/etc/psf/Makefile
new file mode 100644
index 00000000..e92178cd
--- /dev/null
+++ b/etc/psf/Makefile
@@ -0,0 +1,24 @@
+TABLE = default.u
+
+FONTS += sans6x8.psfu
+FONTS += sans6x10.psfu
+FONTS += sans6x12.psfu
+
+PNGS = $(FONTS:psfu=png)
+
+all: $(FONTS)
+
+png: $(PNGS)
+
+$(FONTS): $(TABLE)
+
+.SUFFIXES: .psf .psfu .png
+
+.psf.psfu:
+	psfaddtable $< $(TABLE) $@
+
+.psf.png:
+	psf2png $< > $@
+
+clean:
+	rm -f $(FONTS) $(PNGS)
diff --git a/etc/psf/default.u b/etc/psf/default.u
new file mode 100644
index 00000000..790ad92b
--- /dev/null
+++ b/etc/psf/default.u
@@ -0,0 +1,259 @@
+#
+# Character table extracted from font default8x16.psfu
+#
+0x000	U+2008
+0x001	U+263a
+0x002	U+263b
+0x003	U+2665
+0x004	U+2666
+0x005	U+2663
+0x006	U+2660
+0x007	U+2022
+0x008	U+25d8
+0x009	U+25cb
+0x00a	U+25d9
+0x00b	U+2642
+0x00c	U+2640
+0x00d	U+266a
+0x00e	U+266b U+266c
+0x00f	U+263c
+0x010	U+25b6 U+25ba
+0x011	U+25c0 U+25c4
+0x012	U+2195
+0x013	U+203c
+0x014	U+00b6
+0x015	U+00a7
+0x016	U+25ac
+0x017	U+21a8
+0x018	U+2191
+0x019	U+2193
+0x01a	U+2192
+0x01b	U+2190
+0x01c	U+221f U+2319
+0x01d	U+2194
+0x01e	U+25b2
+0x01f	U+25bc
+0x020	U+0020 U+00a0 U+2000 U+2001 U+2002 U+2003 U+2004 U+2005 U+2006 U+2007 U+2008 U+2009 U+200a U+202f
+0x021	U+0021
+0x022	U+0022
+0x023	U+0023
+0x024	U+0024
+0x025	U+0025
+0x026	U+0026
+0x027	U+0027
+0x028	U+0028
+0x029	U+0029
+0x02a	U+002a
+0x02b	U+002b
+0x02c	U+002c
+0x02d	U+002d
+0x02e	U+002e
+0x02f	U+002f
+0x030	U+0030
+0x031	U+0031
+0x032	U+0032
+0x033	U+0033
+0x034	U+0034
+0x035	U+0035
+0x036	U+0036
+0x037	U+0037
+0x038	U+0038
+0x039	U+0039
+0x03a	U+003a
+0x03b	U+003b
+0x03c	U+003c
+0x03d	U+003d
+0x03e	U+003e
+0x03f	U+003f
+0x040	U+0040
+0x041	U+0041
+0x042	U+0042
+0x043	U+0043
+0x044	U+0044
+0x045	U+0045
+0x046	U+0046
+0x047	U+0047
+0x048	U+0048
+0x049	U+0049
+0x04a	U+004a
+0x04b	U+004b
+0x04c	U+004c
+0x04d	U+004d
+0x04e	U+004e
+0x04f	U+004f
+0x050	U+0050
+0x051	U+0051
+0x052	U+0052
+0x053	U+0053
+0x054	U+0054
+0x055	U+0055
+0x056	U+0056
+0x057	U+0057
+0x058	U+0058
+0x059	U+0059
+0x05a	U+005a
+0x05b	U+005b
+0x05c	U+005c
+0x05d	U+005d
+0x05e	U+005e
+0x05f	U+005f
+0x060	U+0060
+0x061	U+0061
+0x062	U+0062
+0x063	U+0063
+0x064	U+0064
+0x065	U+0065
+0x066	U+0066
+0x067	U+0067
+0x068	U+0068
+0x069	U+0069
+0x06a	U+006a
+0x06b	U+006b
+0x06c	U+006c
+0x06d	U+006d
+0x06e	U+006e
+0x06f	U+006f
+0x070	U+0070
+0x071	U+0071
+0x072	U+0072
+0x073	U+0073
+0x074	U+0074
+0x075	U+0075
+0x076	U+0076
+0x077	U+0077
+0x078	U+0078
+0x079	U+0079
+0x07a	U+007a
+0x07b	U+007b
+0x07c	U+007c
+0x07d	U+007d
+0x07e	U+007e
+0x07f	U+2302
+0x080	U+00c7
+0x081	U+00fc
+0x082	U+00e9
+0x083	U+00e2
+0x084	U+00e4
+0x085	U+00e0
+0x086	U+00e5
+0x087	U+00e7
+0x088	U+00ea
+0x089	U+00eb
+0x08a	U+00e8
+0x08b	U+00ef
+0x08c	U+00ee
+0x08d	U+00ec
+0x08e	U+00c4
+0x08f	U+00c5 U+212b
+0x090	U+00c9
+0x091	U+00e6
+0x092	U+00c6
+0x093	U+00f4
+0x094	U+00f6
+0x095	U+00f2
+0x096	U+00fb
+0x097	U+00f9
+0x098	U+00ff
+0x099	U+00d6
+0x09a	U+00dc
+0x09b	U+00a2
+0x09c	U+00a3
+0x09d	U+00a5
+0x09e	U+20a7
+0x09f	U+0192
+0x0a0	U+00e1
+0x0a1	U+00ed
+0x0a2	U+00f3
+0x0a3	U+00fa
+0x0a4	U+00f1
+0x0a5	U+00d1
+0x0a6	U+00aa
+0x0a7	U+00ba
+0x0a8	U+00bf
+0x0a9	U+2310
+0x0aa	U+00ac
+0x0ab	U+00bd
+0x0ac	U+00bc
+0x0ad	U+00a1
+0x0ae	U+00ab
+0x0af	U+00bb
+0x0b0	U+2591
+0x0b1	U+2592
+0x0b2	U+2593
+0x0b3	U+2502
+0x0b4	U+2524
+0x0b5	U+2561
+0x0b6	U+2562
+0x0b7	U+2556
+0x0b8	U+2555
+0x0b9	U+2563
+0x0ba	U+2551
+0x0bb	U+2557
+0x0bc	U+255d
+0x0bd	U+255c
+0x0be	U+255b
+0x0bf	U+2510
+0x0c0	U+2514
+0x0c1	U+2534
+0x0c2	U+252c
+0x0c3	U+251c
+0x0c4	U+2500
+0x0c5	U+253c
+0x0c6	U+255e
+0x0c7	U+255f
+0x0c8	U+255a
+0x0c9	U+2554
+0x0ca	U+2569
+0x0cb	U+2566
+0x0cc	U+2560
+0x0cd	U+2550
+0x0ce	U+256c
+0x0cf	U+2567
+0x0d0	U+2568
+0x0d1	U+2564
+0x0d2	U+2565
+0x0d3	U+2559
+0x0d4	U+2558
+0x0d5	U+2552
+0x0d6	U+2553
+0x0d7	U+256b
+0x0d8	U+256a
+0x0d9	U+2518
+0x0da	U+250c
+0x0db	U+2588
+0x0dc	U+2584
+0x0dd	U+258c
+0x0de	U+2590
+0x0df	U+2580
+0x0e0	U+03b1
+0x0e1	U+00df U+03b2
+0x0e2	U+0393
+0x0e3	U+03c0
+0x0e4	U+03a3
+0x0e5	U+03c3
+0x0e6	U+00b5 U+03bc
+0x0e7	U+03c4
+0x0e8	U+03a6
+0x0e9	U+0398
+0x0ea	U+03a9 U+2126
+0x0eb	U+03b4
+0x0ec	U+221e
+0x0ed	U+03c6 U+2205 U+2300
+0x0ee	U+03b5 U+2208
+0x0ef	U+2229
+0x0f0	U+2261
+0x0f1	U+00b1
+0x0f2	U+2265
+0x0f3	U+2264
+0x0f4	U+2320
+0x0f5	U+2321
+0x0f6	U+00f7
+0x0f7	U+2248
+0x0f8	U+00b0
+0x0f9	U+2219 U+22c5
+0x0fa	U+00b7
+0x0fb	U+221a
+0x0fc	U+207f
+0x0fd	U+00b2
+0x0fe	U+220e U+25a0
+0x0ff	U+00a0
diff --git a/etc/psf/sans6x10.psf b/etc/psf/sans6x10.psf
new file mode 100644
index 00000000..09bb1af6
--- /dev/null
+++ b/etc/psf/sans6x10.psf
Binary files differdiff --git a/etc/psf/sans6x12.psf b/etc/psf/sans6x12.psf
new file mode 100644
index 00000000..75c1fd49
--- /dev/null
+++ b/etc/psf/sans6x12.psf
Binary files differdiff --git a/etc/psf/sans6x8.psf b/etc/psf/sans6x8.psf
new file mode 100644
index 00000000..fef671d8
--- /dev/null
+++ b/etc/psf/sans6x8.psf
Binary files differdiff --git a/etc/wsconsctl.conf b/etc/wsconsctl.conf
new file mode 100644
index 00000000..05f29b6a
--- /dev/null
+++ b/etc/wsconsctl.conf
@@ -0,0 +1,26 @@
+display.brightness=50%
+keyboard.backlight=0%
+
+mouse1.tp.tapping=1
+mouse1.tp.scaling=0.2
+mouse1.reverse_scrolling=1
+
+keyboard1.repeat.del1=200
+keyboard1.repeat.deln=50
+
+keyboard1.map+='keycode 30 = exclam 1'
+keyboard1.map+='keycode 31 = at 2'
+keyboard1.map+='keycode 32 = numbersign 3'
+keyboard1.map+='keycode 33 = dollar 4'
+keyboard1.map+='keycode 34 = percent 5'
+keyboard1.map+='keycode 35 = asciicircum 6'
+keyboard1.map+='keycode 36 = ampersand 7'
+keyboard1.map+='keycode 37 = asterisk 8'
+keyboard1.map+='keycode 38 = parenleft 9'
+keyboard1.map+='keycode 39 = parenright 0'
+keyboard1.map+='keycode 45 = underscore minus'
+keyboard1.map+='keycode 47 = braceleft bracketleft'
+keyboard1.map+='keycode 48 = braceright bracketright'
+keyboard1.map+='keycode 49 = bar backslash'
+keyboard1.map+='keycode 50 = bar backslash'
+keyboard1.map+='keycode 57 = Escape'
diff --git a/gpl.c b/gpl.c
new file mode 100644
index 00000000..604089c3
--- /dev/null
+++ b/gpl.c
@@ -0,0 +1,20 @@
+/* Copyright (C) 2021  C. McEnroe <june@causal.agency>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
diff --git a/home/.config/X/modmap b/home/.config/X/modmap
new file mode 100644
index 00000000..b0b1ea79
--- /dev/null
+++ b/home/.config/X/modmap
@@ -0,0 +1,16 @@
+clear Lock
+keysym Caps_Lock = Escape
+keysym 1 = exclam 1
+keysym 2 = at 2
+keysym 3 = numbersign 3
+keysym 4 = dollar 4
+keysym 5 = percent 5
+keysym 6 = asciicircum 6
+keysym 7 = ampersand 7
+keysym 8 = asterisk 8
+keysym 9 = parenleft 9
+keysym 0 = parenright 0
+keysym minus = underscore minus
+keysym bracketleft = braceleft bracketleft
+keysym bracketright = braceright bracketright
+keysym backslash = bar backslash
diff --git a/home/.config/X/resources b/home/.config/X/resources
new file mode 100644
index 00000000..44b8e791
--- /dev/null
+++ b/home/.config/X/resources
@@ -0,0 +1,49 @@
+Xft.dpi: 144
+Xft.antialias: true
+Xft.hinting: false
+
+Xcursor.size: 64
+Xcursor.theme: dmz-aa
+
+XLock.usefirst: false
+XLock.echokeys: true
+
+*Background: rgb:14/13/0E
+*Foreground: rgb:B7/A9/80
+*BorderColor: rgb:99/8D/6B
+
+XTerm*utf8: true
+XTerm*metaSendsEscape: true
+XTerm*alternateScroll: true
+XTerm*allowMouseOps: false
+XTerm*bellIsUrgent: true
+XTerm*charClass: 33:48,36-47:48,58-59:48,61:48,63-64:48,95:48,126:48
+
+XTerm*VT100*translations: #override \n\
+	Super <Key>C: copy-selection(CLIPBOARD) \n\
+	Super <Key>V: insert-selection(CLIPBOARD)
+
+XTerm*faceName: Go Mono:size=11
+XTerm*internalBorder: 6
+XTerm*colorBDMode: true
+XTerm*scrollBar: false
+XTerm*pointerMode: 2
+
+XTerm*color0: rgb:16/15/10
+XTerm*color1: rgb:A3/28/10
+XTerm*color2: rgb:72/7A/18
+XTerm*color3: rgb:A3/77/20
+XTerm*color4: rgb:3D/62/66
+XTerm*color5: rgb:7A/49/55
+XTerm*color6: rgb:55/7A/55
+XTerm*color7: rgb:99/8D/6B
+XTerm*color8: rgb:4C/46/35
+XTerm*color9: rgb:CC/32/14
+XTerm*color10: rgb:8E/99/1E
+XTerm*color11: rgb:CC/95/28
+XTerm*color12: rgb:4C/7B/7F
+XTerm*color13: rgb:99/5B/6B
+XTerm*color14: rgb:6B/99/6B
+XTerm*color15: rgb:CC/BC/8E
+XTerm*colorBD: rgb:CC/BC/8E
+XTerm*cursorColor: rgb:7A/71/55
diff --git a/home/.config/cwm/cwmrc b/home/.config/cwm/cwmrc
new file mode 100644
index 00000000..d72ec163
--- /dev/null
+++ b/home/.config/cwm/cwmrc
@@ -0,0 +1,87 @@
+sticky yes
+snapdist 10
+moveamount 10
+
+ignore clock
+autogroup 0 clock,XTerm
+gap 38 0 0 0
+
+unbind-key all
+bind-key 4-n terminal
+bind-key 4-t "firefox -new-tab about:blank"
+bind-key 4-Delete lock
+bind-key 4-Down window-lower
+bind-key 4-Up window-raise
+bind-key 4-slash menu-window
+bind-key 4-Tab group-cycle
+bind-key 4S-Tab group-rcycle
+bind-key 4-grave window-cycle
+bind-key 4S-grave window-rcycle
+bind-key 4-w window-close
+bind-key 4-exclam group-only-1
+bind-key 4-at group-only-2
+bind-key 4-numbersign group-only-3
+bind-key 4-dollar group-only-4
+bind-key 4-percent group-only-5
+bind-key 4-asciicircum group-only-6
+bind-key 4-ampersand group-only-7
+bind-key 4-asterisk group-only-8
+bind-key 4-parenleft group-only-9
+bind-key 4S-exclam window-movetogroup-1
+bind-key 4S-at window-movetogroup-2
+bind-key 4S-numbersign window-movetogroup-3
+bind-key 4S-dollar window-movetogroup-4
+bind-key 4S-percent window-movetogroup-5
+bind-key 4S-asciicircum window-movetogroup-6
+bind-key 4S-ampersand window-movetogroup-7
+bind-key 4S-asterisk window-movetogroup-8
+bind-key 4S-parenleft window-movetogroup-9
+bind-key 4-f window-fullscreen
+bind-key 4-m window-maximize
+bind-key 4-equal window-vmaximize
+bind-key 4S-equal window-hmaximize
+bind-key 4-underscore window-vtile
+bind-key 4S-underscore window-htile
+bind-key 4-h window-move-left-big
+bind-key 4-j window-move-down-big
+bind-key 4-k window-move-up-big
+bind-key 4-l window-move-right-big
+bind-key 4S-h window-move-left
+bind-key 4S-j window-move-down
+bind-key 4S-k window-move-up
+bind-key 4S-l window-move-right
+bind-key 4S-y window-snap-up-left
+bind-key 4S-u window-snap-up-right
+bind-key 4S-b window-snap-down-left
+bind-key 4S-n window-snap-down-right
+bind-key 4M-h window-resize-left
+bind-key 4M-j window-resize-down
+bind-key 4M-k window-resize-up
+bind-key 4M-l window-resize-right
+bind-key 4MS-h window-resize-left-big
+bind-key 4MS-j window-resize-down-big
+bind-key 4MS-k window-resize-up-big
+bind-key 4MS-l window-resize-right-big
+bind-key 4-space menu-exec
+bind-key 4S-r restart
+bind-key 4S-q quit
+
+bind-key F1 "xbacklight -steps 1 -5"
+bind-key F2 "xbacklight -steps 1 +5"
+bind-key F10 "sndioctl output.mute=!"
+bind-key F11 "sndioctl output.level=-0.05"
+bind-key F12 "sndioctl output.level=+0.05"
+
+unbind-mouse all
+bind-mouse 4-1 window-move
+bind-mouse 4S-1 window-resize
+
+fontname "Go Mono:size=11"
+borderwidth 2
+color inactiveborder rgb:4C/46/35
+color activeborder rgb:99/8D/6B
+color urgencyborder rgb:A3/77/20
+color menubg rgb:14/13/0E
+color menufg rgb:B7/A9/80
+color font rgb:B7/A9/80
+color selfont rgb:14/13/0E
diff --git a/home/.config/git/config b/home/.config/git/config
new file mode 100644
index 00000000..ada63e41
--- /dev/null
+++ b/home/.config/git/config
@@ -0,0 +1,24 @@
+[user]
+	name = C. McEnroe
+	email = june@causal.agency
+
+[commit]
+	verbose = true
+
+[diff]
+	colorMoved = default
+
+[merge]
+	conflictStyle = diff3
+
+[pull]
+	rebase = true
+
+[rebase]
+	autosquash = true
+
+[pretty]
+	log = %Cred%h %Creset%s%C(yellow)%d %Cgreen(%ar) %Cblue<%aN>
+
+[include]
+	path = ./private
diff --git a/home/.config/git/ignore b/home/.config/git/ignore
new file mode 100644
index 00000000..fea54519
--- /dev/null
+++ b/home/.config/git/ignore
@@ -0,0 +1,2 @@
+*.DS_store
+*.dSYM/
diff --git a/home/.config/htop/htoprc b/home/.config/htop/htoprc
new file mode 100644
index 00000000..cdda268d
--- /dev/null
+++ b/home/.config/htop/htoprc
@@ -0,0 +1,29 @@
+# Beware! This file is rewritten by htop when settings are changed in the interface.
+# The parser is also very primitive, and not human-friendly.
+fields=0 48 39 2 46 49 1
+sort_key=47
+sort_direction=1
+hide_threads=0
+hide_kernel_threads=1
+hide_userland_threads=1
+shadow_other_users=0
+show_thread_names=0
+show_program_path=1
+highlight_base_name=1
+highlight_megabytes=1
+highlight_threads=1
+tree_view=1
+header_margin=0
+detailed_cpu_time=0
+cpu_count_from_zero=0
+show_cpu_usage=1
+show_cpu_frequency=0
+update_process_names=0
+account_guest_in_cpu_meter=0
+color_scheme=0
+enable_mouse=0
+delay=15
+left_meters=AllCPUs2
+left_meter_modes=1
+right_meters=Memory Swap
+right_meter_modes=1 1
diff --git a/home/.editrc b/home/.editrc
new file mode 100644
index 00000000..cf779a7d
--- /dev/null
+++ b/home/.editrc
@@ -0,0 +1 @@
+bind -v
diff --git a/home/.gdbinit b/home/.gdbinit
new file mode 100644
index 00000000..9422460c
--- /dev/null
+++ b/home/.gdbinit
@@ -0,0 +1 @@
+set disassembly-flavor intel
diff --git a/home/.hushlogin b/home/.hushlogin
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/home/.hushlogin
diff --git a/home/.inputrc b/home/.inputrc
new file mode 100644
index 00000000..b2cc9d61
--- /dev/null
+++ b/home/.inputrc
@@ -0,0 +1 @@
+set editing-mode vi
diff --git a/home/.lldbinit b/home/.lldbinit
new file mode 100644
index 00000000..73f3e676
--- /dev/null
+++ b/home/.lldbinit
@@ -0,0 +1 @@
+settings set target.x86-disassembly-flavor intel
diff --git a/home/.local/bin/aes b/home/.local/bin/aes
new file mode 100755
index 00000000..32b52637
--- /dev/null
+++ b/home/.local/bin/aes
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -eu
+
+enwiden() {
+	exec tr ' -~' ' !-~'
+}
+[ $# -gt 0 ] && echo "$*" | enwiden || enwiden
diff --git a/home/.local/bin/clock b/home/.local/bin/clock
new file mode 100755
index 00000000..ef8cd6d8
--- /dev/null
+++ b/home/.local/bin/clock
@@ -0,0 +1,17 @@
+#!/bin/sh
+set -eu
+
+tput civis
+sleep=$(( 60 - $(date +'%S' | sed 's/^0//') ))
+while :; do
+	if [ $(apm -a) -eq 1 ]; then
+		printf '%3s%%' "$(apm -l)"
+	else
+		test $(apm -b) -eq 2 && tput setaf 1 bold
+		printf '%3.3sm' "$(apm -m)"
+		tput sgr0
+	fi
+	printf ' %s\r' "$(date +'%a %H:%M')"
+	sleep $sleep
+	sleep=60
+done
diff --git a/home/.local/bin/def b/home/.local/bin/def
new file mode 100755
index 00000000..6a1681d3
--- /dev/null
+++ b/home/.local/bin/def
@@ -0,0 +1,47 @@
+#!/bin/sh
+set -eu
+
+macro=$1
+headers='
+assert.h
+complex.h
+ctype.h
+errno.h
+fenv.h
+float.h
+inttypes.h
+iso646.h
+limits.h
+locale.h
+math.h
+setjmp.h
+signal.h
+stdalign.h
+stdarg.h
+stdatomic.h
+stdbool.h
+stddef.h
+stdint.h
+stdio.h
+stdlib.h
+stdnoreturn.h
+string.h
+tgmath.h
+threads.h
+time.h
+uchar.h
+wchar.h
+wctype.h
+'
+
+for header in $headers; do
+	defined=$(
+		echo "$macro" \
+		| cc -E -x c -include "$header" - \
+		2> /dev/null \
+		| tail -n 1
+	)
+	[ $? -ne 0 -o "$defined" = "$macro" ] && continue
+	echo "#include <${header}>"
+	echo "$defined"
+done
diff --git a/home/.local/bin/deg b/home/.local/bin/deg
new file mode 100755
index 00000000..216029ed
--- /dev/null
+++ b/home/.local/bin/deg
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -eu
+cat <<EOF
+${1}°F = $(dc -e "1k $(echo "$1" | sed 's/^-/_/') 32-1.8/p")°C
+${1}°C = $(dc -e "1k $(echo "$1" | sed 's/^-/_/') 1.8*32+p")°F
+EOF
diff --git a/home/.local/bin/git-password b/home/.local/bin/git-password
new file mode 100755
index 00000000..41351e38
--- /dev/null
+++ b/home/.local/bin/git-password
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -eu
+
+url=$1
+echo "url=${url}" \
+	| git credential fill \
+	| sed -En 's/^password=(.*)/\1/p'
diff --git a/home/.local/bin/mdate b/home/.local/bin/mdate
new file mode 100755
index 00000000..daff50dc
--- /dev/null
+++ b/home/.local/bin/mdate
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec date +'.Dd %B %e, %Y'
diff --git a/home/.local/bin/nasd b/home/.local/bin/nasd
new file mode 100755
index 00000000..d64b2c3a
--- /dev/null
+++ b/home/.local/bin/nasd
@@ -0,0 +1,9 @@
+#!/bin/sh
+set -eu
+
+dir=$(mktemp -d)
+echo 'bits 64' > "${dir}/input"
+cat >> "${dir}/input"
+nasm -o "${dir}/output" "${dir}/input" || true
+ndisasm -b 64 "${dir}/output" || true
+rm -r "$dir"
diff --git a/home/.local/bin/notify-send b/home/.local/bin/notify-send
new file mode 100755
index 00000000..5630440d
--- /dev/null
+++ b/home/.local/bin/notify-send
@@ -0,0 +1,9 @@
+#!/usr/bin/osascript
+
+on run argv
+	if count of argv is 2 then
+		display notification (item 2 of argv) with title (item 1 of argv)
+	else
+		display notification (item 1 of argv)
+	end if
+end run
diff --git a/home/.local/bin/np b/home/.local/bin/np
new file mode 100755
index 00000000..b0eb2326
--- /dev/null
+++ b/home/.local/bin/np
@@ -0,0 +1,7 @@
+#!/usr/bin/osascript
+
+tell application "Music"
+	tell current track
+		get "/me is listening to " & artist & " — " & name
+	end tell
+end tell
diff --git a/home/.local/bin/open b/home/.local/bin/open
new file mode 100755
index 00000000..9439f07d
--- /dev/null
+++ b/home/.local/bin/open
@@ -0,0 +1,19 @@
+#!/bin/sh
+set -eu
+
+if [ -n "${SSH_CLIENT:-}" ]; then
+	exec pbd -o "$@"
+fi
+
+case "$1" in
+	(*.gif|*.jpeg|*.jpg|*.png)
+		curl -LSs "$1" | imv -
+		;;
+	(https://youtu.be/*|https://www.youtube.com/watch*|https://twitch.tv/*)
+		ulimit -c 0 # mpv segfaults on exit every time on OpenBSD...
+		exec mpv "$1" >/dev/null 2>&1
+		;;
+	(*)
+		exec firefox -new-tab "$1" >/dev/null 2>&1
+		;;
+esac
diff --git a/home/.local/bin/pbcopy b/home/.local/bin/pbcopy
new file mode 100755
index 00000000..a804f836
--- /dev/null
+++ b/home/.local/bin/pbcopy
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -eu
+
+if [ -n "${SSH_CLIENT:-}" ]; then
+	exec pbd -c
+elif [ -n "${DISPLAY:-}" ]; then
+	exec xsel -bi
+else
+	echo "${0}: don't know what to do" >&2
+	exit 1
+fi
diff --git a/home/.local/bin/pbpaste b/home/.local/bin/pbpaste
new file mode 100755
index 00000000..2924f01e
--- /dev/null
+++ b/home/.local/bin/pbpaste
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -eu
+
+if [ -n "${SSH_CLIENT:-}" ]; then
+	exec pbd -p
+elif [ -n "${DISPLAY:-}" ]; then
+	exec xsel -bo
+else
+	echo "${0}: don't know what to do" >&2
+	exit 1
+fi
diff --git a/home/.local/bin/versions b/home/.local/bin/versions
new file mode 100755
index 00000000..25e5ff72
--- /dev/null
+++ b/home/.local/bin/versions
@@ -0,0 +1,9 @@
+#!/bin/sh
+set -u
+
+for repo in ~/src/git/*; do
+	version=$(git -C "${repo}" describe --dirty 2>/dev/null)
+	if [ $? -eq 0 ]; then
+		echo "${repo##*/}-${version#v}"
+	fi
+done | sort -nr -t '-' -k 3 | column -t -s '-'
diff --git a/home/.profile b/home/.profile
new file mode 100644
index 00000000..f4e4169e
--- /dev/null
+++ b/home/.profile
@@ -0,0 +1,23 @@
+_PATH=$PATH PATH=
+path() { [ -d "$1" ] && PATH="${PATH}${PATH:+:}${1}"; }
+for prefix in '' /usr/local /opt/local /usr ~/.local; do
+	path "${prefix}/sbin"
+	path "${prefix}/bin"
+done
+path /usr/X11R6/bin
+path /usr/games
+export MANPATH=:~/.local/share/man
+
+export EDITOR=vi
+type nvi >/dev/null && EDITOR=nvi
+export EXINIT='set ai iclower sm sw=4 ts=4 para=BlBdPpIt sect=ShSs | map gg 1G'
+export PAGER=less
+export LESS=FRXix4
+export CLICOLOR=1
+export MANSECT=2:3:1:8:6:5:7:4:9
+export NETHACKOPTIONS='pickup_types:$!?+/=, color, DECgraphics'
+
+[ -e /usr/share/mk/sys.mk ] || export CFLAGS=-O
+[ -d /usr/home ] && cd
+
+export ENV=~/.shrc
diff --git a/home/.shrc b/home/.shrc
new file mode 100644
index 00000000..4b0e6c61
--- /dev/null
+++ b/home/.shrc
@@ -0,0 +1,63 @@
+set -o noclobber -o nounset -o vi
+
+CDPATH=:~
+
+alias vi=$EDITOR
+alias ls='LC_COLLATE=C ls -p'
+alias ll='ls -hl'
+alias ff='find . -type f -name'
+alias bc='bc -l'
+alias ag='ag --pager=$PAGER'
+alias gs='git status --short --branch || ls' gd='git diff'
+alias gsh='git show' gl='git log --graph --pretty=log'
+alias gco='git checkout' gb='git branch' gm='git merge' gst='git stash'
+alias ga='git add' gmv='git mv' grm='git rm'
+alias gc='git commit' gca='gc --amend' gt='git tag'
+alias gp='git push' gu='git pull' gf='git fetch' gr='git rebase'
+alias rand='openssl rand -base64 33'
+alias private='eval "$(gpg -d ~/.private)"'
+type doas >/dev/null && alias sudo=doas
+
+man() {
+	test $# -ne 1 && { command man "$@"; return $?; }
+	(IFS=:
+	for sect in $MANSECT; do
+		command man -w $sect "$1" >/dev/null 2>&1 && exec man $sect "$1"
+	done
+	exec man "$1")
+}
+
+cd() {
+	local path
+	if [ $# -eq 0 ]; then
+		command cd
+	elif [ "${1%%:*}" != "$1" ]; then
+		path=${1#*:}
+		[ -n "${path}" ] || path=${PWD#${HOME}/}
+		SSH_CD=$path ssh -o SendEnv=SSH_CD "${1%%:*}"
+	elif [ -e "$1" -a ! -d "$1" ]; then
+		command cd "${1%/*}" && $EDITOR "${1##*/}"
+	else
+		command cd "$@"
+	fi
+}
+if [ -n "${SSH_CD:-}" ]; then
+	cd "${SSH_CD}"
+	unset SSH_CD
+fi
+
+export LESS_TERMCAP_us=$(tput sitm)
+export LESS_TERMCAP_ue=$(tput ritm)
+
+hostname=$(hostname -s)
+rprompt() {
+	local pwd
+	pwd=${PWD#${HOME}}
+	[ "${pwd}" != "${PWD}" ] && pwd="~${pwd}"
+	[ "${TERM%-*}" = 'xterm' ] \
+		&& printf '\33]0;%s\a' "${SSH_CLIENT:+${hostname}:}${pwd##*/}" >&2
+	printf '%s' "${SSH_CLIENT:+${hostname}:}${pwd}"
+}
+PS1='
+$ '
+RPS1='${?#0} $(rprompt)'
diff --git a/home/.ssh/config b/home/.ssh/config
new file mode 100644
index 00000000..3fc6a8db
--- /dev/null
+++ b/home/.ssh/config
@@ -0,0 +1,18 @@
+IgnoreUnknown Include
+Include config_private
+
+HashKnownHosts yes
+
+SendEnv LANG LC_*
+
+Host monday beastie puffy toaster tux progynova
+	HostName %h.local
+	ForwardAgent yes
+	RemoteForward 7062 127.0.0.1:7062
+
+Host june july
+	HostName %h.nyc3.do.causal.agency
+	Port 2222
+
+Host git.causal.agency temp.causal.agency
+	Port 2222
diff --git a/home/.xsession b/home/.xsession
new file mode 100644
index 00000000..b6ebd8a1
--- /dev/null
+++ b/home/.xsession
@@ -0,0 +1,12 @@
+. ~/.profile
+export LC_CTYPE=en_US.UTF-8
+
+xset r rate 175 m 5/4 0
+xmodmap ~/.config/X/modmap
+xrdb -load ~/.config/X/resources
+xsetroot -bitmap /usr/X11R6/include/X11/bitmaps/xsnow \
+	-bg rgb:14/13/0E -fg rgb:7A/49/55 
+
+sctd 3600 &
+xterm -name clock -geometry 14x1-0+0 -sl 0 -e clock &
+exec cwm -c ~/.config/cwm/cwmrc
diff --git a/install.sh b/install.sh
new file mode 100644
index 00000000..9e18c6bf
--- /dev/null
+++ b/install.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+set -eu
+
+X=
+while getopts 'X' opt; do
+	case "$opt" in
+		(X) X=1;;
+		(?) exit 1;;
+	esac
+done
+
+packages='curl htop sl the_silver_searcher tree'
+
+FreeBSD() {
+	sudo pkg install ddate $packages
+}
+
+OpenBSD() {
+	doas pkg_add $packages
+	if test $X; then
+		doas pkg_add firefox go-fonts imv scrot sct w3m-- xcursor-dmz xsel
+	fi
+}
+
+Linux() {
+	sudo pacman -Sy --needed bc ctags gdb openssh vi $packages
+}
+
+installMacPorts() {
+	xcode-select --install
+	xcodebuild -license
+	dir=MacPorts-2.6.3
+	tar=${dir}.tar.bz2
+	curl -O "https://distfiles.macports.org/MacPorts/${tar}"
+	tar -x -f $tar
+	(cd $dir && ./configure)
+	make -C $dir
+	sudo make -C $dir install
+	rm -fr $tar $dir
+}
+
+Darwin() {
+	[ -d /opt/local ] || installMacPorts
+	sudo /opt/local/bin/port selfupdate
+	sudo /opt/local/bin/port -N install git mandoc nvi pkgconfig $packages
+	sudo mkdir -p /opt/local/etc/select/man
+	printf 'bin/man\nshare/man/man1/man.1\nshare/man/man1/man.1.gz\n' \
+		| sudo tee /opt/local/etc/select/man/base >/dev/null
+	printf '/usr/bin/man\n/usr/share/man/man1/man.1\n-\n' \
+		| sudo tee /opt/local/etc/select/man/system >/dev/null
+	sudo port select --set man system
+}
+
+$(uname)
diff --git a/link.sh b/link.sh
new file mode 100644
index 00000000..6763f2e0
--- /dev/null
+++ b/link.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+set -eu
+
+die() {
+	echo "$*"
+	exit 1
+}
+
+if [ $# -eq 1 ]; then
+	link=$1
+	file="${PWD}/home/${link#${HOME}/}"
+	[ ! -f "$file" ] || die "${file} exists"
+	mkdir -p "${file%/*}"
+	mv "$link" "$file"
+fi
+
+find home -type f | while read -r find; do
+	file="${PWD}/${find}"
+	link="${HOME}/${find#home/}"
+	mkdir -p "${link%/*}"
+	[ \( -f "$link" -a -L "$link" \) -o ! -f "$link" ] || die "${link} exists"
+	ln -fs "$file" "$link"
+done
diff --git a/port/caesar/.gitignore b/port/caesar/.gitignore
new file mode 100644
index 00000000..e2c3034b
--- /dev/null
+++ b/port/caesar/.gitignore
@@ -0,0 +1,2 @@
+caesar
+rot13
diff --git a/port/caesar/Makefile b/port/caesar/Makefile
new file mode 100644
index 00000000..01205b16
--- /dev/null
+++ b/port/caesar/Makefile
@@ -0,0 +1,19 @@
+PREFIX = ~/.local
+MANDIR = ${PREFIX}/share/man
+
+LDLIBS = -lm
+
+all: caesar rot13
+
+clean:
+	rm -f caesar rot13
+
+install: caesar rot13 caesar.6
+	install -d ${PREFIX}/bin ${MANDIR}/man6
+	install caesar rot13 ${PREFIX}/bin
+	install -m 644 caesar.6 ${MANDIR}/man6/caesar.6
+	install -m 644 caesar.6 ${MANDIR}/man6/rot13.6
+
+uninstall:
+	rm -f ${PREFIX}/bin/caesar ${PREFIX}/bin/rot13
+	rm -f ${MANDIR}/man6/caesar.6 ${MANDIR}/man6/rot13.6
diff --git a/port/caesar/caesar.6 b/port/caesar/caesar.6
new file mode 100644
index 00000000..4c4bbfb4
--- /dev/null
+++ b/port/caesar/caesar.6
@@ -0,0 +1,73 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\"	The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"	@(#)caesar.6	8.2 (Berkeley) 11/16/93
+.\" $FreeBSD: releng/11.2/usr.bin/caesar/caesar.6 216239 2010-12-06 19:12:51Z uqs $
+.\"
+.Dd November 16, 1993
+.Dt CAESAR 6
+.Os
+.Sh NAME
+.Nm caesar , rot13
+.Nd decrypt caesar ciphers
+.Sh SYNOPSIS
+.Nm
+.Op Ar rotation
+.Nm rot13
+.Sh DESCRIPTION
+The
+.Nm
+utility attempts to decrypt caesar ciphers using English letter frequency
+statistics.
+.Nm Caesar
+reads from the standard input and writes to the standard output.
+.Pp
+The optional numerical argument
+.Ar rotation
+may be used to specify a specific rotation value.
+If invoked as
+.Nm rot13 ,
+a rotation value of 13 will be used.
+.Pp
+The frequency (from most common to least) of English letters is as follows:
+.Bd -ragged -offset indent
+ETAONRISHDLFCMUGPYWBVKXJQZ
+.Ed
+.Pp
+Their frequencies as a percentage are as follows:
+.Bd -ragged -offset indent
+E(13), T(10.5), A(8.1), O(7.9), N(7.1), R(6.8), I(6.3), S(6.1), H(5.2),
+D(3.8), L(3.4), F(2.9), C(2.7), M(2.5), U(2.4), G(2),
+P(1.9), Y(1.9),
+W(1.5), B(1.4), V(.9), K(.4), X(.15), J(.13), Q(.11), Z(.07).
+.Ed
+.Pp
+Rotated postings to
+.Tn USENET
+and some of the databases used by the
+.Xr fortune 6
+program are rotated by 13 characters.
diff --git a/port/caesar/caesar.c b/port/caesar/caesar.c
new file mode 100644
index 00000000..cd6cd579
--- /dev/null
+++ b/port/caesar/caesar.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Adams.
+ *
+ * Authors:
+ *	Stan King, John Eldridge, based on algorithm suggested by
+ *	Bob Morris
+ * 29-Sep-82
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+	The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char sccsid[] = "@(#)caesar.c    8.1 (Berkeley) 5/31/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+//__FBSDID("$FreeBSD: releng/11.2/usr.bin/caesar/caesar.c 241846 2012-10-22 03:06:53Z eadler $");
+
+#include <errno.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#define	LINELENGTH	2048
+#define	ROTATE(ch, perm) \
+     isascii(ch) ? ( \
+	isupper(ch) ? ('A' + (ch - 'A' + perm) % 26) : \
+	    islower(ch) ? ('a' + (ch - 'a' + perm) % 26) : ch) : ch
+
+/*
+ * letter frequencies (taken from some unix(tm) documentation)
+ * (unix is a trademark of Bell Laboratories)
+ */
+static double stdf[26] = {
+	7.97, 1.35, 3.61, 4.78, 12.37, 2.01, 1.46, 4.49, 6.39, 0.04,
+	0.42, 3.81, 2.69, 5.92,  6.96, 2.91, 0.08, 6.63, 8.77, 9.68,
+	2.62, 0.81, 1.88, 0.23,  2.07, 0.06,
+};
+
+static void printit(char *);
+
+int
+main(int argc, char **argv)
+{
+	int ch, dot, i, nread, winnerdot = 0;
+	char *inbuf;
+	int obs[26], try, winner;
+
+	if (argc > 1)
+		printit(argv[1]);
+
+	if (!(inbuf = malloc((size_t)LINELENGTH))) {
+		(void)fprintf(stderr, "caesar: out of memory.\n");
+		exit(1);
+	}
+
+	/* adjust frequency table to weight low probs REAL low */
+	for (i = 0; i < 26; ++i)
+		stdf[i] = log(stdf[i]) + log(26.0 / 100.0);
+
+	/* zero out observation table */
+	bzero(obs, 26 * sizeof(int));
+
+	if ((nread = read(STDIN_FILENO, inbuf, (size_t)LINELENGTH)) < 0) {
+		(void)fprintf(stderr, "caesar: %s\n", strerror(errno));
+		exit(1);
+	}
+	for (i = nread; i--;) {
+		ch = (unsigned char) inbuf[i];
+		if (isascii(ch)) {
+			if (islower(ch))
+				++obs[ch - 'a'];
+			else if (isupper(ch))
+				++obs[ch - 'A'];
+		}
+	}
+
+	/*
+	 * now "dot" the freqs with the observed letter freqs
+	 * and keep track of best fit
+	 */
+	for (try = winner = 0; try < 26; ++try) { /* += 13) { */
+		dot = 0;
+		for (i = 0; i < 26; i++)
+			dot += obs[i] * stdf[(i + try) % 26];
+		/* initialize winning score */
+		if (try == 0)
+			winnerdot = dot;
+		if (dot > winnerdot) {
+			/* got a new winner! */
+			winner = try;
+			winnerdot = dot;
+		}
+	}
+
+	for (;;) {
+		for (i = 0; i < nread; ++i) {
+			ch = (unsigned char) inbuf[i];
+			putchar(ROTATE(ch, winner));
+		}
+		if (nread < LINELENGTH)
+			break;
+		if ((nread = read(STDIN_FILENO, inbuf, (size_t)LINELENGTH)) < 0) {
+			(void)fprintf(stderr, "caesar: %s\n", strerror(errno));
+			exit(1);
+		}
+	}
+	exit(0);
+}
+
+static void
+printit(char *arg)
+{
+	int ch, rot;
+
+	if ((rot = atoi(arg)) < 0) {
+		(void)fprintf(stderr, "caesar: bad rotation value.\n");
+		exit(1);
+	}
+	while ((ch = getchar()) != EOF)
+		putchar(ROTATE(ch, rot));
+	exit(0);
+}
diff --git a/port/caesar/rot13.sh b/port/caesar/rot13.sh
new file mode 100644
index 00000000..8ce4b94e
--- /dev/null
+++ b/port/caesar/rot13.sh
@@ -0,0 +1,33 @@
+#!/bin/sh -
+#
+# Copyright (c) 1992, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)rot13.sh	8.1 (Berkeley) 5/31/93
+# $FreeBSD: releng/11.2/usr.bin/caesar/rot13.sh 278616 2015-02-12 05:35:00Z cperciva $
+
+exec caesar 13 "$@"
diff --git a/port/cgram/.gitignore b/port/cgram/.gitignore
new file mode 100644
index 00000000..d4f2ec10
--- /dev/null
+++ b/port/cgram/.gitignore
@@ -0,0 +1 @@
+cgram
diff --git a/port/cgram/Makefile b/port/cgram/Makefile
new file mode 100644
index 00000000..02f11eec
--- /dev/null
+++ b/port/cgram/Makefile
@@ -0,0 +1,17 @@
+PREFIX = ~/.local
+MANDIR = ${PREFIX}/share/man
+
+LDLIBS = -lcurses
+
+cgram:
+
+clean:
+	rm -f cgram
+
+install: cgram cgram.6
+	install -d ${PREFIX}/bin ${MANDIR}/man6
+	install cgram ${PREFIX}/bin
+	install -m 644 cgram.6 ${MANDIR}/man6
+
+uninstall:
+	rm -f ${PREFIX}/bin/cgram ${MANDIR}/man6/cgram.6
diff --git a/port/cgram/cgram.6 b/port/cgram/cgram.6
new file mode 100644
index 00000000..9f315804
--- /dev/null
+++ b/port/cgram/cgram.6
@@ -0,0 +1,65 @@
+.\" $NetBSD: cgram.6,v 1.2 2013/08/04 07:55:09 wiz Exp $
+.\"
+.\" Copyright (c) 2004, 2013 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by David A. Holland.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd August 3, 2013
+.Dt CGRAM 6
+.Os
+.Sh NAME
+.Nm cgram
+.Nd solve Sunday-paper cryptograms
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+is a curses-based widget for solving Sunday-paper-type cryptograms
+based on substitution ciphers.
+A random cleartext is chosen using
+.Xr fortune 6
+and a random substitution key is generated.
+.Pp
+The ciphertext is displayed.
+Typing a letter changes the key so that the letter under the cursor
+maps to the newly typed letter, and updates the display accordingly.
+Use Emacs-type cursor commands to move around.
+Enter a tilde
+.Pq ~
+to quit.
+Press asterisk
+.Pq *
+to enter an easier mode where correct letters are displayed in
+boldface.
+.Sh SEE ALSO
+.Xr caesar 6
+.Sh HISTORY
+.Nm
+was written circa 2004.
+It was imported into
+.Nx
+in 2013 and first appeared in
+.Nx 7.0 .
diff --git a/port/cgram/cgram.c b/port/cgram/cgram.c
new file mode 100644
index 00000000..76ea55fb
--- /dev/null
+++ b/port/cgram/cgram.c
@@ -0,0 +1,344 @@
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+#include <err.h>
+#include <assert.h>
+#include <curses.h>
+#include "pathnames.h"
+
+////////////////////////////////////////////////////////////
+
+static char *xstrdup(const char *s) {
+   char *ret;
+
+   ret = malloc(strlen(s) + 1);
+   if (ret == NULL) {
+      errx(1, "Out of memory");
+   }
+   strcpy(ret, s);
+   return ret;
+}
+
+////////////////////////////////////////////////////////////
+
+struct stringarray {
+   char **v;
+   int num;
+};
+
+static void stringarray_init(struct stringarray *a) {
+   a->v = NULL;
+   a->num = 0;
+}
+
+static void stringarray_cleanup(struct stringarray *a) {
+   free(a->v);
+}
+
+static void stringarray_add(struct stringarray *a, const char *s) {
+   a->v = realloc(a->v, (a->num + 1) * sizeof(a->v[0]));
+   if (a->v == NULL) {
+      errx(1, "Out of memory");
+   }
+   a->v[a->num] = xstrdup(s);
+   a->num++;
+}
+
+////////////////////////////////////////////////////////////
+
+static struct stringarray lines;
+static struct stringarray sollines;
+static bool hinting;
+static int scrolldown;
+static unsigned curx;
+static int cury;
+
+static void readquote(void) {
+   FILE *f = popen(_PATH_FORTUNE, "r");
+   if (!f) {
+      err(1, "%s", _PATH_FORTUNE);
+   }
+
+   char buf[128], buf2[8*sizeof(buf)];
+   while (fgets(buf, sizeof(buf), f)) {
+      char *s = strrchr(buf, '\n');
+      assert(s);
+      assert(strlen(s)==1);
+      *s = 0;
+
+      int i,j;
+      for (i=j=0; buf[i]; i++) {
+	 if (buf[i]=='\t') {
+	    buf2[j++] = ' ';
+	    while (j%8) buf2[j++] = ' ';
+	 }
+	 else if (buf[i]=='\b') {
+	    if (j>0) j--;
+	 }
+	 else {
+	    buf2[j++] = buf[i];
+	 }
+      }
+      buf2[j] = 0;
+
+      stringarray_add(&lines, buf2);
+      stringarray_add(&sollines, buf2);
+   }
+
+   pclose(f);
+}
+
+static void encode(void) {
+   int used[26];
+   for (int i=0; i<26; i++) used[i] = 0;
+
+   int key[26];
+   int keypos=0;
+   while (keypos < 26) {
+      int c = random()%26;
+      if (used[c]) continue;
+      key[keypos++] = c;
+      used[c] = 1;
+   }
+
+   for (int y=0; y<lines.num; y++) {
+      for (unsigned x=0; lines.v[y][x]; x++) {
+	 if (islower((unsigned char)lines.v[y][x])) {
+	    int q = lines.v[y][x]-'a';
+	    lines.v[y][x] = 'a'+key[q];
+	 }
+	 if (isupper((unsigned char)lines.v[y][x])) {
+	    int q = lines.v[y][x]-'A';
+	    lines.v[y][x] = 'A'+key[q];
+	 }
+      }
+   }
+}
+
+static int substitute(int ch) {
+   assert(cury>=0 && cury<lines.num);
+   if (curx >= strlen(lines.v[cury])) {
+      beep();
+      return -1;
+   }
+
+   int och = lines.v[cury][curx];
+   if (!isalpha((unsigned char)och)) {
+      beep();
+      return -1;
+   }
+
+   int loch = tolower((unsigned char)och);
+   int uoch = toupper((unsigned char)och);
+   int lch = tolower((unsigned char)ch);
+   int uch = toupper((unsigned char)ch);
+
+   for (int y=0; y<lines.num; y++) {
+      for (unsigned x=0; lines.v[y][x]; x++) {
+	 if (lines.v[y][x]==loch) {
+	    lines.v[y][x] = lch;
+	 }
+	 else if (lines.v[y][x]==uoch) {
+	    lines.v[y][x] = uch;
+	 }
+	 else if (lines.v[y][x]==lch) {
+	    lines.v[y][x] = loch;
+	 }
+	 else if (lines.v[y][x]==uch) {
+	    lines.v[y][x] = uoch;
+	 }
+      }
+   }
+   return 0;
+}
+
+////////////////////////////////////////////////////////////
+
+static void redraw(void) {
+   erase();
+   bool won = true;
+   for (int i=0; i<LINES-1; i++) {
+      move(i, 0);
+      int ln = i+scrolldown;
+      if (ln < lines.num) {
+	 for (unsigned j=0; lines.v[i][j]; j++) {
+	    int ch = lines.v[i][j];
+	    if (ch != sollines.v[i][j] && isalpha((unsigned char)ch)) {
+	       won = false;
+	    }
+	    bool bold=false;
+	    if (hinting && ch==sollines.v[i][j] &&
+		isalpha((unsigned char)ch)) {
+	       bold = true;
+	       attron(A_BOLD);
+	    }
+	    addch(lines.v[i][j]);
+	    if (bold) {
+	       attroff(A_BOLD);
+	    }
+	 }
+      }
+      clrtoeol();
+   }
+
+   move(LINES-1, 0);
+   if (won) {
+      addstr("*solved* ");
+   }
+   addstr("~ to quit, * to cheat, ^pnfb to move");
+
+   move(LINES-1, 0);
+
+   move(cury-scrolldown, curx);
+
+   refresh();
+}
+
+static void opencurses(void) {
+    initscr();
+    cbreak();
+    noecho();
+}
+
+static void closecurses(void) {
+   endwin();
+}
+
+////////////////////////////////////////////////////////////
+
+static void loop(void) {
+   bool done=false;
+   while (!done) {
+      redraw();
+      int ch = getch();
+      switch (ch) {
+       case 1: /* ^A */
+	curx=0;
+	break;
+       case 2: /* ^B */
+	if (curx > 0) {
+	   curx--;
+	}
+	else if (cury > 0) {
+	   cury--;
+	   curx = strlen(lines.v[cury]);
+	}
+	break;
+       case 5: /* ^E */
+	curx = strlen(lines.v[cury]);
+	break;
+       case 6: /* ^F */
+	if (curx < strlen(lines.v[cury])) {
+	   curx++;
+	}
+	else if (cury < lines.num - 1) {
+	   cury++;
+	   curx = 0;
+	}
+	break;
+       case 12: /* ^L */
+	clear();
+	break;
+       case 14: /* ^N */
+	if (cury < lines.num-1) {
+	   cury++;
+	}
+	if (curx > strlen(lines.v[cury])) {
+	   curx =  strlen(lines.v[cury]);
+	}
+	if (scrolldown < cury - (LINES-2)) {
+	   scrolldown = cury - (LINES-2);
+	}
+	break;
+       case 16: /* ^P */
+	if (cury > 0) {
+	   cury--;
+	}
+	if (curx > strlen(lines.v[cury])) {
+	   curx = strlen(lines.v[cury]);
+	}
+	if (scrolldown > cury) {
+	   scrolldown = cury;
+	}
+	break;
+       case '*':
+	hinting = !hinting;
+	break;
+       case '~':
+	done = true;
+	break;
+       default:
+	if (isalpha(ch)) {
+	   if (!substitute(ch)) {
+	      if (curx < strlen(lines.v[cury])) {
+		 curx++;
+	      }
+	      if (curx==strlen(lines.v[cury]) && cury < lines.num-1) {
+		 curx=0;
+		 cury++;
+	      }
+	   }
+	}
+	else if (curx < strlen(lines.v[cury]) && ch==lines.v[cury][curx]) {
+	   curx++;
+	   if (curx==strlen(lines.v[cury]) && cury < lines.num-1) {
+	      curx=0;
+	      cury++;
+	   }
+	}
+	else {
+	   beep();
+	}
+	break;
+      }
+   }
+}
+
+////////////////////////////////////////////////////////////
+
+int main(void) {
+   stringarray_init(&lines);
+   stringarray_init(&sollines);
+   srandom(time(NULL));
+   readquote();
+   encode();
+   opencurses();
+
+   loop();
+
+   closecurses();
+   stringarray_cleanup(&sollines);
+   stringarray_cleanup(&lines);
+   return 0;
+}
diff --git a/port/cgram/pathnames.h b/port/cgram/pathnames.h
new file mode 100644
index 00000000..40db1eed
--- /dev/null
+++ b/port/cgram/pathnames.h
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _PATH_FORTUNE "fortune"
diff --git a/port/ddate/.gitignore b/port/ddate/.gitignore
new file mode 100644
index 00000000..eaa8a5fd
--- /dev/null
+++ b/port/ddate/.gitignore
@@ -0,0 +1 @@
+ddate
diff --git a/port/ddate/Makefile b/port/ddate/Makefile
new file mode 100644
index 00000000..c1bc4553
--- /dev/null
+++ b/port/ddate/Makefile
@@ -0,0 +1,15 @@
+PREFIX ?= ~/.local
+MANDIR ?= ${PREFIX}/share/man
+
+ddate:
+
+clean:
+	rm -f ddate
+
+install: ddate ddate.1
+	install -d ${PREFIX}/bin ${MANDIR}/man1
+	install ddate ${PREFIX}/bin
+	install -m 644 ddate.1 ${MANDIR}/man1
+
+uninstall:
+	rm -f ${PREFIX}/bin/ddate ${MANDIR}/man1/ddate.1
diff --git a/port/ddate/ddate.1 b/port/ddate/ddate.1
new file mode 100644
index 00000000..c340578f
--- /dev/null
+++ b/port/ddate/ddate.1
@@ -0,0 +1,117 @@
+.\" All Rites Reversed.  This file is in the PUBLIC DOMAIN.
+.\" Kallisti.
+.TH DDATE 1 "Bureaucracy 3161" "ddate" "Emperor Norton User Command"
+.SH NAME
+ddate \- convert Gregorian dates to Discordian dates
+.SH SYNOPSIS
+.B ddate
+.RI [ \fB+\fPformat]
+.RI [ date ]
+.SH DESCRIPTION
+.B ddate
+prints the date in Discordian date format.
+.PP
+If called with no arguments,
+.B ddate
+will get the current system date, convert this to the Discordian
+date format and print this on the standard output. Alternatively, a
+Gregorian date may be specified on the command line, in the form of a numerical
+day, month and year.
+.PP
+If a format string is specified, the Discordian date will be printed in
+a format specified by the string. This mechanism works similarly to the
+format string mechanism of
+.B date(1),
+only almost completely differently. The fields are:
+.IP %A
+Full name of the day of the week (i.e., Sweetmorn)
+.IP %a
+Abbreviated name of the day of the week (i.e., SM)
+.IP %B
+Full name of the season (i.e., Chaos)
+.IP %b
+Abbreviated name of the season (i.e., Chs)
+.IP %d
+Cardinal number of day in season (i.e., 23)
+.IP %e
+Ordinal number of day in season (i.e., 23rd)
+.IP %H
+Name of current Holyday, if any
+.IP %N
+Magic code to prevent rest of format from being printed unless today is
+a Holyday.
+.IP %n
+Newline
+.IP %t
+Tab
+.IP %X
+Number of days remaining until X-Day. (Not valid if the SubGenius options
+are not compiled in.)
+.IP %Y
+The year of our Lady Discord (i.e., 3182)
+.IP %{
+.IP %}
+Used to enclose the part of the string which is to be replaced with the
+words "St. Tib's Day" if the current day is St. Tib's Day.
+.IP %\.
+Try it and see.
+.bp
+.SH EXAMPLES
+.nf
+% ddate
+.br
+Sweetmorn, Bureaucracy 42, 3161 YOLD
+.PP
+% ddate +'Today is %{%A, the %e of %B%}, %Y. %N%nCelebrate %H'
+.br
+Today is Sweetmorn, the 42nd of Bureaucracy, 3161.
+.PP
+% ddate +"It's %{%A, the %e of %B%}, %Y. %N%nCelebrate %H" 26 9 1995
+.br
+It's Prickle-Prickle, the 50th of Bureaucracy, 3161.
+.br
+Celebrate Bureflux
+.PP
+% ddate +"Today's %{%A, the %e of %B%}, %Y. %N%nCelebrate %H" 29 2 1996
+.br
+Today's St. Tib's Day, 3162.
+.br
+
+.SH BUGS
+
+.B ddate(1)
+will produce undefined behavior if asked to produce the date for St. Tib's
+day and its format string does not contain the St. Tib's Day delimiters
+%{ and %}.
+
+.SH NOTE
+
+After `X-Day' passed without incident, the Church of the SubGenius
+declared that it had got the year upside down - X-Day is actually in 8661 AD
+rather than 1998 AD.  Thus, the True X-Day is Cfn 40, 9827.
+
+.SH AUTHOR
+.nh
+Original program by Druel the Chaotic aka Jeremy Johnson (mpython@gnu.ai.mit.edu)
+.br
+Major rewrite by Lee H:. O:. Smith, KYTP, aka Andrew Bulhak (acb@dev.null.org)
+.br
+Gregorian B.C.E. dates fixed by Chaplain Nyan the Wiser, aka Dan Dart (ntw@dandart.co.uk)
+.br
+Five tons of flax.
+
+.SH DISTRIBUTION POLICY
+
+Public domain. All rites reversed.
+
+.SH SEE ALSO
+
+date(1),
+.br
+http://www.subgenius.com/
+.br
+Malaclypse the Younger,
+.I "Principia Discordia, Or How I Found Goddess And What I Did To Her When I Found Her"
+
+.SH AVAILABILITY
+The ddate command is available from https://github.com/bo0ts/ddate.
diff --git a/port/ddate/ddate.c b/port/ddate/ddate.c
new file mode 100644
index 00000000..c0a6bf37
--- /dev/null
+++ b/port/ddate/ddate.c
@@ -0,0 +1,399 @@
+/* $ DVCS ID: $jer|,523/lhos,KYTP!41023161\b"?" <<= DO NOT DELETE! */
+
+/* ddate.c .. converts boring normal dates to fun Discordian Date -><-
+   written  the 65th day of The Aftermath in the Year of Our Lady of 
+   Discord 3157 by Druel the Chaotic aka Jeremy Johnson aka
+   mpython@gnu.ai.mit.edu  
+      28 Sever St Apt #3
+      Worcester MA 01609
+
+   and I'm not responsible if this program messes anything up (except your 
+   mind, I'm responsible for that)
+
+   (k) YOLD 3161 and all time before and after.
+   Reprint, reuse, and recycle what you wish.
+   This program is in the public domain.  Distribute freely.  Or not.
+
+   Majorly hacked, extended and bogotified/debogotified on 
+   Sweetmorn, Bureaucracy 42, 3161 YOLD, by Lee H:. O:. Smith, KYTP, 
+   aka Andrew Bulhak, aka acb@dev.null.org
+
+   Slightly hackled and crackled by a sweet firey stove on
+   Boomtime, the 53rd day of Bureaucracy in the YOLD 3179,
+   by Chaplain Nyan the Wiser, aka Dan Dart, aka ntw@dandart.co.uk
+
+   and I'm not responsible if this program messes anything up (except your 
+   mind, I'm responsible for that) (and that goes for me as well --lhos)
+
+   Version history:
+   Bureflux 3161:      First release of enhanced ddate with format strings
+   59 Bcy, 3161:       PRAISE_BOB and KILL_BOB options split, other minor
+                       changes.
+   53 Bcy, 3179:       Fixed gregorian date conversions less than YOLD 1167
+
+   1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL>
+   - added Native Language Support
+
+   2000-03-17 Burt Holzman <holzman+ddate@gmail.com>
+   - added range checks for dates
+
+   2014-06-07 William Woodruff <william@tuffbizz.com>
+   - removed gettext dependent locale code
+
+   15th of Confusion, 3180:
+   - call out adherents of the wrong fruit
+
+   FIVE TONS OF FLAX
+*/
+
+/* configuration options  VVVVV   READ THIS!!! */
+
+/* If you wish ddate(1) to print the date in the same format as Druel's 
+ * original ddate when called in immediate mode, define OLD_IMMEDIATE_FMT 
+ */
+
+#define OLD_IMMEDIATE_FMT
+
+/* If you wish to use the US format for aneristic dates (m-d-y), as opposed to
+ * the Commonwealth format, define US_FORMAT.
+ */
+
+/* #define US_FORMAT */
+
+/* If you are ideologically, theologically or otherwise opposed to the 
+ * Church of the SubGenius and do not wish your copy of ddate(1) to contain
+ * code for counting down to X-Day, undefine KILL_BOB */
+
+#define KILL_BOB 13013
+
+/* If you wish ddate(1) to contain SubGenius slogans, define PRAISE_BOB */
+
+/*#define PRAISE_BOB 13013*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdio.h>
+
+
+// work around includes and defines from formerly c.h
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
+#endif
+
+/* &a[0] degrades to a pointer: a different type from an array */
+# define __must_be_array(a) \
+	BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(__typeof__(a), __typeof__(&a[0])))
+
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+
+/* work around hacks for standalone package */
+#define PACKAGE "ddate"
+#define PACKAGE_STRING "Stand Alone"
+
+#ifndef __GNUC__
+#define inline /* foo */
+#endif
+
+#ifdef KILL_BOB
+int xday_countdown(int yday, int year);
+#endif
+
+
+/* string constants */
+
+char *day_long[5] = { 
+    "Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange"
+};
+
+char *day_short[5] = {"SM","BT","PD","PP","SO"};
+
+char *season_long[5] = { 
+    "Chaos", "Discord", "Confusion", "Bureaucracy", "The Aftermath"
+};
+
+char *season_short[5] = {"Chs", "Dsc", "Cfn", "Bcy", "Afm"};
+
+char *holyday[5][2] = { 
+    { "Mungday", "Chaoflux" },
+    { "Mojoday", "Discoflux" },
+    { "Syaday",  "Confuflux" },
+    { "Zaraday", "Bureflux" },
+    { "Maladay", "Afflux" }
+};
+
+struct disc_time {
+    int season; /* 0-4 */
+    int day; /* 0-72 */
+    int yday; /* 0-365 */
+    int year; /* 3066- */
+};
+
+char *excl[] = {
+    "Hail Eris!", "All Hail Discordia!", "Kallisti!", "Fnord.", "Or not.",
+    "Wibble.", "Pzat!", "P'tang!", "Frink!",
+#ifdef PRAISE_BOB
+    "Slack!", "Praise \"Bob\"!", "Or kill me.",
+#endif /* PRAISE_BOB */
+    /* randomness, from the Net and other places. Feel free to add (after
+       checking with the relevant authorities, of course). */
+    "Grudnuk demand sustenance!", "Keep the Lasagna flying!",
+    "You are what you see.",
+    "Or is it?", "This statement is false.",
+    "Lies and slander, sire!", "Hee hee hee!",
+#if defined(linux) || defined (__linux__) || defined (__linux)
+    "Hail Eris, Hack Linux!",
+#elif defined(__APPLE__)
+    "This Fruit is not the True Fruit of Discord.",
+#endif
+    ""
+};
+
+char default_fmt[] = "%{%A, %B %d%}, %Y YOLD";
+char *default_immediate_fmt=
+#ifdef OLD_IMMEDIATE_FMT
+"Today is %{%A, the %e day of %B%} in the YOLD %Y%N%nCelebrate %H"
+#else
+default_fmt
+#endif
+;
+
+#define DY(y) (y+1166)
+
+static inline char *ending(int i) {
+	return i/10==1?"th":(i%10==1?"st":(i%10==2?"nd":(i%10==3?"rd":"th")));
+}
+
+static inline int leapp(int i) {
+	return (!(DY(i)%4))&&((DY(i)%100)||(!(DY(i)%400)));
+}
+
+/* select a random string */
+static inline char *sel(char **strings, int num) {
+	return(strings[random()%num]);
+}
+
+void print(struct disc_time,char **); /* old */
+void format(char *buf, const char* fmt, struct disc_time dt);
+/* read a fortune file */
+int load_fortunes(char *fn, char *delim, char** result);
+
+struct disc_time convert(int,int);
+struct disc_time makeday(int,int,int);
+
+int
+main (int argc, char *argv[]) {
+    time_t t;
+    struct tm *eris;
+    int bob,raw;
+    struct disc_time hastur;
+    char schwa[23*17], *fnord=0;
+    int pi;
+    char *progname, *p;
+
+    progname = argv[0];
+    if ((p = strrchr(progname, '/')) != NULL)
+	progname = p+1;
+
+    srandom(time(NULL));
+    /* do args here */
+    for(pi=1; pi<argc; pi++) {
+	switch(argv[pi][0]) {
+	case '+': fnord=argv[pi]+1; break;
+	case '-': 
+	    switch(argv[pi][1]) {
+	    case 'V':
+		printf(("%s (%s)\n"), progname, PACKAGE_STRING);
+	    default: goto usage;
+	    }
+	default: goto thud;
+	}
+    }
+
+  thud:
+    if (argc-pi==3){ 
+	int moe=atoi(argv[pi]), larry=atoi(argv[pi+1]), curly=atoi(argv[pi+2]);
+	hastur=makeday(
+#ifdef US_FORMAT
+	    moe,larry,
+#else
+	    larry,moe,
+#endif
+	    curly);
+	if (hastur.season == -1) {
+		printf("Invalid date -- out of range\n");
+		return -1;
+	}
+	fnord=fnord?fnord:default_fmt;
+    } else if (argc!=pi) { 
+      usage:
+	fprintf(stderr,("usage: %s [+format] [day month year]\n"), argv[0]);
+	exit(1);
+    } else {
+	t= time(NULL);
+	eris=localtime(&t);
+	bob=eris->tm_yday; /* days since Jan 1. */
+	raw=eris->tm_year; /* years since 1980 */
+	hastur=convert(bob,raw);
+	fnord=fnord?fnord:default_immediate_fmt;
+    }
+    format(schwa, fnord, hastur);
+    printf("%s\n", schwa);
+   
+    return 0;
+}
+
+void format(char *buf, const char* fmt, struct disc_time dt)
+{
+    int tib_start=-1, tib_end=0;
+    int i, fmtlen=strlen(fmt);
+    char *bufptr=buf;
+
+/*    fprintf(stderr, "format(%p, \"%s\", dt)\n", buf, fmt);*/
+
+    /* first, find extents of St. Tib's Day area, if defined */
+    for(i=0; i<fmtlen; i++) {
+	if(fmt[i]=='%') {
+	    switch(fmt[i+1]) {
+	    case 'A':
+	    case 'a':
+	    case 'd':
+	    case 'e':
+		if(tib_start>0)	    tib_end=i+1;
+		else		    tib_start=i;
+		break;
+	    case '{': tib_start=i; break;
+	    case '}': tib_end=i+1; break;
+	    }
+	}
+    }
+
+    /* now do the formatting */
+    buf[0]=0;
+
+    for(i=0; i<fmtlen; i++) {
+	if((i==tib_start) && (dt.day==-1)) {
+	    /* handle St. Tib's Day */
+	    strcpy(bufptr, ("St. Tib's Day"));
+	    bufptr += strlen(bufptr);
+	    i=tib_end;
+	} else {
+	    if(fmt[i]=='%') {
+		char *wibble=0, snarf[23];
+		switch(fmt[++i]) {
+		case 'A': wibble=day_long[dt.yday%5]; break;
+		case 'a': wibble=day_short[dt.yday%5]; break;
+		case 'B': wibble=season_long[dt.season]; break;
+		case 'b': wibble=season_short[dt.season]; break;
+		case 'd': sprintf(snarf, "%d", dt.day+1); wibble=snarf; break;
+		case 'e': sprintf(snarf, "%d%s", dt.day+1, ending(dt.day+1)); 
+		    wibble=snarf; break;
+		case 'H': if(dt.day==4||dt.day==49)
+		    wibble=holyday[dt.season][dt.day==49]; break;
+		case 'N': if(dt.day!=4&&dt.day!=49) goto eschaton; break;
+		case 'n': *(bufptr++)='\n'; break;
+		case 't': *(bufptr++)='\t'; break;
+
+		case 'Y': sprintf(snarf, "%d", dt.year); wibble=snarf; break;
+		case '.': wibble=sel(excl, ARRAY_SIZE(excl));
+		    break;
+#ifdef KILL_BOB
+		case 'X': sprintf(snarf, "%d", 
+				  xday_countdown(dt.yday, dt.year));
+				  wibble = snarf; break;
+#endif /* KILL_BOB */
+		}
+		if(wibble) {
+/*		    fprintf(stderr, "wibble = (%s)\n", wibble);*/
+		    strcpy(bufptr, wibble); bufptr+=strlen(wibble);
+		}
+	    } else {
+		*(bufptr++) = fmt[i];
+	    }
+	}
+    }
+  eschaton:
+    *(bufptr)=0;
+}
+
+struct disc_time makeday(int imonth,int iday,int iyear) /*i for input */
+{ 
+    struct disc_time funkychickens;
+    
+    int cal[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
+    int dayspast=0;
+
+    memset(&funkychickens,0,sizeof(funkychickens));
+    /* basic range checks */
+    if (imonth < 1 || imonth > 12 || iyear == 0) {
+	    funkychickens.season = -1;
+	    return funkychickens;
+    }
+    if (iday < 1 || iday > cal[imonth-1]) {
+	    if (!(imonth == 2 && iday == 29 && iyear%4 == 0 &&
+		  (iyear%100 != 0 || iyear%400 == 0))) {
+		    funkychickens.season = -1;
+		    return funkychickens;
+	    }
+    }
+    
+    imonth--;
+    /*  note: gregorian year 0 doesn't exist so
+     *  add one if user specifies a year less than 0 */
+    funkychickens.year= iyear+1166 + ((0 > iyear)?1:0);
+    while(imonth>0) { dayspast+=cal[--imonth]; }
+    funkychickens.day=dayspast+iday-1;
+    funkychickens.season=0;
+    if((funkychickens.year%4)==2) {
+	if (funkychickens.day==59 && iday==29)  funkychickens.day=-1;
+    }
+    funkychickens.yday=funkychickens.day;
+/*               note: EQUAL SIGN...hopefully that fixes it */
+    while(funkychickens.day>=73) {
+	funkychickens.season++;
+	funkychickens.day-=73;
+    }
+    return funkychickens;
+}
+
+struct disc_time convert(int nday, int nyear)
+{  struct disc_time funkychickens;
+   
+   funkychickens.year = nyear+3066;
+   funkychickens.day=nday;
+   funkychickens.season=0;
+   if ((funkychickens.year%4)==2)
+     {if (funkychickens.day==59)
+	funkychickens.day=-1;
+     else if (funkychickens.day >59)
+       funkychickens.day-=1;
+    }
+   funkychickens.yday=funkychickens.day;
+   while (funkychickens.day>=73)
+     { funkychickens.season++;
+       funkychickens.day-=73;
+     }
+   return funkychickens;
+  
+ }
+
+#ifdef KILL_BOB
+
+/* Code for counting down to X-Day, X-Day being Cfn 40, 3164 
+ *
+ * After `X-Day' passed without incident, the CoSG declared that it had 
+ * got the year upside down --- X-Day is actually in 8661 AD rather than 
+ * 1998 AD.
+ *
+ * Thus, the True X-Day is Cfn 40, 9827.
+ *
+ */
+
+int xday_countdown(int yday, int year) {
+    int r=(185-yday)+(((yday<59)&&(leapp(year)))?1:0);
+    while(year<9827) r+=(leapp(++year)?366:365);
+    while(year>9827) r-=(leapp(year--)?366:365);
+    return r;
+}
+
+#endif
diff --git a/port/file2c/.gitignore b/port/file2c/.gitignore
new file mode 100644
index 00000000..aafb358f
--- /dev/null
+++ b/port/file2c/.gitignore
@@ -0,0 +1 @@
+file2c
diff --git a/port/file2c/Makefile b/port/file2c/Makefile
new file mode 100644
index 00000000..09f6b5d0
--- /dev/null
+++ b/port/file2c/Makefile
@@ -0,0 +1,15 @@
+PREFIX = ~/.local
+MANDIR = ${PREFIX}/share/man
+
+file2c:
+
+clean:
+	rm -f file2c
+
+install: file2c file2c.1
+	install -d ${PREFIX}/bin ${MANDIR}/man1
+	install file2c ${PREFIX}/bin
+	install -m 644 file2c.1 ${MANDIR}/man1
+
+uninstall:
+	rm -f ${PREFIX}/bin/file2c ${MANDIR}/man1/file2c.1
diff --git a/port/file2c/file2c.1 b/port/file2c/file2c.1
new file mode 100644
index 00000000..fe1fe5e7
--- /dev/null
+++ b/port/file2c/file2c.1
@@ -0,0 +1,75 @@
+.\"----------------------------------------------------------------------------
+.\" "THE BEER-WARE LICENSE" (Revision 42):
+.\" <phk@FreeBSD.org> wrote this file.  As long as you retain this notice, you
+.\" can do whatever you want with this file. If we meet some day, and you think
+.\" this stuff is worth it, you can buy me a beer in return.  Poul-Henning Kamp
+.\" ---------------------------------------------------------------------------
+.\"
+.\" $FreeBSD: releng/11.2/usr.bin/file2c/file2c.1 173197 2007-10-30 17:49:00Z ru $
+.\"
+.Dd March 22, 2007
+.Dt FILE2C 1
+.Os
+.Sh NAME
+.Nm file2c
+.Nd convert file to c-source
+.Sh SYNOPSIS
+.Nm
+.Op Fl sx
+.Op Fl n Ar count
+.Op Ar prefix Op Ar suffix
+.Sh DESCRIPTION
+The
+.Nm
+utility reads a file from stdin and writes it to stdout, converting each
+byte to its decimal or hexadecimal representation on the fly.
+The byte values are separated by a comma.
+This also means that the last byte value is not followed by a comma.
+By default the byte values are printed in decimal, but when the
+.Fl x
+option is given, the values will be printed in hexadecimal.
+When
+.Fl s
+option is given, each line is printed with a leading tab and each comma is
+followed by a space except for the last one on the line.
+.Pp
+If more than 70 characters are printed on the same line, that line is
+ended and the output continues on the next line.
+With the
+.Fl n
+option this can be made to happen after the specified number of
+byte values have been printed.
+The length of the line will not be considered anymore.
+To have all the byte values printed on the same line, give the
+.Fl n
+option a negative number.
+.Pp
+A prefix and suffix strings can be printed before and after the byte values
+(resp.)
+If a suffix is to be printed, a prefix must also be specified.
+The first non-option word is the prefix, which may optionally be followed
+by a word that is to be used as the suffix.
+.Pp
+This program is typically used to embed binary files into C source files.
+The prefix is used to define an array type and the suffix is used to end
+the C statement.
+The
+.Fl n , s
+and
+.Fl x
+options are useful when the binary data represents a bitmap and the output
+needs to remain readable and/or editable.
+Fonts, for example, are a good example of this.
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+date | file2c 'const char date[] = {' ',0};'
+.Ed
+.Pp
+will produce:
+.Bd -literal -offset indent
+const char date[] = {
+83,97,116,32,74,97,110,32,50,56,32,49,54,58,50,56,58,48,53,
+32,80,83,84,32,49,57,57,53,10
+,0};
+.Ed
diff --git a/port/file2c/file2c.c b/port/file2c/file2c.c
new file mode 100644
index 00000000..cff7f602
--- /dev/null
+++ b/port/file2c/file2c.c
@@ -0,0 +1,92 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ */
+
+#include <sys/cdefs.h>
+//__FBSDID("$FreeBSD: releng/11.2/usr.bin/file2c/file2c.c 200462 2009-12-13 03:14:06Z delphij $");
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void
+usage(void)
+{
+
+	fprintf(stderr, "usage: %s [-sx] [-n count] [prefix [suffix]]\n",
+	    "file2c");
+	exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int c, count, linepos, maxcount, pretty, radix;
+
+	maxcount = 0;
+	pretty = 0;
+	radix = 10;
+	while ((c = getopt(argc, argv, "n:sx")) != -1) {
+		switch (c) {
+		case 'n':	/* Max. number of bytes per line. */
+			maxcount = strtol(optarg, NULL, 10);
+			break;
+		case 's':	/* Be more style(9) comliant. */
+			pretty = 1;
+			break;
+		case 'x':	/* Print hexadecimal numbers. */
+			radix = 16;
+			break;
+		case '?':
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc > 0)
+		printf("%s\n", argv[0]);
+	count = linepos = 0;
+	while((c = getchar()) != EOF) {
+		if (count) {
+			putchar(',');
+			linepos++;
+		}
+		if ((maxcount == 0 && linepos > 70) ||
+		    (maxcount > 0 && count >= maxcount)) {
+			putchar('\n');
+			count = linepos = 0;
+		}
+		if (pretty) {
+			if (count) {
+				putchar(' ');
+				linepos++;
+			} else {
+				putchar('\t');
+				linepos += 8;
+			}
+		}
+		switch (radix) {
+		case 10:
+			linepos += printf("%d", c);
+			break;
+		case 16:
+			linepos += printf("0x%02x", c);
+			break;
+		default:
+			abort();
+		}
+		count++;
+	}
+	putchar('\n');
+	if (argc > 1)
+		printf("%s\n", argv[1]);
+	return (0);
+}
diff --git a/port/wcwidth/.gitignore b/port/wcwidth/.gitignore
new file mode 100644
index 00000000..132e8098
--- /dev/null
+++ b/port/wcwidth/.gitignore
@@ -0,0 +1,3 @@
+*.o
+libwcwidth.dylib
+wcfix
diff --git a/port/wcwidth/COPYRIGHT b/port/wcwidth/COPYRIGHT
new file mode 100644
index 00000000..e6472371
--- /dev/null
+++ b/port/wcwidth/COPYRIGHT
@@ -0,0 +1,190 @@
+musl as a whole is licensed under the following standard MIT license:
+
+----------------------------------------------------------------------
+Copyright © 2005-2020 Rich Felker, et al.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+----------------------------------------------------------------------
+
+Authors/contributors include:
+
+A. Wilcox
+Ada Worcester
+Alex Dowad
+Alex Suykov
+Alexander Monakov
+Andre McCurdy
+Andrew Kelley
+Anthony G. Basile
+Aric Belsito
+Arvid Picciani
+Bartosz Brachaczek
+Benjamin Peterson
+Bobby Bingham
+Boris Brezillon
+Brent Cook
+Chris Spiegel
+Clément Vasseur
+Daniel Micay
+Daniel Sabogal
+Daurnimator
+David Carlier
+David Edelsohn
+Denys Vlasenko
+Dmitry Ivanov
+Dmitry V. Levin
+Drew DeVault
+Emil Renner Berthing
+Fangrui Song
+Felix Fietkau
+Felix Janda
+Gianluca Anzolin
+Hauke Mehrtens
+He X
+Hiltjo Posthuma
+Isaac Dunham
+Jaydeep Patil
+Jens Gustedt
+Jeremy Huntwork
+Jo-Philipp Wich
+Joakim Sindholt
+John Spencer
+Julien Ramseier
+Justin Cormack
+Kaarle Ritvanen
+Khem Raj
+Kylie McClain
+Leah Neukirchen
+Luca Barbato
+Luka Perkov
+M Farkas-Dyck (Strake)
+Mahesh Bodapati
+Markus Wichmann
+Masanori Ogino
+Michael Clark
+Michael Forney
+Mikhail Kremnyov
+Natanael Copa
+Nicholas J. Kain
+orc
+Pascal Cuoq
+Patrick Oppenlander
+Petr Hosek
+Petr Skocik
+Pierre Carrier
+Reini Urban
+Rich Felker
+Richard Pennington
+Ryan Fairfax
+Samuel Holland
+Segev Finer
+Shiz
+sin
+Solar Designer
+Stefan Kristiansson
+Stefan O'Rear
+Szabolcs Nagy
+Timo Teräs
+Trutz Behn
+Valentin Ochs
+Will Dietz
+William Haddon
+William Pitcock
+
+Portions of this software are derived from third-party works licensed
+under terms compatible with the above MIT license:
+
+The TRE regular expression implementation (src/regex/reg* and
+src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed
+under a 2-clause BSD license (license text in the source files). The
+included version has been heavily modified by Rich Felker in 2012, in
+the interests of size, simplicity, and namespace cleanliness.
+
+Much of the math library code (src/math/* and src/complex/*) is
+Copyright © 1993,2004 Sun Microsystems or
+Copyright © 2003-2011 David Schultz or
+Copyright © 2003-2009 Steven G. Kargl or
+Copyright © 2003-2009 Bruce D. Evans or
+Copyright © 2008 Stephen L. Moshier or
+Copyright © 2017-2018 Arm Limited
+and labelled as such in comments in the individual source files. All
+have been licensed under extremely permissive terms.
+
+The ARM memcpy code (src/string/arm/memcpy_el.S) is Copyright © 2008
+The Android Open Source Project and is licensed under a two-clause BSD
+license. It was taken from Bionic libc, used on Android.
+
+The implementation of DES for crypt (src/crypt/crypt_des.c) is
+Copyright © 1994 David Burren. It is licensed under a BSD license.
+
+The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was
+originally written by Solar Designer and placed into the public
+domain. The code also comes with a fallback permissive license for use
+in jurisdictions that may not recognize the public domain.
+
+The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011
+Valentin Ochs and is licensed under an MIT-style license.
+
+The x86_64 port was written by Nicholas J. Kain and is licensed under
+the standard MIT terms.
+
+The mips and microblaze ports were originally written by Richard
+Pennington for use in the ellcc project. The original code was adapted
+by Rich Felker for build system and code conventions during upstream
+integration. It is licensed under the standard MIT terms.
+
+The mips64 port was contributed by Imagination Technologies and is
+licensed under the standard MIT terms.
+
+The powerpc port was also originally written by Richard Pennington,
+and later supplemented and integrated by John Spencer. It is licensed
+under the standard MIT terms.
+
+All other files which have no copyright comments are original works
+produced specifically for use as part of this library, written either
+by Rich Felker, the main author of the library, or by one or more
+contibutors listed above. Details on authorship of individual files
+can be found in the git version control history of the project. The
+omission of copyright and license comments in each file is in the
+interest of source tree size.
+
+In addition, permission is hereby granted for all public header files
+(include/* and arch/*/bits/*) and crt files intended to be linked into
+applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit
+the copyright notice and permission notice otherwise required by the
+license, and to use these files without any requirement of
+attribution. These files include substantial contributions from:
+
+Bobby Bingham
+John Spencer
+Nicholas J. Kain
+Rich Felker
+Richard Pennington
+Stefan Kristiansson
+Szabolcs Nagy
+
+all of whom have explicitly granted such permission.
+
+This file previously contained text expressing a belief that most of
+the files covered by the above exception were sufficiently trivial not
+to be subject to copyright, resulting in confusion over whether it
+negated the permissions granted in the license. In the spirit of
+permissive licensing, and of not having licensing issues being an
+obstacle to adoption, that text has been removed.
diff --git a/port/wcwidth/Makefile b/port/wcwidth/Makefile
new file mode 100644
index 00000000..50faa653
--- /dev/null
+++ b/port/wcwidth/Makefile
@@ -0,0 +1,27 @@
+PREFIX ?= ~/.local
+
+OBJS = wcwidth.o wcswidth.o
+
+all: libwcwidth.dylib wcfix
+
+libwcwidth.dylib: ${OBJS}
+	${CC} -dynamiclib ${LDFLAGS} ${OBJS} -o $@
+
+wcwidth.o: nonspacing.h wide.h
+
+.SUFFIXES: .in
+
+.in:
+	sed 's|%%PREFIX%%|${PREFIX}|g' $< > $@
+	chmod a+x $@
+
+clean:
+	rm -f libwcwidth.dylib wcfix ${OBJS}
+
+install: libwcwidth.dylib wcfix
+	install -d ${PREFIX}/lib ${PREFIX}/bin
+	install -m 644 libwcwidth.dylib ${PREFIX}/lib
+	install wcfix ${PREFIX}/bin
+
+uninstall:
+	rm -f ${PREFIX}/lib/libwcwidth.dylib ${PREFIX}/bin/wcfix
diff --git a/port/wcwidth/nonspacing.h b/port/wcwidth/nonspacing.h
new file mode 100644
index 00000000..5d05a3d1
--- /dev/null
+++ b/port/wcwidth/nonspacing.h
@@ -0,0 +1,89 @@
+16,16,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,16,16,32,16,16,16,33,34,35,
+36,37,38,39,16,16,40,16,16,16,16,16,16,16,16,16,16,16,41,42,16,16,43,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,44,16,45,46,47,48,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,49,16,16,50,
+51,16,52,53,54,16,16,16,16,16,16,55,16,16,56,16,57,58,59,60,61,62,63,64,65,66,
+67,68,16,69,70,71,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,72,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,73,74,16,16,16,75,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,76,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,77,78,16,16,16,16,16,16,16,79,16,16,16,16,16,80,81,82,16,16,16,16,16,83,
+84,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,248,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,254,255,255,255,255,191,182,0,0,0,0,0,0,0,63,0,255,23,0,0,0,0,0,248,255,
+255,0,0,1,0,0,0,0,0,0,0,0,0,0,0,192,191,159,61,0,0,0,128,2,0,0,0,255,255,255,
+7,0,0,0,0,0,0,0,0,0,0,192,255,1,0,0,0,0,0,0,248,15,32,0,0,192,251,239,62,0,0,
+0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,255,255,255,255,
+255,7,0,0,0,0,0,0,20,254,33,254,0,12,0,0,0,2,0,0,0,0,0,0,16,30,32,0,0,12,0,0,
+64,6,0,0,0,0,0,0,16,134,57,2,0,0,0,35,0,6,0,0,0,0,0,0,16,190,33,0,0,12,0,0,
+252,2,0,0,0,0,0,0,144,30,32,64,0,12,0,0,0,4,0,0,0,0,0,0,0,1,32,0,0,0,0,0,0,17,
+0,0,0,0,0,0,192,193,61,96,0,12,0,0,0,2,0,0,0,0,0,0,144,64,48,0,0,12,0,0,0,3,0,
+0,0,0,0,0,24,30,32,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,4,92,0,0,0,0,0,0,0,0,0,0,0,
+242,7,128,127,0,0,0,0,0,0,0,0,0,0,0,0,242,31,0,63,0,0,0,0,0,0,0,0,0,3,0,0,160,
+2,0,0,0,0,0,0,254,127,223,224,255,254,255,255,255,31,64,0,0,0,0,0,0,0,0,0,0,0,
+0,224,253,102,0,0,0,195,1,0,30,0,100,32,0,32,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0,
+0,0,28,0,0,0,12,0,0,0,12,0,0,0,0,0,0,0,176,63,64,254,15,32,0,0,0,0,0,120,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,1,4,14,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,9,0,0,0,0,0,0,64,127,
+229,31,248,159,0,0,0,0,0,0,255,127,0,0,0,0,0,0,0,0,15,0,0,0,0,0,208,23,4,0,0,
+0,0,248,15,0,3,0,0,0,60,59,0,0,0,0,0,0,64,163,3,0,0,0,0,0,0,240,207,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,253,33,16,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,
+251,0,248,0,0,0,124,0,0,0,0,0,0,223,255,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,
+255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,128,247,63,0,0,0,192,0,0,0,0,0,0,0,0,0,0,3,0,68,8,0,0,96,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,255,255,3,128,0,0,0,0,192,63,0,0,128,255,3,0,
+0,0,0,0,7,0,0,0,0,0,200,51,0,0,0,0,32,0,0,0,0,0,0,0,0,126,102,0,8,16,0,0,0,0,
+0,16,0,0,0,0,0,0,157,193,2,0,0,0,0,48,64,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,0,0,0,0,0,64,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,255,255,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,110,240,0,0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,
+0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,192,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,255,
+127,0,0,0,0,0,0,128,3,0,0,0,0,0,120,38,0,32,0,0,0,0,0,0,7,0,0,0,128,239,31,0,
+0,0,0,0,0,0,8,0,3,0,0,0,0,0,192,127,0,30,0,0,0,0,0,0,0,0,0,0,0,128,211,64,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,248,7,0,0,3,0,0,0,0,0,0,24,1,0,0,0,192,
+31,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,92,0,0,64,0,0,0,0,0,
+0,0,0,0,0,248,133,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,60,176,1,0,0,48,0,0,0,
+0,0,0,0,0,0,0,248,167,1,0,0,0,0,0,0,0,0,0,0,0,0,40,191,0,0,0,0,0,0,0,0,0,0,0,
+0,224,188,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+128,255,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,12,1,0,0,0,254,7,0,0,0,0,248,121,128,0,
+126,14,0,0,0,0,0,252,127,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,191,0,0,0,
+0,0,0,0,0,0,0,252,255,255,252,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,180,191,0,
+0,0,0,0,0,0,0,0,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,
+0,0,0,0,0,0,0,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,0,0,0,0,0,127,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,
+0,128,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,15,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,248,255,231,15,0,0,0,60,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,255,255,255,255,255,255,127,248,255,255,255,255,255,31,32,0,16,0,0,248,
+254,255,0,0,0,0,0,0,0,0,0,
+0,127,255,255,249,219,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,240,7,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,
diff --git a/port/wcwidth/wcfix.in b/port/wcwidth/wcfix.in
new file mode 100644
index 00000000..832c83d6
--- /dev/null
+++ b/port/wcwidth/wcfix.in
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -eu
+
+export DYLD_FORCE_FLAT_NAMESPACE=1
+export DYLD_INSERT_LIBRARIES=%%PREFIX%%/lib/libwcwidth.dylib
+
+exec "$@"
diff --git a/port/wcwidth/wcswidth.c b/port/wcwidth/wcswidth.c
new file mode 100644
index 00000000..5c8a5a4d
--- /dev/null
+++ b/port/wcwidth/wcswidth.c
@@ -0,0 +1,8 @@
+#include <wchar.h>
+
+int wcswidth(const wchar_t *wcs, size_t n)
+{
+	int l=0, k=0;
+	for (; n-- && *wcs && (k = wcwidth(*wcs)) >= 0; l+=k, wcs++);
+	return (k < 0) ? k : l;
+}
diff --git a/port/wcwidth/wcwidth.c b/port/wcwidth/wcwidth.c
new file mode 100644
index 00000000..36256a53
--- /dev/null
+++ b/port/wcwidth/wcwidth.c
@@ -0,0 +1,29 @@
+#include <wchar.h>
+
+static const unsigned char table[] = {
+#include "nonspacing.h"
+};
+
+static const unsigned char wtable[] = {
+#include "wide.h"
+};
+
+int wcwidth(wchar_t wc)
+{
+	if (wc < 0xffU)
+		return (wc+1 & 0x7f) >= 0x21 ? 1 : wc ? -1 : 0;
+	if ((wc & 0xfffeffffU) < 0xfffe) {
+		if ((table[table[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1)
+			return 0;
+		if ((wtable[wtable[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1)
+			return 2;
+		return 1;
+	}
+	if ((wc & 0xfffe) == 0xfffe)
+		return -1;
+	if (wc-0x20000U < 0x20000)
+		return 2;
+	if (wc == 0xe0001 || wc-0xe0020U < 0x5f || wc-0xe0100U < 0xef)
+		return 0;
+	return 1;
+}
diff --git a/port/wcwidth/wide.h b/port/wcwidth/wide.h
new file mode 100644
index 00000000..e403c9a5
--- /dev/null
+++ b/port/wcwidth/wide.h
@@ -0,0 +1,65 @@
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,18,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,19,16,20,21,22,16,16,16,23,16,16,24,25,26,27,28,17,
+17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,29,
+17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,17,17,30,16,16,16,16,31,16,16,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,17,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,16,16,16,33,
+34,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,35,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,36,17,17,37,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,38,39,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,40,41,42,43,44,45,46,47,16,48,49,16,16,16,16,
+16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,6,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,48,0,0,0,0,0,0,255,15,0,0,0,0,128,0,0,8,
+0,2,12,0,96,48,64,16,0,0,4,44,36,32,12,0,0,0,1,0,0,0,80,184,0,0,0,0,0,0,0,224,
+0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,251,255,255,255,255,255,255,255,
+255,255,255,15,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,63,0,0,0,255,15,255,255,255,255,
+255,255,255,127,254,255,255,255,255,255,255,255,255,255,127,254,255,255,255,
+255,255,255,255,255,255,255,255,255,224,255,255,255,255,255,254,255,255,255,
+255,255,255,255,255,255,255,127,255,255,255,255,255,7,255,255,255,255,15,0,
+255,255,255,255,255,127,255,255,255,255,255,0,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,
+0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,31,255,255,255,255,255,255,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,
+255,255,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,15,0,0,0,0,0,0,0,0,0,0,0,0,0,255,3,0,0,255,255,255,255,247,255,127,15,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,255,255,255,255,255,255,255,
+255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,7,0,255,255,255,127,0,0,0,0,0,
+0,7,0,240,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+15,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,64,254,7,0,0,0,0,0,0,0,0,0,0,0,0,7,0,255,255,255,
+255,255,15,255,1,3,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+1,224,191,255,255,255,255,255,255,255,255,223,255,255,15,0,255,255,255,255,
+255,135,15,0,255,255,17,255,255,255,255,255,255,255,255,127,253,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+159,255,255,255,255,255,255,255,63,0,120,255,255,255,0,0,4,0,0,96,0,16,0,0,0,
+0,0,0,0,0,0,0,248,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,
+255,255,255,255,255,255,63,16,39,0,0,24,240,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,255,15,0,
+0,0,224,255,255,255,255,255,255,255,255,255,255,255,255,123,252,255,255,255,
+255,231,199,255,255,255,231,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,15,7,7,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,
diff --git a/prune.sh b/prune.sh
new file mode 100644
index 00000000..fabe865f
--- /dev/null
+++ b/prune.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -eu
+
+find -L ~/.config ~/.local -type l -lname "${PWD}/*" | while read -r link; do
+	echo "$link"
+	rm "$link"
+done
diff --git a/txt/.notemap b/txt/.notemap
new file mode 100644
index 00000000..acfe44ab
--- /dev/null
+++ b/txt/.notemap
@@ -0,0 +1,3 @@
+5347bbff-b124-432a-a67e-5d90fa34d0d4 books.txt
+9efa92ef-a8c0-4fa6-8a2a-13315992162b music.txt
+2d3c58f2-a193-4ff5-b79f-fd430f80eb64 shows.txt
diff --git a/txt/books.txt b/txt/books.txt
new file mode 100644
index 00000000..44b3ad66
--- /dev/null
+++ b/txt/books.txt
@@ -0,0 +1,129 @@
+[ 2021 ]
+
+ 12. ★★☆ Margaret Atwood, The Year of the Flood
+ 11. ★★☆ Charlie Jane Anders, All the Birds in the Sky
+ 10. ★☆☆ Margaret Atwood, The Penelopiad
+  9. ★★★ Alix E. Harrow, The Once and Future Witches
+  8. ★★☆ Mary Robinette Kowal, The Relentless Moon
+  7. ★☆☆ Connie Willis, Bellwether
+  6. ★★☆ Carmen Maria Machado, Her Body and Other Parties
+  5. ★★☆ N. K. Jemisin, The City We Became
+  4. ★★★ Martha Wells, Network Effect
+  3. ★★☆ Nnedi Okorafor, Binti
+  2. ★★☆ Kai Cheng Thom, I Hope We Choose Love
+  1. ★★☆ Tade Thompson, The Rosewater Redemption
+
+[ 2020 ]
+
+ 19. ★★★ N. K. Jemisin, The Awakened Kingdom
+ 18. ★★☆ N. K. Jemisin, The Kingdom of Gods
+ 17. ★★☆ N. K. Jemisin, The Broken Kingdoms
+ 16. ★★☆ Ann Leckie, Ancillary Justice
+ 15. ★★☆ Madeline Miller, The Song of Achilles
+ 14. ★★☆ N. K. Jemisin, The Hundred Thousand Kingdoms
+ 13. ★★☆ Annalee Newitz, Autonomous
+ 12. ★☆☆ Tade Thompson, The Rosewater Insurrection
+ 11. ★★☆ Alix E. Harrow, The Ten Thousand Doors of January
+ 10. ★★☆ N. K. Jemisin, The Stone Sky
+  9. ★★★ Kai Cheng Thom, Fierce Femmes and Notorious Liars
+  8. ★☆☆ Amal El-Mohtar & Max Gladstone, This Is How You Lose the Time War
+  7. ★★★ N. K. Jemisin, The Obelisk Gate
+  6. ★★★ Becky Chambers, To Be Taught, If Fortunate
+  5. ★★★ Annalee Newitz, The Future of Another Timeline
+  4. ★★☆ Rivers Solomon, The Deep
+  3. ★★☆ ed. Hope Nicholson, Love Beyond Body, Space & Time
+  2. ★★☆ Tade Thompson, Rosewater
+  1. ★★★ Meg Elison, The Book of Flora
+
+[ 2019 ]
+
+ 42. ★★★ Meg Elison, The Book of Etta
+ 41. ★★☆ Martha Wells, Exit Strategy
+ 40. ★★☆ Martha Wells, Artificial Condition
+ 39. ★★☆ N. K. Jemisin, The Fifth Season
+ 38. ★★☆ Martha Wells, Rogue Protocol
+ 37. ★★☆ Yoon Ha Lee, Ninefox Gambit
+ 36. ★★★ Rebecca Makkai, The Great Believers
+ 35. ★★☆ Meg Elison, The Book of the Unnamed Midwife
+ 34. ★★☆ Martha Wells, All Systems Red
+ 33. ★★☆ Nnedi Okorafor, The Book of Phoenix
+ 32. ★★☆ JY Yang, The Red Threads of Fortune
+ 31. ★★☆ JY Yang, The Black Tides of Heaven
+ 30. ★☆☆ Rebecca Roanhorse, Trail of Lightning
+ 29. ★☆☆ Jo Walton, The Just City
+ 28. ★★★ Arkady Martine, A Memory Called Empire
+ 27. ★★☆ Mary Robinette Kowal, The Fated Sky
+ 26. ★★★ Becky Chambers, Record of a Spaceborn Few
+ 25. ★★☆ Mary Robinette Kowal, The Calculating Stars
+ 24. ★★☆ Octavia E. Butler, Imago
+ 23. ★★☆ Octavia E. Butler, Kindred
+ 22. ★★☆ Octavia E. Butler, Adulthood Rites
+ 21. ★★★ Octavia E. Butler, Wild Seed
+ 20. ★★☆ Octavia E. Butler, Parable of the Talents
+ 19. ★★☆ Jeff VanderMeer, Acceptance
+ 18. ★★★ Becky Chambers, A Closed and Common Orbit
+ 17. ★★☆ Octavia E. Butler, Dawn
+ 16. ★★☆ Jeff VanderMeer, Authority
+ 15. ★★☆ Octavia E. Butler, Parable of the Sower
+ 14. ★★☆ C. A. Higgins, Lightless
+ 13. ★☆☆ Alfred Bester, The Demolished Man
+ 12. ★★★ Karin Tidbeck, Amatka
+ 11. ★☆☆ Catherynne M. Valente, Space Opera
+ 10. ★★☆ Rivers Solomon, An Unkindness of Ghosts
+  9. ★★★ Becky Chambers, The Long Way to a Small Angry Planet
+  8. ★★☆ Emily St. John Mandel, Station Eleven
+  7. ★★★ Douglas Adams, Dirk Gently's Holistic Detective Agency
+  6. ★★★ Octavia E. Butler, Fledgling
+  5. ★★★ Jeff VanderMeer, Annihilation
+  4. ★★☆ Alfred Bester, The Stars My Destination
+  3. ★☆☆ Samuel R. Delany, The Einstein Intersection
+  2. ★★★ Octavia E. Butler, Mind of My Mind
+  1. ★☆☆ Robert A. Heinlein, Waldo & Magic, Inc.
+
+[ 2018 ]
+
+ 25. ★★★ Douglas Adams, The Long Dark Tea-Time of the Soul
+ 24. ★★☆ Khaled Hosseini, A Thousand Splendid Suns
+ 23. ★★☆ Nathan Altice, I Am Error
+ 22. ★★★ Ruth Ozeki, A Tale for the Time Being
+ 21. ★☆☆ Arthur C. Clarke, Earthlight
+ 20. ★★★ Terry Pratchett & Neil Gaiman, Good Omens
+ 19. ★☆☆ Robert A. Heinlein, Starship Troopers
+ 18. ★★☆ Liu Cixin, The Three-Body Problem
+ 17. ★★☆ Robert A. Heinlein, Revolt in 2100
+ 16. ★★☆ ed. Robert A. Heinlein, Tomorrow, the Stars
+ 15. ★★☆ Robert A. Heinlein, Assignment in Eternity
+ 14. ★☆☆ Robert A. Heinlein, The Rolling Stones
+ 13. ★★☆ Robert A. Heinlein, Starman Jones
+ 12. ★☆☆ Robert A. Heinlein, Space Cadet
+ 11. ★☆☆ Robert A. Heinlein, Farmer in the Sky
+ 10. ★★☆ Robert A. Heinlein, Between Planets
+  9. ★★☆ Robert A. Heinlein, Red Planet
+  8. ★☆☆ Robert A. Heinlein, Rocket Ship Galileo
+  7. ★☆☆ Robert A. Heinlein, Sixth Column
+  6. ★☆☆ Robert A. Heinlein, Beyond This Horizon
+  5. ★★☆ Robert A. Heinlein, Orphans of the Sky
+  4. ★★☆ Tracy Kidder, The Soul of a New Machine
+  3. ★★★ James Alan Gardner, Ascending
+  2. ★★★ Anne Frank, The Diary of a Young Girl
+  1. ★★★ Harper Lee, Go Set a Watchman
+
+[ 2017 ]
+
+  1. ★★★ James Alan Gardner, Expendable
+
+[ ... ]
+
+ • ★★★ Harper Lee, To Kill a Mockingbird
+ • ★★★ Douglas Adams, The Hitchhiker's Guide to the Galaxy
+ • ★★★ Douglas Adams, The Restaurant at the End of the Universe
+ • ★★☆ Douglas Adams, Life, the Universe and Everything
+ • ★★★ Douglas Adams, So Long, and Thanks for All the Fish
+ • ★★☆ Douglas Adams, Mostly Harmless
+ • ★☆☆ Eoin Colfer, And Another Thing...
+ • ★★★ Margaret Atwood, The Handmaid's Tale
+ • ★★☆ Kazuo Ishiguro, Never Let Me Go
+ • ★★★ Philip Pullman, The Goldan Compass
+ • ★★★ Philip Pullman, The Subtle Knife
+ • ★★★ Philip Pullman, The Amber Spyglass
+ • ★★★ William Goldman, The Princess Bride
diff --git a/txt/music.txt b/txt/music.txt
new file mode 100644
index 00000000..cd3705c4
--- /dev/null
+++ b/txt/music.txt
@@ -0,0 +1,285 @@
+Feist — I Feel It All
+<https://youtu.be/g-1Gb2rxzlk>
+
+The Blow — "Come On Petunia"
+<https://youtu.be/MO1HSfzK1Ns>
+
+Black Country, New Road — Sunglasses
+<https://youtu.be/UAZhzi9cpkc>
+
+Mitski, Xiu Xiu — Between the Breaths
+<https://youtu.be/HnhTkFl5Imw>
+
+Black Dresses — CREEP U
+<https://youtu.be/w9RSZmltcVI>
+
+BACKXWASH — DONT COME TO THE WOODS
+<https://youtu.be/7ZcJsWyGbOw>
+
+William Bonney — See Ya Later
+<https://youtu.be/-uCUlvUlr9s>
+
+Four Tet — Hands
+<https://youtu.be/ojZoQbRt1tc>
+
+Buffy Sainte-Marie — Darling Don't Cry
+
+LINGUA IGNOTA — DO YOU DOUBT ME TRAITOR
+<https://youtu.be/M1ZweG__q-w>
+
+Lizzo — Truth Hurts
+<https://youtu.be/P00HMxdsVZI>
+
+Hobo Johnson and The Lovemakers — Tiny Desk Concert
+<https://youtu.be/A8a2EosJIbM>
+
+Poppy — Concrete
+<https://youtu.be/WwoGhpYdebQ>
+
+Kim Petras — Do Me
+<https://youtu.be/LShK0Yhd964>
+
+Kim Petras — Heart to Break
+<https://youtu.be/5CPeHQHAQyo>
+
+Anomie — Avorter n'est pas tuer
+<https://youtu.be/W1iGXRDeZf4>
+
+Zhaoze — Birds Contending
+<https://zhaoze.bandcamp.com/album/birds-contending>
+
+Colin Stetson — Reborn
+<https://youtu.be/MVnSFj6XQZY>
+
+Holly Herndon — Frontier
+<https://youtu.be/rvNqNgHAEys>
+
+Bleachers — Tiny Desk Concert
+<https://youtu.be/QCtkkX2f18M>
+
+Girlpool — Tiny Desk Concert
+<https://youtu.be/VNM8Tg9pvDU>
+
+Daughters — You Won't Get What You Want
+<https://daughters.bandcamp.com/album/you-wont-get-what-you-want>
+
+La Dispute — FULTON STREET I
+<https://ladispute.bandcamp.com/track/fulton-street-i>
+
+KASHIWA Daisuke — Stella
+<https://youtu.be/ei7cdynwRMA>
+
+Jeff Wayne — The Eve of the War
+<https://youtu.be/6YwFvmnbj3E>
+
+Julia Holter — I Shall Love 2
+<https://youtu.be/k5uwPaCvbhA>
+
+Chromatics — Running Up That Hill
+<https://youtu.be/Mgv88ZLi6LY>
+
+Low — Double Negative
+<https://lowtheband.bandcamp.com/album/double-negative>
+
+Rival Consoles — Helios
+<https://youtu.be/T8n-XC_2a-k>
+
+Tiffany — I Think We're Alone Now
+<https://youtu.be/w6Q3mHyzn78>
+
+Pulp — Common People
+<https://youtu.be/yuTMWgOduFM>
+
+XTC — Making Plans for Nigel
+<https://youtu.be/gZjZBCZWxpg>
+
+New Order — Temptation
+<https://youtu.be/xxDv_RTdLQo>
+
+Street Sects — In for a World of Hurt
+<https://streetsects.bandcamp.com/track/in-for-a-world-of-hurt>
+
+New Order — Temptation
+<https://youtu.be/xxDv_RTdLQo>
+
+Blondie — Heart of Glass
+<https://youtu.be/fWPhhlKHM80>
+
+Suuns — Watch You, Watch Me
+<https://suuns.bandcamp.com/track/watch-you-watch-me>
+
+Neckbeard Deathcamp — White Nationalism is for Basement Dwelling Losers
+<https://neckbearddeathcamp.bandcamp.com/album/white-nationalism-is-for-basement-dwelling-losers>
+
+rook — shed blood
+<https://rooksfeather.bandcamp.com/album/shed-blood>
+
+SOPHIE — OIL OF EVERY PEARL'S UN-INSIDES
+<http://smarturl.it/SOPHIEALBUM>
+
+Rosetta — Utopioid
+<https://theanaesthete.bandcamp.com/album/utopioid>
+
+Desire — Under Your Spell
+<https://youtu.be/9K7rmxjk5RQ>
+
+Broken Social Scene — Anthems for a Seventeen-Year Old Girl
+<https://youtu.be/DDqNL0js0iU>
+
+Shinsei Kamattechan — Yuugure no tori
+<https://youtu.be/sUW4dDWiz-A>
+
+Petite Meller — The Flute
+<https://youtu.be/BLwgeV7dXOI>
+
+King Gizzard and the Lizard Wizard — Flying Microtonal Banana
+<https://youtu.be/D0BsgJxw208>
+
+rook&nomie — DAYDREAM
+<https://youtu.be/00TdaTffFeY>
+
+Spiritualized — Ladies and Gentlemen We Are Floating in Space
+<https://youtu.be/p47V3w4m1yg>
+
+Street Sects — End Position
+<https://streetsects.bandcamp.com/album/end-position-2>
+
+Converge — The Dusk in Us
+<https://convergecult.bandcamp.com/album/the-dusk-in-us>
+
+St. Vincent — MASSEDUCTION
+
+Godspeed You! Black Emperor — Undoing a Luciferian Towers
+<https://godspeedyoublackemperor.bandcamp.com/track/undoing-a-luciferian-towers>
+
+Florist — Tiny Desk Concert
+<https://youtu.be/WbyyxIZ02Zs>
+
+CHVRCHES — Tiny Desk Concert
+<https://youtu.be/haunJARHPm4>
+
+Aurora — Tiny Desk Concert
+<https://youtu.be/evBgLWQwAFA>
+
+Joy Division — Atmosphere
+<https://youtu.be/1EdUjlawLJM>
+
+Underworld — Born Slippy
+<https://youtu.be/iTFrCbQGyvM>
+
+Converge — I Can Tell You About Pain
+<https://convergecult.bandcamp.com/album/i-can-tell-you-about-pain>
+
+CocoRosie — Lost Girls
+<https://youtu.be/aRa-SlftLQo>
+
+FAUVE — Blizzard
+<https://youtu.be/HMpmedi_pH4>
+
+FAUVE — Nuits Fauves
+<https://youtu.be/cwaAppsy5yo>
+
+Sarin — House of Leaves
+<https://sarin.bandcamp.com/track/house-of-leaves-split-w-guiltfeeder>
+
+Chromatics — Shadow
+<https://youtu.be/IGUboLZx3Tk>
+
+Arcade Fire — Creature Comfort
+<https://youtu.be/xzwicesJQ7E>
+
+Arcade Fire — Everything Now
+<https://youtu.be/zC30BYR3CUk>
+
+Ed Schrader's Music Beat — Sermon
+
+Jessica Moss — Pools of Light
+
+Death Grips — Steroids (Crouching Tiger Hidden Gabber Megamix)
+<https://youtu.be/JUTKTk60aGk>
+
+Saltland — A Common Truth
+
+Colin Stetson — All This I Do for Glory
+<https://colinstetson.bandcamp.com/album/all-this-i-do-for-glory>
+
+Xiu Xiu — Forget
+<https://youtu.be/ywRzfwA75pY>
+
+Arca — Arca
+
+Joni Void — Dissociation (Kyla's Song)
+<http://cstrecords.com/cst125/>
+
+Do Make Say Think — Bound and Boundless
+<http://cstrecords.com/cst120/>
+
+The Body Lovers / The Body Haters
+
+Unwound — Leaves Turn Inside You
+
+Xiu Xiu — Fabulous Muscles
+
+Xiu Xiu — A Promise
+
+Saltland — Light of Mercy
+<http://cstrecords.com/saltland-releases-new-single-light-of-mercy/>
+
+Jessica Moss — Glaciers I (Pt I)
+<http://cstrecords.com/cst124/>
+
+BNNY RBBT — Big World
+<http://www.bnnyrbbt.fans>
+
+Woman is the Earth — Depths
+<https://womanistheearth.bandcamp.com/album/depths>
+
+Saltland — I Only Wish This For You
+<http://cstrecords.com/cst123/>
+
+Sun Kil Moon — Benji
+<https://youtu.be/UtndQzCUEY4>
+
+Neil Cicierega — Mouth Moods
+<http://www.neilcic.com/mouthmoods/>
+
+Those Who Walk Away — "First Degraded Rhythm" + "First Partially Recollected Conversation"
+<http://cstrecords.com/cst122/>
+
+Dan Smith — Some Tunes
+<https://thedancemyth.bandcamp.com/album/some-tunes>
+
+Avec le soleil sortant de sa bouche — Pas pire pop, I Love You So Much
+
+Arcade Fire — I Give You Power
+<https://youtu.be/f6jma9VQEls>
+
+Xiu Xiu — Jenny GoGo
+<https://youtu.be/WMT6MsA3ut8>
+
+Kero Kero Bonito — Fish Bowl
+<https://youtu.be/FY-CjOJCjJE>
+
+Avec le soleil sortant de sa bouche — Alizé et Margaret D. Midi moins le quart. Sur la plage, un palmier ensanglanté
+<http://cstrecords.com/cst121/>
+
+G.L.O.S.S. — Trans Day of Revenge
+<https://girlslivingoutsidesocietysshit.bandcamp.com/releases>
+
+Joy Division — Love Will Tear Us Apart
+<https://youtu.be/zuuObGsB0No>
+
+Xiu Xiu — Honeysuckle
+<https://youtu.be/hYKGR8Er4vM>
+
+Xiu Xiu — Ceremony
+<https://youtu.be/95ms8A2XJY0>
+
+Xiu Xiu — I Luv the Valley Oh
+<https://youtu.be/dztURk0_DOg>
+
+Porter Robinson & Madeon — Shelter
+<https://youtu.be/fzQ6gRAEoy0>
+
+Julien Baker — Tiny Desk Concert
+<https://youtu.be/tADWPTqR_4A>
diff --git a/txt/plan.7 b/txt/plan.7
new file mode 100644
index 00000000..f861d395
--- /dev/null
+++ b/txt/plan.7
@@ -0,0 +1,26 @@
+.Dd March 10, 2021
+.Dt PLAN 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm plan
+.Nd possible future projects
+.
+.Sh DESCRIPTION
+.
+.Ss feed reader for IMAP
+.Bl -bullet -compact
+.It
+why use a separate reader
+when you already have a mail client?
+.It
+feeds go into IMAP mailboxes
+.It
+feed entries turn into internet messages
+.It
+read/unread state like normal mail
+.It
+reply or forward feed entries as email
+.It
+all state in IMAP, easy to cron
+.El
diff --git a/txt/shows.txt b/txt/shows.txt
new file mode 100644
index 00000000..62e6a071
--- /dev/null
+++ b/txt/shows.txt
@@ -0,0 +1,32 @@
+2020-01-23 (La Sala Rossa) Secondsight, BIG|BRAVE
+2019-12-10 (Casa del Popolo) meth, Street Sects
+2019-06-22 (Casa del Popolo) Persons Unknown, Palissade, Police des Moeurs, Blu Anxiety
+2019-06-22 (Bar Le Ritz) Cloakroom, Pelican
+2019-06-21 (La Vitrola) Leash Aggression, CPU Rave, Vitex, Pinocchio
+2019-06-20 (Casa del Popolo) Beep Test, Liar//Lier, Wetware
+2019-06-18 (Casa del Popolo) Nomadic War Machine; it foot, it ears; Wendy Eisenberg
+2019-06-17 (La Sala Rossa) SaskPWR, Lubomyr Melnyk, Architek Percussion (Nicole Lizée)
+2019-06-08 (La Sotterenea) Eliza Kavtion, Beep Test, Dri Hiev
+2019-05-03 (Turbo Haüs) Issfenn
+2019-04-21 (Bar Le Ritz PDB) Claud, Hatchie, Girlpool
+2018-11-14 (Turbo Haus) Juss, Street Sects
+2018-07-26 (Theatre Fairmount) Uniform, Deafheaven
+2018-06-14 (Casa del Popolo) Markus Floats, Mark Lowe, Carodiaro, Eliza Kavtion
+2018-04-26 (Bar Le Ritz PDB) Strega, Show of Bedlam, Rosetta
+2018-04-13 (La Vitrola) Eliza Kavtion, Lungbutter, Wrekmeister Harmonies
+2018-03-08 (Brasserie Beaubien) Clayborne, Nightwitches, Aseethe, Vile Creature
+2018-02-08 (Bar Le Ritz PDB) KGD, Efrim Manuel Menuck
+2017-09-30 (La Sala Rossa) Truster, Lungbutter, Big Brave
+2017-09-19 (La Vitrola) Saccharine, Xiu Xiu
+2017-06-01 (Casa del Popolo) Genevieve Heistek, Saltland
+2017-05-27 (l’Escogriffe) Sough, Ikaray, C H R I S T
+2017-05-14 (Bar Le Ritz PDB) Mutter’d, Aim Low, Wrekmeister Harmonies
+2016-09-25 (La Sala Rossa) Cloud Rat, Wolves in the Throne Room
+2016-09-21 (Theatre Paradoxe) Godspeed You! Black Emperor
+2016-04-08 (La Sala Rossa) Caro Diaro, Wreckage With Stick, Ought
+2015-08-13 (La Vitrola) Gutser, Chemical Way, Saul Hittner, In the Name of Havoc
+2015-06-10 (La Sala Rossa) Ryan Sawyer, Colin Stetson & Sarah Neufeld
+2015-06-09 (La Sala Rossa) The Mile End Ladies String Auxiliary, Christof Migone
+2015-06-08 (Casa del Popolo) C H R I S T, Jessica Moss, Big Brave
+2014-01-19 (Metropolis) Godspeed You! Black Emperor
+2014-01-18 (l’Olympia) Elf Power, Neutral Milk Hotel
diff --git a/txt/trouble-at-jinx-hotel.txt b/txt/trouble-at-jinx-hotel.txt
new file mode 100644
index 00000000..4db28dd6
--- /dev/null
+++ b/txt/trouble-at-jinx-hotel.txt
@@ -0,0 +1,236 @@
+mollasses
+
+...à l'hôtel jinx...
+
+thierry amar; flüffy erskine; norsola johnson; kate lawrence;
+jennifer ménard; mike moya; sam shalabi; scott lregrande chernoff...
+chris brokaw; bruce cawdron; david michael curry; lisa gamble; efrim
+menuck; michel meunier; js truchy; thalia zedek... à l'hôtel jinx.
+
+lucky 13: siren's song. la la la, amerika. Saint Christopher's
+blues.  sign of judgment. Lynn Canyon wedding song. coda. You Can't
+Win. trouble in mind. las niñas. Miss Peach's Pawnshop. buffaloed
+at Wounded Knee/the weeping winds. no love lost. songs from the
+basement.
+
+trouble at jinx hotel. these cursed recordings began in late winter
+2003 at the hotel2tango in montréal. it was a bitter sunday in
+march. the world was on the brink of war in old mesopotamia, and
+we were all of us sick with the influenza. headaches. fevers. wrecked
+guts and water on the lungs. coughing like crazy. everyone with a
+base case of the shakes.  the hotel wass cold and its ghosts were
+unhappy. electricity was being weird. lights flickered and all the
+microphones crackled with live current. little blue sparks dancing
+through the studio rooms. amplifiers were picking up strange radio
+frequencies and the piano, which couldn't hold a tune to save its
+own life, was behaving like a cantankerous old man. our cats and
+dogs were nervous and excited, a-jitter the way animals get whenever
+catastrope looms. we started with a 16-track tape machine, three
+spools of 1/4" analog audio tape, and a lucky thirteen little songs
+hanging in our heads. but when the 16-track busted and broke down
+a few days later, we moved to 2" tape on a 24-track machine and
+began anew with our fingers crossed. then the 24-track showed the
+first symptoms of its own terrible illness. so we did what we could
+with digital audio tape while our trusty friend and technician,
+garfield lamb, nursed the equipment back to some semblance of health.
+then we transfered the digital recordings to 2" tape and went to
+work again...  waiting to see what the gods would do next. they
+toyed with us for a while before unleashing their full fury: the
+24-track, seizing mid-song one evening, ate a reel of tape and sent
+it spewing around the studio in little pieces, a scene as cruel as
+confetti at a funeral.  garfield somehow managed to splice the tape
+together again. but our nerves were now in tatters too, scattered
+all over the hotel floor. when we finally finished tracking this
+music in the summer of 2003, the cats and dogs didn't look any less
+worried than when we'd begun. of course, they sensed the disaster
+we would soon see for ourselves: as we started mixing the very next
+night, a faulty lamp at the studio short-circuited and the tape
+machine caught on fire. a freak electrical accident. there was an
+asterisk of light; then a dragon-tail of smoke coiling across the
+room; then there was a darkness; and then the 24-track quietly died.
+the animals, cowering in corners, whined, *why didn't you fucking
+fools jump ship at the first sign of the storm*? that's when we
+drifted away from the jinxed hotel... floating until we found safe
+harbour in a studio we could borrow for a few days - one whose own
+resident dogs, zoro and ciska, greeted us with idiot grins and the
+contented looks of drunken sailors on calm seas. mixing was finished
+in a kind of delirious distress at MixArt in notre-dame-de-grace,
+montréal, autumn 2003. these lucky thirteen. dieu merci. slgc. *new
+year's eve* 2003.
+
+siren's song. instrumental.
+
+la la la, amerika. amerika is crawling with cops tonight as we put
+our fists through the windows of the world i know you're broken and
+you're down at the mouth but you're pouting like a punished little
+girl when darkness calls night falls oh mother where are the children
+you've orphaned all over the Earth we're frightened and sickened
+and poverty-stricken and waiting for you to send word when darkness
+calls the sky falls we're tired mother and we're poor we're wretched
+at your teeming shore we're homeless and tossed by the storm looking
+for the light at your golden door when darkness calls night falls.
+
+flüffy: marching drum, percussion & choir; norsola: cello & voice;
+kate: violin; jennifer: voice; moya: Crumar organ; dmc: viola &
+choir; gamble: choir; efrim: choir; js: upright bass; thalia: voice;
+scott: acoustic guitars & vocals; scott recorded the air-raid siren
+and bombs on the nightly news when baghdad was attacked by the
+united states and great britain in march 2003; words & music by
+scott chernoff, as stolen from the inscription on the statue of
+liberty: *give me your tired your poor your huddled masses yearning
+to breathe the wretched refuse of your teeming shore send these the
+homeless tempest-tossed to me i lift my lamp beside the golden
+door*.
+
+Saint Christopher's blues. the locusts are in the jimson Shit Creek
+is on the rise and you're out wrecking eternity this town looks
+like a prison in September's crimson sky when i should be loving
+you perfectly you're such a pretty little mess with your Saint
+Christopher blues and that doomed little look in your eye the weight
+of the world rests all upon your tiny shoes but June why are you
+so terrified give me the locust and the flood i'm a Fool for Love
+but i'm kneeling in Shit Creek tonight.
+
+thierry: upright & electric bass; flüffy: marching drum; kate:
+violin; jennifer: voice; moya: piano & Crumar organ; bruce: drums;
+gramble: scrap metal; scott: electric guitar & vocals. words & music
+by scott chernoff.
+
+sign of judgment. yes sign of judgment yes sign of judgment yes
+sign of judgment time ain't long i don't like old Satan none of his
+tempting charms cheats you at your Jesus now and roll you in his
+arms yes sign of judgment yes sign of judgment yes sign of judgment
+time ain't long i don't like old Satan nothing he say or do tell
+one lie to hurt us all and two to make it true yes sign of judgment
+yes sign of judgment yes sign of judgment time ain't long.
+
+norsola: cello; jennifer: voice; efrim: electric guitar & harmonium;
+js: upright bass; thalia: voice; scott: acoustic guitar & vocals.
+words & music by kid prince moore (traditional).
+
+Lynn Canyon wedding song. well i never cared very much for the west
+except for you there who i loved the best now please take me back
+east i wrote your name in silver and green where Love and Hate Play
+with Fate and History now please take me back east oh please take
+me back east we came to the Canyon across the calm and the wild
+from Mountain to Mountain and Island to aisle i came for Abel you
+came for Cain and we danced on the tables between the cradle and
+the grave and i never cared very much for the west but i will be
+there in my Sunday best if you will wear your red wedding dress
+then please take me back east oh please take me back east.
+
+flüffy: saw; jennifer: voice; moya: piano & Crumar organ; dmc: viola
+& saw; js: upright bass & voice; scott: acoustic guitar & vocals.
+words & music by scott chernoff.
+
+coda. instrumental.
+
+flüffy: metal heating duct; michel: banjo; dog & voice drone recorded
+by julian evans on video tape at Lynn Canyon 2002; mixed by scott
+& harris newman in montréal 2003.
+
+You Can't Win. you with your railroad bones and you with your
+golden-spiked skin you with the wind in your clothes and your hat
+like a house caving in oh man You Can't Win oh man You Can't Win
+oh man You Can't Win when the Angel of Light is at the window with
+the Sanctimonious Kid when you're down and out from Sacramento on
+the New York City skids oh man You Can't Win oh man You Can't Win
+oh man You Can't Win when the East River is groaning all below the
+Williamsburg Bridge when airplanes are exploding in the skyline's
+falling limbs oh man You Can't Win oh man You Can't Win oh man You
+Can't Win when the Swans are at your throat with their busted burning
+wings when they squeal and scream and moan at every last ship that
+comes in oh man You Can't Win oh man You Can't Win oh man You Can't
+Win.
+
+flüffy: chor; norsola: choir; jennifer: voice; moya: electric guitars
+& Crumar organ; sam: oud; bruce: drums & marimba; dmc: choir;
+gramble: scrap metal & choir; efrim: electric guitar & choir; js:
+choir; thalia: choir; scott: electric & acoustic guitars, vocals.
+words & music by scott chernoff, on the road from sacramento to
+nyc. september 11 2001, while reading jack black's 1926 hobo memoir
+*You Can't Win* and listening to the music of michael gira..
+
+trouble in mind. trouble in mind i'm blue but i won't be blue all
+the way you know that sun is gonna shine on me someday i'm gonna
+lay my head on some lonesome railroad line and let that big
+motherfucking engine nullify my mind trouble in mind i'm blue but
+i won't be blue all the way you know that sun is gonna shine on me
+someday.
+
+jennifer: voice; sam: electric guitars; chris: electric guitar;
+scott: acoustic guitar & vocals. words & music traditional, as
+learned from a 1960s  recording by sam lightnin' hopkins.
+
+las niñas. instrumental.
+
+andrew mayrs recorded his baby daughter illimani crying while banging
+on a piano in kitsilano, vancouver 2003; screaming children recorded
+by scott at rue roy, montréal 2003. mixed by scott & harris newman
+in montréal 2003.
+
+Miss Peach's Pawnshop. i bought this cross on the island of St.
+Thomas because i wanted jesus on my chest i got this tattoo in New
+Orleans and this one in the Orient for my honeymoon and i got this
+scar somewhere far far away i was drunk as hell and i tried to walk
+home and my hands were broken and i found this prayer in the back
+of a magazine i know it's ripped but it came like this it came like
+this i bought this song at Miss Peach's Pawn with this old overcoat
+and a gold-plated wristwatch and i found this prayer in the back
+of a magazine i know it's ripped but it came like this it came like
+this it came like this it came like this.
+
+norsola: cello; kate: violin; jennifer: voice; moya: electric guitar,
+piano & Crumar organ; dmc: viola; js: upright bass; scott: acoustic
+guitar & vocals. words by hilary peach & scott chernoff; music by
+scott chernoff. this is a bastard adaptation of peach's poem *Tattoo*
+from her album, *Poems Only Dogs Can Hear*.
+
+buffaloed at Wounded Knee/the weeping winds. instrumental. (for
+l.p. & a.i.m. and a pox on mean-spirited sons of bitches everywhere).
+
+flüffy: saws; dmc: saws.
+
+no love lost. good-bye old friend it's time to go this is the end
+of all that we know when you worship in the house of burden i worship
+in the house of blame as you learn to let your spirit burn i yearn
+to be a flame to be a flame and there's no love no love lost as
+there is the righteous there is the wrong and there is the night
+which is betrayed by the dawn and there's no love no love lost as
+there is the sinner there is the god and there is the singer profaned
+by the song and there's no love no love lost there's no love no
+love lost.
+
+thierry: electric bass; flüffy: bowed piano & saws; jennifer: voice;
+moya: piano; michel: banjo; thalia: voice; scott: acoustic guitar,
+bowed piano & vocals. words & music by scott chernoff.
+
+songs from the basement. there's a song for hatred and a song for
+love a song for betrayal and a song for trust there's a song for
+what's fated a song for dumb luck a song for what's sacred and one
+for what's fucked there's a song which is prayed a song which is
+cussed these songs from the basement which play unto us.
+
+norsola: cello; kate: violin; jennifer: voice; sam: electric guitars;
+bruce: marimba & cymbals; dmc: viola; efrim: harmonium; garfield
+lamb: knife; scott: acoustic guitar & vocals. words & music by scott
+chernoff.  mixed by howard bilerman at the hotel... ...after the
+storm had all blown by...
+
+recorded by howard bilerman, efrim & scott at mom & pop sounds in
+the hotel2tango, mile end, montréal 2003. except where noted,
+everything was mixed by howard bilerman & molasses at MixArt in
+notre-dame-de-grace, montréal 2003. mastered by harris newman in
+little italy, montréal 2003 & 2004. all songs c&p SOCAN 2004, except
+*sign of judgment* & *trouble in mind* which are in the public
+domain. hilary peach's poem *Tattoo*, which inspired *Miss Peach's
+Pawnshop*, is c&p hilary peach 2003. all music arranged by molasses,
+design and hand-lettering by slgc ink. in tokyo. photography by dmc
+& norsola. layout by slgc ink. with assistance by sean o'hara. all
+love to those who helped: sohara & gary at alien8 recordings; howard
+bilerman; cada del popolo; don & ian at constellation; eric craven;
+julian evans; michael feuerstack; aidan girt; lea grahovac; taras
+grescoe; garfield lamb; tim mahony; andrew mayrs, vally mendoza &
+illimani mendoza-mayrs; boris & nicolas at MixArt; harris newman,
+*who is never wrong*; hilary peach; sugar & smokey; and all the
+cars and dogs... xoxo...
diff --git a/txt/tweets.txt b/txt/tweets.txt
new file mode 100644
index 00000000..240351ec
--- /dev/null
+++ b/txt/tweets.txt
@@ -0,0 +1,32 @@
+triangle forum
+https://twitter.com/g0m/status/963890156477603841
+
+damn ya ass fat what's ya pronouns?
+https://twitter.com/msbigmilk/status/1291218055939448833
+
+afab
+https://twitter.com/ErisGael/status/1257524779046907904
+
+chaos emeralds
+https://twitter.com/ErisGael/status/1190147736244490240
+
+might as well salivate
+https://twitter.com/prawn_meat/status/857444039833944064
+
+notifications
+https://twitter.com/WTMMP/status/1259694226595610629
+
+all robot & computers
+https://twitter.com/cryptcrier/status/1238339418160680960
+
+the wet world
+https://twitter.com/TragicAllyHere/status/1137187876507140098
+
+a purchase?
+https://twitter.com/pant_leg/status/1027693563604230144
+
+influencer
+https://twitter.com/witchpuppy/status/974690828257054721
+
+-Wint-conversion
+https://twitter.com/jckarter/status/967802665080995840
diff --git a/www/causal.agency/.gitignore b/www/causal.agency/.gitignore
new file mode 100644
index 00000000..7935a3c1
--- /dev/null
+++ b/www/causal.agency/.gitignore
@@ -0,0 +1,3 @@
+*.html
+scheme.css
+scheme.png
diff --git a/www/causal.agency/Makefile b/www/causal.agency/Makefile
new file mode 100644
index 00000000..95f52120
--- /dev/null
+++ b/www/causal.agency/Makefile
@@ -0,0 +1,20 @@
+WEBROOT = /usr/local/www/causal.agency
+
+FILES = index.html style.css scheme.css scheme.png
+
+all: ${FILES}
+
+index.html: index.7
+	mandoc -T html -O style=style.css index.7 > index.html
+
+scheme.css:
+	scheme -st > scheme.css
+
+scheme.png:
+	scheme -g > scheme.png
+
+install: ${FILES}
+	install -C -m 644 ${FILES} ${WEBROOT}
+
+clean:
+	rm -f index.html scheme.css scheme.png
diff --git a/www/causal.agency/index.7 b/www/causal.agency/index.7
new file mode 100644
index 00000000..489b0b53
--- /dev/null
+++ b/www/causal.agency/index.7
@@ -0,0 +1,70 @@
+.Dd May 19, 2021
+.Dt CAUSAL.AGENCY 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm june
+.Nd computer enthusiast!!
+.
+.Sh SYNOPSIS
+.Nm mail
+.Mt june@causal.agency
+.Nm
+in
+.Li #ascii.town
+on tilde.chat
+.
+.Sh DESCRIPTION
+I make mostly IRC software in C.
+I like
+.Fx
+and
+.Ox
+but also the GPL.
+I want to learn to talk to strangers
+and seek out as much magic as I can.
+.
+.Pp
+.Lk https://git.causal.agency code
+\(em
+.Lk https://text.causal.agency words
+.Pq Lk gopher://text.causal.agency gopher
+\(em
+.Lk /list/ mailist
+\(em
+.Lk https://liberapay.com/june/ liberapay
+.
+.Pp
+These are some things I've done:
+.Bl -tag -width Ds
+.It Lk https://git.causal.agency/pounce/about pounce
+a multi-client-first IRC bouncer
+.It Lk https://git.causal.agency/catgirl/about catgirl
+a cosy IRC client
+.It Lk https://git.causal.agency/litterbox/about litterbox
+a full-text search IRC logger
+.It Lk https://git.causal.agency/scooper/about scooper
+a web interface for litterbox
+.It Lk https://git.causal.agency/catsit/about catsit
+a process supervisor
+.It Lk https://git.causal.agency/imbox/about "imbox & git-fetch-email"
+a tool to pull patches out of IMAP
+.It Lk https://git.causal.agency/bubger/about bubger
+a mailing list archive generator for IMAP
+.It Lk https://git.causal.agency/notemap/about notemap
+a tool to mirror text files to IMAP notes
+.It Lk https://ascii.town/explore.html torus@ascii.town
+a collaborative ASCII art project
+.It Lk ssh://play@ascii.town play@ascii.town
+a 2048 and snake game over
+.Xr ssh 1
+.It Lk https://git.causal.agency/cards/about cards
+a
+.Pa CARDS.DLL
+loader for SDL
+.It Lk scheme.png scheme
+an earthy terminal colour scheme
+.El
+.
+.Sh SEE ALSO
+.Lk /bin/ bin
diff --git a/www/causal.agency/style.css b/www/causal.agency/style.css
new file mode 100644
index 00000000..368d8da1
--- /dev/null
+++ b/www/causal.agency/style.css
@@ -0,0 +1,23 @@
+@import url("scheme.css");
+
+table.head, table.foot { width: 100%; }
+td.head-rtitle, td.foot-os { text-align: right; }
+td.head-vol { text-align: center; }
+div.Pp { margin: 1ex 0ex; }
+div.Nd, div.Bf, div.Op { display: inline; }
+span.Pa, span.Ad { font-style: italic; }
+span.Ms { font-weight: bold; }
+dl.Bl-diag > dt { font-weight: bold; }
+code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn,
+code.Cd { font-weight: bold; font-family: inherit; }
+
+html { font-family: monospace; line-height: 1.25em; }
+body { max-width: 80ch; margin: 1em auto; padding: 0 1ch; }
+table { border-collapse: collapse; }
+table.Nm code.Nm { padding-right: 1ch; }
+table.foot { margin-top: 1em; }
+
+html { background-color: var(--ansi16); color: var(--ansi17); }
+a { color: var(--ansi4); }
+a:visited { color: var(--ansi5); }
+a.permalink { color: var(--ansi3); text-decoration: none; }
diff --git a/www/git.causal.agency/.gitignore b/www/git.causal.agency/.gitignore
new file mode 100644
index 00000000..a0ae074c
--- /dev/null
+++ b/www/git.causal.agency/.gitignore
@@ -0,0 +1,6 @@
+about-filter
+hilex
+htagml
+mtags
+owner-filter
+source-filter
diff --git a/www/git.causal.agency/Makefile b/www/git.causal.agency/Makefile
new file mode 100644
index 00000000..638c21e7
--- /dev/null
+++ b/www/git.causal.agency/Makefile
@@ -0,0 +1,20 @@
+ETC = /usr/local/etc
+WWW = /usr/local/www/cgit
+LIBEXEC = /usr/local/libexec
+
+BIN = ../../bin
+BINS = about-filter source-filter owner-filter hilex htagml mtags
+
+all: ${BINS}
+
+install: cgitrc custom.css ${BINS}
+	install -m 644 cgitrc ${ETC}
+	install -m 644 custom.css ${WWW}
+	install ${BINS} ${LIBEXEC}
+
+hilex htagml mtags::
+	${MAKE} -C ${BIN} $@
+	ln -f ${BIN}/$@ $@
+
+clean:
+	rm -f ${BINS}
diff --git a/www/git.causal.agency/about-filter.sh b/www/git.causal.agency/about-filter.sh
new file mode 100644
index 00000000..2ff645e2
--- /dev/null
+++ b/www/git.causal.agency/about-filter.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+options=fragment,man=%N.%S,includes=../tree/%I
+
+case "$1" in
+	(README.[1-9])
+		exec /usr/bin/mandoc -T html -O $options
+		;;
+	(*.[1-9])
+		exec /usr/bin/mandoc -T html -O $options,toc
+		;;
+	(*)
+		exec /usr/local/libexec/hilex -l text -f html -o pre
+		;;
+esac
diff --git a/www/git.causal.agency/cgit/.gitignore b/www/git.causal.agency/cgit/.gitignore
new file mode 100644
index 00000000..bca98fcf
--- /dev/null
+++ b/www/git.causal.agency/cgit/.gitignore
@@ -0,0 +1,13 @@
+# Files I don't care to see in git-status/commit
+/cgit
+/git
+cgit.conf
+CGIT-CFLAGS
+VERSION
+cgitrc.5
+cgitrc.5.fo
+cgitrc.5.html
+cgitrc.5.pdf
+cgitrc.5.xml
+*.o
+*.d
diff --git a/www/git.causal.agency/cgit/.mailmap b/www/git.causal.agency/cgit/.mailmap
new file mode 100644
index 00000000..03b54796
--- /dev/null
+++ b/www/git.causal.agency/cgit/.mailmap
@@ -0,0 +1,10 @@
+Florian Pritz <bluewind@xinu.at> <bluewind@xssn.at>
+Harley Laue <losinggeneration@gmail.com> <losinggeneration@aim.com>
+John Keeping <john@keeping.me.uk> <john@metanate.com>
+Lars Hjemli <hjemli@gmail.com> <larsh@hal-2004.(none)>
+Lars Hjemli <hjemli@gmail.com> <larsh@hatman.(none)>
+Lars Hjemli <hjemli@gmail.com> <larsh@slackbox.hjemli.net>
+Lars Hjemli <hjemli@gmail.com> <larsh@slaptop.hjemli.net>
+Lukas Fleischer <lfleischer@lfos.de> <cgit@cryptocrack.de>
+Lukas Fleischer <lfleischer@lfos.de> <info@cryptocrack.de>
+Stefan Bühler <source@stbuehler.de> <lighttpd@stbuehler.de>
diff --git a/www/git.causal.agency/cgit/AUTHORS b/www/git.causal.agency/cgit/AUTHORS
new file mode 100644
index 00000000..031de338
--- /dev/null
+++ b/www/git.causal.agency/cgit/AUTHORS
@@ -0,0 +1,13 @@
+Maintainer:
+	Jason A. Donenfeld <Jason@zx2c4.com>
+
+Contributors:
+	Jason A. Donenfeld <Jason@zx2c4.com>
+	Lukas Fleischer <cgit@cryptocrack.de>
+	Johan Herland <johan@herland.net>
+	Lars Hjemli <hjemli@gmail.com>
+	Ferry Huberts <ferry.huberts@pelagic.nl>
+	John Keeping <john@keeping.me.uk>
+
+Previous Maintainer:
+	Lars Hjemli <hjemli@gmail.com>
diff --git a/www/git.causal.agency/cgit/COPYING b/www/git.causal.agency/cgit/COPYING
new file mode 100644
index 00000000..d159169d
--- /dev/null
+++ b/www/git.causal.agency/cgit/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/www/git.causal.agency/cgit/Makefile b/www/git.causal.agency/cgit/Makefile
new file mode 100644
index 00000000..bfaa4ee6
--- /dev/null
+++ b/www/git.causal.agency/cgit/Makefile
@@ -0,0 +1,170 @@
+all::
+
+CGIT_VERSION = causal agency
+CGIT_SCRIPT_NAME = cgit.cgi
+CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
+CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
+CGIT_CONFIG = /etc/cgitrc
+CACHE_ROOT = /var/cache/cgit
+prefix = /usr/local
+libdir = $(prefix)/lib
+filterdir = $(libdir)/cgit/filters
+docdir = $(prefix)/share/doc/cgit
+htmldir = $(docdir)
+pdfdir = $(docdir)
+mandir = $(prefix)/share/man
+SHA1_HEADER = <openssl/sha.h>
+GIT_VER = 2.32.0
+GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.gz
+INSTALL = install
+COPYTREE = cp -r
+MAN5_TXT = $(wildcard *.5.txt)
+MAN_TXT  = $(MAN5_TXT)
+DOC_MAN5 = $(patsubst %.txt,%,$(MAN5_TXT))
+DOC_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
+DOC_PDF  = $(patsubst %.txt,%.pdf,$(MAN_TXT))
+
+ASCIIDOC = asciidoc
+ASCIIDOC_EXTRA =
+ASCIIDOC_HTML = xhtml11
+ASCIIDOC_COMMON = $(ASCIIDOC) $(ASCIIDOC_EXTRA)
+TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML)
+
+# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
+# do not support the 'size specifiers' introduced by C99, namely ll, hh,
+# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
+# some C compilers supported these specifiers prior to C99 as an extension.
+#
+# Define HAVE_LINUX_SENDFILE to use sendfile()
+
+#-include config.mak
+
+-include git/config.mak.uname
+#
+# Let the user override the above settings.
+#
+-include cgit.conf
+
+export CGIT_VERSION CGIT_SCRIPT_NAME CGIT_SCRIPT_PATH CGIT_DATA_PATH CGIT_CONFIG CACHE_ROOT
+
+#
+# Define a way to invoke make in subdirs quietly, shamelessly ripped
+# from git.git
+#
+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1  =
+
+ifneq ($(findstring w,$(MAKEFLAGS)),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifndef V
+	QUIET_SUBDIR0  = +@subdir=
+	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
+			 $(MAKE) $(PRINT_DIR) -C $$subdir
+	QUIET_TAGS     = @echo '   ' TAGS $@;
+	export V
+endif
+
+.SUFFIXES:
+
+all:: cgit
+
+cgit:
+	$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk ../cgit $(EXTRA_GIT_TARGETS) NO_CURL=1
+
+sparse:
+	$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk NO_CURL=1 cgit-sparse
+
+test:
+	@$(MAKE) --no-print-directory cgit EXTRA_GIT_TARGETS=all
+	$(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
+
+install: all
+	$(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_SCRIPT_PATH)
+	$(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
+	$(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH)
+	$(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
+	$(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
+	$(INSTALL) -m 0644 favicon.ico $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico
+	$(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt
+	$(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir)
+	$(COPYTREE) filters/* $(DESTDIR)$(filterdir)
+
+install-doc: install-man install-html install-pdf
+
+install-man: doc-man
+	$(INSTALL) -m 0755 -d $(DESTDIR)$(mandir)/man5
+	$(INSTALL) -m 0644 $(DOC_MAN5) $(DESTDIR)$(mandir)/man5
+
+install-html: doc-html
+	$(INSTALL) -m 0755 -d $(DESTDIR)$(htmldir)
+	$(INSTALL) -m 0644 $(DOC_HTML) $(DESTDIR)$(htmldir)
+
+install-pdf: doc-pdf
+	$(INSTALL) -m 0755 -d $(DESTDIR)$(pdfdir)
+	$(INSTALL) -m 0644 $(DOC_PDF) $(DESTDIR)$(pdfdir)
+
+uninstall:
+	rm -f $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
+	rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
+	rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
+	rm -f $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico
+
+uninstall-doc: uninstall-man uninstall-html uninstall-pdf
+
+uninstall-man:
+	@for i in $(DOC_MAN5); do \
+	    rm -fv $(DESTDIR)$(mandir)/man5/$$i; \
+	done
+
+uninstall-html:
+	@for i in $(DOC_HTML); do \
+	    rm -fv $(DESTDIR)$(htmldir)/$$i; \
+	done
+
+uninstall-pdf:
+	@for i in $(DOC_PDF); do \
+	    rm -fv $(DESTDIR)$(pdfdir)/$$i; \
+	done
+
+doc: doc-man doc-html doc-pdf
+doc-man: doc-man5
+doc-man5: $(DOC_MAN5)
+doc-html: $(DOC_HTML)
+doc-pdf: $(DOC_PDF)
+
+%.5 : %.5.txt
+	a2x -f manpage $<
+
+$(DOC_HTML): %.html : %.txt
+	$(TXT_TO_HTML) -o $@+ $< && \
+	mv $@+ $@
+
+$(DOC_PDF): %.pdf : %.txt
+	a2x -f pdf cgitrc.5.txt
+
+clean: clean-doc
+	$(RM) cgit VERSION CGIT-CFLAGS *.o tags
+	$(RM) -r .deps
+
+cleanall: clean
+	$(MAKE) -C git clean
+
+clean-doc:
+	$(RM) cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo
+
+get-git:
+	curl -L $(GIT_URL) | tar -xzf - && rm -rf git && mv git-$(GIT_VER) git
+
+tags:
+	$(QUIET_TAGS)find . -name '*.[ch]' | xargs ctags
+
+.PHONY: all cgit git get-git
+.PHONY: clean clean-doc cleanall
+.PHONY: doc doc-html doc-man doc-pdf
+.PHONY: install install-doc install-html install-man install-pdf
+.PHONY: tags test
+.PHONY: uninstall uninstall-doc uninstall-html uninstall-man uninstall-pdf
diff --git a/www/git.causal.agency/cgit/README b/www/git.causal.agency/cgit/README
new file mode 100644
index 00000000..371cf21f
--- /dev/null
+++ b/www/git.causal.agency/cgit/README
@@ -0,0 +1,86 @@
+cgit - CGI for Git
+==================
+
+This is an attempt to create a fast web interface for the Git SCM, using a
+built-in cache to decrease server I/O pressure.
+
+Installation
+------------
+
+Building cgit involves building a proper version of Git. How to do this
+depends on how you obtained the cgit sources:
+
+a) If you're working in a cloned cgit repository, you first need to
+initialize and update the Git submodule:
+
+    $ git submodule init     # register the Git submodule in .git/config
+    $ $EDITOR .git/config    # if you want to specify a different url for git
+    $ git submodule update   # clone/fetch and checkout correct git version
+
+b) If you're building from a cgit tarball, you can download a proper git
+version like this:
+
+    $ make get-git
+
+When either a) or b) has been performed, you can build and install cgit like
+this:
+
+    $ make
+    $ sudo make install
+
+This will install `cgit.cgi` and `cgit.css` into `/var/www/htdocs/cgit`. You
+can configure this location (and a few other things) by providing a `cgit.conf`
+file (see the Makefile for details).
+
+
+Dependencies
+------------
+
+* libzip
+* libcrypto (OpenSSL)
+* libssl (OpenSSL)
+
+Apache configuration
+--------------------
+
+A new `Directory` section must probably be added for cgit, possibly something
+like this:
+
+    <Directory "/var/www/htdocs/cgit/">
+        AllowOverride None
+        Options +ExecCGI
+        Order allow,deny
+        Allow from all
+    </Directory>
+
+
+Runtime configuration
+---------------------
+
+The file `/etc/cgitrc` is read by cgit before handling a request. In addition
+to runtime parameters, this file may also contain a list of repositories
+displayed by cgit (see `cgitrc.5.txt` for further details).
+
+The cache
+---------
+
+When cgit is invoked it looks for a cache file matching the request and
+returns it to the client. If no such cache file exists (or if it has expired),
+the content for the request is written into the proper cache file before the
+file is returned.
+
+If the cache file has expired but cgit is unable to obtain a lock for it, the
+stale cache file is returned to the client. This is done to favour page
+throughput over page freshness.
+
+The generated content contains the complete response to the client, including
+the HTTP headers `Modified` and `Expires`.
+
+Online presence
+---------------
+
+* The cgit homepage is hosted by cgit at <https://git.zx2c4.com/cgit/about/>
+
+* Patches, bug reports, discussions and support should go to the cgit
+  mailing list: <cgit@lists.zx2c4.com>. To sign up, visit
+  <https://lists.zx2c4.com/mailman/listinfo/cgit>
diff --git a/www/git.causal.agency/cgit/cache.c b/www/git.causal.agency/cgit/cache.c
new file mode 100644
index 00000000..578b73b0
--- /dev/null
+++ b/www/git.causal.agency/cgit/cache.c
@@ -0,0 +1,475 @@
+/* cache.c: cache management
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ *
+ *
+ * The cache is just a directory structure where each file is a cache slot,
+ * and each filename is based on the hash of some key (e.g. the cgit url).
+ * Each file contains the full key followed by the cached content for that
+ * key.
+ *
+ */
+
+#include "cgit.h"
+#include "cache.h"
+#include "html.h"
+#ifdef HAVE_LINUX_SENDFILE
+#include <sys/sendfile.h>
+#endif
+
+#define CACHE_BUFSIZE (1024 * 4)
+
+struct cache_slot {
+	const char *key;
+	size_t keylen;
+	int ttl;
+	cache_fill_fn fn;
+	int cache_fd;
+	int lock_fd;
+	int stdout_fd;
+	const char *cache_name;
+	const char *lock_name;
+	int match;
+	struct stat cache_st;
+	int bufsize;
+	char buf[CACHE_BUFSIZE];
+};
+
+/* Open an existing cache slot and fill the cache buffer with
+ * (part of) the content of the cache file. Return 0 on success
+ * and errno otherwise.
+ */
+static int open_slot(struct cache_slot *slot)
+{
+	char *bufz;
+	ssize_t bufkeylen = -1;
+
+	slot->cache_fd = open(slot->cache_name, O_RDONLY);
+	if (slot->cache_fd == -1)
+		return errno;
+
+	if (fstat(slot->cache_fd, &slot->cache_st))
+		return errno;
+
+	slot->bufsize = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
+	if (slot->bufsize < 0)
+		return errno;
+
+	bufz = memchr(slot->buf, 0, slot->bufsize);
+	if (bufz)
+		bufkeylen = bufz - slot->buf;
+
+	if (slot->key)
+		slot->match = bufkeylen == slot->keylen &&
+		    !memcmp(slot->key, slot->buf, bufkeylen + 1);
+
+	return 0;
+}
+
+/* Close the active cache slot */
+static int close_slot(struct cache_slot *slot)
+{
+	int err = 0;
+	if (slot->cache_fd > 0) {
+		if (close(slot->cache_fd))
+			err = errno;
+		else
+			slot->cache_fd = -1;
+	}
+	return err;
+}
+
+/* Print the content of the active cache slot (but skip the key). */
+static int print_slot(struct cache_slot *slot)
+{
+#ifdef HAVE_LINUX_SENDFILE
+	off_t start_off;
+	int ret;
+
+	start_off = slot->keylen + 1;
+
+	do {
+		ret = sendfile(STDOUT_FILENO, slot->cache_fd, &start_off,
+				slot->cache_st.st_size - start_off);
+		if (ret < 0) {
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
+			return errno;
+		}
+		return 0;
+	} while (1);
+#else
+	ssize_t i, j;
+
+	i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET);
+	if (i != slot->keylen + 1)
+		return errno;
+
+	do {
+		i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
+		if (i > 0)
+			j = xwrite(STDOUT_FILENO, slot->buf, i);
+	} while (i > 0 && j == i);
+
+	if (i < 0 || j != i)
+		return errno;
+	else
+		return 0;
+#endif
+}
+
+/* Check if the slot has expired */
+static int is_expired(struct cache_slot *slot)
+{
+	if (slot->ttl < 0)
+		return 0;
+	else
+		return slot->cache_st.st_mtime + slot->ttl * 60 < time(NULL);
+}
+
+/* Check if the slot has been modified since we opened it.
+ * NB: If stat() fails, we pretend the file is modified.
+ */
+static int is_modified(struct cache_slot *slot)
+{
+	struct stat st;
+
+	if (stat(slot->cache_name, &st))
+		return 1;
+	return (st.st_ino != slot->cache_st.st_ino ||
+		st.st_mtime != slot->cache_st.st_mtime ||
+		st.st_size != slot->cache_st.st_size);
+}
+
+/* Close an open lockfile */
+static int close_lock(struct cache_slot *slot)
+{
+	int err = 0;
+	if (slot->lock_fd > 0) {
+		if (close(slot->lock_fd))
+			err = errno;
+		else
+			slot->lock_fd = -1;
+	}
+	return err;
+}
+
+/* Create a lockfile used to store the generated content for a cache
+ * slot, and write the slot key + \0 into it.
+ * Returns 0 on success and errno otherwise.
+ */
+static int lock_slot(struct cache_slot *slot)
+{
+	struct flock lock = {
+		.l_type = F_WRLCK,
+		.l_whence = SEEK_SET,
+		.l_start = 0,
+		.l_len = 0,
+	};
+
+	slot->lock_fd = open(slot->lock_name, O_RDWR | O_CREAT,
+			     S_IRUSR | S_IWUSR);
+	if (slot->lock_fd == -1)
+		return errno;
+	if (fcntl(slot->lock_fd, F_SETLK, &lock) < 0) {
+		int saved_errno = errno;
+		close(slot->lock_fd);
+		slot->lock_fd = -1;
+		return saved_errno;
+	}
+	if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0)
+		return errno;
+	return 0;
+}
+
+/* Release the current lockfile. If `replace_old_slot` is set the
+ * lockfile replaces the old cache slot, otherwise the lockfile is
+ * just deleted.
+ */
+static int unlock_slot(struct cache_slot *slot, int replace_old_slot)
+{
+	int err;
+
+	if (replace_old_slot)
+		err = rename(slot->lock_name, slot->cache_name);
+	else
+		err = unlink(slot->lock_name);
+
+	/* Restore stdout and close the temporary FD. */
+	if (slot->stdout_fd >= 0) {
+		dup2(slot->stdout_fd, STDOUT_FILENO);
+		close(slot->stdout_fd);
+		slot->stdout_fd = -1;
+	}
+
+	if (err)
+		return errno;
+
+	return 0;
+}
+
+/* Generate the content for the current cache slot by redirecting
+ * stdout to the lock-fd and invoking the callback function
+ */
+static int fill_slot(struct cache_slot *slot)
+{
+	/* Preserve stdout */
+	slot->stdout_fd = dup(STDOUT_FILENO);
+	if (slot->stdout_fd == -1)
+		return errno;
+
+	/* Redirect stdout to lockfile */
+	if (dup2(slot->lock_fd, STDOUT_FILENO) == -1)
+		return errno;
+
+	/* Generate cache content */
+	slot->fn();
+
+	/* Make sure any buffered data is flushed to the file */
+	if (fflush(stdout))
+		return errno;
+
+	/* update stat info */
+	if (fstat(slot->lock_fd, &slot->cache_st))
+		return errno;
+
+	return 0;
+}
+
+/* Crude implementation of 32-bit FNV-1 hash algorithm,
+ * see http://www.isthe.com/chongo/tech/comp/fnv/ for details
+ * about the magic numbers.
+ */
+#define FNV_OFFSET 0x811c9dc5
+#define FNV_PRIME  0x01000193
+
+unsigned long hash_str(const char *str)
+{
+	unsigned long h = FNV_OFFSET;
+	unsigned char *s = (unsigned char *)str;
+
+	if (!s)
+		return h;
+
+	while (*s) {
+		h *= FNV_PRIME;
+		h ^= *s++;
+	}
+	return h;
+}
+
+static int process_slot(struct cache_slot *slot)
+{
+	int err;
+
+	/*
+	 * Make sure any buffered data is flushed before we redirect,
+	 * do sendfile(2) or write(2)
+	 */
+	if (fflush(stdout))
+		return errno;
+
+	err = open_slot(slot);
+	if (!err && slot->match) {
+		if (is_expired(slot)) {
+			if (!lock_slot(slot)) {
+				/* If the cachefile has been replaced between
+				 * `open_slot` and `lock_slot`, we'll just
+				 * serve the stale content from the original
+				 * cachefile. This way we avoid pruning the
+				 * newly generated slot. The same code-path
+				 * is chosen if fill_slot() fails for some
+				 * reason.
+				 *
+				 * TODO? check if the new slot contains the
+				 * same key as the old one, since we would
+				 * prefer to serve the newest content.
+				 * This will require us to open yet another
+				 * file-descriptor and read and compare the
+				 * key from the new file, so for now we're
+				 * lazy and just ignore the new file.
+				 */
+				if (is_modified(slot) || fill_slot(slot)) {
+					unlock_slot(slot, 0);
+					close_lock(slot);
+				} else {
+					close_slot(slot);
+					unlock_slot(slot, 1);
+					slot->cache_fd = slot->lock_fd;
+				}
+			}
+		}
+		if ((err = print_slot(slot)) != 0) {
+			cache_log("[cgit] error printing cache %s: %s (%d)\n",
+				  slot->cache_name,
+				  strerror(err),
+				  err);
+		}
+		close_slot(slot);
+		return err;
+	}
+
+	/* If the cache slot does not exist (or its key doesn't match the
+	 * current key), lets try to create a new cache slot for this
+	 * request. If this fails (for whatever reason), lets just generate
+	 * the content without caching it and fool the caller to believe
+	 * everything worked out (but print a warning on stdout).
+	 */
+
+	close_slot(slot);
+	if ((err = lock_slot(slot)) != 0) {
+		cache_log("[cgit] Unable to lock slot %s: %s (%d)\n",
+			  slot->lock_name, strerror(err), err);
+		slot->fn();
+		return 0;
+	}
+
+	if ((err = fill_slot(slot)) != 0) {
+		cache_log("[cgit] Unable to fill slot %s: %s (%d)\n",
+			  slot->lock_name, strerror(err), err);
+		unlock_slot(slot, 0);
+		close_lock(slot);
+		slot->fn();
+		return 0;
+	}
+	// We've got a valid cache slot in the lock file, which
+	// is about to replace the old cache slot. But if we
+	// release the lockfile and then try to open the new cache
+	// slot, we might get a race condition with a concurrent
+	// writer for the same cache slot (with a different key).
+	// Lets avoid such a race by just printing the content of
+	// the lock file.
+	slot->cache_fd = slot->lock_fd;
+	unlock_slot(slot, 1);
+	if ((err = print_slot(slot)) != 0) {
+		cache_log("[cgit] error printing cache %s: %s (%d)\n",
+			  slot->cache_name,
+			  strerror(err),
+			  err);
+	}
+	close_slot(slot);
+	return err;
+}
+
+/* Print cached content to stdout, generate the content if necessary. */
+int cache_process(int size, const char *path, const char *key, int ttl,
+		  cache_fill_fn fn)
+{
+	unsigned long hash;
+	int i;
+	struct strbuf filename = STRBUF_INIT;
+	struct strbuf lockname = STRBUF_INIT;
+	struct cache_slot slot;
+	int result;
+
+	/* If the cache is disabled, just generate the content */
+	if (size <= 0 || ttl == 0) {
+		fn();
+		return 0;
+	}
+
+	/* Verify input, calculate filenames */
+	if (!path) {
+		cache_log("[cgit] Cache path not specified, caching is disabled\n");
+		fn();
+		return 0;
+	}
+	if (!key)
+		key = "";
+	hash = hash_str(key) % size;
+	strbuf_addstr(&filename, path);
+	strbuf_ensure_end(&filename, '/');
+	for (i = 0; i < 8; i++) {
+		strbuf_addf(&filename, "%x", (unsigned char)(hash & 0xf));
+		hash >>= 4;
+	}
+	strbuf_addbuf(&lockname, &filename);
+	strbuf_addstr(&lockname, ".lock");
+	slot.fn = fn;
+	slot.ttl = ttl;
+	slot.stdout_fd = -1;
+	slot.cache_name = filename.buf;
+	slot.lock_name = lockname.buf;
+	slot.key = key;
+	slot.keylen = strlen(key);
+	result = process_slot(&slot);
+
+	strbuf_release(&filename);
+	strbuf_release(&lockname);
+	return result;
+}
+
+/* Return a strftime formatted date/time
+ * NB: the result from this function is to shared memory
+ */
+static char *sprintftime(const char *format, time_t time)
+{
+	static char buf[64];
+	struct tm tm;
+
+	if (!time)
+		return NULL;
+	gmtime_r(&time, &tm);
+	strftime(buf, sizeof(buf)-1, format, &tm);
+	return buf;
+}
+
+int cache_ls(const char *path)
+{
+	DIR *dir;
+	struct dirent *ent;
+	int err = 0;
+	struct cache_slot slot = { NULL };
+	struct strbuf fullname = STRBUF_INIT;
+	size_t prefixlen;
+
+	if (!path) {
+		cache_log("[cgit] cache path not specified\n");
+		return -1;
+	}
+	dir = opendir(path);
+	if (!dir) {
+		err = errno;
+		cache_log("[cgit] unable to open path %s: %s (%d)\n",
+			  path, strerror(err), err);
+		return err;
+	}
+	strbuf_addstr(&fullname, path);
+	strbuf_ensure_end(&fullname, '/');
+	prefixlen = fullname.len;
+	while ((ent = readdir(dir)) != NULL) {
+		if (strlen(ent->d_name) != 8)
+			continue;
+		strbuf_setlen(&fullname, prefixlen);
+		strbuf_addstr(&fullname, ent->d_name);
+		slot.cache_name = fullname.buf;
+		if ((err = open_slot(&slot)) != 0) {
+			cache_log("[cgit] unable to open path %s: %s (%d)\n",
+				  fullname.buf, strerror(err), err);
+			continue;
+		}
+		htmlf("%s %s %10"PRIuMAX" %s\n",
+		      fullname.buf,
+		      sprintftime("%Y-%m-%d %H:%M:%S",
+				  slot.cache_st.st_mtime),
+		      (uintmax_t)slot.cache_st.st_size,
+		      slot.buf);
+		close_slot(&slot);
+	}
+	closedir(dir);
+	strbuf_release(&fullname);
+	return 0;
+}
+
+/* Print a message to stdout */
+void cache_log(const char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+	vfprintf(stderr, format, args);
+	va_end(args);
+}
+
diff --git a/www/git.causal.agency/cgit/cache.h b/www/git.causal.agency/cgit/cache.h
new file mode 100644
index 00000000..470da4fc
--- /dev/null
+++ b/www/git.causal.agency/cgit/cache.h
@@ -0,0 +1,37 @@
+/*
+ * Since git has it's own cache.h which we include,
+ * lets test on CGIT_CACHE_H to avoid confusion
+ */
+
+#ifndef CGIT_CACHE_H
+#define CGIT_CACHE_H
+
+typedef void (*cache_fill_fn)(void);
+
+
+/* Print cached content to stdout, generate the content if necessary.
+ *
+ * Parameters
+ *   size    max number of cache files
+ *   path    directory used to store cache files
+ *   key     the key used to lookup cache files
+ *   ttl     max cache time in seconds for this key
+ *   fn      content generator function for this key
+ *
+ * Return value
+ *   0 indicates success, everything else is an error
+ */
+extern int cache_process(int size, const char *path, const char *key, int ttl,
+			 cache_fill_fn fn);
+
+
+/* List info about all cache entries on stdout */
+extern int cache_ls(const char *path);
+
+/* Print a message to stdout */
+__attribute__((format (printf,1,2)))
+extern void cache_log(const char *format, ...);
+
+extern unsigned long hash_str(const char *str);
+
+#endif /* CGIT_CACHE_H */
diff --git a/www/git.causal.agency/cgit/cgit.c b/www/git.causal.agency/cgit/cgit.c
new file mode 100644
index 00000000..a86970de
--- /dev/null
+++ b/www/git.causal.agency/cgit/cgit.c
@@ -0,0 +1,1105 @@
+/* cgit.c: cgi for the git scm
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "cache.h"
+#include "cmd.h"
+#include "configfile.h"
+#include "html.h"
+#include "ui-shared.h"
+#include "ui-stats.h"
+#include "ui-blob.h"
+#include "ui-summary.h"
+#include "scan-tree.h"
+
+const char *cgit_version = CGIT_VERSION;
+
+__attribute__((constructor))
+static void constructor_environment()
+{
+	/* Do not look in /etc/ for gitconfig and gitattributes. */
+	setenv("GIT_CONFIG_NOSYSTEM", "1", 1);
+	setenv("GIT_ATTR_NOSYSTEM", "1", 1);
+	unsetenv("HOME");
+	unsetenv("XDG_CONFIG_HOME");
+}
+
+static void add_mimetype(const char *name, const char *value)
+{
+	struct string_list_item *item;
+
+	item = string_list_insert(&ctx.cfg.mimetypes, name);
+	item->util = xstrdup(value);
+}
+
+static void process_cached_repolist(const char *path);
+
+static void repo_config(struct cgit_repo *repo, const char *name, const char *value)
+{
+	const char *path;
+	struct string_list_item *item;
+
+	if (!strcmp(name, "name"))
+		repo->name = xstrdup(value);
+	else if (!strcmp(name, "clone-url"))
+		repo->clone_url = xstrdup(value);
+	else if (!strcmp(name, "desc"))
+		repo->desc = xstrdup(value);
+	else if (!strcmp(name, "owner"))
+		repo->owner = xstrdup(value);
+	else if (!strcmp(name, "homepage"))
+		repo->homepage = xstrdup(value);
+	else if (!strcmp(name, "defbranch"))
+		repo->defbranch = xstrdup(value);
+	else if (!strcmp(name, "extra-head-content"))
+		repo->extra_head_content = xstrdup(value);
+	else if (!strcmp(name, "snapshots"))
+		repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
+	else if (!strcmp(name, "enable-blame"))
+		repo->enable_blame = atoi(value);
+	else if (!strcmp(name, "enable-commit-graph"))
+		repo->enable_commit_graph = atoi(value);
+	else if (!strcmp(name, "enable-log-filecount"))
+		repo->enable_log_filecount = atoi(value);
+	else if (!strcmp(name, "enable-log-linecount"))
+		repo->enable_log_linecount = atoi(value);
+	else if (!strcmp(name, "enable-remote-branches"))
+		repo->enable_remote_branches = atoi(value);
+	else if (!strcmp(name, "enable-subject-links"))
+		repo->enable_subject_links = atoi(value);
+	else if (!strcmp(name, "enable-html-serving"))
+		repo->enable_html_serving = atoi(value);
+	else if (!strcmp(name, "branch-sort")) {
+		if (!strcmp(value, "age"))
+			repo->branch_sort = 1;
+		if (!strcmp(value, "name"))
+			repo->branch_sort = 0;
+	} else if (!strcmp(name, "commit-sort")) {
+		if (!strcmp(value, "date"))
+			repo->commit_sort = 1;
+		if (!strcmp(value, "topo"))
+			repo->commit_sort = 2;
+	} else if (!strcmp(name, "max-stats"))
+		repo->max_stats = cgit_find_stats_period(value, NULL);
+	else if (!strcmp(name, "module-link"))
+		repo->module_link= xstrdup(value);
+	else if (skip_prefix(name, "module-link.", &path)) {
+		item = string_list_append(&repo->submodules, xstrdup(path));
+		item->util = xstrdup(value);
+	} else if (!strcmp(name, "section"))
+		repo->section = xstrdup(value);
+	else if (!strcmp(name, "snapshot-prefix"))
+		repo->snapshot_prefix = xstrdup(value);
+	else if (!strcmp(name, "readme") && value != NULL) {
+		if (repo->readme.items == ctx.cfg.readme.items)
+			memset(&repo->readme, 0, sizeof(repo->readme));
+		string_list_append(&repo->readme, xstrdup(value));
+	} else if (!strcmp(name, "logo") && value != NULL)
+		repo->logo = xstrdup(value);
+	else if (!strcmp(name, "logo-link") && value != NULL)
+		repo->logo_link = xstrdup(value);
+	else if (!strcmp(name, "hide"))
+		repo->hide = atoi(value);
+	else if (!strcmp(name, "ignore"))
+		repo->ignore = atoi(value);
+	else if (ctx.cfg.enable_filter_overrides) {
+		if (!strcmp(name, "about-filter"))
+			repo->about_filter = cgit_new_filter(value, ABOUT);
+		else if (!strcmp(name, "commit-filter"))
+			repo->commit_filter = cgit_new_filter(value, COMMIT);
+		else if (!strcmp(name, "source-filter"))
+			repo->source_filter = cgit_new_filter(value, SOURCE);
+		else if (!strcmp(name, "email-filter"))
+			repo->email_filter = cgit_new_filter(value, EMAIL);
+		else if (!strcmp(name, "owner-filter"))
+			repo->owner_filter = cgit_new_filter(value, OWNER);
+	}
+}
+
+static void config_cb(const char *name, const char *value)
+{
+	const char *arg;
+
+	if (!strcmp(name, "section"))
+		ctx.cfg.section = xstrdup(value);
+	else if (!strcmp(name, "repo.url"))
+		ctx.repo = cgit_add_repo(value);
+	else if (ctx.repo && !strcmp(name, "repo.path"))
+		ctx.repo->path = trim_end(value, '/');
+	else if (ctx.repo && skip_prefix(name, "repo.", &arg))
+		repo_config(ctx.repo, arg, value);
+	else if (!strcmp(name, "readme"))
+		string_list_append(&ctx.cfg.readme, xstrdup(value));
+	else if (!strcmp(name, "root-title"))
+		ctx.cfg.root_title = xstrdup(value);
+	else if (!strcmp(name, "root-desc"))
+		ctx.cfg.root_desc = xstrdup(value);
+	else if (!strcmp(name, "root-readme"))
+		ctx.cfg.root_readme = xstrdup(value);
+	else if (!strcmp(name, "css"))
+		ctx.cfg.css = xstrdup(value);
+	else if (!strcmp(name, "favicon"))
+		ctx.cfg.favicon = xstrdup(value);
+	else if (!strcmp(name, "footer"))
+		ctx.cfg.footer = xstrdup(value);
+	else if (!strcmp(name, "head-include"))
+		ctx.cfg.head_include = xstrdup(value);
+	else if (!strcmp(name, "header"))
+		ctx.cfg.header = xstrdup(value);
+	else if (!strcmp(name, "logo"))
+		ctx.cfg.logo = xstrdup(value);
+	else if (!strcmp(name, "logo-link"))
+		ctx.cfg.logo_link = xstrdup(value);
+	else if (!strcmp(name, "module-link"))
+		ctx.cfg.module_link = xstrdup(value);
+	else if (!strcmp(name, "strict-export"))
+		ctx.cfg.strict_export = xstrdup(value);
+	else if (!strcmp(name, "virtual-root"))
+		ctx.cfg.virtual_root = ensure_end(value, '/');
+	else if (!strcmp(name, "noplainemail"))
+		ctx.cfg.noplainemail = atoi(value);
+	else if (!strcmp(name, "noheader"))
+		ctx.cfg.noheader = atoi(value);
+	else if (!strcmp(name, "snapshots"))
+		ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
+	else if (!strcmp(name, "enable-filter-overrides"))
+		ctx.cfg.enable_filter_overrides = atoi(value);
+	else if (!strcmp(name, "enable-follow-links"))
+		ctx.cfg.enable_follow_links = atoi(value);
+	else if (!strcmp(name, "enable-http-clone"))
+		ctx.cfg.enable_http_clone = atoi(value);
+	else if (!strcmp(name, "enable-index-links"))
+		ctx.cfg.enable_index_links = atoi(value);
+	else if (!strcmp(name, "enable-index-owner"))
+		ctx.cfg.enable_index_owner = atoi(value);
+	else if (!strcmp(name, "enable-blame"))
+		ctx.cfg.enable_blame = atoi(value);
+	else if (!strcmp(name, "enable-commit-graph"))
+		ctx.cfg.enable_commit_graph = atoi(value);
+	else if (!strcmp(name, "enable-log-filecount"))
+		ctx.cfg.enable_log_filecount = atoi(value);
+	else if (!strcmp(name, "enable-log-linecount"))
+		ctx.cfg.enable_log_linecount = atoi(value);
+	else if (!strcmp(name, "enable-remote-branches"))
+		ctx.cfg.enable_remote_branches = atoi(value);
+	else if (!strcmp(name, "enable-subject-links"))
+		ctx.cfg.enable_subject_links = atoi(value);
+	else if (!strcmp(name, "enable-html-serving"))
+		ctx.cfg.enable_html_serving = atoi(value);
+	else if (!strcmp(name, "enable-tree-linenumbers"))
+		ctx.cfg.enable_tree_linenumbers = atoi(value);
+	else if (!strcmp(name, "enable-git-config"))
+		ctx.cfg.enable_git_config = atoi(value);
+	else if (!strcmp(name, "max-stats"))
+		ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
+	else if (!strcmp(name, "cache-size"))
+		ctx.cfg.cache_size = atoi(value);
+	else if (!strcmp(name, "cache-root"))
+		ctx.cfg.cache_root = xstrdup(expand_macros(value));
+	else if (!strcmp(name, "cache-root-ttl"))
+		ctx.cfg.cache_root_ttl = atoi(value);
+	else if (!strcmp(name, "cache-repo-ttl"))
+		ctx.cfg.cache_repo_ttl = atoi(value);
+	else if (!strcmp(name, "cache-scanrc-ttl"))
+		ctx.cfg.cache_scanrc_ttl = atoi(value);
+	else if (!strcmp(name, "cache-static-ttl"))
+		ctx.cfg.cache_static_ttl = atoi(value);
+	else if (!strcmp(name, "cache-dynamic-ttl"))
+		ctx.cfg.cache_dynamic_ttl = atoi(value);
+	else if (!strcmp(name, "cache-about-ttl"))
+		ctx.cfg.cache_about_ttl = atoi(value);
+	else if (!strcmp(name, "cache-snapshot-ttl"))
+		ctx.cfg.cache_snapshot_ttl = atoi(value);
+	else if (!strcmp(name, "case-sensitive-sort"))
+		ctx.cfg.case_sensitive_sort = atoi(value);
+	else if (!strcmp(name, "about-filter"))
+		ctx.cfg.about_filter = cgit_new_filter(value, ABOUT);
+	else if (!strcmp(name, "commit-filter"))
+		ctx.cfg.commit_filter = cgit_new_filter(value, COMMIT);
+	else if (!strcmp(name, "email-filter"))
+		ctx.cfg.email_filter = cgit_new_filter(value, EMAIL);
+	else if (!strcmp(name, "owner-filter"))
+		ctx.cfg.owner_filter = cgit_new_filter(value, OWNER);
+	else if (!strcmp(name, "auth-filter"))
+		ctx.cfg.auth_filter = cgit_new_filter(value, AUTH);
+	else if (!strcmp(name, "embedded"))
+		ctx.cfg.embedded = atoi(value);
+	else if (!strcmp(name, "max-atom-items"))
+		ctx.cfg.max_atom_items = atoi(value);
+	else if (!strcmp(name, "max-message-length"))
+		ctx.cfg.max_msg_len = atoi(value);
+	else if (!strcmp(name, "max-repodesc-length"))
+		ctx.cfg.max_repodesc_len = atoi(value);
+	else if (!strcmp(name, "max-blob-size"))
+		ctx.cfg.max_blob_size = atoi(value);
+	else if (!strcmp(name, "max-repo-count"))
+		ctx.cfg.max_repo_count = atoi(value);
+	else if (!strcmp(name, "max-commit-count"))
+		ctx.cfg.max_commit_count = atoi(value);
+	else if (!strcmp(name, "project-list"))
+		ctx.cfg.project_list = xstrdup(expand_macros(value));
+	else if (!strcmp(name, "scan-path"))
+		if (ctx.cfg.cache_size)
+			process_cached_repolist(expand_macros(value));
+		else if (ctx.cfg.project_list)
+			scan_projects(expand_macros(value),
+				      ctx.cfg.project_list, repo_config);
+		else
+			scan_tree(expand_macros(value), repo_config);
+	else if (!strcmp(name, "scan-hidden-path"))
+		ctx.cfg.scan_hidden_path = atoi(value);
+	else if (!strcmp(name, "section-from-path"))
+		ctx.cfg.section_from_path = atoi(value);
+	else if (!strcmp(name, "repository-sort"))
+		ctx.cfg.repository_sort = xstrdup(value);
+	else if (!strcmp(name, "section-sort"))
+		ctx.cfg.section_sort = atoi(value);
+	else if (!strcmp(name, "source-filter"))
+		ctx.cfg.source_filter = cgit_new_filter(value, SOURCE);
+	else if (!strcmp(name, "summary-log"))
+		ctx.cfg.summary_log = atoi(value);
+	else if (!strcmp(name, "summary-branches"))
+		ctx.cfg.summary_branches = atoi(value);
+	else if (!strcmp(name, "summary-tags"))
+		ctx.cfg.summary_tags = atoi(value);
+	else if (!strcmp(name, "side-by-side-diffs"))
+		ctx.cfg.difftype = atoi(value) ? DIFF_SSDIFF : DIFF_UNIFIED;
+	else if (!strcmp(name, "agefile"))
+		ctx.cfg.agefile = xstrdup(value);
+	else if (!strcmp(name, "mimetype-file"))
+		ctx.cfg.mimetype_file = xstrdup(value);
+	else if (!strcmp(name, "renamelimit"))
+		ctx.cfg.renamelimit = atoi(value);
+	else if (!strcmp(name, "remove-suffix"))
+		ctx.cfg.remove_suffix = atoi(value);
+	else if (!strcmp(name, "robots"))
+		ctx.cfg.robots = xstrdup(value);
+	else if (!strcmp(name, "clone-prefix"))
+		ctx.cfg.clone_prefix = xstrdup(value);
+	else if (!strcmp(name, "clone-url"))
+		ctx.cfg.clone_url = xstrdup(value);
+	else if (!strcmp(name, "local-time"))
+		ctx.cfg.local_time = atoi(value);
+	else if (!strcmp(name, "commit-sort")) {
+		if (!strcmp(value, "date"))
+			ctx.cfg.commit_sort = 1;
+		if (!strcmp(value, "topo"))
+			ctx.cfg.commit_sort = 2;
+	} else if (!strcmp(name, "branch-sort")) {
+		if (!strcmp(value, "age"))
+			ctx.cfg.branch_sort = 1;
+		if (!strcmp(value, "name"))
+			ctx.cfg.branch_sort = 0;
+	} else if (skip_prefix(name, "mimetype.", &arg))
+		add_mimetype(arg, value);
+	else if (!strcmp(name, "include"))
+		parse_configfile(expand_macros(value), config_cb);
+}
+
+static void querystring_cb(const char *name, const char *value)
+{
+	if (!value)
+		value = "";
+
+	if (!strcmp(name,"r")) {
+		ctx.qry.repo = xstrdup(value);
+		ctx.repo = cgit_get_repoinfo(value);
+	} else if (!strcmp(name, "p")) {
+		ctx.qry.page = xstrdup(value);
+	} else if (!strcmp(name, "url")) {
+		if (*value == '/')
+			value++;
+		ctx.qry.url = xstrdup(value);
+		cgit_parse_url(value);
+	} else if (!strcmp(name, "qt")) {
+		ctx.qry.grep = xstrdup(value);
+	} else if (!strcmp(name, "q")) {
+		ctx.qry.search = xstrdup(value);
+	} else if (!strcmp(name, "h")) {
+		ctx.qry.head = xstrdup(value);
+		ctx.qry.has_symref = 1;
+	} else if (!strcmp(name, "id")) {
+		ctx.qry.oid = xstrdup(value);
+		ctx.qry.has_oid = 1;
+	} else if (!strcmp(name, "id2")) {
+		ctx.qry.oid2 = xstrdup(value);
+		ctx.qry.has_oid = 1;
+	} else if (!strcmp(name, "ofs")) {
+		ctx.qry.ofs = atoi(value);
+	} else if (!strcmp(name, "path")) {
+		ctx.qry.path = trim_end(value, '/');
+	} else if (!strcmp(name, "name")) {
+		ctx.qry.name = xstrdup(value);
+	} else if (!strcmp(name, "s")) {
+		ctx.qry.sort = xstrdup(value);
+	} else if (!strcmp(name, "showmsg")) {
+		ctx.qry.showmsg = atoi(value);
+	} else if (!strcmp(name, "period")) {
+		ctx.qry.period = xstrdup(value);
+	} else if (!strcmp(name, "dt")) {
+		ctx.qry.difftype = atoi(value);
+		ctx.qry.has_difftype = 1;
+	} else if (!strcmp(name, "ss")) {
+		/* No longer generated, but there may be links out there. */
+		ctx.qry.difftype = atoi(value) ? DIFF_SSDIFF : DIFF_UNIFIED;
+		ctx.qry.has_difftype = 1;
+	} else if (!strcmp(name, "all")) {
+		ctx.qry.show_all = atoi(value);
+	} else if (!strcmp(name, "context")) {
+		ctx.qry.context = atoi(value);
+	} else if (!strcmp(name, "ignorews")) {
+		ctx.qry.ignorews = atoi(value);
+	} else if (!strcmp(name, "follow")) {
+		ctx.qry.follow = atoi(value);
+	}
+}
+
+static void prepare_context(void)
+{
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.cfg.agefile = "info/web/last-modified";
+	ctx.cfg.cache_size = 0;
+	ctx.cfg.cache_max_create_time = 5;
+	ctx.cfg.cache_root = CGIT_CACHE_ROOT;
+	ctx.cfg.cache_about_ttl = 15;
+	ctx.cfg.cache_snapshot_ttl = 5;
+	ctx.cfg.cache_repo_ttl = 5;
+	ctx.cfg.cache_root_ttl = 5;
+	ctx.cfg.cache_scanrc_ttl = 15;
+	ctx.cfg.cache_dynamic_ttl = 5;
+	ctx.cfg.cache_static_ttl = -1;
+	ctx.cfg.case_sensitive_sort = 1;
+	ctx.cfg.branch_sort = 0;
+	ctx.cfg.commit_sort = 0;
+	ctx.cfg.css = "/cgit.css";
+	ctx.cfg.logo = "/cgit.png";
+	ctx.cfg.favicon = "/favicon.ico";
+	ctx.cfg.local_time = 0;
+	ctx.cfg.enable_http_clone = 1;
+	ctx.cfg.enable_index_owner = 1;
+	ctx.cfg.enable_tree_linenumbers = 1;
+	ctx.cfg.enable_git_config = 0;
+	ctx.cfg.max_repo_count = 50;
+	ctx.cfg.max_commit_count = 50;
+	ctx.cfg.max_lock_attempts = 5;
+	ctx.cfg.max_msg_len = 80;
+	ctx.cfg.max_repodesc_len = 80;
+	ctx.cfg.max_blob_size = 0;
+	ctx.cfg.max_stats = 0;
+	ctx.cfg.project_list = NULL;
+	ctx.cfg.renamelimit = -1;
+	ctx.cfg.remove_suffix = 0;
+	ctx.cfg.robots = "index, nofollow";
+	ctx.cfg.root_title = "Git repository browser";
+	ctx.cfg.root_desc = "a fast webinterface for the git dscm";
+	ctx.cfg.scan_hidden_path = 0;
+	ctx.cfg.script_name = CGIT_SCRIPT_NAME;
+	ctx.cfg.section = "";
+	ctx.cfg.repository_sort = "name";
+	ctx.cfg.section_sort = 1;
+	ctx.cfg.summary_branches = 10;
+	ctx.cfg.summary_log = 10;
+	ctx.cfg.summary_tags = 10;
+	ctx.cfg.max_atom_items = 10;
+	ctx.cfg.difftype = DIFF_UNIFIED;
+	ctx.env.cgit_config = getenv("CGIT_CONFIG");
+	ctx.env.http_host = getenv("HTTP_HOST");
+	ctx.env.https = getenv("HTTPS");
+	ctx.env.no_http = getenv("NO_HTTP");
+	ctx.env.path_info = getenv("PATH_INFO");
+	ctx.env.query_string = getenv("QUERY_STRING");
+	ctx.env.request_method = getenv("REQUEST_METHOD");
+	ctx.env.script_name = getenv("SCRIPT_NAME");
+	ctx.env.server_name = getenv("SERVER_NAME");
+	ctx.env.server_port = getenv("SERVER_PORT");
+	ctx.env.http_cookie = getenv("HTTP_COOKIE");
+	ctx.env.http_referer = getenv("HTTP_REFERER");
+	ctx.env.content_length = getenv("CONTENT_LENGTH") ? strtoul(getenv("CONTENT_LENGTH"), NULL, 10) : 0;
+	ctx.env.authenticated = 0;
+	ctx.page.mimetype = "text/html";
+	ctx.page.charset = PAGE_ENCODING;
+	ctx.page.filename = NULL;
+	ctx.page.size = 0;
+	ctx.page.modified = time(NULL);
+	ctx.page.expires = ctx.page.modified;
+	ctx.page.etag = NULL;
+	string_list_init(&ctx.cfg.mimetypes, 1);
+	if (ctx.env.script_name)
+		ctx.cfg.script_name = xstrdup(ctx.env.script_name);
+	if (ctx.env.query_string)
+		ctx.qry.raw = xstrdup(ctx.env.query_string);
+	if (!ctx.env.cgit_config)
+		ctx.env.cgit_config = CGIT_CONFIG;
+}
+
+struct refmatch {
+	char *req_ref;
+	char *first_ref;
+	int match;
+};
+
+static int find_current_ref(const char *refname, const struct object_id *oid,
+			    int flags, void *cb_data)
+{
+	struct refmatch *info;
+
+	info = (struct refmatch *)cb_data;
+	if (!strcmp(refname, info->req_ref))
+		info->match = 1;
+	if (!info->first_ref)
+		info->first_ref = xstrdup(refname);
+	return info->match;
+}
+
+static void free_refmatch_inner(struct refmatch *info)
+{
+	if (info->first_ref)
+		free(info->first_ref);
+}
+
+static char *find_default_branch(struct cgit_repo *repo)
+{
+	struct refmatch info;
+	char *ref;
+
+	info.req_ref = repo->defbranch;
+	info.first_ref = NULL;
+	info.match = 0;
+	for_each_branch_ref(find_current_ref, &info);
+	if (info.match)
+		ref = info.req_ref;
+	else
+		ref = info.first_ref;
+	if (ref)
+		ref = xstrdup(ref);
+	free_refmatch_inner(&info);
+
+	return ref;
+}
+
+static char *guess_defbranch(void)
+{
+	const char *ref, *refname;
+	struct object_id oid;
+
+	ref = resolve_ref_unsafe("HEAD", 0, &oid, NULL);
+	if (!ref || !skip_prefix(ref, "refs/heads/", &refname))
+		return "master";
+	return xstrdup(refname);
+}
+
+/* The caller must free filename and ref after calling this. */
+static inline void parse_readme(const char *readme, char **filename, char **ref, struct cgit_repo *repo)
+{
+	const char *colon;
+
+	*filename = NULL;
+	*ref = NULL;
+
+	if (!readme || !readme[0])
+		return;
+
+	/* Check if the readme is tracked in the git repo. */
+	colon = strchr(readme, ':');
+	if (colon && strlen(colon) > 1) {
+		/* If it starts with a colon, we want to use
+		 * the default branch */
+		if (colon == readme && repo->defbranch)
+			*ref = xstrdup(repo->defbranch);
+		else
+			*ref = xstrndup(readme, colon - readme);
+		readme = colon + 1;
+	}
+
+	/* Prepend repo path to relative readme path unless tracked. */
+	if (!(*ref) && readme[0] != '/')
+		*filename = fmtalloc("%s/%s", repo->path, readme);
+	else
+		*filename = xstrdup(readme);
+}
+static void choose_readme(struct cgit_repo *repo)
+{
+	int found;
+	char *filename, *ref;
+	struct string_list_item *entry;
+
+	if (!repo->readme.nr)
+		return;
+
+	found = 0;
+	for_each_string_list_item(entry, &repo->readme) {
+		parse_readme(entry->string, &filename, &ref, repo);
+		if (!filename) {
+			free(filename);
+			free(ref);
+			continue;
+		}
+		if (ref) {
+			if (cgit_ref_path_exists(filename, ref, 1)) {
+				found = 1;
+				break;
+			}
+		}
+		else if (!access(filename, R_OK)) {
+			found = 1;
+			break;
+		}
+		free(filename);
+		free(ref);
+	}
+	repo->readme.strdup_strings = 1;
+	string_list_clear(&repo->readme, 0);
+	repo->readme.strdup_strings = 0;
+	if (found)
+		string_list_append(&repo->readme, filename)->util = ref;
+}
+
+static void print_no_repo_clone_urls(const char *url)
+{
+        html("<tr><td><a rel='vcs-git' href='");
+        html_url_path(url);
+        html("' title='");
+        html_attr(ctx.repo->name);
+        html(" Git repository'>");
+        html_txt(url);
+        html("</a></td></tr>\n");
+}
+
+static void prepare_repo_env(int *nongit)
+{
+	/* The path to the git repository. */
+	setenv("GIT_DIR", ctx.repo->path, 1);
+
+	/* Setup the git directory and initialize the notes system. Both of these
+	 * load local configuration from the git repository, so we do them both while
+	 * the HOME variables are unset. */
+	setup_git_directory_gently(nongit);
+	load_display_notes(NULL);
+}
+
+static int prepare_repo_cmd(int nongit)
+{
+	struct object_id oid;
+	int rc;
+
+	if (nongit) {
+		const char *name = ctx.repo->name;
+		rc = errno;
+		ctx.page.title = fmtalloc("%s - %s", ctx.cfg.root_title,
+						"config error");
+		ctx.repo = NULL;
+		cgit_print_http_headers();
+		cgit_print_docstart();
+		cgit_print_pageheader();
+		cgit_print_error("Failed to open %s: %s", name,
+				 rc ? strerror(rc) : "Not a valid git repository");
+		cgit_print_docend();
+		return 1;
+	}
+	ctx.page.title = fmtalloc("%s - %s", ctx.repo->name, ctx.repo->desc);
+
+	if (!ctx.repo->defbranch)
+		ctx.repo->defbranch = guess_defbranch();
+
+	if (!ctx.qry.head) {
+		ctx.qry.nohead = 1;
+		ctx.qry.head = find_default_branch(ctx.repo);
+	}
+
+	if (!ctx.qry.head) {
+		cgit_print_http_headers();
+		cgit_print_docstart();
+		cgit_print_pageheader();
+		cgit_print_error("Repository seems to be empty");
+		if (!strcmp(ctx.qry.page, "summary")) {
+			html("<table class='list'><tr class='nohover'><td>&nbsp;</td></tr><tr class='nohover'><th class='left'>Clone</th></tr>\n");
+			cgit_prepare_repo_env(ctx.repo);
+			cgit_add_clone_urls(print_no_repo_clone_urls);
+			html("</table>\n");
+		}
+		cgit_print_docend();
+		return 1;
+	}
+
+	if (get_oid(ctx.qry.head, &oid)) {
+		char *old_head = ctx.qry.head;
+		ctx.qry.head = xstrdup(ctx.repo->defbranch);
+		cgit_print_error_page(404, "Not found",
+				"Invalid branch: %s", old_head);
+		free(old_head);
+		return 1;
+	}
+	string_list_sort(&ctx.repo->submodules);
+	cgit_prepare_repo_env(ctx.repo);
+	choose_readme(ctx.repo);
+	return 0;
+}
+
+static inline void open_auth_filter(const char *function)
+{
+	cgit_open_filter(ctx.cfg.auth_filter, function,
+		ctx.env.http_cookie ? ctx.env.http_cookie : "",
+		ctx.env.request_method ? ctx.env.request_method : "",
+		ctx.env.query_string ? ctx.env.query_string : "",
+		ctx.env.http_referer ? ctx.env.http_referer : "",
+		ctx.env.path_info ? ctx.env.path_info : "",
+		ctx.env.http_host ? ctx.env.http_host : "",
+		ctx.env.https ? ctx.env.https : "",
+		ctx.qry.repo ? ctx.qry.repo : "",
+		ctx.qry.page ? ctx.qry.page : "",
+		cgit_currentfullurl(),
+		cgit_loginurl());
+}
+
+/* We intentionally keep this rather small, instead of looping and
+ * feeding it to the filter a couple bytes at a time. This way, the
+ * filter itself does not need to handle any denial of service or
+ * buffer bloat issues. If this winds up being too small, people
+ * will complain on the mailing list, and we'll increase it as needed. */
+#define MAX_AUTHENTICATION_POST_BYTES 4096
+/* The filter is expected to spit out "Status: " and all headers. */
+static inline void authenticate_post(void)
+{
+	char buffer[MAX_AUTHENTICATION_POST_BYTES];
+	ssize_t len;
+
+	open_auth_filter("authenticate-post");
+	len = ctx.env.content_length;
+	if (len > MAX_AUTHENTICATION_POST_BYTES)
+		len = MAX_AUTHENTICATION_POST_BYTES;
+	if ((len = read(STDIN_FILENO, buffer, len)) < 0)
+		die_errno("Could not read POST from stdin");
+	if (fwrite(buffer, 1, len, stdout) < len)
+		die_errno("Could not write POST to stdout");
+	cgit_close_filter(ctx.cfg.auth_filter);
+	exit(0);
+}
+
+static inline void authenticate_cookie(void)
+{
+	/* If we don't have an auth_filter, consider all cookies valid, and thus return early. */
+	if (!ctx.cfg.auth_filter) {
+		ctx.env.authenticated = 1;
+		return;
+	}
+
+	/* If we're having something POST'd to /login, we're authenticating POST,
+	 * instead of the cookie, so call authenticate_post and bail out early.
+	 * This pattern here should match /?p=login with POST. */
+	if (ctx.env.request_method && ctx.qry.page && !ctx.repo && \
+	    !strcmp(ctx.env.request_method, "POST") && !strcmp(ctx.qry.page, "login")) {
+		authenticate_post();
+		return;
+	}
+
+	/* If we've made it this far, we're authenticating the cookie for real, so do that. */
+	open_auth_filter("authenticate-cookie");
+	ctx.env.authenticated = cgit_close_filter(ctx.cfg.auth_filter);
+}
+
+static void process_request(void)
+{
+	struct cgit_cmd *cmd;
+	int nongit = 0;
+
+	/* If we're not yet authenticated, no matter what page we're on,
+	 * display the authentication body from the auth_filter. This should
+	 * never be cached. */
+	if (!ctx.env.authenticated) {
+		ctx.page.title = "Authentication Required";
+		cgit_print_http_headers();
+		cgit_print_docstart();
+		cgit_print_pageheader();
+		open_auth_filter("body");
+		cgit_close_filter(ctx.cfg.auth_filter);
+		cgit_print_docend();
+		return;
+	}
+
+	if (ctx.repo)
+		prepare_repo_env(&nongit);
+
+	cmd = cgit_get_cmd();
+	if (!cmd) {
+		ctx.page.title = "cgit error";
+		cgit_print_error_page(404, "Not found", "Invalid request");
+		return;
+	}
+
+	if (!ctx.cfg.enable_http_clone && cmd->is_clone) {
+		ctx.page.title = "cgit error";
+		cgit_print_error_page(404, "Not found", "Invalid request");
+		return;
+	}
+
+	if (cmd->want_repo && !ctx.repo) {
+		cgit_print_error_page(400, "Bad request",
+				"No repository selected");
+		return;
+	}
+
+	/* If cmd->want_vpath is set, assume ctx.qry.path contains a "virtual"
+	 * in-project path limit to be made available at ctx.qry.vpath.
+	 * Otherwise, no path limit is in effect (ctx.qry.vpath = NULL).
+	 */
+	ctx.qry.vpath = cmd->want_vpath ? ctx.qry.path : NULL;
+
+	if (ctx.repo && prepare_repo_cmd(nongit))
+		return;
+
+	cmd->fn();
+}
+
+static int cmp_repos(const void *a, const void *b)
+{
+	const struct cgit_repo *ra = a, *rb = b;
+	return strcmp(ra->url, rb->url);
+}
+
+static char *build_snapshot_setting(int bitmap)
+{
+	const struct cgit_snapshot_format *f;
+	struct strbuf result = STRBUF_INIT;
+
+	for (f = cgit_snapshot_formats; f->suffix; f++) {
+		if (cgit_snapshot_format_bit(f) & bitmap) {
+			if (result.len)
+				strbuf_addch(&result, ' ');
+			strbuf_addstr(&result, f->suffix);
+		}
+	}
+	return strbuf_detach(&result, NULL);
+}
+
+static char *get_first_line(char *txt)
+{
+	char *t = xstrdup(txt);
+	char *p = strchr(t, '\n');
+	if (p)
+		*p = '\0';
+	return t;
+}
+
+static void print_repo(FILE *f, struct cgit_repo *repo)
+{
+	struct string_list_item *item;
+	fprintf(f, "repo.url=%s\n", repo->url);
+	fprintf(f, "repo.name=%s\n", repo->name);
+	fprintf(f, "repo.path=%s\n", repo->path);
+	if (repo->owner)
+		fprintf(f, "repo.owner=%s\n", repo->owner);
+	if (repo->desc) {
+		char *tmp = get_first_line(repo->desc);
+		fprintf(f, "repo.desc=%s\n", tmp);
+		free(tmp);
+	}
+	for_each_string_list_item(item, &repo->readme) {
+		if (item->util)
+			fprintf(f, "repo.readme=%s:%s\n", (char *)item->util, item->string);
+		else
+			fprintf(f, "repo.readme=%s\n", item->string);
+	}
+	if (repo->defbranch)
+		fprintf(f, "repo.defbranch=%s\n", repo->defbranch);
+	if (repo->extra_head_content)
+		fprintf(f, "repo.extra-head-content=%s\n", repo->extra_head_content);
+	if (repo->module_link)
+		fprintf(f, "repo.module-link=%s\n", repo->module_link);
+	if (repo->section)
+		fprintf(f, "repo.section=%s\n", repo->section);
+	if (repo->homepage)
+		fprintf(f, "repo.homepage=%s\n", repo->homepage);
+	if (repo->clone_url)
+		fprintf(f, "repo.clone-url=%s\n", repo->clone_url);
+	fprintf(f, "repo.enable-blame=%d\n",
+	        repo->enable_blame);
+	fprintf(f, "repo.enable-commit-graph=%d\n",
+	        repo->enable_commit_graph);
+	fprintf(f, "repo.enable-log-filecount=%d\n",
+	        repo->enable_log_filecount);
+	fprintf(f, "repo.enable-log-linecount=%d\n",
+	        repo->enable_log_linecount);
+	if (repo->about_filter && repo->about_filter != ctx.cfg.about_filter)
+		cgit_fprintf_filter(repo->about_filter, f, "repo.about-filter=");
+	if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter)
+		cgit_fprintf_filter(repo->commit_filter, f, "repo.commit-filter=");
+	if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter)
+		cgit_fprintf_filter(repo->source_filter, f, "repo.source-filter=");
+	if (repo->email_filter && repo->email_filter != ctx.cfg.email_filter)
+		cgit_fprintf_filter(repo->email_filter, f, "repo.email-filter=");
+	if (repo->owner_filter && repo->owner_filter != ctx.cfg.owner_filter)
+		cgit_fprintf_filter(repo->owner_filter, f, "repo.owner-filter=");
+	if (repo->snapshots != ctx.cfg.snapshots) {
+		char *tmp = build_snapshot_setting(repo->snapshots);
+		fprintf(f, "repo.snapshots=%s\n", tmp ? tmp : "");
+		free(tmp);
+	}
+	if (repo->snapshot_prefix)
+		fprintf(f, "repo.snapshot-prefix=%s\n", repo->snapshot_prefix);
+	if (repo->max_stats != ctx.cfg.max_stats)
+		fprintf(f, "repo.max-stats=%s\n",
+		        cgit_find_stats_periodname(repo->max_stats));
+	if (repo->logo)
+		fprintf(f, "repo.logo=%s\n", repo->logo);
+	if (repo->logo_link)
+		fprintf(f, "repo.logo-link=%s\n", repo->logo_link);
+	fprintf(f, "repo.enable-remote-branches=%d\n", repo->enable_remote_branches);
+	fprintf(f, "repo.enable-subject-links=%d\n", repo->enable_subject_links);
+	fprintf(f, "repo.enable-html-serving=%d\n", repo->enable_html_serving);
+	if (repo->branch_sort == 1)
+		fprintf(f, "repo.branch-sort=age\n");
+	if (repo->commit_sort) {
+		if (repo->commit_sort == 1)
+			fprintf(f, "repo.commit-sort=date\n");
+		else if (repo->commit_sort == 2)
+			fprintf(f, "repo.commit-sort=topo\n");
+	}
+	fprintf(f, "repo.hide=%d\n", repo->hide);
+	fprintf(f, "repo.ignore=%d\n", repo->ignore);
+	fprintf(f, "\n");
+}
+
+static void print_repolist(FILE *f, struct cgit_repolist *list, int start)
+{
+	int i;
+
+	for (i = start; i < list->count; i++)
+		print_repo(f, &list->repos[i]);
+}
+
+/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc'
+ * and return 0 on success.
+ */
+static int generate_cached_repolist(const char *path, const char *cached_rc)
+{
+	struct strbuf locked_rc = STRBUF_INIT;
+	int result = 0;
+	int idx;
+	FILE *f;
+
+	strbuf_addf(&locked_rc, "%s.lock", cached_rc);
+	f = fopen(locked_rc.buf, "wx");
+	if (!f) {
+		/* Inform about the error unless the lockfile already existed,
+		 * since that only means we've got concurrent requests.
+		 */
+		result = errno;
+		if (result != EEXIST)
+			fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
+				locked_rc.buf, strerror(result), result);
+		goto out;
+	}
+	idx = cgit_repolist.count;
+	if (ctx.cfg.project_list)
+		scan_projects(path, ctx.cfg.project_list, repo_config);
+	else
+		scan_tree(path, repo_config);
+	print_repolist(f, &cgit_repolist, idx);
+	if (rename(locked_rc.buf, cached_rc))
+		fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
+			locked_rc.buf, cached_rc, strerror(errno), errno);
+	fclose(f);
+out:
+	strbuf_release(&locked_rc);
+	return result;
+}
+
+static void process_cached_repolist(const char *path)
+{
+	struct stat st;
+	struct strbuf cached_rc = STRBUF_INIT;
+	time_t age;
+	unsigned long hash;
+
+	hash = hash_str(path);
+	if (ctx.cfg.project_list)
+		hash += hash_str(ctx.cfg.project_list);
+	strbuf_addf(&cached_rc, "%s/rc-%8lx", ctx.cfg.cache_root, hash);
+
+	if (stat(cached_rc.buf, &st)) {
+		/* Nothing is cached, we need to scan without forking. And
+		 * if we fail to generate a cached repolist, we need to
+		 * invoke scan_tree manually.
+		 */
+		if (generate_cached_repolist(path, cached_rc.buf)) {
+			if (ctx.cfg.project_list)
+				scan_projects(path, ctx.cfg.project_list,
+					      repo_config);
+			else
+				scan_tree(path, repo_config);
+		}
+		goto out;
+	}
+
+	parse_configfile(cached_rc.buf, config_cb);
+
+	/* If the cached configfile hasn't expired, lets exit now */
+	age = time(NULL) - st.st_mtime;
+	if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
+		goto out;
+
+	/* The cached repolist has been parsed, but it was old. So lets
+	 * rescan the specified path and generate a new cached repolist
+	 * in a child-process to avoid latency for the current request.
+	 */
+	if (fork())
+		goto out;
+
+	exit(generate_cached_repolist(path, cached_rc.buf));
+out:
+	strbuf_release(&cached_rc);
+}
+
+static void cgit_parse_args(int argc, const char **argv)
+{
+	int i;
+	const char *arg;
+	int scan = 0;
+
+	for (i = 1; i < argc; i++) {
+		if (!strcmp(argv[i], "--version")) {
+			printf("CGit %s | https://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION);
+#ifndef HAVE_LINUX_SENDFILE
+			printf("[-] ");
+#else
+			printf("[+] ");
+#endif
+			printf("Linux sendfile() usage\n");
+
+			exit(0);
+		}
+		if (skip_prefix(argv[i], "--cache=", &arg)) {
+			ctx.cfg.cache_root = xstrdup(arg);
+		} else if (!strcmp(argv[i], "--nohttp")) {
+			ctx.env.no_http = "1";
+		} else if (skip_prefix(argv[i], "--query=", &arg)) {
+			ctx.qry.raw = xstrdup(arg);
+		} else if (skip_prefix(argv[i], "--repo=", &arg)) {
+			ctx.qry.repo = xstrdup(arg);
+		} else if (skip_prefix(argv[i], "--page=", &arg)) {
+			ctx.qry.page = xstrdup(arg);
+		} else if (skip_prefix(argv[i], "--head=", &arg)) {
+			ctx.qry.head = xstrdup(arg);
+			ctx.qry.has_symref = 1;
+		} else if (skip_prefix(argv[i], "--oid=", &arg)) {
+			ctx.qry.oid = xstrdup(arg);
+			ctx.qry.has_oid = 1;
+		} else if (skip_prefix(argv[i], "--ofs=", &arg)) {
+			ctx.qry.ofs = atoi(arg);
+		} else if (skip_prefix(argv[i], "--scan-tree=", &arg) ||
+		           skip_prefix(argv[i], "--scan-path=", &arg)) {
+			/*
+			 * HACK: The global snapshot bit mask defines the set
+			 * of allowed snapshot formats, but the config file
+			 * hasn't been parsed yet so the mask is currently 0.
+			 * By setting all bits high before scanning we make
+			 * sure that any in-repo cgitrc snapshot setting is
+			 * respected by scan_tree().
+			 *
+			 * NOTE: We assume that there aren't more than 8
+			 * different snapshot formats supported by cgit...
+			 */
+			ctx.cfg.snapshots = 0xFF;
+			scan++;
+			scan_tree(arg, repo_config);
+		}
+	}
+	if (scan) {
+		qsort(cgit_repolist.repos, cgit_repolist.count,
+			sizeof(struct cgit_repo), cmp_repos);
+		print_repolist(stdout, &cgit_repolist, 0);
+		exit(0);
+	}
+}
+
+static int calc_ttl(void)
+{
+	if (!ctx.repo)
+		return ctx.cfg.cache_root_ttl;
+
+	if (!ctx.qry.page)
+		return ctx.cfg.cache_repo_ttl;
+
+	if (!strcmp(ctx.qry.page, "about"))
+		return ctx.cfg.cache_about_ttl;
+
+	if (!strcmp(ctx.qry.page, "snapshot"))
+		return ctx.cfg.cache_snapshot_ttl;
+
+	if (ctx.qry.has_oid)
+		return ctx.cfg.cache_static_ttl;
+
+	if (ctx.qry.has_symref)
+		return ctx.cfg.cache_dynamic_ttl;
+
+	return ctx.cfg.cache_repo_ttl;
+}
+
+int cmd_main(int argc, const char **argv)
+{
+	const char *path;
+	int err, ttl;
+
+	atexit(cgit_cleanup_filters);
+
+	prepare_context();
+	cgit_repolist.length = 0;
+	cgit_repolist.count = 0;
+	cgit_repolist.repos = NULL;
+
+	cgit_parse_args(argc, argv);
+	parse_configfile(expand_macros(ctx.env.cgit_config), config_cb);
+	ctx.repo = NULL;
+	http_parse_querystring(ctx.qry.raw, querystring_cb);
+
+	/* If virtual-root isn't specified in cgitrc, lets pretend
+	 * that virtual-root equals SCRIPT_NAME, minus any possibly
+	 * trailing slashes.
+	 */
+	if (!ctx.cfg.virtual_root && ctx.cfg.script_name)
+		ctx.cfg.virtual_root = ensure_end(ctx.cfg.script_name, '/');
+
+	/* If no url parameter is specified on the querystring, lets
+	 * use PATH_INFO as url. This allows cgit to work with virtual
+	 * urls without the need for rewriterules in the webserver (as
+	 * long as PATH_INFO is included in the cache lookup key).
+	 */
+	path = ctx.env.path_info;
+	if (!ctx.qry.url && path) {
+		if (path[0] == '/')
+			path++;
+		ctx.qry.url = xstrdup(path);
+		if (ctx.qry.raw) {
+			char *newqry = fmtalloc("%s?%s", path, ctx.qry.raw);
+			free(ctx.qry.raw);
+			ctx.qry.raw = newqry;
+		} else
+			ctx.qry.raw = xstrdup(ctx.qry.url);
+		cgit_parse_url(ctx.qry.url);
+	}
+
+	/* Before we go any further, we set ctx.env.authenticated by checking to see
+	 * if the supplied cookie is valid. All cookies are valid if there is no
+	 * auth_filter. If there is an auth_filter, the filter decides. */
+	authenticate_cookie();
+
+	ttl = calc_ttl();
+	if (ttl < 0)
+		ctx.page.expires += 10 * 365 * 24 * 60 * 60; /* 10 years */
+	else
+		ctx.page.expires += ttl * 60;
+	if (!ctx.env.authenticated || (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD")))
+		ctx.cfg.cache_size = 0;
+	err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
+			    ctx.qry.raw, ttl, process_request);
+	cgit_cleanup_filters();
+	if (err)
+		cgit_print_error("Error processing page: %s (%d)",
+				 strerror(err), err);
+	return err;
+}
diff --git a/www/git.causal.agency/cgit/cgit.css b/www/git.causal.agency/cgit/cgit.css
new file mode 100644
index 00000000..f3dbb7a9
--- /dev/null
+++ b/www/git.causal.agency/cgit/cgit.css
@@ -0,0 +1,877 @@
+div#cgit {
+	padding: 0em;
+	margin: 0em;
+	font-family: sans-serif;
+	font-size: 10pt;
+	color: #333;
+	background: white;
+	padding: 4px;
+}
+
+div#cgit a {
+	color: blue;
+	text-decoration: none;
+}
+
+div#cgit a:hover {
+	text-decoration: underline;
+}
+
+div#cgit table {
+	border-collapse: collapse;
+}
+
+div#cgit table#header {
+	width: 100%;
+	margin-bottom: 1em;
+}
+
+div#cgit table#header td.logo {
+	width: 96px;
+	vertical-align: top;
+}
+
+div#cgit table#header td.main {
+	font-size: 250%;
+	padding-left: 10px;
+	white-space: nowrap;
+}
+
+div#cgit table#header td.main a {
+	color: #000;
+}
+
+div#cgit table#header td.form {
+	text-align: right;
+	vertical-align: bottom;
+	padding-right: 1em;
+	padding-bottom: 2px;
+	white-space: nowrap;
+}
+
+div#cgit table#header td.form form,
+div#cgit table#header td.form input,
+div#cgit table#header td.form select {
+	font-size: 90%;
+}
+
+div#cgit table#header td.sub {
+	color: #777;
+	border-top: solid 1px #ccc;
+	padding-left: 10px;
+}
+
+div#cgit table.tabs {
+	border-bottom: solid 3px #ccc;
+	border-collapse: collapse;
+	margin-top: 2em;
+	margin-bottom: 0px;
+	width: 100%;
+}
+
+div#cgit table.tabs td {
+	padding: 0px 1em;
+	vertical-align: bottom;
+}
+
+div#cgit table.tabs td a {
+	padding: 2px 0.25em;
+	color: #777;
+	font-size: 110%;
+}
+
+div#cgit table.tabs td a.active {
+	color: #000;
+	background-color: #ccc;
+}
+
+div#cgit table.tabs a[href^="http://"]:after, div#cgit table.tabs a[href^="https://"]:after {
+	content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhcJDQY+gm2TAAAAHWlUWHRDb21tZW50AAAAAABDcmVhdGVkIHdpdGggR0lNUGQuZQcAAABbSURBVAhbY2BABs4MU4CwhYHBh2Erww4wrGFQZHjI8B8IgUIscJWyDHcggltQhI4zGDCcRwhChPggHIggP1QoAVmQkSETrGoHsiAEsACtBYN0oDAMbgU6EBcAAL2eHUt4XUU4AAAAAElFTkSuQmCC);
+	opacity: 0.5;
+	margin: 0 0 0 5px;
+}
+
+div#cgit table.tabs td.form {
+	text-align: right;
+}
+
+div#cgit table.tabs td.form form {
+	padding-bottom: 2px;
+	font-size: 90%;
+	white-space: nowrap;
+}
+
+div#cgit table.tabs td.form input,
+div#cgit table.tabs td.form select {
+	font-size: 90%;
+}
+
+div#cgit div.path {
+	margin: 0px;
+	padding: 5px 2em 2px 2em;
+	color: #000;
+	background-color: #eee;
+}
+
+div#cgit div.content {
+	margin: 0px;
+	padding: 2em;
+	border-bottom: solid 3px #ccc;
+}
+
+
+div#cgit table.list {
+	width: 100%;
+	border: none;
+	border-collapse: collapse;
+}
+
+div#cgit table.list tr {
+	background: white;
+}
+
+div#cgit table.list tr.logheader {
+	background: #eee;
+}
+
+div#cgit table.list tr:nth-child(even) {
+	background: #f7f7f7;
+}
+
+div#cgit table.list tr:nth-child(odd) {
+	background: white;
+}
+
+div#cgit table.list tr:hover {
+	background: #eee;
+}
+
+div#cgit table.list tr.nohover {
+	background: white;
+}
+
+div#cgit table.list tr.nohover:hover {
+	background: white;
+}
+
+div#cgit table.list tr.nohover-highlight:hover:nth-child(even) {
+	background: #f7f7f7;
+}
+
+div#cgit table.list tr.nohover-highlight:hover:nth-child(odd) {
+	background: white;
+}
+
+div#cgit table.list th {
+	font-weight: bold;
+	/* color: #888;
+	border-top: dashed 1px #888;
+	border-bottom: dashed 1px #888;
+	*/
+	padding: 0.1em 0.5em 0.05em 0.5em;
+	vertical-align: baseline;
+}
+
+div#cgit table.list td {
+	border: none;
+	padding: 0.1em 0.5em 0.1em 0.5em;
+}
+
+div#cgit table.list td.commitgraph {
+	font-family: monospace;
+	white-space: pre;
+}
+
+div#cgit table.list td.commitgraph .column1 {
+	color: #a00;
+}
+
+div#cgit table.list td.commitgraph .column2 {
+	color: #0a0;
+}
+
+div#cgit table.list td.commitgraph .column3 {
+	color: #aa0;
+}
+
+div#cgit table.list td.commitgraph .column4 {
+	color: #00a;
+}
+
+div#cgit table.list td.commitgraph .column5 {
+	color: #a0a;
+}
+
+div#cgit table.list td.commitgraph .column6 {
+	color: #0aa;
+}
+
+div#cgit table.list td.logsubject {
+	font-family: monospace;
+	font-weight: bold;
+}
+
+div#cgit table.list td.logmsg {
+	font-family: monospace;
+	white-space: pre;
+	padding: 0 0.5em;
+}
+
+div#cgit table.list td a {
+	color: black;
+}
+
+div#cgit table.list td a.ls-dir {
+	font-weight: bold;
+	color: #00f;
+}
+
+div#cgit table.list td a:hover {
+	color: #00f;
+}
+
+div#cgit img {
+	border: none;
+}
+
+div#cgit input#switch-btn {
+	margin: 2px 0px 0px 0px;
+}
+
+div#cgit td#sidebar input.txt {
+	width: 100%;
+	margin: 2px 0px 0px 0px;
+}
+
+div#cgit table#grid {
+	margin: 0px;
+}
+
+div#cgit td#content {
+	vertical-align: top;
+	padding: 1em 2em 1em 1em;
+	border: none;
+}
+
+div#cgit div#summary {
+	vertical-align: top;
+	margin-bottom: 1em;
+}
+
+div#cgit table#downloads {
+	float: right;
+	border-collapse: collapse;
+	border: solid 1px #777;
+	margin-left: 0.5em;
+	margin-bottom: 0.5em;
+}
+
+div#cgit table#downloads th {
+	background-color: #ccc;
+}
+
+div#cgit div#blob {
+	border: solid 1px black;
+}
+
+div#cgit div.error {
+	color: red;
+	font-weight: bold;
+	margin: 1em 2em;
+}
+
+div#cgit a.ls-blob, div#cgit a.ls-dir, div#cgit .ls-mod {
+	font-family: monospace;
+}
+
+div#cgit td.ls-size {
+	text-align: right;
+	font-family: monospace;
+	width: 10em;
+}
+
+div#cgit td.ls-mode {
+	font-family: monospace;
+	width: 10em;
+}
+
+div#cgit table.blob {
+	margin-top: 0.5em;
+	border-top: solid 1px black;
+}
+
+div#cgit table.blob td.hashes,
+div#cgit table.blob td.lines {
+	margin: 0; padding: 0 0 0 0.5em;
+	vertical-align: top;
+	color: black;
+}
+
+div#cgit table.blob td.linenumbers {
+	margin: 0; padding: 0 0.5em 0 0.5em;
+	vertical-align: top;
+	text-align: right;
+	border-right: 1px solid gray;
+}
+
+div#cgit table.blob pre {
+	padding: 0; margin: 0;
+}
+
+div#cgit table.blob td.linenumbers a,
+div#cgit table.ssdiff td.lineno a {
+	color: gray;
+	text-align: right;
+	text-decoration: none;
+}
+
+div#cgit table.blob td.linenumbers a:hover,
+div#cgit table.ssdiff td.lineno a:hover {
+	color: black;
+}
+
+div#cgit table.blame td.hashes,
+div#cgit table.blame td.lines,
+div#cgit table.blame td.linenumbers {
+	padding: 0;
+}
+
+div#cgit table.blame td.hashes div.alt,
+div#cgit table.blame td.lines div.alt {
+	padding: 0 0.5em 0 0.5em;
+}
+
+div#cgit table.blame td.linenumbers div.alt {
+	padding: 0 0.5em 0 0;
+}
+
+div#cgit table.blame div.alt:nth-child(even) {
+	background: #eee;
+}
+
+div#cgit table.blame div.alt:nth-child(odd) {
+	background: white;
+}
+
+div#cgit table.blame td.lines > div {
+	position: relative;
+}
+
+div#cgit table.blame td.lines > div > pre {
+	padding: 0 0 0 0.5em;
+	position: absolute;
+	top: 0;
+}
+
+div#cgit table.bin-blob {
+	margin-top: 0.5em;
+	border: solid 1px black;
+}
+
+div#cgit table.bin-blob th {
+	font-family: monospace;
+	white-space: pre;
+	border: solid 1px #777;
+	padding: 0.5em 1em;
+}
+
+div#cgit table.bin-blob td {
+	font-family: monospace;
+	white-space: pre;
+	border-left: solid 1px #777;
+	padding: 0em 1em;
+}
+
+div#cgit table.nowrap td {
+	white-space: nowrap;
+}
+
+div#cgit table.commit-info {
+	border-collapse: collapse;
+	margin-top: 1.5em;
+}
+
+div#cgit div.cgit-panel {
+	float: right;
+	margin-top: 1.5em;
+}
+
+div#cgit div.cgit-panel table {
+	border-collapse: collapse;
+	border: solid 1px #aaa;
+	background-color: #eee;
+}
+
+div#cgit div.cgit-panel th {
+	text-align: center;
+}
+
+div#cgit div.cgit-panel td {
+	padding: 0.25em 0.5em;
+}
+
+div#cgit div.cgit-panel td.label {
+	padding-right: 0.5em;
+}
+
+div#cgit div.cgit-panel td.ctrl {
+	padding-left: 0.5em;
+}
+
+div#cgit table.commit-info th {
+	text-align: left;
+	font-weight: normal;
+	padding: 0.1em 1em 0.1em 0.1em;
+	vertical-align: top;
+}
+
+div#cgit table.commit-info td {
+	font-weight: normal;
+	padding: 0.1em 1em 0.1em 0.1em;
+}
+
+div#cgit div.commit-subject {
+	font-weight: bold;
+	font-size: 125%;
+	margin: 1.5em 0em 0.5em 0em;
+	padding: 0em;
+}
+
+div#cgit div.notes-header {
+	font-weight: bold;
+	padding-top: 1.5em;
+}
+
+div#cgit div.notes {
+	white-space: pre;
+	font-family: monospace;
+	border: solid 1px #ee9;
+	background-color: #ffd;
+	padding: 0.3em 2em 0.3em 1em;
+	float: left;
+}
+
+div#cgit div.notes-footer {
+	clear: left;
+}
+
+div#cgit div.diffstat-header {
+	font-weight: bold;
+	padding-top: 1.5em;
+}
+
+div#cgit table.diffstat {
+	border-collapse: collapse;
+	border: solid 1px #aaa;
+	background-color: #eee;
+}
+
+div#cgit table.diffstat th {
+	font-weight: normal;
+	text-align: left;
+	text-decoration: underline;
+	padding: 0.1em 1em 0.1em 0.1em;
+	font-size: 100%;
+}
+
+div#cgit table.diffstat td {
+	padding: 0.2em 0.2em 0.1em 0.1em;
+	font-size: 100%;
+	border: none;
+}
+
+div#cgit table.diffstat td.mode {
+	white-space: nowrap;
+}
+
+div#cgit table.diffstat td span.modechange {
+	padding-left: 1em;
+	color: red;
+}
+
+div#cgit table.diffstat td.add a {
+	color: green;
+}
+
+div#cgit table.diffstat td.del a {
+	color: red;
+}
+
+div#cgit table.diffstat td.upd a {
+	color: blue;
+}
+
+div#cgit table.diffstat td.graph {
+	width: 500px;
+	vertical-align: middle;
+}
+
+div#cgit table.diffstat td.graph table {
+	border: none;
+}
+
+div#cgit table.diffstat td.graph td {
+	padding: 0px;
+	border: 0px;
+	height: 7pt;
+}
+
+div#cgit table.diffstat td.graph td.add {
+	background-color: #5c5;
+}
+
+div#cgit table.diffstat td.graph td.rem {
+	background-color: #c55;
+}
+
+div#cgit div.diffstat-summary {
+	color: #888;
+	padding-top: 0.5em;
+}
+
+div#cgit table.diff {
+	width: 100%;
+}
+
+div#cgit table.diff td span.head {
+	font-weight: bold;
+	color: black;
+}
+
+div#cgit table.diff td span.hunk {
+	color: #009;
+}
+
+div#cgit table.diff td span.add {
+	color: green;
+}
+
+div#cgit table.diff td span.del {
+	color: red;
+}
+
+div#cgit .oid {
+	font-family: monospace;
+	font-size: 90%;
+}
+
+div#cgit .left {
+	text-align: left;
+}
+
+div#cgit .right {
+	text-align: right;
+}
+
+div#cgit table.list td.reposection {
+	font-style: italic;
+	color: #888;
+}
+
+div#cgit a.button {
+	font-size: 80%;
+}
+
+div#cgit a.primary {
+	font-size: 100%;
+}
+
+div#cgit a.secondary {
+	font-size: 90%;
+}
+
+div#cgit td.toplevel-repo {
+
+}
+
+div#cgit table.list td.sublevel-repo {
+	padding-left: 1.5em;
+}
+
+div#cgit ul.pager {
+	list-style-type: none;
+	text-align: center;
+	margin: 1em 0em 0em 0em;
+	padding: 0;
+}
+
+div#cgit ul.pager li {
+	display: inline-block;
+	margin: 0.25em 0.5em;
+}
+
+div#cgit ul.pager a {
+	color: #777;
+}
+
+div#cgit ul.pager .current {
+	font-weight: bold;
+}
+
+div#cgit span.age-mins {
+	font-weight: bold;
+	color: #080;
+}
+
+div#cgit span.age-hours {
+	color: #080;
+}
+
+div#cgit span.age-days {
+	color: #040;
+}
+
+div#cgit span.age-weeks {
+	color: #444;
+}
+
+div#cgit span.age-months {
+	color: #888;
+}
+
+div#cgit span.age-years {
+	color: #bbb;
+}
+
+div#cgit span.insertions {
+	color: #080;
+}
+
+div#cgit span.deletions {
+	color: #800;
+}
+
+div#cgit div.footer {
+	margin-top: 0.5em;
+	text-align: center;
+	font-size: 80%;
+	color: #ccc;
+}
+
+div#cgit div.footer a {
+	color: #ccc;
+	text-decoration: none;
+}
+
+div#cgit div.footer a:hover {
+	text-decoration: underline;
+}
+
+div#cgit a.branch-deco {
+	color: #000;
+	padding: 0px 0.25em;
+	background-color: #88ff88;
+	border: solid 1px #007700;
+}
+
+div#cgit a.tag-deco {
+	color: #000;
+	padding: 0px 0.25em;
+	background-color: #ffff88;
+	border: solid 1px #777700;
+}
+
+div#cgit a.tag-annotated-deco {
+	color: #000;
+	padding: 0px 0.25em;
+	background-color: #ffcc88;
+	border: solid 1px #777700;
+}
+
+div#cgit a.remote-deco {
+	color: #000;
+	padding: 0px 0.25em;
+	background-color: #ccccff;
+	border: solid 1px #000077;
+}
+
+div#cgit a.deco {
+	color: #000;
+	padding: 0px 0.25em;
+	background-color: #ff8888;
+	border: solid 1px #770000;
+}
+
+div#cgit div.commit-subject a.branch-deco,
+div#cgit div.commit-subject a.tag-deco,
+div#cgit div.commit-subject a.tag-annotated-deco,
+div#cgit div.commit-subject a.remote-deco,
+div#cgit div.commit-subject a.deco {
+	font-size: 75%;
+}
+
+div#cgit table.stats {
+	border: solid 1px black;
+	border-collapse: collapse;
+}
+
+div#cgit table.stats th {
+	text-align: left;
+	padding: 1px 0.5em;
+	background-color: #eee;
+	border: solid 1px black;
+}
+
+div#cgit table.stats td {
+	text-align: right;
+	padding: 1px 0.5em;
+	border: solid 1px black;
+}
+
+div#cgit table.stats td.total {
+	font-weight: bold;
+	text-align: left;
+}
+
+div#cgit table.stats td.sum {
+	color: #c00;
+	font-weight: bold;
+/*	background-color: #eee; */
+}
+
+div#cgit table.stats td.left {
+	text-align: left;
+}
+
+div#cgit table.vgraph {
+	border-collapse: separate;
+	border: solid 1px black;
+	height: 200px;
+}
+
+div#cgit table.vgraph th {
+	background-color: #eee;
+	font-weight: bold;
+	border: solid 1px white;
+	padding: 1px 0.5em;
+}
+
+div#cgit table.vgraph td {
+	vertical-align: bottom;
+	padding: 0px 10px;
+}
+
+div#cgit table.vgraph div.bar {
+	background-color: #eee;
+}
+
+div#cgit table.hgraph {
+	border: solid 1px black;
+	width: 800px;
+}
+
+div#cgit table.hgraph th {
+	background-color: #eee;
+	font-weight: bold;
+	border: solid 1px black;
+	padding: 1px 0.5em;
+}
+
+div#cgit table.hgraph td {
+	vertical-align: middle;
+	padding: 2px 2px;
+}
+
+div#cgit table.hgraph div.bar {
+	background-color: #eee;
+	height: 1em;
+}
+
+div#cgit table.ssdiff {
+	width: 100%;
+}
+
+div#cgit table.ssdiff td {
+	font-size: 75%;
+	font-family: monospace;
+	white-space: pre;
+	padding: 1px 4px 1px 4px;
+	border-left: solid 1px #aaa;
+	border-right: solid 1px #aaa;
+}
+
+div#cgit table.ssdiff td.add {
+	color: black;
+	background: #cfc;
+	min-width: 50%;
+}
+
+div#cgit table.ssdiff td.add_dark {
+	color: black;
+	background: #aca;
+	min-width: 50%;
+}
+
+div#cgit table.ssdiff span.add {
+	background: #cfc;
+	font-weight: bold;
+}
+
+div#cgit table.ssdiff td.del {
+	color: black;
+	background: #fcc;
+	min-width: 50%;
+}
+
+div#cgit table.ssdiff td.del_dark {
+	color: black;
+	background: #caa;
+	min-width: 50%;
+}
+
+div#cgit table.ssdiff span.del {
+	background: #fcc;
+	font-weight: bold;
+}
+
+div#cgit table.ssdiff td.changed {
+	color: black;
+	background: #ffc;
+	min-width: 50%;
+}
+
+div#cgit table.ssdiff td.changed_dark {
+	color: black;
+	background: #cca;
+	min-width: 50%;
+}
+
+div#cgit table.ssdiff td.lineno {
+	color: black;
+	background: #eee;
+	text-align: right;
+	width: 3em;
+	min-width: 3em;
+}
+
+div#cgit table.ssdiff td.hunk {
+	color: black;
+	background: #ccf;
+	border-top: solid 1px #aaa;
+	border-bottom: solid 1px #aaa;
+}
+
+div#cgit table.ssdiff td.head {
+	border-top: solid 1px #aaa;
+	border-bottom: solid 1px #aaa;
+}
+
+div#cgit table.ssdiff td.head div.head {
+	font-weight: bold;
+	color: black;
+}
+
+div#cgit table.ssdiff td.foot {
+	border-top: solid 1px #aaa;
+	border-left: none;
+	border-right: none;
+	border-bottom: none;
+}
+
+div#cgit table.ssdiff td.space {
+	border: none;
+}
+
+div#cgit table.ssdiff td.space div {
+	min-height: 3em;
+}
diff --git a/www/git.causal.agency/cgit/cgit.h b/www/git.causal.agency/cgit/cgit.h
new file mode 100644
index 00000000..72fcd849
--- /dev/null
+++ b/www/git.causal.agency/cgit/cgit.h
@@ -0,0 +1,397 @@
+#ifndef CGIT_H
+#define CGIT_H
+
+
+#include <git-compat-util.h>
+#include <stdbool.h>
+
+#include <cache.h>
+#include <grep.h>
+#include <object.h>
+#include <object-store.h>
+#include <tree.h>
+#include <commit.h>
+#include <tag.h>
+#include <diff.h>
+#include <diffcore.h>
+#include <strvec.h>
+#include <refs.h>
+#include <revision.h>
+#include <log-tree.h>
+#include <archive.h>
+#include <string-list.h>
+#include <xdiff-interface.h>
+#include <xdiff/xdiff.h>
+#include <utf8.h>
+#include <notes.h>
+#include <graph.h>
+
+/* Add isgraph(x) to Git's sane ctype support (see git-compat-util.h) */
+#undef isgraph
+#define isgraph(x) (isprint((x)) && !isspace((x)))
+
+
+/*
+ * Limits used for relative dates
+ */
+#define TM_MIN    60
+#define TM_HOUR  (TM_MIN * 60)
+#define TM_DAY   (TM_HOUR * 24)
+#define TM_WEEK  (TM_DAY * 7)
+#define TM_YEAR  (TM_DAY * 365)
+#define TM_MONTH (TM_YEAR / 12.0)
+
+
+/*
+ * Default encoding
+ */
+#define PAGE_ENCODING "UTF-8"
+
+#define BIT(x)	(1U << (x))
+
+typedef void (*configfn)(const char *name, const char *value);
+typedef void (*filepair_fn)(struct diff_filepair *pair);
+typedef void (*linediff_fn)(char *line, int len);
+
+typedef enum {
+	DIFF_UNIFIED, DIFF_SSDIFF, DIFF_STATONLY
+} diff_type;
+
+typedef enum {
+	ABOUT, COMMIT, SOURCE, EMAIL, AUTH, OWNER
+} filter_type;
+
+struct cgit_filter {
+	int (*open)(struct cgit_filter *, va_list ap);
+	int (*close)(struct cgit_filter *);
+	void (*fprintf)(struct cgit_filter *, FILE *, const char *prefix);
+	void (*cleanup)(struct cgit_filter *);
+	int argument_count;
+};
+
+struct cgit_exec_filter {
+	struct cgit_filter base;
+	char *cmd;
+	char **argv;
+	int old_stdout;
+	int pid;
+};
+
+struct cgit_repo {
+	char *url;
+	char *name;
+	char *path;
+	char *desc;
+	char *extra_head_content;
+	char *owner;
+	char *homepage;
+	char *defbranch;
+	char *module_link;
+	struct string_list readme;
+	char *section;
+	char *clone_url;
+	char *logo;
+	char *logo_link;
+	char *snapshot_prefix;
+	int snapshots;
+	int enable_blame;
+	int enable_commit_graph;
+	int enable_log_filecount;
+	int enable_log_linecount;
+	int enable_remote_branches;
+	int enable_subject_links;
+	int enable_html_serving;
+	int max_stats;
+	int branch_sort;
+	int commit_sort;
+	time_t mtime;
+	struct cgit_filter *about_filter;
+	struct cgit_filter *commit_filter;
+	struct cgit_filter *source_filter;
+	struct cgit_filter *email_filter;
+	struct cgit_filter *owner_filter;
+	struct string_list submodules;
+	int hide;
+	int ignore;
+};
+
+typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name,
+	      const char *value);
+
+struct cgit_repolist {
+	int length;
+	int count;
+	struct cgit_repo *repos;
+};
+
+struct commitinfo {
+	struct commit *commit;
+	char *author;
+	char *author_email;
+	unsigned long author_date;
+	int author_tz;
+	char *committer;
+	char *committer_email;
+	unsigned long committer_date;
+	int committer_tz;
+	char *subject;
+	char *msg;
+	char *msg_encoding;
+};
+
+struct taginfo {
+	char *tagger;
+	char *tagger_email;
+	unsigned long tagger_date;
+	int tagger_tz;
+	char *msg;
+};
+
+struct refinfo {
+	const char *refname;
+	struct object *object;
+	union {
+		struct taginfo *tag;
+		struct commitinfo *commit;
+	};
+};
+
+struct reflist {
+	struct refinfo **refs;
+	int alloc;
+	int count;
+};
+
+struct cgit_query {
+	int has_symref;
+	int has_oid;
+	int has_difftype;
+	char *raw;
+	char *repo;
+	char *page;
+	char *search;
+	char *grep;
+	char *head;
+	char *oid;
+	char *oid2;
+	char *path;
+	char *name;
+	char *url;
+	char *period;
+	int   ofs;
+	int nohead;
+	char *sort;
+	int showmsg;
+	diff_type difftype;
+	int show_all;
+	int context;
+	int ignorews;
+	int follow;
+	char *vpath;
+};
+
+struct cgit_config {
+	char *agefile;
+	char *cache_root;
+	char *clone_prefix;
+	char *clone_url;
+	char *css;
+	char *favicon;
+	char *footer;
+	char *head_include;
+	char *header;
+	char *logo;
+	char *logo_link;
+	char *mimetype_file;
+	char *module_link;
+	char *project_list;
+	struct string_list readme;
+	char *robots;
+	char *root_title;
+	char *root_desc;
+	char *root_readme;
+	char *script_name;
+	char *section;
+	char *repository_sort;
+	char *virtual_root;	/* Always ends with '/'. */
+	char *strict_export;
+	int cache_size;
+	int cache_dynamic_ttl;
+	int cache_max_create_time;
+	int cache_repo_ttl;
+	int cache_root_ttl;
+	int cache_scanrc_ttl;
+	int cache_static_ttl;
+	int cache_about_ttl;
+	int cache_snapshot_ttl;
+	int case_sensitive_sort;
+	int embedded;
+	int enable_filter_overrides;
+	int enable_follow_links;
+	int enable_http_clone;
+	int enable_index_links;
+	int enable_index_owner;
+	int enable_blame;
+	int enable_commit_graph;
+	int enable_log_filecount;
+	int enable_log_linecount;
+	int enable_remote_branches;
+	int enable_subject_links;
+	int enable_html_serving;
+	int enable_tree_linenumbers;
+	int enable_git_config;
+	int local_time;
+	int max_atom_items;
+	int max_repo_count;
+	int max_commit_count;
+	int max_lock_attempts;
+	int max_msg_len;
+	int max_repodesc_len;
+	int max_blob_size;
+	int max_stats;
+	int noplainemail;
+	int noheader;
+	int renamelimit;
+	int remove_suffix;
+	int scan_hidden_path;
+	int section_from_path;
+	int snapshots;
+	int section_sort;
+	int summary_branches;
+	int summary_log;
+	int summary_tags;
+	diff_type difftype;
+	int branch_sort;
+	int commit_sort;
+	struct string_list mimetypes;
+	struct cgit_filter *about_filter;
+	struct cgit_filter *commit_filter;
+	struct cgit_filter *source_filter;
+	struct cgit_filter *email_filter;
+	struct cgit_filter *owner_filter;
+	struct cgit_filter *auth_filter;
+};
+
+struct cgit_page {
+	time_t modified;
+	time_t expires;
+	size_t size;
+	const char *mimetype;
+	const char *charset;
+	const char *filename;
+	const char *etag;
+	const char *title;
+	int status;
+	const char *statusmsg;
+};
+
+struct cgit_environment {
+	const char *cgit_config;
+	const char *http_host;
+	const char *https;
+	const char *no_http;
+	const char *path_info;
+	const char *query_string;
+	const char *request_method;
+	const char *script_name;
+	const char *server_name;
+	const char *server_port;
+	const char *http_cookie;
+	const char *http_referer;
+	unsigned int content_length;
+	int authenticated;
+};
+
+struct cgit_context {
+	struct cgit_environment env;
+	struct cgit_query qry;
+	struct cgit_config cfg;
+	struct cgit_repo *repo;
+	struct cgit_page page;
+};
+
+typedef int (*write_archive_fn_t)(const char *, const char *);
+
+struct cgit_snapshot_format {
+	const char *suffix;
+	const char *mimetype;
+	write_archive_fn_t write_func;
+};
+
+extern const char *cgit_version;
+
+extern struct cgit_repolist cgit_repolist;
+extern struct cgit_context ctx;
+extern const struct cgit_snapshot_format cgit_snapshot_formats[];
+
+extern char *cgit_default_repo_desc;
+extern struct cgit_repo *cgit_add_repo(const char *url);
+extern struct cgit_repo *cgit_get_repoinfo(const char *url);
+extern void cgit_repo_config_cb(const char *name, const char *value);
+
+extern int chk_zero(int result, char *msg);
+extern int chk_positive(int result, char *msg);
+extern int chk_non_negative(int result, char *msg);
+
+extern char *trim_end(const char *str, char c);
+extern char *ensure_end(const char *str, char c);
+
+extern void strbuf_ensure_end(struct strbuf *sb, char c);
+
+extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
+extern void cgit_free_reflist_inner(struct reflist *list);
+extern int cgit_refs_cb(const char *refname, const struct object_id *oid,
+			int flags, void *cb_data);
+
+extern void cgit_free_commitinfo(struct commitinfo *info);
+extern void cgit_free_taginfo(struct taginfo *info);
+
+void cgit_diff_tree_cb(struct diff_queue_struct *q,
+		       struct diff_options *options, void *data);
+
+extern int cgit_diff_files(const struct object_id *old_oid,
+			   const struct object_id *new_oid,
+			   unsigned long *old_size, unsigned long *new_size,
+			   int *binary, int context, int ignorews,
+			   linediff_fn fn);
+
+extern void cgit_diff_tree(const struct object_id *old_oid,
+			   const struct object_id *new_oid,
+			   filepair_fn fn, const char *prefix, int ignorews);
+
+extern void cgit_diff_commit(struct commit *commit, filepair_fn fn,
+			     const char *prefix);
+
+__attribute__((format (printf,1,2)))
+extern char *fmt(const char *format,...);
+
+__attribute__((format (printf,1,2)))
+extern char *fmtalloc(const char *format,...);
+
+extern struct commitinfo *cgit_parse_commit(struct commit *commit);
+extern struct taginfo *cgit_parse_tag(struct tag *tag);
+extern void cgit_parse_url(const char *url);
+
+extern const char *cgit_repobasename(const char *reponame);
+
+extern int cgit_parse_snapshots_mask(const char *str);
+extern const struct object_id *cgit_snapshot_get_sig(const char *ref,
+						     const struct cgit_snapshot_format *f);
+extern const unsigned cgit_snapshot_format_bit(const struct cgit_snapshot_format *f);
+
+extern int cgit_open_filter(struct cgit_filter *filter, ...);
+extern int cgit_close_filter(struct cgit_filter *filter);
+extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix);
+extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv);
+extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype);
+extern void cgit_cleanup_filters(void);
+
+extern void cgit_prepare_repo_env(struct cgit_repo * repo);
+
+extern int readfile(const char *path, char **buf, size_t *size);
+
+extern char *expand_macros(const char *txt);
+
+extern char *get_mimetype_for_filename(const char *filename);
+
+#endif /* CGIT_H */
diff --git a/www/git.causal.agency/cgit/cgit.mk b/www/git.causal.agency/cgit/cgit.mk
new file mode 100644
index 00000000..5b9ed5be
--- /dev/null
+++ b/www/git.causal.agency/cgit/cgit.mk
@@ -0,0 +1,114 @@
+# This Makefile is run in the "git" directory in order to re-use Git's
+# build variables and operating system detection.  Hence all files in
+# CGit's directory must be prefixed with "../".
+include Makefile
+
+CGIT_PREFIX = ../
+
+-include $(CGIT_PREFIX)cgit.conf
+
+# The CGIT_* variables are inherited when this file is called from the
+# main Makefile - they are defined there.
+
+$(CGIT_PREFIX)VERSION: force-version
+	@cd $(CGIT_PREFIX) && '$(SHELL_PATH_SQ)' ./gen-version.sh "$(CGIT_VERSION)"
+-include $(CGIT_PREFIX)VERSION
+.PHONY: force-version
+
+# CGIT_CFLAGS is a separate variable so that we can track it separately
+# and avoid rebuilding all of Git when these variables change.
+CGIT_CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
+CGIT_CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
+CGIT_CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
+
+PKG_CONFIG ?= pkg-config
+
+ifdef NO_C99_FORMAT
+	CFLAGS += -DNO_C99_FORMAT
+endif
+
+# Add -ldl to linker flags on systems that commonly use GNU libc.
+ifneq (,$(filter $(uname_S),Linux GNU GNU/kFreeBSD))
+	CGIT_LIBS += -ldl
+endif
+
+# glibc 2.1+ offers sendfile which the most common C library on Linux
+ifeq ($(uname_S),Linux)
+	HAVE_LINUX_SENDFILE = YesPlease
+endif
+
+ifdef HAVE_LINUX_SENDFILE
+	CGIT_CFLAGS += -DHAVE_LINUX_SENDFILE
+endif
+
+CGIT_OBJ_NAMES += cgit.o
+CGIT_OBJ_NAMES += cache.o
+CGIT_OBJ_NAMES += cmd.o
+CGIT_OBJ_NAMES += configfile.o
+CGIT_OBJ_NAMES += filter.o
+CGIT_OBJ_NAMES += html.o
+CGIT_OBJ_NAMES += parsing.o
+CGIT_OBJ_NAMES += scan-tree.o
+CGIT_OBJ_NAMES += shared.o
+CGIT_OBJ_NAMES += ui-atom.o
+CGIT_OBJ_NAMES += ui-blame.o
+CGIT_OBJ_NAMES += ui-blob.o
+CGIT_OBJ_NAMES += ui-clone.o
+CGIT_OBJ_NAMES += ui-commit.o
+CGIT_OBJ_NAMES += ui-diff.o
+CGIT_OBJ_NAMES += ui-log.o
+CGIT_OBJ_NAMES += ui-patch.o
+CGIT_OBJ_NAMES += ui-plain.o
+CGIT_OBJ_NAMES += ui-refs.o
+CGIT_OBJ_NAMES += ui-repolist.o
+CGIT_OBJ_NAMES += ui-shared.o
+CGIT_OBJ_NAMES += ui-snapshot.o
+CGIT_OBJ_NAMES += ui-ssdiff.o
+CGIT_OBJ_NAMES += ui-stats.o
+CGIT_OBJ_NAMES += ui-summary.o
+CGIT_OBJ_NAMES += ui-tag.o
+CGIT_OBJ_NAMES += ui-tree.o
+
+CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES))
+
+# Only cgit.c reference CGIT_VERSION so we only rebuild its objects when the
+# version changes.
+CGIT_VERSION_OBJS := $(addprefix $(CGIT_PREFIX),cgit.o cgit.sp)
+$(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION
+$(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \
+	-DCGIT_VERSION='"$(CGIT_VERSION)"'
+
+# Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not
+# handled by that and we must handle them ourselves.
+cgit_dep_files := $(foreach f,$(CGIT_OBJS),$(dir $f).depend/$(notdir $f).d)
+cgit_dep_files_present := $(wildcard $(cgit_dep_files))
+ifneq ($(cgit_dep_files_present),)
+include $(cgit_dep_files_present)
+endif
+
+ifeq ($(wildcard $(CGIT_PREFIX).depend),)
+missing_dep_dirs += $(CGIT_PREFIX).depend
+endif
+
+$(CGIT_PREFIX).depend:
+	@mkdir -p $@
+
+$(CGIT_PREFIX)CGIT-CFLAGS: FORCE
+	@FLAGS='$(subst ','\'',$(CGIT_CFLAGS))'; \
+	    if test x"$$FLAGS" != x"`cat ../CGIT-CFLAGS 2>/dev/null`" ; then \
+		echo 1>&2 "    * new CGit build flags"; \
+		echo "$$FLAGS" >$(CGIT_PREFIX)CGIT-CFLAGS; \
+            fi
+
+$(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs)
+	$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $<
+
+$(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS)
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS)
+
+CGIT_SP_OBJS := $(patsubst %.o,%.sp,$(CGIT_OBJS))
+
+$(CGIT_SP_OBJS): %.sp: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS FORCE
+	$(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $(SPARSE_FLAGS) $<
+
+cgit-sparse: $(CGIT_SP_OBJS)
diff --git a/www/git.causal.agency/cgit/cgit.png b/www/git.causal.agency/cgit/cgit.png
new file mode 100644
index 00000000..425528ee
--- /dev/null
+++ b/www/git.causal.agency/cgit/cgit.png
Binary files differdiff --git a/www/git.causal.agency/cgit/cgitrc.5.txt b/www/git.causal.agency/cgit/cgitrc.5.txt
new file mode 100644
index 00000000..8d663952
--- /dev/null
+++ b/www/git.causal.agency/cgit/cgitrc.5.txt
@@ -0,0 +1,977 @@
+:man source:   cgit
+:man manual:   cgit
+
+CGITRC(5)
+========
+
+
+NAME
+----
+cgitrc - runtime configuration for cgit
+
+
+SYNOPSIS
+--------
+Cgitrc contains all runtime settings for cgit, including the list of git
+repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank
+lines, and lines starting with '#', are ignored.
+
+
+LOCATION
+--------
+The default location of cgitrc, defined at compile time, is /etc/cgitrc. At
+runtime, cgit will consult the environment variable CGIT_CONFIG and, if
+defined, use its value instead.
+
+
+GLOBAL SETTINGS
+---------------
+about-filter::
+	Specifies a command which will be invoked to format the content of
+	about pages (both top-level and for each repository). The command will
+	get the content of the about-file on its STDIN, the name of the file
+	as the first argument, and the STDOUT from the command will be
+	included verbatim on the about page. Default value: none. See
+	also: "FILTER API".
+
+agefile::
+	Specifies a path, relative to each repository path, which can be used
+	to specify the date and time of the youngest commit in the repository.
+	The first line in the file is used as input to the "parse_date"
+	function in libgit. Recommended timestamp-format is "yyyy-mm-dd
+	hh:mm:ss". You may want to generate this file from a post-receive
+	hook. Default value: "info/web/last-modified".
+
+auth-filter::
+	Specifies a command that will be invoked for authenticating repository
+	access. Receives quite a few arguments, and data on both stdin and
+	stdout for authentication processing. Details follow later in this
+	document. If no auth-filter is specified, no authentication is
+	performed. Default value: none. See also: "FILTER API".
+
+branch-sort::
+	Flag which, when set to "age", enables date ordering in the branch ref
+	list, and when set to "name" enables ordering by branch name. Default
+	value: "name".
+
+cache-about-ttl::
+	Number which specifies the time-to-live, in minutes, for the cached
+	version of the repository about page. See also: "CACHE". Default
+	value: "15".
+
+cache-dynamic-ttl::
+	Number which specifies the time-to-live, in minutes, for the cached
+	version of repository pages accessed without a fixed SHA1. See also:
+	"CACHE". Default value: "5".
+
+cache-repo-ttl::
+	Number which specifies the time-to-live, in minutes, for the cached
+	version of the repository summary page. See also: "CACHE". Default
+	value: "5".
+
+cache-root::
+	Path used to store the cgit cache entries. Default value:
+	"/var/cache/cgit". See also: "MACRO EXPANSION".
+
+cache-root-ttl::
+	Number which specifies the time-to-live, in minutes, for the cached
+	version of the repository index page. See also: "CACHE". Default
+	value: "5".
+
+cache-scanrc-ttl::
+	Number which specifies the time-to-live, in minutes, for the result
+	of scanning a path for git repositories. See also: "CACHE". Default
+	value: "15".
+
+case-sensitive-sort::
+	Sort items in the repo list case sensitively. Default value: "1".
+	See also: repository-sort, section-sort.
+
+cache-size::
+	The maximum number of entries in the cgit cache. When set to "0",
+	caching is disabled. See also: "CACHE". Default value: "0"
+
+cache-snapshot-ttl::
+	Number which specifies the time-to-live, in minutes, for the cached
+	version of snapshots. See also: "CACHE". Default value: "5".
+
+cache-static-ttl::
+	Number which specifies the time-to-live, in minutes, for the cached
+	version of repository pages accessed with a fixed SHA1. See also:
+	"CACHE". Default value: -1".
+
+clone-prefix::
+	Space-separated list of common prefixes which, when combined with a
+	repository url, generates valid clone urls for the repository. This
+	setting is only used if `repo.clone-url` is unspecified. Default value:
+	none.
+
+clone-url::
+	Space-separated list of clone-url templates. This setting is only
+	used if `repo.clone-url` is unspecified. Default value: none. See
+	also: "MACRO EXPANSION", "FILTER API".
+
+commit-filter::
+	Specifies a command which will be invoked to format commit messages.
+	The command will get the message on its STDIN, and the STDOUT from the
+	command will be included verbatim as the commit message, i.e. this can
+	be used to implement bugtracker integration. Default value: none.
+	See also: "FILTER API".
+
+commit-sort::
+	Flag which, when set to "date", enables strict date ordering in the
+	commit log, and when set to "topo" enables strict topological
+	ordering. If unset, the default ordering of "git log" is used. Default
+	value: unset.
+
+css::
+	Url which specifies the css document to include in all cgit pages.
+	Default value: "/cgit.css".
+
+email-filter::
+	Specifies a command which will be invoked to format names and email
+	address of committers, authors, and taggers, as represented in various
+	places throughout the cgit interface. This command will receive an
+	email address and an origin page string as its command line arguments,
+	and the text to format on STDIN. It is to write the formatted text back
+	out onto STDOUT. Default value: none. See also: "FILTER API".
+
+embedded::
+	Flag which, when set to "1", will make cgit generate a html fragment
+	suitable for embedding in other html pages. Default value: none. See
+	also: "noheader".
+
+enable-blame::
+	Flag which, when set to "1", will allow cgit to provide a "blame" page
+	for files, and will make it generate links to that page in appropriate
+	places. Default value: "0".
+
+enable-commit-graph::
+	Flag which, when set to "1", will make cgit print an ASCII-art commit
+	history graph to the left of the commit messages in the repository
+	log page. Default value: "0".
+
+enable-filter-overrides::
+	Flag which, when set to "1", allows all filter settings to be
+	overridden in repository-specific cgitrc files. Default value: none.
+
+enable-follow-links::
+	Flag which, when set to "1", allows users to follow a file in the log
+	view.  Default value: "0".
+
+enable-git-config::
+	Flag which, when set to "1", will allow cgit to use git config to set
+	any repo specific settings. This option is used in conjunction with
+	"scan-path", and must be defined prior, to augment repo-specific
+	settings. The keys gitweb.owner, gitweb.category, gitweb.description,
+	and gitweb.homepage will map to the cgit keys repo.owner, repo.section,
+	repo.desc, and repo.homepage respectively. All git config keys that begin
+	with "cgit." will be mapped to the corresponding "repo." key in cgit.
+	Default value: "0". See also: scan-path, section-from-path.
+
+enable-http-clone::
+	If set to "1", cgit will act as a dumb HTTP endpoint for git clones.
+	You can add "http://$HTTP_HOST$SCRIPT_NAME/$CGIT_REPO_URL" to clone-url
+	to expose this feature. If you use an alternate way of serving git
+	repositories, you may wish to disable this. Default value: "1".
+
+enable-html-serving::
+	Flag which, when set to "1", will allow the /plain handler to serve
+	mimetype headers that result in the file being treated as HTML by the
+	browser. When set to "0", such file types are returned instead as
+	text/plain or application/octet-stream. Default value: "0". See also:
+	"repo.enable-html-serving".
+
+enable-index-links::
+	Flag which, when set to "1", will make cgit generate extra links for
+	each repo in the repository index (specifically, to the "summary",
+	"commit" and "tree" pages). Default value: "0".
+
+enable-index-owner::
+	Flag which, when set to "1", will make cgit display the owner of
+	each repo in the repository index. Default value: "1".
+
+enable-log-filecount::
+	Flag which, when set to "1", will make cgit print the number of
+	modified files for each commit on the repository log page. Default
+	value: "0".
+
+enable-log-linecount::
+	Flag which, when set to "1", will make cgit print the number of added
+	and removed lines for each commit on the repository log page. Default
+	value: "0".
+
+enable-remote-branches::
+	Flag which, when set to "1", will make cgit display remote branches
+	in the summary and refs views. Default value: "0". See also:
+	"repo.enable-remote-branches".
+
+enable-subject-links::
+	Flag which, when set to "1", will make cgit use the subject of the
+	parent commit as link text when generating links to parent commits
+	in commit view. Default value: "0". See also:
+	"repo.enable-subject-links".
+
+enable-tree-linenumbers::
+	Flag which, when set to "1", will make cgit generate linenumber links
+	for plaintext blobs printed in the tree view. Default value: "1".
+
+favicon::
+	Url used as link to a shortcut icon for cgit. It is suggested to use
+	the value "/favicon.ico" since certain browsers will ignore other
+	values. Default value: "/favicon.ico".
+
+footer::
+	The content of the file specified with this option will be included
+	verbatim at the bottom of all pages (i.e. it replaces the standard
+	"generated by..." message. Default value: none.
+
+head-include::
+	The content of the file specified with this option will be included
+	verbatim in the html HEAD section on all pages. Default value: none.
+
+header::
+	The content of the file specified with this option will be included
+	verbatim at the top of all pages. Default value: none.
+
+include::
+	Name of a configfile to include before the rest of the current config-
+	file is parsed. Default value: none. See also: "MACRO EXPANSION".
+
+local-time::
+	Flag which, if set to "1", makes cgit print commit and tag times in the
+	servers timezone. Default value: "0".
+
+logo::
+	Url which specifies the source of an image which will be used as a logo
+	on all cgit pages. Default value: "/cgit.png".
+
+logo-link::
+	Url loaded when clicking on the cgit logo image. If unspecified the
+	calculated url of the repository index page will be used. Default
+	value: none.
+
+max-atom-items::
+	Specifies the number of items to display in atom feeds view. Default
+	value: "10".
+
+max-blob-size::
+	Specifies the maximum size of a blob to display HTML for in KBytes.
+	Default value: "0" (limit disabled).
+
+max-commit-count::
+	Specifies the number of entries to list per page in "log" view. Default
+	value: "50".
+
+max-message-length::
+	Specifies the maximum number of commit message characters to display in
+	"log" view. Default value: "80".
+
+max-repo-count::
+	Specifies the number of entries to list per page on the	repository
+	index page. Default value: "50".
+
+max-repodesc-length::
+	Specifies the maximum number of repo description characters to display
+	on the repository index page. Default value: "80".
+
+max-stats::
+	Set the default maximum statistics period. Valid values are "week",
+	"month", "quarter" and "year". If unspecified, statistics are
+	disabled. Default value: none. See also: "repo.max-stats".
+
+mimetype.<ext>::
+	Set the mimetype for the specified filename extension. This is used
+	by the `plain` command when returning blob content.
+
+mimetype-file::
+	Specifies the file to use for automatic mimetype lookup. If specified
+	then this field is used as a fallback when no "mimetype.<ext>" match is
+	found. If unspecified then no such lookup is performed. The typical file
+	to use on a Linux system is /etc/mime.types. The format of the file must
+	comply to:
+	- a comment line is an empty line or a line starting with a hash (#),
+	  optionally preceded by whitespace
+	- a non-comment line starts with the mimetype (like image/png), followed
+	  by one or more file extensions (like jpg), all separated by whitespace
+	Default value: none. See also: "mimetype.<ext>".
+
+module-link::
+	Text which will be used as the formatstring for a hyperlink when a
+	submodule is printed in a directory listing. The arguments for the
+	formatstring are the path and SHA1 of the submodule commit. Default
+	value: none.
+
+noplainemail::
+	If set to "1" showing full author email addresses will be disabled.
+	Default value: "0".
+
+noheader::
+	Flag which, when set to "1", will make cgit omit the standard header
+	on all pages. Default value: none. See also: "embedded".
+
+owner-filter::
+	Specifies a command which will be invoked to format the Owner
+	column of the main page.  The command will get the owner on STDIN,
+	and the STDOUT from the command will be included verbatim in the
+	table.  This can be used to link to additional context such as an
+	owners home page.  When active this filter is used instead of the
+	default owner query url.  Default value: none.
+	See also: "FILTER API".
+
+project-list::
+	A list of subdirectories inside of scan-path, relative to it, that
+	should loaded as git repositories. This must be defined prior to
+	scan-path. Default value: none. See also: scan-path, "MACRO
+	EXPANSION".
+
+readme::
+	Text which will be used as default value for "repo.readme". Multiple
+	config keys may be specified, and cgit will use the first found file
+	in this list. This is useful in conjunction with scan-path. Default
+	value: none. See also: scan-path, repo.readme.
+
+remove-suffix::
+	If set to "1" and scan-path is enabled, if any repositories are found
+	with a suffix of ".git", this suffix will be removed for the url and
+	name. This must be defined prior to scan-path. Default value: "0".
+	See also: scan-path.
+
+renamelimit::
+	Maximum number of files to consider when detecting renames. The value
+	 "-1" uses the compiletime value in git (for further info, look at
+	  `man git-diff`). Default value: "-1".
+
+repository-sort::
+	The way in which repositories in each section are sorted. Valid values
+	are "name" for sorting by the repo name or "age" for sorting by the
+	most recently updated repository. Default value: "name". See also:
+	section, case-sensitive-sort, section-sort.
+
+robots::
+	Text used as content for the "robots" meta-tag. Default value:
+	"index, nofollow".
+
+root-desc::
+	Text printed below the heading on the repository index page. Default
+	value: "a fast webinterface for the git dscm".
+
+root-readme::
+	The content of the file specified with this option will be included
+	verbatim below the "about" link on the repository index page. Default
+	value: none.
+
+root-title::
+	Text printed as heading on the repository index page. Default value:
+	"Git Repository Browser".
+
+scan-hidden-path::
+	If set to "1" and scan-path is enabled, scan-path will recurse into
+	directories whose name starts with a period ('.'). Otherwise,
+	scan-path will stay away from such directories (considered as
+	"hidden"). Note that this does not apply to the ".git" directory in
+	non-bare repos. This must be defined prior to scan-path.
+	Default value: 0. See also: scan-path.
+
+scan-path::
+	A path which will be scanned for repositories. If caching is enabled,
+	the result will be cached as a cgitrc include-file in the cache
+	directory. If project-list has been defined prior to scan-path,
+	scan-path loads only the directories listed in the file pointed to by
+	project-list. Be advised that only the global settings taken
+	before the scan-path directive will be applied to each repository.
+	Default value: none. See also: cache-scanrc-ttl, project-list,
+	"MACRO EXPANSION".
+
+section::
+	The name of the current repository section - all repositories defined
+	after this option will inherit the current section name. Default value:
+	none.
+
+section-sort::
+	Flag which, when set to "1", will sort the sections on the repository
+	listing by name. Set this flag to "0" if the order in the cgitrc file should
+	be preserved. Default value: "1". See also: section,
+	case-sensitive-sort, repository-sort.
+
+section-from-path::
+	A number which, if defined prior to scan-path, specifies how many
+	path elements from each repo path to use as a default section name.
+	If negative, cgit will discard the specified number of path elements
+	above the repo directory. Default value: "0".
+
+side-by-side-diffs::
+	If set to "1" shows side-by-side diffs instead of unidiffs per
+	default. Default value: "0".
+
+snapshots::
+	Text which specifies the default set of snapshot formats that cgit
+	generates links for. The value is a space-separated list of zero or
+	more of the values "tar", "tar.gz", "tar.bz2", "tar.lz", "tar.xz",
+	"tar.zst" and "zip". The special value "all" enables all snapshot
+	formats. Default value: none.
+	All compressors use default settings. Some settings can be influenced
+	with environment variables, for example set ZSTD_CLEVEL=10 in web
+	server environment for higher (but slower) zstd compression.
+
+source-filter::
+	Specifies a command which will be invoked to format plaintext blobs
+	in the tree view. The command will get the blob content on its STDIN
+	and the name of the blob as its only command line argument. The STDOUT
+	from the command will be included verbatim as the blob contents, i.e.
+	this can be used to implement e.g. syntax highlighting. Default value:
+	none. See also: "FILTER API".
+
+summary-branches::
+	Specifies the number of branches to display in the repository "summary"
+	view. Default value: "10".
+
+summary-log::
+	Specifies the number of log entries to display in the repository
+	"summary" view. Default value: "10".
+
+summary-tags::
+	Specifies the number of tags to display in the repository "summary"
+	view. Default value: "10".
+
+strict-export::
+	Filename which, if specified, needs to be present within the repository
+	for cgit to allow access to that repository. This can be used to emulate
+	gitweb's EXPORT_OK and STRICT_EXPORT functionality and limit cgit's
+	repositories to match those exported by git-daemon. This option must
+	be defined prior to scan-path.
+
+virtual-root::
+	Url which, if specified, will be used as root for all cgit links. It
+	will also cause cgit to generate 'virtual urls', i.e. urls like
+	'/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
+	value: none.
+	NOTE: cgit has recently learned how to use PATH_INFO to achieve the
+	same kind of virtual urls, so this option will probably be deprecated.
+
+
+REPOSITORY SETTINGS
+-------------------
+repo.about-filter::
+	Override the default about-filter. Default value: none. See also:
+	"enable-filter-overrides". See also: "FILTER API".
+
+repo.branch-sort::
+	Flag which, when set to "age", enables date ordering in the branch ref
+	list, and when set to "name" enables ordering by branch name. Default
+	value: "name".
+
+repo.clone-url::
+	A list of space-separated urls which can be used to clone this repo.
+	Default value: none. See also: "MACRO EXPANSION".
+
+repo.commit-filter::
+	Override the default commit-filter. Default value: none. See also:
+	"enable-filter-overrides". See also: "FILTER API".
+
+repo.commit-sort::
+	Flag which, when set to "date", enables strict date ordering in the
+	commit log, and when set to "topo" enables strict topological
+	ordering. If unset, the default ordering of "git log" is used. Default
+	value: unset.
+
+repo.defbranch::
+	The name of the default branch for this repository. If no such branch
+	exists in the repository, the first branch name (when sorted) is used
+	as default instead. Default value: branch pointed to by HEAD, or
+	"master" if there is no suitable HEAD.
+
+repo.desc::
+	The value to show as repository description. Default value: none.
+
+repo.email-filter::
+	Override the default email-filter. Default value: none. See also:
+	"enable-filter-overrides". See also: "FILTER API".
+
+repo.enable-blame::
+	A flag which can be used to disable the global setting
+	`enable-blame'. Default value: none.
+
+repo.enable-commit-graph::
+	A flag which can be used to disable the global setting
+	`enable-commit-graph'. Default value: none.
+
+repo.enable-html-serving::
+	A flag which can be used to override the global setting
+	`enable-html-serving`. Default value: none.
+
+repo.enable-log-filecount::
+	A flag which can be used to disable the global setting
+	`enable-log-filecount'. Default value: none.
+
+repo.enable-log-linecount::
+	A flag which can be used to disable the global setting
+	`enable-log-linecount'. Default value: none.
+
+repo.enable-remote-branches::
+	Flag which, when set to "1", will make cgit display remote branches
+	in the summary and refs views. Default value: <enable-remote-branches>.
+
+repo.enable-subject-links::
+	A flag which can be used to override the global setting
+	`enable-subject-links'. Default value: none.
+
+repo.extra-head-content::
+	This value will be added verbatim to the head section of each page
+	displayed for this repo. Default value: none.
+
+repo.hide::
+	Flag which, when set to "1", hides the repository from the repository
+	index. The repository can still be accessed by providing a direct path.
+	Default value: "0". See also: "repo.ignore".
+
+repo.homepage::
+	The value to show as repository homepage. Default value: none.
+
+repo.ignore::
+	Flag which, when set to "1", ignores the repository. The repository
+	is not shown in the index and cannot be accessed by providing a direct
+	path. Default value: "0". See also: "repo.hide".
+
+repo.logo::
+	Url which specifies the source of an image which will be used as a logo
+	on this repo's pages. Default value: global logo.
+
+repo.logo-link::
+	Url loaded when clicking on the cgit logo image. If unspecified the
+	calculated url of the repository index page will be used. Default
+	value: global logo-link.
+
+repo.module-link::
+	Text which will be used as the formatstring for a hyperlink when a
+	submodule is printed in a directory listing. The arguments for the
+	formatstring are the path and SHA1 of the submodule commit. Default
+	value: <module-link>
+
+repo.module-link.<path>::
+	Text which will be used as the formatstring for a hyperlink when a
+	submodule with the specified subdirectory path is printed in a
+	directory listing. The only argument for the formatstring is the SHA1
+	of the submodule commit. Default value: none.
+
+repo.max-stats::
+	Override the default maximum statistics period. Valid values are equal
+	to the values specified for the global "max-stats" setting. Default
+	value: none.
+
+repo.name::
+	The value to show as repository name. Default value: <repo.url>.
+
+repo.owner::
+	A value used to identify the owner of the repository. Default value:
+	none.
+
+repo.owner-filter::
+	Override the default owner-filter. Default value: none. See also:
+	"enable-filter-overrides". See also: "FILTER API".
+
+repo.path::
+	An absolute path to the repository directory. For non-bare repositories
+	this is the .git-directory. Default value: none.
+
+repo.readme::
+	A path (relative to <repo.path>) which specifies a file to include
+	verbatim as the "About" page for this repo. You may also specify a
+	git refspec by head or by hash by prepending the refspec followed by
+	a colon. For example, "master:docs/readme.mkd". If the value begins
+	with a colon, i.e. ":docs/readme.rst", the default branch of the
+	repository will be used. Sharing any file will expose that entire
+	directory tree to the "/about/PATH" endpoints, so be sure that there
+	are no non-public files located in the same directory as the readme
+	file. Default value: <readme>.
+
+repo.section::
+	Override the current section name for this repository. Default value:
+	none.
+
+repo.snapshots::
+	A mask of snapshot formats for this repo that cgit generates links for,
+	restricted by the global "snapshots" setting. Default value:
+	<snapshots>.
+
+repo.snapshot-prefix::
+	Prefix to use for snapshot links instead of the repository basename.
+	For example, the "linux-stable" repository may wish to set this to
+	"linux" so that snapshots are in the format "linux-3.15.4" instead
+	of "linux-stable-3.15.4".  Default value: <empty> meaning to use
+	the repository basename.
+
+repo.source-filter::
+	Override the default source-filter. Default value: none. See also:
+	"enable-filter-overrides". See also: "FILTER API".
+
+repo.url::
+	The relative url used to access the repository. This must be the first
+	setting specified for each repo. Default value: none.
+
+
+REPOSITORY-SPECIFIC CGITRC FILE
+-------------------------------
+When the option "scan-path" is used to auto-discover git repositories, cgit
+will try to parse the file "cgitrc" within any found repository. Such a
+repo-specific config file may contain any of the repo-specific options
+described above, except "repo.url" and "repo.path". Additionally, the "filter"
+options are only acknowledged in repo-specific config files when
+"enable-filter-overrides" is set to "1".
+
+Note: the "repo." prefix is dropped from the option names in repo-specific
+config files, e.g. "repo.desc" becomes "desc".
+
+
+FILTER API
+----------
+By default, filters are separate processes that are executed each time they
+are needed.  Alternative technologies may be used by prefixing the filter
+specification with the relevant string; available values are:
+
+'exec:'::
+	The default "one process per filter" mode.
+
+
+Parameters are provided to filters as follows.
+
+about filter::
+	This filter is given a single parameter: the filename of the source
+	file to filter. The filter can use the filename to determine (for
+	example) the type of syntax to follow when formatting the readme file.
+	The about text that is to be filtered is available on standard input
+	and the filtered text is expected on standard output.
+
+auth filter::
+	The authentication filter receives 12 parameters:
+	  - filter action, explained below, which specifies which action the
+	    filter is called for
+	  - http cookie
+	  - http method
+	  - http referer
+	  - http path
+	  - http https flag
+	  - cgit repo
+	  - cgit page
+	  - cgit url
+	  - cgit login url
+	When the filter action is "body", this filter must write to output the
+	HTML for displaying the login form, which POSTs to the login url. When
+	the filter action is "authenticate-cookie", this filter must validate
+	the http cookie and return a 0 if it is invalid or 1 if it is invalid,
+	in the exit code / close function. If the filter action is
+	"authenticate-post", this filter receives POST'd parameters on
+	standard input, and should write a complete CGI response, preferably
+	with a 302 redirect, and write to output one or more "Set-Cookie"
+	HTTP headers, each followed by a newline.
+
+commit filter::
+	This filter is given no arguments. The commit message text that is to
+	be filtered is available on standard input and the filtered text is
+	expected on standard output.
+
+email filter::
+	This filter is given two parameters: the email address of the relevant
+	author and a string indicating the originating page. The filter will
+	then receive the text string to format on standard input and is
+	expected to write to standard output the formatted text to be included
+	in the page.
+
+owner filter::
+	This filter is given no arguments.  The owner text is available on
+	standard input and the filter is expected to write to standard
+	output.  The output is included in the Owner column.
+
+source filter::
+	This filter is given a single parameter: the filename of the source
+	file to filter. The filter can use the filename to determine (for
+	example) the syntax highlighting mode. The contents of the source
+	file that is to be filtered is available on standard input and the
+	filtered contents is expected on standard output.
+
+
+All filters are handed the following environment variables:
+
+- CGIT_REPO_URL (from repo.url)
+- CGIT_REPO_NAME (from repo.name)
+- CGIT_REPO_PATH (from repo.path)
+- CGIT_REPO_OWNER (from repo.owner)
+- CGIT_REPO_DEFBRANCH (from repo.defbranch)
+- CGIT_REPO_SECTION (from repo.section)
+- CGIT_REPO_CLONE_URL (from repo.clone-url)
+
+If a setting is not defined for a repository and the corresponding global
+setting is also not defined (if applicable), then the corresponding
+environment variable will be unset.
+
+
+MACRO EXPANSION
+---------------
+The following cgitrc options support a simple macro expansion feature,
+where tokens prefixed with "$" are replaced with the value of a similarly
+named environment variable:
+
+- cache-root
+- include
+- project-list
+- scan-path
+
+Macro expansion will also happen on the content of $CGIT_CONFIG, if
+defined.
+
+One usage of this feature is virtual hosting, which in its simplest form
+can be accomplished by adding the following line to /etc/cgitrc:
+
+	include=/etc/cgitrc.d/$HTTP_HOST
+
+The following options are expanded during request processing, and support
+the environment variables defined in "FILTER API":
+
+- clone-url
+- repo.clone-url
+
+
+CACHE
+-----
+
+All cache ttl values are in minutes. Negative ttl values indicate that a page
+type will never expire, and thus the first time a URL is accessed, the result
+will be cached indefinitely, even if the underlying git repository changes.
+Conversely, when a ttl value is zero, the cache is disabled for that
+particular page type, and the page type is never cached.
+
+SIGNATURES
+----------
+
+Cgit can host .asc signatures corresponding to various snapshot formats,
+through use of git notes. For example, the following command may be used to
+add a signature to a .tar.xz archive:
+
+    git notes --ref=refs/notes/signatures/tar.xz add -C "$(
+	gpg --output - --armor --detach-sign cgit-1.1.tar.xz |
+	git hash-object -w --stdin
+    )" v1.1
+
+If it is instead desirable to attach a signature of the underlying .tar, this
+will be linked, as a special case, beside a .tar.* link that does not have its
+own signature. For example, a signature of a tarball of the latest tag might
+be added with a similar command:
+
+    tag="$(git describe --abbrev=0)"
+    git notes --ref=refs/notes/signatures/tar add -C "$(
+        git archive --format tar --prefix "cgit-${tag#v}/" "$tag" |
+        gpg --output - --armor --detach-sign |
+        git hash-object -w --stdin
+    )" "$tag"
+
+Since git-archive(1) is expected to produce stable output between versions,
+this allows one to generate a long-term signature of the contents of a given
+tag.
+
+EXAMPLE CGITRC FILE
+-------------------
+
+....
+# Enable caching of up to 1000 output entries
+cache-size=1000
+
+
+# Specify some default clone urls using macro expansion
+clone-url=git://foo.org/$CGIT_REPO_URL git@foo.org:$CGIT_REPO_URL
+
+# Specify the css url
+css=/css/cgit.css
+
+
+# Show owner on index page
+enable-index-owner=1
+
+
+# Allow http transport git clone
+enable-http-clone=1
+
+
+# Show extra links for each repository on the index page
+enable-index-links=1
+
+
+# Enable blame page and create links to it from tree page
+enable-blame=1
+
+
+# Enable ASCII art commit history graph on the log pages
+enable-commit-graph=1
+
+
+# Show number of affected files per commit on the log pages
+enable-log-filecount=1
+
+
+# Show number of added/removed lines per commit on the log pages
+enable-log-linecount=1
+
+
+# Sort branches by date
+branch-sort=age
+
+
+# Add a cgit favicon
+favicon=/favicon.ico
+
+
+# Use a custom logo
+logo=/img/mylogo.png
+
+
+# Enable statistics per week, month and quarter
+max-stats=quarter
+
+
+# Set the title and heading of the repository index page
+root-title=example.com git repositories
+
+
+# Set a subheading for the repository index page
+root-desc=tracking the foobar development
+
+
+# Include some more info about example.com on the index page
+root-readme=/var/www/htdocs/about.html
+
+
+# Allow download of tar.gz, tar.bz2 and zip-files
+snapshots=tar.gz tar.bz2 zip
+
+
+##
+## List of common mimetypes
+##
+
+mimetype.gif=image/gif
+mimetype.html=text/html
+mimetype.jpg=image/jpeg
+mimetype.jpeg=image/jpeg
+mimetype.pdf=application/pdf
+mimetype.png=image/png
+mimetype.svg=image/svg+xml
+
+
+# Highlight source code with python pygments-based highlighter
+source-filter=/var/www/cgit/filters/syntax-highlighting.py
+
+# Format markdown, restructuredtext, manpages, text files, and html files
+# through the right converters
+about-filter=/var/www/cgit/filters/about-formatting.sh
+
+##
+## Search for these files in the root of the default branch of repositories
+## for coming up with the about page:
+##
+readme=:README.md
+readme=:readme.md
+readme=:README.mkd
+readme=:readme.mkd
+readme=:README.rst
+readme=:readme.rst
+readme=:README.html
+readme=:readme.html
+readme=:README.htm
+readme=:readme.htm
+readme=:README.txt
+readme=:readme.txt
+readme=:README
+readme=:readme
+readme=:INSTALL.md
+readme=:install.md
+readme=:INSTALL.mkd
+readme=:install.mkd
+readme=:INSTALL.rst
+readme=:install.rst
+readme=:INSTALL.html
+readme=:install.html
+readme=:INSTALL.htm
+readme=:install.htm
+readme=:INSTALL.txt
+readme=:install.txt
+readme=:INSTALL
+readme=:install
+
+
+##
+## List of repositories.
+## PS: Any repositories listed when section is unset will not be
+##     displayed under a section heading
+## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos')
+##      and included like this:
+##        include=/etc/cgitrepos
+##
+
+
+repo.url=foo
+repo.path=/pub/git/foo.git
+repo.desc=the master foo repository
+repo.owner=fooman@example.com
+repo.readme=info/web/about.html
+
+
+repo.url=bar
+repo.path=/pub/git/bar.git
+repo.desc=the bars for your foo
+repo.owner=barman@example.com
+repo.readme=info/web/about.html
+
+
+# The next repositories will be displayed under the 'extras' heading
+section=extras
+
+
+repo.url=baz
+repo.path=/pub/git/baz.git
+repo.desc=a set of extensions for bar users
+
+repo.url=wiz
+repo.path=/pub/git/wiz.git
+repo.desc=the wizard of foo
+
+
+# Add some mirrored repositories
+section=mirrors
+
+
+repo.url=git
+repo.path=/pub/git/git.git
+repo.desc=the dscm
+
+
+repo.url=linux
+repo.path=/pub/git/linux.git
+repo.desc=the kernel
+
+# Disable adhoc downloads of this repo
+repo.snapshots=0
+
+# Disable line-counts for this repo
+repo.enable-log-linecount=0
+
+# Restrict the max statistics period for this repo
+repo.max-stats=month
+....
+
+
+BUGS
+----
+Comments currently cannot appear on the same line as a setting; the comment
+will be included as part of the value. E.g. this line:
+
+	robots=index  # allow indexing
+
+will generate the following html element:
+
+	<meta name='robots' content='index  # allow indexing'/>
+
+
+
+AUTHOR
+------
+Lars Hjemli <hjemli@gmail.com>
+Jason A. Donenfeld <Jason@zx2c4.com>
diff --git a/www/git.causal.agency/cgit/cmd.c b/www/git.causal.agency/cgit/cmd.c
new file mode 100644
index 00000000..0eb75b1d
--- /dev/null
+++ b/www/git.causal.agency/cgit/cmd.c
@@ -0,0 +1,208 @@
+/* cmd.c: the cgit command dispatcher
+ *
+ * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "cmd.h"
+#include "cache.h"
+#include "ui-shared.h"
+#include "ui-atom.h"
+#include "ui-blame.h"
+#include "ui-blob.h"
+#include "ui-clone.h"
+#include "ui-commit.h"
+#include "ui-diff.h"
+#include "ui-log.h"
+#include "ui-patch.h"
+#include "ui-plain.h"
+#include "ui-refs.h"
+#include "ui-repolist.h"
+#include "ui-snapshot.h"
+#include "ui-stats.h"
+#include "ui-summary.h"
+#include "ui-tag.h"
+#include "ui-tree.h"
+
+static void HEAD_fn(void)
+{
+	cgit_clone_head();
+}
+
+static void atom_fn(void)
+{
+	cgit_print_atom(ctx.qry.head, ctx.qry.path, ctx.cfg.max_atom_items);
+}
+
+static void about_fn(void)
+{
+	if (ctx.repo) {
+		size_t path_info_len = ctx.env.path_info ? strlen(ctx.env.path_info) : 0;
+		if (!ctx.qry.path &&
+		    ctx.qry.url[strlen(ctx.qry.url) - 1] != '/' &&
+		    (!path_info_len || ctx.env.path_info[path_info_len - 1] != '/')) {
+			char *currenturl = cgit_currenturl();
+			char *redirect = fmtalloc("%s/", currenturl);
+			cgit_redirect(redirect, true);
+			free(currenturl);
+			free(redirect);
+		} else if (ctx.repo->readme.nr)
+			cgit_print_repo_readme(ctx.qry.path);
+		else if (ctx.repo->homepage)
+			cgit_redirect(ctx.repo->homepage, false);
+		else {
+			char *currenturl = cgit_currenturl();
+			char *redirect = fmtalloc("%s../", currenturl);
+			cgit_redirect(redirect, false);
+			free(currenturl);
+			free(redirect);
+		}
+	} else
+		cgit_print_site_readme();
+}
+
+static void blame_fn(void)
+{
+	if (ctx.repo->enable_blame)
+		cgit_print_blame();
+	else
+		cgit_print_error_page(403, "Forbidden", "Blame is disabled");
+}
+
+static void blob_fn(void)
+{
+	cgit_print_blob(ctx.qry.oid, ctx.qry.path, ctx.qry.head, 0);
+}
+
+static void commit_fn(void)
+{
+	cgit_print_commit(ctx.qry.oid, ctx.qry.path);
+}
+
+static void diff_fn(void)
+{
+	cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 1, 0);
+}
+
+static void rawdiff_fn(void)
+{
+	cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 1, 1);
+}
+
+static void info_fn(void)
+{
+	cgit_clone_info();
+}
+
+static void log_fn(void)
+{
+	cgit_print_log(ctx.qry.oid, ctx.qry.ofs, ctx.cfg.max_commit_count,
+		       ctx.qry.grep, ctx.qry.search, ctx.qry.path, 1,
+		       ctx.repo->enable_commit_graph,
+		       ctx.repo->commit_sort);
+}
+
+static void ls_cache_fn(void)
+{
+	ctx.page.mimetype = "text/plain";
+	ctx.page.filename = "ls-cache.txt";
+	cgit_print_http_headers();
+	cache_ls(ctx.cfg.cache_root);
+}
+
+static void objects_fn(void)
+{
+	cgit_clone_objects();
+}
+
+static void repolist_fn(void)
+{
+	cgit_print_repolist();
+}
+
+static void patch_fn(void)
+{
+	cgit_print_patch(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path);
+}
+
+static void plain_fn(void)
+{
+	cgit_print_plain();
+}
+
+static void refs_fn(void)
+{
+	cgit_print_refs();
+}
+
+static void snapshot_fn(void)
+{
+	cgit_print_snapshot(ctx.qry.head, ctx.qry.oid, ctx.qry.path,
+			    ctx.qry.nohead);
+}
+
+static void stats_fn(void)
+{
+	cgit_show_stats();
+}
+
+static void summary_fn(void)
+{
+	cgit_print_summary();
+}
+
+static void tag_fn(void)
+{
+	cgit_print_tag(ctx.qry.oid);
+}
+
+static void tree_fn(void)
+{
+	cgit_print_tree(ctx.qry.oid, ctx.qry.path);
+}
+
+#define def_cmd(name, want_repo, want_vpath, is_clone) \
+	{#name, name##_fn, want_repo, want_vpath, is_clone}
+
+struct cgit_cmd *cgit_get_cmd(void)
+{
+	static struct cgit_cmd cmds[] = {
+		def_cmd(HEAD, 1, 0, 1),
+		def_cmd(atom, 1, 0, 0),
+		def_cmd(about, 0, 0, 0),
+		def_cmd(blame, 1, 1, 0),
+		def_cmd(blob, 1, 0, 0),
+		def_cmd(commit, 1, 1, 0),
+		def_cmd(diff, 1, 1, 0),
+		def_cmd(info, 1, 0, 1),
+		def_cmd(log, 1, 1, 0),
+		def_cmd(ls_cache, 0, 0, 0),
+		def_cmd(objects, 1, 0, 1),
+		def_cmd(patch, 1, 1, 0),
+		def_cmd(plain, 1, 0, 0),
+		def_cmd(rawdiff, 1, 1, 0),
+		def_cmd(refs, 1, 0, 0),
+		def_cmd(repolist, 0, 0, 0),
+		def_cmd(snapshot, 1, 0, 0),
+		def_cmd(stats, 1, 1, 0),
+		def_cmd(summary, 1, 0, 0),
+		def_cmd(tag, 1, 0, 0),
+		def_cmd(tree, 1, 1, 0),
+	};
+	int i;
+
+	if (ctx.qry.page == NULL) {
+		if (ctx.repo)
+			ctx.qry.page = "summary";
+		else
+			ctx.qry.page = "repolist";
+	}
+
+	for (i = 0; i < sizeof(cmds)/sizeof(*cmds); i++)
+		if (!strcmp(ctx.qry.page, cmds[i].name))
+			return &cmds[i];
+	return NULL;
+}
diff --git a/www/git.causal.agency/cgit/cmd.h b/www/git.causal.agency/cgit/cmd.h
new file mode 100644
index 00000000..6249b1d8
--- /dev/null
+++ b/www/git.causal.agency/cgit/cmd.h
@@ -0,0 +1,16 @@
+#ifndef CMD_H
+#define CMD_H
+
+typedef void (*cgit_cmd_fn)(void);
+
+struct cgit_cmd {
+	const char *name;
+	cgit_cmd_fn fn;
+	unsigned int want_repo:1,
+		want_vpath:1,
+		is_clone:1;
+};
+
+extern struct cgit_cmd *cgit_get_cmd(void);
+
+#endif /* CMD_H */
diff --git a/www/git.causal.agency/cgit/configfile.c b/www/git.causal.agency/cgit/configfile.c
new file mode 100644
index 00000000..e0391091
--- /dev/null
+++ b/www/git.causal.agency/cgit/configfile.c
@@ -0,0 +1,90 @@
+/* configfile.c: parsing of config files
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include <git-compat-util.h>
+#include "configfile.h"
+
+static int next_char(FILE *f)
+{
+	int c = fgetc(f);
+	if (c == '\r') {
+		c = fgetc(f);
+		if (c != '\n') {
+			ungetc(c, f);
+			c = '\r';
+		}
+	}
+	return c;
+}
+
+static void skip_line(FILE *f)
+{
+	int c;
+
+	while ((c = next_char(f)) && c != '\n' && c != EOF)
+		;
+}
+
+static int read_config_line(FILE *f, struct strbuf *name, struct strbuf *value)
+{
+	int c = next_char(f);
+
+	strbuf_reset(name);
+	strbuf_reset(value);
+
+	/* Skip comments and preceding spaces. */
+	for(;;) {
+		if (c == EOF)
+			return 0;
+		else if (c == '#' || c == ';')
+			skip_line(f);
+		else if (!isspace(c))
+			break;
+		c = next_char(f);
+	}
+
+	/* Read variable name. */
+	while (c != '=') {
+		if (c == '\n' || c == EOF)
+			return 0;
+		strbuf_addch(name, c);
+		c = next_char(f);
+	}
+
+	/* Read variable value. */
+	c = next_char(f);
+	while (c != '\n' && c != EOF) {
+		strbuf_addch(value, c);
+		c = next_char(f);
+	}
+
+	return 1;
+}
+
+int parse_configfile(const char *filename, configfile_value_fn fn)
+{
+	static int nesting;
+	struct strbuf name = STRBUF_INIT;
+	struct strbuf value = STRBUF_INIT;
+	FILE *f;
+
+	/* cancel deeply nested include-commands */
+	if (nesting > 8)
+		return -1;
+	if (!(f = fopen(filename, "r")))
+		return -1;
+	nesting++;
+	while (read_config_line(f, &name, &value))
+		fn(name.buf, value.buf);
+	nesting--;
+	fclose(f);
+	strbuf_release(&name);
+	strbuf_release(&value);
+	return 0;
+}
+
diff --git a/www/git.causal.agency/cgit/configfile.h b/www/git.causal.agency/cgit/configfile.h
new file mode 100644
index 00000000..af7ca197
--- /dev/null
+++ b/www/git.causal.agency/cgit/configfile.h
@@ -0,0 +1,10 @@
+#ifndef CONFIGFILE_H
+#define CONFIGFILE_H
+
+#include "cgit.h"
+
+typedef void (*configfile_value_fn)(const char *name, const char *value);
+
+extern int parse_configfile(const char *filename, configfile_value_fn fn);
+
+#endif /* CONFIGFILE_H */
diff --git a/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile b/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile
new file mode 100755
index 00000000..2f72ae9c
--- /dev/null
+++ b/www/git.causal.agency/cgit/contrib/hooks/post-receive.agefile
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# An example hook to update the "agefile" for CGit's idle time calculation.
+#
+# This hook assumes that you are using the default agefile location of
+# "info/web/last-modified".  If you change the value in your cgitrc then you
+# must also change it here.
+#
+# To install the hook, copy (or link) it to the file "hooks/post-receive" in
+# each of your repositories.
+#
+
+agefile="$(git rev-parse --git-dir)"/info/web/last-modified
+
+mkdir -p "$(dirname "$agefile")" &&
+git for-each-ref \
+	--sort=-authordate --count=1 \
+	--format='%(authordate:iso8601)' \
+	>"$agefile"
diff --git a/www/git.causal.agency/cgit/favicon.ico b/www/git.causal.agency/cgit/favicon.ico
new file mode 100644
index 00000000..56ff5938
--- /dev/null
+++ b/www/git.causal.agency/cgit/favicon.ico
Binary files differdiff --git a/www/git.causal.agency/cgit/filter.c b/www/git.causal.agency/cgit/filter.c
new file mode 100644
index 00000000..2b6c838e
--- /dev/null
+++ b/www/git.causal.agency/cgit/filter.c
@@ -0,0 +1,222 @@
+/* filter.c: filter framework functions
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "html.h"
+
+static inline void reap_filter(struct cgit_filter *filter)
+{
+	if (filter && filter->cleanup)
+		filter->cleanup(filter);
+}
+
+void cgit_cleanup_filters(void)
+{
+	int i;
+	reap_filter(ctx.cfg.about_filter);
+	reap_filter(ctx.cfg.commit_filter);
+	reap_filter(ctx.cfg.source_filter);
+	reap_filter(ctx.cfg.email_filter);
+	reap_filter(ctx.cfg.owner_filter);
+	reap_filter(ctx.cfg.auth_filter);
+	for (i = 0; i < cgit_repolist.count; ++i) {
+		reap_filter(cgit_repolist.repos[i].about_filter);
+		reap_filter(cgit_repolist.repos[i].commit_filter);
+		reap_filter(cgit_repolist.repos[i].source_filter);
+		reap_filter(cgit_repolist.repos[i].email_filter);
+		reap_filter(cgit_repolist.repos[i].owner_filter);
+	}
+}
+
+static int open_exec_filter(struct cgit_filter *base, va_list ap)
+{
+	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
+	int pipe_fh[2];
+	int i;
+
+	for (i = 0; i < filter->base.argument_count; i++)
+		filter->argv[i + 1] = va_arg(ap, char *);
+
+	chk_zero(fflush(stdout), "unable to flush STDOUT");
+	filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
+		"Unable to duplicate STDOUT");
+	chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess");
+	filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
+	if (filter->pid == 0) {
+		close(pipe_fh[1]);
+		chk_non_negative(dup2(pipe_fh[0], STDIN_FILENO),
+			"Unable to use pipe as STDIN");
+		execvp(filter->cmd, filter->argv);
+		die_errno("Unable to exec subprocess %s", filter->cmd);
+	}
+	close(pipe_fh[0]);
+	chk_non_negative(dup2(pipe_fh[1], STDOUT_FILENO),
+		"Unable to use pipe as STDOUT");
+	close(pipe_fh[1]);
+	return 0;
+}
+
+static int close_exec_filter(struct cgit_filter *base)
+{
+	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
+	int i, exit_status = 0;
+
+	chk_zero(fflush(stdout), "unable to flush STDOUT");
+	chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
+		"Unable to restore STDOUT");
+	close(filter->old_stdout);
+	if (filter->pid < 0)
+		goto done;
+	waitpid(filter->pid, &exit_status, 0);
+	if (WIFEXITED(exit_status))
+		goto done;
+	die("Subprocess %s exited abnormally", filter->cmd);
+
+done:
+	for (i = 0; i < filter->base.argument_count; i++)
+		filter->argv[i + 1] = NULL;
+	return WEXITSTATUS(exit_status);
+
+}
+
+static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, const char *prefix)
+{
+	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
+	fprintf(f, "%sexec:%s\n", prefix, filter->cmd);
+}
+
+static void cleanup_exec_filter(struct cgit_filter *base)
+{
+	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
+	if (filter->argv) {
+		free(filter->argv);
+		filter->argv = NULL;
+	}
+	if (filter->cmd) {
+		free(filter->cmd);
+		filter->cmd = NULL;
+	}
+}
+
+static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count)
+{
+	struct cgit_exec_filter *f;
+	int args_size = 0;
+
+	f = xmalloc(sizeof(*f));
+	/* We leave argv for now and assign it below. */
+	cgit_exec_filter_init(f, xstrdup(cmd), NULL);
+	f->base.argument_count = argument_count;
+	args_size = (2 + argument_count) * sizeof(char *);
+	f->argv = xmalloc(args_size);
+	memset(f->argv, 0, args_size);
+	f->argv[0] = f->cmd;
+	return &f->base;
+}
+
+void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv)
+{
+	memset(filter, 0, sizeof(*filter));
+	filter->base.open = open_exec_filter;
+	filter->base.close = close_exec_filter;
+	filter->base.fprintf = fprintf_exec_filter;
+	filter->base.cleanup = cleanup_exec_filter;
+	filter->cmd = cmd;
+	filter->argv = argv;
+	/* The argument count for open_filter is zero by default, unless called from new_filter, above. */
+	filter->base.argument_count = 0;
+}
+
+int cgit_open_filter(struct cgit_filter *filter, ...)
+{
+	int result;
+	va_list ap;
+	if (!filter)
+		return 0;
+	va_start(ap, filter);
+	result = filter->open(filter, ap);
+	va_end(ap);
+	return result;
+}
+
+int cgit_close_filter(struct cgit_filter *filter)
+{
+	if (!filter)
+		return 0;
+	return filter->close(filter);
+}
+
+void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
+{
+	filter->fprintf(filter, f, prefix);
+}
+
+
+
+static const struct {
+	const char *prefix;
+	struct cgit_filter *(*ctor)(const char *cmd, int argument_count);
+} filter_specs[] = {
+	{ "exec", new_exec_filter },
+};
+
+struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
+{
+	char *colon;
+	int i;
+	size_t len;
+	int argument_count;
+
+	if (!cmd || !cmd[0])
+		return NULL;
+
+	colon = strchr(cmd, ':');
+	len = colon - cmd;
+	/*
+	 * In case we're running on Windows, don't allow a single letter before
+	 * the colon.
+	 */
+	if (len == 1)
+		colon = NULL;
+
+	switch (filtertype) {
+		case AUTH:
+			argument_count = 12;
+			break;
+
+		case EMAIL:
+			argument_count = 2;
+			break;
+
+		case OWNER:
+			argument_count = 0;
+			break;
+
+		case SOURCE:
+		case ABOUT:
+			argument_count = 1;
+			break;
+
+		case COMMIT:
+		default:
+			argument_count = 0;
+			break;
+	}
+
+	/* If no prefix is given, exec filter is the default. */
+	if (!colon)
+		return new_exec_filter(cmd, argument_count);
+
+	for (i = 0; i < ARRAY_SIZE(filter_specs); i++) {
+		if (len == strlen(filter_specs[i].prefix) &&
+		    !strncmp(filter_specs[i].prefix, cmd, len))
+			return filter_specs[i].ctor(colon + 1, argument_count);
+	}
+
+	die("Invalid filter type: %.*s", (int) len, cmd);
+}
diff --git a/www/git.causal.agency/cgit/filters/about-formatting.sh b/www/git.causal.agency/cgit/filters/about-formatting.sh
new file mode 100755
index 00000000..85daf9c2
--- /dev/null
+++ b/www/git.causal.agency/cgit/filters/about-formatting.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# This may be used with the about-filter or repo.about-filter setting in cgitrc.
+# It passes formatting of about pages to differing programs, depending on the usage.
+
+# Markdown support requires python and markdown-python.
+# RestructuredText support requires python and docutils.
+# Man page support requires groff.
+
+# The following environment variables can be used to retrieve the configuration
+# of the repository for which this script is called:
+# CGIT_REPO_URL        ( = repo.url       setting )
+# CGIT_REPO_NAME       ( = repo.name      setting )
+# CGIT_REPO_PATH       ( = repo.path      setting )
+# CGIT_REPO_OWNER      ( = repo.owner     setting )
+# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
+# CGIT_REPO_SECTION    ( = section        setting )
+# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
+
+cd "$(dirname $0)/html-converters/"
+case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" in
+	*.markdown|*.mdown|*.md|*.mkd) exec ./md2html; ;;
+	*.rst) exec ./rst2html; ;;
+	*.[1-9]) exec ./man2html; ;;
+	*.htm|*.html) exec cat; ;;
+	*.txt|*) exec ./txt2html; ;;
+esac
diff --git a/www/git.causal.agency/cgit/filters/commit-links.sh b/www/git.causal.agency/cgit/filters/commit-links.sh
new file mode 100755
index 00000000..796ac308
--- /dev/null
+++ b/www/git.causal.agency/cgit/filters/commit-links.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+# This script can be used to generate links in commit messages.
+#
+# To use this script, refer to this file with either the commit-filter or the
+# repo.commit-filter options in cgitrc.
+#
+# The following environment variables can be used to retrieve the configuration
+# of the repository for which this script is called:
+# CGIT_REPO_URL        ( = repo.url       setting )
+# CGIT_REPO_NAME       ( = repo.name      setting )
+# CGIT_REPO_PATH       ( = repo.path      setting )
+# CGIT_REPO_OWNER      ( = repo.owner     setting )
+# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
+# CGIT_REPO_SECTION    ( = section        setting )
+# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
+#
+
+regex=''
+
+# This expression generates links to commits referenced by their SHA1.
+regex=$regex'
+s|\b([0-9a-fA-F]{7,64})\b|<a href="./?id=\1">\1</a>|g'
+
+# This expression generates links to a fictional bugtracker.
+regex=$regex'
+s|#([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g'
+
+sed -re "$regex"
diff --git a/www/git.causal.agency/cgit/filters/email-gravatar.py b/www/git.causal.agency/cgit/filters/email-gravatar.py
new file mode 100755
index 00000000..012113c5
--- /dev/null
+++ b/www/git.causal.agency/cgit/filters/email-gravatar.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+
+# This script may be used with the email-filter or repo.email-filter settings in cgitrc.
+#
+# The following environment variables can be used to retrieve the configuration
+# of the repository for which this script is called:
+# CGIT_REPO_URL        ( = repo.url       setting )
+# CGIT_REPO_NAME       ( = repo.name      setting )
+# CGIT_REPO_PATH       ( = repo.path      setting )
+# CGIT_REPO_OWNER      ( = repo.owner     setting )
+# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
+# CGIT_REPO_SECTION    ( = section        setting )
+# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
+#
+# It receives an email address on argv[1] and text on stdin. It prints
+# to stdout that text prepended by a gravatar at 10pt.
+
+import sys
+import hashlib
+import codecs
+
+email = sys.argv[1].lower().strip()
+if email[0] == '<':
+        email = email[1:]
+if email[-1] == '>':
+        email = email[0:-1]
+
+page = sys.argv[2]
+
+sys.stdin = codecs.getreader("utf-8")(sys.stdin.detach())
+sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
+
+md5 = hashlib.md5(email.encode()).hexdigest()
+text = sys.stdin.read().strip()
+
+print("<img src='//www.gravatar.com/avatar/" + md5 + "?s=13&amp;d=retro' width='13' height='13' alt='Gravatar' /> " + text)
diff --git a/www/git.causal.agency/cgit/filters/html-converters/man2html b/www/git.causal.agency/cgit/filters/html-converters/man2html
new file mode 100755
index 00000000..0ef78841
--- /dev/null
+++ b/www/git.causal.agency/cgit/filters/html-converters/man2html
@@ -0,0 +1,4 @@
+#!/bin/sh
+echo "<div style=\"font-family: monospace\">"
+groff -mandoc -T html -P -r -P -l | egrep -v '(<html>|<head>|<meta|<title>|</title>|</head>|<body>|</body>|</html>|<!DOCTYPE|"http://www.w3.org)'
+echo "</div>"
diff --git a/www/git.causal.agency/cgit/filters/html-converters/md2html b/www/git.causal.agency/cgit/filters/html-converters/md2html
new file mode 100755
index 00000000..59f43a84
--- /dev/null
+++ b/www/git.causal.agency/cgit/filters/html-converters/md2html
@@ -0,0 +1,304 @@
+#!/usr/bin/env python3
+import markdown
+import sys
+import io
+from pygments.formatters import HtmlFormatter
+from markdown.extensions.toc import TocExtension
+sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
+sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
+sys.stdout.write('''
+<style>
+.markdown-body {
+    font-size: 14px;
+    line-height: 1.6;
+    overflow: hidden;
+}
+.markdown-body>*:first-child {
+    margin-top: 0 !important;
+}
+.markdown-body>*:last-child {
+    margin-bottom: 0 !important;
+}
+.markdown-body a.absent {
+    color: #c00;
+}
+.markdown-body a.anchor {
+    display: block;
+    padding-left: 30px;
+    margin-left: -30px;
+    cursor: pointer;
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+}
+.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
+    margin: 20px 0 10px;
+    padding: 0;
+    font-weight: bold;
+    -webkit-font-smoothing: antialiased;
+    cursor: text;
+    position: relative;
+}
+.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link {
+    display: none;
+    color: #000;
+}
+.markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor {
+    text-decoration: none;
+    line-height: 1;
+    padding-left: 0;
+    margin-left: -22px;
+    top: 15%;
+}
+.markdown-body h1:hover a.anchor .mini-icon-link, .markdown-body h2:hover a.anchor .mini-icon-link, .markdown-body h3:hover a.anchor .mini-icon-link, .markdown-body h4:hover a.anchor .mini-icon-link, .markdown-body h5:hover a.anchor .mini-icon-link, .markdown-body h6:hover a.anchor .mini-icon-link {
+    display: inline-block;
+}
+div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#cgit .markdown-body h3 a.toclink, div#cgit .markdown-body h4 a.toclink, div#cgit .markdown-body h5 a.toclink, div#cgit .markdown-body h6 a.toclink {
+    color: black;
+}
+.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code {
+    font-size: inherit;
+}
+.markdown-body h1 {
+    font-size: 28px;
+    color: #000;
+}
+.markdown-body h2 {
+    font-size: 24px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+}
+.markdown-body h3 {
+    font-size: 18px;
+}
+.markdown-body h4 {
+    font-size: 16px;
+}
+.markdown-body h5 {
+    font-size: 14px;
+}
+.markdown-body h6 {
+    color: #777;
+    font-size: 14px;
+}
+.markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre {
+    margin: 15px 0;
+}
+.markdown-body hr {
+    border: 2px solid #ccc;
+}
+.markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child {
+    margin-top: 0;
+    padding-top: 0;
+}
+.markdown-body a:first-child h1, .markdown-body a:first-child h2, .markdown-body a:first-child h3, .markdown-body a:first-child h4, .markdown-body a:first-child h5, .markdown-body a:first-child h6 {
+    margin-top: 0;
+    padding-top: 0;
+}
+.markdown-body h1+p, .markdown-body h2+p, .markdown-body h3+p, .markdown-body h4+p, .markdown-body h5+p, .markdown-body h6+p {
+    margin-top: 0;
+}
+.markdown-body li p.first {
+    display: inline-block;
+}
+.markdown-body ul, .markdown-body ol {
+    padding-left: 30px;
+}
+.markdown-body ul.no-list, .markdown-body ol.no-list {
+    list-style-type: none;
+    padding: 0;
+}
+.markdown-body ul li>:first-child, .markdown-body ul li ul:first-of-type, .markdown-body ul li ol:first-of-type, .markdown-body ol li>:first-child, .markdown-body ol li ul:first-of-type, .markdown-body ol li ol:first-of-type {
+    margin-top: 0px;
+}
+.markdown-body ul li p:last-of-type, .markdown-body ol li p:last-of-type {
+    margin-bottom: 0;
+}
+.markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul {
+    margin-bottom: 0;
+}
+.markdown-body dl {
+    padding: 0;
+}
+.markdown-body dl dt {
+    font-size: 14px;
+    font-weight: bold;
+    font-style: italic;
+    padding: 0;
+    margin: 15px 0 5px;
+}
+.markdown-body dl dt:first-child {
+    padding: 0;
+}
+.markdown-body dl dt>:first-child {
+    margin-top: 0px;
+}
+.markdown-body dl dt>:last-child {
+    margin-bottom: 0px;
+}
+.markdown-body dl dd {
+    margin: 0 0 15px;
+    padding: 0 15px;
+}
+.markdown-body dl dd>:first-child {
+    margin-top: 0px;
+}
+.markdown-body dl dd>:last-child {
+    margin-bottom: 0px;
+}
+.markdown-body blockquote {
+    border-left: 4px solid #DDD;
+    padding: 0 15px;
+    color: #777;
+}
+.markdown-body blockquote>:first-child {
+    margin-top: 0px;
+}
+.markdown-body blockquote>:last-child {
+    margin-bottom: 0px;
+}
+.markdown-body table th {
+    font-weight: bold;
+}
+.markdown-body table th, .markdown-body table td {
+    border: 1px solid #ccc;
+    padding: 6px 13px;
+}
+.markdown-body table tr {
+    border-top: 1px solid #ccc;
+    background-color: #fff;
+}
+.markdown-body table tr:nth-child(2n) {
+    background-color: #f8f8f8;
+}
+.markdown-body img {
+    max-width: 100%;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box;
+}
+.markdown-body span.frame {
+    display: block;
+    overflow: hidden;
+}
+.markdown-body span.frame>span {
+    border: 1px solid #ddd;
+    display: block;
+    float: left;
+    overflow: hidden;
+    margin: 13px 0 0;
+    padding: 7px;
+    width: auto;
+}
+.markdown-body span.frame span img {
+    display: block;
+    float: left;
+}
+.markdown-body span.frame span span {
+    clear: both;
+    color: #333;
+    display: block;
+    padding: 5px 0 0;
+}
+.markdown-body span.align-center {
+    display: block;
+    overflow: hidden;
+    clear: both;
+}
+.markdown-body span.align-center>span {
+    display: block;
+    overflow: hidden;
+    margin: 13px auto 0;
+    text-align: center;
+}
+.markdown-body span.align-center span img {
+    margin: 0 auto;
+    text-align: center;
+}
+.markdown-body span.align-right {
+    display: block;
+    overflow: hidden;
+    clear: both;
+}
+.markdown-body span.align-right>span {
+    display: block;
+    overflow: hidden;
+    margin: 13px 0 0;
+    text-align: right;
+}
+.markdown-body span.align-right span img {
+    margin: 0;
+    text-align: right;
+}
+.markdown-body span.float-left {
+    display: block;
+    margin-right: 13px;
+    overflow: hidden;
+    float: left;
+}
+.markdown-body span.float-left span {
+    margin: 13px 0 0;
+}
+.markdown-body span.float-right {
+    display: block;
+    margin-left: 13px;
+    overflow: hidden;
+    float: right;
+}
+.markdown-body span.float-right>span {
+    display: block;
+    overflow: hidden;
+    margin: 13px auto 0;
+    text-align: right;
+}
+.markdown-body code, .markdown-body tt {
+    margin: 0 2px;
+    padding: 0px 5px;
+    border: 1px solid #eaeaea;
+    background-color: #f8f8f8;
+    border-radius: 3px;
+}
+.markdown-body code {
+    white-space: nowrap;
+}
+.markdown-body pre>code {
+    margin: 0;
+    padding: 0;
+    white-space: pre;
+    border: none;
+    background: transparent;
+}
+.markdown-body .highlight pre, .markdown-body pre {
+    background-color: #f8f8f8;
+    border: 1px solid #ccc;
+    font-size: 13px;
+    line-height: 19px;
+    overflow: auto;
+    padding: 6px 10px;
+    border-radius: 3px;
+}
+.markdown-body pre code, .markdown-body pre tt {
+    margin: 0;
+    padding: 0;
+    background-color: transparent;
+    border: none;
+}
+''')
+sys.stdout.write(HtmlFormatter(style='pastie').get_style_defs('.highlight'))
+sys.stdout.write('''
+</style>   
+''')
+sys.stdout.write("<div class='markdown-body'>")
+sys.stdout.flush()
+# Note: you may want to run this through bleach for sanitization
+markdown.markdownFromFile(
+	output_format="html5",
+	extensions=[
+		"markdown.extensions.fenced_code",
+		"markdown.extensions.codehilite",
+		"markdown.extensions.tables",
+		"markdown.extensions.sane_lists",
+		TocExtension(anchorlink=True)],
+	extension_configs={
+		"markdown.extensions.codehilite":{"css_class":"highlight"}})
+sys.stdout.write("</div>")
diff --git a/www/git.causal.agency/cgit/filters/html-converters/rst2html b/www/git.causal.agency/cgit/filters/html-converters/rst2html
new file mode 100755
index 00000000..02d90f81
--- /dev/null
+++ b/www/git.causal.agency/cgit/filters/html-converters/rst2html
@@ -0,0 +1,2 @@
+#!/bin/bash
+exec rst2html.py --template <(echo -e "%(stylesheet)s\n%(body_pre_docinfo)s\n%(docinfo)s\n%(body)s")
diff --git a/www/git.causal.agency/cgit/filters/html-converters/txt2html b/www/git.causal.agency/cgit/filters/html-converters/txt2html
new file mode 100755
index 00000000..495eeceb
--- /dev/null
+++ b/www/git.causal.agency/cgit/filters/html-converters/txt2html
@@ -0,0 +1,4 @@
+#!/bin/sh
+echo "<pre>"
+sed "s|&|\\&amp;|g;s|'|\\&apos;|g;s|\"|\\&quot;|g;s|<|\\&lt;|g;s|>|\\&gt;|g"
+echo "</pre>"
diff --git a/www/git.causal.agency/cgit/filters/syntax-highlighting.py b/www/git.causal.agency/cgit/filters/syntax-highlighting.py
new file mode 100755
index 00000000..e912594c
--- /dev/null
+++ b/www/git.causal.agency/cgit/filters/syntax-highlighting.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+
+# This script uses Pygments and Python3. You must have both installed
+# for this to work.
+#
+# http://pygments.org/
+# http://python.org/
+#
+# It may be used with the source-filter or repo.source-filter settings
+# in cgitrc.
+#
+# The following environment variables can be used to retrieve the
+# configuration of the repository for which this script is called:
+# CGIT_REPO_URL        ( = repo.url       setting )
+# CGIT_REPO_NAME       ( = repo.name      setting )
+# CGIT_REPO_PATH       ( = repo.path      setting )
+# CGIT_REPO_OWNER      ( = repo.owner     setting )
+# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
+# CGIT_REPO_SECTION    ( = section        setting )
+# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
+
+
+import sys
+import io
+from pygments import highlight
+from pygments.util import ClassNotFound
+from pygments.lexers import TextLexer
+from pygments.lexers import guess_lexer
+from pygments.lexers import guess_lexer_for_filename
+from pygments.formatters import HtmlFormatter
+
+
+sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace')
+sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
+data = sys.stdin.read()
+filename = sys.argv[1]
+formatter = HtmlFormatter(style='pastie', nobackground=True)
+
+try:
+	lexer = guess_lexer_for_filename(filename, data)
+except ClassNotFound:
+	# check if there is any shebang
+	if data[0:2] == '#!':
+		lexer = guess_lexer(data)
+	else:
+		lexer = TextLexer()
+except TypeError:
+	lexer = TextLexer()
+
+# highlight! :-)
+# printout pygments' css definitions as well
+sys.stdout.write('<style>')
+sys.stdout.write(formatter.get_style_defs('.highlight'))
+sys.stdout.write('</style>')
+sys.stdout.write(highlight(data, lexer, formatter, outfile=None))
diff --git a/www/git.causal.agency/cgit/filters/syntax-highlighting.sh b/www/git.causal.agency/cgit/filters/syntax-highlighting.sh
new file mode 100755
index 00000000..840bc34f
--- /dev/null
+++ b/www/git.causal.agency/cgit/filters/syntax-highlighting.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+# This script can be used to implement syntax highlighting in the cgit
+# tree-view by referring to this file with the source-filter or repo.source-
+# filter options in cgitrc.
+#
+# This script requires a shell supporting the ${var##pattern} syntax.
+# It is supported by at least dash and bash, however busybox environments
+# might have to use an external call to sed instead.
+#
+# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax
+# highlighting, so you'll probably want something like the following included
+# in your css file:
+#
+# Style definition file generated by highlight 2.4.8, http://www.andre-simon.de/
+#
+# table.blob .num  { color:#2928ff; }
+# table.blob .esc  { color:#ff00ff; }
+# table.blob .str  { color:#ff0000; }
+# table.blob .dstr { color:#818100; }
+# table.blob .slc  { color:#838183; font-style:italic; }
+# table.blob .com  { color:#838183; font-style:italic; }
+# table.blob .dir  { color:#008200; }
+# table.blob .sym  { color:#000000; }
+# table.blob .kwa  { color:#000000; font-weight:bold; }
+# table.blob .kwb  { color:#830000; }
+# table.blob .kwc  { color:#000000; font-weight:bold; }
+# table.blob .kwd  { color:#010181; }
+#
+#
+# Style definition file generated by highlight 2.6.14, http://www.andre-simon.de/
+#
+# body.hl  { background-color:#ffffff; }
+# pre.hl   { color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New';}
+# .hl.num  { color:#2928ff; }
+# .hl.esc  { color:#ff00ff; }
+# .hl.str  { color:#ff0000; }
+# .hl.dstr { color:#818100; }
+# .hl.slc  { color:#838183; font-style:italic; }
+# .hl.com  { color:#838183; font-style:italic; }
+# .hl.dir  { color:#008200; }
+# .hl.sym  { color:#000000; }
+# .hl.line { color:#555555; }
+# .hl.mark { background-color:#ffffbb;}
+# .hl.kwa  { color:#000000; font-weight:bold; }
+# .hl.kwb  { color:#830000; }
+# .hl.kwc  { color:#000000; font-weight:bold; }
+# .hl.kwd  { color:#010181; }
+#
+#
+# Style definition file generated by highlight 3.8, http://www.andre-simon.de/
+#
+# body.hl { background-color:#e0eaee; }
+# pre.hl  { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New';}
+# .hl.num { color:#b07e00; }
+# .hl.esc { color:#ff00ff; }
+# .hl.str { color:#bf0303; }
+# .hl.pps { color:#818100; }
+# .hl.slc { color:#838183; font-style:italic; }
+# .hl.com { color:#838183; font-style:italic; }
+# .hl.ppc { color:#008200; }
+# .hl.opt { color:#000000; }
+# .hl.lin { color:#555555; }
+# .hl.kwa { color:#000000; font-weight:bold; }
+# .hl.kwb { color:#0057ae; }
+# .hl.kwc { color:#000000; font-weight:bold; }
+# .hl.kwd { color:#010181; }
+#
+#
+# Style definition file generated by highlight 3.13, http://www.andre-simon.de/
+#
+# body.hl { background-color:#e0eaee; }
+# pre.hl  { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New',monospace;}
+# .hl.num { color:#b07e00; }
+# .hl.esc { color:#ff00ff; }
+# .hl.str { color:#bf0303; }
+# .hl.pps { color:#818100; }
+# .hl.slc { color:#838183; font-style:italic; }
+# .hl.com { color:#838183; font-style:italic; }
+# .hl.ppc { color:#008200; }
+# .hl.opt { color:#000000; }
+# .hl.ipl { color:#0057ae; }
+# .hl.lin { color:#555555; }
+# .hl.kwa { color:#000000; font-weight:bold; }
+# .hl.kwb { color:#0057ae; }
+# .hl.kwc { color:#000000; font-weight:bold; }
+# .hl.kwd { color:#010181; }
+#
+#
+# The following environment variables can be used to retrieve the configuration
+# of the repository for which this script is called:
+# CGIT_REPO_URL        ( = repo.url       setting )
+# CGIT_REPO_NAME       ( = repo.name      setting )
+# CGIT_REPO_PATH       ( = repo.path      setting )
+# CGIT_REPO_OWNER      ( = repo.owner     setting )
+# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
+# CGIT_REPO_SECTION    ( = section        setting )
+# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
+#
+
+# store filename and extension in local vars
+BASENAME="$1"
+EXTENSION="${BASENAME##*.}"
+
+[ "${BASENAME}" = "${EXTENSION}" ] && EXTENSION=txt
+[ -z "${EXTENSION}" ] && EXTENSION=txt
+
+# map Makefile and Makefile.* to .mk
+[ "${BASENAME%%.*}" = "Makefile" ] && EXTENSION=mk
+
+# highlight versions 2 and 3 have different commandline options. Specifically,
+# the -X option that is used for version 2 is replaced by the -O xhtml option
+# for version 3.
+#
+# Version 2 can be found (for example) on EPEL 5, while version 3 can be
+# found (for example) on EPEL 6.
+#
+# This is for version 2
+exec highlight --force -f -I -X -S "$EXTENSION" 2>/dev/null
+
+# This is for version 3
+#exec highlight --force -f -I -O xhtml -S "$EXTENSION" 2>/dev/null
diff --git a/www/git.causal.agency/cgit/gen-version.sh b/www/git.causal.agency/cgit/gen-version.sh
new file mode 100755
index 00000000..80cf49af
--- /dev/null
+++ b/www/git.causal.agency/cgit/gen-version.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Get version-info specified in Makefile
+V=$1
+
+# Use `git describe` to get current version if we're inside a git repo
+if test "$(git rev-parse --git-dir 2>/dev/null)" = '.git'
+then
+	V=$(git describe --abbrev=4 HEAD 2>/dev/null)
+fi
+
+new="CGIT_VERSION = $V"
+old=$(cat VERSION 2>/dev/null)
+
+# Exit if VERSION is uptodate
+test "$old" = "$new" && exit 0
+
+# Update VERSION with new version-info
+echo "$new" > VERSION
+cat VERSION
diff --git a/www/git.causal.agency/cgit/html.c b/www/git.causal.agency/cgit/html.c
new file mode 100644
index 00000000..cefcf5e7
--- /dev/null
+++ b/www/git.causal.agency/cgit/html.c
@@ -0,0 +1,344 @@
+/* html.c: helper functions for html output
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "html.h"
+#include "url.h"
+
+/* Percent-encoding of each character, except: a-zA-Z0-9!$()*,./:;@- */
+static const char* url_escape_table[256] = {
+	"%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
+	"%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
+	"%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
+	"%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
+	"%20", NULL,  "%22", "%23", NULL,  "%25", "%26", "%27",
+	NULL,  NULL,  NULL,  "%2b", NULL,  NULL,  NULL,  NULL,
+	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
+	NULL,  NULL,  NULL,  NULL,  "%3c", "%3d", "%3e", "%3f",
+	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
+	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
+	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
+	NULL,  NULL,  NULL,  NULL,  "%5c", NULL,  "%5e", NULL,
+	"%60", NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
+	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
+	NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
+	NULL,  NULL,  NULL,  "%7b", "%7c", "%7d", NULL,  "%7f",
+	"%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
+	"%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
+	"%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
+	"%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
+	"%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
+	"%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
+	"%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
+	"%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
+	"%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
+	"%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
+	"%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
+	"%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
+	"%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
+	"%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
+	"%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
+	"%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
+};
+
+char *fmt(const char *format, ...)
+{
+	static char buf[8][1024];
+	static int bufidx;
+	int len;
+	va_list args;
+
+	bufidx++;
+	bufidx &= 7;
+
+	va_start(args, format);
+	len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args);
+	va_end(args);
+	if (len > sizeof(buf[bufidx])) {
+		fprintf(stderr, "[html.c] string truncated: %s\n", format);
+		exit(1);
+	}
+	return buf[bufidx];
+}
+
+char *fmtalloc(const char *format, ...)
+{
+	struct strbuf sb = STRBUF_INIT;
+	va_list args;
+
+	va_start(args, format);
+	strbuf_vaddf(&sb, format, args);
+	va_end(args);
+
+	return strbuf_detach(&sb, NULL);
+}
+
+void html_raw(const char *data, size_t size)
+{
+	if (fwrite(data, 1, size, stdout) != size)
+		die_errno("write error on html output");
+}
+
+void html(const char *txt)
+{
+	html_raw(txt, strlen(txt));
+}
+
+void htmlf(const char *format, ...)
+{
+	va_list args;
+	struct strbuf buf = STRBUF_INIT;
+
+	va_start(args, format);
+	strbuf_vaddf(&buf, format, args);
+	va_end(args);
+	html(buf.buf);
+	strbuf_release(&buf);
+}
+
+void html_txtf(const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	html_vtxtf(format, args);
+	va_end(args);
+}
+
+void html_vtxtf(const char *format, va_list ap)
+{
+	va_list cp;
+	struct strbuf buf = STRBUF_INIT;
+
+	va_copy(cp, ap);
+	strbuf_vaddf(&buf, format, cp);
+	va_end(cp);
+	html_txt(buf.buf);
+	strbuf_release(&buf);
+}
+
+void html_txt(const char *txt)
+{
+	if (txt)
+		html_ntxt(txt, strlen(txt));
+}
+
+ssize_t html_ntxt(const char *txt, size_t len)
+{
+	const char *t = txt;
+	ssize_t slen;
+
+	if (len > SSIZE_MAX)
+		return -1;
+
+	slen = (ssize_t) len;
+	while (t && *t && slen--) {
+		int c = *t;
+		if (c == '<' || c == '>' || c == '&') {
+			html_raw(txt, t - txt);
+			if (c == '>')
+				html("&gt;");
+			else if (c == '<')
+				html("&lt;");
+			else if (c == '&')
+				html("&amp;");
+			txt = t + 1;
+		}
+		t++;
+	}
+	if (t != txt)
+		html_raw(txt, t - txt);
+	return slen;
+}
+
+void html_attrf(const char *fmt, ...)
+{
+	va_list ap;
+	struct strbuf sb = STRBUF_INIT;
+
+	va_start(ap, fmt);
+	strbuf_vaddf(&sb, fmt, ap);
+	va_end(ap);
+
+	html_attr(sb.buf);
+	strbuf_release(&sb);
+}
+
+void html_attr(const char *txt)
+{
+	const char *t = txt;
+	while (t && *t) {
+		int c = *t;
+		if (c == '<' || c == '>' || c == '\'' || c == '\"' || c == '&') {
+			html_raw(txt, t - txt);
+			if (c == '>')
+				html("&gt;");
+			else if (c == '<')
+				html("&lt;");
+			else if (c == '\'')
+				html("&#x27;");
+			else if (c == '"')
+				html("&quot;");
+			else if (c == '&')
+				html("&amp;");
+			txt = t + 1;
+		}
+		t++;
+	}
+	if (t != txt)
+		html(txt);
+}
+
+void html_url_path(const char *txt)
+{
+	const char *t = txt;
+	while (t && *t) {
+		unsigned char c = *t;
+		const char *e = url_escape_table[c];
+		if (e && c != '+' && c != '&') {
+			html_raw(txt, t - txt);
+			html(e);
+			txt = t + 1;
+		}
+		t++;
+	}
+	if (t != txt)
+		html(txt);
+}
+
+void html_url_arg(const char *txt)
+{
+	const char *t = txt;
+	while (t && *t) {
+		unsigned char c = *t;
+		const char *e = url_escape_table[c];
+		if (c == ' ')
+			e = "+";
+		if (e) {
+			html_raw(txt, t - txt);
+			html(e);
+			txt = t + 1;
+		}
+		t++;
+	}
+	if (t != txt)
+		html(txt);
+}
+
+void html_header_arg_in_quotes(const char *txt)
+{
+	const char *t = txt;
+	while (t && *t) {
+		unsigned char c = *t;
+		const char *e = NULL;
+		if (c == '\\')
+			e = "\\\\";
+		else if (c == '\r')
+			e = "\\r";
+		else if (c == '\n')
+			e = "\\n";
+		else if (c == '"')
+			e = "\\\"";
+		if (e) {
+			html_raw(txt, t - txt);
+			html(e);
+			txt = t + 1;
+		}
+		t++;
+	}
+	if (t != txt)
+		html(txt);
+
+}
+
+void html_hidden(const char *name, const char *value)
+{
+	html("<input type='hidden' name='");
+	html_attr(name);
+	html("' value='");
+	html_attr(value);
+	html("'/>");
+}
+
+void html_option(const char *value, const char *text, const char *selected_value)
+{
+	html("<option value='");
+	html_attr(value);
+	html("'");
+	if (selected_value && !strcmp(selected_value, value))
+		html(" selected='selected'");
+	html(">");
+	html_txt(text);
+	html("</option>\n");
+}
+
+void html_intoption(int value, const char *text, int selected_value)
+{
+	htmlf("<option value='%d'%s>", value,
+	      value == selected_value ? " selected='selected'" : "");
+	html_txt(text);
+	html("</option>");
+}
+
+void html_link_open(const char *url, const char *title, const char *class)
+{
+	html("<a href='");
+	html_attr(url);
+	if (title) {
+		html("' title='");
+		html_attr(title);
+	}
+	if (class) {
+		html("' class='");
+		html_attr(class);
+	}
+	html("'>");
+}
+
+void html_link_close(void)
+{
+	html("</a>");
+}
+
+void html_fileperm(unsigned short mode)
+{
+	htmlf("%c%c%c", (mode & 4 ? 'r' : '-'),
+	      (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-'));
+}
+
+int html_include(const char *filename)
+{
+	FILE *f;
+	char buf[4096];
+	size_t len;
+
+	if (!(f = fopen(filename, "r"))) {
+		fprintf(stderr, "[cgit] Failed to include file %s: %s (%d).\n",
+			filename, strerror(errno), errno);
+		return -1;
+	}
+	while ((len = fread(buf, 1, 4096, f)) > 0)
+		html_raw(buf, len);
+	fclose(f);
+	return 0;
+}
+
+void http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value))
+{
+	const char *t = txt;
+
+	while (t && *t) {
+		char *name = url_decode_parameter_name(&t);
+		if (*name) {
+			char *value = url_decode_parameter_value(&t);
+			fn(name, value);
+			free(value);
+		}
+		free(name);
+	}
+}
diff --git a/www/git.causal.agency/cgit/html.h b/www/git.causal.agency/cgit/html.h
new file mode 100644
index 00000000..fa4de775
--- /dev/null
+++ b/www/git.causal.agency/cgit/html.h
@@ -0,0 +1,37 @@
+#ifndef HTML_H
+#define HTML_H
+
+#include "cgit.h"
+
+extern void html_raw(const char *txt, size_t size);
+extern void html(const char *txt);
+
+__attribute__((format (printf,1,2)))
+extern void htmlf(const char *format,...);
+
+__attribute__((format (printf,1,2)))
+extern void html_txtf(const char *format,...);
+
+__attribute__((format (printf,1,0)))
+extern void html_vtxtf(const char *format, va_list ap);
+
+__attribute__((format (printf,1,2)))
+extern void html_attrf(const char *format,...);
+
+extern void html_txt(const char *txt);
+extern ssize_t html_ntxt(const char *txt, size_t len);
+extern void html_attr(const char *txt);
+extern void html_url_path(const char *txt);
+extern void html_url_arg(const char *txt);
+extern void html_header_arg_in_quotes(const char *txt);
+extern void html_hidden(const char *name, const char *value);
+extern void html_option(const char *value, const char *text, const char *selected_value);
+extern void html_intoption(int value, const char *text, int selected_value);
+extern void html_link_open(const char *url, const char *title, const char *class);
+extern void html_link_close(void);
+extern void html_fileperm(unsigned short mode);
+extern int html_include(const char *filename);
+
+extern void http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value));
+
+#endif /* HTML_H */
diff --git a/www/git.causal.agency/cgit/parsing.c b/www/git.causal.agency/cgit/parsing.c
new file mode 100644
index 00000000..72b59b3c
--- /dev/null
+++ b/www/git.causal.agency/cgit/parsing.c
@@ -0,0 +1,223 @@
+/* parsing.c: parsing of config files
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+
+/*
+ * url syntax: [repo ['/' cmd [ '/' path]]]
+ *   repo: any valid repo url, may contain '/'
+ *   cmd:  log | commit | diff | tree | view | blob | snapshot
+ *   path: any valid path, may contain '/'
+ *
+ */
+void cgit_parse_url(const char *url)
+{
+	char *c, *cmd, *p;
+	struct cgit_repo *repo;
+
+	if (!url || url[0] == '\0')
+		return;
+
+	ctx.qry.page = NULL;
+	ctx.repo = cgit_get_repoinfo(url);
+	if (ctx.repo) {
+		ctx.qry.repo = ctx.repo->url;
+		return;
+	}
+
+	cmd = NULL;
+	c = strchr(url, '/');
+	while (c) {
+		c[0] = '\0';
+		repo = cgit_get_repoinfo(url);
+		if (repo) {
+			ctx.repo = repo;
+			cmd = c;
+		}
+		c[0] = '/';
+		c = strchr(c + 1, '/');
+	}
+
+	if (ctx.repo) {
+		ctx.qry.repo = ctx.repo->url;
+		p = strchr(cmd + 1, '/');
+		if (p) {
+			p[0] = '\0';
+			if (p[1])
+				ctx.qry.path = trim_end(p + 1, '/');
+		}
+		if (cmd[1])
+			ctx.qry.page = xstrdup(cmd + 1);
+	}
+}
+
+static char *substr(const char *head, const char *tail)
+{
+	char *buf;
+
+	if (tail < head)
+		return xstrdup("");
+	buf = xmalloc(tail - head + 1);
+	strlcpy(buf, head, tail - head + 1);
+	return buf;
+}
+
+static void parse_user(const char *t, char **name, char **email, unsigned long *date, int *tz)
+{
+	struct ident_split ident;
+	unsigned email_len;
+
+	if (!split_ident_line(&ident, t, strchrnul(t, '\n') - t)) {
+		*name = substr(ident.name_begin, ident.name_end);
+
+		email_len = ident.mail_end - ident.mail_begin;
+		*email = xmalloc(strlen("<") + email_len + strlen(">") + 1);
+		xsnprintf(*email, email_len + 3, "<%.*s>", email_len, ident.mail_begin);
+
+		if (ident.date_begin)
+			*date = strtoul(ident.date_begin, NULL, 10);
+		if (ident.tz_begin)
+			*tz = atoi(ident.tz_begin);
+	}
+}
+
+#ifdef NO_ICONV
+#define reencode(a, b, c)
+#else
+static const char *reencode(char **txt, const char *src_enc, const char *dst_enc)
+{
+	char *tmp;
+
+	if (!txt)
+		return NULL;
+
+	if (!*txt || !src_enc || !dst_enc)
+		return *txt;
+
+	/* no encoding needed if src_enc equals dst_enc */
+	if (!strcasecmp(src_enc, dst_enc))
+		return *txt;
+
+	tmp = reencode_string(*txt, dst_enc, src_enc);
+	if (tmp) {
+		free(*txt);
+		*txt = tmp;
+	}
+	return *txt;
+}
+#endif
+
+static const char *next_header_line(const char *p)
+{
+	p = strchr(p, '\n');
+	if (!p)
+		return NULL;
+	return p + 1;
+}
+
+static int end_of_header(const char *p)
+{
+	return !p || (*p == '\n');
+}
+
+struct commitinfo *cgit_parse_commit(struct commit *commit)
+{
+	struct commitinfo *ret;
+	const char *p = repo_get_commit_buffer(the_repository, commit, NULL);
+	const char *t;
+
+	ret = xcalloc(1, sizeof(struct commitinfo));
+	ret->commit = commit;
+
+	if (!p)
+		return ret;
+
+	if (!skip_prefix(p, "tree ", &p))
+		die("Bad commit: %s", oid_to_hex(&commit->object.oid));
+	p += the_hash_algo->hexsz + 1;
+
+	while (skip_prefix(p, "parent ", &p))
+		p += the_hash_algo->hexsz + 1;
+
+	if (p && skip_prefix(p, "author ", &p)) {
+		parse_user(p, &ret->author, &ret->author_email,
+			&ret->author_date, &ret->author_tz);
+		p = next_header_line(p);
+	}
+
+	if (p && skip_prefix(p, "committer ", &p)) {
+		parse_user(p, &ret->committer, &ret->committer_email,
+			&ret->committer_date, &ret->committer_tz);
+		p = next_header_line(p);
+	}
+
+	if (p && skip_prefix(p, "encoding ", &p)) {
+		t = strchr(p, '\n');
+		if (t) {
+			ret->msg_encoding = substr(p, t + 1);
+			p = t + 1;
+		}
+	}
+
+	if (!ret->msg_encoding)
+		ret->msg_encoding = xstrdup("UTF-8");
+
+	while (!end_of_header(p))
+		p = next_header_line(p);
+	while (p && *p == '\n')
+		p++;
+	if (!p)
+		return ret;
+
+	t = strchrnul(p, '\n');
+	ret->subject = substr(p, t);
+	while (*t == '\n')
+		t++;
+	ret->msg = xstrdup(t);
+
+	reencode(&ret->author, ret->msg_encoding, PAGE_ENCODING);
+	reencode(&ret->author_email, ret->msg_encoding, PAGE_ENCODING);
+	reencode(&ret->committer, ret->msg_encoding, PAGE_ENCODING);
+	reencode(&ret->committer_email, ret->msg_encoding, PAGE_ENCODING);
+	reencode(&ret->subject, ret->msg_encoding, PAGE_ENCODING);
+	reencode(&ret->msg, ret->msg_encoding, PAGE_ENCODING);
+
+	return ret;
+}
+
+struct taginfo *cgit_parse_tag(struct tag *tag)
+{
+	void *data;
+	enum object_type type;
+	unsigned long size;
+	const char *p;
+	struct taginfo *ret = NULL;
+
+	data = read_object_file(&tag->object.oid, &type, &size);
+	if (!data || type != OBJ_TAG)
+		goto cleanup;
+
+	ret = xcalloc(1, sizeof(struct taginfo));
+
+	for (p = data; !end_of_header(p); p = next_header_line(p)) {
+		if (skip_prefix(p, "tagger ", &p)) {
+			parse_user(p, &ret->tagger, &ret->tagger_email,
+				&ret->tagger_date, &ret->tagger_tz);
+		}
+	}
+
+	while (p && *p == '\n')
+		p++;
+
+	if (p && *p)
+		ret->msg = xstrdup(p);
+
+cleanup:
+	free(data);
+	return ret;
+}
diff --git a/www/git.causal.agency/cgit/robots.txt b/www/git.causal.agency/cgit/robots.txt
new file mode 100644
index 00000000..1b33266d
--- /dev/null
+++ b/www/git.causal.agency/cgit/robots.txt
@@ -0,0 +1,4 @@
+User-agent: *
+Disallow: /*/snapshot/*
+Disallow: /*/blame/*
+Allow: /
diff --git a/www/git.causal.agency/cgit/scan-tree.c b/www/git.causal.agency/cgit/scan-tree.c
new file mode 100644
index 00000000..6a2f65a8
--- /dev/null
+++ b/www/git.causal.agency/cgit/scan-tree.c
@@ -0,0 +1,270 @@
+/* scan-tree.c
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "scan-tree.h"
+#include "configfile.h"
+#include "html.h"
+#include <config.h>
+
+/* return 1 if path contains a objects/ directory and a HEAD file */
+static int is_git_dir(const char *path)
+{
+	struct stat st;
+	struct strbuf pathbuf = STRBUF_INIT;
+	int result = 0;
+
+	strbuf_addf(&pathbuf, "%s/objects", path);
+	if (stat(pathbuf.buf, &st)) {
+		if (errno != ENOENT)
+			fprintf(stderr, "Error checking path %s: %s (%d)\n",
+				path, strerror(errno), errno);
+		goto out;
+	}
+	if (!S_ISDIR(st.st_mode))
+		goto out;
+
+	strbuf_reset(&pathbuf);
+	strbuf_addf(&pathbuf, "%s/HEAD", path);
+	if (stat(pathbuf.buf, &st)) {
+		if (errno != ENOENT)
+			fprintf(stderr, "Error checking path %s: %s (%d)\n",
+				path, strerror(errno), errno);
+		goto out;
+	}
+	if (!S_ISREG(st.st_mode))
+		goto out;
+
+	result = 1;
+out:
+	strbuf_release(&pathbuf);
+	return result;
+}
+
+static struct cgit_repo *repo;
+static repo_config_fn config_fn;
+
+static void scan_tree_repo_config(const char *name, const char *value)
+{
+	config_fn(repo, name, value);
+}
+
+static int gitconfig_config(const char *key, const char *value, void *cb)
+{
+	const char *name;
+
+	if (!strcmp(key, "gitweb.owner"))
+		config_fn(repo, "owner", value);
+	else if (!strcmp(key, "gitweb.description"))
+		config_fn(repo, "desc", value);
+	else if (!strcmp(key, "gitweb.category"))
+		config_fn(repo, "section", value);
+	else if (!strcmp(key, "gitweb.homepage"))
+		config_fn(repo, "homepage", value);
+	else if (skip_prefix(key, "cgit.", &name))
+		config_fn(repo, name, value);
+
+	return 0;
+}
+
+static char *xstrrchr(char *s, char *from, int c)
+{
+	while (from >= s && *from != c)
+		from--;
+	return from < s ? NULL : from;
+}
+
+static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn)
+{
+	struct stat st;
+	struct passwd *pwd;
+	size_t pathlen;
+	struct strbuf rel = STRBUF_INIT;
+	char *p, *slash;
+	int n;
+	size_t size;
+
+	if (stat(path->buf, &st)) {
+		fprintf(stderr, "Error accessing %s: %s (%d)\n",
+			path->buf, strerror(errno), errno);
+		return;
+	}
+
+	strbuf_addch(path, '/');
+	pathlen = path->len;
+
+	if (ctx.cfg.strict_export) {
+		strbuf_addstr(path, ctx.cfg.strict_export);
+		if(stat(path->buf, &st))
+			return;
+		strbuf_setlen(path, pathlen);
+	}
+
+	strbuf_addstr(path, "noweb");
+	if (!stat(path->buf, &st))
+		return;
+	strbuf_setlen(path, pathlen);
+
+	if (!starts_with(path->buf, base))
+		strbuf_addbuf(&rel, path);
+	else
+		strbuf_addstr(&rel, path->buf + strlen(base) + 1);
+
+	if (!strcmp(rel.buf + rel.len - 5, "/.git"))
+		strbuf_setlen(&rel, rel.len - 5);
+	else if (rel.len && rel.buf[rel.len - 1] == '/')
+		strbuf_setlen(&rel, rel.len - 1);
+
+	repo = cgit_add_repo(rel.buf);
+	config_fn = fn;
+	if (ctx.cfg.enable_git_config) {
+		strbuf_addstr(path, "config");
+		git_config_from_file(gitconfig_config, path->buf, NULL);
+		strbuf_setlen(path, pathlen);
+	}
+
+	if (ctx.cfg.remove_suffix) {
+		size_t urllen;
+		strip_suffix(repo->url, ".git", &urllen);
+		strip_suffix_mem(repo->url, &urllen, "/");
+		repo->url[urllen] = '\0';
+	}
+	repo->path = xstrdup(path->buf);
+	while (!repo->owner) {
+		if ((pwd = getpwuid(st.st_uid)) == NULL) {
+			fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
+				path->buf, strerror(errno), errno);
+			break;
+		}
+		if (pwd->pw_gecos)
+			if ((p = strchr(pwd->pw_gecos, ',')))
+				*p = '\0';
+		repo->owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
+	}
+
+	if (repo->desc == cgit_default_repo_desc || !repo->desc) {
+		strbuf_addstr(path, "description");
+		if (!stat(path->buf, &st))
+			readfile(path->buf, &repo->desc, &size);
+		strbuf_setlen(path, pathlen);
+	}
+
+	if (ctx.cfg.section_from_path) {
+		n = ctx.cfg.section_from_path;
+		if (n > 0) {
+			slash = rel.buf - 1;
+			while (slash && n && (slash = strchr(slash + 1, '/')))
+				n--;
+		} else {
+			slash = rel.buf + rel.len;
+			while (slash && n && (slash = xstrrchr(rel.buf, slash - 1, '/')))
+				n++;
+		}
+		if (slash && !n) {
+			*slash = '\0';
+			repo->section = xstrdup(rel.buf);
+			*slash = '/';
+			if (starts_with(repo->name, repo->section)) {
+				repo->name += strlen(repo->section);
+				if (*repo->name == '/')
+					repo->name++;
+			}
+		}
+	}
+
+	strbuf_addstr(path, "cgitrc");
+	if (!stat(path->buf, &st))
+		parse_configfile(path->buf, &scan_tree_repo_config);
+
+	strbuf_release(&rel);
+}
+
+static void scan_path(const char *base, const char *path, repo_config_fn fn)
+{
+	DIR *dir = opendir(path);
+	struct dirent *ent;
+	struct strbuf pathbuf = STRBUF_INIT;
+	size_t pathlen = strlen(path);
+	struct stat st;
+
+	if (!dir) {
+		fprintf(stderr, "Error opening directory %s: %s (%d)\n",
+			path, strerror(errno), errno);
+		return;
+	}
+
+	strbuf_add(&pathbuf, path, strlen(path));
+	if (is_git_dir(pathbuf.buf)) {
+		add_repo(base, &pathbuf, fn);
+		goto end;
+	}
+	strbuf_addstr(&pathbuf, "/.git");
+	if (is_git_dir(pathbuf.buf)) {
+		add_repo(base, &pathbuf, fn);
+		goto end;
+	}
+	/*
+	 * Add one because we don't want to lose the trailing '/' when we
+	 * reset the length of pathbuf in the loop below.
+	 */
+	pathlen++;
+	while ((ent = readdir(dir)) != NULL) {
+		if (ent->d_name[0] == '.') {
+			if (ent->d_name[1] == '\0')
+				continue;
+			if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
+				continue;
+			if (!ctx.cfg.scan_hidden_path)
+				continue;
+		}
+		strbuf_setlen(&pathbuf, pathlen);
+		strbuf_addstr(&pathbuf, ent->d_name);
+		if (stat(pathbuf.buf, &st)) {
+			fprintf(stderr, "Error checking path %s: %s (%d)\n",
+				pathbuf.buf, strerror(errno), errno);
+			continue;
+		}
+		if (S_ISDIR(st.st_mode))
+			scan_path(base, pathbuf.buf, fn);
+	}
+end:
+	strbuf_release(&pathbuf);
+	closedir(dir);
+}
+
+void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
+{
+	struct strbuf line = STRBUF_INIT;
+	FILE *projects;
+	int err;
+
+	projects = fopen(projectsfile, "r");
+	if (!projects) {
+		fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
+			projectsfile, strerror(errno), errno);
+		return;
+	}
+	while (strbuf_getline(&line, projects) != EOF) {
+		if (!line.len)
+			continue;
+		strbuf_insert(&line, 0, "/", 1);
+		strbuf_insert(&line, 0, path, strlen(path));
+		scan_path(path, line.buf, fn);
+	}
+	if ((err = ferror(projects))) {
+		fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
+			projectsfile, strerror(err), err);
+	}
+	fclose(projects);
+	strbuf_release(&line);
+}
+
+void scan_tree(const char *path, repo_config_fn fn)
+{
+	scan_path(path, path, fn);
+}
diff --git a/www/git.causal.agency/cgit/scan-tree.h b/www/git.causal.agency/cgit/scan-tree.h
new file mode 100644
index 00000000..1afbd4bb
--- /dev/null
+++ b/www/git.causal.agency/cgit/scan-tree.h
@@ -0,0 +1,2 @@
+extern void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn);
+extern void scan_tree(const char *path, repo_config_fn fn);
diff --git a/www/git.causal.agency/cgit/shared.c b/www/git.causal.agency/cgit/shared.c
new file mode 100644
index 00000000..8115469a
--- /dev/null
+++ b/www/git.causal.agency/cgit/shared.c
@@ -0,0 +1,579 @@
+/* shared.c: global vars + some callback functions
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+
+struct cgit_repolist cgit_repolist;
+struct cgit_context ctx;
+
+int chk_zero(int result, char *msg)
+{
+	if (result != 0)
+		die_errno("%s", msg);
+	return result;
+}
+
+int chk_positive(int result, char *msg)
+{
+	if (result <= 0)
+		die_errno("%s", msg);
+	return result;
+}
+
+int chk_non_negative(int result, char *msg)
+{
+	if (result < 0)
+		die_errno("%s", msg);
+	return result;
+}
+
+char *cgit_default_repo_desc = "[no description]";
+struct cgit_repo *cgit_add_repo(const char *url)
+{
+	struct cgit_repo *ret;
+
+	if (++cgit_repolist.count > cgit_repolist.length) {
+		if (cgit_repolist.length == 0)
+			cgit_repolist.length = 8;
+		else
+			cgit_repolist.length *= 2;
+		cgit_repolist.repos = xrealloc(cgit_repolist.repos,
+					       cgit_repolist.length *
+					       sizeof(struct cgit_repo));
+	}
+
+	ret = &cgit_repolist.repos[cgit_repolist.count-1];
+	memset(ret, 0, sizeof(struct cgit_repo));
+	ret->url = trim_end(url, '/');
+	ret->name = ret->url;
+	ret->path = NULL;
+	ret->desc = cgit_default_repo_desc;
+	ret->extra_head_content = NULL;
+	ret->owner = NULL;
+	ret->homepage = NULL;
+	ret->section = ctx.cfg.section;
+	ret->snapshots = ctx.cfg.snapshots;
+	ret->enable_blame = ctx.cfg.enable_blame;
+	ret->enable_commit_graph = ctx.cfg.enable_commit_graph;
+	ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
+	ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
+	ret->enable_remote_branches = ctx.cfg.enable_remote_branches;
+	ret->enable_subject_links = ctx.cfg.enable_subject_links;
+	ret->enable_html_serving = ctx.cfg.enable_html_serving;
+	ret->max_stats = ctx.cfg.max_stats;
+	ret->branch_sort = ctx.cfg.branch_sort;
+	ret->commit_sort = ctx.cfg.commit_sort;
+	ret->module_link = ctx.cfg.module_link;
+	ret->readme = ctx.cfg.readme;
+	ret->mtime = -1;
+	ret->about_filter = ctx.cfg.about_filter;
+	ret->commit_filter = ctx.cfg.commit_filter;
+	ret->source_filter = ctx.cfg.source_filter;
+	ret->email_filter = ctx.cfg.email_filter;
+	ret->owner_filter = ctx.cfg.owner_filter;
+	ret->clone_url = ctx.cfg.clone_url;
+	ret->submodules.strdup_strings = 1;
+	ret->hide = ret->ignore = 0;
+	return ret;
+}
+
+struct cgit_repo *cgit_get_repoinfo(const char *url)
+{
+	int i;
+	struct cgit_repo *repo;
+
+	for (i = 0; i < cgit_repolist.count; i++) {
+		repo = &cgit_repolist.repos[i];
+		if (repo->ignore)
+			continue;
+		if (!strcmp(repo->url, url))
+			return repo;
+	}
+	return NULL;
+}
+
+void cgit_free_commitinfo(struct commitinfo *info)
+{
+	free(info->author);
+	free(info->author_email);
+	free(info->committer);
+	free(info->committer_email);
+	free(info->subject);
+	free(info->msg);
+	free(info->msg_encoding);
+	free(info);
+}
+
+char *trim_end(const char *str, char c)
+{
+	int len;
+
+	if (str == NULL)
+		return NULL;
+	len = strlen(str);
+	while (len > 0 && str[len - 1] == c)
+		len--;
+	if (len == 0)
+		return NULL;
+	return xstrndup(str, len);
+}
+
+char *ensure_end(const char *str, char c)
+{
+	size_t len = strlen(str);
+	char *result;
+
+	if (len && str[len - 1] == c)
+		return xstrndup(str, len);
+
+	result = xmalloc(len + 2);
+	memcpy(result, str, len);
+	result[len] = '/';
+	result[len + 1] = '\0';
+	return result;
+}
+
+void strbuf_ensure_end(struct strbuf *sb, char c)
+{
+	if (!sb->len || sb->buf[sb->len - 1] != c)
+		strbuf_addch(sb, c);
+}
+
+void cgit_add_ref(struct reflist *list, struct refinfo *ref)
+{
+	size_t size;
+
+	if (list->count >= list->alloc) {
+		list->alloc += (list->alloc ? list->alloc : 4);
+		size = list->alloc * sizeof(struct refinfo *);
+		list->refs = xrealloc(list->refs, size);
+	}
+	list->refs[list->count++] = ref;
+}
+
+static struct refinfo *cgit_mk_refinfo(const char *refname, const struct object_id *oid)
+{
+	struct refinfo *ref;
+
+	ref = xmalloc(sizeof (struct refinfo));
+	ref->refname = xstrdup(refname);
+	ref->object = parse_object(the_repository, oid);
+	switch (ref->object->type) {
+	case OBJ_TAG:
+		ref->tag = cgit_parse_tag((struct tag *)ref->object);
+		break;
+	case OBJ_COMMIT:
+		ref->commit = cgit_parse_commit((struct commit *)ref->object);
+		break;
+	}
+	return ref;
+}
+
+void cgit_free_taginfo(struct taginfo *tag)
+{
+	if (tag->tagger)
+		free(tag->tagger);
+	if (tag->tagger_email)
+		free(tag->tagger_email);
+	if (tag->msg)
+		free(tag->msg);
+	free(tag);
+}
+
+static void cgit_free_refinfo(struct refinfo *ref)
+{
+	if (ref->refname)
+		free((char *)ref->refname);
+	switch (ref->object->type) {
+	case OBJ_TAG:
+		cgit_free_taginfo(ref->tag);
+		break;
+	case OBJ_COMMIT:
+		cgit_free_commitinfo(ref->commit);
+		break;
+	}
+	free(ref);
+}
+
+void cgit_free_reflist_inner(struct reflist *list)
+{
+	int i;
+
+	for (i = 0; i < list->count; i++) {
+		cgit_free_refinfo(list->refs[i]);
+	}
+	free(list->refs);
+}
+
+int cgit_refs_cb(const char *refname, const struct object_id *oid, int flags,
+		  void *cb_data)
+{
+	struct reflist *list = (struct reflist *)cb_data;
+	struct refinfo *info = cgit_mk_refinfo(refname, oid);
+
+	if (info)
+		cgit_add_ref(list, info);
+	return 0;
+}
+
+void cgit_diff_tree_cb(struct diff_queue_struct *q,
+		       struct diff_options *options, void *data)
+{
+	int i;
+
+	for (i = 0; i < q->nr; i++) {
+		if (q->queue[i]->status == 'U')
+			continue;
+		((filepair_fn)data)(q->queue[i]);
+	}
+}
+
+static int load_mmfile(mmfile_t *file, const struct object_id *oid)
+{
+	enum object_type type;
+
+	if (is_null_oid(oid)) {
+		file->ptr = (char *)"";
+		file->size = 0;
+	} else {
+		file->ptr = read_object_file(oid, &type,
+		                           (unsigned long *)&file->size);
+	}
+	return 1;
+}
+
+/*
+ * Receive diff-buffers from xdiff and concatenate them as
+ * needed across multiple callbacks.
+ *
+ * This is basically a copy of xdiff-interface.c/xdiff_outf(),
+ * ripped from git and modified to use globals instead of
+ * a special callback-struct.
+ */
+static char *diffbuf = NULL;
+static int buflen = 0;
+
+static int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
+{
+	int i;
+
+	for (i = 0; i < nbuf; i++) {
+		if (mb[i].ptr[mb[i].size-1] != '\n') {
+			/* Incomplete line */
+			diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
+			memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
+			buflen += mb[i].size;
+			continue;
+		}
+
+		/* we have a complete line */
+		if (!diffbuf) {
+			((linediff_fn)priv)(mb[i].ptr, mb[i].size);
+			continue;
+		}
+		diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
+		memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
+		((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
+		free(diffbuf);
+		diffbuf = NULL;
+		buflen = 0;
+	}
+	if (diffbuf) {
+		((linediff_fn)priv)(diffbuf, buflen);
+		free(diffbuf);
+		diffbuf = NULL;
+		buflen = 0;
+	}
+	return 0;
+}
+
+int cgit_diff_files(const struct object_id *old_oid,
+		    const struct object_id *new_oid, unsigned long *old_size,
+		    unsigned long *new_size, int *binary, int context,
+		    int ignorews, linediff_fn fn)
+{
+	mmfile_t file1, file2;
+	xpparam_t diff_params;
+	xdemitconf_t emit_params;
+	xdemitcb_t emit_cb;
+
+	if (!load_mmfile(&file1, old_oid) || !load_mmfile(&file2, new_oid))
+		return 1;
+
+	*old_size = file1.size;
+	*new_size = file2.size;
+
+	if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) ||
+	    (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) {
+		*binary = 1;
+		if (file1.size)
+			free(file1.ptr);
+		if (file2.size)
+			free(file2.ptr);
+		return 0;
+	}
+
+	memset(&diff_params, 0, sizeof(diff_params));
+	memset(&emit_params, 0, sizeof(emit_params));
+	memset(&emit_cb, 0, sizeof(emit_cb));
+	diff_params.flags = XDF_NEED_MINIMAL;
+	if (ignorews)
+		diff_params.flags |= XDF_IGNORE_WHITESPACE;
+	emit_params.ctxlen = context > 0 ? context : 3;
+	emit_params.flags = XDL_EMIT_FUNCNAMES;
+	emit_cb.out_line = filediff_cb;
+	emit_cb.priv = fn;
+	xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
+	if (file1.size)
+		free(file1.ptr);
+	if (file2.size)
+		free(file2.ptr);
+	return 0;
+}
+
+void cgit_diff_tree(const struct object_id *old_oid,
+		    const struct object_id *new_oid,
+		    filepair_fn fn, const char *prefix, int ignorews)
+{
+	struct diff_options opt;
+	struct pathspec_item item;
+
+	memset(&item, 0, sizeof(item));
+	diff_setup(&opt);
+	opt.output_format = DIFF_FORMAT_CALLBACK;
+	opt.detect_rename = 1;
+	opt.rename_limit = ctx.cfg.renamelimit;
+	opt.flags.recursive = 1;
+	if (ignorews)
+		DIFF_XDL_SET(&opt, IGNORE_WHITESPACE);
+	opt.format_callback = cgit_diff_tree_cb;
+	opt.format_callback_data = fn;
+	if (prefix) {
+		item.match = xstrdup(prefix);
+		item.len = strlen(prefix);
+		opt.pathspec.nr = 1;
+		opt.pathspec.items = &item;
+	}
+	diff_setup_done(&opt);
+
+	if (old_oid && !is_null_oid(old_oid))
+		diff_tree_oid(old_oid, new_oid, "", &opt);
+	else
+		diff_root_tree_oid(new_oid, "", &opt);
+	diffcore_std(&opt);
+	diff_flush(&opt);
+
+	free(item.match);
+}
+
+void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix)
+{
+	const struct object_id *old_oid = NULL;
+
+	if (commit->parents)
+		old_oid = &commit->parents->item->object.oid;
+	cgit_diff_tree(old_oid, &commit->object.oid, fn, prefix,
+		       ctx.qry.ignorews);
+}
+
+int cgit_parse_snapshots_mask(const char *str)
+{
+	struct string_list tokens = STRING_LIST_INIT_DUP;
+	struct string_list_item *item;
+	const struct cgit_snapshot_format *f;
+	int rv = 0;
+
+	/* favor legacy setting */
+	if (atoi(str))
+		return 1;
+
+	if (strcmp(str, "all") == 0)
+		return INT_MAX;
+
+	string_list_split(&tokens, str, ' ', -1);
+	string_list_remove_empty_items(&tokens, 0);
+
+	for_each_string_list_item(item, &tokens) {
+		for (f = cgit_snapshot_formats; f->suffix; f++) {
+			if (!strcmp(item->string, f->suffix) ||
+			    !strcmp(item->string, f->suffix + 1)) {
+				rv |= cgit_snapshot_format_bit(f);
+				break;
+			}
+		}
+	}
+
+	string_list_clear(&tokens, 0);
+	return rv;
+}
+
+typedef struct {
+	char * name;
+	char * value;
+} cgit_env_var;
+
+void cgit_prepare_repo_env(struct cgit_repo * repo)
+{
+	cgit_env_var env_vars[] = {
+		{ .name = "CGIT_REPO_URL", .value = repo->url },
+		{ .name = "CGIT_REPO_NAME", .value = repo->name },
+		{ .name = "CGIT_REPO_PATH", .value = repo->path },
+		{ .name = "CGIT_REPO_OWNER", .value = repo->owner },
+		{ .name = "CGIT_REPO_DEFBRANCH", .value = repo->defbranch },
+		{ .name = "CGIT_REPO_SECTION", .value = repo->section },
+		{ .name = "CGIT_REPO_CLONE_URL", .value = repo->clone_url }
+	};
+	int env_var_count = ARRAY_SIZE(env_vars);
+	cgit_env_var *p, *q;
+	static char *warn = "cgit warning: failed to set env: %s=%s\n";
+
+	p = env_vars;
+	q = p + env_var_count;
+	for (; p < q; p++)
+		if (p->value && setenv(p->name, p->value, 1))
+			fprintf(stderr, warn, p->name, p->value);
+}
+
+/* Read the content of the specified file into a newly allocated buffer,
+ * zeroterminate the buffer and return 0 on success, errno otherwise.
+ */
+int readfile(const char *path, char **buf, size_t *size)
+{
+	int fd, e;
+	struct stat st;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return errno;
+	if (fstat(fd, &st)) {
+		e = errno;
+		close(fd);
+		return e;
+	}
+	if (!S_ISREG(st.st_mode)) {
+		close(fd);
+		return EISDIR;
+	}
+	*buf = xmalloc(st.st_size + 1);
+	*size = read_in_full(fd, *buf, st.st_size);
+	e = errno;
+	(*buf)[*size] = '\0';
+	close(fd);
+	return (*size == st.st_size ? 0 : e);
+}
+
+static int is_token_char(char c)
+{
+	return isalnum(c) || c == '_';
+}
+
+/* Replace name with getenv(name), return pointer to zero-terminating char
+ */
+static char *expand_macro(char *name, int maxlength)
+{
+	char *value;
+	size_t len;
+
+	len = 0;
+	value = getenv(name);
+	if (value) {
+		len = strlen(value) + 1;
+		if (len > maxlength)
+			len = maxlength;
+		strlcpy(name, value, len);
+		--len;
+	}
+	return name + len;
+}
+
+#define EXPBUFSIZE (1024 * 8)
+
+/* Replace all tokens prefixed by '$' in the specified text with the
+ * value of the named environment variable.
+ * NB: the return value is a static buffer, i.e. it must be strdup'd
+ * by the caller.
+ */
+char *expand_macros(const char *txt)
+{
+	static char result[EXPBUFSIZE];
+	char *p, *start;
+	int len;
+
+	p = result;
+	start = NULL;
+	while (p < result + EXPBUFSIZE - 1 && txt && *txt) {
+		*p = *txt;
+		if (start) {
+			if (!is_token_char(*txt)) {
+				if (p - start > 0) {
+					*p = '\0';
+					len = result + EXPBUFSIZE - start - 1;
+					p = expand_macro(start, len) - 1;
+				}
+				start = NULL;
+				txt--;
+			}
+			p++;
+			txt++;
+			continue;
+		}
+		if (*txt == '$') {
+			start = p;
+			txt++;
+			continue;
+		}
+		p++;
+		txt++;
+	}
+	*p = '\0';
+	if (start && p - start > 0) {
+		len = result + EXPBUFSIZE - start - 1;
+		p = expand_macro(start, len);
+		*p = '\0';
+	}
+	return result;
+}
+
+char *get_mimetype_for_filename(const char *filename)
+{
+	char *ext, *mimetype, *token, line[1024], *saveptr;
+	FILE *file;
+	struct string_list_item *mime;
+
+	if (!filename)
+		return NULL;
+
+	ext = strrchr(filename, '.');
+	if (!ext)
+		return NULL;
+	++ext;
+	if (!ext[0])
+		return NULL;
+	mime = string_list_lookup(&ctx.cfg.mimetypes, ext);
+	if (mime)
+		return xstrdup(mime->util);
+
+	if (!ctx.cfg.mimetype_file)
+		return NULL;
+	file = fopen(ctx.cfg.mimetype_file, "r");
+	if (!file)
+		return NULL;
+	while (fgets(line, sizeof(line), file)) {
+		if (!line[0] || line[0] == '#')
+			continue;
+		mimetype = strtok_r(line, " \t\r\n", &saveptr);
+		while ((token = strtok_r(NULL, " \t\r\n", &saveptr))) {
+			if (!strcasecmp(ext, token)) {
+				fclose(file);
+				return xstrdup(mimetype);
+			}
+		}
+	}
+	fclose(file);
+	return NULL;
+}
diff --git a/www/git.causal.agency/cgit/tests/.gitignore b/www/git.causal.agency/cgit/tests/.gitignore
new file mode 100644
index 00000000..3fd2e965
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/.gitignore
@@ -0,0 +1,2 @@
+trash\ directory.t*
+test-results
diff --git a/www/git.causal.agency/cgit/tests/Makefile b/www/git.causal.agency/cgit/tests/Makefile
new file mode 100644
index 00000000..65e11173
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/Makefile
@@ -0,0 +1,17 @@
+include ../git/config.mak.uname
+-include ../cgit.conf
+
+SHELL_PATH ?= $(SHELL)
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+
+all: $(T)
+
+$(T):
+	@'$(SHELL_PATH_SQ)' $@ $(CGIT_TEST_OPTS)
+
+clean:
+	$(RM) -rf trash
+
+.PHONY: $(T) clean
diff --git a/www/git.causal.agency/cgit/tests/filters/dump.sh b/www/git.causal.agency/cgit/tests/filters/dump.sh
new file mode 100755
index 00000000..da6f7a1b
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/filters/dump.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+[ "$#" -gt 0 ] && printf "%s " "$*"
+tr '[:lower:]' '[:upper:]'
diff --git a/www/git.causal.agency/cgit/tests/setup.sh b/www/git.causal.agency/cgit/tests/setup.sh
new file mode 100755
index 00000000..31e7d5bb
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/setup.sh
@@ -0,0 +1,161 @@
+# This file should be sourced by all test-scripts
+#
+# Main functions:
+#   prepare_tests(description) - setup for testing, i.e. create repos+config
+#   run_test(description, script) - run one test, i.e. eval script
+#
+# Helper functions
+#   cgit_query(querystring) - call cgit with the specified querystring
+#   cgit_url(url) - call cgit with the specified virtual url
+#
+# Example script:
+#
+# . setup.sh
+# prepare_tests "html validation"
+# run_test 'repo index' 'cgit_url "/" | tidy -e'
+# run_test 'repo summary' 'cgit_url "/foo" | tidy -e'
+
+# We don't want to run Git commands through Valgrind, so we filter out the
+# --valgrind option here and handle it ourselves.  We copy the arguments
+# assuming that none contain a newline, although other whitespace is
+# preserved.
+LF='
+'
+test_argv=
+
+while test $# != 0
+do
+	case "$1" in
+	--va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
+		cgit_valgrind=t
+		test_argv="$test_argv${LF}--verbose"
+		;;
+	*)
+		test_argv="$test_argv$LF$1"
+		;;
+	esac
+	shift
+done
+
+OLDIFS=$IFS
+IFS=$LF
+set -- $test_argv
+IFS=$OLDIFS
+
+: ${TEST_DIRECTORY=$(pwd)/../git/t}
+: ${TEST_OUTPUT_DIRECTORY=$(pwd)}
+TEST_NO_CREATE_REPO=YesPlease
+. "$TEST_DIRECTORY"/test-lib.sh
+
+# Prepend the directory containing cgit to PATH.
+if test -n "$cgit_valgrind"
+then
+	GIT_VALGRIND="$TEST_DIRECTORY/valgrind"
+	CGIT_VALGRIND=$(cd ../valgrind && pwd)
+	PATH="$CGIT_VALGRIND/bin:$PATH"
+	export GIT_VALGRIND CGIT_VALGRIND
+else
+	PATH="$(pwd)/../..:$PATH"
+fi
+
+FILTER_DIRECTORY=$(cd ../filters && pwd)
+
+mkrepo() {
+	name=$1
+	count=$2
+	test_create_repo "$name"
+	(
+		cd "$name"
+		n=1
+		while test $n -le $count
+		do
+			echo $n >file-$n
+			git add file-$n
+			git commit -m "commit $n"
+			n=$(expr $n + 1)
+		done
+		case "$3" in
+		testplus)
+			echo "hello" >a+b
+			git add a+b
+			git commit -m "add a+b"
+			git branch "1+2"
+			;;
+		commit-graph)
+			git commit-graph write
+			;;
+		esac
+	)
+}
+
+setup_repos()
+{
+	rm -rf cache
+	mkdir -p cache
+	mkrepo repos/foo 5 >/dev/null
+	mkrepo repos/bar 50 commit-graph >/dev/null
+	mkrepo repos/foo+bar 10 testplus >/dev/null
+	mkrepo "repos/with space" 2 >/dev/null
+	mkrepo repos/filter 5 testplus >/dev/null
+	cat >cgitrc <<EOF
+virtual-root=/
+cache-root=$PWD/cache
+
+cache-size=1021
+snapshots=tar.gz tar.bz tar.lz tar.xz tar.zst zip
+enable-log-filecount=1
+enable-log-linecount=1
+summary-log=5
+summary-branches=5
+summary-tags=5
+clone-url=git://example.org/\$CGIT_REPO_URL.git
+enable-filter-overrides=1
+
+repo.url=foo
+repo.path=$PWD/repos/foo/.git
+# Do not specify a description for this repo, as it then will be assigned
+# the constant value "[no description]" (which actually used to cause a
+# segfault).
+
+repo.url=bar
+repo.path=$PWD/repos/bar/.git
+repo.desc=the bar repo
+
+repo.url=foo+bar
+repo.path=$PWD/repos/foo+bar/.git
+repo.desc=the foo+bar repo
+
+repo.url=with space
+repo.path=$PWD/repos/with space/.git
+repo.desc=spaced repo
+
+repo.url=filter-exec
+repo.path=$PWD/repos/filter/.git
+repo.desc=filtered repo
+repo.about-filter=exec:$FILTER_DIRECTORY/dump.sh
+repo.commit-filter=exec:$FILTER_DIRECTORY/dump.sh
+repo.email-filter=exec:$FILTER_DIRECTORY/dump.sh
+repo.source-filter=exec:$FILTER_DIRECTORY/dump.sh
+repo.readme=master:a+b
+EOF
+}
+
+cgit_query()
+{
+	CGIT_CONFIG="$PWD/cgitrc" QUERY_STRING="$1" cgit
+}
+
+cgit_url()
+{
+	CGIT_CONFIG="$PWD/cgitrc" QUERY_STRING="url=$1" cgit
+}
+
+strip_headers() {
+	while read -r line
+	do
+		test -z "$line" && break
+	done
+	cat
+}
+
+test -z "$CGIT_TEST_NO_CREATE_REPOS" && setup_repos
diff --git a/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh b/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh
new file mode 100755
index 00000000..dd84fe3f
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+if [ "${CGIT_TEST_NO_GIT_VERSION}" = "YesPlease" ]; then
+	exit 0
+fi
+
+test_description='Check Git version is correct'
+CGIT_TEST_NO_CREATE_REPOS=YesPlease
+. ./setup.sh
+
+test_expect_success 'extract Git version from Makefile' '
+	sed -n -e "/^GIT_VER[ 	]*=/ {
+		s/^GIT_VER[ 	]*=[ 	]*//
+		p
+	}" ../../Makefile >makefile_version
+'
+
+# Note that Git's GIT-VERSION-GEN script applies "s/-/./g" to the version
+# string to produce the internal version in the GIT-VERSION-FILE, so we
+# must apply the same transformation to the version in the Makefile before
+# comparing them.
+test_expect_success 'test Git version matches Makefile' '
+	( cat ../../git/GIT-VERSION-FILE || echo "No GIT-VERSION-FILE" ) |
+	sed -e "s/GIT_VERSION[ 	]*=[ 	]*//" -e "s/\\.dirty$//" >git_version &&
+	sed -e "s/-/./g" makefile_version >makefile_git_version &&
+	test_cmp git_version makefile_git_version
+'
+
+test_expect_success 'test submodule version matches Makefile' '
+	if ! test -e ../../git/.git
+	then
+		echo "git/ is not a Git repository" >&2
+	else
+		(
+			cd ../.. &&
+			sm_oid=$(git ls-files --stage -- git |
+				sed -e "s/^[0-9]* \\([0-9a-f]*\\) [0-9]	.*$/\\1/") &&
+			cd git &&
+			git describe --match "v[0-9]*" $sm_oid
+		) | sed -e "s/^v//" -e "s/-/./" >sm_version &&
+		test_cmp sm_version makefile_version
+	fi
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0010-validate-html.sh b/www/git.causal.agency/cgit/tests/t0010-validate-html.sh
new file mode 100755
index 00000000..ca08d69d
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0010-validate-html.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description='Validate html with tidy'
+. ./setup.sh
+
+
+test_url()
+{
+	tidy_opt="-eq"
+	test -z "$NO_TIDY_WARNINGS" || tidy_opt+=" --show-warnings no"
+	cgit_url "$1" >tidy-$test_count.tmp || return
+	sed -e "1,4d" tidy-$test_count.tmp >tidy-$test_count || return
+	"$tidy" $tidy_opt tidy-$test_count
+	rc=$?
+
+	# tidy returns with exitcode 1 on warnings, 2 on error
+	if test $rc = 2
+	then
+		false
+	else
+		:
+	fi
+}
+
+tidy=`which tidy 2>/dev/null`
+test -n "$tidy" || {
+	skip_all='Skipping html validation tests: tidy not found'
+	test_done
+	exit
+}
+
+test_expect_success 'index page' 'test_url ""'
+test_expect_success 'foo' 'test_url "foo"'
+test_expect_success 'foo/log' 'test_url "foo/log"'
+test_expect_success 'foo/tree' 'test_url "foo/tree"'
+test_expect_success 'foo/tree/file-1' 'test_url "foo/tree/file-1"'
+test_expect_success 'foo/commit' 'test_url "foo/commit"'
+test_expect_success 'foo/diff' 'test_url "foo/diff"'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh b/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh
new file mode 100755
index 00000000..657765d8
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+test_description='Validate cache'
+. ./setup.sh
+
+test_expect_success 'verify cache-size=0' '
+
+	rm -f cache/* &&
+	sed -e "s/cache-size=1021$/cache-size=0/" cgitrc >cgitrc.tmp &&
+	mv -f cgitrc.tmp cgitrc &&
+	cgit_url "" &&
+	cgit_url "foo" &&
+	cgit_url "foo/refs" &&
+	cgit_url "foo/tree" &&
+	cgit_url "foo/log" &&
+	cgit_url "foo/diff" &&
+	cgit_url "foo/patch" &&
+	cgit_url "bar" &&
+	cgit_url "bar/refs" &&
+	cgit_url "bar/tree" &&
+	cgit_url "bar/log" &&
+	cgit_url "bar/diff" &&
+	cgit_url "bar/patch" &&
+	ls cache >output &&
+	test_line_count = 0 output
+'
+
+test_expect_success 'verify cache-size=1' '
+
+	rm -f cache/* &&
+	sed -e "s/cache-size=0$/cache-size=1/" cgitrc >cgitrc.tmp &&
+	mv -f cgitrc.tmp cgitrc &&
+	cgit_url "" &&
+	cgit_url "foo" &&
+	cgit_url "foo/refs" &&
+	cgit_url "foo/tree" &&
+	cgit_url "foo/log" &&
+	cgit_url "foo/diff" &&
+	cgit_url "foo/patch" &&
+	cgit_url "bar" &&
+	cgit_url "bar/refs" &&
+	cgit_url "bar/tree" &&
+	cgit_url "bar/log" &&
+	cgit_url "bar/diff" &&
+	cgit_url "bar/patch" &&
+	ls cache >output &&
+	test_line_count = 1 output
+'
+
+test_expect_success 'verify cache-size=1021' '
+
+	rm -f cache/* &&
+	sed -e "s/cache-size=1$/cache-size=1021/" cgitrc >cgitrc.tmp &&
+	mv -f cgitrc.tmp cgitrc &&
+	cgit_url "" &&
+	cgit_url "foo" &&
+	cgit_url "foo/refs" &&
+	cgit_url "foo/tree" &&
+	cgit_url "foo/log" &&
+	cgit_url "foo/diff" &&
+	cgit_url "foo/patch" &&
+	cgit_url "bar" &&
+	cgit_url "bar/refs" &&
+	cgit_url "bar/tree" &&
+	cgit_url "bar/log" &&
+	cgit_url "bar/diff" &&
+	cgit_url "bar/patch" &&
+	ls cache >output &&
+	test_line_count = 13 output &&
+	cgit_url "foo/ls_cache" >output.full &&
+	strip_headers <output.full >output &&
+	test_line_count = 13 output &&
+	# Check that ls_cache output is cached correctly
+	cgit_url "foo/ls_cache" >output.second &&
+	test_cmp output.full output.second
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0101-index.sh b/www/git.causal.agency/cgit/tests/t0101-index.sh
new file mode 100755
index 00000000..82ef9b04
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0101-index.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description='Check content on index page'
+. ./setup.sh
+
+test_expect_success 'generate index page' 'cgit_url "" >tmp'
+test_expect_success 'find foo repo' 'grep "foo" tmp'
+test_expect_success 'find foo description' 'grep "\[no description\]" tmp'
+test_expect_success 'find bar repo' 'grep "bar" tmp'
+test_expect_success 'find bar description' 'grep "the bar repo" tmp'
+test_expect_success 'find foo+bar repo' 'grep ">foo+bar<" tmp'
+test_expect_success 'verify foo+bar link' 'grep "/foo+bar/" tmp'
+test_expect_success 'verify "with%20space" link' 'grep "/with%20space/" tmp'
+test_expect_success 'no tree-link' '! grep "foo/tree" tmp'
+test_expect_success 'no log-link' '! grep "foo/log" tmp'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0102-summary.sh b/www/git.causal.agency/cgit/tests/t0102-summary.sh
new file mode 100755
index 00000000..b8864cb1
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0102-summary.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='Check content on summary page'
+. ./setup.sh
+
+test_expect_success 'generate foo summary' 'cgit_url "foo" >tmp'
+test_expect_success 'find commit 1' 'grep "commit 1" tmp'
+test_expect_success 'find commit 5' 'grep "commit 5" tmp'
+test_expect_success 'find branch master' 'grep "master" tmp'
+test_expect_success 'no tags' '! grep "tags" tmp'
+test_expect_success 'clone-url expanded correctly' '
+	grep "git://example.org/foo.git" tmp
+'
+
+test_expect_success 'generate bar summary' 'cgit_url "bar" >tmp'
+test_expect_success 'no commit 45' '! grep "commit 45" tmp'
+test_expect_success 'find commit 46' 'grep "commit 46" tmp'
+test_expect_success 'find commit 50' 'grep "commit 50" tmp'
+test_expect_success 'find branch master' 'grep "master" tmp'
+test_expect_success 'no tags' '! grep "tags" tmp'
+test_expect_success 'clone-url expanded correctly' '
+	grep "git://example.org/bar.git" tmp
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0103-log.sh b/www/git.causal.agency/cgit/tests/t0103-log.sh
new file mode 100755
index 00000000..bdf1435a
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0103-log.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='Check content on log page'
+. ./setup.sh
+
+test_expect_success 'generate foo/log' 'cgit_url "foo/log" >tmp'
+test_expect_success 'find commit 1' 'grep "commit 1" tmp'
+test_expect_success 'find commit 5' 'grep "commit 5" tmp'
+
+test_expect_success 'generate bar/log' 'cgit_url "bar/log" >tmp'
+test_expect_success 'find commit 1' 'grep "commit 1" tmp'
+test_expect_success 'find commit 50' 'grep "commit 50" tmp'
+
+test_expect_success 'generate "with%20space/log?qt=grep&q=commit+1"' '
+	cgit_url "with+space/log&qt=grep&q=commit+1" >tmp
+'
+test_expect_success 'find commit 1' 'grep "commit 1" tmp'
+test_expect_success 'find link with %20 in path' 'grep "/with%20space/log/?qt=grep" tmp'
+test_expect_success 'find link with + in arg' 'grep "/log/?qt=grep&amp;q=commit+1" tmp'
+test_expect_success 'no links with space in path' '! grep "href=./with space/" tmp'
+test_expect_success 'no links with space in arg' '! grep "q=commit 1" tmp'
+test_expect_success 'commit 2 is not visible' '! grep "commit 2" tmp'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0104-tree.sh b/www/git.causal.agency/cgit/tests/t0104-tree.sh
new file mode 100755
index 00000000..2e140f59
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0104-tree.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='Check content on tree page'
+. ./setup.sh
+
+test_expect_success 'generate bar/tree' 'cgit_url "bar/tree" >tmp'
+test_expect_success 'find file-1' 'grep "file-1" tmp'
+test_expect_success 'find file-50' 'grep "file-50" tmp'
+
+test_expect_success 'generate bar/tree/file-50' 'cgit_url "bar/tree/file-50" >tmp'
+
+test_expect_success 'find line 1' '
+	grep "<a id=.n1. href=.#n1.>1</a>" tmp
+'
+
+test_expect_success 'no line 2' '
+	! grep "<a id=.n2. href=.#n2.>2</a>" tmp
+'
+
+test_expect_success 'generate foo+bar/tree' 'cgit_url "foo%2bbar/tree" >tmp'
+
+test_expect_success 'verify a+b link' '
+	grep "/foo+bar/tree/a+b" tmp
+'
+
+test_expect_success 'generate foo+bar/tree?h=1+2' 'cgit_url "foo%2bbar/tree&h=1%2b2" >tmp'
+
+test_expect_success 'verify a+b?h=1+2 link' '
+	grep "/foo+bar/tree/a+b?h=1%2b2" tmp
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0105-commit.sh b/www/git.causal.agency/cgit/tests/t0105-commit.sh
new file mode 100755
index 00000000..cfed1e7d
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0105-commit.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='Check content on commit page'
+. ./setup.sh
+
+test_expect_success 'generate foo/commit' 'cgit_url "foo/commit" >tmp'
+test_expect_success 'find tree link' 'grep "<a href=./foo/tree/.>" tmp'
+test_expect_success 'find parent link' 'grep -E "<a href=./foo/commit/\?id=.+>" tmp'
+
+test_expect_success 'find commit subject' '
+	grep "<div class=.commit-subject.>commit 5<" tmp
+'
+
+test_expect_success 'find commit msg' 'grep "<pre class=.commit-msg.></pre>" tmp'
+test_expect_success 'find diffstat' 'grep "<table summary=.diffstat. class=.diffstat.>" tmp'
+
+test_expect_success 'find diff summary' '
+	grep "1 files changed, 1 insertions, 0 deletions" tmp
+'
+
+test_expect_success 'get root commit' '
+	root=$(cd repos/foo && git rev-list --reverse HEAD | head -1) &&
+	cgit_url "foo/commit&id=$root" >tmp &&
+	grep "</html>" tmp
+'
+
+test_expect_success 'root commit contains diffstat' '
+	grep "<a href=./foo/diff/file-1.id=[0-9a-f]\{40,64\}.>file-1</a>" tmp
+'
+
+test_expect_success 'root commit contains diff' '
+	grep ">diff --git a/file-1 b/file-1" tmp &&
+	grep "<span class=.add.>+1</span>" tmp
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0106-diff.sh b/www/git.causal.agency/cgit/tests/t0106-diff.sh
new file mode 100755
index 00000000..62a0a74a
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0106-diff.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='Check content on diff page'
+. ./setup.sh
+
+test_expect_success 'generate foo/diff' 'cgit_url "foo/diff" >tmp'
+test_expect_success 'find diff header' 'grep "a/file-5 b/file-5" tmp'
+test_expect_success 'find blob link' 'grep "<a href=./foo/tree/file-5?id=" tmp'
+test_expect_success 'find added file' 'grep "new file mode 100644" tmp'
+
+test_expect_success 'find hunk header' '
+	grep "<span class=.hunk.>@@ -0,0 +1 @@</span>" tmp
+'
+
+test_expect_success 'find added line' '
+	grep "<span class=.add.>+5</span>" tmp
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0107-snapshot.sh b/www/git.causal.agency/cgit/tests/t0107-snapshot.sh
new file mode 100755
index 00000000..0811ec40
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0107-snapshot.sh
@@ -0,0 +1,205 @@
+#!/bin/sh
+
+test_description='Verify snapshot'
+. ./setup.sh
+
+test_expect_success 'get foo/snapshot/master.tar.gz' '
+	cgit_url "foo/snapshot/master.tar.gz" >tmp
+'
+
+test_expect_success 'check html headers' '
+	head -n 1 tmp |
+	grep "Content-Type: application/x-gzip" &&
+
+	head -n 2 tmp |
+	grep "Content-Disposition: inline; filename=.master.tar.gz."
+'
+
+test_expect_success 'strip off the header lines' '
+	strip_headers <tmp >master.tar.gz
+'
+
+test_expect_success 'verify gzip format' '
+	gunzip --test master.tar.gz
+'
+
+test_expect_success 'untar' '
+	rm -rf master &&
+	gzip -dc master.tar.gz | tar -xf -
+'
+
+test_expect_success 'count files' '
+	ls master/ >output &&
+	test_line_count = 5 output
+'
+
+test_expect_success 'verify untarred file-5' '
+	grep "^5$" master/file-5 &&
+	test_line_count = 1 master/file-5
+'
+
+if test -n "$(which lzip 2>/dev/null)"; then
+	test_set_prereq LZIP
+else
+	say 'Skipping LZIP validation tests: lzip not found'
+fi
+
+test_expect_success LZIP 'get foo/snapshot/master.tar.lz' '
+	cgit_url "foo/snapshot/master.tar.lz" >tmp
+'
+
+test_expect_success LZIP 'check html headers' '
+	head -n 1 tmp |
+	grep "Content-Type: application/x-lzip" &&
+
+	head -n 2 tmp |
+	grep "Content-Disposition: inline; filename=.master.tar.lz."
+'
+
+test_expect_success LZIP 'strip off the header lines' '
+	strip_headers <tmp >master.tar.lz
+'
+
+test_expect_success LZIP 'verify lzip format' '
+	lzip --test master.tar.lz
+'
+
+test_expect_success LZIP 'untar' '
+	rm -rf master &&
+	lzip -dc master.tar.lz | tar -xf -
+'
+
+test_expect_success LZIP 'count files' '
+	ls master/ >output &&
+	test_line_count = 5 output
+'
+
+test_expect_success LZIP 'verify untarred file-5' '
+	grep "^5$" master/file-5 &&
+	test_line_count = 1 master/file-5
+'
+
+if test -n "$(which xz 2>/dev/null)"; then
+	test_set_prereq XZ
+else
+	say 'Skipping XZ validation tests: xz not found'
+fi
+
+test_expect_success XZ 'get foo/snapshot/master.tar.xz' '
+	cgit_url "foo/snapshot/master.tar.xz" >tmp
+'
+
+test_expect_success XZ 'check html headers' '
+	head -n 1 tmp |
+	grep "Content-Type: application/x-xz" &&
+
+	head -n 2 tmp |
+	grep "Content-Disposition: inline; filename=.master.tar.xz."
+'
+
+test_expect_success XZ 'strip off the header lines' '
+	strip_headers <tmp >master.tar.xz
+'
+
+test_expect_success XZ 'verify xz format' '
+	xz --test master.tar.xz
+'
+
+test_expect_success XZ 'untar' '
+	rm -rf master &&
+	xz -dc master.tar.xz | tar -xf -
+'
+
+test_expect_success XZ 'count files' '
+	ls master/ >output &&
+	test_line_count = 5 output
+'
+
+test_expect_success XZ 'verify untarred file-5' '
+	grep "^5$" master/file-5 &&
+	test_line_count = 1 master/file-5
+'
+
+if test -n "$(which zstd 2>/dev/null)"; then
+	test_set_prereq ZSTD
+else
+	say 'Skipping ZSTD validation tests: zstd not found'
+fi
+
+test_expect_success ZSTD 'get foo/snapshot/master.tar.zst' '
+	cgit_url "foo/snapshot/master.tar.zst" >tmp
+'
+
+test_expect_success ZSTD 'check html headers' '
+	head -n 1 tmp |
+	grep "Content-Type: application/x-zstd" &&
+
+	head -n 2 tmp |
+	grep "Content-Disposition: inline; filename=.master.tar.zst."
+'
+
+test_expect_success ZSTD 'strip off the header lines' '
+	strip_headers <tmp >master.tar.zst
+'
+
+test_expect_success ZSTD 'verify zstd format' '
+	zstd --test master.tar.zst
+'
+
+test_expect_success ZSTD 'untar' '
+	rm -rf master &&
+	zstd -dc master.tar.zst | tar -xf -
+'
+
+test_expect_success ZSTD 'count files' '
+	ls master/ >output &&
+	test_line_count = 5 output
+'
+
+test_expect_success ZSTD 'verify untarred file-5' '
+	grep "^5$" master/file-5 &&
+	test_line_count = 1 master/file-5
+'
+
+test_expect_success 'get foo/snapshot/master.zip' '
+	cgit_url "foo/snapshot/master.zip" >tmp
+'
+
+test_expect_success 'check HTML headers (zip)' '
+	head -n 1 tmp |
+	grep "Content-Type: application/x-zip" &&
+
+	head -n 2 tmp |
+	grep "Content-Disposition: inline; filename=.master.zip."
+'
+
+test_expect_success 'strip off the header lines (zip)' '
+	strip_headers <tmp >master.zip
+'
+
+if test -n "$(which unzip 2>/dev/null)"; then
+	test_set_prereq UNZIP
+else
+	say 'Skipping ZIP validation tests: unzip not found'
+fi
+
+test_expect_success UNZIP 'verify zip format' '
+	unzip -t master.zip
+'
+
+test_expect_success UNZIP 'unzip' '
+	rm -rf master &&
+	unzip master.zip
+'
+
+test_expect_success UNZIP 'count files (zip)' '
+	ls master/ >output &&
+	test_line_count = 5 output
+'
+
+test_expect_success UNZIP 'verify unzipped file-5' '
+	grep "^5$" master/file-5 &&
+	test_line_count = 1 master/file-5
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0108-patch.sh b/www/git.causal.agency/cgit/tests/t0108-patch.sh
new file mode 100755
index 00000000..013d6802
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0108-patch.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='Check content on patch page'
+. ./setup.sh
+
+test_expect_success 'generate foo/patch' '
+	cgit_query "url=foo/patch" >tmp
+'
+
+test_expect_success 'find `From:` line' '
+	grep "^From: " tmp
+'
+
+test_expect_success 'find `Date:` line' '
+	grep "^Date: " tmp
+'
+
+test_expect_success 'find `Subject:` line' '
+	grep "^Subject: commit 5" tmp
+'
+
+test_expect_success 'find `cgit` signature' '
+	tail -2 tmp | head -1 | grep "^cgit"
+'
+
+test_expect_success 'compare with output of git-format-patch(1)' '
+	CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" ../../VERSION) &&
+	git --git-dir="$PWD/repos/foo/.git" format-patch --subject-prefix="" --signature="cgit $CGIT_VERSION" --stdout HEAD^ >tmp2 &&
+	strip_headers <tmp >tmp_ &&
+	test_cmp tmp_ tmp2
+'
+
+test_expect_success 'find initial commit' '
+	root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD)
+'
+
+test_expect_success 'generate patch for initial commit' '
+	cgit_query "url=foo/patch&id=$root" >tmp
+'
+
+test_expect_success 'find `cgit` signature' '
+	tail -2 tmp | head -1 | grep "^cgit"
+'
+
+test_expect_success 'generate patches for multiple commits' '
+	id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) &&
+	id2=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD~3) &&
+	cgit_query "url=foo/patch&id=$id&id2=$id2" >tmp
+'
+
+test_expect_success 'find `cgit` signature' '
+	tail -2 tmp | head -1 | grep "^cgit"
+'
+
+test_expect_success 'compare with output of git-format-patch(1)' '
+	CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" ../../VERSION) &&
+	git --git-dir="$PWD/repos/foo/.git" format-patch -N --subject-prefix="" --signature="cgit $CGIT_VERSION" --stdout HEAD~3..HEAD >tmp2 &&
+	strip_headers <tmp >tmp_ &&
+	test_cmp tmp_ tmp2
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh b/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh
new file mode 100755
index 00000000..189ef281
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='Ensure that git does not access $HOME'
+. ./setup.sh
+
+test -n "$(which strace 2>/dev/null)" || {
+	skip_all='Skipping access validation tests: strace not found'
+	test_done
+	exit
+}
+
+strace true 2>/dev/null || {
+	skip_all='Skipping access validation tests: strace not functional'
+	test_done
+	exit
+}
+
+test_no_home_access () {
+	non_existent_path="/path/to/some/place/that/does/not/possibly/exist"
+	while test -d "$non_existent_path"; do
+		non_existent_path="$non_existent_path/$(date +%N)"
+	done &&
+	strace \
+		-E HOME="$non_existent_path" \
+		-E CGIT_CONFIG="$PWD/cgitrc" \
+		-E QUERY_STRING="url=$1" \
+		-e access -f -o strace.out cgit &&
+	! grep "$non_existent_path" strace.out
+}
+
+test_no_home_access_success() {
+	test_expect_success "do not access \$HOME: $1" "
+		test_no_home_access '$1'
+	"
+}
+
+test_no_home_access_success
+test_no_home_access_success foo
+test_no_home_access_success foo/refs
+test_no_home_access_success foo/log
+test_no_home_access_success foo/tree
+test_no_home_access_success foo/tree/file-1
+test_no_home_access_success foo/commit
+test_no_home_access_success foo/diff
+test_no_home_access_success foo/patch
+test_no_home_access_success foo/snapshot/master.tar.gz
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh b/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh
new file mode 100755
index 00000000..66fa7d5d
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='Check content on rawdiff page'
+. ./setup.sh
+
+test_expect_success 'generate foo/rawdiff' '
+	cgit_query "url=foo/rawdiff" >tmp
+'
+
+test_expect_success 'compare with output of git-diff(1)' '
+	git --git-dir="$PWD/repos/foo/.git" diff HEAD^.. >tmp2 &&
+	sed "1,4d" tmp >tmp_ &&
+	cmp tmp_ tmp2
+'
+
+test_expect_success 'find initial commit' '
+	root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD)
+'
+
+test_expect_success 'generate diff for initial commit' '
+	cgit_query "url=foo/rawdiff&id=$root" >tmp
+'
+
+test_expect_success 'compare with output of git-diff-tree(1)' '
+	git --git-dir="$PWD/repos/foo/.git" diff-tree -p --no-commit-id --root "$root" >tmp2 &&
+	sed "1,4d" tmp >tmp_ &&
+	cmp tmp_ tmp2
+'
+
+test_expect_success 'generate diff for multiple commits' '
+	id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) &&
+	id2=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD~3) &&
+	cgit_query "url=foo/rawdiff&id=$id&id2=$id2" >tmp
+'
+
+test_expect_success 'compare with output of git-diff(1)' '
+	git --git-dir="$PWD/repos/foo/.git" diff HEAD~3..HEAD >tmp2 &&
+	sed "1,4d" tmp >tmp_ &&
+	cmp tmp_ tmp2
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0111-filter.sh b/www/git.causal.agency/cgit/tests/t0111-filter.sh
new file mode 100755
index 00000000..e5d35750
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0111-filter.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='Check filtered content'
+. ./setup.sh
+
+prefixes="exec"
+
+for prefix in $prefixes
+do
+	test_expect_success "generate filter-$prefix/tree/a%2bb" "
+		cgit_url 'filter-$prefix/tree/a%2bb' >tmp
+	"
+
+	test_expect_success "check whether the $prefix source filter works" '
+		grep "<code>a+b HELLO$" tmp
+	'
+
+	test_expect_success "generate filter-$prefix/about/" "
+		cgit_url 'filter-$prefix/about/' >tmp
+	"
+
+	test_expect_success "check whether the $prefix about filter works" '
+		grep "<div id='"'"'summary'"'"'>a+b HELLO$" tmp
+	'
+
+	test_expect_success "generate filter-$prefix/commit/" "
+		cgit_url 'filter-$prefix/commit/' >tmp
+	"
+
+	test_expect_success "check whether the $prefix commit filter works" '
+		grep "<div class='"'"'commit-subject'"'"'>ADD A+B" tmp
+	'
+
+	test_expect_success "check whether the $prefix email filter works for authors" '
+		grep "<author@example.com> commit A U THOR &LT;AUTHOR@EXAMPLE.COM&GT;" tmp
+	'
+
+	test_expect_success "check whether the $prefix email filter works for committers" '
+		grep "<committer@example.com> commit C O MITTER &LT;COMMITTER@EXAMPLE.COM&GT;" tmp
+	'
+done
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/valgrind/bin/cgit b/www/git.causal.agency/cgit/tests/valgrind/bin/cgit
new file mode 100755
index 00000000..dcdfbe53
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/valgrind/bin/cgit
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# Note that we currently use Git's suppression file and there are variables
+# $GIT_VALGRIND and $CGIT_VALGRIND which point to different places.
+exec valgrind -q --error-exitcode=126 \
+	--suppressions="$GIT_VALGRIND/default.supp" \
+	--gen-suppressions=all \
+	--leak-check=no \
+	--track-origins=yes \
+	--log-fd=4 \
+	--input-fd=4 \
+	"$CGIT_VALGRIND/../../cgit" "$@"
diff --git a/www/git.causal.agency/cgit/ui-atom.c b/www/git.causal.agency/cgit/ui-atom.c
new file mode 100644
index 00000000..8329e01a
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-atom.c
@@ -0,0 +1,158 @@
+/* ui-atom.c: functions for atom feeds
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-atom.h"
+#include "html.h"
+#include "ui-shared.h"
+
+static void add_entry(struct commit *commit, const char *host)
+{
+	char delim = '&';
+	char *hex;
+	char *mail, *t, *t2;
+	struct commitinfo *info;
+
+	info = cgit_parse_commit(commit);
+	hex = oid_to_hex(&commit->object.oid);
+	html("<entry>\n");
+	html("<title>");
+	html_txt(info->subject);
+	html("</title>\n");
+	html("<updated>");
+	html_txt(show_date(info->committer_date, 0,
+                    date_mode_from_type(DATE_ISO8601_STRICT)));
+	html("</updated>\n");
+	html("<author>\n");
+	if (info->author) {
+		html("<name>");
+		html_txt(info->author);
+		html("</name>\n");
+	}
+	if (info->author_email && !ctx.cfg.noplainemail) {
+		mail = xstrdup(info->author_email);
+		t = strchr(mail, '<');
+		if (t)
+			t++;
+		else
+			t = mail;
+		t2 = strchr(t, '>');
+		if (t2)
+			*t2 = '\0';
+		html("<email>");
+		html_txt(t);
+		html("</email>\n");
+		free(mail);
+	}
+	html("</author>\n");
+	html("<published>");
+	html_txt(show_date(info->author_date, 0,
+                    date_mode_from_type(DATE_ISO8601_STRICT)));
+	html("</published>\n");
+	if (host) {
+		char *pageurl;
+		html("<link rel='alternate' type='text/html' href='");
+		html(cgit_httpscheme());
+		html_attr(host);
+		pageurl = cgit_pageurl(ctx.repo->url, "commit", NULL);
+		html_attr(pageurl);
+		if (ctx.cfg.virtual_root)
+			delim = '?';
+		html_attrf("%cid=%s", delim, hex);
+		html("'/>\n");
+		free(pageurl);
+	}
+	html("<id>");
+	html_txtf("urn:%s:%s", the_hash_algo->name, hex);
+	html("</id>\n");
+	html("<content type='text'>\n");
+	html_txt(info->msg);
+	html("</content>\n");
+	html("</entry>\n");
+	cgit_free_commitinfo(info);
+}
+
+
+void cgit_print_atom(char *tip, const char *path, int max_count)
+{
+	char *host;
+	const char *argv[] = {NULL, tip, NULL, NULL, NULL};
+	struct commit *commit;
+	struct rev_info rev;
+	int argc = 2;
+	int first = 1;
+
+	if (ctx.qry.show_all)
+		argv[1] = "--all";
+	else if (!tip)
+		argv[1] = ctx.qry.head;
+
+	if (path) {
+		argv[argc++] = "--";
+		argv[argc++] = path;
+	}
+
+	init_revisions(&rev, NULL);
+	rev.abbrev = DEFAULT_ABBREV;
+	rev.commit_format = CMIT_FMT_DEFAULT;
+	rev.verbose_header = 1;
+	rev.show_root_diff = 0;
+	rev.max_count = max_count;
+	setup_revisions(argc, argv, &rev, NULL);
+	prepare_revision_walk(&rev);
+
+	host = cgit_hosturl();
+	ctx.page.mimetype = "text/xml";
+	ctx.page.charset = "utf-8";
+	cgit_print_http_headers();
+	html("<feed xmlns='http://www.w3.org/2005/Atom'>\n");
+	html("<title>");
+	html_txt(ctx.repo->name);
+	if (path) {
+		html("/");
+		html_txt(path);
+	}
+	if (tip && !ctx.qry.show_all) {
+		html(", branch ");
+		html_txt(tip);
+	}
+	html("</title>\n");
+	html("<subtitle>");
+	html_txt(ctx.repo->desc);
+	html("</subtitle>\n");
+	if (host) {
+		char *fullurl = cgit_currentfullurl();
+		char *repourl = cgit_repourl(ctx.repo->url);
+		html("<id>");
+		html_txtf("%s%s%s", cgit_httpscheme(), host, fullurl);
+		html("</id>\n");
+		html("<link rel='self' href='");
+		html_attrf("%s%s%s", cgit_httpscheme(), host, fullurl);
+		html("'/>\n");
+		html("<link rel='alternate' type='text/html' href='");
+		html_attrf("%s%s%s", cgit_httpscheme(), host, repourl);
+		html("'/>\n");
+		free(fullurl);
+		free(repourl);
+	}
+	while ((commit = get_revision(&rev)) != NULL) {
+		if (first) {
+			html("<updated>");
+			html_txt(show_date(commit->date, 0,
+				date_mode_from_type(DATE_ISO8601_STRICT)));
+			html("</updated>\n");
+			first = 0;
+		}
+		add_entry(commit, host);
+		free_commit_buffer(the_repository->parsed_objects, commit);
+		free_commit_list(commit->parents);
+		commit->parents = NULL;
+	}
+	html("</feed>\n");
+	free(host);
+}
diff --git a/www/git.causal.agency/cgit/ui-atom.h b/www/git.causal.agency/cgit/ui-atom.h
new file mode 100644
index 00000000..dda953bb
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-atom.h
@@ -0,0 +1,6 @@
+#ifndef UI_ATOM_H
+#define UI_ATOM_H
+
+extern void cgit_print_atom(char *tip, const char *path, int max_count);
+
+#endif
diff --git a/www/git.causal.agency/cgit/ui-blame.c b/www/git.causal.agency/cgit/ui-blame.c
new file mode 100644
index 00000000..4adec2b9
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-blame.c
@@ -0,0 +1,306 @@
+/* ui-blame.c: functions for blame output
+ *
+ * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-blame.h"
+#include "html.h"
+#include "ui-shared.h"
+#include "strvec.h"
+#include "blame.h"
+
+
+static char *emit_suspect_detail(struct blame_origin *suspect)
+{
+	struct commitinfo *info;
+	struct strbuf detail = STRBUF_INIT;
+
+	info = cgit_parse_commit(suspect->commit);
+
+	strbuf_addf(&detail, "author  %s", info->author);
+	if (!ctx.cfg.noplainemail)
+		strbuf_addf(&detail, " %s", info->author_email);
+	strbuf_addf(&detail, "  %s\n",
+		    show_date(info->author_date, info->author_tz,
+				    cgit_date_mode(DATE_ISO8601)));
+
+	strbuf_addf(&detail, "committer  %s", info->committer);
+	if (!ctx.cfg.noplainemail)
+		strbuf_addf(&detail, " %s", info->committer_email);
+	strbuf_addf(&detail, "  %s\n\n",
+		    show_date(info->committer_date, info->committer_tz,
+				    cgit_date_mode(DATE_ISO8601)));
+
+	strbuf_addstr(&detail, info->subject);
+
+	cgit_free_commitinfo(info);
+	return strbuf_detach(&detail, NULL);
+}
+
+static void emit_blame_entry_hash(struct blame_entry *ent)
+{
+	struct blame_origin *suspect = ent->suspect;
+	struct object_id *oid = &suspect->commit->object.oid;
+	unsigned long line = 0;
+
+	char *detail = emit_suspect_detail(suspect);
+	html("<span class='oid'>");
+	cgit_commit_link(find_unique_abbrev(oid, DEFAULT_ABBREV), detail,
+			 NULL, ctx.qry.head, oid_to_hex(oid), suspect->path);
+	html("</span>");
+	free(detail);
+
+	while (line++ < ent->num_lines)
+		html("\n");
+}
+
+static void emit_blame_entry_linenumber(struct blame_entry *ent)
+{
+	const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n";
+
+	unsigned long lineno = ent->lno;
+	while (lineno < ent->lno + ent->num_lines)
+		htmlf(numberfmt, ++lineno);
+}
+
+static void emit_blame_entry_line_background(struct blame_scoreboard *sb,
+					     struct blame_entry *ent)
+{
+	unsigned long line;
+	size_t len, maxlen = 2;
+	const char* pos, *endpos;
+
+	for (line = ent->lno; line < ent->lno + ent->num_lines; line++) {
+		html("\n");
+		pos = blame_nth_line(sb, line);
+		endpos = blame_nth_line(sb, line + 1);
+		len = 0;
+		while (pos < endpos) {
+			len++;
+			if (*pos++ == '\t')
+				len = (len + 7) & ~7;
+		}
+		if (len > maxlen)
+			maxlen = len;
+	}
+
+	for (len = 0; len < maxlen - 1; len++)
+		html(" ");
+}
+
+struct walk_tree_context {
+	char *curr_rev;
+	int match_baselen;
+	int state;
+};
+
+static void print_object(const struct object_id *oid, const char *path,
+			 const char *basename, const char *rev)
+{
+	enum object_type type;
+	char *buf;
+	unsigned long size;
+	struct strvec rev_argv = STRVEC_INIT;
+	struct rev_info revs;
+	struct blame_scoreboard sb;
+	struct blame_origin *o;
+	struct blame_entry *ent = NULL;
+
+	type = oid_object_info(the_repository, oid, &size);
+	if (type == OBJ_BAD) {
+		cgit_print_error_page(404, "Not found", "Bad object name: %s",
+				      oid_to_hex(oid));
+		return;
+	}
+
+	buf = read_object_file(oid, &type, &size);
+	if (!buf) {
+		cgit_print_error_page(500, "Internal server error",
+			"Error reading object %s", oid_to_hex(oid));
+		return;
+	}
+
+	strvec_push(&rev_argv, "blame");
+	strvec_push(&rev_argv, rev);
+	init_revisions(&revs, NULL);
+	revs.diffopt.flags.allow_textconv = 1;
+	setup_revisions(rev_argv.nr, rev_argv.v, &revs, NULL);
+	init_scoreboard(&sb);
+	sb.revs = &revs;
+	sb.repo = the_repository;
+	sb.path = path;
+	setup_scoreboard(&sb, &o);
+	o->suspects = blame_entry_prepend(NULL, 0, sb.num_lines, o);
+	prio_queue_put(&sb.commits, o->commit);
+	blame_origin_decref(o);
+	sb.ent = NULL;
+	sb.path = path;
+	assign_blame(&sb, 0);
+	blame_sort_final(&sb);
+	blame_coalesce(&sb);
+
+	cgit_set_title_from_path(path);
+
+	cgit_print_layout_start();
+	htmlf("blob: %s (", oid_to_hex(oid));
+	cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path);
+	html(") (");
+	cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path);
+	html(")\n");
+
+	if (buffer_is_binary(buf, size)) {
+		html("<div class='error'>blob is binary.</div>");
+		goto cleanup;
+	}
+	if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
+		htmlf("<div class='error'>blob size (%ldKB)"
+		      " exceeds display size limit (%dKB).</div>",
+		      size / 1024, ctx.cfg.max_blob_size);
+		goto cleanup;
+	}
+
+	html("<table class='blame blob'>\n<tr>\n");
+
+	/* Commit hashes */
+	html("<td class='hashes'>");
+	for (ent = sb.ent; ent; ent = ent->next) {
+		html("<div class='alt'><pre>");
+		emit_blame_entry_hash(ent);
+		html("</pre></div>");
+	}
+	html("</td>\n");
+
+	/* Line numbers */
+	if (ctx.cfg.enable_tree_linenumbers) {
+		html("<td class='linenumbers'>");
+		for (ent = sb.ent; ent; ent = ent->next) {
+			html("<div class='alt'><pre>");
+			emit_blame_entry_linenumber(ent);
+			html("</pre></div>");
+		}
+		html("</td>\n");
+	}
+
+	html("<td class='lines'><div>");
+
+	/* Colored bars behind lines */
+	html("<div>");
+	for (ent = sb.ent; ent; ) {
+		struct blame_entry *e = ent->next;
+		html("<div class='alt'><pre>");
+		emit_blame_entry_line_background(&sb, ent);
+		html("</pre></div>");
+		free(ent);
+		ent = e;
+	}
+	html("</div>");
+
+	free((void *)sb.final_buf);
+
+	/* Lines */
+	html("<pre><code>");
+	if (ctx.repo->source_filter) {
+		char *filter_arg = xstrdup(basename);
+		cgit_open_filter(ctx.repo->source_filter, filter_arg);
+		html_raw(buf, size);
+		cgit_close_filter(ctx.repo->source_filter);
+		free(filter_arg);
+	} else {
+		html_txt(buf);
+	}
+	html("</code></pre>");
+
+	html("</div></td>\n");
+
+	html("</tr>\n</table>\n");
+
+	cgit_print_layout_end();
+
+cleanup:
+	free(buf);
+}
+
+static int walk_tree(const struct object_id *oid, struct strbuf *base,
+		     const char *pathname, unsigned mode, void *cbdata)
+{
+	struct walk_tree_context *walk_tree_ctx = cbdata;
+
+	if (base->len == walk_tree_ctx->match_baselen) {
+		if (S_ISREG(mode)) {
+			struct strbuf buffer = STRBUF_INIT;
+			strbuf_addbuf(&buffer, base);
+			strbuf_addstr(&buffer, pathname);
+			print_object(oid, buffer.buf, pathname,
+				     walk_tree_ctx->curr_rev);
+			strbuf_release(&buffer);
+			walk_tree_ctx->state = 1;
+		} else if (S_ISDIR(mode)) {
+			walk_tree_ctx->state = 2;
+		}
+	} else if (base->len < INT_MAX
+			&& (int)base->len > walk_tree_ctx->match_baselen) {
+		walk_tree_ctx->state = 2;
+	} else if (S_ISDIR(mode)) {
+		return READ_TREE_RECURSIVE;
+	}
+	return 0;
+}
+
+static int basedir_len(const char *path)
+{
+	char *p = strrchr(path, '/');
+	if (p)
+		return p - path + 1;
+	return 0;
+}
+
+void cgit_print_blame(void)
+{
+	const char *rev = ctx.qry.oid;
+	struct object_id oid;
+	struct commit *commit;
+	struct pathspec_item path_items = {
+		.match = ctx.qry.path,
+		.len = ctx.qry.path ? strlen(ctx.qry.path) : 0
+	};
+	struct pathspec paths = {
+		.nr = 1,
+		.items = &path_items
+	};
+	struct walk_tree_context walk_tree_ctx = {
+		.state = 0
+	};
+
+	if (!rev)
+		rev = ctx.qry.head;
+
+	if (get_oid(rev, &oid)) {
+		cgit_print_error_page(404, "Not found",
+			"Invalid revision name: %s", rev);
+		return;
+	}
+	commit = lookup_commit_reference(the_repository, &oid);
+	if (!commit || parse_commit(commit)) {
+		cgit_print_error_page(404, "Not found",
+			"Invalid commit reference: %s", rev);
+		return;
+	}
+
+	walk_tree_ctx.curr_rev = xstrdup(rev);
+	walk_tree_ctx.match_baselen = (path_items.match) ?
+				       basedir_len(path_items.match) : -1;
+
+	read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
+		  &paths, walk_tree, &walk_tree_ctx);
+	if (!walk_tree_ctx.state)
+		cgit_print_error_page(404, "Not found", "Not found");
+	else if (walk_tree_ctx.state == 2)
+		cgit_print_error_page(404, "No blame for folders",
+			"Blame is not available for folders.");
+
+	free(walk_tree_ctx.curr_rev);
+}
diff --git a/www/git.causal.agency/cgit/ui-blame.h b/www/git.causal.agency/cgit/ui-blame.h
new file mode 100644
index 00000000..5b97e035
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-blame.h
@@ -0,0 +1,6 @@
+#ifndef UI_BLAME_H
+#define UI_BLAME_H
+
+extern void cgit_print_blame(void);
+
+#endif /* UI_BLAME_H */
diff --git a/www/git.causal.agency/cgit/ui-blob.c b/www/git.causal.agency/cgit/ui-blob.c
new file mode 100644
index 00000000..c10ae42e
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-blob.c
@@ -0,0 +1,182 @@
+/* ui-blob.c: show blob content
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-blob.h"
+#include "html.h"
+#include "ui-shared.h"
+
+struct walk_tree_context {
+	const char *match_path;
+	struct object_id *matched_oid;
+	unsigned int found_path:1;
+	unsigned int file_only:1;
+};
+
+static int walk_tree(const struct object_id *oid, struct strbuf *base,
+		const char *pathname, unsigned mode, void *cbdata)
+{
+	struct walk_tree_context *walk_tree_ctx = cbdata;
+
+	if (walk_tree_ctx->file_only && !S_ISREG(mode))
+		return READ_TREE_RECURSIVE;
+	if (strncmp(base->buf, walk_tree_ctx->match_path, base->len)
+		|| strcmp(walk_tree_ctx->match_path + base->len, pathname))
+		return READ_TREE_RECURSIVE;
+	oidcpy(walk_tree_ctx->matched_oid, oid);
+	walk_tree_ctx->found_path = 1;
+	return 0;
+}
+
+int cgit_ref_path_exists(const char *path, const char *ref, int file_only)
+{
+	struct object_id oid;
+	unsigned long size;
+	struct pathspec_item path_items = {
+		.match = xstrdup(path),
+		.len = strlen(path)
+	};
+	struct pathspec paths = {
+		.nr = 1,
+		.items = &path_items
+	};
+	struct walk_tree_context walk_tree_ctx = {
+		.match_path = path,
+		.matched_oid = &oid,
+		.found_path = 0,
+		.file_only = file_only
+	};
+
+	if (get_oid(ref, &oid))
+		goto done;
+	if (oid_object_info(the_repository, &oid, &size) != OBJ_COMMIT)
+		goto done;
+	read_tree(the_repository,
+		  repo_get_commit_tree(the_repository, lookup_commit_reference(the_repository, &oid)),
+		  &paths, walk_tree, &walk_tree_ctx);
+
+done:
+	free(path_items.match);
+	return walk_tree_ctx.found_path;
+}
+
+int cgit_print_file(char *path, const char *head, int file_only)
+{
+	struct object_id oid;
+	enum object_type type;
+	char *buf;
+	unsigned long size;
+	struct commit *commit;
+	struct pathspec_item path_items = {
+		.match = path,
+		.len = strlen(path)
+	};
+	struct pathspec paths = {
+		.nr = 1,
+		.items = &path_items
+	};
+	struct walk_tree_context walk_tree_ctx = {
+		.match_path = path,
+		.matched_oid = &oid,
+		.found_path = 0,
+		.file_only = file_only
+	};
+
+	if (get_oid(head, &oid))
+		return -1;
+	type = oid_object_info(the_repository, &oid, &size);
+	if (type == OBJ_COMMIT) {
+		commit = lookup_commit_reference(the_repository, &oid);
+		read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
+			  &paths, walk_tree, &walk_tree_ctx);
+		if (!walk_tree_ctx.found_path)
+			return -1;
+		type = oid_object_info(the_repository, &oid, &size);
+	}
+	if (type == OBJ_BAD)
+		return -1;
+	buf = read_object_file(&oid, &type, &size);
+	if (!buf)
+		return -1;
+	buf[size] = '\0';
+	html_raw(buf, size);
+	free(buf);
+	return 0;
+}
+
+void cgit_print_blob(const char *hex, char *path, const char *head, int file_only)
+{
+	struct object_id oid;
+	enum object_type type;
+	char *buf;
+	unsigned long size;
+	struct commit *commit;
+	struct pathspec_item path_items = {
+		.match = path,
+		.len = path ? strlen(path) : 0
+	};
+	struct pathspec paths = {
+		.nr = 1,
+		.items = &path_items
+	};
+	struct walk_tree_context walk_tree_ctx = {
+		.match_path = path,
+		.matched_oid = &oid,
+		.found_path = 0,
+		.file_only = file_only
+	};
+
+	if (hex) {
+		if (get_oid_hex(hex, &oid)) {
+			cgit_print_error_page(400, "Bad request",
+					"Bad hex value: %s", hex);
+			return;
+		}
+	} else {
+		if (get_oid(head, &oid)) {
+			cgit_print_error_page(404, "Not found",
+					"Bad ref: %s", head);
+			return;
+		}
+	}
+
+	type = oid_object_info(the_repository, &oid, &size);
+
+	if ((!hex) && type == OBJ_COMMIT && path) {
+		commit = lookup_commit_reference(the_repository, &oid);
+		read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
+			  &paths, walk_tree, &walk_tree_ctx);
+		type = oid_object_info(the_repository, &oid, &size);
+	}
+
+	if (type == OBJ_BAD) {
+		cgit_print_error_page(404, "Not found",
+				"Bad object name: %s", hex);
+		return;
+	}
+
+	buf = read_object_file(&oid, &type, &size);
+	if (!buf) {
+		cgit_print_error_page(500, "Internal server error",
+				"Error reading object %s", hex);
+		return;
+	}
+
+	buf[size] = '\0';
+	if (buffer_is_binary(buf, size))
+		ctx.page.mimetype = "application/octet-stream";
+	else
+		ctx.page.mimetype = "text/plain";
+	ctx.page.filename = path;
+
+	html("X-Content-Type-Options: nosniff\n");
+	html("Content-Security-Policy: default-src 'none'\n");
+	cgit_print_http_headers();
+	html_raw(buf, size);
+	free(buf);
+}
diff --git a/www/git.causal.agency/cgit/ui-blob.h b/www/git.causal.agency/cgit/ui-blob.h
new file mode 100644
index 00000000..16847b20
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-blob.h
@@ -0,0 +1,8 @@
+#ifndef UI_BLOB_H
+#define UI_BLOB_H
+
+extern int cgit_ref_path_exists(const char *path, const char *ref, int file_only);
+extern int cgit_print_file(char *path, const char *head, int file_only);
+extern void cgit_print_blob(const char *hex, char *path, const char *head, int file_only);
+
+#endif /* UI_BLOB_H */
diff --git a/www/git.causal.agency/cgit/ui-clone.c b/www/git.causal.agency/cgit/ui-clone.c
new file mode 100644
index 00000000..5dccb639
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-clone.c
@@ -0,0 +1,126 @@
+/* ui-clone.c: functions for http cloning, based on
+ * git's http-backend.c by Shawn O. Pearce
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-clone.h"
+#include "html.h"
+#include "ui-shared.h"
+#include "packfile.h"
+#include "object-store.h"
+
+static int print_ref_info(const char *refname, const struct object_id *oid,
+                          int flags, void *cb_data)
+{
+	struct object *obj;
+
+	if (!(obj = parse_object(the_repository, oid)))
+		return 0;
+
+	htmlf("%s\t%s\n", oid_to_hex(oid), refname);
+	if (obj->type == OBJ_TAG) {
+		if (!(obj = deref_tag(the_repository, obj, refname, 0)))
+			return 0;
+		htmlf("%s\t%s^{}\n", oid_to_hex(&obj->oid), refname);
+	}
+	return 0;
+}
+
+static void print_pack_info(void)
+{
+	struct packed_git *pack;
+	char *offset;
+
+	ctx.page.mimetype = "text/plain";
+	ctx.page.filename = "objects/info/packs";
+	cgit_print_http_headers();
+	reprepare_packed_git(the_repository);
+	for (pack = get_packed_git(the_repository); pack; pack = pack->next) {
+		if (pack->pack_local) {
+			offset = strrchr(pack->pack_name, '/');
+			if (offset && offset[1] != '\0')
+				++offset;
+			else
+				offset = pack->pack_name;
+			htmlf("P %s\n", offset);
+		}
+	}
+}
+
+static void send_file(const char *path)
+{
+	struct stat st;
+
+	if (stat(path, &st)) {
+		switch (errno) {
+		case ENOENT:
+			cgit_print_error_page(404, "Not found", "Not found");
+			break;
+		case EACCES:
+			cgit_print_error_page(403, "Forbidden", "Forbidden");
+			break;
+		default:
+			cgit_print_error_page(400, "Bad request", "Bad request");
+		}
+		return;
+	}
+	ctx.page.mimetype = "application/octet-stream";
+	ctx.page.filename = path;
+	skip_prefix(path, ctx.repo->path, &ctx.page.filename);
+	skip_prefix(ctx.page.filename, "/", &ctx.page.filename);
+	cgit_print_http_headers();
+	html_include(path);
+}
+
+void cgit_clone_info(void)
+{
+	if (!ctx.qry.path || strcmp(ctx.qry.path, "refs")) {
+		cgit_print_error_page(400, "Bad request", "Bad request");
+		return;
+	}
+
+	ctx.page.mimetype = "text/plain";
+	ctx.page.filename = "info/refs";
+	cgit_print_http_headers();
+	for_each_ref(print_ref_info, NULL);
+}
+
+void cgit_clone_objects(void)
+{
+	char *p;
+
+	if (!ctx.qry.path)
+		goto err;
+
+	if (!strcmp(ctx.qry.path, "info/packs")) {
+		print_pack_info();
+		return;
+	}
+
+	/* Avoid directory traversal by forbidding "..", but also work around
+	 * other funny business by just specifying a fairly strict format. For
+	 * example, now we don't have to stress out about the Cygwin port.
+	 */
+	for (p = ctx.qry.path; *p; ++p) {
+		if (*p == '.' && *(p + 1) == '.')
+			goto err;
+		if (!isalnum(*p) && *p != '/' && *p != '.' && *p != '-')
+			goto err;
+	}
+
+	send_file(git_path("objects/%s", ctx.qry.path));
+	return;
+
+err:
+	cgit_print_error_page(400, "Bad request", "Bad request");
+}
+
+void cgit_clone_head(void)
+{
+	send_file(git_path("%s", "HEAD"));
+}
diff --git a/www/git.causal.agency/cgit/ui-clone.h b/www/git.causal.agency/cgit/ui-clone.h
new file mode 100644
index 00000000..3e460a3d
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-clone.h
@@ -0,0 +1,8 @@
+#ifndef UI_CLONE_H
+#define UI_CLONE_H
+
+void cgit_clone_info(void);
+void cgit_clone_objects(void);
+void cgit_clone_head(void);
+
+#endif /* UI_CLONE_H */
diff --git a/www/git.causal.agency/cgit/ui-commit.c b/www/git.causal.agency/cgit/ui-commit.c
new file mode 100644
index 00000000..b49259e6
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-commit.c
@@ -0,0 +1,148 @@
+/* ui-commit.c: generate commit view
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-commit.h"
+#include "html.h"
+#include "ui-shared.h"
+#include "ui-diff.h"
+#include "ui-log.h"
+
+void cgit_print_commit(char *hex, const char *prefix)
+{
+	struct commit *commit, *parent;
+	struct commitinfo *info, *parent_info;
+	struct commit_list *p;
+	struct strbuf notes = STRBUF_INIT;
+	struct object_id oid;
+	char *tmp, *tmp2;
+	int parents = 0;
+
+	if (!hex)
+		hex = ctx.qry.head;
+
+	if (get_oid(hex, &oid)) {
+		cgit_print_error_page(400, "Bad request",
+				"Bad object id: %s", hex);
+		return;
+	}
+	commit = lookup_commit_reference(the_repository, &oid);
+	if (!commit) {
+		cgit_print_error_page(404, "Not found",
+				"Bad commit reference: %s", hex);
+		return;
+	}
+	info = cgit_parse_commit(commit);
+
+	format_display_notes(&oid, &notes, PAGE_ENCODING, 1);
+
+	load_ref_decorations(NULL, DECORATE_FULL_REFS);
+
+	ctx.page.title = fmtalloc("%s - %s", info->subject, ctx.page.title);
+	cgit_print_layout_start();
+	cgit_print_diff_ctrls();
+	html("<table summary='commit info' class='commit-info'>\n");
+	html("<tr><th>author</th><td>");
+	cgit_open_filter(ctx.repo->email_filter, info->author_email, "commit");
+	html_txt(info->author);
+	if (!ctx.cfg.noplainemail) {
+		html(" ");
+		html_txt(info->author_email);
+	}
+	cgit_close_filter(ctx.repo->email_filter);
+	html("</td><td class='right'>");
+	html_txt(show_date(info->author_date, info->author_tz,
+				cgit_date_mode(DATE_ISO8601)));
+	html("</td></tr>\n");
+	html("<tr><th>committer</th><td>");
+	cgit_open_filter(ctx.repo->email_filter, info->committer_email, "commit");
+	html_txt(info->committer);
+	if (!ctx.cfg.noplainemail) {
+		html(" ");
+		html_txt(info->committer_email);
+	}
+	cgit_close_filter(ctx.repo->email_filter);
+	html("</td><td class='right'>");
+	html_txt(show_date(info->committer_date, info->committer_tz,
+				cgit_date_mode(DATE_ISO8601)));
+	html("</td></tr>\n");
+	html("<tr><th>commit</th><td colspan='2' class='oid'>");
+	tmp = oid_to_hex(&commit->object.oid);
+	cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, prefix);
+	html(" (");
+	cgit_patch_link("patch", NULL, NULL, NULL, tmp, prefix);
+	html(")</td></tr>\n");
+	html("<tr><th>tree</th><td colspan='2' class='oid'>");
+	tmp = xstrdup(hex);
+	cgit_tree_link(oid_to_hex(get_commit_tree_oid(commit)), NULL, NULL,
+		       ctx.qry.head, tmp, NULL);
+	if (prefix) {
+		html(" /");
+		cgit_tree_link(prefix, NULL, NULL, ctx.qry.head, tmp, prefix);
+	}
+	free(tmp);
+	html("</td></tr>\n");
+	for (p = commit->parents; p; p = p->next) {
+		parent = lookup_commit_reference(the_repository, &p->item->object.oid);
+		if (!parent) {
+			html("<tr><td colspan='3'>");
+			cgit_print_error("Error reading parent commit");
+			html("</td></tr>");
+			continue;
+		}
+		html("<tr><th>parent</th>"
+		     "<td colspan='2' class='oid'>");
+		tmp = tmp2 = oid_to_hex(&p->item->object.oid);
+		if (ctx.repo->enable_subject_links) {
+			parent_info = cgit_parse_commit(parent);
+			tmp2 = parent_info->subject;
+		}
+		cgit_commit_link(tmp2, NULL, NULL, ctx.qry.head, tmp, prefix);
+		html(" (");
+		cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
+			       oid_to_hex(&p->item->object.oid), prefix);
+		html(")</td></tr>");
+		parents++;
+	}
+	if (ctx.repo->snapshots) {
+		html("<tr><th>download</th><td colspan='2' class='oid'>");
+		cgit_print_snapshot_links(ctx.repo, hex, "<br/>");
+		html("</td></tr>");
+	}
+	html("</table>\n");
+	html("<div class='commit-subject'>");
+	cgit_open_filter(ctx.repo->commit_filter);
+	html_txt(info->subject);
+	cgit_close_filter(ctx.repo->commit_filter);
+	show_commit_decorations(commit);
+	html("</div>");
+	html("<pre class='commit-msg'>");
+	cgit_open_filter(ctx.repo->commit_filter);
+	html_txt(info->msg);
+	cgit_close_filter(ctx.repo->commit_filter);
+	html("</pre>");
+	if (notes.len != 0) {
+		html("<div class='notes-header'>Notes</div>");
+		html("<div class='notes'>");
+		cgit_open_filter(ctx.repo->commit_filter);
+		html_txt(notes.buf);
+		cgit_close_filter(ctx.repo->commit_filter);
+		html("</div>");
+		html("<div class='notes-footer'></div>");
+	}
+	if (parents < 3) {
+		if (parents)
+			tmp = oid_to_hex(&commit->parents->item->object.oid);
+		else
+			tmp = NULL;
+		cgit_print_diff(ctx.qry.oid, tmp, prefix, 0, 0);
+	}
+	strbuf_release(&notes);
+	cgit_free_commitinfo(info);
+	cgit_print_layout_end();
+}
diff --git a/www/git.causal.agency/cgit/ui-commit.h b/www/git.causal.agency/cgit/ui-commit.h
new file mode 100644
index 00000000..8198b4ba
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-commit.h
@@ -0,0 +1,6 @@
+#ifndef UI_COMMIT_H
+#define UI_COMMIT_H
+
+extern void cgit_print_commit(char *hex, const char *prefix);
+
+#endif /* UI_COMMIT_H */
diff --git a/www/git.causal.agency/cgit/ui-diff.c b/www/git.causal.agency/cgit/ui-diff.c
new file mode 100644
index 00000000..2a64ae8f
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-diff.c
@@ -0,0 +1,505 @@
+/* ui-diff.c: show diff between two blobs
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-diff.h"
+#include "html.h"
+#include "ui-shared.h"
+#include "ui-ssdiff.h"
+
+struct object_id old_rev_oid[1];
+struct object_id new_rev_oid[1];
+
+static int files, slots;
+static int total_adds, total_rems, max_changes;
+static int lines_added, lines_removed;
+
+static struct fileinfo {
+	char status;
+	struct object_id old_oid[1];
+	struct object_id new_oid[1];
+	unsigned short old_mode;
+	unsigned short new_mode;
+	char *old_path;
+	char *new_path;
+	unsigned int added;
+	unsigned int removed;
+	unsigned long old_size;
+	unsigned long new_size;
+	unsigned int binary:1;
+} *items;
+
+static int use_ssdiff = 0;
+static struct diff_filepair *current_filepair;
+static const char *current_prefix;
+
+struct diff_filespec *cgit_get_current_old_file(void)
+{
+	return current_filepair->one;
+}
+
+struct diff_filespec *cgit_get_current_new_file(void)
+{
+	return current_filepair->two;
+}
+
+static void print_fileinfo(struct fileinfo *info)
+{
+	char *class;
+
+	switch (info->status) {
+	case DIFF_STATUS_ADDED:
+		class = "add";
+		break;
+	case DIFF_STATUS_COPIED:
+		class = "cpy";
+		break;
+	case DIFF_STATUS_DELETED:
+		class = "del";
+		break;
+	case DIFF_STATUS_MODIFIED:
+		class = "upd";
+		break;
+	case DIFF_STATUS_RENAMED:
+		class = "mov";
+		break;
+	case DIFF_STATUS_TYPE_CHANGED:
+		class = "typ";
+		break;
+	case DIFF_STATUS_UNKNOWN:
+		class = "unk";
+		break;
+	case DIFF_STATUS_UNMERGED:
+		class = "stg";
+		break;
+	default:
+		die("bug: unhandled diff status %c", info->status);
+	}
+
+	html("<tr>");
+	html("<td class='mode'>");
+	if (is_null_oid(info->new_oid)) {
+		cgit_print_filemode(info->old_mode);
+	} else {
+		cgit_print_filemode(info->new_mode);
+	}
+
+	if (info->old_mode != info->new_mode &&
+	    !is_null_oid(info->old_oid) &&
+	    !is_null_oid(info->new_oid)) {
+		html("<span class='modechange'>[");
+		cgit_print_filemode(info->old_mode);
+		html("]</span>");
+	}
+	htmlf("</td><td class='%s'>", class);
+	cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.oid,
+		       ctx.qry.oid2, info->new_path);
+	if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) {
+		htmlf(" (%s from ",
+		      info->status == DIFF_STATUS_COPIED ? "copied" : "renamed");
+		html_txt(info->old_path);
+		html(")");
+	}
+	html("</td><td class='right'>");
+	if (info->binary) {
+		htmlf("bin</td><td class='graph'>%ld -> %ld bytes",
+		      info->old_size, info->new_size);
+		return;
+	}
+	htmlf("%d", info->added + info->removed);
+	html("</td><td class='graph'>");
+	htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes));
+	htmlf("<td class='add' style='width: %.1f%%;'/>",
+	      info->added * 100.0 / max_changes);
+	htmlf("<td class='rem' style='width: %.1f%%;'/>",
+	      info->removed * 100.0 / max_changes);
+	htmlf("<td class='none' style='width: %.1f%%;'/>",
+	      (max_changes - info->removed - info->added) * 100.0 / max_changes);
+	html("</tr></table></td></tr>\n");
+}
+
+static void count_diff_lines(char *line, int len)
+{
+	if (line && (len > 0)) {
+		if (line[0] == '+')
+			lines_added++;
+		else if (line[0] == '-')
+			lines_removed++;
+	}
+}
+
+static int show_filepair(struct diff_filepair *pair)
+{
+	/* Always show if we have no limiting prefix. */
+	if (!current_prefix)
+		return 1;
+
+	/* Show if either path in the pair begins with the prefix. */
+	if (starts_with(pair->one->path, current_prefix) ||
+	    starts_with(pair->two->path, current_prefix))
+		return 1;
+
+	/* Otherwise we don't want to show this filepair. */
+	return 0;
+}
+
+static void inspect_filepair(struct diff_filepair *pair)
+{
+	int binary = 0;
+	unsigned long old_size = 0;
+	unsigned long new_size = 0;
+
+	if (!show_filepair(pair))
+		return;
+
+	files++;
+	lines_added = 0;
+	lines_removed = 0;
+	cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size, &new_size,
+			&binary, 0, ctx.qry.ignorews, count_diff_lines);
+	if (files >= slots) {
+		if (slots == 0)
+			slots = 4;
+		else
+			slots = slots * 2;
+		items = xrealloc(items, slots * sizeof(struct fileinfo));
+	}
+	items[files-1].status = pair->status;
+	oidcpy(items[files-1].old_oid, &pair->one->oid);
+	oidcpy(items[files-1].new_oid, &pair->two->oid);
+	items[files-1].old_mode = pair->one->mode;
+	items[files-1].new_mode = pair->two->mode;
+	items[files-1].old_path = xstrdup(pair->one->path);
+	items[files-1].new_path = xstrdup(pair->two->path);
+	items[files-1].added = lines_added;
+	items[files-1].removed = lines_removed;
+	items[files-1].old_size = old_size;
+	items[files-1].new_size = new_size;
+	items[files-1].binary = binary;
+	if (lines_added + lines_removed > max_changes)
+		max_changes = lines_added + lines_removed;
+	total_adds += lines_added;
+	total_rems += lines_removed;
+}
+
+static void cgit_print_diffstat(const struct object_id *old_oid,
+				const struct object_id *new_oid,
+				const char *prefix)
+{
+	int i;
+
+	html("<div class='diffstat-header'>");
+	cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.oid,
+		       ctx.qry.oid2, NULL);
+	if (prefix) {
+		html(" (limited to '");
+		html_txt(prefix);
+		html("')");
+	}
+	html("</div>");
+	html("<table summary='diffstat' class='diffstat'>");
+	max_changes = 0;
+	cgit_diff_tree(old_oid, new_oid, inspect_filepair, prefix,
+		       ctx.qry.ignorews);
+	for (i = 0; i<files; i++)
+		print_fileinfo(&items[i]);
+	html("</table>");
+	html("<div class='diffstat-summary'>");
+	htmlf("%d files changed, %d insertions, %d deletions",
+	      files, total_adds, total_rems);
+	html("</div>");
+}
+
+
+/*
+ * print a single line returned from xdiff
+ */
+static void print_line(char *line, int len)
+{
+	char *class = "ctx";
+	char c = line[len-1];
+
+	if (line[0] == '+')
+		class = "add";
+	else if (line[0] == '-')
+		class = "del";
+	else if (line[0] == '@')
+		class = "hunk";
+
+	htmlf("<span class='%s'>", class);
+	line[len-1] = '\0';
+	html_txt(line);
+	line[len-1] = c;
+	html("</span>\n");
+}
+
+static void header(const struct object_id *oid1, char *path1, int mode1,
+		   const struct object_id *oid2, char *path2, int mode2)
+{
+	char *abbrev1, *abbrev2;
+	int subproject;
+
+	subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
+	html("<span class='head'>");
+	html("diff --git a/");
+	html_txt(path1);
+	html(" b/");
+	html_txt(path2);
+	html("\n");
+
+	if (mode1 == 0)
+		htmlf("new file mode %.6o\n", mode2);
+
+	if (mode2 == 0)
+		htmlf("deleted file mode %.6o\n", mode1);
+
+	if (!subproject) {
+		abbrev1 = xstrdup(find_unique_abbrev(oid1, DEFAULT_ABBREV));
+		abbrev2 = xstrdup(find_unique_abbrev(oid2, DEFAULT_ABBREV));
+		htmlf("index %s..%s", abbrev1, abbrev2);
+		free(abbrev1);
+		free(abbrev2);
+		if (mode1 != 0 && mode2 != 0) {
+			htmlf(" %.6o", mode1);
+			if (mode2 != mode1)
+				htmlf("..%.6o", mode2);
+		}
+		html("\n");
+		if (is_null_oid(oid1)) {
+			path1 = "dev/null";
+			html("--- /");
+		} else
+			html("--- a/");
+		if (mode1 != 0)
+			cgit_tree_link(path1, NULL, NULL, ctx.qry.head,
+				       oid_to_hex(old_rev_oid), path1);
+		else
+			html_txt(path1);
+		html("\n");
+		if (is_null_oid(oid2)) {
+			path2 = "dev/null";
+			html("+++ /");
+		} else
+			html("+++ b/");
+		if (mode2 != 0)
+			cgit_tree_link(path2, NULL, NULL, ctx.qry.head,
+				       oid_to_hex(new_rev_oid), path2);
+		else
+			html_txt(path2);
+		html("\n");
+	}
+	html("</span>");
+}
+
+static void filepair_cb(struct diff_filepair *pair)
+{
+	unsigned long old_size = 0;
+	unsigned long new_size = 0;
+	int binary = 0;
+	linediff_fn print_line_fn = print_line;
+
+	if (!show_filepair(pair))
+		return;
+
+	current_filepair = pair;
+	if (use_ssdiff) {
+		cgit_ssdiff_header_begin();
+		print_line_fn = cgit_ssdiff_line_cb;
+	}
+	header(&pair->one->oid, pair->one->path, pair->one->mode,
+	       &pair->two->oid, pair->two->path, pair->two->mode);
+	if (use_ssdiff)
+		cgit_ssdiff_header_end();
+	if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
+		if (S_ISGITLINK(pair->one->mode))
+			print_line_fn(fmt("-Subproject %s", oid_to_hex(&pair->one->oid)), 52);
+		if (S_ISGITLINK(pair->two->mode))
+			print_line_fn(fmt("+Subproject %s", oid_to_hex(&pair->two->oid)), 52);
+		if (use_ssdiff)
+			cgit_ssdiff_footer();
+		return;
+	}
+	if (cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size,
+			    &new_size, &binary, ctx.qry.context,
+			    ctx.qry.ignorews, print_line_fn))
+		cgit_print_error("Error running diff");
+	if (binary) {
+		if (use_ssdiff)
+			html("<tr><td colspan='4'>Binary files differ</td></tr>");
+		else
+			html("Binary files differ");
+	}
+	if (use_ssdiff)
+		cgit_ssdiff_footer();
+}
+
+void cgit_print_diff_ctrls(void)
+{
+	int i, curr;
+
+	html("<div class='cgit-panel'>");
+	html("<b>diff options</b>");
+	html("<form method='get'>");
+	cgit_add_hidden_formfields(1, 0, ctx.qry.page);
+	html("<table>");
+	html("<tr><td colspan='2'/></tr>");
+	html("<tr>");
+	html("<td class='label'>context:</td>");
+	html("<td class='ctrl'>");
+	html("<select name='context' onchange='this.form.submit();'>");
+	curr = ctx.qry.context;
+	if (!curr)
+		curr = 3;
+	for (i = 1; i <= 10; i++)
+		html_intoption(i, fmt("%d", i), curr);
+	for (i = 15; i <= 40; i += 5)
+		html_intoption(i, fmt("%d", i), curr);
+	html("</select>");
+	html("</td>");
+	html("</tr><tr>");
+	html("<td class='label'>space:</td>");
+	html("<td class='ctrl'>");
+	html("<select name='ignorews' onchange='this.form.submit();'>");
+	html_intoption(0, "include", ctx.qry.ignorews);
+	html_intoption(1, "ignore", ctx.qry.ignorews);
+	html("</select>");
+	html("</td>");
+	html("</tr><tr>");
+	html("<td class='label'>mode:</td>");
+	html("<td class='ctrl'>");
+	html("<select name='dt' onchange='this.form.submit();'>");
+	curr = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype;
+	html_intoption(0, "unified", curr);
+	html_intoption(1, "ssdiff", curr);
+	html_intoption(2, "stat only", curr);
+	html("</select></td></tr>");
+	html("<tr><td/><td class='ctrl'>");
+	html("<noscript><input type='submit' value='reload'/></noscript>");
+	html("</td></tr></table>");
+	html("</form>");
+	html("</div>");
+}
+
+void cgit_print_diff(const char *new_rev, const char *old_rev,
+		     const char *prefix, int show_ctrls, int raw)
+{
+	struct commit *commit, *commit2;
+	const struct object_id *old_tree_oid, *new_tree_oid;
+	diff_type difftype;
+
+	/*
+	 * If "follow" is set then the diff machinery needs to examine the
+	 * entire commit to detect renames so we must limit the paths in our
+	 * own callbacks and not pass the prefix to the diff machinery.
+	 */
+	if (ctx.qry.follow && ctx.cfg.enable_follow_links) {
+		current_prefix = prefix;
+		prefix = "";
+	} else {
+		current_prefix = NULL;
+	}
+
+	if (!new_rev)
+		new_rev = ctx.qry.head;
+	if (get_oid(new_rev, new_rev_oid)) {
+		cgit_print_error_page(404, "Not found",
+			"Bad object name: %s", new_rev);
+		return;
+	}
+	commit = lookup_commit_reference(the_repository, new_rev_oid);
+	if (!commit || parse_commit(commit)) {
+		cgit_print_error_page(404, "Not found",
+			"Bad commit: %s", oid_to_hex(new_rev_oid));
+		return;
+	}
+	new_tree_oid = get_commit_tree_oid(commit);
+
+	if (old_rev) {
+		if (get_oid(old_rev, old_rev_oid)) {
+			cgit_print_error_page(404, "Not found",
+				"Bad object name: %s", old_rev);
+			return;
+		}
+	} else if (commit->parents && commit->parents->item) {
+		oidcpy(old_rev_oid, &commit->parents->item->object.oid);
+	} else {
+		oidclr(old_rev_oid);
+	}
+
+	if (!is_null_oid(old_rev_oid)) {
+		commit2 = lookup_commit_reference(the_repository, old_rev_oid);
+		if (!commit2 || parse_commit(commit2)) {
+			cgit_print_error_page(404, "Not found",
+				"Bad commit: %s", oid_to_hex(old_rev_oid));
+			return;
+		}
+		old_tree_oid = get_commit_tree_oid(commit2);
+	} else {
+		old_tree_oid = NULL;
+	}
+
+	if (raw) {
+		struct diff_options diffopt;
+
+		diff_setup(&diffopt);
+		diffopt.output_format = DIFF_FORMAT_PATCH;
+		diffopt.flags.recursive = 1;
+		diff_setup_done(&diffopt);
+
+		ctx.page.mimetype = "text/plain";
+		cgit_print_http_headers();
+		if (old_tree_oid) {
+			diff_tree_oid(old_tree_oid, new_tree_oid, "",
+				       &diffopt);
+		} else {
+			diff_root_tree_oid(new_tree_oid, "", &diffopt);
+		}
+		diffcore_std(&diffopt);
+		diff_flush(&diffopt);
+
+		return;
+	}
+
+	difftype = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype;
+	use_ssdiff = difftype == DIFF_SSDIFF;
+
+	if (show_ctrls) {
+		cgit_print_layout_start();
+		cgit_print_diff_ctrls();
+	}
+
+	/*
+	 * Clicking on a link to a file in the diff stat should show a diff
+	 * of the file, showing the diff stat limited to a single file is
+	 * pretty useless.  All links from this point on will be to
+	 * individual files, so we simply reset the difftype in the query
+	 * here to avoid propagating DIFF_STATONLY to the individual files.
+	 */
+	if (difftype == DIFF_STATONLY)
+		ctx.qry.difftype = ctx.cfg.difftype;
+
+	cgit_print_diffstat(old_rev_oid, new_rev_oid, prefix);
+
+	if (difftype == DIFF_STATONLY)
+		return;
+
+	if (use_ssdiff) {
+		html("<table summary='ssdiff' class='ssdiff'>");
+	} else {
+		html("<table summary='diff' class='diff'>");
+		html("<tr><td><pre>");
+	}
+	cgit_diff_tree(old_rev_oid, new_rev_oid, filepair_cb, prefix,
+		       ctx.qry.ignorews);
+	if (!use_ssdiff)
+		html("</pre></td></tr>");
+	html("</table>");
+
+	if (show_ctrls)
+		cgit_print_layout_end();
+}
diff --git a/www/git.causal.agency/cgit/ui-diff.h b/www/git.causal.agency/cgit/ui-diff.h
new file mode 100644
index 00000000..39264a16
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-diff.h
@@ -0,0 +1,15 @@
+#ifndef UI_DIFF_H
+#define UI_DIFF_H
+
+extern void cgit_print_diff_ctrls(void);
+
+extern void cgit_print_diff(const char *new_hex, const char *old_hex,
+			    const char *prefix, int show_ctrls, int raw);
+
+extern struct diff_filespec *cgit_get_current_old_file(void);
+extern struct diff_filespec *cgit_get_current_new_file(void);
+
+extern struct object_id old_rev_oid[1];
+extern struct object_id new_rev_oid[1];
+
+#endif /* UI_DIFF_H */
diff --git a/www/git.causal.agency/cgit/ui-log.c b/www/git.causal.agency/cgit/ui-log.c
new file mode 100644
index 00000000..b443ca73
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-log.c
@@ -0,0 +1,555 @@
+/* ui-log.c: functions for log output
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-log.h"
+#include "html.h"
+#include "ui-shared.h"
+#include "strvec.h"
+
+static int files, add_lines, rem_lines, lines_counted;
+
+/*
+ * The list of available column colors in the commit graph.
+ */
+static const char *column_colors_html[] = {
+	"<span class='column1'>",
+	"<span class='column2'>",
+	"<span class='column3'>",
+	"<span class='column4'>",
+	"<span class='column5'>",
+	"<span class='column6'>",
+	"</span>",
+};
+
+#define COLUMN_COLORS_HTML_MAX (ARRAY_SIZE(column_colors_html) - 1)
+
+static void count_lines(char *line, int size)
+{
+	if (size <= 0)
+		return;
+
+	if (line[0] == '+')
+		add_lines++;
+
+	else if (line[0] == '-')
+		rem_lines++;
+}
+
+static void inspect_files(struct diff_filepair *pair)
+{
+	unsigned long old_size = 0;
+	unsigned long new_size = 0;
+	int binary = 0;
+
+	files++;
+	if (ctx.repo->enable_log_linecount)
+		cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size,
+				&new_size, &binary, 0, ctx.qry.ignorews,
+				count_lines);
+}
+
+void show_commit_decorations(struct commit *commit)
+{
+	const struct name_decoration *deco;
+	static char buf[1024];
+
+	buf[sizeof(buf) - 1] = 0;
+	deco = get_name_decoration(&commit->object);
+	if (!deco)
+		return;
+	html("<span class='decoration'>");
+	while (deco) {
+		struct object_id oid_tag, peeled;
+		int is_annotated = 0;
+
+		strlcpy(buf, prettify_refname(deco->name), sizeof(buf));
+		switch(deco->type) {
+		case DECORATION_NONE:
+			/* If the git-core doesn't recognize it,
+			 * don't display anything. */
+			break;
+		case DECORATION_REF_LOCAL:
+			html(" ");
+			cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
+				ctx.qry.vpath, 0, NULL, NULL,
+				ctx.qry.showmsg, 0);
+			break;
+		case DECORATION_REF_TAG:
+			html(" ");
+			if (!read_ref(deco->name, &oid_tag) && !peel_iterated_oid(&oid_tag, &peeled))
+				is_annotated = !oideq(&oid_tag, &peeled);
+			cgit_tag_link(buf, NULL, is_annotated ? "tag-annotated-deco" : "tag-deco", buf);
+			break;
+		case DECORATION_REF_REMOTE:
+			if (!ctx.repo->enable_remote_branches)
+				break;
+			html(" ");
+			cgit_log_link(buf, NULL, "remote-deco", NULL,
+				oid_to_hex(&commit->object.oid),
+				ctx.qry.vpath, 0, NULL, NULL,
+				ctx.qry.showmsg, 0);
+			break;
+		default:
+			html(" ");
+			cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
+					oid_to_hex(&commit->object.oid),
+					ctx.qry.vpath);
+			break;
+		}
+		deco = deco->next;
+	}
+	html("</span>");
+}
+
+static void handle_rename(struct diff_filepair *pair)
+{
+	/*
+	 * After we have seen a rename, we generate links to the previous
+	 * name of the file so that commit & diff views get fed the path
+	 * that is correct for the commit they are showing, avoiding the
+	 * need to walk the entire history leading back to every commit we
+	 * show in order detect renames.
+	 */
+	if (0 != strcmp(ctx.qry.vpath, pair->two->path)) {
+		free(ctx.qry.vpath);
+		ctx.qry.vpath = xstrdup(pair->two->path);
+	}
+	inspect_files(pair);
+}
+
+static int show_commit(struct commit *commit, struct rev_info *revs)
+{
+	struct commit_list *parents = commit->parents;
+	struct commit *parent;
+	int found = 0, saved_fmt;
+	struct diff_flags saved_flags = revs->diffopt.flags;
+
+	/* Always show if we're not in "follow" mode with a single file. */
+	if (!ctx.qry.follow)
+		return 1;
+
+	/*
+	 * In "follow" mode, we don't show merges.  This is consistent with
+	 * "git log --follow -- <file>".
+	 */
+	if (parents && parents->next)
+		return 0;
+
+	/*
+	 * If this is the root commit, do what rev_info tells us.
+	 */
+	if (!parents)
+		return revs->show_root_diff;
+
+	/* When we get here we have precisely one parent. */
+	parent = parents->item;
+	/* If we can't parse the commit, let print_commit() report an error. */
+	if (parse_commit(parent))
+		return 1;
+
+	files = 0;
+	add_lines = 0;
+	rem_lines = 0;
+
+	revs->diffopt.flags.recursive = 1;
+	diff_tree_oid(get_commit_tree_oid(parent),
+		      get_commit_tree_oid(commit),
+		      "", &revs->diffopt);
+	diffcore_std(&revs->diffopt);
+
+	found = !diff_queue_is_empty();
+	saved_fmt = revs->diffopt.output_format;
+	revs->diffopt.output_format = DIFF_FORMAT_CALLBACK;
+	revs->diffopt.format_callback = cgit_diff_tree_cb;
+	revs->diffopt.format_callback_data = handle_rename;
+	diff_flush(&revs->diffopt);
+	revs->diffopt.output_format = saved_fmt;
+	revs->diffopt.flags = saved_flags;
+
+	lines_counted = 1;
+	return found;
+}
+
+static void print_commit(struct commit *commit, struct rev_info *revs)
+{
+	struct commitinfo *info;
+	int columns = revs->graph ? 4 : 3;
+	struct strbuf graphbuf = STRBUF_INIT;
+	struct strbuf msgbuf = STRBUF_INIT;
+
+	if (ctx.repo->enable_log_filecount)
+		columns++;
+	if (ctx.repo->enable_log_linecount)
+		columns++;
+
+	if (revs->graph) {
+		/* Advance graph until current commit */
+		while (!graph_next_line(revs->graph, &graphbuf)) {
+			/* Print graph segment in otherwise empty table row */
+			html("<tr class='nohover'><td class='commitgraph'>");
+			html(graphbuf.buf);
+			htmlf("</td><td colspan='%d' /></tr>\n", columns);
+			strbuf_setlen(&graphbuf, 0);
+		}
+		/* Current commit's graph segment is now ready in graphbuf */
+	}
+
+	info = cgit_parse_commit(commit);
+	htmlf("<tr%s>", ctx.qry.showmsg ? " class='logheader'" : "");
+
+	if (revs->graph) {
+		/* Print graph segment for current commit */
+		html("<td class='commitgraph'>");
+		html(graphbuf.buf);
+		html("</td>");
+		strbuf_setlen(&graphbuf, 0);
+	}
+	else {
+		html("<td>");
+		cgit_print_age(info->committer_date, info->committer_tz, TM_WEEK * 2);
+		html("</td>");
+	}
+
+	htmlf("<td%s>", ctx.qry.showmsg ? " class='logsubject'" : "");
+	if (ctx.qry.showmsg) {
+		/* line-wrap long commit subjects instead of truncating them */
+		size_t subject_len = strlen(info->subject);
+
+		if (subject_len > ctx.cfg.max_msg_len &&
+		    ctx.cfg.max_msg_len >= 15) {
+			/* symbol for signaling line-wrap (in PAGE_ENCODING) */
+			const char wrap_symbol[] = { ' ', 0xE2, 0x86, 0xB5, 0 };
+			int i = ctx.cfg.max_msg_len - strlen(wrap_symbol);
+
+			/* Rewind i to preceding space character */
+			while (i > 0 && !isspace(info->subject[i]))
+				--i;
+			if (!i) /* Oops, zero spaces. Reset i */
+				i = ctx.cfg.max_msg_len - strlen(wrap_symbol);
+
+			/* add remainder starting at i to msgbuf */
+			strbuf_add(&msgbuf, info->subject + i, subject_len - i);
+			strbuf_trim(&msgbuf);
+			strbuf_add(&msgbuf, "\n\n", 2);
+
+			/* Place wrap_symbol at position i in info->subject */
+			strlcpy(info->subject + i, wrap_symbol, subject_len - i + 1);
+		}
+	}
+	cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
+			 oid_to_hex(&commit->object.oid), ctx.qry.vpath);
+	show_commit_decorations(commit);
+	html("</td><td>");
+	cgit_open_filter(ctx.repo->email_filter, info->author_email, "log");
+	html_txt(info->author);
+	cgit_close_filter(ctx.repo->email_filter);
+
+	if (revs->graph) {
+		html("</td><td>");
+		cgit_print_age(info->committer_date, info->committer_tz, TM_WEEK * 2);
+	}
+
+	if (!lines_counted && (ctx.repo->enable_log_filecount ||
+			       ctx.repo->enable_log_linecount)) {
+		files = 0;
+		add_lines = 0;
+		rem_lines = 0;
+		cgit_diff_commit(commit, inspect_files, ctx.qry.vpath);
+	}
+
+	if (ctx.repo->enable_log_filecount)
+		htmlf("</td><td>%d", files);
+	if (ctx.repo->enable_log_linecount)
+		htmlf("</td><td><span class='deletions'>-%d</span>/"
+			"<span class='insertions'>+%d</span>", rem_lines, add_lines);
+
+	html("</td></tr>\n");
+
+	if ((revs->graph && !graph_is_commit_finished(revs->graph))
+			|| ctx.qry.showmsg) { /* Print a second table row */
+		html("<tr class='nohover-highlight'>");
+
+		if (ctx.qry.showmsg) {
+			/* Concatenate commit message + notes in msgbuf */
+			if (info->msg && *(info->msg)) {
+				strbuf_addstr(&msgbuf, info->msg);
+				strbuf_addch(&msgbuf, '\n');
+			}
+			format_display_notes(&commit->object.oid,
+					     &msgbuf, PAGE_ENCODING, 0);
+			strbuf_addch(&msgbuf, '\n');
+			strbuf_ltrim(&msgbuf);
+		}
+
+		if (revs->graph) {
+			int lines = 0;
+
+			/* Calculate graph padding */
+			if (ctx.qry.showmsg) {
+				/* Count #lines in commit message + notes */
+				const char *p = msgbuf.buf;
+				lines = 1;
+				while ((p = strchr(p, '\n'))) {
+					p++;
+					lines++;
+				}
+			}
+
+			/* Print graph padding */
+			html("<td class='commitgraph'>");
+			while (lines > 0 || !graph_is_commit_finished(revs->graph)) {
+				if (graphbuf.len)
+					html("\n");
+				strbuf_setlen(&graphbuf, 0);
+				graph_next_line(revs->graph, &graphbuf);
+				html(graphbuf.buf);
+				lines--;
+			}
+			html("</td>\n");
+		}
+		else
+			html("<td/>"); /* Empty 'Age' column */
+
+		/* Print msgbuf into remainder of table row */
+		htmlf("<td colspan='%d'%s>\n", columns - (revs->graph ? 1 : 0),
+			ctx.qry.showmsg ? " class='logmsg'" : "");
+		html_txt(msgbuf.buf);
+		html("</td></tr>\n");
+	}
+
+	strbuf_release(&msgbuf);
+	strbuf_release(&graphbuf);
+	cgit_free_commitinfo(info);
+}
+
+static const char *disambiguate_ref(const char *ref, int *must_free_result)
+{
+	struct object_id oid;
+	struct strbuf longref = STRBUF_INIT;
+
+	strbuf_addf(&longref, "refs/heads/%s", ref);
+	if (get_oid(longref.buf, &oid) == 0) {
+		*must_free_result = 1;
+		return strbuf_detach(&longref, NULL);
+	}
+
+	*must_free_result = 0;
+	strbuf_release(&longref);
+	return ref;
+}
+
+static char *next_token(char **src)
+{
+	char *result;
+
+	if (!src || !*src)
+		return NULL;
+	while (isspace(**src))
+		(*src)++;
+	if (!**src)
+		return NULL;
+	result = *src;
+	while (**src) {
+		if (isspace(**src)) {
+			**src = '\0';
+			(*src)++;
+			break;
+		}
+		(*src)++;
+	}
+	return result;
+}
+
+void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
+		    const char *path, int pager, int commit_graph, int commit_sort)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strvec rev_argv = STRVEC_INIT;
+	int i, columns = commit_graph ? 4 : 3;
+	int must_free_tip = 0;
+
+	/* rev_argv.argv[0] will be ignored by setup_revisions */
+	strvec_push(&rev_argv, "log_rev_setup");
+
+	if (!tip)
+		tip = ctx.qry.head;
+	tip = disambiguate_ref(tip, &must_free_tip);
+	strvec_push(&rev_argv, tip);
+
+	if (grep && pattern && *pattern) {
+		pattern = xstrdup(pattern);
+		if (!strcmp(grep, "grep") || !strcmp(grep, "author") ||
+		    !strcmp(grep, "committer")) {
+			strvec_pushf(&rev_argv, "--%s=%s", grep, pattern);
+		} else if (!strcmp(grep, "range")) {
+			char *arg;
+			/* Split the pattern at whitespace and add each token
+			 * as a revision expression. Do not accept other
+			 * rev-list options. Also, replace the previously
+			 * pushed tip (it's no longer relevant).
+			 */
+			strvec_pop(&rev_argv);
+			while ((arg = next_token(&pattern))) {
+				if (*arg == '-') {
+					fprintf(stderr, "Bad range expr: %s\n",
+						arg);
+					break;
+				}
+				strvec_push(&rev_argv, arg);
+			}
+		}
+	}
+
+	if (!path || !ctx.cfg.enable_follow_links) {
+		/*
+		 * If we don't have a path, "follow" is a no-op so make sure
+		 * the variable is set to false to avoid needing to check
+		 * both this and whether we have a path everywhere.
+		 */
+		ctx.qry.follow = 0;
+	}
+
+	if (commit_graph && !ctx.qry.follow) {
+		strvec_push(&rev_argv, "--graph");
+		strvec_push(&rev_argv, "--color");
+		graph_set_column_colors(column_colors_html,
+					COLUMN_COLORS_HTML_MAX);
+	}
+
+	if (commit_sort == 1)
+		strvec_push(&rev_argv, "--date-order");
+	else if (commit_sort == 2)
+		strvec_push(&rev_argv, "--topo-order");
+
+	if (path && ctx.qry.follow)
+		strvec_push(&rev_argv, "--follow");
+	strvec_push(&rev_argv, "--");
+	if (path)
+		strvec_push(&rev_argv, path);
+
+	init_revisions(&rev, NULL);
+	rev.abbrev = DEFAULT_ABBREV;
+	rev.commit_format = CMIT_FMT_DEFAULT;
+	rev.verbose_header = 1;
+	rev.show_root_diff = 0;
+	rev.ignore_missing = 1;
+	rev.simplify_history = 1;
+	setup_revisions(rev_argv.nr, rev_argv.v, &rev, NULL);
+	load_ref_decorations(NULL, DECORATE_FULL_REFS);
+	rev.show_decorations = 1;
+	rev.grep_filter.ignore_case = 1;
+
+	rev.diffopt.detect_rename = 1;
+	rev.diffopt.rename_limit = ctx.cfg.renamelimit;
+	if (ctx.qry.ignorews)
+		DIFF_XDL_SET(&rev.diffopt, IGNORE_WHITESPACE);
+
+	compile_grep_patterns(&rev.grep_filter);
+	prepare_revision_walk(&rev);
+
+	if (pager) {
+		cgit_print_layout_start();
+		html("<table class='list nowrap'>");
+	}
+
+	html("<tr class='nohover'>");
+	if (commit_graph)
+		html("<th></th>");
+	else
+		html("<th class='left'>Age</th>");
+	html("<th class='left'>Commit message");
+	if (pager) {
+		html(" (");
+		cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL,
+			      NULL, ctx.qry.head, ctx.qry.oid,
+			      ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep,
+			      ctx.qry.search, ctx.qry.showmsg ? 0 : 1,
+			      ctx.qry.follow);
+		html(")");
+	}
+	html("</th><th class='left'>Author</th>");
+	if (rev.graph)
+		html("<th class='left'>Age</th>");
+	if (ctx.repo->enable_log_filecount) {
+		html("<th class='left'>Files</th>");
+		columns++;
+	}
+	if (ctx.repo->enable_log_linecount) {
+		html("<th class='left'>Lines</th>");
+		columns++;
+	}
+	html("</tr>\n");
+
+	if (ofs<0)
+		ofs = 0;
+
+	for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; /* nop */) {
+		if (show_commit(commit, &rev))
+			i++;
+		free_commit_buffer(the_repository->parsed_objects, commit);
+		free_commit_list(commit->parents);
+		commit->parents = NULL;
+	}
+
+	for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; /* nop */) {
+		/*
+		 * In "follow" mode, we must count the files and lines the
+		 * first time we invoke diff on a given commit, and we need
+		 * to do that to see if the commit touches the path we care
+		 * about, so we do it in show_commit.  Hence we must clear
+		 * lines_counted here.
+		 *
+		 * This has the side effect of avoiding running diff twice
+		 * when we are both following renames and showing file
+		 * and/or line counts.
+		 */
+		lines_counted = 0;
+		if (show_commit(commit, &rev)) {
+			i++;
+			print_commit(commit, &rev);
+		}
+		free_commit_buffer(the_repository->parsed_objects, commit);
+		free_commit_list(commit->parents);
+		commit->parents = NULL;
+	}
+	if (pager) {
+		html("</table><ul class='pager'>");
+		if (ofs > 0) {
+			html("<li>");
+			cgit_log_link("[prev]", NULL, NULL, ctx.qry.head,
+				      ctx.qry.oid, ctx.qry.vpath,
+				      ofs - cnt, ctx.qry.grep,
+				      ctx.qry.search, ctx.qry.showmsg,
+				      ctx.qry.follow);
+			html("</li>");
+		}
+		if ((commit = get_revision(&rev)) != NULL) {
+			html("<li>");
+			cgit_log_link("[next]", NULL, NULL, ctx.qry.head,
+				      ctx.qry.oid, ctx.qry.vpath,
+				      ofs + cnt, ctx.qry.grep,
+				      ctx.qry.search, ctx.qry.showmsg,
+				      ctx.qry.follow);
+			html("</li>");
+		}
+		html("</ul>");
+		cgit_print_layout_end();
+	} else if ((commit = get_revision(&rev)) != NULL) {
+		htmlf("<tr class='nohover'><td colspan='%d'>", columns);
+		cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL,
+			      ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg,
+			      ctx.qry.follow);
+		html("</td></tr>\n");
+	}
+
+	/* If we allocated tip then it is safe to cast away const. */
+	if (must_free_tip)
+		free((char*) tip);
+}
diff --git a/www/git.causal.agency/cgit/ui-log.h b/www/git.causal.agency/cgit/ui-log.h
new file mode 100644
index 00000000..325607cd
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-log.h
@@ -0,0 +1,9 @@
+#ifndef UI_LOG_H
+#define UI_LOG_H
+
+extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
+			   char *pattern, const char *path, int pager,
+			   int commit_graph, int commit_sort);
+extern void show_commit_decorations(struct commit *commit);
+
+#endif /* UI_LOG_H */
diff --git a/www/git.causal.agency/cgit/ui-patch.c b/www/git.causal.agency/cgit/ui-patch.c
new file mode 100644
index 00000000..4ac03cbe
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-patch.c
@@ -0,0 +1,98 @@
+/* ui-patch.c: generate patch view
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-patch.h"
+#include "html.h"
+#include "ui-shared.h"
+
+/* two commit hashes with two dots in between and termination */
+#define REV_RANGE_LEN 2 * GIT_MAX_HEXSZ + 3
+
+void cgit_print_patch(const char *new_rev, const char *old_rev,
+		      const char *prefix)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct object_id new_rev_oid, old_rev_oid;
+	char rev_range[REV_RANGE_LEN];
+	const char *rev_argv[] = { NULL, "--reverse", "--format=email", rev_range, "--", prefix, NULL };
+	int rev_argc = ARRAY_SIZE(rev_argv) - 1;
+	char *patchname;
+
+	if (!prefix)
+		rev_argc--;
+
+	if (!new_rev)
+		new_rev = ctx.qry.head;
+
+	if (get_oid(new_rev, &new_rev_oid)) {
+		cgit_print_error_page(404, "Not found",
+				"Bad object id: %s", new_rev);
+		return;
+	}
+	commit = lookup_commit_reference(the_repository, &new_rev_oid);
+	if (!commit) {
+		cgit_print_error_page(404, "Not found",
+				"Bad commit reference: %s", new_rev);
+		return;
+	}
+
+	if (old_rev) {
+		if (get_oid(old_rev, &old_rev_oid)) {
+			cgit_print_error_page(404, "Not found",
+					"Bad object id: %s", old_rev);
+			return;
+		}
+		if (!lookup_commit_reference(the_repository, &old_rev_oid)) {
+			cgit_print_error_page(404, "Not found",
+					"Bad commit reference: %s", old_rev);
+			return;
+		}
+	} else if (commit->parents && commit->parents->item) {
+		oidcpy(&old_rev_oid, &commit->parents->item->object.oid);
+	} else {
+		oidclr(&old_rev_oid);
+	}
+
+	if (is_null_oid(&old_rev_oid)) {
+		memcpy(rev_range, oid_to_hex(&new_rev_oid), the_hash_algo->hexsz + 1);
+	} else {
+		xsnprintf(rev_range, REV_RANGE_LEN, "%s..%s", oid_to_hex(&old_rev_oid),
+			oid_to_hex(&new_rev_oid));
+	}
+
+	patchname = fmt("%s.patch", rev_range);
+	ctx.page.mimetype = "text/plain";
+	ctx.page.filename = patchname;
+	cgit_print_http_headers();
+
+	if (ctx.cfg.noplainemail) {
+		rev_argv[2] = "--format=format:From %H Mon Sep 17 00:00:00 "
+			      "2001%nFrom: %an%nDate: %aD%n%w(78,0,1)Subject: "
+			      "%s%n%n%w(0)%b";
+	}
+
+	init_revisions(&rev, NULL);
+	rev.abbrev = DEFAULT_ABBREV;
+	rev.verbose_header = 1;
+	rev.diff = 1;
+	rev.show_root_diff = 1;
+	rev.max_parents = 1;
+	rev.diffopt.output_format |= DIFF_FORMAT_DIFFSTAT |
+			DIFF_FORMAT_PATCH | DIFF_FORMAT_SUMMARY;
+	if (prefix)
+		rev.diffopt.stat_sep = fmt("(limited to '%s')\n\n", prefix);
+	setup_revisions(rev_argc, rev_argv, &rev, NULL);
+	prepare_revision_walk(&rev);
+
+	while ((commit = get_revision(&rev)) != NULL) {
+		log_tree_commit(&rev, commit);
+		printf("-- \ncgit %s\n\n", cgit_version);
+	}
+}
diff --git a/www/git.causal.agency/cgit/ui-patch.h b/www/git.causal.agency/cgit/ui-patch.h
new file mode 100644
index 00000000..7a6cacd5
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-patch.h
@@ -0,0 +1,7 @@
+#ifndef UI_PATCH_H
+#define UI_PATCH_H
+
+extern void cgit_print_patch(const char *new_rev, const char *old_rev,
+			     const char *prefix);
+
+#endif /* UI_PATCH_H */
diff --git a/www/git.causal.agency/cgit/ui-plain.c b/www/git.causal.agency/cgit/ui-plain.c
new file mode 100644
index 00000000..65a205fa
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-plain.c
@@ -0,0 +1,207 @@
+/* ui-plain.c: functions for output of plain blobs by path
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-plain.h"
+#include "html.h"
+#include "ui-shared.h"
+
+struct walk_tree_context {
+	int match_baselen;
+	int match;
+};
+
+static int print_object(const struct object_id *oid, const char *path)
+{
+	enum object_type type;
+	char *buf, *mimetype;
+	unsigned long size;
+
+	type = oid_object_info(the_repository, oid, &size);
+	if (type == OBJ_BAD) {
+		cgit_print_error_page(404, "Not found", "Not found");
+		return 0;
+	}
+
+	buf = read_object_file(oid, &type, &size);
+	if (!buf) {
+		cgit_print_error_page(404, "Not found", "Not found");
+		return 0;
+	}
+
+	mimetype = get_mimetype_for_filename(path);
+	ctx.page.mimetype = mimetype;
+
+	if (!ctx.repo->enable_html_serving) {
+		html("X-Content-Type-Options: nosniff\n");
+		html("Content-Security-Policy: default-src 'none'\n");
+		if (mimetype) {
+			/* Built-in white list allows PDF and everything that isn't text/ and application/ */
+			if ((!strncmp(mimetype, "text/", 5) || !strncmp(mimetype, "application/", 12)) && strcmp(mimetype, "application/pdf"))
+				ctx.page.mimetype = NULL;
+		}
+	}
+
+	if (!ctx.page.mimetype) {
+		if (buffer_is_binary(buf, size)) {
+			ctx.page.mimetype = "application/octet-stream";
+			ctx.page.charset = NULL;
+		} else {
+			ctx.page.mimetype = "text/plain";
+		}
+	}
+	ctx.page.filename = path;
+	ctx.page.size = size;
+	ctx.page.etag = oid_to_hex(oid);
+	cgit_print_http_headers();
+	html_raw(buf, size);
+	free(mimetype);
+	free(buf);
+	return 1;
+}
+
+static char *buildpath(const char *base, int baselen, const char *path)
+{
+	if (path[0])
+		return fmtalloc("%.*s%s/", baselen, base, path);
+	else
+		return fmtalloc("%.*s/", baselen, base);
+}
+
+static void print_dir(const struct object_id *oid, const char *base,
+		      int baselen, const char *path)
+{
+	char *fullpath, *slash;
+	size_t len;
+
+	fullpath = buildpath(base, baselen, path);
+	slash = (fullpath[0] == '/' ? "" : "/");
+	ctx.page.etag = oid_to_hex(oid);
+	cgit_print_http_headers();
+	htmlf("<html><head><title>%s", slash);
+	html_txt(fullpath);
+	htmlf("</title></head>\n<body>\n<h2>%s", slash);
+	html_txt(fullpath);
+	html("</h2>\n<ul>\n");
+	len = strlen(fullpath);
+	if (len > 1) {
+		fullpath[len - 1] = 0;
+		slash = strrchr(fullpath, '/');
+		if (slash)
+			*(slash + 1) = 0;
+		else {
+			free(fullpath);
+			fullpath = NULL;
+		}
+		html("<li>");
+		cgit_plain_link("../", NULL, NULL, ctx.qry.head, ctx.qry.oid,
+				fullpath);
+		html("</li>\n");
+	}
+	free(fullpath);
+}
+
+static void print_dir_entry(const struct object_id *oid, const char *base,
+			    int baselen, const char *path, unsigned mode)
+{
+	char *fullpath;
+
+	fullpath = buildpath(base, baselen, path);
+	if (!S_ISDIR(mode) && !S_ISGITLINK(mode))
+		fullpath[strlen(fullpath) - 1] = 0;
+	html("  <li>");
+	if (S_ISGITLINK(mode)) {
+		cgit_submodule_link(NULL, fullpath, oid_to_hex(oid));
+	} else
+		cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.oid,
+				fullpath);
+	html("</li>\n");
+	free(fullpath);
+}
+
+static void print_dir_tail(void)
+{
+	html(" </ul>\n</body></html>\n");
+}
+
+static int walk_tree(const struct object_id *oid, struct strbuf *base,
+		const char *pathname, unsigned mode, void *cbdata)
+{
+	struct walk_tree_context *walk_tree_ctx = cbdata;
+
+	if (base->len == walk_tree_ctx->match_baselen) {
+		if (S_ISREG(mode) || S_ISLNK(mode)) {
+			if (print_object(oid, pathname))
+				walk_tree_ctx->match = 1;
+		} else if (S_ISDIR(mode)) {
+			print_dir(oid, base->buf, base->len, pathname);
+			walk_tree_ctx->match = 2;
+			return READ_TREE_RECURSIVE;
+		}
+	} else if (base->len < INT_MAX && (int)base->len > walk_tree_ctx->match_baselen) {
+		print_dir_entry(oid, base->buf, base->len, pathname, mode);
+		walk_tree_ctx->match = 2;
+	} else if (S_ISDIR(mode)) {
+		return READ_TREE_RECURSIVE;
+	}
+
+	return 0;
+}
+
+static int basedir_len(const char *path)
+{
+	char *p = strrchr(path, '/');
+	if (p)
+		return p - path + 1;
+	return 0;
+}
+
+void cgit_print_plain(void)
+{
+	const char *rev = ctx.qry.oid;
+	struct object_id oid;
+	struct commit *commit;
+	struct pathspec_item path_items = {
+		.match = ctx.qry.path,
+		.len = ctx.qry.path ? strlen(ctx.qry.path) : 0
+	};
+	struct pathspec paths = {
+		.nr = 1,
+		.items = &path_items
+	};
+	struct walk_tree_context walk_tree_ctx = {
+		.match = 0
+	};
+
+	if (!rev)
+		rev = ctx.qry.head;
+
+	if (get_oid(rev, &oid)) {
+		cgit_print_error_page(404, "Not found", "Not found");
+		return;
+	}
+	commit = lookup_commit_reference(the_repository, &oid);
+	if (!commit || parse_commit(commit)) {
+		cgit_print_error_page(404, "Not found", "Not found");
+		return;
+	}
+	if (!path_items.match) {
+		path_items.match = "";
+		walk_tree_ctx.match_baselen = -1;
+		print_dir(get_commit_tree_oid(commit), "", 0, "");
+		walk_tree_ctx.match = 2;
+	}
+	else
+		walk_tree_ctx.match_baselen = basedir_len(path_items.match);
+	read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
+		  &paths, walk_tree, &walk_tree_ctx);
+	if (!walk_tree_ctx.match)
+		cgit_print_error_page(404, "Not found", "Not found");
+	else if (walk_tree_ctx.match == 2)
+		print_dir_tail();
+}
diff --git a/www/git.causal.agency/cgit/ui-plain.h b/www/git.causal.agency/cgit/ui-plain.h
new file mode 100644
index 00000000..5bff07b8
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-plain.h
@@ -0,0 +1,6 @@
+#ifndef UI_PLAIN_H
+#define UI_PLAIN_H
+
+extern void cgit_print_plain(void);
+
+#endif /* UI_PLAIN_H */
diff --git a/www/git.causal.agency/cgit/ui-refs.c b/www/git.causal.agency/cgit/ui-refs.c
new file mode 100644
index 00000000..456f610d
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-refs.c
@@ -0,0 +1,219 @@
+/* ui-refs.c: browse symbolic refs
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-refs.h"
+#include "html.h"
+#include "ui-shared.h"
+
+static inline int cmp_age(int age1, int age2)
+{
+	/* age1 and age2 are assumed to be non-negative */
+	return age2 - age1;
+}
+
+static int cmp_ref_name(const void *a, const void *b)
+{
+	struct refinfo *r1 = *(struct refinfo **)a;
+	struct refinfo *r2 = *(struct refinfo **)b;
+
+	return strcmp(r1->refname, r2->refname);
+}
+
+static int cmp_branch_age(const void *a, const void *b)
+{
+	struct refinfo *r1 = *(struct refinfo **)a;
+	struct refinfo *r2 = *(struct refinfo **)b;
+
+	return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
+}
+
+static int get_ref_age(struct refinfo *ref)
+{
+	if (!ref->object)
+		return 0;
+	switch (ref->object->type) {
+	case OBJ_TAG:
+		return ref->tag ? ref->tag->tagger_date : 0;
+	case OBJ_COMMIT:
+		return ref->commit ? ref->commit->committer_date : 0;
+	}
+	return 0;
+}
+
+static int cmp_tag_age(const void *a, const void *b)
+{
+	struct refinfo *r1 = *(struct refinfo **)a;
+	struct refinfo *r2 = *(struct refinfo **)b;
+
+	return cmp_age(get_ref_age(r1), get_ref_age(r2));
+}
+
+static int print_branch(struct refinfo *ref)
+{
+	struct commitinfo *info = ref->commit;
+	char *name = (char *)ref->refname;
+
+	if (!info)
+		return 1;
+	html("<tr><td>");
+	cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL,
+		      ctx.qry.showmsg, 0);
+	html("</td><td>");
+
+	if (ref->object->type == OBJ_COMMIT) {
+		cgit_commit_link(info->subject, NULL, NULL, name, NULL, NULL);
+		html("</td><td>");
+		cgit_open_filter(ctx.repo->email_filter, info->author_email, "refs");
+		html_txt(info->author);
+		cgit_close_filter(ctx.repo->email_filter);
+		html("</td><td colspan='2'>");
+		cgit_print_age(info->committer_date, info->committer_tz, -1);
+	} else {
+		html("</td><td></td><td>");
+		cgit_object_link(ref->object);
+	}
+	html("</td></tr>\n");
+	return 0;
+}
+
+static void print_tag_header(void)
+{
+	html("<tr class='nohover'><th class='left'>Tag</th>"
+	     "<th class='left'>Download</th>"
+	     "<th class='left'>Author</th>"
+	     "<th class='left' colspan='2'>Age</th></tr>\n");
+}
+
+static int print_tag(struct refinfo *ref)
+{
+	struct tag *tag = NULL;
+	struct taginfo *info = NULL;
+	char *name = (char *)ref->refname;
+	struct object *obj = ref->object;
+
+	if (obj->type == OBJ_TAG) {
+		tag = (struct tag *)obj;
+		obj = tag->tagged;
+		info = ref->tag;
+		if (!info)
+			return 1;
+	}
+
+	html("<tr><td>");
+	cgit_tag_link(name, NULL, NULL, name);
+	html("</td><td>");
+	if (ctx.repo->snapshots && (obj->type == OBJ_COMMIT))
+		cgit_print_snapshot_links(ctx.repo, name, "&nbsp;&nbsp;");
+	else
+		cgit_object_link(obj);
+	html("</td><td>");
+	if (info) {
+		if (info->tagger) {
+			cgit_open_filter(ctx.repo->email_filter, info->tagger_email, "refs");
+			html_txt(info->tagger);
+			cgit_close_filter(ctx.repo->email_filter);
+		}
+	} else if (ref->object->type == OBJ_COMMIT) {
+		cgit_open_filter(ctx.repo->email_filter, ref->commit->author_email, "refs");
+		html_txt(ref->commit->author);
+		cgit_close_filter(ctx.repo->email_filter);
+	}
+	html("</td><td colspan='2'>");
+	if (info) {
+		if (info->tagger_date > 0)
+			cgit_print_age(info->tagger_date, info->tagger_tz, -1);
+	} else if (ref->object->type == OBJ_COMMIT) {
+		cgit_print_age(ref->commit->commit->date, 0, -1);
+	}
+	html("</td></tr>\n");
+
+	return 0;
+}
+
+static void print_refs_link(const char *path)
+{
+	html("<tr class='nohover'><td colspan='5'>");
+	cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path);
+	html("</td></tr>");
+}
+
+void cgit_print_branches(int maxcount)
+{
+	struct reflist list;
+	int i;
+
+	html("<tr class='nohover'><th class='left'>Branch</th>"
+	     "<th class='left'>Commit message</th>"
+	     "<th class='left'>Author</th>"
+	     "<th class='left' colspan='2'>Age</th></tr>\n");
+
+	list.refs = NULL;
+	list.alloc = list.count = 0;
+	for_each_branch_ref(cgit_refs_cb, &list);
+	if (ctx.repo->enable_remote_branches)
+		for_each_remote_ref(cgit_refs_cb, &list);
+
+	if (maxcount == 0 || maxcount > list.count)
+		maxcount = list.count;
+
+	qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
+	if (ctx.repo->branch_sort == 0)
+		qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name);
+
+	for (i = 0; i < maxcount; i++)
+		print_branch(list.refs[i]);
+
+	if (maxcount < list.count)
+		print_refs_link("heads");
+
+	cgit_free_reflist_inner(&list);
+}
+
+void cgit_print_tags(int maxcount)
+{
+	struct reflist list;
+	int i;
+
+	list.refs = NULL;
+	list.alloc = list.count = 0;
+	for_each_tag_ref(cgit_refs_cb, &list);
+	if (list.count == 0)
+		return;
+	qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
+	if (!maxcount)
+		maxcount = list.count;
+	else if (maxcount > list.count)
+		maxcount = list.count;
+	print_tag_header();
+	for (i = 0; i < maxcount; i++)
+		print_tag(list.refs[i]);
+
+	if (maxcount < list.count)
+		print_refs_link("tags");
+
+	cgit_free_reflist_inner(&list);
+}
+
+void cgit_print_refs(void)
+{
+	cgit_print_layout_start();
+	html("<table class='list nowrap'>");
+
+	if (ctx.qry.path && starts_with(ctx.qry.path, "heads"))
+		cgit_print_branches(0);
+	else if (ctx.qry.path && starts_with(ctx.qry.path, "tags"))
+		cgit_print_tags(0);
+	else {
+		cgit_print_branches(0);
+		html("<tr class='nohover'><td colspan='5'>&nbsp;</td></tr>");
+		cgit_print_tags(0);
+	}
+	html("</table>");
+	cgit_print_layout_end();
+}
diff --git a/www/git.causal.agency/cgit/ui-refs.h b/www/git.causal.agency/cgit/ui-refs.h
new file mode 100644
index 00000000..1d4a54a2
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-refs.h
@@ -0,0 +1,8 @@
+#ifndef UI_REFS_H
+#define UI_REFS_H
+
+extern void cgit_print_branches(int maxcount);
+extern void cgit_print_tags(int maxcount);
+extern void cgit_print_refs(void);
+
+#endif /* UI_REFS_H */
diff --git a/www/git.causal.agency/cgit/ui-repolist.c b/www/git.causal.agency/cgit/ui-repolist.c
new file mode 100644
index 00000000..97b11c5f
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-repolist.c
@@ -0,0 +1,381 @@
+/* ui-repolist.c: functions for generating the repolist page
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-repolist.h"
+#include "html.h"
+#include "ui-shared.h"
+
+static time_t read_agefile(const char *path)
+{
+	time_t result;
+	size_t size;
+	char *buf = NULL;
+	struct strbuf date_buf = STRBUF_INIT;
+
+	if (readfile(path, &buf, &size)) {
+		free(buf);
+		return 0;
+	}
+
+	if (parse_date(buf, &date_buf) == 0)
+		result = strtoul(date_buf.buf, NULL, 10);
+	else
+		result = 0;
+	free(buf);
+	strbuf_release(&date_buf);
+	return result;
+}
+
+static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime)
+{
+	struct strbuf path = STRBUF_INIT;
+	struct stat s;
+	struct cgit_repo *r = (struct cgit_repo *)repo;
+
+	if (repo->mtime != -1) {
+		*mtime = repo->mtime;
+		return 1;
+	}
+	strbuf_addf(&path, "%s/%s", repo->path, ctx.cfg.agefile);
+	if (stat(path.buf, &s) == 0) {
+		*mtime = read_agefile(path.buf);
+		if (*mtime) {
+			r->mtime = *mtime;
+			goto end;
+		}
+	}
+
+	strbuf_reset(&path);
+	strbuf_addf(&path, "%s/refs/heads/%s", repo->path,
+		    repo->defbranch ? repo->defbranch : "master");
+	if (stat(path.buf, &s) == 0) {
+		*mtime = s.st_mtime;
+		r->mtime = *mtime;
+		goto end;
+	}
+
+	strbuf_reset(&path);
+	strbuf_addf(&path, "%s/%s", repo->path, "packed-refs");
+	if (stat(path.buf, &s) == 0) {
+		*mtime = s.st_mtime;
+		r->mtime = *mtime;
+		goto end;
+	}
+
+	*mtime = 0;
+	r->mtime = *mtime;
+end:
+	strbuf_release(&path);
+	return (r->mtime != 0);
+}
+
+static void print_modtime(struct cgit_repo *repo)
+{
+	time_t t;
+	if (get_repo_modtime(repo, &t))
+		cgit_print_age(t, 0, -1);
+}
+
+static int is_match(struct cgit_repo *repo)
+{
+	if (!ctx.qry.search)
+		return 1;
+	if (repo->url && strcasestr(repo->url, ctx.qry.search))
+		return 1;
+	if (repo->name && strcasestr(repo->name, ctx.qry.search))
+		return 1;
+	if (repo->desc && strcasestr(repo->desc, ctx.qry.search))
+		return 1;
+	if (repo->owner && strcasestr(repo->owner, ctx.qry.search))
+		return 1;
+	return 0;
+}
+
+static int is_in_url(struct cgit_repo *repo)
+{
+	if (!ctx.qry.url)
+		return 1;
+	if (repo->url && starts_with(repo->url, ctx.qry.url))
+		return 1;
+	return 0;
+}
+
+static int is_visible(struct cgit_repo *repo)
+{
+	if (repo->hide || repo->ignore)
+		return 0;
+	if (!(is_match(repo) && is_in_url(repo)))
+		return 0;
+	return 1;
+}
+
+static int any_repos_visible(void)
+{
+	int i;
+
+	for (i = 0; i < cgit_repolist.count; i++) {
+		if (is_visible(&cgit_repolist.repos[i]))
+			return 1;
+	}
+	return 0;
+}
+
+static void print_sort_header(const char *title, const char *sort)
+{
+	char *currenturl = cgit_currenturl();
+	html("<th class='left'><a href='");
+	html_attr(currenturl);
+	htmlf("?s=%s", sort);
+	if (ctx.qry.search) {
+		html("&amp;q=");
+		html_url_arg(ctx.qry.search);
+	}
+	htmlf("'>%s</a></th>", title);
+	free(currenturl);
+}
+
+static void print_header(void)
+{
+	html("<tr class='nohover'>");
+	print_sort_header("Name", "name");
+	print_sort_header("Description", "desc");
+	if (ctx.cfg.enable_index_owner)
+		print_sort_header("Owner", "owner");
+	print_sort_header("Idle", "idle");
+	if (ctx.cfg.enable_index_links)
+		html("<th class='left'>Links</th>");
+	html("</tr>\n");
+}
+
+
+static void print_pager(int items, int pagelen, char *search, char *sort)
+{
+	int i, ofs;
+	char *class = NULL;
+	html("<ul class='pager'>");
+	for (i = 0, ofs = 0; ofs < items; i++, ofs = i * pagelen) {
+		class = (ctx.qry.ofs == ofs) ? "current" : NULL;
+		html("<li>");
+		cgit_index_link(fmt("[%d]", i + 1), fmt("Page %d", i + 1),
+				class, search, sort, ofs, 0);
+		html("</li>");
+	}
+	html("</ul>");
+}
+
+static int cmp(const char *s1, const char *s2)
+{
+	if (s1 && s2) {
+		if (ctx.cfg.case_sensitive_sort)
+			return strcmp(s1, s2);
+		else
+			return strcasecmp(s1, s2);
+	}
+	if (s1 && !s2)
+		return -1;
+	if (s2 && !s1)
+		return 1;
+	return 0;
+}
+
+static int sort_name(const void *a, const void *b)
+{
+	const struct cgit_repo *r1 = a;
+	const struct cgit_repo *r2 = b;
+
+	return cmp(r1->name, r2->name);
+}
+
+static int sort_desc(const void *a, const void *b)
+{
+	const struct cgit_repo *r1 = a;
+	const struct cgit_repo *r2 = b;
+
+	return cmp(r1->desc, r2->desc);
+}
+
+static int sort_owner(const void *a, const void *b)
+{
+	const struct cgit_repo *r1 = a;
+	const struct cgit_repo *r2 = b;
+
+	return cmp(r1->owner, r2->owner);
+}
+
+static int sort_idle(const void *a, const void *b)
+{
+	const struct cgit_repo *r1 = a;
+	const struct cgit_repo *r2 = b;
+	time_t t1, t2;
+
+	t1 = t2 = 0;
+	get_repo_modtime(r1, &t1);
+	get_repo_modtime(r2, &t2);
+	return t2 - t1;
+}
+
+static int sort_section(const void *a, const void *b)
+{
+	const struct cgit_repo *r1 = a;
+	const struct cgit_repo *r2 = b;
+	int result;
+
+	result = cmp(r1->section, r2->section);
+	if (!result) {
+		if (!strcmp(ctx.cfg.repository_sort, "age"))
+			result = sort_idle(r1, r2);
+		if (!result)
+			result = cmp(r1->name, r2->name);
+	}
+	return result;
+}
+
+struct sortcolumn {
+	const char *name;
+	int (*fn)(const void *a, const void *b);
+};
+
+static const struct sortcolumn sortcolumn[] = {
+	{"section", sort_section},
+	{"name", sort_name},
+	{"desc", sort_desc},
+	{"owner", sort_owner},
+	{"idle", sort_idle},
+	{NULL, NULL}
+};
+
+static int sort_repolist(char *field)
+{
+	const struct sortcolumn *column;
+
+	for (column = &sortcolumn[0]; column->name; column++) {
+		if (strcmp(field, column->name))
+			continue;
+		qsort(cgit_repolist.repos, cgit_repolist.count,
+			sizeof(struct cgit_repo), column->fn);
+		return 1;
+	}
+	return 0;
+}
+
+
+void cgit_print_repolist(void)
+{
+	int i, columns = 3, hits = 0, header = 0;
+	char *last_section = NULL;
+	char *section;
+	char *repourl;
+	int sorted = 0;
+
+	if (!any_repos_visible()) {
+		cgit_print_error_page(404, "Not found", "No repositories found");
+		return;
+	}
+
+	if (ctx.cfg.enable_index_links)
+		++columns;
+	if (ctx.cfg.enable_index_owner)
+		++columns;
+
+	ctx.page.title = ctx.cfg.root_title;
+	cgit_print_http_headers();
+	cgit_print_docstart();
+	cgit_print_pageheader();
+
+	if (ctx.qry.sort)
+		sorted = sort_repolist(ctx.qry.sort);
+	else if (ctx.cfg.section_sort)
+		sort_repolist("section");
+
+	html("<table summary='repository list' class='list nowrap'>");
+	for (i = 0; i < cgit_repolist.count; i++) {
+		ctx.repo = &cgit_repolist.repos[i];
+		if (!is_visible(ctx.repo))
+			continue;
+		hits++;
+		if (hits <= ctx.qry.ofs)
+			continue;
+		if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
+			continue;
+		if (!header++)
+			print_header();
+		section = ctx.repo->section;
+		if (section && !strcmp(section, ""))
+			section = NULL;
+		if (!sorted &&
+		    ((last_section == NULL && section != NULL) ||
+		    (last_section != NULL && section == NULL) ||
+		    (last_section != NULL && section != NULL &&
+		     strcmp(section, last_section)))) {
+			htmlf("<tr class='nohover-highlight'><td colspan='%d' class='reposection'>",
+			      columns);
+			html_txt(section);
+			html("</td></tr>");
+			last_section = section;
+		}
+		htmlf("<tr><td class='%s'>",
+		      !sorted && section ? "sublevel-repo" : "toplevel-repo");
+		cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
+		html("</td><td>");
+		repourl = cgit_repourl(ctx.repo->url);
+		html_link_open(repourl, NULL, NULL);
+		free(repourl);
+		if (html_ntxt(ctx.repo->desc, ctx.cfg.max_repodesc_len) < 0)
+			html("...");
+		html_link_close();
+		html("</td><td>");
+		if (ctx.cfg.enable_index_owner) {
+			if (ctx.repo->owner_filter) {
+				cgit_open_filter(ctx.repo->owner_filter);
+				html_txt(ctx.repo->owner);
+				cgit_close_filter(ctx.repo->owner_filter);
+			} else {
+				char *currenturl = cgit_currenturl();
+				html("<a href='");
+				html_attr(currenturl);
+				html("?q=");
+				html_url_arg(ctx.repo->owner);
+				html("'>");
+				html_txt(ctx.repo->owner);
+				html("</a>");
+				free(currenturl);
+			}
+			html("</td><td>");
+		}
+		print_modtime(ctx.repo);
+		html("</td>");
+		if (ctx.cfg.enable_index_links) {
+			html("<td>");
+			cgit_summary_link("summary", NULL, "button", NULL);
+			html(" ");
+			cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
+				      0, NULL, NULL, ctx.qry.showmsg, 0);
+			html(" ");
+			cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
+			html("</td>");
+		}
+		html("</tr>\n");
+	}
+	html("</table>");
+	if (hits > ctx.cfg.max_repo_count)
+		print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search, ctx.qry.sort);
+	cgit_print_docend();
+}
+
+void cgit_print_site_readme(void)
+{
+	cgit_print_layout_start();
+	if (!ctx.cfg.root_readme)
+		goto done;
+	cgit_open_filter(ctx.cfg.about_filter, ctx.cfg.root_readme);
+	html_include(ctx.cfg.root_readme);
+	cgit_close_filter(ctx.cfg.about_filter);
+done:
+	cgit_print_layout_end();
+}
diff --git a/www/git.causal.agency/cgit/ui-repolist.h b/www/git.causal.agency/cgit/ui-repolist.h
new file mode 100644
index 00000000..1b6b3227
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-repolist.h
@@ -0,0 +1,7 @@
+#ifndef UI_REPOLIST_H
+#define UI_REPOLIST_H
+
+extern void cgit_print_repolist(void);
+extern void cgit_print_site_readme(void);
+
+#endif /* UI_REPOLIST_H */
diff --git a/www/git.causal.agency/cgit/ui-shared.c b/www/git.causal.agency/cgit/ui-shared.c
new file mode 100644
index 00000000..dfaf5952
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-shared.c
@@ -0,0 +1,1241 @@
+/* ui-shared.c: common web output functions
+ *
+ * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-shared.h"
+#include "cmd.h"
+#include "html.h"
+#include "version.h"
+
+static const char cgit_doctype[] =
+"<!DOCTYPE html>\n";
+
+static char *http_date(time_t t)
+{
+	static char day[][4] =
+		{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+	static char month[][4] =
+		{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+	struct tm tm;
+	gmtime_r(&t, &tm);
+	return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm.tm_wday],
+		   tm.tm_mday, month[tm.tm_mon], 1900 + tm.tm_year,
+		   tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
+void cgit_print_error(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	cgit_vprint_error(fmt, ap);
+	va_end(ap);
+}
+
+void cgit_vprint_error(const char *fmt, va_list ap)
+{
+	va_list cp;
+	html("<div class='error'>");
+	va_copy(cp, ap);
+	html_vtxtf(fmt, cp);
+	va_end(cp);
+	html("</div>\n");
+}
+
+const char *cgit_httpscheme(void)
+{
+	if (ctx.env.https && !strcmp(ctx.env.https, "on"))
+		return "https://";
+	else
+		return "http://";
+}
+
+char *cgit_hosturl(void)
+{
+	if (ctx.env.http_host)
+		return xstrdup(ctx.env.http_host);
+	if (!ctx.env.server_name)
+		return NULL;
+	if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80)
+		return xstrdup(ctx.env.server_name);
+	return fmtalloc("%s:%s", ctx.env.server_name, ctx.env.server_port);
+}
+
+char *cgit_currenturl(void)
+{
+	const char *root = cgit_rooturl();
+
+	if (!ctx.qry.url)
+		return xstrdup(root);
+	if (root[0] && root[strlen(root) - 1] == '/')
+		return fmtalloc("%s%s", root, ctx.qry.url);
+	return fmtalloc("%s/%s", root, ctx.qry.url);
+}
+
+char *cgit_currentfullurl(void)
+{
+	const char *root = cgit_rooturl();
+	const char *orig_query = ctx.env.query_string ? ctx.env.query_string : "";
+	size_t len = strlen(orig_query);
+	char *query = xmalloc(len + 2), *start_url, *ret;
+
+	/* Remove all url=... parts from query string */
+	memcpy(query + 1, orig_query, len + 1);
+	query[0] = '?';
+	start_url = query;
+	while ((start_url = strstr(start_url, "url=")) != NULL) {
+		if (start_url[-1] == '?' || start_url[-1] == '&') {
+			const char *end_url = strchr(start_url, '&');
+			if (end_url)
+				memmove(start_url, end_url + 1, strlen(end_url));
+			else
+				start_url[0] = '\0';
+		} else
+			++start_url;
+	}
+	if (!query[1])
+		query[0] = '\0';
+
+	if (!ctx.qry.url)
+		ret = fmtalloc("%s%s", root, query);
+	else if (root[0] && root[strlen(root) - 1] == '/')
+		ret = fmtalloc("%s%s%s", root, ctx.qry.url, query);
+	else
+		ret = fmtalloc("%s/%s%s", root, ctx.qry.url, query);
+	free(query);
+	return ret;
+}
+
+const char *cgit_rooturl(void)
+{
+	if (ctx.cfg.virtual_root)
+		return ctx.cfg.virtual_root;
+	else
+		return ctx.cfg.script_name;
+}
+
+const char *cgit_loginurl(void)
+{
+	static const char *login_url;
+	if (!login_url)
+		login_url = fmtalloc("%s?p=login", cgit_rooturl());
+	return login_url;
+}
+
+char *cgit_repourl(const char *reponame)
+{
+	if (ctx.cfg.virtual_root)
+		return fmtalloc("%s%s/", ctx.cfg.virtual_root, reponame);
+	else
+		return fmtalloc("?r=%s", reponame);
+}
+
+char *cgit_fileurl(const char *reponame, const char *pagename,
+		   const char *filename, const char *query)
+{
+	struct strbuf sb = STRBUF_INIT;
+	char *delim;
+
+	if (ctx.cfg.virtual_root) {
+		strbuf_addf(&sb, "%s%s/%s/%s", ctx.cfg.virtual_root, reponame,
+			    pagename, (filename ? filename:""));
+		delim = "?";
+	} else {
+		strbuf_addf(&sb, "?url=%s/%s/%s", reponame, pagename,
+			    (filename ? filename : ""));
+		delim = "&amp;";
+	}
+	if (query)
+		strbuf_addf(&sb, "%s%s", delim, query);
+	return strbuf_detach(&sb, NULL);
+}
+
+char *cgit_pageurl(const char *reponame, const char *pagename,
+		   const char *query)
+{
+	return cgit_fileurl(reponame, pagename, NULL, query);
+}
+
+const char *cgit_repobasename(const char *reponame)
+{
+	/* I assume we don't need to store more than one repo basename */
+	static char rvbuf[1024];
+	int p;
+	const char *rv;
+	size_t len;
+
+	len = strlcpy(rvbuf, reponame, sizeof(rvbuf));
+	if (len >= sizeof(rvbuf))
+		die("cgit_repobasename: truncated repository name '%s'", reponame);
+	p = len - 1;
+	/* strip trailing slashes */
+	while (p && rvbuf[p] == '/')
+		rvbuf[p--] = '\0';
+	/* strip trailing .git */
+	if (p >= 3 && starts_with(&rvbuf[p-3], ".git")) {
+		p -= 3;
+		rvbuf[p--] = '\0';
+	}
+	/* strip more trailing slashes if any */
+	while (p && rvbuf[p] == '/')
+		rvbuf[p--] = '\0';
+	/* find last slash in the remaining string */
+	rv = strrchr(rvbuf, '/');
+	if (rv)
+		return ++rv;
+	return rvbuf;
+}
+
+const char *cgit_snapshot_prefix(const struct cgit_repo *repo)
+{
+	if (repo->snapshot_prefix)
+		return repo->snapshot_prefix;
+
+	return cgit_repobasename(repo->url);
+}
+
+static void site_url(const char *page, const char *search, const char *sort, int ofs, int always_root)
+{
+	char *delim = "?";
+
+	if (always_root || page)
+		html_attr(cgit_rooturl());
+	else {
+		char *currenturl = cgit_currenturl();
+		html_attr(currenturl);
+		free(currenturl);
+	}
+
+	if (page) {
+		htmlf("?p=%s", page);
+		delim = "&amp;";
+	}
+	if (search) {
+		html(delim);
+		html("q=");
+		html_attr(search);
+		delim = "&amp;";
+	}
+	if (sort) {
+		html(delim);
+		html("s=");
+		html_attr(sort);
+		delim = "&amp;";
+	}
+	if (ofs) {
+		html(delim);
+		htmlf("ofs=%d", ofs);
+	}
+}
+
+static void site_link(const char *page, const char *name, const char *title,
+		      const char *class, const char *search, const char *sort, int ofs, int always_root)
+{
+	html("<a");
+	if (title) {
+		html(" title='");
+		html_attr(title);
+		html("'");
+	}
+	if (class) {
+		html(" class='");
+		html_attr(class);
+		html("'");
+	}
+	html(" href='");
+	site_url(page, search, sort, ofs, always_root);
+	html("'>");
+	html_txt(name);
+	html("</a>");
+}
+
+void cgit_index_link(const char *name, const char *title, const char *class,
+		     const char *pattern, const char *sort, int ofs, int always_root)
+{
+	site_link(NULL, name, title, class, pattern, sort, ofs, always_root);
+}
+
+static char *repolink(const char *title, const char *class, const char *page,
+		      const char *head, const char *path)
+{
+	char *delim = "?";
+
+	html("<a");
+	if (title) {
+		html(" title='");
+		html_attr(title);
+		html("'");
+	}
+	if (class) {
+		html(" class='");
+		html_attr(class);
+		html("'");
+	}
+	html(" href='");
+	if (ctx.cfg.virtual_root) {
+		html_url_path(ctx.cfg.virtual_root);
+		html_url_path(ctx.repo->url);
+		if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
+			html("/");
+		if (page) {
+			html_url_path(page);
+			html("/");
+			if (path)
+				html_url_path(path);
+		}
+	} else {
+		html_url_path(ctx.cfg.script_name);
+		html("?url=");
+		html_url_arg(ctx.repo->url);
+		if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
+			html("/");
+		if (page) {
+			html_url_arg(page);
+			html("/");
+			if (path)
+				html_url_arg(path);
+		}
+		delim = "&amp;";
+	}
+	if (head && ctx.repo->defbranch && strcmp(head, ctx.repo->defbranch)) {
+		html(delim);
+		html("h=");
+		html_url_arg(head);
+		delim = "&amp;";
+	}
+	return fmt("%s", delim);
+}
+
+static void reporevlink(const char *page, const char *name, const char *title,
+			const char *class, const char *head, const char *rev,
+			const char *path)
+{
+	char *delim;
+
+	delim = repolink(title, class, page, head, path);
+	if (rev && ctx.qry.head != NULL && strcmp(rev, ctx.qry.head)) {
+		html(delim);
+		html("id=");
+		html_url_arg(rev);
+	}
+	html("'>");
+	html_txt(name);
+	html("</a>");
+}
+
+void cgit_summary_link(const char *name, const char *title, const char *class,
+		       const char *head)
+{
+	reporevlink(NULL, name, title, class, head, NULL, NULL);
+}
+
+void cgit_tag_link(const char *name, const char *title, const char *class,
+		   const char *tag)
+{
+	reporevlink("tag", name, title, class, tag, NULL, NULL);
+}
+
+void cgit_tree_link(const char *name, const char *title, const char *class,
+		    const char *head, const char *rev, const char *path)
+{
+	reporevlink("tree", name, title, class, head, rev, path);
+}
+
+void cgit_plain_link(const char *name, const char *title, const char *class,
+		     const char *head, const char *rev, const char *path)
+{
+	reporevlink("plain", name, title, class, head, rev, path);
+}
+
+void cgit_blame_link(const char *name, const char *title, const char *class,
+		     const char *head, const char *rev, const char *path)
+{
+	reporevlink("blame", name, title, class, head, rev, path);
+}
+
+void cgit_log_link(const char *name, const char *title, const char *class,
+		   const char *head, const char *rev, const char *path,
+		   int ofs, const char *grep, const char *pattern, int showmsg,
+		   int follow)
+{
+	char *delim;
+
+	delim = repolink(title, class, "log", head, path);
+	if (rev && ctx.qry.head && strcmp(rev, ctx.qry.head)) {
+		html(delim);
+		html("id=");
+		html_url_arg(rev);
+		delim = "&amp;";
+	}
+	if (grep && pattern) {
+		html(delim);
+		html("qt=");
+		html_url_arg(grep);
+		delim = "&amp;";
+		html(delim);
+		html("q=");
+		html_url_arg(pattern);
+	}
+	if (ofs > 0) {
+		html(delim);
+		html("ofs=");
+		htmlf("%d", ofs);
+		delim = "&amp;";
+	}
+	if (showmsg) {
+		html(delim);
+		html("showmsg=1");
+		delim = "&amp;";
+	}
+	if (follow) {
+		html(delim);
+		html("follow=1");
+	}
+	html("'>");
+	html_txt(name);
+	html("</a>");
+}
+
+void cgit_commit_link(const char *name, const char *title, const char *class,
+		      const char *head, const char *rev, const char *path)
+{
+	char *delim;
+
+	delim = repolink(title, class, "commit", head, path);
+	if (rev && ctx.qry.head && strcmp(rev, ctx.qry.head)) {
+		html(delim);
+		html("id=");
+		html_url_arg(rev);
+		delim = "&amp;";
+	}
+	if (ctx.qry.difftype) {
+		html(delim);
+		htmlf("dt=%d", ctx.qry.difftype);
+		delim = "&amp;";
+	}
+	if (ctx.qry.context > 0 && ctx.qry.context != 3) {
+		html(delim);
+		html("context=");
+		htmlf("%d", ctx.qry.context);
+		delim = "&amp;";
+	}
+	if (ctx.qry.ignorews) {
+		html(delim);
+		html("ignorews=1");
+		delim = "&amp;";
+	}
+	if (ctx.qry.follow) {
+		html(delim);
+		html("follow=1");
+	}
+	html("'>");
+	if (name[0] != '\0') {
+		if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
+			html_ntxt(name, ctx.cfg.max_msg_len - 3);
+			html("...");
+		} else
+			html_txt(name);
+	} else
+		html_txt("(no commit message)");
+	html("</a>");
+}
+
+void cgit_refs_link(const char *name, const char *title, const char *class,
+		    const char *head, const char *rev, const char *path)
+{
+	reporevlink("refs", name, title, class, head, rev, path);
+}
+
+void cgit_snapshot_link(const char *name, const char *title, const char *class,
+			const char *head, const char *rev,
+			const char *archivename)
+{
+	reporevlink("snapshot", name, title, class, head, rev, archivename);
+}
+
+void cgit_diff_link(const char *name, const char *title, const char *class,
+		    const char *head, const char *new_rev, const char *old_rev,
+		    const char *path)
+{
+	char *delim;
+
+	delim = repolink(title, class, "diff", head, path);
+	if (new_rev && ctx.qry.head != NULL && strcmp(new_rev, ctx.qry.head)) {
+		html(delim);
+		html("id=");
+		html_url_arg(new_rev);
+		delim = "&amp;";
+	}
+	if (old_rev) {
+		html(delim);
+		html("id2=");
+		html_url_arg(old_rev);
+		delim = "&amp;";
+	}
+	if (ctx.qry.difftype) {
+		html(delim);
+		htmlf("dt=%d", ctx.qry.difftype);
+		delim = "&amp;";
+	}
+	if (ctx.qry.context > 0 && ctx.qry.context != 3) {
+		html(delim);
+		html("context=");
+		htmlf("%d", ctx.qry.context);
+		delim = "&amp;";
+	}
+	if (ctx.qry.ignorews) {
+		html(delim);
+		html("ignorews=1");
+		delim = "&amp;";
+	}
+	if (ctx.qry.follow) {
+		html(delim);
+		html("follow=1");
+	}
+	html("'>");
+	html_txt(name);
+	html("</a>");
+}
+
+void cgit_patch_link(const char *name, const char *title, const char *class,
+		     const char *head, const char *rev, const char *path)
+{
+	reporevlink("patch", name, title, class, head, rev, path);
+}
+
+void cgit_stats_link(const char *name, const char *title, const char *class,
+		     const char *head, const char *path)
+{
+	reporevlink("stats", name, title, class, head, NULL, path);
+}
+
+static void cgit_self_link(char *name, const char *title, const char *class)
+{
+	if (!strcmp(ctx.qry.page, "repolist"))
+		cgit_index_link(name, title, class, ctx.qry.search, ctx.qry.sort,
+				ctx.qry.ofs, 1);
+	else if (!strcmp(ctx.qry.page, "summary"))
+		cgit_summary_link(name, title, class, ctx.qry.head);
+	else if (!strcmp(ctx.qry.page, "tag"))
+		cgit_tag_link(name, title, class, ctx.qry.has_oid ?
+			       ctx.qry.oid : ctx.qry.head);
+	else if (!strcmp(ctx.qry.page, "tree"))
+		cgit_tree_link(name, title, class, ctx.qry.head,
+			       ctx.qry.has_oid ? ctx.qry.oid : NULL,
+			       ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "plain"))
+		cgit_plain_link(name, title, class, ctx.qry.head,
+				ctx.qry.has_oid ? ctx.qry.oid : NULL,
+				ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "blame"))
+		cgit_blame_link(name, title, class, ctx.qry.head,
+				ctx.qry.has_oid ? ctx.qry.oid : NULL,
+				ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "log"))
+		cgit_log_link(name, title, class, ctx.qry.head,
+			      ctx.qry.has_oid ? ctx.qry.oid : NULL,
+			      ctx.qry.path, ctx.qry.ofs,
+			      ctx.qry.grep, ctx.qry.search,
+			      ctx.qry.showmsg, ctx.qry.follow);
+	else if (!strcmp(ctx.qry.page, "commit"))
+		cgit_commit_link(name, title, class, ctx.qry.head,
+				 ctx.qry.has_oid ? ctx.qry.oid : NULL,
+				 ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "patch"))
+		cgit_patch_link(name, title, class, ctx.qry.head,
+				ctx.qry.has_oid ? ctx.qry.oid : NULL,
+				ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "refs"))
+		cgit_refs_link(name, title, class, ctx.qry.head,
+			       ctx.qry.has_oid ? ctx.qry.oid : NULL,
+			       ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "snapshot"))
+		cgit_snapshot_link(name, title, class, ctx.qry.head,
+				   ctx.qry.has_oid ? ctx.qry.oid : NULL,
+				   ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "diff"))
+		cgit_diff_link(name, title, class, ctx.qry.head,
+			       ctx.qry.oid, ctx.qry.oid2,
+			       ctx.qry.path);
+	else if (!strcmp(ctx.qry.page, "stats"))
+		cgit_stats_link(name, title, class, ctx.qry.head,
+				ctx.qry.path);
+	else {
+		/* Don't known how to make link for this page */
+		repolink(title, class, ctx.qry.page, ctx.qry.head, ctx.qry.path);
+		html("><!-- cgit_self_link() doesn't know how to make link for page '");
+		html_txt(ctx.qry.page);
+		html("' -->");
+		html_txt(name);
+		html("</a>");
+	}
+}
+
+void cgit_object_link(struct object *obj)
+{
+	char *page, *shortrev, *fullrev, *name;
+
+	fullrev = oid_to_hex(&obj->oid);
+	shortrev = xstrdup(fullrev);
+	shortrev[10] = '\0';
+	if (obj->type == OBJ_COMMIT) {
+		cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL,
+				 ctx.qry.head, fullrev, NULL);
+		return;
+	} else if (obj->type == OBJ_TREE)
+		page = "tree";
+	else if (obj->type == OBJ_TAG)
+		page = "tag";
+	else
+		page = "blob";
+	name = fmt("%s %s...", type_name(obj->type), shortrev);
+	reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL);
+}
+
+static struct string_list_item *lookup_path(struct string_list *list,
+					    const char *path)
+{
+	struct string_list_item *item;
+
+	while (path && path[0]) {
+		if ((item = string_list_lookup(list, path)))
+			return item;
+		if (!(path = strchr(path, '/')))
+			break;
+		path++;
+	}
+	return NULL;
+}
+
+void cgit_submodule_link(const char *class, char *path, const char *rev)
+{
+	struct string_list *list;
+	struct string_list_item *item;
+	char tail, *dir;
+	size_t len;
+
+	len = 0;
+	tail = 0;
+	list = &ctx.repo->submodules;
+	item = lookup_path(list, path);
+	if (!item) {
+		len = strlen(path);
+		tail = path[len - 1];
+		if (tail == '/') {
+			path[len - 1] = 0;
+			item = lookup_path(list, path);
+		}
+	}
+	if (item || ctx.repo->module_link) {
+		html("<a ");
+		if (class)
+			htmlf("class='%s' ", class);
+		html("href='");
+		if (item) {
+			html_attrf(item->util, rev);
+		} else {
+			dir = strrchr(path, '/');
+			if (dir)
+				dir++;
+			else
+				dir = path;
+			html_attrf(ctx.repo->module_link, dir, rev);
+		}
+		html("'>");
+		html_txt(path);
+		html("</a>");
+	} else {
+		html("<span");
+		if (class)
+			htmlf(" class='%s'", class);
+		html(">");
+		html_txt(path);
+		html("</span>");
+	}
+	html_txtf(" @ %.7s", rev);
+	if (item && tail)
+		path[len - 1] = tail;
+}
+
+const struct date_mode *cgit_date_mode(enum date_mode_type type)
+{
+	static struct date_mode mode;
+	mode.type = type;
+	mode.local = ctx.cfg.local_time;
+	return &mode;
+}
+
+static void print_rel_date(time_t t, int tz, double value,
+	const char *class, const char *suffix)
+{
+	htmlf("<span class='%s' title='", class);
+	html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601)));
+	htmlf("'>%.0f %s</span>", value, suffix);
+}
+
+void cgit_print_age(time_t t, int tz, time_t max_relative)
+{
+	time_t now, secs;
+
+	if (!t)
+		return;
+	time(&now);
+	secs = now - t;
+	if (secs < 0)
+		secs = 0;
+
+	if (secs > max_relative && max_relative >= 0) {
+		html("<span title='");
+		html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601)));
+		html("'>");
+		html_txt(show_date(t, tz, cgit_date_mode(DATE_SHORT)));
+		html("</span>");
+		return;
+	}
+
+	if (secs < TM_HOUR * 2) {
+		print_rel_date(t, tz, secs * 1.0 / TM_MIN, "age-mins", "min.");
+		return;
+	}
+	if (secs < TM_DAY * 2) {
+		print_rel_date(t, tz, secs * 1.0 / TM_HOUR, "age-hours", "hours");
+		return;
+	}
+	if (secs < TM_WEEK * 2) {
+		print_rel_date(t, tz, secs * 1.0 / TM_DAY, "age-days", "days");
+		return;
+	}
+	if (secs < TM_MONTH * 2) {
+		print_rel_date(t, tz, secs * 1.0 / TM_WEEK, "age-weeks", "weeks");
+		return;
+	}
+	if (secs < TM_YEAR * 2) {
+		print_rel_date(t, tz, secs * 1.0 / TM_MONTH, "age-months", "months");
+		return;
+	}
+	print_rel_date(t, tz, secs * 1.0 / TM_YEAR, "age-years", "years");
+}
+
+void cgit_print_http_headers(void)
+{
+	if (ctx.env.no_http && !strcmp(ctx.env.no_http, "1"))
+		return;
+
+	if (ctx.page.status)
+		htmlf("Status: %d %s\n", ctx.page.status, ctx.page.statusmsg);
+	if (ctx.page.mimetype && ctx.page.charset)
+		htmlf("Content-Type: %s; charset=%s\n", ctx.page.mimetype,
+		      ctx.page.charset);
+	else if (ctx.page.mimetype)
+		htmlf("Content-Type: %s\n", ctx.page.mimetype);
+	if (ctx.page.size)
+		htmlf("Content-Length: %zd\n", ctx.page.size);
+	if (ctx.page.filename) {
+		html("Content-Disposition: inline; filename=\"");
+		html_header_arg_in_quotes(ctx.page.filename);
+		html("\"\n");
+	}
+	if (!ctx.env.authenticated)
+		html("Cache-Control: no-cache, no-store\n");
+	htmlf("Last-Modified: %s\n", http_date(ctx.page.modified));
+	htmlf("Expires: %s\n", http_date(ctx.page.expires));
+	if (ctx.page.etag)
+		htmlf("ETag: \"%s\"\n", ctx.page.etag);
+	html("\n");
+	if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD"))
+		exit(0);
+}
+
+void cgit_redirect(const char *url, bool permanent)
+{
+	htmlf("Status: %d %s\n", permanent ? 301 : 302, permanent ? "Moved" : "Found");
+	html("Location: ");
+	html_url_path(url);
+	html("\n\n");
+}
+
+static void print_rel_vcs_link(const char *url)
+{
+	html("<link rel='vcs-git' href='");
+	html_attr(url);
+	html("' title='");
+	html_attr(ctx.repo->name);
+	html(" Git repository'/>\n");
+}
+
+void cgit_print_docstart(void)
+{
+	char *host = cgit_hosturl();
+
+	if (ctx.cfg.embedded) {
+		if (ctx.cfg.header)
+			html_include(ctx.cfg.header);
+		return;
+	}
+
+	html(cgit_doctype);
+	html("<html lang='en'>\n");
+	html("<head>\n");
+	html("<title>");
+	html_txt(ctx.page.title);
+	html("</title>\n");
+	htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
+	if (ctx.cfg.robots && *ctx.cfg.robots)
+		htmlf("<meta name='robots' content='%s'/>\n", ctx.cfg.robots);
+	html("<link rel='stylesheet' type='text/css' href='");
+	html_attr(ctx.cfg.css);
+	html("'/>\n");
+	if (ctx.cfg.favicon) {
+		html("<link rel='shortcut icon' href='");
+		html_attr(ctx.cfg.favicon);
+		html("'/>\n");
+	}
+	if (host && ctx.repo && ctx.qry.head) {
+		char *fileurl;
+		struct strbuf sb = STRBUF_INIT;
+		strbuf_addf(&sb, "h=%s", ctx.qry.head);
+
+		html("<link rel='alternate' title='Atom feed' href='");
+		html(cgit_httpscheme());
+		html_attr(host);
+		fileurl = cgit_fileurl(ctx.repo->url, "atom", ctx.qry.vpath,
+				       sb.buf);
+		html_attr(fileurl);
+		html("' type='application/atom+xml'/>\n");
+		strbuf_release(&sb);
+		free(fileurl);
+	}
+	if (ctx.repo)
+		cgit_add_clone_urls(print_rel_vcs_link);
+	if (ctx.cfg.head_include)
+		html_include(ctx.cfg.head_include);
+	if (ctx.repo && ctx.repo->extra_head_content)
+		html(ctx.repo->extra_head_content);
+	html("</head>\n");
+	html("<body>\n");
+	if (ctx.cfg.header)
+		html_include(ctx.cfg.header);
+	free(host);
+}
+
+void cgit_print_docend(void)
+{
+	html("</div> <!-- class=content -->\n");
+	if (ctx.cfg.embedded) {
+		html("</div> <!-- id=cgit -->\n");
+		if (ctx.cfg.footer)
+			html_include(ctx.cfg.footer);
+		return;
+	}
+	if (ctx.cfg.footer)
+		html_include(ctx.cfg.footer);
+	else {
+		htmlf("<div class='footer'>generated by <a href='https://git.causal.agency/src/log/www/git.causal.agency/cgit'>cgit %s</a> "
+			"(<a href='https://git-scm.com/'>git %s</a>) at ", cgit_version, git_version_string);
+		html_txt(show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601)));
+		html("</div>\n");
+	}
+	html("</div> <!-- id=cgit -->\n");
+	html("</body>\n</html>\n");
+}
+
+void cgit_print_error_page(int code, const char *msg, const char *fmt, ...)
+{
+	va_list ap;
+	ctx.page.expires = ctx.cfg.cache_dynamic_ttl;
+	ctx.page.status = code;
+	ctx.page.statusmsg = msg;
+	cgit_print_layout_start();
+	va_start(ap, fmt);
+	cgit_vprint_error(fmt, ap);
+	va_end(ap);
+	cgit_print_layout_end();
+}
+
+void cgit_print_layout_start(void)
+{
+	cgit_print_http_headers();
+	cgit_print_docstart();
+	cgit_print_pageheader();
+}
+
+void cgit_print_layout_end(void)
+{
+	cgit_print_docend();
+}
+
+static void add_clone_urls(void (*fn)(const char *), char *txt, char *suffix)
+{
+	struct strbuf **url_list = strbuf_split_str(txt, ' ', 0);
+	int i;
+
+	for (i = 0; url_list[i]; i++) {
+		strbuf_rtrim(url_list[i]);
+		if (url_list[i]->len == 0)
+			continue;
+		if (suffix && *suffix)
+			strbuf_addf(url_list[i], "/%s", suffix);
+		fn(url_list[i]->buf);
+	}
+
+	strbuf_list_free(url_list);
+}
+
+void cgit_add_clone_urls(void (*fn)(const char *))
+{
+	if (ctx.repo->clone_url)
+		add_clone_urls(fn, expand_macros(ctx.repo->clone_url), NULL);
+	else if (ctx.cfg.clone_prefix)
+		add_clone_urls(fn, ctx.cfg.clone_prefix, ctx.repo->url);
+}
+
+static int print_this_commit_option(void)
+{
+	struct object_id oid;
+	if (!ctx.qry.head || get_oid(ctx.qry.head, &oid))
+		return 1;
+	html_option(oid_to_hex(&oid), "this commit", ctx.qry.head);
+	return 0;
+}
+
+static int print_branch_option(const char *refname, const struct object_id *oid,
+			       int flags, void *cb_data)
+{
+	char *name = (char *)refname;
+	html_option(name, name, ctx.qry.head);
+	return 0;
+}
+
+void cgit_add_hidden_formfields(int incl_head, int incl_search,
+				const char *page)
+{
+	if (!ctx.cfg.virtual_root) {
+		struct strbuf url = STRBUF_INIT;
+
+		strbuf_addf(&url, "%s/%s", ctx.qry.repo, page);
+		if (ctx.qry.vpath)
+			strbuf_addf(&url, "/%s", ctx.qry.vpath);
+		html_hidden("url", url.buf);
+		strbuf_release(&url);
+	}
+
+	if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
+	    strcmp(ctx.qry.head, ctx.repo->defbranch))
+		html_hidden("h", ctx.qry.head);
+
+	if (ctx.qry.oid)
+		html_hidden("id", ctx.qry.oid);
+	if (ctx.qry.oid2)
+		html_hidden("id2", ctx.qry.oid2);
+	if (ctx.qry.showmsg)
+		html_hidden("showmsg", "1");
+
+	if (incl_search) {
+		if (ctx.qry.grep)
+			html_hidden("qt", ctx.qry.grep);
+		if (ctx.qry.search)
+			html_hidden("q", ctx.qry.search);
+	}
+}
+
+static const char *hc(const char *page)
+{
+	if (!ctx.qry.page)
+		return NULL;
+
+	return strcmp(ctx.qry.page, page) ? NULL : "active";
+}
+
+static void cgit_print_path_crumbs(char *path)
+{
+	char *old_path = ctx.qry.path;
+	char *p = path, *q, *end = path + strlen(path);
+	int levels = 0;
+
+	ctx.qry.path = NULL;
+	cgit_self_link("root", NULL, NULL);
+	ctx.qry.path = p = path;
+	while (p < end) {
+		if (!(q = strchr(p, '/')) || levels > 15)
+			q = end;
+		*q = '\0';
+		html_txt("/");
+		cgit_self_link(p, NULL, NULL);
+		if (q < end)
+			*q = '/';
+		p = q + 1;
+		++levels;
+	}
+	ctx.qry.path = old_path;
+}
+
+static void print_header(void)
+{
+	char *logo = NULL, *logo_link = NULL;
+
+	html("<table id='header'>\n");
+	html("<tr>\n");
+
+	if (ctx.repo && ctx.repo->logo && *ctx.repo->logo)
+		logo = ctx.repo->logo;
+	else
+		logo = ctx.cfg.logo;
+	if (ctx.repo && ctx.repo->logo_link && *ctx.repo->logo_link)
+		logo_link = ctx.repo->logo_link;
+	else
+		logo_link = ctx.cfg.logo_link;
+	if (logo && *logo) {
+		html("<td class='logo' rowspan='2'><a href='");
+		if (logo_link && *logo_link)
+			html_attr(logo_link);
+		else
+			html_attr(cgit_rooturl());
+		html("'><img src='");
+		html_attr(logo);
+		html("' alt='cgit logo'/></a></td>\n");
+	}
+
+	html("<td class='main'>");
+	if (ctx.repo) {
+		cgit_index_link("index", NULL, NULL, NULL, NULL, 0, 1);
+		html(" : ");
+		cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
+		if (ctx.env.authenticated) {
+			html("</td><td class='form'>");
+			html("<form method='get'>\n");
+			cgit_add_hidden_formfields(0, 1, ctx.qry.page);
+			html("<select name='h' onchange='this.form.submit();'>\n");
+			print_this_commit_option();
+			html("<optgroup label='branches'>");
+			for_each_branch_ref(print_branch_option, ctx.qry.head);
+			if (ctx.repo->enable_remote_branches)
+				for_each_remote_ref(print_branch_option, ctx.qry.head);
+			html("</optgroup>");
+			html("</select> ");
+			html("<input type='submit' value='switch'/>");
+			html("</form>");
+		}
+	} else
+		html_txt(ctx.cfg.root_title);
+	html("</td></tr>\n");
+
+	html("<tr><td class='sub'>");
+	if (ctx.repo) {
+		html_txt(ctx.repo->desc);
+		html("</td><td class='sub right'>");
+		if (ctx.repo->owner_filter) {
+			cgit_open_filter(ctx.repo->owner_filter);
+			html_txt(ctx.repo->owner);
+			cgit_close_filter(ctx.repo->owner_filter);
+		} else {
+			html_txt(ctx.repo->owner);
+		}
+	} else {
+		if (ctx.cfg.root_desc)
+			html_txt(ctx.cfg.root_desc);
+	}
+	html("</td></tr></table>\n");
+}
+
+void cgit_print_pageheader(void)
+{
+	html("<div id='cgit'>");
+	if (!ctx.env.authenticated || !ctx.cfg.noheader)
+		print_header();
+
+	html("<table class='tabs'><tr><td>\n");
+	if (ctx.env.authenticated && ctx.repo) {
+		if (ctx.repo->readme.nr) {
+			reporevlink("about", "about", NULL,
+				    hc("about"), ctx.qry.head, NULL,
+				    NULL);
+			html(" ");
+		}
+		cgit_summary_link("summary", NULL, hc("summary"),
+				  ctx.qry.head);
+		html(" ");
+		cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head,
+			       ctx.qry.oid, NULL);
+		html(" ");
+		cgit_log_link("log", NULL, hc("log"), ctx.qry.head,
+			      NULL, ctx.qry.vpath, 0, NULL, NULL,
+			      ctx.qry.showmsg, ctx.qry.follow);
+		html(" ");
+		if (ctx.qry.page && !strcmp(ctx.qry.page, "blame"))
+			cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head,
+				        ctx.qry.oid, ctx.qry.vpath);
+		else
+			cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
+				       ctx.qry.oid, ctx.qry.vpath);
+		html(" ");
+		cgit_commit_link("commit", NULL, hc("commit"),
+				 ctx.qry.head, ctx.qry.oid, ctx.qry.vpath);
+		html(" ");
+		cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head,
+			       ctx.qry.oid, ctx.qry.oid2, ctx.qry.vpath);
+		if (ctx.repo->max_stats) {
+			html(" ");
+			cgit_stats_link("stats", NULL, hc("stats"),
+					ctx.qry.head, ctx.qry.vpath);
+		}
+		if (ctx.repo->homepage) {
+			html(" <a href='");
+			html_attr(ctx.repo->homepage);
+			html("'>homepage</a>");
+		}
+		html("</td><td class='form'>");
+		html("<form class='right' method='get' action='");
+		if (ctx.cfg.virtual_root) {
+			char *fileurl = cgit_fileurl(ctx.qry.repo, "log",
+						   ctx.qry.vpath, NULL);
+			html_url_path(fileurl);
+			free(fileurl);
+		}
+		html("'>\n");
+		cgit_add_hidden_formfields(1, 0, "log");
+		html("<select name='qt'>\n");
+		html_option("grep", "log msg", ctx.qry.grep);
+		html_option("author", "author", ctx.qry.grep);
+		html_option("committer", "committer", ctx.qry.grep);
+		html_option("range", "range", ctx.qry.grep);
+		html("</select>\n");
+		html("<input class='txt' type='search' size='10' name='q' value='");
+		html_attr(ctx.qry.search);
+		html("'/>\n");
+		html("<input type='submit' value='search'/>\n");
+		html("</form>\n");
+	} else if (ctx.env.authenticated) {
+		char *currenturl = cgit_currenturl();
+		site_link(NULL, "index", NULL, hc("repolist"), NULL, NULL, 0, 1);
+		if (ctx.cfg.root_readme)
+			site_link("about", "about", NULL, hc("about"),
+				  NULL, NULL, 0, 1);
+		html("</td><td class='form'>");
+		html("<form method='get' action='");
+		html_attr(currenturl);
+		html("'>\n");
+		html("<input type='search' name='q' size='10' value='");
+		html_attr(ctx.qry.search);
+		html("'/>\n");
+		html("<input type='submit' value='search'/>\n");
+		html("</form>");
+		free(currenturl);
+	}
+	html("</td></tr></table>\n");
+	if (ctx.env.authenticated && ctx.repo && ctx.qry.vpath) {
+		html("<div class='path'>");
+		html("path: ");
+		cgit_print_path_crumbs(ctx.qry.vpath);
+		if (ctx.cfg.enable_follow_links && !strcmp(ctx.qry.page, "log")) {
+			html(" (");
+			ctx.qry.follow = !ctx.qry.follow;
+			cgit_self_link(ctx.qry.follow ? "follow" : "unfollow",
+					NULL, NULL);
+			ctx.qry.follow = !ctx.qry.follow;
+			html(")");
+		}
+		html("</div>");
+	}
+	html("<div class='content'>");
+}
+
+void cgit_print_filemode(unsigned short mode)
+{
+	if (S_ISDIR(mode))
+		html("d");
+	else if (S_ISLNK(mode))
+		html("l");
+	else if (S_ISGITLINK(mode))
+		html("m");
+	else
+		html("-");
+	html_fileperm(mode >> 6);
+	html_fileperm(mode >> 3);
+	html_fileperm(mode);
+}
+
+void cgit_compose_snapshot_prefix(struct strbuf *filename, const char *base,
+				  const char *ref)
+{
+	struct object_id oid;
+
+	/*
+	 * Prettify snapshot names by stripping leading "v" or "V" if the tag
+	 * name starts with {v,V}[0-9] and the prettify mapping is injective,
+	 * i.e. each stripped tag can be inverted without ambiguities.
+	 */
+	if (get_oid(fmt("refs/tags/%s", ref), &oid) == 0 &&
+	    (ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1]) &&
+	    ((get_oid(fmt("refs/tags/%s", ref + 1), &oid) == 0) +
+	     (get_oid(fmt("refs/tags/v%s", ref + 1), &oid) == 0) +
+	     (get_oid(fmt("refs/tags/V%s", ref + 1), &oid) == 0) == 1))
+		ref++;
+
+	strbuf_addf(filename, "%s-%s", base, ref);
+}
+
+void cgit_print_snapshot_links(const struct cgit_repo *repo, const char *ref,
+			       const char *separator)
+{
+	const struct cgit_snapshot_format *f;
+	struct strbuf filename = STRBUF_INIT;
+	const char *basename;
+	size_t prefixlen;
+
+	basename = cgit_snapshot_prefix(repo);
+	if (starts_with(ref, basename))
+		strbuf_addstr(&filename, ref);
+	else
+		cgit_compose_snapshot_prefix(&filename, basename, ref);
+
+	prefixlen = filename.len;
+	for (f = cgit_snapshot_formats; f->suffix; f++) {
+		if (!(repo->snapshots & cgit_snapshot_format_bit(f)))
+			continue;
+		strbuf_setlen(&filename, prefixlen);
+		strbuf_addstr(&filename, f->suffix);
+		cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL,
+				   filename.buf);
+		if (cgit_snapshot_get_sig(ref, f)) {
+			strbuf_addstr(&filename, ".asc");
+			html(" (");
+			cgit_snapshot_link("sig", NULL, NULL, NULL, NULL,
+					   filename.buf);
+			html(")");
+		} else if (starts_with(f->suffix, ".tar") && cgit_snapshot_get_sig(ref, &cgit_snapshot_formats[0])) {
+			strbuf_setlen(&filename, strlen(filename.buf) - strlen(f->suffix));
+			strbuf_addstr(&filename, ".tar.asc");
+			html(" (");
+			cgit_snapshot_link("sig", NULL, NULL, NULL, NULL,
+					   filename.buf);
+			html(")");
+		}
+		html(separator);
+	}
+	strbuf_release(&filename);
+}
+
+void cgit_set_title_from_path(const char *path)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char *slash, *last_slash;
+
+	if (!path)
+		return;
+
+	last_slash = path + strlen(path);
+	for (slash = last_slash; slash > path; --slash) {
+		if (*slash != '/') continue;
+		strbuf_add(&sb, slash + 1, last_slash - slash - 1);
+		strbuf_addstr(&sb, " \xc2\xab ");
+		last_slash = slash;
+	}
+	strbuf_add(&sb, path, last_slash - path);
+	strbuf_addf(&sb, " - %s", ctx.page.title);
+	ctx.page.title = strbuf_detach(&sb, NULL);
+}
diff --git a/www/git.causal.agency/cgit/ui-shared.h b/www/git.causal.agency/cgit/ui-shared.h
new file mode 100644
index 00000000..6964873a
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-shared.h
@@ -0,0 +1,87 @@
+#ifndef UI_SHARED_H
+#define UI_SHARED_H
+
+extern const char *cgit_httpscheme(void);
+extern char *cgit_hosturl(void);
+extern const char *cgit_rooturl(void);
+extern char *cgit_currenturl(void);
+extern char *cgit_currentfullurl(void);
+extern const char *cgit_loginurl(void);
+extern char *cgit_repourl(const char *reponame);
+extern char *cgit_fileurl(const char *reponame, const char *pagename,
+			  const char *filename, const char *query);
+extern char *cgit_pageurl(const char *reponame, const char *pagename,
+			  const char *query);
+
+extern void cgit_add_clone_urls(void (*fn)(const char *));
+
+extern void cgit_index_link(const char *name, const char *title,
+			    const char *class, const char *pattern, const char *sort, int ofs, int always_root);
+extern void cgit_summary_link(const char *name, const char *title,
+			      const char *class, const char *head);
+extern void cgit_tag_link(const char *name, const char *title,
+			  const char *class, const char *tag);
+extern void cgit_tree_link(const char *name, const char *title,
+			   const char *class, const char *head,
+			   const char *rev, const char *path);
+extern void cgit_plain_link(const char *name, const char *title,
+			    const char *class, const char *head,
+			    const char *rev, const char *path);
+extern void cgit_blame_link(const char *name, const char *title,
+			    const char *class, const char *head,
+			    const char *rev, const char *path);
+extern void cgit_log_link(const char *name, const char *title,
+			  const char *class, const char *head, const char *rev,
+			  const char *path, int ofs, const char *grep,
+			  const char *pattern, int showmsg, int follow);
+extern void cgit_commit_link(const char *name, const char *title,
+			     const char *class, const char *head,
+			     const char *rev, const char *path);
+extern void cgit_patch_link(const char *name, const char *title,
+			    const char *class, const char *head,
+			    const char *rev, const char *path);
+extern void cgit_refs_link(const char *name, const char *title,
+			   const char *class, const char *head,
+			   const char *rev, const char *path);
+extern void cgit_snapshot_link(const char *name, const char *title,
+			       const char *class, const char *head,
+			       const char *rev, const char *archivename);
+extern void cgit_diff_link(const char *name, const char *title,
+			   const char *class, const char *head,
+			   const char *new_rev, const char *old_rev,
+			   const char *path);
+extern void cgit_stats_link(const char *name, const char *title,
+			    const char *class, const char *head,
+			    const char *path);
+extern void cgit_object_link(struct object *obj);
+
+extern void cgit_submodule_link(const char *class, char *path,
+				const char *rev);
+
+extern void cgit_print_layout_start(void);
+extern void cgit_print_layout_end(void);
+
+__attribute__((format (printf,1,2)))
+extern void cgit_print_error(const char *fmt, ...);
+__attribute__((format (printf,1,0)))
+extern void cgit_vprint_error(const char *fmt, va_list ap);
+extern const struct date_mode *cgit_date_mode(enum date_mode_type type);
+extern void cgit_print_age(time_t t, int tz, time_t max_relative);
+extern void cgit_print_http_headers(void);
+extern void cgit_redirect(const char *url, bool permanent);
+extern void cgit_print_docstart(void);
+extern void cgit_print_docend(void);
+__attribute__((format (printf,3,4)))
+extern void cgit_print_error_page(int code, const char *msg, const char *fmt, ...);
+extern void cgit_print_pageheader(void);
+extern void cgit_print_filemode(unsigned short mode);
+extern void cgit_compose_snapshot_prefix(struct strbuf *filename,
+					 const char *base, const char *ref);
+extern void cgit_print_snapshot_links(const struct cgit_repo *repo,
+				      const char *ref, const char *separator);
+extern const char *cgit_snapshot_prefix(const struct cgit_repo *repo);
+extern void cgit_add_hidden_formfields(int incl_head, int incl_search,
+				       const char *page);
+
+extern void cgit_set_title_from_path(const char *path);
+#endif /* UI_SHARED_H */
diff --git a/www/git.causal.agency/cgit/ui-snapshot.c b/www/git.causal.agency/cgit/ui-snapshot.c
new file mode 100644
index 00000000..28013935
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-snapshot.c
@@ -0,0 +1,319 @@
+/* ui-snapshot.c: generate snapshot of a commit
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-snapshot.h"
+#include "html.h"
+#include "ui-shared.h"
+
+static int write_archive_type(const char *format, const char *hex, const char *prefix)
+{
+	struct strvec argv = STRVEC_INIT;
+	const char **nargv;
+	int result;
+	strvec_push(&argv, "snapshot");
+	strvec_push(&argv, format);
+	if (prefix) {
+		struct strbuf buf = STRBUF_INIT;
+		strbuf_addstr(&buf, prefix);
+		strbuf_addch(&buf, '/');
+		strvec_push(&argv, "--prefix");
+		strvec_push(&argv, buf.buf);
+		strbuf_release(&buf);
+	}
+	strvec_push(&argv, hex);
+	/*
+	 * Now we need to copy the pointers to arguments into a new
+	 * structure because write_archive will rearrange its arguments
+	 * which may result in duplicated/missing entries causing leaks
+	 * or double-frees in strvec_clear.
+	 */
+	nargv = xmalloc(sizeof(char *) * (argv.nr + 1));
+	/* strvec guarantees a trailing NULL entry. */
+	memcpy(nargv, argv.v, sizeof(char *) * (argv.nr + 1));
+
+	if (fflush(stdout))
+		return errno;
+
+	result = write_archive(argv.nr, nargv, NULL, the_repository, NULL, 0);
+	strvec_clear(&argv);
+	free(nargv);
+	return result;
+}
+
+static int write_tar_archive(const char *hex, const char *prefix)
+{
+	return write_archive_type("--format=tar", hex, prefix);
+}
+
+static int write_zip_archive(const char *hex, const char *prefix)
+{
+	return write_archive_type("--format=zip", hex, prefix);
+}
+
+static int write_compressed_tar_archive(const char *hex,
+					const char *prefix,
+					char *filter_argv[])
+{
+	int rv;
+	struct cgit_exec_filter f;
+	cgit_exec_filter_init(&f, filter_argv[0], filter_argv);
+
+	cgit_open_filter(&f.base);
+	rv = write_tar_archive(hex, prefix);
+	cgit_close_filter(&f.base);
+	return rv;
+}
+
+static int write_tar_gzip_archive(const char *hex, const char *prefix)
+{
+	char *argv[] = { "gzip", "-n", NULL };
+	return write_compressed_tar_archive(hex, prefix, argv);
+}
+
+static int write_tar_bzip2_archive(const char *hex, const char *prefix)
+{
+	char *argv[] = { "bzip2", NULL };
+	return write_compressed_tar_archive(hex, prefix, argv);
+}
+
+static int write_tar_lzip_archive(const char *hex, const char *prefix)
+{
+	char *argv[] = { "lzip", NULL };
+	return write_compressed_tar_archive(hex, prefix, argv);
+}
+
+static int write_tar_xz_archive(const char *hex, const char *prefix)
+{
+	char *argv[] = { "xz", NULL };
+	return write_compressed_tar_archive(hex, prefix, argv);
+}
+
+static int write_tar_zstd_archive(const char *hex, const char *prefix)
+{
+	char *argv[] = { "zstd", "-T0", NULL };
+	return write_compressed_tar_archive(hex, prefix, argv);
+}
+
+const struct cgit_snapshot_format cgit_snapshot_formats[] = {
+	/* .tar must remain the 0 index */
+	{ ".tar",	"application/x-tar",	write_tar_archive	},
+	{ ".tar.gz",	"application/x-gzip",	write_tar_gzip_archive	},
+	{ ".tar.bz2",	"application/x-bzip2",	write_tar_bzip2_archive	},
+	{ ".tar.lz",	"application/x-lzip",	write_tar_lzip_archive	},
+	{ ".tar.xz",	"application/x-xz",	write_tar_xz_archive	},
+	{ ".tar.zst",	"application/x-zstd",	write_tar_zstd_archive	},
+	{ ".zip",	"application/x-zip",	write_zip_archive	},
+	{ NULL }
+};
+
+static struct notes_tree snapshot_sig_notes[ARRAY_SIZE(cgit_snapshot_formats)];
+
+const struct object_id *cgit_snapshot_get_sig(const char *ref,
+					      const struct cgit_snapshot_format *f)
+{
+	struct notes_tree *tree;
+	struct object_id oid;
+
+	if (get_oid(ref, &oid))
+		return NULL;
+
+	tree = &snapshot_sig_notes[f - &cgit_snapshot_formats[0]];
+	if (!tree->initialized) {
+		struct strbuf notes_ref = STRBUF_INIT;
+
+		strbuf_addf(&notes_ref, "refs/notes/signatures/%s",
+			    f->suffix + 1);
+
+		init_notes(tree, notes_ref.buf, combine_notes_ignore, 0);
+		strbuf_release(&notes_ref);
+	}
+
+	return get_note(tree, &oid);
+}
+
+static const struct cgit_snapshot_format *get_format(const char *filename)
+{
+	const struct cgit_snapshot_format *fmt;
+
+	for (fmt = cgit_snapshot_formats; fmt->suffix; fmt++) {
+		if (ends_with(filename, fmt->suffix))
+			return fmt;
+	}
+	return NULL;
+}
+
+const unsigned cgit_snapshot_format_bit(const struct cgit_snapshot_format *f)
+{
+	return BIT(f - &cgit_snapshot_formats[0]);
+}
+
+static int make_snapshot(const struct cgit_snapshot_format *format,
+			 const char *hex, const char *prefix,
+			 const char *filename)
+{
+	struct object_id oid;
+
+	if (get_oid(hex, &oid)) {
+		cgit_print_error_page(404, "Not found",
+				"Bad object id: %s", hex);
+		return 1;
+	}
+	if (!lookup_commit_reference(the_repository, &oid)) {
+		cgit_print_error_page(400, "Bad request",
+				"Not a commit reference: %s", hex);
+		return 1;
+	}
+	ctx.page.etag = oid_to_hex(&oid);
+	ctx.page.mimetype = xstrdup(format->mimetype);
+	ctx.page.filename = xstrdup(filename);
+	cgit_print_http_headers();
+	init_archivers();
+	format->write_func(hex, prefix);
+	return 0;
+}
+
+static int write_sig(const struct cgit_snapshot_format *format,
+		     const char *hex, const char *archive,
+		     const char *filename)
+{
+	const struct object_id *note = cgit_snapshot_get_sig(hex, format);
+	enum object_type type;
+	unsigned long size;
+	char *buf;
+
+	if (!note) {
+		cgit_print_error_page(404, "Not found",
+				"No signature for %s", archive);
+		return 0;
+	}
+
+	buf = read_object_file(note, &type, &size);
+	if (!buf) {
+		cgit_print_error_page(404, "Not found", "Not found");
+		return 0;
+	}
+
+	html("X-Content-Type-Options: nosniff\n");
+	html("Content-Security-Policy: default-src 'none'\n");
+	ctx.page.etag = oid_to_hex(note);
+	ctx.page.mimetype = xstrdup("application/pgp-signature");
+	ctx.page.filename = xstrdup(filename);
+	cgit_print_http_headers();
+
+	html_raw(buf, size);
+	free(buf);
+	return 0;
+}
+
+/* Try to guess the requested revision from the requested snapshot name.
+ * First the format extension is stripped, e.g. "cgit-0.7.2.tar.gz" become
+ * "cgit-0.7.2". If this is a valid commit object name we've got a winner.
+ * Otherwise, if the snapshot name has a prefix matching the result from
+ * repo_basename(), we strip the basename and any following '-' and '_'
+ * characters ("cgit-0.7.2" -> "0.7.2") and check the resulting name once
+ * more. If this still isn't a valid commit object name, we check if pre-
+ * pending a 'v' or a 'V' to the remaining snapshot name ("0.7.2" ->
+ * "v0.7.2") gives us something valid.
+ */
+static const char *get_ref_from_filename(const struct cgit_repo *repo,
+					 const char *filename,
+					 const struct cgit_snapshot_format *format)
+{
+	const char *reponame;
+	struct object_id oid;
+	struct strbuf snapshot = STRBUF_INIT;
+	int result = 1;
+
+	strbuf_addstr(&snapshot, filename);
+	strbuf_setlen(&snapshot, snapshot.len - strlen(format->suffix));
+
+	if (get_oid(snapshot.buf, &oid) == 0)
+		goto out;
+
+	reponame = cgit_snapshot_prefix(repo);
+	if (starts_with(snapshot.buf, reponame)) {
+		const char *new_start = snapshot.buf;
+		new_start += strlen(reponame);
+		while (new_start && (*new_start == '-' || *new_start == '_'))
+			new_start++;
+		strbuf_splice(&snapshot, 0, new_start - snapshot.buf, "", 0);
+	}
+
+	if (get_oid(snapshot.buf, &oid) == 0)
+		goto out;
+
+	strbuf_insert(&snapshot, 0, "v", 1);
+	if (get_oid(snapshot.buf, &oid) == 0)
+		goto out;
+
+	strbuf_splice(&snapshot, 0, 1, "V", 1);
+	if (get_oid(snapshot.buf, &oid) == 0)
+		goto out;
+
+	result = 0;
+	strbuf_release(&snapshot);
+
+out:
+	return result ? strbuf_detach(&snapshot, NULL) : NULL;
+}
+
+void cgit_print_snapshot(const char *head, const char *hex,
+			 const char *filename, int dwim)
+{
+	const struct cgit_snapshot_format* f;
+	const char *sig_filename = NULL;
+	char *adj_filename = NULL;
+	char *prefix = NULL;
+
+	if (!filename) {
+		cgit_print_error_page(400, "Bad request",
+				"No snapshot name specified");
+		return;
+	}
+
+	if (ends_with(filename, ".asc")) {
+		sig_filename = filename;
+
+		/* Strip ".asc" from filename for common format processing */
+		adj_filename = xstrdup(filename);
+		adj_filename[strlen(adj_filename) - 4] = '\0';
+		filename = adj_filename;
+	}
+
+	f = get_format(filename);
+	if (!f || (!sig_filename && !(ctx.repo->snapshots & cgit_snapshot_format_bit(f)))) {
+		cgit_print_error_page(400, "Bad request",
+				"Unsupported snapshot format: %s", filename);
+		return;
+	}
+
+	if (!hex && dwim) {
+		hex = get_ref_from_filename(ctx.repo, filename, f);
+		if (hex == NULL) {
+			cgit_print_error_page(404, "Not found", "Not found");
+			return;
+		}
+		prefix = xstrdup(filename);
+		prefix[strlen(filename) - strlen(f->suffix)] = '\0';
+	}
+
+	if (!hex)
+		hex = head;
+
+	if (!prefix)
+		prefix = xstrdup(cgit_snapshot_prefix(ctx.repo));
+
+	if (sig_filename)
+		write_sig(f, hex, filename, sig_filename);
+	else
+		make_snapshot(f, hex, prefix, filename);
+
+	free(prefix);
+	free(adj_filename);
+}
diff --git a/www/git.causal.agency/cgit/ui-snapshot.h b/www/git.causal.agency/cgit/ui-snapshot.h
new file mode 100644
index 00000000..a8deec36
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-snapshot.h
@@ -0,0 +1,7 @@
+#ifndef UI_SNAPSHOT_H
+#define UI_SNAPSHOT_H
+
+extern void cgit_print_snapshot(const char *head, const char *hex,
+				const char *filename, int dwim);
+
+#endif /* UI_SNAPSHOT_H */
diff --git a/www/git.causal.agency/cgit/ui-ssdiff.c b/www/git.causal.agency/cgit/ui-ssdiff.c
new file mode 100644
index 00000000..af8bc9e0
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-ssdiff.c
@@ -0,0 +1,420 @@
+#include "cgit.h"
+#include "ui-ssdiff.h"
+#include "html.h"
+#include "ui-shared.h"
+#include "ui-diff.h"
+
+extern int use_ssdiff;
+
+static int current_old_line, current_new_line;
+static int **L = NULL;
+
+struct deferred_lines {
+	int line_no;
+	char *line;
+	struct deferred_lines *next;
+};
+
+static struct deferred_lines *deferred_old, *deferred_old_last;
+static struct deferred_lines *deferred_new, *deferred_new_last;
+
+static void create_or_reset_lcs_table(void)
+{
+	int i;
+
+	if (L != NULL) {
+		memset(*L, 0, sizeof(int) * MAX_SSDIFF_SIZE);
+		return;
+	}
+
+	// xcalloc will die if we ran out of memory;
+	// not very helpful for debugging
+	L = (int**)xcalloc(MAX_SSDIFF_M, sizeof(int *));
+	*L = (int*)xcalloc(MAX_SSDIFF_SIZE, sizeof(int));
+
+	for (i = 1; i < MAX_SSDIFF_M; i++) {
+		L[i] = *L + i * MAX_SSDIFF_N;
+	}
+}
+
+static char *longest_common_subsequence(char *A, char *B)
+{
+	int i, j, ri;
+	int m = strlen(A);
+	int n = strlen(B);
+	int tmp1, tmp2;
+	int lcs_length;
+	char *result;
+
+	// We bail if the lines are too long
+	if (m >= MAX_SSDIFF_M || n >= MAX_SSDIFF_N)
+		return NULL;
+
+	create_or_reset_lcs_table();
+
+	for (i = m; i >= 0; i--) {
+		for (j = n; j >= 0; j--) {
+			if (A[i] == '\0' || B[j] == '\0') {
+				L[i][j] = 0;
+			} else if (A[i] == B[j]) {
+				L[i][j] = 1 + L[i + 1][j + 1];
+			} else {
+				tmp1 = L[i + 1][j];
+				tmp2 = L[i][j + 1];
+				L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2);
+			}
+		}
+	}
+
+	lcs_length = L[0][0];
+	result = xmalloc(lcs_length + 2);
+	memset(result, 0, sizeof(*result) * (lcs_length + 2));
+
+	ri = 0;
+	i = 0;
+	j = 0;
+	while (i < m && j < n) {
+		if (A[i] == B[j]) {
+			result[ri] = A[i];
+			ri += 1;
+			i += 1;
+			j += 1;
+		} else if (L[i + 1][j] >= L[i][j + 1]) {
+			i += 1;
+		} else {
+			j += 1;
+		}
+	}
+
+	return result;
+}
+
+static int line_from_hunk(char *line, char type)
+{
+	char *buf1, *buf2;
+	int len, res;
+
+	buf1 = strchr(line, type);
+	if (buf1 == NULL)
+		return 0;
+	buf1 += 1;
+	buf2 = strchr(buf1, ',');
+	if (buf2 == NULL)
+		return 0;
+	len = buf2 - buf1;
+	buf2 = xmalloc(len + 1);
+	strlcpy(buf2, buf1, len + 1);
+	res = atoi(buf2);
+	free(buf2);
+	return res;
+}
+
+static char *replace_tabs(char *line)
+{
+	char *prev_buf = line;
+	char *cur_buf;
+	size_t linelen = strlen(line);
+	int n_tabs = 0;
+	int i;
+	char *result;
+	size_t result_len;
+
+	if (linelen == 0) {
+		result = xmalloc(1);
+		result[0] = '\0';
+		return result;
+	}
+
+	for (i = 0; i < linelen; i++) {
+		if (line[i] == '\t')
+			n_tabs += 1;
+	}
+	result_len = linelen + n_tabs * 8;
+	result = xmalloc(result_len + 1);
+	result[0] = '\0';
+
+	for (;;) {
+		cur_buf = strchr(prev_buf, '\t');
+		if (!cur_buf) {
+			linelen = strlen(result);
+			strlcpy(&result[linelen], prev_buf, result_len - linelen + 1);
+			break;
+		} else {
+			linelen = strlen(result);
+			strlcpy(&result[linelen], prev_buf, cur_buf - prev_buf + 1);
+			linelen = strlen(result);
+			memset(&result[linelen], ' ', 8 - (linelen % 8));
+			result[linelen + 8 - (linelen % 8)] = '\0';
+		}
+		prev_buf = cur_buf + 1;
+	}
+	return result;
+}
+
+static int calc_deferred_lines(struct deferred_lines *start)
+{
+	struct deferred_lines *item = start;
+	int result = 0;
+	while (item) {
+		result += 1;
+		item = item->next;
+	}
+	return result;
+}
+
+static void deferred_old_add(char *line, int line_no)
+{
+	struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
+	item->line = xstrdup(line);
+	item->line_no = line_no;
+	item->next = NULL;
+	if (deferred_old) {
+		deferred_old_last->next = item;
+		deferred_old_last = item;
+	} else {
+		deferred_old = deferred_old_last = item;
+	}
+}
+
+static void deferred_new_add(char *line, int line_no)
+{
+	struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
+	item->line = xstrdup(line);
+	item->line_no = line_no;
+	item->next = NULL;
+	if (deferred_new) {
+		deferred_new_last->next = item;
+		deferred_new_last = item;
+	} else {
+		deferred_new = deferred_new_last = item;
+	}
+}
+
+static void print_part_with_lcs(char *class, char *line, char *lcs)
+{
+	int line_len = strlen(line);
+	int i, j;
+	char c[2] = " ";
+	int same = 1;
+
+	j = 0;
+	for (i = 0; i < line_len; i++) {
+		c[0] = line[i];
+		if (same) {
+			if (line[i] == lcs[j])
+				j += 1;
+			else {
+				same = 0;
+				htmlf("<span class='%s'>", class);
+			}
+		} else if (line[i] == lcs[j]) {
+			same = 1;
+			html("</span>");
+			j += 1;
+		}
+		html_txt(c);
+	}
+	if (!same)
+		html("</span>");
+}
+
+static void print_ssdiff_line(char *class,
+			      int old_line_no,
+			      char *old_line,
+			      int new_line_no,
+			      char *new_line, int individual_chars)
+{
+	char *lcs = NULL;
+
+	if (old_line)
+		old_line = replace_tabs(old_line + 1);
+	if (new_line)
+		new_line = replace_tabs(new_line + 1);
+	if (individual_chars && old_line && new_line)
+		lcs = longest_common_subsequence(old_line, new_line);
+	html("<tr>\n");
+	if (old_line_no > 0) {
+		struct diff_filespec *old_file = cgit_get_current_old_file();
+		char *lineno_str = fmt("n%d", old_line_no);
+		char *id_str = fmt("id=%s#%s", is_null_oid(&old_file->oid)?"HEAD":oid_to_hex(old_rev_oid), lineno_str);
+		char *fileurl = cgit_fileurl(ctx.repo->url, "tree", old_file->path, id_str);
+		html("<td class='lineno'><a href='");
+		html(fileurl);
+		htmlf("'>%s</a>", lineno_str + 1);
+		html("</td>");
+		htmlf("<td class='%s'>", class);
+		free(fileurl);
+	} else if (old_line)
+		htmlf("<td class='lineno'></td><td class='%s'>", class);
+	else
+		htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
+	if (old_line) {
+		if (lcs)
+			print_part_with_lcs("del", old_line, lcs);
+		else
+			html_txt(old_line);
+	}
+
+	html("</td>\n");
+	if (new_line_no > 0) {
+		struct diff_filespec *new_file = cgit_get_current_new_file();
+		char *lineno_str = fmt("n%d", new_line_no);
+		char *id_str = fmt("id=%s#%s", is_null_oid(&new_file->oid)?"HEAD":oid_to_hex(new_rev_oid), lineno_str);
+		char *fileurl = cgit_fileurl(ctx.repo->url, "tree", new_file->path, id_str);
+		html("<td class='lineno'><a href='");
+		html(fileurl);
+		htmlf("'>%s</a>", lineno_str + 1);
+		html("</td>");
+		htmlf("<td class='%s'>", class);
+		free(fileurl);
+	} else if (new_line)
+		htmlf("<td class='lineno'></td><td class='%s'>", class);
+	else
+		htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
+	if (new_line) {
+		if (lcs)
+			print_part_with_lcs("add", new_line, lcs);
+		else
+			html_txt(new_line);
+	}
+
+	html("</td></tr>");
+	if (lcs)
+		free(lcs);
+	if (new_line)
+		free(new_line);
+	if (old_line)
+		free(old_line);
+}
+
+static void print_deferred_old_lines(void)
+{
+	struct deferred_lines *iter_old, *tmp;
+	iter_old = deferred_old;
+	while (iter_old) {
+		print_ssdiff_line("del", iter_old->line_no,
+				  iter_old->line, -1, NULL, 0);
+		tmp = iter_old->next;
+		free(iter_old);
+		iter_old = tmp;
+	}
+}
+
+static void print_deferred_new_lines(void)
+{
+	struct deferred_lines *iter_new, *tmp;
+	iter_new = deferred_new;
+	while (iter_new) {
+		print_ssdiff_line("add", -1, NULL,
+				  iter_new->line_no, iter_new->line, 0);
+		tmp = iter_new->next;
+		free(iter_new);
+		iter_new = tmp;
+	}
+}
+
+static void print_deferred_changed_lines(void)
+{
+	struct deferred_lines *iter_old, *iter_new, *tmp;
+	int n_old_lines = calc_deferred_lines(deferred_old);
+	int n_new_lines = calc_deferred_lines(deferred_new);
+	int individual_chars = (n_old_lines == n_new_lines ? 1 : 0);
+
+	iter_old = deferred_old;
+	iter_new = deferred_new;
+	while (iter_old || iter_new) {
+		if (iter_old && iter_new)
+			print_ssdiff_line("changed", iter_old->line_no,
+					  iter_old->line,
+					  iter_new->line_no, iter_new->line,
+					  individual_chars);
+		else if (iter_old)
+			print_ssdiff_line("changed", iter_old->line_no,
+					  iter_old->line, -1, NULL, 0);
+		else if (iter_new)
+			print_ssdiff_line("changed", -1, NULL,
+					  iter_new->line_no, iter_new->line, 0);
+		if (iter_old) {
+			tmp = iter_old->next;
+			free(iter_old);
+			iter_old = tmp;
+		}
+
+		if (iter_new) {
+			tmp = iter_new->next;
+			free(iter_new);
+			iter_new = tmp;
+		}
+	}
+}
+
+void cgit_ssdiff_print_deferred_lines(void)
+{
+	if (!deferred_old && !deferred_new)
+		return;
+	if (deferred_old && !deferred_new)
+		print_deferred_old_lines();
+	else if (!deferred_old && deferred_new)
+		print_deferred_new_lines();
+	else
+		print_deferred_changed_lines();
+	deferred_old = deferred_old_last = NULL;
+	deferred_new = deferred_new_last = NULL;
+}
+
+/*
+ * print a single line returned from xdiff
+ */
+void cgit_ssdiff_line_cb(char *line, int len)
+{
+	char c = line[len - 1];
+	line[len - 1] = '\0';
+	if (line[0] == '@') {
+		current_old_line = line_from_hunk(line, '-');
+		current_new_line = line_from_hunk(line, '+');
+	}
+
+	if (line[0] == ' ') {
+		if (deferred_old || deferred_new)
+			cgit_ssdiff_print_deferred_lines();
+		print_ssdiff_line("ctx", current_old_line, line,
+				  current_new_line, line, 0);
+		current_old_line += 1;
+		current_new_line += 1;
+	} else if (line[0] == '+') {
+		deferred_new_add(line, current_new_line);
+		current_new_line += 1;
+	} else if (line[0] == '-') {
+		deferred_old_add(line, current_old_line);
+		current_old_line += 1;
+	} else if (line[0] == '@') {
+		html("<tr><td colspan='4' class='hunk'>");
+		html_txt(line);
+		html("</td></tr>");
+	} else {
+		html("<tr><td colspan='4' class='ctx'>");
+		html_txt(line);
+		html("</td></tr>");
+	}
+	line[len - 1] = c;
+}
+
+void cgit_ssdiff_header_begin(void)
+{
+	current_old_line = -1;
+	current_new_line = -1;
+	html("<tr><td class='space' colspan='4'><div></div></td></tr>");
+	html("<tr><td class='head' colspan='4'>");
+}
+
+void cgit_ssdiff_header_end(void)
+{
+	html("</td></tr>");
+}
+
+void cgit_ssdiff_footer(void)
+{
+	if (deferred_old || deferred_new)
+		cgit_ssdiff_print_deferred_lines();
+	html("<tr><td class='foot' colspan='4'></td></tr>");
+}
diff --git a/www/git.causal.agency/cgit/ui-ssdiff.h b/www/git.causal.agency/cgit/ui-ssdiff.h
new file mode 100644
index 00000000..11f27144
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-ssdiff.h
@@ -0,0 +1,25 @@
+#ifndef UI_SSDIFF_H
+#define UI_SSDIFF_H
+
+/*
+ * ssdiff line limits
+ */
+#ifndef MAX_SSDIFF_M
+#define MAX_SSDIFF_M 128
+#endif
+
+#ifndef MAX_SSDIFF_N
+#define MAX_SSDIFF_N 128
+#endif
+#define MAX_SSDIFF_SIZE ((MAX_SSDIFF_M) * (MAX_SSDIFF_N))
+
+extern void cgit_ssdiff_print_deferred_lines(void);
+
+extern void cgit_ssdiff_line_cb(char *line, int len);
+
+extern void cgit_ssdiff_header_begin(void);
+extern void cgit_ssdiff_header_end(void);
+
+extern void cgit_ssdiff_footer(void);
+
+#endif /* UI_SSDIFF_H */
diff --git a/www/git.causal.agency/cgit/ui-stats.c b/www/git.causal.agency/cgit/ui-stats.c
new file mode 100644
index 00000000..09b3625e
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-stats.c
@@ -0,0 +1,426 @@
+#include "cgit.h"
+#include "ui-stats.h"
+#include "html.h"
+#include "ui-shared.h"
+
+struct authorstat {
+	long total;
+	struct string_list list;
+};
+
+#define DAY_SECS (60 * 60 * 24)
+#define WEEK_SECS (DAY_SECS * 7)
+
+static void trunc_week(struct tm *tm)
+{
+	time_t t = timegm(tm);
+	t -= ((tm->tm_wday + 6) % 7) * DAY_SECS;
+	gmtime_r(&t, tm);
+}
+
+static void dec_week(struct tm *tm)
+{
+	time_t t = timegm(tm);
+	t -= WEEK_SECS;
+	gmtime_r(&t, tm);
+}
+
+static void inc_week(struct tm *tm)
+{
+	time_t t = timegm(tm);
+	t += WEEK_SECS;
+	gmtime_r(&t, tm);
+}
+
+static char *pretty_week(struct tm *tm)
+{
+	static char buf[10];
+
+	strftime(buf, sizeof(buf), "W%V %G", tm);
+	return buf;
+}
+
+static void trunc_month(struct tm *tm)
+{
+	tm->tm_mday = 1;
+}
+
+static void dec_month(struct tm *tm)
+{
+	tm->tm_mon--;
+	if (tm->tm_mon < 0) {
+		tm->tm_year--;
+		tm->tm_mon = 11;
+	}
+}
+
+static void inc_month(struct tm *tm)
+{
+	tm->tm_mon++;
+	if (tm->tm_mon > 11) {
+		tm->tm_year++;
+		tm->tm_mon = 0;
+	}
+}
+
+static char *pretty_month(struct tm *tm)
+{
+	static const char *months[] = {
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+	};
+	return fmt("%s %d", months[tm->tm_mon], tm->tm_year + 1900);
+}
+
+static void trunc_quarter(struct tm *tm)
+{
+	trunc_month(tm);
+	while (tm->tm_mon % 3 != 0)
+		dec_month(tm);
+}
+
+static void dec_quarter(struct tm *tm)
+{
+	dec_month(tm);
+	dec_month(tm);
+	dec_month(tm);
+}
+
+static void inc_quarter(struct tm *tm)
+{
+	inc_month(tm);
+	inc_month(tm);
+	inc_month(tm);
+}
+
+static char *pretty_quarter(struct tm *tm)
+{
+	return fmt("Q%d %d", tm->tm_mon / 3 + 1, tm->tm_year + 1900);
+}
+
+static void trunc_year(struct tm *tm)
+{
+	trunc_month(tm);
+	tm->tm_mon = 0;
+}
+
+static void dec_year(struct tm *tm)
+{
+	tm->tm_year--;
+}
+
+static void inc_year(struct tm *tm)
+{
+	tm->tm_year++;
+}
+
+static char *pretty_year(struct tm *tm)
+{
+	return fmt("%d", tm->tm_year + 1900);
+}
+
+static const struct cgit_period periods[] = {
+	{'w', "week", 12, 4, trunc_week, dec_week, inc_week, pretty_week},
+	{'m', "month", 12, 4, trunc_month, dec_month, inc_month, pretty_month},
+	{'q', "quarter", 12, 4, trunc_quarter, dec_quarter, inc_quarter, pretty_quarter},
+	{'y', "year", 12, 4, trunc_year, dec_year, inc_year, pretty_year},
+};
+
+/* Given a period code or name, return a period index (1, 2, 3 or 4)
+ * and update the period pointer to the correcsponding struct.
+ * If no matching code is found, return 0.
+ */
+int cgit_find_stats_period(const char *expr, const struct cgit_period **period)
+{
+	int i;
+	char code = '\0';
+
+	if (!expr)
+		return 0;
+
+	if (strlen(expr) == 1)
+		code = expr[0];
+
+	for (i = 0; i < sizeof(periods) / sizeof(periods[0]); i++)
+		if (periods[i].code == code || !strcmp(periods[i].name, expr)) {
+			if (period)
+				*period = &periods[i];
+			return i + 1;
+		}
+	return 0;
+}
+
+const char *cgit_find_stats_periodname(int idx)
+{
+	if (idx > 0 && idx < 4)
+		return periods[idx - 1].name;
+	else
+		return "";
+}
+
+static void add_commit(struct string_list *authors, struct commit *commit,
+	const struct cgit_period *period)
+{
+	struct commitinfo *info;
+	struct string_list_item *author, *item;
+	struct authorstat *authorstat;
+	struct string_list *items;
+	char *tmp;
+	struct tm date;
+	time_t t;
+	uintptr_t *counter;
+
+	info = cgit_parse_commit(commit);
+	tmp = xstrdup(info->author);
+	author = string_list_insert(authors, tmp);
+	if (!author->util)
+		author->util = xcalloc(1, sizeof(struct authorstat));
+	else
+		free(tmp);
+	authorstat = author->util;
+	items = &authorstat->list;
+	t = info->committer_date;
+	gmtime_r(&t, &date);
+	period->trunc(&date);
+	tmp = xstrdup(period->pretty(&date));
+	item = string_list_insert(items, tmp);
+	counter = (uintptr_t *)&item->util;
+	if (*counter)
+		free(tmp);
+	(*counter)++;
+
+	authorstat->total++;
+	cgit_free_commitinfo(info);
+}
+
+static int cmp_total_commits(const void *a1, const void *a2)
+{
+	const struct string_list_item *i1 = a1;
+	const struct string_list_item *i2 = a2;
+	const struct authorstat *auth1 = i1->util;
+	const struct authorstat *auth2 = i2->util;
+
+	return auth2->total - auth1->total;
+}
+
+/* Walk the commit DAG and collect number of commits per author per
+ * timeperiod into a nested string_list collection.
+ */
+static struct string_list collect_stats(const struct cgit_period *period)
+{
+	struct string_list authors;
+	struct rev_info rev;
+	struct commit *commit;
+	const char *argv[] = {NULL, ctx.qry.head, NULL, NULL, NULL, NULL};
+	int argc = 3;
+	time_t now;
+	long i;
+	struct tm tm;
+	char tmp[11];
+
+	time(&now);
+	gmtime_r(&now, &tm);
+	period->trunc(&tm);
+	for (i = 1; i < period->count; i++)
+		period->dec(&tm);
+	strftime(tmp, sizeof(tmp), "%Y-%m-%d", &tm);
+	argv[2] = xstrdup(fmt("--since=%s", tmp));
+	if (ctx.qry.path) {
+		argv[3] = "--";
+		argv[4] = ctx.qry.path;
+		argc += 2;
+	}
+	init_revisions(&rev, NULL);
+	rev.abbrev = DEFAULT_ABBREV;
+	rev.commit_format = CMIT_FMT_DEFAULT;
+	rev.max_parents = 1;
+	rev.verbose_header = 1;
+	rev.show_root_diff = 0;
+	setup_revisions(argc, argv, &rev, NULL);
+	prepare_revision_walk(&rev);
+	memset(&authors, 0, sizeof(authors));
+	while ((commit = get_revision(&rev)) != NULL) {
+		add_commit(&authors, commit, period);
+		free_commit_buffer(the_repository->parsed_objects, commit);
+		free_commit_list(commit->parents);
+		commit->parents = NULL;
+	}
+	return authors;
+}
+
+static void print_combined_authorrow(struct string_list *authors, int from,
+				     int to, const char *name,
+				     const char *leftclass,
+				     const char *centerclass,
+				     const char *rightclass,
+				     const struct cgit_period *period)
+{
+	struct string_list_item *author;
+	struct authorstat *authorstat;
+	struct string_list *items;
+	struct string_list_item *date;
+	time_t now;
+	long i, j, total, subtotal;
+	struct tm tm;
+	char *tmp;
+
+	time(&now);
+	gmtime_r(&now, &tm);
+	period->trunc(&tm);
+	for (i = 1; i < period->count; i++)
+		period->dec(&tm);
+
+	total = 0;
+	htmlf("<tr><td class='%s'>%s</td>", leftclass,
+		fmt(name, to - from + 1));
+	for (j = 0; j < period->count; j++) {
+		tmp = period->pretty(&tm);
+		period->inc(&tm);
+		subtotal = 0;
+		for (i = from; i <= to; i++) {
+			author = &authors->items[i];
+			authorstat = author->util;
+			items = &authorstat->list;
+			date = string_list_lookup(items, tmp);
+			if (date)
+				subtotal += (uintptr_t)date->util;
+		}
+		htmlf("<td class='%s'>%ld</td>", centerclass, subtotal);
+		total += subtotal;
+	}
+	htmlf("<td class='%s'>%ld</td></tr>", rightclass, total);
+}
+
+static void print_authors(struct string_list *authors, int top,
+			  const struct cgit_period *period)
+{
+	struct string_list_item *author;
+	struct authorstat *authorstat;
+	struct string_list *items;
+	struct string_list_item *date;
+	time_t now;
+	long i, j, total;
+	struct tm tm;
+	char *tmp;
+
+	time(&now);
+	gmtime_r(&now, &tm);
+	period->trunc(&tm);
+	for (i = 1; i < period->count; i++)
+		period->dec(&tm);
+
+	html("<table class='stats'><tr><th>Author</th>");
+	for (j = 0; j < period->count; j++) {
+		tmp = period->pretty(&tm);
+		htmlf("<th>%s</th>", tmp);
+		period->inc(&tm);
+	}
+	html("<th>Total</th></tr>\n");
+
+	if (top <= 0 || top > authors->nr)
+		top = authors->nr;
+
+	for (i = 0; i < top; i++) {
+		author = &authors->items[i];
+		html("<tr><td class='left'>");
+		html_txt(author->string);
+		html("</td>");
+		authorstat = author->util;
+		items = &authorstat->list;
+		total = 0;
+		for (j = 0; j < period->count; j++)
+			period->dec(&tm);
+		for (j = 0; j < period->count; j++) {
+			tmp = period->pretty(&tm);
+			period->inc(&tm);
+			date = string_list_lookup(items, tmp);
+			if (!date)
+				html("<td>0</td>");
+			else {
+				htmlf("<td>%lu</td>", (uintptr_t)date->util);
+				total += (uintptr_t)date->util;
+			}
+		}
+		htmlf("<td class='sum'>%ld</td></tr>", total);
+	}
+
+	if (top < authors->nr)
+		print_combined_authorrow(authors, top, authors->nr - 1,
+			"Others (%ld)", "left", "", "sum", period);
+
+	print_combined_authorrow(authors, 0, authors->nr - 1, "Total",
+		"total", "sum", "sum", period);
+	html("</table>");
+}
+
+/* Create a sorted string_list with one entry per author. The util-field
+ * for each author is another string_list which is used to calculate the
+ * number of commits per time-interval.
+ */
+void cgit_show_stats(void)
+{
+	struct string_list authors;
+	const struct cgit_period *period;
+	int top, i;
+	const char *code = "w";
+
+	if (ctx.qry.period)
+		code = ctx.qry.period;
+
+	i = cgit_find_stats_period(code, &period);
+	if (!i) {
+		cgit_print_error_page(404, "Not found",
+			"Unknown statistics type: %c", code[0]);
+		return;
+	}
+	if (i > ctx.repo->max_stats) {
+		cgit_print_error_page(400, "Bad request",
+			"Statistics type disabled: %s", period->name);
+		return;
+	}
+	authors = collect_stats(period);
+	qsort(authors.items, authors.nr, sizeof(struct string_list_item),
+		cmp_total_commits);
+
+	top = ctx.qry.ofs;
+	if (!top)
+		top = 10;
+
+	cgit_print_layout_start();
+	html("<div class='cgit-panel'>");
+	html("<b>stat options</b>");
+	html("<form method='get'>");
+	cgit_add_hidden_formfields(1, 0, "stats");
+	html("<table><tr><td colspan='2'/></tr>");
+	if (ctx.repo->max_stats > 1) {
+		html("<tr><td class='label'>Period:</td>");
+		html("<td class='ctrl'><select name='period' onchange='this.form.submit();'>");
+		for (i = 0; i < ctx.repo->max_stats; i++)
+			html_option(fmt("%c", periods[i].code),
+				    periods[i].name, fmt("%c", period->code));
+		html("</select></td></tr>");
+	}
+	html("<tr><td class='label'>Authors:</td>");
+	html("<td class='ctrl'><select name='ofs' onchange='this.form.submit();'>");
+	html_intoption(10, "10", top);
+	html_intoption(25, "25", top);
+	html_intoption(50, "50", top);
+	html_intoption(100, "100", top);
+	html_intoption(-1, "all", top);
+	html("</select></td></tr>");
+	html("<tr><td/><td class='ctrl'>");
+	html("<noscript><input type='submit' value='Reload'/></noscript>");
+	html("</td></tr></table>");
+	html("</form>");
+	html("</div>");
+	htmlf("<h2>Commits per author per %s", period->name);
+	if (ctx.qry.path) {
+		html(" (path '");
+		html_txt(ctx.qry.path);
+		html("')");
+	}
+	html("</h2>");
+	print_authors(&authors, top, period);
+	cgit_print_layout_end();
+}
+
diff --git a/www/git.causal.agency/cgit/ui-stats.h b/www/git.causal.agency/cgit/ui-stats.h
new file mode 100644
index 00000000..0e61b03d
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-stats.h
@@ -0,0 +1,28 @@
+#ifndef UI_STATS_H
+#define UI_STATS_H
+
+#include "cgit.h"
+
+struct cgit_period {
+	const char code;
+	const char *name;
+	int max_periods;
+	int count;
+
+	/* Convert a tm value to the first day in the period */
+	void (*trunc)(struct tm *tm);
+
+	/* Update tm value to start of next/previous period */
+	void (*dec)(struct tm *tm);
+	void (*inc)(struct tm *tm);
+
+	/* Pretty-print a tm value */
+	char *(*pretty)(struct tm *tm);
+};
+
+extern int cgit_find_stats_period(const char *expr, const struct cgit_period **period);
+extern const char *cgit_find_stats_periodname(int idx);
+
+extern void cgit_show_stats(void);
+
+#endif /* UI_STATS_H */
diff --git a/www/git.causal.agency/cgit/ui-summary.c b/www/git.causal.agency/cgit/ui-summary.c
new file mode 100644
index 00000000..947812a8
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-summary.c
@@ -0,0 +1,148 @@
+/* ui-summary.c: functions for generating repo summary page
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-summary.h"
+#include "html.h"
+#include "ui-blob.h"
+#include "ui-log.h"
+#include "ui-plain.h"
+#include "ui-refs.h"
+#include "ui-shared.h"
+
+static int urls;
+
+static void print_url(const char *url)
+{
+	int columns = 3;
+
+	if (ctx.repo->enable_log_filecount)
+		columns++;
+	if (ctx.repo->enable_log_linecount)
+		columns++;
+
+	if (urls++ == 0) {
+		htmlf("<tr class='nohover'><td colspan='%d'>&nbsp;</td></tr>", columns);
+		htmlf("<tr class='nohover'><th class='left' colspan='%d'>Clone</th></tr>\n", columns);
+	}
+
+	htmlf("<tr><td colspan='%d'><a rel='vcs-git' href='", columns);
+	html_url_path(url);
+	html("' title='");
+	html_attr(ctx.repo->name);
+	html(" Git repository'>");
+	html_txt(url);
+	html("</a></td></tr>\n");
+}
+
+void cgit_print_summary(void)
+{
+	int columns = 3;
+
+	if (ctx.repo->enable_log_filecount)
+		columns++;
+	if (ctx.repo->enable_log_linecount)
+		columns++;
+
+	cgit_print_layout_start();
+	html("<table summary='repository info' class='list nowrap'>");
+	cgit_print_branches(ctx.cfg.summary_branches);
+	htmlf("<tr class='nohover'><td colspan='%d'>&nbsp;</td></tr>", columns);
+	cgit_print_tags(ctx.cfg.summary_tags);
+	if (ctx.cfg.summary_log > 0) {
+		htmlf("<tr class='nohover'><td colspan='%d'>&nbsp;</td></tr>", columns);
+		cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL,
+			       NULL, NULL, 0, 0, 0);
+	}
+	urls = 0;
+	cgit_add_clone_urls(print_url);
+	html("</table>");
+	cgit_print_layout_end();
+}
+
+/* The caller must free the return value. */
+static char* append_readme_path(const char *filename, const char *ref, const char *path)
+{
+	char *file, *base_dir, *full_path, *resolved_base = NULL, *resolved_full = NULL;
+	/* If a subpath is specified for the about page, make it relative
+	 * to the directory containing the configured readme. */
+
+	file = xstrdup(filename);
+	base_dir = dirname(file);
+	if (!strcmp(base_dir, ".") || !strcmp(base_dir, "..")) {
+		if (!ref) {
+			free(file);
+			return NULL;
+		}
+		full_path = xstrdup(path);
+	} else
+		full_path = fmtalloc("%s/%s", base_dir, path);
+
+	if (!ref) {
+		resolved_base = realpath(base_dir, NULL);
+		resolved_full = realpath(full_path, NULL);
+		if (!resolved_base || !resolved_full || !starts_with(resolved_full, resolved_base)) {
+			free(full_path);
+			full_path = NULL;
+		}
+	}
+
+	free(file);
+	free(resolved_base);
+	free(resolved_full);
+
+	return full_path;
+}
+
+void cgit_print_repo_readme(const char *path)
+{
+	char *filename, *ref, *mimetype;
+	int free_filename = 0;
+
+	mimetype = get_mimetype_for_filename(path);
+	if (mimetype && (!strncmp(mimetype, "image/", 6) || !strncmp(mimetype, "video/", 6))) {
+		ctx.page.mimetype = mimetype;
+		ctx.page.charset = NULL;
+		cgit_print_plain();
+		free(mimetype);
+		return;
+	}
+	free(mimetype);
+
+	cgit_print_layout_start();
+	if (ctx.repo->readme.nr == 0)
+		goto done;
+
+	filename = ctx.repo->readme.items[0].string;
+	ref = ctx.repo->readme.items[0].util;
+
+	if (path) {
+		free_filename = 1;
+		filename = append_readme_path(filename, ref, path);
+		if (!filename)
+			goto done;
+	}
+
+	/* Print the calculated readme, either from the git repo or from the
+	 * filesystem, while applying the about-filter.
+	 */
+	html("<div id='summary'>");
+	cgit_open_filter(ctx.repo->about_filter, filename);
+	if (ref)
+		cgit_print_file(filename, ref, 1);
+	else
+		html_include(filename);
+	cgit_close_filter(ctx.repo->about_filter);
+
+	html("</div>");
+	if (free_filename)
+		free(filename);
+
+done:
+	cgit_print_layout_end();
+}
diff --git a/www/git.causal.agency/cgit/ui-summary.h b/www/git.causal.agency/cgit/ui-summary.h
new file mode 100644
index 00000000..cba696af
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-summary.h
@@ -0,0 +1,7 @@
+#ifndef UI_SUMMARY_H
+#define UI_SUMMARY_H
+
+extern void cgit_print_summary(void);
+extern void cgit_print_repo_readme(const char *path);
+
+#endif /* UI_SUMMARY_H */
diff --git a/www/git.causal.agency/cgit/ui-tag.c b/www/git.causal.agency/cgit/ui-tag.c
new file mode 100644
index 00000000..05952429
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-tag.c
@@ -0,0 +1,120 @@
+/* ui-tag.c: display a tag
+ *
+ * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-tag.h"
+#include "html.h"
+#include "ui-shared.h"
+
+static void print_tag_content(char *buf)
+{
+	char *p;
+
+	if (!buf)
+		return;
+
+	html("<div class='commit-subject'>");
+	p = strchr(buf, '\n');
+	if (p)
+		*p = '\0';
+	html_txt(buf);
+	html("</div>");
+	if (p) {
+		html("<pre class='commit-msg'>");
+		html_txt(++p);
+		html("</pre>");
+	}
+}
+
+static void print_download_links(char *revname)
+{
+	html("<tr><th>download</th><td class='oid'>");
+	cgit_print_snapshot_links(ctx.repo, revname, "<br/>");
+	html("</td></tr>");
+}
+
+void cgit_print_tag(char *revname)
+{
+	struct strbuf fullref = STRBUF_INIT;
+	struct object_id oid;
+	struct object *obj;
+
+	if (!revname)
+		revname = ctx.qry.head;
+
+	strbuf_addf(&fullref, "refs/tags/%s", revname);
+	if (get_oid(fullref.buf, &oid)) {
+		cgit_print_error_page(404, "Not found",
+			"Bad tag reference: %s", revname);
+		goto cleanup;
+	}
+	obj = parse_object(the_repository, &oid);
+	if (!obj) {
+		cgit_print_error_page(500, "Internal server error",
+			"Bad object id: %s", oid_to_hex(&oid));
+		goto cleanup;
+	}
+	if (obj->type == OBJ_TAG) {
+		struct tag *tag;
+		struct taginfo *info;
+
+		tag = lookup_tag(the_repository, &oid);
+		if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
+			cgit_print_error_page(500, "Internal server error",
+				"Bad tag object: %s", revname);
+			goto cleanup;
+		}
+		cgit_print_layout_start();
+		html("<table class='commit-info'>\n");
+		html("<tr><td>tag name</td><td>");
+		html_txt(revname);
+		htmlf(" (%s)</td></tr>\n", oid_to_hex(&oid));
+		if (info->tagger_date > 0) {
+			html("<tr><td>tag date</td><td>");
+			html_txt(show_date(info->tagger_date, info->tagger_tz,
+						cgit_date_mode(DATE_ISO8601)));
+			html("</td></tr>\n");
+		}
+		if (info->tagger) {
+			html("<tr><td>tagged by</td><td>");
+			cgit_open_filter(ctx.repo->email_filter, info->tagger_email, "tag");
+			html_txt(info->tagger);
+			if (info->tagger_email && !ctx.cfg.noplainemail) {
+				html(" ");
+				html_txt(info->tagger_email);
+			}
+			cgit_close_filter(ctx.repo->email_filter);
+			html("</td></tr>\n");
+		}
+		html("<tr><td>tagged object</td><td class='oid'>");
+		cgit_object_link(tag->tagged);
+		html("</td></tr>\n");
+		if (ctx.repo->snapshots)
+			print_download_links(revname);
+		html("</table>\n");
+		print_tag_content(info->msg);
+		cgit_print_layout_end();
+		cgit_free_taginfo(info);
+	} else {
+		cgit_print_layout_start();
+		html("<table class='commit-info'>\n");
+		html("<tr><td>tag name</td><td>");
+		html_txt(revname);
+		html("</td></tr>\n");
+		html("<tr><td>tagged object</td><td class='oid'>");
+		cgit_object_link(obj);
+		html("</td></tr>\n");
+		if (ctx.repo->snapshots)
+			print_download_links(revname);
+		html("</table>\n");
+		cgit_print_layout_end();
+	}
+
+cleanup:
+	strbuf_release(&fullref);
+}
diff --git a/www/git.causal.agency/cgit/ui-tag.h b/www/git.causal.agency/cgit/ui-tag.h
new file mode 100644
index 00000000..d295cdcd
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-tag.h
@@ -0,0 +1,6 @@
+#ifndef UI_TAG_H
+#define UI_TAG_H
+
+extern void cgit_print_tag(char *revname);
+
+#endif /* UI_TAG_H */
diff --git a/www/git.causal.agency/cgit/ui-tree.c b/www/git.causal.agency/cgit/ui-tree.c
new file mode 100644
index 00000000..21e0b884
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-tree.c
@@ -0,0 +1,411 @@
+/* ui-tree.c: functions for tree output
+ *
+ * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ *   (see COPYING for full license text)
+ */
+
+#include "cgit.h"
+#include "ui-tree.h"
+#include "html.h"
+#include "ui-shared.h"
+
+struct walk_tree_context {
+	char *curr_rev;
+	char *match_path;
+	int state;
+};
+
+static void print_text_buffer(const char *name, char *buf, unsigned long size)
+{
+	unsigned long lineno, idx;
+	const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n";
+
+	html("<table summary='blob content' class='blob'>\n");
+
+	if (ctx.cfg.enable_tree_linenumbers) {
+		html("<tr><td class='linenumbers'><pre>");
+		idx = 0;
+		lineno = 0;
+
+		if (size) {
+			htmlf(numberfmt, ++lineno);
+			while (idx < size - 1) { // skip absolute last newline
+				if (buf[idx] == '\n')
+					htmlf(numberfmt, ++lineno);
+				idx++;
+			}
+		}
+		html("</pre></td>\n");
+	}
+	else {
+		html("<tr>\n");
+	}
+
+	if (ctx.repo->source_filter) {
+		char *filter_arg = xstrdup(name);
+		html("<td class='lines'><pre><code>");
+		cgit_open_filter(ctx.repo->source_filter, filter_arg);
+		html_raw(buf, size);
+		cgit_close_filter(ctx.repo->source_filter);
+		free(filter_arg);
+		html("</code></pre></td></tr></table>\n");
+		return;
+	}
+
+	html("<td class='lines'><pre><code>");
+	html_txt(buf);
+	html("</code></pre></td></tr></table>\n");
+}
+
+#define ROWLEN 32
+
+static void print_binary_buffer(char *buf, unsigned long size)
+{
+	unsigned long ofs, idx;
+	static char ascii[ROWLEN + 1];
+
+	html("<table summary='blob content' class='bin-blob'>\n");
+	html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>");
+	for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) {
+		htmlf("<tr><td class='right'>%04lx</td><td class='hex'>", ofs);
+		for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
+			htmlf("%*s%02x",
+			      idx == 16 ? 4 : 1, "",
+			      buf[idx] & 0xff);
+		html(" </td><td class='hex'>");
+		for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
+			ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.';
+		ascii[idx] = '\0';
+		html_txt(ascii);
+		html("</td></tr>\n");
+	}
+	html("</table>\n");
+}
+
+static void print_object(const struct object_id *oid, const char *path, const char *basename, const char *rev)
+{
+	enum object_type type;
+	char *buf;
+	unsigned long size;
+	int is_binary;
+
+	type = oid_object_info(the_repository, oid, &size);
+	if (type == OBJ_BAD) {
+		cgit_print_error_page(404, "Not found",
+			"Bad object name: %s", oid_to_hex(oid));
+		return;
+	}
+
+	buf = read_object_file(oid, &type, &size);
+	if (!buf) {
+		cgit_print_error_page(500, "Internal server error",
+			"Error reading object %s", oid_to_hex(oid));
+		return;
+	}
+	is_binary = buffer_is_binary(buf, size);
+
+	cgit_set_title_from_path(path);
+
+	cgit_print_layout_start();
+	htmlf("blob: %s (", oid_to_hex(oid));
+	cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
+		        rev, path);
+	if (ctx.repo->enable_blame && !is_binary) {
+		html(") (");
+		cgit_blame_link("blame", NULL, NULL, ctx.qry.head,
+			        rev, path);
+	}
+	html(")\n");
+
+	if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
+		htmlf("<div class='error'>blob size (%ldKB) exceeds display size limit (%dKB).</div>",
+				size / 1024, ctx.cfg.max_blob_size);
+		return;
+	}
+
+	if (is_binary)
+		print_binary_buffer(buf, size);
+	else
+		print_text_buffer(basename, buf, size);
+
+	free(buf);
+}
+
+struct single_tree_ctx {
+	struct strbuf *path;
+	struct object_id oid;
+	char *name;
+	size_t count;
+};
+
+static int single_tree_cb(const struct object_id *oid, struct strbuf *base,
+			  const char *pathname, unsigned mode, void *cbdata)
+{
+	struct single_tree_ctx *ctx = cbdata;
+
+	if (++ctx->count > 1)
+		return -1;
+
+	if (!S_ISDIR(mode)) {
+		ctx->count = 2;
+		return -1;
+	}
+
+	ctx->name = xstrdup(pathname);
+	oidcpy(&ctx->oid, oid);
+	strbuf_addf(ctx->path, "/%s", pathname);
+	return 0;
+}
+
+static void write_tree_link(const struct object_id *oid, char *name,
+			    char *rev, struct strbuf *fullpath)
+{
+	size_t initial_length = fullpath->len;
+	struct tree *tree;
+	struct single_tree_ctx tree_ctx = {
+		.path = fullpath,
+		.count = 1,
+	};
+	struct pathspec paths = {
+		.nr = 0
+	};
+
+	oidcpy(&tree_ctx.oid, oid);
+
+	while (tree_ctx.count == 1) {
+		cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, rev,
+			       fullpath->buf);
+
+		tree = lookup_tree(the_repository, &tree_ctx.oid);
+		if (!tree)
+			return;
+
+		free(tree_ctx.name);
+		tree_ctx.name = NULL;
+		tree_ctx.count = 0;
+
+		read_tree(the_repository, tree, &paths, single_tree_cb, &tree_ctx);
+
+		if (tree_ctx.count != 1)
+			break;
+
+		html(" / ");
+		name = tree_ctx.name;
+	}
+
+	strbuf_setlen(fullpath, initial_length);
+}
+
+static int ls_item(const struct object_id *oid, struct strbuf *base,
+		const char *pathname, unsigned mode, void *cbdata)
+{
+	struct walk_tree_context *walk_tree_ctx = cbdata;
+	char *name;
+	struct strbuf fullpath = STRBUF_INIT;
+	struct strbuf linkpath = STRBUF_INIT;
+	struct strbuf class = STRBUF_INIT;
+	enum object_type type;
+	unsigned long size = 0;
+	char *buf;
+
+	name = xstrdup(pathname);
+	strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "",
+		    ctx.qry.path ? "/" : "", name);
+
+	if (!S_ISGITLINK(mode)) {
+		type = oid_object_info(the_repository, oid, &size);
+		if (type == OBJ_BAD) {
+			htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
+			      name,
+			      oid_to_hex(oid));
+			goto cleanup;
+		}
+	}
+
+	html("<tr><td class='ls-mode'>");
+	cgit_print_filemode(mode);
+	html("</td><td>");
+	if (S_ISGITLINK(mode)) {
+		cgit_submodule_link("ls-mod", fullpath.buf, oid_to_hex(oid));
+	} else if (S_ISDIR(mode)) {
+		write_tree_link(oid, name, walk_tree_ctx->curr_rev,
+				&fullpath);
+	} else {
+		char *ext = strrchr(name, '.');
+		strbuf_addstr(&class, "ls-blob");
+		if (ext)
+			strbuf_addf(&class, " %s", ext + 1);
+		cgit_tree_link(name, NULL, class.buf, ctx.qry.head,
+			       walk_tree_ctx->curr_rev, fullpath.buf);
+	}
+	if (S_ISLNK(mode)) {
+		html(" -> ");
+		buf = read_object_file(oid, &type, &size);
+		if (!buf) {
+			htmlf("Error reading object: %s", oid_to_hex(oid));
+			goto cleanup;
+		}
+		strbuf_addbuf(&linkpath, &fullpath);
+		strbuf_addf(&linkpath, "/../%s", buf);
+		strbuf_normalize_path(&linkpath);
+		cgit_tree_link(buf, NULL, class.buf, ctx.qry.head,
+			walk_tree_ctx->curr_rev, linkpath.buf);
+		free(buf);
+		strbuf_release(&linkpath);
+	}
+	htmlf("</td><td class='ls-size'>%li</td>", size);
+
+	html("<td>");
+	cgit_log_link("log", NULL, "button", ctx.qry.head,
+		      walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL,
+		      ctx.qry.showmsg, 0);
+	if (ctx.repo->max_stats) {
+		html(" ");
+		cgit_stats_link("stats", NULL, "button", ctx.qry.head,
+				fullpath.buf);
+	}
+	if (!S_ISGITLINK(mode)) {
+		html(" ");
+		cgit_plain_link("plain", NULL, "button", ctx.qry.head,
+				walk_tree_ctx->curr_rev, fullpath.buf);
+	}
+	if (!S_ISDIR(mode) && ctx.repo->enable_blame) {
+		html(" ");
+		cgit_blame_link("blame", NULL, "button", ctx.qry.head,
+				walk_tree_ctx->curr_rev, fullpath.buf);
+	}
+	html("</td></tr>\n");
+
+cleanup:
+	free(name);
+	strbuf_release(&fullpath);
+	strbuf_release(&class);
+	return 0;
+}
+
+static void ls_head(void)
+{
+	cgit_print_layout_start();
+	html("<table summary='tree listing' class='list'>\n");
+	html("<tr class='nohover'>");
+	html("<th class='left'>Mode</th>");
+	html("<th class='left'>Name</th>");
+	html("<th class='right'>Size</th>");
+	html("<th/>");
+	html("</tr>\n");
+}
+
+static void ls_tail(void)
+{
+	html("</table>\n");
+	cgit_print_layout_end();
+}
+
+static void ls_tree(const struct object_id *oid, const char *path, struct walk_tree_context *walk_tree_ctx)
+{
+	struct tree *tree;
+	struct pathspec paths = {
+		.nr = 0
+	};
+
+	tree = parse_tree_indirect(oid);
+	if (!tree) {
+		cgit_print_error_page(404, "Not found",
+			"Not a tree object: %s", oid_to_hex(oid));
+		return;
+	}
+
+	ls_head();
+	read_tree(the_repository, tree, &paths, ls_item, walk_tree_ctx);
+	ls_tail();
+}
+
+
+static int walk_tree(const struct object_id *oid, struct strbuf *base,
+		const char *pathname, unsigned mode, void *cbdata)
+{
+	struct walk_tree_context *walk_tree_ctx = cbdata;
+
+	if (walk_tree_ctx->state == 0) {
+		struct strbuf buffer = STRBUF_INIT;
+
+		strbuf_addbuf(&buffer, base);
+		strbuf_addstr(&buffer, pathname);
+		if (strcmp(walk_tree_ctx->match_path, buffer.buf))
+			return READ_TREE_RECURSIVE;
+
+		if (S_ISDIR(mode)) {
+			walk_tree_ctx->state = 1;
+			cgit_set_title_from_path(buffer.buf);
+			strbuf_release(&buffer);
+			ls_head();
+			return READ_TREE_RECURSIVE;
+		} else {
+			walk_tree_ctx->state = 2;
+			print_object(oid, buffer.buf, pathname, walk_tree_ctx->curr_rev);
+			strbuf_release(&buffer);
+			return 0;
+		}
+	}
+	ls_item(oid, base, pathname, mode, walk_tree_ctx);
+	return 0;
+}
+
+/*
+ * Show a tree or a blob
+ *   rev:  the commit pointing at the root tree object
+ *   path: path to tree or blob
+ */
+void cgit_print_tree(const char *rev, char *path)
+{
+	struct object_id oid;
+	struct commit *commit;
+	struct pathspec_item path_items = {
+		.match = path,
+		.len = path ? strlen(path) : 0
+	};
+	struct pathspec paths = {
+		.nr = path ? 1 : 0,
+		.items = &path_items
+	};
+	struct walk_tree_context walk_tree_ctx = {
+		.match_path = path,
+		.state = 0
+	};
+
+	if (!rev)
+		rev = ctx.qry.head;
+
+	if (get_oid(rev, &oid)) {
+		cgit_print_error_page(404, "Not found",
+			"Invalid revision name: %s", rev);
+		return;
+	}
+	commit = lookup_commit_reference(the_repository, &oid);
+	if (!commit || parse_commit(commit)) {
+		cgit_print_error_page(404, "Not found",
+			"Invalid commit reference: %s", rev);
+		return;
+	}
+
+	walk_tree_ctx.curr_rev = xstrdup(rev);
+
+	if (path == NULL) {
+		ls_tree(get_commit_tree_oid(commit), NULL, &walk_tree_ctx);
+		goto cleanup;
+	}
+
+	read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
+		  &paths, walk_tree, &walk_tree_ctx);
+	if (walk_tree_ctx.state == 1)
+		ls_tail();
+	else if (walk_tree_ctx.state == 2)
+		cgit_print_layout_end();
+	else
+		cgit_print_error_page(404, "Not found", "Path not found");
+
+cleanup:
+	free(walk_tree_ctx.curr_rev);
+}
diff --git a/www/git.causal.agency/cgit/ui-tree.h b/www/git.causal.agency/cgit/ui-tree.h
new file mode 100644
index 00000000..bbd34e35
--- /dev/null
+++ b/www/git.causal.agency/cgit/ui-tree.h
@@ -0,0 +1,6 @@
+#ifndef UI_TREE_H
+#define UI_TREE_H
+
+extern void cgit_print_tree(const char *rev, char *path);
+
+#endif /* UI_TREE_H */
diff --git a/www/git.causal.agency/cgitrc b/www/git.causal.agency/cgitrc
new file mode 100644
index 00000000..8ccd7c72
--- /dev/null
+++ b/www/git.causal.agency/cgitrc
@@ -0,0 +1,29 @@
+root-title=causal agency
+root-desc=“I think some people from the Gentoo project are behind this.”
+logo=
+
+clone-url=https://$HTTP_HOST/$CGIT_REPO_URL
+snapshots=tar.gz zip
+
+enable-blame=1
+enable-commit-graph=1
+enable-subject-links=1
+enable-follow-links=1
+enable-index-owner=0
+repository-sort=age
+branch-sort=age
+
+css=/custom.css
+email-filter=/usr/local/libexec/cgit-email
+about-filter=/usr/local/libexec/about-filter
+source-filter=/usr/local/libexec/source-filter
+owner-filter=/usr/local/libexec/owner-filter
+
+readme=:README.7
+readme=:README
+
+remove-suffix=1
+enable-git-config=1
+scan-path=/home/june/pub
+
+cache-size=1024
diff --git a/www/git.causal.agency/custom.css b/www/git.causal.agency/custom.css
new file mode 100644
index 00000000..3bc61c90
--- /dev/null
+++ b/www/git.causal.agency/custom.css
@@ -0,0 +1,86 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+@import url("cgit.css");
+
+* { line-height: 1.25em; }
+
+div#cgit {
+	max-width: 117ch;
+	margin: auto;
+	font-family: monospace;
+	-moz-tab-size: 4;
+	tab-size: 4;
+}
+
+div#cgit table#header td.sub {
+	border-top: none;
+}
+div#cgit table#header td.sub.right {
+	padding-right: 1em;
+}
+div#cgit table.tabs {
+	border-bottom: none;
+}
+div#cgit div.content {
+	border-bottom: none;
+}
+div#cgit table.list th a {
+	color: inherit;
+}
+div#cgit table.list tr:nth-child(even) {
+	background: inherit;
+}
+div#cgit table.list tr:hover {
+	background: inherit;
+}
+div#cgit table.list tr.nohover-highlight:hover:nth-child(even) {
+	background: inherit;
+}
+
+div#cgit table.blob td.linenumbers a:target {
+	color: goldenrod;
+	text-decoration: underline;
+	outline: none;
+}
+
+div#cgit div#summary {
+	max-width: 80ch;
+}
+
+/* for hilex(1) */
+div#cgit pre .Ke { color: dimgray; }
+div#cgit pre .Ma { color: green; }
+div#cgit pre .Co { color: navy; }
+div#cgit pre .St { color: teal; }
+div#cgit pre .Fo { color: teal; font-weight: bold; }
+div#cgit pre .Su { color: olive; }
+
+/* for htagml(1) */
+div#cgit pre a.tag { color: inherit; text-decoration: underline; }
+div#cgit pre a.tag:target { color: goldenrod; outline: none; }
+
+/* for mandoc(1) */
+table.head, table.foot { width: 100%; }
+td.head-rtitle, td.foot-os { text-align: right; }
+td.head-vol { text-align: center; }
+div.Pp { margin: 1ex 0ex; }
+div.Nd, div.Bf, div.Op { display: inline; }
+span.Pa, span.Ad { font-style: italic; }
+span.Ms { font-weight: bold; }
+dl.Bl-diag > dt { font-weight: bold; }
+code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn,
+code.Cd { font-weight: bold; font-family: inherit; }
+
+h1.Sh { font-size: 1.5em; }
+table.Nm td:first-child { padding-right: 1ch; }
+code.Fl { white-space: nowrap; }
+span.RsT { font-style: italic; }
+dl.Bl-tag:not(.Bl-compact) dt { margin-top: 1em; }
+ul.Bl-bullet:not(.Bl-compact) li { margin-top: 1em; }
+div.Bd-indent { margin-left: 4ch; }
+table.Bl-column { width: 100%; }
+table.foot { margin-top: 1em; }
+
+div#cgit a.permalink { color: inherit; }
diff --git a/www/git.causal.agency/owner-filter.sh b/www/git.causal.agency/owner-filter.sh
new file mode 100644
index 00000000..18e74cf1
--- /dev/null
+++ b/www/git.causal.agency/owner-filter.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -eu
+
+cat <<EOF
+<a href="https://liberapay.com/june/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
+EOF
diff --git a/www/git.causal.agency/source-filter.sh b/www/git.causal.agency/source-filter.sh
new file mode 100644
index 00000000..514272db
--- /dev/null
+++ b/www/git.causal.agency/source-filter.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+set -eu
+
+ctags=/usr/bin/ctags
+mtags=/usr/local/libexec/mtags
+hilex=/usr/local/libexec/hilex
+htagml=/usr/local/libexec/htagml
+
+case "$1" in
+	(*.[chlmy]|Makefile|*.mk|*.[1-9]|.profile|.shrc|*.sh)
+		tmp=$(mktemp -d -t source-filter)
+		trap 'rm -fr "${tmp}"' EXIT
+		cd "${tmp}"
+		cat >"$1"
+		touch tags
+		case "$1" in
+			(*.[chlmy]) $ctags -w "$1";;
+			(*) $mtags "$1";;
+		esac
+		$hilex -f html "$1" | $htagml -i "$1"
+		;;
+	(*)
+		exec $hilex -t -n "$1" -f html
+		;;
+esac
diff --git a/www/temp.causal.agency/.gitignore b/www/temp.causal.agency/.gitignore
new file mode 100644
index 00000000..e31ee94e
--- /dev/null
+++ b/www/temp.causal.agency/.gitignore
@@ -0,0 +1 @@
+up
diff --git a/www/temp.causal.agency/Makefile b/www/temp.causal.agency/Makefile
new file mode 100644
index 00000000..3e908305
--- /dev/null
+++ b/www/temp.causal.agency/Makefile
@@ -0,0 +1,16 @@
+WEBROOT = /usr/local/www/temp.causal.agency
+
+CFLAGS += -std=c11 -Wall -Wextra -Wpedantic -I/usr/local/include
+LDFLAGS += -static -L/usr/local/lib
+LDLIBS = -lkcgihtml -lkcgi -lz -lmd
+
+up:
+
+clean:
+	rm -f up
+
+install: up
+	install -m 700 up ${WEBROOT}/up
+
+uninstall:
+	rm -f ${WEBROOT}/up
diff --git a/www/temp.causal.agency/up.c b/www/temp.causal.agency/up.c
new file mode 100644
index 00000000..885b8acd
--- /dev/null
+++ b/www/temp.causal.agency/up.c
@@ -0,0 +1,156 @@
+/* Copyright (C) 2020  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 <fcntl.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capsicum.h>
+#include <sys/types.h>
+#include <sysexits.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <kcgi.h>
+#include <kcgihtml.h>
+
+static int cwd = -1;
+
+static const struct kvalid Key = { NULL, "file" };
+
+static enum kcgi_err head(struct kreq *req, enum khttp http, enum kmime mime) {
+	return khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[http])
+		|| khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[mime]);
+}
+
+static enum kcgi_err fail(struct kreq *req, enum khttp http) {
+	return head(req, http, KMIME_TEXT_PLAIN)
+		|| khttp_body(req)
+		|| khttp_printf(req, "%s\n", khttps[http]);
+}
+
+static enum kcgi_err handle(struct kreq *req) {
+	if (req->page) return fail(req, KHTTP_404);
+
+	if (req->method == KMETHOD_GET) {
+		struct khtmlreq html;
+		struct khtmlreq *h = &html;
+		return head(req, KHTTP_200, KMIME_TEXT_HTML)
+			|| khttp_body(req)
+			|| khtml_open(h, req, 0)
+			|| khtml_elem(h, KELEM_DOCTYPE)
+			|| khtml_elem(h, KELEM_TITLE)
+			|| khtml_puts(h, "Upload")
+			|| khtml_closeelem(h, 1)
+			|| khtml_attr(
+				h, KELEM_FORM,
+				KATTR_METHOD, "post",
+				KATTR_ACTION, "",
+				KATTR_ENCTYPE, "multipart/form-data",
+				KATTR__MAX
+			)
+			|| khtml_attr(
+				h, KELEM_INPUT,
+				KATTR_TYPE, "file",
+				KATTR_NAME, Key.name,
+				KATTR__MAX
+			)
+			|| khtml_attr(
+				h, KELEM_INPUT,
+				KATTR_TYPE, "submit",
+				KATTR_VALUE, "Upload",
+				KATTR__MAX
+			)
+			|| khtml_close(h);
+
+	} else if (req->method == KMETHOD_POST) {
+		struct kpair *field = req->fieldmap[0];
+		if (!field || !field->valsz) return fail(req, KHTTP_400);
+
+		char name[256];
+		const char *ext = strrchr(field->file, '.');
+		if (!ext) ext = "";
+		snprintf(
+			name, sizeof(name), "%jx%08x%s",
+			(intmax_t)time(NULL), arc4random(), ext
+		);
+
+		int fd = openat(cwd, name, O_CREAT | O_EXCL | O_WRONLY, 0644);
+		if (fd < 0) {
+			warn("openat");
+			return fail(req, KHTTP_507);
+		}
+		ssize_t len = write(fd, field->val, field->valsz);
+		int error = close(fd);
+		if (len < 0 || error) {
+			warn("write");
+			return fail(req, KHTTP_507);
+		}
+
+		return head(req, KHTTP_303, KMIME_TEXT_PLAIN)
+			|| khttp_head(req, kresps[KRESP_LOCATION], "/%s", name)
+			|| khttp_body(req)
+			|| khttp_puts(req, name);
+
+	} else {
+		return fail(req, KHTTP_405);
+	}
+}
+
+static void sandbox(void) {
+	cwd = open(".", O_DIRECTORY);
+	if (cwd < 0) err(EX_CONFIG, ".");
+
+	int error = cap_enter();
+	if (error) err(EX_OSERR, "cap_enter");
+
+	cap_rights_t rights;
+	cap_rights_init(&rights, CAP_LOOKUP, CAP_CREATE, CAP_PWRITE);
+	error = cap_rights_limit(cwd, &rights);
+	if (error) err(EX_OSERR, "cap_rights_limit");
+}
+
+int main(void) {
+	const char *page = "up";
+	if (khttp_fcgi_test()) {
+		struct kfcgi *fcgi;
+		enum kcgi_err error = khttp_fcgi_init(&fcgi, &Key, 1, &page, 1, 0);
+		if (error) errx(EX_CONFIG, "khttp_fcgi_init: %s", kcgi_strerror(error));
+		sandbox();
+		for (
+			struct kreq req;
+			KCGI_OK == (error = khttp_fcgi_parse(fcgi, &req));
+			khttp_free(&req)
+		) {
+			error = handle(&req);
+			if (error && error != KCGI_HUP) break;
+		}
+		if (error != KCGI_EXIT) {
+			errx(EX_PROTOCOL, "khttp_fcgi_parse: %s", kcgi_strerror(error));
+		}
+		khttp_fcgi_free(fcgi);
+	} else {
+		struct kreq req;
+		enum kcgi_err error = khttp_parse(&req, &Key, 1, &page, 1, 0);
+		if (error) errx(EX_PROTOCOL, "khttp_parse: %s", kcgi_strerror(error));
+		error = handle(&req);
+		if (error) errx(EX_PROTOCOL, "%s", kcgi_strerror(error));
+		khttp_free(&req);
+	}
+}
diff --git a/www/text.causal.agency/.gitignore b/www/text.causal.agency/.gitignore
new file mode 100644
index 00000000..8fe3acc9
--- /dev/null
+++ b/www/text.causal.agency/.gitignore
@@ -0,0 +1,3 @@
+*.txt
+feed.atom
+igp
diff --git a/www/text.causal.agency/001-make.7 b/www/text.causal.agency/001-make.7
new file mode 100644
index 00000000..b4805729
--- /dev/null
+++ b/www/text.causal.agency/001-make.7
@@ -0,0 +1,159 @@
+.Dd September 17, 2018
+.Dt MAKE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Using Make
+.Nd writing less Makefile
+.
+.Sh DESCRIPTION
+Let's talk about
+.Xr make 1 .
+I think an important thing to know about
+.Xr make 1
+is that you don't need to write a
+.Pa Makefile
+to use it.
+There are default rules
+for C, C++ and probably Fortran.
+To build
+.Pa foo
+from
+.Pa foo.c ,
+just run:
+.
+.Pp
+.Dl make foo
+.
+.Pp
+The default rule for C files uses the
+.Ev CFLAGS
+variable,
+so you can set that in the environment
+to pass flags to the C compiler:
+.
+.Pp
+.Dl CFLAGS=-Wall make foo
+.
+.Pp
+It also uses
+.Ev LDLIBS
+for linking,
+so you can add libraries with:
+.
+.Pp
+.Dl LDLIBS=-lcurses make foo
+.
+.Pp
+Obviously writing this every time
+would become tedious,
+so it might be time to write a
+.Pa Makefile .
+But it really doesn't need much:
+.
+.Bd -literal -offset indent
+CFLAGS += -Wall -Wextra
+LDLIBS = -lcurses
+
+foo:
+.Ed
+.
+.Pp
+Assigning
+.Ev CFLAGS
+with
+.Ql +=
+preserves the system default
+or anything passed in the environment.
+Declaring
+.Pa foo
+as the first rule
+makes it the default when
+.Ql make
+is run without a target.
+Note that the rule doesn't need a definition;
+the default will still be used.
+.
+.Pp
+If
+.Pa foo
+is built from serveral source files,
+unfortunately a rule definition is required:
+.
+.Bd -literal -offset indent
+OBJS = foo.o bar.o baz.o
+
+foo: $(OBJS)
+	$(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
+.Ed
+.
+.Pp
+This rule uses
+.Ev LDFLAGS
+for passing linker flags,
+which is what the default rule does.
+The
+.Ql $@
+variable here expands to
+.Ql foo ,
+so this rule can be copied easily
+for other binary targets.
+.
+.Pp
+If some sources depend on a header file,
+they can be automatically rebuilt
+when the header changes
+by declaring a dependency rule:
+.
+.Pp
+.Dl foo.o bar.o: foo.h
+.
+.Pp
+Note that several files can appear
+either side of the
+.Ql ":" .
+.
+.Pp
+Lastly,
+it's always nice to add a
+.Cm clean
+target:
+.
+.Bd -literal -offset indent
+clean:
+	rm -f $(OBJS) foo
+.Ed
+.
+.Pp
+I hope this helps getting started with
+.Xr make 1
+without writing too much
+.Pa Makefile !
+.
+.Sh EXAMPLES
+The example
+.Pa Makefile
+in its entirety:
+.
+.Bd -literal -offset indent
+CFLAGS += -Wall -Wextra
+LDLIBS = -lcurses
+OBJS = foo.o bar.o baz.o
+
+foo: $(OBJS)
+	$(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
+
+foo.o bar.o: foo.h
+
+clean:
+	rm -f $(OBJS) foo
+.Ed
+.
+.Sh AUTHORS
+.An Mt june@causal.agency
+.
+.Pp
+This document is produced from
+.Xr mdoc 7
+source available from
+.Lk https://git.causal.agency/src/tree/www/text.causal.agency
diff --git a/www/text.causal.agency/002-writing-mdoc.7 b/www/text.causal.agency/002-writing-mdoc.7
new file mode 100644
index 00000000..b377d364
--- /dev/null
+++ b/www/text.causal.agency/002-writing-mdoc.7
@@ -0,0 +1,138 @@
+.Dd September 27, 2018
+.Dt WRITING-MDOC 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Writing mdoc
+.Nd semantic markup
+.
+.Sh DESCRIPTION
+I recently learned how to write man pages
+so that I could document
+a bunch of little programs I've written.
+Modern man pages are written in
+.Xr mdoc 7 ,
+whose documentation is also available from
+.Lk http://mandoc.bsd.lv .
+.
+.Pp
+.Xr mdoc 7
+differs from many other markup languages
+by providing
+.Dq semantic markup
+rather than just
+.Dq physical markup.
+What this means is that
+the markup indicates what something is,
+not how to format it.
+For example,
+the
+.Ql \&Ar
+macro is used to indicate
+command-line arguments
+rather than one of the macros
+for bold, italic or underline.
+This frees each author of having to choose
+and enables consistent presentation
+across different man pages.
+.
+.Pp
+Another advantage of semantic markup
+is that information can be extracted from it.
+For example,
+.Xr makewhatis 8
+can easily extract the name and short description
+from each man page
+thanks to the
+.Ql \&Nm
+and
+.Ql \&Nd
+macros.
+I use the same information
+to generate an Atom feed for these documents,
+though in admittedly a much less robust way than
+.Xr mandoc 1 .
+.
+.Pp
+When it comes to actually writing
+.Xr mdoc 7 ,
+it can take some getting used to.
+The language is of
+.Xr roff 7
+lineage
+so its syntax is very particular.
+Macros cannot appear inline,
+but must start on new lines
+beginning with
+.Ql \&. .
+Sentences should likewise
+always start on a new line.
+Since I'm in the habit of writing with
+semantic line breaks,
+I actually find these requirements
+fit in well.
+.
+.Pp
+The more frustrating syntax limitation to me
+is the rule against empty lines.
+Without them,
+it can be quite difficult to edit a lengthy document.
+Thankfully,
+lines with only a
+.Ql \&.
+on them are allowed,
+but this still causes visual noise.
+To alleviate that,
+I have a
+.Xr vim 1
+syntax file for
+.Xr mdoc 7
+which conceals the lone dots:
+.
+.Bd -literal -offset indent
+if exists("b:current_syntax")
+	finish
+endif
+
+runtime! syntax/nroff.vim
+unlet! b:current_syntax
+
+setlocal sections+=ShSs
+syntax match mdocBlank /^\\.$/ conceal
+setlocal conceallevel=2
+
+let b:current_syntax = "mdoc"
+.Ed
+.
+.Pp
+It also adds the
+.Xr mdoc 7
+section header and subsection header macros to the
+.Cm sections
+option to make
+.Xr vim 1 Ap s
+.Ic {
+and
+.Ic }
+motions
+aware of them.
+.
+.Pp
+With that,
+I've found writing man pages pleasant and rewarding.
+I've started writing other documents with
+.Xr mdoc 7
+as well,
+as you can see here.
+.
+.Sh SEE ALSO
+.Lk http://rhodesmill.org/brandon/2012/one-sentence-per-line/ "Semantic Linefeeds"
+.
+.Sh AUTHORS
+.An Mt june@causal.agency
+.
+.Pp
+This document is produced from
+.Xr mdoc 7
+source available from
+.Lk https://git.causal.agency/src/tree/www/text.causal.agency
diff --git a/www/text.causal.agency/003-pleasant-c.7 b/www/text.causal.agency/003-pleasant-c.7
new file mode 100644
index 00000000..16030b7e
--- /dev/null
+++ b/www/text.causal.agency/003-pleasant-c.7
@@ -0,0 +1,120 @@
+.Dd September 30, 2018
+.Dt PLEASANT-C 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Pleasant C
+.Nd it's good, actually
+.
+.Sh DESCRIPTION
+I've been writing a lot of C lately
+and actually find it very pleasant.
+I want to talk about some of its ergonomic features.
+These are C99 features unless otherwise noted.
+.
+.Ss Initializer syntax
+Struct and union initializer syntax
+is well generalized.
+Designators can be chained,
+making initializing nested structs easy,
+and all uninitialized fields are zeroed.
+.
+.Bd -literal -offset indent
+struct {
+	struct pollfd fds[2];
+} loop = {
+	.fds[0].fd = STDIN_FILENO,
+	.fds[1].fd = STDOUT_FILENO,
+	.fds[0].events = POLLIN,
+	.fds[1].events = POLLOUT,
+};
+.Ed
+.
+.Ss Variable-length arrays
+VLAs can be multi-dimensional,
+which can avoid manual stride multiplications
+needed to index a flat
+.Xr malloc 3 Ap d
+array.
+.
+.Bd -literal -offset indent
+uint8_t glyphs[len][height][width];
+fread(glyphs, height * width, len, stdin);
+.Ed
+.
+.Ss Incomplete array types
+The last field of a struct can be an
+.Dq incomplete
+array type,
+which means it doesn't have a length.
+A variable amount of space for the struct can be
+.Xr malloc 3 Ap d ,
+or the struct can be used as
+a sort of pointer with fields.
+.
+.Bd -literal -offset indent
+struct Line {
+	enum Filter type;
+	uint8_t data[];
+} *line = &png.data[1 + lineSize()];
+.Ed
+.
+.Ss Anonymous struct and union fields (C11)
+Members of structs or unions
+which are themselves structs or unions
+can be unnamed.
+In that case,
+each of the inner fields
+is treated as a member of the outer struct or union.
+This makes working with tagged unions nicer.
+.
+.Bd -literal -offset indent
+struct Message {
+	enum { Foo, Bar } type;
+	union {
+		uint8_t foo;
+		uint32_t bar;
+	};
+} msg = { .type = Foo, .foo = 0xFF };
+.Ed
+.
+.Ss Static assert (C11)
+Assertions can be made at compile time.
+Most useful for checking sizes of structs.
+.
+.Bd -literal -offset indent
+static_assert(13 == sizeof(struct PNGHeader), "PNG IHDR size");
+.Ed
+.
+.Ss Leading-break switch
+This one is just an odd style choice
+I came across that C happens to allow.
+To prevent accidental fall-through
+in switch statements,
+you can put breaks before the case labels.
+.
+.Bd -literal -offset indent
+while (0 < (opt = getopt(argc, argv, "h:w:"))) {
+	switch (opt) {
+		break; case 'h': height = optarg;
+		break; case 'w': width = optarg;
+		break; default:  return EX_USAGE;
+	}
+}
+.Ed
+.
+.Sh AUTHORS
+.An Mt june@causal.agency
+.
+.Pp
+This document is produced from
+.Xr mdoc 7
+source available from
+.Lk https://git.causal.agency/src/tree/www/text.causal.agency
+.
+.Sh CAVEATS
+This isn't meant to be advice.
+It's just how I like to write C,
+and I don't
+.Dq ship
+software in C.
diff --git a/www/text.causal.agency/004-uloc.7 b/www/text.causal.agency/004-uloc.7
new file mode 100644
index 00000000..edd78d80
--- /dev/null
+++ b/www/text.causal.agency/004-uloc.7
@@ -0,0 +1,64 @@
+.Dd December 14, 2018
+.Dt ULOC 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm ULOC
+.Nd unique lines of code
+.
+.Sh DESCRIPTION
+There are many tools available
+which measure SLOC: source lines of code.
+These tools are strangely complex
+for what they intend to do,
+which is to estimate the relative sizes of projects.
+They perform some amount of parsing
+in order to discount comments in various languages,
+and for reasons unknown each format their ouput
+in some oddly encumbered way.
+.
+.Pp
+I propose a much simpler method
+of estimating relative sizes of projects:
+unique lines of code.
+ULOC can be calculated with standard tools as follows:
+.
+.Bd -literal -offset indent
+sort -u *.h *.c | wc -l
+.Ed
+.
+.Pp
+In my opinion,
+the number this produces
+should be a better estimate of
+the complexity of a project.
+Compared to SLOC,
+not only are blank lines discounted,
+but so are close-brace lines
+and other repetitive code
+such as common includes.
+On the other hand,
+ULOC counts comments,
+which require just as much maintenance
+as the code around them does,
+while avoiding inflating the result
+with license headers which appear in every file,
+for example.
+.
+.Pp
+It can also be amusing
+to read all of your code sorted alphabetically.
+.
+.Sh AUTHORS
+.An Mt june@causal.agency
+.
+.Pp
+This document is produced from
+.Xr mdoc 7
+source available from
+.Lk https://git.causal.agency/src/tree/www/text.causal.agency
+.
+.Sh CAVEATS
+Estimates such as these
+should not be used for decision making
+as if they were data.
diff --git a/www/text.causal.agency/005-testing-c.7 b/www/text.causal.agency/005-testing-c.7
new file mode 100644
index 00000000..d0c636ff
--- /dev/null
+++ b/www/text.causal.agency/005-testing-c.7
@@ -0,0 +1,73 @@
+.Dd December 21, 2018
+.Dt TESTING-C 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Testing C
+.Nd a simple unit testing setup
+.
+.Sh DESCRIPTION
+This is a simple approach
+to unit testing in C
+that I've used in a couple projects.
+At the bottom of a C file
+with some code I want to test,
+I add:
+.
+.Bd -literal -offset indent
+#ifdef TEST
+#include <assert.h>
+
+int main(void) {
+	assert(...);
+	assert(...);
+}
+
+#endif
+.Ed
+.
+.Pp
+This file normally produces a
+.Pa .o
+to be linked into the main binary.
+For testing,
+I produce separate binaries
+and run them with
+.Xr make 1 :
+.
+.Bd -literal -offset indent
+TESTS = foo.t bar.t
+
+\&.SUFFIXES: .t
+
+\&.c.t:
+	$(CC) $(CFLAGS) -DTEST $(LDFLAGS) $< $(LDLIBS) -o $@
+
+test: $(TESTS)
+	set -e; $(TESTS:%=./%;)
+.Ed
+.
+.Pp
+Note that the test binaries
+aren't linked with the rest of the code,
+so there is potential for simple stubbing or mocking.
+.
+.Pp
+To get the best output
+from C's simple
+.Xr assert 3 ,
+it's best to assert the result
+of a helper function
+which takes the expected output
+and the test input,
+rather than calling
+.Xr assert 3
+inside the helper function.
+This way,
+the message printed by the assert failure
+contains a useful line number
+and the expected output
+rather than just variable names.
+.
+.Sh AUTHORS
+.An Mt june@causal.agency
diff --git a/www/text.causal.agency/006-some-libs.7 b/www/text.causal.agency/006-some-libs.7
new file mode 100644
index 00000000..5af65404
--- /dev/null
+++ b/www/text.causal.agency/006-some-libs.7
@@ -0,0 +1,96 @@
+.Dd December 11, 2019
+.Dt SOME-LIBS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Some Libraries
+.Nd good ones
+.
+.Sh DESCRIPTION
+This is a little list of C libraries
+I've had good experiences using.
+.
+.Bl -tag -width Ds
+.It Fl lcurl
+The library behind the
+.Xr curl 1
+command.
+It downloads or uploads things on the internet
+through a number of protocols,
+not just HTTP.
+It has an easy-to-use library API,
+appropriately named
+.Xr libcurl-easy 3 .
+I've used it to implement a
+.Lk https://causal.agency/bin/title.html "page title fetcher" .
+.
+.It Fl lcurses
+Okay so this one really isn't great.
+Its interfaces can seem archaic
+and its documentation is often poor.
+However, it gets the job done
+and is commonly available pretty much everywhere.
+Interesting to note that
+.Nx
+uses its own implementation of curses
+that is not GNU ncurses,
+unlike
+.Fx .
+.
+.It Fl ledit
+This is a BSD line editing library,
+similar to GNU readline.
+It supports right-aligned prompts,
+which I prefer for variable-length
+information in shells.
+.
+.It Fl lkcgi
+A CGI and FastCGI library
+for web applications in C.
+Don't worry,
+it isolates HTTP parsing and input validation
+from application logic
+in sandboxed processes.
+I think it's an excellent example
+of how to design an API for C.
+I used it to implement the
+.Lk https://ascii.town/explore.html "torus web viewer" .
+.
+.It Fl lsqlite3
+An embedded relational database engine.
+It's amazing what you can do with this,
+and it's super easy to use!
+My one gripe with it is that the library and SQL documentation
+are not available as
+.Xr man 1
+pages.
+I'm currently working on a project using SQLite,
+but it hasn't gotten very far yet.
+.
+.It Fl ltls
+This is a new library in LibreSSL
+which provides a much simpler interface for TLS sockets
+compared to
+.Fl lssl .
+It's much more like what you'd expect
+from other TLS socket wrappers,
+with calls like
+.Xr tls_connect 3 ,
+.Xr tls_read 3
+and
+.Xr tls_write 3 .
+I've used this for IRC clients, bouncers and bots.
+.
+.It Fl lz
+An implementation of the DEFLATE compression algorithm
+and gzip format.
+It's all documented in comments in
+.In zlib.h ,
+which isn't bad,
+but for my own use I copied the docs into
+.Lk https://code.causal.agency/june/zlib-man-pages "manual pages" .
+I've used this for decoding and encoding PNG images.
+.El
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/007-cgit-setup.7 b/www/text.causal.agency/007-cgit-setup.7
new file mode 100644
index 00000000..44fb436a
--- /dev/null
+++ b/www/text.causal.agency/007-cgit-setup.7
@@ -0,0 +1,271 @@
+.Dd December 15, 2019
+.Dt CGIT-SETUP 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm cgit setup
+.Nd configuration notes
+.
+.Sh DESCRIPTION
+I just set up cgit on
+.Lk https://git.causal.agency
+to replace an instance of gitea.
+After 30 days of uptime,
+gitea had accumulated over 11 hours of CPU time
+and was using hundreds of megabytes of memory.
+cgit is much more lightweight
+and much more in line with my aesthetic.
+I'm documenting how I set it up here
+mostly to remind myself in the future.
+.
+.Ss slowcgi
+cgit is CGI software,
+but
+.Xr nginx 8
+only supports FastCGI.
+I used
+.Xr slowcgi 8
+as a compatibility layer
+by adding the following to
+.Pa /etc/rc.conf :
+.Bd -literal -offset indent
+slowcgi_enable="YES"
+slowcgi_flags="-p / -s /var/run/slowcgi.sock"
+.Ed
+.
+.Ss nginx
+I added the following in a new
+.Cm server
+block to
+.Pa /usr/local/etc/nginx/nginx.conf :
+.Bd -literal -offset indent
+root /usr/local/www/cgit;
+location / {
+	try_files $uri @cgit;
+}
+location @cgit {
+	fastcgi_pass unix:/var/run/slowcgi.sock;
+	fastcgi_param SCRIPT_FILENAME $document_root/cgit.cgi;
+	fastcgi_param SCRIPT_NAME /;
+	fastcgi_param PATH_INFO $uri;
+	fastcgi_param QUERY_STRING $query_string;
+	fastcgi_param REQUEST_METHOD $request_method;
+	fastcgi_param CONTENT_TYPE $content_type;
+	fastcgi_param CONTENT_LENGTH $content_length;
+	fastcgi_param HTTPS $https if_not_empty;
+	fastcgi_param SERVER_PORT $server_port;
+	fastcgi_param SERVER_NAME $server_name;
+}
+.Ed
+.
+.Pp
+The
+.Cm try_files
+directive causes
+.Xr nginx 8
+to first try to serve static files from
+.Pa /usr/local/www/cgit
+before passing anything else on to FastCGI.
+.
+.Pp
+The
+.Va SCRIPT_FILENAME
+parameter tells
+.Xr slowcgi 8
+the path of the CGI binary to run.
+Setting
+.Va SCRIPT_NAME
+to
+.Pa /
+tells cgit its root URL
+and avoids it using query strings for everything.
+.
+.Ss cgit
+cgit doesn't provide any configuration to start from,
+so you have to just read
+.Xr cgitrc 5 .
+I added the following to
+.Pa /usr/local/etc/cgitrc :
+.Bd -literal -offset indent
+cache-size=1024
+clone-url=https://$HTTP_HOST/$CGIT_REPO_URL
+snapshots=tar.gz zip
+remove-suffix=1
+enable-git-config=1
+scan-path=/home/june/pub
+.Ed
+.
+.Pp
+The
+.Cm cache-size
+option enables caching,
+which by default is stored in
+.Pa /var/cache/cgit ,
+so I made sure that directory exists
+and is writable by the
+.Sy www
+user.
+The
+.Cm clone-url
+option sets the clone URL to advertise.
+cgit will automatically serve git over HTTP.
+The
+.Cm snapshots
+option makes tarballs available for tags and commits.
+.
+.Pp
+The
+.Cm scan-path
+option causes cgit to scan the given path
+for git repositories.
+I'm putting mine in
+.Pa ~/pub .
+The
+.Cm remove-suffix
+option causes cgit to remove the
+.Pa .git
+suffix from the URLs it uses
+for the repositories it finds,
+so that
+.Pa ~/pub/pounce.git
+is served at
+.Pa /pounce .
+The
+.Cm enable-git-config
+option allows controlling some cgit options
+from the
+.Xr git-config 1
+of each repository.
+See
+.Sx git
+below.
+.
+.Pp
+I also set up a filter to render
+.Xr mdoc 7
+files
+and do syntax highlighting
+by adding the following to
+.Pa cgitrc :
+.Bd -literal -offset indent
+readme=:README.7
+readme=:README
+about-filter=/usr/local/libexec/cgit-filter
+source-filter=/usr/local/libexec/cgit-filter
+.Ed
+.
+.Pp
+The
+.Cm readme
+options tell cgit which files to look for
+to render the
+.Dq about
+page.
+The colon prefix causes it to look for them
+in the git tree.
+The
+.Pa /usr/local/libexec/cgit-filter
+script contains the following:
+.Bd -literal -offset indent
+#!/bin/sh
+case "$1" in
+	(*.[1-9])
+		/usr/bin/mandoc -T utf8 | /usr/local/libexec/ttpre
+		;;
+	(*)
+		exec /usr/local/libexec/hi -t -n "$1" -f html -o anchor
+		;;
+esac
+.Ed
+.
+.Pp
+Filter scripts are run with the filename as their first argument
+and the contents of the file on standard input.
+The
+.Xr ttpre 1
+command is my own utility to convert
+.Xr man 1
+output to HTML.
+The
+.Xr hi 1
+command is my own
+.Lk https://causal.agency/bin/hi.html "syntax highlighter" .
+.
+.Ss git
+I create my repositories in
+.Pa ~/pub
+with
+.Ql git init --bare
+and use
+.Pa git.causal.agency:pub/example.git
+locally as the remote.
+Descriptions are set by editing the
+.Pa description
+file in each repository.
+The section and homepage can be set with
+.Xr git-config 1
+through the keys
+.Cm cgit.section
+and
+.Cm cgit.homepage ,
+respectively,
+thanks to the
+.Cm enable-git-config
+option above.
+.
+.Ss Redirects
+I added the following to the
+.Cm server
+block that used to serve gitea in
+.Pa nginx.conf :
+.Bd -literal -offset indent
+location ~* /june/([^.]+)[.]git(.*) {
+	return 301 https://git.causal.agency/$1$2?$query_string;
+}
+location ~* /june/([^/]+) {
+	return 301 https://git.causal.agency/$1;
+}
+location / {
+	return 301 https://git.causal.agency;
+}
+.Ed
+.
+.Pp
+This redirects any links to my gitea repos
+to the corresponding repo in cgit.
+The first
+.Sy location
+block also redirects gitea HTTP clone URLs to cgit
+so that
+.Xr git-pull 1
+continues to work on existing clones.
+.
+.Ss Update: fast HTTPS clones
+Someone pointed out that cloning my repos
+over HTTPS was incredibly slow,
+and this is because cgit only implements the
+.Dq dumb
+HTTP git transport.
+To speed up cloning,
+I send the URLs used by the
+.Dq smart
+HTTP transport to
+.Xr git-http-backend 1
+instead:
+.Bd -literal -offset indent
+location ~ /.+/(info/refs|git-upload-pack) {
+	fastcgi_pass unix:/var/run/slowcgi.sock;
+	fastcgi_param SCRIPT_NAME /usr/local/libexec/git-core/git-http-backend;
+	fastcgi_param GIT_HTTP_EXPORT_ALL 1;
+	fastcgi_param GIT_PROJECT_ROOT /home/june/pub;
+	include fastcgi_params;
+}
+.Ed
+.
+.Pp
+I factored out the FastCGI parameters
+I'm using with cgit
+to be included here as well.
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/008-how-irc.7 b/www/text.causal.agency/008-how-irc.7
new file mode 100644
index 00000000..aba1bbf9
--- /dev/null
+++ b/www/text.causal.agency/008-how-irc.7
@@ -0,0 +1,193 @@
+.Dd March  8, 2020
+.Dt HOW-IRC 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm How I Relay Chat
+.Nd in code
+.
+.Sh DESCRIPTION
+I've been writing a lot of IRC software lately
+.Pq Sx SEE ALSO ,
+and developed some nice code patterns
+that I've been reusing.
+Here they are.
+.
+.Ss Parsing
+I use fixed size buffers almost everywhere,
+so it's necessary to know IRC's size limits.
+A traditional IRC message is a maximum of 512 bytes,
+but the IRCv3 message-tags spec adds
+(unreasonably, in my opinion)
+8191 bytes for tags.
+IRC messages also have a maximum of 15 command parameters.
+.Bd -literal -offset indent
+enum { MessageCap = 8191 + 512 };
+enum { ParamCap = 15 };
+.Ed
+.
+.Pp
+If I'm using tags,
+I'll use X macros
+to declare the set I care about.
+X macros are a way of maintaining parallel arrays,
+or in this case an enum and an array.
+.Bd -literal -offset indent
+#define ENUM_TAG \e
+	X("msgid", TagMsgid) \e
+	X("time", TagTime)
+
+enum Tag {
+#define X(name, id) id,
+	ENUM_TAG
+#undef X
+	TagCap,
+};
+
+static const char *TagNames[TagCap] = {
+#define X(name, id) [id] = name,
+	ENUM_TAG
+#undef X
+};
+.Ed
+.
+.Pp
+The TagNames array is used by the parsing function
+to assign tag values into the message structure,
+which looks like this:
+.Bd -literal -offset indent
+struct Message {
+	char *tags[TagCap];
+	char *nick;
+	char *user;
+	char *host;
+	char *cmd;
+	char *params[ParamCap];
+};
+.Ed
+.
+.Pp
+I'm a fan of using
+.Xr strsep 3
+for simple parsing.
+Although it modifies its input
+(replacing delimiters with NUL terminators),
+since the raw message is in a static buffer,
+it is ideal for so-called zero-copy parsing.
+I'm not going to include the whole parsing function here,
+but I will at least include the part that many get wrong,
+which is dealing with the colon-prefixed trailing parameter:
+.Bd -literal -offset indent
+msg.cmd = strsep(&line, " ");
+for (int i = 0; line && i < ParamCap; ++i) {
+	if (line[0] == ':') {
+		msg.params[i] = &line[1];
+		break;
+	}
+	msg.params[i] = strsep(&line, " ");
+}
+.Ed
+.
+.Ss Handling
+To handle IRC commands and replies
+I add handler functions to a big array.
+I usually have some form of helper as well
+to check the number of expected parameters.
+.Bd -literal -offset indent
+typedef void HandlerFn(struct Message *msg);
+
+static const struct Handler {
+	const char *cmd;
+	HandlerFn *fn;
+} Handlers[] = {
+	{ "001", handleReplyWelcome },
+	{ "PING", handlePing },
+	{ "PRIVMSG", handlePrivmsg },
+};
+.Ed
+.
+.Pp
+Since I keep these arrays sorted anyway,
+I started using the standard
+.Xr bsearch 3
+function,
+but a basic for loop probably works just as well.
+I do wish I could compile-time assert
+that the array really is sorted, though.
+.Bd -literal -offset indent
+static int compar(const void *cmd, const void *_handler) {
+	const struct Handler *handler = _handler;
+	return strcmp(cmd, handler->cmd);
+}
+
+void handle(struct Message msg) {
+	if (!msg.cmd) return;
+	const struct Handler *handler = bsearch(
+		msg.cmd,
+		Handlers, ARRAY_LEN(Handlers),
+		sizeof(*handler), compar
+	);
+	if (handler) handler->fn(&msg);
+}
+.Ed
+.
+.Ss Capabilities
+For IRCv3 capabilties
+I use X macros again,
+this time with another handy macro
+for declaring bit flag enums.
+.Bd -literal -offset indent
+#define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit
+
+#define ENUM_CAP \e
+	X("message-tags", CapMessageTags) \e
+	X("sasl", CapSASL) \e
+	X("server-time", CapServerTime)
+
+enum Cap {
+#define X(name, id) BIT(id),
+	ENUM_CAP
+#undef X
+};
+
+static const char *CapNames[] = {
+#define X(name, id) [id##Bit] = name,
+	ENUM_CAP
+#undef X
+};
+.Ed
+.
+.Pp
+The
+.Fn BIT
+macro declares, for example,
+.Dv CapSASL
+as the bit flag and
+.Dv CapSASLBit
+as the corresponding index.
+The
+.Vt "enum Cap"
+is used as a set,
+for example checking if SASL is enabled with
+.Ql caps & CapSASL .
+.
+.Pp
+These patterns are serving my IRC software well,
+and my IRC projects are serving me well.
+It is immensely satisfying
+to be (near) constantly using software
+that I wrote myself and am happy with,
+regardless of how niche it may be.
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk https://git.causal.agency/pounce/about "IRC bouncer"
+.It
+.Lk https://git.causal.agency/litterbox/about "IRC logger"
+.It
+.Lk https://git.causal.agency/catgirl/about "IRC client"
+.El
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/009-casual-update.7 b/www/text.causal.agency/009-casual-update.7
new file mode 100644
index 00000000..0548436a
--- /dev/null
+++ b/www/text.causal.agency/009-casual-update.7
@@ -0,0 +1,127 @@
+.Dd May  6, 2020
+.Dt CASUAL-UPDATE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm casual update
+.Nd software developments
+.
+.Sh DESCRIPTION
+I've been figuring out more of IMAP
+and Internet Messages in general
+while working on a new project
+so I've revisited some older ones.
+I've copied my somewhat more proper
+IMAP parsing code into them,
+so they should be more robust.
+.
+.Pp
+.Xr imbox 1
+is my tool to export messages
+in mboxrd format directly from IMAP.
+It's mostly for applying patches sent by email
+without having any kind of local mail setup.
+For that,
+it includes the
+.Xr git-fetch-email 1
+wrapper which works very similarly to
+.Xr git-send-email 1 .
+I learned by reading the source of
+.Xr git-subtree 1
+that
+.Xr git-rev-parse 1
+can be used by shell scripts
+to parse long options,
+so I added those.
+I also added the
+.Fl Fl apply
+flag to automatically pipe to
+.Xr git-am 1
+with the right flags for mboxrd.
+.
+.Pp
+.Xr notemap 1
+is a tool for mirroring text files
+to an IMAP Notes mailbox,
+which is used by FastMail's web UI
+and the macOS/iOS Notes app.
+Its original parsing code
+was particularly ad-hoc.
+Since I've now learned
+how UTF-8 headers are encoded,
+I updated it to properly encode
+the file name in the Subject line.
+.
+.Pp
+I also got distracted by
+a conversation about UNIX-domain sockets
+where I was comparing the macOS and FreeBSD
+.Xr unix 4
+pages and the Linux
+.Xr unix 7
+page.
+This lead me to make
+.Xr exman 1 ,
+a tool to locally install and read
+manual pages for Linux, POSIX,
+.Fx ,
+.Nx
+and
+.Ox .
+I've already gotten quite a bit of use out of it.
+.
+.Pp
+In yet another IRC distraction,
+I was talking about some further plans for my IRC software,
+and realized it might be time to write
+my future projects list down.
+I opened a
+.Pa .plan
+file,
+immediately wondered how anyone can write plain text,
+then switched to a
+.Pa plan.7
+file.
+There's nothing I won't use
+.Xr mdoc 7
+for.
+After a little setup,
+I can now be fingered,
+and make jokes about this silly little protocol
+from the days of old.
+.Xr finger 1 Ap s
+default output fills me with joy:
+.Bd -unfilled -offset indent
+No Mail.
+No Plan.
+.Ed
+.
+.Pp
+And speaking of IRC and plans,
+I've been meaning to tag
+.Xr catgirl 1
+version 1.0 for a while now.
+I've been using it as my main client
+and my commits to it have really slowed down.
+When I do tag it,
+I'm planning on writing another post
+about my whole
+.Dq suite
+of IRC software
+and how the parts work together.
+Watch this space.
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk https://git.causal.agency/imbox "imbox"
+.It
+.Lk https://git.causal.agency/notemap "notemap"
+.It
+.Lk https://git.causal.agency/exman "exman"
+.It
+.Lk https://git.causal.agency/catgirl "catgirl"
+.El
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/010-irc-suite.7 b/www/text.causal.agency/010-irc-suite.7
new file mode 100644
index 00000000..515a30ab
--- /dev/null
+++ b/www/text.causal.agency/010-irc-suite.7
@@ -0,0 +1,409 @@
+.Dd June 19, 2020
+.Dt IRC-SUITE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm IRC suite
+.Nd my own IRC software
+.
+.Sh DESCRIPTION
+Over the past months
+.Po
+eight of them, according to
+.Xr git-log 1
+.Pc
+I developed a new
+.Dq suite
+of IRC software
+that I now use full-time,
+consisting of a bouncer,
+a new logging and search solution,
+and a terminal client.
+These new programs share some characteristics:
+they are all TLS-only
+and use the libtls API from LibreSSL,
+they can all be entirely configured from the command line
+or with equivalent configuration files,
+they are all designed as
+a one process to one IRC connection mapping,
+and they all take advantage of IRCv3 features.
+.
+.Pp
+For context,
+I was previously running
+the znc IRC bouncer
+and using the Textual IRC client
+with its plain text logs.
+I also continue to use
+the Palaver IRC client for iOS.
+.
+.Ss Background
+A bouncer is a piece of server software
+that stays connected to IRC at all times
+and acts as a relay
+between your client and the IRC server.
+When the client is disconnected,
+the bouncer buffers incoming messages
+to send to the client when it reconnects.
+.
+.Pp
+Aside from this,
+bouncers have another advantage:
+client multiplexing.
+Several clients,
+for instance on different computers
+or a phone,
+should be able to connect to the same bouncer,
+and send and receive messages under the same nick.
+Unfortunately,
+znc does not handle this use-case well at all.
+Out of the box it offers two options:
+either any client connection totally clears the buffer,
+causing other clients to miss chat history;
+or the buffer is never cleared,
+causing every client connection
+to be repeatedly spammed with redundant history.
+There is also a znc wiki page
+that suggests one way to solve this issue
+is to connect znc to itself multiple times over.
+Yikes.
+.
+.Ss pounce
+My dissatisfaction with
+connecting multiple clients to znc
+directly motivated me to start working
+on a new multi-client-focused IRC bouncer.
+The result is
+.Xr pounce 1 ,
+based on a rather straightforward
+single-producer (the IRC server)
+multiple-consumer (each IRC client)
+ring buffer.
+Each client has its own
+independent position in the buffer
+so can properly be brought up to date
+whenever it connects.
+.
+.Pp
+Additionally,
+by assuming support for the IRCv3
+.Sy server-time
+extension,
+all IRC events can be accurately
+relayed to clients at any time,
+and the internals of
+.Xr pounce 1
+can be kept very simple.
+In fact,
+it completely avoids parsing most IRC messages,
+simply pushing them into the buffer
+with an associated timestamp.
+.
+.Pp
+The usernames sent by clients during registration
+are used as opaque identifiers for buffer consumers.
+This was chosen since most clients
+can be configured to send an arbitrary username,
+and those that don't often default
+to the name of the client itself,
+making it an appropriate identifier.
+.
+.Pp
+Later,
+I added a way for clients
+to be informed of their own buffer positions
+using a vendor-specific IRCv3 capability.
+This means a client
+can save the position
+of the last message it actually received,
+and request to set its position
+when it reconnects,
+ensuring no messages are lost
+to network issues
+or software crashes.
+.
+.Ss calico
+Due to the simple design of mapping
+one process to one IRC (server) connection,
+it is necessary to run several instances of
+.Xr pounce 1 .
+Initially I simply used different ports for each,
+but as I connected to more networks
+and even ran some instances for friends,
+it became less feasible.
+.
+.Pp
+The solution I came up with
+was to dispatch incoming connections
+using Server Name Indication, or SNI.
+This way,
+multiple domains pointing to the same host
+could be used with only one port
+to connect to different instances of
+.Xr pounce 1 .
+For example,
+I use a
+.Li *.irc.causal.agency
+wildcard DNS entry
+and a subdomain for each IRC network,
+all on port 6697.
+.
+.Pp
+The
+.Xr calico 1
+daemon included with pounce
+accomplishes this dispatch
+using the
+.Dv MSG_PEEK
+flag of
+.Xr recvmsg(2)
+on incoming connections.
+Since SNI is immediately sent by TLS clients
+as part of the ClientHello message in clear-text,
+it can be processed
+without doing any actual TLS.
+The connection itself is then
+sent to the corresponding
+.Xr pounce 1
+instance
+over UNIX-domain socket,
+which handles TLS as normal.
+This means that
+.Xr calico 1
+and
+.Xr pounce 1
+operate entirely independently of each other.
+.
+.Ss litterbox
+Based on the multiple-consumer ring buffer design,
+I realized it would be easy
+to implement additional functionality
+as independent purpose-built clients
+which connect to
+.Xr pounce 1
+alongside regular clients.
+This could allow dedicated OTR or DCC software
+to operate in parallel with a basic client,
+or for more passive software
+to provide notifications
+or dedicated logging.
+.
+.Pp
+For the latter,
+I wanted to do better than
+plain text log files.
+.Xr grep 1
+over files works fine,
+but search could be faster and smarter,
+and the text format is
+more lossy and less structured
+than I'd like it to be.
+Conveniently,
+SQLite provides an extension
+(actually two)
+for full-text search.
+.
+.Pp
+The litterbox project
+is my dedicated logging solution
+using SQLite FTS5.
+It consists of three tools:
+the
+.Xr litterbox 1
+daemon itself which connects to pounce
+and logs messages to SQLite,
+the
+.Xr scoop 1
+command line query tool,
+and the
+.Xr unscoop 1
+plain text import tool.
+The
+.Xr scoop 1
+tool constructs SQL queries
+and formats the results for viewing,
+with coloured nicks
+and piped to a pager
+by default.
+.
+.Pp
+The
+.Xr litterbox 1
+daemon
+can also provide a simple
+.Dq online
+.Pq over IRC
+search query interface
+to other connected clients.
+The simplest way to allow different
+.Xr pounce 1
+clients to talk to each other
+was to route private messages to self
+internally without sending them to the IRC server.
+So from any client
+I can simply message myself
+a full-text search query
+and
+.Xr litterbox 1
+responds with the results.
+.
+.Pp
+Along with routing self-messages,
+.Xr pounce 1
+also provides a vendor-specific IRCv3 capability
+for passive clients such as
+.Xr litterbox 1
+to indicate that they should not influence
+the automatic away status,
+which is normally only set
+when no clients are connected.
+.
+.Pp
+An advantage of this architecture
+of dedicated clients
+rather than bouncer modules
+is that they need not run
+on the same host.
+I run my bouncers on a VPS,
+but I'd rather not store my private logs there,
+so
+.Xr litterbox 1
+runs instead on a Raspberry Pi
+in my apartment.
+Also,
+since it is essentially
+just a regular IRC bot,
+it could be used independently
+for keeping public logs for a channel.
+.
+.Ss catgirl
+There's not really that much to say
+about the client,
+.Xr catgirl 1 .
+Of the three projects
+it contains the most code
+but is also the least interesting,
+in my opinion.
+It just does what I want a client to do,
+and gets the details right.
+.
+.Pp
+Tab complete is ordered by most recently seen or used,
+and completing several nicks
+inserts commas between them
+as well as the colon following the final nick.
+In the input line,
+the prompt is updated
+to reflect whether the input
+will be interpreted as a command or as a message.
+Messages are automatically scanned for URLs,
+which can be opened or copied with commands
+specifying the nick or a substring of the URL.
+.
+.Pp
+Scrolling in a window creates a split view,
+keeping the latest messages visible.
+Nick colours are based instead on usernames,
+keeping them more stable across renames,
+and mentions in messages are coloured
+to make the conversation easier to follow.
+The visibility of ignored messages
+can be toggled at any time.
+Channels can be muted
+so their activity is hidden
+from the status line
+unless you are pinged.
+.
+.Pp
+.Xr catgirl 1
+is configured entirely on the command line
+or in equivalent simple configuration files.
+There's no dynamic manipulation of state
+using complex
+.Ql /
+commands like in some other clients.
+.
+.Pp
+The major caveat is that
+.Xr catgirl 1
+connects to only one network at a time.
+This keeps the configuration, the interface
+and the code much simpler.
+.Xr tmux 1 ,
+.Xr screen 1
+or a tabbed terminal emulator
+are good options to run several instances.
+.
+.Pp
+If you're interested in giving
+.Xr catgirl 1
+a quick (and necessarily limited) try,
+you can
+.Li ssh chat@ascii.town .
+.
+.Ss Future
+I think I'm done with IRC software for now.
+As mentioned above,
+there are a few more pieces
+that could fit in to this setup,
+but I don't really want or need them right now.
+One thing I definitely want to try
+at some point
+is adding a litterbox component
+to index the contents of URLs
+to make finding previously shared links easier.
+.
+.Pp
+If you try any of this software
+and have feedback,
+let me know in
+.Li #ascii.town
+on tilde.chat
+or by email.
+And of course,
+patches are always welcome.
+.
+.Ss Update: scooper
+Somehow I had the motivation
+to create a web interface for litterbox:
+.Xr scooper 1 .
+It can be used either as CGI
+or as a FastCGI worker,
+and I used the excellent
+.Xr kcgi 3
+library for it.
+.
+.Pp
+The main advantage of this interface
+is that you can click on a search result
+to be brought to its context in the log viewer.
+I also added an option to
+.Xr litterbox 1
+to provide a corresponding scooper link
+in response to its query interface.
+.
+.Pp
+A small demo of scooper is hosted at
+.Aq Lk "https://causal.agency/scooper/" .
+It publicly logs the
+.Li #litterbox
+channel on tilde.chat.
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk "https://git.causal.agency/pounce" pounce
+.It
+.Lk "https://git.causal.agency/litterbox" litterbox
+.It
+.Lk "https://git.causal.agency/catgirl" catgirl
+.It
+.Lk "https://www.sqlite.org/fts5.html" "SQLite FTS5 Extension"
+.It
+.Lk "https://git.causal.agency/scooper" scooper
+.It
+.Lk "https://kristaps.bsd.lv/kcgi/" kcgi
+.El
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/011-libretls.7 b/www/text.causal.agency/011-libretls.7
new file mode 100644
index 00000000..c29c325e
--- /dev/null
+++ b/www/text.causal.agency/011-libretls.7
@@ -0,0 +1,220 @@
+.Dd August  9, 2020
+.Dt LIBRETLS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm LibreTLS
+.Nd libtls for OpenSSL
+.
+.Sh DESCRIPTION
+This is a sort of announcement post about LibreTLS,
+my port of libtls from LibreSSL to OpenSSL.
+If you've wanted to try any of my software
+but have been unable to because of LibreSSL,
+LibreTLS is an option that will likely work for you.
+I'm including instructions
+for building it and my IRC software
+on Debian as an example,
+since manually installing libraries
+is less straightforward than it could be.
+.
+.Pp
+libtls is
+.Do
+a new TLS library,
+designed to make it easier to write foolproof applications
+.Dc .
+It was developed as part of LibreSSL,
+.Ox Ap s
+fork of OpenSSL,
+and is implemented against their version of libssl.
+It provides a nice high-level API
+for TLS sockets,
+with functions like
+.Xr tls_connect 3 ,
+.Xr tls_read 3
+and
+.Xr tls_write 3 .
+This is a vast improvement over libssl's
+confusing mess of an API!
+Its relative obscurity is a real shame
+for C programmers.
+.
+.Pp
+An obvious cause of its obscurity
+is that it is tied to LibreSSL.
+Although LibreSSL is available
+for platforms other than
+.Ox ,
+it conflicts with OpenSSL
+so is difficult to install alongside it
+and is often not packaged at all.
+Additionally,
+even if a user manually installs LibreSSL,
+libtls is likely not to work on some distros
+due to its hardcoded CA bundle file path.
+.
+.Pp
+Since libtls is implemented against libssl,
+which originates in OpenSSL,
+it should be possible to use libtls with it.
+This is what I set out to do in LibreTLS.
+I started by importing the sources
+from a LibreSSL-portable release,
+then worked on porting the portions
+that were incompatible with OpenSSL.
+.
+.Pp
+The simpler changes just involved
+replacing internal struct field accesses
+with public APIs.
+libtls accesses libssl internals
+using a hack to get the header files
+to declare private struct fields,
+and for basically no reason.
+The bigger changes involved
+reimplementing some functions
+which only exist in LibreSSL,
+but these were still quite small.
+I also imported the necessary compatibility functions
+from LibreSSL's libcrypto
+and adapated the autotools build files
+to produce only a libtls
+which depends on OpenSSL.
+.
+.Pp
+Along the way
+I decided to make one small behavioural change
+in order for LibreTLS to be more likely
+to work for everyone.
+I removed the hardcoded CA file path
+and changed the default configuration
+to use OpenSSL's default CA paths,
+which include a CA directory.
+This seems to be the preferred CA source
+on systems such as Debian,
+where the default CA file path doesn't exist.
+.
+.Pp
+I think the reason LibreSSL
+wants to avoid using a CA directory
+is so that it can fully load the CA file
+once before being sandboxed.
+However,
+using OpenSSL's default configuration,
+the CA file will still be loaded immediately
+if it exists.
+If it doesn't exist,
+sandboxed applications
+will fail when trying to
+load certificates from the directory,
+but unsandboxed applications
+will work just fine.
+Since LibreSSL's libtls
+would fail either way,
+I think the new behaviour
+is an improvement.
+.
+.Pp
+Another advantage of separating libtls from LibreSSL
+is that it is unencumbered by OpenSSL's
+awkward double-license,
+both of which are incompatible with the GPL.
+libtls is all new ISC-licensed code,
+and future versions of OpenSSL (3.0)
+will be released under the Apache 2.0 license,
+which is compatible with GPLv3.
+In the future,
+GPL software will be able to link with
+libtls and OpenSSL without additional permissions.
+.
+.Pp
+It's also worth noting that LibreSSL
+likely will not be able to import any code
+from future versions of OpenSSL,
+since Apache 2.0 is on
+.Ox Ap s
+license shitlist.
+LLVM is also slowly changing their license
+to Apache 2.0,
+so it'll be interesting to see what
+.Ox
+does.
+.
+.Ss Installing Manually
+To install LibreTLS on Debian,
+for example,
+fetch a release tarball from
+.Lk https://causal.agency/libretls/
+and install the build dependencies:
+.Bd -literal -offset indent
+sudo apt-get install build-essential libssl-dev pkgconf
+.Ed
+.
+.Pp
+.Xr pkgconf 1
+isn't a dependency of LibreTLS itself,
+but it's how my software
+configures its build
+for a dependency on libtls.
+The usual build steps
+will install the library:
+.Bd -literal -offset indent
+\&./configure
+make all
+sudo make install
+.Ed
+.
+.Pp
+The library will be installed in
+.Pa /usr/local/lib
+by default,
+and you need to make sure
+the dynamic linker
+will be able to find it there.
+On Debian,
+.Pa /usr/local/lib
+already appears in
+.Pa /etc/ld.so.conf.d/libc.conf ,
+but on other systems
+you'll probably need to add it to either
+.Pa /etc/ld.so.conf
+or a new file such as
+.Pa /etc/ld.so.conf.d/local.conf .
+Once the library is installed
+and the path is configured,
+the linker cache needs to be refreshed:
+.Bd -literal -offset indent
+sudo ldconfig
+.Ed
+.
+.Pp
+You'll probably also need to set
+.Ev PKG_CONFIG_PATH
+for the configure scripts
+of my software:
+.Bd -literal -offset indent
+PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure
+.Ed
+.
+.Pp
+On
+.Fx ,
+LibreTLS and some of my IRC software
+can be installed from my own
+.Lk https://git.causal.agency/ports/ "ports tree"
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk https://git.causal.agency/libretls/about LibreTLS
+.It
+.Lk https://man.openbsd.org/tls_init.3 "libtls API documentation"
+.El
+.
+.Pp
+Another alternative libtls implementation,
+.Lk https://sr.ht/~mcf/libtls-bearssl/ "libtls-bearssl"
+.
+.Sh AUTHORS
+.An June Bug Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/012-inability.7 b/www/text.causal.agency/012-inability.7
new file mode 100644
index 00000000..d352143b
--- /dev/null
+++ b/www/text.causal.agency/012-inability.7
@@ -0,0 +1,39 @@
+.Dd November 26, 2020
+.Dt INABILITY 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Inability
+.Nd losing the ability to create
+.
+.Sh DESCRIPTION
+For often weeks, sometimes months at a time,
+I lose the ability to write new code.
+I can still make fixes
+and little cleanups
+in my existing projects,
+but if I try to work on something new,
+nothing happens.
+I can't get anything done.
+.
+.Pp
+I think it's now been
+over 3 months
+since I've created anything.
+I don't know what to do about it.
+In the past I've eventually
+regained the ability to code,
+but it's unclear to me how or why.
+I also don't know what
+I should be doing instead.
+Writing code is the only hobby
+I've ever really developed,
+so without it I basically
+don't do anything.
+.
+.Pp
+Does this happen to anyone else?
+How do you cope?
+.
+.Sh AUTHORS
+.Mt june@causal.agency
diff --git a/www/text.causal.agency/013-hot-tips.7 b/www/text.causal.agency/013-hot-tips.7
new file mode 100644
index 00000000..63b6e353
--- /dev/null
+++ b/www/text.causal.agency/013-hot-tips.7
@@ -0,0 +1,156 @@
+.Dd December  2, 2020
+.Dt HOT-TIPS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm hot tips
+.Nd from my files
+.
+.Sh DESCRIPTION
+This is a short list of tips
+from my configuration files and code
+that might be useful.
+.
+.Ss Shell
+.Bl -tag -width Ds
+.It CDPATH=:~
+This is useful if you sometimes type,
+for example,
+.Ql cd src/bin
+wanting to go to
+.Pa ~/src/bin
+but you aren't in
+.Pa ~ .
+If the path doesn't exist
+in the current directory,
+.Ic cd
+will try it in
+.Pa ~
+as well.
+.
+.It alias ls='LC_COLLATE=C ls'
+This makes it so that
+.Xr ls 1
+lists files in ASCIIbetical order,
+which puts capitalized names like
+.Pa README
+and
+.Pa Makefile
+first.
+.
+.It git config --global commit.verbose true
+Not shell but close enough.
+This makes it so the entire diff is shown
+below the usual summary
+in the editor for a
+.Xr git-commit(1)
+message.
+Useful for doing a quick review
+of what you're committing.
+.El
+.
+.Ss (neo)vim
+.Bl -tag -width Ds
+.It set inccommand=nosplit
+This is the only
+.Xr nvim 1
+feature I really care about
+aside from the improved defaults.
+This provides a live preview of what a
+.Ic :s
+substitution command will do.
+It makes it much easier to
+write complex substitutions.
+.
+.It nmap <leader>s vip:sort<CR>
+This mapping sorts the lines of a paragraph,
+or block of text separated by blank lines.
+I use this a lot to sort
+#include directives.
+.
+.It nmap <leader>S $vi{:sort<CR>
+Similar to the last mapping,
+this one sorts lines inside braces.
+I use this to sort
+switch statement cases
+or array initializers.
+.
+.It nmap <leader>a m':0/^#include <<CR>:nohlsearch<CR>O#include <
+I use this mapping to add new
+#include directives,
+usually followed by
+.Ic <leader>s
+and
+.Ic ''
+to sort them
+and return to where I was.
+.
+.It nmap <leader>d :0delete<CR>:0read !date +'.Dd \e%B \e%e, \e%Y'<CR>
+I use this to replace the first line of
+.Xr mdoc 7
+files with the current date.
+.El
+.
+.Ss C
+.Bl -tag -width Ds
+.It #define Q(...) #__VA_ARGS__
+This is what I've started using
+to quote things like SQL statements
+or HTML fragments in C.
+Anything that happens to be valid C tokens,
+which is most code,
+can be quoted this way.
+Macros are not expanded
+inside the quoted part.
+You can embed (matched) quotes
+without having to escape them.
+Whitespace gets collapsed,
+so you can write nicely formatted multi-line SQL
+that doesn't mess up your debug logging,
+for example.
+.Bd -literal -offset indent
+const char *sql = Q(
+	INSERT OR IGNORE INTO names (nick, user, host)
+	VALUES (:nick, :user, :host);
+);
+.Ed
+.
+.It #define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit
+I use this macro to declare bitflag enums.
+It takes advantage of
+auto-incrementing enum items
+so you don't need to set the values manually.
+You also get constants
+for both the bit index
+and the flag value
+for each item.
+.Bd -literal -offset indent
+enum Attr {
+	BIT(Bold),
+	BIT(Reverse),
+	BIT(Italic),
+	BIT(Underline),
+};
+.Ed
+.Pp
+For example,
+defines
+.Sy ItalicBit = 2
+and
+.Sy Italic = 1 << 2 .
+Ignore the extraneous constants.
+.
+.It typedef int FnType(const char *str, size_t len);
+You can just typedef function types!
+It annoys me more than it probably should
+that everyone writes ugly
+function pointer typedefs.
+Just stick
+.Sy typedef
+on the front of a function declaration
+and use
+.Vt FnType * .
+.El
+.
+.Sh AUTHORS
+.Mt june@causal.agency
diff --git a/www/text.causal.agency/014-using-vi.7 b/www/text.causal.agency/014-using-vi.7
new file mode 100644
index 00000000..e6a6a7a0
--- /dev/null
+++ b/www/text.causal.agency/014-using-vi.7
@@ -0,0 +1,135 @@
+.Dd January 11, 2021
+.Dt USING-VI 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Using vi
+.Nd simpler tools
+.
+.Sh DESCRIPTION
+Happy new year
+and hello from
+.Xr vi 1 !
+I'm in the mood to post something
+but not in the mood for
+.Dq social
+media.
+This one will probably be short.
+.
+.Pp
+Yesterday I was trying to work on sandboxing
+.Xr catgirl 1
+(that's the IRC client I work on)
+with
+.Xr pledge 2
+and
+.Xr unveil 2
+on
+.Ox ,
+as suggested by the maintainer of its port.
+I've done similar things before,
+but only on server software
+rather than user software.
+.
+.Pp
+Anyway I was in
+.Xr ssh 1
+to my
+.Ox
+VM
+.Po
+sadly I don't currently have any hardware to run
+.Ox
+on
+.Pc
+using my usual editor,
+which is
+.Xr nvim 1 .
+I'm honestly not very thrilled
+with what neovim is doing lately,
+but the cleaned up defaults
+make my configuration files happier.
+.
+.Pp
+The real problem with
+.Xr nvim 1 ,
+though,
+is that it's laggy as hell on
+.Ox .
+There is significant delay
+on every single keystroke,
+as if I'm typing remotely to a server
+on the other side of the world,
+but this is on a local VM!
+.
+.Pp
+So I did the only reasonable thing:
+I typed
+.Sy :qa
+followed by
+.Sy vi .
+The difference was astonishing.
+Typing and editing suddenly felt
+.Em physical
+again.
+(I put that in italics even though I know it won't render.)
+Not only was it a vast improvement over
+.Xr nvim 1
+in
+.Xr ssh 1
+in a VM,
+it was a marked improvement over
+.Xr nvim 1
+running locally and natively.
+.
+.Pp
+Now obviously
+.Xr vi 1
+doesn't have all the bells and whistles
+of newer editors,
+but of course the core editing model
+that makes
+.Xr vim 1
+and
+.Xr nvim 1
+so good is there,
+and in purer form,
+I think.
+The
+.Xr vi 1
+manual page
+is feasible to just sit down and read,
+and learn everything there is to know about the editor.
+I set up a basic configuration
+and got coding.
+.Bd -literal -offset indent
+export EXINIT='set ai ic sm sw=4 ts=4'
+.Ed
+.
+.Pp
+After I finished my
+.Xr pledge 2
+and
+.Xr unveil 2
+patch,
+I was so pleased with
+.Xr vi 1
+that I kept on using it
+yesterday and today
+for other work,
+and obviously to write this post.
+Despite the lack of editor amenities,
+its responsiveness and simplicity
+are enough to make using it
+.Em comfortable
+and perhaps
+.Em cosy .
+I'm not sure I'll ever use
+.Xr vi 1
+full-time,
+but for now I am much less likely
+to launch
+.Xr nvim 1 .
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/015-reusing-tags.7 b/www/text.causal.agency/015-reusing-tags.7
new file mode 100644
index 00000000..19546496
--- /dev/null
+++ b/www/text.causal.agency/015-reusing-tags.7
@@ -0,0 +1,155 @@
+.Dd January 17, 2021
+.Dt REUSING-TAGS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm reusing tags
+.Nd beyond ctags
+.
+.Sh DESCRIPTION
+I've tried to start writing this post a couple times now
+and I keep getting bogged down in explanations,
+so I'm just going to tell you
+about some cool things I did
+and hope they make sense.
+.
+.Pp
+When I wrote my first syntax highlighter,
+I decided that function definitions
+should have anchor links,
+because line number anchor links
+are entirely useless
+if you expect the file to change at all.
+Since the syntax highlighter
+was somewhat deliberately just a big pile of regex,
+I hacked in more regex to try
+to identify function and type definitions.
+It wasn't elegant and it didn't always work well.
+It did work though,
+and I found the links very useful.
+.
+.Pp
+Recently I was thinking about
+the lexer generator
+.Xr lex 1
+and decided to
+rewrite the syntax highlighter
+using it.
+Really syntax highlighting
+is no different than lexical analysis.
+I ran into a problem though,
+trying to preserve my anchor link function,
+because really that should involve
+some amount of parsing.
+Trying to port my regex hacks to
+.Xr lex 1
+made the lexers way more complicated
+and less reliable,
+so I gave up on it for a while.
+.
+.Pp
+And then,
+probably in the shower,
+I realized I was approaching it
+completely from the wrong direction.
+There's already a tool that does what I want,
+and I already use it:
+.Xr ctags 1 .
+All I need to do is use its output
+to insert anchor links
+into my syntax highlighter output.
+In an afternoon I wrote
+.Xr htagml 1 ,
+which loads tag definitions for its input file,
+then scans through the input for where they match.
+It can either HTML-escape
+the input as it goes,
+or use already formatted HTML
+being piped into it from a syntax highlighter.
+.
+.Pp
+The result is three simple tools
+working together to accomplish
+what a more complex tool
+couldn't reliably achieve.
+I'm very pleased with it,
+and I've updated my site and cgit
+to use the new
+.Xr lex 1 Ns -based
+highlighter,
+.Xr ctags 1
+and
+.Xr htagml 1 .
+I'm currently missing a lexer for
+.Xr sh 1 ,
+but I plan to write it eventually.
+I also want to write a tool
+to generate tags for
+.Xr make 1 ,
+.Xr mdoc 7
+and perhaps
+.Xr sh 1 .
+The cool thing about generating more kinds of tags
+is that they'll not only improve
+the HTML output,
+they'll also be usable in my editor.
+.
+.Pp
+Speaking of generating different kinds of tags,
+I also wrote some scripts not too long ago
+for reading IETF RFCs offline.
+The plain text files are available to
+.Xr rsync 1 ,
+but they're hard to navigate on their own.
+By scanning the files for headings
+and generating tags,
+it allows jumping to sections using
+.Ic :ta
+or
+.Ic ^]
+in
+.Xr vi 1 .
+For
+.Xr nvim 1
+I also added an
+.Ic :RFC
+command to open an RFC by number
+and set up
+.Ic ^]
+to work optimally for them.
+.
+.Pp
+I'm still using
+.Xr vi 1
+for most of my editing,
+by the way.
+And of course
+.Xr ctags 1
+was made to work with it!
+Simple old tools
+are really doing it for me lately.
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk https://causal.agency/bin/htagml.html htagml
+.It
+.Lk https://causal.agency/bin/hilex.html hilex
+.It
+.Lk https://git.causal.agency/src/tree/doc/rfc rfctags
+.El
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.
+.Sh ADDENDUM
+.Xr catgirl 1 ,
+.Xr pounce 1 ,
+.Xr litterbox 1
+and
+.Xr scooper 1
+all have new releases,
+if you're using any of them.
+Also, this space is now
+available over gopher,
+if that's your sort of thing.
diff --git a/www/text.causal.agency/016-using-openbsd.7 b/www/text.causal.agency/016-using-openbsd.7
new file mode 100644
index 00000000..b843e3c3
--- /dev/null
+++ b/www/text.causal.agency/016-using-openbsd.7
@@ -0,0 +1,505 @@
+.Dd February 14, 2021
+.Dt USING-OPENBSD 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Using OpenBSD
+.Nd for real
+.
+.Sh DESCRIPTION
+Hello from
+.Ox !
+After wishing one too many times
+that I had a real BSD
+on a physical machine,
+I finally got around to
+just installing one on my
+mid-2014 MacBook Pro.
+I hadn't done it sooner
+because I didn't realize
+how easy it would be.
+It helped that I already had a
+.Dq Boot Camp
+partition with a disused Windows 8 install
+that I could replace.
+.
+.Pp
+I roughly followed an old jcs gist
+along with the
+.Ox
+Disk Setup guide.
+I'm once again happy
+that I bought a printer\(em
+they're very useful for instructions
+to install an operating system
+on your only usable computer.
+I set up encrypted softraid
+and the operating system
+installed smoothly.
+.
+.Pp
+Next I had to install rEFInd,
+since the default Mac boot manager
+is really not keen on booting much.
+Installing it requires using the
+macOS recovery partition these days.
+But there was a problem
+with my new boot menu:
+I was promised a picture of Puffy,
+and instead I just got some abstract coloured circles!
+Turns out a bunch of OS icons
+got removed from rEFInd at some point,
+and I had to rescue Puffy
+from the git history.
+.
+.Pp
+So I could happily boot
+.Ox
+by selecting Puffy,
+but I had no networking.
+I thought the wifi chip might be supported by
+.Xr bwfm 4 ,
+but I got unlucky and it's a BCM4360,
+which everything hates.
+Based on the jcs gist,
+I checked the list of hardware
+supported by the
+.Xr urtwn 4
+driver for a wifi dongle to order.
+Just having a clear list
+in the driver manual is wonderful.
+I went with the Edimax EW-7811Un v2,
+which I could get for around $20.
+It's nice and tiny,
+though it has a piercing blue LED
+(destroy all blue LEDs)
+which I had to cover with electrical tape.
+.
+.Pp
+I had to do one other thing
+before I could get it all working, though.
+When I had checked the
+.Xr urtwn 4
+hardware list,
+I had been looking at
+.Ox Ns -current ,
+but I had installed
+.Ox 6.8 ,
+and support for the v2 hardware
+I had bought was added after that release.
+So I downloaded a snapshot
+.Pa bsd.rd
+along with the
+.Xr urtwn 4
+firmware file
+to a USB drive
+and upgraded the system.
+.
+.Pp
+Connecting to wifi with
+.Xr ifconfig 8
+is a breeze, by the way,
+and then you just write the same thing to a
+.Xr hostname.if 5
+file to make it automatic.
+I wanted to use
+.Ox
+for exactly this reason:
+simple, consistent, cohesive, well-documented tools.
+.
+.Pp
+Finally, I got to configuring.
+The console is configured with
+.Xr wsconsctl 8 ,
+and similarly you can put the commands in
+.Xr wsconsctl.conf 5
+to have them run at boot.
+I added
+.Li display.brightness=50%
+to tone down the brightness,
+which is initially 100%,
+and
+.Li keyboard.backlight=0%
+to turn off those annoying lights.
+.Xr wsconsctl.conf 5
+is also where you can set
+trackpad settings if you're not using
+.Xr synaptics 4 .
+I ended up using:
+.Bd -literal -offset indent
+mouse1.tp.tapping=1
+mouse1.tp.scaling=0.2
+mouse1.reverse_scrolling=1
+.Ed
+.Pp
+This enables tapping with several fingers
+to simulate different mouse buttons,
+makes the cursor move at a reasonable speed
+and scrolling move in the right direction.
+I also set up my usual modified QWERTY layout.
+.
+.Pp
+For
+.Xr X 7
+I had enabled
+.Xr xenodm 1 ,
+which seems quite nice.
+It automatically prompts you to add your
+.Xr ssh 1
+keys to
+.Xr ssh-agent 1
+when you log in.
+One of the reasons I had not wanted
+to set up another graphical system
+is that I thought
+I would have to make too many choices,
+and that I would have to choose least bad options
+rather than actually good options,
+but
+.Ox
+already includes reasonable choices.
+I wanted to use
+.Xr cwm 1 ,
+so I started a basic
+.Pa .xsession
+file:
+.Bd -literal -offset indent
+\&. ~/.profile
+export LC_CTYPE=en_US.UTF-8
+xset r rate 175 m 5/4 0
+xmodmap ~/.config/X/modmap
+xrdb -load ~/.config/X/resources
+exec cwm -c ~/.config/cwm/cwmrc
+.Ed
+.
+.Pp
+The
+.Xr xset 1
+command sets keyboard repeat rate
+and mouse acceleration.
+I spent some time going through
+.Xr cwm 1 Ap s
+functions and writing up bindings
+that would get me something close enough
+to what I'm used to in macOS.
+Most importantly,
+putting everything on the 4 modifier (command key).
+.
+.Pp
+I also added key bindings on F1 and F2
+to adjust the brightness with
+.Xr xbacklight 1 ,
+and on F10, F11 and F12
+to adjust volume with
+.Xr sndioctl 1 .
+I'm not sure why the F keys
+just send regular F1, F2, etc.\&
+regardless of the Fn key.
+I don't use F keys for anything else though,
+so I'm not too concerned.
+Once again,
+.Xr sndioctl 1
+is such an easy straightforward tool:
+.Bd -literal -offset indent
+bind-key F10 "sndioctl output.mute=!"
+bind-key F11 "sndioctl output.level=-0.05"
+bind-key F12 "sndioctl output.level=+0.05"
+.Ed
+.
+.Pp
+For aesthetic configuration,
+I added a new output to my
+.Xr scheme 1
+colour scheme tool for
+.Xr X 7 Ns -style
+RGB and
+.Xr xterm 1
+resources.
+Normally I use the
+.Em Go Mono
+font,
+but since
+.Ox
+already includes
+.Em Luxi Mono ,
+which
+.Em Go Mono
+is based on,
+I used that.
+The most important configuration
+to make anything readable on a high-DPI display is:
+.Bd -literal -offset indent
+Xft.dpi: 144
+Xft.antialias: true
+Xft.hinting: false
+.Ed
+.
+.Pp
+I'm annoyed that I haven't found
+where these resources are actually documented.
+I would hope they'd be in
+.Xr Xft 3
+or something,
+but they're not.
+Anyway,
+turning off hinting
+seems absolutely necessary
+to prevent text from looking like garbage.
+.
+.Pp
+It seems that to get a reasonably sized cursor
+I need to install
+.Sy xcursor-dmz .
+I'd prefer if there wasn't this one
+extra package that I needed
+for a reasonable setup.
+Tangentially,
+I've never understood why
+the black versions of dmz cursors
+are called
+.Dq aa
+when it seems like that
+would stand for antialiasing
+or something.
+.Bd -literal -offset indent
+Xcursor.size: 64
+Xcursor.theme: dmz-aa
+.Ed
+.
+.Pp
+For a desktop background,
+I found a cute bitmap (little picture)
+of snowflakes already in the system
+and used colours from my usual scheme:
+.Bd -literal -offset indent
+xsetroot -bitmap /usr/X11R6/include/X11/bitmaps/xsnow \e
+	-bg rgb:14/13/0E -fg rgb:7A/49/55
+.Ed
+.
+.Pp
+Since I'd rather not install anything
+I don't have to,
+I went with the default
+.Xr xterm 1 .
+It seems more than adequate, honestly.
+I read through its RESOURCES
+section to configure it how I like.
+The important settings are
+.Sy XTerm*utf8
+and
+.Sy XTerm*metaSendsEscape .
+Since I'm used to copying and pasting on macOS,
+I added equivalent
+.Dq translations :
+.Bd -literal -offset indent
+XTerm*VT100*translations: #override \en\e
+	Super <Key>C: copy-selection(CLIPBOARD) \en\e
+	Super <Key>V: insert-selection(CLIPBOARD)
+.Ed
+.
+.Pp
+The next thing I needed
+was a clock and battery indicator.
+I actually had my battery die on me
+while I was doing all this,
+which reminded me.
+.Xr xclock 1
+would be perfect,
+but then I'd need something else
+for battery.
+There are a couple basic battery indicators
+for X in ports,
+but they're terribly ugly.
+I wanted something as simple as
+.Xr xclock 1 ,
+but that I could add some other text to.
+Then I realized I could just use
+.Xr xterm 1
+for that.
+To my
+.Pa xsession
+I added:
+.Bd -literal -offset indent
+xterm -name clock -geometry 14x1-0+0 -sl 0 -e clock &
+.Ed
+.Pp
+This places a little terminal
+in the top-right corner of the screen
+with no scrollback lines,
+running a script called
+.Pa clock .
+To have
+.Xr cwm 1
+treat it like a
+.Dq panel
+and show it on every desktop,
+I added this to my
+.Pa cwmrc :
+.Bd -literal -offset indent
+ignore clock
+autogroup 0 clock,XTerm
+.Ed
+.Pp
+The
+.Pa clock
+script simply uses
+.Xr date 1
+and
+.Xr apm 8
+to print the time and battery charge
+every minute:
+.Bd -literal -offset indent
+tput civis
+sleep=$(( 60 - $(date +'%S' | sed 's/^0//') ))
+while :; do
+	if [ $(apm -a) -eq 1 ]; then
+		printf '%3s%%' "$(apm -l)"
+	else
+		test $(apm -b) -eq 2 && tput setaf 1 bold
+		printf '%3.3sm' "$(apm -m)"
+		tput sgr0
+	fi
+	printf ' %s\r' "$(date +'%a %H:%M')"
+	sleep $sleep
+	sleep=60
+done
+.Ed
+.Pp
+The initial setting of
+.Va sleep
+is to align the updates
+with the minute ticking over.
+I made the battery output
+a bit fancier by showing
+percentage while charging,
+minutes left while discharging,
+and highlighting in red
+when the battery is
+.Dq critical .
+.
+.Pp
+Now is a good time to mention adding
+.Ql apmd_flags=-A
+to
+.Pa /etc/rc.conf.local
+to enable
+.Dq automatic performance adjustment ,
+or not running your battery flat
+as fast as possible mode.
+It seems like I can get up to 3 hours
+of battery life depending on the screen brightness,
+but this is quite an old battery by now.
+.
+.Pp
+The other thing I needed
+was something to tone down
+that awful, evil blue light from the screen.
+I asked around and someone told me about
+.Xr sct 1 ,
+originally written by tedu.
+The package also includes a little
+.Xr sctd 1
+script that you can add to your
+.Pa .xsession
+to have it automatically adjust
+the colour temperature throughout the day.
+My eyes are no longer being assaulted.
+.
+.Pp
+While I was doing all this,
+I of course needed to talk about it on IRC,
+and it was very nice to be able to
+install my own IRC client with:
+.Bd -literal -offset indent
+doas pkg_add catgirl
+.Ed
+.Pp
+I don't plan to do
+general Web Browsing on
+.Ox ,
+and there is definitely
+no good choice for browser,
+so I just installed
+.Xr imv 1 ,
+.Xr mpv 1 ,
+.Xr youtube-dl 1
+and
+.Xr w3m 1 .
+I wrote a script
+to open images by piping
+.Xr curl 1
+into
+.Xr imv 1 ,
+videos with
+.Xr mpv 1 ,
+and everything else with
+.Xr w3m 1
+in a new
+.Xr xterm 1 .
+Annoyingly,
+.Xr mpv 1
+seems incapable of exiting
+without segfaulting.
+That's quality.
+.
+.Pp
+One thing I am still missing
+is automatic brightness adjustment
+based on ambient light
+like macOS can do.
+I can read the sensor with
+.Xr sysctl 8
+.Cm hw.sensors.asmc0.illuminance0 ,
+which is measured in lux.
+I tried doing something with it in a script,
+but it seems tricky to map its value
+to brightness adjustments
+and to play nice with manual brightness changes,
+so I'll just keep doing it manually for now.
+.
+.Pp
+Update:
+prx sent mail to let me know about
+.Aq Lk https://github.com/jcs/xdimmer .
+I should've guessed jcs had written something.
+.
+.Pp
+And that's my current
+.Ox
+setup after a week of using it.
+I'm quite enjoying it,
+and still being pleasantly surprised
+by the quality-of-life from
+.Ox
+tools and documentation.
+For a small example,
+I can jump to sections
+or flag definitions in
+.Xr man 1
+using
+.Ic :t .
+Systems without basic usability like that
+should be ashamed.
+.
+.Pp
+I would post a screenshot,
+but this is
+.Li text.causal.agency
+;)
+.
+.Sh SEE ALSO
+.Lk https://gist.github.com/jcs/5573685
+.Pp
+My full configurations are in
+.Aq Lk https://git.causal.agency/src .
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.
+.Sh BUGS
+There's a red LED
+inside the headphone jack
+that is always on
+and I have no idea how to turn off.
+If anyone knows
+please send me an email.
diff --git a/www/text.causal.agency/017-unpasswords.7 b/www/text.causal.agency/017-unpasswords.7
new file mode 100644
index 00000000..f9643f2f
--- /dev/null
+++ b/www/text.causal.agency/017-unpasswords.7
@@ -0,0 +1,153 @@
+.Dd February 20, 2021
+.Dt UNPASSWORDS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Unpasswords
+.Nd password anti-management
+.
+.Sh DESCRIPTION
+Right away I want to say
+that I'm not trying to tell anyone
+how to manage their online authentication.
+This is just how I do it,
+and I haven't seen anyone else write about it.
+.
+.Pp
+I don't use a password manager.
+It's not a type of software
+I want to deal with.
+For the small handful of sites
+that I use regularly
+and that actually matter,
+I use strong passwords
+(stored in my noggin)
+and TOTP.
+For everything else,
+I simply do not know the password,
+and neither does any software.
+.
+.Pp
+I think I started doing this one time
+when I had legitimately forgotten
+the password to some old account.
+I clicked on
+.Dq forgot my password
+and opened the email,
+but I didn't want to
+come up with a new password
+I would just forget again.
+Instead I set a random one
+.Po
+I usually use
+.Ql openssl rand -base64 33
+for this
+.Pc
+and immediately used that to log in
+while it was still in my clipboard.
+Next time I wanted to log in,
+I could use
+.Dq forgot my password
+again.
+.
+.Pp
+Thinking about it,
+I realized that any web authentication
+with an email password reset flow
+is only ever as strong as
+the authentication for your email account.
+So what is the point of having
+all these passwords set on different sites?
+They all answer to your email account,
+and storing them in a password manager
+seems to add another potential point of failure.
+May as well have no other passwords at all,
+or as close as possible.
+.Po
+Shout out to sites like Liberapay
+and asciienema
+which let me not set a password at all.
+.Pc
+.
+.Pp
+So I started doing that for any site
+that I don't regularly log in to.
+Going through the password reset flow
+can be a bit slow,
+but it doesn't need to be done often.
+And I can do it from anywhere
+I have access to my email,
+which I feel is more easily reliable
+than syncing password management databases.
+It's quite stress-free.
+.
+.Pp
+After doing this manually for years,
+this week I finally got around to
+writing some automation for it.
+A while ago I had written
+.Xr imbox 1 ,
+a tool to directly export mail
+in mboxrd format from IMAP,
+along with
+.Xr git-fetch-email 1 ,
+a wrapper which offloads configuration to
+.Xr git-config 1 .
+It can match emails by
+Subject, From, To and Cc.
+This week I added a flag
+to use IMAP IDLE
+to wait for a matching message
+if there isn't one already,
+and a flag to move matching messages
+(for example to Trash)
+after exporting them.
+.
+.Pp
+With those two new flags,
+I started writing some shell scripts
+to automate the password reset flow
+using
+.Xr curl 1
+to submit forms and
+.Xr git-fetch-email 1
+with
+.Xr sed 1
+to pull the reset tokens
+from my inbox.
+At the end of the script,
+the random password it set
+is copied to the clipboard
+and the login page for the site is opened.
+So now logging in is as simple
+as running a command,
+waiting for the login page to open,
+and pasting.
+.
+.Pp
+The script isn't sophisticated,
+but I don't think it needs to be.
+I've written functions
+for a couple different sites already,
+and they all work in mostly the same way.
+Writing a new one is just a matter
+of identifying the form URLs and fields
+along with where the token is in the email.
+I'm not going to turn this automation
+into any kind of generally usable project,
+because I don't want to have to
+maintain functions for tonnes of different services.
+If you're interested in this idea,
+I encourage you to use my script as a template
+and implement the functions for services you use.
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk https://git.causal.agency/imbox
+.It
+.Lk https://causal.agency/bin/sup.html
+.El
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/018-operating-systems.7 b/www/text.causal.agency/018-operating-systems.7
new file mode 100644
index 00000000..691102e2
--- /dev/null
+++ b/www/text.causal.agency/018-operating-systems.7
@@ -0,0 +1,86 @@
+.Dd February 22, 2021
+.Dt OPERATING-SYSTEMS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Operating systems
+.Nd criteria
+.
+.Sh DESCRIPTION
+Sometimes in conversation
+I use the term
+.Dq real operating system
+which people,
+perhaps rightfully,
+take as inflammatory.
+But I have actually thought about
+what I mean when I say
+.Dq real operating system
+and come up with
+this list of criteria.
+.
+.Pp
+An operating system should be...
+.Bl -bullet
+.It
+Consistent and cohesive:
+all parts of the system should have similar
+usage, configuration, documentation and so on.
+Parts of the system should naturally work together,
+because they were designed to do so.
+.
+.It
+Documented:
+the system should include its own documentation.
+A user should not have to
+search some external wiki
+to learn how the system works.
+It should be obvious
+where to find documentation
+on a particular topic.
+.
+.It
+Programmable:
+the system should provide
+a way to program the computer.
+A computer which cannot be programmed
+is not a computer at all.
+Usually this takes the form
+of a C compiler
+and the tools that go with it.
+In earlier times,
+it might have been
+a BASIC interpreter.
+.
+.It
+Examinable and modifiable:
+the full source tree
+for the system should be included,
+or easily obtainable
+through official means.
+A user should have no trouble
+finding the corresponding source
+for a part of the system.
+Together with the previous point,
+the source tree should be
+compiled by the included toolchain,
+allowing local modification.
+.El
+.
+.Pp
+Some things that may be parts
+of real operating systems,
+but are not themselves operating systems:
+a kernel,
+a package manager,
+a collection of packages.
+.
+.Pp
+I will leave it as an
+.Dq exercise for the reader
+to guess which operating systems
+meet these criteria
+and which don't.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/019-mailing-list.7 b/www/text.causal.agency/019-mailing-list.7
new file mode 100644
index 00000000..b3490a94
--- /dev/null
+++ b/www/text.causal.agency/019-mailing-list.7
@@ -0,0 +1,286 @@
+.Dd March  4, 2021
+.Dt MAILING-LIST 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Mailing List
+.Nd a small-scale approach
+.
+.Sh DESCRIPTION
+When I initially published
+some software I expected
+other people to use,
+I just asked that patches
+be mailed directly to me,
+but I figured that
+if more people were interested,
+it would be better
+to have a mailing list.
+Unfortunately
+email software,
+mailing list options in particular,
+are quite daunting.
+I wanted a light-weight option
+that would require me to host
+as little software as possible.
+.
+.Pp
+My regular email is hosted by Fastmail,
+and I poked around its settings
+to see what I could do.
+It turns out Fastmail lets you
+configure address aliases to
+.Dq also send to all contacts in
+a contacts group.
+That's a mailing list!
+I created a group called
+.Dq List
+and an alias called
+.Mt list@causal.agency
+configured to deliver to that group.
+So it's really just an alias
+for my regular address
+that happens to also
+deliver to another group of people.
+.
+.Pp
+It's easier to just configure
+and manage one mailing list,
+so what I do is ask patches and feedback
+to be sent to
+.Mt list+catgirl@causal.agency ,
+for example.
+Fastmail treats any
+.Ar +suffix
+the same as the base address,
+but the full address can be used
+by subscribers to filter mail by topic
+if they wish.
+.
+.Pp
+To subscribe someone to the list,
+I add their contact to the group.
+For a long time I was planning
+to write some software
+to manage these subscriptions.
+It should be possible
+to process subscription requests from IMAP
+and manipulate the contact group with CardDAV.
+When I went to start implementing this,
+however,
+I found CardDAV (and WebDAV in general)
+completely inscrutable.
+It's the kind of protocol
+that is split across like 20
+different RFCs
+and you can't understand anything
+by just reading
+the one you actually care about.
+So I've given up on that
+and will keep manually subscribing people
+on request.
+.
+.Pp
+The only thing missing, then,
+is a way for people to read
+mail sent to the list
+while they aren't subscribed.
+All the existing
+mailing list archive software
+I know of
+expects to have the mail locally,
+but I'd rather keep all my mail in IMAP.
+First,
+in order to make sure
+I keep a complete archive
+of the mailing list in IMAP,
+I added a small amount
+of Sieve code
+to my Fastmail filters configuration:
+.Bd -literal -offset indent
+if address :matches ["To", "Cc"] "list*@causal.agency" {
+	fileinto :copy :flags "\e\eSeen" "INBOX.List";
+}
+.Ed
+.
+.Pp
+Sieve is a small standard language
+specifically for filtering mail.
+This bit of code matches
+anything sent to the list
+and adds a copy of it
+(the original is going into my inbox)
+to the
+.Dq List
+folder
+and marks the copy as read.
+.
+.Pp
+With a pristine IMAP mailbox
+to export from,
+I wrote a new archive generator.
+It's called
+.Xr bubger 1
+kirg (have it in a way).
+My goal was to render directly from IMAP
+and produce only static files as output,
+making it not only easy to serve,
+but also to run in one place
+and copy the files elsewhere.
+That's important to me
+because it has access to my email,
+so I'd rather run it
+on my local network and
+.Xr rsync 1
+its output into The Cloud.
+The static files are in
+HTML, Atom and mboxrd formats.
+.
+.Pp
+The architecture of
+.Xr bubger 1
+is that for each piece of mail,
+identified by its UID in the mailbox,
+HTML and Atom fragments
+are exported along with the mboxrd.
+Those fragments are then stitched together
+using the IMAP SORT and THREAD extensions
+to make full pages and feeds
+for each thread.
+The fragments act as a cache
+for subsequent runs.
+.
+.Pp
+I admit I did some
+pretty questionable things
+to achieve this.
+Namely,
+I wrote a small string templating engine in C.
+I use it to produce the HTML
+and XML for Atom,
+as well as to generate URLs
+and paths.
+I'm really happy with how it works, actually.
+This is also where
+I really started using
+one of my favourite C hacks:
+.Bd -literal -offset indent
+#define Q(...) #__VA_ARGS__
+.Ed
+.
+.Pp
+I quote all my HTML/XML templates
+with this and it's lovely.
+.
+.Pp
+I've been working on
+.Xr bubger 1
+on and off for almost a year now,
+and it's been interesting.
+I learned a lot about how email
+works from having to deal with
+all the ways a message can be.
+Thankfully a lot of that dealing
+is done by the IMAP server.
+.
+.Pp
+As for running it,
+I initially just ran it with
+.Xr cron 8 ,
+and that's still a good way to go.
+To hook it up to
+.Xr rsync 1 ,
+pipe it like so:
+.Bd -literal -offset indent
+bubger -C list [...] | rsync -a --files-from=- list remote:list
+.Ed
+.
+.Pp
+Later,
+I got a little annoyed
+with having to wait
+for the next run
+if I wanted to link
+to some mail I just received.
+I added an option
+to use IMAP IDLE
+to wait for new mail continuously
+and I started running it
+under my process supervisor,
+.Xr catsitd 8 .
+.
+.Pp
+The setup is a little more complex
+to feed the list of updated files to
+.Xr rsync 1 .
+I added the
+.Xr catsit-watch 1
+utility to run a command
+when a file changes,
+and in my
+.Xr catsit.conf 5
+I have the following:
+.Bd -literal -offset indent
+bubger	~/.local/libexec/bubger
+rsync	catsit-watch -i -f ~/list/UIDNEXT ~/.local/libexec/rsync
+.Ed
+.
+.Pp
+The
+.Pa ~/.local/libexec/bubger
+script runs
+.Xr bubger 1 ,
+writing the list of updated paths to
+.Pa ~/list/FILES :
+.Bd -literal -offset indent
+exec bubger -i -C ~/list [...] >~/list/FILES
+.Ed
+.
+.Pp
+And the
+.Pa ~/.local/libexec/rsync
+script gets run each time a
+.Xr bubger 1
+update completes
+.Po
+.Pa UIDNEXT
+is always the last file written
+.Pc
+and copies the listed files
+to the remote host:
+.Bd -literal -offset indent
+exec rsync -a --files-from=$HOME/list/FILES ~/list remote:list
+.Ed
+.
+.Pp
+I haven't tagged any
+.Xr bubger 1
+releases yet
+because it hasn't gotten
+a huge amount of testing,
+and I'm not sure anyone but me
+would even want to use it.
+But I'm happy
+with how it's working right now,
+so I might tag 1.0 soon
+just for fun.
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk https://causal.agency/list/
+.It
+.Lk https://git.causal.agency/bubger/about
+.It
+.Lk https://git.causal.agency/catsit/about
+.El
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.
+.Sh BUGS
+Almost every time
+I try to type
+.Dq mailing list
+I instead type
+.Dq mailist list .
diff --git a/www/text.causal.agency/020-c-style.7 b/www/text.causal.agency/020-c-style.7
new file mode 100644
index 00000000..9816dbc3
--- /dev/null
+++ b/www/text.causal.agency/020-c-style.7
@@ -0,0 +1,172 @@
+.Dd March 16, 2021
+.Dt C-STYLE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm C Style
+.Nd a rough description
+.
+.Sh DESCRIPTION
+This is a rough description
+of the style in which I write C,
+since it's uncommon
+but some people seem to like it.
+I don't have any hard rules,
+it just needs to look right.
+.
+.Ss Superficialities
+I use tabs
+and they're set to 4 characters wide
+in my editor.
+I keep my lines shorter than 80 columns,
+which I enforce by
+not resizing my terminal's width.
+I use block indentation only,
+meaning I write long function calls
+like this:
+.Bd -literal -offset indent
+fprintf(
+    imap.w, "%s UID THREAD %s UTF-8 %s\er\en",
+    Atoms[thread], algo, search
+);
+.Ed
+.Pp
+Anything that can be sorted
+should be sorted,
+with trailing commas where possible.
+This and block indentation
+make for simpler diffs.
+.Pp
+I either write single-line ifs
+or always use braces.
+I put parentheses
+around ternary expressions.
+I use camelCase
+for functions and variables,
+and PascalCase for types and constants.
+When an acronym appears
+in an identifier,
+it's in either all lower case
+or all upper case.
+The despicable SCREAMING_SNAKE_CASE
+is reserved for macros.
+I don't set globals or statics to zero
+since that is already the default.
+I don't compare against zero or NULL
+unnecessarily.
+.
+.Ss \&No side-effects in control flow
+I never write a function call
+with side-effects
+inside the condition of an if statement.
+I find this makes following the
+.Dq happy path
+through functions
+much easier.
+I write things like this:
+.Bd -literal -offset indent
+pidFile = open(pidPath, O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
+if (pidFile < 0) err(EX_CANTCREAT, "%s", pidPath);
+
+error = flock(pidFile, LOCK_EX | LOCK_NB);
+if (error && errno != EWOULDBLOCK) err(EX_IOERR, "%s", pidPath);
+if (error) errx(EX_CANTCREAT, "%s: file is locked", pidPath);
+.Ed
+.Pp
+I do write side-effects
+inside for and while statement heads,
+since that's generally expected.
+For some reason
+I like to write the constant first
+if I'm comparing the result of an assignment
+with a side-effect.
+.Bd -literal -offset indent
+for (ssize_t len; 0 <= (len = getline(&buf, &cap, file)); ++line)
+.Ed
+.
+.Ss Paragraphs
+I leave blank lines
+between logical chunks of
+.Dq things happening .
+This is usually between side-effects
+with their related error handling,
+or between groups of closely related side-effects.
+I try to keep variable declarations
+glued to the top of the bit of code
+they're used in.
+.
+.Ss Leading break
+I've mentioned this previously.
+I write my switch statement breaks
+before each case label.
+Doing this aligns nicely,
+and being in the habit
+means I always avoid
+accidental fallthrough.
+.Bd -literal -offset indent
+switch (opt) {
+    break; case 'a': append = 1;
+    break; case 'd': delay = strtol(optarg, NULL, 10);
+    break; case 'f': watch(kq, optarg);
+    break; case 'i': init = 1;
+    break; default: return EX_USAGE;
+}
+.Ed
+.
+.Ss Function type definitions
+Function types are always typedef'd,
+and it's the function type itself
+that is defined,
+not a function pointer type!
+I put the typedef above any functions
+that are supposed to be of that type
+so it's clear what the pattern is.
+.Bd -literal -offset indent
+typedef void Action(struct Service *service);
+Action *fn = NULL;
+.Ed
+.
+.Ss Constants
+I prefer enums over #defines
+for integer constants,
+and static const strings over #defines
+unless I want to do concatenation.
+.Bd -literal -offset indent
+enum { Cap = 1024 };
+.Ed
+.Pp
+I avoid the preprocessor
+wherever possible,
+with the notable exception of X macros,
+which I've talked about previously.
+Doing things in the actual language
+makes for easier debugging.
+.
+.Ss Organization
+I usually use only one header file
+in each project.
+The dependency is easy to declare
+and the complete rebuild
+when the header changes
+isn't a problem for small projects.
+Unless it's a single-file program,
+I name the file which contains main
+something generic,
+since the name of the project
+isn't relevant to its function.
+I name functions like
+.Ar nounVerb ,
+and all the functions for a 
+.Ar noun
+are defined in
+.Pa noun.c .
+Not really to do with C,
+but I always put a FILES section
+in my README pages
+to briefly describe
+the layout of the code
+for anyone looking to
+read or make changes to it.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/021-time-machine.7 b/www/text.causal.agency/021-time-machine.7
new file mode 100644
index 00000000..93d35c1e
--- /dev/null
+++ b/www/text.causal.agency/021-time-machine.7
@@ -0,0 +1,144 @@
+.Dd April 25, 2021
+.Dt TIME-MACHINE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Time Machine
+.Nd an awful one
+.
+.Sh DESCRIPTION
+If, like me,
+you have a Raspberry Pi 3 at home
+that you've just upgraded to
+.Fx 13.0
+which has a hard drive
+from an old laptop
+attached to it by USB adapter
+with ZFS on it
+and you want to
+use that as a Time Machine
+backup destination
+over SMB using
+.Xr samba 8 ,
+despite
+.Xr samba 8
+being awful software
+and using ZFS on a system
+with only 1 GB of RAM
+being a terrible idea,
+this is how to do it.
+.
+.Pp
+In
+.Pa /usr/local/etc/smb4.conf :
+.Bd -literal -offset indent
+[global]
+vfs objects = zfsacl catia fruit streams_xattr
+fruit:metadata = stream
+fruit:model = Macmini
+
+[TimeMachine]
+read only = no
+path = /media/zhdd/backup/TimeMachine
+fruit:time machine = yes
+fruit:time machine max size = 250G
+.Ed
+.
+.Pp
+The important thing here is
+.Sy zfsacl
+in the vfs objects list.
+Most pages will tell you about the others,
+but without
+.Sy zfsacl
+Time Machine will just fail to
+create the backup
+and not provide any useful error.
+I'm not actually sure if the
+.Sy fruit:metadata
+setting is required,
+but a bunch of pages recommend it.
+The
+.Sy fruit:model
+just makes it look nice in Finder.
+The rest creates an SMB share called
+.Dq TimeMachine
+that macOS will be willing to use.
+You can limit the size of the share that
+.Xr samba 8
+reports so that Time Machine
+doesn't fill up the whole drive.
+.
+.Pp
+The other important thing to do
+is to create some swap space.
+When I first tried backing up
+to this share,
+it stopped after a while
+because
+.Xr smbd 8
+got killed
+when there was nowhere to swap pages to.
+A wiki page told me to
+create swap on ZFS like this:
+.Bd -literal -offset indent
+zfs create -V 2G \e
+	-o org.freebsd:swap=on \e
+	-o checksum=off \e
+	-o compression=off \e
+	-o dedup=off \e
+	-o sync=disabled \e
+	-o primarycache=none \e
+	zhdd/swap
+swapon /dev/zvol/zhdd/swap
+.Ed
+.
+.Pp
+To be fair to
+.Xr samba 8 ,
+most of the memory
+is being used by the ZFS ARC
+.Po
+which you can see in
+.Xr top 1
+.Pc ,
+but
+.Xr smbd 8
+still seems to be using
+far more memory than is reasonable.
+It's interesting seeing processes
+with 0 RES in
+.Xr htop 1
+because they're all being swapped out
+while the ARC takes half the available RAM.
+And having to wait for my shell
+to be paged back in when I quit
+.Xr htop 1 .
+.
+.Pp
+Anyway,
+as expected this whole thing
+is terribly slow.
+On my initial backup,
+I'm currently at 26.49 GB
+of 104.22 GB
+with an estimate of 8 hours remaining.
+Normally transfer time estimates
+are wildly inaccurate,
+but I think in this case it's right.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.
+.Sh BUGS
+.Fx
+doesn't seem to want to mount
+the ZFS volumes on the hard-drive-over-USB
+automatically at boot.
+I have to
+.Xr zpool-import 8
+the drive manually each time.
+I don't know if there's a workaround for this,
+but I don't have anything essential
+to the system on the drive,
+and it doesn't need to reboot often.
diff --git a/www/text.causal.agency/022-swans-are-dead.7 b/www/text.causal.agency/022-swans-are-dead.7
new file mode 100644
index 00000000..8664e886
--- /dev/null
+++ b/www/text.causal.agency/022-swans-are-dead.7
@@ -0,0 +1,164 @@
+.Dd May  5, 2021
+.Dt SWANS-ARE-DEAD 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Swans Are Dead
+.Nd album by Swans
+.
+.Sh DESCRIPTION
+Swans Are Dead
+is the best Swans album.
+Among my favourites are
+Soundtracks for the Blind,
+To Be Kind
+and Love of Life,
+but Swans Are Dead
+is the one I come back to
+most consistently.
+I'm always in the mood
+to listen to these tunes.
+.
+.Pp
+It's interesting to me
+that I enjoy it so much,
+I think because I had the expectation
+that live albums
+are not of the same quality
+as studio albums,
+but that's just completely untrue
+in the case of Swans.
+The performances are excellent
+and the recording is
+for the most part perfect.
+The album feels live,
+without any distracting deficiencies
+of live recording
+that would take you out
+of just enjoying the music.
+.
+.Bl -ohang
+.It Dq Feel Happiness
+This track feels kind of special
+since it's the only song on the album
+that was never released
+as part of another project.
+I absolutely love this format of song.
+It's like 10 minutes of build
+before any lyrics happen,
+which you only get after
+the wave of the first part
+of the song collapses.
+It bookends the first disc nicely with
+.Dq Blood Promise,
+I think,
+which is sort of the reverse.
+.
+.It Dq Blood On Yr Hands
+This is such a great start
+to the Jarboe-focused
+section of the black disc.
+A cappella apart from the hum
+of the equipment on stage,
+I love this vocal performance.
+I sing this song,
+terribly,
+in the shower.
+The lack of instrumental
+seems to make it stick in my mind even more.
+.
+.It Dq I Crawled
+This is another great vocal performance
+by Jarboe.
+It's so much more dynamic and intense
+than the version of this song
+released much earlier on Young God
+with Gira's vocals.
+I remember seeing a bad comment
+somewhere online
+from someone who couldn't stand
+any Swans song Jarboe sang on.
+They must have never heard
+this version of
+.Dq I Crawled.
+Incredible.
+.
+.It Dq Blood Promise
+My favourite track on
+Swans Are Dead,
+by far.
+I had actually never heard of
+.Dq The Whiffenpoof Song
+until I looked up
+the recording they use
+to introduce this song
+and indicate it's the last of the show.
+Anyway,
+this track highlights
+what makes Swans live albums
+so interesting.
+This performance of the song
+has evolved so much
+from the studio recording on
+The Great Annihilator.
+They share the same lyrics,
+but the earlier version is only 4:15,
+to the live version's fifteen and a half minutes!
+And it sucks me in the whole time.
+As the song winds down
+you can hear an audience member yell,
+.Dq Don't stop!
+and I agree.
+.
+.It Dq The Sound
+One of my all-time favourite songs.
+It's the one that got me to listen to
+Soundtracks for the Blind,
+and might've gotten me into Swans altogether.
+I don't quite remember
+what order I started listening to things in.
+This version of it is great.
+I don't think I could choose
+between this and the studio recording.
+There are just
+two ways to enjoy it.
+I love how frantic the guitars get
+at the height of this track.
+.
+.It Dq I See Them All Lined Up
+This version of the song
+is way more harsh
+than the version on Soundtracks.
+It loses some contrast
+between the verses
+and the explosions of sound
+punctuating them,
+it just hits hard
+the whole time.
+.
+.It Dq Yum Yab
+An absolute banger.
+The drums sound so good on this
+and they really get me moving.
+The whole thing is delightfully nasty and fun.
+.El
+.
+.Pp
+Everything else on the album
+is good too,
+of course,
+I just don't have as much to say.
+There's almost two and a half hours of music
+on this thing!
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+I want to try writing
+about different kinds of things here,
+and this is my first attempt
+at doing so.
+There's more music
+I want to write about,
+and maybe some other
+non-computer topics.
diff --git a/www/text.causal.agency/023-sparse-checkout.7 b/www/text.causal.agency/023-sparse-checkout.7
new file mode 100644
index 00000000..925bc043
--- /dev/null
+++ b/www/text.causal.agency/023-sparse-checkout.7
@@ -0,0 +1,144 @@
+.Dd June  9, 2021
+.Dt SPARSE-CHECKOUT 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Sparse Checkout
+.Nd a cool git feature
+.
+.Sh DESCRIPTION
+I was going to write a post about
+.Xr git-subtree 1
+(and I still plan to!)
+but while talking about it
+with a friend
+I came across another command:
+.Xr git-sparse-checkout 1 .
+I got pretty excited because
+I already had a use case for it.
+.
+.Pp
+.Xr git-sparse-checkout 1
+does pretty much what it sounds like.
+It lets you only have
+a subset of files in the repository actually
+.Dq checked out .
+This is really useful
+for huge respositories
+where you are only interested in
+some part of it.
+Any operation touching the working tree
+is much faster because
+it can skip all the files you don't care about.
+.
+.Pp
+My use case is with the
+.Fx
+.Xr ports 7
+tree,
+which recently moved to git
+and contains almost 14 thousand files.
+Working with the whole repository
+was super painful.
+.Xr git-status 1 ,
+which I run as a habit
+when my shell is idle,
+would take dozens of seconds
+to check the whole working tree
+and report back.
+(I didn't get any real time measurements
+before enabling
+.Xr git-sparse-checkout 1 ,
+and I'm not about to disable it now,
+since it'd have to check out
+all those files again.)
+I'm only actually working on
+a small handful of ports,
+so all that work is wasted.
+Time to turn on sparse checkout:
+.Bd -literal -offset indent
+git sparse-checkout init --cone
+.Ed
+.
+.Pp
+The
+.Fl \-cone
+option here
+(which I keep reading as
+.Dq clone
+because it's git)
+restricts the kinds of patterns
+you can use to select files to check out,
+but makes the calculation more efficient.
+Basically it means you can only select
+paths along with everything below them,
+which I think is pretty much
+always what you want anyway.
+Enabling sparse checkout
+can take quite a while
+because it has to do a lot of un-checking-out.
+I should mention
+that you can pass
+.Fl \-sparse
+to
+.Xr git-clone 1
+to avoid ever checking out
+the whole tree.
+.
+.Pp
+The default selection when you run
+.Cm init
+is to check out all the files
+at the root of the repository,
+but none of the subdirectories.
+For
+.Xr ports 7 ,
+I also want to check out
+the shared scripts and Makefiles:
+.Bd -literal -offset indent
+git sparse-checkout add Keywords Mk Templates Tools
+.Ed
+.
+.Pp
+And then I can selectively check out
+just the ports I'm working on:
+.Bd -literal -offset indent
+git sparse-checkout add irc/catgirl irc/pounce
+.Ed
+.
+.Pp
+After enabling sparse checkout,
+.Xr git-status 1
+takes what I'd call
+a normal amount of time.
+I also did this on
+a couple-weeks-out-of-date copy of the
+.Xr ports 7
+tree,
+and when I ran
+.Xr git-pull 1
+it was also really quick,
+because it didn't have to bother
+updating all those files
+I'm not interested in.
+It still downloads all the git objects,
+of course,
+and you can just add any new paths you need
+to the sparse checkout list.
+My disk usage also went down
+by about a gigabyte.
+.
+.Pp
+I'm super pleased to discover this part of git,
+because it makes working with huge
+and/or monorepo-style repositories
+so much more feasible!
+You can see how I came across it,
+since
+.Xr git-subtree 1
+is also a useful tool for monorepos.
+Stay tuned for that post,
+I guess :)
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/024-seprintf.7 b/www/text.causal.agency/024-seprintf.7
new file mode 100644
index 00000000..d1af2e1a
--- /dev/null
+++ b/www/text.causal.agency/024-seprintf.7
@@ -0,0 +1,137 @@
+.Dd June 12, 2021
+.Dt SEPRINTF 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm seprintf
+.Nd an snprintf alternative
+.
+.Sh SYNOPSIS
+.Ft "char *"
+.Fn seprintf "char *ptr" "char *end" "const char *fmt" "..."
+.
+.Sh DESCRIPTION
+While discussing string building in C recently,
+mcf pointed out
+.Xr seprint 2
+from Plan 9,
+and it kind of blew my mind.
+I had implemented my own function in
+.Xr catgirl 1
+for building up strings using
+.Xr snprintf 3
+and a struct containing
+pointer, length and capacity,
+but it felt out of place.
+.Fn seprintf
+(I add the
+.Dq f ,
+Plan 9 doesn't)
+is a much simpler
+and more
+.Dq C-like
+interface with really nice usage patterns.
+.
+.Pp
+The obvious difference from
+.Xr snprintf 3
+is that
+.Fn seprintf
+takes an
+.Fa end
+pointer
+rather than a size.
+This means you need only calculate it
+once for each buffer,
+rather than subtracting
+the running length from the buffer size.
+.Fn seprintf Ap s
+return value is a pointer
+to the terminating null
+of the string it wrote,
+so you can pass that back in
+to continue appending
+to the same buffer.
+.
+.Pp
+I'm not sure of the exact behaviour on Plan 9,
+but my implementation indicates truncation occurred
+by returning the
+.Fa end
+pointer.
+That makes it both easy to check,
+and perfectly fine to keep calling
+.Fn seprintf
+anyway.
+It just won't write anything if
+.Fa ptr
+==
+.Fa end .
+.
+.Pp
+In the case of formatting failure
+(which should be prevented by
+compile-time format string checking,
+but should still be considered),
+.Fn seprintf
+returns
+.Dv NULL .
+I'm again not sure if this matches Plan 9.
+I like this a lot better than
+.Xr snprintf 3
+returning -1,
+because an unchecked
+.Dv NULL
+is likely to quickly cause a crash,
+while blindly adding
+.Xr snprintf 3 Ap s
+return value
+to your running length
+is a non-obvious logic error.
+.
+.Sh EXAMPLES
+Here's an example of what some code using
+.Fn seprintf
+might look like:
+.Bd -literal -offset indent
+char buf[4096];
+char *ptr = buf, *end = &buf[sizeof(buf)];
+ptr = seprintf(ptr, end, "argv: ");
+for (int i = 1; i < argc; ++i) {
+	ptr = seprintf(
+		ptr, end, "%s%s",
+		(i > 1 ? ", " : ""), argv[i]
+	);
+}
+if (ptr == end) errx(1, "truncation occurred :(");
+.Ed
+.
+.Pp
+And here is the very short implementation of it against
+.Xr vsnprintf 3
+which I copy into my project header files:
+.Bd -literal -offset indent
+#include <stdarg.h>
+#include <stdio.h>
+static inline char *
+seprintf(char *ptr, char *end, const char *fmt, ...)
+	__attribute__((format(printf, 3, 4)));
+static inline char *
+seprintf(char *ptr, char *end, const char *fmt, ...) {
+	va_list ap;
+	va_start(ap, fmt);
+	int n = vsnprintf(ptr, end - ptr, fmt, ap);
+	va_end(ap);
+	if (n < 0) return NULL;
+	if (n > end - ptr) return end;
+	return ptr + n;
+}
+.Ed
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+Another short one before
+.Xr git-subtree 1 .
+I just think this function
+is really neat.
diff --git a/www/text.causal.agency/Makefile b/www/text.causal.agency/Makefile
new file mode 100644
index 00000000..a9330045
--- /dev/null
+++ b/www/text.causal.agency/Makefile
@@ -0,0 +1,49 @@
+WEBROOT ?= /usr/local/www/text.causal.agency
+LIBEXEC ?= /usr/local/libexec
+
+CFLAGS += -Wall -Wextra
+
+TXTS += 001-make.txt
+TXTS += 002-writing-mdoc.txt
+TXTS += 003-pleasant-c.txt
+TXTS += 004-uloc.txt
+TXTS += 005-testing-c.txt
+TXTS += 006-some-libs.txt
+TXTS += 007-cgit-setup.txt
+TXTS += 008-how-irc.txt
+TXTS += 009-casual-update.txt
+TXTS += 010-irc-suite.txt
+TXTS += 011-libretls.txt
+TXTS += 012-inability.txt
+TXTS += 013-hot-tips.txt
+TXTS += 014-using-vi.txt
+TXTS += 015-reusing-tags.txt
+TXTS += 016-using-openbsd.txt
+TXTS += 017-unpasswords.txt
+TXTS += 018-operating-systems.txt
+TXTS += 019-mailing-list.txt
+TXTS += 020-c-style.txt
+TXTS += 021-time-machine.txt
+TXTS += 022-swans-are-dead.txt
+TXTS += 023-sparse-checkout.txt
+TXTS += 024-seprintf.txt
+
+all: ${TXTS}
+
+.SUFFIXES: .7 .txt
+
+.7.txt:
+	mandoc -T utf8 $< | col -bx > $@
+
+feed.atom: feed.sh ${TXTS}
+	sh feed.sh > feed.atom
+
+clean:
+	rm -f ${TXTS} feed.atom igp
+
+install: ${TXTS} feed.atom
+	install -p -m 644 ${TXTS} feed.atom ${WEBROOT}
+
+install-igp: igp
+	install igp ${LIBEXEC}
+	install -p -m 644 igp.c ${WEBROOT}
diff --git a/www/text.causal.agency/feed.sh b/www/text.causal.agency/feed.sh
new file mode 100644
index 00000000..f45bd326
--- /dev/null
+++ b/www/text.causal.agency/feed.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+set -eu
+
+readonly Root='https://text.causal.agency'
+
+updated=$(date -u '+%FT%TZ')
+cat <<-EOF
+	<?xml version="1.0" encoding="utf-8"?>
+	<feed xmlns="http://www.w3.org/2005/Atom">
+	<title>Causal Agency</title>
+	<author><name>June</name><email>june@causal.agency</email></author>
+	<link href="${Root}"/>
+	<link rel="self" href="${Root}/feed.atom"/>
+	<id>${Root}/</id>
+	<updated>${updated}</updated>
+EOF
+
+encode() {
+	sed '
+		s/&/\&amp;/g
+		s/</\&lt;/g
+		s/"/\&quot;/g
+	' "$@"
+}
+
+for txt in *.txt; do
+	entry="${txt%.txt}.7"
+	date=$(grep '^[.]Dd' "$entry" | cut -c 5-)
+	title=$(grep '^[.]Nm' "$entry" | cut -c 5- | encode)
+	summary=$(grep '^[.]Nd' "$entry" | cut -c 5- | encode)
+	published=$(date -ju -f '%B %d, %Y %T' "${date} 00:00:00" '+%FT%TZ')
+	mtime=$(stat -f '%m' "$entry")
+	updated=$(date -ju -f '%s' "$mtime" '+%FT%TZ')
+	cat <<-EOF
+		<entry>
+		<title>${title}</title>
+		<summary>${summary}</summary>
+		<link href="${Root}/${txt}"/>
+		<id>${Root}/${txt}</id>
+		<published>${published}</published>
+		<updated>${updated}</updated>
+		<content type="xhtml">
+		<div xmlns="http://www.w3.org/1999/xhtml">
+	EOF
+	printf '<pre>'
+	encode "$txt"
+	cat <<-EOF
+		</pre>
+		</div>
+		</content>
+		</entry>
+	EOF
+done
+
+echo '</feed>'
diff --git a/www/text.causal.agency/igp.1 b/www/text.causal.agency/igp.1
new file mode 100644
index 00000000..ccfaeaa6
--- /dev/null
+++ b/www/text.causal.agency/igp.1
@@ -0,0 +1,49 @@
+.Dd January 14, 2021
+.Dt IGP 1
+.Os
+.
+.Sh NAME
+.Nm igp
+.Nd insane gopher posse
+.
+.Sh SYNOPSIS
+.Nm
+.Op Fl h Ar host
+.Op Fl p Ar port
+.Ar directory
+.
+.Sh DESCRIPTION
+The
+.Nm
+utility is a simple
+Internet Gopher server
+which can be started by
+.Xr inetd 8 .
+It serves directory listings
+and files under
+.Ar directory .
+.
+.Pp
+The arguments are as follows:
+.Bl -tag -width Ds
+.It Fl h Ar host
+Set the host used in directory listings.
+The default is the local host name.
+.It Fl p Ar port
+Set the port used in directory listeings.
+The default is port 70.
+.El
+.
+.Sh STANDARDS
+.Rs
+.%A B. Alberti
+.%A F. Anklesaria
+.%A D. Johnson
+.%A P. Lindner
+.%A M. McCahill
+.%A D. Torrey
+.%T The Internet Gopher Protocol
+.%I IETF
+.%R RFC 1436
+.%D March 1993
+.Re
diff --git a/www/text.causal.agency/igp.c b/www/text.causal.agency/igp.c
new file mode 100644
index 00000000..d7db2b28
--- /dev/null
+++ b/www/text.causal.agency/igp.c
@@ -0,0 +1,143 @@
+/* Copyright (C) 2021  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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sysexits.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef __FreeBSD__
+#include <sys/capsicum.h>
+#endif
+
+static int compar(const void *_a, const void *_b) {
+	const struct dirent *a = _a;
+	const struct dirent *b = _b;
+	if (a->d_type != b->d_type) {
+		return (a->d_type > b->d_type) - (a->d_type < b->d_type);
+	}
+	return strcmp(a->d_name, b->d_name);
+}
+
+int main(int argc, char *argv[]) {
+	int error;
+	const char *host = NULL;
+	const char *port = "70";
+	for (int opt; 0 < (opt = getopt(argc, argv, "h:p:"));) {
+		switch (opt) {
+			break; case 'h': host = optarg;
+			break; case 'p': port = optarg;
+			break; default:  return EX_USAGE;
+		}
+	}
+	if (optind == argc) return EX_USAGE;
+	if (!host) {
+		static char buf[256];
+		error = gethostname(buf, sizeof(buf));
+		if (error) abort();
+		host = buf;
+	}
+
+	const char *path = argv[optind];
+	int root = open(path, O_RDONLY | O_DIRECTORY);
+	if (root < 0) err(EX_NOINPUT, "/");
+
+#ifdef __FreeBSD__
+	cap_rights_t cap;
+	error = cap_enter()
+		|| cap_rights_limit(STDIN_FILENO, cap_rights_init(&cap, CAP_READ))
+		|| cap_rights_limit(STDOUT_FILENO, cap_rights_init(&cap, CAP_WRITE))
+		|| cap_rights_limit(STDERR_FILENO, &cap)
+		|| cap_rights_limit(
+			root, cap_rights_init(&cap, CAP_PREAD, CAP_FSTATAT, CAP_FSTATFS)
+		);
+	if (error) abort();
+#else
+#warning "This is completely insecure without capsicum(4)!"
+#endif
+
+	char buf[1024];
+	if (!fgets(buf, sizeof(buf), stdin)) return EX_PROTOCOL;
+	char *ptr = buf;
+	char *sel = strsep(&ptr, "\t\r\n");
+	if (sel[0] == '/') sel++;
+
+	int fd = (sel[0] ? openat(root, sel, O_RDONLY) : root);
+	if (fd < 0) err(EX_NOINPUT, "%s", sel);
+
+	struct stat stat;
+	error = fstat(fd, &stat);
+	if (error) err(EX_IOERR, "%s", sel);
+	if (!(stat.st_mode & (S_IFREG | S_IFDIR))) {
+		errx(EX_NOINPUT, "%s: Not a file or directory", sel);
+	}
+
+	if (stat.st_mode & S_IFREG) {
+#ifdef __FreeBSD__
+		error = sendfile(fd, STDOUT_FILENO, 0, 0, NULL, NULL, 0);
+		if (!error) return EX_OK;
+#endif
+		char buf[4096];
+		for (ssize_t len; 0 < (len = read(fd, buf, sizeof(buf)));) {
+			fwrite(buf, len, 1, stdout);
+		}
+		return EX_OK;
+	}
+
+	DIR *dir = fdopendir(fd);
+	if (!dir) err(EX_IOERR, "%s", sel);
+
+	size_t len = 0;
+	size_t width = 0;
+	static struct dirent ents[4096];
+	for (struct dirent *ent; len < 4096 && (ent = readdir(dir));) {
+		if (ent->d_name[0] == '.') continue;
+		if (ent->d_type != DT_REG && ent->d_type != DT_DIR) continue;
+		if (ent->d_namlen > width) width = ent->d_namlen;
+		ents[len++] = *ent;
+	}
+
+	qsort(ents, len, sizeof(ents[0]), compar);
+	for (size_t i = 0; i < len; ++i) {
+		char mtime[26] = "";
+		if (ents[i].d_type == DT_REG) {
+			error = fstatat(fd, ents[i].d_name, &stat, 0);
+			if (error) err(EX_IOERR, "%s/%s", sel, ents[i].d_name);
+			ctime_r(&stat.st_mtime, mtime);
+			mtime[24] = '\0';
+		}
+		printf(
+			"%c%-*s  %s\t%s%s%s\t%s\t%s\r\n",
+			(ents[i].d_type == DT_DIR ? '1' : '0'),
+			(int)width, ents[i].d_name, mtime,
+			sel, (sel[0] ? "/" : ""), ents[i].d_name, host, port
+		);
+	}
+
+	printf("i-- \t\t%s\t%s\r\n", host, port);
+	printf("0Served by IGP (AGPLv3)\tigp.c\ttext.causal.agency\t70\r\n");
+	printf(".\r\n");
+}