aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2016-12-14 11:41:35 +1300
committerTom Ryder <tom@sanctum.geek.nz>2016-12-14 11:41:35 +1300
commitcd7a151621ec072238882d8379950eb99eddac2d (patch)
treee6aaa337de415bc9bee766ee2fb93d00a47cb174
parentAdd gwp(1) (diff)
parentAdd completion for ad() (diff)
downloaddotfiles-cd7a151621ec072238882d8379950eb99eddac2d.tar.gz
dotfiles-cd7a151621ec072238882d8379950eb99eddac2d.zip
Merge branch 'master' into freebsd
-rw-r--r--.gitignore3
-rw-r--r--Makefile13
-rw-r--r--README.markdown12
-rw-r--r--bash/bash_completion.d/_ssh_config_hosts.bash1
-rw-r--r--bash/bash_completion.d/_text_filenames.bash5
-rw-r--r--bash/bash_completion.d/ad.bash2
-rw-r--r--bash/bash_completion.d/eds.bash1
-rw-r--r--bash/bash_completion.d/find.bash2
-rw-r--r--bash/bash_completion.d/keep.bash2
-rw-r--r--bash/bash_completion.d/kill.bash1
-rw-r--r--bash/bash_completion.d/make.bash1
-rw-r--r--bash/bash_completion.d/man.bash7
-rw-r--r--bash/bash_completion.d/sd.bash1
-rw-r--r--bash/bash_completion.d/td.bash1
-rwxr-xr-xbin/chc31
-rwxr-xr-xbin/clog7
-rw-r--r--bin/ddup.awk2
-rw-r--r--bin/gwp.awk9
-rw-r--r--bin/han.bash2
-rwxr-xr-xbin/loc10
-rw-r--r--bin/med.awk5
-rw-r--r--bin/mftl.awk6
-rw-r--r--bin/rfct.awk15
-rw-r--r--bin/rndi.awk8
-rw-r--r--bin/sec.awk24
-rw-r--r--bin/slsf.awk3
-rw-r--r--bin/unf.awk8
-rw-r--r--bin/uts.awk6
-rw-r--r--games/drakon.awk23
-rw-r--r--games/kvlt.sed3
-rw-r--r--games/strik.sed64
-rw-r--r--man/man1/chc.1df35
-rw-r--r--man/man1/clog.1df15
-rw-r--r--man/man1/loc.1df14
-rw-r--r--man/man1/sec.1df21
-rw-r--r--man/man1/uts.1df16
-rw-r--r--man/man6/aesth.6df6
-rw-r--r--man/man6/strik.6df19
-rw-r--r--pdksh/pdkshrc.d/prompt.pdksh12
-rw-r--r--sh/shrc.d/ad.sh77
-rw-r--r--sh/shrc.d/bd.sh12
-rw-r--r--sh/shrc.d/sd.sh8
-rw-r--r--sh/shrc.d/tree.sh3
-rw-r--r--vim/after/ftdetect/diff.vim4
-rw-r--r--vim/after/ftdetect/sh.vim52
-rw-r--r--vim/after/ftplugin/sh.vim12
-rw-r--r--vim/after/syntax/sh.vim167
-rw-r--r--zsh/zshrc7
-rw-r--r--zsh/zshrc.d/prompt.zsh7
49 files changed, 577 insertions, 188 deletions
diff --git a/.gitignore b/.gitignore
index 7bd19d59..d7a435fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,15 +9,18 @@ bin/mode
bin/rfct
bin/rndi
bin/sd2u
+bin/sec
bin/slsf
bin/su2d
bin/tot
bin/unf
+bin/uts
games/acq
games/aesth
games/drakon
games/kvlt
games/rot13
+games/strik
games/zs
git/gitconfig
gnupg/gpg.conf
diff --git a/Makefile b/Makefile
index 3f48b8d9..363f9e19 100644
--- a/Makefile
+++ b/Makefile
@@ -77,10 +77,12 @@ all : bin/csmw \
bin/rfct \
bin/rndi \
bin/sd2u \
+ bin/sec \
bin/slsf \
bin/su2d \
bin/tot \
bin/unf \
+ bin/uts \
git/gitconfig \
gnupg/gpg.conf
@@ -97,15 +99,18 @@ clean distclean :
bin/rfct \
bin/rndi \
bin/sd2u \
+ bin/sec \
bin/slsf \
bin/su2d \
bin/tot \
bin/unf \
+ bin/uts \
games/acq \
games/aesth \
games/drakon \
games/kvlt \
games/rot13 \
+ games/strik \
games/zs \
git/gitconfig \
gnupg/gpg.conf \
@@ -188,8 +193,8 @@ install-bash-completion : install-bash
install -pm 0644 -- bash/bash_completion.d/* "$(HOME)"/.bash_completion.d
install-bin : bin/csmw bin/ddup bin/gwp bin/han bin/mean bin/med bin/mftl \
- bin/mode bin/rfct bin/rndi bin/sd2u bin/slsf bin/su2d bin/tot bin/unf \
- install-bin-man
+ bin/mode bin/rfct bin/rndi bin/sd2u bin/sec bin/slsf bin/su2d bin/tot \
+ bin/unf bin/uts install-bin-man
install -m 0755 -d -- "$(HOME)"/.local/bin
for name in bin/* ; do \
[ -x "$$name" ] || continue ; \
@@ -219,8 +224,8 @@ install-finger :
install -pm 0644 -- finger/project "$(HOME)"/.project
install -pm 0644 -- finger/pgpkey "$(HOME)"/.pgpkey
-install-games : games/acq games/aesth games/drakon games/kvlt games/rot13 games/zs \
- check-games install-games-man
+install-games : games/acq games/aesth games/drakon games/kvlt games/rot13 \
+ games/strik games/zs check-games install-games-man
install -m 0755 -d -- "$(HOME)"/.local/games
for name in games/* ; do \
[ -x "$$name" ] || continue ; \
diff --git a/README.markdown b/README.markdown
index 5eae69ac..f4c73817 100644
--- a/README.markdown
+++ b/README.markdown
@@ -114,7 +114,7 @@ older syntax for certain things such as appending items to arrays:
array[${#array[@]}]=$item
Compare this to the much nicer syntax available since 3.1-alpha1, which
-actually works for arrays with sparse indexes, unlike the above syntax:
+actually works for arrays with sparse indices, unlike the above syntax:
array+=("$item")
@@ -167,6 +167,9 @@ terminals.
If a function can be written in POSIX `sh` without too much hackery, I put it
in `sh/shrc.d` to be loaded by any POSIX interactive shell. Those include:
+* `ad()` is a `cd` shortcut accepting targets like `/u/l/b` for
+ `/usr/local/bin`, as long as they are unique, emulating a feature of the
+ Zsh `cd` builtin that I like.
* `bc()` silences startup messages from GNU `bc(1)`.
* `bd()` changes into a named ancestor of the current directory.
* `diff()` forces the unified format for `diff(1)`.
@@ -419,6 +422,8 @@ Installed by the `install-bin` target:
* `cf(1df)` prints a count of entries in a given directory.
* `cfr(1df)` does the same as `cf(1df)`, but recurses into subdirectories as
well.
+* `chc(1df)` caches the output of a command.
+* `clog(1df)` is a tiny timestamped log system.
* `clrd(1df)` sets up a per-line file read, clearing the screen first.
* `clwr(1df)` sets up a per-line file write, clearing the screen before each
line
@@ -454,6 +459,7 @@ Installed by the `install-bin` target:
* `jfc(1df)` adds and commits lazily to a Git repository.
* `jfcd(1df)` watches a directory for changes and runs `jfc(1df)` if it sees
any.
+* `loc(1df)` is a quick-search wrapped around `find(1)`.
* `maybe(1df)` is like `true(1)` or `false(1)`; given a probability of
success,
it exits with success or failure. Good for quick tests.
@@ -472,6 +478,7 @@ Installed by the `install-bin` target:
* `rgl(1df)` is a very crude interactive `grep(1)` loop.
* `shb(1df)` attempts to build shebang lines for scripts from the system
paths.
+* `sec(1df)` converts `hh:mm:ss` or `mm:ss` timestamps to seconds
* `spr(1df)` posts its input to the sprunge.us pastebin.
* `sqs(1df)` chops off query strings from filenames, usually downloads.
* `sshi(1df)` prints human-readable SSH connection details.
@@ -486,6 +493,8 @@ Installed by the `install-bin` target:
tolerating blips or temporary failures in `cron(8)` scripts.
* `umake(1df)` iterates upwards through the directory tree from `$PWD` until
it finds a Makefile for which to run `make(1)` with the given arguments.
+* `uts(1df)` gets the current UNIX timestamp in an unorthodox way that should
+ work on all POSIX-compliant operating systems.
There's some silly stuff in `install-games`:
@@ -497,6 +506,7 @@ There's some silly stuff in `install-games`:
* `kvlt(6df)` translates input to emulate a style of typing unique to black
metal communities on the internet.
* `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.
* `xyzzy(6df)` teleports to a marked location on the filesystem.
* `zs(6df)` prepends "z" case-appropriately to every occurrence of "s" in the
diff --git a/bash/bash_completion.d/_ssh_config_hosts.bash b/bash/bash_completion.d/_ssh_config_hosts.bash
index 02e9af06..8f45c412 100644
--- a/bash/bash_completion.d/_ssh_config_hosts.bash
+++ b/bash/bash_completion.d/_ssh_config_hosts.bash
@@ -14,6 +14,7 @@ _ssh_config_hosts() {
done
# Generate completion reply
+ local host
for host in "${hosts[@]}" ; do
[[ $host == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue
COMPREPLY[${#COMPREPLY[@]}]=$host
diff --git a/bash/bash_completion.d/_text_filenames.bash b/bash/bash_completion.d/_text_filenames.bash
index 577d082a..09d831a5 100644
--- a/bash/bash_completion.d/_text_filenames.bash
+++ b/bash/bash_completion.d/_text_filenames.bash
@@ -8,6 +8,7 @@
# the thing I want, and I want it to stay fast.
#
_text_filenames() {
+ local item
while IFS= read -r item ; do
# Exclude blanks
@@ -66,8 +67,8 @@ _text_filenames() {
# Other known binary extensions
# (I haven't included .com; on UNIX, that's more likely to be
# something I saved from a website and named after the domain)
- *.a|*.dat|*.drv|*.exe|*.o|*.torrent|*.wad|*.rom) ;;
- *.A|*.DAT|*.DRV|*.EXE|*.O|*.TORRENT|*.WAD|*.ROM) ;;
+ *.a|*.drv|*.exe|*.o|*.torrent|*.wad|*.rom) ;;
+ *.A|*.DRV|*.EXE|*.O|*.TORRENT|*.WAD|*.ROM) ;;
# Complete everything else; some of it will still be binary
*) COMPREPLY[${#COMPREPLY[@]}]=$item ;;
diff --git a/bash/bash_completion.d/ad.bash b/bash/bash_completion.d/ad.bash
new file mode 100644
index 00000000..390fcb00
--- /dev/null
+++ b/bash/bash_completion.d/ad.bash
@@ -0,0 +1,2 @@
+# Completion function for ad(); just directories
+complete -A directory ad
diff --git a/bash/bash_completion.d/eds.bash b/bash/bash_completion.d/eds.bash
index 3829e157..58ecf402 100644
--- a/bash/bash_completion.d/eds.bash
+++ b/bash/bash_completion.d/eds.bash
@@ -4,6 +4,7 @@ _eds() {
local edspath
edspath=${EDSPATH:-"$HOME"/.local/bin}
[[ -d $edspath ]] || return
+ local executable
while IFS= read -rd '' executable ; do
[[ -n $executable ]] || continue
COMPREPLY[${#COMPREPLY[@]}]=$executable
diff --git a/bash/bash_completion.d/find.bash b/bash/bash_completion.d/find.bash
index 74dc17ad..007a83bd 100644
--- a/bash/bash_completion.d/find.bash
+++ b/bash/bash_completion.d/find.bash
@@ -7,6 +7,7 @@ _find() {
# Backtrack through words so far; if none of them look like options, we're
# still completing directory names
+ local i
local -i opts
for ((i = COMP_CWORD; i >= 0; i--)) ; do
case ${COMP_WORDS[i]} in
@@ -26,6 +27,7 @@ _find() {
compopt -o bashdefault -o default
# Iterate through whatever the subshell gives us; don't add blank items, though
+ local item
while read -r item ; do
[[ -n $item ]] || continue
COMPREPLY[${#COMPREPLY[@]}]=$item
diff --git a/bash/bash_completion.d/keep.bash b/bash/bash_completion.d/keep.bash
index 41423af0..7148ad2b 100644
--- a/bash/bash_completion.d/keep.bash
+++ b/bash/bash_completion.d/keep.bash
@@ -25,6 +25,7 @@ _keep() {
# Complete with appropriate mode
case $mode in
names)
+ local word
while IFS= read -r word ; do
[[ -n $word ]] || continue
COMPREPLY[${#COMPREPLY[@]}]=$word
@@ -32,6 +33,7 @@ _keep() {
-- "${COMP_WORDS[COMP_CWORD]}")
;;
kept)
+ local word
while IFS= read -r word ; do
[[ -n $word ]] || continue
COMPREPLY[${#COMPREPLY[@]}]=$word
diff --git a/bash/bash_completion.d/kill.bash b/bash/bash_completion.d/kill.bash
index 0aca041f..dccc926b 100644
--- a/bash/bash_completion.d/kill.bash
+++ b/bash/bash_completion.d/kill.bash
@@ -1,6 +1,7 @@
# Complete kill builtin with jobspecs (prefixed with % so it will accept them)
# and this user's PIDs (requires pgrep(1))
_kill() {
+ local pid
while read -r pid ; do
case $pid in
"${COMP_WORDS[COMP_CWORD]}"*)
diff --git a/bash/bash_completion.d/make.bash b/bash/bash_completion.d/make.bash
index 67c577f3..b3314e21 100644
--- a/bash/bash_completion.d/make.bash
+++ b/bash/bash_completion.d/make.bash
@@ -10,6 +10,7 @@ _make() {
[[ -n $mf ]] || return 1
# Iterate through the Makefile, line by line
+ local line
while IFS= read -r line ; do
case $line in
diff --git a/bash/bash_completion.d/man.bash b/bash/bash_completion.d/man.bash
index 9861e9dd..efc93153 100644
--- a/bash/bash_completion.d/man.bash
+++ b/bash/bash_completion.d/man.bash
@@ -8,6 +8,12 @@ _man() {
local word
word=${COMP_WORDS[COMP_CWORD]}
+ # Don't bother if the word has slashes in it, the user is probably trying
+ # to complete an actual path
+ case $word in
+ */*) return 1 ;;
+ esac
+
# If this is the second word, and the previous word started with a number,
# we'll assume that's the section to search
local section subdir
@@ -18,6 +24,7 @@ _man() {
# Read completion results from a subshell and add them to the COMPREPLY
# array individually
+ local page
while IFS= read -rd '' page ; do
[[ -n $page ]] || continue
COMPREPLY[${#COMPREPLY[@]}]=$page
diff --git a/bash/bash_completion.d/sd.bash b/bash/bash_completion.d/sd.bash
index 0c59f544..aeee1615 100644
--- a/bash/bash_completion.d/sd.bash
+++ b/bash/bash_completion.d/sd.bash
@@ -8,6 +8,7 @@ _sd() {
[[ $PWD != / ]] || return 1
# Build list of matching sibiling directories
+ local dirname
while IFS= read -rd '' dirname ; do
[[ -n $dirname ]] || continue
COMPREPLY[${#COMPREPLY[@]}]=$dirname
diff --git a/bash/bash_completion.d/td.bash b/bash/bash_completion.d/td.bash
index 6d995d0e..eb29992b 100644
--- a/bash/bash_completion.d/td.bash
+++ b/bash/bash_completion.d/td.bash
@@ -2,6 +2,7 @@
_td() {
local dir
dir=${TODO_DIR:-"$HOME"/Todo}
+ local fn
while IFS= read -rd '' fn ; do
COMPREPLY[${#COMPREPLY[@]}]=$fn
done < <(
diff --git a/bin/chc b/bin/chc
new file mode 100755
index 00000000..b1e4000d
--- /dev/null
+++ b/bin/chc
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Cache the output of a command and emit it straight from the cache if not
+# expired on each run
+
+# First argument is the cache path, second is the duration in seconds
+cac=$1 dur=$2
+shift 2
+
+# Get the current timestamp with uts(1df)
+uts=$(uts) || exit
+
+# Function checks cache exists, is readable, and not expired
+fresh() {
+ [ -f "$cac" ] || return
+ [ -r "$cac" ] || return
+ exp=$(sed 1q -- "$cac") || return
+ [ "$((exp > uts))" -eq 1 ]
+}
+
+# Write runs the command and writes it to the cache
+write() {
+ exp=$((uts + dur))
+ printf '%u\n' "$exp"
+ "$@"
+}
+
+# If the cache isn't fresh, try to write a new one, or bail out
+fresh "$cac" || write "$@" > "$cac" || exit
+
+# Emit the content (exclude the first line, which is the timestamp)
+sed 1d -- "$cac"
diff --git a/bin/clog b/bin/clog
new file mode 100755
index 00000000..d52ab4c5
--- /dev/null
+++ b/bin/clog
@@ -0,0 +1,7 @@
+#!/bin/sh
+# Record a timestamped message to a logfile, defaulting to ~/.clog
+{
+ date
+ cat -
+ printf '%s\n' --
+} >>"${CLOG:-"$HOME"/.clog}"
diff --git a/bin/ddup.awk b/bin/ddup.awk
index 28b4d135..2dec1d00 100644
--- a/bin/ddup.awk
+++ b/bin/ddup.awk
@@ -1,2 +1,2 @@
-#!/usr/bin/awk -f
+# Skip duplicate lines (without requiring sorted input)
!seen[$0]++
diff --git a/bin/gwp.awk b/bin/gwp.awk
index db2764ec..32fe97f2 100644
--- a/bin/gwp.awk
+++ b/bin/gwp.awk
@@ -1,12 +1,11 @@
-#!/usr/bin/awk -f
# Search for alphanumeric words in a file
BEGIN {
# Name self
self = "gwp"
- # Words are separated by any non-alphanumeric character
- FS = "[^[:alnum:]]"
+ # Words are separated by any non-alphanumeric characters
+ FS = "[^a-zA-Z0-9]+"
# First argument is the word required; push its case downward so we can
# match case-insensitively
@@ -52,6 +51,4 @@ function fnpr() {
}
# Exit zero if we found at least one match, non-zero otherwise
-END {
- exit(!found)
-}
+END { exit(!found) }
diff --git a/bin/han.bash b/bin/han.bash
index 8536b61b..97dc3a19 100644
--- a/bin/han.bash
+++ b/bin/han.bash
@@ -21,7 +21,7 @@ fi
# the script exits
td=
cleanup() {
- [[ -n "$td" ]] && rm -fr -- "$td"
+ [[ -n $td ]] && rm -fr -- "$td"
}
trap cleanup EXIT
td=$(mktd "$self") || exit
diff --git a/bin/loc b/bin/loc
new file mode 100755
index 00000000..7f4ac1fa
--- /dev/null
+++ b/bin/loc
@@ -0,0 +1,10 @@
+#!/bin/sh
+if [ "$#" -eq 0 ] ; then
+ printf >&2 'loc: Need a search term\n'
+ exit 2
+fi
+for pat ; do
+ find . \
+ -name .\* ! -name . -prune -o \
+ -name \*"$pat"\* -prune -print
+done
diff --git a/bin/med.awk b/bin/med.awk
index 83f0eb74..8167f8dd 100644
--- a/bin/med.awk
+++ b/bin/med.awk
@@ -7,11 +7,10 @@ END {
# Error out if we read no values at all
if (!NR)
exit(1)
- if (NR % 2) {
+ if (NR % 2)
med = vals[(NR+1)/2]
- } else {
+ else
med = (vals[NR/2] + vals[NR/2+1]) / 2
- }
print med
if (warn)
exit(1)
diff --git a/bin/mftl.awk b/bin/mftl.awk
index f210de33..21976337 100644
--- a/bin/mftl.awk
+++ b/bin/mftl.awk
@@ -2,9 +2,7 @@
# could be reasonably expected to call directly
# Separators are space, tab, or colon
-BEGIN {
- FS = "[ \t:]"
-}
+BEGIN { FS = "[ \t:]" }
# Skip comments
/^#/ { next }
@@ -28,7 +26,7 @@ BEGIN {
# unique; this probably needs refinement
for (i = 1; i < NF; i++)
if ($i ~ /^[a-zA-Z0-9][a-zA-Z0-9.\/_-]*$/)
- ats[$i]
+ ats[$i]++
}
# Print unique determined targets, sorted
diff --git a/bin/rfct.awk b/bin/rfct.awk
index 53e948b2..2f0cc42d 100644
--- a/bin/rfct.awk
+++ b/bin/rfct.awk
@@ -1,15 +1,8 @@
# Format an RFC in text format for terminal reading
# A record is a paragraph
-BEGIN {
- RS=""
-}
+BEGIN { RS = "" }
-# Skip any block without at least one alphanumeric char
-!/[[:alnum:]]/ { next }
-
-# Skip any block with a page break marker in it
-/\x0c/ { next }
-
-# Print the block followed by two newlines
-{ printf "%s\n\n", $0 }
+# Print the block followed by two newlines, as long as it has at least one
+# alphanumeric character and no pagebreak characters
+/[a-zA-Z0-9]/ && !/\x0c/ { printf "%s\n\n", $0 }
diff --git a/bin/rndi.awk b/bin/rndi.awk
index 337498cb..49df4398 100644
--- a/bin/rndi.awk
+++ b/bin/rndi.awk
@@ -5,17 +5,15 @@
BEGIN {
# Seed with the third argument if given
- if (ARGV[3]) {
+ if (ARGV[3])
srand(ARGV[3])
- }
# If not, just seed with what is probably a date/time-derived value
- else {
+ 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(ARGV[1] + rand() * (ARGV[2] - ARGV[1] + 1))
# Bail before processing any lines
exit
diff --git a/bin/sec.awk b/bin/sec.awk
new file mode 100644
index 00000000..3ebf02b6
--- /dev/null
+++ b/bin/sec.awk
@@ -0,0 +1,24 @@
+# Convert [[[hh:]mm:]ss] timestamps to seconds
+
+# Separator is :, strip out leading zeroes
+BEGIN { FS = ":0*" }
+
+# If no fields, too many fields, or illegal characters, warn, skip line, accrue
+# errors
+!NF || NF > 3 || /[^0-9:]/ {
+ print "sec: Bad format" > "/dev/stderr"
+ err = 1
+ next
+}
+
+# Match hh:mm:ss
+NF == 3 { printf "%u\n", $1 * 3600 + $2 * 60 + $3 }
+
+# Match mm:ss
+NF == 2 { printf "%u\n", $1 * 60 + $2 }
+
+# Match ss (in which case all we've done is strip zeroes)
+NF == 1 { printf "%u\n", $1 }
+
+# Done, exit 1 if we had any errors on the way
+END { exit(err > 0) }
diff --git a/bin/slsf.awk b/bin/slsf.awk
index 9d12225d..3d5c190f 100644
--- a/bin/slsf.awk
+++ b/bin/slsf.awk
@@ -6,5 +6,4 @@ FNR == 1 || /### sls/ { sls = 1 }
# If processing flag set, directive is "Host", and hostname has no wildcards,
# then print it
-!sls { next }
-$1 == "Host" && $2 !~ /\*/ { print $2 }
+sls && $1 == "Host" && $2 !~ /\*/ { print $2 }
diff --git a/bin/unf.awk b/bin/unf.awk
index 22a10aa8..a9837a8a 100644
--- a/bin/unf.awk
+++ b/bin/unf.awk
@@ -18,9 +18,7 @@ body {
}
# Write any buffer contents once we hit a line not starting with a space
-/^[^ \t]/ {
- wrbuf()
-}
+/^[^ \t]/ { wrbuf() }
# Append the current line to the buffer
{
@@ -29,6 +27,4 @@ body {
}
# Write the buffer out again when we hit the end
-END {
- wrbuf()
-}
+END { wrbuf() }
diff --git a/bin/uts.awk b/bin/uts.awk
new file mode 100644
index 00000000..3aaec2ab
--- /dev/null
+++ b/bin/uts.awk
@@ -0,0 +1,6 @@
+# Print the current UNIX epoch timestamp in a POSIX compatible fashion
+BEGIN {
+ srand()
+ print srand()
+ exit
+}
diff --git a/games/drakon.awk b/games/drakon.awk
index e960a6c0..39bb3732 100644
--- a/games/drakon.awk
+++ b/games/drakon.awk
@@ -1,20 +1,13 @@
# TyPe lIkE AnDoR DrAkOn fRoM AnCiEnT DoMaInS Of mYsTeRy
# <http://www.adomgb.info/adomgb-4.html>
{
- s = ""
- u = 0
- for (i = 1; i <= length($0); i++) {
- c = substr($0, i, 1)
- if (c ~ /[a-zA-Z]/) {
- if (u) {
- c = toupper(c)
- }
- else {
- c = tolower(c)
- }
- u = !u
- }
- s = s c
+ line = ""
+ case = 0
+ for (i = 1; i <= length; i++) {
+ char = substr($0, i, 1)
+ if (char ~ /[a-zA-Z]/)
+ char = (case = !case) ? tolower(char) : toupper(char)
+ line = line char
}
- print s
+ print line
}
diff --git a/games/kvlt.sed b/games/kvlt.sed
index 29a92227..e2905a35 100644
--- a/games/kvlt.sed
+++ b/games/kvlt.sed
@@ -33,8 +33,9 @@ s,\([^A-Z]\)GOOD\([^A-Z]\),\1TRUE\2,g
s,\([^A-Z]\)GREAT\([^A-Z]\),\1TRUE\2,g
s,\([^A-Z]\)NICE\([^A-Z]\),\1TRUE\2,g
-# WAR -> KRIEG
+# WAR/COOL -> KRIEG
s,\([^A-Z]\)WAR\([^A-Z]\),\1KRIEG\2,g
+s,\([^A-Z]\)COOL\([^A-Z]\),\1KRIEG\2,g
# Double-letters are easy
s,CC,KK,g
diff --git a/games/strik.sed b/games/strik.sed
new file mode 100644
index 00000000..bc1cbdc5
--- /dev/null
+++ b/games/strik.sed
@@ -0,0 +1,64 @@
+# Strike out text
+s/a/a̶/g
+s/b/b̶/g
+s/c/c̶/g
+s/d/d̶/g
+s/e/e̶/g
+s/f/f̶/g
+s/g/g̶/g
+s/h/h̶/g
+s/i/i̶/g
+s/j/j̶/g
+s/k/k̶/g
+s/l/l̶/g
+s/m/m̶/g
+s/n/n̶/g
+s/o/o̶/g
+s/p/p̶/g
+s/q/q̶/g
+s/r/r̶/g
+s/s/s̶/g
+s/t/t̶/g
+s/u/u̶/g
+s/v/v̶/g
+s/w/w̶/g
+s/x/x̶/g
+s/y/y̶/g
+s/z/z̶/g
+s/A/A̶/g
+s/B/B̶/g
+s/C/C̶/g
+s/D/D̶/g
+s/E/E̶/g
+s/F/F̶/g
+s/G/G̶/g
+s/H/H̶/g
+s/I/I̶/g
+s/J/J̶/g
+s/K/K̶/g
+s/L/L̶/g
+s/M/M̶/g
+s/N/N̶/g
+s/O/O̶/g
+s/P/P̶/g
+s/Q/Q̶/g
+s/R/R̶/g
+s/S/S̶/g
+s/T/T̶/g
+s/U/U̶/g
+s/V/V̶/g
+s/W/W̶/g
+s/X/X̶/g
+s/Y/Y̶/g
+s/Z/Z̶/g
+s/0/0̶/g
+s/1/1̶/g
+s/2/2̶/g
+s/3/3̶/g
+s/4/4̶/g
+s/5/5̶/g
+s/6/6̶/g
+s/7/7̶/g
+s/8/8̶/g
+s/9/9̶/g
+s/ / ̶/g
diff --git a/man/man1/chc.1df b/man/man1/chc.1df
new file mode 100644
index 00000000..e447d7a7
--- /dev/null
+++ b/man/man1/chc.1df
@@ -0,0 +1,35 @@
+.TH CHC 1df "December 2016" "Manual page for chc"
+.SH NAME
+.B chc
+\- cache the output of a command to avoid running it too often
+.SH USAGE
+.B chc
+CACHE_PATH DURATION COMMAND [ARG1...]
+.SH SYNOPSIS
+.B chc
+/tmp/example.chc 20 curl http://www.example.com/
+.SH DESCRIPTION
+.B chc
+runs the command given in its third argument onwards, and saves the output in
+the file with path given in the first argument, and on each subsequent request
+before the duration in the second argument expires, it emits the content
+directly, rather than running the command. If it's run after the expiry date,
+it runs the command again, and refreshes the cache.
+.P
+This is intended as a quick way to just add three words in front of any given
+expensive command to prevent it running too often. This might be particularly
+useful if a script is called to get data far more often than it actually needs
+to poll to get that data.
+.P
+No file locking is implemented. If you need it, you're probably already at the
+point that you need to write a proper solution, but you could always use Linux
+flock(1) or daemontool's setlock(1) in the command if you're stubborn:
+.P
+ flock -x /var/lock/example.chc chc /var/cache/example.chc 20 curl http://www.example.com/
+.P
+If you want to express the duration in human-readable terms, sec(1df) might be
+useful too.
+.SH SEE ALSO
+sec(1df), uts(1df), flock(1), setlock(1)
+.SH AUTHOR
+Tom Ryder <tom@sanctum.geek.nz>
diff --git a/man/man1/clog.1df b/man/man1/clog.1df
new file mode 100644
index 00000000..bb03765d
--- /dev/null
+++ b/man/man1/clog.1df
@@ -0,0 +1,15 @@
+.TH CLOG 1df "December 2016" "Manual page for clog"
+.SH NAME
+.B clog
+\- record timestamped logs in a file
+.SH SYNOPSIS
+.B clog
+getting real tired of all this overengineering
+^D
+.SH DESCRIPTION
+.B clog
+receives a message on stdin, timestamps it with a leading date(1), and writes
+it to the file with path in environment variable CLOG, defaulting to ~/.clog,
+terminating each entry with two hyphens.
+.SH AUTHOR
+Tom Ryder <tom@sanctum.geek.nz>
diff --git a/man/man1/loc.1df b/man/man1/loc.1df
new file mode 100644
index 00000000..f288402c
--- /dev/null
+++ b/man/man1/loc.1df
@@ -0,0 +1,14 @@
+.TH LOC 1df "December 2016" "Manual page for loc"
+.SH NAME
+.B loc
+\- find files matching a pattern in the current directory
+.SH SYNOPSIS
+.B loc PATTERN1 [PATTERN2...]
+.SH DESCRIPTION
+.B loc
+is a simple wrapper around find(1) which searches in the current directory tree
+for filenames matching a pattern, and prints them to stdout, newline-separated.
+It skips dotfiles, and doesn't interate further into a directory if it matches
+the terms. It is intended only for interactive use as a shortcut.
+.SH AUTHOR
+Tom Ryder <tom@sanctum.geek.nz>
diff --git a/man/man1/sec.1df b/man/man1/sec.1df
new file mode 100644
index 00000000..589b6a74
--- /dev/null
+++ b/man/man1/sec.1df
@@ -0,0 +1,21 @@
+.TH SEC 1df "December 2016" "Manual page for sec"
+.SH NAME
+.B sec
+\- convert colon-delimited durations to seconds
+.SH USAGE
+.B sec
+FILE1 [FILE2 ...]
+.br
+.B sec
+< FILE
+.br
+printf '1:02:54\\n' |
+.br
+sec=$(printf '%s\n' "$timestamp" | sec)
+.B sec
+.SH DESCRIPTION
+Applies awk(1) to convert hh:mm:ss or mm:ss timestamps into a count of seconds.
+Exits zero if all lines were successfully recognised and converted, non-zero
+otherwise.
+.SH AUTHOR
+Tom Ryder <tom@sanctum.geek.nz>
diff --git a/man/man1/uts.1df b/man/man1/uts.1df
new file mode 100644
index 00000000..8b821fe4
--- /dev/null
+++ b/man/man1/uts.1df
@@ -0,0 +1,16 @@
+.TH UTS 1df "December 2016" "Manual page for uts"
+.SH NAME
+.B uts
+\- prints the current UNIX timestamp
+.SH SYNOPSIS
+.B uts
+.SH DESCRIPTION
+.B uts
+applies POSIX's specifications for the behaviour of awk's srand() function to
+print the current UNIX timestamp.
+.SH SEE ALSO
+awk(1), date(1)
+.br
+<http://pubs.opengroup.org/onlinepubs/9699919799/utilities/awk.html#tag_20_06_13_12>
+.SH AUTHOR
+Tom Ryder <tom@sanctum.geek.nz>
diff --git a/man/man6/aesth.6df b/man/man6/aesth.6df
index 1febe86c..ba2176da 100644
--- a/man/man6/aesth.6df
+++ b/man/man6/aesth.6df
@@ -10,9 +10,9 @@ lynx -dump https://sanctum.geek.nz/ |
.B aesth
.SH DESCRIPTION
.B aesth
-converts the 26 letters of the English alphabet, both upper and lower case, to
-their full-width text equivalents for the CJK environment from the Basic
-Multilingual Plane.
+converts the 26 letters of the English alphabet, both upper and lower case, the
+arabic numerals, and the space character to their full-width text equivalents
+for the CJK environment from the Basic Multilingual Plane.
.P
The results are printed in UTF-8; they're hard-coded within the script.
.SH AUTHOR
diff --git a/man/man6/strik.6df b/man/man6/strik.6df
new file mode 100644
index 00000000..3d5840a7
--- /dev/null
+++ b/man/man6/strik.6df
@@ -0,0 +1,19 @@
+.TH STRIK 6df "August 2016" "Manual page for strik"
+.SH NAME
+.B strik
+\- strike out text
+.SH USAGE
+.B strik
+/usr/share/dict/words
+.br
+lynx -dump https://sanctum.geek.nz/ |
+.B strik
+.SH DESCRIPTION
+.B strik
+converts the 26 letters of the English alphabet, both upper and lower case, the
+Arabic numerals, and the space character to their equivalents with a Unicode
+strikethrough.
+.P
+The results are printed in UTF-8; they're hard-coded within the script.
+.SH AUTHOR
+Tom Ryder <tom@sanctum.geek.nz>
diff --git a/pdksh/pdkshrc.d/prompt.pdksh b/pdksh/pdkshrc.d/prompt.pdksh
index fd99c5f7..63e965f9 100644
--- a/pdksh/pdkshrc.d/prompt.pdksh
+++ b/pdksh/pdkshrc.d/prompt.pdksh
@@ -23,7 +23,7 @@ prompt() {
PS1=$PS1'\w'
# Add sub-commands; VCS, job, and return status checks
- PS1=$PS1'$(prompt vcs)$(prompt job)'
+ PS1=$PS1'$(prompt vcs)$(prompt job)$(prompt ret "$?")'
# Add prefix and suffix
PS1='${PROMPT_PREFIX}'$PS1'${PROMPT_SUFFIX}'
@@ -43,7 +43,7 @@ prompt() {
# Prepare reset code
reset=$(tput sgr0 || tput me)
- # Check if we have non-bold bright green available
+ # Check if we have non-bold bright yellow available
if ((colors >= 16)) ; then
format=$(
: "${PROMPT_COLOR:=11}"
@@ -53,7 +53,7 @@ prompt() {
tput AF "$PROMPT_COLOR" 0 0
)
- # If we have only eight colors, use bold green
+ # If we have only eight colors, use bold yellow
elif ((colors >= 8)) ; then
format=$(
: "${PROMPT_COLOR:=3}"
@@ -167,6 +167,12 @@ prompt() {
done
;;
+ # Show return status of previous command in angle brackets, if not zero
+ ret)
+ local ret=$2
+ ((ret)) && printf '<%u>' "$ret"
+ ;;
+
# Show the count of background jobs in curly brackets, if not zero
job)
typeset -i jobc
diff --git a/sh/shrc.d/ad.sh b/sh/shrc.d/ad.sh
new file mode 100644
index 00000000..d3e5a90a
--- /dev/null
+++ b/sh/shrc.d/ad.sh
@@ -0,0 +1,77 @@
+# Find an abbreviated path
+ad() {
+
+ # Check argument count
+ if [ "$#" -ne 1 ] ; then
+ printf >&2 'ad(): Need just one argument\n'
+ return 2
+ fi
+
+ # Change the positional parameters from the abbreviated request
+ # to any matched directory
+ set -- "$(
+
+ # Clean up and anchor the request
+ req=${1%/}/
+ case $req in
+ (/*) ;;
+ (*) req=${PWD%/}/${req#/}/ ;;
+ esac
+
+ # Start building the target directory; go through the request piece by
+ # piece until it is used up
+ dir=
+ while [ -n "$req" ] ; do
+
+ # Chop the next front bit off the request and add it to the dir
+ dir=${dir%/}/${req%%/*}
+ req=${req#*/}
+
+ # If that exists, all is well and we can keep iterating
+ [ -d "$dir" ] && continue
+
+ # Set the positional parameters to a glob expansion of the
+ # abbreviated directory given
+ set -- "$dir"*
+
+ # Iterate through the positional parameters filtering out
+ # directories; we need to run right through the whole list to check
+ # that we have at most one match
+ entd=
+ for ent ; do
+ [ -d "$ent" ] || continue
+
+ # If we already found a match and have found another one, bail
+ # out
+ if [ -n "$entd" ] ; then
+ printf >&2 'ad(): More than one matching dir for %s*:\n' \
+ "$dir"
+ printf >&2 '%s\n' "$@"
+ exit 1
+ fi
+
+ # Otherwise, this can be our first one
+ entd=$ent
+ done
+
+ # If we found no match, bail out
+ if [ -z "$entd" ] ; then
+ printf >&2 'ad(): No matching dirs: %s*\n' "$dir"
+ exit 1
+ fi
+
+ # All is well, tack on what we have found and keep going
+ dir=$entd
+
+ done
+
+ # Print the target
+ printf '%s\n' "$dir"
+ )"
+
+ # If the subshell printed nothing, return with failure
+ [ -n "$1" ] || return
+
+ # Try to change into the determined directory
+ command cd -- "$@"
+}
diff --git a/sh/shrc.d/bd.sh b/sh/shrc.d/bd.sh
index 14d65c3b..1043a8cb 100644
--- a/sh/shrc.d/bd.sh
+++ b/sh/shrc.d/bd.sh
@@ -22,28 +22,28 @@ bd() {
case $req in
# Just go straight to the root or dot directories if asked
- /|.|..)
+ (/|.|..)
dirname=$req
;;
# Anything with a leading / needs to anchor to the start of the
# path. A strange request though. Why not just use cd?
- /*)
+ (/*)
dirname=$req
case $PWD in
- "$dirname"/*) ;;
- *) dirname='' ;;
+ ("$dirname"/*) ;;
+ (*) dirname='' ;;
esac
;;
# In all other cases, iterate through the PWD to find a match, or
# whittle the target down to an empty string trying
- *)
+ (*)
dirname=$PWD
while [ -n "$dirname" ] ; do
dirname=${dirname%/*}
case $dirname in
- */"$req") break ;;
+ (*/"$req") break ;;
esac
done
;;
diff --git a/sh/shrc.d/sd.sh b/sh/shrc.d/sd.sh
index 80cb7e39..ce59bf99 100644
--- a/sh/shrc.d/sd.sh
+++ b/sh/shrc.d/sd.sh
@@ -53,7 +53,7 @@ sd() {
else
for sib in ../.* ../* ; do
case ${sib#../} in
- .|..|"${PWD##*/}") continue ;;
+ (.|..|"${PWD##*/}") continue ;;
esac
set -- "$@" "$sib"
done
@@ -61,12 +61,12 @@ sd() {
# We should have exactly one sibling
case $# in
- 1) ;;
- 0)
+ (1) ;;
+ (0)
printf >&2 'sd(): No siblings\n'
exit 1
;;
- *)
+ (*)
printf >&2 'sd(): More than one sibling\n'
exit 1
;;
diff --git a/sh/shrc.d/tree.sh b/sh/shrc.d/tree.sh
index dd78f8ee..b4f91df8 100644
--- a/sh/shrc.d/tree.sh
+++ b/sh/shrc.d/tree.sh
@@ -7,7 +7,8 @@ tree() {
if (
# Not if -n is in the arguments and -C isn't
- while getopts 'nC' opt ; do
+ # Don't tell me about missing options, either
+ while getopts 'nC' opt 2>/dev/null ; do
case $opt in
n) n=1 ;;
C) C=1 ;;
diff --git a/vim/after/ftdetect/diff.vim b/vim/after/ftdetect/diff.vim
deleted file mode 100644
index c9df9184..00000000
--- a/vim/after/ftdetect/diff.vim
+++ /dev/null
@@ -1,4 +0,0 @@
-" nwatch.diff.* is a diff
-autocmd BufNewFile,BufRead
- \ nwatch.diff.*
- \ setlocal filetype=diff
diff --git a/vim/after/ftdetect/sh.vim b/vim/after/ftdetect/sh.vim
index 72d72d1d..3bc10ba7 100644
--- a/vim/after/ftdetect/sh.vim
+++ b/vim/after/ftdetect/sh.vim
@@ -1,31 +1,31 @@
-" .shrc is a shell script
-autocmd BufNewFile,BufRead
- \ .shrc
- \ setlocal filetype=sh
+" Add automatic commands to
+augroup dfsh
-" .xinitrc is a shell script
-autocmd BufNewFile,BufRead
- \ .xinitrc
- \ setlocal filetype=sh
+ " Names/paths of things that are Bash shell script
+ autocmd BufNewFile,BufRead
+ \ **/.dotfiles/bash/**,bash-fc-*
+ \ let b:is_bash = 1 |
+ \ setlocal filetype=sh
-" Files in /etc/default are shell script
-autocmd BufNewFile,BufRead
- \ /etc/default/*
- \ setlocal filetype=sh
+ " Names/paths of things that are Korn shell script
+ autocmd BufNewFile,BufRead
+ \ **/.dotfiles/pdksh/**,.pdkshrc,*.pdksh
+ \ let b:is_kornshell = 1 |
+ \ setlocal filetype=sh
-" Files in **/.dotfiles/sh/** are shell script
-autocmd BufNewFile,BufRead
- \ **/.dotfiles/sh/**
- \ setlocal filetype=sh
+ " Names/paths of things that are POSIX shell script
+ autocmd BufNewFile,BufRead
+ \ **/.dotfiles/sh/**,.shinit,.shrc,.xinitrc,/etc/default/*
+ \ let b:is_posix = 1 |
+ \ setlocal filetype=sh
-" Edited bash command lines are Bash script
-autocmd BufNewFile,BufRead
- \ bash-fc-*
- \ let g:is_bash = 1 |
- \ setlocal filetype=sh
+ " If we determined something is b:is_kornshell, tack on b:is_ksh as well so
+ " we can still tease out what is actually a kornshell script after sh.vim is
+ " done changing our options for us; it conflates POSIX with Korn shell.
+ autocmd BufNewFile,BufRead
+ \ *
+ \ if exists('b:is_kornshell') |
+ \ let b:is_ksh = 1 |
+ \ endif
-" Files in **/.dotfiles/bash/** are Bash script
-autocmd BufNewFile,BufRead
- \ **/.dotfiles/bash/**
- \ let g:is_bash = 1 |
- \ setlocal filetype=sh
+augroup END
diff --git a/vim/after/ftplugin/sh.vim b/vim/after/ftplugin/sh.vim
index 96ad8e6d..2b73611e 100644
--- a/vim/after/ftplugin/sh.vim
+++ b/vim/after/ftplugin/sh.vim
@@ -1,9 +1,9 @@
-" Include slashes as part of 'isk' so that e.g. 'local' in '/usr/local/mysql'
-" doesn't highlight
-let g:sh_isk='@,48-57,_,192-255,.,/'
-
-" Assume POSIX, I never write Bourne
-let g:is_posix=1
+" Default to POSIX shell, as I never write Bourne, and if I write Bash or Ksh
+" it'll be denoted with either a shebang or an appropriate extension. At the
+" time of writing, changing this also prompts sh.vim to set g:is_kornshell,
+" which is absurd, and requires a bit more massaging in after/syntax/sh.vim to
+" turn off some unwanted stuff.
+let g:is_posix = 1
" Use han(1df) as a man(1) wrapper for Bash files if available
if exists('b:is_bash') && executable('han')
diff --git a/vim/after/syntax/sh.vim b/vim/after/syntax/sh.vim
index 37ad8518..e8b41ba4 100644
--- a/vim/after/syntax/sh.vim
+++ b/vim/after/syntax/sh.vim
@@ -1,77 +1,98 @@
-" shDerefSimple in sh.vim is not quite right, so let's fix it up
-syntax clear shDerefSimple
+" If g:is_posix is set, g:is_kornshell is probably set too, a strange decision
+" by sh.vim. No matter; we can tease out whether this is actually a Korn shell
+" script using our own b:is_ksh flag set at the end of
+" ~/.vim/after/ftdetect/sh.vim, and if it isn't, we'll throw away the
+" highlighting groups for ksh.
+if exists('g:is_posix') && exists('g:is_kornshell') && !exists('b:is_ksh')
+ syntax clear kshSpecialVariables
+ syntax clear kshStatement
+endif
-" $var, $VAR, $var_new, $_var, $var1 ...
-syntax match shDerefSimple '\$\h[a-zA-Z0-9_]*'
-" $0, $1, $2 ...
-syntax match shDerefSimple '\$\d'
-" $-, $#, $* ...
-syntax match shDerefSimple '\$[-#*@!?$]'
+" Some corrections for highlighting if we have any of POSIX, Bash, or Ksh
+if exists('g:is_posix') || exists('b:is_bash') || exists('b:is_ksh')
-" Trust me to get my dereferencing right
-syntax clear shDerefWordError
+ " The syntax highlighter seems to flag '/baz' in '"${foo:-"$bar"/baz}"' as an
+ " error, and I'm pretty sure it's not, at least in POSIX sh, Bash, and Ksh.
+ syntax clear shDerefWordError
-" I don't like having 'restart', 'start" etc highlighted
-syntax clear bashAdminStatement
+ " The syntax highlighter doesn't match parens for subshells for 'if' tests
+ " correctly if they're on separate lines. This happens enough that it's
+ " probably not worth keeping the error.
+ syntax clear shParenError
-" Limit bashStatement only to alphanumeric shell builtins, except for the ones
-" that declare variables (declare, typeset, local, export, unset) as they're
-" used in shSetList
-syntax clear bashStatement
-syntax keyword bashStatement
- \ alias
- \ bg
- \ bind
- \ break
- \ builtin
- \ caller
- \ cd
- \ command
- \ compgen
- \ complete
- \ compopt
- \ continue
- \ coproc
- \ dirs
- \ disown
- \ echo
- \ enable
- \ eval
- \ exec
- \ exit
- \ false
- \ fc
- \ fg
- \ function
- \ getopts
- \ hash
- \ help
- \ history
- \ jobs
- \ kill
- \ let
- \ logout
- \ mapfile
- \ popd
- \ printf
- \ pushd
- \ pwd
- \ read
- \ readarray
- \ readonly
- \ return
- \ set
- \ shift
- \ shopt
- \ source
- \ suspend
- \ test
- \ time
- \ times
- \ trap
- \ true
- \ type
- \ ulimit
- \ umask
- \ unalias
- \ wait
+endif
+
+" Some corrections for highlighting specific to the Bash mode
+if exists('b:is_bash')
+
+ " I don't like bashAdminStatement; these are not keywords, they're just
+ " strings for init scripts.
+ syntax clear bashAdminStatement
+
+ " Reduce bashStatement down to just builtins; highlighting 'grep' is not
+ " very useful. This list was taken from `compgen -A helptopic` on Bash
+ " 4.4.5.
+ syntax clear bashStatement
+ syntax keyword bashStatement
+ \ .
+ \ :
+ \ alias
+ \ bg
+ \ bind
+ \ break
+ \ builtin
+ \ caller
+ \ cd
+ \ command
+ \ compgen
+ \ complete
+ \ compopt
+ \ continue
+ \ coproc
+ \ dirs
+ \ disown
+ \ echo
+ \ enable
+ \ eval
+ \ exec
+ \ exit
+ \ false
+ \ fc
+ \ fg
+ \ function
+ \ getopts
+ \ hash
+ \ help
+ \ history
+ \ jobs
+ \ kill
+ \ let
+ \ logout
+ \ mapfile
+ \ popd
+ \ printf
+ \ pushd
+ \ pwd
+ \ read
+ \ readarray
+ \ readonly
+ \ return
+ \ select
+ \ set
+ \ shift
+ \ shopt
+ \ source
+ \ suspend
+ \ test
+ \ time
+ \ times
+ \ trap
+ \ true
+ \ type
+ \ ulimit
+ \ umask
+ \ unalias
+ \ until
+ \ variables
+ \ wait
+endif
diff --git a/zsh/zshrc b/zsh/zshrc
index 2376e568..6edcdb67 100644
--- a/zsh/zshrc
+++ b/zsh/zshrc
@@ -7,6 +7,13 @@ HISTFILE=$HOME/.zsh_history
SAVEHIST=$((1 << 12))
HISTSIZE=$((1 << 10))
+# Set a SHLVL-derived value that takes tmux into account. This is used to show
+# the current SHLVL in the prompt
+if [[ -n $TMUX && -z $TMUX_SHLVL ]] ; then
+ TMUX_SHLVL=$((SHLVL - 1))
+ export TMUX_SHLVL
+fi
+
# Load POSIX shell startup files and then Bash-specific ones
for sh in "$ENV" "$HOME"/.zshrc.d/*.zsh ; do
[[ -e $sh ]] && source "$sh"
diff --git a/zsh/zshrc.d/prompt.zsh b/zsh/zshrc.d/prompt.zsh
index ca5ecf3e..0ac2be55 100644
--- a/zsh/zshrc.d/prompt.zsh
+++ b/zsh/zshrc.d/prompt.zsh
@@ -32,6 +32,13 @@ prompt() {
# Add terminating "$" or "#" sign
PS1=$PS1'%#'
+ # Add > signs at the front of the prompt to show the current shell
+ # level, taking tmux sessions into account
+ local shlvl
+ for ((shlvl = SHLVL - TMUX_SHLVL; shlvl > 1; shlvl--)) ; do
+ PS1='>'$PS1
+ done
+
# Bold and color the prompt if it looks like we can
if (( $({ tput colors || tput Co ; } 2>/dev/null) >= 8 )) ; then
PS1='%B%F{green}'$PS1'%f%b'