[Bash-completion-devel] squashing bug #311844 (completion for /sbin/tc)

Raphaël Droz raphael.droz+floss at gmail.com
Sun Nov 20 17:55:47 UTC 2011


I'm back with this one:
http://alioth.debian.org/tracker/index.php?func=detail&aid=311844&group_id=100114&atid=413095
http://www.mail-archive.com/bash-completion-devel@lists.alioth.debian.org/msg01042.html

I reworked the whole so that it's far more readable/reviewable, even
with its 900 LOC.

I've kept some _tc_error which indicate unimplemented completion
paths but, if removed, anyone using tc should appreciate this completion
in its current shape.

The the most missing part is about the "action" object where almost all
has still to be done.

but other than that it is fairly usable, advises welcomed

[ files attached, tc-* helpers living into helpers/ ]

Git:
http://gitorious.org/drzraf/bash-completion/blobs/master/completions/tc
http://gitorious.org/drzraf/bash-completion/trees/master/helpers


Raph
-------------- next part --------------
# tc(8) langu^Wcommand completion                             -*- shell-script -*-
# Rapha?l Droz <raphael.droz+floss at gmail.com>

_tc() {

    . ${BASH_SOURCE[0]%/*}/../helpers/tc-helpers
    . ${BASH_SOURCE[0]%/*}/../helpers/tc-qdisc
    . ${BASH_SOURCE[0]%/*}/../helpers/tc-class
    . ${BASH_SOURCE[0]%/*}/../helpers/tc-filter
    . ${BASH_SOURCE[0]%/*}/../helpers/tc-action

    local cur prev words cword
    _init_completion || return

    local object cmd objpos QDISC_KIND CLASS_QDISC_KIND i

    QDISC_KIND="{p,b}fifo{,_fast} htb tbf prio cbq red sfq stab hfsc netem drr gred"
    CLASS_QDISC_KIND='cbq dsmark drr hfsc htb'
    # TODO: find a stable way to identify both categories, like
    # CLASS_QDISC_KIND="$(_tc_getsched sch)"

    # if i=1 after esac; then we'll have to deal with more complex things
    let i=0
    COMPREPLY=()
    case $prev in
	qdisc)
	    COMPREPLY+=( link )
            ;;&
	qdisc|class|filter)
	    COMPREPLY+=( add del replace change show list ls help )
            COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- $cur ) )
	    ;;
	action|actions)
	    _tc-action
	    ;;
	monitor)
	    ;;
	-batch|-b)
	    _filedir
	    ;;
	rate|bandwidth|ceil|weight|peakrate)
	    if [[ -n $cur ]]; then
		[[ ${cur:${#cur}-1:1} =~ k|K|m|M|g|G|b|B ]] &&
		COMPREPLY=( $(compgen -W "${cur}{bit,ps}" -- $cur ) ) ||
		COMPREPLY=( $(compgen -W "${cur}{b,k,m,g}{bit,bps}" -- $cur ) )
	    fi
	    ;;

	# TODO : max,min,limit also ?
	burst|avpkt|mpu|cell|quantum|mtu|limit|max|min)
	    if [[ -n $cur ]]; then
		[[ ${cur:${#cur}-1:1} =~ k|K|m|M|g|G|b ]] &&
		COMPREPLY=( $(compgen -W "${cur}{bit,}" -- $cur ) ) ||
		COMPREPLY=( $(compgen -W "${cur}{k,m,g,}{bit,}" -- $cur ) )
		[[ ${cur:${#cur}-1:1} = a ]] && COMPREPLY=( )
	    fi
	    ;;
	linklayer)
	    # TODO
	    COMPREPLY=( atm )
	    ;;
	delay)
	    # TODO
	    COMPREPLY=( $(compgen -W "${cur}..." -- $cur ) )
	    ;;

	# TODO1: unsure this always applies
	flowid|classid)
	    COMPREPLY=( $(compgen -W "$(_tc-handle-list class)" -- $cur ) )
	    ;;

	# TODO2: unsure this always apply
	parent)
	    COMPREPLY=( $(compgen -W "$(_tc-handle-list parent)" -- $cur ) )
	    ;;

	# 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|ls|list|show)
	    COMPREPLY=( $(compgen -W 'dev' -- $cur ) )
	    ;;
	# 'dev' keyword is always followed by an interface name
	dev)
	    _available_interfaces
	    ;;
        *)
            i=1
            ;;
    esac
    [[ $i -eq 0 ]] && return 0

    # may be useful soon
    object=$(_tc_get_arg_matching 'qdisc|class|filter|actions?|monitor' 1 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 [[ $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)

    # see TODO2
    #    _tc_in_cmd parent && ! _tc_has_args 'parent' 1 &&
    #    	COMPREPLY=( $(compgen -W "$(_tc-handle-list qdisc)" -- $cur ) ) &&
    #	return 0

    # see TODO1
    #    _tc_in_cmd classid && ! _tc_has_args 'classid' 1 &&
    #    	COMPREPLY=( $(compgen -W "$(_tc-handle-list class)" -- $cur ) ) &&
    #	return 0

    _tc_in_cmd handle && ! _tc_has_args 'handle' 1 && return 0

    # we factor that stuff here rather than below
    # it about handling all the keywords after the qdisc|class object
    # but before a QDISC has been specified
    if [[ $object =~ qdisc|class && $cmd != show ]]; then
	# argument and kernel variable argument (see _tc-makecomp())
	local arg qdisc excl
	local strleft=()
	[[ $object = qdisc ]] && {
	    arg='handle root ingress parent estimator stab help'
	    qdisc=$QDISC_KIND
	    excl='root ingress parent'
	}
	[[ $object = class ]] && {
	    arg='classid parent root help'
	    qdisc=$CLASS_QDISC_KIND
	    excl='root parent'
	}

        # if there is already a DQISC in the cmd, let's
        # forget about $arg option and continue,
        # they should have been
        # specified before.
	if ! _tc_in_cmd "$qdisc"; then
	    _tc-makecomp "$arg" "$qdisc" "$excl"
	    return 0
	fi
    fi

    # others cases not treated above
    case $object in
	# TODO: the 'show' command is mostly common
        # for 'qdisc' and 'filter' objects
	qdisc)
	    _tc-qdisc "$cmd"
	    ;;
	class)
	    _tc-class "$cmd"
	    ;;
	filter)
	    _tc-filter "$cmd"
	    ;;
	action|actions)
	    _tc-action "$cmd"
	    ;;
    esac

    return 0
} &&
complete -F _tc +o nospace tc
-------------- next part --------------
# tc(8) helpers                             -*- shell-script -*-
# Rapha?l Droz <raphael.droz+floss at gmail.com>

_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 <= $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 <= $((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 has been appended

            # It 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]}" > /dev/stderr
            # echo -e "\nCOMP_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 the command line word from COMP_WORDS which matches
# the regexp in $1, optionally between "start" and "end" position
# returns the position or 255 if not found (no one should enter
# a 255 words long tc command line)
_tc_get_arg_matching() {
    [[ -z $1 ]] && return 255
    local let i=1 let limit=$COMP_CWORD let start=1
    local aarg a
    aarg=$1 && shift
    [[ -n $1 ]] && { let start=$1; shift; }
    [[ -n $1 ]] && { let limit=$1; shift; }
    (( $limit > $COMP_CWORD )) && limit=$COMP_CWORD
    (( $start < 1 )) && start=1
    (( $start > $limit )) && {
	a=$limit
	limit=$start
	start=$a
    }

    let i=$start
    while (( $i < $limit )); do
	if [[ ${COMP_WORDS[$i]} =~ $aarg ]]; then
	    echo ${COMP_WORDS[$i]}
	    return $i
        fi
	((i++))
    done
    return 255
}

# Returns 0 if the at least one of the words
# passed in the first argument is found in the cmdline.
# If a second argument "i" is given, the search starts
# from ${COMP_WORDS[$i]} instead of 1
_tc_in_cmd() {
    [[ -z $1 ]] && return 1
    local let i=0 j=0
    local aarg=($(compgen -W "$1")) && shift
    [[ -n $1 ]] && i=$1 && shift

    while [ $((i++)) -lt $((COMP_CWORD-1)) ]; do
        while (( $j < ${#aarg[@]} )); do
	    [[ ${COMP_WORDS[$i]} = ${aarg[$j]} ]] && {
                #echo "${aarg[$j]} match ${COMP_WORDS[$i]} ($i)" > /dev/stderr
	        return 0
	    }
            ((j++))
        done
        j=0
    done
    return 1
}

_tc_dumpcmd() {
    local let i=0
    while [[ -n ${COMP_WORDS[$i]} ]]; do
	echo ${COMP_WORDS[$((i++))]}
    done
    return
}
_tc_get-device() {
    local let i=0
    _tc_dumpcmd | \
    while read f; do
	[[ $i -eq 1 ]] && echo $f && break
	[[ $f = dev ]] && i=1
    done
    [[ $i -eq 1 ]]
}
_tc_get-command() {
    local let i=0
    _tc_dumpcmd|while read f; do
	[[ $i -eq 1 && $f =~ add|change|del(ete)?|replace|show|get|list|ls|flush ]] && \
            echo $f && break
        [[ $f =~ qdisc|class|filter|actions? ]] && i=1
    done
    (( $i == 1 ))
}

# list available kernel schedulers|actions|filters
_tc_getsched() {
    echo $(ls /lib/modules/$(uname -r)/kernel/net/sched/$1_*.ko 2>/dev/null| \
        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 strleft=()
    arg="$1" && shift
    varg="$1" && shift
    excl="$1" && shift
    #echo -e "\n1=$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+=($i)
    done
    COMPREPLY=( $(compgen -W "${strleft[*]} $varg" -- $cur ) )
}

# create a list of a given OBJECT for a given INTERFACE
# if no INTERFACE is given, it's guess with _tc_get-device
# if "all" is given as the INTERFACE, all interface are used
_tc-handle-list() {
    local obj=$1 && shift
    # default (no 2nd parameter, default to the current used interface)
    local aif="dev $(_tc_get-device)"
    # sadly, _known_interface should be usable outside the COMPREPLY :|
    if [[ -n $1 ]]; then
	aif="dev $1"
	[ $aid = all ] && unset aif
    fi

    local tc
    which tc &> /dev/null || tc=/sbin/tc	    # not root ?
    if [[ $obj =~ qdisc|class|filter ]]; then
	[[ $obj = qdisc ]] && $tc $obj show $aif|sed 's/^.* .* \([0-9a-f]\{1,4\}:\).*/\1/' 2>/dev/null
	[[ $obj = class ]] && $tc $obj show $aif|sed 's/^.* .* \([0-9a-f]\{1,4\}:.\).*/\1/' 2>/dev/null
    fi
    return 0
}
-------------- next part --------------
# tc(8) qdisc helpers                             -*- shell-script -*-
# Rapha?l Droz <raphael.droz+floss at gmail.com>

_tc-qdisc() {
    local cmd="$1" && shift

    # show [ dev IF ] [ ingress ]
    case $cmd in
        show)
            ## TODO
            ## would have been really nice
	    ## but the real 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=(ingress)
	    elif ! _tc_in_cmd ingress; then
	        COMPREPLY=(dev ingress)
	    fi
            ;;
        # here we expect that the QDISC is already specified in
        # the command line
        *)
            _tc-qdisc-qdisc && return 0

	    case $prev in
                # may want to move this one in _tc()
                # tc qdisc (add|change|replace|link) dev DEV stab <TAB>
	        stab)
                    COMPREPLY=(mtu tsize mpu overhead linklayer)
                    ;;

                *)
                    # very specific cases

                    # tc qdisc (add|change|replace|link) dev DEV netmem distribution <TAB>
                    if _tc_in_cmd netmem && [[ $prev = distribution ]]; then
                        COMPREPLY=(uniform normal pareto paretonormal)
                    else
	                COMPREPLY=( _tc_error1 )
                        return 0;
                    fi
                    ;;
            esac
            ;;
    esac

    COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- $cur ) )
    return 0
}

