mirror of
				https://github.com/openvswitch/ovs
				synced 2025-10-29 15:28:56 +00:00 
			
		
		
		
	Modifying $COMP_WORDBREAKS in completion script is not the recommended as it is a global variable and the modification could affect the behavior of other completion scripts. As a workaround, this commit uses the _get_comp_words_by_ref which allows user to exclude characters out of $COMP_WORDBREAKS and reassemble input command line. However, as a side effect, the bash completion module cannot handle characters defined in $COMP_WORDBREAKS (e.g. ':' and '=') correctly in the resulting completions. Thusly, we need to trim the colon-word and equal-word prefixes from reply. Signed-off-by: Alex Wang <alexw@nicira.com>
		
			
				
	
	
		
			826 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			826 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| SAVE_IFS=$IFS
 | |
| IFS="
 | |
| "
 | |
| _OVSDB_SERVER_LOCATION=""
 | |
| 
 | |
| # Run ovs-vsctl and make sure that ovs-vsctl is always called with
 | |
| # the correct --db argument.
 | |
| _ovs_vsctl () {
 | |
|     local _db
 | |
| 
 | |
|     if [ -n "$_OVSDB_SERVER_LOCATION" ]; then
 | |
|         _db="--db=$_OVSDB_SERVER_LOCATION"
 | |
|     fi
 | |
|     ovs-vsctl ${_db} "$@"
 | |
| }
 | |
| 
 | |
| # ovs-vsctl --commands outputs in this format:
 | |
| #
 | |
| # main = <localopts>,<name>,<options>
 | |
| # localopts = ([<localopt>] )*
 | |
| # localopt = --[^]]*
 | |
| # name = [^,]*
 | |
| # arguments = ((!argument|?argument|*argument|+argument) )*
 | |
| # argument = ([^ ]*|argument\|argument)
 | |
| #
 | |
| # The [] characters in local options are just delimiters.  The
 | |
| # argument prefixes mean:
 | |
| #   !argument :: The argument is required
 | |
| #   ?argument :: The argument is optional
 | |
| #   *argument :: The argument may appear any number (0 or more) times
 | |
| #   +argument :: The argument may appear one or more times
 | |
| # A bar (|) character in an argument means thing before bar OR thing
 | |
| # after bar; for example, del-port can take a port or an interface.
 | |
| 
 | |
| _OVS_VSCTL_COMMANDS="$(_ovs_vsctl --commands)"
 | |
| 
 | |
| # This doesn't complete on short arguments, so it filters them out.
 | |
| _OVS_VSCTL_OPTIONS="$(_ovs_vsctl --options | awk '/^--/ { print $0 }' \
 | |
|                       | sed -e 's/\(.*\)=ARG/\1=/')"
 | |
| IFS=$SAVE_IFS
 | |
| 
 | |
| declare -A _OVS_VSCTL_PARSED_ARGS
 | |
| declare -A _OVS_VSCTL_NEW_RECORDS
 | |
| 
 | |
| # This is a convenience function to make sure that user input is
 | |
| # looked at as a fixed string when being compared to something.  $1 is
 | |
| # the input; this behaves like 'grep "^$1"' but deals with regex
 | |
| # metacharacters in $1.
 | |
