diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2018-12-01 02:48:02 +1300 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2018-12-01 02:48:02 +1300 |
commit | 32ca9ad0fa8d7691022a44989c1832672fb51548 (patch) | |
tree | 9ef3779f21a9f2c500d62ca3a31ec37aec930af8 | |
parent | Merge branch 'release/v2.5.0' (diff) | |
parent | Bump VERSION (diff) | |
download | dotfiles-32ca9ad0fa8d7691022a44989c1832672fb51548.tar.gz dotfiles-32ca9ad0fa8d7691022a44989c1832672fb51548.zip |
Merge branch 'release/v2.6.0'v2.6.0
* release/v2.6.0: (26 commits)
Bump VERSION
Overhaul and expand abook address Bash completion
Restore correct completion function for mutt(1)
Use simpler keyword complete for `path` subcommand
Break up a couple of long lines
Remove unneeded local array declaration
Remove unneeded empty result short circuit
Remove redundant error code in short circuits
Adjust loop short circuits and pattern matches
Remove unneeded quoting in case statement
Remove needless line breaks from git completion
Use inverting exclam mark outside conditional
Remove stray comment no longer applicable
Use array+=() syntax in two Bash completion files
Remove null-result guard from completion gens
Use simpler method of no case completion check
Overhaul `keep` completion
Simplify an array operation in `bd` completion
Overhaul text filename generation
Use full `if` condition for SSH hostname comp src
...
38 files changed, 423 insertions, 351 deletions
@@ -28,3 +28,5 @@ Known issues it? * The b:undo\_indent definition for the perl filetype can probably be pushed upstream. +* The `_text_filenames` completion handler for Bash won't work on files with + newlines in their names. Can it be made to? @@ -352,7 +352,7 @@ install: install-bin \ install-vim install-conf: - sh install/install-conf.sh + sh install/conf.sh install-abook: mkdir -p -- $(HOME)/.abook @@ -465,7 +465,7 @@ install-ksh: check-ksh install-sh cp -p -- ksh/kshrc.d/* $(HOME)/.kshrc.d install-login-shell: check-login-shell - sh install/install-login-shell.sh + sh install/login-shell.sh install-perlcritic: cp -p -- perlcritic/perlcriticrc $(HOME)/.perlcriticrc @@ -1,2 +1,2 @@ -tejr dotfiles v2.5.0 -Fri Nov 30 01:30:00 UTC 2018 +tejr dotfiles v2.6.0 +Fri Nov 30 13:48:02 UTC 2018 diff --git a/bash/bash_completion.d/_abook_addresses.bash b/bash/bash_completion.d/_abook_addresses.bash index 8e341172..e79eef42 100644 --- a/bash/bash_completion.d/_abook_addresses.bash +++ b/bash/bash_completion.d/_abook_addresses.bash @@ -1,10 +1,33 @@ # Email addresses from abook(1) _abook_addresses() { - while IFS=$'\t' read -r address _ ; do - case $address in - "${COMP_WORDS[COMP_CWORD]}"*) - COMPREPLY[${#COMPREPLY[@]}]=$address - ;; - esac - done < <(abook --mutt-query \@) + + # Needs abook(1) + hash abook 2>/dev/null || return + + # Iterate through words produced by subshell + local word + while read -r word ; do + [[ -n $word ]] || continue + COMPREPLY[${#COMPREPLY[@]}]=$word + done < <( + + # Set case-insensitive matching if appropriate + while read -r _ setting ; do + case $setting in + ('completion-ignore-case on') + shopt -s nocasematch 2>/dev/null + break + ;; + esac + done < <(bind -v) + + # Generate list of email addresses from abook(1) + while IFS=$'\t' read -r address _ ; do + case $address in + ("${COMP_WORDS[COMP_CWORD]}"*) + printf '%s\n' "$address" + ;; + esac + done < <(abook --mutt-query \@) + ) } diff --git a/bash/bash_completion.d/_ssh_config_hosts.bash b/bash/bash_completion.d/_ssh_config_hosts.bash index 8f45c412..c26457cf 100644 --- a/bash/bash_completion.d/_ssh_config_hosts.bash +++ b/bash/bash_completion.d/_ssh_config_hosts.bash @@ -1,22 +1,48 @@ # Complete ssh_config(5) hostnames _ssh_config_hosts() { - # Read hostnames from existent config files, no asterisks - local -a hosts - local config option value - for config in "$HOME"/.ssh/config /etc/ssh/ssh_config ; do - [[ -e $config ]] || continue - while read -r option value _ ; do - [[ $option == Host ]] || continue - [[ $value != *'*'* ]] || continue - hosts[${#hosts[@]}]=$value - done < "$config" - done - - # Generate completion reply - local host - for host in "${hosts[@]}" ; do - [[ $host == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue - COMPREPLY[${#COMPREPLY[@]}]=$host - done + # Don't complete anything that wouldn't be in a valid hostname + case ${COMP_WORDS[COMP_CWORD]} in + *[!a-zA-Z0-9.-]*) return 1 ;; + esac + + # Iterate through words from a subshell + while read -r word ; do + [[ -n $word ]] || continue + COMPREPLY[${#COMPREPLY[@]}]=$word + done < <( + + # Check bind settings to see if we should match case insensitively + while read -r _ setting ; do + case $setting in + ('completion-ignore-case on') + shopt -qs nocasematch 2>/dev/null + break + ;; + esac + done < <(bind -v) + + # 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 + while read -r option value _ ; do + [[ $option == Host ]] || continue + + # Check host value + case $value in + + # Don't complete with wildcard characters + (*'*'*) ;; + + # Found a match; print it + ("${COMP_WORDS[COMP_CWORD]}"*) + printf '%s\n' "$value" + ;; + esac + + done < "$config" + done + ) } diff --git a/bash/bash_completion.d/_text_filenames.bash b/bash/bash_completion.d/_text_filenames.bash index a9d767b7..9cc1c722 100644 --- a/bash/bash_completion.d/_text_filenames.bash +++ b/bash/bash_completion.d/_text_filenames.bash @@ -14,71 +14,146 @@ _text_filenames() { # Exclude blanks [[ -n $item ]] || continue + # Exclude nonexistent (some sort of error) + [[ -e $item ]] || continue + + # Exclude files with block, character, pipe, or socket type + ! [[ -b $item ]] || continue + ! [[ -c $item ]] || continue + ! [[ -p $item ]] || continue + ! [[ -S $item ]] || continue + # Accept directories if [[ -d $item ]] ; then COMPREPLY[${#COMPREPLY[@]}]=$item continue fi - # Exclude files with block, character, pipe, or socket type - [[ ! -b $item ]] || continue - [[ ! -c $item ]] || continue - [[ ! -p $item ]] || continue - [[ ! -S $item ]] || continue - # Check the filename extension to know what to exclude - case $item in - - # Binary image file formats - *.bmp|*.gif|*.ico|*.jpeg|*.jpg|*.png|*.tif|*.xcf) ;; - *.BMP|*.GIF|*.ICO|*.JPEG|*.JPG|*.PNG|*.TIF|*.XCF) ;; - - # Video file formats - *.avi|*.gifv|*.mkv|*.mov|*.mpg|*.rm|*.webm) ;; - *.AVI|*.GIFV|*.MKV|*.MOV|*.MPG|*.RM|*.WEBM) ;; - - # Lossy audio file formats - *.au|*.m4a|*.mp[34]|*.ogg|*.snd|*.wma) ;; - *.AU|*.M4A|*.MP[34]|*.OGG|*.SND|*.WMA) ;; - - # Lossless/source audio file formats - *.aup|*.flac|*.mid|*.h2song|*.nwc|*.s3m|*.wav) ;; - *.AUP|*.FLAC|*.MID|*.H2SONG|*.NWC|*.S3M|*.WAV) ;; - - # Compressed/archived file formats - *.cab|*.deb|*.lzm|*.pack|*.tar|*.tar.bz2|*.tar.gz|*.tar.xz|*.zip) ;; - *.CAB|*.DEB|*.LZM|*.PACK|*.TAR|*.TAR.BZ2|*.TAR.GZ|*.TAR.XZ|*.ZIP) ;; - - # Document formats - # (Not .doc, it's a plaintext format sometimes) - *.cbr|*.docx|*.epub|*.odp|*.odt|*.pdf|*.xls|*.xlsx) ;; - *.CBR|*.DOCX|*.EPUB|*.ODP|*.ODT|*.PDF|*.XLS|*.XLSX) ;; - - # Filesystems/disk images - *.bin|*.cue|*.hdf|*.img|*.iso|*.mdf|*.raw) ;; - *.BIN|*.CUE|*.HDF|*.IMG|*.ISO|*.MDF|*.RAW) ;; - - # Font files - *.ttf) ;; - *.TTF) ;; - - # Index file formats - *.idx) ;; - *.IDX) ;; - - # Encrypted file formats - *.gpg) ;; - *.GPG) ;; - - # 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|*.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 ;; + ( + # Case-insensitive matching available since 3.1-alpha + shopt -qs nocasematch 2>/dev/null + + # Match against known binary patterns + case $item in + + # Archives + (*.7z) ;; + (*.bz2) ;; + (*.gz) ;; + (*.jar) ;; + (*.rar) ;; + (*.tar) ;; + (*.xz) ;; + (*.zip) ;; + + # Bytecode + (*.class) ;; + (*.pyc) ;; + + # Databases + (*.db) ;; + (*.dbm) ;; + (*.sdbm) ;; + (*.sqlite) ;; + + # Disk + (*.adf) ;; + (*.bin) ;; + (*.hdf) ;; + (*.iso) ;; + + # Documents + (*.docx) ;; + (*.djvu) ;; + (*.odp) ;; + (*.ods) ;; + (*.odt) ;; + (*.pdf) ;; + (*.ppt) ;; + (*.xls) ;; + (*.xlsx) ;; + + # Encrypted + (*.asc) ;; + (*.gpg) ;; + + # Executables + (*.exe) ;; + + # Fonts + (*.ttf) ;; + + # Images + (*.bmp) ;; + (*.gd2) ;; + (*.gif) ;; + (*.ico) ;; + (*.jpeg) ;; + (*.jpg) ;; + (*.pbm) ;; + (*.png) ;; + (*.psd) ;; + (*.tga) ;; + (*.xbm) ;; + (*.xcf) ;; + (*.xpm) ;; + + # Incomplete + (*.filepart) ;; + + # Objects + (*.a) ;; + (*.o) ;; + + # Sound + (*.au) ;; + (*.aup) ;; + (*.flac) ;; + (*.mid) ;; + (*.m4a) ;; + (*.mp3) ;; + (*.ogg) ;; + (*.opus) ;; + (*.s3m) ;; + (*.wav) ;; + + # System-specific + (.DS_Store) ;; + + # Translation + (*.gmo) ;; + + # Version control + (.git) ;; + (.hg) ;; + (.svn) ;; + + # Video + (*.avi) ;; + (*.gifv) ;; + (*.mp4) ;; + (*.ogv) ;; + (*.rm) ;; + (*.swf) ;; + (*.webm) ;; + + # Vim + (*~) ;; + (*.swp) ;; + + # Not binary that we can tell; maybe editable + (*) exit 0 ;; + + esac + + # Known usually-binary extension; flag failure + exit 1 + + ) || continue + + # Complete everything else; some of it will still be binary + COMPREPLY[${#COMPREPLY[@]}]=$item - esac done < <(compgen -A file -- "${COMP_WORDS[COMP_CWORD]}") } diff --git a/bash/bash_completion.d/awk.bash b/bash/bash_completion.d/awk.bash index 1e01381a..5f121d8a 100644 --- a/bash/bash_completion.d/awk.bash +++ b/bash/bash_completion.d/awk.bash @@ -1,4 +1,5 @@ # Completion for awk(1) with files that look editable -declare -F _text_filenames >/dev/null || +if ! declare -F _text_filenames >/dev/null ; then source "$HOME"/.bash_completion.d/_text_filenames.bash +fi complete -F _text_filenames -o filenames awk diff --git a/bash/bash_completion.d/bd.bash b/bash/bash_completion.d/bd.bash index 86146a4e..e67cdd09 100644 --- a/bash/bash_completion.d/bd.bash +++ b/bash/bash_completion.d/bd.bash @@ -2,21 +2,24 @@ _bd() { # Only makes sense for the first argument - ((COMP_CWORD == 1)) || return 1 + ((COMP_CWORD == 1)) || return # Build a list of dirnames in $PWD local -a dirnames IFS=/ read -rd '' -a dirnames < <(printf '%s\0' "${PWD#/}") # Remove the last element in the array (the current directory) - ((${#dirnames[@]})) || return 1 - dirnames=("${dirnames[@]:0:$((${#dirnames[@]}-1))}") + ((${#dirnames[@]})) || return + dirnames=("${dirnames[@]:0:${#dirnames[@]}-1}") # Add the matching dirnames to the reply local dirname for dirname in "${dirnames[@]}" ; do - [[ $dirname == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue - COMPREPLY[${#COMPREPLY[@]}]=$(printf %q "$dirname") + case $dirname in + "${COMP_WORDS[COMP_CWORD]}"*) + COMPREPLY[${#COMPREPLY[@]}]=$(printf %q "$dirname") + ;; + esac done } complete -F _bd bd diff --git a/bash/bash_completion.d/cat.bash b/bash/bash_completion.d/cat.bash index 430cd58c..2da60ff0 100644 --- a/bash/bash_completion.d/cat.bash +++ b/bash/bash_completion.d/cat.bash @@ -1,4 +1,5 @@ # Completion for cat(1) with files that look editable -declare -F _text_filenames >/dev/null || +if ! declare -F _text_filenames >/dev/null ; then source "$HOME"/.bash_completion.d/_text_filenames.bash +fi complete -F _text_filenames -o filenames cat diff --git a/bash/bash_completion.d/ed.bash b/bash/bash_completion.d/ed.bash index c7fc6fde..b9651957 100644 --- a/bash/bash_completion.d/ed.bash +++ b/bash/bash_completion.d/ed.bash @@ -1,4 +1,5 @@ # Completion for ed(1) with files that look editable -declare -F _text_filenames >/dev/null || +if ! declare -F _text_filenames >/dev/null ; then source "$HOME"/.bash_completion.d/_text_filenames.bash +fi complete -F _text_filenames -o filenames ed diff --git a/bash/bash_completion.d/eds.bash b/bash/bash_completion.d/eds.bash index ea6de618..1c5b2aa2 100644 --- a/bash/bash_completion.d/eds.bash +++ b/bash/bash_completion.d/eds.bash @@ -11,17 +11,12 @@ _eds() { done < <( shopt -s dotglob nullglob - # Make globbing case-insensitive if appropriate; is there a cleaner way - # to find this value? - while read -r _ option value ; do - case $option in - (completion-ignore-case) - case $value in - (on) - shopt -s nocaseglob - break - ;; - esac + # Make globbing case-insensitive if appropriate + while read -r _ setting ; do + case $setting in + ('completion-ignore-case on') + shopt -s nocaseglob + break ;; esac done < <(bind -v) @@ -30,18 +25,14 @@ _eds() { files=("${EDSPATH:-"$HOME"/.local/bin}"/"${COMP_WORDS[COMP_CWORD]}"*) declare -a executables for file in "${files[@]}" ; do - [[ -f $file && -x $file ]] || continue + ! [[ -d $file ]] || continue + [[ -e $file ]] || continue + [[ -x $file ]] || continue executables[${#executables[@]}]=${file##*/} done - # Print quoted entries, null-delimited, if there was at least one; - # otherwise, just print a null character to stop this hanging in Bash - # 4.4 - if ((${#executables[@]})) ; then - printf '%q\0' "${executables[@]}" - else - printf '\0' - fi + # Print quoted entries, null-delimited + printf '%q\0' "${executables[@]}" ) } complete -F _eds eds diff --git a/bash/bash_completion.d/ex.bash b/bash/bash_completion.d/ex.bash index 6805b8b1..00875df5 100644 --- a/bash/bash_completion.d/ex.bash +++ b/bash/bash_completion.d/ex.bash @@ -1,4 +1,5 @@ # Completion for ex(1) with files that look editable -declare -F _text_filenames >/dev/null || +if ! declare -F _text_filenames >/dev/null ; then source "$HOME"/.bash_completion.d/_text_filenames.bash +fi complete -F _text_filenames -o filenames ex diff --git a/bash/bash_completion.d/find.bash b/bash/bash_completion.d/find.bash index 007a83bd..7e2ae9c3 100644 --- a/bash/bash_completion.d/find.bash +++ b/bash/bash_completion.d/find.bash @@ -30,7 +30,7 @@ _find() { local item while read -r item ; do [[ -n $item ]] || continue - COMPREPLY[${#COMPREPLY[@]}]=$item + COMPREPLY+=("$item") done < <( # If the word being completed starts with a dash, just complete it as @@ -64,7 +64,7 @@ _find() { # Otherwise, look at the word *before* this one to figure out what to # complete - case "${COMP_WORDS[COMP_CWORD-1]}" in + case ${COMP_WORDS[COMP_CWORD-1]} in # Args to -exec and -execdir should be commands (-exec|-execdir) diff --git a/bash/bash_completion.d/ftp.bash b/bash/bash_completion.d/ftp.bash index d7ee8963..a584dd81 100644 --- a/bash/bash_completion.d/ftp.bash +++ b/bash/bash_completion.d/ftp.bash @@ -4,7 +4,7 @@ _ftp() { # Bail if the .netrc file is illegible local netrc netrc=$HOME/.netrc - [[ -r $netrc ]] || return 1 + [[ -r $netrc ]] || return # Tokenize the file local -a tokens diff --git a/bash/bash_completion.d/git.bash b/bash/bash_completion.d/git.bash index 2fd1bb98..a2edb468 100644 --- a/bash/bash_completion.d/git.bash +++ b/bash/bash_completion.d/git.bash @@ -12,16 +12,14 @@ _git() { refs) local ref while IFS= read -r ref ; do - [[ -n $ref ]] || continue ref=${ref#refs/*/} case $ref in + '') continue ;; "${COMP_WORDS[COMP_CWORD]}"*) COMPREPLY[${#COMPREPLY[@]}]=$ref ;; esac - done < <(git for-each-ref \ - --format '%(refname)' \ - 2>/dev/null) + done < <(git for-each-ref --format '%(refname)' 2>/dev/null) return ;; @@ -51,9 +49,7 @@ _git() { COMPREPLY[${#COMPREPLY[@]}]=$alias ;; esac - done < <(git config \ - --get-regexp '^alias\.' \ - 2>/dev/null) + done < <(git config --get-regexp '^alias\.' 2>/dev/null) return ;; @@ -63,7 +59,8 @@ _git() { execpath=$(git --exec-path) || return local path for path in "$execpath"/git-"${COMP_WORDS[COMP_CWORD]}"* ; do - [[ -f $path ]] || continue + ! [[ -d $path ]] || continue + [[ -e $path ]] || continue [[ -x $path ]] || continue COMPREPLY[${#COMPREPLY[@]}]=${path#"$execpath"/git-} done diff --git a/bash/bash_completion.d/gpg.bash b/bash/bash_completion.d/gpg.bash index f98cb193..697e4a65 100644 --- a/bash/bash_completion.d/gpg.bash +++ b/bash/bash_completion.d/gpg.bash @@ -2,18 +2,21 @@ _gpg() { # Bail if no gpg(1) - hash gpg 2>/dev/null || return 1 + hash gpg 2>/dev/null || return # Bail if not completing an option - [[ ${COMP_WORDS[COMP_CWORD]} == --* ]] || return 1 + case ${COMP_WORDS[COMP_CWORD]} in + --*) return 1 ;; + esac # Generate completion reply from gpg(1) options local option while read -r option ; do - [[ $option == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue - COMPREPLY[${#COMPREPLY[@]}]=$option + case $option in + "${COMP_WORDS[COMP_CWORD]}"*) + COMPREPLY[${#COMPREPLY[@]}]=$option + ;; + esac done < <(gpg --dump-options 2>/dev/null) } - -# bashdefault requires Bash >=3.0 complete -F _gpg -o bashdefault -o default gpg diff --git a/bash/bash_completion.d/grep.bash b/bash/bash_completion.d/grep.bash index 86c191cb..e9986b15 100644 --- a/bash/bash_completion.d/grep.bash +++ b/bash/bash_completion.d/grep.bash @@ -1,4 +1,5 @@ # Completion for grep(1) with files that look editable -declare -F _text_filenames >/dev/null || +if ! declare -F _text_filenames >/dev/null ; then source "$HOME"/.bash_completion.d/_text_filenames.bash +fi complete -F _text_filenames -o filenames grep diff --git a/bash/bash_completion.d/head.bash b/bash/bash_completion.d/head.bash index fa7cb878..0f4a7131 100644 --- a/bash/bash_completion.d/head.bash +++ b/bash/bash_completion.d/head.bash @@ -1,4 +1,5 @@ # Completion for head(1) with files that look editable -declare -F _text_filenames >/dev/null || +if ! declare -F _text_filenames >/dev/null ; then source "$HOME"/.bash_completion.d/_text_filenames.bash +fi complete -F _text_filenames -o filenames head diff --git a/bash/bash_completion.d/keep.bash b/bash/bash_completion.d/keep.bash index 77b37059..00b1469e 100644 --- a/bash/bash_completion.d/keep.bash +++ b/bash/bash_completion.d/keep.bash @@ -2,71 +2,56 @@ # stuff that's already kept _keep() { - # Default is to complete with function and variable names + # Determine what we're doing based on first completion word local mode - mode=names - - # Iterate through the words up to the previous word to figure out how to - # complete this one - local i - for ((i = 0; i < COMP_CWORD; i++)) ; do - case ${COMP_WORDS[i]} in - --) - mode=names - break - ;; - -d) - mode=kept - break - ;; + mode=keep + if ((COMP_CWORD > 1)) ; then + case ${COMP_WORDS[1]} in + -h) return 1 ;; + -d) mode=delete ;; esac - done + fi - # Complete with appropriate mode - case $mode in - names) - local word - while IFS= read -r word ; do - [[ -n $word ]] || continue - COMPREPLY[${#COMPREPLY[@]}]=$word - done < <(compgen -A function -A variable \ - -- "${COMP_WORDS[COMP_CWORD]}") - ;; - kept) - local word - while IFS= read -r word ; do - [[ -n $word ]] || continue - COMPREPLY[${#COMPREPLY[@]}]=$word - done < <( - shopt -s dotglob nullglob + # Collect words from an appropriate type of completion + local word + while read -r word ; do + [[ -n $word ]] || continue + COMPREPLY[${#COMPREPLY[@]}]=$word + done < <( - # Make globbing case-insensitive if appropriate; is there a cleaner way - # to find this value? - while read -r _ option value ; do - case $option in - (completion-ignore-case) - case $value in - (on) - shopt -s nocaseglob - break - ;; - esac + # Switch on second word; is it a -d option? + case $mode in + + # Keepable names: all functions and variables + (keep) + compgen -A function -A variable \ + -- "${COMP_WORDS[COMP_CWORD]}" + ;; + + # Kept names: .bash-suffixed names in keep dir + (delete) + # Make globs behave correctly + shopt -s nullglob + while read -r _ setting ; do + case $setting in + ('completion-ignore-case on') + shopt -s nocaseglob + break ;; esac done < <(bind -v) - keep=${BASHKEEP:-"$HOME"/.bashkeep.d} - declare -a keeps - keeps=("$keep"/"${COMP_WORDS[COMP_CWORD]}"*.bash) - keeps=("${keeps[@]##*/}") - keeps=("${keeps[@]%.bash}") - if ((${#keeps[@]})) ; then - printf '%s\n' "${keeps[@]}" - else - printf '\n' - fi - ) - ;; - esac + # Build list of kept names + dir=${BASHKEEP:-"$HOME"/.bashkeep.d} + cword=${COMP_WORDS[COMP_CWORD]} + kept=("$dir"/"$cword"*.bash) + kept=("${kept[@]##*/}") + kept=("${kept[@]%.bash}") + + # Print kept names + printf '%s\n' "${kept[@]}" + ;; + esac + ) } complete -F _keep keep diff --git a/bash/bash_completion.d/m4.bash b/bash/bash_completion.d/m4.bash index 5811fd5b..bb50f3b2 100644 --- a/bash/bash_completion.d/m4.bash +++ b/bash/bash_completion.d/m4.bash @@ -1,4 +1,5 @@ # Completion for m4(1) with files that look editable -declare -F _text_filenames >/dev/null || +if ! declare -F _text_filenames >/dev/null ; then source "$HOME"/.bash_completion.d/_text_filenames.bash +fi complete -F _text_filenames -o filenames m4 diff --git a/bash/bash_completion.d/mail.bash b/bash/bash_completion.d/mail.bash index 65c4ae80..5d1cdec0 100644 --- a/bash/bash_completion.d/mail.bash +++ b/bash/bash_completion.d/mail.bash @@ -1,6 +1,5 @@ # Completion for mail(1) with abook(1) email addresses -declare -F _abook_addresses >/dev/null || - source "$HOME"/.bash_completion.d/_abook_addresses.bash - -# bashdefault requires Bash >=3.0 +if ! declare -F _text_filenames >/dev/null ; then + source "$HOME"/.bash_completion.d/_text_filenames.bash +fi complete -F _abook_addresses -o bashdefault -o default mail diff --git a/bash/bash_completion.d/make.bash b/bash/bash_completion.d/make.bash index c36a039a..0f39ef4b 100644 --- a/bash/bash_completion.d/make.bash +++ b/bash/bash_completion.d/make.bash @@ -5,9 +5,10 @@ _make() { # first, then "Makefile"). You may want to add "GNU-makefile" after this. local mf for mf in makefile Makefile '' ; do - [[ -f $mf ]] && break + [[ -e $mf ]] || continue + break done - [[ -n $mf ]] || return 1 + [[ -n $mf ]] || return # Iterate through the Makefile, line by line local line @@ -39,7 +40,7 @@ _make() { *[^[:word:]./-]*) ;; # Add targets that match what we're completing - ${COMP_WORDS[COMP_CWORD]}*) + "${COMP_WORDS[COMP_CWORD]}"*) COMPREPLY[${#COMPREPLY[@]}]=$target ;; esac diff --git a/bash/bash_completion.d/man.bash b/bash/bash_completion.d/man.bash index 1efa7c52..ffef48ec 100644 --- a/bash/bash_completion.d/man.bash +++ b/bash/bash_completion.d/man.bash @@ -2,7 +2,7 @@ _man() { # Don't even bother if we don't have manpath(1) - hash manpath 2>/dev/null || return 1 + hash manpath 2>/dev/null || return # Snarf the word local word @@ -17,9 +17,13 @@ _man() { # 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 - if ((COMP_CWORD > 1)) && [[ ${COMP_WORDS[COMP_CWORD-1]} == [0-9]* ]] ; then - section=${COMP_WORDS[COMP_CWORD-1]} - subdir=man${section%%[^0-9]*} + if ((COMP_CWORD > 1)) ; then + case ${COMP_WORDS[COMP_CWORD-1]} in + [0-9]*) + section=${COMP_WORDS[COMP_CWORD-1]} + subdir=man${section%%[^0-9]*} + ;; + esac fi # Read completion results from a subshell and add them to the COMPREPLY @@ -35,17 +39,12 @@ _man() { shopt -u dotglob shopt -s extglob nullglob - # Make globbing case-insensitive if appropriate; is there a cleaner way - # to find this value? - while read -r _ option value ; do - case $option in - (completion-ignore-case) - case $value in - (on) - shopt -s nocaseglob - break - ;; - esac + # Make globbing case-insensitive if appropriate + while read -r _ setting ; do + case $setting in + ('completion-ignore-case on') + shopt -s nocaseglob + break ;; esac done < <(bind -v) @@ -59,7 +58,9 @@ _man() { for manpath in "${manpaths[@]}" ; do [[ -n $manpath ]] || continue if [[ -n $section ]] ; then - for page in "$manpath"/"$subdir"/"$word"*."$section"?(.[glx]z|.bz2|.lzma|.Z) ; do + for page in \ + "$manpath"/"$subdir"/"$word"*."$section"?(.[glx]z|.bz2|.lzma|.Z) + do pages[${#pages[@]}]=$page done else @@ -74,14 +75,8 @@ _man() { pages=("${pages[@]%.@([glx]z|bz2|lzma|Z)}") pages=("${pages[@]%.[0-9]*}") - # Print quoted entries, null-delimited, if there was at least one; - # otherwise, just print a null character to stop this hanging in Bash - # 4.4 - if ((${#pages[@]})) ; then - printf '%q\0' "${pages[@]}" - else - printf '\0' - fi + # Print quoted entries, null-delimited + printf '%q\0' "${pages[@]}" ) } complete -F _man -o bashdefault -o default man diff --git a/bash/bash_completion.d/mex.bash b/bash/bash_completion.d/mex.bash index d25f1824..bc3d2c7b 100644 --- a/bash/bash_completion.d/mex.bash +++ b/bash/bash_completion.d/mex.bash @@ -6,7 +6,8 @@ _mex() { for dir in "${path[@]}" ; do [[ -d $dir ]] || continue for name in "$dir"/* ; do - [[ -f $name ]] || continue + [[ -e $name ]] || continue + ! [[ -d $name ]] || continue ! [[ -x $name ]] || continue COMPREPLY[${#COMPREPLY[@]}]=${name##*/} done diff --git a/bash/bash_completion.d/mutt.bash b/bash/bash_completion.d/mutt.bash index c7f02ac7..002eb48e 100644 --- a/bash/bash_completion.d/mutt.bash +++ b/bash/bash_completion.d/mutt.bash @@ -1,4 +1,5 @@ # Completion for mutt(1) with abook(1) email addresses -declare -F _abook_addresses >/dev/null || +if ! declare -F _abook_addresses >/dev/null ; then source "$HOME"/.bash_completion.d/_abook_addresses.bash +fi complete -F _abook_addresses -o bashdefault -o default mutt diff --git a/bash/bash_completion.d/mysql.bash b/bash/bash_completion.d/mysql.bash index ad153adc..3ff97090 100644 --- a/bash/bash_completion.d/mysql.bash +++ b/bash/bash_completion.d/mysql.bash @@ -2,12 +2,12 @@ _mysql() { # Only makes sense for first argument - ((COMP_CWORD == 1)) || return 1 + ((COMP_CWORD == 1)) || return # Bail if directory doesn't exist local dirname dirname=$HOME/.mysql - [[ -d $dirname ]] || return 1 + [[ -d $dirname ]] || return # Return the names of the .cnf files sans prefix as completions local db @@ -19,17 +19,12 @@ _mysql() { # Set options so that globs expand correctly shopt -s dotglob nullglob - # Make globbing case-insensitive if appropriate; is there a cleaner way - # to find this value? - while read -r _ option value ; do - case $option in - (completion-ignore-case) - case $value in - (on) - shopt -s nocaseglob - break - ;; - esac + # Make globbing case-insensitive if appropriate + while read -r _ setting ; do + case $setting in + ('completion-ignore-case on') + shopt -s nocaseglob + break ;; esac done < <(bind -v) @@ -40,14 +35,8 @@ _mysql() { cnfs=("${cnfs[@]#"$dirname"/}") cnfs=("${cnfs[@]%.cnf}") - # Print quoted entries, null-delimited, if there was at least one; - # otherwise, just print a null character to stop this hanging in Bash - # 4.4 - if ((${#cnfs[@]})) ; then - printf '%q\0' "${cnfs[@]}" - else - printf '\0' - fi + # Print quoted entries, null-delimited + printf '%q\0' "${cnfs[@]}" ) } complete -F _mysql -o bashdefault -o default mysql diff --git a/bash/bash_completion.d/pass.bash b/bash/bash_completion.d/pass.bash index ec5959be..176886dc 100644 --- a/bash/bash_completion.d/pass.bash +++ b/bash/bash_completion.d/pass.bash @@ -8,7 +8,7 @@ _pass() # If we can't read the password directory, just bail local passdir passdir=${PASSWORD_STORE_DIR:-"$HOME"/.password-store} - [[ -r $passdir ]] || return 1 + [[ -r $passdir ]] || return # Iterate through list of .gpg paths, extension stripped, null-delimited, # and filter them down to the ones matching the completing word (compgen @@ -16,24 +16,19 @@ _pass() local entry while IFS= read -rd '' entry ; do [[ -n $entry ]] || continue - COMPREPLY[${#COMPREPLY[@]}]=$entry + COMPREPLY+=("$entry") done < <( # Set shell options to expand globs the way we expect shopt -u dotglob shopt -s globstar nullglob - # Make globbing case-insensitive if appropriate; is there a cleaner way - # to find this value? - while read -r _ option value ; do - case $option in - (completion-ignore-case) - case $value in - (on) - shopt -s nocaseglob - break - ;; - esac + # Make globbing case-insensitive if appropriate + while read -r _ setting ; do + case $setting in + ('completion-ignore-case on') + shopt -s nocaseglob + break ;; esac done < <(bind -v) @@ -45,14 +40,8 @@ _pass() entries=("${entries[@]#"$passdir"/}") entries=("${entries[@]%.gpg}") - # Print quoted entries, null-delimited, if there was at least one; - # otherwise, just print a null character to stop this hanging in Bash - # 4.4 - if ((${#entries[@]})) ; then - printf '%q\0' "${entries[@]}" - else - printf '\0' - fi + # Print quoted entries, null-delimited + printf '%q\0' "${entries[@]}" ) } complete -F _pass pass diff --git a/bash/bash_completion.d/path.bash b/bash/bash_completion.d/path.bash index 8b72a062..7143b448 100644 --- a/bash/bash_completion.d/path.bash +++ b/bash/bash_completion.d/path.bash @@ -6,10 +6,18 @@ _path() { # Complete operation as first word local cmd - for cmd in list insert append remove shift pop check help ; do - [[ $cmd == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue + while read -r cmd ; do COMPREPLY[${#COMPREPLY[@]}]=$cmd - done + done < <(compgen -W ' + append + check + help + insert + list + pop + remove + shift + ' -- "${COMP_WORDS[COMP_CWORD]}") # Complete with either directories or $PATH entries as all other words else @@ -26,17 +34,12 @@ _path() { # Set options to glob correctly shopt -s dotglob nullglob - # Make globbing case-insensitive if appropriate; is there a cleaner way - # to find this value? - while read -r _ option value ; do - case $option in - (completion-ignore-case) - case $value in - (on) - shopt -s nocaseglob - break - ;; - esac + # Make globbing case-insensitive if appropriate + while read -r _ setting ; do + case $setting in + ('completion-ignore-case on') + shopt -s nocaseglob + break ;; esac done < <(bind -v) @@ -46,25 +49,23 @@ _path() { dirnames=("${COMP_WORDS[COMP_CWORD]}"*/) dirnames=("${dirnames[@]%/}") - # Print quoted entries, null-delimited, if there was at - # least one; otherwise, just print a null character to stop - # this hanging in Bash 4.4 - if ((${#dirnames[@]})) ; then - printf '%q\0' "${dirnames[@]}" - else - printf '\0' - fi + # Print quoted entries, null-delimited + printf '%q\0' "${dirnames[@]}" ) ;; # Complete with directories from PATH remove) local -a promptarr - IFS=: read -rd '' -a promptarr < <(printf '%s\0' "$PATH") + IFS=: read -rd '' -a promptarr < \ + <(printf '%s\0' "$PATH") local part for part in "${promptarr[@]}" ; do - [[ $part == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue - COMPREPLY[${#COMPREPLY[@]}]=$(printf '%q' "$part") + case $part in + "${COMP_WORDS[COMP_CWORD]}"*) + COMPREPLY[${#COMPREPLY[@]}]=$(printf '%q' "$part") + ;; + esac done ;; diff --git a/bash/bash_completion.d/sd.bash b/bash/bash_completion.d/sd.bash index 578a69fd..e7e82f80 100644 --- a/bash/bash_completion.d/sd.bash +++ b/bash/bash_completion.d/sd.bash @@ -2,10 +2,12 @@ _sd() { # Only makes sense for the first argument - ((COMP_CWORD == 1)) || return 1 + ((COMP_CWORD == 1)) || return # Current directory can't be root directory - [[ $PWD != / ]] || return 1 + case $PWD in + /) return 1 ;; + esac # Build list of matching sibling directories local dirname @@ -17,17 +19,12 @@ _sd() { # Set options to glob correctly shopt -s dotglob nullglob - # Make globbing case-insensitive if appropriate; is there a cleaner way - # to find this value? - while read -r _ option value ; do - case $option in - (completion-ignore-case) - case $value in - (on) - shopt -s nocaseglob - break - ;; - esac + # Make globbing case-insensitive if appropriate + while read -r _ setting ; do + case $setting in + ('completion-ignore-case on') + shopt -s nocaseglob + break ;; esac done < <(bind -v) @@ -42,18 +39,14 @@ _sd() { local -a sibs local dirname for dirname in "${dirnames[@]}" ; do - [[ $dirname != "${PWD##*/}" ]] || continue - sibs[${#sibs[@]}]=$dirname + case $dirname in + "${PWD##*/}") ;; + *) sibs[${#sibs[@]}]=$dirname ;; + esac done - # Print quoted sibs, null-delimited, if there was at least one; - # otherwise, just print a null character to stop this hanging in Bash - # 4.4 - if ((${#sibs[@]})) ; then - printf '%q\0' "${sibs[@]}" - else - printf '\0' - fi + # Print quoted sibling directories, null-delimited + printf '%q\0' "${sibs[@]}" ) } complete -F _sd sd diff --git a/bash/bash_completion.d/sftp.bash b/bash/bash_completion.d/sftp.bash index ad4d406f..b54b06b7 100644 --- a/bash/bash_completion.d/sftp.bash +++ b/bash/bash_completion.d/sftp.bash @@ -1,4 +1,5 @@ # Completion for sftp(1) with ssh_config(5) hostnames -declare -F _ssh_config_hosts >/dev/null || +if ! declare -F _ssh_config_hosts >/dev/null ; then source "$HOME"/.bash_completion.d/_ssh_config_hosts.bash +fi complete -F _ssh_config_hosts -o bashdefault -o default sftp diff --git a/bash/bash_completion.d/ssh-copy-id.bash b/bash/bash_completion.d/ssh-copy-id.bash index 336df4ea..cbecfa5f 100644 --- a/bash/bash_completion.d/ssh-copy-id.bash +++ b/bash/bash_completion.d/ssh-copy-id.bash @@ -1,4 +1,5 @@ # Completion for ssh-copy-id(1) with ssh_config(5) hostnames -declare -F _ssh_config_hosts >/dev/null || +if ! declare -F _ssh_config_hosts >/dev/null ; then source "$HOME"/.bash_completion.d/_ssh_config_hosts.bash +fi complete -F _ssh_config_hosts -o bashdefault -o default ssh-copy-id diff --git a/bash/bash_completion.d/ssh.bash b/bash/bash_completion.d/ssh.bash index 7ec82596..6d327c91 100644 --- a/bash/bash_completion.d/ssh.bash +++ b/bash/bash_completion.d/ssh.bash @@ -1,4 +1,5 @@ # Completion for ssh(1) with ssh_config(5) hostnames -declare -F _ssh_config_hosts >/dev/null || +if ! declare -F _ssh_config_hosts >/dev/null ; then source "$HOME"/.bash_completion.d/_ssh_config_hosts.bash +fi complete -F _ssh_config_hosts -o bashdefault -o default ssh diff --git a/bash/bash_completion.d/td.bash b/bash/bash_completion.d/td.bash index 377ef6ce..92927c28 100644 --- a/bash/bash_completion.d/td.bash +++ b/bash/bash_completion.d/td.bash @@ -29,14 +29,8 @@ _td() { fns=("$dir"/"${COMP_WORDS[COMP_CWORD]}"*) fns=("${fns[@]#"$dir"/}") - # Print quoted entries, null-delimited, if there was at least one; - # otherwise, just print a null character to stop this hanging in Bash - # 4.4 - if ((${#fns[@]})) ; then - printf '%q\0' "${fns[@]}" - else - printf '\0' - fi + # Print quoted entries, null-delimited + printf '%q\0' "${fns[@]}" ) } complete -F _td td diff --git a/bash/bash_completion.d/ud.bash b/bash/bash_completion.d/ud.bash index aa59a4fc..c7dee582 100644 --- a/bash/bash_completion.d/ud.bash +++ b/bash/bash_completion.d/ud.bash @@ -2,40 +2,32 @@ _ud() { # Only makes sense for the second argument - ((COMP_CWORD == 2)) || return 1 + ((COMP_CWORD == 2)) || return # Iterate through directories, null-separated, add them to completions local dirname while IFS= read -rd '' dirname ; do + [[ -n "$dirname" ]] || continue COMPREPLY[${#COMPREPLY[@]}]=$dirname done < <( # Set options to glob correctly shopt -s dotglob nullglob - # Make globbing case-insensitive if appropriate; is there a cleaner way - # to find this value? - while read -r _ option value ; do - case $option in - (completion-ignore-case) - case $value in - (on) - shopt -s nocaseglob - break - ;; - esac + # Make globbing case-insensitive if appropriate + while read -r _ setting ; do + case $setting in + ('completion-ignore-case on') + shopt -s nocaseglob + break ;; esac done < <(bind -v) # Collect directory names, strip trailing slashes - local -a dirnames dirnames=("${COMP_WORDS[COMP_CWORD]}"*/) dirnames=("${dirnames[@]%/}") - # Bail if no results to prevent empty output - ((${#dirnames[@]})) || exit 1 - # Print results null-delimited printf '%s\0' "${dirnames[@]}" ) diff --git a/bash/bashrc b/bash/bashrc index dcf3df53..3b4c91bd 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -6,9 +6,7 @@ esac # Don't do anything if restricted, not even sourcing the ENV file # Testing $- for "r" doesn't work -if shopt -q restricted_shell >/dev/null 2>&1 ; then - return -fi +! shopt -q restricted_shell >/dev/null 2>&1 || 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/check/login-shell.sh b/check/login-shell.sh index 2972d98d..88eac59e 100644 --- a/check/login-shell.sh +++ b/check/login-shell.sh @@ -1,10 +1,12 @@ -target=check-sh -case ${SHELL##*/} in +shell=$(getent passwd "$USER" | cut -d: -f7) +case ${shell##*/} in bash) target=check-bash ;; ksh|ksh88|ksh93|mksh|pdksh) target=check-ksh ;; zsh) target=check-zsh ;; + *) + target=check-sh ;; esac make "$target" diff --git a/install/install-conf.sh b/install/conf.sh index f50cde73..f50cde73 100644 --- a/install/install-conf.sh +++ b/install/conf.sh diff --git a/install/install-login-shell.sh b/install/login-shell.sh index f38aa0c1..54741fde 100644 --- a/install/install-login-shell.sh +++ b/install/login-shell.sh @@ -1,10 +1,12 @@ -target=install-sh -case ${SHELL##*/} in +shell=$(getent passwd "$USER" | cut -d: -f7) +case ${shell##*/} in bash) target=install-bash ;; ksh|ksh88|ksh93|mksh|pdksh) target=install-ksh ;; zsh) target=install-zsh ;; + *) + target=install-sh ;; esac make "$target" |