# command line like:
# tc qdisc (add|change|replace|link) dev DEV QDISC <TAB>
_tc-qdisc-qdisc() {
    # accepted values from $(ls -1 iproute2-2.6.38/src/tc/q_*.c) are:
    # atm cbq drr dsmark fifo gred hfsc htb ingress multiq netem prio red rr sfq tbf
    case $prev in
        atm|drr|dsmark|multiq|rr)
            return 0
            ;;
        # TODO {p,b}fifo_fast ?
	bfifo|pfifo)
	    COMPREPLY=(limit)
	    ;;
	cbq)
            # TODO : cell mpu ewma
	    COMPREPLY=(bandwidth avpkt)
	    ;;
	gred)
            # not pretty
	    COMPREPLY=(setup DP limit min max avpkt burst probability bandwidth prio)
	    ;;
	hfsc)
	    COMPREPLY=(default)
	    ;;
	htb)
	    COMPREPLY=(default r2q debug)
	    ;;
	netem)
	    COMPREPLY=(limit delay distribution drop corrupt duplicate reorder gap)
	    ;;
	prio)
	    COMPREPLY=(bands priomap mutiqueue)
	    ;;
	red)
	    COMPREPLY=(limit min max avpkt burst probability bandwidth)
	    ;;
	sfq)
	    COMPREPLY=(limit perturb quantum)
	    ;;
	tbf)
	    COMPREPLY=(limit burst rate mtu peakrate latency overhead linklayer)
	    ;;
        # when the previous parameter is not a valid QDISC,
        # we fallback on _tc-qdisc()
	*)
            return 1
	    ;;
    esac

    COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- $cur ) )
    return 0
}
-------------- next part --------------
# tc(8) class helpers                             -*- shell-script -*-
# Rapha?l Droz <raphael.droz+floss at gmail.com>