| _ovs_vsctl_check_startswith_string () {
 | |
|     awk 'index($0, thearg)==1' thearg="$1"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on global options.
 | |
| _ovs_vsctl_bashcomp_globalopt () {
 | |
|     local options result
 | |
| 
 | |
|     options=""
 | |
|     result=$(printf "%s\n" "${_OVS_VSCTL_OPTIONS}" \
 | |
|              | _ovs_vsctl_check_startswith_string "${1%=*}")
 | |
|     if [[ $result =~ "=" ]]; then
 | |
|         options="NOSPACE"
 | |
|     fi
 | |
|     printf -- "${options}\nEO\n${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on local options.
 | |
| _ovs_vsctl_bashcomp_localopt () {
 | |
|     local options result possible_opts
 | |
| 
 | |
|     possible_opts=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" | cut -f1 -d',')
 | |
|     # This finds all options that could go together with the
 | |
|     # already-seen ones
 | |
|     for prefix_arg in $1; do
 | |
|         possible_opts=$(printf "%s\n" "$possible_opts" \
 | |
|                         | grep -- "\[${prefix_arg%%=*}=\?\]")
 | |
|     done
 | |
|     result=$(printf "%s\n" "${possible_opts}" \
 | |
|              | tr ' ' '\n' | tr -s '\n' | sort | uniq)
 | |
|     # This removes the already-seen options from the list so that
 | |
|     # users aren't completed for the same option twice.
 | |
|     for prefix_arg in $1; do
 | |
|         result=$(printf "%s\n" "${result}" \
 | |
|                  | grep -v -- "\[${prefix_arg%%=*}=\?\]")
 | |
|     done
 | |
|     result=$(printf "%s\n" "${result}" | sed -ne 's/\[\(.*\)\]/\1/p' \
 | |
|              | _ovs_vsctl_check_startswith_string "$2")
 | |
|     if [[ $result =~ "=" ]]; then
 | |
|         options="NOSPACE"
 | |
|     fi
 | |
|     printf -- "${options}\nEO\n${result}"
 | |
| }
 | |
| 
 | |
| # $1 = given local options.
 | |
| # $2 = word to complete on.
 | |
| # Complete on command that could contain the given local options.
 | |
| _ovs_vsctl_bashcomp_command () {
 | |
|     local result possible_cmds
 | |
| 
 | |
|     possible_cmds=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}")
 | |
|     for prefix_arg in $1; do
 | |
|         possible_cmds=$(printf "%s\n" "$possible_cmds" \
 | |
|                         | grep -- "\[$prefix_arg=\?\]")
 | |
|     done
 | |
|     result=$(printf "%s\n" "${possible_cmds}" \
 | |
|              | cut -f2 -d',' \
 | |
|              | _ovs_vsctl_check_startswith_string "$2")
 | |
|     printf -- "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = completion result to check.
 | |
| # Return 0 if the completion result is non-empty, otherwise return 1.
 | |
| _ovs_vsctl_detect_nonzero_completions () {
 | |
|     local tmp newarg
 | |
| 
 | |
|     newarg=${1#*EO}
 | |
|     readarray tmp <<< "$newarg"
 | |
|     if [ "${#tmp[@]}" -eq 1 ] && [ "${#newarg}" -eq 0 ]; then
 | |
|         return 1
 | |
|     fi
 | |
|     return 0
 | |
| }
 | |
| 
 | |
| # $1 = argument format to expand.
 | |
| # Expand '+ARGUMENT' in argument format to '!ARGUMENT *ARGUMENT'.
 | |
| _ovs_vsctl_expand_command () {
 | |
|     result=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" \
 | |
|              | grep -- ",$1," | cut -f3 -d',' | tr ' ' '\n' \
 | |
|              | awk '/\+.*/ { name=substr($0,2);
 | |
|                              print "!"name; print "*"name; next; }
 | |
|                     1')
 | |
|     printf -- "${result}\n!--"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on table.
 | |
| _ovs_vsctl_complete_table () {
 | |
|     local result
 | |
| 
 | |
|     result=$(ovsdb-client --no-heading list-tables $_OVSDB_SERVER_LOCATION Open_vSwitch \
 | |
|         | _ovs_vsctl_check_startswith_string "$1")
 | |
|     printf -- "EO\n%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on record.  Provide both the name and uuid.
 | |
| _ovs_vsctl_complete_record () {
 | |
|     local table uuids names new_record
 | |
| 
 | |
|     table="${_OVS_VSCTL_PARSED_ARGS[TABLE]}"
 | |
|     new_record="${_OVS_VSCTL_NEW_RECORDS[${table^^}]}"
 | |
|     # Tables should always have an _uuid column
 | |
|     uuids=$(_ovs_vsctl --no-heading -f table -d bare --columns=_uuid \
 | |
|                       list $table | _ovs_vsctl_check_startswith_string "$1")
 | |
|     # Names don't always exist, silently ignore if the name column is
 | |
|     # unavailable.
 | |
|     names=$(_ovs_vsctl --no-heading -f table -d bare \
 | |
|                       --columns=name list $table \
 | |
|                       2>/dev/null \
 | |
|             | _ovs_vsctl_check_startswith_string "$1")
 | |
|     printf -- "EO\n%s\n%s\n%s\n" "${uuids}" "${names}" "${new_record}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on bridge.
 | |
| _ovs_vsctl_complete_bridge () {
 | |
|     local result
 | |
| 
 | |
|     result=$(_ovs_vsctl list-br | _ovs_vsctl_check_startswith_string "$1")
 | |
|     printf -- "EO\n%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on port.  If a bridge has already been specified,
 | |
| # just complete for that bridge.
 | |
| _ovs_vsctl_complete_port () {
 | |
|     local ports result
 | |
| 
 | |
|     if [ -n "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}" ]; then
 | |
|         ports=$(_ovs_vsctl list-ports "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}")
 | |
|     else
 | |
|         local all_ports
 | |
|         all_ports=$(_ovs_vsctl --format=table \
 | |
|                               --no-headings \
 | |
|                               --columns=name \
 | |
|                               list Port)
 | |
|         ports=$(printf "$all_ports" | tr -d '" ' | sort -u)
 | |
|     fi
 | |
|     result=$(_ovs_vsctl_check_startswith_string "$1" <<< "$ports")
 | |
|     printf -- "EO\n%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # $1:  Atom to complete (as usual)
 | |
| # $2:  Table to complete the key in
 | |
| # $3:  Column to find keys in
 | |
| # $4:  Prefix for each completion
 | |
| # Complete on key based on given table and column info.
 | |
| _ovs_vsctl_complete_key_given_table_column () {
 | |
|     local keys
 | |
| 
 | |
|     keys=$(_ovs_vsctl --no-heading --columns="$3" list \
 | |
|                      "$2" \
 | |
|            | tr -d '{\"}' | tr -s ', ' '\n' | cut -d'=' -f1 \
 | |
|            | xargs printf "$4%s\n" | _ovs_vsctl_check_startswith_string "$4$1")
 | |
|     result="${keys}"
 | |
|     printf -- "%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on key.
 | |
| __complete_key () {
 | |
|     # KEY is used in both br-set-external-id/br-get-external id (in
 | |
|     # which case it is implicitly a key in the external-id column) and
 | |
|     # in remove, where it is a table key.  This checks to see if table
 | |
|     # is set (the remove scenario), and then decides what to do.
 | |
|     local result
 | |
| 
 | |
|     if [ -n "${_OVS_VSCTL_PARSED_ARGS[TABLE]}" ]; then
 | |
|         local column=$(tr -d '\n' <<< ${_OVS_VSCTL_PARSED_ARGS["COLUMN"]})
 | |
|         result=$(_ovs_vsctl_complete_key_given_table_column \
 | |
|                      "$1" \
 | |
|                      ${_OVS_VSCTL_PARSED_ARGS["TABLE"]} \
 | |
|                      $column \
 | |
|                      "")
 | |
|     else
 | |
|         result=$(_ovs_vsctl br-get-external-id \
 | |
|                            ${_OVS_VSCTL_PARSED_ARGS["BRIDGE"]} \
 | |
|                  | cut -d'=' -f1 | _ovs_vsctl_check_startswith_string "$1")
 | |
|     fi
 | |
|     printf -- "%s" "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on key.
 | |
| _ovs_vsctl_complete_key () {
 | |
|     # KEY is used in both br-set-external-id/br-get-external id (in
 | |
|     # which case it is implicitly a key in the external-id column) and
 | |
|     # in remove, where it is a table key.  This checks to see if table
 | |
|     # is set (the remove scenario), and then decides what to do.
 | |
|     local result
 | |
| 
 | |
|     result="$(__complete_key $1)"
 | |
|     # If result is empty, just use user input as result.
 | |
|     if [ -z "$result" ]; then
 | |
|         result=$1
 | |
|     fi
 | |
|     printf -- "EO\n%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on value.
 | |
| _ovs_vsctl_complete_value () {
 | |
|     local result
 | |
| 
 | |
|     # Just use user input as result.
 | |
|     result=$1
 | |
| 
 | |
|     printf -- "EO\n%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on key=value.
 | |
| _ovs_vsctl_complete_key_value () {
 | |
|     local orig_completions new_completions
 | |
| 
 | |
|     orig_completions=$(__complete_key "$1")
 | |
|     for completion in ${orig_completions#*EO}; do
 | |
|         new_completions="${new_completions} ${completion}="
 | |
|     done
 | |
|     # If 'new_completions' is empty, just use user input as result.
 | |
|     if [ -z "$new_completions" ]; then
 | |
|         new_completions=$1
 | |
|     fi
 | |
|     printf -- "NOSPACE\nEO\n%s" "${new_completions}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on column.
 | |
| _ovs_vsctl_complete_column () {
 | |
|     local columns result
 | |
| 
 | |
|     columns=$(ovsdb-client --no-headings list-columns $_OVSDB_SERVER_LOCATION \
 | |
|         Open_vSwitch ${_OVS_VSCTL_PARSED_ARGS["TABLE"]})
 | |
|     result=$(printf "%s\n" "${columns}" \
 | |
|              | tr -d ':' | cut -d' ' -f1 \
 | |
|              | _ovs_vsctl_check_startswith_string "$1" | sort | uniq)
 | |
|     printf -- "EO\n%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # Extract all system interfaces.
 | |
| _ovs_vsctl_get_sys_intf () {
 | |
|     local result
 | |
| 
 | |
|     case "$(uname -o)" in
 | |
|         *Linux*)
 | |
|             result=$(ip -o link 2>/dev/null | cut -d':' -f2 \
 | |
|                      | sed -e 's/^ \(.*\)/\1/')
 | |
|             ;;
 | |
|         *)
 | |
|             result=$(ifconfig -a -s 2>/dev/null | cut -f1 -d' ' | tail -n +2)
 | |
|             ;;
 | |
|     esac
 | |
|     printf "%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on system interface.
 | |
| _ovs_vsctl_complete_sysiface () {
 | |
|     local result
 | |
| 
 | |
|     result=$(_ovs_vsctl_get_sys_intf | _ovs_vsctl_check_startswith_string "$1")
 | |
|     printf -- "EO\n%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on interface.  If a bridge has already been specified,
 | |
| # just complete for that bridge.
 | |
| _ovs_vsctl_complete_iface () {
 | |
|     local result
 | |
| 
 | |
|     if [ -n "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}" ]; then
 | |
|         result=$(_ovs_vsctl list-ifaces "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}")
 | |
|     else
 | |
|         for bridge in $(_ovs_vsctl list-br); do
 | |
|             local ifaces
 | |
| 
 | |
|             ifaces=$(_ovs_vsctl list-ifaces "${bridge}")
 | |
|             result="${result} ${ifaces}"
 | |
|         done
 | |
|     fi
 | |
|     printf "EO\n%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on COLUMN?:KEY=VALUE.
 | |
| _ovs_vsctl_complete_column_optkey_value () {
 | |
|     local result column key value completion
 | |
| 
 | |
|     column=$(printf "%s\n" "$1" | cut -d '=' -f1 | cut -d':' -f1)
 | |
|     key=$(printf "%s\n" "$1" | cut -d '=' -f1 | cut -s -d':' -f2)
 | |
|     # The tr -d '\n' <<< makes sure that there are no leading or
 | |
|     # trailing accidental newlines.
 | |
|     table=$(tr -d '\n' <<< ${_OVS_VSCTL_PARSED_ARGS["TABLE"]})
 | |
|     # This might also be called after add-port or add-bond; in those
 | |
|     # cases, the table should implicitly be assumed to be "Port".
 | |
|     # This is done by checking if a NEW- parameter has been
 | |
|     # encountered and, if it has, using that type without the NEW- as
 | |
|     # the table.
 | |
|     if [ -z "$table" ]; then
 | |
|         if [ -n ${_OVS_VSCTL_PARSED_ARGS["NEW-PORT"]} ] \
 | |
|            || [ -n ${_OVS_VSCTL_PARSED_ARGS["NEW-BOND-PORT"]} ]; then
 | |
|             table="Port"
 | |
|         fi
 | |
|     fi
 | |
|     if [ -z "$key" ]; then
 | |
|         local columns=$(ovsdb-client --no-headings list-columns \
 | |
|             $_OVSDB_SERVER_LOCATION Open_vSwitch $table)
 | |
| 
 | |
|         result=$(printf "%s\n" "${columns}" \
 | |
|                  | awk '/key.*value/ { print $1":"; next }
 | |
|                                      { print $1; next }' \
 | |
|                  | _ovs_vsctl_check_startswith_string "$1" | sort | uniq)
 | |
|     fi
 | |
|     if [[ $1 =~ ":" ]]; then
 | |
|         result=$(_ovs_vsctl_complete_key_given_table_column \
 | |
|                      "$key" "$table" "$column" "$column:")
 | |
|     fi
 | |
|     # If result is empty, just use user input as result.
 | |
|     if [ -z "$result" ]; then
 | |
|         result=$1
 | |
|     fi
 | |
|     printf -- "NOSPACE\nEO\n%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on filename.
 | |
| _ovs_vsctl_complete_filename () {
 | |
|     local result
 | |
| 
 | |
|     result=$(compgen -o filenames -A file "$1")
 | |
|     printf -- "EO\n%s\n" "${result}"
 | |
| }
 | |
| 
 | |
| _ovs_vsctl_complete_bridge_fail_mode () {
 | |
|     printf -- "EO\nstandalone\nsecure"
 | |
| }
 | |
| 
 | |
| # $1 = word to complete on.
 | |
| # Complete on target.
 | |
| _ovs_vsctl_complete_target () {
 | |
|     local result
 | |
| 
 | |
|     if [[ "$1" =~ ^p?u ]]; then
 | |
|         local protocol pathname expansion_base result
 | |
| 
 | |
|         protocol=$(cut -d':' -f1 <<< "$1")
 | |
|         pathname=$(cut -s -d':' -f2 <<< "$1")
 | |
|         expansion_base=$(compgen -W "unix punix" "$protocol")
 | |
|         expansion_base="$expansion_base:"
 | |
|         result=$(compgen -o filenames -A file \
 | |
|                          -P $expansion_base "${pathname}")
 | |
|         printf -- "NOSPACE\nEO\n%s\n" "${result}"
 | |
|     else
 | |
|         printf -- "NOSPACE\nEO\nssl:\ntcp:\nunix:\npssl:\nptcp:\npunix:"
 | |
|     fi
 | |
| }
 | |
| 
 | |
| # Extract PS1 prompt.
 | |
| _ovs_vsctl_get_PS1 () {
 | |
|     if [ "$test" = "true" ]; then
 | |
|         printf -- "> "
 | |
|         return;
 | |
|     fi
 | |
| 
 | |
|     # Original inspiration from
 | |
|     # http://stackoverflow.com/questions/10060500/bash-how-to-evaluate-ps1-ps2,
 | |
|     # but changed quite a lot to make it more robust.
 | |
| 
 | |
|     # Make sure the PS1 used doesn't include any of the special
 | |
|     # strings used to identify the prompt
 | |
|     myPS1="$(sed 's/Begin prompt/\\Begin prompt/; s/End prompt/\\End prompt/' <<< "$PS1")"
 | |
|     # Export the current environment in case the prompt uses any
 | |
|     vars="$(env | cut -d'=' -f1)"
 | |
|     for var in $vars; do export $var; done
 | |
|     funcs="$(declare -F | cut -d' ' -f3)"
 | |
|     for func in $funcs; do export -f $func; done
 | |
|     # Get the prompt
 | |
|     v="$(bash --norc --noprofile -i 2>&1 <<< $'PS1=\"'"$myPS1"$'\" \n# Begin prompt\n# End prompt')"
 | |
|     v="${v##*# Begin prompt}"
 | |
|     printf -- "$(tail -n +2 <<< "${v%# End prompt*}" | sed 's/\\Begin prompt/Begin prompt/; s/\\End prompt/End prompt/')"
 | |
| 
 | |
| }
 | |
| 
 | |
| # Request a new value from user.  Nothing to complete on.
 | |
| _ovs_vsctl_complete_new () {
 | |
|     local two_word_type message result
 | |
| 
 | |
|     if [ ! "$1" = "--" ]; then
 | |
|         two_word_type="${2/-/ }"
 | |
|         message="\nEnter a ${two_word_type,,}:\n$(_ovs_vsctl_get_PS1)$COMP_LINE"
 | |
|         if [ -n "$1" ]; then
 | |
|             result="$1"
 | |
|         fi
 | |
|         printf -- "NOCOMP\nBM%sEM\nEO\n%s\n" "${message}" "${result}"
 | |
|     fi
 | |
| }
 | |
| 
 | |
| _ovs_vsctl_complete_dashdash () {
 | |
|     printf -- "EO\n%s\n" "--"
 | |
| }
 | |
| 
 | |
| 
 | |
| # These functions are given two arguments:
 | |
| #
 | |
| # $1 is the word being completed
 | |
| #
 | |
| # $2 is the type of completion --- only currently useful for the
 | |
| # NEW-* functions.
 | |
| #
 | |
| # Note that the NEW-* functions actually are ``completed''; currently
 | |
| # the completions are just used to save the fact that they have
 | |
| # appeared for later use (i.e. implicit table calculation).
 | |
| #
 | |
| # The output is of the form <options>EO<completions>, where EO stands
 | |
| # for end options.  Currently available options are:
 | |
| #  - NOSPACE: Do not add a space at the end of each completion
 | |
| #  - NOCOMP: Do not complete, but store the output of the completion
 | |
| #    func in _OVS_VSCTL_PARSED_ARGS for later usage.
 | |
| #  - BM<message>EM: Print the <message>
 | |
| declare -A _OVS_VSCTL_ARG_COMPLETION_FUNCS=(
 | |
|     ["TABLE"]=_ovs_vsctl_complete_table
 | |
|     ["RECORD"]=_ovs_vsctl_complete_record
 | |
|     ["BRIDGE"]=_ovs_vsctl_complete_bridge
 | |
|     ["PARENT"]=_ovs_vsctl_complete_bridge
 | |
|     ["PORT"]=_ovs_vsctl_complete_port
 | |
|     ["KEY"]=_ovs_vsctl_complete_key
 | |
|     ["VALUE"]=_ovs_vsctl_complete_value
 | |
|     ["ARG"]=_ovs_vsctl_complete_value
 | |
|     ["IFACE"]=_ovs_vsctl_complete_iface
 | |
|     ["SYSIFACE"]=_ovs_vsctl_complete_sysiface
 | |
|     ["COLUMN"]=_ovs_vsctl_complete_column
 | |
|     ["COLUMN?:KEY"]=_ovs_vsctl_complete_column_optkey_value
 | |
|     ["COLUMN?:KEY=VALUE"]=_ovs_vsctl_complete_column_optkey_value
 | |
|     ["KEY=VALUE"]=_ovs_vsctl_complete_key_value
 | |
|     ["?KEY=VALUE"]=_ovs_vsctl_complete_key_value
 | |
|     ["PRIVATE-KEY"]=_ovs_vsctl_complete_filename
 | |
|     ["CERTIFICATE"]=_ovs_vsctl_complete_filename
 | |
|     ["CA-CERT"]=_ovs_vsctl_complete_filename
 | |
|     ["MODE"]=_ovs_vsctl_complete_bridge_fail_mode
 | |
|     ["TARGET"]=_ovs_vsctl_complete_target
 | |
|     ["NEW-BRIDGE"]=_ovs_vsctl_complete_new
 | |
|     ["NEW-PORT"]=_ovs_vsctl_complete_new
 | |
|     ["NEW-BOND-PORT"]=_ovs_vsctl_complete_new
 | |
|     ["NEW-VLAN"]=_ovs_vsctl_complete_new
 | |
|     ["--"]=_ovs_vsctl_complete_dashdash
 | |
| )
 | |
| 
 | |
| # $1: Argument type, may include vertical bars to mean OR
 | |
| # $2: Beginning of completion
 | |
| #
 | |
| # Note that this checks for existance in
 | |
| # _OVS_VSCTL_ARG_COMPLETION_FUNCS; if the argument type ($1) is not
 | |
| # there it will fail gracefully.
 | |
| _ovs_vsctl_possible_completions_of_argument () {
 | |
|     local possible_types completions tmp
 | |
| 
 | |
|     completions="EO"
 | |
| 
 | |
|     possible_types=$(printf "%s\n" "$1" | tr '|' '\n')
 | |
|     for type in $possible_types; do
 | |
|         if [ ${_OVS_VSCTL_ARG_COMPLETION_FUNCS["${type^^}"]} ]; then
 | |
|             tmp=$(${_OVS_VSCTL_ARG_COMPLETION_FUNCS["${type^^}"]} \
 | |
|                       "$2" "${type^^}")
 | |
|             tmp_noEO="${tmp#*EO}"
 | |
|             tmp_EO="${tmp%%EO*}"
 | |
|             completions=$(printf "%s%s\n%s" "${tmp_EO}" \
 | |
|                                  "${completions}" "${tmp_noEO}")
 | |
|         fi
 | |
|     done
 | |
|     printf "%s\n" "${completions}"
 | |
| }
 | |
| 
 | |
| # $1 = List of argument types
 | |
| # $2 = current pointer into said list
 | |
| # $3 = word to complete on
 | |
| # Outputs list of possible completions
 | |
| # The return value is the index in the cmd_args($1) list that should
 | |
| # next be matched, if only one of them did, or 254 if there are no
 | |
| # matches, so it doesn't know what comes next.
 | |
| _ovs_vsctl_complete_argument() {
 | |
|     local cmd_args arg expansion index
 | |
| 
 | |
|     new=$(printf "%s\n" "$1" | grep -- '.\+')
 | |
|     readarray -t cmd_args <<< "$new";
 | |
|     arg=${cmd_args[$2]}
 | |
|     case ${arg:0:1} in
 | |
|         !)
 | |
|             expansion=$(_ovs_vsctl_possible_completions_of_argument \
 | |
|                             "${arg:1}" $3)
 | |
|             index=$(($2+1))
 | |
|             ;;
 | |
|         \?|\*)
 | |
|             local tmp1 tmp2 arg2_index tmp2_noEO tmp2_EO
 | |
|             tmp1=$(_ovs_vsctl_possible_completions_of_argument "${arg:1}" $3)
 | |
|             tmp2=$(_ovs_vsctl_complete_argument "$1" "$(($2+1))" "$3")
 | |
|             arg2_index=$?
 | |
|             if _ovs_vsctl_detect_nonzero_completions "$tmp1" \
 | |
|                && _ovs_vsctl_detect_nonzero_completions "$tmp2"; then
 | |
|                 if [ "${arg:0:1}" = "*" ]; then
 | |
|                     index=$2;
 | |
|                 else
 | |
|                     index=$(($2+1));
 | |
|                 fi
 | |
|             fi
 | |
|             if _ovs_vsctl_detect_nonzero_completions "$tmp1" \
 | |
|                && (! _ovs_vsctl_detect_nonzero_completions "$tmp2"); then
 | |
|                 if [ "${arg:0:1}" = "*" ]; then
 | |
|                     index=$2;
 | |
|                 else
 | |
|                     index=$(($2+1));
 | |
|                 fi
 | |
|             fi
 | |
|             if (! _ovs_vsctl_detect_nonzero_completions "$tmp1") \
 | |
|                && _ovs_vsctl_detect_nonzero_completions "$tmp2"; then
 | |
|                 index=$arg2_index
 | |
|             fi
 | |
|             if (! _ovs_vsctl_detect_nonzero_completions "$tmp1") \
 | |
|                && (! _ovs_vsctl_detect_nonzero_completions "$tmp2"); then
 | |
|                 index=254
 | |
|             fi
 | |
|             # Don't allow secondary completions to inhibit primary
 | |
|             # completions:
 | |
|             if [[ $tmp2 =~ ^([^E]|E[^O])*NOCOMP ]]; then
 | |
|                 tmp2=""
 | |
|             fi
 | |
|             tmp2_noEO="${tmp2#*EO}"
 | |
|             tmp2_EO="${tmp2%%EO*}"
 | |
|             expansion=$(printf "%s%s\n%s" "${tmp2_EO}" \
 | |
|                                "${tmp1}" "${tmp2_noEO}")
 | |
|             ;;
 | |
|     esac
 | |
|     printf "%s\n" "$expansion"
 | |
|     return $index
 | |
| }
 | |
| 
 | |
| _ovs_vsctl_detect_nospace () {
 | |
|     if [[ $1 =~ ^([^E]|E[^O])*NOSPACE ]]; then
 | |
|         _OVS_VSCTL_COMP_NOSPACE=true
 | |
|     fi
 | |
| }
 | |
| 
 | |
| _ovs_vsctl_process_messages () {
 | |
|     local message
 | |
| 
 | |
|     message="${1#*BM}"
 | |
|     message="${message%%EM*}"
 | |
|     if [ "$test" = "true" ]; then
 | |
|         printf -- "--- BEGIN MESSAGE"
 | |
|     fi
 | |
|     printf "${message}"
 | |
|     if [ "$test" = "true" ]; then
 | |
|         printf -- "--- END MESSAGE"
 | |
|     fi
 | |
| }
 | |
| 
 | |
| # colon, equal sign will mess up the completion output, just
 | |
| # removes the colon-word and equal-word prefix from COMPREPLY items.
 | |
| #
 | |
| # Implementation of this function refers to the __ltrim_colon_completions
 | |
| # function defined in bash_completion module.
 | |
| #
 | |
| # $1:  Current argument
 | |
| # $2:  $COMP_WORDBREAKS
 | |
| # $3:  ${COMPREPLY[@]}
 | |
| _ovs_vsctl_trim_compreply() {
 | |
|     local cur comp_wordbreaks
 | |
|     local compreply
 | |
| 
 | |
|     cur=$1 && shift
 | |
|     comp_wordbreaks=$1 && shift
 | |
|     compreply=( $@ )
 | |
| 
 | |
|     if [[ "$cur" == *:* && "$comp_wordbreaks" == *:* ]]; then
 | |
|         local colon_word=${cur%${cur##*:}}
 | |
|         local i=${#compreply[*]}
 | |
|         cur=${cur##*:}
 | |
|         while [ $((--i)) -ge 0 ]; do
 | |
|             compreply[$i]=${compreply[$i]#"$colon_word"}
 | |
|         done
 | |
|     fi
 | |
| 
 | |
|     if [[ "$cur" == *=* && "$comp_wordbreaks" == *=* ]]; then
 | |
|         local equal_word=${cur%${cur##*=}}
 | |
|         local i=${#compreply[*]}
 | |
|         while [ $((--i)) -ge 0 ]; do
 | |
|             compreply[$i]=${compreply[$i]#"$equal_word"}
 | |
|         done
 | |
|     fi
 | |
| 
 | |
|     printf "%s " "${compreply[@]}"
 | |
| }
 | |
| 
 | |
| # The general strategy here is that the same functions that decide
 | |
| # completions can also capture the necessary context for later
 | |
| # completions.  This means that there is no distinction between the
 | |
| # processing for words that are not the current word and words that
 | |
| # are the current word.
 | |
| #
 | |
| # Parsing up until the command word happens starts with everything
 | |
| # valid; as the syntax order of ovs-vsctl is fairly strict, when types
 | |
| # of words that preclude other words from happending can turn them
 | |
| # off; this is controlled by valid_globals, valid_opts, and
 | |
| # valid_commands.  given_opts is used to narrow down which commands
 | |
| # are valid based on the previously given options.
 | |
| #
 | |
| # After the command has been detected, the parsing becomes more
 | |
| # complicated.  The cmd_pos variable is set to 0 when the command is
 | |
| # detected; it is used as a pointer into an array of the argument
 | |
| # types for that given command.  The argument types are stored in both
 | |
| # cmd_args and raw_cmd as the main loop uses properties of arrays to
 | |
| # detect certain conditions, but arrays cannot be passed to functions.
 | |
| # To be able to deal with optional or repeatable arguments, the exit
 | |
| # status of the function _ovs_vsctl_complete_argument represents where
 | |
| # it has determined that the next argument will be.
 | |
| _ovs_vsctl_bashcomp () {
 | |
|     local words cword valid_globals cmd_args raw_cmd cmd_pos valid_globals valid_opts
 | |
|     local test="false"
 | |
| 
 | |
|     # Does not support BASH_VERSION < 4.0
 | |
|     if [ ${BASH_VERSINFO[0]} -lt 4 ]; then
 | |
|         return 0
 | |
|     fi
 | |
| 
 | |
|     # Prepare the COMP_* variables based on input.
 | |
|     if [ "$1" = "test" ]; then
 | |
|         test="true"
 | |
|         export COMP_LINE="ovs-vsctl $2"
 | |
|         tmp="ovs-vsctl"$'\n'"$(tr ' ' '\n' <<< "${COMP_LINE}x")"
 | |
|         tmp="${tmp%x}"
 | |
|         readarray -t COMP_WORDS \
 | |
|                   <<< "$tmp"
 | |
|         export COMP_WORDS
 | |
|         export COMP_CWORD="$((${#COMP_WORDS[@]}-1))"
 | |
|     else
 | |
|         # If not in test mode, reassembles the COMP_WORDS and COMP_CWORD
 | |
|         # using just space as word break.
 | |
|         _get_comp_words_by_ref -n "\"'><=;|&(:" -w words -i cword
 | |
|         COMP_WORDS=( "${words[@]}" )
 | |
|         COMP_CWORD=${cword}
 | |
|     fi
 | |
| 
 | |
|     # Extract the conf.db path.
 | |
|     db=$(sed -n 's/.*--db=\([^ ]*\).*/\1/p' <<< "$COMP_LINE")
 | |
|     if [ -n "$db" ]; then
 | |
|         _OVSDB_SERVER_LOCATION="$db"
 | |
|     fi
 | |
| 
 | |
|     # If having trouble accessing the database, return.
 | |
|     if ! _ovs_vsctl get-manager 1>/dev/null 2>/dev/null; then
 | |
|         return 1;
 | |
|     fi
 | |
| 
 | |
|     _OVS_VSCTL_PARSED_ARGS=()
 | |
|     _OVS_VSCTL_NEW_RECORDS=()
 | |
|     cmd_pos=-1
 | |
|     valid_globals=true
 | |
|     valid_opts=true
 | |
|     valid_commands=true
 | |
|     given_opts=""
 | |
|     index=1
 | |
|     for word in "${COMP_WORDS[@]:1:${COMP_CWORD}} "; do
 | |
|         _OVS_VSCTL_COMP_NOSPACE=false
 | |
|         local completion
 | |
|         completion=""
 | |
|         if [ $cmd_pos -gt -1 ]; then
 | |
|             local tmp tmp_noop arg possible_newindex
 | |
|             tmp=$(_ovs_vsctl_complete_argument "$raw_cmd" "$cmd_pos" "$word")
 | |
|             possible_newindex=$?
 | |
|             # Check for nospace.
 | |
|             _ovs_vsctl_detect_nospace $tmp
 | |
|             # Remove all options.
 | |
|             tmp_noop="${tmp#*EO}"
 | |
| 
 | |
|             # Allow commands to specify that they should not be
 | |
|             # completed
 | |
|             if ! [[ $tmp =~ ^([^E]|E[^O])*NOCOMP ]]; then
 | |
|                 # Directly assignment, since 'completion' is guaranteed to
 | |
|                 # to be empty.
 | |
|                 completion="$tmp_noop"
 | |
|                 # If intermediate completion is empty, it means that the current
 | |
|                 # argument is invalid.  And we should not continue.
 | |
|                 if [ $index -lt $COMP_CWORD ] \
 | |
|                     && (! _ovs_vsctl_detect_nonzero_completions "$completion"); then
 | |
|                     _ovs_vsctl_process_messages "BM\nCannot complete \'${COMP_WORDS[$index]}\' at index ${index}:\n$(_ovs_vsctl_get_PS1)${COMP_LINE}EM\nEO\n"
 | |
|                     return 1
 | |
|                 fi
 | |
|             else
 | |
|                 # Only allow messages when there is no completion
 | |
|                 # printout and when on the current word.
 | |
|                 if [ $index -eq $COMP_CWORD ]; then
 | |
|                     _ovs_vsctl_process_messages "${tmp}"
 | |
|                 fi
 | |
|                 # Append the new record to _OVS_VSCTL_NEW_RECORDS.
 | |
|                 _OVS_VSCTL_NEW_RECORDS["${cmd_args[$cmd_pos]##*-}"]="${_OVS_VSCTL_NEW_RECORDS["${cmd_args[$cmd_pos]##*-}"]} $tmp_noop"
 | |
|             fi
 | |
|             if [[ $cmd_pos -lt ${#cmd_args} ]]; then
 | |
|                 _OVS_VSCTL_PARSED_ARGS["${cmd_args[$cmd_pos]:1}"]=$word
 | |
|             fi
 | |
|             if [ $possible_newindex -lt 254 ]; then
 | |
|                 cmd_pos=$possible_newindex
 | |
|             fi
 | |
|         fi
 | |
| 
 | |
|         if [ $valid_globals == true ]; then
 | |
|             tmp=$(_ovs_vsctl_bashcomp_globalopt $word)
 | |
|             _ovs_vsctl_detect_nospace $tmp
 | |
|             completion="${completion} ${tmp#*EO}"
 | |
|         fi
 | |
|         if [ $valid_opts == true ]; then
 | |
|             tmp=$(_ovs_vsctl_bashcomp_localopt "$given_opts" $word)
 | |
|             _ovs_vsctl_detect_nospace $tmp
 | |
|             completion="${completion} ${tmp#*EO}"
 | |
|             if [ $index -lt $COMP_CWORD ] \
 | |
|                && _ovs_vsctl_detect_nonzero_completions "$tmp"; then
 | |
|                 valid_globals=false
 | |
|                 given_opts="${given_opts} ${word}"
 | |
|             fi
 | |
|         fi
 | |
|         if [ $valid_commands = true ]; then
 | |
|             tmp=$(_ovs_vsctl_bashcomp_command "$given_opts" $word)
 | |
|             _ovs_vsctl_detect_nospace $tmp
 | |
|             completion="${completion} ${tmp#*EO}"
 | |
|             if [ $index -lt $COMP_CWORD ] \
 | |
|                && _ovs_vsctl_detect_nonzero_completions "$tmp"; then
 | |
|                 valid_globals=false
 | |
|                 valid_opts=false
 | |
|                 valid_commands=false
 | |
|                 cmd_pos=0
 | |
|                 raw_cmd=$(_ovs_vsctl_expand_command "$word")
 | |
|                 readarray -t cmd_args <<< "$raw_cmd"
 | |
|             fi
 | |
|         fi
 | |
|         if [ "$word" = "--" ] && [ $index -lt $COMP_CWORD ]; then
 | |
|             # Empty the parsed args array.
 | |
|             _OVS_VSCTL_PARSED_AGS=()
 | |
|             cmd_pos=-1
 | |
|             # No longer allow global options after '--'.
 | |
|             valid_globals=false
 | |
|             valid_opts=true
 | |
|             valid_commands=true
 | |
|             given_opts=""
 | |
|         fi
 | |
|         completion="$(sort -u <<< "$(tr ' ' '\n' <<< ${completion})")"
 | |
|         if [ $index -eq $COMP_CWORD ]; then
 | |
|             if [ "$test" = "true" ]; then
 | |
|                 completion="$(_ovs_vsctl_trim_compreply "$word" ":=" ${completion} | \
 | |
|                               tr ' ' '\n')"
 | |
|                 if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then
 | |
|                     printf "%s" "$completion" | sed -e '/^$/d'
 | |
|                 else
 | |
|                     printf "%s" "$completion" | sed -e '/^$/d; s/$/ /g'
 | |
|                 fi
 | |
|                 printf "\n"
 | |
|             else
 | |
|                 if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then
 | |
|                     compopt -o nospace
 | |
|                     COMPREPLY=( $(compgen -W "${completion}" -- $word) )
 | |
|                 else
 | |
|                     compopt +o nospace
 | |
|                     COMPREPLY=( $(compgen -W "${completion}" -- $word) )
 | |
|                 fi
 | |
|                 COMPREPLY=( $(_ovs_vsctl_trim_compreply "$word" \
 | |
|                               "${COMP_WORDBREAKS}" ${COMPREPLY[@]}) )
 | |
|             fi
 | |
|         fi
 | |
|         index=$(($index+1))
 | |
|     done
 | |
| }
 | |
| 
 | |
| if [ "$1" = "test" ]; then
 | |
|     _ovs_vsctl_bashcomp "$@"
 | |
| else
 | |
|     complete -F _ovs_vsctl_bashcomp ovs-vsctl
 | |
| fi
 |