github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/data/completion/bash/etelpmoc.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  # etelpmoc is the reverse of complete: it de-serialises the tab completion
    18  # request into the appropriate environment variables expected by the tab
    19  # completion tools, performs whatever action is wanted, and serialises the
    20  # result. It accomplishes this by having functions override the builtin
    21  # completion commands.
    22  #
    23  # this always runs "inside", in the same environment you get when doing "snap
    24  # run --shell", and snap-exec is the one setting the first argument to the
    25  # completion script set in the snap. The rest of the arguments come through
    26  # from snap-run --command=complete <snap> <args...>
    27  
    28  _die() {
    29      echo "$*" >&2
    30      exit 1
    31  }
    32  
    33  if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
    34      _die "ERROR: this is meant to be run, not sourced."
    35  fi
    36  
    37  if [[ "${#@}" -lt 8 ]]; then
    38      _die "USAGE: $0 <script> <COMP_TYPE> <COMP_KEY> <COMP_POINT> <COMP_CWORD> <COMP_WORDBREAKS> <COMP_LINE> cmd [args...]"
    39  fi
    40  
    41  # De-serialize the command line arguments and populate tab completion environment
    42  _compscript="$1"
    43  shift
    44  COMP_TYPE="$1"
    45  shift
    46  COMP_KEY="$1"
    47  shift
    48  COMP_POINT="$1"
    49  shift
    50  COMP_CWORD="$1"
    51  shift
    52  COMP_WORDBREAKS="$1"
    53  shift
    54  # duplication, but whitespace is eaten and that throws off COMP_POINT
    55  COMP_LINE="$1"
    56  shift
    57  # rest of the args is the command itself
    58  COMP_WORDS=("$@")
    59  
    60  COMPREPLY=()
    61  
    62  if [[ ! "$_compscript" ]]; then
    63      _die "ERROR: completion script filename can't be empty"
    64  fi
    65  if [[ ! -f "$_compscript" ]]; then
    66      _die "ERROR: completion script does not exist"
    67  fi
    68  
    69  # Source the bash-completion library functions and common completion setup
    70  # shellcheck disable=SC1091
    71  . /usr/share/bash-completion/bash_completion
    72  # Now source the snap's 'completer' script itself
    73  # shellcheck disable=SC1090
    74  . "$_compscript"
    75  
    76  # _compopts is an associative array, which keys are options. The options are
    77  # described in bash(1)'s description of the -o option to the "complete"
    78  # builtin, and they affect how the completion options are presented to the user
    79  # (e.g. adding a slash for directories, whether to add a space after the
    80  # completion, etc). These need setting in the user's environment so need
    81  # serializing separately from the completions themselves.
    82  declare -A _compopts
    83  
    84  # wrap compgen, setting _compopts for any options given.
    85  # (as these options need handling separately from the completions)
    86  compgen() {
    87      local opt
    88  
    89      while getopts :o: opt; do
    90          case "$opt" in
    91              o)
    92                  _compopts["$OPTARG"]=1
    93                  ;;
    94              *)
    95                  # Do nothing, explicitly. This silences shellcheck's detector
    96                  # of unhandled command line options.
    97                  ;;
    98          esac
    99      done
   100      builtin compgen "$@"
   101  }
   102  
   103  # compopt replaces the original compopt with one that just sets/unsets entries
   104  # in _compopts
   105  compopt() {
   106      local i
   107  
   108      for ((i=0; i<$#; i++)); do
   109          # in bash, ${!x} does variable indirection. Thus if x=1, ${!x} becomes $1.
   110          case "${!i}" in
   111              -o)
   112                  ((i++))
   113                  _compopts[${!i}]=1
   114                  ;;
   115              +o)
   116                  ((i++))
   117                  unset _compopts[${!i}]
   118                  ;;
   119          esac
   120      done
   121  }
   122  
   123  _compfunc="_minimal"
   124  _compact=""
   125  # this is a lot more complicated than it should be, but it's how you
   126  # get the result of 'complete -p "$1"' into an array, splitting it as
   127  # the shell would.
   128  readarray -t _comp < <(xargs -n1 < <(complete -p "$1") )
   129  # _comp is now an array of the appropriate 'complete' invocation, word-split as
   130  # the shell would, so we can now inspect it with getopts to determine the
   131  # appropriate completion action.
   132  # Unfortunately shellcheck doesn't know about readarray:
   133  # shellcheck disable=SC2154
   134  if [[ "${_comp[*]}" ]]; then
   135      while getopts :abcdefgjksuvA:C:W:o:F: opt "${_comp[@]:1}"; do
   136          case "$opt" in
   137              a)
   138                  _compact="alias"
   139                  ;;
   140              b)
   141                  _compact="builtin"
   142                  ;;
   143              c)
   144                  _compact="command"
   145                  ;;
   146              d)
   147                  _compact="directory"
   148                  ;;
   149              e)
   150                  _compact="export"
   151                  ;;
   152              f)
   153                  _compact="file"
   154                  ;;
   155              g)
   156                  _compact="group"
   157                  ;;
   158              j)
   159                  _compact="job"
   160                  ;;
   161              k)
   162                  _compact="keyword"
   163                  ;;
   164              s)
   165                  _compact="service"
   166                  ;;
   167              u)
   168                  _compact="user"
   169                  ;;
   170              v)
   171                  _compact="variable"
   172                  ;;
   173              A)
   174                  _compact="$OPTARG"
   175                  ;;
   176              o)
   177                  _compopts["$OPTARG"]=1
   178                  ;;
   179              C|F)
   180                  _compfunc="$OPTARG"
   181                  ;;
   182              W)
   183                  readarray -t COMPREPLY < <( builtin compgen -W "$OPTARG" -- "${COMP_WORDS[COMP_CWORD]}" )
   184                  _compfunc=""
   185                  ;;
   186              *)
   187                  # P, G, S, and X are not supported yet
   188                  _die "ERROR: unknown option -$OPTARG"
   189                  ;;
   190          esac
   191      done
   192  fi
   193  
   194  _bounce=""
   195  case "$_compact" in
   196      # these are for completing things that'll be interpreted by the
   197      # "outside" bash, so send them back to be completed there.
   198      "alias"|"export"|"job"|"variable")
   199          _bounce="$_compact"
   200          ;;
   201  esac
   202  
   203  if [ ! "$_bounce" ]; then
   204      if [ "$_compact" ]; then
   205          readarray -t COMPREPLY < <( builtin compgen -A "$_compact" -- "${COMP_WORDS[COMP_CWORD]}" )
   206      elif [ "$_compfunc" ]; then
   207          # execute completion function (or the command if -C)
   208  
   209          # from https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html:
   210          #   When the function or command is invoked, the first argument ($1) is
   211          #   the name of the command whose arguments are being completed, the
   212          #   second argument ($2) is the word being completed, and the third
   213          #   argument ($3) is the word preceding the word being completed on the
   214          #   current command line.
   215          # that's "$1" "${COMP_WORDS[COMP_CWORD]}" and "${COMP_WORDS[COMP_CWORD-1]}"
   216          # (probably)
   217          $_compfunc "$1" "${COMP_WORDS[COMP_CWORD]}" "${COMP_WORDS[COMP_CWORD-1]}"
   218      fi
   219  fi
   220  
   221  # print completions to stdout
   222  echo "${!_compopts[@]}"
   223  echo "$_bounce"
   224  echo ""
   225  printf "%s\\n" "${COMPREPLY[@]}"