_tc-class() {
    local cmd="$1" && shift

    # TODO: redundant with _tc()
    local CLASS_QDISC_KIND='cbq dsmark drr hfsc htb'

    # show [ dev IF ] [ ingress ]
    case $cmd in
        show)
            COMPREPLY=( $(compgen -W 'dev root parent' -- $cur ) )
            return 0
            ;;
    esac

    ! _tc_in_cmd "$CLASS_QDISC_KIND" && {
	COMPREPLY=( _tc_error2 )
        return 0
    }

    local totest ret
    # maybe we have something the filter can handle, give $cur to it
    # even if not the previous word
    totest=$(_tc_get_arg_matching "${CLASS_QDISC_KIND// /|}")
    ret=$?
    # otherwise let's test and throw an error
    [ $ret -eq 255 ] && totest=$prev
    case $totest in
	cbq)
	    _tc-class-cbq
	    return 0
	    ;;
	dnmask)
	    COMPREPLY=(mask value)
	    ;;
	drr)
	    COMPREPLY=(quantum)
	    ;;
	hfsc)
            _tc-class-cbq
            return 0
	    ;;
	htb)
	    _tc-class-htb
	    return 0
	    ;;
	*)
	    COMPREPLY=( tc_error6 )
	    return 0
	    ;;
    esac

    COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- $cur ) )
    return 0
}


