github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/data/completion/bash/complete.sh (about) 1 # shellcheck shell=bash 2 # 3 # Copyright (C) 2017 Canonical Ltd 4 # 5 # This program is free software: you can redistribute it and/or modify 6 # it under the terms of the GNU General Public License version 3 as 7 # published by the Free Software Foundation. 8 # 9 # This program is distributed in the hope that it will be useful, 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 # GNU General Public License for more details. 13 # 14 # You should have received a copy of the GNU General Public License 15 # along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 # _complete_from_snap performs the tab completion request by calling the 18 # appropriate 'snap run --command=complete' with serialized args, and 19 # deserializes the response into the usual tab completion result. 20 # 21 # How snap command completion works is: 22 # 1. snapd's complete.sh is sourced into the user's shell environment 23 # 2. user performs '<command> <tab>'. If '<command>' is a snap command, 24 # proceed to step '3', otherwise perform normal bash completion 25 # 3. run 'snap run --command=complete ...', converting bash completion 26 # environment into serialized command line arguments 27 # 4. 'snap run --command=complete ...' exec()s 'etelpmoc.sh' within the snap's 28 # runtime environment and confinement 29 # 5. 'etelpmoc.sh' takes the serialized command line arguments from step '3' 30 # and puts them back into the bash completion environment variables 31 # 6. 'etelpmoc.sh' sources the snap's 'completer' script, performs the bash 32 # completion and serializes the resulting completion environment variables 33 # by printing to stdout the results in a format that snapd's complete.sh 34 # will understand, then exits 35 # 7. control returns to snapd's 'complete.sh' and it deserializes the output 36 # from 'etelpmoc.sh', validates the results and puts the validated results 37 # into the bash completion environment variables 38 # 8. bash displays the results to the user 39 type -t _complete_from_snap > /dev/null || 40 _complete_from_snap() { 41 { 42 # De-serialize the output of 'snap run --command=complete ...' into the format 43 # bash expects: 44 read -r -a opts 45 # opts is expected to be a series of compopt options 46 if [[ ${#opts[@]} -gt 0 ]]; then 47 if [[ "${opts[0]}" == "cannot" ]]; then 48 # older snap-execs sent errors over stdout :-( 49 return 1 50 fi 51 52 for i in "${opts[@]}"; do 53 if ! [[ "$i" =~ ^[a-z]+$ ]]; then 54 # only lowercase alpha characters allowed 55 return 2 56 fi 57 done 58 fi 59 60 read -r bounced 61 case "$bounced" in 62 ""|"alias"|"export"|"job"|"variable") 63 # OK 64 ;; 65 *) 66 # unrecognised bounce 67 return 2 68 ;; 69 esac 70 71 read -r sep 72 if [ -n "$sep" ]; then 73 # non-blank separator? madness! 74 return 2 75 fi 76 local oldIFS="$IFS" 77 78 if [ ! "$bounced" ]; then 79 local IFS=$'\n' 80 # Ignore any suspicious results that are uncommon in filenames and that 81 # might be used to trick the user. A whitelist approach would be better 82 # but is impractical with UTF-8 and common characters like quotes. 83 COMPREPLY=( $( command grep -v '[[:cntrl:];&?*{}]' ) ) 84 IFS="$oldIFS" 85 fi 86 87 if [[ ${#opts[@]} -gt 0 ]]; then 88 # shellcheck disable=SC2046 89 # (we *want* word splitting to happen here) 90 compopt $(printf " -o %s" "${opts[@]}") 91 fi 92 if [ "$bounced" ]; then 93 # We validated '$bounced' above and '${COMP_WORDS[$COMP_CWORD]}' is 94 # coming from the user's session, not the snap so skip input 95 # validation: we aren't trying to protect the user from themselves. 96 COMPREPLY+=(compgen -A "$bounced" -- "${COMP_WORDS[$COMP_CWORD]}") 97 fi 98 } < <( 99 snap run --command=complete "$1" "$COMP_TYPE" "$COMP_KEY" "$COMP_POINT" "$COMP_CWORD" "$COMP_WORDBREAKS" "$COMP_LINE" "${COMP_WORDS[@]}" 2>/dev/null || return 1 100 ) 101 102 } 103 104 # this file can be sourced directly as e.g. /usr/lib/snapd/complete.sh, or via 105 # a symlink from /usr/share/bash-completion/completions/. In the first case we 106 # want to load the default loader; in the second, the specific one. 107 # 108 if [[ "${BASH_SOURCE[0]}" =~ ^/usr/share/bash-completion/completions/ ]]; then 109 complete -F _complete_from_snap "$1" 110 else 111 112 # _complete_from_snap_maybe calls _complete_from_snap if the command is in 113 # bin/snap, and otherwise does bash-completion's _completion_loader (which is 114 # what -D would've done before). 115 type -t _complete_from_snap_maybe > /dev/null || 116 _complete_from_snap_maybe() { 117 local etel=snap/core/current/usr/lib/snapd/etelpmoc.sh 118 # catch /snap/bin and /var/lib/snapd/snap/bin 119 if [[ "$(command -v "$1")" =~ ^(/var/lib/snapd)?/snap/bin/ && ( -e "/var/lib/snapd/$etel" || -e "/$etel" ) ]]; then 120 complete -F _complete_from_snap "$1" 121 return 124 122 fi 123 # fallback to the old -D 124 _completion_loader "$1" 125 } 126 127 complete -D -F _complete_from_snap_maybe 128 fi 129