blob: f92c7ab5d75a5488d9a73b72faaf0ba6bca6521d [file] [log] [blame]
#!/bin/bash
# -----------------------------------------------------------------
# ipset set listing wrapper script
#
# https://github.com/AllKind/ipset_list
# https://sourceforge.net/projects/ipset-list/
# -----------------------------------------------------------------
# Copyright (C) 2013-2014 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 <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------
#
# This is the bash programmable completion for ipset_list
# (put it into ~/.bash_completion or /etc/bash_completion.d/)
#
# -----------------------------------------------------------------
# Name may be modified
ipset_list=ipset_list
# -----------------------------------------------------------------
# -----------------------------------------------------------------
# DO NOT MODIFY ANYTHING BEYOND THIS LINE!
# -----------------------------------------------------------------
shopt -s extglob
# -----------------------------------------------------------------
# Functions
# -----------------------------------------------------------------
_ipset_list_show_sets() {
COMPREPLY=( $( compgen -W '${sets[@]}' -- "$cur" ) )
# dedupe sets listing
for ((i=set_index; i < ${#words[@]}-1; i++)); do
_ipset_list_remove_reply_entry "${words[i]}"
done
}
_ipset_list_remove_reply_entry() {
local -i x
while (($#)); do
for x in ${!COMPREPLY[@]}; do
if [[ ${COMPREPLY[x]} = $1 ]]; then
if [[ $_DEBUG_NF_COMPLETION ]]; then
printf "removing dupe entry COMPREPLY[$x]: %s\n" \
"${COMPREPLY[x]}"
fi
unset COMPREPLY[x]
break
fi
done
shift
done
}
# -----------------------------------------------------------------
# Main
# -----------------------------------------------------------------
_ipset_list_complete() {
local -i i=x=got_bashcompl=0
local -i show_all=isolate=show_members=resolve=headers_only=names_only=0
local -i header_operation=set_index=0
local cur prev cword words str_tmp
local sets=( $("${ipset_list:-ipset_list}" -n) )
local opts=(-- -? -a -c -d -h -i -m -n -r -s -t -v)
local Copts=(-Ca -Cs -Co)
local Fopts=(-Fh -Fi -Fg -Fr -Oi)
local Hopts=(-Hi -Hr -Hs -Ht -Hv)
local Topts=(-Tm -To -Ts)
local Xopts=(-Xh -Xg -Xr -Xs -Xo)
local arr_types=()
: ${PATH:=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}
COMPREPLY=()
# expecting _get_comp_words_by_ref() to exist from bash_completion
if declare -f _get_comp_words_by_ref &>/dev/null; then got_bashcompl=1
_get_comp_words_by_ref -n : cur prev cword words || return
else got_bashcompl=0 # not so neat, but a workaround
COMP_WORDBREAKS="${COMP_WORDBREAKS//:/}"
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
cword=$COMP_CWORD
for i in ${!COMP_WORDS[@]}; do words[i]="${COMP_WORDS[i]}"; done
fi
#_DEBUG_NF_COMPLETION=Y
if [[ $_DEBUG_NF_COMPLETION ]]; 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 "\ncur: <%s> prev: <%s>\n" "$cur" "$prev"
printf "words:\n"
printf "<%s>\n" "${words[@]}"
fi
# collect info of cmdline
for ((i=1; i < ${#words[@]}-1; i++)); do
case "${words[i]}" in
-a) ((set_index)) && break || show_all=1 ;;
-i) ((set_index)) && break || isolate=1 Copts=(-Co) ;;
-m) ((set_index)) && break || show_members=1 ;;
-n) ((set_index)) && break || names_only=1 ;;
-r) ((set_index)) && break || resolve=1 ;;
-t) ((set_index)) && break || headers_only=1 Xopts=(-Xh -Xs) Fopts=(${Fopts[*]/-Oi/}) ;;
--) ((set_index)) && break || set_index=$((i+1)) ;;
-\?|-h|-v)
((set_index)) || return 0
;;
@(-Fh|-Fi|-Hi|-Xh)) ((set_index)) && break || header_operation=1 ;;
*)
((set_index)) && break
# options expecting an opt arg
str_tmp="@(-@(d|Fg|Fh|Fi|Fr|Hi|Ht|Hr|Hs|Hv|Mc|Oi|To|Xg|Xh|Xr|Xs))"
if [[ ${words[i-1]} = $str_tmp ]]; then
continue
fi
# if not an option, register set index
str_tmp="@(-@(-|?|a|c|d|h|i|m|n|r|s|t|v|Ca|Cs|Co|Fg|Fh|Fi|Fr|Hi|Ht|Hr|Hs|Hv|Mc|Oi|To|Tm|Ts|Xg|Xh|Xo|Xr))"
if [[ ${words[i]} != $str_tmp ]]; then
for x in ${!sets[@]}; do
if [[ ${sets[x]} = ${words[i]} ]]; then
set_index=$i
break
fi
done
fi
esac
done
# invalid combinations of options
if ((names_only)); then
if ((headers_only)); then
return 0
fi
fi
if ((headers_only||names_only)); then
if ((show_all || show_members || isolate || resolve)); then
return 0
fi
elif ((isolate)); then
if ((show_all || header_operation)); then
return 0
fi
fi
# start setting compreply
# all depends on $set_index
if ((set_index)); then
if ((isolate && cword > set_index)); then
return 0 # allow only one set with isolate
fi
# dont' allow an option after the set name(s)
# allows to list sets which start with an hyphen
# and also handles those who have the name of ipset_list options
_ipset_list_show_sets
else
if [[ $prev = @(-@(\?|d|h|v|Fg|Fi|Fr|Hi|Ht|Oi|To|Xg|Xr)) ]]; then
return 0
elif [[ $prev = -Xs ]]; then
# list sets if user does not want to enter a glob
_ipset_list_show_sets
elif [[ $prev = -Ht ]]; then i=0
# show supported set types
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
COMPREPLY=( $( compgen -W '${arr_types[@]}' -- "$cur" ) )
elif [[ $prev = @(-Fh|-Xh) ]]; then
# retrieve list of headers
if ((${#sets[*]} > 0)); then
while read -r; do
[[ $REPLY = Name ]] && continue
COMPREPLY[${#COMPREPLY[@]}]="$REPLY"
done < <("$ipset_list" -t "${sets[0]}"|command awk -F: '{print $1}')
compopt -o nospace
local IFS=$'\n'
if [[ $prev = -Xh ]]; then
COMPREPLY=( $( compgen -P '"' -S ':*"' \
-W '${COMPREPLY[@]}' -- "$cur" ) )
elif [[ $prev = -Fh ]]; then
COMPREPLY=( $( compgen -P '"' -S ':"' \
-W '${COMPREPLY[@]}' -- "$cur" ) )
fi
fi
elif [[ $prev = @(-@(Hr|Hs|Hv|Mc)) ]]; then
# options making use of arithmetic comparison
compopt -o nospace
COMPREPLY=( $( compgen -P '\' -W '\! \< \> \<= \>=' -- "$cur" ) )
elif [[ $cur = -* ]]; then
# any option is requested
case "$prev" in
@(-@(\?|d|h|v|Fg|Fh|Fi|Fr|Hi|Ht|Hr|Hs|Hv|Mc|Oi|To|Xg|Xh|Xr)))
# options that exclude any other option,
# or need a value we can't predict
return 0
;;
esac
# these options don't allow any other
if ((${#words[@]} > 2)); then
opts=("${opts[@]/@(-v|-h|-\?)/}")
fi
# some options allow only a subset of other options
if ((isolate)); then
COMPREPLY=( $(compgen -W '-- -Co -d -r -s -Fg -Fr -Oi -To -Xg -Xo -Xr' -- $cur ) )
elif ((names_only)); then
COMPREPLY=( $(compgen -W \
'-- -c ${Copts[@]} ${Fopts[@]} ${Hopts[@]} -Mc ${Topts[@]} -Xs' \
-- $cur ) )
elif ((headers_only)); then
COMPREPLY=( $(compgen -W \
'-- -c ${Copts[@]} ${Fopts[@]} ${Hopts[@]} -Mc ${Topts[@]} ${Xopts[@]}' \
-- $cur ) )
elif ((show_members)); then
COMPREPLY=( $(compgen -W \
'-- -c -d -r -s ${Copts[@]} ${Fopts[@]} ${Hopts[@]} -Mc ${Topts[@]} -Xg -Xr -Xo' \
-- $cur ) )
elif ((show_all)); then
COMPREPLY=( $(compgen -W \
'-- -c -d -r -s ${Copts[@]} ${Fopts[@]} ${Hopts[@]} -Mc ${Topts[@]} ${Xopts[@]}' \
-- $cur ) )
elif ((resolve)); then
COMPREPLY=( $(compgen -W \
'-- -a -c -d -s -m ${Copts[@]} ${Fopts[@]} ${Hopts[@]} -Mc ${Topts[@]} ${Xopts[@]}' \
-- $cur ) )
elif ((header_operation)); then
COMPREPLY=( $(compgen -W \
'-- -a -c -d -s -m -t ${Copts[@]} ${Fopts[@]} ${Hopts[@]} -Mc ${Topts[@]} ${Xopts[@]}' \
-- $cur ) )
else
COMPREPLY=( $(compgen -W \
'${opts[@]} ${Copts[@]} ${Fopts[@]} ${Hopts[@]} -Mc ${Topts[@]} ${Xopts[@]}' \
-- $cur ) )
fi
# post process the reply
if ((${#COMPREPLY[@]})); then
x=$((set_index ? set_index : ${#words[*]}-1))
# mutual exclusive options
for ((i=1; i < x; i++)); do
case "${words[i]}" in
-Fg) _ipset_list_remove_reply_entry "-Fr" ;;
-Fr) _ipset_list_remove_reply_entry "-Fg" ;;
-Xg) _ipset_list_remove_reply_entry "-Xr" ;;
-Xr) _ipset_list_remove_reply_entry "-Xg" ;;
esac
# options allowed multiple times
if [[ ${words[i]} = @(""|-|-@(Fh|Fi|Hi|Mc|Oi|Xh|Xs)) ]]; then
continue
else # remove dupe
_ipset_list_remove_reply_entry "${words[i]}"
fi
done
fi
elif [[ $cur = * ]]; then
# non option request
# default to sets listing
_ipset_list_show_sets
fi
fi
((got_bashcompl)) && __ltrim_colon_completions "$cur"
if [[ $_DEBUG_NF_COMPLETION ]]; then
printf "COMPREPLY:\n"
printf "<%s>\n" "${COMPREPLY[@]}"
fi
}
complete -F _ipset_list_complete "${ipset_list:-ipset_list}"