_tc-class-cbq() {
    case $prev in
	cbq)
            # TODO: not really correct
	    COMPREPLY=( $(compgen -W 'bandwidth rate maxburst' -- $cur ) )
	    ;;
    esac
    return 0
}

_tc-class-hfsc() {
    case $prev in
	rt|ls|sc|ul)
            # TODO
            #COMPREPLY=( $( compgen -W '{1..9}' -- "$cur" ) )
            ;;
        *)
	    COMPREPLY=( $(compgen -W 'rt ls sc ul' -- $cur ) )
	    ;;
    esac
    return 0
}

_tc-class-htb() {
    case $prev in
	htb)
	    COMPREPLY=(rate burst mpu overhead prio
                       slot pslot ceil cburst mtu quantum)
	    ;;
	burst|mpu|overhead|prio|slot|pslot|ceil|cburst|mtu|quantum)
            return 0
	    ;;
	*)
	    COMPREPLY=(burst mpu overhead prio slot pslot
                       ceil cburst mtu quantum)
	    ;;
    esac

    COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- $cur ) )
    return 0
}
-------------- next part --------------
# tc(8) filter helpers                             -*- shell-script -*-
# Rapha?l Droz <raphael.droz+floss at gmail.com>

# filtering is a bit more complex than qdisc and class
_tc-filter() {
    local cmd="$1" && shift

    if [[ $cmd = show ]]; then
        # old solution, see qdisc show in _tc-qdisc()
        #TODO : use _tc_makecomp
        #TODO : replace available_interface with those
        # which contain a filter
	if _tc_in_cmd dev; then
	    ! _tc_has_args 'dev' 1 && _available_interfaces || \
	        COMPREPLY=( $(compgen -W 'root parent' -- $cur ) )
	else
	    COMPREPLY=( $(compgen -W 'dev' -- $cur ) )
	fi
        return 0
    fi

    # WARN: this list depends on what has been compiled
    # TODO: handle kernel builtin modules ?
    local cls=$(_tc_getsched cls)
    [[ -z ${cls[@]} ]] && \
        cls="api basic cgroup flow fw route rsvp rsvp6 tcindex u32"

    local filtertype filterpos
    local PROTO='ip{v4,v6,} irda {r,}arp wan_ppp ppp{ses,disc}'

    # TODO : parent is also accepted, before or after protocol
    # need to look of the mutual exclusions
    # TODO : prio is also accepted (at least after protocol)
    if ! _tc_in_cmd protocol; then
        COMPREPLY=(protocol)
	_tc_in_cmd pref && COMPREPLY+=(pref)
        COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- $cur ) )
	return 0
    fi
    if ! _tc_has_args protocol 1; then
	COMPREPLY=( $(compgen -W "$PROTO" -- $cur ) )
	return 0
    fi

    if ! _tc_in_cmd "$cls"; then
	_tc_in_cmd estimator && ! _tc_has_args estimator 2 && return 0
	_tc-makecomp 'estimator root classid handle help pref parent' "$cls" 'root parent classid'
	return 0
    fi

    filtertype=$(_tc_get_arg_matching ${cls// /|})
    filterpos=$?
    _tc-filter-filter $filtertype $filterpos && return 0

    # TODO shouldn't happens
    COMPREPLY=( _tc_error_filter_$filtertype )
    return 0
}

_tc-filter-filter() {
    local filtertype=$1 && shift
    local let filterpos=$1 && shift

    case $filtertype in
	basic)
            # giving up here : complexity 1, developper 0
	    (( $COMP_CWORD > $((filterpos+1)) )) && return 0
	    COMPREPLY=( $(compgen -W 'match police action classid' -- $cur ) )
	    ;;
	flow)
	    _tc-filter-flow
	    ;;
	u32)
	    _tc-filter-u32
	    ;;
	fw)
	    _tc-filter-fw
	    ;;
	route)
	    _tc-filter-route
	    ;;
	rsvp|rsvp6)
	    _tc-filter-rsvp
	    ;;
	tcindex)
	    _tc-filter-tcindex
	    ;;
	*)
            return 1
	    ;;
    esac
    return 0
}

