# -*- mode: shell-script; sh-basic-offset: 8; indent-tabs-mode: t -*-
# ex: ts=8 sw=8 noet filetype=sh

# 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
}
