summaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/ipset_bash_completion/README.md55
-rw-r--r--utils/ipset_bash_completion/ipset_bash_completion1153
-rw-r--r--utils/ipset_list/README.md11
-rwxr-xr-xutils/ipset_list/ipset_list426
-rw-r--r--utils/ipset_list/ipset_list_bash_completion55
5 files changed, 1179 insertions, 521 deletions
diff --git a/utils/ipset_bash_completion/README.md b/utils/ipset_bash_completion/README.md
index 7a43eaa..ce5c47e 100644
--- a/utils/ipset_bash_completion/README.md
+++ b/utils/ipset_bash_completion/README.md
@@ -29,11 +29,12 @@ Providing some kind of interactive help.
- Show and complete services (also named port ranges), protocols,
icmp[6] types and interface names when adding, deleting or testing elements.
- Show and complete hostnames, when adding, deleting or testing elements.
-- Show and complete mac addresses.
+- Show and complete ip and mac addresses (dynamically and from file).
- Complete on filenames if the current option requires it.
-- Complete variable names, command substitution and globbing patterns.
+- Complete variable names and command substitution.
- Do not complete if an invalid combination of options is used.
- Do not complete if an invalid value of an option argument is detected.
+- Environment variables allow to modify completion behaviour.
Installation
@@ -90,6 +91,9 @@ Taking the description from the bash man-page:
attempts to read /etc/hosts to obtain the list of possible hostname completions.
When HOSTFILE is unset, the hostname list is cleared.
+As it is impossible to distinguish between IPv4 and IPv6 hostnames without resolving
+them, it is considered best practice to seperate IPv4 hosts from IPv6 hosts
+in different files.
If the *bash-completion* package is available hostname completion is extended
the following way (description from bash-completion source):
@@ -103,10 +107,11 @@ the following way (description from bash-completion source):
Also the environment variable **_IPSET_SSH_CONFIGS** controls which files are taken
-as ssh_config files, in order to retrieve the globl and user known_host files,
+as ssh_config files, in order to retrieve the global and user known_host files,
which will be used for hostname completion.
-For all *net* type of sets, if hostname completion is attempted,
+For all *net* type of sets and the hash:ip,mark set type, if hostname completion is attempted,
+if the environment variable **_IPSET_COMP_NETWORKS** is set to a non-empty value,
networks are retrieved from /etc/networks.
Also a list of ip addresses can be supplied using the environment variable
@@ -116,8 +121,7 @@ is done automatically based on the set header.
---
-When deleting elements from one of the following set types:
-**hash:ip,port hash:ip,port,ip hash:ip,port,net hash:net,port hash:net,iface**
+When deleting elements from any but list:set set types:
the environment variable **_IPSET_COMPL_DEL_MODE** is queried to decide how to complete.
If it is set to 'members' it will list the members of the set.
If it is set to 'spec' it will follow the format of a port specification ([proto:]port).
@@ -126,8 +130,7 @@ the list of possible completions (this is the default).
---
-When testing elements from one of the following set types:
-**hash:ip,port hash:ip,port,ip hash:ip,port,net hash:net,port hash:net,iface**
+When testing elements from any but list:set set types:
the environment variable **_IPSET_COMPL_TEST_MODE** is queried to decide how to complete.
If it is set to 'members' it will list the members of the set.
If it is set to 'spec' it will follow the format of a port specification ([proto:]port).
@@ -147,7 +150,7 @@ If the variable is unset mac addresses are fetched from arp cache,
---
When adding elements to one of the following set types:
-**hash:ip,port hash:ip,port,ip hash:ip,port,net hash:net,port**
+**hash:ip,port hash:ip,port,ip hash:ip,port,net hash:net,port hash:net,port,net**
and completion is attempted on the port specification,
the list of possible completions may become quite long.
Especially if no characters are given to match on.
@@ -158,15 +161,31 @@ values such a port specification can possibly have.
---
At any time completion on variable names (starting with '$' or '${'),
-command substitution (starting with '$(') and file name [ext] globbing
-patterns is available.
+or command substitution (starting with '$(') is available.
+Using this with values validated by input validation, will stop further completion.
+In that case it is recommended to disable input validation (see below).
+
+
+---
+
+If the environment variable **_IPSET_VALIDATE_INPUT** is set to a non empty value
+validation of users input is disabled.
+
+---
+
+If the environment variable **_DEBUG_NF_COMPLETION** is defined (any value)
+debugging information is displayed.
Compatibility
=============
-Tested with ipset v6.16.1.
+Compatible with ipset versions 6+.
+
+Tested with ipset v6.20.1.
+
+bash v4+ is required.
Compatibility for future ipset versions cannot be promised, as new options may appear,
which of course are currently unpredictable.
@@ -180,9 +199,6 @@ Also the colon (if there) is removed from COMP_WORDBREAKS.
This alteration is globally, which might affect other completions,
if they do not take care of it themselves.
-If the bash-completion package is available bash v4+ is required.
-Otherwise bash v3.2 and upwards are supported.
-
The iproute program (ip) is needed to display information about the local system.
@@ -193,3 +209,12 @@ Availability
https://github.com/AllKind/ipset-bash-completion
http://sourceforge.net/projects/ipset-bashcompl/
+
+
+
+Bugs
+============
+
+Please report bugs!
+
+
diff --git a/utils/ipset_bash_completion/ipset_bash_completion b/utils/ipset_bash_completion/ipset_bash_completion
index cc7ea7b..25f8db2 100644
--- a/utils/ipset_bash_completion/ipset_bash_completion
+++ b/utils/ipset_bash_completion/ipset_bash_completion
@@ -7,7 +7,7 @@
# https://sourceforge.net/projects/ipset-bashcompl
# -----------------------------------------------------------------
-# Copyright (C) 2013 AllKind (AllKind@fastest.cc)
+# 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
@@ -23,11 +23,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------
+# Compatible with ipset versions: 6+
# Tested with ipset versions:
-# 6.16.1
+# 6.20.1
# -----------------------------------------------------------------
# Requirements:
#
+# bash v4 or greater.
+#
# The bash completion package version 2.0 or greater is recommended.
# http://bash-completion.alioth.debian.org/
#
@@ -43,41 +46,16 @@
#
# -----------------------------------------------------------------
#
-# Version 2.0
+# Version 2.5
#
# -----------------------------------------------------------------
+shopt -s extglob
+
# -----------------------------------------------------------------
# Functions
# -----------------------------------------------------------------
-_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 # 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" ) )
-else # last fallback is filename completion
- if ((got_bashcompl)); then
- _filedir
- else
- compopt -o nospace
- COMPREPLY=( $( compgen -f -- "$cur" ) )
- fi
-fi
-}
-
_ipset_colon_ltrim() {
((got_bashcompl)) || return 0
__ltrim_colon_completions "$1"
@@ -95,15 +73,16 @@ return 1
}
_ipset_get_set_type() {
+local n d
while read n d; do
[[ $n = Type: ]] && printf '%s\n' $d && break
done < <(ipset -t list "$1" 2>/dev/null)
}
-_ipset_set_has_timout() {
+_ipset_set_has_option() {
while read -r; do
- [[ $REPLY = Header:*timeout* ]] && return 0
-done < <(ipset -t list "$1")
+ [[ $REPLY = Header:*$1* ]] && return 0
+done < <(ipset -t list "$2")
return 1
}
@@ -144,18 +123,10 @@ while read -r; do
done < <(ipset list "$1" 2>/dev/null)
}
-_ipset_set_has_timeout() {
-while read -r; do
- [[ $REPLY = Header:*timeout* ]] && return 0
-done < <(ipset -t list "$1")
-return 1
-}
-
_ipset_get_set_family() {
while read -r; do
[[ $REPLY = Header:*"family inet6"* ]] && printf "v6\n" && return
[[ $REPLY = Header:*"family inet "* ]] && printf "v4\n" && return
- [[ $REPLY = Header:*"range "*:*:* ]] && printf "v6\n" && return
[[ $REPLY = Header:*"range "*.*.*.* ]] && printf "v4\n" && return
done < <(ipset -t list "$1")
}
@@ -163,9 +134,15 @@ done < <(ipset -t list "$1")
_ipset_dedupe_cmd_opts() {
local str_opt
local -i idx
-for str_opt in "${@}"; do
+for str_opt; do
for idx in ${!arr_dupe_cmd_opts[@]}; do
- [[ $str_opt = ${arr_dupe_cmd_opts[idx]} ]] && continue 2
+ if [[ $str_opt = ${arr_dupe_cmd_opts[idx]} ]]; then
+ if [[ $_DEBUG_NF_COMPLETION ]]; then
+ printf "removing dupe option str_opt: %s\n" \
+ "${arr_dupe_cmd_opts[idx]}"
+ fi
+ continue 2
+ fi
done
printf "%s\n" "$str_opt"
done
@@ -225,7 +202,7 @@ else
str_list='- ${arr_opts[@]}'
fi
fi
-COMPREPLY=( $( compgen -W "- $str_list" -- "$cur" ) )
+COMPREPLY=( $( compgen -W "$str_list" -- "$cur" ) )
((${#COMPREPLY[@]})) || return 0
# post process the reply
@@ -274,6 +251,7 @@ done
_ipset_get_networks() {
local foo str_net rest
[[ -r /etc/networks ]] || return 0
+[[ ${_IPSET_COMP_NETWORKS-1} ]] || return 0
while read -r foo str_net rest; do
[[ $foo = @(""|*([[:blank:]])#*) ]] && continue
[[ $str_net = *([[:blank:]])#* ]] && continue
@@ -291,13 +269,42 @@ while read -r str_name rest; do
done < /etc/protocols
}
+_ipset_get_svnum() {
+# find service num to set offset
+local str_name str_num str_p=all rest
+local _str_p _str_o=""
+while (($#)); do
+ if [[ $1 = -p ]]; then
+ _str_p="${2:-all}"
+ shift
+ elif [[ $1 = -o && ${2:-"-no"} != -* ]]; then
+ # second part of range will have offset = first_part_of_range+1
+ _str_o="$2"
+ shift
+ fi
+ shift
+done
+if [[ $_str_o && $_str_o != +([[:digit:]]) ]]; then
+ while read str_name str_num rest; do
+ if [[ $str_name = *([[:blank:]])#* ]]; then continue
+ elif [[ $_str_p != all && ${str_num#*/} != $_str_p ]]; then
+ continue
+ fi
+ [[ $str_name = $_str_o ]] && printf "%s\n" ${str_num%/*} && return
+
+ done < /etc/services
+else
+ printf "%s\n" "$_str_o"
+fi
+}
+
_ipset_get_services() {
local str_offset="" str_name str_num str_p=all rest
while (($#)); do
if [[ $1 = -p ]]; then
str_p="${2:-all}"
shift
- elif [[ $1 = -o && $2 ]]; then
+ elif [[ $1 = -o && ${2:-"-no"} != -* ]]; then
# second part of range will have offset = first_part_of_range+1
str_offset="${2}"
shift
@@ -306,15 +313,10 @@ while (($#)); do
done
# find service num to set offset
if [[ $str_offset && $str_offset != +([[:digit:]]) ]]; then
- while read str_name str_num rest; do
- if [[ $str_name = *([[:blank:]])#* ]]; then continue
- elif [[ $str_p != all && ${str_num#*/} != $str_p ]]; then
- continue
- fi
- [[ $str_name = $str_offset ]] && str_offset=${str_num%/*} && break
- done < /etc/services
- [[ $str_offset = +([[:digit:]]) ]] || return 0
+ str_offset=$(_ipset_get_svnum -p "$str_p" -o "$str_offset")
+ [[ $str_offset = +([[:digit:]]) ]] || str_offset="" # we failed
fi
+# identify and print the services
while read -r str_name str_num rest; do
if [[ $str_name = @(""|*([[:blank:]])#*) ]]; then continue
elif [[ $str_p != all && ${str_num#*/} != $str_p ]]; then
@@ -335,201 +337,432 @@ while read -r; do
done < <(PATH=${PATH}:/sbin command ip -o link show)
}
+_ipset_get_iplist() {
+# if a file with ip addresses is in env var, load em
+local str_ip rest
+if [[ $1 = v4 ]]; then
+str_regex='^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/([3][0-2]|[1-2]?[0-9]))?$'
+elif [[ $1 = v6 ]]; then
+str_regex='^([0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}|([0-9a-fA-F]{1,4}:){1}(:[0-9a-fA-F]{1,4}){1,6}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,6}(:[0-9a-fA-F]{1,4}){1})(/([1][0-2][0-9]|[1-9]?[0-9]))?$'
+else return 0
+fi
+[[ $_IPSET_IPLIST_FILE && -r $_IPSET_IPLIST_FILE ]] || return 0
+while read -r str_ip rest; do
+ [[ $str_ip = *([[:blank:]])\#* ]] && continue
+ str_ip="${str_ip//\#*/}"
+ [[ $str_ip =~ $str_regex ]] && printf "%s\n" "$str_ip"
+done < "${_IPSET_IPLIST_FILE}"
+}
+
+_ipset_complete_elements() {
+local lcur="$1" str_lprefix=""
+local -i no_range=0
+shift
+while (($#)); do
+ [[ $1 = --no-range ]] && no_range=1
+ shift
+done
+if [[ $lcur = *-[[]+([!]]) ]]; then # incomplete range - host/port with dash
+ str_lprefix="${lcur%\-[*}-"
+ lcur="${lcur#"$str_lprefix"}"
+elif [[ $lcur = \[*\]-* ]]; then # first part of host/portname range
+ str_lprefix="${lcur%\]-*}]-" lcur="${lcur##*\]-}"
+elif [[ $lcur = *-* ]]; then
+ str_lprefix="${lcur%-*}-" lcur="${lcur##*-}"
+elif [[ $lcur =~ (tcp|udp):.* ]]; then # proto:port (bitmap:port)
+ str_lprefix="${BASH_REMATCH[1]}:" lcur="${lcur#*:}"
+ no_range=0 # workaround
+fi
+if [[ $str_lprefix ]]; then
+ [[ $str_lprefix = */* ]] && return 1
+ ((no_range)) && return 1
+ _ipset_get_members --names-only "$str_setname"
+ COMPREPLY+=( $( compgen -P "$str_lprefix" -W '${arr_members[@]/*\/*/}' -- "$lcur" ) )
+else
+ _ipset_get_members --names-only "$str_setname"
+ COMPREPLY+=( $( compgen -W '${arr_members[@]}' -- "$lcur" ) )
+fi
+}
+
+_ipset_complete_portrange() {
+# complete port ranges
+local lcur="$1"
+local str_lprefix="$lcur" str_p=""
+str_var=0 str_glob='[^[]*-*'
+if [[ $lcur = *:* ]]; then # look for `proto:'
+ ((got_bp_proto)) || return 0 # supported since ipset v6.20
+ # only tcp/udp is valid as PROTO spec
+ [[ ${lcur%%:*} = @(tcp|udp) ]] || return 0
+ str_p=${lcur%%:*}
+ lcur="${lcur#$str_p:}"
+fi
+if [[ $lcur = \[*-*\]-* ]]; then # spec with bracket
+ str_var="${lcur#\[}"
+ str_var="${str_var%%\]*}"
+ lcur="${lcur#*\]-}"
+ str_lprefix=${str_lprefix%"$lcur"}
+elif [[ $lcur = $str_glob ]]; then # spec without bracket
+ str_var="${lcur%%-*}"
+ lcur="${lcur#*-}"
+ str_lprefix=${str_lprefix%"$lcur"}
+else # no prefix
+ str_lprefix=""
+ compopt -o nospace
+fi
+if [[ $str_p ]]; then # we have a proto spec
+ COMPREPLY=( $(compgen -P "$str_p:$str_lprefix" \
+ -W '$(_ipset_get_services -o $str_var -p $str_p)' -- "$lcur" ) )
+ _ipset_colon_ltrim "$str_p:$str_lprefix$lcur"
+else
+ if [[ $str_lprefix ]]; then
+ COMPREPLY=( $(compgen -P "$str_lprefix" \
+ -W '$(_ipset_get_services -o $str_var)' -- "$lcur" ) )
+ else
+ if ((got_bp_proto)); then # supported since ipset v6.20
+ COMPREPLY=( $(compgen \
+ -W 'tcp: udp: $(_ipset_get_services)' -- "$lcur" ) )
+ else # only tcp services prior to ipset v6.20
+ COMPREPLY=( $(compgen \
+ -W '$(_ipset_get_services -p tcp)' -- "$lcur" ) )
+ fi
+ fi
+fi
+}
+
+_ipset_complete_hostnames() {
+local -i idx got_bracket=0
+local lcur="${1//@(\[|\])}"
+[[ $lcur = $1 ]] || got_bracket=1
+if ((got_bashcompl)); then
+ _ipset_known_hosts -F "$_IPSET_SSH_CONFIGS" -- "$lcur"
+else
+ if [[ ${COMP_KNOWN_HOSTS_WITH_HOSTFILE-1} ]]; then
+ COMPREPLY+=( $( compgen -A hostname -- "$lcur" ) )
+ fi
+fi
+for idx in ${!COMPREPLY[@]}; do
+ if [[ ${COMPREPLY[idx]} = *-* ]]; then
+ COMPREPLY[idx]="[${COMPREPLY[idx]}]"
+ else
+ ((got_bracket)) && unset COMPREPLY[idx]
+ fi
+done
+#_ipset_colon_ltrim "$lcur"
+}
+
_ipset_complete_iface_spec() {
-if [[ $cur != *,* ]]; then
- str_prefix=""
+local lcur="$1" str_lprefix=""
+if [[ $lcur != *,* ]]; then
+ str_lprefix="" str_glob='+([![])-*'
compopt -o nospace
- if [[ $cur = *-* ]]; then # range spec
- str_prefix="${cur%-*}-" cur="${cur#*-}"
+ if [[ x$str_action != xtest ]]; then
+ if [[ $lcur = \[*-*\]-* ]]; then # hostrange spec
+ str_lprefix="${lcur%\]-*}]-" lcur="${lcur#*\]-}"
+ elif [[ $lcur = *-[[]+([!]]) ]]; then # incomplete range - host with dash
+ str_lprefix="${lcur%-\[*}-"
+ lcur="${lcur#"$str_lprefix"}"
+ elif [[ $lcur = $str_glob ]]; then # range spec
+ str_lprefix="${lcur%-*}-" lcur="${lcur#*-}"
+ fi
fi
# ip-list from file
- COMPREPLY=( $( compgen -W \
+ COMPREPLY+=( $( compgen -W \
'$(_ipset_get_iplist "$(_ipset_get_set_family "$str_setname")")' \
- -- "$cur" ) )
+ -- "$lcur" ) )
# networks
- while read; do
- [[ $REPLY = $cur* ]] && COMPREPLY+=( "$REPLY" )
- done < <(_ipset_get_networks)
+ COMPREPLY+=( $( compgen -W '$(_ipset_get_networks)' -- "$lcur" ) )
# hostnames
- if ((got_bashcompl)); then
- _known_hosts_real -F "$_IPSET_SSH_CONFIGS" -- "$cur"
+ _ipset_complete_hostnames "$lcur"
+ if [[ $str_lprefix ]]; then # range spec
+ COMPREPLY=( $( compgen -P "$str_lprefix" -W '${COMPREPLY[@]//*\/*/}' \
+ -- "$lcur" ) )
else
- if [[ ${COMP_KNOWN_HOSTS_WITH_HOSTFILE-1} ]]; then
- COMPREPLY+=( $( compgen -A hostname "$cur" ) )
- fi
- _ipset_colon_ltrim "$cur"
- fi
- if [[ $str_prefix ]]; then # range spec
- COMPREPLY=( $( compgen -P "$str_prefix" -W '${COMPREPLY[@]}' -- "$cur" ) )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- "$lcur" ) )
fi
if ((${#COMPREPLY[@]} == 1)); then
- if [[ ${COMPREPLY[*]} = @(*/*|*-*) ]]; then
+ if [[ $str_lprefix || x$str_action = xtest ]]; then
COMPREPLY=( ${COMPREPLY[*]}, )
- fi
+ else
+ COMPREPLY=( ${COMPREPLY[*]}, ${COMPREPLY[*]}- )
+ fi
fi
-elif [[ $cur = *,* ]]; then
- str_prefix="${cur}" cur="${cur#*,}" str_var=""
- str_prefix="${str_prefix%"$cur"}"
- if [[ $cur = physdev:* ]]; then
- cur="${cur#physdev:}"
- str_prefix="${str_prefix}physdev:"
+ _ipset_colon_ltrim "$str_lprefix$lcur"
+elif [[ $lcur = *,* ]]; then
+ str_lprefix="${lcur}" lcur="${lcur#*,}" str_var=""
+ str_lprefix="${str_lprefix%"$lcur"}"
+ if [[ $lcur = physdev:* ]]; then
+ lcur="${lcur#physdev:}"
+ str_lprefix="${str_lprefix}physdev:"
else
str_var="physdev:"
fi
- COMPREPLY=( $( compgen -P "$str_prefix" -W \
- '${str_var} $(_ipset_get_ifnames)' -- "$cur" ) )
+ COMPREPLY+=( $( compgen -P "$str_lprefix" -W \
+ '${str_var} $(_ipset_get_ifnames)' -- "$lcur" ) )
[[ ${COMPREPLY[0]} = *physdev: ]] && compopt -o nospace
- _ipset_colon_ltrim "$str_prefix"
+ _ipset_colon_ltrim "$str_lprefix"
+fi
+}
+
+_ipset_complete_host_spec() {
+local lcur="$1" str_lprefix="" str_lsuffix=""
+local -i no_range=0 v4_only=0
+if [[ $lcur = *,* && $str_type = hash:ip,mark ]]; then
+ return 0
+fi
+shift
+compopt -o nospace
+while (($#)); do
+ [[ $1 = --no-range ]] && no_range=1
+ [[ $1 = --v4 ]] && v4_only=1
+ shift
+done
+# range spec
+if [[ $lcur = @(""|+([[:word:]])) ]]; then # empty or [:word:]
+ str_lsuffix="-"
+elif [[ $lcur = [[]*-*[]] ]]; then # host with hyphen
+ str_lsuffix="-"
+elif [[ $lcur = [[]*([!]]) ]]; then # incomplete host with dash
+ str_lsuffix="-"
+elif [[ $lcur = *-[[]+([!]]) ]]; then # incomplete range - host with dash
+ str_lprefix="${lcur%\-[*}-"
+ lcur="${lcur#"$str_lprefix"}"
+elif [[ $lcur = \[*\]-* ]]; then # first part of hostname range
+ str_lprefix="${lcur%\]-*}]-" lcur="${lcur##*\]-}"
+elif [[ $lcur != *-* ]]; then # no hypen
+ str_lsuffix="-"
+else # ip-range
+ str_lprefix="${lcur%-*}-" lcur="${lcur##*-}"
+fi
+if [[ $str_lprefix ]]; then
+ # range not valid
+ ((no_range)) && return 1
+ # range not valid if first part is ip/cidr
+ [[ $str_lprefix = */* ]] && return 1
+fi
+# ip-list from file
+if [[ $str_lprefix ]]; then
+ # only ipv4 range supported
+ COMPREPLY+=( $( compgen -W '$(_ipset_get_iplist v4)' -- "$lcur" ) )
+elif ((v4_only)); then
+ # this type only supports ipv4
+ COMPREPLY+=( $( compgen -W '$(_ipset_get_iplist v4)' -- "$lcur" ) )
+else
+ # we gather the family type
+ COMPREPLY+=( $( compgen -W \
+ '$(_ipset_get_iplist "$(_ipset_get_set_family "$str_setname")")' \
+ -- "$lcur" ) )
+ _ipset_colon_ltrim "$lcur"
+fi
+_ipset_complete_hostnames "$lcur"
+if [[ $str_lprefix ]]; then
+ # if the prefix is defined add it to compreply
+ COMPREPLY=( $( compgen -P "$str_lprefix" -W '${COMPREPLY[@]}' -- "$lcur" ) )
+else
+ # add networks for hash:net?(,net), hash:ip,mark, or bitmap:ip for add/del action
+ if [[ $str_type = hash:@(net?(,net)|ip,mark) ]] || \
+ [[ $str_type = bitmap:* && $str_action = @(add|create|del) ]]
+ then
+ COMPREPLY+=( $( compgen -W '$(_ipset_get_networks)' -- "$lcur" ) )
+ fi
+fi
+if ((${#COMPREPLY[@]} == 1)); then
+ if [[ $str_lprefix ]]; then
+ # we can add a space, if it's a range (not with hash:net,net or hash:ip,mark)
+ if [[ $str_type != hash:@(net,net|ip,mark) ]]; then
+ compopt +o nospace
+ fi
+ fi
fi
}
_ipset_complete_hostport_spec() {
# complete on host,proto:port[,host] spec
-local str_proto str_glob2
-if [[ $str_type = hash:ip,port,@(ip|net) ]]; then str_suffix=','
+local str_proto str_glob2 str_lprefix lcur str_lprefix2 lcur2
+local lcur="$1"
+if [[ $str_type = hash:@(ip|net),port,@(ip|net) ]]; then str_suffix=','
else str_suffix=''
fi
str_regex='^[^,]+,([^,]+)?$'
-if [[ $cur != *,* ]]; then
- str_prefix=""
+if [[ $lcur != *,* ]]; then
+ str_lprefix="" str_suffix=""
compopt -o nospace
- if [[ $str_type = hash:net,port && $str_action = @(add|del) && $cur = *-* ]]
- then # range spec
- str_prefix="${cur%-*}-" cur="${cur#*-}"
+ if [[ $str_type = hash:@(ip,port|net,port|ip,port,ip|ip,port,net|net,port,net) && \
+ $str_action = @(add|del|test) ]]
+ then
+ # range spec
+ if [[ $lcur = @(""|+([[:word:]])) ]]; then # empty or [:word:]
+ str_suffix="-"
+ elif [[ $lcur = [[]*-*[]] ]]; then # host with hyphen
+ str_suffix="-"
+ elif [[ $lcur = [[]*([!]]) ]]; then # incomplete host with dash
+ str_suffix="-"
+ elif [[ $lcur = *-[[]+([!]]) ]]; then # incomplete range - host with dash
+ str_lprefix="${lcur%\-[*}-"
+ lcur="${lcur#"$str_lprefix"}"
+ elif [[ $lcur = \[*\]-* ]]; then # first part of hostname range
+ str_lprefix="${lcur%\]-*}]-" lcur="${lcur##*\]-}"
+ elif [[ $lcur != *-* ]]; then # no hypen
+ str_suffix="-"
+ else # ip-range
+ str_lprefix="${lcur%-*}-" lcur="${lcur##*-}"
+ fi
fi
# ip-list from file
- COMPREPLY=( $( compgen -W \
+ COMPREPLY+=( $( compgen -W \
'$(_ipset_get_iplist "$(_ipset_get_set_family "$str_setname")")' \
- -- "$cur" ) )
- if [[ $str_type = hash:net,port ]]; then
- COMPREPLY+=( $( compgen -W '$(_ipset_get_networks)' -- "$cur" ) )
- _ipset_colon_ltrim "$cur"
+ -- "$lcur" ) )
+ if [[ $str_type = hash:net,port?(,net) ]]; then
+ COMPREPLY+=( $( compgen -W '$(_ipset_get_networks)' -- "$lcur" ) )
+ _ipset_colon_ltrim "$lcur"
fi
- if ((got_bashcompl)); then
- _known_hosts_real -F "$_IPSET_SSH_CONFIGS" -- "$cur"
- else
- if [[ ${COMP_KNOWN_HOSTS_WITH_HOSTFILE-1} ]]; then
- COMPREPLY+=( $( compgen -A hostname "$cur" ) )
- fi
- _ipset_colon_ltrim "$cur"
- fi
- if [[ $str_prefix ]]; then # range spec
- COMPREPLY=( $( compgen -P "$str_prefix" -W '${COMPREPLY[@]}' -- "$cur" ) )
+ _ipset_complete_hostnames "$lcur"
+ if [[ $str_lprefix ]]; then # range spec
+ COMPREPLY=( $( compgen -P "$str_lprefix" -W '${COMPREPLY[@]}' -- "$lcur" ) )
fi
if ((${#COMPREPLY[@]} == 1)); then
- if [[ $str_type = hash:net,port ]]; then
- if [[ ${COMPREPLY[*]} = @(*/*|*-*) ]]; then
- COMPREPLY=( ${COMPREPLY[*]}, )
- fi
+ if [[ $str_suffix = - ]]; then
+ COMPREPLY=( $( compgen -W '${COMPREPLY[*]}, ${COMPREPLY[*]}-' -- "$lcur" ) )
else
COMPREPLY=( ${COMPREPLY[*]}, )
fi
fi
-elif [[ $cur =~ $str_regex ]]; then
- (( $(IFS=,; set -- $str_type; printf "%d\n" $#) == 3 )) && compopt -o nospace
- str_glob='[^\[]*-' # otherwise messes up my vim syntax highlightning
+ _ipset_colon_ltrim "$str_lprefix$lcur"
+elif [[ $lcur =~ $str_regex ]]; then
+ compopt -o nospace
+ str_glob='[^[]*-' # otherwise messes up my vim syntax highlightning
str_regex='.*,(icmp|icmp6|tcp|sctp|udp|udplite):.*' # for compat put regex in var
- if [[ $cur != *icmp* && \
- $cur = *,@(?(tcp:|sctp:|udp:|udplite:)@(+([[:word:]])-|\[*-*\]-)|\[*-*\]-)* ]]
+ if [[ $lcur != *icmp* && \
+ $lcur = *,@(?(tcp:|sctp:|udp:|udplite:)@(+([[:word:]])-|\[*-*\]-)|\[*-*\]-)* ]]
then # range spec
- str_prefix="$cur" str_glob='*[[]*' str_glob2='*-\[*' str_proto="tcp" str_var=""
- [[ $cur =~ .*(tcp|sctp|udp|udplite):.* ]] && str_proto=${BASH_REMATCH[1]}
- if [[ $cur = *\[*-*\]-* ]]; then
- str_var="${cur#*,*[}" cur="${cur#*\]-}"
- str_prefix=${str_prefix%"$cur"} str_var="${str_var%\]*}"
- elif [[ $cur = $str_glob2 ]]; then
- str_var="${cur#*,}" cur="${cur#*-}"
+ str_lprefix="$lcur" str_glob='*[[]*' str_glob2='*,*-\[*' str_proto="tcp" str_var=""
+ [[ $lcur =~ .*(tcp|sctp|udp|udplite):.* ]] && str_proto=${BASH_REMATCH[1]}
+ if [[ $lcur = *,*\[*-*\]-* ]]; then
+ str_var="${lcur#*,*[}" lcur="${lcur##*\]-}"
+ str_lprefix=${str_lprefix%"$lcur"} str_var="${str_var%\]*}"
+ elif [[ $lcur = $str_glob2 ]]; then
+ str_var="${lcur#*,}" lcur="${lcur##*-}"
str_var="${str_var#${BASH_REMATCH[1]}:}"
- str_prefix=${str_prefix%"$cur"} str_var="${str_var%%-*}"
- elif [[ $cur != $str_glob ]]; then
- str_var="${cur#*,}" cur="${cur##*-}"
+ str_lprefix=${str_lprefix%"$lcur"} str_var="${str_var%%-*}"
+ else
+ str_var="${lcur#*,}" lcur="${lcur##*-}"
str_var="${str_var#${BASH_REMATCH[1]}:}"
- str_prefix=${str_prefix%"$cur"} str_var="${str_var%-*}"
+ str_lprefix=${str_lprefix%"$lcur"} str_var="${str_var%-*}"
+ fi
+ COMPREPLY+=( $( compgen -P "$str_lprefix" -S "$str_suffix" -W \
+ '$(_ipset_get_services -p "$str_proto" -o "$str_var")' -- "$lcur") )
+ if [[ $str_lprefix = *:* ]]; then
+ str_lprefix="${str_lprefix%:*}:"
fi
- COMPREPLY=( $( compgen -P "$str_prefix" -S "$str_suffix" -W \
- '$(_ipset_get_services -p "$str_proto" -o "$str_var")' -- "$cur") )
- if [[ $str_prefix = *:* ]]; then
- str_prefix="${str_prefix%:*}:"
+ _ipset_colon_ltrim "${str_lprefix}"
+ if ((${#COMPREPLY[@]} == 1)); then
+ if [[ $str_lprefix && $str_type != hash:@(ip|net),port,@(ip|net) ]]; then
+ compopt +o nospace
+ fi
fi
- _ipset_colon_ltrim "${str_prefix}"
- elif [[ $cur =~ $str_regex ]]; then
+ elif [[ $lcur =~ $str_regex ]]; then
# icmp[6] and services with (tcp|udp|sctp|udplite): prefix
str_var=${BASH_REMATCH[1]}
- str_prefix="${cur}" cur="${cur#*,}"
- str_prefix="${str_prefix%"$cur"}"
- cur="${cur#${BASH_REMATCH[1]}:}"
- str_prefix="${str_prefix}${BASH_REMATCH[1]}:"
+ str_lprefix="${lcur}" lcur="${lcur#*,}"
+ str_lprefix="${str_lprefix%"$lcur"}"
+ lcur="${lcur#${BASH_REMATCH[1]}:}"
+ str_lprefix="${str_lprefix}${BASH_REMATCH[1]}:"
if [[ $str_var = icmp ]]; then
- COMPREPLY=( $( compgen -P "$str_prefix" -S "$str_suffix" -W \
- '${arr_icmp_types[@]}' -- "$cur" ) )
+ COMPREPLY+=( $( compgen -P "$str_lprefix" -S "$str_suffix" -W \
+ '${arr_icmp_types[@]}' -- "$lcur" ) )
elif [[ $str_var = icmp6 ]]; then
- COMPREPLY=( $( compgen -P "$str_prefix" -S "$str_suffix" -W \
- '${arr_icmp6_types[@]}' -- "$cur" ) )
+ COMPREPLY+=( $( compgen -P "$str_lprefix" -S "$str_suffix" -W \
+ '${arr_icmp6_types[@]}' -- "$lcur" ) )
elif [[ $str_var = @(tcp|udp|sctp|udplite) ]]; then
- COMPREPLY=( $( compgen -P "$str_prefix" -W \
- '$(_ipset_get_services -p $str_var)' -- "$cur" ) )
- compopt -o nospace
+ COMPREPLY+=( $( compgen -P "$str_lprefix" -W \
+ '$(_ipset_get_services -p $str_var)' -- "$lcur" ) )
fi
- _ipset_colon_ltrim "$str_prefix"
- elif [[ $cur = *,* ]]; then # first attempt :/ long list
- str_prefix="${cur%,*}," cur="${cur#*,}"
+ _ipset_colon_ltrim "$str_lprefix"
+ elif [[ $lcur = *,* ]]; then # first attempt :/ long list
+ str_lprefix="${lcur%,*}," lcur="${lcur#*,}"
str_var="tcp: udp: sctp: udplite: icmp: icmp6:"
# add the services
- COMPREPLY=( $( compgen -P "$str_prefix" -W \
- '$str_var $(_ipset_get_services -p tcp)' -- "$cur" ) )
- for str_var in $( compgen -P "$str_prefix" -S ":0$str_suffix" -W \
- '$(_ipset_get_protocols)' -- "$cur" )
- do
- COMPREPLY+=( "$str_var" ) # add the protocols
- done
- _ipset_colon_ltrim "$str_prefix$cur"
+ COMPREPLY+=( $( compgen -P "$str_lprefix" -W \
+ '$str_var $(_ipset_get_services)' -- "$lcur" ) )
+ # add the protocols
+ COMPREPLY+=( $( compgen -P "$str_lprefix" -S ":0$str_suffix" -W \
+ '$(_ipset_get_protocols)' -- "$lcur" ) )
+ _ipset_colon_ltrim "$str_lprefix$lcur"
compopt -o nospace
fi
-elif [[ $cur = *,*,* && $str_type = hash:ip,port,@(ip|net) ]]; then
- str_prefix="${cur}" cur="${cur##*,}"
- str_prefix="${str_prefix%"$cur"}"
+elif [[ $lcur = *,*,* && $str_type = hash:@(ip,port,@(ip|net)|net,port,net) ]]; then
+ str_lprefix2="${lcur}" lcur2="${lcur##*,}"
+ str_lprefix2="${str_lprefix2%"$lcur2"}"
+ lcur="$lcur2"
# ip-list from file
- COMPREPLY=( $( compgen -W \
+ COMPREPLY+=( $( compgen -W \
'$(_ipset_get_iplist "$(_ipset_get_set_family "$str_setname")")' \
- -- "$cur" ) )
- if [[ $str_type = hash:ip,port,net ]]; then
- while read; do
- [[ $REPLY = $cur* ]] && COMPREPLY+=( "$str_prefix$REPLY" )
- done < <(_ipset_get_networks)
- _ipset_colon_ltrim "$str_prefix$cur"
- fi
- if ((got_bashcompl)); then
- _known_hosts_real -p "$str_prefix" -F "$_IPSET_SSH_CONFIGS" -- "$cur"
- else
- if [[ ${COMP_KNOWN_HOSTS_WITH_HOSTFILE-1} ]]; then
- COMPREPLY+=( $( compgen -P "$str_prefix" -A hostname "$cur" ) )
+ -- "$lcur2" ) )
+ if [[ $str_type = hash:@(ip|net),port,net && x$str_action != xtest ]]; then
+ # range spec
+ if [[ $lcur = @(""|+([[:word:]])) ]]; then # empty or [:word:]
+ str_suffix="-"
+ elif [[ $lcur = [[]*-*[]] ]]; then # host with hyphen
+ str_suffix="-"
+ elif [[ $lcur = [[]+([!]]) ]]; then # incomplete host with dash
+ str_suffix="-"
+ elif [[ $lcur = *-[[]+([!]]) ]]; then # incomplete range - host with dash
+ str_lprefix="${lcur%\-[*}-"
+ lcur="${lcur#"$str_lprefix"}"
+ elif [[ $lcur = \[*\]-* ]]; then # first part of hostname range
+ str_lprefix="${lcur%\]-*}]-" lcur="${lcur##*\]-}"
+ elif [[ $lcur != *-* ]]; then # no hypen
+ str_suffix="-"
+ else # ip-range
+ str_lprefix="${lcur%-*}-" lcur="${lcur##*-}"
fi
- _ipset_colon_ltrim "$str_prefix$cur"
+ # networks
+ COMPREPLY+=( $( compgen -W '$(_ipset_get_networks)' -- "$lcur" ) )
+ fi
+ _ipset_complete_hostnames "$lcur"
+ if [[ $str_lprefix ]]; then
+ COMPREPLY=( $( compgen -P "$str_lprefix" \
+ -W '${COMPREPLY[@]}' -- "$lcur" ) )
fi
+ if [[ $str_lprefix2 ]]; then
+ COMPREPLY=( $( compgen -P "$str_lprefix2" \
+ -W '${COMPREPLY[@]}' -- "$lcur2" ) )
+ fi
+ _ipset_colon_ltrim "$str_lprefix2$lcur2"
if ((${#COMPREPLY[@]} == 1)); then
- if [[ $str_type = hash:ip,port,net && ${COMPREPLY[*]} != */* ]]; then
+ if [[ $str_type = hash:@(ip|net),port,net && \
+ ${COMPREPLY[*]##*,} != */* ]]
+ then
compopt -o nospace
- COMPREPLY=( ${COMPREPLY[*]}/ )
fi
fi
fi
}
-_ipset_get_iplist() {
-# if a file with ip addresses is in env var, load em
-local str_ip rest
-if [[ $1 = v4 ]]; then
-str_regex='^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/([3][0-2]|[1-2]?[0-9]))?$'
-elif [[ $1 = v6 ]]; then
-str_regex='^([0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}|([0-9a-fA-F]{1,4}:){1}(:[0-9a-fA-F]{1,4}){1,6}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,6}(:[0-9a-fA-F]{1,4}){1})(/([1][0-2][0-9]|[1-9]?[0-9]))?$'
-else return 0
+_ipset_complete_netnet_spec() {
+# complete hash:net,net sets
+local lcur="$1"
+if [[ $lcur = *,* ]]; then
+ str_lprefix="$lcur" lcur="${lcur#*,}"
+ str_lprefix="${str_lprefix%,*},"
+ _ipset_complete_host_spec "$lcur"
+ compopt -o nospace
+ COMPREPLY+=( $( compgen -P "$str_lprefix" -W '${COMPREPLY[@]}' -- "$lcur" ) )
+ if ((${#COMPREPLY[@]} == 1 )); then
+ str_glob='@(*/*|\[*\]-*|+([![])-*)'
+ [[ ${COMPREPLY[0]#*,} = $str_glob ]] && compopt +o nospace
+ fi
+else
+ _ipset_complete_host_spec "$lcur"
+ compopt -o nospace
+ if ((${#COMPREPLY[@]} == 1 )); then
+ str_glob='@(*/*|\[*\]-\[*\]|+([![])-+([![])|\[*\]-+([![])|+([![])-\[*\])'
+ if [[ ${COMPREPLY[0]} = $str_glob ]]; then
+ COMPREPLY=( ${COMPREPLY[*]}, )
+ else
+ COMPREPLY=( ${COMPREPLY[*]}- ${COMPREPLY[*]}, )
+ fi
+ fi
fi
-[[ $_IPSET_IPLIST_FILE && -r $_IPSET_IPLIST_FILE ]] || return 0
-while read -r str_ip rest; do
- [[ $str_ip = *([[:blank:]])\#* ]] && continue
- str_ip="${str_ip//\#*/}"
- [[ $str_ip =~ $str_regex ]] && printf "%s\n" "$str_ip"
-done < "${_IPSET_IPLIST_FILE}"
}
# -----------------------------------------------------------------
@@ -537,14 +770,15 @@ done < "${_IPSET_IPLIST_FILE}"
# -----------------------------------------------------------------
_ipset_complete() {
-shopt -s extglob
local cur prev cword words ips_version
local str_action str_setname str_type str_filename
local str_glob str_regex str_prefix str_suffix
local str_tmp="" str_var=""
-local str_timeout="timeout" str_order="before after" str_counters=""
+local str_timeout="timeout" str_order="before after" str_forceadd=""
+local str_counters="" str_bp_counters="" str_comment="" str_markmask=""
local -i i=x=y=0
local -i got_bashcompl=got_action=action_index=order_index=set_has_timeout=0
+local -i got_bp_proto=0
local -i ignore_errors=use_file=names_only=headers_only=save_format=res_sort=0
local arr_sets=() arr_types=() arr_members=() arr_unknown_opts=()
local arr_dupe_cmd_opts=() arr_used_opts=() arr_tmp=()
@@ -618,6 +852,9 @@ neigbour-advertisement
redirect
)
+# at least bash 4 is required
+((${BASH_VERSINFO[0]} < 4)) && return 0
+
COMPREPLY=()
# ipset version check 6.x upwards (to v?) is supported
@@ -628,10 +865,30 @@ read -a ips_version <<< ${ips_version//./ }
[[ ${ips_version[0]} = +([[:digit:]]) ]] || return 1
((ips_version[0] < 6)) && return 1
-# ipset -gt v6.17 has counters flag
-if ((ips_version[0] == 6 && ips_version[1] >= 17)) || ((ips_version[0] > 6))
-then
+# ipset -ge v6.19 has counters flag
+# ipset -ge v6.20 has comment flag
+# ipset -ge v6.2? has hash:ip,mark markmask flag
+if ((ips_version[0] > 6)); then
str_counters="counters"
+ str_bp_counters="bytes packets"
+ str_comment="comment"
+ str_markmask="markmask"
+ got_bp_proto=1
+else
+ if ((ips_version[0] == 6 && ips_version[1] >= 19)); then
+ str_counters="counters"
+ str_bp_counters="bytes packets"
+ fi
+ if ((ips_version[0] == 6 && ips_version[1] >= 20)); then
+ str_comment="comment"
+ got_bp_proto=1
+ fi
+ if ((ips_version[0] == 6 && ips_version[1] >= 21)); then
+ str_comment="comment"
+ str_markmask="markmask"
+ str_forceadd="forceadd"
+ got_bp_proto=1
+ fi
fi
# expecting _get_comp_words_by_ref() to exist from bash_completion
@@ -659,6 +916,14 @@ __ltrim_colon_completions() {
done
fi
}
+
+# construct own known_hosts function from origin
+# just remove the __ltrim_colon_completions call
+# to avoid unwanted ltrim if we need to work with the list of hosts
+# ugly hack - gimme better ;p
+if ! declare -F _ipset_known_hosts &>/dev/null; then
+eval '_ipset_known_hosts() { '$(declare -f _known_hosts_real | grep -v __ltrim_colon_completions | grep -Ev "^_known_hosts_real.*$" | grep -Ev "^(\{|\})")'; }'
+fi
fi
#_DEBUG_NF_COMPLETION=Y
@@ -739,7 +1004,7 @@ case "${words[i]}" in
order_index=$i str_order=""
fi
;;
- timeout|range|maxelem|family|hashsize|size|netmask|nomatch|counters)
+ timeout|range|maxelem|family|hashsize|size|netmask|nomatch|counters|bytes|packets|comment|markmask|forceadd)
if ((got_action && i > action_index+2)); then
str_tmp="$COMP_LINE"
[[ $str_setname = ${words[i]} ]] && str_tmp="${str_tmp/${words[i]}/}"
@@ -786,6 +1051,18 @@ fi
# return 0
# ;;
#esac
+# catch variables and command substitution
+if [[ $cur == \$\(* ]]; then # command substitution
+ COMPREPLY=( $(compgen -c -P '$(' ${cur#??}) )
+ return 0
+elif [[ $cur == \$\{* ]]; then # variables with a leading `${'
+ COMPREPLY=( $(compgen -v -P '${' -S '}' ${cur#??}) )
+ return 0
+elif [[ $cur == \$* ]]; then # variables with a leading `$'
+ COMPREPLY=( $(compgen -v -P '$' ${cur#?} ) )
+ return 0
+fi
+
case "$prev" in # depend on previous option
-o|-output)
# make sure it's not a filename named -o or -output
@@ -879,32 +1156,23 @@ elif ((cword == action_index+2)) && [[ $str_setname = $prev ]]; then
add)
str_type=$(_ipset_get_set_type "$str_setname")
case "$str_type" in
- hash:ip|bitmap:ip|hash:net)
- # ip-list from file
- COMPREPLY=( $( compgen -W \
- '$(_ipset_get_iplist "$(_ipset_get_set_family "$str_setname")")' \
- -- "$cur" ) )
- if ((got_bashcompl)); then
- _known_hosts_real -F "$_IPSET_SSH_CONFIGS" -- "$cur"
- else
- if [[ ${COMP_KNOWN_HOSTS_WITH_HOSTFILE-1} ]]; then
- COMPREPLY=( $( compgen -A hostname "$cur" ) )
- fi
- _ipset_colon_ltrim "$cur"
- fi
- if [[ $str_type = bitmap:ip,mac ]]; then
- compopt -o nospace
- ((${#COMPREPLY[@]} == 1)) && COMPREPLY=( ${COMPREPLY[*]}, )
- elif [[ $str_type = hash:net ]]; then
- COMPREPLY+=( $( compgen -W '$(_ipset_get_networks)' -- "$cur" ) )
- if ((${#COMPREPLY[@]} == 1)); then
- if [[ ${COMPREPLY[*]} != */* ]]; then
- compopt -o nospace
- COMPREPLY=( ${COMPREPLY[*]}/ )
- fi
- fi
- _ipset_colon_ltrim "$cur"
- fi
+ bitmap:ip)
+ _ipset_complete_host_spec "$cur" --v4
+ _ipset_colon_ltrim "$cur"
+ ;;
+ hash:ip|hash:net|hash:ip,mark)
+ _ipset_complete_host_spec "$cur"
+ _ipset_colon_ltrim "$cur"
+ ;;
+ hash:net,iface)
+ _ipset_complete_iface_spec "$cur"
+ ;;
+ hash:@(ip,port|ip,port,ip|ip,port,net|net,port|net,port,net))
+ _ipset_complete_hostport_spec "$cur"
+ ;;
+ hash:net,net)
+ _ipset_complete_netnet_spec "$cur"
+ _ipset_colon_ltrim "$cur"
;;
bitmap:ip,mac)
if [[ $cur = *,* ]]; then
@@ -954,38 +1222,17 @@ elif ((cword == action_index+2)) && [[ $str_setname = $prev ]]; then
_ipset_colon_ltrim "$str_prefix$cur"
fi
else
- # ip-list from file
- COMPREPLY=( $( compgen -W \
- '$(_ipset_get_iplist "$(_ipset_get_set_family "$str_setname")")' \
- -- "$cur" ) )
- if ((got_bashcompl)); then
- _known_hosts_real -F "$_IPSET_SSH_CONFIGS" -- "$cur"
- else
- if [[ ${COMP_KNOWN_HOSTS_WITH_HOSTFILE-1} ]]; then
- COMPREPLY=( $( compgen -A hostname "$cur" ) )
- fi
- _ipset_colon_ltrim "$cur"
- fi
compopt -o nospace
- ((${#COMPREPLY[@]} == 1)) && COMPREPLY=( ${COMPREPLY[*]}, )
+ _ipset_complete_host_spec "$cur" --v4 --no-range
+ _ipset_colon_ltrim "$cur"
+ if ((${#COMPREPLY[@]} == 1)); then
+ COMPREPLY=( ${COMPREPLY[*]}, )
+ fi
fi
;;
bitmap:port)
# complete port [range]
- str_tmp="$cur" str_var=""
- str_glob='[^\[]*-*'
- if [[ $cur = \[*-*\]-* ]]; then str_var="${cur#\[}"
- str_var="${str_var%%\]*}"
- cur="${cur#*\]-}"
- str_tmp=${str_tmp%"$cur"}
- elif [[ $cur = $str_glob ]]; then str_var="${cur%%-*}"
- cur="${cur#*-}"
- str_tmp=${str_tmp%"$cur"}
- else str_tmp=""
- compopt -o nospace
- fi
- COMPREPLY=( $( compgen -P "$str_tmp" \
- -W '$(_ipset_get_services -o $str_var)' -- "$cur" ) )
+ _ipset_complete_portrange "$cur"
;;
# show sets if the set to add is of type list:set
list:*) arr_tmp=() arr_sets=( $(ipset list -n) )
@@ -1000,89 +1247,220 @@ elif ((cword == action_index+2)) && [[ $str_setname = $prev ]]; then
COMPREPLY=( $( compgen -W '${arr_tmp[@]}' -- "$cur" ) )
_ipset_colon_ltrim "$cur"
;;
- hash:@(ip,port|ip,port,ip|ip,port,net|net,port))
- _ipset_complete_hostport_spec
- ;;
- hash:net,iface)
- _ipset_complete_iface_spec
- ;;
esac
;;
- del|test)
+ del)
str_type=$(_ipset_get_set_type "$str_setname")
- if [[ $str_action = del && $str_type = bitmap:@(ip|port) ]]; then
- # complete members
+ if [[ $str_type = bitmap:ip ]]; then
str_prefix=""
- _ipset_get_members --names-only "$str_setname"
- if [[ $cur = *-* ]]; then
- if [[ $str_type = bitmap:port ]]; then
- for i in ${!arr_members[@]}; do
- ((${arr_members[i]} <= ${cur%%-*})) && \
- unset arr_members[i] || break
- done
+ if [[ ${_IPSET_COMPL_DEL_MODE:=both} = members ]]; then
+ _ipset_complete_elements "$cur"
+ elif [[ ${_IPSET_COMPL_DEL_MODE} = spec ]]; then
+ _ipset_complete_host_spec "$cur" --v4
+ _ipset_colon_ltrim "$cur"
+ else
+ _ipset_complete_host_spec "$cur" --v4
+ _ipset_complete_elements "$cur"
+ _ipset_colon_ltrim "$cur"
+ fi
+ elif [[ $str_type = bitmap:port ]]; then
+ str_prefix=""
+ if [[ ${_IPSET_COMPL_DEL_MODE:=both} = members ]]; then
+ _ipset_complete_elements "$cur"
+ elif [[ ${_IPSET_COMPL_DEL_MODE} = spec ]]; then
+ _ipset_complete_portrange "$cur"
+ else
+ _ipset_complete_portrange "$cur"
+ _ipset_get_members --names-only "$str_setname"
+ str_glob='?(tcp:|udp:)@([![]*-*|\[?*\]-*)'
+ if [[ $cur = $str_glob ]]; then
+ str_var="${cur#?(tcp:|udp:)}" # offset
+ str_tmp="${cur%"$str_var"}" # proto
+ # identify service number by name, to find the offset
+ if [[ $str_var != +([[:digit:]]) ]]; then
+ if [[ $str_var = \[+([![])\]-* ]]; then
+ str_var="${str_var%%\]*}"
+ str_var="${str_var#\[}"
+ elif [[ $str_var = *-* ]]; then
+ str_var="${str_var%%-*}"
+ fi
+ str_var=$(_ipset_get_svnum -p "${str_tmp:-all}" -o "$str_var")
+ fi
+ if [[ $str_var = +([[:digit:]]) ]]; then
+ for i in ${!arr_members[@]}; do
+ ((${arr_members[i]} <= $str_var)) && \
+ unset arr_members[i] || break
+ done
+ fi
+ str_prefix="${cur%-*}-" cur="${cur##*-}"
fi
- str_prefix="${cur%-*}-" cur="${cur#*-}"
+ COMPREPLY+=( $( compgen -P "$str_prefix" -W '${arr_members[@]}' -- "$cur" ) )
+ fi
+ elif [[ $str_type = bitmap:ip,mac ]]; then
+ str_prefix=""
+ if [[ ${_IPSET_COMPL_DEL_MODE:=both} = members ]]; then
+ _ipset_complete_elements "$cur" --no-range
+ _ipset_colon_ltrim "$cur"
+ elif [[ ${_IPSET_COMPL_DEL_MODE} = spec ]]; then
+ _ipset_complete_host_spec "$cur" --v4 --no-range
+ _ipset_colon_ltrim "$cur"
else
- compopt -o nospace
+ _ipset_complete_host_spec "$cur" --v4 --no-range
+ _ipset_complete_elements "$cur" --no-range
+ _ipset_colon_ltrim "$cur"
fi
- COMPREPLY=( $( compgen -P "$str_prefix" -W '${arr_members[@]}' -- "$cur" ) )
- elif [[ $str_action = del && $str_type = hash:@(ip,port|ip,port,ip|ip,port,net|net,port) ]]
+ elif [[ $str_type = hash:@(ip?(,mark)|net) ]]; then
+ str_prefix=""
+ if [[ ${_IPSET_COMPL_DEL_MODE:=both} = members ]]; then
+ _ipset_complete_elements "$cur"
+ _ipset_colon_ltrim "$cur"
+ elif [[ ${_IPSET_COMPL_DEL_MODE} = spec ]]; then
+ _ipset_complete_host_spec "$cur"
+ _ipset_colon_ltrim "$cur"
+ else
+ _ipset_complete_host_spec "$cur"
+ _ipset_complete_elements "$cur"
+ _ipset_colon_ltrim "$cur"
+ fi
+ elif [[ $str_type = hash:net,net ]]; then
+ if [[ ${_IPSET_COMPL_DEL_MODE:=both} = members ]]; then
+ _ipset_complete_elements "$cur"
+ _ipset_colon_ltrim "$cur"
+ elif [[ ${_IPSET_COMPL_DEL_MODE} = spec ]]; then
+ _ipset_complete_netnet_spec "$cur"
+ _ipset_colon_ltrim "$cur"
+ else
+ _ipset_complete_netnet_spec "$cur"
+ _ipset_complete_elements "$cur"
+ _ipset_colon_ltrim "$cur"
+ fi
+ elif [[ $str_type = hash:@(ip,port|ip,port,ip|ip,port,net|net,port|net,port,net) ]]
then
if [[ ${_IPSET_COMPL_DEL_MODE:=both} = members ]]; then
- _ipset_get_members --names-only "$str_setname"
- COMPREPLY=( $( compgen -W '${arr_members[@]}' -- "$cur" ) )
+ _ipset_complete_elements "$cur" --no-range
_ipset_colon_ltrim "$cur"
elif [[ ${_IPSET_COMPL_DEL_MODE} = spec ]]; then
- _ipset_complete_hostport_spec
+ _ipset_complete_hostport_spec "$cur"
else
- _ipset_get_members --names-only "$str_setname"
- _ipset_complete_hostport_spec
- COMPREPLY+=( $( compgen -W '${arr_members[@]}' -- "$cur" ) )
+ _ipset_complete_elements "$cur" --no-range
+ _ipset_complete_hostport_spec "$cur"
+ _ipset_colon_ltrim "$cur"
+ fi
+ elif [[ $str_type = hash:net,iface ]]; then
+ if [[ ${_IPSET_COMPL_TEST_MODE:=both} = members ]]; then
+ _ipset_complete_elements "$cur" --no-range
+ _ipset_colon_ltrim "$cur"
+ elif [[ ${_IPSET_COMPL_TEST_MODE} = spec ]]; then
+ _ipset_complete_iface_spec "$cur"
+ else
+ _ipset_complete_elements "$cur" --no-range
+ _ipset_complete_iface_spec "$cur"
+ _ipset_colon_ltrim "$cur"
+ fi
+ else
+ _ipset_complete_elements "$cur" --no-range
+ _ipset_colon_ltrim "$cur"
+ fi
+ ;;
+ test)
+ str_type=$(_ipset_get_set_type "$str_setname")
+ if [[ $str_type = bitmap:ip ]]; then
+ if [[ ${_IPSET_COMPL_TEST_MODE:=both} = members ]]; then
+ _ipset_complete_elements "$cur" --no-range
+ elif [[ ${_IPSET_COMPL_TEST_MODE} = spec ]]; then
+ _ipset_complete_host_spec "$cur" --no-range --v4
+ else
+ _ipset_complete_elements "$cur" --no-range
+ if ! _ipset_complete_host_spec "$cur" --no-range --v4; then
+ COMPREPLY=()
+ fi
+ fi
+ elif [[ $str_type = hash:ip?(,mark) ]]; then
+ if [[ ${_IPSET_COMPL_TEST_MODE:=both} = members ]]; then
+ _ipset_complete_elements "$cur" --no-range
+ _ipset_colon_ltrim "$cur"
+ elif [[ ${_IPSET_COMPL_TEST_MODE} = spec ]]; then
+ _ipset_complete_host_spec "$cur" --no-range
+ _ipset_colon_ltrim "$cur"
+ else
+ _ipset_complete_elements "$cur" --no-range
+ if ! _ipset_complete_host_spec "$cur" --no-range; then
+ COMPREPLY=()
+ fi
+ _ipset_colon_ltrim "$cur"
+ fi
+ elif [[ $str_type = hash:net ]]; then
+ if [[ ${_IPSET_COMPL_TEST_MODE:=both} = members ]]; then
+ _ipset_complete_elements "$cur" --no-range
+ _ipset_colon_ltrim "$cur"
+ elif [[ ${_IPSET_COMPL_TEST_MODE} = spec ]]; then
+ _ipset_complete_host_spec "$cur"
+ _ipset_colon_ltrim "$cur"
+ else
+ _ipset_complete_elements "$cur" --no-range
+ if ! _ipset_complete_host_spec "$cur"; then
+ COMPREPLY=()
+ fi
_ipset_colon_ltrim "$cur"
fi
- elif [[ $str_action = test && $str_type = hash:@(ip,port|ip,port,ip|ip,port,net|net,port) ]]
+ elif [[ $str_type = hash:@(ip,port|ip,port,ip|ip,port,net|net,port|net,port,net) ]]
then
if [[ ${_IPSET_COMPL_TEST_MODE:=both} = members ]]; then
- _ipset_get_members --names-only "$str_setname"
- COMPREPLY=( $( compgen -W '${arr_members[@]}' -- "$cur" ) )
+ _ipset_complete_elements "$cur" --no-range
_ipset_colon_ltrim "$cur"
elif [[ ${_IPSET_COMPL_TEST_MODE} = spec ]]; then
- _ipset_complete_hostport_spec
+ _ipset_complete_hostport_spec "$cur"
else
- _ipset_get_members --names-only "$str_setname"
- _ipset_complete_hostport_spec
- COMPREPLY+=( $( compgen -W '${arr_members[@]}' -- "$cur" ) )
+ _ipset_complete_elements "$cur" --no-range
+ _ipset_complete_hostport_spec "$cur"
_ipset_colon_ltrim "$cur"
fi
- elif [[ $str_action = del && $str_type = hash:net,iface ]]; then
- if [[ ${_IPSET_COMPL_DEL_MODE:=both} = members ]]; then
- _ipset_get_members --names-only "$str_setname"
- COMPREPLY=( $( compgen -W '${arr_members[@]}' -- "$cur" ) )
+ elif [[ $str_type = hash:net,iface ]]; then
+ if [[ ${_IPSET_COMPL_TEST_MODE:=both} = members ]]; then
+ _ipset_complete_elements "$cur" --no-range
_ipset_colon_ltrim "$cur"
- elif [[ ${_IPSET_COMPL_DEL_MODE} = spec ]]; then
- _ipset_complete_iface_spec
+ elif [[ ${_IPSET_COMPL_TEST_MODE} = spec ]]; then
+ _ipset_complete_iface_spec "$cur"
else
- _ipset_get_members --names-only "$str_setname"
- _ipset_complete_iface_spec
- COMPREPLY+=( $( compgen -W '${arr_members[@]}' -- "$cur" ) )
+ _ipset_complete_elements "$cur" --no-range
+ _ipset_complete_iface_spec "$cur"
_ipset_colon_ltrim "$cur"
fi
- elif [[ $str_action = test && $str_type = hash:net,iface ]]; then
+ elif [[ $str_type = bitmap:port ]]; then
+ str_prefix="" str_tmp="$cur"
+ if [[ $cur = @(tcp|udp):* ]]; then
+ ((got_bp_proto)) || return 0 # supported since ipset v6.20
+ str_prefix="${cur%:*}"
+ str_tmp="${str_tmp#${str_prefix}:}"
+ fi
if [[ ${_IPSET_COMPL_TEST_MODE:=both} = members ]]; then
- _ipset_get_members --names-only "$str_setname"
- COMPREPLY=( $( compgen -W '${arr_members[@]}' -- "$cur" ) )
+ _ipset_complete_elements "$cur" --no-range
_ipset_colon_ltrim "$cur"
elif [[ ${_IPSET_COMPL_TEST_MODE} = spec ]]; then
- _ipset_complete_iface_spec
+ if ((got_bp_proto)); then # supported since ipset v6.20
+ COMPREPLY=( $(compgen -W \
+ 'tcp: udp: $(_ipset_get_services -p "$str_prefix")' -- "$str_tmp" ) )
+ [[ ${COMPREPLY[*]} = @(tcp|udp): ]] && compopt -o nospace
+ _ipset_colon_ltrim "$cur"
+ else # only tcp services prior to ipset v6.20
+ COMPREPLY=( $(compgen \
+ -W '$(_ipset_get_services -p tcp)' -- "$cur" ) )
+ fi
else
- _ipset_get_members --names-only "$str_setname"
- _ipset_complete_iface_spec
- COMPREPLY+=( $( compgen -W '${arr_members[@]}' -- "$cur" ) )
+ if ((got_bp_proto)); then # supported since ipset v6.20
+ COMPREPLY=( $(compgen -W \
+ 'tcp: udp: $(_ipset_get_services -p "$str_prefix")' -- "$str_tmp" ) )
+ [[ ${COMPREPLY[*]} = @(tcp|udp): ]] && compopt -o nospace
+ _ipset_colon_ltrim "$cur"
+ else # only tcp services prior to ipset v6.20
+ COMPREPLY=( $(compgen \
+ -W '$(_ipset_get_services -p tcp)' -- "$cur" ) )
+ fi
+ _ipset_complete_elements "$cur" --no-range
_ipset_colon_ltrim "$cur"
fi
else
- _ipset_get_members --names-only "$str_setname"
- COMPREPLY=( $( compgen -W '${arr_members[@]}' -- "$cur" ) )
+ _ipset_complete_elements "$cur" --no-range
_ipset_colon_ltrim "$cur"
fi
;;
@@ -1091,49 +1469,60 @@ elif ((cword == action_index+3)) && [[ $cur != -* ]]; then
case "$str_action" in
add)
str_type=$(_ipset_get_set_type "$str_setname")
- if _ipset_set_has_timout "$str_setname"; then
+ if _ipset_set_has_option timeout "$str_setname"; then
str_timeout=timeout
else
str_timeout=""
fi
+ if ! _ipset_set_has_option counters "$str_setname"; then
+ str_bp_counters=""
+ fi
+ if ! _ipset_set_has_option comment "$str_setname"; then
+ str_comment=""
+ fi
case "$str_type" in
hash:*net*)
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts $str_timeout $str_counters nomatch)' \
+ '$(_ipset_dedupe_cmd_opts $str_timeout $str_bp_counters $str_comment nomatch)' \
-- "$cur" ) )
;;
hash:*!(net)*|bitmap:*)
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts $str_timeout $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts $str_timeout $str_bp_counters $str_comment)' \
-- "$cur" ) )
;;
list:*)
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts $str_order $str_timeout $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts $str_order $str_timeout $str_bp_counters $str_comment)' \
-- "$cur" ) )
;;
esac
;;
create|n)
case "$prev" in
+ hash:ip,mark)
+ COMPREPLY=( $( compgen -W \
+ '$(_ipset_dedupe_cmd_opts family hashsize timeout maxelem $str_counters $str_markmask $str_comment $str_forceadd)' \
+ -- "$cur" ) )
+ ;;
hash:*)
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts family hashsize timeout maxelem $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts family hashsize timeout maxelem $str_counters $str_comment $str_forceadd)' \
-- "$cur" ) )
;;
bitmap:ip)
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts range netmask timeout $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts range netmask timeout $str_counters $str_comment)' \
-- "$cur" ) )
;;
bitmap:!(ip)?*)
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts range timeout $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts range timeout $str_counters $str_comment)' \
-- "$cur" ) )
;;
list:*)
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts size timeout $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts size timeout $str_counters $str_comment)' \
-- "$cur" ) )
;;
esac
@@ -1142,126 +1531,165 @@ elif ((cword == action_index+3)) && [[ $cur != -* ]]; then
str_type=$(_ipset_get_set_type "$str_setname")
if [[ $str_type = list:* ]]; then
COMPREPLY=( $( compgen -W '$str_order' -- "$cur" ) )
+ elif [[ $str_action = test && $str_type = hash:*net* ]]; then
+ COMPREPLY=( $( compgen -W 'nomatch' -- "$cur" ) )
fi
;;
esac
elif ((cword == action_index+3)) && [[ $cur = -* ]]; then
_ipset_get_options
-elif ((cword >= action_index+4)); then # add options following
- if [[ $cur = -* && \
- $prev != @(timeout|hashsize|size|family|maxelem|range|netmask|before|after|counters) ]]
- then _ipset_get_options
- return 0
+elif ((cword >= action_index+4)) && [[ $cur = -* ]]; then # add all following hyphen options
+ if [[ $prev != @(timeout|hashsize|size|family|maxelem|range|netmask|before|after|bytes|packets|comment|markmask) ]]
+ then
+ _ipset_get_options
fi
+elif ((cword >= action_index+4)); then # add all following non-hyphen options
case "$str_action" in
add)
str_type=$(_ipset_get_set_type "$str_setname")
- if _ipset_set_has_timout "$str_setname"; then
+ if _ipset_set_has_option timeout "$str_setname"; then
str_timeout=timeout
else
str_timeout=""
fi
+ if ! _ipset_set_has_option counters "$str_setname"; then
+ str_bp_counters=""
+ fi
+ if ! _ipset_set_has_option comment "$str_setname"; then
+ str_comment=""
+ fi
# validate option argument values
- for ((x=$action_index+3; x < ${#words[@]}; x++)); do
- [[ ${words[x]} = timeout && \
- ${words[x+1]} != @(+([[:digit:]])|0x+([[:xdigit:]])) ]] && return 0
- done
+ if [[ ${_IPSET_VALIDATE_INPUT-1} ]]; then
+ for ((x=$action_index+3; x < ${#words[@]}; x++)); do
+ [[ ${words[x]} = @(timeout|bytes|packets) && \
+ ${words[x+1]} != @(+([[:digit:]])|0[xX]+([[:xdigit:]])) ]] && return 0
+ done
+ fi
case "$str_type" in
hash:*net*)
- if [[ $prev != timeout ]]; then
+ if [[ $prev != @(timeout|bytes|packets|comment) ]]; then
+ COMPREPLY=( $( compgen -W \
+ '$(_ipset_dedupe_cmd_opts $str_timeout $str_bp_counters $str_comment nomatch)' \
+ -- "$cur" ) )
+ fi
+ ;;
+ hash:*!(net)*|bitmap:*)
+ if [[ $prev != @(timeout|bytes|packets|comment) ]]; then
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts $str_timeout $str_counters nomatch)' \
+ '$(_ipset_dedupe_cmd_opts $str_timeout $str_bp_counters $str_comment)' \
-- "$cur" ) )
fi
;;
list:*)
if [[ $prev = @(before|after) ]] && ((cword-1 == order_index)); then
- _ipset_get_members --names-only "$str_setname"
- COMPREPLY=( $( compgen -W '${arr_members[@]}' -- "$cur" ) )
+ _ipset_complete_elements "$cur"
_ipset_colon_ltrim "$cur"
- elif [[ $prev != timeout ]]; then
+ elif [[ $prev != @(timeout|bytes|packets) ]]; then
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts $str_order $str_timeout $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts $str_order $str_timeout $str_bp_counters $str_comment)' \
-- "$cur" ) )
fi
;;
esac
;;
create|n)
- for ((x=$action_index+3; x < ${#words[@]}; x++)); do
- if [[ ${words[x+1]} && ${words[x+1]} != $cur ]]; then
- case "${words[x]}" in # validate option argument values
- @(hashsize|timeout|size|maxelem))
- [[ ${words[x+1]} != @(+([[:digit:]])|0x+([[:xdigit:]])) ]] && return 0
- ;;
- family)
- [[ ${words[x+1]} != inet?(6) ]] && return 0
- ;;
- range)
- case "$str_type" in
- bitmap:port)
- [[ ${words[x+1]} != *-* ]] && return 0
- ;;
- *)
- [[ ${words[x+1]} != @(*-*|*/+([[:digit:]])) ]] && return 0
- ;;
- esac
- ;;
- esac
- fi
- done
+ # validate option argument values
+ if [[ ${_IPSET_VALIDATE_INPUT-1} ]]; then
+ for ((x=$action_index+3; x < ${#words[@]}; x++)); do
+ if [[ ${words[x+1]} && ${words[x+1]} != $cur ]]; then
+ case "${words[x]}" in
+ @(hashsize|timeout|size|maxelem|markmask))
+ [[ ${words[x+1]} != @(+([[:digit:]])|0[xX]+([[:xdigit:]])) ]] && return 0
+ ;;
+ family)
+ [[ ${words[x+1]} != inet?(6) ]] && return 0
+ ;;
+ range)
+ case "$str_type" in
+ bitmap:port)
+ [[ ${words[x+1]} != *-* ]] && return 0
+ ;;
+ *)
+ [[ ${words[x+1]} != @(*-*|*/+([[:digit:]])) ]] && return 0
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ fi
case "${words[action_index+2]}" in # must be the set type
+ hash:ip,mark)
+ if [[ $prev = family ]]; then
+ COMPREPLY=( $( compgen -W \
+ '$(_ipset_dedupe_cmd_opts inet inet6)' \
+ -- "$cur" ) )
+ elif [[ $prev != @(hashsize|timeout|maxelem|markmask) ]]; then
+ COMPREPLY=( $( compgen -W \
+ '$(_ipset_dedupe_cmd_opts family hashsize timeout maxelem $str_counters $str_markmask $str_comment $str_forceadd)' \
+ -- "$cur" ) )
+ fi
+ ;;
hash:*)
if [[ $prev = family ]]; then
COMPREPLY=( $( compgen -W \
'$(_ipset_dedupe_cmd_opts inet inet6)' \
-- "$cur" ) )
- elif [[ $prev != @(family|hashsize|timeout|maxelem) ]]; then
+ elif [[ $prev != @(hashsize|timeout|maxelem) ]]; then
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts family hashsize timeout maxelem $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts family hashsize timeout maxelem $str_counters $str_comment $str_forceadd)' \
-- "$cur" ) )
fi
;;
bitmap:ip)
if [[ $prev != @(range|netmask|timeout) ]]; then
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts range netmask timeout $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts range netmask timeout $str_counters $str_comment)' \
-- "$cur" ) )
fi
;;
bitmap:!(ip)?*)
if [[ $prev != @(range|timeout) ]]; then
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts range timeout $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts range timeout $str_counters $str_comment)' \
-- "$cur" ) )
fi
;;
list:*)
if [[ $prev != @(size|timeout) ]]; then
COMPREPLY=( $( compgen -W \
- '$(_ipset_dedupe_cmd_opts size timeout $str_counters)' \
+ '$(_ipset_dedupe_cmd_opts size timeout $str_counters $str_comment)' \
-- "$cur" ) )
fi
;;
esac
if [[ ${words[action_index+2]} = bitmap:port && $prev = range ]]; then
# complete port ranges
- str_prefix="$cur" str_suffix="" str_var="" str_glob='[^\[]*-*'
- if [[ $cur = \[*-*\]-* ]]; then
- str_var="${cur#\[}"
- str_var="${str_var%%\]*}"
- cur="${cur#*\]-}"
- str_prefix=${str_prefix%"$cur"}
- elif [[ $cur = $str_glob ]]; then
- str_var="${cur%%-*}"
- cur="${cur#*-}"
- str_prefix=${str_prefix%"$cur"}
- else
- str_prefix="" str_suffix=-
- compopt -o nospace
+ _ipset_complete_portrange "$cur"
+ elif [[ ${words[action_index+2]} = bitmap:* && $prev = range ]]; then
+ str_prefix=""
+ if [[ $cur = @(""|+([[:word:]])) ]]; then # empty or [:word:]
+ :
+ elif [[ $cur = [\[]*-*[\]] ]]; then # host with hyphen
+ :
+ elif [[ $cur = [[]*([!]]) ]]; then # incomplete host with dash
+ :
+ elif [[ $cur = *-[[]+([!]]) ]]; then # incomplete range - host with dash
+ str_prefix="${cur%\-[*}-"
+ elif [[ $cur = \[*\]-* ]]; then # first part of hostname range
+ str_prefix="${cur%\]-*}]-"
+ elif [[ $cur != *-* ]]; then # no hypen
+ :
+ else # ip-range
+ str_prefix="${cur%-*}-"
+ fi
+ _ipset_complete_host_spec "$cur" --v4
+ if ((${#COMPREPLY[@]} == 1)); then
+ if [[ -z $str_prefix && ${COMPREPLY[*]} != */* ]]; then
+ compopt -o nospace
+ COMPREPLY=( $( compgen -W '${COMPREPLY[*]}/ ${COMPREPLY[*]}-' -- "$cur" ) )
+ fi
fi
- COMPREPLY=( $( compgen -P "$str_prefix" -S "$str_suffix" \
- -W '$(_ipset_get_services -o $str_var)' -- "$cur" ) )
fi
;;
del|test)
@@ -1327,9 +1755,6 @@ else
fi
fi
fi
-if ! ((${#COMPREPLY[@]})); then # last exit brooklyn
- [[ $cur ]] && _ipset_bash_default_compl "$cur"
-fi
if [[ $_DEBUG_NF_COMPLETION ]]; then
printf "COMPREPLY:\n"
printf "<%s>\n" "${COMPREPLY[@]}"
diff --git a/utils/ipset_list/README.md b/utils/ipset_list/README.md
index 4806252..579d3bf 100644
--- a/utils/ipset_list/README.md
+++ b/utils/ipset_list/README.md
@@ -1,7 +1,8 @@
ipset_list
==========
-ipset set listing wrapper script
+ipset set listing wrapper script written for the bash shell.
+It allows you to match and display sets, headers and elements in various ways.
Features:
@@ -12,10 +13,13 @@ Features:
- Choose a delimiter character for separating members.
- Show only sets containing a specific (glob matching) header.
- Arithmetic comparison on headers with an integer value.
+- Arithmetic comparison on flags of the headers 'Header' field.
+- Arithmetic comparison on member options with an integer value.
- Match members using a globbing or regex pattern.
- Suppress listing of (glob matching) sets.
- Suppress listing of (glob matching) headers.
- Suppress listing of members matching a glob or regex pattern.
+- Suppress listing of members options.
- Calculate the total size in memory of all matching sets.
- Calculate the amount of matching, excluded and traversed sets.
- Colorize the output.
@@ -62,5 +66,10 @@ Examples:
- `ipset_list -c -t -Cs -Ts -Xh "@(Size*|Re*|Header):*" -Ht "!(bitmap:*)"` - find all sets not of any bitmap type, count their members sum, display only the 'Type' header, count amount of matching and traversed sets.
- `ipset_list -Co -c -Ts -Tm` - show all set names, count their members, count total amount of sets, show total memory usage of all sets, colorize the output
- `ipset_list -m -r -To 0` - show members of all sets, try to resolve hosts, set the timeout to 0 (effectivly disabling it).
+- `ipset_list -m -Xo setA` - show members of setA, but suppress displaying of the elements options.
+- `ipset_list -m -Oi packets:0` - show members of all sets which have a packet count of 0.
+- `ipset_list -m -Oi "packets:>100" -Oi "bytes:>1024"` - show members of all sets which have a packet count greater than 100 and a byte count greater than 1024.
+- `ipset_list -n -Ca "foo*"` - show only set names matching the glob "foo*" and enable all counters.
+- `ipset_list -Hi "markmask:>=0x0000beef" -Hi timeout:\!10000` - show only sets with the header 'Header' fields containing a markmask greater or equal to 0x0000beef and a timeout which is not 10000.
diff --git a/utils/ipset_list/ipset_list b/utils/ipset_list/ipset_list
index 18743a5..6db57ae 100755
--- a/utils/ipset_list/ipset_list
+++ b/utils/ipset_list/ipset_list
@@ -7,7 +7,7 @@
# https://sourceforge.net/projects/ipset-list/
# -----------------------------------------------------------------
-# Copyright (C) 2013 AllKind (AllKind@fastest.cc)
+# 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
@@ -23,8 +23,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------
+# Compatible with ipset version 6+
# Tested with ipset versions:
-# 6.16.1
+# 6.16.1, 6.20.1
# -----------------------------------------------------------------
# -----------------------------------------------------------------
@@ -34,6 +35,8 @@
# - Choose a delimiter character for separating members.
# - Show only sets containing a specific (glob matching) header.
# - Arithmetic comparison on headers with an integer value.
+# - Arithmetic comparison on flags of the headers 'Header' field.
+# - Arithmetic comparison on member options with an integer value.
# - Match members using a globbing or regex pattern.
# - Suppress listing of (glob matching) sets.
# - Suppress listing of (glob matching) headers.
@@ -42,6 +45,7 @@
# - Calculate the amount of matching, excluded and traversed sets.
# - Colorize the output.
# - Operate on a single, selected, or all sets.
+# - Programmable completion is included to make usage easier and faster.
# -----------------------------------------------------------------
# -----------------------------------------------------------------
@@ -58,7 +62,7 @@
# $0 -m -r -s setA - show members of setA resolved and sorted
# $0 -Ts - show all set names and total count of sets.
# $0 -Tm - calculate total size in memory of all sets.
-# $0 -Mc 0 - show sets with zero members
+# $0 -Mc 0 - show sets with zero members
# $0 -Fi References:0 - show all sets with 0 references
# $0 -Hr 0 - shortcut for `-Fi References:0'
# $0 -Xs setA -Xs setB - show all set names, but exclude setA and setB.
@@ -91,7 +95,7 @@
# $0 -c -m -Xg "210.*" setA - show members of setA, but suppress listing of entries
#+ matching the glob pattern "210.*", show count of excluded and total members.
#
-# $0 -t -Tm -Xh "@(Type|Re*|Header):*"
+# $0 -t -Tm -Xh "@(Type|Re*|Header):*"
#+ show all sets headers, but suppress all but name and memsize entry,
#+ calculate the total memory size of all sets.
#
@@ -114,14 +118,39 @@
#
# $0 -m -r -To 0 - show members of all sets, try to resolve hosts,
# set the timeout to 0 (effectivly disabling it).
+#
+# $0 -m -Xo setA - show members of setA,
+# + but suppress displaying of the elements options.
+#
+# $0 -m -Oi packets:0
+# + show members of all sets which have a packet count of 0.
+#
+# $0 -m -Oi "packets:>100" -Oi "bytes:>1024"
+# + show members of all sets which have a
+# + packet count greater than 100 and a byte count greater than 1024.
+#
+# $0 -n -Ca "foo*"
+# + show only set names matching the glob "foo*" and enable all counters.
+#
+# $0 -Hi "markmask:>=0x0000beef" -Hi timeout:\!10000`
+# + show only sets with the header 'Header' fields containing a markmask
+# + greater or equal to 0x0000beef and a timeout which is not 10000.
# -----------------------------------------------------------------
# -----------------------------------------------------------------
# Modify here
# -----------------------------------------------------------------
-# path to ipset. defaults to `/sbin/ipset' if unset.
-ipset="/sbin/ipset"
+# modify your PATH variable
+# by default the path is only set if the PATH variable is not already set in the environment
+# PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+: ${PATH:=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}
+
+# path to ipset.
+# defaults to `/sbin/ipset' if unset.
+#ipset="/sbin/ipset"
+# find in path if not declared in parent environment
+: ${ipset:=$(command -v ipset)}
# default delimiter character for set members (elements).
# defaults to whitespace if unset.
@@ -136,6 +165,7 @@ TMOUT=30
colorize=0
# path to cl (to colorize the output).
+# http://sourceforge.net/projects/colorize-shell/ or
# https://github.com/AllKind/cl
# defaults to `/usr/local/bin/cl' if unset.
cl="/usr/local/bin/cl"
@@ -198,18 +228,21 @@ set +u
# variables
export LC_ALL=C
-readonly version=2.7
+readonly version=3.1
readonly me="${0//*\//}"
readonly oIFS="$IFS"
-declare ips_version="" str_search="" str_xclude="" opt str_hval str_op
+declare ips_version="" str_search="" str_xclude="" opt str_name str_val str_op
declare -i show_all=show_count=show_members=headers_only=names_only=isolate=calc_mem=count_sets=sets_total=0
-declare -i match_on_header=glob_search=regex_search=member_count=match_count=do_count=0
-declare -i exclude_header=glob_xclude_element=glob_xclude_element=exclude_set=0
-declare -i in_header=found_set=found_hxclude=found_sxclude=xclude_count=mem_total=mem_tmp=set_count=sets_sum=i=x=idx=0
-declare -a arr_sets=() arr_par=() arr_hcache=() arr_mcache=() arr_hsearch=()
-declare -a arr_hsearch_int=() arr_hxclude=() arr_sxclude=() arr_match_on_msum=()
+declare -i match_on_header=glob_search=regex_search=member_count=match_count=do_count=opt_int_search=0
+declare -i exclude_header=glob_xclude_element=glob_xclude_element=exclude_set=xclude_member_opts=0
+declare -i in_header=found_set=found_member_opt=found_hxclude=found_sxclude=xclude_count=mem_total=mem_tmp=set_count=sets_sum=i=x=y=idx=0
+declare -a arr_sets=() arr_par=() arr_hcache=() arr_mcache=() arr_hsearch=() arr_tmp=()
+declare -a arr_hsearch_int=() arr_hsearch_xint=() arr_hxclude=() arr_sxclude=() arr_match_on_msum=() arr_opt_int_search=()
+# -----------------------------------------------------------------
# functions
+# -----------------------------------------------------------------
+
ex_miss_optarg() {
printf "%s of option \`%s' is missing\n" "$2" "$1" >&2
exit 2
@@ -224,9 +257,68 @@ is_int() {
[[ $1 = +([[:digit:]]) ]]
}
+is_digit_or_xigit() {
+[[ $1 = @(+([[:digit:]])|0x+([[:xdigit:]])) ]]
+}
+
is_compare_str() {
[[ $1 = ?(\!|<|>|<=|>=)+([[:digit:]]) ]]
}
+
+add_search_to_member_cache() {
+if ((show_members || show_all || isolate)); then
+ arr_mcache[i++]="$REPLY"
+fi
+}
+
+arith_elem_opt_search() {
+found_member_opt=0
+for y in ${!arr_opt_int_search[@]}; do
+ str_val="${arr_opt_int_search[y]#*:}"
+ str_op="${str_val//[[:digit:]]}" # compare operator defaults to `=='
+ [[ ${str_op:===} = \! ]] && str_op='!='
+ set -- $REPLY
+ shift
+ while (($# > 1)); do
+ if [[ $1 = ${arr_opt_int_search[y]%:*} ]]; then
+ if is_int "${str_val//[[:punct:]]}"; then
+ if (($2 $str_op ${str_val//[[:punct:]]})); then
+ let found_member_opt+=1
+ shift
+ fi
+ fi
+ fi
+ shift
+ done
+done
+if ((opt_int_search == found_member_opt)); then
+ let match_count+=1
+ add_search_to_member_cache
+fi
+}
+
+xclude_elem_search() {
+if ((glob_xclude_element)); then # exclude matching members
+ if [[ $REPLY = $str_xclude ]]; then
+ let xclude_count+=1
+ return 0
+ fi
+elif ((regex_xclude_element)); then # exclude matching members
+ if [[ $REPLY =~ $str_xclude ]]; then
+ let xclude_count+=1
+ return 0
+ else
+ if (($? == 2)); then
+ printf "Invalid regex pattern \`%s'.\n" "$str_xclude" >&2
+ exit 1
+ fi
+ fi
+fi
+return 1
+}
+
+# -----------------------------------------------------------------
+# main
# -----------------------------------------------------------------
# validate value of colorize
@@ -238,36 +330,47 @@ fi
while (($#)); do
case "$1" in
-\?|-h) printf "\n\tipset set listing wrapper script\n\n"
- printf '%s [option [opt-arg]] [set-name] [...]\n\n' "$me"
- printf '%s %s\n' "$me" "{-?|-h} | -n"
- printf '%s %s\n\t%s\n' "$me" "[-i|-r|-s|-Co] [-d char] [-To value]"\
- "[{-Fg|-Fr}|{-Xg|-Xr} pattern] -- set-name"
- printf '%s %s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n' "$me"\
- "[-t|-c|-Ca|-Co|-Cs|-Tm|-Ts]"\
- "[-Fh header-glob:value-glob] [...]"\
- "[-Fi header-glob:[!|<|>|<=|>=]value] [...]"\
- "[-Fg|-Fr pattern] [-Ht type-glob]"\
- "[-Hr|-Hs|-Hv [!|<|>|<=|>=]value]"\
- "[-Mc [!|<|>|<=|>=]value] [...] [-To value]"\
- "[-Xh header-glob:value-glob] [...]"\
- "[-Xs setname-glob] [...] -- [set-name] [...]"
- printf '%s %s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n' "$me"\
- "[-a|-c|-m|-r|-s|-Ca|-Co|-Cs|-Tm|-Ts] [-d char]"\
- "[-Fh header-glob:value-glob] [...]"\
- "[-Fi header-glob:[!|<|>|<=|>=]value] [...]"\
- "[-Fg|-Fr pattern] [-Ht type-glob]"\
- "[-Hr|-Hs|-Hv [!|<|>|<=|>=]value]"\
- "[-Mc [!|<|>|<=|>=]value] [...] [-To value]"\
+ printf '%s [option [opt-arg]] [set-name-glob] [...]\n\n' "$me"
+ printf '%s %s\n' "$me" "{-?|-h} | -v"
+ printf '%s %s\n\t%s\n\t%s\n' "$me"\
+ "[-i|-r|-s|-Co|-Xo] [-d char] [-To value]"\
+ "[-Fg|-Fr pattern] [-Xg|-Xr pattern]"\
+ "[-Oi option-glob:[!|<|>|<=|>=]value] [...] -- set-name"
+ printf '%s %s\n\t%s\n\t%s\n\t%s\n' "$me"\
+ "[-n|-c|-Ca|-Co|-Cs|-Tm|-Ts|-Xs] [-To value]"\
+ "[-Fh header-glob:value-glob] [...] [-Fg|-Fr pattern]"\
+ "[-Hi glob:[!|<|>|<=|>=]value] [...]"\
+ "[-Oi option-glob:[!|<|>|<=|>=]value] [...] -- [set-name-glob] [...]"
+ printf '%s %s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n' "$me"\
+ "[-t|-c|-Ca|-Co|-Cs|-Tm|-Ts]"\
+ "[-Fh header-glob:value-glob] [...]"\
+ "[-Fi header-glob:[!|<|>|<=|>=]value] [...]"\
+ "[-Fg|-Fr pattern] [-Ht type-glob]"\
+ "[-Hi glob:[!|<|>|<=|>=]value] [...]"\
+ "[-Hr|-Hs|-Hv [!|<|>|<=|>=]value]"\
+ "[-Mc [!|<|>|<=|>=]value] [...] [-To value]"\
+ "[-Oi option-glob:[!|<|>|<=|>=]value] [...]"\
"[-Xh header-glob:value-glob] [...]"\
- "[-Xg|-Xr pattern] [-Xs setname-glob] [...] -- [set-name] [...]"
+ "[-Xs set-name-glob] [...] -- [set-name-glob] [...]"
+ printf '%s %s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n' "$me"\
+ "[-a|-c|-m|-r|-s|-Ca|-Co|-Cs|-Tm|-Ts|-Xo] [-d char]"\
+ "[-Fh header-glob:value-glob] [...]"\
+ "[-Fi header-glob:[!|<|>|<=|>=]value] [...]"\
+ "[-Fg|-Fr pattern] [-Ht type-glob]"\
+ "[-Hi glob:[!|<|>|<=|>=]value] [...]"\
+ "[-Hr|-Hs|-Hv [!|<|>|<=|>=]value]"\
+ "[-Mc [!|<|>|<=|>=]value] [...]"\
+ "[-Oi option-glob:[!|<|>|<=|>=]value] [...]"\
+ "[-To value] [-Xh header-glob:value-glob] [...]"\
+ "[-Xg|-Xr pattern] [-Xs set-name-glob] [...] -- [set-name-glob] [...]"
printf 'options:\n'
printf '%-13s%s\n' '-a' 'show all information but with default delim (whitespace).'\
- '-c' 'calculate members and match (-Fg|-Fr) sum.'\
+ '-c' 'calculate members and match sum.'\
'-d delim' 'delimiter character for separating member entries.'\
'-h|-?' 'show this help text.'\
'-i' 'show only the members of a single set.'\
'-m' 'show set members.'\
- '-n' "show set names only (raw \`ipset list -n' output)."\
+ '-n' "show set names only."\
'-r' 'try to resolve ip addresses in the output (slow!).'\
'-s' 'print elements sorted (if supported by the set type).'\
'-t' 'show set headers only.'\
@@ -281,14 +384,18 @@ while (($#)); do
'show sets containing one or more [ext]glob matching headers.'
printf '%s\n\t%s\n' '-Fi header-glob:[!|<|>|<=|>=]value [...]'\
'show sets matching one or more integer valued header entries.'
+ printf '%s\n\t%s\n' '-Hi header-glob:[!|<|>|<=|>=]value [...]'\
+ "match one or more integer valued headers \`Header' entries."
printf '%-24s%s\n' '-Ht set-type-glob' 'match on set type.'\
'-Hr [!|<|>|<=|>=]value' 'match on number of references (value=int).'\
'-Hs [!|<|>|<=|>=]value' 'match on size in memory (value=int).'\
'-Hv [!|<|>|<=|>=]value' 'match on revision number (value=int).'
printf '%-30s%s\n' '-Mc [!|<|>|<=|>=]value [...]' 'match on member count (value=int).'
+ printf '%s\n\t%s\n' '-Oi option-glob:[!|<|>|<=|>=]value [...]' 'match on member options (value=int).'
printf '%-13s%s\n' '-Tm' 'calculate total memory usage of all matching sets.'\
'-To' 'set timeout value (int) for read (listing sets).'\
- '-Ts' 'count amount of traversed sets.'
+ '-Ts' 'count amount of traversed sets.'\
+ '-Xo' 'suppress display of member options.'
printf '%s\n\t%s\n' '-Xh header-glob:value-glob [...]'\
'exclude one or more [ext]glob matching header entries.'
printf '%-13s%s\n' '-Xg pattern' 'exclude members matching a [ext]glob pattern.'\
@@ -352,6 +459,15 @@ while (($#)); do
str_search="$2"
shift 2
;;
+ -Oi) let opt_int_search+=1
+ [[ $2 ]] || ex_miss_optarg $1 "pattern"
+ if [[ $2 = *:* ]] && is_compare_str "${2#*:}"; then
+ arr_opt_int_search[y++]="$2"
+ shift 2
+ else
+ ex_invalid_usage "invalid format of header descriptor. expecting: \`glob:[!|<|>|<=|>=]value'"
+ fi
+ ;;
-Fh) let match_on_header+=1 # show only sets, which contain a matching header entry
[[ $2 ]] || ex_miss_optarg $1 "header pattern"
if [[ $2 = *:* ]]; then
@@ -363,13 +479,22 @@ while (($#)); do
;;
-Fi) let match_on_header+=1 # show only sets, containing a matching (int compare) header entry
[[ $2 ]] || ex_miss_optarg $1 "header pattern"
- if is_compare_str "$2"; then
+ if [[ $2 = *:* ]] && is_compare_str "${2#*:}"; then
arr_hsearch_int[idx++]="$2"
shift 2
else
ex_invalid_usage "invalid format of header descriptor. expecting: \`name:[!|<|>|<=|>=]value'"
fi
;;
+ -Hi) let match_on_header+=1 # match on name + integer (digit & xdigit) inside of headers 'Header' flag
+ [[ $2 ]] || ex_miss_optarg $1 "header pattern"
+ if [[ $2 = *:?(\!|<|>|<=|>=)@(+([[:digit:]])|0x+([[:xdigit:]])) ]]; then
+ arr_hsearch_xint[${#arr_hsearch_xint[@]}]="$2"
+ shift 2
+ else
+ ex_invalid_usage "invalid format of headers \'Header' flag descriptor. expecting: \`name:[!|<|>|<=|>=]value'"
+ fi
+ ;;
-Hr) let match_on_header+=1 # shortcut for -Fi References:...
[[ $2 ]] || ex_miss_optarg $1 "header pattern"
if is_compare_str "$2"; then
@@ -445,6 +570,9 @@ while (($#)); do
str_xclude="$2"
shift 2
;;
+ -Xo) xclude_member_opts=1 # don't show elements options
+ shift
+ ;;
-Xs) exclude_set=1 # don't show certain sets
[[ $2 ]] || ex_miss_optarg $1 "set name ([ext]glob pattern)"
arr_sxclude[${#arr_sxclude[@]}]="$2"
@@ -460,7 +588,7 @@ while (($#)); do
*) break
esac
done
-declare -i i=x=idx=0
+declare -i i=x=y=idx=0
# check for ipset program and version
[[ -x ${ipset:=/sbin/ipset} ]] || {
@@ -486,24 +614,25 @@ fi
# option logic
if ((names_only)); then
- if ((headers_only||show_count||show_members||show_all||isolate||\
- match_on_header||do_count||glob_search||regex_search||calc_mem||\
- glob_xclude_element||regex_xclude_element||count_sets||sets_total||exclude_set))
+ if ((headers_only||show_members||show_all||isolate||\
+ glob_xclude_element||regex_xclude_element||xclude_member_opts))
then
- ex_invalid_usage "option -n does not allow another option"
+ ex_invalid_usage "option -n does not allow this combination of options"
fi
- # raw ipset output
- "$ipset" list -n
- exit $?
fi
if ((headers_only)); then
if ((show_members || show_all || isolate)); then
ex_invalid_usage "options -t and -a|-i|-m are mutually exclusive"
fi
fi
+if ((headers_only)); then
+ if ((xclude_member_opts||glob_xclude_element||regex_xclude_element)); then
+ ex_invalid_usage "options -t and -Xg|-Xr|-Xo are mutually exclusive"
+ fi
+fi
if ((isolate)); then
if ((show_count||show_all||calc_mem||count_sets||sets_total||exclude_set)); then
- ex_invalid_usage "options -i and -a|-c|-Cs|-Tm|-Ts|-Xs are mutually exclusive"
+ ex_invalid_usage "options -i and -a|-c|-Ca|-Cs|-Tm|-Ts|-Xs are mutually exclusive"
fi
if ((match_on_header)); then
ex_invalid_usage "option -i does not allow matching on header entries"
@@ -513,9 +642,6 @@ if ((glob_search || regex_search)); then
if ((glob_search && regex_search)); then
ex_invalid_usage "options -Fg and -Fr are mutually exclusive"
fi
- if ((glob_xclude_element || regex_xclude_element)); then
- ex_invalid_usage "options -Fg|-Fr and -Xg|-Xr are mutually exclusive"
- fi
fi
if ((exclude_header)); then
if ! ((headers_only || show_all)); then
@@ -524,7 +650,7 @@ if ((exclude_header)); then
fi
if ((glob_xclude_element || regex_xclude_element)); then
if ! ((show_members || show_all || isolate)); then
- ex_invalid_usage "options -Fg|-Fr require any of -a|-i|-m"
+ ex_invalid_usage "options -Xg|-Xr require any of -a|-i|-m"
fi
fi
if ((colorize)); then
@@ -551,7 +677,7 @@ if ((colorize)); then
for opt in col_fg col_bg col_headers col_members col_match col_memsize \
col_set_count col_set_total col_highlight
do
- ($cl ${!opt}) || ex_invalid_usage "variable \`$opt' has an invalid color value: \`${!opt}'"
+ ("$cl" ${!opt}) || ex_invalid_usage "variable \`$opt' has an invalid color value: \`${!opt}'"
done
[[ -t 1 ]] || colorize=0 # output is not a terminal
fi
@@ -565,10 +691,13 @@ if ! ((${#arr_sets[@]})); then
exit 1
fi
if [[ $1 ]]; then # there are remaining arg(s)
- for opt in "$@"; do found_set=0 # check if the sets exist
+ for opt; do found_set=0 # check if the sets exist
for idx in ${!arr_sets[@]}; do
- if [[ $opt = ${arr_sets[idx]} ]]; then found_set=1
- break
+ if [[ ${arr_sets[idx]} = $opt ]]; then found_set=1
+ # match could be a glob, thus multiple matches possible
+ # save to temp array
+ arr_tmp[${#arr_tmp[@]}]="${arr_sets[idx]}"
+ unset arr_sets[idx]
fi
done
if ! ((found_set)); then
@@ -580,7 +709,10 @@ if [[ $1 ]]; then # there are remaining arg(s)
ex_invalid_usage "option -i is only valid for a single set"
fi
fi
- arr_sets=("$@") # reassign remaining args
+ arr_sets=("${arr_tmp[@]}") # reassign matched sets
+ if ((isolate && ${#arr_sets[@]} > 1)); then
+ ex_invalid_usage "option -i is only valid for a single set"
+ fi
else
if ((isolate)); then
ex_invalid_usage "option -i is only valid for a single set"
@@ -589,7 +721,7 @@ fi
# read sets
for idx in "${!arr_sets[@]}"; do found_set=0 arr_hcache=() arr_mcache=()
- while read -r || {
+ while read -r || {
(($? > 128)) && \
printf "timeout reached or signal received, while reading set \`%s'.\n" \
"${arr_sets[idx]}" >&2 && continue 2;
@@ -603,26 +735,32 @@ for idx in "${!arr_sets[@]}"; do found_set=0 arr_hcache=() arr_mcache=()
fi
let sets_sum+=1
if ((exclude_set)); then # don't show certain sets
- for x in ${!arr_sxclude[@]}; do
- if [[ ${arr_sets[idx]} = ${arr_sxclude[x]} ]]; then let found_sxclude+=1
+ for y in ${!arr_sxclude[@]}; do
+ if [[ ${arr_sets[idx]} = ${arr_sxclude[y]} ]]; then let found_sxclude+=1
continue 3 # don't unset, as user could list sets multiple times
fi
done
fi
- in_header=1 found_set=1 found_header=0 member_count=0 match_count=0 xclude_count=0 mem_tmp=0 i=0 x=0
+ in_header=1 found_set=1 found_header=0 member_count=0 match_count=0 xclude_count=0 mem_tmp=0 i=0 x=0
if ! ((isolate)); then # if showing members only, continue without saving any header data
- if ! ((headers_only||show_members||show_all||show_count||match_on_header||do_count||calc_mem||glob_search||regex_search))
+ if ((names_only)); then
+ if ((colorize)); then
+ arr_hcache[x++]="$("$cl" bold $col_headers)${REPLY#*:+([[:blank:]])}$("$cl" normal $col_fg $col_bg)"
+ else
+ arr_hcache[x++]="${REPLY#*:+([[:blank:]])}"
+ fi
+ elif ! ((headers_only||show_members||show_all||show_count||match_on_header||do_count||calc_mem||glob_search||regex_search||opt_int_search))
then
- in_header=0
+ in_header=0
if ((colorize)); then
- arr_hcache[x++]="$($cl bold $col_headers)${REPLY}$($cl normal $col_fg $col_bg)"
+ arr_hcache[x++]="$("$cl" bold $col_headers)${REPLY}$("$cl" normal $col_fg $col_bg)"
else
arr_hcache[x++]="$REPLY"
fi
break # nothing to show but the names
else
if ((colorize)); then
- arr_hcache[x++]=$'\n'"$($cl bold $col_headers)${REPLY}$($cl normal $col_fg $col_bg)"
+ arr_hcache[x++]=$'\n'"$("$cl" bold $col_headers)${REPLY}$("$cl" normal $col_fg $col_bg)"
else
arr_hcache[x++]=$'\n'"$REPLY"
fi
@@ -641,8 +779,8 @@ for idx in "${!arr_sets[@]}"; do found_set=0 arr_hcache=() arr_mcache=()
fi
fi
if ((exclude_header)); then # don't show certain headers
- for idx in ${!arr_hxclude[@]}; do
- if [[ ${REPLY%%:*} = ${arr_hxclude[idx]%%:*} && ${REPLY#*: } = ${arr_hxclude[idx]#*:} ]]
+ for y in ${!arr_hxclude[@]}; do
+ if [[ ${REPLY%%:*} = ${arr_hxclude[y]%%:*} && ${REPLY#*: } = ${arr_hxclude[y]#*:} ]]
then found_hxclude=1
break
fi
@@ -650,7 +788,7 @@ for idx in "${!arr_sets[@]}"; do found_set=0 arr_hcache=() arr_mcache=()
fi
if ((show_all && ! found_hxclude)); then
if ((colorize)); then
- arr_hcache[x++]="$($cl bold $col_headers)${REPLY}$($cl normal $col_fg $col_bg)"
+ arr_hcache[x++]="$("$cl" bold $col_headers)${REPLY}$("$cl" normal $col_fg $col_bg)"
else
arr_hcache[x++]="$REPLY"
fi
@@ -663,25 +801,56 @@ for idx in "${!arr_sets[@]}"; do found_set=0 arr_hcache=() arr_mcache=()
fi
if ((in_header)); then # we should be in the header
if ((match_on_header && found_header < match_on_header)); then # match on an header entry
- for idx in ${!arr_hsearch[@]}; do # string compare
- if [[ ${REPLY%%:*} = ${arr_hsearch[idx]%%:*} && ${REPLY#*: } = ${arr_hsearch[idx]#*:} ]]
+ for y in ${!arr_hsearch[@]}; do # string compare
+ if [[ ${REPLY%%:*} = ${arr_hsearch[y]%%:*} && ${REPLY#*: } = ${arr_hsearch[y]#*:} ]]
then let found_header+=1
fi
done
- for idx in ${!arr_hsearch_int[@]}; do # int compare
- if [[ ${REPLY%%:*} = ${arr_hsearch_int[idx]%%:*} ]]; then # header name matches
+ for y in ${!arr_hsearch_int[@]}; do # int compare
+ if [[ ${REPLY%%:*} = ${arr_hsearch_int[y]%%:*} ]]; then # header name matches
if ! is_int "${REPLY#*: }"; then
printf "header value \`%s' is not an integer.\n" "${REPLY#*: }" >&2
exit 1
fi
- str_hval="${arr_hsearch_int[idx]#*:}"
- str_op="${str_hval//[[:digit:]]}" # compare operator defaults to `=='
+ str_val="${arr_hsearch_int[y]#*:}"
+ str_op="${str_val//[[:digit:]]}" # compare operator defaults to `=='
[[ ${str_op:===} = \! ]] && str_op='!='
- if ((${REPLY#*: } $str_op ${str_hval//[[:punct:]]})); then
+ if ((${REPLY#*: } $str_op ${str_val//[[:punct:]]})); then
let found_header+=1
fi
fi
done
+ # search and arithmetic compare values of the headers 'Header' flag
+ if ((${#arr_hsearch_xint[@]})) && [[ ${REPLY%%:*} = Header ]]; then
+ set -- ${REPLY#*:}
+ while (($#)); do
+ if is_digit_or_xigit "$1"; then
+ shift
+ continue
+ fi
+ for y in ${!arr_hsearch_xint[@]}; do
+ str_name="${arr_hsearch_xint[y]%%:*}"
+ str_val="${arr_hsearch_xint[y]#*:}"
+ if [[ $str_val = ??0x+([[:xdigit:]]) ]]; then
+ str_op="${str_val%0x*}"
+ elif [[ $str_val = ??+([[:digit:]]) ]]; then
+ str_op="${str_val//[[:digit:]]}"
+ fi
+ str_val="${str_val#"${str_op}"}"
+ [[ ${str_op:===} = \! ]] && str_op='!='
+ if [[ $1 = $str_name ]]; then
+ if is_digit_or_xigit "$2"; then
+ if (($2 $str_op $str_val)); then
+ let found_header+=1
+ shift
+ break
+ fi
+ fi
+ fi
+ done
+ shift
+ done
+ fi
fi
if ((calc_mem)); then
if [[ ${REPLY%%:*} = "Size in memory" ]]; then
@@ -695,8 +864,8 @@ for idx in "${!arr_sets[@]}"; do found_set=0 arr_hcache=() arr_mcache=()
fi
if ((headers_only || show_all)); then found_hxclude=0
if ((exclude_header)); then # don't show certain headers
- for idx in ${!arr_hxclude[@]}; do
- if [[ ${REPLY%%:*} = ${arr_hxclude[idx]%%:*} && ${REPLY#*: } = ${arr_hxclude[idx]#*:} ]]
+ for y in ${!arr_hxclude[@]}; do
+ if [[ ${REPLY%%:*} = ${arr_hxclude[y]%%:*} && ${REPLY#*: } = ${arr_hxclude[y]#*:} ]]
then found_hxclude=1
break
fi
@@ -707,28 +876,42 @@ for idx in "${!arr_sets[@]}"; do found_set=0 arr_hcache=() arr_mcache=()
fi
fi
else # this should be a member entry
- if ((show_members || show_all || isolate || glob_search || regex_search)); then
- if ((glob_search)); then # show sets with matching members
- if [[ $REPLY = $str_search ]]; then let match_count+=1
- if ((show_members || show_all || isolate)); then
- arr_mcache[i++]="$REPLY"
+ if ((show_members || show_all || isolate || glob_search || regex_search || opt_int_search)); then
+ if ((glob_search)); then # show sets with glob pattern matching members
+ if ! xclude_elem_search; then
+ if [[ $REPLY = $str_search ]]; then
+ if ((opt_int_search)); then
+ arith_elem_opt_search
+ else
+ let match_count+=1
+ add_search_to_member_cache
+ fi
fi
fi
- elif ((regex_search)); then # show sets with matching members
- if [[ $REPLY =~ $str_search ]]; then let match_count+=1
- if ((show_members || show_all || isolate)); then
- arr_mcache[i++]="$REPLY"
- fi
- else
- if (($? == 2)); then
- printf "Invalid regex pattern \`%s'.\n" "$str_search"
- exit 1
+ elif ((regex_search)); then # show sets with regex pattern matching members
+ if ! xclude_elem_search; then
+ if [[ $REPLY =~ $str_search ]]; then
+ if ((opt_int_search)); then
+ arith_elem_opt_search
+ else
+ let match_count+=1
+ add_search_to_member_cache
+ fi
+ else
+ if (($? == 2)); then
+ printf "Invalid regex pattern \`%s'.\n" "$str_search" >&2
+ exit 1
+ fi
fi
fi
+ elif ((opt_int_search)); then # show sets with matching member options
+ if ! xclude_elem_search; then
+ arith_elem_opt_search
+ fi
else
if ((glob_xclude_element)); then # exclude matching members
if ! [[ $REPLY = $str_xclude ]]; then
- arr_mcache[i++]="$REPLY"
+ add_search_to_member_cache
else let xclude_count+=1
fi
elif ((regex_xclude_element)); then # exclude matching members
@@ -736,10 +919,10 @@ for idx in "${!arr_sets[@]}"; do found_set=0 arr_hcache=() arr_mcache=()
let xclude_count+=1
else
if (($? == 2)); then
- printf "Invalid regex pattern \`%s'.\n" "$str_xclude"
+ printf "Invalid regex pattern \`%s'.\n" "$str_xclude" >&2
exit 1
fi
- arr_mcache[i++]="$REPLY"
+ add_search_to_member_cache
fi
else
arr_mcache[i++]="$REPLY"
@@ -755,10 +938,10 @@ for idx in "${!arr_sets[@]}"; do found_set=0 arr_hcache=() arr_mcache=()
esac
done < <("$ipset" list "${arr_sets[idx]}" "${arr_par[@]}")
if ((found_set)); then # print gathered information
- if ((glob_search || regex_search)) && ((match_count == 0)); then
- continue # glob or regex search didn't match
+ if ((glob_search || regex_search || opt_int_search)) && ((match_count == 0)); then
+ continue # glob, regex or option-integer search didn't match
fi
- if ((${#arr_match_on_msum[@]} > 0)); then # match on member sum
+ if ((${#arr_match_on_msum[@]} > 0)); then # match on member sum (do_count=1)
for i in ${!arr_match_on_msum[@]}; do
str_op="${arr_match_on_msum[i]//[[:digit:]]}"
[[ ${str_op:===} = \! ]] && str_op='!='
@@ -771,64 +954,69 @@ for idx in "${!arr_sets[@]}"; do found_set=0 arr_hcache=() arr_mcache=()
if ((calc_mem)); then
let mem_total+=$mem_tmp
fi
- if ((${#arr_hcache[@]})); then
+ if ((${#arr_hcache[@]})); then # print header
if ((colorize)); then
- printf "$($cl $col_headers)%b$($cl normal $col_fg $col_bg)\n" "${arr_hcache[@]}"
+ printf "$("$cl" $col_headers)%b$("$cl" normal $col_fg $col_bg)\n" "${arr_hcache[@]}"
else
printf "%s\n" "${arr_hcache[@]}"
fi
fi
- if ((${#arr_mcache[@]})); then
+ if ((${#arr_mcache[@]})); then # print members
+ if ((xclude_member_opts)); then
+ arr_mcache=( "${arr_mcache[@]%% *}" )
+ fi
IFS="${delim:= }"
if ((colorize)); then
- printf "$($cl $col_members)%s$($cl normal $col_fg $col_bg)" "${arr_mcache[*]}"
+ printf "$("$cl" $col_members)%s$("$cl" normal $col_fg $col_bg)" "${arr_mcache[*]}"
else
printf "%s" "${arr_mcache[*]}"
fi
IFS="$oIFS"
printf "\n"
fi
- if ((show_count)); then
- if ((glob_search || regex_search)); then
+ if ((show_count)); then # print counters
+ if ((glob_search || regex_search || opt_int_search)); then
if ((colorize)); then
- printf "$($cl $col_match)Match count$($cl normal $col_fg $col_bg):\
- $($cl bold $col_match)%d$($cl normal $col_fg $col_bg)\n" $match_count
+ printf "$("$cl" $col_match)Match count$("$cl" normal $col_fg $col_bg):\
+ $("$cl" bold $col_match)%d$("$cl" normal $col_fg $col_bg)\n" $match_count
else
printf "Match count: %d\n" $match_count
fi
fi
if ((glob_xclude_element || regex_xclude_element)); then
if ((colorize)); then
- printf "$($cl $col_match)Exclude count$($cl normal $col_fg $col_bg):\
- $($cl bold $col_match)%d$($cl normal $col_fg $col_bg)\n" $xclude_count
+ printf "$("$cl" $col_match)Exclude count$("$cl" normal $col_fg $col_bg):\
+ $("$cl" bold $col_match)%d$("$cl" normal $col_fg $col_bg)\n" $xclude_count
else
printf "Exclude count: %d\n" $xclude_count
fi
fi
if ((colorize)); then
- printf "$($cl bold $col_highlight)Member count$($cl normal $col_fg $col_bg):\
- $($cl bold $col_members)%d$($cl normal $col_fg $col_bg)\n" $member_count
+ printf "$("$cl" bold $col_highlight)Member count$("$cl" normal $col_fg $col_bg):\
+ $("$cl" bold $col_members)%d$("$cl" normal $col_fg $col_bg)\n" $member_count
else
printf "Member count: %d\n" $member_count
fi
fi
fi
done
+
+# print global counters
if ((count_sets || calc_mem || sets_total || exclude_set)); then
printf "\n"
if ((count_sets)); then
if ((colorize)); then
- printf "$($cl bold $col_highlight)Count$($cl normal $col_fg $col_bg) of all\
- $($cl bold $col_set_count)matched sets$($cl normal $col_fg $col_bg):\
- $($cl bold $col_set_count)%d$($cl normal $col_fg $col_bg)\n" $set_count
+ printf "$("$cl" bold $col_highlight)Count$("$cl" normal $col_fg $col_bg) of all\
+ $("$cl" bold $col_set_count)matched sets$("$cl" normal $col_fg $col_bg):\
+ $("$cl" bold $col_set_count)%d$("$cl" normal $col_fg $col_bg)\n" $set_count
else
printf "Count of all matched sets: %d\n" $set_count
fi
if ((exclude_set)); then
if ((colorize)); then
- printf "$($cl bold $col_highlight)Count$($cl normal $col_fg $col_bg) of all\
- $($cl bold $col_match)excluded sets$($cl normal $col_fg $col_bg):\
- $($cl bold $col_match)%d$($cl normal $col_fg $col_bg)\n" $found_sxclude
+ printf "$("$cl" bold $col_highlight)Count$("$cl" normal $col_fg $col_bg) of all\
+ $("$cl" bold $col_match)excluded sets$("$cl" normal $col_fg $col_bg):\
+ $("$cl" bold $col_match)%d$("$cl" normal $col_fg $col_bg)\n" $found_sxclude
else
printf "Count of all excluded sets: %d\n" $found_sxclude
fi
@@ -836,17 +1024,17 @@ if ((count_sets || calc_mem || sets_total || exclude_set)); then
fi
if ((sets_total)); then
if ((colorize)); then
- printf "$($cl bold $col_highlight)Count$($cl normal $col_fg $col_bg) of all\
- $($cl bold $col_set_total)traversed sets$($cl normal $col_fg $col_bg):\
- $($cl bold $col_set_total)%d$($cl normal $col_fg $col_bg)\n" $sets_sum
+ printf "$("$cl" bold $col_highlight)Count$("$cl" normal $col_fg $col_bg) of all\
+ $("$cl" bold $col_set_total)traversed sets$("$cl" normal $col_fg $col_bg):\
+ $("$cl" bold $col_set_total)%d$("$cl" normal $col_fg $col_bg)\n" $sets_sum
else
printf "Count of all traversed sets: %d\n" $sets_sum
fi
fi
if ((calc_mem)); then
if ((colorize)); then
- printf "$($cl bold $col_memsize)Total memory size$($cl normal $col_fg $col_bg)\
- of all matched sets: $($cl bold $col_memsize)%d$($cl normal $col_fg $col_bg)\n" $mem_total
+ printf "$("$cl" bold $col_memsize)Total memory size$("$cl" normal $col_fg $col_bg)\
+ of all matched sets: $("$cl" bold $col_memsize)%d$("$cl" normal $col_fg $col_bg)\n" $mem_total
else
printf "Total memory size of all matched sets: %d\n" $mem_total
fi
diff --git a/utils/ipset_list/ipset_list_bash_completion b/utils/ipset_list/ipset_list_bash_completion
index e4faf7f..f92c7ab 100644
--- a/utils/ipset_list/ipset_list_bash_completion
+++ b/utils/ipset_list/ipset_list_bash_completion
@@ -7,7 +7,7 @@
# https://sourceforge.net/projects/ipset-list/
# -----------------------------------------------------------------
-# Copyright (C) 2013 AllKind (AllKind@fastest.cc)
+# 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
@@ -75,15 +75,16 @@ done
_ipset_list_complete() {
local -i i=x=got_bashcompl=0
-local -i show_all=isolate=show_members=resolve=headers_only=set_index=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" -n ) )
+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)
-local Hopts=(-Hr -Hs -Ht -Hv)
+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)
+local Xopts=(-Xh -Xg -Xr -Xs -Xo)
local arr_types=()
: ${PATH:=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}
@@ -120,22 +121,23 @@ for ((i=1; i < ${#words[@]}-1; i++)); do
-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 ;;
+ -t) ((set_index)) && break || headers_only=1 Xopts=(-Xh -Xs) Fopts=(${Fopts[*]/-Oi/}) ;;
--) ((set_index)) && break || set_index=$((i+1)) ;;
- -\?|-h|-n|-v)
+ -\?|-h|-v)
((set_index)) || return 0
;;
- @(-Fh|-Fi|-Xh)) ((set_index)) && break || header_operation=1 ;;
+ @(-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|Ht|Hr|Hs|Hv|Mc|To|Xg|Xh|Xr|Xs))"
+ 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|Ht|Hr|Hs|Hv|Mc|To|Tm|Ts|Xg|Xh|Xr))"
+ 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
@@ -148,7 +150,12 @@ for ((i=1; i < ${#words[@]}-1; i++)); do
done
# invalid combinations of options
-if ((headers_only)); then
+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
@@ -169,7 +176,7 @@ if ((set_index)); then
# and also handles those who have the name of ipset_list options
_ipset_list_show_sets
else
-if [[ $prev = @(-@(\?|d|h|n|v|Fg|Fi|Fr|Ht|To|Xg|Xr)) ]]; then
+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
@@ -217,7 +224,7 @@ elif [[ $prev = @(-@(Hr|Hs|Hv|Mc)) ]]; then
elif [[ $cur = -* ]]; then
# any option is requested
case "$prev" in
- @(-@(\?|d|h|n|v|Fg|Fh|Fi|Fr|Ht|Hr|Hs|Hv|Mc|To|Xg|Xh|Xr)))
+ @(-@(\?|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
@@ -225,18 +232,22 @@ elif [[ $cur = -* ]]; then
esac
# these options don't allow any other
if ((${#words[@]} > 2)); then
- opts=("${opts[@]/@(-n|-h|-\?)/}")
+ opts=("${opts[@]/@(-v|-h|-\?)/}")
fi
# some options allow only a subset of other options
if ((isolate)); then
- COMPREPLY=( $(compgen -W '-- -Co -d -r -s' -- $cur ) )
+ 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[@]}' \
+ '-- -c -d -r -s ${Copts[@]} ${Fopts[@]} ${Hopts[@]} -Mc ${Topts[@]} -Xg -Xr -Xo' \
-- $cur ) )
elif ((show_all)); then
COMPREPLY=( $(compgen -W \
@@ -261,13 +272,13 @@ elif [[ $cur = -* ]]; then
# mutual exclusive options
for ((i=1; i < x; i++)); do
case "${words[i]}" in
- -Fg) _ipset_list_remove_reply_entry "-Fr" "-Xg" "-Xr" ;;
- -Fr) _ipset_list_remove_reply_entry "-Fg" "-Xg" "-Xr" ;;
- -Xg) _ipset_list_remove_reply_entry "-Fg" "-Fr" "-Xr" ;;
- -Xr) _ipset_list_remove_reply_entry "-Fg" "-Fr" "-Xg" ;;
+ -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|Mc|Xh|Xs)) ]]; then
+ if [[ ${words[i]} = @(""|-|-@(Fh|Fi|Hi|Mc|Oi|Xh|Xs)) ]]; then
continue
else # remove dupe
_ipset_list_remove_reply_entry "${words[i]}"