# (C) 2010 magicant

# Completion script for the "set" built-in command.
# Completion function "completion/set" is used for the "bash" "dash", "ksh",
# "mksh", "sh", "yash" commands as well. Supports POSIX 2008, bash 4.1,
# dash 0.5.5.1, AT&T ksh 20100621, mksh R39c, yash 2.25.

function completion/set {

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

	typeset OPTIONS FOPTIONS OOPTIONS ARGOPT PREFIX
	FOPTIONS=( #>#
	"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
	(set|yash)
		OOPTIONS=("$OOPTIONS" #>#
		"h --hashondef; cache full paths of commands in a function when defined"
		) #<#
		;;
	(bash)
		OOPTIONS=("$OOPTIONS" #>#
		"h --hashall; cache full paths of commands in a function when defined"
		) #<#
		;;
	(ksh|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 (*sh)
		FOPTIONS=("$FOPTIONS" #>#
		"c; execute the first operand as a shell script"
		) #<#
		case $prog in
		(ksh|yash)
			FOPTIONS=("$FOPTIONS" #>#
			"i --interactive; work in the interactive mode"
			) #<#
			;;
		(*)
			FOPTIONS=("$FOPTIONS" #>#
			"i; work in the interactive mode"
			) #<#
			;;
		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
	esac
	case $prog in (set|[bdy]ash|ksh|mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"--emacs; emacs-like line-editing"
		) #<#
		case $prog in
		([by]ash|ksh)
			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
	esac
	case $prog in (set|[by]ash|ksh)
		FOPTIONS=("$FOPTIONS" #>#
		"--help"
		) #<#
		case $prog in ([by]ash|ksh)
			FOPTIONS=("$FOPTIONS" #>#
			"--version"
			) #<#
		esac
	esac
	case $prog in ([by]ash|mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"--posix; force strict POSIX conformance"
		) #<#
	esac
	case $prog in ([by]ash)
		FOPTIONS=("$FOPTIONS" #>#
		"--noprofile; don't read profile file"
		) #<#
	esac
	case $prog in (set|yash|ksh|mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"--braceexpand; enable brace expansion"
		) #<#
	esac
	case $prog in (set|yash|ksh)
		OOPTIONS=("$OOPTIONS" #>#
		"--markdirs; append a slash to directory names after pathname expansion"
		) #<#
	esac
	case $prog in (set|yash)
		OOPTIONS=("$OOPTIONS" #>#
		"--notifyle; print job status immediately when done while line-editing"
		"--nocaseglob; make pathname expansion case-insensitive"
		"--dotglob; don't treat a period at the beginning of a filename specially"
		"--extendedglob; enable recursive pathname expansion"
		"--nullglob; remove words that matched nothing in pathname expansion"
		"--curasync; a newly-executed background job becomes the current job"
		"--curbg; a background job becomes the current job when resumed"
		"--curstop; a background job becomes the current job when stopped"
		"--histspace; don't save a command starting with a space in the history"
		"--le-convmeta; always treat meta-key flags in line-editing"
		"--le-noconvmeta; never treat meta-key flags in line-editing"
		"--le-visiblebell; alert with a flash, not a bell"
		"--le-promptsp; ensure the prompt is printed at the beginning of a line"
		"--le-alwaysrp; always show the right prompt during line-editing"
		"--le-compdebug; print debugging info during command line completion"
		) #<#
		case $prog in (yash)
			FOPTIONS=("$FOPTIONS" #>#
			"--norcfile; don't read rc file"
			"--rcfile:; specify the rc file"
			) #<#
		esac
	esac
	case $prog in (bash|ksh|mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"p --privileged; work in the privileged mode"
		) #<#
	esac
	case $prog in (bash|ksh)
		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"
		) #<#
	esac
	case $prog in (bash|mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"P --physical; make the -P option the default in the cd commands etc."
		) #<#
	esac
	case $prog in (bash)
		compoptopts="$compoptopts -e"
		FOPTIONS=("$FOPTIONS" #>#
		"--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" #>#
		"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"
		) #<#
	esac
	case $prog in (ksh|mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"r --restricted; work in the restricted mode"
		"--bgnice; run background jobs at lower priorities"
		"--gmacs; gmacs-like line-editing"
		) #<#
	esac
	case $prog in (ksh)
		FOPTIONS=("$FOPTIONS" #>#
		"D --dump-strings; extract translatable strings"
		"E; read the rc file"
		"R:; create a cross reference database in the specified file"
		) #<#
		OOPTIONS=("$OOPTIONS" #>#
		"G --globstar; enable recursive pathname expansion"
		"t; execute one command only"
		"--multiline; allow multiple line editing"
		"--showme; trace commands preceded by a semicolon"
		"--viraw; vi-like line-editing without canonical input handling"
		) #<#
	esac
	case $prog in (mksh)
		OOPTIONS=("$OOPTIONS" #>#
		"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:
