--- /dev/null
+# fscrypt_bash_completion
+#
+# Copyright 2017 Google Inc.
+# Author: Henry-Joseph Audéoud (h.audeoud@gmail.com)
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+
+# Prefer completion script style COMPREPLY=($(...)) assignment.
+# shellcheck disable=SC2207
+true # To apply shellcheck directive to all file
+
+
+# Output list of possible mount points
+_fscrypt_mountpoints()
+{
+ # shellcheck disable=SC2016
+ fscrypt status 2>/dev/null | \
+ command awk 'substr($0, 1, 1) == "/" && $5 == "Yes" { print $1 }'
+}
+
+
+# Complete with all possible mountpoints
+_fscrypt_complete_mountpoint()
+{
+ COMPREPLY=($(compgen -W "$(_fscrypt_mountpoints)" -- "${cur}"))
+}
+
+
+# Output list of possible policy or protector IDs
+# $1: the mount point on which policies are looked for.
+# $2: the section (policy or protector) to retrieve
+_fscrypt_status_section()
+{
+ local section=${2^^}
+ # shellcheck disable=SC2016
+ fscrypt status "$1" 2>/dev/null | \
+ command awk '/^[[:xdigit:]]{16}/ && section == "'"$section"'" { print $1; next; }
+ { section = $1 }'
+}
+
+
+# Complete with policies or protectors
+_fscrypt_complete_policy_or_protector()
+{
+ local status_section="$1"
+ if [[ $cur = *:* ]]; then
+ # Complete with IDs of the given mountpoint
+ local mountpoint="${cur%:*}" id="${cur#*:}"
+ COMPREPLY=($(compgen \
+ -W "$(_fscrypt_status_section "${mountpoint}" "${status_section}")" \
+ -- "${id}"))
+ else
+ # Complete with mountpoints, with colon and without ending space
+ COMPREPLY=($(compgen -W "$(_fscrypt_mountpoints)" \
+ -- "${cur}" | sed s/\$/:/))
+ compopt -o nospace
+ fi
+}
+
+
+# Complete with all arguments of that function
+_fscrypt_complete_word()
+{
+ COMPREPLY=($(compgen -W "$*" -- "${cur}"))
+}
+
+
+# Complete with all arguments of that function, plus global options
+_fscrypt_complete_option()
+{
+ local additional_opts=( "$@" )
+ # Add global options, always correct
+ additional_opts+=( --verbose --quiet --help )
+ COMPREPLY=($(compgen -W "${additional_opts[*]}" -- "${cur}"))
+}
+
+
+_fscrypt()
+{
+ # Initialize completion: compute some local variables to easily
+ # detect what is written on the command line. -s is for splitting
+ # long options on `=`, and -n is for splitting them also on `:`
+ # (used in the protectors/policies `MOUNTPOINT:ID` forms).
+ #
+ # `split` is set by `_init_completion -s`, we must declare it local
+ # even if we don't use it, not to modify the environment.
+ # shellcheck disable=SC2034
+ local cur prev words cword split
+ _init_completion -s -n : || return
+
+ # Complete the options with argument here, if previous word were such
+ # an option. It would be too difficult to check if they take place in
+ # the correct command (such as `fscrypt status # --key ...`)—and that
+ # is the command's job—so just complete them first.
+ case $prev in
+ --key)
+ # Any file is accepted
+ _filedir
+ return ;;
+ --name)
+ # New value, nothing to complete
+ return ;;
+ --policy|--protector|--unlock-with)
+ local p_or_p="${prev#--}"
+ [[ $p_or_p = unlock-with ]] && p_or_p=protector
+ _fscrypt_complete_policy_or_protector "${p_or_p}"
+ return ;;
+ --source)
+ # Complete with keywords
+ _fscrypt_complete_word \
+ pam_passphrase custom_passphrase raw_key
+ return ;;
+ --time)
+ # It's a time, hard to complete a number…
+ return ;;
+ --user)
+ # Complete with a user
+ COMPREPLY=($(compgen -u -- "${cur}"))
+ return ;;
+ esac
+
+ # Fetch positional arguments (i.e. subcommands)
+ local positional
+ positional=()
+ local iword
+ for ((iword = 1; iword < ${#words[@]} - 1; iword++)); do
+ [[ ${words[iword - 1]} == --@(key|name|policy|protector|unlock-with|source|time|user) ]] \
+ && continue # Argument of previous option, skip
+ [[ ${words[iword]} == -* ]] && continue # Option, skip
+ positional+=("${words[iword]}")
+ done
+
+ # If completing the first positional, complete with all possible commands
+ if [[ ${#positional[@]} == 0 ]]; then
+ if [[ $cur == -* ]]; then
+ _fscrypt_complete_option
+ else
+ _fscrypt_complete_word \
+ encrypt lock metadata purge setup status unlock
+ fi
+ return
+ fi
+
+ # Complete according to that provided
+ case ${positional[0]-} in
+ encrypt) # Directory or option
+ if [[ $cur == -* ]]; then
+ _fscrypt_complete_option \
+ --policy= --unlock-with= --protector= --source= \
+ --user= --name= --key= --skip-unlock --no-recovery
+ else
+ _filedir -d
+ fi ;;
+ lock) # Directory or option
+ if [[ $cur == -* ]]; then
+ _fscrypt_complete_option --user= --all-users
+ else
+ _filedir -d
+ fi ;;
+ purge) # Mountpoint or options
+ if [[ $cur == -* ]]; then
+ _fscrypt_complete_option --user= --force
+ else
+ _fscrypt_complete_mountpoint
+ fi ;;
+ setup) # Mountpoint or options
+ if [[ $cur == -* ]]; then
+ _fscrypt_complete_option --time= --force
+ else
+ _fscrypt_complete_mountpoint
+ fi ;;
+ status) # Directory (only global options for this command)
+ if [[ $cur == -* ]]; then
+ _fscrypt_complete_option
+ else
+ _filedir -d
+ fi ;;
+ unlock) # Directory or option
+ if [[ $cur == -* ]]; then
+ _fscrypt_complete_option --unlock-with= --user= --key=
+ else
+ _filedir -d
+ fi ;;
+ metadata)
+ # This command has subcommands
+ if [[ ${#positional[@]} = 1 ]]; then
+ if [[ $cur = -* ]]; then
+ _fscrypt_complete_option
+ else
+ # Still no subcommand, complete with them
+ _fscrypt_complete_word \
+ add-protector-to-policy create change-passphrase \
+ destroy dump remove-protector-from-policy
+ fi
+ return
+ fi
+ # We have a subcommand, complete according to it
+ case ${positional[1]-} in
+ add-protector-to-policy) # Options only
+ _fscrypt_complete_option \
+ --protector= --policy= --unlock-with= --key=
+ ;;
+ change-passphrase) # Options only
+ _fscrypt_complete_option --protector=
+ ;;
+ destroy) # Mountpoint or option
+ if [[ $cur == -* ]]; then
+ _fscrypt_complete_option \
+ --protector= --policy= --force
+ else
+ _fscrypt_complete_mountpoint
+ fi ;;
+ dump) # Options only
+ _fscrypt_complete_option --protector= --policy=
+ ;;
+ remove-protector-from-policy) # Options only
+ _fscrypt_complete_option \
+ --protector= --policy= --force
+ ;;
+ create)
+ # This subcommand has subsubcommands
+ if [[ ${#positional[@]} = 2 ]]; then
+ if [[ $cur = -* ]]; then
+ _fscrypt_complete_option
+ else
+ # Still no subcommand, complete with them
+ _fscrypt_complete_word protector policy
+ fi
+ return
+ fi
+ # We have a subsubcommand, complete according to it
+ case ${positional[2]-} in
+ policy) # Mountpoint or option
+ if [[ $cur = -* ]]; then
+ _fscrypt_complete_option --protector= --key=
+ else
+ _fscrypt_complete_mountpoint
+ fi ;;
+ protector) # Mountpoint or option
+ if [[ $cur = -* ]]; then
+ _fscrypt_complete_option \
+ --source= --name= --key= --user=
+ else
+ _fscrypt_complete_mountpoint
+ fi ;;
+ *)
+ # Unrecognized subsubcommand… Suppose a new
+ # unknown subsubcommand and complete with
+ # global options only
+ _fscrypt_complete_option
+ ;;
+ esac
+ ;;
+ *)
+ # Unrecognized subcommand… Suppose a new unknown
+ # subcommand and complete with global options only
+ _fscrypt_complete_option
+ ;;
+ esac
+ ;;
+ *)
+ # Unrecognized command… Suppose a new unknown command and
+ # complete with global options only
+ _fscrypt_complete_option
+ ;;
+ esac
+
+ # When the sole offered completion is --*=, do not put a space after
+ # the equal sign as we wait for the argument value.
+ [[ ${#COMPREPLY[@]} == 1 ]] && [[ ${COMPREPLY[0]} == "--"*"=" ]] \
+ && compopt -o nospace
+} &&
+ complete -F _fscrypt fscrypt
+
+# ex: filetype=bash