aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2017-07-07 08:47:53 +1200
committerTom Ryder <tom@sanctum.geek.nz>2017-07-07 08:47:53 +1200
commit53ebb4a269b8155346a535b8a24b991093a59a90 (patch)
tree23f3c3d8e937ef6b2dd045c1d2dfc9cd1194fd48
parentMerge branch 'master' into port/sunos/illumos/openindiana (diff)
parentEscape % signs in prompt command output (diff)
downloaddotfiles-53ebb4a269b8155346a535b8a24b991093a59a90.tar.gz
dotfiles-53ebb4a269b8155346a535b8a24b991093a59a90.zip
Merge branch 'master' into port/sunos/illumos/openindiana
-rw-r--r--.gitignore8
-rw-r--r--ISSUES.markdown5
-rw-r--r--Makefile31
-rw-r--r--README.markdown15
-rw-r--r--bash/bashrc.d/prompt.bash14
-rw-r--r--bin/csmw.awk1
-rw-r--r--bin/ddup.awk6
-rw-r--r--bin/gwp.awk11
-rw-r--r--bin/hms.awk16
-rw-r--r--bin/max.awk6
-rw-r--r--bin/maybe.sh3
-rw-r--r--bin/mean.awk3
-rw-r--r--bin/med.awk10
-rw-r--r--bin/mex.sh6
-rw-r--r--bin/mftl.awk12
-rw-r--r--bin/mi5.awk19
-rw-r--r--bin/mktd.sh5
-rw-r--r--bin/mw.awk4
-rw-r--r--bin/oii.mi519
-rw-r--r--bin/onl.awk4
-rw-r--r--bin/rnda.sh7
-rw-r--r--bin/rndi.awk38
-rw-r--r--bin/rndl.awk39
-rw-r--r--bin/rndl.mi538
-rw-r--r--bin/sec.awk18
-rw-r--r--bin/tm.sh2
-rw-r--r--bin/tot.awk1
-rw-r--r--bin/trs.awk11
-rw-r--r--bin/unf.awk2
-rw-r--r--bin/xrq.awk13
-rw-r--r--games/drakon.awk2
-rw-r--r--games/philsay.sh46
-rw-r--r--games/pks.awk53
-rw-r--r--gnupg/gpg.conf.mi52
-rw-r--r--gnupg/sks-keyservers.net/README.markdown9
-rw-r--r--gnupg/sks-keyservers.net/crl.pem27
-rw-r--r--gnupg/sks-keyservers.net/sks-keyservers.netCA.pem32
-rw-r--r--gnupg/sks-keyservers.net/sks-keyservers.netCA.pem.asc16
-rw-r--r--install/install-conf.sh10
-rw-r--r--keychain/profile.d/keychain.sh2
-rw-r--r--ksh/kshrc.d/prompt.ksh4
-rw-r--r--man/man1/mi5.1df2
-rw-r--r--man/man1/oii.1df21
-rw-r--r--man/man1/rndi.1df15
-rw-r--r--man/man1/rndl.1df2
-rw-r--r--man/man1/trs.1df2
-rw-r--r--man/man6/philsay.6df28
-rw-r--r--man/man6/pks.6df29
-rw-r--r--man/man7/dotfiles.7df947
-rw-r--r--man/man7/dotfiles.7df.header3
-rw-r--r--mpd/profile.d/mpd.sh2
-rw-r--r--sh/profile.d/games.sh2
-rw-r--r--sh/shrc.d/ed.sh4
-rw-r--r--sh/shrc.d/env.sh8
-rw-r--r--sh/shrc.d/hgrep.sh2
-rw-r--r--sh/shrc.d/prompt.sh2
-rw-r--r--tmux/tmux.conf2
m---------vim/bundle/unimpaired0
-rw-r--r--wget/wgetrc14
-rw-r--r--zsh/zshrc.d/prompt.zsh14
60 files changed, 1407 insertions, 262 deletions
diff --git a/.gitignore b/.gitignore
index c4a552f8..deccfe5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,6 +67,9 @@ bin/motd
bin/murl
bin/mw
bin/nlbr
+bin/oii
+bin/oii.sh
+bin/oii.m4
bin/onl
bin/osc
bin/p
@@ -92,8 +95,6 @@ bin/rnda
bin/rndf
bin/rndi
bin/rndl
-bin/rndl.sh
-bin/rndl.m4
bin/rnds
bin/sd2u
bin/sec
@@ -148,6 +149,8 @@ games/chkl
games/dr
games/drakon
games/kvlt
+games/philsay
+games/pks
games/rndn
games/rot13
games/squ
@@ -159,5 +162,4 @@ git/gitconfig.m4
gnupg/gpg.conf
gnupg/gpg.conf.m4
include/mktd.m4
-man/man7/dotfiles.7df
urxvt/ext/select
diff --git a/ISSUES.markdown b/ISSUES.markdown
index a69e07df..48007919 100644
--- a/ISSUES.markdown
+++ b/ISSUES.markdown
@@ -4,9 +4,6 @@ Known issues
* man(1) completion doesn't work on OpenBSD as manpath(1) isn't a thing on
that system; need to find some way of finding which manual directories
should be searched at runtime, if there is one.
-* OpenBSD doesn't have a `pandoc` package at all. It would be nice to find
- some way of converting the README.markdown into a palatable troff format
- with some more readily available (and preferably less heavyweight) tool.
* The checks gscr(1df) makes to determine where it is are a bit naïve (don't
work with bare repos) and could probably be improved with some appropriate
git-reflog(1) calls
@@ -23,3 +20,5 @@ Known issues
my own stuff in there
* Completion for custom functions e.g. `sd` should ideally respect
`completion-ignore-case` setting
+* Document `install-conf` target once I'm sure it's not a dumb idea
+* Need to decide whether I care about XDG, and implement it if I do
diff --git a/Makefile b/Makefile
index d498f4c6..6cfee7a1 100644
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,6 @@
install-bin \
install-bin-man \
install-curl \
- install-dotfiles-man \
install-dunst \
install-ex \
install-finger \
@@ -42,6 +41,7 @@
install-vim-gui-config \
install-vim-pathogen \
install-vim-plugins \
+ install-wget \
install-x \
install-zsh \
check \
@@ -137,6 +137,7 @@ BINS = bin/ap \
bin/murl \
bin/mw \
bin/nlbr \
+ bin/oii \
bin/onl \
bin/osc \
bin/pa \
@@ -202,8 +203,8 @@ BINS = bin/ap \
BINS_M4 = bin/chn.m4 \
bin/edda.m4 \
+ bin/oii.m4 \
bin/pst.m4 \
- bin/rndl.m4 \
bin/swr.m4 \
bin/tlcs.m4 \
bin/try.m4 \
@@ -211,8 +212,8 @@ BINS_M4 = bin/chn.m4 \
BINS_SH = bin/chn.sh \
bin/edda.sh \
+ bin/oii.sh \
bin/pst.sh \
- bin/rndl.sh \
bin/swr.sh \
bin/tlcs.sh \
bin/try.sh \
@@ -225,6 +226,8 @@ GAMES = games/aaf \
games/dr \
games/drakon \
games/kvlt \
+ games/philsay \
+ games/pks \
games/rndn \
games/rot13 \
games/squ \
@@ -276,8 +279,8 @@ clean distclean:
bin/chn.sh: bin/chn.m4 include/mktd.m4
bin/edda.sh: bin/edda.m4 include/mktd.m4
+bin/oii.sh: bin/oii.m4 include/mktd.m4
bin/pst.sh: bin/pst.m4 include/mktd.m4
-bin/rndl.sh: bin/rndl.m4 include/mktd.m4
bin/swr.sh: bin/swr.m4 include/mktd.m4
bin/tlcs.sh: bin/tlcs.m4 include/mktd.m4
bin/try.sh: bin/try.m4 include/mktd.m4
@@ -295,14 +298,9 @@ KEYSERVER = hkps://hkps.pool.sks-keyservers.net
gnupg/gpg.conf: gnupg/gpg.conf.m4
m4 \
- -D HOME=$(HOME) \
-D KEYSERVER=$(KEYSERVER) \
gnupg/gpg.conf.m4 > $@
-man/man7/dotfiles.7df: README.markdown man/man7/dotfiles.7df.header
- cat man/man7/dotfiles.7df.header README.markdown | \
- pandoc -sS -t man -o $@
-
MAILDIR = $(HOME)/Mail
install: install-bin \
@@ -315,6 +313,9 @@ install: install-bin \
install-readline \
install-vim
+install-conf:
+ sh install/install-conf.sh
+
install-abook:
mkdir -p -- $(HOME)/.abook
cp -p -- abook/abookrc $(HOME)/.abook
@@ -341,10 +342,6 @@ install-bin-man:
install-curl:
cp -p -- curl/curlrc $(HOME)/.curlrc
-install-dotfiles-man: man/man7/dotfiles.7df
- mkdir -p -- $(HOME)/.local/share/man/man7
- cp -p -- man/man7/*.7df $(HOME)/.local/share/man/man7
-
install-dunst: install-x
mkdir -p -- $(HOME)/.config/dunst
cp -p -- dunst/dunstrc $(HOME)/.config/dunst
@@ -370,9 +367,8 @@ install-git: git/gitconfig
cp -p -- git/gitconfig $(HOME)/.gitconfig
install-gnupg: gnupg/gpg.conf
- mkdir -m 0700 -p -- $(HOME)/.gnupg $(HOME)/.gnupg/sks-keyservers.net
+ mkdir -m 0700 -p -- $(HOME)/.gnupg
cp -p -- gnupg/*.conf $(HOME)/.gnupg
- cp -p -- gnupg/sks-keyservers.net/* $(HOME)/.gnupg/sks-keyservers.net
install-gtk:
mkdir -p -- $(HOME)/.config/gtk-3.0
@@ -393,7 +389,7 @@ install-less:
install-mpd: install-sh
mkdir -p -- $(HOME)/.mpd/playlists
- cp -p .. mpd/profile.d/* $(HOME)/.profile.d
+ cp -p -- mpd/profile.d/* $(HOME)/.profile.d
cp -p -- mpd/mpdconf $(HOME)/.mpdconf
install-mutt:
@@ -483,6 +479,9 @@ install-vim-pathogen: install-vim-plugins
mkdir -p -- $(HOME)/.vim/autoload
ln -fs -- ../bundle/pathogen/autoload/pathogen.vim $(HOME)/.vim/autoload
+install-wget:
+ cp -p -- wget/wgetrc $(HOME)/.wgetrc
+
install-x: check-xinit
mkdir -p -- \
$(HOME)/.config \
diff --git a/README.markdown b/README.markdown
index bd4d482a..adc4c73a 100644
--- a/README.markdown
+++ b/README.markdown
@@ -203,8 +203,6 @@ in `sh/shrc.d` to be loaded by any POSIX interactive shell. Those include:
* `bc()` silences startup messages from GNU `bc(1)`.
* `ed()` tries to get verbose error messages, a prompt, and a Readline
environment for `ed(1)`.
-* `env()` sorts the output of `env(1)` if it was invoked with no arguments,
- just for convenience when running it interactively.
* `gdb()` silences startup messages from `gdb(1)`.
* `gpg()` quietens `gpg(1)` down for most commands.
* `grep()` tries to apply color and other options good for interactive use if
@@ -499,6 +497,7 @@ Installed by the `install-bin` target:
* `motd(1df)` shows the system MOTD.
* `mw(1df)` prints alphabetic space-delimited words from the input one per
line.
+* `oii(1df)` runs a command on input only if there is any.
* `onl(1df)` crunches input down to one printable line.
* `osc(1df)` implements a `netcat(1)`-like wrapper for `openssl(1)`'s
`s_client` subcommand.
@@ -555,6 +554,8 @@ There's some silly stuff in `install-games`:
* `squ(6df)` makes a reduced Latin square out of each line of input.
* `kvlt(6df)` translates input to emulate a style of typing unique to black
metal communities on the internet.
+* `philsay(6df)` shows a picture to accompany `pks(6df)` output.
+* `pks(6df)` laughs at a randomly selected word.
* `rndn(6df)` implements an esoteric random number generation algorithm.
* `strik(6df)` outputs s̶t̶r̶i̶k̶e̶d̶ ̶o̶u̶t̶ struck out text.
* `rot13(6df)` rotates the Latin letters in its input.
@@ -566,13 +567,9 @@ Manuals
-------
The `install-bin` and `install-games` targets install manuals for each script
-they install. There's also an `install-dotfiles-man` target that uses
-`pandoc(1)` to reformat this document as a manual page for section 7
-(`dotfiles(7df)`) if you want that. I haven't made that install by default,
-because `pandoc(1)` is a bit heavy.
-
-If you want to use the manuals, you may need to add `~/.local/share/man` to
-your `~/.manpath` or `/etc/manpath` configuration, depending on your system.
+they install. If you want to use the manuals, you may need to add
+`~/.local/share/man` to your `~/.manpath` or `/etc/manpath` configuration,
+depending on your system.
Testing
-------
diff --git a/bash/bashrc.d/prompt.bash b/bash/bashrc.d/prompt.bash
index 782af287..a6506a60 100644
--- a/bash/bashrc.d/prompt.bash
+++ b/bash/bashrc.d/prompt.bash
@@ -167,7 +167,10 @@ prompt() {
# Print the status in brackets; add a git: prefix only if there
# might be another VCS prompt (because PROMPT_VCS is set)
printf '(%s%s%s%s)' \
- "${PROMPT_VCS:+git:}" "$name" "${proc:+:"$proc"}" "$state"
+ "${PROMPT_VCS:+git:}" \
+ "${name//\\/\\\\}" \
+ "${proc:+:"${proc//\\/\\\\}"}" \
+ "${state//\\/\\\\}"
;;
# Subversion prompt function
@@ -193,6 +196,7 @@ prompt() {
branch=${branch#/}
branch=${branch#branches/}
branch=${branch%%/*}
+ [[ -n $branch ]] || branch=unknown
# Parse the output of svn status to determine working copy state
local symbol
@@ -210,7 +214,9 @@ prompt() {
((untracked)) && state=${state}'?'
# Print the state in brackets with an svn: prefix
- printf '(svn:%s%s)' "${branch:-unknown}" "$state"
+ printf '(svn:%s%s)' \
+ "${branch//\\/\\\\}" \
+ "${state//\\/\\\\}"
;;
# VCS wrapper prompt function; print the first relevant prompt, if any
@@ -224,7 +230,7 @@ prompt() {
# Show return status of previous command in angle brackets, if not zero
ret)
# shellcheck disable=SC2154
- ((ret)) && printf '<%u>' "$ret"
+ ((ret)) && printf '<%u>' "${ret//\\/\\\\}"
;;
# Show the count of background jobs in curly brackets, if not zero
@@ -233,7 +239,7 @@ prompt() {
while read -r ; do
((jobc++))
done < <(jobs -p)
- ((jobc)) && printf '{%u}' "$jobc"
+ ((jobc)) && printf '{%u}' "${jobc//\\/\\\\}"
;;
# No argument given, print prompt strings and vars
diff --git a/bin/csmw.awk b/bin/csmw.awk
index 4479d8f8..351fc749 100644
--- a/bin/csmw.awk
+++ b/bin/csmw.awk
@@ -1,4 +1,5 @@
# Print an English comma-separated list of monospace-quoted words (backticks)
+BEGIN { wc = 0 }
{
for (i = 1; i <= NF; i++)
ws[++wc] = "`" $i "`"
diff --git a/bin/ddup.awk b/bin/ddup.awk
index 2dec1d00..63381cfb 100644
--- a/bin/ddup.awk
+++ b/bin/ddup.awk
@@ -1,2 +1,6 @@
# Skip duplicate lines (without requiring sorted input)
-!seen[$0]++
+$0 in seen { next }
+length($0) {
+ seen[$0] = 1
+ print
+}
diff --git a/bin/gwp.awk b/bin/gwp.awk
index f1e3b3bd..6b558388 100644
--- a/bin/gwp.awk
+++ b/bin/gwp.awk
@@ -7,6 +7,9 @@ BEGIN {
# Words are separated by any non-alphanumeric characters
FS = "[^a-zA-Z0-9]+"
+ # Nothing found yet
+ found = 0
+
# First argument is the word required; push its case downward so we can
# match case-insensitively
word = tolower(ARGV[1])
@@ -15,15 +18,17 @@ BEGIN {
ARGV[1] = ""
# Bail out if we don't have a suitable word
- if (!word)
+ if (!length(word))
fail("Need a single non-null alphanumeric string as a search word")
if (word ~ FS)
fail("Word contains non-alphanumeric characters; use grep(1)")
}
# Bailout function
-function fail(str) {
- printf "%s: %s\n", self, str | "cat >&2"
+function fail(msg) {
+ stderr = "cat >&2"
+ printf "%s: %s\n", self, msg | stderr
+ close(stderr)
exit(2)
}
diff --git a/bin/hms.awk b/bin/hms.awk
index 3054db44..2aa492a1 100644
--- a/bin/hms.awk
+++ b/bin/hms.awk
@@ -1,19 +1,23 @@
# Convert seconds to colon-delimited durations
BEGIN {
OFS = ":"
+ ex = 0
+ stderr = ""
}
# Refuse to deal with anything that's not a positive (unsigned) integer
/[^0-9]/ {
- print "hms: Bad number" | "cat >&2"
- err = 1
+ if (!stderr)
+ stderr = "cat >&2"
+ print "hms: Bad number" | stderr
+ ex = 1
next
}
# Integer looks valid
{
# Break it down into hours, minutes, and seconds
- s = $0
+ s = int($0 + 0)
h = int(s / 3600)
s %= 3600
m = int(s / 60)
@@ -29,4 +33,8 @@ BEGIN {
}
# Done, exit 1 if we had any errors on the way
-END { exit(err > 0) }
+END {
+ if (stderr)
+ close(stderr)
+ exit(ex)
+}
diff --git a/bin/max.awk b/bin/max.awk
index 11d4efd9..f6b84ead 100644
--- a/bin/max.awk
+++ b/bin/max.awk
@@ -1,8 +1,6 @@
# Get the maximum of a list of numbers
-{
- if (NR == 1 || $1 > max)
- max = $1
-}
+BEGIN { max = 0 }
+NR == 1 || $1 > max { max = $1 + 0 }
END {
if (!NR)
exit(1)
diff --git a/bin/maybe.sh b/bin/maybe.sh
index 6e5c8658..bda7bbc0 100644
--- a/bin/maybe.sh
+++ b/bin/maybe.sh
@@ -19,5 +19,4 @@ if [ "$((num >= 0 || den >= 1))" -ne 1 ] ; then
fi
# Perform the test; that's our exit value
-seed=$(rnds)
-test "$(rndi 1 "$den" "$seed")" -le "$num"
+test "$(rndi 1 "$den")" -le "$num"
diff --git a/bin/mean.awk b/bin/mean.awk
index b34dc111..98060389 100644
--- a/bin/mean.awk
+++ b/bin/mean.awk
@@ -1,5 +1,6 @@
# Get the mean of a list of numbers
-{ tot += $1 }
+BEGIN { tot = 0 }
+{ tot += $1 + 0 }
END {
# Error out if we read no values at all
if (!NR)
diff --git a/bin/med.awk b/bin/med.awk
index aee120cb..0f4d6086 100644
--- a/bin/med.awk
+++ b/bin/med.awk
@@ -1,7 +1,13 @@
# Get the median of a list of numbers
+BEGIN {
+ self = "med"
+ stderr = "cat >&2"
+}
{ vals[NR] = $1 }
NR > 1 && vals[NR] < vals[NR-1] && !warn++ {
- printf "med: Input not sorted!\n" | "cat >&2"
+ if (!stderr)
+ stderr = "cat >&2"
+ printf "%s: Input not sorted!\n", self | stderr
}
END {
# Error out if we read no values at all
@@ -12,6 +18,8 @@ END {
else
med = (vals[NR/2] + vals[NR/2+1]) / 2
print med
+ if (stderr)
+ close(stderr)
if (warn)
exit(1)
}
diff --git a/bin/mex.sh b/bin/mex.sh
index 0b3d6c7e..cf4e07e7 100644
--- a/bin/mex.sh
+++ b/bin/mex.sh
@@ -37,7 +37,11 @@ for name ; do
# If the "found" variable was defined to something, we'll try to change its
# permissions
if [ -n "$found" ] ; then
- chmod +x -- "$found" || ex=1
+ case $found in
+ /*) ;;
+ *) found=$PWD/$found ;;
+ esac
+ chmod +x "$found" || ex=1
# If not, we'll report that we couldn't find it, and flag an error for the
# exit status
diff --git a/bin/mftl.awk b/bin/mftl.awk
index 21976337..916348b2 100644
--- a/bin/mftl.awk
+++ b/bin/mftl.awk
@@ -19,7 +19,7 @@ BEGIN { FS = "[ \t:]" }
}
# Check lines matching expected "targets:dependencies" format
-/^[a-zA-Z0-9][a-zA-Z0-9 \t_-]+:([^=]|$)/ {
+/^[a-zA-Z0-9][a-zA-Z0-9./ \t_-]+:([^=]|$)/ {
# Iterate through the targets that don't look like substitutions or
# inference rules and stack them up into an array's keys to keep them
@@ -31,6 +31,12 @@ BEGIN { FS = "[ \t:]" }
# Print unique determined targets, sorted
END {
- for (t in ats)
- print t | "sort"
+ sort = ""
+ for (t in ats) {
+ if (!sort)
+ sort = "sort"
+ print t | sort
+ }
+ if (sort)
+ close(sort)
}
diff --git a/bin/mi5.awk b/bin/mi5.awk
index 48d71657..7acb6f3b 100644
--- a/bin/mi5.awk
+++ b/bin/mi5.awk
@@ -4,19 +4,14 @@ BEGIN {
# You can change any of these, but while changing these is still relatively
# sane...
- if (!length(open))
- open = "<%"
- if (!length(shut))
- shut = "%>"
+ open = "<%"
+ shut = "%>"
# ... changing these probably isn't, and should compel you to rethink your
# code, or quite possibly your entire life thus far.
- if (!length(quote))
- quote = "`"
- if (!length(unquote))
- unquote = "'"
- if (!length(dnl))
- dnl = "dnl"
+ quote = "`"
+ unquote = "'"
+ dnl = "dnl"
# We do not start in a block
bmac = 0
@@ -24,7 +19,9 @@ BEGIN {
# Fatal error function
function fatal(str) {
- printf "%s: %s\n", self, str | "cat >&2"
+ stderr = "cat >&2"
+ printf "%s: %s\n", self, str | stderr
+ close(stderr)
exit(1)
}
diff --git a/bin/mktd.sh b/bin/mktd.sh
index 62b10396..89cdc7c3 100644
--- a/bin/mktd.sh
+++ b/bin/mktd.sh
@@ -1,11 +1,8 @@
# Try to make a random temp directory
-# Get a random seed from rnds(1df); if it's empty, that's still workable
-seed=$(rnds)
-
# Build the intended directory name, with the last element a random integer
# from 1..2^31
-dn=${TMPDIR:-/tmp}/${1:-mktd}.$$.$(rndi 1 2147483648 "$seed")
+dn=${TMPDIR:-/tmp}/${1:-mktd}.$$.$(rndi 1 2147483648)
# Create the directory and print its name if successful
mkdir -m 700 -- "$dn" && printf '%s\n' "$dn"
diff --git a/bin/mw.awk b/bin/mw.awk
index 84332fac..95d70c32 100644
--- a/bin/mw.awk
+++ b/bin/mw.awk
@@ -1,10 +1,10 @@
# Crude approach to get alphabetic words one per line from input, not sorted or
# deduplicated
BEGIN {
- RS = "(--|['_-]*[^[:alnum:]'_-]+['_-]*)"
+ FS = "(--|['_-]*[^[:alnum:]'_-]+['_-]*)"
}
{
for (i = 1; i <= NF; i++)
- if ($i ~ /[[:alpha:]]/)
+ if ($i ~ /[a-zA-Z]/)
print $i
}
diff --git a/bin/oii.mi5 b/bin/oii.mi5
new file mode 100644
index 00000000..51f37fb4
--- /dev/null
+++ b/bin/oii.mi5
@@ -0,0 +1,19 @@
+# Only run a command on input if there was at least one byte
+self=oii
+
+# Need at least a command name
+if [ "$#" -eq 0 ] ; then
+ printf >&2 '%s: Need a command\n' "$self"
+ exit 2
+fi
+
+<%
+include(`include/mktd.m4')
+%>
+
+# There is probably a way better way to do this than writing the whole file to
+# disk and then reading it off again, but until I think of something better,
+# this works and is byte-safe.
+cat - > "$td"/in
+[ -s "$td"/in ] || exit
+"$@" < "$td"/in
diff --git a/bin/onl.awk b/bin/onl.awk
index 466b8451..15e4f46d 100644
--- a/bin/onl.awk
+++ b/bin/onl.awk
@@ -2,8 +2,8 @@
# For each line of input ...
{
- # Strip out non-printable characters and rebuild the fields
- gsub(/[[:cntrl:]]/, "")
+ # Strip out whitespace characters and rebuild the fields
+ gsub(/[\n\t\r ]+/, "")
# Print each field, without a newline; add a leading space if it's not the
# very first one
diff --git a/bin/rnda.sh b/bin/rnda.sh
index b09a8b6f..6a755305 100644
--- a/bin/rnda.sh
+++ b/bin/rnda.sh
@@ -6,11 +6,8 @@ if [ "$#" -eq 0 ] ; then
exit 2
fi
-# Get a random seed from rnds(1df); if it's empty, that's still workable
-seed=$(rnds)
-
-# Get a random integet from 1 to the number of arguments
-argi=$(rndi 1 "$#" "$seed") || exit
+# Get a random integer from 1 to the number of arguments
+argi=$(rndi 1 "$#") || exit
# Shift until that argument is the first argument
shift "$((argi-1))"
diff --git a/bin/rndi.awk b/bin/rndi.awk
index 49df4398..7d5a5b96 100644
--- a/bin/rndi.awk
+++ b/bin/rndi.awk
@@ -1,20 +1,44 @@
# Get a low-quality random number between two integers. Depending on the awk
-# implementation, if you don't provide a third argument (a seed), you might get
-# very predictable random numbers based on the current epoch second.
+# implementation, if you don't have rnds(1df) available to generate a seed of
+# sufficient quality, you might get very predictable random numbers based on
+# the current epoch second.
BEGIN {
+ self = "rndi"
- # Seed with the third argument if given
- if (ARGV[3])
- srand(ARGV[3])
+ # Check we have two arguments
+ if (ARGC != 3)
+ fail("Need a lower and upper bound")
- # If not, just seed with what is probably a date/time-derived value
+ # Floor args and check for sanity
+ lower = int(ARGV[1] + 0)
+ upper = int(ARGV[2] + 0)
+ if (lower >= upper)
+ fail("Bounds must be numeric, first lower than second")
+
+ # Get a random seed if rnds(1df) available
+ rnds = "rnds 2>/dev/null"
+ rnds | getline seed
+ close(rnds)
+
+ # Truncate the seed to 8 characters because mawk might choke on it
+ seed = substr(seed,1,8)
+ if (length(seed))
+ srand(seed + 0)
else
srand()
# Print a random integer bounded by the first and second arguments
- print int(ARGV[1] + rand() * (ARGV[2] - ARGV[1] + 1))
+ print int(lower + rand() * (upper - lower + 1))
# Bail before processing any lines
exit
}
+
+# Bailout function
+function fail(str) {
+ stderr = "cat >&2"
+ printf "%s: %s\n", self, str | stderr
+ close(stderr)
+ exit(2)
+}
diff --git a/bin/rndl.awk b/bin/rndl.awk
new file mode 100644
index 00000000..99f5b4e1
--- /dev/null
+++ b/bin/rndl.awk
@@ -0,0 +1,39 @@
+# Print a random line from input
+
+# Process arguments
+BEGIN {
+
+ # Name self
+ self = "rndl"
+
+ # Get a random seed if rnds(1df) available
+ rnds = "rnds 2>/dev/null"
+ rnds | getline seed
+ close(rnds)
+
+ # Truncate the seed to 8 characters because mawk might choke on it
+ seed = substr(seed,1,8)
+ if (length(seed))
+ srand(seed + 0)
+ else
+ srand()
+}
+
+# Iterate over the lines, randomly assigning the first field of each one with a
+# decreasing probability
+rand() * NR < 1 { ln = $0 }
+
+# Check and print
+END {
+
+ # Check that we processed at least one line
+ if (!NR) {
+ stderr = "cat >&2"
+ printf "%s: No lines found on input\n", self | stderr
+ close(stderr)
+ exit(1)
+ }
+
+ # Print the line
+ print ln
+}
diff --git a/bin/rndl.mi5 b/bin/rndl.mi5
deleted file mode 100644
index f99ccbea..00000000
--- a/bin/rndl.mi5
+++ /dev/null
@@ -1,38 +0,0 @@
-# Print a random line from input
-self=rndl
-
-# If there are no arguments, we're checking stdin; this is more complicated
-# than checking file arguments because we have to count the lines in order to
-# correctly choose a random one, and two passes means we require a temporary
-# file if we don't want to read all of the input into memory (!)
-if [ "$#" -eq 0 ] ; then
-
-<%
-include(`include/mktd.m4')
-%>
-
- # We'll operate on stdin in the temp directory; write the script's stdin to
- # it with cat(1)
- set -- "$td"/stdin
- cat >"$td"/stdin
-fi
-
-# Count the number of lines in the input
-lc=$(sed -- '$=;d' "$@") || exit
-
-# If there were none, bail
-case $lc in
- ''|0)
- printf >&2 'rndl: No lines found on input\n'
- exit 2
- ;;
-esac
-
-# Try to get a random seed from rnds(1df) for rndi(1df)
-seed=$(rnds)
-
-# Get a random line number from rndi(1df)
-ri=$(rndi 1 "$lc" "$seed") || exit
-
-# Print the line using sed(1)
-sed -- "$ri"'!d' "$@"
diff --git a/bin/sec.awk b/bin/sec.awk
index 001b017d..645df147 100644
--- a/bin/sec.awk
+++ b/bin/sec.awk
@@ -1,13 +1,19 @@
# Convert [[[hh:]mm:]ss] timestamps to seconds
# Separator is :, strip out leading zeroes
-BEGIN { FS = ":0*" }
+BEGIN {
+ FS = ":0*"
+ stderr = ""
+ ex = 0
+}
# If no fields, too many fields, or illegal characters, warn, skip line, accrue
# errors
!NF || NF > 3 || /[^0-9:]/ {
- print "sec: Bad format" | "cat >&2"
- err = 1
+ if (!stderr)
+ stderr = "cat >&2"
+ print "sec: Bad format" | stderr
+ ex = 1
next
}
@@ -21,4 +27,8 @@ NF == 2 { printf "%u\n", $1 * 60 + $2 }
NF == 1 { printf "%u\n", $1 }
# Done, exit 1 if we had any errors on the way
-END { exit(err > 0) }
+END {
+ if (stderr)
+ close(stderr)
+ exit(ex)
+}
diff --git a/bin/tm.sh b/bin/tm.sh
index 774dccb1..d5422869 100644
--- a/bin/tm.sh
+++ b/bin/tm.sh
@@ -5,7 +5,7 @@ if [ "$#" -gt 0 ] ; then
:
# If a session exists, just attach to it
-elif command tmux has-session 2>/dev/null ; then
+elif tmux has-session 2>/dev/null ; then
set -- attach-session -d
# Create a new session with an appropriate name
diff --git a/bin/tot.awk b/bin/tot.awk
index eda25724..add5f00e 100644
--- a/bin/tot.awk
+++ b/bin/tot.awk
@@ -1,3 +1,4 @@
# Total a list of numbers
+BEGIN { tot = 0 }
{ tot += $1 }
END { print tot }
diff --git a/bin/trs.awk b/bin/trs.awk
index 5966c520..fbb7eeba 100644
--- a/bin/trs.awk
+++ b/bin/trs.awk
@@ -1,11 +1,8 @@
-# Substitute one string for another in input (no regex)
+# Substitute one string for another in input (no newlines, no regex)
BEGIN {
# Name self
self = "trs"
- # No wordsplitting required
- FS = ""
-
# Two and only two arguments required
if (ARGC != 3)
fail("Need a string and a replacement")
@@ -21,8 +18,10 @@ BEGIN {
}
# Bailout function
-function fail(str) {
- printf "%s: %s\n", self, str | "cat >&2"
+function fail(msg) {
+ stderr = "cat >&2"
+ printf "%s: %s\n", self, msg | stderr
+ close(stderr)
exit(2)
}
diff --git a/bin/unf.awk b/bin/unf.awk
index ac6172f7..7acb09c2 100644
--- a/bin/unf.awk
+++ b/bin/unf.awk
@@ -1,5 +1,7 @@
# Unfold header lines in an internet message, don't touch the body
+BEGIN { buf = "" }
+
# Function to write and empty the buffer
function wrbuf() {
if (length(buf))
diff --git a/bin/xrq.awk b/bin/xrq.awk
index 686cf677..ffd5f124 100644
--- a/bin/xrq.awk
+++ b/bin/xrq.awk
@@ -8,19 +8,22 @@ BEGIN {
# Check we have at least one resource name
if (ARGC < 2) {
- print "xrq: Need at least one resource name" | "cat >&2"
+ stderr = "cat >&2"
+ print "xrq: Need at least one resource name" | stderr
+ close(stderr)
exit(2)
}
# Run `xrdb -query` and search for instances of the requested resource
- while ("xrdb -query" | getline) {
- for (i in ARGV) {
+ xrdb = "xrdb -query"
+ found = 0
+ while (xrdb | getline)
+ for (i in ARGV)
if ($1 == ARGV[i]) {
found = 1
print $2
}
- }
- }
+ close(xrdb)
# Exit successfully if we found at least one result
exit(!found)
diff --git a/games/drakon.awk b/games/drakon.awk
index ce619585..ebca4e95 100644
--- a/games/drakon.awk
+++ b/games/drakon.awk
@@ -6,7 +6,7 @@
tog = 0
for (i = 1; i <= len; i++) {
chr = substr($0, i, 1)
- if (chr ~ /[[:alpha:]]/)
+ if (chr ~ /[a-zA-Z]/)
chr = (tog = !tog) ? tolower(chr) : toupper(chr)
lin = lin chr
}
diff --git a/games/philsay.sh b/games/philsay.sh
new file mode 100644
index 00000000..9270c52e
--- /dev/null
+++ b/games/philsay.sh
@@ -0,0 +1,46 @@
+speech=$(pks "$@") || exit
+printf '\n%066s\n' '( '"$speech"' )'
+cat <<'EOF'
+ /
+
+ .''''''''''''''''''''''..
+ .'''''''''''''''''''''''''''
+ .'''''''''''''''''''''''''''''
+ ,'''''''''''''''''''''''''''''''
+ '''''''''''''''''''''''''''''''':
+ ,'''''''''''##`'''''''''''''''.'''`
+ ;''''''''.###########,'''''',###'''
+ ;'''''';#################:'#####.''
+ `:''''''#########################'.
+ ::` ,'+########################';
+ ''''''': .#####################''
+ ''''''''.####` `;#############;##'
+ ;''''''',####,###: +############.
+ ,###''''''#############` ;##:#######
+ ,#:##''';+#####+ :###### +##+ +
+ ,'#;#,''#####',+###` ;####`+
+ ,#'#,#';############++. ,`##
+ :#####+:#######,@,``@@,#####'
+ ;#+#+#############++++##.#+## +
+ ###+################'####'##
+ #######+###################.# :.
+ ######'########################'
+ ,+#####;#######################
+ ,#######;############'####+###:
+ ,#######################+#####'
+ ,###############' ` #'# +'#
+ #,##.###########'##+##'###'####
+ ``@.############## `+#@@@@@######
+ +```@@################ ,,. . ####.
+ ;````@@,##.##############':..:######
+ ;`````@@@########.##################
+ +````````@@@@#####;####################:
+ +`````.`````@@@@######`###################```+
+ +````````,`````'@@@@@##'#####################`````.
++ ``````````.``````@@@@@@##'###'################```````` +
+```````````````````@@@@@@@'#####;##########,##'`````````````.+
+```````````````````@@@@@@@@@+#####':####+:+'````````````````````,
+```````````````````,@@@@@@@@@#:#########'@@@``````````````````````
+```````````.````````@@@@@@@@@@@@#'#####@@@@@```````````````````````
+```````````.````````@@@@@@@@@@@@@@' @@@@@@@.``````````````````````
+EOF
diff --git a/games/pks.awk b/games/pks.awk
new file mode 100644
index 00000000..1a441980
--- /dev/null
+++ b/games/pks.awk
@@ -0,0 +1,53 @@
+# Ha, ha, ha! Awk!
+
+# Process arguments
+BEGIN {
+
+ # If no arguments left, assume a dictionary file
+ if (ARGC == 1) {
+ ARGC = 2
+ if ("DICT" in ENVIRON)
+ ARGV[1] = ENVIRON["DICT"]
+ else
+ ARGV[1] = "/usr/share/dict/words"
+ }
+
+ # Get a random seed if rnds(1df) available
+ rnds = "rnds 2>/dev/null"
+ rnds | getline seed
+ close(rnds)
+
+ # Truncate the seed to 8 characters because mawk might choke on it
+ seed = substr(seed,1,8)
+ if (length(seed))
+ srand(seed + 0)
+ else
+ srand()
+}
+
+# Iterate over the lines, randomly assigning the first field of each one with a
+# decreasing probability; this method allows a single pass over the input,
+# though it requires a lot of random numbers
+$1 ~ /[a-zA-Z]/ && rand() * ++n < 1 { wr = $1 }
+
+# Ha, ha! Conclusion!
+END {
+
+ # Check that we processed at least one line
+ if (!NR)
+ exit 1
+
+ # Strip trailing possessives and punctuation
+ sub(/[^a-zA-Z]+s*$/, "", wr)
+
+ # Two or three "ha"s? Important decisions here folks
+ hr = int(rand()*2+1)
+ for (ha = "Ha"; hi < hr; hi++)
+ ha = ha ", ha"
+
+ # Capitalise the word
+ wr = toupper(substr(wr,1,1)) substr(wr,2)
+
+ # Print the laughter and the word
+ printf "%s! %s!\n", ha, wr
+}
diff --git a/gnupg/gpg.conf.mi5 b/gnupg/gpg.conf.mi5
index c6793b64..1617a979 100644
--- a/gnupg/gpg.conf.mi5
+++ b/gnupg/gpg.conf.mi5
@@ -26,7 +26,7 @@ keyserver <% KEYSERVER %>
# Retrieve keys automatically; check the keyserver port cert; use whichever
# server is proffered from the pool
-keyserver-options auto-key-retrieve check-cert no-honor-keyserver-url ca-certfile=<% HOME %>/.gnupg/sks-keyservers.net/sks-keyservers.netCA.pem
+keyserver-options auto-key-retrieve no-honor-keyserver-url
# Include trust/validity for UIDs in listings
list-options show-uid-validity
diff --git a/gnupg/sks-keyservers.net/README.markdown b/gnupg/sks-keyservers.net/README.markdown
deleted file mode 100644
index bb19e80e..00000000
--- a/gnupg/sks-keyservers.net/README.markdown
+++ /dev/null
@@ -1,9 +0,0 @@
-sks-keyservers.net CA, CRL, and signature
-=========================================
-
-These files are downloaded from links on the [sks-keyservers.net][1] overview
-page. I've included both their signature file and the revocation list, but it's
-your responsibility to make sure that everything here is verified to your
-satisfaction.
-
-[1]: https://sks-keyservers.net/overview-of-pools.php
diff --git a/gnupg/sks-keyservers.net/crl.pem b/gnupg/sks-keyservers.net/crl.pem
deleted file mode 100644
index ce8cd7a8..00000000
--- a/gnupg/sks-keyservers.net/crl.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN X509 CRL-----
-MIIEhzCCAm8CAQEwDQYJKoZIhvcNAQELBQAwXDELMAkGA1UEBhMCTk8xDTALBgNV
-BAgMBE9zbG8xHjAcBgNVBAoMFXNrcy1rZXlzZXJ2ZXJzLm5ldCBDQTEeMBwGA1UE
-AwwVc2tzLWtleXNlcnZlcnMubmV0IENBFw0xNjA4MDgxOTMwMjdaFw0xNzAyMDQx
-OTMwMjdaMIIBzDASAgEBFw0xMjEwMDkwMTAyMDVaMBICAQIXDTEyMTAwOTAxMDIw
-NVowEgIBAxcNMTQwNTAxMTEyMDU2WjASAgEEFw0xMjEwMDkwMTAyMDVaMBICAQgX
-DTE0MDUwNjE4MjQzMVowEgIBDBcNMTQwNjI4MTI0NTU2WjASAgERFw0xNDA0MjYx
-MjU4MjdaMBICARMXDTEzMTExMzE5MzczM1owEgIBFBcNMTQwNDI5MTczNDA0WjAS
-AgEYFw0xNDA1MDYxODIyMDVaMBICASEXDTE0MDUwMjEyNDQ1MlowEgIBIhcNMTQw
-NDI5MTczNDA0WjASAgEjFw0xMzExMTMxOTM3MzNaMBICASQXDTE0MDUwNzIwMTIy
-OFowEgIBKBcNMTQwNDI5MjAwNjAyWjASAgEpFw0xNDA0MjYxNDI0MjRaMBICASsX
-DTE0MDUwMzE0NDgwNlowEgIBLRcNMTQwNDMwMDgxNTU4WjASAgEuFw0xNDA0MzAw
-ODE2MTdaMBICAS8XDTE0MDQyNjEzMDIxNlowEgIBMxcNMTQwNDI5MTczNjIwWjAS
-AgE0Fw0xNDA1MTIxODQwNThaMBICAWsXDTE1MTEyNTE5MjkyNlqgDzANMAsGA1Ud
-FAQEAgIQJzANBgkqhkiG9w0BAQsFAAOCAgEAKbyM6U6B+RleyuF4O1t/G7SVhvHl
-Yc6bqeV4zNj6B2j9Qw9YtO14USsxVw7RKZPhNvzSJhgBxxRtAOks0tVuGOkjN57V
-qMc9Hiwd/d5WQOkDvaJv44yT5tUq3NhaV8c8bQzeogjXW4h5I0+YsLT84phAnwXK
-Gj95A50vmjXZX7zUbS0TT6GVMjI0RBUIoRu5Ueax4gyX9WOKu0RQ4gZONzlYvbMk
-exX+VWD9JWaA3pWKsxyUrRnDR1e7tUsnDXsh3i5krQinEQ1JYf3/lpGWMOygJV40
-9L0PhZB7cX4xDV2Vg0kBfKU7gr/C0uEF9fZJut8tqwe2LKj54dYa3ktX9Jbjq53F
-WZthON6mMJxtTtjAdWyhQbbM+zy7hOE3cc4aivm9ZAXl30UNBWlPQsJRk3hTScMd
-ye+A9RDRmG68qsoIWbTn/W5PBdiQ+MIxzO8fYwzs8yywQ1/VHHvbS1q0YC1PNono
-6ayc9vHbghGc/RK67BTa1Oj9fqOVgy4XVxzPPzl4JAc4b6ELASS3owzuwUE8CrxP
-drmYWn5ZII+w+0DNn9H5lPasxR5RQ/qPW7T5a8xpuNz+Uj/X4Baedl2DU4wzSP1m
-nvntH0EgqgVXKFpxUT94CSRSiSOReElUZ17j7v28d8IhHCW3rou/JFxMgTIqmIg4
-jyCyTTMl3V9xWCw=
------END X509 CRL-----
diff --git a/gnupg/sks-keyservers.net/sks-keyservers.netCA.pem b/gnupg/sks-keyservers.net/sks-keyservers.netCA.pem
deleted file mode 100644
index 24a2ad2e..00000000
--- a/gnupg/sks-keyservers.net/sks-keyservers.netCA.pem
+++ /dev/null
@@ -1,32 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFizCCA3OgAwIBAgIJAK9zyLTPn4CPMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNV
-BAYTAk5PMQ0wCwYDVQQIDARPc2xvMR4wHAYDVQQKDBVza3Mta2V5c2VydmVycy5u
-ZXQgQ0ExHjAcBgNVBAMMFXNrcy1rZXlzZXJ2ZXJzLm5ldCBDQTAeFw0xMjEwMDkw
-MDMzMzdaFw0yMjEwMDcwMDMzMzdaMFwxCzAJBgNVBAYTAk5PMQ0wCwYDVQQIDARP
-c2xvMR4wHAYDVQQKDBVza3Mta2V5c2VydmVycy5uZXQgQ0ExHjAcBgNVBAMMFXNr
-cy1rZXlzZXJ2ZXJzLm5ldCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
-ggIBANdsWy4PXWNUCkS3L//nrd0GqN3dVwoBGZ6w94Tw2jPDPifegwxQozFXkG6I
-6A4TK1CJLXPvfz0UP0aBYyPmTNadDinaB9T4jIwd4rnxl+59GiEmqkN3IfPsv5Jj
-MkKUmJnvOT0DEVlEaO1UZIwx5WpfprB3mR81/qm4XkAgmYrmgnLXd/pJDAMk7y1F
-45b5zWofiD5l677lplcIPRbFhpJ6kDTODXh/XEdtF71EAeaOdEGOvyGDmCO0GWqS
-FDkMMPTlieLA/0rgFTcz4xwUYj/cD5e0ZBuSkYsYFAU3hd1cGfBue0cPZaQH2HYx
-Qk4zXD8S3F4690fRhr+tki5gyG6JDR67aKp3BIGLqm7f45WkX1hYp+YXywmEziM4
-aSbGYhx8hoFGfq9UcfPEvp2aoc8u5sdqjDslhyUzM1v3m3ZGbhwEOnVjljY6JJLx
-MxagxnZZSAY424ZZ3t71E/Mn27dm2w+xFRuoy8JEjv1d+BT3eChM5KaNwrj0IO/y
-u8kFIgWYA1vZ/15qMT+tyJTfyrNVV/7Df7TNeWyNqjJ5rBmt0M6NpHG7CrUSkBy9
-p8JhimgjP5r0FlEkgg+lyD+V79H98gQfVgP3pbJICz0SpBQf2F/2tyS4rLm+49rP
-fcOajiXEuyhpcmzgusAj/1FjrtlynH1r9mnNaX4e+rLWzvU5AgMBAAGjUDBOMB0G
-A1UdDgQWBBTkwyoJFGfYTVISTpM8E+igjdq28zAfBgNVHSMEGDAWgBTkwyoJFGfY
-TVISTpM8E+igjdq28zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQAR
-OXnYwu3g1ZjHyley3fZI5aLPsaE17cOImVTehC8DcIphm2HOMR/hYTTL+V0G4P+u
-gH+6xeRLKSHMHZTtSBIa6GDL03434y9CBuwGvAFCMU2GV8w92/Z7apkAhdLToZA/
-X/iWP2jeaVJhxgEcH8uPrnSlqoPBcKC9PrgUzQYfSZJkLmB+3jEa3HKruy1abJP5
-gAdQvwvcPpvYRnIzUc9fZODsVmlHVFBCl2dlu/iHh2h4GmL4Da2rRkUMlbVTdioB
-UYIvMycdOkpH5wJftzw7cpjsudGas0PARDXCFfGyKhwBRFY7Xp7lbjtU5Rz0Gc04
-lPrhDf0pFE98Aw4jJRpFeWMjpXUEaG1cq7D641RpgcMfPFvOHY47rvDTS7XJOaUT
-BwRjmDt896s6vMDcaG/uXJbQjuzmmx3W2Idyh3s5SI0GTHb0IwMKYb4eBUIpQOnB
-cE77VnCYqKvN1NVYAqhWjXbY7XasZvszCRcOG+W3FqNaHOK/n/0ueb0uijdLan+U
-f4p1bjbAox8eAOQS/8a3bzkJzdyBNUKGx1BIK2IBL9bn/HravSDOiNRSnZ/R3l9G
-ZauX0tu7IIDlRCILXSyeazu0aj/vdT3YFQXPcvt5Fkf5wiNTo53f72/jYEJd6qph
-WrpoKqrwGwTpRUCMhYIUt65hsTxCiJJ5nKe39h46sg==
------END CERTIFICATE-----
diff --git a/gnupg/sks-keyservers.net/sks-keyservers.netCA.pem.asc b/gnupg/sks-keyservers.net/sks-keyservers.netCA.pem.asc
deleted file mode 100644
index 5f11bc56..00000000
--- a/gnupg/sks-keyservers.net/sks-keyservers.netCA.pem.asc
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQIcBAABCgAGBQJUCZzxAAoJEPw7F94F4Tag/Z8P/jUOTbsCYT+bG7L+/D9s1KCz
-G2H9X4fV/fBeeAFWjgV6iNBEzZuFx9FYxmECyR1JzRektfWa3JR+rt2pipGO2UQ2
-Il2Ti6K7mVNyEnsgfq5otky7UDewmW+p5u1I7PNVcnHmArE7EueX5WB1vYhY2faY
-B4xsFuaLacQVIz9JFyKiTGu0WSkpnlByaCoMPJgifwwGhPNK8X23isKDY8U9hahh
-xWHRf/57Z+g407d6dEG/1ax8ELf68KRLGalZv9fOcZfRbFT4JV4bq/rsZuNptAqf
-8A5RDnsgXFyIgDptWnYYpra/HCPNOKdL/TxcASTsEH6s9NNw9mvNpE//JWYMV4FM
-N6h9aTezSEwAnD781JrPPZ8BlpRYWtnd3UaSbBbOdb6mze0Oh39yvYEcEO8edvC1
-RLH5OJyJIIkkO46B8cYBtokjRlcAeBgFf5GLlwdh6zGcERqsTRHtA8FmLbl1v3w1
-Xj1tUAoex27ke+z+QKBOp06t6eeNavDeN0/jDidSPQ4Q6QVy8KP9eeMDAQkd9+1F
-aCMvSxEkbuiy05grzQC0jqOXAIVwfu33kcFr2Z7boxVNEjM/ng6Ty4WXWWWbADaH
-nXGamvmUrCiODMRl4DYTB65H27tSv2J9j8WyA+IiokJOglH63nyJJy+XWbRmlgmI
-+ds5RCeuq2/uVOGhSP7t
-=O9Kt
------END PGP SIGNATURE-----
diff --git a/install/install-conf.sh b/install/install-conf.sh
new file mode 100644
index 00000000..f50cde73
--- /dev/null
+++ b/install/install-conf.sh
@@ -0,0 +1,10 @@
+# Read extra targets from an optional ~/.dotfiles.conf file
+if [ -e "$HOME"/.dotfiles.conf ] ; then
+ while read -r line ; do
+ case $line in
+ '#'*|'') ;;
+ *) set -- "$@" "$line" ;;
+ esac
+ done < "$HOME"/.dotfiles.conf
+fi
+make install "$@"
diff --git a/keychain/profile.d/keychain.sh b/keychain/profile.d/keychain.sh
index f3d25c62..c0319d49 100644
--- a/keychain/profile.d/keychain.sh
+++ b/keychain/profile.d/keychain.sh
@@ -2,4 +2,4 @@
command -v keychain >/dev/null 2>&1 || return
eval "$(TERM=${TERM:-ansi} keychain \
--eval --ignore-missing --quick --quiet \
- id_dsa id_rsa id_ecsda)"
+ id_dsa id_rsa id_ecsda id_ed25519)"
diff --git a/ksh/kshrc.d/prompt.ksh b/ksh/kshrc.d/prompt.ksh
index 866cf79e..c5f3ee1b 100644
--- a/ksh/kshrc.d/prompt.ksh
+++ b/ksh/kshrc.d/prompt.ksh
@@ -30,9 +30,7 @@ function prompt {
(*'MIRBSD KSH'*) ksh=mksh ;;
esac
case ${SHELL##*/} in
- ('') ;;
- (ksh) ;;
- ("$ksh") ;;
+ (''|ksh|"$ksh") ;;
(*) PS1=$ksh:$PS1 ;;
esac
diff --git a/man/man1/mi5.1df b/man/man1/mi5.1df
index 6466f35d..53d98bf1 100644
--- a/man/man1/mi5.1df
+++ b/man/man1/mi5.1df
@@ -7,7 +7,7 @@
FILE > out.m4
.br
.B mi5
--v open='{{{' -v shut='}}}' FILE > out.m4
+open='{{{' shut='}}}' FILE > out.m4
.br
.B mi5
FILE1 FILE2 > out.m4
diff --git a/man/man1/oii.1df b/man/man1/oii.1df
new file mode 100644
index 00000000..f5bb2678
--- /dev/null
+++ b/man/man1/oii.1df
@@ -0,0 +1,21 @@
+.TH OII 1df "June 2017" "Manual page for oii"
+.SH NAME
+.B oii
+\- run a command on input only if there's at least one byte of input
+.SH USAGE
+.B oii
+CMD [ARGS ...] < file
+.br
+program |
+.B oii
+CMD [ARGS ...]
+.SH DESCRIPTION
+Run the given program passing in stdin but only if at least one byte of input
+is actually received, rather like the -E switch to mail(1) behaves on
+bsd-mailx. If no input is received, exit silently with an error status.
+.SH CAVEATS
+It's slow, and doesn't work as a pipe. The entire input is written to disk and
+then tested for filesize before being re-emitted. There's almost certainly a
+more efficient way to do this while still remaining byte-safe.
+.SH AUTHOR
+Tom Ryder <tom@sanctum.geek.nz>
diff --git a/man/man1/rndi.1df b/man/man1/rndi.1df
index 767ad148..e9588ab7 100644
--- a/man/man1/rndi.1df
+++ b/man/man1/rndi.1df
@@ -5,20 +5,15 @@
.SH SYNOPSIS
.B rndi
0 10
-.br
-.B rndi
-0 10 "$(rnds)"
.SH DESCRIPTION
.B rndi
returns a random integer ranging from the first argument to the second argument
-in a POSIX-compliant way (using awk), using the optional third argument as a
-seed.
+in a POSIX-compliant way (using awk), using rnds(1df) if available for a seed.
.P
-The answer returned is low-quality; given some implementations of awk and no
-properly random seed, it may even return the same result if run within the same
-second. This should not be used in any sort of security or statistical context.
-The author wrote it to support scripts to choose a random background image from
-a directory.
+The answer returned is low-quality; on some platforms, it may even return the
+same result if run within the same second. This should not be used in any sort
+of security or statistical context. The author wrote it to support scripts to
+choose a random background image from a directory.
.SH SEE ALSO
rnda(1df), rndf(1df), rndl(1df), rnds(1df), rndn(6df)
.SH AUTHOR
diff --git a/man/man1/rndl.1df b/man/man1/rndl.1df
index ec44564a..0e952724 100644
--- a/man/man1/rndl.1df
+++ b/man/man1/rndl.1df
@@ -13,7 +13,7 @@ command |
.B rndl
.SH DESCRIPTION
.B rndl
-prints a random line from its input, using rndi(1df) to choose it. This is
+prints a random line from its input, using rnds(1df) as a seed. This is
probably not a high-quality source, but should differ within seconds and
between runs on most systems.
.SH SEE ALSO
diff --git a/man/man1/trs.1df b/man/man1/trs.1df
index fa5d2d19..93b2cad3 100644
--- a/man/man1/trs.1df
+++ b/man/man1/trs.1df
@@ -19,5 +19,7 @@ implementations. It is thereby the string complement for tr(1).
The first argument cannot be a null string. The second argument can be blank
(but must still be specified) to implicitly delete all occurrences of the
string.
+.SH CAVEATS
+It can't replace newlines.
.SH AUTHOR
Tom Ryder <tom@sanctum.geek.nz>
diff --git a/man/man6/philsay.6df b/man/man6/philsay.6df
new file mode 100644
index 00000000..4de7c476
--- /dev/null
+++ b/man/man6/philsay.6df
@@ -0,0 +1,28 @@
+.TH PHILSAY 6df "July 2017" "Manual page for philsay"
+.SH NAME
+.B philsay
+\- Ha, ha, ha! ASCII art!
+.SH USAGE
+.B philsay
+.br
+.B philsay
+FILE1 FILE2
+.br
+program |
+.B philsay
+-
+.br
+DICT=$HOME/dict
+.B philsay
+.SH DESCRIPTION
+.B philsay
+shows a picture of our founder, Phil Ken Sebben, saying the first word of a
+line randomly chosen from the input using pks(6df).
+.P
+A hyphen character "-" can be given as an argument to select standard input.
+.SH SEE ALSO
+pks(6df), cowsay(1df)
+.br
+<https://www.youtube.com/watch?v=F8ID1KJQxB8>
+.SH AUTHOR
+Tom Ryder <tom@sanctum.geek.nz>
diff --git a/man/man6/pks.6df b/man/man6/pks.6df
new file mode 100644
index 00000000..dc430eff
--- /dev/null
+++ b/man/man6/pks.6df
@@ -0,0 +1,29 @@
+.TH PKS 6df "July 2017" "Manual page for pks"
+.SH NAME
+.B pks
+\- select and laugh at a random word from a system dictionary or fileset
+.SH USAGE
+.B pks
+.br
+.B pks
+FILE1 FILE2
+.br
+program |
+.B pks
+-
+.br
+DICT=$HOME/dict
+.B pks
+.SH DESCRIPTION
+.B pks
+picks the first word from a random line on a set of files and laughs at it. If
+no files are given, it defaults to /usr/share/dict/words, or the value of DICT
+(ha, ha!) if specified in the environment.
+.P
+A hyphen character "-" can be given as an argument to select standard input.
+.SH SEE ALSO
+philsay(6df)
+.br
+<https://www.youtube.com/watch?v=F8ID1KJQxB8>
+.SH AUTHOR
+Tom Ryder <tom@sanctum.geek.nz>
diff --git a/man/man7/dotfiles.7df b/man/man7/dotfiles.7df
new file mode 100644
index 00000000..831af06d
--- /dev/null
+++ b/man/man7/dotfiles.7df
@@ -0,0 +1,947 @@
+.\" Automatically generated by Pandoc 1.17.2
+.\"
+.TH "DOTFILES" "7" "June 2016" "Tom Ryder's personal scripts and configuration" ""
+.hy
+.SH Dotfiles (Tom Ryder)
+.PP
+This is my personal repository of configuration files and scripts for
+\f[C]$HOME\f[], including most of the settings that migrate well between
+machines.
+.PP
+This repository began as a simple way to share Vim and tmux
+configuration, but over time a lot of scripts and shell configuration
+have been added, making it into a personal suite of custom Unix tools.
+.SS Installation
+.IP
+.nf
+\f[C]
+$\ git\ clone\ https://sanctum.geek.nz/code/dotfiles.git\ ~/.dotfiles
+$\ cd\ ~/.dotfiles
+$\ git\ submodule\ init
+$\ git\ submodule\ update
+$\ make
+$\ make\ \-n\ install
+$\ make\ install
+\f[]
+.fi
+.PP
+For the default \f[C]all\f[] target, you'll need a POSIX\-fearing
+userland, including \f[C]make(1)\f[] and \f[C]m4(1)\f[].
+.PP
+The installation \f[C]Makefile\f[] will overwrite things standing in the
+way of its installed files without backing them up, so read the output
+of \f[C]make\ \-n\ install\f[] before running \f[C]make\ install\f[] to
+make sure you aren't going to lose anything unexpected.
+If you're still not sure, install it in a temporary directory so you can
+explore:
+.IP
+.nf
+\f[C]
+$\ tmpdir=$(mktemp\ \-d)
+$\ make\ install\ HOME="$tmpdir"
+$\ env\ \-i\ HOME="$tmpdir"\ TERM="$TERM"\ "$SHELL"\ \-l
+\f[]
+.fi
+.PP
+The default \f[C]install\f[] target will install these targets and all
+their dependencies.
+Note that you don't actually have to have any of this except \f[C]sh\f[]
+installed.
+.IP \[bu] 2
+\f[C]install\-bin\f[]
+.IP \[bu] 2
+\f[C]install\-bin\-man\f[]
+.IP \[bu] 2
+\f[C]install\-curl\f[]
+.IP \[bu] 2
+\f[C]install\-ex\f[]
+.IP \[bu] 2
+\f[C]install\-git\f[]
+.IP \[bu] 2
+\f[C]install\-gnupg\f[]
+.IP \[bu] 2
+\f[C]install\-less\f[]
+.IP \[bu] 2
+\f[C]install\-login\-shell\f[]
+.IP \[bu] 2
+\f[C]install\-readline\f[]
+.IP \[bu] 2
+\f[C]install\-vim\f[]
+.PP
+The \f[C]install\-login\-shell\f[] looks at your \f[C]SHELL\f[]
+environment variable and tries to figure out which shell's configuration
+files to install, falling back on \f[C]install\-sh\f[].
+.PP
+The remaining dotfiles can be installed with the other
+\f[C]install\-*\f[] targets.
+Try \f[C]awk\ \-f\ bin/mftl.awk\ Makefile\f[] in the project's root
+directory to see a list.
+.SS Tools
+.PP
+Configuration is included for:
+.IP \[bu] 2
+Bourne\-style POSIX shells, sharing a \f[C]\&.profile\f[], an
+\f[C]ENV\f[] file, and some helper functions:
+.RS 2
+.IP \[bu] 2
+GNU Bash (https://www.gnu.org/software/bash/) (2.05a or higher)
+.IP \[bu] 2
+Korn shell (http://www.kornshell.com/) (\f[C]ksh93\f[], \f[C]pdksh\f[],
+\f[C]mksh\f[])
+.IP \[bu] 2
+Z shell (https://www.zsh.org/)
+.RE
+.IP \[bu] 2
+Abook (http://abook.sourceforge.net/) \[en] curses address book program
+.IP \[bu] 2
+cURL (https://curl.haxx.se/) \[en] Command\-line tool for transferring
+data with URL syntax
+.IP \[bu] 2
+Dunst (http://knopwob.org/dunst/) \[en] A lightweight X11 notification
+daemon that works with \f[C]libnotify\f[]
+.IP \[bu] 2
+\f[C]finger(1)\f[] \[en] User information lookup program
+.IP \[bu] 2
+Git (https://git-scm.com/) \[en] Distributed version control system
+.IP \[bu] 2
+GnuPG (https://www.gnupg.org/) \[en] GNU Privacy Guard, for private
+communication and file encryption
+.IP \[bu] 2
+GTK+ (https://www.gtk.org/) \[en] GIMP Toolkit, for graphical user
+interface elements
+.IP \[bu] 2
+i3 (https://i3wm.org/) \[en] Tiling window manager
+.IP \[bu] 2
+less (https://www.gnu.org/software/less/) \[en] Terminal pager
+.IP \[bu] 2
+Mutt (http://www.mutt.org/) \[en] Terminal mail user agent
+.IP \[bu] 2
+\f[C]mysql(1)\f[] (https://linux.die.net/man/1/mysql) \[en]
+Command\-line MySQL client
+.IP \[bu] 2
+Ncmpcpp (https://rybczak.net/ncmpcpp/) \[en] ncurses music player client
+.IP \[bu] 2
+Newsbeuter (https://www.newsbeuter.org/) \[en] Terminal RSS/Atom feed
+reader
+.IP \[bu] 2
+\f[C]psql(1)\f[] (https://linux.die.net/man/1/psql) \[en] Command\-line
+PostgreSQL client
+.IP \[bu] 2
+Perl::Critic (http://perlcritic.com/) \[en] static source code analysis
+engine for Perl
+.IP \[bu] 2
+Perl::Tidy (http://perltidy.sourceforge.net/) \[en] Perl indenter and
+reformatter
+.IP \[bu] 2
+Readline (https://cnswww.cns.cwru.edu/php/chet/readline/rltop.html)
+\[en] GNU library for user input used by Bash, MySQL, and others
+.IP \[bu] 2
+rxvt\-unicode (http://software.schmorp.de/pkg/rxvt-unicode.html) \[en]
+Fork of the rxvt terminal emulator with Unicode support
+.IP \[bu] 2
+Subversion (https://subversion.apache.org/) \[en] Apache Subversion, a
+version control system
+.IP \[bu] 2
+tmux (https://tmux.github.io/) \[en] Terminal multiplexer similar to GNU
+Screen
+.IP \[bu] 2
+Vim (http://www.vim.org/) \[en] Vi IMproved, a text editor
+.IP \[bu] 2
+X11 (https://www.x.org/wiki/) \[en] Windowing system with network
+transparency for Unix
+.PP
+The configurations for shells, GnuPG, Mutt, tmux, and Vim are the most
+expansive, and most likely to be of interest.
+The i3 configuration is mostly changed to make window switching behave
+like Vim windows and tmux panes do, and there's a fair few resources
+defined for rxvt\-unicode.
+.SS Shell
+.PP
+My \f[C]\&.profile\f[] and other files in \f[C]sh\f[] are written in
+POSIX shell script, so they should work in most \f[C]sh(1)\f[]
+implementations.
+Individual scripts called by \f[C]\&.profile\f[] are saved in
+\f[C]\&.profile.d\f[] and iterated on login for ease of management.
+Most of these boil down to exporting variables appropriate to the system
+and the software it has available.
+.PP
+Configuration that should be sourced for all POSIX\-fearing interactive
+shells is kept in \f[C]~/.shrc\f[], with subscripts read from
+\f[C]~/.shrc.d\f[].
+There's a shim in \f[C]~/.shinit\f[] to act as \f[C]ENV\f[].
+I make an effort to target POSIX for my functions and scripts where I
+can so that the same files can be loaded for all shells.
+.PP
+On GNU/Linux I use Bash, on BSD I use some variant of Korn Shell,
+preferably \f[C]ksh93\f[] if it's available.
+.PP
+As I occasionally have work on very old internal systems, my Bash is
+written to work with any version 2.05a or
+newer (http://wiki.bash-hackers.org/scripting/bashchanges).
+This is why I use older syntax for certain things such as appending
+items to arrays:
+.IP
+.nf
+\f[C]
+array[${#array[\@]}]=$item
+\f[]
+.fi
+.PP
+Compare this to the much nicer syntax available since 3.1\-alpha1, which
+actually works for arrays with sparse indices, unlike the above syntax:
+.IP
+.nf
+\f[C]
+array+=("$item")
+\f[]
+.fi
+.PP
+Where I do use features that are only available in versions of Bash
+newer than 2.05a, such as newer \f[C]shopt\f[] options or
+\f[C]PROMPT_DIRTRIM\f[], they are only run after testing
+\f[C]BASH_VERSINFO\f[] appropriately.
+.SS Prompt
+.PP
+A terminal session with my prompt looks something like this:
+.IP
+.nf
+\f[C]
+~$\ ssh\ remote
+remote:~$\ cd\ .dotfiles
+remote:~/.dotfiles(master+!)$\ git\ status
+\ M\ README.markdown
+M\ \ bash/bashrc.d/prompt.bash
+A\ \ init
+remote:~/.dotfiles(master+!)$\ foobar
+foobar:\ command\ not\ found
+remote:~/.dotfiles(master+!)<127>$\ sleep\ 5\ &
+[1]\ 28937
+remote:~/.dotfiles(master+!){1}$
+\f[]
+.fi
+.PP
+The hostname is elided if not connected via SSH.
+The working directory with tilde abbreviation for \f[C]$HOME\f[] is
+always shown.
+The rest of the prompt expands based on context to include these
+elements in this order:
+.IP \[bu] 2
+Whether in a Git repository if applicable, and punctuation to show
+repository status including reference to upstreams at a glance.
+Subversion support can also be enabled (I need it at work), in which
+case a \f[C]git:\f[] or \f[C]svn:\f[] prefix is added appropriately.
+.IP \[bu] 2
+The number of running background jobs, if non\-zero.
+.IP \[bu] 2
+The exit status of the last command, if non\-zero.
+.PP
+You can set \f[C]PROMPT_COLOR\f[], \f[C]PROMPT_PREFIX\f[], and
+\f[C]PROMPT_SUFFIX\f[] too, which all do about what you'd expect.
+.PP
+If you start up Bash, Ksh, or Zsh and it detects that it's not normally
+your \f[C]$SHELL\f[], the prompt will display an appropriate prefix.
+.PP
+This is all managed within the \f[C]prompt\f[] function.
+There's some mildly hacky logic on \f[C]tput\f[] codes included such
+that it should work correctly for most common terminals using both
+\f[C]termcap(5)\f[] and \f[C]terminfo(5)\f[], including *BSD systems.
+It's also designed to degrade gracefully for eight\-color and no\-color
+terminals.
+.SS Functions
+.PP
+If a function can be written in POSIX \f[C]sh\f[] without too much
+hackery, I put it in \f[C]sh/shrc.d\f[] to be loaded by any POSIX
+interactive shell.
+Those include:
+.IP \[bu] 2
+Four functions for using a \[lq]marked\[rq] directory, which I find a
+more manageable concept than the \f[C]pushd\f[]/\f[C]popd\f[] directory
+stack:
+.RS 2
+.IP \[bu] 2
+\f[C]md()\f[] marks a given (or the current) directory.
+.IP \[bu] 2
+\f[C]gd()\f[] goes to the marked directory.
+.IP \[bu] 2
+\f[C]pmd()\f[] prints the marked directory.
+.IP \[bu] 2
+\f[C]xd()\f[] swaps the current and marked directories.
+.RE
+.IP \[bu] 2
+Ten other directory management and navigation functions:
+.RS 2
+.IP \[bu] 2
+\f[C]bd()\f[] changes into a named ancestor of the current directory.
+.IP \[bu] 2
+\f[C]gt()\f[] changes into a directory or into a file's directory.
+.IP \[bu] 2
+\f[C]lgt()\f[] runs \f[C]gt()\f[] on the first result from a
+\f[C]loc(1df)\f[] search.
+.IP \[bu] 2
+\f[C]mkcd()\f[] creates a directory and changes into it.
+.IP \[bu] 2
+\f[C]pd()\f[] changes to the argument's parent directory.
+.IP \[bu] 2
+\f[C]rd()\f[] replaces the first instance of its first argument with its
+second argument in \f[C]$PWD\f[], emulating a feature of the Zsh
+\f[C]cd\f[] builtin that I like.
+.IP \[bu] 2
+\f[C]scr()\f[] creates a temporary directory and changes into it.
+.IP \[bu] 2
+\f[C]sd()\f[] changes into a sibling of the current directory.
+.IP \[bu] 2
+\f[C]ud()\f[] changes into an indexed ancestor of a directory.
+.IP \[bu] 2
+\f[C]vr()\f[] tries to change to the root directory of a source control
+repository.
+.RE
+.IP \[bu] 2
+\f[C]bc()\f[] silences startup messages from GNU \f[C]bc(1)\f[].
+.IP \[bu] 2
+\f[C]ed()\f[] tries to get verbose error messages, a prompt, and a
+Readline environment for \f[C]ed(1)\f[].
+.IP \[bu] 2
+\f[C]env()\f[] sorts the output of \f[C]env(1)\f[] if it was invoked
+with no arguments, just for convenience when running it interactively.
+.IP \[bu] 2
+\f[C]gdb()\f[] silences startup messages from \f[C]gdb(1)\f[].
+.IP \[bu] 2
+\f[C]gpg()\f[] quietens \f[C]gpg(1)\f[] down for most commands.
+.IP \[bu] 2
+\f[C]grep()\f[] tries to apply color and other options good for
+interactive use if available.
+.IP \[bu] 2
+\f[C]hgrep()\f[] allows searching \f[C]$HISTFILE\f[].
+.IP \[bu] 2
+\f[C]keychain()\f[] keeps \f[C]$GPG_TTY\f[] up to date if a GnuPG agent
+is available.
+.IP \[bu] 2
+\f[C]ls()\f[] tries to apply color and other options good for
+interactive use if available.
+.RS 2
+.IP \[bu] 2
+\f[C]la()\f[] runs \f[C]ls\ \-A\f[] if it can, or \f[C]ls\ \-a\f[]
+otherwise.
+.IP \[bu] 2
+\f[C]ll()\f[] runs \f[C]ls\ \-Al\f[] if it can, or \f[C]ls\ \-al\f[]
+otherwise.
+.RE
+.IP \[bu] 2
+\f[C]path()\f[] manages the contents of \f[C]PATH\f[] conveniently.
+.IP \[bu] 2
+\f[C]scp()\f[] tries to detect forgotten hostnames in \f[C]scp(1)\f[]
+command calls.
+.IP \[bu] 2
+\f[C]sudo()\f[] forces \f[C]\-H\f[] for \f[C]sudo(8)\f[] calls so that
+\f[C]$HOME\f[] is never preserved; I hate having \f[C]root\f[]\-owned
+files in my home directory.
+.IP \[bu] 2
+\f[C]tree()\f[] colorizes GNU \f[C]tree(1)\f[] output if possible
+(without having \f[C]LS_COLORS\f[] set).
+.IP \[bu] 2
+\f[C]x()\f[] is a one\-key shortcut for \f[C]exec\ startx\f[].
+.PP
+There are a few other little tricks defined for other shells providing
+non\-POSIX features, as compatibility allows:
+.IP \[bu] 2
+\f[C]keep()\f[] stores ad\-hoc shell functions and variables (Bash, Korn
+Shell 93, Z shell).
+.IP \[bu] 2
+\f[C]prompt()\f[] sets up my interactive prompt (Bash, Korn Shell, Z
+shell).
+.IP \[bu] 2
+\f[C]pushd()\f[] adds a default destination of \f[C]$HOME\f[] to the
+\f[C]pushd\f[] builtin (Bash).
+.IP \[bu] 2
+\f[C]vared()\f[] allows interactively editing a variable with Readline,
+emulating a Zsh function I like by the same name (Bash).
+.IP \[bu] 2
+\f[C]ver()\f[] prints the current shell's version information (Bash,
+Korn Shell, Z shell).
+.SS Completion
+.PP
+I find the \f[C]bash\-completion\f[] package a bit too heavy for my
+tastes, and turn it off using a stub file installed in
+\f[C]~/.config/bash_completion\f[].
+The majority of the time I just want to complete paths anyway, and this
+makes for a quicker startup without a lot of junk functions in my Bash
+namespace.
+.PP
+I do make some exceptions with completions defined in
+\f[C]\&.bash_completion.d\f[] files, for things I really do get tired of
+typing repeatedly:
+.IP \[bu] 2
+Bash builtins: commands, help topics, shell options, variables, etc.
+.IP \[bu] 2
+\f[C]find(1)\f[]'s more portable options
+.IP \[bu] 2
+\f[C]ftp(1)\f[] hostnames from \f[C]~/.netrc\f[]
+.IP \[bu] 2
+\f[C]git(1)\f[] subcommands, remotes, branches, tags, and addable files
+.IP \[bu] 2
+\f[C]gpg(1)\f[] long options
+.IP \[bu] 2
+\f[C]make(1)\f[] targets read from a \f[C]Makefile\f[]
+.IP \[bu] 2
+\f[C]man(1)\f[] page titles
+.IP \[bu] 2
+\f[C]pass(1)\f[] entries
+.IP \[bu] 2
+\f[C]ssh(1)\f[] hostnames from \f[C]~/.ssh/config\f[]
+.PP
+For commands that pretty much always want to operate on text, such as
+text file or stream editors, I exclude special file types and extensions
+I know are binary.
+I don't actually read the file, so this is more of a heuristic thing,
+and sometimes it will get things wrong.
+.PP
+I also add completions for my own scripts and functions where useful.
+The completions are dynamically loaded if Bash is version 4.0 or
+greater.
+Otherwise, they're all loaded on startup.
+.SS Korn shell
+.PP
+These are experimental; they are mostly used to tinker with MirBSD
+\f[C]mksh\f[], AT&T \f[C]ksh93\f[], and OpenBSD \f[C]pdksh\f[].
+All shells in this family default to a yellow prompt if detected.
+.SS Zsh
+.PP
+These are experimental; I do not like Zsh much at the moment.
+The files started as a joke (\f[C]exec\ bash\f[]).
+\f[C]zsh\f[] shells default to having a prompt coloured cyan.
+.SS GnuPG
+.PP
+The configuration for GnuPG is intended to follow RiseUp's OpenPGP best
+practices (https://riseup.net/en/security/message-security/openpgp/best-practices).
+The configuration file is rebuilt using \f[C]mi5(1df)\f[] and
+\f[C]make(1)\f[] because it requires hard\-coding a path to the SKS
+keyserver certificate authority, and neither tilde nor \f[C]$HOME\f[]
+expansion works for this.
+.SS Mutt
+.PP
+My mail is kept in individual Maildirs under \f[C]~/Mail\f[], with
+\f[C]inbox\f[] being where most unfiltered mail is sent.
+I use Getmail (http://pyropus.ca/software/getmail/),
+maildrop (https://www.courier-mta.org/maildrop/), and
+MSMTP (http://msmtp.sourceforge.net/); the configurations for these are
+not included here.
+I sign whenever I have some indication that the recipient might be using
+a PGP implementation, and I encrypt whenever I have a public key
+available for them.
+The GnuPG and S/MIME interfacing is done with
+GPGme (https://www.gnupg.org/related_software/gpgme/), rather than
+defining commands for each crypto operation.
+I wrote an article about this
+setup (https://sanctum.geek.nz/arabesque/linux-crypto-email/) if it
+sounds appealing.
+.PP
+You'll need Abook (http://abook.sourceforge.net/) installed if you want
+to use the \f[C]query_command\f[] I have defined, and
+msmtp (http://msmtp.sourceforge.net/) for the \f[C]sendmail\f[] command.
+.SS rxvt\-unicode
+.PP
+I've butchered the URxvt Perl extensions
+\f[C]selection\-to\-clipboard\f[] and \f[C]selection\f[] into a single
+\f[C]select\f[] extension in \f[C]~/.urxvt/ext\f[], which is the only
+extension I define in \f[C]~/.Xresources\f[].
+.PP
+The included \f[C]\&.Xresources\f[] file assumes that \f[C]urxvt\f[] can
+use 256 colors and Perl extensions.
+If you're missing functionality, try changing \f[C]perl\-ext\-common\f[]
+to \f[C]default\f[].
+.PP
+My choice of font is Ubuntu Mono (http://font.ubuntu.com/), but the file
+should allow falling back to the more common Deja Vu Sans
+Mono (https://dejavu-fonts.github.io/).
+I've found Terminus (http://terminus-font.sourceforge.net/) works well
+too, but bitmap fonts are not really my cup of tea.
+The Lohit Kannada font bit is purely to make ಠ_ಠ work correctly.
+( ͡° ͜ʖ ͡°) seems to work out of the box.
+.SS tmux
+.PP
+These are just generally vi\-friendly settings, not much out of the
+ordinary.
+Note that the configuration presently uses a hard\-coded 256\-color
+colorscheme, and uses non\-login shells, with an attempt to control the
+environment to stop shells thinking they have access to an X display.
+.PP
+The shell scripts in \f[C]bin\f[] include \f[C]tm(1df)\f[], a shortcut
+to make \f[C]attach\f[] into the default command if no arguments are
+given and sessions do already exist.
+My \f[C]~/.inputrc\f[] file binds Alt+M to run that, and Tmux in turn
+binds the same key combination to detach.
+.SS Vim
+.PP
+The majority of the \f[C]\&.vimrc\f[] file is just setting options, with
+a few mappings.
+I try not to deviate too much from the Vim defaults behaviour in terms
+of interactive behavior and keybindings.
+.PP
+The configuration is extensively commented, mostly because I was reading
+through it one day and realised I'd forgotten what half of it did.
+Plugins are loaded using \@tpope's
+pathogen.vim (https://github.com/tpope/vim-pathogen).
+.SS Scripts
+.PP
+Where practical, I make short scripts into POSIX (but not Bourne)
+\f[C]sh(1)\f[], \f[C]awk(1)\f[], or \f[C]sed(1)\f[] scripts in
+\f[C]~/.local/bin\f[].
+I try to use shell functions only when I actually need to, which tends
+to be when I need to tinker with the namespace of the user's current
+shell.
+.PP
+Installed by the \f[C]install\-bin\f[] target:
+.IP \[bu] 2
+Three SSH\-related scripts:
+.RS 2
+.IP \[bu] 2
+\f[C]sls(1df)\f[] prints hostnames read from a \f[C]ssh_config(5)\f[]
+file.
+It uses \f[C]slsf(1df)\f[] to read each one.
+.IP \[bu] 2
+\f[C]sra(1df)\f[] runs a command on multiple hosts read from
+\f[C]sls(1df)\f[] and prints output.
+.IP \[bu] 2
+\f[C]sta(1df)\f[] runs a command on multiple hosts read from
+\f[C]sls(1df)\f[] and prints the hostname if the command returns zero.
+.RE
+.IP \[bu] 2
+Five URL\-related shortcut scripts:
+.RS 2
+.IP \[bu] 2
+\f[C]hurl(1df)\f[] extracts values of \f[C]href\f[] attributes of
+\f[C]<a>\f[] tags, sorts them uniquely, and writes them to
+\f[C]stdout\f[]; it requires pup (https://github.com/ericchiang/pup).
+.IP \[bu] 2
+\f[C]murl(1df)\f[] converts Markdown documents to HTML with
+\f[C]pandoc(1)\f[] and runs the output through \f[C]hurl(1df)\f[].
+.IP \[bu] 2
+\f[C]urlc(1df)\f[] accepts a list of URLs on \f[C]stdin\f[] and writes
+error messages to \f[C]stderr\f[] if any of the URLs are broken,
+redirecting, or are insecure and have working secure versions; requires
+\f[C]curl(1)\f[].
+.IP \[bu] 2
+\f[C]urlh(1df)\f[] prints the values for a given HTTP header from a HEAD
+response.
+.IP \[bu] 2
+\f[C]urlmt(1df)\f[] prints the MIME type from the \f[C]Content\-Type\f[]
+header as retrieved by \f[C]urlh(1df)\f[].
+.RE
+.IP \[bu] 2
+Three RFC\-related shortcut scripts:
+.RS 2
+.IP \[bu] 2
+\f[C]rfcf(1df)\f[] fetches ASCII RFCs from the IETF website.
+.IP \[bu] 2
+\f[C]rfct(1df)\f[] formats ASCII RFCs.
+.IP \[bu] 2
+\f[C]rfcr(1df)\f[] does both, displaying in a pager if appropriate, like
+a \f[C]man(1)\f[] reader for RFCs.
+.RE
+.IP \[bu] 2
+Five toy random\-number scripts (not for sensitive/dead\-serious use):
+.RS 2
+.IP \[bu] 2
+\f[C]rndi(1df)\f[] gets a random integer within two bounds.
+.IP \[bu] 2
+\f[C]rnds(1df)\f[] attempts to get an optional random seed for
+\f[C]rndi(1df)\f[].
+.IP \[bu] 2
+\f[C]rnda(1df)\f[] uses \f[C]rndi(1df)\f[] to choose a random argument.
+.IP \[bu] 2
+\f[C]rndf(1df)\f[] uses \f[C]rnda(1df)\f[] to choose a random file from
+a directory.
+.IP \[bu] 2
+\f[C]rndl(1df)\f[] uses \f[C]rndi(1df)\f[] to choose a random line from
+files.
+.RE
+.IP \[bu] 2
+Four file formatting scripts:
+.RS 2
+.IP \[bu] 2
+\f[C]d2u(1df)\f[] converts DOS line endings in files to UNIX ones.
+.IP \[bu] 2
+\f[C]u2d(1df)\f[] converts UNIX line endings in files to DOS ones.
+.IP \[bu] 2
+\f[C]stbl(1df)\f[] strips a trailing blank line from the files in its
+arguments.
+.IP \[bu] 2
+\f[C]stws(1df)\f[] strips trailing spaces from the ends of lines of the
+files in its arguments.
+.RE
+.IP \[bu] 2
+Seven stream formatting scripts:
+.RS 2
+.IP \[bu] 2
+\f[C]sd2u(1df)\f[] converts DOS line endings in streams to UNIX ones.
+.IP \[bu] 2
+\f[C]su2d(1df)\f[] converts UNIX line endings in streams to DOS ones.
+.IP \[bu] 2
+\f[C]slow(1df)\f[] converts uppercase to lowercase.
+.IP \[bu] 2
+\f[C]supp(1df)\f[] converts lowercase to uppercase.
+.IP \[bu] 2
+\f[C]tl(1df)\f[] tags input lines with a prefix or suffix, basically a
+\f[C]sed(1)\f[] shortcut.
+.IP \[bu] 2
+\f[C]tlcs(1df)\f[] executes a command and uses \f[C]tl(1df)\f[] to tag
+stdout and stderr lines, and color them if you want.
+.IP \[bu] 2
+\f[C]unf(1df)\f[] joins lines with leading spaces to the previous line.
+Intended for unfolding HTTP headers, but it should work for most RFC 822
+formats.
+.RE
+.IP \[bu] 2
+Six simple aggregators for numbers:
+.RS 2
+.IP \[bu] 2
+\f[C]max(1df)\f[] prints the maximum.
+.IP \[bu] 2
+\f[C]mean(1df)\f[] prints the mean.
+.IP \[bu] 2
+\f[C]med(1df)\f[] prints the median.
+.IP \[bu] 2
+\f[C]min(1df)\f[] prints the minimum.
+.IP \[bu] 2
+\f[C]mode(1df)\f[] prints the first encountered mode.
+.IP \[bu] 2
+\f[C]tot(1df)\f[] totals the set.
+.RE
+.IP \[bu] 2
+Three quick\-and\-dirty HTML tools:
+.RS 2
+.IP \[bu] 2
+\f[C]htenc(1df)\f[] encodes.
+.IP \[bu] 2
+\f[C]htdec(1df)\f[] decodes.
+.IP \[bu] 2
+\f[C]htrec(1df)\f[] wraps \f[C]a\f[] tags around URLs.
+.RE
+.IP \[bu] 2
+Two internet message quoting tools:
+.RS 2
+.IP \[bu] 2
+\f[C]quo(1df)\f[] indents with quoting right angle\-brackets.
+.IP \[bu] 2
+\f[C]wro(1df)\f[] adds a quote attribution header to its input.
+.RE
+.IP \[bu] 2
+Six Git\-related tools:
+.RS 2
+.IP \[bu] 2
+\f[C]fgscr(1df)\f[] finds Git repositories in a directory root and
+scrubs them with \f[C]gscr(1df)\f[].
+.IP \[bu] 2
+\f[C]grc(1df)\f[] quietly tests whether the given directory appears to
+be a Git repository with pending changes.
+.IP \[bu] 2
+\f[C]gscr(1df)\f[] scrubs Git repositories.
+.IP \[bu] 2
+\f[C]isgr(1df)\f[] quietly tests whether the given directory appears to
+be a Git repository.
+.IP \[bu] 2
+\f[C]jfc(1df)\f[] adds and commits lazily to a Git repository.
+.IP \[bu] 2
+\f[C]jfcd(1df)\f[] watches a directory for changes and runs
+\f[C]jfc(1df)\f[] if it sees any.
+.RE
+.IP \[bu] 2
+Two time duration functions:
+.RS 2
+.IP \[bu] 2
+\f[C]hms(1df)\f[] converts seconds to \f[C]hh:mm:ss\f[] or
+\f[C]mm:ss\f[] timestamps.
+.IP \[bu] 2
+\f[C]sec(1df)\f[] converts \f[C]hh:mm:ss\f[] or \f[C]mm:ss\f[]
+timestamps to seconds.
+.RE
+.IP \[bu] 2
+Three pipe interaction tools:
+.RS 2
+.IP \[bu] 2
+\f[C]pst(1df)\f[] runs an interactive program on data before passing it
+along a pipeline.
+.IP \[bu] 2
+\f[C]ped(1df)\f[] runs \f[C]pst(1df)\f[] with \f[C]$EDITOR\f[] or
+\f[C]ed(1)\f[].
+.IP \[bu] 2
+\f[C]pvi(1df)\f[] runs \f[C]pvi(1df)\f[] with \f[C]$VISUAL\f[] or
+\f[C]vi(1)\f[].
+.RE
+.IP \[bu] 2
+\f[C]ap(1df)\f[] reads arguments for a given command from the standard
+input, prompting if appropriate.
+.IP \[bu] 2
+\f[C]apf(1df)\f[] prepends arguments to a command with ones read from a
+file, intended as a framework for shell wrappers or functions.
+.IP \[bu] 2
+\f[C]ax(1df)\f[] evaluates an awk expression given on the command line;
+this is intended as a quick way to test how Awk would interpret a given
+expression.
+.IP \[bu] 2
+\f[C]bcq(1df)\f[] runs \f[C]bc(1)\f[], quieting it down if need be.
+.IP \[bu] 2
+\f[C]bel(1df)\f[] prints a terminal bell character.
+.IP \[bu] 2
+\f[C]bl(1df)\f[] generates a given number of blank lines.
+.IP \[bu] 2
+\f[C]bp(1df)\f[] runs \f[C]br(1df)\f[] after prompting for an URL.
+.IP \[bu] 2
+\f[C]br(1df)\f[] launches \f[C]$BROWSER\f[].
+.IP \[bu] 2
+\f[C]ca(1df)\f[] prints a count of its given arguments.
+.IP \[bu] 2
+\f[C]cf(1df)\f[] prints a count of entries in a given directory.
+.IP \[bu] 2
+\f[C]cfr(1df)\f[] does the same as \f[C]cf(1df)\f[], but recurses into
+subdirectories as well.
+.IP \[bu] 2
+\f[C]chc(1df)\f[] caches the output of a command.
+.IP \[bu] 2
+\f[C]chn(1df)\f[] runs a filter over its input a given number of times.
+.IP \[bu] 2
+\f[C]clog(1df)\f[] is a tiny timestamped log system.
+.IP \[bu] 2
+\f[C]clrd(1df)\f[] sets up a per\-line file read, clearing the screen
+first.
+.IP \[bu] 2
+\f[C]clwr(1df)\f[] sets up a per\-line file write, clearing the screen
+before each line.
+.IP \[bu] 2
+\f[C]csmw(1df)\f[] prints an English list of monospace\-quoted words
+read from the input.
+.IP \[bu] 2
+\f[C]dam(1df)\f[] buffers all its input before emitting it as output.
+.IP \[bu] 2
+\f[C]ddup(1df)\f[] removes duplicate lines from unsorted input.
+.IP \[bu] 2
+\f[C]dmp(1df)\f[] copies a pass(1) entry selected by \f[C]dmenu(1)\f[]
+to the X CLIPBOARD.
+.IP \[bu] 2
+\f[C]dub(1df)\f[] lists the biggest entries in a directory.
+.IP \[bu] 2
+\f[C]edda(1df)\f[] provides a means to run \f[C]ed(1)\f[] over a set of
+files preserving any options, mostly useful for scripts.
+.IP \[bu] 2
+\f[C]eds(1df)\f[] edits executable script files in \f[C]EDSPATH\f[],
+defaulting to \f[C]~/.local/bin\f[], for personal scripting snippets.
+.IP \[bu] 2
+\f[C]exm(1df)\f[] works around a screen\-clearing quirk of Vim's
+\f[C]ex\f[] mode.
+.IP \[bu] 2
+\f[C]finc(1df)\f[] counts the number of results returned from a set of
+given \f[C]find(1)\f[] conditions.
+.IP \[bu] 2
+\f[C]fnl(1df)\f[] runs a command and saves its output and error into
+temporary files, printing their paths and line counts.
+.IP \[bu] 2
+\f[C]fnp(1df)\f[] prints the given files to stdout, each with a
+plaintext heading with the filename in it.
+.IP \[bu] 2
+\f[C]gms(1df)\f[] runs a set of \f[C]getmailrc\f[] files; does much the
+same thing as the script \f[C]getmails\f[] in the \f[C]getmail\f[]
+suite, but runs the requests in parallel and does up to three silent
+retries using \f[C]try(1df)\f[].
+.IP \[bu] 2
+\f[C]grec(1df)\f[] is a more logically\-named \f[C]grep\ \-c\f[].
+.IP \[bu] 2
+\f[C]gred(1df)\f[] is a more logically\-named \f[C]grep\ \-v\f[].
+.IP \[bu] 2
+\f[C]gwp(1df)\f[] searches for alphanumeric words in a similar way to
+\f[C]grep(1)\f[].
+.IP \[bu] 2
+\f[C]han(1df)\f[] provides a \f[C]keywordprg\f[] for Vim's Bash script
+filetype that will look for \f[C]help\f[] topics.
+You could use it from the shell too.
+.IP \[bu] 2
+\f[C]igex(1df)\f[] wraps around a command to allow you to ignore error
+conditions that don't actually worry you, exiting with 0 anyway.
+.IP \[bu] 2
+\f[C]ix(1df)\f[] posts its input to the ix.io pastebin.
+.IP \[bu] 2
+\f[C]jfp(1df)\f[] prints its input, excluding any shebang on the first
+line only.
+.IP \[bu] 2
+\f[C]loc(1df)\f[] is a quick\-search wrapped around \f[C]find(1)\f[].
+.IP \[bu] 2
+\f[C]maybe(1df)\f[] is like \f[C]true(1)\f[] or \f[C]false(1)\f[]; given
+a probability of success, it exits with success or failure.
+Good for quick tests.
+.IP \[bu] 2
+\f[C]mex(1df)\f[] makes given filenames in \f[C]$PATH\f[] executable.
+.IP \[bu] 2
+\f[C]mi5(1df)\f[] pre\-processes a crude but less painful macro
+expansion file format into \f[C]m4\f[] input.
+.IP \[bu] 2
+\f[C]mftl(1df)\f[] finds usable\-looking targets in Makefiles.
+.IP \[bu] 2
+\f[C]mkcp(1df)\f[] creates a directory and copies preceding arguments
+into it.
+.IP \[bu] 2
+\f[C]mkmv(1df)\f[] creates a directory and moves preceding arguments
+into it.
+.IP \[bu] 2
+\f[C]motd(1df)\f[] shows the system MOTD.
+.IP \[bu] 2
+\f[C]mw(1df)\f[] prints alphabetic space\-delimited words from the input
+one per line.
+.IP \[bu] 2
+\f[C]oii(1df)\f[] runs a command on input only if there is any.
+.IP \[bu] 2
+\f[C]onl(1df)\f[] crunches input down to one printable line.
+.IP \[bu] 2
+\f[C]osc(1df)\f[] implements a \f[C]netcat(1)\f[]\-like wrapper for
+\f[C]openssl(1)\f[]'s \f[C]s_client\f[] subcommand.
+.IP \[bu] 2
+\f[C]p(1df)\f[] prints concatenated standard input; \f[C]cat(1)\f[] as
+it should always have been.
+.IP \[bu] 2
+\f[C]pa(1df)\f[] prints its arguments, one per line.
+.IP \[bu] 2
+\f[C]pp(1df)\f[] prints the full path of each argument using
+\f[C]$PWD\f[].
+.IP \[bu] 2
+\f[C]pph(1df)\f[] runs \f[C]pp(1df)\f[] and includes a leading
+\f[C]$HOSTNAME:\f[].
+.IP \[bu] 2
+\f[C]paz(1df)\f[] print its arguments terminated by NULL chars.
+.IP \[bu] 2
+\f[C]pit(1df)\f[] runs its input through a pager if its standard output
+looks like a terminal.
+.IP \[bu] 2
+\f[C]plmu(1df)\f[] retrieves a list of installed modules from
+\f[C]plenv\f[] (https://github.com/tokuhirom/plenv), filters out any
+modules in \f[C]~/.plenv/non\-cpan\-modules\f[], and updates them all.
+.IP \[bu] 2
+\f[C]pwg(1df)\f[] generates just one decent password with
+\f[C]pwgen(1)\f[].
+.IP \[bu] 2
+\f[C]rep(1df)\f[] repeats a command a given number of times.
+.IP \[bu] 2
+\f[C]rgl(1df)\f[] is a very crude interactive \f[C]grep(1)\f[] loop.
+.IP \[bu] 2
+\f[C]shb(1df)\f[] attempts to build shebang lines for scripts from the
+system paths.
+.IP \[bu] 2
+\f[C]sqs(1df)\f[] chops off query strings from filenames, usually
+downloads.
+.IP \[bu] 2
+\f[C]sshi(1df)\f[] prints human\-readable SSH connection details.
+.IP \[bu] 2
+\f[C]stex(1df)\f[] strips extensions from filenames.
+.IP \[bu] 2
+\f[C]sue(8df)\f[] execs \f[C]sudoedit(8)\f[] as the owner of all the
+file arguments given, perhaps in cases where you may not necessarily
+have \f[C]root\f[] \f[C]sudo(8)\f[] privileges.
+.IP \[bu] 2
+\f[C]swr(1df)\f[] allows you to run commands locally specifying remote
+files in \f[C]scp(1)\f[]'s HOST:PATH format.
+.IP \[bu] 2
+\f[C]td(1df)\f[] manages a to\-do file for you with \f[C]$EDITOR\f[] and
+\f[C]git(1)\f[]; I used to use Taskwarrior, but found it too complex and
+buggy.
+.IP \[bu] 2
+\f[C]tm(1df)\f[] runs \f[C]tmux(1)\f[] with
+\f[C]attach\-session\ \-d\f[] if a session exists, and
+\f[C]new\-session\f[] if it doesn't.
+.IP \[bu] 2
+\f[C]trs(1df)\f[] replaces strings (not regular expression) in its
+input.
+.IP \[bu] 2
+\f[C]try(1df)\f[] repeats a command up to a given number of times until
+it succeeds, only printing error output if all three attempts failed.
+Good for tolerating blips or temporary failures in \f[C]cron(8)\f[]
+scripts.
+.IP \[bu] 2
+\f[C]umake(1df)\f[] iterates upwards through the directory tree from
+\f[C]$PWD\f[] until it finds a Makefile for which to run
+\f[C]make(1)\f[] with the given arguments.
+.IP \[bu] 2
+\f[C]uts(1df)\f[] gets the current UNIX timestamp in an unorthodox way
+that should work on all POSIX\-compliant operating systems.
+.IP \[bu] 2
+\f[C]vest(1df)\f[] runs \f[C]test(1)\f[] but fails with explicit output
+via \f[C]vex(1df)\f[].
+.IP \[bu] 2
+\f[C]vex(1df)\f[] runs a command and prints \f[C]true\f[] or
+\f[C]false\f[] explicitly to \f[C]stdout\f[] based on the exit value.
+.IP \[bu] 2
+\f[C]xrbg(1df)\f[] applies the same randomly\-selected background to
+each X screen.
+.IP \[bu] 2
+\f[C]xrq(1df)\f[] gets the values of specific resources out of
+\f[C]xrdb\ \-query\f[] output.
+.PP
+There's some silly stuff in \f[C]install\-games\f[]:
+.IP \[bu] 2
+\f[C]aaf(6df)\f[] gets a random ASCII Art
+Farts (http://www.asciiartfarts.com/) comic.
+.IP \[bu] 2
+\f[C]acq(6df)\f[] allows you to interrogate AC, the interplanetary
+computer.
+.IP \[bu] 2
+\f[C]aesth(6df)\f[] converts English letters to their fullwidth CJK
+analogues, for AESTHETIC PURPOSES.
+.IP \[bu] 2
+\f[C]squ(6df)\f[] makes a reduced Latin square out of each line of
+input.
+.IP \[bu] 2
+\f[C]kvlt(6df)\f[] translates input to emulate a style of typing unique
+to black metal communities on the internet.
+.IP \[bu] 2
+\f[C]rndn(6df)\f[] implements an esoteric random number generation
+algorithm.
+.IP \[bu] 2
+\f[C]strik(6df)\f[] outputs s̶t̶r̶i̶k̶e̶d̶ ̶o̶u̶t̶ struck out text.
+.IP \[bu] 2
+\f[C]rot13(6df)\f[] rotates the Latin letters in its input.
+.IP \[bu] 2
+\f[C]xyzzy(6df)\f[] teleports to a marked location on the filesystem.
+.IP \[bu] 2
+\f[C]zs(6df)\f[] prepends \[lq]z\[rq] case\-appropriately to every
+occurrence of \[lq]s\[rq] in the text on its standard input.
+.SS Manuals
+.PP
+The \f[C]install\-bin\f[] and \f[C]install\-games\f[] targets install
+manuals for each script they install.
+There's also an \f[C]install\-dotfiles\-man\f[] target that uses
+\f[C]pandoc(1)\f[] to reformat this document as a manual page for
+section 7 (\f[C]dotfiles(7df)\f[]) if you want that.
+I haven't made that install by default, because \f[C]pandoc(1)\f[] is a
+bit heavy.
+.PP
+If you want to use the manuals, you may need to add
+\f[C]~/.local/share/man\f[] to your \f[C]~/.manpath\f[] or
+\f[C]/etc/manpath\f[] configuration, depending on your system.
+.SS Testing
+.PP
+You can check that both sets of shell scripts are syntactically correct
+with \f[C]make\ check\-bash\f[], \f[C]make\ check\-sh\f[], or
+\f[C]make\ check\f[] for everything including the scripts in
+\f[C]bin\f[] and \f[C]games\f[].
+There's no proper test suite for the actual functionality (yet).
+.PP
+If you have ShellCheck (https://www.shellcheck.net/) and/or
+Perl::Critic (http://perlcritic.com/), there's a \f[C]lint\f[] target
+for the shell script files and Perl files respectively.
+The files don't need to pass that check to be installed.
+.SS Known issues
+.PP
+See ISSUES.markdown.
+.SS License
+.PP
+Public domain; see the included \f[C]UNLICENSE\f[] file.
+It's just configuration and simple scripts, so do whatever you like with
+it if any of it's useful to you.
+If you're feeling generous, please join and/or donate to a free software
+advocacy group, and let me know you did it because of this project:
+.IP \[bu] 2
+Free Software Foundation (https://www.fsf.org/)
+.IP \[bu] 2
+Software in the Public Interest (https://www.spi-inc.org/)
+.IP \[bu] 2
+FreeBSD Foundation (https://www.freebsdfoundation.org/)
+.IP \[bu] 2
+OpenBSD Foundation (http://www.openbsdfoundation.org/)
+.SH AUTHORS
+Tom Ryder.
diff --git a/man/man7/dotfiles.7df.header b/man/man7/dotfiles.7df.header
deleted file mode 100644
index cc8aef57..00000000
--- a/man/man7/dotfiles.7df.header
+++ /dev/null
@@ -1,3 +0,0 @@
-% DOTFILES(7) Tom Ryder's personal scripts and configuration
-% Tom Ryder
-% June 2016
diff --git a/mpd/profile.d/mpd.sh b/mpd/profile.d/mpd.sh
index daa55af6..5a14aef2 100644
--- a/mpd/profile.d/mpd.sh
+++ b/mpd/profile.d/mpd.sh
@@ -1,3 +1,3 @@
# Start an mpd process if one isn't already running
-command mpd >/dev/null 2>&1 || return
+command -v mpd >/dev/null 2>&1 || return
[ -s "$HOME"/.mpd/pid ] || mpd
diff --git a/sh/profile.d/games.sh b/sh/profile.d/games.sh
index ee56c593..956d1de1 100644
--- a/sh/profile.d/games.sh
+++ b/sh/profile.d/games.sh
@@ -1,3 +1,3 @@
# Add ~/.local/games to PATH if it exists
[ -d "$HOME"/.local/games ] || return
-PATH=$HOME/.local/games:$PATH
+PATH=$PATH:$HOME/.local/games
diff --git a/sh/shrc.d/ed.sh b/sh/shrc.d/ed.sh
index a2b7818e..e6b6eee8 100644
--- a/sh/shrc.d/ed.sh
+++ b/sh/shrc.d/ed.sh
@@ -1,3 +1,7 @@
+# Our ~/.profile should already have made a directory with the supported
+# options for us; if not, we won't be wrapping ed(1) with a function at all
+[ -d "$HOME"/.cache/sh/opt/ed ] || return
+
# Define function proper
ed() {
diff --git a/sh/shrc.d/env.sh b/sh/shrc.d/env.sh
deleted file mode 100644
index 4fa980f2..00000000
--- a/sh/shrc.d/env.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-# Sort the output of env(1) for me
-env() {
- if [ "$#" -eq 0 ] ; then
- command env | sort
- else
- command env "$@"
- fi
-}
diff --git a/sh/shrc.d/hgrep.sh b/sh/shrc.d/hgrep.sh
index fe297ab3..9d7542b4 100644
--- a/sh/shrc.d/hgrep.sh
+++ b/sh/shrc.d/hgrep.sh
@@ -9,7 +9,7 @@ hgrep() {
return 2
fi
if [ -z "$HISTFILE" ] ; then
- printf >&2 'hgrep(): No HISTFILE\n'
+ printf >&2 'hgrep(): HISTFILE unset or null\n'
return 2
fi
grep "$@" "$HISTFILE"
diff --git a/sh/shrc.d/prompt.sh b/sh/shrc.d/prompt.sh
index 6f1e8e1f..30e4e9d8 100644
--- a/sh/shrc.d/prompt.sh
+++ b/sh/shrc.d/prompt.sh
@@ -7,5 +7,5 @@ PS1='$ ' PS2='> ' PS3='? ' PS4='+ '
# If we have an SSH_CLIENT or SSH_CONNECTION environment variable, put the
# hostname in PS1 too.
if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_CONNECTION" ] ; then
- PS1=$(hostname)'$ '
+ PS1=$(hostname -s)'$ '
fi
diff --git a/tmux/tmux.conf b/tmux/tmux.conf
index 09ba0251..4e277515 100644
--- a/tmux/tmux.conf
+++ b/tmux/tmux.conf
@@ -13,7 +13,7 @@ set-environment -gru WINDOWID
set-option -g update-environment ''
# Setting this makes each new pane a non-login shell, which suits me better
-set-option -g default-command '$SHELL'
+set-option -g default-command "$SHELL"
# Expect a 256-color terminal
set-option -g default-terminal 'screen-256color'
diff --git a/vim/bundle/unimpaired b/vim/bundle/unimpaired
-Subproject 7bbbca73233b1492a8c3d16f91f34340eccc9b3
+Subproject e1e0cc3859323f354b8d905ca177e172c7d69f0
diff --git a/wget/wgetrc b/wget/wgetrc
new file mode 100644
index 00000000..eb3f20ba
--- /dev/null
+++ b/wget/wgetrc
@@ -0,0 +1,14 @@
+# No, no, dig UP, stupid
+no_parent = on
+
+# Don't take no for an answer
+retry_connrefused = on
+
+# It's the only thing that works against the machines
+robots = off
+
+# Shorten default timeout
+timeout = 60
+
+# Reduce default number of attempts
+tries = 3
diff --git a/zsh/zshrc.d/prompt.zsh b/zsh/zshrc.d/prompt.zsh
index 7c695e25..b612c704 100644
--- a/zsh/zshrc.d/prompt.zsh
+++ b/zsh/zshrc.d/prompt.zsh
@@ -125,7 +125,10 @@ prompt() {
# Print the status in brackets; add a git: prefix only if there
# might be another VCS prompt (because PROMPT_VCS is set)
printf '(%s%s%s%s)' \
- "${PROMPT_VCS:+git:}" "$name" "${proc:+:"$proc"}" "$state"
+ "${PROMPT_VCS:+git:}" \
+ "${name//\%/%%}" \
+ "${proc:+:"${proc//\%/%%}"}" \
+ "${state//\%/%%}"
;;
# Subversion prompt function
@@ -151,6 +154,7 @@ prompt() {
branch=${branch#/}
branch=${branch#branches/}
branch=${branch%%/*}
+ [[ -n $branch ]] || branch=unknown
# Parse the output of svn status to determine working copy state
local symbol
@@ -168,7 +172,9 @@ prompt() {
((untracked)) && state=${state}'?'
# Print the state in brackets with an svn: prefix
- printf '(svn:%s%s)' "${branch:-unknown}" "$state"
+ printf '(svn:%s%s)' \
+ "${branch//\%/%%}" \
+ "${state//\%/%%}"
;;
# VCS wrapper prompt function; print the first relevant prompt, if any
@@ -182,7 +188,7 @@ prompt() {
# Show return status of previous command in angle brackets, if not zero
ret)
# shellcheck disable=SC2154
- ((ret)) && printf '<%u>' "$ret"
+ ((ret)) && printf '<%u>' "${ret//\%/%%}"
;;
# Show the count of background jobs in curly brackets, if not zero
@@ -191,7 +197,7 @@ prompt() {
while read -r ; do
((jobc++))
done < <(jobs -p)
- ((jobc)) && printf '{%u}' "$jobc"
+ ((jobc)) && printf '{%u}' "${jobc//\%/%%}"
;;
# No argument given, print prompt strings and vars