diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2018-12-14 13:14:00 +1300 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2018-12-14 13:14:00 +1300 |
commit | e0ff7ac01d6439d826ff9ef6f134a7638ade714f (patch) | |
tree | 781004065fefcbfd8e543a61de3d168383e71bde | |
parent | Merge branch 'release/v3.1.0' (diff) | |
parent | Bump VERSION (diff) | |
download | dotfiles-3.2.0.tar.gz (sig) dotfiles-3.2.0.zip |
Merge branch 'release/v3.2.0'v3.2.0
* release/v3.2.0:
Bump VERSION
Refactor some conditionals
Factor out zsh ENV hack into one file
Refactor "path list" not to require a subshell
Correct completion for deep pass(1) directories
Move filetype.vim helper funcs into autoload
Fix a local var name in openssl(1ssl) completion
Correct a variable ref in openssl(1ssl) completion
Disable shellcheck rules for missed definition
Add filenames treatment to mex(1df) completion
Remove unneeded declaration
Refactor some completions to avoid loops
Remove unneeded stdout redirect
Remove unneeded semicolon from sh "for VAR ; do"
Substitute bad `continue` for `return`
Add actual completion matching to git completion
Apply much simpler completion to Git
58 files changed, 314 insertions, 239 deletions
@@ -1,2 +1,2 @@ -tejr dotfiles v3.1.0 -Wed Dec 5 22:40:16 UTC 2018 +tejr dotfiles v3.2.0 +Fri Dec 14 00:14:00 UTC 2018 diff --git a/bash/bash_completion.d/_ssh_config_hosts.bash b/bash/bash_completion.d/_ssh_config_hosts.bash index 3f937a2a..0959f52b 100644 --- a/bash/bash_completion.d/_ssh_config_hosts.bash +++ b/bash/bash_completion.d/_ssh_config_hosts.bash @@ -1,31 +1,26 @@ # Complete ssh_config(5) hostnames _ssh_config_hosts() { - # Iterate through words from a subshell - local ci comp - while read -r comp ; do - COMPREPLY[ci++]=$comp - done < <( + # Iterate through SSH client config paths + local config + for config in "$HOME"/.ssh/config /etc/ssh/ssh_config ; do + [[ -e $config ]] || continue - # Iterate through SSH client config paths - for config in "$HOME"/.ssh/config /etc/ssh/ssh_config ; do - [[ -e $config ]] || continue + # Read 'Host' options and their first value from file + local option value ci + while read -r option value _ ; do + [[ $option == Host ]] || continue - # Read 'Host' options and their first value from file - while read -r option value _ ; do - [[ $option == Host ]] || continue + # Check host value + case $value in + # No empties + '') ;; + # No wildcards + *'*'*) ;; + # Found a match; print it + "$2"*) COMPREPLY[ci++]=$value ;; + esac - # Check host value - case $value in - # No empties - ('') ;; - # No wildcards - (*'*'*) ;; - # Found a match; print it - ("$2"*) printf '%s\n' "$value" ;; - esac - - done < "$config" - done - ) + done < "$config" + done } diff --git a/bash/bash_completion.d/git.bash b/bash/bash_completion.d/git.bash new file mode 100644 index 00000000..c3a4d49c --- /dev/null +++ b/bash/bash_completion.d/git.bash @@ -0,0 +1,41 @@ +# Complete Git with branch names or tag names if specific keys are used, but +# fall back on filenames otherwise; it's too complicated to be worth trying to +# do it all contextually + +# Requires Bash >=4.0 for COMP_KEY +((BASH_VERSINFO[0] >= 4)) || return + +# Define and set helper function +_git() { + + # What completion to do + case $COMP_KEY in + + # Complete with branch names if C-x,B is pressed + 98) + local ci + while read -r _ ref ; do + branch=${ref#refs/heads/} + case $branch in + "$2"*) COMPREPLY[ci++]=$branch ;; + esac + done < <(git show-ref --heads) + ;; + + # Complete with tag names if C-x,T is pressed + 116) + local ci + while read -r _ ref ; do + tag=${ref#refs/tags/} + case $tag in + "$2"*) COMPREPLY[ci++]=$tag ;; + esac + done < <(git show-ref --tags) + ;; + + # Do no completion, so we fall back on filenames + *) return 1 ;; + + esac +} +complete -F _git -o bashdefault -o default git diff --git a/bash/bash_completion.d/gpg.bash b/bash/bash_completion.d/gpg.bash index c6f92676..5a055352 100644 --- a/bash/bash_completion.d/gpg.bash +++ b/bash/bash_completion.d/gpg.bash @@ -13,14 +13,9 @@ _gpg() { # Generate completion reply from gpg(1) options local ci comp while read -r comp ; do - COMPREPLY[ci++]=$comp - done < <( - gpg --dump-options 2>/dev/null | - while read -r option ; do - case $option in - ("$2"*) printf '%s\n' "$option" ;; - esac - done - ) + case $comp in + "$2"*) COMPREPLY[ci++]=$comp ;; + esac + done < <(gpg --dump-options 2>/dev/null) } complete -F _gpg -o bashdefault -o default gpg diff --git a/bash/bash_completion.d/make.bash b/bash/bash_completion.d/make.bash index 7f8b8125..909c52fb 100644 --- a/bash/bash_completion.d/make.bash +++ b/bash/bash_completion.d/make.bash @@ -34,11 +34,11 @@ _make() { esac # Break the target up with space delimiters - declare -a targets IFS=' ' read -a targets -r \ < <(printf '%s\n' "${line%%:*}") # Short-circuit if there are no targets + # shellcheck disable=SC2154 ((${#targets[@]})) || exit # Make matches behave correctly diff --git a/bash/bash_completion.d/man.bash b/bash/bash_completion.d/man.bash index 50ab852e..714fa493 100644 --- a/bash/bash_completion.d/man.bash +++ b/bash/bash_completion.d/man.bash @@ -56,7 +56,6 @@ _man() { fi # Add pages from each manual directory - local pages pi for mp in "${manpaths[@]}" ; do [[ -n $mp ]] || continue diff --git a/bash/bash_completion.d/mex.bash b/bash/bash_completion.d/mex.bash index b1e0e1a7..d9604b2c 100644 --- a/bash/bash_completion.d/mex.bash +++ b/bash/bash_completion.d/mex.bash @@ -51,4 +51,4 @@ _mex() { done ) } -complete -F _mex mex +complete -F _mex -o filenames mex diff --git a/bash/bash_completion.d/openssl.bash b/bash/bash_completion.d/openssl.bash index 1cb4bd07..1e2a9c58 100644 --- a/bash/bash_completion.d/openssl.bash +++ b/bash/bash_completion.d/openssl.bash @@ -8,25 +8,18 @@ _openssl() { ((COMP_CWORD == 1)) || return # Iterate through completions produced by subshell - local ci comp - while read -r comp ; do - COMPREPLY[ci++]=$comp + local -a subcmds + local ci subcmd + while read -a subcmds -r ; do + for subcmd in "${subcmds[@]}" ; do + case $subcmd in + "$2"*) COMPREPLY[ci++]=$subcmd ;; + esac + done done < <( - - # Run each of the command-listing commands; read each line into an - # array of subcommands (they are printed as a table) - for list in commands digest-commands cipher-commands ; do - openssl list -"$list" - done | { - declare -a subcmds - while read -a subcmds -r ; do - for subcmd in "${subcmds[@]}" ; do - case $subcmd in - ("$2"*) printf '%s\n' "$subcmd" ;; - esac - done - done - } + openssl list -commands \ + -cipher-commands \ + -digest-commands ) } complete -F _openssl -o bashdefault -o default openssl diff --git a/bash/bash_completion.d/pass.bash b/bash/bash_completion.d/pass.bash index 5a6e0b6c..760e774e 100644 --- a/bash/bash_completion.d/pass.bash +++ b/bash/bash_completion.d/pass.bash @@ -30,7 +30,7 @@ _pass() { # Try to iterate into subdirs, use depth search with ** if available if shopt -s globstar 2>/dev/null ; then - for entry in "$pass_dir"/"$2"**/*.gpg ; do + for entry in "$pass_dir"/"$2"*/**/*.gpg ; do entries[ei++]=$entry done else diff --git a/bash/bash_completion.d/path.bash b/bash/bash_completion.d/path.bash index 9234f132..c3bb7b80 100644 --- a/bash/bash_completion.d/path.bash +++ b/bash/bash_completion.d/path.bash @@ -62,11 +62,11 @@ _path() { fi # Break PATH into parts - declare -a paths IFS=: read -a paths -d '' -r \ < <(printf '%s\0' "$PATH") # Print shell-quoted matching parts, null-terminated + # shellcheck disable=SC2154 for path in "${paths[@]}" ; do case $path in ("$2"*) printf '%q\0' "$path" ;; diff --git a/bash/bashrc b/bash/bashrc index a05526f2..0400e41d 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -6,7 +6,7 @@ esac # Don't do anything if restricted, not even sourcing the ENV file # Testing $- for "r" doesn't work -! shopt -q restricted_shell >/dev/null 2>&1 || return +! shopt -q restricted_shell 2>/dev/null || return # Clear away all aliases; we do this here rather than in the $ENV file shared # between POSIX shells, because ksh relies on aliases to implement certain diff --git a/bash/bashrc.d/keep.bash b/bash/bashrc.d/keep.bash index 48196aeb..191dac4b 100644 --- a/bash/bashrc.d/keep.bash +++ b/bash/bashrc.d/keep.bash @@ -83,7 +83,7 @@ EOF # Iterate through the NAMEs given local name - for name ; do + for name do # Check NAMEs for validity case $name in diff --git a/bash/bashrc.d/vared.bash b/bash/bashrc.d/vared.bash index e024f48a..491e5bff 100644 --- a/bash/bashrc.d/vared.bash +++ b/bash/bashrc.d/vared.bash @@ -24,7 +24,7 @@ vared() { return 2 fi local name - for name ; do + for name do IFS= read -e -i "${!name}" -p "${prompt:-"$name"=}" -r -- "${name?}" done } @@ -10,7 +10,7 @@ shift # Iterate through the remaining args; it's legal for there to be none, but in # that case the user may as well just have invoked the command directly -for arg ; do +for arg do # If this is the first iteration, clear the params away (we grabbed them in # the for statement) @@ -16,7 +16,7 @@ shift 2 if [ "$#" -gt 0 ] ; then # Iterate through any remaining arguments - for carg ; do + for carg do # If this is the first command argument, then before we add it, we'll # add all the ones from the file first if it exists @@ -10,7 +10,7 @@ fi r=$(printf '\r') # Iterate over arguments and apply the same ed(1) script to each of them -for fn ; do +for fn do # Note the heredoc WORD is intentionally unquoted because we want to expand # $r within it to get a literal carriage return; the escape characters @@ -21,7 +21,7 @@ case :$PATH: in esac # Prepend the path to each of the names given if they don't look like options -for arg ; do +for arg do [ -n "$reset" ] || set -- && reset=1 case $arg in --) @@ -44,7 +44,7 @@ done "${VISUAL:-"${EDITOR:-ed}"}" "$@" # Make any created scripts executable if they now appear to be files -for script ; do +for script do [ -f "$script" ] || continue chmod +x -- "$script" done @@ -4,7 +4,7 @@ [ "$#" -gt 0 ] || set -- - # Iterate through arguments -for arg ; do +for arg do # We'll print the filename "-stdin-" rather than - just to be slightly more # explicit @@ -7,7 +7,7 @@ if [ "$#" -eq 0 ] ; then fi # Iterate through each search term and run an appropriate find(1) command -for pat ; do +for pat do # Skip dotfiles, dotdirs, and symbolic links; print anything that matches # the term as a substring (and stop iterating through it) @@ -9,7 +9,7 @@ if [ "$#" -eq 0 ] ; then fi # Iterate through the given names -for name ; do +for name do # Clear the found variable found= diff --git a/bin/mkcp.sh b/bin/mkcp.sh index 10308263..3acf12f0 100644 --- a/bin/mkcp.sh +++ b/bin/mkcp.sh @@ -7,7 +7,7 @@ if [ "$#" -lt 2 ] ; then fi # Get the last argument (the directory to create) -for dir ; do : ; done +for dir do : ; done # Create it, or bail mkdir -p -- "$dir" || exit diff --git a/bin/mked.sh b/bin/mked.sh index 4e280205..93e21573 100644 --- a/bin/mked.sh +++ b/bin/mked.sh @@ -1,6 +1,6 @@ #!/bin/sh # Create paths to all files before invoking editor -for file ; do +for file do mkdir -p -- "${file%/*}" || exit done exec "$EDITOR" "$@" diff --git a/bin/mkmv.sh b/bin/mkmv.sh index 53b5aa8f..832c205e 100644 --- a/bin/mkmv.sh +++ b/bin/mkmv.sh @@ -7,7 +7,7 @@ if [ "$#" -lt 2 ] ; then fi # Get the last argument (the directory to create) -for dir ; do : ; done +for dir do : ; done # Create it, or bail mkdir -p -- "$dir" || exit diff --git a/bin/mkvi.sh b/bin/mkvi.sh index 244b89f8..c5974383 100644 --- a/bin/mkvi.sh +++ b/bin/mkvi.sh @@ -1,6 +1,6 @@ #!/bin/sh # Create paths to all files before invoking editor -for file ; do +for file do mkdir -p -- "${file%/*}" || exit done exec "$VISUAL" "$@" @@ -1,5 +1,5 @@ # Print the full path to each argument; path need not exist -for arg ; do +for arg do case $arg in /*) path=$arg ;; *) path=$PWD/$arg ;; @@ -8,7 +8,7 @@ if [ "$#" -eq 0 ] ; then fi # Iterate through the given files -for sn ; do +for sn do # Strip trailing slash if any and then query string sn=${sn%/} diff --git a/bin/stbl.sh b/bin/stbl.sh index 23d77703..2f6702b1 100644 --- a/bin/stbl.sh +++ b/bin/stbl.sh @@ -7,7 +7,7 @@ if [ "$#" -eq 0 ] ; then fi # Iterate over arguments and apply the same ed(1) script to each of them -for fn ; do +for fn do ed -s -- "$fn" <<'EOF' || ex=1 $g/^ *$/d w diff --git a/bin/stex.sh b/bin/stex.sh index 14d2cabf..b27d9cf8 100644 --- a/bin/stex.sh +++ b/bin/stex.sh @@ -13,7 +13,7 @@ ext=$1 shift # Iterate through the given files (remaining args) -for sn ; do +for sn do # Strip trailing slash if any and then extension sn=${sn%/} diff --git a/bin/stws.sh b/bin/stws.sh index ce2c14d0..59a8652a 100644 --- a/bin/stws.sh +++ b/bin/stws.sh @@ -7,7 +7,7 @@ if [ "$#" -eq 0 ] ; then fi # Iterate over arguments and apply the same ed(1) script to each of them -for fn ; do +for fn do ed -s -- "$fn" <<'EOF' || ex=1 g/ *$/ s/ *$// w @@ -4,7 +4,7 @@ user= # Iterate over the given files -for file ; do +for file do # Get the file's owner, or bail file_owner=$(stat -c %U -- "$file") || exit @@ -10,7 +10,7 @@ fi r=$(printf '\r') # Iterate over arguments and apply the same ed(1) script to each of them -for fn ; do +for fn do # Note the heredoc WORD is intentionally unquoted because we want to expand # $r within it to get a literal carriage return; the escape characters @@ -7,7 +7,7 @@ if [ "$#" -eq 0 ] ; then fi # Iterate over the URL arguments -for url ; do ( +for url do ( # Look for patterns in the URL that suggest transformations case $url in diff --git a/check/bash.sh b/check/bash.sh index 1f9e1b38..510f2af0 100644 --- a/check/bash.sh +++ b/check/bash.sh @@ -5,7 +5,7 @@ set \ bash/bash_profile \ bash/bashrc \ bash/bashrc.d/*.bash -for bash ; do +for bash do bash -n -- "$bash" || exit done printf 'GNU Bash dotfiles parsed successfully.\n' diff --git a/check/ksh.sh b/check/ksh.sh index f4bade82..cf83bc1f 100644 --- a/check/ksh.sh +++ b/check/ksh.sh @@ -1,7 +1,7 @@ set \ ksh/kshrc \ ksh/kshrc.d/*.ksh -for ksh ; do +for ksh do ksh -n -- "$ksh" || exit done sh -n -- ksh/shrc.d/ksh.sh || exit diff --git a/check/sh.sh b/check/sh.sh index 92910c11..e0162f47 100644 --- a/check/sh.sh +++ b/check/sh.sh @@ -4,7 +4,7 @@ set \ sh/shinit \ sh/shrc \ sh/shrc.d/*.sh -for sh ; do +for sh do sh -n -- "$sh" || exit done printf 'POSIX shell dotfiles parsed successfully.\n' diff --git a/check/xinit.sh b/check/xinit.sh index fa235c9d..ae03b8c2 100644 --- a/check/xinit.sh +++ b/check/xinit.sh @@ -1,7 +1,7 @@ set \ X/xinitrc \ X/xinitrc.d/*.sh -for xinit ; do +for xinit do sh -n -- "$xinit" || exit done printf 'Xinit startup scripts parsed successfully.\n' diff --git a/check/zsh.sh b/check/zsh.sh index ce209584..0170e586 100644 --- a/check/zsh.sh +++ b/check/zsh.sh @@ -2,7 +2,7 @@ set \ zsh/zprofile \ zsh/zshrc.d/*.zsh \ zsh/zshrc -for zsh ; do +for zsh do zsh -n -- "$zsh" || exit done sh -n zsh/profile.d/zsh.sh || exit diff --git a/ksh/kshrc.d/keep.ksh b/ksh/kshrc.d/keep.ksh index f6593c3d..629b2fe6 100644 --- a/ksh/kshrc.d/keep.ksh +++ b/ksh/kshrc.d/keep.ksh @@ -94,7 +94,7 @@ EOF # Iterate through the NAMEs given typeset name - for name ; do + for name do # Check NAMEs for validity case $name in diff --git a/readline/inputrc b/readline/inputrc index c11d8fe7..857952cd 100644 --- a/readline/inputrc +++ b/readline/inputrc @@ -62,6 +62,12 @@ $if Bash # Alt+A cycles through completion options "\ea": menu-complete + # Special completion keys for git(1) + ## Branches + "\C-xb": complete + ## Tags + "\C-xt": complete + # Ctrl-Alt-L to clear screen; more ksh-like "\e\C-l": clear-screen @@ -1,5 +1,7 @@ # Add ~/.local/bin to PATH if it exists -[ -d "$HOME"/.local/bin ] && PATH=$HOME/.local/bin:$PATH +if [ -d "$HOME"/.local/bin ] ; then + PATH=$HOME/.local/bin:$PATH +fi # Load all supplementary scripts in ~/.profile.d for sh in "$HOME"/.profile.d/*.sh ; do @@ -13,11 +15,3 @@ 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/downloads.sh b/sh/profile.d/downloads.sh index 865cb859..1a89bc3f 100644 --- a/sh/profile.d/downloads.sh +++ b/sh/profile.d/downloads.sh @@ -8,7 +8,7 @@ esac [ -z "$TMUX" ] || return # Not if ~/.hushlogin exists -[ -e "$HOME"/.hushlogin ] && return +! [ -e "$HOME"/.hushlogin ] || return # Not if ~/.downloads doesn't [ -f "$HOME"/.downloads ] || return @@ -27,5 +27,7 @@ esac printf 'You have %u unsorted files in %s.\n' "$#" "$dir" lc=$((lc+1)) done < "$HOME"/.downloads - [ "$((lc > 0))" -eq 1 ] && printf '\n' + if [ "$lc" -gt 0 ] ; then + printf '\n' + fi ) diff --git a/sh/profile.d/options.sh b/sh/profile.d/options.sh index 73f62243..58376fb3 100644 --- a/sh/profile.d/options.sh +++ b/sh/profile.d/options.sh @@ -22,9 +22,10 @@ options() { # Iterate through remaining arguments (desired options), creating files to # show they're available if found in the help output - for opt ; do - command -p grep -q -- '[^[:alnum:]]--'"$opt"'[^[:alnum:]]' help && - touch -- "$opt" + for opt do + command -p grep -q -- \ + '[^[:alnum:]]--'"$opt"'[^[:alnum:]]' help || continue + touch -- "$opt" done } diff --git a/sh/profile.d/welcome.sh b/sh/profile.d/welcome.sh index ede7a05f..cdd41edb 100644 --- a/sh/profile.d/welcome.sh +++ b/sh/profile.d/welcome.sh @@ -20,8 +20,10 @@ esac # Show a fortune if welcome fortune ; then - [ -d "$HOME"/.local/share/games/fortunes ] && - : "${FORTUNE_PATH:="$HOME"/.local/share/games/fortunes}" + if ! [ -n "$FORTUNE_PATH"] && + [ -d "$HOME"/.local/share/games/fortunes ] ; then + FORTUNE_PATH=$HOME/.local/share/games/fortunes + fi fortune -s "$FORTUNE_PATH" printf '\n' fi @@ -34,7 +36,9 @@ esac # Run verse(1) if we haven't seen it already today if welcome verse ; then - [ -f "$HOME"/.verse ] && read -r last <"$HOME"/.verse + if [ -f "$HOME"/.verse ] ; then + read -r last <"$HOME"/.verse + fi now=$(date +%Y%m%d) if [ "$now" -gt "${last:-0}" ] ; then verse @@ -1,4 +1,7 @@ -# If the shell is interactive, source ~/.shrc -case $- in *i*) - [ -f "$HOME"/.shrc ] && . "$HOME"/.shrc ;; +# If the shell is interactive, and ~/.shrc exists, source it +case $- in + *i*) + if [ -f "$HOME"/.shrc ] ; then + . "$HOME"/.shrc + fi esac @@ -22,5 +22,7 @@ done unset -v sh # If ENV_EXT was set and exists, source that too, then clean it away -[ -e "$ENV_EXT" ] && . "$ENV_EXT" +if [ -e "$ENV_EXT" ] ; then + . "$ENV_EXT" +fi unset -v ENV_EXT diff --git a/sh/shrc.d/bc.sh b/sh/shrc.d/bc.sh index aee88e09..591b4359 100644 --- a/sh/shrc.d/bc.sh +++ b/sh/shrc.d/bc.sh @@ -6,8 +6,9 @@ bc() { # Add --quiet to stop the annoying welcome banner - [ -e "$HOME"/.cache/sh/opt/bc/quiet ] && + if [ -e "$HOME"/.cache/sh/opt/bc/quiet ] ; then set -- --quiet "$@" + fi # Run bc(1) with the concluded arguments command bc "$@" diff --git a/sh/shrc.d/ed.sh b/sh/shrc.d/ed.sh index e6b6eee8..dc8433f6 100644 --- a/sh/shrc.d/ed.sh +++ b/sh/shrc.d/ed.sh @@ -12,16 +12,18 @@ ed() { fi # Add --verbose to explain errors - [ -e "$HOME"/.cache/sh/opt/ed/verbose ] && + if [ -e "$HOME"/.cache/sh/opt/ed/verbose ] ; then set -- --verbose "$@" + fi # Add an asterisk prompt (POSIX feature) set -- -p\* "$@" # Run in rlwrap(1) if available set -- ed "$@" - command -v rlwrap >/dev/null 2>&1 && + if command -v rlwrap >/dev/null 2>&1 ; then set -- rlwrap --history-filename=/dev/null "$@" + fi # Run determined command command "$@" diff --git a/sh/shrc.d/grep.sh b/sh/shrc.d/grep.sh index 43797ef5..997babc9 100644 --- a/sh/shrc.d/grep.sh +++ b/sh/shrc.d/grep.sh @@ -9,37 +9,43 @@ unset -v GREP_OPTIONS grep() { # Add --binary-files=without-match to gracefully skip binary files - [ -e "$HOME"/.cache/sh/opt/grep/binary-files ] && + if [ -e "$HOME"/.cache/sh/opt/grep/binary-files ] ; then set -- --binary-files=without-match "$@" + fi # Add --color=auto if the terminal has at least 8 colors - [ -e "$HOME"/.cache/sh/opt/grep/color ] && - [ "$({ tput colors||tput Co||echo 0; } 2>/dev/null)" -ge 8 ] && + if [ -e "$HOME"/.cache/sh/opt/grep/color ] && + [ "$({ tput colors||tput Co||echo 0; } 2>/dev/null)" -ge 8 ] ; then set -- --color=auto "$@" + fi # Add --devices=skip to gracefully skip devices - [ -e "$HOME"/.cache/sh/opt/grep/devices ] && + if [ -e "$HOME"/.cache/sh/opt/grep/devices ] ; then set -- --devices=skip "$@" + fi # Add --directories=skip to gracefully skip directories - [ -e "$HOME"/.cache/sh/opt/grep/directories ] && + if [ -e "$HOME"/.cache/sh/opt/grep/directories ] ; then set -- --directories=skip "$@" + fi # Add --exclude to ignore .gitignore and .gitmodules files - [ -e "$HOME"/.cache/sh/opt/grep/exclude ] && + if [ -e "$HOME"/.cache/sh/opt/grep/exclude ] ; then set -- \ --exclude=.gitignore \ --exclude=.gitmodules \ "$@" + fi # Add --exclude-dir to ignore version control dot-directories - [ -e "$HOME"/.cache/sh/opt/grep/exclude-dir ] && + if [ -e "$HOME"/.cache/sh/opt/grep/exclude-dir ] ; then set -- \ --exclude-dir=.cvs \ --exclude-dir=.git \ --exclude-dir=.hg \ --exclude-dir=.svn \ "$@" + fi # Run grep(1) with the concluded arguments command grep "$@" diff --git a/sh/shrc.d/gt.sh b/sh/shrc.d/gt.sh index 95ab4c2f..7a52571d 100644 --- a/sh/shrc.d/gt.sh +++ b/sh/shrc.d/gt.sh @@ -19,7 +19,9 @@ gt() { done # If target isn't a directory, chop to its parent - [ -d "$1" ] || set -- "${1%/*}" + if ! [ -d "$1" ] ; then + set -- "${1%/*}" + fi # Try to change into the determined directory, or root if empty command cd -- "${1:-/}" diff --git a/sh/shrc.d/ls.sh b/sh/shrc.d/ls.sh index 7e843cc7..1083dfca 100644 --- a/sh/shrc.d/ls.sh +++ b/sh/shrc.d/ls.sh @@ -12,26 +12,30 @@ unset -v LS_OPTIONS LS_COLORS # Define function proper ls() { - # -F to show trailing indicators of the filetype - # -q to replace control chars with '?' + # POSIX options: + ## -F to show trailing indicators of the filetype + ## -q to replace control chars with '?' set -- -Fq "$@" - - # If output is to a terminal, add -x to format entries across, not down - [ -t 1 ] && set -- -x "$@" - - # Add --block-size=K to always show the filesize in kibibytes - [ -e "$HOME"/.cache/sh/opt/ls/block-size ] && + ## -x to format entries across, not down, if output looks like a terminal + if [ -t 1 ] ; then + set -- -x "$@" + fi + + # GNU options: + ## Add --block-size=K to always show the filesize in kibibytes + if [ -e "$HOME"/.cache/sh/opt/ls/block-size ] ; then set -- --block-size=1024 "$@" - - # Add --color if the terminal has at least 8 colors - [ -e "$HOME"/.cache/sh/opt/ls/color ] && - [ "$({ tput colors||tput Co||echo 0; } 2>/dev/null)" -ge 8 ] && + fi + ## Add --color if the terminal has at least 8 colors + if [ -e "$HOME"/.cache/sh/opt/ls/color ] && + [ "$(exec 2>/dev/null;tput colors||tput Co||echo 0)" -ge 8 ] ; then set -- --color=auto "$@" - - # Add --time-style='+%Y-%m-%d %H:%M:%S' to show the date in my preferred - # (fixed) format - [ -e "$HOME"/.cache/sh/opt/ls/time-style ] && + fi + ## Add --time-style='+%Y-%m-%d %H:%M:%S' to show the date in my preferred + ## (fixed) format + if [ -e "$HOME"/.cache/sh/opt/ls/time-style ] ; then set -- --time-style='+%Y-%m-%d %H:%M:%S' "$@" + fi # If the operating system is FreeBSD, there are some specific options we # can add that might mean different things to e.g. GNU ls(1) diff --git a/sh/shrc.d/mkcd.sh b/sh/shrc.d/mkcd.sh index c59a8c54..cd882b51 100644 --- a/sh/shrc.d/mkcd.sh +++ b/sh/shrc.d/mkcd.sh @@ -1,4 +1,5 @@ # Create a directory and change into it mkcd() { - mkdir -p -- "$1" && command cd -- "$1" + command -p mkdir -p -- "$1" || return + command cd -- "$1" } diff --git a/sh/shrc.d/path.sh b/sh/shrc.d/path.sh index b6b1820f..a854e148 100644 --- a/sh/shrc.d/path.sh +++ b/sh/shrc.d/path.sh @@ -5,15 +5,16 @@ path() { case $1 in # List current directories in PATH - list|'') ( - path=$PATH: - while [ -n "$path" ] ; do - dir=${path%%:*} - path=${path#*:} - [ -n "$dir" ] || continue - printf '%s\n' "$dir" + list|'') + set -- "$PATH": + while [ -n "$1" ] ; do + case $1 in + :*) ;; + *) printf '%s\n' "${1%%:*}" ;; + esac + set -- "${1#*:}" done - ) ;; + ;; # Helper function checks directory argument makes sense _argcheck) @@ -33,7 +34,9 @@ path() { # Add a directory at the start of $PATH insert) - [ "$#" -eq 2 ] || set -- "$1" "$PWD" + if ! [ "$#" -eq 2 ] ; then + set -- "$1" "$PWD" + fi path _argcheck "$@" || return if path check "$2" ; then printf >&2 'path(): %s: %s already in PATH\n' "$@" @@ -44,7 +47,9 @@ path() { # Add a directory to the end of $PATH append) - [ "$#" -eq 2 ] || set -- "$1" "$PWD" + if ! [ "$#" -eq 2 ] ; then + set -- "$1" "$PWD" + fi path _argcheck "$@" || return if path check "$2" ; then printf >&2 'path(): %s: %s already in PATH\n' "$@" @@ -55,7 +60,9 @@ path() { # Remove a directory from $PATH remove) - [ "$#" -eq 2 ] || set -- "$1" "$PWD" + if ! [ "$#" -eq 2 ] ; then + set -- "$1" "$PWD" + fi path _argcheck "$@" || return if ! path check "$2" ; then printf >&2 'path(): %s: %s not in PATH\n' "$@" @@ -107,7 +114,9 @@ path() { # Check whether a directory is in PATH check) path _argcheck "$@" || return - [ "$#" -eq 2 ] || set -- "$1" "$PWD" + if ! [ "$#" -eq 2 ] ; then + set -- "$1" "$PWD" + fi case :$PATH: in *:"$2":*) return 0 ;; esac diff --git a/sh/shrc.d/tree.sh b/sh/shrc.d/tree.sh index d462f3e1..a7e5bef3 100644 --- a/sh/shrc.d/tree.sh +++ b/sh/shrc.d/tree.sh @@ -22,7 +22,7 @@ tree() { [ -t 1 ] || exit # Not if output terminal doesn't have at least 8 colors - [ "$({ tput colors||tput Co||echo 0; } 2>/dev/null)" -ge 8 ] + [ "$(exec 2>/dev/null;tput colors||tput Co||echo 0)" -ge 8 ] ) ; then set -- -C "$@" diff --git a/sh/shrc.d/vr.sh b/sh/shrc.d/vr.sh index 8b35357c..c7057ec2 100644 --- a/sh/shrc.d/vr.sh +++ b/sh/shrc.d/vr.sh @@ -11,9 +11,14 @@ vr() { exit 2 fi - # Get path from first argument, strip trailing slash + # Get path from first argument path=${1:-"$PWD"} - [ "$path" = / ] || path=${path%/} + + # Strip a trailing slash + case $path in + (/) ;; + (*) path=${path%/} ;; + esac # Step into the directory cd -- "$path" || exit @@ -34,7 +39,7 @@ vr() { # that is the root (bad) while svn info >/dev/null 2>&1 ; do root=$PWD - [ "$root" = / ] && break + ! [ "$root" = / ] || break cd .. || exit done if [ -n "$root" ] ; then diff --git a/vim/autoload/filetype.vim b/vim/autoload/filetype.vim new file mode 100644 index 00000000..d1e4e3d7 --- /dev/null +++ b/vim/autoload/filetype.vim @@ -0,0 +1,76 @@ +" Helper function to run the 'filetypedetect' group on a file with its +" extension stripped off +function! filetype#StripRepeat() abort + + " Check we have the fnameescape() function + if !exists('*fnameescape') + return + endif + + " Expand the match result + let l:fn = expand('<afile>') + + " Strip leading and trailing #hashes# + if l:fn =~# '^#\+.*#\+$' + let l:fn = substitute(l:fn, '^#\+\(.\+\)#\+$', '\1', '') + + " Strip trailing tilde~ + elseif l:fn =~# '\~$' + let l:fn = substitute(l:fn, '\~$', '', '') + + " Strip generic .extension + else + let l:fn = expand('<afile>:r') + endif + + " Re-run the group if there's anything left + if strlen(l:fn) + execute 'doautocmd filetypedetect BufRead ' . fnameescape(l:fn) + endif + +endfunction + +" Helper function to run the 'filetypedetect' group on a file in a temporary +" sudoedit(8) directory, modifying it with an attempt to reverse the temporary +" filename change +function! filetype#SudoRepeat() abort + + " Check we have the fnameescape() function + if !exists('*fnameescape') + return + endif + + " Expand the match result + let l:fn = expand('<afile>') + + " myfileXXQGS16A.conf: strip eight chars before final period + if l:fn =~# '/[^./]\+\w\{8}\.[^./]\+$' + let l:fr = expand('<afile>:r') + let l:fe = expand('<afile>:e') + let l:fn = strpart(l:fr, -8, strlen(l:fr)) . '.' . l:fe + + " myfile.XXQGS16A: strip extension + elseif l:fn =~# '/[^./]\+\.\w\{8}$' + let l:fn = expand('<afile>:r') + + " Unrecognised pattern; return, don't repeat + else + return + endif + + " Re-run the group if there's anything left + if strlen(l:fn) + execute 'doautocmd filetypedetect BufRead ' . fnameescape(l:fn) + endif + +endfunction + +" Check whether the first line was changed and looks like a shebang, and if +" so, re-run filetype detection +function! filetype#CheckShebang() abort + if line('''[') == 1 && getline(1) =~# '^#!' + doautocmd filetypedetect BufRead + endif +endfunction + + diff --git a/vim/filetype.vim b/vim/filetype.vim index cc0de4e4..3ac816d4 100644 --- a/vim/filetype.vim +++ b/vim/filetype.vim @@ -10,81 +10,6 @@ if !has('autocmd') || &compatible finish endif -" Helper function to run the 'filetypedetect' group on a file with its -" extension stripped off -function! s:StripRepeat() - - " Check we have the fnameescape() function - if !exists('*fnameescape') - return - endif - - " Expand the match result - let l:fn = expand('<afile>') - - " Strip leading and trailing #hashes# - if l:fn =~# '^#\+.*#\+$' - let l:fn = substitute(l:fn, '^#\+\(.\+\)#\+$', '\1', '') - - " Strip trailing tilde~ - elseif l:fn =~# '\~$' - let l:fn = substitute(l:fn, '\~$', '', '') - - " Strip generic .extension - else - let l:fn = expand('<afile>:r') - endif - - " Re-run the group if there's anything left - if strlen(l:fn) - execute 'doautocmd filetypedetect BufRead ' . fnameescape(l:fn) - endif - -endfunction - -" Helper function to run the 'filetypedetect' group on a file in a temporary -" sudoedit(8) directory, modifying it with an attempt to reverse the temporary -" filename change -function! s:SudoRepeat() - - " Check we have the fnameescape() function - if !exists('*fnameescape') - return - endif - - " Expand the match result - let l:fn = expand('<afile>') - - " myfileXXQGS16A.conf: strip eight chars before final period - if l:fn =~# '/[^./]\+\w\{8}\.[^./]\+$' - let l:fr = expand('<afile>:r') - let l:fe = expand('<afile>:e') - let l:fn = strpart(l:fr, -8, strlen(l:fr)) . '.' . l:fe - - " myfile.XXQGS16A: strip extension - elseif l:fn =~# '/[^./]\+\.\w\{8}$' - let l:fn = expand('<afile>:r') - - " Unrecognised pattern; return, don't repeat - else - return - endif - - " Re-run the group if there's anything left - if strlen(l:fn) - execute 'doautocmd filetypedetect BufRead ' . fnameescape(l:fn) - endif - -endfunction - -" Check whether the first line was changed and looks like a shebang, and if -" so, re-run filetype detection -function! s:CheckShebang() - if line('''[') == 1 && getline(1) =~# '^#!' - doautocmd filetypedetect BufRead - endif -endfunction - " Use our own filetype detection rules augroup filetypedetect autocmd! @@ -97,7 +22,7 @@ augroup filetypedetect \,?*~ \,?*.{bak,example,in,new,old,orig,sample,test} \,?*.dpkg-{bak,dist,new,old} - \ call s:StripRepeat() + \ call filetype#StripRepeat() " Stuff Tom cares about enough and edits often enough to type based on " filename patterns follows. @@ -534,7 +459,7 @@ augroup filetypedetect \ /var/tmp/?*????????.* \,/var/tmp/?*.???????? \ if !did_filetype() - \| call s:SudoRepeat() + \| call filetype#SudoRepeat() \|endif " Generic text, config, and log files, if no type assigned yet @@ -568,6 +493,6 @@ augroup filetypedetect " On leaving insert mode, check whether the first line was changed and looks " like a shebang format, and if so, re-run filetype detection - autocmd InsertLeave * call s:CheckShebang() + autocmd InsertLeave * call filetype#CheckShebang() augroup END diff --git a/zsh/profile.d/zsh.sh b/zsh/profile.d/zsh.sh index 47de6d4d..37ec8014 100644 --- a/zsh/profile.d/zsh.sh +++ b/zsh/profile.d/zsh.sh @@ -5,24 +5,33 @@ # ~/.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? +# This hack is only applicable to interactive zsh invoked as sh/ksh, when ENV +# exists, so check each of those: +## Interactive? +case $- in + *i*) ;; + *) return ;; +esac +## zsh? [ -n "$ZSH_VERSION" ] || return +## Invoked as sh or ksh? case $ZSH_NAME in sh|ksh) ;; *) return ;; esac +## ENV exists? +[ -e "$ENV" ] || return # 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 ! ( +# we'll source $ENV ourselves, since ~/.profile probably didn't do it +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 + ! [ "$zv" -gt "$fv" ] || exit 1 + ! [ "$zv" -lt "$fv" ] || exit 0 + zvs=${ZSH_VERSION#*.} done ) ; then - ENV_FORCE=1 + . "$ENV" fi diff --git a/zsh/zshrc.d/keep.zsh b/zsh/zshrc.d/keep.zsh index 59696301..c47748cd 100644 --- a/zsh/zshrc.d/keep.zsh +++ b/zsh/zshrc.d/keep.zsh @@ -83,7 +83,7 @@ EOF # Iterate through the NAMEs given local name - for name ; do + for name do # Check NAMEs for validity case $name in |