[Bash-completion-devel] tc completion attempt
Raph
gibboris at gmail.com
Sun May 31 01:42:26 UTC 2009
Hello,
today was a bit rainy, a perfect weather
to try tc and to begin the first percent
of it's completion (300 lines)
enjoy
Raph
-------------- next part --------------
#!/bin/bash
## completion (attempt) for tc(8) language^Wcommand
## Rapha?l Droz <gibboris at gmail.com>
have tc && {
_tc_has_args() {
[ $# -ne 2 ] && return 1
local i j opt argnb
opt=$1 && shift
let argnb=$1
let i=1
# first search for this option
while [ $i -le $COMP_CWORD ]; do
# found, let's have a look to its arguments now
if [ ${COMP_WORDS[$i]} = $opt ]; then
let j=$i+1
while [[ $j -le $((i+$argnb)) ]]; do
# this one is null, returns 1
[[ -z "${COMP_WORDS[$j]}" ]] && return 1
((j++))
done
# last but not least :
# either the next argument should exists
# either $COMP_CWORDS should be $j+1 to
# be sure a space as been appended
# won't work with -o nospace
# or it will if -o nospace does increment
# $COMP_CWORD as does the default behavior (untested)
# echo -e "\n$opt, j=$j\nCOMP_WORDS[$((j-1))]=${COMP_WORDS[$j-1]}
# echo -e "COMP_WORDS[$j]=${COMP_WORDS[$j]}\nCOMP_CWORD=$COMP_CWORD passed" > /dev/stderr
[ -n "${COMP_WORDS[$j]}" ] && return 0
# notice here that j had been given a final
# while-increment so it should be equal
# to the new CWORD
[ $j -eq $COMP_CWORD ] && return 0
return 1
fi
((i++))
done
return 1
}
# echoes argument matching the regexp in COMP_WORDS between
# optionnal start and end position
# return the position or -1 if not found
_tc_get_arg_matching() {
[ -z "$1" ] && return -1
local i aarg limit start a
limit=$COMP_CWORD
start=1
aarg=$1 && shift
[ -n "$1" ] && {
let start=$1; shift;
}
[ -n "$1" ] && {
let limit=$1; shift;
}
[ $limit -gt $COMP_CWORD ] && limit=$COMP_CWORD
[ $start -lt 1 ] && start=1
[ $start -gt $limit ] && {
a=$limit
limit=$start
start=$a
}
let i=$start
while [ $i -lt $limit ]; do
[[ ${COMP_WORDS[$i]} =~ $aarg ]] &&
echo ${COMP_WORDS[$i]} &&
return $i
((i++))
done
return -1
}
# returns 0 if the argument is found in the cmdline
# if several argument are given, returns 0 if one of them
# is found in the cmdline
_tc_in_cmd() {
[ -z "$1" ] && return 1
local i aarg
let i=0
#echo -e "\n\n\n=====$@"
aarg=$1 && shift
#echo -e "$aarg :"
while [ $((i++)) -lt $((COMP_CWORD-1)) ]; do
[[ ${COMP_WORDS[$i]} = "$aarg" ]] && {
#echo "$aarg match ${COMP_WORDS[$i]} ($i)"
return 0
}
done
# a bit tricky :
#?if args left, process them and returns (recursive)
# otherwise, with only one argument, the test is false
# so returns still return 1
[[ -n "$@" ]] && _tc_in_cmd $@
return $?
}
# list available kernel schedulers|actions|filters
_tc_getsched() {
echo $(ls /lib/modules/$(uname -r)/kernel/net/sched/$1_*.ko|\
sed "s/^.*$1_\([a-z0-9-]*\)\.ko/\1/")
}
# generate COMPREPLY being given
# some options candidate for completion
# some other options candidate for completion
# which won't be checked for duplication
# some options mutually exclusive
_tc-makecomp() {
# argument and kernel variable argument
local arg varg excl
local strleft=()
arg="$1" && shift
varg="$1" && shift
excl="$1" && shift
#echo -e "1=$arg\n2=$varg\n3=$excl" > /dev/stderr
for i in $arg; do
# if $i already in cmdline : skip it from compgen
# if :
# $i is part of the exclusive arguments
# and
# one of the exclusive arguments is already in the cmdline
# --> skip it from compgen
_tc_in_cmd $i || {
[[ $i =~ ${excl// /|} ]] && _tc_in_cmd $excl
} && continue
strleft[${#strleft[*]}]=$i;
done
COMPREPLY=( $(compgen -W "${strleft[*]} $varg" -- $cur ) )
}
# short dream about completing the action OBJECT of tc
_tc-action() {
if [ -z "$cmd" ]; then
COMPREPLY=( $(compgen -W "add change replace get delete \
ls list flush action help" -- $cur ) )
else
local ACTSPECOP
case $cmd in
add|change|replace)
ACTSPECOP=ACR
;;
get|delete)
ACTSPECOP=GD
;;
ls|list|flush)
ACTSPECOP=FL
;;
action)
ACTSPECOP=x
;;
esac
#_tc_getsched act
COMPREPLY=( )
fi
return 0
}
# filtering is a bit more advanced
_tc-filter() {
# TODO: non-exhaustive list, as no local documentation available right now
local proto='ipv{4,6} irda {r,}arp wan_ppp ppp{ses,disc}'
local cls=$(_tc_getsched cls)
local filtertype filterpos
if ! _tc_in_cmd protocol; then
_tc_in_cmd pref && COMPREPLY=( $(compgen -W 'protocol' -- $cur ) ) ||
COMPREPLY=( $(compgen -W 'pref protocol' -- $cur ) )
return 0
else
if ! _tc_has_args protocol 1; then
COMPREPLY=( $(compgen -W "$proto" -- $cur ) )
return 0
else
if ! _tc_in_cmd $cls; then
_tc_in_cmd estimator && ! _tc_has_args estimator 2 && return 0
_tc-makecomp 'estimator root classid handle help' "$cls" 'root classid'
return 0
else
filtertype=$(_tc_get_arg_matching ${cls// /|})
filterpos=$?
# giving up here : complexity 1, developper 0
[ $COMP_CWORD -gt $((filterpos+1)) ] && return 0
case $filtertype in
basic)
COMPREPLY=( $(compgen -W 'match police action classid' -- $cur ) )
return 0
;;
flow)
COMPREPLY=( $(compgen -W 'map hash' -- $cur ) )
return 0
;;
u32)
COMPREPLY=( $(compgen -W 'match link classid police offset \
ht hashkey sample divisor help' -- $cur ) )
return 0
;;
fw)
COMPREPLY=( $(compgen -W 'classid police help' -- $cur ) )
return 0
;;
route)
COMPREPLY=( $(compgen -W 'from{,if} to flowid police' -- $cur ) )
return 0
;;
rsvp?(6))
COMPREPLY=( $(compgen -W 'ipproto' -- $cur ) )
return 0
;;
tcindex)
COMPREPLY=( $(compgen -W 'hash mask shift pass_on fall_through \
classid police' -- $cur ) )
return 0
;;
*)
# TODO shouldn't happens
COMPREPLY=( _tc_error_$filtertype )
return 0
;;
esac
fi
fi
fi
# TODO shouldn't happens
COMPREPLY=( _tc_error3 )
return 0
}
# here comes the completion root
_tc() {
local cur object cmd objpos
COMPREPLY=()
cur=`_get_cword`
prev=${COMP_WORDS[COMP_CWORD-1]}
# the OBJECTS
case $prev in
qdisc)
COMPREPLY=( $(compgen -W 'add del replace change link help show' -- $cur ) )
return 0
;;
@(class|filter))
COMPREPLY=( $(compgen -W 'add del replace change show help' -- $cur ))
return 0
;;
action?(s))
_tc-action
return 0
;;
monitor)
return 0
;;
-batch)
_filedir
return 0
;;
#### appends 'dev' if ${prev} is one of the following
#### as compgen -W '...' -S ' dev' doesn't appear to work
#### 'show' case is handled separatly later
@(add|del|replace|change|link))
COMPREPLY=( $(compgen -W 'dev' -- $cur ) )
return 0
;;
# 'dev' keyword is always followed by an interface name
dev)
_available_interfaces
return 0
;;
esac
# we be soon useful
object=$(_tc_get_arg_matching 'qdisc|class|filter|actions?|monitor' 0 5)
objpos=$?
cmd=${COMP_WORDS[$objpos+1]}
# to avoid (possible|futur|imaginary) confusion in the tc ocean
# in case we find some other dashed switches,
# so limit the impact of these tests to the case
# where the OBJECT is not yet defined
# (at the beginning of the command line)
# TODO: look for mutually exclusive option (are they all ?)
if [[ "$cur" == -* ]] && [[ -z $object ]]; then
COMPREPLY=( $( compgen -W '-s -stats -statistics -d -details -r -raw -p -pretty -i -iec -batch -force' -- $cur ) )
return 0
# no case matched until now, we are at the very beginning of the cmdline
# maybe a duplicate of [ -z "$object" ] below
elif [[ $COMP_CWORD -eq 1 ]]; then
COMPREPLY=( $( compgen -W 'qdisc class filter action monitor' -- $cur ) )
return 0
fi
#?here comes the hard stuff, no more $prev to dig around
# no more stones petit Poucet
# but hopefully, we just grabbed :
# the OBJECT
# its position
# and, as we go, ...
# the following command
[ -z "$object" ] && {
# TODO : OBJECT completion according to given switches
# because no OBJECT means a switch
COMPREPLY=( $( compgen -W 'qdisc class filter action monitor' -- $cur ) )
return 0
}
[ -z "$cmd" ] && COMPREPLY=( _tc_error5 ) && return
#?grabs some common option which need an argument
# (they have a priority over the other cases)
_tc_in_cmd parent && ! _tc_has_args parent 1 &&
COMPREPLY=( $(compgen -W "$(tc qdisc show |\
sed 's/^.* .* \([0-9a-f]\{1,4\}\):.*/\1/')" -- $cur ) ) &&
return 0
_tc_in_cmd classid && ! _tc_has_args classid 1 &&
return 0
_tc_in_cmd handle && ! _tc_has_args handle 1 &&
return 0
## factor that stuff here rather than below
if [[ $object =~ qdisc|class ]] && [ $cmd != show ]; then
# argument and kernel variable argument (see _tc-makecomp())
local arg varg excl
local strleft=()
[ $object = qdisc ] && {
arg='handle root ingress parent estimator stab help'
varg='{p,b}fifo{,_fast} htb tbf prio cbq red sfq'
excl='root ingress parent'
}
[ $object = class ] && {
arg='classid parent root help'
varg=$(_tc_getsched sch)
excl='root parent'
}
_tc-makecomp "$arg" "$varg" "$excl"
return 0
fi
## others cases no treated before
case $object in
qdisc)
# show [ dev IF ] [ ingress ]
if [ $cmd = show ]; then
## would have been really nice
## but the really principles of spaces law
#_available_interfaces
# a=${COMPREPLY[*]}
#COMPREPLY=()
#b=${a// /\\ ,}
#COMPREPLY=( $(compgen -S test -W "{dev\ {$b\ },}{ingress,}" -- $cur ) )
## bug even without "space bug" above
#COMPREPLY=( $(compgen -S test -W "{dev_{$b},}{ingress,}" -- $cur ) )
# old solution
if _tc_in_cmd dev; then
! _tc_has_args dev 1 && _available_interfaces ||
COMPREPLY=( $(compgen -W 'ingress' -- $cur ) )
elif ! _tc_in_cmd ingress; then
COMPREPLY=( $(compgen -W 'dev ingress' -- $cur ) )
fi
else
COMPREPLY=( _tc_error1 )
fi
return 0
;;
class)
if [ $cmd = show ]; then
COMPREPLY=( $(compgen -W 'dev root parent' -- $cur ) )
else
COMPREPLY=( _tc_error2 )
fi
return 0
;;
filter)
_tc-filter
return 0
;;
action?(s))
_tc-action
return 0
;;
esac
}
} && complete -F _tc tc
More information about the Bash-completion-devel
mailing list