_tc-filter-u32-match() {
    echo TODO
}

_tc-filter-u32() {
    local sample='ip ip6 udp tcp icmp u{32,16,8} mark'
    local u32opts ipopts tcpopts
    case $prev in
	u32)
            #TODO: the following is only valid if the word before
            # $prev is *NOT* 'match'
	    COMPREPLY=( $(compgen -W 'match link classid police offset \
				      ht hashkey sample divisor flowid divisor help' -- $cur ) )
	    ;;
	match)
	    COMPREPLY=( $(compgen -W "$sample" -- $cur ) )
	    ;;
        #look TODO1
        #@(flowid|classid))
        #	COMPREPLY=( $(compgen -W "$(_tc-handle-list class)" -- $cur ) )
        #	;;
	police)
	    COMPREPLY=( $(compgen -W 'rate' -- $cur ) )
	    ;;
	rate)
	    COMPREPLY=( )
	    ;;
	burst)
	    COMPREPLY=( )
	    ;;
	link)
	    COMPREPLY=( )
	    ;;
	offset)
	    COMPREPLY=( )
	    ;;
	ht)
	    COMPREPLY=( )
	    ;;
	hashkey)
	    COMPREPLY=( )
	    ;;
	sample)
	    COMPREPLY=( $(compgen -W "$sample" -- $cur ) )
	    ;;
        *)
            _tc-filter-u32-deep
            ;;
    esac

    return 0
}

