# Copyright (c) 2013, Aneurin Price # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without # restriction, including without limitation the rights to use, # copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following # conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. if [[ -w /dev/zfs ]]; then __ZFS_CMD="zfs" __ZPOOL_CMD="zpool" else __ZFS_CMD="sudo zfs" __ZPOOL_CMD="sudo zpool" fi __zfs_get_commands() { $__ZFS_CMD 2>&1 | awk '/^\t[a-z]/ {print $1}' | cut -f1 -d '|' | uniq } __zfs_get_properties() { $__ZFS_CMD get 2>&1 | awk '$2 == "YES" || $2 == "NO" {print $1}'; echo all name space } __zfs_get_editable_properties() { $__ZFS_CMD get 2>&1 | awk '$2 == "YES" {print $1"="}' } __zfs_get_inheritable_properties() { $__ZFS_CMD get 2>&1 | awk '$3 == "YES" {print $1}' } __zfs_list_datasets() { $__ZFS_CMD list -H -o name -t filesystem,volume } __zfs_list_filesystems() { $__ZFS_CMD list -H -o name -t filesystem } __zfs_match_snapshot() { local base_dataset=${cur%@*} if [[ $base_dataset != $cur ]] then $__ZFS_CMD list -H -o name -t snapshot -d 1 $base_dataset else $__ZFS_CMD list -H -o name -t filesystem,volume | awk '{print $1"@"}' fi } __zfs_match_explicit_snapshot() { local base_dataset=${cur%@*} if [[ $base_dataset != $cur ]] then $__ZFS_CMD list -H -o name -t snapshot -d 1 $base_dataset fi } __zfs_match_multiple_snapshots() { local existing_opts=$(expr "$cur" : '\(.*\)[%,]') if [[ $existing_opts ]] then local base_dataset=${cur%@*} if [[ $base_dataset != $cur ]] then local cur=${cur##*,} if [[ $cur =~ ^%|%.*% ]] then # correct range syntax is start%end return 1 fi local range_start=$(expr "$cur" : '\(.*%\)') $__ZFS_CMD list -H -o name -t snapshot -d 1 $base_dataset | sed 's$.*@$'$range_start'$g' fi else __zfs_match_explicit_snapshot; __zfs_list_datasets fi } __zfs_list_volumes() { $__ZFS_CMD list -H -o name -t volume } __zfs_argument_chosen() { local word property for word in $(seq $((COMP_CWORD-1)) -1 2) do local prev="${COMP_WORDS[$word]}" if [[ ${COMP_WORDS[$word-1]} != -[tos] ]] then if [[ "$prev" == [^,]*,* ]] || [[ "$prev" == *[@:]* ]] then return 0 fi for property in $@ do if [[ $prev == "$property" ]] then return 0 fi done fi done return 1 } __zfs_complete_ordered_arguments() { local list1=$1 local list2=$2 local cur=$3 local extra=$4 if __zfs_argument_chosen $list1 then COMPREPLY=($(compgen -W "$list2 $extra" -- "$cur")) else COMPREPLY=($(compgen -W "$list1 $extra" -- "$cur")) fi } __zfs_complete_multiple_options() { local options=$1 local cur=$2 COMPREPLY=($(compgen -W "$options" -- "${cur##*,}")) local existing_opts=$(expr "$cur" : '\(.*,\)') if [[ $existing_opts ]] then COMPREPLY=( "${COMPREPLY[@]/#/${existing_opts}}" ) fi } __zfs_complete_switch() { local options=$1 if [[ ${cur:0:1} == - ]] then COMPREPLY=($(compgen -W "-{$options}" -- "$cur")) return 0 else return 1 fi } __zfs_complete() { local cur prev cmd cmds COMPREPLY=() # Don't split on colon _get_comp_words_by_ref -n : -c cur -p prev -w COMP_WORDS -i COMP_CWORD cmd="${COMP_WORDS[1]}" if [[ ${prev##*/} == zfs ]] then cmds=$(__zfs_get_commands) COMPREPLY=($(compgen -W "$cmds -?" -- "$cur")) return 0 fi case "${cmd}" in clone) case "${prev}" in -o) COMPREPLY=($(compgen -W "$(__zfs_get_editable_properties)" -- "$cur")) ;; *) if ! __zfs_complete_switch "o,p" then if __zfs_argument_chosen then COMPREPLY=($(compgen -W "$(__zfs_list_datasets)" -- "$cur")) else COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) fi fi ;; esac ;; get) case "${prev}" in -d) COMPREPLY=($(compgen -W "" -- "$cur")) ;; -t) __zfs_complete_multiple_options "filesystem volume snapshot bookmark all" "$cur" ;; -s) __zfs_complete_multiple_options "local default inherited temporary received none" "$cur" ;; -o) __zfs_complete_multiple_options "name property value source received all" "$cur" ;; *) if ! __zfs_complete_switch "H,r,p,d,o,t,s" then if __zfs_argument_chosen $(__zfs_get_properties) then COMPREPLY=($(compgen -W "$(__zfs_match_explicit_snapshot) $(__zfs_list_datasets)" -- "$cur")) else __zfs_complete_multiple_options "$(__zfs_get_properties)" "$cur" fi fi ;; esac ;; inherit) if ! __zfs_complete_switch "r" then __zfs_complete_ordered_arguments "$(__zfs_get_inheritable_properties)" "$(__zfs_match_explicit_snapshot) $(__zfs_list_datasets)" $cur fi ;; list) case "${prev}" in -d) COMPREPLY=($(compgen -W "" -- "$cur")) ;; -t) __zfs_complete_multiple_options "filesystem volume snapshot bookmark all" "$cur" ;; -o) __zfs_complete_multiple_options "$(__zfs_get_properties)" "$cur" ;; -s|-S) COMPREPLY=($(compgen -W "$(__zfs_get_properties)" -- "$cur")) ;; *) if ! __zfs_complete_switch "H,r,d,o,t,s,S" then COMPREPLY=($(compgen -W "$(__zfs_match_explicit_snapshot) $(__zfs_list_datasets)" -- "$cur")) fi ;; esac ;; promote) COMPREPLY=($(compgen -W "$(__zfs_list_filesystems)" -- "$cur")) ;; rollback) if ! __zfs_complete_switch "r,R,f" then COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) fi ;; send) if ! __zfs_complete_switch "d,n,P,p,R,v,i,I" then COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) fi ;; snapshot) case "${prev}" in -o) COMPREPLY=($(compgen -W "$(__zfs_get_editable_properties)" -- "$cur")) ;; *) if ! __zfs_complete_switch "o,r" then COMPREPLY=($(compgen -W "$(__zfs_list_datasets | awk '{print $1"@"}')" -- "$cur")) fi ;; esac ;; set) __zfs_complete_ordered_arguments "$(__zfs_get_editable_properties)" "$(__zfs_match_explicit_snapshot) $(__zfs_list_datasets)" $cur ;; upgrade) case "${prev}" in -a|-V|-v) COMPREPLY=($(compgen -W "" -- "$cur")) ;; *) if ! __zfs_complete_switch "a,V,v,r" then COMPREPLY=($(compgen -W "$(__zfs_list_filesystems)" -- "$cur")) fi ;; esac ;; destroy) if ! __zfs_complete_switch "d,f,n,p,R,r,v" then __zfs_complete_multiple_options "$(__zfs_match_multiple_snapshots)" $cur fi ;; *) COMPREPLY=($(compgen -W "$(__zfs_match_explicit_snapshot) $(__zfs_list_datasets)" -- "$cur")) ;; esac __ltrim_colon_completions "$cur" return 0 } __zpool_get_commands() { $__ZPOOL_CMD 2>&1 | awk '/^\t[a-z]/ {print $1}' | uniq } __zpool_get_properties() { $__ZPOOL_CMD get 2>&1 | awk '$2 == "YES" || $2 == "NO" {print $1}'; echo all } __zpool_get_editable_properties() { $__ZPOOL_CMD get 2>&1 | awk '$2 == "YES" {print $1"="}' } __zpool_list_pools() { $__ZPOOL_CMD list -H -o name } __zpool_complete() { local cur prev cmd cmds COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" cmd="${COMP_WORDS[1]}" if [[ ${prev##*/} == zpool ]] then cmds=$(__zpool_get_commands) COMPREPLY=($(compgen -W "$cmds" -- "$cur")) return 0 fi case "${cmd}" in get) __zfs_complete_ordered_arguments "$(__zpool_get_properties)" "$(__zpool_list_pools)" $cur return 0 ;; import) if [[ $prev == -d ]] then _filedir -d else COMPREPLY=($(compgen -W "$(__zpool_list_pools) -d" -- "$cur")) fi return 0 ;; set) __zfs_complete_ordered_arguments "$(__zpool_get_editable_properties)" "$(__zpool_list_pools)" $cur return 0 ;; add|attach|clear|create|detach|offline|online|remove|replace) local pools="$(__zpool_list_pools)" if __zfs_argument_chosen $pools then _filedir else COMPREPLY=($(compgen -W "$pools" -- "$cur")) fi return 0 ;; *) COMPREPLY=($(compgen -W "$(__zpool_list_pools)" -- "$cur")) return 0 ;; esac } complete -F __zfs_complete zfs complete -F __zpool_complete zpool