# (C) 2011-2023 magicant

# Completion script for the "set" built-in command.
# Completion function "completion/set" is used for the "ksh" and "yash"
# commands as well. Supports AT&T ksh 20100621, yash 2.47.

function completion/set {

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

	typeset OPTIONS SOPTIONS LOPTIONS
	OPTIONS=( #>#
	"o:; specify an option"
	"--help"
	) #<#
	SOPTIONS=() LOPTIONS=()

	case $prog in
	(yash)
		OPTIONS=("$OPTIONS" #>#
		"--noprofile; don't read the profile file"
		"--norcfile; don't read the yashrc file"
		"--profile:; specify the profile file"
		"--rcfile:; specify the yashrc file"
		"V --version; print version info"
		) #<#
		;;
	(ksh)
		OPTIONS=("$OPTIONS" #>#
		"D --dump-strings; extract translatable strings"
		"R:; create a cross reference database in the specified file"
		"--version"
		) #<#
		;;
	esac

	command -f completion/set::getopt "$prog"

	typeset i=2 ARGOPT PREFIX
	while [ $i -le ${WORDS[#]} ]; do
		case ${WORDS[i]} in
		(--|-|++)
			complete -f
			return
			;;
		(++*)
			case $prog in
			(set|yash)
				;;
			(*)
				complete -f
				return
				;;
			esac
			;;
		(-*|+?*)
			command -f completion/set::checkoption "${WORDS[i]}" false
			case $ARGOPT in
			(f)
				i=$((i+1))
				if [ $i -gt ${WORDS[#]} ]; then
					complete -P "$PREFIX" -f
					return
				fi
				;;
			(o)
				i=$((i+1))
				if [ $i -gt ${WORDS[#]} ]; then
					command -f completion/set::completelongoption
					return
				fi
				;;
			esac
			;;
		(*)
			complete -f
			return
			;;
		esac
		i=$((i+1))
	done

	case $TARGETWORD in
	([+-]*)
		command -f completion/set::checkoption "$TARGETWORD" true
		case $ARGOPT in
		(s)
			command -f completion/set::completeshortoption
			;;
		(o)
			command -f completion/set::completelongoption
			;;
		(*)
			complete -P "$PREFIX" -f
			;;
		esac
		;;
	(*)
		complete -f
		;;
	esac

}

function completion/set::checkoption {

	case $1 in
		(--*|++*)
			command -f completion/set::checklongoption "$@"
			;;
		(*)
			command -f completion/set::checkshortoption "$@"
			;;
	esac

}

function completion/set::checkshortoption {

	typeset word="${1#[+-]}"

	while [ "$word" ]; do
		for opt in ${OPTIONS%%;*}; do
			case $opt in (?:)
				case ${word[1]} in ("${opt%:}")
					case $opt in
						(o:) ARGOPT=o ;;
						(*)  ARGOPT=f ;;
					esac
					if $2; then
						PREFIX=${1%"${word#?}"}
					elif [ "${word#?}" ]; then
						ARGOPT= PREFIX=
					else
						PREFIX=
					fi
					return
				esac
			esac
		done
		word=${word#?}
	done
	
	if $2; then
		ARGOPT=s PREFIX=$1
	else
		ARGOPT= PREFIX=
	fi

}

function completion/set::checklongoption {

	case $prog in (ksh)
		case $1 in (++*)
			ARGOPT= PREFIX=
			return
		esac
	esac

	typeset MATCHES opt word name
	MATCHES=()

	word=${1#[+-][+-]}
	name=${word%%=*}
	for opt in ${OPTIONS%%;*}; do
		case $opt in (--*)
			case ${opt%:} in ("--$name"*)
				MATCHES=("$MATCHES" "$opt")
			esac
		esac
	done

	case $prog in
		(set|yash) name=$(tr -Cd \[:alnum:] <<<$word 2>/dev/null) ;;
		(ksh)      name=$(tr -d  _-         <<<$word 2>/dev/null) ;;
		(*)        name=$word ;;
	esac

	for opt in ${LOPTIONS%%;*}; do
		for opt in $opt no$opt; do
			case $opt in ("$name"*)
				MATCHES=("$MATCHES" "$opt")
			esac
		done
	done

	if [ ${MATCHES[#]} -eq 1 ]; then
		case ${MATCHES[1]} in (--*:)
			case $word in
				(*=*)
					if $2; then
						ARGOPT=f PREFIX=${1%"${1#*=}"}
						return
					fi
					;;
				(*)
					if $2; then
						ARGOPT=o PREFIX=
					else
						ARGOPT=f PREFIX=
					fi
					return
					;;
			esac
		esac
	fi
	
	if $2; then
		ARGOPT=o PREFIX=${1[1,2]}
	else
		ARGOPT= PREFIX=
	fi

}

function completion/set::getopt {

	SOPTIONS=( #>#
	"a; export all variables when assigned"
	"b; print job status immediately when done"
	"C; disallow >-redirection to overwrite an existing file"
	"e; exit immediately when a command's exit status is non-zero"
	"f; disable pathname expansion (globbing)"
	"m; enable job control"
	"n; don't execute any commands"
	"u; disallow expansion of undefined variables"
	"v; echo commands entered to the shell"
	"x; print a command line before executing it"
	) #<#
	LOPTIONS=( #>#
	"allexport; export all variables when assigned"
	"braceexpand; enable brace expansion"
	"clobber; allow >-redirection to overwrite an existing file"
	"emacs; emacs-like line-editing"
	"errexit; exit immediately when a command's exit status is non-zero"
	"exec; actually execute commands"
	"glob; enable pathname expansion (globbing)"
	"ignoreeof; don't exit when an end-of-file is entered"
	"markdirs; append a slash to directory names after pathname expansion"
	"monitor; enable job control"
	"notify; print job status immediately when done"
	"unset; allow expansion of undefined variables"
	"verbose; echo commands entered to the shell"
	"vi; vi-like line-editing"
	"xtrace; print a command line before executing it"
	) #<#

	case $prog in
	(set|yash)
		SOPTIONS=("$SOPTIONS" #>#
		"h; cache full paths of commands in a function when defined"
		) #<#
		LOPTIONS=("$LOPTIONS" #>#
		"caseglob; make pathname expansion case-sensitive"
		"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"
		"dotglob; don't treat a period at the beginning of a filename specially"
		"emptylastfield; don't remove empty last field in field splitting"
		"errreturn; return immediately when a command's exit status is non-zero"
		"extendedglob; enable recursive pathname expansion"
		"forlocal; make the iteration variable local in a for loop"
		"hashondef; cache full paths of commands in a function when defined"
		"histspace; don't save a command starting with a space in the history"
		"leconvmeta; always treat meta-key flags in line-editing"
		"lenoconvmeta; never treat meta-key flags in line-editing"
		"lepredict; suggest a command fragment while line-editing"
		"lepredictempty; suggest a command fragment on empty input while line-editing"
		"levisiblebell; alert with a flash, not a bell"
		"lepromptsp; ensure the prompt is printed at the beginning of a line"
		"lealwaysrp; always show the right prompt during line-editing"
		"letrimright; trim the space to the right of the right prompt"
		"lecompdebug; print debugging info during command line completion"
		"notifyle; print job status immediately when done while line-editing"
		"nullglob; remove words that matched nothing in pathname expansion"
		"pipefail; return last non-zero exit status of commands in a pipe"
		"posix; force strict POSIX conformance"
		"traceall; print trace of auxiliary commands"
		) #<#
		;;
	(ksh)
		SOPTIONS=("$SOPTIONS" #>#
		"G; enable recursive pathname expansion"
		"H; enable !-expansion on the command line"
		"h; cache full paths of commands when entered"
		"k; allow assignments in the middle of command arguments"
		"p; work in the privileged mode"
		"r; work in the restricted mode"
		"t; execute one command only"
		) #<#
		LOPTIONS=("$LOPTIONS" #>#
		"bgnice; run background jobs at lower priorities"
		"gmacs; gmacs-like line-editing"
		"globstar; enable recursive pathname expansion"
		"histexpand; enable !-expansion on the command line"
		"keyword; allow assignments in the middle of command arguments"
		"multiline; allow multiple line editing"
		"pipefail; return last non-zero exit status of commands in a pipe"
		"privileged; work in the privileged mode"
		"restricted; work in the restricted mode"
		"showme; trace commands preceded by a semicolon"
		"trackall; cache full paths of commands when entered"
		"viraw; vi-like line-editing without canonical input handling"
		) #<#
		;;
	esac
	case $prog in (yash|ksh)
		SOPTIONS=("$SOPTIONS" #>#
		"c; execute the first operand as a shell script"
		"i; work in the interactive mode"
		"l; work as a login shell"
		"s; read commands from the standard input"
		) #<#
		LOPTIONS=("$LOPTIONS" #>#
		"interactive; work in the interactive mode"
		"login; work as a login shell"
		) #<#
		case $prog in
		(yash)
			LOPTIONS=("$LOPTIONS" #>#
			"cmdline; execute the command specified on the command line"
			"stdin; read commands from the standard input"
			) #<#
			;;
		(ksh)
			SOPTIONS=("$SOPTIONS" #>#
			"E; read the \$ENV file on shell invocation"
			) #<#
			LOPTIONS=("$LOPTIONS" #>#
			"rc; read the \$ENV file on shell invocation"
			) #<#
			;;
		esac
	esac

}

function completion/set::completeshortoption {

	typeset opt desc
	for opt in "$OPTIONS" "$SOPTIONS"; do
		command -f completion/set::getdesc
		for opt in ${opt%%;*}; do
			opt=${opt%:}
			case $opt in (?)
				complete -O -P "$PREFIX" ${desc:+-D "$desc"} -- "$opt"
			esac
		done
	done

}

function completion/set::completelongoption {

	typeset opt desc part normpart

	case $TARGETWORD in (--*)
		for opt in "$OPTIONS"; do
			command -f completion/set::getdesc
			for opt in ${opt%%;*}; do
				case $opt in
				(--*:)
					complete -T -P -- ${desc:+-D "$desc"} -- "${{opt#--}%:}="
					;;
				(--*)
					complete -P -- ${desc:+-D "$desc"} -- "${opt#--}"
					;;
				esac
			done
		done
	esac

	part=${TARGETWORD#"$PREFIX"}
	case $prog in
		(set|yash) normpart=$(tr -Cd \[:alnum:] <<<$part 2>/dev/null) ;;
		(ksh)      normpart=$(tr -d  _-         <<<$part 2>/dev/null) ;;
		(*)        normpart=$part ;;
	esac

	for opt in "$LOPTIONS"; do
		command -f completion/set::getdesc
		opt=${opt%%;*}
		for opt in $opt no$opt; do
			case $opt in ("$normpart"*)
				complete -P "$PREFIX" ${desc:+-D "$desc"} -- "$part${opt#"$normpart"}"
			esac
		done
	done

}

function completion/set::getdesc {

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

}


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