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
#!/bin/bash ## completion (attempt) for tc(8) language^Wcommand ## Raphaël Droz <gibbo...@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
_______________________________________________ Bash-completion-devel mailing list Bash-completion-devel@lists.alioth.debian.org http://lists.alioth.debian.org/mailman/listinfo/bash-completion-devel