# (C) 2010-2011 magicant

# Completion script for the "sh" built-in command.
# Completion function "completion/sh" is used for the "bash", "dash" and "mksh"
# commands as well. Supports POSIX 2008, bash 4.1, dash 0.5.5.1, mksh R39c.

function completion/sh {

	typeset compoptopts=
	typeset prog="${WORDS[1]##*/}"

	typeset OPTIONS FOPTIONS OOPTIONS ARGOPT PREFIX
	FOPTIONS=( #>#
	"c; execute the first operand as a shell script"
	"i; work in the interactive mode"
	"o:; specify an option"
	) #<#
	OOPTIONS=( #>#
	"a --allexport; export all variables when assigned"
	"b --notify; print job status immediately when done"
	"C --noclobber; disallow >-redirection to overwrite an existing file"
	"e --errexit; exit immediately when a command's exit status is non-zero"
	"f --noglob; disable pathname expansion (globbing)"
	"m --monitor; enable job control"
	"n --noexec; don't execute any commands"
	"u --nounset; disallow expansion of undefined variables"
	"v --verbose; echo commands entered to the shell"
	"x --xtrace; print a command line before executing it"
	"--ignoreeof; don't exit when an end-of-file is entered"
	"--vi; vi-like line-editing"
	) #<#
	case $prog in
	(bash)
		OOPTIONS=("$OOPTIONS" #>#
		"h --hashall; cache full paths of commands in a function when defined"
		) #<#
		;;
	(mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"h --trackall; cache full paths of commands when entered"
		) #<#
		;;
	(*)
		OOPTIONS=("$OOPTIONS" #>#
		"h; cache full paths of commands in a function when defined"
		) #<#
		;;
	esac

	case $prog in
	(bash)
		FOPTIONS=("$FOPTIONS" #>#
		"l --login; work as a login shell"
		) #<#
		;;
	(dash)
		FOPTIONS=("$FOPTIONS" #>#
		"l; work as a login shell"
		) #<#
		;;
	(mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"l --login; work as a login shell"
		) #<#
		;;
	esac
	case $prog in
	(mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"s --stdin; read commands from the standard input"
		) #<#
		;;
	(*)
		FOPTIONS=("$FOPTIONS" #>#
		"s; read commands from the standard input"
		) #<#
		;;
	esac
	case $prog in ([bd]ash|mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"--emacs; emacs-like line-editing"
		) #<#
	esac
	case $prog in (bash|mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"--posix; force strict POSIX conformance"
		"p --privileged; work in the privileged mode"
		"P --physical; make the -P option the default in the cd commands etc."
		) #<#
	esac
	case $prog in (bash)
		FOPTIONS=("$FOPTIONS" #>#
		"--help"
		"--version"
		"--noprofile; don't read profile file"
		"--debugger; enable extended debugging mode"
		"--dump-po-strings; extract translatable strings in po file format"
		"D --dump-strings; extract translatable strings"
		"--noediting; don't use the Readline library"
		"--norc; don't read rc file"
		"--rcfile: --init-file:; specify the rc file"
		"r --restricted; work in the restricted mode"
		"--rpm-requires; print package names required to run the script"
		"O:; specify a shopt option"
		) #<#
		OOPTIONS=("$OOPTIONS" #>#
		"H --histexpand; enable !-expansion on the command line"
		"k --keyword; allow assignments in the middle of command arguments"
		"--pipefail; return last non-zero exit status of commands in a pipe"
		"B --braceexpand; enable brace expansion"
		"E --errtrace; don't reset ERR trap in subshells"
		"T --functrace; don't reset DEBUG and RETURN traps in subshells"
		"t --onecmd; execute one command only"
		"--history; enable command history"
		) #<#
		compoptopts="$compoptopts -e"
	esac
	case $prog in (mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"--braceexpand; enable brace expansion"
		"r --restricted; work in the restricted mode"
		"--bgnice; run background jobs at lower priorities"
		"--gmacs; gmacs-like line-editing"
		"U --utf8-mode; enable UTF-8 support"
		"X --markdirs; append a slash to directory names after pathname expansion"
		"--arc4random; use arc4random for random number generation"
		"--nohup; don't kill running jobs when exiting"
		"--sh; force very strict POSIX conformance"
		"--vi-esccomplete; use the Escape key for completion in the vi mode"
		"--vi-tabcomplete; use the Tab key for completion in the vi mode"
		) #<#
	esac

	OPTIONS=("$FOPTIONS" "$OOPTIONS")

	# convert plus-prefixed options into hyphen-prefixed options
	typeset i=2
	while [ $i -le ${WORDS[#]} ]; do
		case ${WORDS[i]} in
		(-|--)
			break
			;;
		([+-]*o)
			i=$((i+2))
			;;
		([+-]?*)
			i=$((i+1))
			;;
		(*)
			break
			;;
		esac
	done
	WORDS=("${WORDS[1]}" "${WORDS[2,i-1]/#+/-}" "${WORDS[i,-1]}")

	# the same for $TARGETWORD
	typeset SAVETARGETWORD="$TARGETWORD"
	if [ $i -gt ${WORDS[#]} ]; then
		TARGETWORD=${TARGETWORD/#+/-}
	fi

	command -f completion//parseoptions
	case $ARGOPT in
	(-)
		case $prog in
		(set|yash|ksh)
			;;
		(*)
			OPTIONS=("$OOPTIONS")
			command -f completion/set::removelongopts
			OPTIONS=("$FOPTIONS" "$OPTIONS")
			;;
		esac
		case $SAVETARGETWORD in
		(-*)
			command -f completion//completeoptions $compoptopts
			;;
		(+*)
			TARGETWORD=$SAVETARGETWORD
			command -f completion/set::option short
			;;
		esac
		;;
	(o)
		PREFIX=${SAVETARGETWORD%"${TARGETWORD#"$PREFIX"}"}
		TARGETWORD=$SAVETARGETWORD
		OPTIONS=("$OOPTIONS")
		command -f completion/set::option long
		;;
	(O)
		typeset shopt value
		while read -r shopt value; do
			complete -P "$PREFIX" -- "$shopt"
		done 2>/dev/null <(bash -O </dev/null)
		;;
	(*)
		complete -P "$PREFIX" -f
		;;
	esac

}

function completion/set::removelongopts {
	typeset IFS="
"
	OPTIONS=($(printf '%s\n' "$OPTIONS" | sed 's/--[^;[:blank:]]*//g'))
} 2>/dev/null

function completion/set::option {

	if [ "${1-}" != long ]; then
		typeset single=true
	else
		typeset single=false
	fi

	typeset opts desc
	typeset generated=false
	for opts in "$OPTIONS"; do

		# get description
		case $opts in
		(*\;*)
			desc=${opts#*;}
			while true; do  # trim surrounding spaces
				case $desc in
				([[:space:]]*) desc=${desc#[[:space:]]} ;;
				(*[[:space:]]) desc=${desc%[[:space:]]} ;;
				(*)            break ;;
				esac
			done
			;;
		(*)
			desc=
			;;
		esac

		if $single; then
			# generate single-character option
			for opt in ${opts%%;*}; do
				case $opt in ([[:alnum:]]|[[:alnum:]]:)
					complete -O -P "$TARGETWORD" \
					${desc:+-D "$desc"} "${opt%:}" &&
					generated=true
					break
				esac
			done
		else
			# generate candidate for first match
			for opt in ${opts%%;*}; do
				case $opt in (--*)
					complete -P "$PREFIX" \
						${desc:+-D "$desc"} \
						-- "${opt#--}" &&
					generated=true &&
					break
				esac
			done
		fi

	done

	$generated

}


# vim: set ft=sh ts=8 sts=8 sw=8 noet:
