# cvs(1) completion

have cvs && {
set_prefix()
{
    [ -z ${prefix:-} ] || prefix=${cur%/*}/
    [ -r ${prefix:-}CVS/Entries ] || prefix=""
}

get_entries()
{
    local IFS=$'\n'
    [ -r ${prefix:-}CVS/Entries ] && \
    entries=$(cut -d/ -f2 -s ${prefix:-}CVS/Entries)
}

get_modules()
{
    if [ -n "$prefix" ]; then
        COMPREPLY=( $( command ls -d ${cvsroot}/${prefix}/!(CVSROOT) ) )
    else
        COMPREPLY=( $( command ls -d ${cvsroot}/!(CVSROOT) ) )
    fi
}

_cvs()
{
    local cur count mode i cvsroot cvsroots pwd
    local -a flags miss files entries changed newremoved

    COMPREPLY=()
    cur=`_get_cword`

    count=0
    for i in "${COMP_WORDS[@]}"; do
        [ $count -eq $COMP_CWORD ] && break
        # Last parameter was the CVSROOT, now go back to mode selection
        if [ "${COMP_WORDS[((count))]}" == "$cvsroot" -a "$mode" == "cvsroot" ]; then
            mode=""
        fi
        if [ -z "$mode" ]; then
            case $i in
            -d)
                mode=cvsroot
                cvsroot=${COMP_WORDS[((count+1))]}
                ;;
            @(ad?(d)|new))
                mode=add
                ;;
            @(adm?(in)|rcs))
                mode=admin
                ;;
            ann?(notate))
                mode=annotate
                ;;
            @(checkout|co|get))
                mode=checkout
                ;;
            @(com?(mit)|ci))
                mode=commit
                ;;
            di?(f?(f)))
                mode=diff
                ;;
            ex?(p?(ort)))
                mode=export
                ;;
            ?(un)edit)
                mode=$i
                ;;
            hi?(s?(tory)))
                mode=history
                ;;
            im?(p?(ort)))
                mode=import
                ;;
            re?(l?(ease)))
                mode=release
                ;;
            ?(r)log)
                mode=log
                ;;
            @(rdiff|patch))
                mode=rdiff
                ;;
            @(remove|rm|delete))
                mode=remove
                ;;
            @(rtag|rfreeze))
                mode=rtag
                ;;
            st?(at?(us)))
                mode=status
                ;;
            @(tag|freeze))
                mode=tag
                ;;
            up?(d?(ate)))
                mode=update
                ;;
            *)
                ;;
            esac
        elif [[ "$i" = -* ]]; then
            flags=( "${flags[@]}" $i )
        fi
        count=$((++count))
    done

    case "$mode" in
        add)
            if [[ "$cur" != -* ]]; then
                set_prefix
                if [ $COMP_CWORD -gt 1 -a -r ${prefix:-}CVS/Entries ]; then
                    get_entries
                    [ -z "$cur" ] && \
                    files=$( command ls -Ad !(CVS) ) || \
                    files=$( command ls -d ${cur}* 2>/dev/null )
                    for i in "${entries[@]}"; do
                        files=( ${files[@]/#$i//} )
                    done
                    COMPREPLY=( $( compgen -X '*~' -W '${files[@]}' -- $cur ) )
                fi
            else
                COMPREPLY=( $( compgen -W '-k -m' -- "$cur" ) )
            fi
            ;;
        admin)
            if [[ "$cur" = -* ]]; then
                COMPREPLY=( $( compgen -W '-i -a -A -e -b -c -k -l -u -L -U -m -M \
                    -n -N -o -q -I -s -t -t- -T -V -x -z' -- $cur ) )
            fi
            ;;
        annotate)
            if [[ "$cur" = -* ]]; then
                COMPREPLY=( $( compgen -W '-D -F -f -l -R -r' -- "$cur" ) )
            else
                get_entries
                COMPREPLY=( $( compgen -W '${entries[@]}' -- "$cur" ) )
            fi
            ;;
        checkout)
            if [[ "$cur" != -* ]]; then
                [ -z "$cvsroot" ] && cvsroot=$CVSROOT
                COMPREPLY=( $( cvs -d "$cvsroot" co -c 2> /dev/null | \
                    awk '{print $1}' ) )
                COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- "$cur" ) )
            else
                COMPREPLY=( $( compgen -W '-A -N -P -R -c -f -l -n -p \
                    -s -r -D -d -k -j' -- "$cur" ) )
            fi
            ;;
        commit)
            set_prefix

            if [[ "$cur" != -* ]] && [ -r ${prefix:-}CVS/Entries ]; then
                # if $COMP_CVS_REMOTE is not null, 'cvs commit' will
                # complete on remotely checked-out files (requires
                # passwordless access to the remote repository
                if [ -n "${COMP_CVS_REMOTE:-}" ]; then
                    # this is the least computationally intensive
                # way found so far, but other changes
                # (something other than changed/removed/new)
                # may be missing
                changed=( $( cvs -q diff --brief 2>&1 | \
                sed -ne 's/^Files [^ ]* and \([^ ]*\) differ$/\1/p' ) )
                newremoved=( $( cvs -q diff --brief 2>&1 | \
                sed -ne 's/^cvs diff: \([^ ]*\) .*, no comparison available$/\1/p' ) )
                COMPREPLY=( $( compgen -W '${changed[@]:-} \
                           ${newremoved[@]:-}' -- "$cur" ) )
                else
                    COMPREPLY=( $(compgen $default -- "$cur") )
                fi
            else
                COMPREPLY=( $( compgen -W '-n -R -l -f -F -m -r' -- $cur ) )
            fi
            ;;
        cvsroot)
            if [ -r ~/.cvspass ]; then
                # Ugly escaping because of bash treating ':' specially
                cvsroots=$( sed 's/^[^ ]* //; s/:/\\:/g' ~/.cvspass )
                COMPREPLY=( $( compgen -W '$cvsroots' -- "$cur" ) )
            fi
            ;;
        export)
            if [[ "$cur" != -* ]]; then
                [ -z "$cvsroot" ] && cvsroot=$CVSROOT
                COMPREPLY=( $( cvs -d "$cvsroot" co -c | awk '{print $1}' ) )
                COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- "$cur" ) )
            else
                COMPREPLY=( $( compgen -W '-N -f -l -R -n -r -D -d -k' \
                    -- "$cur" ) )
            fi
            ;;
        diff)
            if [[ "$cur" == -* ]]; then
                _longopt diff
            else
                get_entries
                COMPREPLY=( $( compgen -W '${entries[@]:-}' -- "$cur" ) )
            fi
            ;;
        remove)
            if [[ "$cur" != -* ]]; then
                set_prefix
                if [ $COMP_CWORD -gt 1 -a -r ${prefix:-}CVS/Entries ]; then
                    get_entries
                    # find out what files are missing
                    for i in "${entries[@]}"; do
                        [ ! -r "$i" ] && miss=( "${miss[@]}" $i )
                    done
                    COMPREPLY=( $(compgen -W '${miss[@]:-}' -- "$cur") )
                fi
            else
                COMPREPLY=( $( compgen -W '-f -l -R' -- "$cur" ) )
            fi
            ;;
        import)
            if [[ "$cur" != -* ]]; then
                # starts with same algorithm as checkout
                [ -z "$cvsroot" ] && cvsroot=$CVSROOT
                prefix=${cur%/*}
                if [ -r ${cvsroot}/${prefix} ]; then
                    get_modules
                    COMPREPLY=( ${COMPREPLY[@]#$cvsroot} )
                    COMPREPLY=( ${COMPREPLY[@]#\/} )
                fi
                pwd=$( pwd )
                pwd=${pwd##*/}
                COMPREPLY=( $( compgen -W '${COMPREPLY[@]} $pwd' -- $cur ) )
            else
                COMPREPLY=( $( compgen -W '-d -k -I -b -m -W' -- "$cur" ))
            fi
            ;;
        update)
            if [[ "$cur" = -* ]]; then
                COMPREPLY=( $( compgen -W '-A -P -C -d -f -l -R -p \
                    -k -r -D -j -I -W' -- "$cur" ) )
            fi
            ;;
        "")
            COMPREPLY=( $( compgen -W 'add admin annotate checkout ci co \
                commit diff delete edit export \
                freeze get history import log new \
                patch rcs rdiff release remove \
                rfreeze rlog rm rtag stat status \
                tag unedit up update -H -Q -q -b \
                -d -e -f -l -n -t -r -v -w -x -z \
                --help --version' -- "$cur" ) )
            ;;
        *)
            ;;
    esac

    return 0
}
complete -F _cvs $default cvs
}

# Local variables:
# mode: shell-script
# sh-basic-offset: 4
# sh-indent-comment: t
# indent-tabs-mode: nil
# End:
# ex: ts=4 sw=4 et filetype=sh