_tc-filter-u32-deep() {
    _tc_get_arg_matching u32 &> /dev/null
    local filterpos=$?
    local u32opts=(match link classid police offset ht
        	   hashkey sample divisor flowid divisor)
    # TODO: make the lists (ip,tcp) more complete
    if [ "${COMP_WORDS[$filterpos+1]}" = match ]; then
	if [ "${COMP_WORDS[$filterpos+2]}" = ip ]; then
            # TODO: no put bulk $ipopts but check for duplicates
	    ipopts="tos port src dst {s,d}port protocol"
	    if [[ ! $prev =~ ${ipopts// /|} ]]; then
		COMPREPLY=( $(compgen -W "$iptops ${u32opts[@]}" -- $cur ) )
		return 0
	    else
		case $prev in
		    dst)
			_known_hosts
			return 0
			;;
		esac
	    fi
	elif [ "${COMP_WORDS[$filterpos+2]}" = tcp ]; then
            # TODO: no put bulk $ipopts but check for duplicates
	    tcpopts="dst"
	    if [[ ! $prev =~ ${tcpopts// /|} ]]; then
		COMPREPLY=( $(compgen -W "$tcptops ${u32opts[@]}" -- $cur ) )
		return 0
	    else
		case $prev in
		    dst)
			_known_hosts
			return 0
			;;
		esac
	    fi
	else
	    return 0
	fi
    fi
    # match $sample :: port tos ...
    # police & rate & burst :: mtu peakrate avrate overhead linklayer action {pipe,ok,reclassify,drop,continue}
    return 0;
}

_tc-filter-flow() {
    local key='src dst proto proto-src proto-dst iif priority \
	       mark nfct nfct-src nfct-dst nfct-proto-src \
	       nfct-proto-dst rt-classid sk-uid sk-gi'
    case $prev in
	flow)
	    COMPREPLY=( $(compgen -W 'map hash' -- $cur ) )
	    ;;
	map)
	    COMPREPLY=( $(compgen -W 'key' -- $cur ) )
	    ;;
	hash)
	    COMPREPLY=( $(compgen -W 'keys' -- $cur ) )
	    ;;
	key)
	    COMPREPLY=( $(compgen -W "$key" -- $cur ) )
	    ;;
    esac
    # last arg in "$key" :: and or xor rshift addend
    # and
    # divisor baseclass match police action
    return 0
}

_tc-filter-fw() {
    case $prev in
	fw)
	    COMPREPLY=( $(compgen -W 'classid police help' -- $cur ) )
	    ;;
	classid)
	    COMPREPLY=( )
	    ;;
	police)
	    COMPREPLY=( )
	    ;;
    esac
    return 0
}

_tc-filter-route() {
    case $prev in
	route)
	    COMPREPLY=( $(compgen -W 'from{,if} to flowid police' -- $cur ) )
	    ;;
	fromif)
	    COMPREPLY=( )
	    ;;
	from|to)
	    COMPREPLY=( )
	    ;;
	flowid)
	    COMPREPLY=( )
	    ;;
	police)
	    COMPREPLY=( )
	    ;;
    esac
    return 0
}

_tc-filter-rsvp() {
    case $prev in
	rsvp|rsvp6)
	    COMPREPLY=( $(compgen -W 'ipproto' -- $cur ) )
	    ;;
	ipproto)
	    COMPREPLY=( )
	    ;;
	session)
	    _know_hosts
	    ;;
    esac
    return 0
    ## "$PROTO" :: session | flowlabel | spi/ah | spi/esp | u{8,16,32}
    ## _known_host :: port (COMPREPLY=( ))
    # TODO : sender, classid, police tunnel{id,}
}

_tc-filter-tcindex() {
    case $prev in
	tcindex)
	    COMPREPLY=( $(compgen -W 'hash mask shift pass_on fall_through \
				      classid police' -- $cur ) )
	    ;;
	hash|mask|shift)
	    COMPREPLY=( )
	    ;;
	classid)
	    COMPREPLY=( )
	    ;;
	police)
	    COMPREPLY=( )
	    ;;
    esac
    return 0
}
-------------- next part --------------
# tc(8) action helpers                             -*- shell-script -*-
# Rapha?l Droz <raphael.droz+floss at gmail.com>

# short dream about completing tc "action" object
_tc-action() {
    local cmd="$1" && shift
    local ACTSPECOP

    if [[ -z $cmd ]]; then
	COMPREPLY=( $(compgen -W "add change replace get delete
				  ls list flush action help" -- $cur ) )
    else
	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
}


More information about the Bash-completion-devel mailing list