From: Jozsef Kadlecsik Date: Thu, 21 Feb 2013 15:36:35 +0000 (+0100) Subject: The ipset_bash_completion tool is added X-Git-Tag: v6.17~2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0315d666fd8f40ba41a26a337f2eeabe60f92851;p=ipset The ipset_bash_completion tool is added Source: http://sourceforge.net/projects/ipset-bashcompl --- diff --git a/utils/ipset_bash_completion/README.md b/utils/ipset_bash_completion/README.md new file mode 100644 index 0000000..11df9d5 --- /dev/null +++ b/utils/ipset_bash_completion/README.md @@ -0,0 +1,4 @@ +ipset-bash-completion +===================== + +Programmable completion code (bash) for ipset (netfilter.org) diff --git a/utils/ipset_bash_completion/ipset_bash_completion b/utils/ipset_bash_completion/ipset_bash_completion new file mode 100644 index 0000000..124db67 --- /dev/null +++ b/utils/ipset_bash_completion/ipset_bash_completion @@ -0,0 +1,414 @@ +#!/bin/bash + +# ----------------------------------------------------------------- +# Programmable completion code for ipset (netfilter.org) +# +# https://github.com/AllKind/ipset-bash-completion +# https://sourceforge.net/projects/ipset-bashcompl +# ----------------------------------------------------------------- + +# Copyright (C) 2013 AllKind (AllKind@fastest.cc) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# ----------------------------------------------------------------- +# Tested with ipset versions: +# 6.16.1 +# ----------------------------------------------------------------- +# +# Put it into ~/.bash_completion or /etc/bash_completion.d/ +# +# ----------------------------------------------------------------- +# +# Version 1.9 +# +# ----------------------------------------------------------------- + + +_ipset_bash_default_compl() { # taken from examples - modified by me +# call with the word to be completed as $1 +local t +if [[ $1 == \$\(* ]]; then # command substitution + t=${1#??} + COMPREPLY=( $(compgen -c -P '$(' $t) ) +elif [[ $1 == \$\{* ]]; then # variables with a leading `${' + t=${1#??} + COMPREPLY=( $(compgen -v -P '${' -S '}' $t) ) +elif [[ $1 == \$* ]]; then # variables with a leading `$' + t=${1#?} + COMPREPLY=( $(compgen -v -P '$' $t ) ) +elif [[ "$1" == *@* ]]; then # hostname + t=${1#*@} + COMPREPLY=( $( compgen -A hostname $t ) ) +elif [[ $1 == *[*?[]* ]]; then # sh-style glob pattern + COMPREPLY=( $( compgen -G "$1" ) ) +# ksh-style extended glob pattern - must be complete +elif shopt -q extglob && [[ $1 == *[?*+\!@]\(*\)* ]]; then + COMPREPLY=( $( compgen -G "$1" ) ) +fi +} + +_ipset_complete() { +shopt -s extglob +local cur prev str_action ips_version +local -i i=x=y=got_action=in_list=0 +local -i ignore_errors=use_file=names_only=headers_only=save_format=res_sort=0 +local arr_sets=() arr_types=() arr_members=() + +# ipset version check 6.x upwards (to v?) is supported +ips_version="$(ipset --version)" +ips_version="${ips_version#ipset v}" +ips_version="${ips_version%%.*}" +[[ $ips_version = +([[:digit:]]) ]] || return 1 +((ips_version < 6)) && return 1 + +COMPREPLY=() +COMP_WORDBREAKS=$' \t\n"\'><=;|&(' + +# expecting _get_comp_words_by_ref() to exist from bash_completion +_get_comp_words_by_ref cur || return +_get_comp_words_by_ref prev || return + +#DEBUG=Y +if [[ $DEBUG ]]; then + printf "\nCOMP_WORDBREAKS: <%s>\n" "$COMP_WORDBREAKS" + printf "COMP_LINE: <%s>\n" "$COMP_LINE" + printf "COMP_TYPE: <%s>\n" "$COMP_TYPE" + printf "COMP_POINT: <%s>\n" "$COMP_POINT" + printf "COMP_KEY: <%s>\n" "$COMP_KEY" + printf "COMP_CWORD: <%s>\n" "$COMP_CWORD" + printf "COMP_WORDS:\n" + printf "<%s>\n" "${COMP_WORDS[@]}" + printf "cur: <%s> prev: <%s>\n" "$cur" "$prev" +fi + +for i in ${!COMP_WORDS[@]}; do # check if we already have an action registered + if [[ ${COMP_WORDS[i]} = @(create|add|del|test|destroy|list|save|restore|flush|rename|swap|help|version) ]]; then + if [[ ${COMP_WORDS[i]} != save ]]; then + got_action=1 str_action=${COMP_WORDS[i]} + break + else + if [[ ${COMP_WORDS[i-1]} != -o ]]; then + got_action=1 str_action=${COMP_WORDS[i]} + break + fi + fi + fi +done + +# collect information about used options +for ((i=1; i <= ${#COMP_WORDS[@]}; i++)); do + case "${COMP_WORDS[i]}" in + -\!) ignore_errors=1 ;; + -f) use_file=1 ;; + -n) names_only=1 ;; + -t) headers_only=1 ;; + -o) save_format=1 + if [[ $prev = -o ]]; then + save_format=2 # expecting opt-arg + elif [[ ${COMP_WORDS[i+1]} = save ]]; then + save_format=3 # no -n/-t with -o save + fi + ;; + -r|-s) res_sort=1 ;; + esac +done + +# invalid combination of options +if ((names_only && headers_only)); then + COMPREPLY=() + return 0 +elif ((names_only || headers_only)); then + if ((res_sort || ignore_errors)) || ((save_format == 3)); then + COMPREPLY=() + return 0 + fi +elif ((ignore_errors)); then + if ((res_sort || save_format)); then + COMPREPLY=() + return 0 + fi +fi + +# for help or create, find supported set types and save them into an array +if [[ $str_action = @(help|create) ]]; then i=0 + while read -r; do + [[ $REPLY = "Supported set types:"* ]] && ((!i)) && i=1 && continue + ((i)) || continue + if [[ $REPLY = *:* ]]; then + set -- $REPLY + arr_types[${#arr_types[@]}]="$1" + fi + done < <(ipset help) + for i in ${!arr_types[@]}; do # remove dupe entries + for ((x=i+1; x <= ${#arr_types[@]}; x++)); do + if [[ ${arr_types[i]} = ${arr_types[x]} ]]; then + unset arr_types[x] + fi + done + done +fi + +case "$cur" in + -*) # any option is requested + if ((save_format == 2)); then + COMPREPLY=() + return 0 + fi + case "$prev" in + create|add|del|test|rename|swap) # -option not expected + COMPREPLY=() + return 0 + ;; + \<|\>|-f) compopt -o nospace # expecting filenames as completion + COMPREPLY=( $( compgen -f -- $cur ) ) + return 0 + ;; + -) COMPREPLY=() # interactive mode + return 0 + ;; + esac + if ((got_action)); then + case "$str_action" in + create|add|del) + COMPREPLY=( -\! -q ) + for ((i=1; i <= ${#COMP_WORDS[@]}; i++)); do + if [[ $str_action = ${COMP_WORDS[i]} && ${COMP_WORDS[i+2]} = -* ]]; then + COMPREPLY=() # option not expected, want command value + break + fi + done + ;; + destroy|flush) COMPREPLY=( -q ) ;; + list) + if ((names_only || headers_only)); then + COMPREPLY=( -f -o -q ) + elif ((res_sort)); then + COMPREPLY=( -f -o -q -r -s ) + elif ((save_format == 1)); then + COMPREPLY=( -f -q -r -s -t ) + elif ((save_format == 3)); then + COMPREPLY=( -f -q -r -s ) + else + COMPREPLY=( -f -n -o -q -r -s -t ) + fi + ;; + restore) COMPREPLY=( -\! -f -q ) ;; + save) COMPREPLY=( -f -q ) ;; + rename|swap|test) COMPREPLY=( -q ) + for ((i=1; i <= ${#COMP_WORDS[@]}; i++)); do + if [[ $str_action = ${COMP_WORDS[i]} && ${COMP_WORDS[i+2]} = -* ]]; then + COMPREPLY=() # option not expected, want command value + break + fi + done + ;; + help|version) COMPREPLY=() + return 0 + ;; + esac + else COMPREPLY=( - -\! -f -n -o -q -r -s -t ) + if ((names_only || headers_only)) && ((save_format == 1)); then + COMPREPLY=( -f -q ) + elif ((names_only || headers_only)); then + COMPREPLY=( -f -o -q ) + elif ((res_sort)); then + COMPREPLY=( -f -o -q -r -s ) + elif ((save_format == 1)); then + COMPREPLY=( -f -q -r -s -t ) + elif ((save_format == 3)); then + COMPREPLY=( -f -q -r -s ) + elif ((ignore_errors)); then + COMPREPLY=( -f -q ) + elif ((use_file)); then + COMPREPLY=( -\! -n -o -q -r -s -t ) + fi + fi + ;; + \<|\>) # redirection operator + compopt -o nospace + COMPREPLY=( $( compgen -f ) ) # no $cur, so completion starts without space after redirection + return 0 + ;; + \$\{*) # variables with a leading `${' + COMPREPLY=( $(compgen -v -P '${' -S '}' ${cur#??}) ) + return 0 + ;; + \$*) # variables with a leading `$' + COMPREPLY=( $(compgen -v -P '$' ${cur#?} ) ) + return 0 + ;; + *) # not an option + if ((got_action)); then + arr_sets=( $(ipset list -n ) ) + else + COMPREPLY=( $( compgen -W 'create add del test destroy list save restore flush rename swap help version' -- $cur ) ) + fi + case "$prev" in # depend on previous option + restore) COMPREPLY=( \< ) + for ((i=1; i <= ${#COMP_WORDS[@]}; i++)); do + if [[ ${COMP_WORDS[i]} = -f ]]; then + COMPREPLY=() # don't show redirector if we have option -f + break + fi + done + return 0 + ;; + create|version) COMPREPLY=() + return 0 + ;; + add|del|destroy|rename|swap|test) + COMPREPLY=( $( compgen -W '${arr_sets[@]}' -- $cur ) ) + return 0 + ;; + save) + if [[ $str_action = save ]]; then + COMPREPLY=( $( compgen -W '${arr_sets[@]}' -- $cur ) ) + else + if ((save_format == 3)); then + COMPREPLY=( $( compgen -W 'list' -- $cur ) ) + fi + fi + ;; + list) COMPREPLY=( $( compgen -W '${arr_sets[@]}' -- $cur ) ) + return 0 + ;; + help) COMPREPLY=( $( compgen -W '${arr_types[@]}' -- $cur ) ) + return 0 + ;; + -o) + if ((names_only || headers_only)); then + COMPREPLY=( $( compgen -W 'plain xml' -- $cur ) ) + else + COMPREPLY=( $( compgen -W 'plain save xml' -- $cur ) ) + fi + return 0 + ;; + -f) compopt -o nospace + COMPREPLY=( $( compgen -f -- $cur ) ) + return 0 + ;; + \<|\>) compopt -o nospace + COMPREPLY=( $( compgen -f -- $cur ) ) + return 0 + ;; + -) COMPREPLY=() # interactive mode + return 0 + ;; + *) + if ((got_action)); then + COMPREPLY=( $( compgen -W '${arr_sets[@]}' -- $cur ) ) + for ((i=1; i <= ${#COMP_WORDS[@]}; i++)); do + case "${COMP_WORDS[i]}" in + add) + for ((x=${COMP_WORDS[i+1]}; x <= ${#COMP_WORDS[@]}; x++)); do + if [[ ${COMP_WORDS[x]} = $prev ]]; then + COMPREPLY=() # only list sets after the action command + break 2 + fi + done + break + ;; + create) COMPREPLY=() + if [[ ${COMP_WORDS[i+1]} = $prev ]]; then + COMPREPLY=( $( compgen -W '${arr_types[@]}' -- $cur ) ) + fi + break + ;; + del) # complete members + if [[ ${COMP_WORDS[i+1]} = $prev ]]; then + while read -r; do + [[ $REPLY = Members:* ]] && in_list=1 && continue + ((in_list)) || continue + arr_members[${#arr_members[@]}]="$REPLY" + done < <(ipset list "$prev" 2>/dev/null) + COMPREPLY=( $( compgen -W '${arr_members[@]}' -- $cur ) ) + else + COMPREPLY=() + fi + break + ;; + help) + if [[ ${COMP_WORDS[i+1]} ]]; then + COMPREPLY=() # don't go further than showing the set types + return 0 + fi + break + ;; + restore) COMPREPLY=() # not a redirecton + break + ;; + swap) + for x in ${!arr_sets[@]}; do + if [[ ${arr_sets[x]} = ${COMP_WORDS[i+2]} ]]; then + COMPREPLY=() # only list two sets + break 2 + fi + done + break + ;; + *) + for ((y=1; y <= ${#COMP_WORDS[@]}; y++)); do + [[ ${COMP_WORDS[y]} ]] || continue + for x in ${!arr_sets[@]}; do + if [[ ${arr_sets[x]} = ${COMP_WORDS[y]} ]]; then + COMPREPLY=() # list only one set + break 2 + fi + done + done + ;; + esac + done + else # we don't have the action yet, check options to display appropiate actions + if ((save_format || names_only || headers_only)); then + COMPREPLY=( $( compgen -W 'list' -- $cur ) ) + return 0 + elif ((res_sort)); then + COMPREPLY=( $( compgen -W 'list save' -- $cur ) ) + return 0 + elif ((ignore_errors && use_file)); then + COMPREPLY=( $( compgen -W 'restore' -- $cur ) ) + return 0 + elif ((ignore_errors)); then + COMPREPLY=( $( compgen -W 'create add del restore' -- $cur ) ) + return 0 + elif ((use_file)); then + COMPREPLY=( $( compgen -W 'list save restore' -- $cur ) ) + return 0 + fi + fi + ;; + esac + ;; +esac +if ((${#COMPREPLY[@]})); then # post process the reply + for ((i=1; i <= ${#COMP_WORDS[@]}; i++)); do # remove dupe options + [[ ${COMP_WORDS[i]} = @(""|-) ]] && continue + for x in ${!COMPREPLY[@]}; do + if [[ ${COMP_WORDS[i]} = ${COMPREPLY[x]} ]]; then + unset COMPREPLY[$x] + break + fi + done + done +else + _ipset_bash_default_compl "$cur" +fi +if [[ $DEBUG ]]; then + printf "COMPREPLY:\n" + printf "<%s>\n" "${COMPREPLY[@]}" +fi +} +complete -F _ipset_complete ipset