diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2017-01-06 20:10:53 +1300 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2017-01-06 20:10:53 +1300 |
commit | 22fbdf236c33644eb1f80d9058a0a7e35def1d74 (patch) | |
tree | 960c8ee85e03183022eb54e236745d4126fd1322 | |
parent | Merge branch 'master' into port/bsd/dragonfly-bsd (diff) | |
parent | Rename keyboard trap func less ambiguously (diff) | |
download | dotfiles-22fbdf236c33644eb1f80d9058a0a7e35def1d74.tar.gz dotfiles-22fbdf236c33644eb1f80d9058a0a7e35def1d74.zip |
Merge branch 'master' into port/bsd/dragonfly-bsd
76 files changed, 817 insertions, 134 deletions
@@ -1,11 +1,19 @@ +bin/brnl bin/csmw bin/ddup bin/gwp +bin/jfp bin/han +bin/htdec +bin/htenc +bin/max bin/mean bin/med bin/mftl +bin/min bin/mode +bin/nlbr +bin/onl bin/rfct bin/rndi bin/sd2u @@ -17,6 +25,7 @@ bin/unf bin/uts games/acq games/aesth +games/chkl games/drakon games/kvlt games/rot13 diff --git a/IDEAS.markdown b/IDEAS.markdown new file mode 100644 index 00000000..7f94b027 --- /dev/null +++ b/IDEAS.markdown @@ -0,0 +1,10 @@ +Ideas +===== + +* I can probably share my psql() completions/shortcuts after sanitizing them + a bit +* sxhkd(1) might be nicer than xbindkeys; it's in Debian Testing now +* Wouldn't be too hard to add some HTTP BASIC auth to ix(1df) to make pastes + manageable +* Have eds(1df) accept stdin with the "starting content" for the script +* Convert all the manual pages to mandoc maybe? <https://en.wikipedia.org/wiki/Mandoc> diff --git a/ISSUES.markdown b/ISSUES.markdown index 5d9d43d3..383d7906 100644 --- a/ISSUES.markdown +++ b/ISSUES.markdown @@ -7,20 +7,14 @@ Known issues * 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 naive (don't +* 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 -* I can probably share my psql() completions/shortcuts after sanitizing them - a bit -* sxhkd(1) might be nicer than xbindkeys; it's in Debian Testing now -* dr(1df) is probably more practical in awk -* How come commands I fix with the fc builtin always seem to exit 1 even if - they succeed? Did I do that or is it Bash? -* Wouldn't be too hard to add some HTTP BASIC auth to ix(1df) to make pastes - manageable -* On non-OBSD pdksh and mksh, !! comes out as literal !! after subshell - expansion; a version switch might be necessary +* dr(6df) is probably more practical in awk * Running the block of git(1) commands in the prompt leaves five "stale" jobspecs around that flee after a jobs builtin run; only saw this manifest after 90dcadf; either I understand job specs really poorly or this may be a bug in bash +* I can't find a clean way of detecting a restricted shell for ksh instances + to prevent trying to load anything fancy (works for Bash) + * Zsh, either! $options[restricted] is "off" within the startup file @@ -50,6 +50,7 @@ check-sh \ check-urxvt \ check-yash \ + check-zsh \ lint \ lint-bash \ lint-bin \ @@ -66,14 +67,22 @@ EMAIL := tom@sanctum.geek.nz KEY := 0xC14286EA77BB8872 SENDMAIL := msmtp -all : bin/csmw \ +BINS = bin/brnl \ + bin/csmw \ bin/ddup \ bin/gwp \ bin/han \ + bin/htdec \ + bin/htenc \ + bin/jfp \ + bin/max \ bin/mean \ bin/med \ bin/mftl \ + bin/min \ bin/mode \ + bin/nlbr \ + bin/onl \ bin/rfct \ bin/rndi \ bin/sd2u \ @@ -83,35 +92,22 @@ all : bin/csmw \ bin/tot \ bin/unf \ bin/uts \ - git/gitconfig \ - gnupg/gpg.conf + +GAMES = games/acq \ + games/aesth \ + games/chkl \ + games/drakon \ + games/kvlt \ + games/rot13 \ + games/strik \ + games/zs + +all : $(BINS) git/gitconfig gnupg/gpg.conf clean distclean : rm -f \ - bin/csmw \ - bin/ddup \ - bin/gwp \ - bin/han \ - bin/mean \ - bin/med \ - bin/mftl \ - 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 \ + $(BINS) \ + $(GAMES) \ git/gitconfig \ gnupg/gpg.conf \ man/man7/dotfiles.7df \ @@ -192,9 +188,7 @@ install-bash-completion : install-bash install -pm 0644 -- bash/bash_completion "$(HOME)"/.config/bash_completion 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/sec bin/slsf bin/su2d bin/tot \ - bin/unf bin/uts install-bin-man +install-bin : $(BINS) install-bin-man install -m 0755 -d -- "$(HOME)"/.local/bin for name in bin/* ; do \ [ -x "$$name" ] || continue ; \ @@ -224,8 +218,7 @@ 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/strik games/zs check-games install-games-man +install-games : $(GAMES) install-games-man install -m 0755 -d -- "$(HOME)"/.local/games for name in games/* ; do \ [ -x "$$name" ] || continue ; \ @@ -388,8 +381,11 @@ install-yash : check-yash install-sh install -pm 0644 -- yash/yashrc "$(HOME)"/.yashrc install -pm 0644 -- yash/yashrc.d/* "$(HOME)"/.yashrc.d -install-zsh : install-sh - install -m 0755 -d -- "$(HOME)"/.zshrc.d +install-zsh : check-zsh install-sh + install -m 0755 -d -- \ + "$(HOME)"/.profile.d \ + "$(HOME)"/.zshrc.d + install -pm 0644 -- zsh/profile.d/* "$(HOME)"/.profile.d install -pm 0644 -- zsh/zprofile "$(HOME)"/.zprofile install -pm 0644 -- zsh/zshrc "$(HOME)"/.zshrc install -pm 0644 -- zsh/zshrc.d/* "$(HOME)"/.zshrc.d @@ -404,10 +400,10 @@ check : check-bash \ check-bash : check/bash -check-bin : +check-bin : $(BINS) check/bin -check-games : +check-games : $(GAMES) check/games check-man : @@ -425,6 +421,9 @@ check-urxvt : check-yash : check/yash +check-zsh : + check/zsh + lint : check \ lint-bash \ lint-bin \ @@ -437,10 +436,10 @@ lint : check \ lint-bash : lint/bash -lint-bin : +lint-bin : $(BINS) lint/bin -lint-games : +lint-games : $(GAMES) lint/games lint-ksh : diff --git a/README.markdown b/README.markdown index 109af073..97823267 100644 --- a/README.markdown +++ b/README.markdown @@ -164,10 +164,27 @@ 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. +* Four functions for using a "marked" directory, which I find a more + manageable concept than the `pushd`/`popd` directory stack: + * `md()` marks a given (or the current) directory. + * `gd()` goes to the marked directory. + * `pmd()` prints the marked directory. + * `xd()` swaps the current and marked directories. +* Nine other directory management and navigation functions: + * `ad()` is a `cd` shortcut accepting targets like `/u/l/b` for + `/usr/local/bin`, as long as they are unique. + * `bd()` changes into a named ancestor of the current directory. + * `mkcd()` creates a directory and changes into it. + * `pd()` changes to the argument's parent directory. + * `rd()` replaces the first instance of its first argument with its + second argument in `$PWD`, emulating a feature of the Zsh `cd` builtin + that I like. + * `scr()` creates a temporary directory and changes into it. + * `sd()` changes into a sibling of the current directory. + * `ud()` changes into an indexed ancestor of a directory. + * `vr()` tries to change to the root directory of a source control + repository. * `bc()` silences startup messages from GNU `bc(1)`. -* `bd()` changes into a named ancestor of the current directory. * `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, @@ -181,31 +198,22 @@ in `sh/shrc.d` to be loaded by any POSIX interactive shell. Those include: * `ls()` tries to apply color to `ls(1)` for interactive use if available. * `la()` runs `ls -A` if it can, or `ls -a` otherwise. * `ll()` runs `ls -Al` if it can, or `ls -al` otherwise. -* `mkcd()` creates a directory and changes into it. * `mysql()` allows shortcuts to MySQL configuration files stored in `~/.mysql`. * `path()` manages the contents of `PATH` conveniently. -* `pd()` changes to the argument's parent directory. -* `rd()` replaces the first instance of its first argument with its second - argument in `$PWD`, emulating a feature of the Zsh `cd` builtin that I - like. * `scp()` tries to detect forgotten hostnames in `scp(1)` command calls. -* `scr()` creates a temporary directory and changes into it. -* `sd()` changes into a sibling of the current directory. * `sudo()` forces `-H` for `sudo(8)` calls so that `$HOME` is never preserved; I hate having `root`-owned files in my home directory. * `tmux()` changes the default command for `tmux(1)` to `attach-session -d` if a session exists, or creates a new session if one doesn't. * `tree()` colorizes GNU `tree(1)` output if possible (without having `LS_COLORS` set). -* `ud()` changes into an indexed ancestor of a directory. * `vim()` defines three functions to always use `vim(1)` as my `ex(1)`, `vi(1)` and `view(1)` implementation if it's available. -* `vr()` tries to change to the root directory of a source control - repository. * `x()` is a one-key shortcut for `exec startx`. -There are a few other little tricks defined for other shells: +There are a few other little tricks defined for other shells providing +non-POSIX features, as compatibility allows: * `keep()` stores ad-hoc shell functions and variables (Bash, Korn Shell 93, Z shell). @@ -394,17 +402,23 @@ Installed by the `install-bin` target: * `unf(1df)` joins lines with leading spaces to the previous line. Intended for unfolding HTTP headers, but it should work for most RFC 822 formats. -* Four simple aggregators for numbers: +* Six simple aggregators for numbers: + * `max(1df)` prints the maximum. * `mean(1df)` prints the mean. * `med(1df)` prints the median. + * `min(1df)` prints the minimum. * `mode(1df)` prints the first encountered mode. * `tot(1df)` totals the set. +* Two quick-and-dirty HTML text node content encoding tools: + * `htenc(1df)` encodes. + * `htdec(1df)` decodes. * `ap(1df)` reads arguments for a given command from the standard input, prompting if appropriate * `apf(1df)` prepends arguments to a command with ones read from a file, intended as a framework for shell wrappers or functions. * `ax(1df)` 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. +* `bcq(1df)` runs `bc(1)`, quieting it down if need be. * `bel(1df)` prints a terminal bell character. * `bl(1df)` generates a given number of blank lines. * `bp(1df)` runs `br(1df)` after prompting for an URL @@ -450,6 +464,7 @@ Installed by the `install-bin` target: repository. * `ix(1df)` posts its input to the ix.io pastebin. * `jfc(1df)` adds and commits lazily to a Git repository. +* `jfp(1df)` prints its input, excluding any shebang on the first line only. * `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)`. @@ -461,7 +476,10 @@ Installed by the `install-bin` target: * `mkcp(1df)` creates a directory and copies preceding arguments into it. * `mkmv(1df)` creates a directory and moves preceding arguments into it. * `motd(1df)` shows the system MOTD. +* `onl(1df)` crunches input down to one printable line. * `pa(1df)` prints its arguments, one per line. +* `pp(1df)` prints the full path of each argument using `$PWD`. +* `pph(1df)` runs `pp(1df)` and includes a leading `$HOSTNAME:`. * `paz(1df)` print its arguments terminated by NULL chars. * `pit(1df)` runs its input through a pager if its standard output looks like a terminal. @@ -479,6 +497,8 @@ Installed by the `install-bin` target: * `sue(8df)` execs `sudoedit(8)` as the owner of all the file arguments given, perhaps in cases where you may not necessarily have `root` `sudo(8)` privileges. +* `swr(1df)` allows you to run commands locally specifying remote files in + `scp(1)`'s HOST:PATH format. * `td(1df)` manages a to-do file for you with `$EDITOR` and `git(1)`; I used to use Taskwarrior, but found it too complex and buggy. * `try(1df)` repeats a command up to a given number of times until it diff --git a/X/xbindkeysrc b/X/xbindkeysrc index ee58eb35..00855b09 100644 --- a/X/xbindkeysrc +++ b/X/xbindkeysrc @@ -27,3 +27,6 @@ "exec amixer -q sset Master 5%- unmute" XF86AudioLowerVolume + +"exec urxvtcd -e bcq" + XF86Calculator diff --git a/bash/bash_completion.d/md.bash b/bash/bash_completion.d/md.bash new file mode 100644 index 00000000..6193efe5 --- /dev/null +++ b/bash/bash_completion.d/md.bash @@ -0,0 +1,2 @@ +# Completion for md() +complete -A directory md diff --git a/bash/bashrc b/bash/bashrc index 088182ef..3070c00c 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -4,6 +4,11 @@ case $- in *) return ;; esac +# Don't do anything if restricted, not even sourcing the ENV file +# Testing $- for "r" doesn't work +# shellcheck disable=SC2128 +[ -n "$BASH_VERSINFO" ] && shopt -q restricted_shell && return + # If ENV is set, source it to get all the POSIX-compatible interactive stuff; # we should be able to do this even if we're running a truly ancient Bash [ -n "$ENV" ] && . "$ENV" @@ -17,9 +22,6 @@ esac ((10#${BASH_VERSINFO[1]%%[!0-9]*} < 5)) && return -# Don't do anything if running a restricted shell -shopt -q restricted_shell && return - # Keep around 32K lines of history in file HISTFILESIZE=$((1 << 15)) diff --git a/bin/bcq b/bin/bcq new file mode 100755 index 00000000..7b950b56 --- /dev/null +++ b/bin/bcq @@ -0,0 +1,4 @@ +#!/bin/sh +# Fire up bc(1), hushing it if it looks like GNU +[ -e "$HOME"/.cache/bc/quiet ] && set -- --quiet "$@" +exec bc "$@" diff --git a/bin/brnl.sed b/bin/brnl.sed new file mode 100644 index 00000000..00a33d80 --- /dev/null +++ b/bin/brnl.sed @@ -0,0 +1,2 @@ +# Remove HTML linebreaks from content (undo nlbr(1df)) +s_<br>$__ @@ -1,7 +1,10 @@ #!/bin/sh # Record a timestamped message to a logfile, defaulting to ~/.clog +self=clog +command -v rlwrap >/dev/null 2>&1 && + set -- rlwrap -C "$self" "$@" { date - cat - + "$@" cat - printf '%s\n' -- } >>"${CLOG:-"$HOME"/.clog}" diff --git a/bin/gwp.awk b/bin/gwp.awk index 32fe97f2..976b5b84 100644 --- a/bin/gwp.awk +++ b/bin/gwp.awk @@ -23,7 +23,7 @@ BEGIN { # Bailout function function fail(str) { - printf "%s: %s\n", self, str > "/dev/stderr" + printf "%s: %s\n", self, str | "cat >&2" exit(1) } diff --git a/bin/htdec.sed b/bin/htdec.sed new file mode 100644 index 00000000..6f9d6977 --- /dev/null +++ b/bin/htdec.sed @@ -0,0 +1,4 @@ +# Quick-and-dirty HTML text decoding +s_\<_<_g +s_\>_>_g +s_\&_\&_g diff --git a/bin/htenc.sed b/bin/htenc.sed new file mode 100644 index 00000000..0429b525 --- /dev/null +++ b/bin/htenc.sed @@ -0,0 +1,4 @@ +# Quick-and-dirty HTML text encoding +s_&_\&_g +s_<_\<_g +s_>_\>_g diff --git a/bin/jfp.sed b/bin/jfp.sed new file mode 100644 index 00000000..938c4e4d --- /dev/null +++ b/bin/jfp.sed @@ -0,0 +1,4 @@ +#!/bin/sed -f +1 { + /^\#\!/d +} @@ -1,10 +1,19 @@ #!/bin/sh +# Convenience find(1) wrapper for path substrings + +# Require at least one search term if [ "$#" -eq 0 ] ; then printf >&2 'loc: Need a search term\n' exit 2 fi + +# Iterate through each search term and run an appropriate find(1) command for pat ; do + + # Skip dotfiles, dotdirs, and symbolic links; print anything that matches + # the term as a substring (and stop iterating through it) find . \ -name .\* ! -name . -prune -o \ + -type l -prune -o \ -name \*"$pat"\* -prune -print done diff --git a/bin/max.awk b/bin/max.awk new file mode 100644 index 00000000..11d4efd9 --- /dev/null +++ b/bin/max.awk @@ -0,0 +1,10 @@ +# Get the maximum of a list of numbers +{ + if (NR == 1 || $1 > max) + max = $1 +} +END { + if (!NR) + exit(1) + print max +} diff --git a/bin/med.awk b/bin/med.awk index 8167f8dd..aee120cb 100644 --- a/bin/med.awk +++ b/bin/med.awk @@ -1,7 +1,7 @@ # Get the median of a list of numbers { vals[NR] = $1 } NR > 1 && vals[NR] < vals[NR-1] && !warn++ { - printf "med: Input not sorted!\n" > "/dev/stderr" + printf "med: Input not sorted!\n" | "cat >&2" } END { # Error out if we read no values at all diff --git a/bin/min.awk b/bin/min.awk new file mode 100644 index 00000000..c58a3a8f --- /dev/null +++ b/bin/min.awk @@ -0,0 +1,10 @@ +# Get the minimum of a list of numbers +{ + if (NR == 1 || $1 < min) + min = $1 +} +END { + if (!NR) + exit(1) + print min +} diff --git a/bin/nlbr.sed b/bin/nlbr.sed new file mode 100644 index 00000000..6ba1a3ef --- /dev/null +++ b/bin/nlbr.sed @@ -0,0 +1,2 @@ +# Add HTML linebreaks to content +s_$_<br>_ diff --git a/bin/onl.awk b/bin/onl.awk new file mode 100644 index 00000000..466b8451 --- /dev/null +++ b/bin/onl.awk @@ -0,0 +1,15 @@ +# Flatten input into one single-space separated line with no unprintable chars + +# For each line of input ... +{ + # Strip out non-printable characters and rebuild the fields + gsub(/[[:cntrl:]]/, "") + + # Print each field, without a newline; add a leading space if it's not the + # very first one + for (i = 1; i <= NF; i++) + printf (f++) ? OFS "%s" : "%s", $i +} + +# Print a newline to close the line +END { print "" } @@ -0,0 +1,9 @@ +#!/bin/sh +# Print the full path to each argument; path need not exist +for arg ; do + case $arg in + /*) path=$arg ;; + *) path=$PWD/$arg ;; + esac + printf '%s\n' "$path" +done diff --git a/bin/pph b/bin/pph new file mode 100755 index 00000000..684aaafd --- /dev/null +++ b/bin/pph @@ -0,0 +1,5 @@ +#!/bin/sh +# Run pp(1df) on args, prefix with machine hostname +hostname=$(hostname -s) || exit +pp "$@" | +sed 's_^_'"$hostname":'_' @@ -2,6 +2,6 @@ # Shortcut to generate just one strong password with pwgen(1) # If any arguments are provided, those are used instead if [ "$#" -eq 0 ] ; then - set -- --secure -- "${PWGEN_LENGTH:-15}" "${PWGEN_COUNT:-1}" + set -- --secure -- "${PWGEN_LENGTH:-16}" "${PWGEN_COUNT:-1}" fi pwgen "$@" diff --git a/bin/rfct.awk b/bin/rfct.awk index 2f0cc42d..256841a7 100644 --- a/bin/rfct.awk +++ b/bin/rfct.awk @@ -1,8 +1,16 @@ # Format an RFC in text format for terminal reading # A record is a paragraph -BEGIN { RS = "" } +BEGIN { + RS = "" + ORS = "\n\n" +} -# 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 } +{ + # Strip out control characters, except tab and newline + gsub(/[^[:print:]\n\t]/, "") + + # If there's anything left, print it + if (length) + print +} @@ -36,7 +36,7 @@ lc=$(sed -- '$=;d' "$@") || exit # If there were none, bail case $lc in ''|0) - printf 2>&1 'rndl: No lines found on input\n' + printf >&2 'rndl: No lines found on input\n' exit 2 ;; esac diff --git a/bin/sec.awk b/bin/sec.awk index 3ebf02b6..001b017d 100644 --- a/bin/sec.awk +++ b/bin/sec.awk @@ -6,7 +6,7 @@ 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" + print "sec: Bad format" | "cat >&2" err = 1 next } @@ -7,9 +7,9 @@ if [ -z "$SSH_CONNECTION" ] ; then exit 1 fi -# Print the two variables into a subshell so we can chop them up with read -printf '%s\n' "$SSH_CONNECTION" "${SSH_TTY:-unknown}" | ( - +# Print the two variables into a compound command so we can `read` them +printf '%s\n' "$SSH_CONNECTION" "${SSH_TTY:-unknown}" | +{ # Read connection details from first line read -r ci cp si sp @@ -25,4 +25,4 @@ printf '%s\n' "$SSH_CONNECTION" "${SSH_TTY:-unknown}" | ( "${ch:-"$ci"}" "$cp" \ "${sh:-"$si"}" "$sp" \ "$tty" -) +} @@ -3,7 +3,7 @@ # Check arguments if [ "$#" -eq 0 ] ; then - printf 2>&1 'stbl: Need a filename\n' + printf >&2 'stbl: Need a filename\n' exit 2 fi diff --git a/bin/swr b/bin/swr new file mode 100755 index 00000000..56ab5919 --- /dev/null +++ b/bin/swr @@ -0,0 +1,65 @@ +#!/bin/sh +# Transparently wrap scp(1) targets on the command line +self=swr + +# Create a temporary directory with name in $td, and handle POSIX-ish traps to +# remove it when the script exits. +td= +cleanup() { + [ -n "$td" ] && rm -fr -- "$td" + if [ "$1" != EXIT ] ; then + trap - "$1" + kill "-$1" "$$" + fi +} +for sig in EXIT HUP INT TERM ; do + # shellcheck disable=SC2064 + trap "cleanup $sig" "$sig" +done +td=$(mktd "$self") || exit + +# Set a flag to manage resetting the positional parameters at the start of the +# loop +n=1 +for arg ; do + + # If this is our first iteration, reset the shell parameters + case $n in + 1) set -- ;; + esac + + # Test whether this argument looks like a remote file + if ( + + # Test it contains a colon + case $arg in + *:*) ;; + *) exit 1 ;; + esac + + # Test the part before the first colon has at least one character and + # only hostname characters + case ${arg%%:*} in + '') exit 1 ;; + *[!a-zA-Z0-9-.]*) exit 1 ;; + esac + + ) ; then + + # Looks like a remote file request; try to copy it into the temporary + # directory, bail out completely if we can't + dst=$td/$n + scp -q -- "$arg" "$dst" || exit + set -- "$@" "$dst" + + else + # Just a plain old argument; stack it up + set -- "$@" "$arg" + fi + + # Bump n + n=$((n+1)) +done + +# Run the command with the processed arguments +exec "$@" @@ -3,7 +3,7 @@ # Check arguments if [ "$#" -eq 0 ] ; then - printf 2>&1 'xgo: At least one URL required\n' + printf >&2 'xgo: At least one URL required\n' fi # Iterate over the URL arguments diff --git a/check/zsh b/check/zsh new file mode 100755 index 00000000..39a6c1e9 --- /dev/null +++ b/check/zsh @@ -0,0 +1,6 @@ +#!/bin/sh +for zsh in zsh/* zsh/zshrc.d/* ; do + [ -f "$zsh" ] || continue + zsh -n "$zsh" || exit +done +printf 'All zsh(1) scripts parsed successfully.\n' diff --git a/finger/pgpkey b/finger/pgpkey index b228a50e..6d2cbeec 100644 --- a/finger/pgpkey +++ b/finger/pgpkey @@ -1,5 +1,5 @@ -pub 4096R/0xC14286EA77BB8872 2013-03-12 [expires: 2017-04-17] +pub 4096R/0xC14286EA77BB8872 2013-03-12 [expires: 2018-01-02] Key fingerprint = FA09 C06E 1B67 0CD0 B2F5 DE60 C142 86EA 77BB 8872 uid [ultimate] Thomas Ryder (tyrmored, tejr) <tom@sanctum.geek.nz> -sub 4096R/0x96C2CD91E67AC61D 2013-03-12 [expires: 2017-04-17] -sub 4096R/0xB5AF5F8925926609 2013-03-12 [expires: 2017-04-17] +sub 4096R/0x96C2CD91E67AC61D 2013-03-12 [expires: 2018-01-02] +sub 4096R/0xB5AF5F8925926609 2013-03-12 [expires: 2018-01-02] diff --git a/games/chkl.sed b/games/chkl.sed new file mode 100644 index 00000000..40c35cee --- /dev/null +++ b/games/chkl.sed @@ -0,0 +1,6 @@ +#!/bin/sed -f +# Change an ASCII checklist with [/], [x], and [ ] boxes to a Unicode one +/^#/d +s_^\[ \]_☐_ +s_^\[/\]_☑_ +s_^\[[xX]\]_☒_ @@ -1,7 +1,21 @@ -# Emacs-style key bindings +# Emacs-style key bindings; these are present in ksh88 and pdksh set -o braceexpand set -o emacs +# Track locations of binaries +set -o trackall + +# Use subshells to test these newer options, as ksh93 seems to get very upset +# if you try to set an option and it doesn't exist + +# Try to get "**" as a recursive glob +(set -o globstar) 2>/dev/null && + set -o globstar + +# Try to get !-style history expansion +(set -o histexpand) 2>/dev/null && + set -o histexpand + # Save history HISTFILE=$HOME/.ksh_history HISTSIZE=$((1 << 10)) diff --git a/ksh/kshrc.d/bind.ksh b/ksh/kshrc.d/bind.ksh new file mode 100644 index 00000000..34cb5f5a --- /dev/null +++ b/ksh/kshrc.d/bind.ksh @@ -0,0 +1,28 @@ +# Try to bind ^I to complete words and ^L to clear the screen +case $KSH_VERSION in + + # ksh93 is lovely, but complex; rebind ^L so it does the same as Alt-^L + *'93'*) + keybd_trap() { + # shellcheck disable=SC2154 + case ${.sh.edchar} in + $'\f') .sh.edchar=$'\e\f' ;; + esac + } + trap keybd_trap KEYBD + ;; + + # More straightforward with mksh; bind keys to the appropriate emacs mode + # editing commands + *'MIRBSD KSH'*) + bind '^I'='complete' + bind '^L'='clear-screen' + ;; + + # Similar with pdksh; there's a "complete" command, but not a "clear" one, + # so we fake it with clear(1) and some yanking + *'PD KSH'*) + bind '^I'='complete' + bind -m '^L'='^Uclear^J^Y' + ;; +esac diff --git a/ksh/kshrc.d/keep.ksh b/ksh/kshrc.d/keep.ksh index c57c41ce..0451fa68 100644 --- a/ksh/kshrc.d/keep.ksh +++ b/ksh/kshrc.d/keep.ksh @@ -142,7 +142,6 @@ EOF # Otherwise the user must want us to print all the NAMEs kept ( - typeset keep for keep in "$kshkeep"/*.ksh ; do [[ -f "$keep" ]] || break keep=${keep##*/} diff --git a/ksh/kshrc.d/prompt.ksh b/ksh/kshrc.d/prompt.ksh index add96b2a..84129efc 100644 --- a/ksh/kshrc.d/prompt.ksh +++ b/ksh/kshrc.d/prompt.ksh @@ -136,8 +136,28 @@ function prompt { state=${state}'>' # Tracked files are modified - git diff-files --no-ext-diff --quiet || - state=${state}'!!' + if ! git diff-files --no-ext-diff --quiet ; then + + # Different ksh flavours process a bang in PS1 after prompt + # parameter expansion in different ways + case $KSH_VERSION in + + # ksh93 requires a double-bang to escape it + (*'93'*) state=${state}'!!' ;; + + # OpenBSD's pdksh requires a double-bang too, but its + # upstream does not + (*'PD KSH'*) + case $OS in + ('OpenBSD') state=${state}'!!' ;; + (*) state=${state}'!' ;; + esac + ;; + + # Everything else should need only one bang + (*) state=${state}'!' ;; + esac + fi # Changes are staged git diff-index --cached --no-ext-diff --quiet HEAD || @@ -192,6 +212,7 @@ function prompt { # Show the count of background jobs in curly brackets, if not zero job) + # shellcheck disable=SC2154 ((jobc)) && printf '{%u}' "$jobc" ;; @@ -1,2 +1,2 @@ #!/bin/sh -find bash -type f -print -exec shellcheck -e SC1090 -s bash -- {} \; +find bash -type f -print -exec shellcheck -e SC1090 -s bash -- {} + @@ -1,2 +1,4 @@ #!/bin/sh -find ksh -type f -print -exec shellcheck -e SC1090 -s ksh -- {} \; +find ksh \ + -type f -name '*.sh' -exec shellcheck -e SC1090 -s sh -- {} + -o \ + -type f -exec shellcheck -e SC1090 -s ksh -- {} + @@ -1,2 +1,2 @@ #!/bin/sh -find sh -type f -print -exec shellcheck -e SC1090 -s sh -- {} \; +find sh -type f -print -exec shellcheck -e SC1090 -s sh -- {} + @@ -1,2 +1,2 @@ #!/bin/sh -find yash -type f -print -exec shellcheck -e SC1090 -s sh -- {} \; +find yash -type f -print -exec shellcheck -e SC1090 -s sh -- {} + diff --git a/man/man1/apf.1df b/man/man1/apf.1df index 6f8804e0..025af010 100644 --- a/man/man1/apf.1df +++ b/man/man1/apf.1df @@ -44,7 +44,7 @@ We could do this: b c .P -We could then make a permanent wrapper script in two line: +We could then make a permanent wrapper script in two lines: .P $ cat >~/.local/bin/printargs #!/bin/sh @@ -52,7 +52,7 @@ We could then make a permanent wrapper script in two line: ^D $ chmod +x ~/.local/bin/printargs .P -Or just a shell function, if it's needed interactively: +Or just a shell function, if it's only wanted for interactive shells: .P $ printargs() { apf "$HOME"/.printargsrc printargs "$@" ; } .P diff --git a/man/man1/bcq.1df b/man/man1/bcq.1df new file mode 100644 index 00000000..55b44a69 --- /dev/null +++ b/man/man1/bcq.1df @@ -0,0 +1,13 @@ +.TH BCQ 1df "January 2017" "Manual page for bcq" +.SH NAME +.B bcq +\- run bc(1), quieting it if need be +.SH SYNOPSIS +.B bcq +.SH DESCRIPTION +.B bcq +starts bc(1), checking ~/.cache/bc/quiet to see if a --quiet option is +available, adding it if so to elide the annoying GNU boilerplate for an +interactive session. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/brnl.1df b/man/man1/brnl.1df new file mode 100644 index 00000000..e15eadce --- /dev/null +++ b/man/man1/brnl.1df @@ -0,0 +1,20 @@ +.TH BRNL 1df "January 2017" "Manual page for brnl" +.SH NAME +.B brnl +\- strip trailing HTML linebreaks +.SH SYNOPSIS +.B brnl +FILE +.br +.B brnl +FILE1 FILE... +.br +command | +.B brnl +.SH DESCRIPTION +.B brnl +strips trailing HTML linebreaks (<br>) from content. It reverses nlbr(1df). +.SH SEE ALSO +htenc(1df), htdec(1df), nlbr(1df) +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/clog.1df b/man/man1/clog.1df index a0e6d117..0e3e7b87 100644 --- a/man/man1/clog.1df +++ b/man/man1/clog.1df @@ -13,5 +13,8 @@ getting real tired of all this overengineering 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. +,P +If rlwrap(1) is found, it will be used for the line editing. If not, just the +terminal's cooked mode will be used. .SH AUTHOR Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/htdec.1df b/man/man1/htdec.1df new file mode 100644 index 00000000..0242312a --- /dev/null +++ b/man/man1/htdec.1df @@ -0,0 +1,18 @@ +.TH HTDEC 1df "January 2017" "Manual page for htdec" +.SH NAME +.B htdec +\- quick-and-dirty HTML to text decoding for text nodes +.SH SYNOPSIS +.B htdec +/usr/share/lovecraft/the-silver-key.html > the-silver-key.txt +.br +curl http://example.com/netstat.html | +.B htdec +.SH DESCRIPTION +.B htdec +unescapes left angle quotes, right angle quotes, and ampersands to put HTML +text node content into plain text. +.SH SEE ALSO +htenc(1df), brnl(1df), nlbr(1df) +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/htenc.1df b/man/man1/htenc.1df new file mode 100644 index 00000000..24093f8c --- /dev/null +++ b/man/man1/htenc.1df @@ -0,0 +1,18 @@ +.TH HTENC 1df "January 2017" "Manual page for htenc" +.SH NAME +.B htenc +\- quick-and-dirty text to HTML encoding for text nodes +.SH SYNOPSIS +.B htenc +/usr/share/lovecraft/the-silver-key.txt > the-silver-key.html +.br +netstat -aunt | +.B htenc > netstat.html +.SH DESCRIPTION +.B htenc +escapes ampersands, left angle quotes, and right angle quotes to put text into +a form that should be suitable to include in XML/HTML text nodes. +.SH SEE ALSO +htdec(1df), brnl(1df), nlbr(1df) +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/jfp.1df b/man/man1/jfp.1df new file mode 100644 index 00000000..6e9e18f2 --- /dev/null +++ b/man/man1/jfp.1df @@ -0,0 +1,39 @@ +.TH JFP 1df "January 2017" "Manual page for jfp" +.SH NAME +.B jfp +\- print input, excluding any shebang on the first line +.SH SYNOPSIS +.B jfp +FILE [FILE2 ...] +.br +.B jfp +< FILE +.P +#!/usr/bin/env jfp +.br +All this content will be printed verbatim, +.br +except for the first line, +.br +because it's a shebang. +.SH DESCRIPTION +.B jfp +prints all the input given to it except for the first line if it starts with a +shebang "#!". This means it can be used in a shebang to simply echo the entire +remaining contents of the script. +.SH NOTES +Can you guess what it stands for? +.P +A portable way to do the same thing could be using a heredoc with cat(1): +.P + #!/bin/sh + cat <<'EOD' + All this content will be printed verbatim... + ...except for the first line... + ...because it's a shebang. + EOD +.P +The only snag there is having to check the token word doesn't appear in the +document. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/loc.1df b/man/man1/loc.1df index 2c3c3a80..a70d2d9b 100644 --- a/man/man1/loc.1df +++ b/man/man1/loc.1df @@ -9,7 +9,8 @@ PATTERN1 [PATTERN2...] .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 recurse further into a directory if it matches -the terms. It is intended only for interactive use as a shortcut. +It skips dotfiles and symbolic links, and doesn't recurse 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/max.1df b/man/man1/max.1df new file mode 100644 index 00000000..28431da4 --- /dev/null +++ b/man/man1/max.1df @@ -0,0 +1,19 @@ +.TH MAX 1df "September 2016" "Manual page for max" +.SH NAME +.B max +\- print the maximum of a list of numbers +.SH SYNOPSIS +printf '%u\\n' 3 55 17 | +.B max +.br +.B max +file +.br +.B max +file1 file2 +.SH DESCRIPTION +.B max +collects all the newline-delimited numbers given as input, and prints the +maximum. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/min.1df b/man/man1/min.1df new file mode 100644 index 00000000..e3279d3b --- /dev/null +++ b/man/man1/min.1df @@ -0,0 +1,19 @@ +.TH MIN 1df "September 2016" "Manual page for min" +.SH NAME +.B min +\- print the minimum of a list of numbers +.SH SYNOPSIS +printf '%u\\n' 182 2 22 | +.B min +.br +.B min +file +.br +.B min +file1 file2 +.SH DESCRIPTION +.B min +collects all the newline-delimited numbers given as input, and prints the +minimum. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/nlbr.1df b/man/man1/nlbr.1df new file mode 100644 index 00000000..3cdde6c1 --- /dev/null +++ b/man/man1/nlbr.1df @@ -0,0 +1,21 @@ +.TH NLBR 1df "January 2017" "Manual page for nlbr" +.SH NAME +.B nlbr +\- add trailing HTML linebreaks +.SH SYNOPSIS +.B nlbr +FILE +.br +.B nlbr +FILE1 FILE... +.br +command | +.B nlbr +.SH DESCRIPTION +.B nlbr +adds trailing HTML linebreaks (<br>) to content. Good for running after +htenc(1df). +.SH SEE ALSO +htenc(1df), htdec(1df), brnl(1df) +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/onl.1df b/man/man1/onl.1df new file mode 100644 index 00000000..7e84b9ba --- /dev/null +++ b/man/man1/onl.1df @@ -0,0 +1,18 @@ +.TH ONL 1df "January 2017" "Manual page for onl" +.SH NAME +.B onl +\- force standard input onto one printable line +.SH USAGE +.B onl +FILE1 [FILE2 ...] +.br +.B onl +< FILE +.br +program | +.B onl +.SH DESCRIPTION +Condense input down to one line with only printable characters, no leading or +trailing spaces, and a single space in place of multiple spaces or tabs. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/pp.1df b/man/man1/pp.1df new file mode 100644 index 00000000..322e7b48 --- /dev/null +++ b/man/man1/pp.1df @@ -0,0 +1,19 @@ +.TH PP 1df "January 2017" "Manual page for pp" +.SH NAME +.B pp +\- print the full path to each argument +.SH SYNOPSIS +.B pp +/arg arg2 ./arg3 +.SH DESCRIPTION +.B pp +uses $PWD to print the full path to each of its arguments, unless they begin +with a slash, in which case they are printed verbatim. +.P +The path need not actually exist. +.SH CAVEATS +Newlines in filenames will still work, but the results won't really make sense +as they'll be indistinguishable from newlines separating the files. This is for +generating human-readable file lists, not for machines. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/pph.1df b/man/man1/pph.1df new file mode 100644 index 00000000..0ad98fc5 --- /dev/null +++ b/man/man1/pph.1df @@ -0,0 +1,16 @@ +.TH PPH 1df "January 2017" "Manual page for pp" +.SH NAME +.B pp +\- print the full path to each argument, hostname prepended +.SH SYNOPSIS +.B pp +/arg arg2 ./arg3 +.SH DESCRIPTION +.B pph +runs pp(1df) on the arguments to print the full path to each one, and also +prepends the machine's hostname and a colon to each line. +.SH CAVEATS +Newlines in filenames will mess this up. This is for generating human-readable +file lists, not for machines. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/pwg.1df b/man/man1/pwg.1df index 3bc2e777..0d1c243c 100644 --- a/man/man1/pwg.1df +++ b/man/man1/pwg.1df @@ -7,9 +7,9 @@ .SH DESCRIPTION .B pwg with no arguments runs pwgen(1) with the --secure options and arguments for one -password of fifteen alphanumeric characters, to avoid pwgen(1)'s unusual -default of generating very many relatively low-quality passwords, which is -never what the author wants. +password of 16 alphanumeric characters, to avoid pwgen(1)'s unusual default of +generating very many relatively low-quality passwords, which is never what the +author wants. .P If any arguments are provided, it simply passes these to pwgen(1) untouched. .SH AUTHOR diff --git a/man/man1/swr.1df b/man/man1/swr.1df new file mode 100644 index 00000000..8792b0ed --- /dev/null +++ b/man/man1/swr.1df @@ -0,0 +1,37 @@ +.TH SWR 1df "January 2017" "Manual page for swr" +.SH NAME +.B swr +\- run a command including remote file arguments for scp(1) retrieval +.SH SYNOPSIS +.B swr +cat remote:.ssh/authorized_keys +.br +.B swr +diff .shrc remote:.shrc +.SH DESCRIPTION +.B swr +runs the command given in its arguments, first replacing any arguments in the +form HOST:PATH with copies of the specified files as retrieved with scp(1), +copied into a temporary directory that should be removed on exit under most +circumstances. +.P +This even works for the first argument (i.e. the command), provided that it +will run on the local system once copied in. +.SH CAVEATS +This only works for simple commands; you can't put shell syntax into any of the +arguments. +.P +The whole script will stop if even one of its arguments can't be copied in, as +there's no way to tell whether it's safe to proceed without some of the data. +.P +Don't even think about using this for mission-critical cases or situations +requiring high security. It's a convenience wrapper. +.P +You may not need this at all if your shell has working command substitution and +you find its syntax clearer: +.P + diff .shrc <(ssh remote 'cat .shrc') +.SH SEE ALSO +scp(1), mktd(1df) +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man6/chkl.6df b/man/man6/chkl.6df new file mode 100644 index 00000000..091ee9ed --- /dev/null +++ b/man/man6/chkl.6df @@ -0,0 +1,32 @@ +.TH CHKL 6df "January 2017" "Manual page for chkl" +.SH NAME +.B chkl +\- format an ASCII checklist with Unicode symbols +.SH USAGE +.B chkl +.br +[/] rekt +.br +[ ] not rekt +.br +^D +.SH DESCRIPTION +.B chkl +replaces ASCII approximations for checkboxes in ticked, crossed, or blank state +at the start of lines with appropriate Unicode symbols: +.IP ☐ 0.2i +Unicode Character 'BALLOT BOX' (U+2610) +.IP ☑ +Unicode Character 'BALLOT BOX WITH CHECK' (U+2611) +.IP ☒ +Unicode Character 'BALLOT BOX WITH X' (U+2612) +.P +Lines with a leading # will be ignored. +.SH SEE ALSO +<http://www.fileformat.info/info/unicode/char/2610/index.htm> +.br +<http://www.fileformat.info/info/unicode/char/2611/index.htm> +.br +<http://www.fileformat.info/info/unicode/char/2612/index.htm> +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> @@ -12,3 +12,11 @@ if [ -f "$HOME"/.shinit ] ; then ENV=$HOME/.shinit export ENV fi + +# If ENV_FORCE is set and we're interactive, source ENV explicitly +# At the moment this is just for zsh-as-ksh/sh +if [ -n "$ENV_FORCE" ] ; then + case $- in *i*) + [ -f "$ENV" ] && . "$ENV" ;; + esac +fi diff --git a/sh/profile.d/os.sh b/sh/profile.d/os.sh new file mode 100644 index 00000000..f9d5a79b --- /dev/null +++ b/sh/profile.d/os.sh @@ -0,0 +1,3 @@ +# Store the operating system in an environment variable +OS=$(uname) +export OS diff --git a/sh/shrc.d/ad.sh b/sh/shrc.d/ad.sh index d3e5a90a..55866683 100644 --- a/sh/shrc.d/ad.sh +++ b/sh/shrc.d/ad.sh @@ -15,7 +15,7 @@ ad() { req=${1%/}/ case $req in (/*) ;; - (*) req=${PWD%/}/${req#/}/ ;; + (*) req=${PWD%/}/${req#/} ;; esac # Start building the target directory; go through the request piece by @@ -65,10 +65,13 @@ ad() { done - # Print the target - printf '%s\n' "$dir" + # Print the target with trailing slash to work around newline stripping + printf '%s/' "${dir%/}" )" + # Remove trailing slash + set -- "${1%/}" + # If the subshell printed nothing, return with failure [ -n "$1" ] || return diff --git a/sh/shrc.d/bd.sh b/sh/shrc.d/bd.sh index 1043a8cb..bf64a9aa 100644 --- a/sh/shrc.d/bd.sh +++ b/sh/shrc.d/bd.sh @@ -55,10 +55,13 @@ bd() { exit 1 fi - # Print the target - printf '%s\n' "$dirname" + # Print the target with trailing slash to work around newline stripping + printf '%s/' "${dirname%/}" )" + # Remove trailing slash + set -- "${1%/}" + # If the subshell printed nothing, return with failure [ -n "$1" ] || return diff --git a/sh/shrc.d/gd.sh b/sh/shrc.d/gd.sh new file mode 100644 index 00000000..5a3f54b0 --- /dev/null +++ b/sh/shrc.d/gd.sh @@ -0,0 +1,18 @@ +# Go to marked directory +gd() { + + # Refuse to deal with unwanted arguments + if [ "$#" -gt 0 ] ; then + printf >&2 'gd(): Unspecified argument\n' + return 2 + fi + + # Complain if mark not actually set yet + if ! [ -n "$PMD" ] ; then + printf >&2 'gd(): Mark not set\n' + return 2 + fi + + # Go to the marked directory + cd -- "$PMD" || return +} diff --git a/sh/shrc.d/md.sh b/sh/shrc.d/md.sh new file mode 100644 index 00000000..6fd3d7ca --- /dev/null +++ b/sh/shrc.d/md.sh @@ -0,0 +1,23 @@ +# Set marked directory to given dir or current dir +md() { + + # Accept up to one argument + if [ "$#" -gt 1 ] ; then + printf >&2 'md(): Too many arguments\n' + return 2 + fi + + # If first arg unset or empty, assume the user means the current dir + [ -n "$1" ] || set -- "$PWD" + + # Jump to the dir and emit PWD from a subshell to get an absolute path + set -- "$(cd -- "$1" && printf %s "$PWD")" + + # If that turned up empty, we have failed; the cd call probably threw an + # error for us too + [ -n "$1" ] || return + + # Save the specified path in the marked directory var + # shellcheck disable=SC2034 + PMD=$1 +} diff --git a/sh/shrc.d/pd.sh b/sh/shrc.d/pd.sh index de4ea23b..ce43837b 100644 --- a/sh/shrc.d/pd.sh +++ b/sh/shrc.d/pd.sh @@ -25,10 +25,13 @@ pd() { exit 1 fi - # Print the target - printf '%s\n' "$dirname" + # Print the target with trailing slash to work around newline stripping + printf '%s/' "${dirname%/}" )" + # Remove trailing slash + set -- "${1%/}" + # If the subshell printed nothing, return with failure [ -n "$1" ] || return diff --git a/sh/shrc.d/pmd.sh b/sh/shrc.d/pmd.sh new file mode 100644 index 00000000..03f18b7b --- /dev/null +++ b/sh/shrc.d/pmd.sh @@ -0,0 +1,8 @@ +# Print the marked directory +pmd() { + if ! [ -n "$PMD" ] ; then + printf >&2 'pmd(): Mark not set\n' + return 2 + fi + printf '%s\n' "$PMD" +} diff --git a/sh/shrc.d/rd.sh b/sh/shrc.d/rd.sh index 9fd99a55..3b699c0d 100644 --- a/sh/shrc.d/rd.sh +++ b/sh/shrc.d/rd.sh @@ -51,10 +51,13 @@ rd() { exit 1 fi - # Print the target - printf '%s\n' "$new" + # Print the target with trailing slash to work around newline stripping + printf '%s/' "${new%/}" )" + # Remove trailing slash + set -- "${1%/}" + # If the subshell printed nothing, return with failure [ -n "$1" ] || return diff --git a/sh/shrc.d/sd.sh b/sh/shrc.d/sd.sh index ce59bf99..4d63b7d6 100644 --- a/sh/shrc.d/sd.sh +++ b/sh/shrc.d/sd.sh @@ -72,10 +72,13 @@ sd() { ;; esac - # Print the target - printf '%s\n' "$1" + # Print the target with trailing slash to work around newline stripping + printf '%s/' "${1%/}" )" + # Remove trailing slash + set -- "${1%/}" + # If the subshell printed nothing, return with failure [ -n "$1" ] || return diff --git a/sh/shrc.d/ud.sh b/sh/shrc.d/ud.sh index 44a3a81d..79f4b5e7 100644 --- a/sh/shrc.d/ud.sh +++ b/sh/shrc.d/ud.sh @@ -34,10 +34,13 @@ ud() { exit 1 fi - # Print the target - printf '%s\n' "$dirname" + # Print the target with trailing slash to work around newline stripping + printf '%s/' "${dirname%/}" )" + # Remove trailing slash + set -- "${1%/}" + # If the subshell printed nothing, return with failure [ -n "$1" ] || return diff --git a/sh/shrc.d/xd.sh b/sh/shrc.d/xd.sh new file mode 100644 index 00000000..01b8fd3a --- /dev/null +++ b/sh/shrc.d/xd.sh @@ -0,0 +1,24 @@ +# Swap current directory with marked directory +xd() { + + # Refuse to deal with unwanted arguments + if [ "$#" -gt 0 ] ; then + printf >&2 'gd(): Unspecified argument\n' + return 2 + fi + + # Complain if mark not actually set yet + if ! [ -n "$PMD" ] ; then + printf >&2 'gd(): Mark not set\n' + return 2 + fi + + # Put the current and marked directories into positional params + set -- "$PMD" "$PWD" + + # Try to change into the marked directory + cd -- "$1" || return + + # If that worked, we can swap the mark, and we're done + PMD=$2 +} diff --git a/share/chkl.sample b/share/chkl.sample new file mode 100644 index 00000000..63076fa7 --- /dev/null +++ b/share/chkl.sample @@ -0,0 +1,5 @@ +[/] sh +[ ] bash +[ ] ksh93 +[ ] pdksh +[x] zsh diff --git a/vim/after/ftdetect/sh.vim b/vim/after/ftdetect/sh.vim index c974e293..f2bc0df2 100644 --- a/vim/after/ftdetect/sh.vim +++ b/vim/after/ftdetect/sh.vim @@ -1,4 +1,4 @@ -" Add automatic commands to +" Add automatic commands to choose shell flavours based on filename pattern augroup dfsh " Names/paths of things that are Bash shell script @@ -14,22 +14,6 @@ endif " Load plugins and indentation for file types if has('autocmd') filetype indent plugin on - - " Shortcuts to quickly switch to common file types; handy when using - " editing abstractions like sudoedit(8) - nnoremap _ap :setlocal filetype=apache<CR> - nnoremap _bi :setlocal filetype=bindzone<CR> - nnoremap _cs :setlocal filetype=css<CR> - nnoremap _ht :setlocal filetype=html<CR> - nnoremap _js :setlocal filetype=javascript<CR> - nnoremap _md :setlocal filetype=markdown<CR> - nnoremap _pl :setlocal filetype=perl<CR> - nnoremap _ph :setlocal filetype=php<CR> - nnoremap _py :setlocal filetype=python<CR> - nnoremap _rb :setlocal filetype=ruby<CR> - nnoremap _sh :setlocal filetype=sh<CR> - nnoremap _vi :setlocal filetype=vim<CR> - nnoremap _xm :setlocal filetype=xml<CR> endif " Use backup features if on a UNIX-like system and not using sudo(8) diff --git a/zsh/profile.d/zsh.sh b/zsh/profile.d/zsh.sh new file mode 100644 index 00000000..47de6d4d --- /dev/null +++ b/zsh/profile.d/zsh.sh @@ -0,0 +1,28 @@ +# Zsh before version 5.3.0 emulating POSIX sh(1) or Korn shell only sources the +# interactive shell startup file described in ENV if it's set after +# /etc/profile is sourced, but before ~/.profile is. The other shells I have +# tried (including modern shells emulating POSIX sh(1)) wait until after +# ~/.profile is read. This seems to have been fixed in Zsh commit ID fde365e, +# which was followed by release 5.3.0. + +# Is this zsh masquerading as sh/ksh? +[ -n "$ZSH_VERSION" ] || return +case $ZSH_NAME in + sh|ksh) ;; + *) return ;; +esac + +# Iterate through the zsh version number to see if it's at least 5.3.0; if not, +# we'll have ~/.profile force sourcing $ENV +if ! ( + zvs=$ZSH_VERSION + for fv in 5 3 0 ; do + zv=${zvs%%[!0-9]*} + [ "$((zv > fv))" -eq 1 ] && exit 0 + [ "$((zv < fv))" -eq 1 ] && exit 1 + zvs=${zvs#*.} + [ -n "$zvs" ] || exit 0 + done +) ; then + ENV_FORCE=1 +fi @@ -1,14 +1,16 @@ # Emacs keybindings even if EDITOR is vi(1) bindkey -e +# If ENV is set, source it to get all the POSIX-compatible interactive stuff +[[ -n $ENV ]] && source "$ENV" + # History settings setopt histignorealldups sharehistory HISTFILE=$HOME/.zsh_history SAVEHIST=$((1 << 12)) -HISTSIZE=$((1 << 10)) -# Load POSIX shell startup files and then Bash-specific ones -for sh in "$ENV" "$HOME"/.zshrc.d/*.zsh ; do +# Load Zsh-specific startup files +for sh in "$HOME"/.zshrc.d/*.zsh ; do [[ -e $sh ]] && source "$sh" done unset -v sh |