2
\$\begingroup\$

So the idea came from the fact that

  • connmanctl only completes filenames/directories when used non-interactively,
  • whereas when used interactively (executing connmanctl without arguments an interactive shell is started), it does support autocompletion, but when it comes to completing services (wifi networks, ethernet connections, bluetooth devices, ...) it completes the service path (as it is referred to in man connmanctl, instead of the name of the connection. In other words, if a line of connmanctl services is the following
    myHomeWifi            wifi_79829c350ea0_41542d233241335454_managed_psk
    
    then connmanctl connect wifi<Tab> will complete the second string, whereas I would like connmanctl connect myH<Tab> to complete the first.

If there is an in-connmanctl solution, feel free to point me to that, but I took this unpleasant feature of connmanctl as a chance to write a wrapper for it which also handles autocompletion.

The wrapper itself is just this:

#!/usr/bin/env bash
declare -A map
eval "$(command connmanctl services | \
  awk 'NF > 1 { print "map["$(NF - 1)"]="$NF; next }
              { print "map["$NF"]="$NF }')"
i=0
declare -a args
for arg in "$@"; do
  [[ "${map[$arg]}" ]] && args[i++]="${map[$arg]}" || args[i++]="$arg"
done
command connmanctl "${args[@]}"

Essentially it does the following:

  • It should be called with the same arguments you would call connmanctl.
  • Builds an associative array out of connmanctl services's output,
  • loops on the command line arguments and substitutes the wifi_*-like string if the other string matched (I guess this is a weak point, in the case a service is named, for instance, connect),
  • and then forwards the modified list of arguments to connmanctl.

The autocompletion is handled by the following script, where the heredoc is handmade by exploring how connmanctl completes.

#!/usr/bin/env bash

main='ConnMan'

_ConnMan_completions()
{
  mapfile -t COMPREPLY < <(compgen -W "$(__compListGen "${COMP_WORDS[@]}")" -- "${COMP_WORDS[$COMP_CWORD]}")
}

__compListGen() {
  __tree | __subTree "$@" | __expandServices | __dropChildren
}

__subTree() {
  # forward in to out if no args
  (( $# == 0 )) && tee && return

  # remove the last parameter if it's being completed
  (( $# == COMP_CWORD + 1 )) && set -- "${@:1:$(($# - 1))}"

  # process first entry and pipe recursively
  one=$1
  shift
  __getNode "$one" < /dev/stdin | __subTree "$@"
}

__getNode() {
  # all services match a common string in the heredoc
  [[ "$(command connmanctl services)" =~ $1 ]] && set -- '@services'
  # if first line matching $1 is followed by @repeat line,
  # print only these two; if not, print only lines beginning
  # with tab, up to the next line not beginning with tab
  sed -E '/(^| )'"$1"'( |$)/,/^[^\t]/{
            /^\t@repeat$/{H;x;p;Q}
            h
            /^\t/s/\t//p
          }
          d' /dev/stdin
}

__expandServices() {
  input=$(< /dev/stdin)
  case "$input" in
    [^@]* )
      echo "$input"
      ;;
    @services* )
      command connmanctl services | \
        sed -E 's/^[^ ]* *([^ ].*[^ ])  *[^ ]*$/\1/
                s/^ *//'
      ;;
    # the default case is not needed, as this case
    # statement is tightly bounded to the heredoc below
  esac
}

__dropChildren() {
  sed '/^\t/d' /dev/stdin
}

__tree() {
  # The heredoc below hardcodes connmanctl's completion tree.
  # The special entry @services refers to when connmanctl
  # completes with the available services, whereas the
  # entry @repeat refers to when connmanctl keeps giving
  # the same alternative completions as the for the last
  # word.
  cat <<EOF
$main
    agent
        on
        off
    clock
    config
        @services
            autoconnect domains ipv4 ipv6 mdns nameservers proxy remove timeservers
                @repeat
    connect
        @services
    disable
        ethernet
        offline
        wifi
    disconnect
        @services
    enable
        ethernet
        offline
        wifi
    exit
    help
    monitor
        manager
            on
            off
        services
            on
            off
        tech
            on
            off
        vpnconnection
            on
            off
        vpnmanager
            on
            off
    move-after
        @services
    move-before
        @services
    peer_service
    peers
    quit
    scan
        ethernet
        wifi
    services
        @services
    session
        bearers ctxid ifname srciprule type
            @repeat
    state
    technologies
    tether
        ethernet
            on
            off
        wifi
            on
            off
    tethering_clients
    vpnagent
        on
        off
    vpnconnections
EOF
}

complete -o default -F _ConnMan_completions $main
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.