###############################################################################
#
# Bash completion for Slurm
#
###############################################################################
#  Copyright (C) 2012 Damien François. <damien.francois@uclouvain.Be>
#  Written by Damien François. <damien.francois@uclouvain.Be>.
#
#  This file is part of SLURM, a resource management program.
#  For details, see <https://slurm.schedmd.com/>.
#  Please also read the included file: DISCLAIMER.
#
#  SLURM is free software; you can redistribute it and/or modify it under
#  the terms of the GNU General Public License as published by the Free
#  Software Foundation; either version 2 of the License, or (at your option)
#  any later version.
#
#  In addition, as a special exception, the copyright holders give permission
#  to link the code of portions of this program with the OpenSSL library under
#  certain conditions as described in each individual source file, and
#  distribute linked combinations including the two. You must obey the GNU
#  General Public License in all respects for all of the code used other than
#  OpenSSL. If you modify file(s) with this exception, you may extend this
#  exception to your version of the file(s), but you are not obligated to do
#  so. If you do not wish to do so, delete this exception statement from your
#  version.  If you delete this exception statement from all source files in
#  the program, then also delete it here.
#
#  SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
#  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
#  details.
#
#  You should have received a copy of the GNU General Public License along
#  with SLURM; if not, write to the Free Software Foundation, Inc.,
#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
#
###############################################################################

function compute_set_diff(){
    res=""
    for i in $1; do
	[[ "$2" =~ ${i%%=*} ]] && continue
	res="$i $res"
    done
    echo $res
}

_split_long_opt() {
    [[ $cur == = || $cur == : ]] && cur=""
    [[ $prev == = ]] && prev=${COMP_WORDS[$cword-2]}
}

function find_first_partial_occurence(){
    res=""
    for item1 in $1; do
	for item2 in $2; do
	    if [[ $item2 == "$item1=" ]]; then
		res="$item1"
		break
	    fi
	done
	if [[ $res != "" ]]; then
	    break
	fi
    done
    echo $res
}

function find_first_occurence(){
    res=""
    for item1 in $1; do
	for item2 in $2; do
	    if [[ $item1 = $item2 ]]; then
		res="$item1"
		break
	    fi
	done
	if [[ $res != "" ]]; then
	    break
	fi
    done
    echo $res
}

function offer (){
    remainings=$(compute_set_diff "$1" "${COMP_WORDS[*]}")
    COMPREPLY=( $( compgen -W "$remainings" -- $cur ) )
    if [[ "$1" == *=* || "$1" == *%* || "$1" == *:* ]];
    then
	#echo "NO SPACE $1" >> loglog
	compopt -o nospace
    fi
}

function offer_list () {
    curlist=${cur%,*}
    curitem=${cur##*,}

    if [[ $curlist == $curitem ]]
    then
	COMPREPLY=( $( compgen -W "${1}" -- $cur ) ) ; return
    elif [[ $cur == *,  ]]  ;
    then
	compvalues=""
	for i in $1;do
	    [[ $cur =~ $i ]] && continue
	    compvalues="$i $compvalues "
	done
	uniqueprefix=1
	prefix=${compvalues:0:1}
	for i in $compvalues;do
	    [[ ${i:0:1} == $prefix ]] || uniqueprefix=0
	done
	if [[ $uniqueprefix == 1  ]]
	then
	    compvalues=""
	    for i in $1;do
		[[ $cur =~ $i ]] && continue
		compvalues="$compvalues $curlist,$i"
	    done
	fi
	COMPREPLY=( $( compgen -W "${compvalues}" -- "" ) ) ; return
    else
	compvalues=""
	for i in $1;do
	    [[ $cur =~ $i ]] && continue
	    compvalues="$compvalues $curlist,$i"
	done
	COMPREPLY=( $( compgen -W "${compvalues}" -- $cur ) ) ;
    fi
}

function offer_many () {
    availablevalues=""
    for i in $1;do
	[[ $cur =~ $i ]] && continue
	availablevalues="$i $availablevalues"
    done

    # Check that there is no unique prefix for all remaining options (God knows why I have to do this. Must be missing something)
    # TODO when all suboptions start with the same prefix, it is not working great
    uniqueprefix=1
    prefix=${availablevalues:0:1}
    for i in $availablevalues;do
	[[ ${i:0:1} == $prefix ]] || uniqueprefix=0
    done


    #if [[ "$1" == *'\"'% ]];
    #then
    #    compopt -o nospace
    #fi #added for --format in squeue

    if [[  ${COMP_WORDS[COMP_CWORD-1]} == "$argname" ]]; then
	# echo  "The first value is about to be entered" >> loglog
	cur=""
	COMPREPLY=( $( compgen -W "${1}" -- $cur ) ) ; return
    fi
    if [[ ${COMP_WORDS[COMP_CWORD-1]} == '='  && "$cur" != *,* ]]; then
	# echo  "A supplementary value is being entered" >> loglog
	COMPREPLY=( $( compgen -W "${1}" -- $cur ) ) ; return
    fi
    if [[ ${cur:${#cur}-1:1} == "," && $uniqueprefix == 0  ]]; then
	 echo  "A supplementary value is about to be entered and there is a no unique suffix" >> loglog
	compvalues=""
	for i in $1;do
	    [[ $cur =~ $i ]] && continue
	    compvalues="$i $compvalues"
	done
	cur=""
	COMPREPLY=( $( compgen -W "${compvalues}" -- $cur ) ) ;
	return
    fi
    if [[ "$cur" =~ ","  ]] ; then
	 echo  "A supplementary value is about to be entered and there is a unique prefix or we are in the middle of one" >> loglog
	compvalues=""
	for i in $1;do
	    [[ $cur =~ $i ]] && continue
	    compvalues="$compvalues ${cur%,*},$i"
	    #compvalues="$compvalues $i"
	done
	COMPREPLY=( $( compgen -W "${compvalues}" -- $cur ) ) ;

	# This is lame, we show complete list rather than last element
	return
    fi
    return 255
}

function param () {
    argname="$1"
    [[ ${COMP_WORDS[COMP_CWORD]} == "=" && ${COMP_WORDS[COMP_CWORD-1]} == $1 ]] && return 0
    [[ ${COMP_WORDS[COMP_CWORD-1]} == "=" && ${COMP_WORDS[COMP_CWORD-2]} == $1 ]] && return 0
    [[ ${COMP_WORDS[COMP_CWORD-1]} == $1 ]] && return 0
    return 255
}

function _jobs() {
echo $( scontrol -o show jobs | cut -d' ' -f 1 | cut -d'=' -f 2 ) ;
}
function _wckeys() {
echo $(sacctmgr -p -n list wckeys | cut -d'|' -f1) ;
}
function _qos() {
echo $(sacctmgr -p -n list qos | cut -d'|' -f1) ;
}
function _clusters() {
echo $(sacctmgr -p -n list clusters | cut -d'|' -f1) ;
}
function _jobnames() {
echo $( scontrol -o show jobs | cut -d' ' -f 2 | cut -d'=' -f 2 ) ;
}
function _partitions() {
echo $(scontrol show partitions|grep PartitionName|cut -c 15- |cut -f 1 -d' '|paste -s -d ' ') ;
}
function _nodes() {
echo $(scontrol show nodes | grep NodeName | cut -c 10- | cut -f 1 -d' ' | paste -s -d ' ') ;
}
function _accounts() {
echo $(sacctmgr -pn list accounts | cut -d'|' -f1 | paste -s -d' ') ;
}
function _licenses() {
echo $(scontrol show config| grep Licenses | sed 's/Licenses *=//'| paste -s -d' ') ;
}
function _nodes() {
echo $(scontrol show nodes | grep NodeName | cut -c 10- | cut -f 1 -d' ' | paste -s -d ' ') ;
}
function _features() {
echo $(scontrol -o show nodes|cut -d' ' -f7|sed 's/Features=//'|sort -u|tr -d '()'|paste -d, -s) ;
}
function _users() {
echo $(sacctmgr -pn list users | cut -d'|' -f1) ;
}
function _reservations() {
echo $(scontrol -o show reservations | cut -d' ' -f1 | cut -d= -f2) ;
}
function _gres() {
echo $(scontrol show config | grep GresTypes | cut -d= -f2)
}
function _jobname() {
echo $(scontrol show -o jobs | cut -d' ' -f 2 | sed 's/Name=//')
}
function _resource() {
echo $(sacctmgr -pn list resource | cut -d'|' -f1 | paste -s -d' ')
}
function _step() {
echo $( scontrol -o show step | cut -d' ' -f 1 | cut -d'=' -f 2 ) ;
}

_sacctmgr()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local subopts=""
    local commands="add archive create delete dump list load modify show "
    local shortoptions="-h -i -n -p -P -Q -r -s -v -V"
    local longoptions="--help --immediate --noheader --parsable \
	--parsable2 --quiet --readonly --associations --verbose --version"


    local assocparams="clusters= accounts= users= partition= "

    local assocbasedparams="defaultqos= fairshare= gracetime=\
			    grpcpumins= grpcpurunmins= grpcpus=\
			    grpjobs= grpmemory= grpnodes= grpsubmitjobs=\
			    grpwall= maxcpumins= maxcpus= maxjobs= maxnodes=\
			    maxsubmitjobs= maxwall= qoslevel="

    local qosflags="DenyOneLimit EnforceUsageThreshold NoReserve\
		    PartitionMaxNodes PartitionMinNodes PartitionQos\
		    PartitionTimeLimit"
    local qospreempt="cluster cancel checkpoint requeue suspend"

    local clusflags="aix bgl bgq bluegene crayxt frontend multipleslumd\
		     sunconstellation xcpu"

    # Check whether we are in the middle of an option. If so serve them.
    remainings=$(compute_set_diff "$longoptions" "${COMP_WORDS[*]}")
    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$remainings" ; return ; }

    # Search for a command in the argument list (first occurence)
    # the command might be in any position because of the options
    command=$(find_first_occurence "${COMP_WORDS[*]}" "$commands")

    # If no command has been entered, serve the list of valid commands
    [[ $command == "" ]] && { offer "$commands" ; return ; }

    # Load command has a specific syntax. Treat it first
    [[ $command == "load" ]] && { _filedir ; return ; }

    case $command in
    add|create)
	objects="account cluster coordinator qos user "
	object=$(find_first_occurence "${COMP_WORDS[*]}" "$objects")
	case $object in
	account)
	    params="cluster= description= name= organization= parent=\
		    rawusage= withassoc withcoord withdeleted"
	    if param "cluster" ; then  offer_list "$(_clusters)" ;
	    elif param "parent" ; then  offer_list "$(_accounts)" ;
	    else offer "$params"
	    fi
	    ;;
	cluster)
	    params="classification= flags= name= rpc= wolimits"
	    if param "flags" ; then  offer_list "$clusflags" ;
	    else offer "$params"
	    fi
	    ;;
	coordinator)
	    params="accounts= names="
	    if param "names" ; then  offer_list "$(_users)" ;
	    elif param "accounts" ; then  offer_list "$(_accounts)" ;
	    else offer "$params"
	    fi
	    ;;
	qos)
	    params="flags= gracetime= grpcpumins= grpcpurunmins= grpcpus=\
		    grpjobs= grpnodes= grpsubmitjobs= grpwall= maxcpumins=\
		    maxcpus= maxcpusperuser= maxjobs= maxnodes= mincpus=\
		    maxnodesperuser= maxsubmitjobs= maxwall= name= preempt=\
		    preemptmode= priority= usagefactor= usagethreshold=\
		    withdeleted"
	    if param "preemptmode" ; then  offer_list "$qospreempt" ;
	    elif param "flags" ; then  offer_list "$qosflags" ;
	    elif param "preempt" ; then  offer_list "$(_qos)" ;
	    else offer "$params"
	    fi
	    ;;
	user)
	    params="account= adminlevel= cluster= defaultaccount=\
	       defaultwckey= name= partition= rawusage= wckey= withassoc
	       withcoord withdeleted"
	    if param "defaultaccount" ; then  offer_list "$(_accounts)" ;
	    elif param "account" ; then  offer_list "$(_accounts)";
	    elif param "adminlevel" ; then  offer_list "none operator admin" ;
	    elif param "cluster" ; then  offer_list "$(_cluster)" ;
	    elif param "defaultwckey" ; then  offer_list "$(_wckey)" ;
	    else offer "$params"
	    fi
	    ;;
	*) offer "$objects" ;;
	esac
	;;
    archive)
	objects="dump load"
	object=$(find_first_occurence "${COMP_WORDS[*]}" "$objects")
	case $object in
	dump)
            _filedir
	    ;;
	load)
            _filedir
	    ;;
	*) offer "$objects"
	    ;;
	esac
	;;
    delete)
	objects="account cluster coordinator qos user"
	object=$(find_first_occurence "${COMP_WORDS[*]}" "$objects")
	case $object in
	account)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ; return ;fi
	    params="cluster= description= name= organization= parent="
	    if param "cluster" ; then  offer_list "$(_clusters)" ;
	    elif param "parent" ; then  offer_list "$(_accounts)" ;
	    elif param "name" ; then  offer_list "$(_accounts)" ;
	    else offer "$params"
	    fi
	    ;;
	cluster)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="classification= flags= name= rpc= $assocbasedparams"
	    if param "flags" ; then  offer_list "$clusflags" ;
	    elif param "defaultqos" ; then  offer_list "$(_qos)" ;
	    else offer "$params"
	    fi
	    ;;
	coordinator)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="accounts= names="
	    if param "names" ; then  offer_list "$(_users)" ;
	    elif param "accounts" ; then  offer_list "$(_accounts)" ;
	    else offer "$params"
	    fi
	    ;;
	user)
	    params="account= adminlevel= cluster= defaultaccount=\
		    defaultwckey= name= wckeys= withassoc"
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    if param "defaultaccount" ; then  offer_list "$(_accounts)" ;
	    elif param "account" ; then  offer_list "$(_accounts)";
	    elif param "adminlevel" ; then  offer_list "none operator admin" ;
	    elif param "cluster" ; then  offer_list "$(_cluster)" ;
	    elif param "wckeys" ; then  offer_list "$(_wckeys)" ;
	    elif param "defaultwckey" ; then  offer_list "$(_wckey)" ;
	    else offer "$params" ;
	    fi
	    ;;
	*) offer "$objects"
	    ;;
	esac
	;;
    list|show)
	objects="account association cluster configuration \
	    event problem qos resource transaction user wckey"
	object=$(find_first_occurence "${COMP_WORDS[*]}" "$objects")
	case $object in
	account)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="cluster= description= name= organization= parent=\
		    withassoc withcoord withdeleted $assocparams\
		    $assocbasedparams"
	    if param "cluster" ; then  offer_list "$(_clusters)" ;
	    elif param "parent" ; then  offer_list "$(_accounts)" ;
	    elif param "users" ; then  offer_list "$(_users)" ;
	    elif param "partition" ; then  offer_list "$(_partition)" ;
	    elif param "defaultqos" ; then  offer_list "$(_qos)" ;
	    elif param "name" ; then  offer_list "$(_accounts)" ;
	    else offer "$params"
	    fi
	    ;;
	association)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="$assocparams onlydefaults tree withdeleted withsubaccounts\
		    wolimits wopinfo woplimits"
	    if param "clusters" ; then  offer_list "$(_clusters)" ;
	    elif param "accounts" ; then  offer_list "$(_accounts)" ;
	    elif param "users" ; then  offer_list "$(_users)" ;
	    elif param "partition" ; then  offer_list "$(_partitions)" ;
	    else offer "$params"
	    fi
	    ;;
	cluster)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="classification= flags= name= rpc= $assocbasedparams\
		    wolimits"
	    if param "flags" ; then  offer_list "$clusflags" ;
	    elif param "defaultqos" ; then  offer_list "$(_qos)" ;
	    else offer "$params"
	    fi
	    ;;
	event)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="all_clusters all_time clusters= end= event= maxcpu=\
		    mincpus= nodes= reason= start= states= user= "
	    if param "clusters" ; then  offer_list "$(_clusters)" ;
	    elif param "nodes" ; then  offer_list "$(_nodes)" ;
	    elif param "event" ; then  offer_list "cluster node" ;
	    elif param "states" ; then  offer_list "alloc allocated down drain\
			fail failing idle mixed maint power_down power_up\
			resume" ;
	    elif param "users" ; then  offer_list "$(_users)" ;
	    else offer "$params"
	    fi
	    ;;
	qos)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="flags= gracetime= grpcpumins= grpcpus= grpcpurunmins=\
		    grpjobs= grpnodes= grpsubmitjobs= grpwall= id= maxcpumins=\
		    maxcpusmins= maxcpus= maxjobs= maxnodes= mincpus=\
		    maxnodesperuser= maxsubmitjobs= maxwall= name= preempt=\
		    preemptmode= priority= rawusage= usagefactor=\
		   usagethreshold= withdeleted"
	    if param "preemptmode" ; then  offer_list "cluster cancel\
							 checkpoint requeue\
							 suspend" ;
	    elif param "flags" ; then  offer_list "$qosflags" ;
	    elif param "preempt" ; then  offer_list "$(_qos)" ;
	    else offer "$params"
	    fi
	    ;;
	resource)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="cluster= count= description= flags= servertype= name=\
		    precentallowed= server= type="
	    if param "name" ; then  offer_list "$(_resource)" ;
	    elif param "cluster" ; then offer_list "$(_clusters)" ;
	    else offer "$params"
	    fi
	    ;;
	transaction)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="accounts= action= actor= clusters= endtime= startime=\
		users= withassoc"
	    if param "accounts" ; then  offer_list "$(_accounts)" ;
	    elif param "actor" ; then  offer_list "$(_users)" ;
	    elif param "clusters" ; then  offer_list "$(_clusters)" ;
	    else offer "$params"
	    fi
	    ;;
	user)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="account= adminlevel= cluster= defaultaccount=\
		    defaultwckey= name= partition= wckeys= withassoc withcoord \
		    withdelted"
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    if param "defaultaccount" ; then  offer_list "$(_accounts)" ;
	    elif param "account" ; then  offer_list "$(_accounts)";
	    elif param "adminlevel" ; then  offer_list "none operator admin" ;
	    elif param "cluster" ; then  offer_list "$(_cluster)" ;
	    elif param "wckeys" ; then  offer_list "$(_wckeys)" ;
	    elif param "defaultwckey" ; then  offer_list "$(_wckey)" ;
	    else offer "$params" ;
	    fi
	    ;;
	*) offer "$objects" ;;
	esac
	;;
    modify)
	objects="account cluster job qos user"
	object=$(find_first_occurence "${COMP_WORDS[*]}" "$objects")
	case $object in
	account)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="cluster= description= name= organization= parent=\
		rawusage= $assocbasedparams"
	    if param "cluster" ; then  offer_list "$(_clusters)" ;
	    elif param "parent" ; then  offer_list "$(_accounts)" ;
	    elif param "name" ; then  offer_list "$(_accounts)" ;
	    else offer "$params set"
	    fi
	    ;;
	cluster)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="classification= flags= name= rpc= $assocbasedparams"
	    if param "flags" ; then  offer_list "$clusflags" ;
	    elif param "defaultqos" ; then  offer_list "$(_qos)" ;
	    else offer "$params set"
	    fi
	    ;;
	qos)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="flags= gracetime= grpcpumins= grpcpurunmins= grpcpus=\
		    grpjobs= grpnodes= grpsubmitjobs= grpwall= maxcpumins=\
		    maxcpus= maxcpusperuser= maxjobs= maxnodes= mincpus=\
		    maxnodesperuser= maxsubmitjobs= maxwall= name= preempt=\
		    preemptmode= priority= rawusage= usagefactor=\
		    usagethreshold= withdeleted"
	    if param "flags" ; then  offer_list "$qosflags" ;
	    elif param "name" ; then offer_list "$(_qos)" ;
	    elif param "preemptmode" ; then  offer_list "$qospreempt" ;
	    elif param "preempt" ; then  offer_list "$(_qos)" ;
	    else offer "$params set"
	    fi
	    ;;
	user)
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    params="account= adminlevel= cluster= defaultaccount=\
		    defaultwckey= name= partition= rawusage= wckeys= withassoc"
	    if [[ "${COMP_WORDS[*]}" != *where* ]] ; then offer "where" ;
		    return ;fi
	    if param "defaultaccount" ; then  offer_list "$(_accounts)" ;
	    elif param "account" ; then  offer_list "$(_accounts)";
	    elif param "adminlevel" ; then  offer_list "none operator admin" ;
	    elif param "cluster" ; then  offer_list "$(_cluster)" ;
	    elif param "wckeys" ; then  offer_list "$(_wckeys)" ;
	    elif param "defaultwckey" ; then  offer_list "$(_wckey)" ;
	    else offer "$params" ;
	    fi
	    ;;
	*) offer "$objects"
	    ;;
	esac
	;;

    esac
}
complete -F _sacctmgr sacctmgr

_sreport()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local subopts=""
    local commands="cluster job user reservation"

    local shortoptions="-a -n -h -p -P -Q -t -v -V"
    local longoptions="--all_clusters --help --noheader --parsable\
			--parsable2 --quiet --verbose --version"

    # Check whether we are in the middle of an option. If so serve them.
    remainings=$(compute_set_diff "$longoptions" "${COMP_WORDS[*]}")
    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$remainings" ; return ; }

    # Search for a command in the argument list (first occurence)
    # the command might be in any position because of the options
    command=$(find_first_occurence "${COMP_WORDS[*]}" "$commands")

    # If no command has been entered, serve the list of valid commands
    [[ $command == "" ]] && { offer "$commands" ; return ; }

    opts_all="All_Clusters Clusters= End= Format= Start="

    case $command in
    user)
	objects="TopUsage"
	object=$(find_first_occurence "${COMP_WORDS[*]}" "$objects")
	case $object in
	TopUsage)
	    params="$opts_all Accounts= Group TopCount= Users="
	    if param "Clusters" ; then  offer_list "$(_clusters)" ;
	    elif param "Format" ; then  offer_list "Account Cluster Login\
						    Proper User" ;
	    elif param "Accounts" ; then  offer_list "$(_accounts)" ;
	    elif param "Users" ; then  offer_list "$(_users)" ;
	    else offer "$params"
	    fi
	    ;;
	*) offer "$objects" ;;
	esac
	;;
    reservation)
	objects="Utilization"
	object=$(find_first_occurence "${COMP_WORDS[*]}" "$objects")
	case $object in
	Utilization)
	    params="$opts_all Names= Nodes= Accounts= Group TopCount= Users= "
	    if param "Clusters" ; then  offer_list "$(_clusters)" ;
	    elif param "Format" ; then  offer_list "Allocated Associations \
		Clusters CPUCount CPUTime End Flags Idle Name Nodes\
		ReservationId Start TotalTime";
	    elif param "Nodes" ; then  offer_list "$(_nodes)" ;
	    else offer "$params"
	    fi
	    ;;
	*) offer "$objects" ;;
	esac
	;;
    job)
	objects="SizesByAccount SizesByAccountAndWckey SizesByWckey"
	object=$(find_first_occurence "${COMP_WORDS[*]}" "$objects")
	case $object in
	SizesByAccount|SizesByAccountAndWckey)
	    params="$opts_all Accounts= FlatView GID= Grouping= \
		Jobs= Nodes= Partitions= PrintJobCount Users= Wckeys="
	    if param "Clusters" ; then  offer_list "$(_clusters)" ;
	    elif param "Format" ; then  offer_list "Account Cluster" ;
	    elif param "Accounts" ; then  offer_list "$(_accounts)" ;
	    elif param "GID" ; then  _gids ;
	    elif param "Users" ; then  offer_list "$(_users)" ;
	    elif param "Wckeys" ; then  offer_list "$(_wckeys)" ;
	    else offer "$params"
	    fi
	    ;;
	SizesByWckey)
	    params="$opts_all Accounts= FlatView GID= Grouping= \
		Jobs= Nodes= OPartitions= PrintJobCount Users= Wckeys="
	    if param "Clusters" ; then  offer_list "$(_clusters)" ;
	    elif param "Format" ; then  offer_list "Wckey Cluster" ;
	    elif param "Accounts" ; then  offer_list "$(_accounts)" ;
	    elif param "GID" ; then  _gids ;
	    elif param "Users" ; then  offer_list "$(_users)" ;
	    elif param "Wckeys" ; then  offer_list "$(_wckeys)" ;
	    else offer "$params"
	    fi
	    ;;
	*) offer "$objects" ;;
	esac
	;;
    cluster)
	objects="AccountUtilizationByUser UserUtilizationByAccount \
		    UserUtilizationByWCKey Utilization WCKeyUtilizationByUser"
	object=$(find_first_occurence "${COMP_WORDS[*]}" "$objects")
	case $object in
	Utilization)
	    params="$opts_all Names= Nodes="
	    if param "Clusters" ; then  offer_list "$(_clusters)" ;
	    elif param "Format" ; then  offer_list "Allocated Cluster \
		CPUCount Down Idle Overcommited PlannedDown Reported Reserved";
	    elif param "Nodes" ; then  offer_list "$(_nodes)" ;
	    else offer "$params"
	    fi
	    ;;
	AccountUtilizationByUser|UserUtilizationByAccount)
	    params="$opts_all Accounts= Tree Users= Wckeys="
	    if param "Clusters" ; then  offer_list "$(_clusters)" ;
	    elif param "Format" ; then  offer_list "Accounts Cluster CPUCount\
						    Login Proper Used" ;
	    elif param "Accounts" ; then  offer_list "$(_accounts)" ;
	    elif param "Users" ; then  offer_list "$(_users)" ;
	    elif param "Wckeys" ; then  offer_list "$(_wckeys)" ;
	    else offer "$params"
	    fi
	    ;;
	UserUtilizationByWCKey|WCKeyUtilizationByUser)
	    params="$opts_all Accounts= Tree Users= Wckeys="
	    if param "Clusters" ; then  offer_list "$(_clusters)" ;
	    elif param "Format" ; then  offer_list "Cluster CPUCount Login\
						    Proper Used Wckey" ;
	    elif param "Accounts" ; then  offer_list "$(_accounts)" ;
	    elif param "Users" ; then  offer_list "$(_users)" ;
	    elif param "Wckeys" ; then  offer_list "$(_wckeys)" ;
	    else offer "$params"
	    fi
	    ;;
	*) offer "$objects" ;;
	esac
	;;

    esac
}
complete -F _sreport sreport

_scontrol()
{
    local cur=${COMP_WORDS[COMP_CWORD]}
    local prev=${COMP_WORDS[COMP_CWORD-1]}

    local commands="abort checkpoint cluster create completing delete details\
		    errnumstr help hold notify oneliner\
		    pidinfo listpids ping quit reboot_nodes reconfigure release\
		    requeue requeuehold schedloglevel resume schedloglevel\
		    script setdebug setdebugflags show shutdown suspend\
		    takeover uhold update verbose version wait_job"

    local shortoptions="-a -d -h -M -o -Q -v -V "
    local longoptions="--all --details --help --hide --cluster --oneliner \
			--quiet --verbose --version"

    # Check whether we are in the middle of an option. If so serve them.
    remainings=$(compute_set_diff "$longoptions" "${COMP_WORDS[*]}")
    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$remainings" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $remainings)"; return ; }

    # Search for a command in the argument list (first occurence)
    # the command might be in any position because of the options
    command=$(find_first_occurence "${COMP_WORDS[*]}" "$commands")

    # If no command has been entered, serve the list of valid commands
    [[ $command == "" ]] && { offer "$commands" ; return ; }

    # Otherwise process command
    case $command in
    shutdown) # scontrol shutdown object
	offer "slurmctld controller"
	;;
    setdebug) # scontrol setdebug value
	offer "quiet info warning error debug debug2 debug3 debug4 debug5 " # FIXME
	;;
    uhold | suspend | release | requeue | resume | hold )
	offer "$(_jobs)"
	;; #TODO notify
    checkpoint) # scontrol checkpoint create jobid [parameter1=value1,...]
	# This one has unsusual ordering: object is before command.
	# command subcommand argument #TODO add support for additional options cfr manpage
	objects="able create disable enable error restart requeue vacate"

	if [[ $prev == checkpoint ]]; then
	    offer "$objects";
	elif [[ $objects == *$prev* ]]; then
	    offer "$(_jobs)";
	else
	    echo todo
	    #TODO
	fi
	;;
    show) # scontrol show object [id]
	objects="aliases config block daemons frontend hostlist hostlistsorted\
		 hostnames job nodes partitions reservations slurmd steps\
		 submp topology"

	# Search for the current object in the argument list
	object=$(find_first_occurence "${COMP_WORDS[*]}" "$objects")

	# If no object has yet been (fully) typed in, serve the list of objects
	[[ $object == "" ]] && { offer "$objects" ; return ; }

	# Otherwise, offer the ids depending on the object
	if param "job"          ; then offer  "$(_jobs)"         ; fi
	if param "nodes"        ; then offer_list "$(_nodes)"        ; fi
	if param "partitions"   ; then offer "$(_partitions)"   ; fi
	if param "reservations" ; then offer "$(_reservations)"  ; fi
	#TODO if object "steps"
	;;
    delete) # scontrol delete objectname=id
	parameters="partitionname= reservationname="

	# If a parameter has been fully typed in, serve the corresponding
	# values, otherwise, serve the list of parameters.
	if   param "partitionname"   ; then offer_many "$(_partitions)"
	elif param "reservationname" ; then offer_many "$(_reservations)"
	else offer "$parameters" ; fi
	;;
    update)
	parameters="jobid= step= nodename= partitionname=\
		    reservationname="

	param=$(find_first_partial_occurence "${COMP_WORDS[*]}" "$parameters")
	[[ $param == "" ]] && { offer "$parameters" ; return ; }

	# If a parameter has been fully typed in, serve the corresponding
	# values, if it is the first one.
	if   param "jobid"   ; then offer_many "$(_jobs)" ; return
	elif param "nodename" ; then offer_many "$(_nodes)"  ; return
	elif param "partitionname" ; then offer_many "$(_partitions)" ; return
	elif param "reservationname" ; then offer_many "$(_reservations)"  ; return
	elif param "step" ; then offer_many "$(_step)" ; return
	fi

	# Otherwise, process the others based on the first one
	case $param in
	jobid)
	    local parameters="account=<account> conn-type=<type> \
			      contiguous=<yes|no> dependency=<dependency_list>\
			      eligibletime=yyyy-mm-dd excnodelist=<nodes>\
			      features=<features> geometry=<geo> gres=<list>\
			      jobid=<job_id> licenses=<name>\
			      mincpusnode=<count> minmemorycpu=<megabytes>\
			      minmemorynode=<megabytes>\
			      mintmpdisknode=<megabytes> name=<name>\
			      nice[=delta] nodelist=<nodes>\
			      numcpus=<min_count[-max_count]\
			      numnodes=<min_count[-max_count]>\
			      numtasks=<count> partition=<name>\
			      priority=<number> qos=<name> reqcores=<count>\
			      reqnodelist=<nodes> reqsockets=<count>\
			      reqthreads=<count> requeue=<0|1>\
			      reservationname=<name> rotate=<yes|no>\
			      shared=<yes|no> starttime=yyyy-mm-dd\
			      switches=<count>[@<max-time-to-wait>]\
			      timelimit=[d-]h:m:s userid=<UID or name>\
			      wckey=<key>"

	    remainings=$(compute_set_diff "$parameters" "${COMP_WORDS[*]}")

	    # If a new named argument is about to be entered, serve the list of options
	    [[ $cur == "" && $prev != "=" ]] && { offer "$remainings" ; return ; }

	    # Test all potential arguments and server corresponding values
	    if   param "account"         ; then offer_many "$(_accounts)"
	    elif param "excnodelist"     ; then offer_many "$(_nodes)"
	    elif param "nodelist"        ; then offer_many "$(_nodes)"
	    elif param "features"        ; then offer_many "$(_features)"
	    elif param "gres"            ; then offer_many "$(_gres)"
	    elif param "licences"        ; then offer_many "$(_licenses)"
	    elif param "partition"       ; then offer_many "$(_partitions)"
	    elif param "reservationname" ; then offer_many "$(_reservations)"
	    elif param "qos"             ; then offer_many "$(_qos)"
	    elif param "wckey"           ; then offer_many "$(wckeys)"
	    elif param "conn-type"       ; then offer_many "MESH TORUS NAV"
	    elif param "rotate"          ; then offer_many "yes no"
	    elif param "shared"          ; then offer_many "yes no"
	    else offer "$(sed 's/\=[^ ]*/\=/g' <<< $remainings)"
	    fi
	    ;;
	nodename)
	    local parameters="features=<features> gres=<gres> \
	       reason=<reason> state=<state> weight=<weight>"

	    remainings=$(compute_set_diff "$parameters" "${COMP_WORDS[*]}")

	    # If a new named argument is about to be entered, serve the list of options
	    [[ $cur == "" && $prev != "=" ]] && { offer "$remainings" ;
		    return ; }

	    # Test all potential arguments and server corresponding values
	    if param "features"   ; then offer_many "$(_features)"
	    elif param "gres"     ; then offer_many "$(_gres)"
	    elif param "state"    ; then offer_many "noresp drain fail future\
						     resume power_down\
						     power_up undrain"
	    else offer "$(sed 's/\=[^ ]*/\=/g' <<< $remainings)"
	    fi
	    ;;
	partitionname)
	    local parameters="allowgroups=<name> allocnodes=<node_list>\
			      alternate=<partition_name> default=yes|no\
			      defaulttime=d-h:m:s|unlimited defmempercpu=<MB>\
			      defmempercnode=<MB> disablerootjobs=yes|no\
			      gracetime=<seconds> hidden=yes|no\
			      maxmempercpu=<MB> maxmempercnode=<MB>\
			      maxnodes=<count> maxtime=d-h:m:s|unlimited\
			      minnodes=<count> nodes=<name>\
			      preemptmode=off|cancel|checkpoint|requeue|suspend\
			      priority=count rootonly=yes|no reqresv=<yes|no>\
			      shared=yes|no|exclusive|force\
			      state=up|down|drain|inactive"

	    remainings=$(compute_set_diff "$parameters" "${COMP_WORDS[*]}")
	    # If a new named argument is about to be entered, serve the list of options
	    [[ $cur == "" && $prev != "=" ]] && { offer "$remainings" ;
		    return ; }

	    # Test all potential arguments and server corresponding values
	    if   param "allocnodes"  ; then offer_many "$(_nodes)"
	    elif param "nodes"       ; then offer_many "$(_nodes)"
	    elif param "alternate"   ; then offer_many "$(_partitions)"
	    elif param "default"     ; then offer_many  "yes no"
	    elif param "preemptmode" ; then offer_many "off cancel checkpoint\
							requeue suspend"
	    elif param "shared"      ; then offer_many "yes no exclusive force"
	    elif param "state"       ; then offer_many "up down drain inactive"
	    elif param "disablerootjobs" ; then offer_many "yes no"
	    elif param "hidden"      ; then offer_many "yes no"
	    elif param "rootonly"    ; then offer_many "yes no"
	    elif param "reqresv"     ; then offer_many "yes no"
	    else offer "$(sed 's/\=[^ ]*/\=/g' <<< $remainings)"
	    fi
	    ;;
	reservationname)
	    local parameters="accounts=<account_list> corecnt=<num>\
			      duration=[days-]hours:minutes:seconds\
			      endtime=yyyy-mm-dd[thh:mm[:ss]]\
			      features=<feature_list>\
			      flags=maint,overlap,ignore_jobs,daily,weekly\
			      licenses=<license> nodecnt=<count>\
			      nodes=<node_list> users=<user_list>\
			      partitionname=<partition_list>\
			      starttime=yyyy-mm-dd[thh:mm[:ss]]"

	    remainings=$(compute_set_diff "$parameters" "${COMP_WORDS[*]}")
	    # If a new named argument is about to be entered, serve the list of options
	    [[ $cur == "" && $prev != "=" ]] && { offer "$remainings" ;
		    return ; }

	    # test all potential arguments and server corresponding values
	    if   param "accounts" ; then offer_many  "$(_accounts)"
	    elif param "licences" ; then offer_many "$(_licenses)"
	    elif param "nodes"    ; then offer_many "$(_nodes)"
	    elif param "features" ; then offer_many "$(_features)"
	    elif param "users"    ; then offer_many "$(_users)"
	    elif param "flags"    ; then offer_many "daily first_cores\
						     ignore_jobs license_only\
						     maint overlap part_nodes\
						     spec_nodes static_alloc\
						     time_float weekly"
	    elif param "partitioname" ; then offer_many "$(_partitions)"
	    else offer "$(sed 's/\=[^ ]*/\=/g' <<< $remainings)"
	    fi
	    ;;
	step)
	    local parameters="stepid=<job_id>[.<step_id>]\
			     CompFile=<completion_file> TimeLimit=<time>"

	    remainings=$(compute_set_diff "$parameters" "${COMP_WORDS[*]}")
	    [[ $cur == "" && $prev != "=" ]] && { offer "$remainings" ;
		    return ; }
	    if param "stepid" ; then offer_list "$(_step)" ;
	    fi
	    ;;

	esac
	;;
    create) # command object attribute1=value1 etc.
	parameters="partition reservation"

	param=$(find_first_occurence "${COMP_WORDS[*]}" "$parameters")
	[[ $param == "" ]] && { offer "$parameters" ; return ; }

	# Process object
	case $param in
	partition)
	    local parameters="allocnodes=<node_list> allowgroups=<name>\
			      alternate=<partition_name> default=yes|no\
			      defaulttime=d-h:m:s|unlimited defmempercpu=<MB>\
			      defmempercnode=<MB> disablerootjobs=yes|no\
			      gracetime=<seconds> hidden=yes|no\
			      maxmempercpu=<MB> maxmempercnode=<MB>\
			      maxnodes=<count> maxtime=d-h:m:s|unlimited\
			      minnodes=<count> nodes=<name>\
			      preemptmode=off|cancel|checkpoint|requeue|suspend\
			      priority=count rootonly=yes|no reqresv=<yes|no>\
			      shared=yes|no|exclusive|force\
			      state=up|down|drain|inactive"

	    remainings=$(compute_set_diff "$parameters" "${COMP_WORDS[*]}")
	    # If a new named argument is about to be entered, serve the list of options
	    [[ $cur == "" && $prev != "=" ]] && { offer "$remainings" ; return ; }
	    if   param "allocnodes"  ; then offer_many "$(_nodes)"
	    elif param "nodes"       ; then offer_many "$(_nodes)"
	    elif param "alternate"   ; then offer_many "$(_partitions)"
	    elif param "default"     ; then offer_many  "yes no"
	    elif param "preemptmode" ; then offer_many "off cancel checkpoint\
							requeue suspend"
	    elif param "shared"      ; then offer_many "yes no exclusive force"
	    elif param "state"       ; then offer_many "up down drain inactive"
	    elif param "disablerootjobs" ; then offer_many "yes no"
	    elif param "hidden"      ; then offer_many "yes no"
	    elif param "rootonly"    ; then offer_many "yes no"
	    elif param "reqresv"     ; then offer_many "yes no"
	    fi
	    ;;
	reservation)
	    local parameters="accounts=<account_list> corecnt=<num>\
	                      duration=[days-]hours:minutes:seconds\
	                      endtime=yyyy-mm-dd[thh:mm[:ss]]\
                              features=<feature_list>\
                              flags=maint,overlap,ignore_jobs,daily,weekly\
                              licenses=<license> nodecnt=<count>\
                              nodes=<node_list> users=<user_list>\
	                      partitionname=<partition_list>\
                              starttime=yyyy-mm-dd[thh:mm[:ss]]"

	    remainings=$(compute_set_diff "$parameters" "${COMP_WORDS[*]}")
	    # If a new named argument is about to be entered, serve the list of
	    # options
	    [[ $cur == "" && $prev != "=" ]] && { offer "$remainings" ;
		    return ; }

	    # Test all potential arguments and server corresponding values
	    if   param "accounts" ; then offer_many  "$(_accounts)"
	    elif param "licences" ; then offer_many "$(_licenses)"
	    elif param "nodes"    ; then offer_many "$(_nodes)"
	    elif param "features" ; then offer_many "$(_features)"
	    elif param "users"    ; then offer_many "$(_users)"
	    elif param "flags"    ; then offer_many "daily first_cores\
						     ignore_jobs license_only\
						     maint overlap part_nodes\
						     spec_nodes static_alloc\
						     time_float weekly"
	    elif param "partitioname" ; then offer_many "$(_partitions)"
	    else offer "$(sed 's/\=[^ ]*/\=/g' <<< $remainings)"
	    fi
	    ;;
	esac
	;;
    esac
}
complete -F _scontrol scontrol

_squeue()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-A -a -h -i -j -l -L -M -n -O -o -P -p -q -R -r -S -s\
			-t -u -V -v -w"
    local longoptions="--account<account_list> --all --array --noheader --help
		       --hide --iterate<seconds> --jobs<job_id_list> --long\
		       --licenses=<license_list> --clusters<string>\
		       --name=<name_list> --format<fmtstring>\
		       --Format<fmtstring> --partition<part_list>\
		       --priotity --qos<qos_list>\
		       --reservation=<reservation_name> --steps\
		       --sort<sort_list> --start --state<state_list> --usage\
		       --verbose --version --nodelist<hostlist>"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }

    if [[ $cur == *% ]] ;
    then
	offer "%a(Account) %A(NTasks) %b(gres) %c(mincpu) %C(Ncpus) %d(minTmp) \
	       %D(NNodes) %e(end) %E(dependency) %f(features) %g(group) %G(gID)\
	       %h(shared) %H(Nsockets) %i(id) %I(Ncores/socket) %j(name)\
	       %k(comment) %l(limit) %L(timeleft) %m(mem) %M(time) %n(reqnodes)\
	       %N(alloc_nodes) %O(contiguous) %p(priority) %r(reason)\
	       %R(reason) %s(selecplugin) %t(state) %T(state) \
	       %u(user) %U(uID) %v(reservation) %x(excnodes)" ;
	return;
    fi

    case $prev in
    --account|-A) offer_list "$(_accounts)" ;;
    --jobs|-j) offer_list "$(_jobs)" ;;
    --clusters|-M) offer_list "$(_clusters)" ;;
    --name|-n) offer_list "$(_jobname)" ;;
    --qos) offer_list "$(_qos)" ;;
    --user|-u) offer_list "$(_users)" ;;
    --state|-t) offer_list "pending running suspended completing completed" ;;
    --format|-o|--Format|-O) offer "\\\"%" ;;
    --partition|-p) offer_list "$(_partitions)" ;;
    --reservation|-R) offer_list "$(_reservation)" ;;
    --sort|-S) offer_list "\\\"%" ;;
    --nodelist|-w) offer_list "$(_nodes)" ;;
    esac
}
complete -F _squeue squeue

_scancel()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions=" -A -b -i -M -n -p -q -Q -R -s -t -u -v -V -w"
    local longoptions="--account<account_list> --batch --ctld --help\
		       --interactive --clusters<string> --name<job_name>\
		       --nodelist<hostlist> --partition<part_list>\
		       --qos<qos_list> --quiet --reservation<reservation_name>\
		       --signal<SIGXXX> --state<state_list> --user<user_list>\
		       --usage --verbose --version --wckeys<wckey>"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }

    case $prev in
    --account|-A) offer_list "$(_accounts)" ;;
    --clusters|-M) offer_list "$(_clusters)" ;;
    --name|-n) offer_list "$(_jobnames)" ;;
    --partition|-p) offer_list "$(_partitions)" ;;
    --qos) offer_list "$(_qos)" ;;
    --reservation|-R) offer_list "$(_reservations)" ;;
    --state) offer_list "pending running suspended completing completed" ;;
    --user|-u) offer_list "$(_users)" ;;
    --wckeys) offer_list "$(_wckeys)" ;;
    --nodelist|-w) offer_list "$(_nodes)" ;;
    *) offer_list "$(_jobs)";;
    esac
}
complete -F _scancel scancel

_sshare()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-A -a -h -l -M -p -P -u -v -V"
    local longoptions="--accounts<accounts> --all --noheader --long\
		       --clusters<string> --parsable --parsable2\
			--users<user_list> --verbose --version --help"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }

    case $prev in
    --accounts|-A) offer_list "$(_accounts)" ;;
    --clusters|-M) offer_list "$(_clusters)" ;;
    --users|-u) offer_list "$(_users)" ;;
    esac
}
complete -F _sshare sshare

_sbcast()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-C -f -F -j -p -s -t -v -V"
    local longoptions="--compress --force --fanout<number> --jobid<number>\
		       --preserve --size<bytes> --timeout<seconds> --verbose\
		       --version"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }

    _filedir
}
complete -F _sbcast sbcast

_sinfo()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-a -b -d -e -h -i -l -M -n -N -o -p -r -R -s -S -t -T\
			-v -V"
    local longoptions="--all --bgl --dead --exact --noheader --help --hide\
		       --iterate<seconds> --long --clusters<clusternames>\
		       --nodes<nodelist> --Node --format<fmtstr>\
		       --partition<partition> --responding --list-reasons\
		       --summarize --sort<sortlist> --state<statelist>\
		       --reservation --usage --verbose --version"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }

    if [[ $cur == *% ]] ;
    then
	offer "%all %a(Availability) %A(cpu_usage) %B(max_cpus)\
	       %c(cpus_per_node) %C(cpu_usage) %d(diskspace) %D(NNodes)\
	       %E(reason) %f(features) %F(nodes_usage) %g(group) %G(Gres)\
	       %h(shared) %H(timestamp) %l(time_limit) %L(default_time) %m(mem)\
	       %M(preemt_mode) %n(hostnames) %N(node_names) %o(node_addr)\
	       %O(cpu_load) %e(free_mem) %p(partition_prio) %P(partition) %r(root_jobs)\
	       %R(reason) %s(max_job_size) %S(allowed_allocating_nodes)\
	       %t(state) %T(state) %u(user) %U(uID) %w(weight)\
	       %X(sockets_per_node) %Y(cores_per_socket)\
	       %z(extend_process_info) %Z(threads_per_core)" ;
	return;
    fi

    case $prev in
    --clusters|-M) offer_list "$(_clusters)" ;;
    --nodes|-n) offer_list "$(_nodes)" ;;
    --partition|-p) offer_list "$(_partitions)" ;;
    --state) offer_list "pending running suspended completing completed" ;;
    --format|-o) offer "\\\"%" ;;
    esac
}
complete -F _sinfo sinfo

_sprio()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-h -j -l -M -n -o -u -v -V -w"
    local longoptions="--noheader --help --job<jobids> --long\
		       --clusters<clustername> --norm --format<fmtstr>\
		       --user<userlist> --usage --verbose --version --weights"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }

    if [[ $cur == *% ]] ;
    then
	offer "%a(n_age) %A(w_age) %f(n_fair-share) %F(w_fair-share) %i(JobId)\
	       %j(n_job_size) %J(w_job_size) %N(Nice adjustmen) %p(n_partition)\
	       %P(w_partition) %q(n_qos) %Q(w_qos) %u(User) %Y(priority)\
	       %y(n_priority)" ;
	return;
    fi

    case $prev in
    --jobs|-j) offer_list "$(_jobs)" ;;
    --clusters|-M) offer_list "$(_clusters)" ;;
    --format|-o) offer "\\\"%" ;;
    --user|-u) offer_list "$(_users)" ;;
    esac
}
complete -F _sprio sprio

_sacct()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-a -A -b -c -D -e -E -f -g -h -j -k -K -l -L -M -n\
			-N -o -p -P -q -r -s -S -T -u -v -V -W -x -X"
    local longoptions="--allusers --accounts<accountlist> --brief --completion\
	               --duplicates --helpformat --endtime<time> --file<path>\
                       --group<gidlist> --help --jobs<joblist>\
                       --timelimit-min<time> --timelimit-max<time> --long\
                       --allclusters --clusters<clusterlist> --noheader\
	               --nodeslist<nodes> --name=<jobname> --format<itemlist>\
                       --parsable --parsable2 --qos<qos>\
                       --partition<partitionlist> --state<statelist>\
	               --starttime<time> --truncate --user<userlist> --usage\
                       --verbose --version --wckeys<wckeyslist>\
                       --associations<assoclist> --allocations"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)";
	    return ; }

    case $prev in
    --accounts|-A) offer_list "$(_accounts)" ;;
    --group|--gid|-g) _gids ;;
    --jobs|-j) offer_list "$(_jobs)" ;;
    --clusters|-M) offer_list "$(_clusters)" ;;
    --nodes|-N) offer_list "$(_nodes)" ;;
    --name) offer_list "$(_jobs)" ;;
    --partition) offer_list "$(_partitions)" ;;
    --format|-o) offer_list "$(sacct -e)" ;;
    --state|-s) offer_list "pending running suspended completing completed" ;;
    --qos) offer_list "$(_qos)" ;;
    --user|-u) offer_list "$(_users)" ;;
    --wckeys|-W) offer_list "$(_wckeys)" ;;
    --associations|-x) offer_list "$(_associations)" ;;
    esac
}
complete -F _sacct sacct

_salloc()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-A -B -bb -C -c -d -D -F -g -h -H -I -J -k -K -L -m -n\
			-N -O -p -Q -R -s -S -t -u -V -v -W -w -x"
    local longoptions="--account=<account> --acctg-freq=<seconds>\
		       --extra-node-info=<sockets[:cores[:threads]]>\
		       --begin=<time> --bell --comment=<string>\
		       --constraint=<list> --contiguous\
		       --cores-per-socket=<number> --cpu-freq=<p1[-p2[:p3]]>\
		       --cpus-per-task=<ncpus> --dependency=<deplist>\
		       --chdir=<path> --exclusive=[user] --nodefile=<nodefile>\
		       --get-user-env --gid=<group> --gres=<list> --hold\
		       --help --hint=<type> --immediate=<seconds>\
		       --job-name=<jobname> --jobid=<jobid>\
		       --kill-command=[signal] --no-kill --licenses=<licenses>\
		       --distribution=<dist> --mail-type=<type>\
		       --mail-user=<email> --mem=<MB> --mem-per-cpu=<MB>\
		       --mem-bind=<type> --mincpus=<number>\
		       --nodes=<minnodes[-maxnodes]> --ntasks=<number>\
		       --network=<type> --nice=<[adjustment]>\
		       --ntasks-per-core=<number> --ntasks-per-socket=<ntasks>\
		       --ntasks-per-node=<ntasks> --no-bell --no-shell\
		       --overcommit --power=<flags> --priority=<value>\
		       --profile=<all|none|energy|task|lustre|network>\
		       --partition=<partitionname> --quiet --qos=<qos> --reboot\
		       --reservation=<name> --share --core-spec=<num>\
		       --signal=<sig_num><sig_time>\
		       --sockets-per-node=<number>\
		       --switches=<count><max-time> --time-min=<time>\
		       --threads-per-core=<number> --time-min=<time>\
		       --tmp=<MB> --usage --uid=<user> --version --verbose\
		       --wait=<seconds> --nodelist=<nodelist>\
		       --wait-all-nodes=<0|1> --wckey=<wckey>\
		       --exclude=<nodelist> --blrts-image=<path>\
		       --cnload-image=<path> --conn-type=<type>
		       --geometry=<XxYxZ> --ioload-image=<path>\
		       --linux-image=<path> --mloader-image=<path> --no-rotate\
		       --ramdisk-image=<path>"


    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)";
	    return ; }

    case $prev in
    --account|-A) offer_list "$(_accounts)" ;;
    --constraint|-C) offer_list "$(_features)" ;;
    --cpu-bind) offer_list "none rank map_cpu: mask_cpu: sockets cores \
			     threads ldoms" ;;
    --dependency) offer_list "after: afterany: afternotok:
			     afterok: expand: singleton" ;;
    --gid) _gids ;;
    --partition|-p) offer_list "$(_partitions)" ;;
    --gres) offer_list "$(_gres)" ;;
    --hint) offer "compute_bound memory_bound multithread nomultithread" ;;
    --jobid) offer_list "$(_jobs)" ;;
    --licenses|-L) offer_list "$(_licenses)" ;;
    --distribution|-m) offer "block cyclic plane arbitrary" ;;
    --mail-type) offer_list "BEGIN END FAIL REQUEUE ALL STAGE_OUT TIME_LIMIT\
			     TIME_LIMIT_90 TIME_LIMIT_80 TIME_LIMIT_50" ;;
    --mem-bind) offer "none rank local map_mem: mask_mem:" ;;
    # TODO --network) _configured_interfaces ;;
    --reservation) offer_list "$(_reservations)" ;;
    --clusters) offer_list "$(_clusters)" ;;
    --nodelist) offer_list "$(_nodes)" ;;
    --exclude) offer_list "$(_nodes)" ;;
    --qos) offer_list "$(_qos)" ;;
    :|afterany|after|afternotok|afterok|expand) offer_list "$(_jobs)" ;;
    --profile) offer_list "all none energy task lustre network" ;;
    --wait-all-nodes) offer_list "1 0" ;;
    --conn-type) offer_list "MESH TORUS NAV" ;;
    esac
    #TODO options for blue gene systems
}
complete -F _salloc salloc

_sbatch()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-a -A -B -C -c -d -D -e -F -g -h -H -i -I -J -k -K -L\
			-m -M -n -N -o -O -p -Q -s -S -t -u -v -V -w -x -q -R"
    local longoptions="--array<indexes> --account<account>\
		       --acctg-freq<seconds>\
		       --extra-node-info<sockets[:cores[:threads]]>\
		       --bb<spec> --begin=<time> --checkpoint<time>\
		       --checkpoint-dir<directory> --comment<string>\
		       --constraint<list> --contiguous\
		       --cores-per-sopcket<number> --cpus-per-task<number>\
		       --dependency<deplist> --workdir<directory>\
		       --error<filename pattern> --exclusive<user>\
		       --export<environment variables|ALL|NONE>\
		       --export-file<filename|fd> --nodefile<nodefile>\
		       --get-user-env --gid<group>\
		       --gres<list> --hold --help --hint<type> --immediate\
		       --ignore-pbs --input<filename>\
		       --job-name<jobname> --jobid<jobid> --no-kill\
		       --licenses<license> --clusters<string>\
		       --distribution<dist>\
		       --mail-type<type> --mail-user<user> --mem<MB>\
		       --mem-per-cpu<MB> --mem-bind<type> --mincpus<n>\
		       --nodes<minnodes[-maxnodes]> --ntasks<number>\
		       --network<type> --nice[adjustment] --no-requeue\
		       --ntasks-per-core<ntasks>  --ntasks-per-socket<ntasks>\
		       --ntasks-per-node<ntasks> --overcommit\
		       --output<filename> --open-mode\
		       --parsable --partition<partition_names> --power<flags>\
		       --priority<value>\
		       --profile<type>\
		       --propagate<limit> --quiet --qos<qos> --reboot\
		       --requeue --reservation<name> --share --core-spec<num>\
		       --sicp --signal<signal> --sockets-per-node<sockets>\
		       --switches<type> --time<time>\
		       --tasks-per-node<n> --test-only\
		       --threads-per-core<threads> --time-min<time>\
		       --tmp<MB> --usage --uid=<user> --version --verbose\
		       --nodelist<node name list> --wait-all-nodes<value>\
		       --wckey<wckey> --wrap<command string>\
		       --exclude<node name list> --blrts-image<path>\
		       --cnload-image<path> --conn-type<type>\
		       --geometry<XxYxZ> --ioload-image<path>\
		       --linux-image<path> --mloader-image<path> --no-rotate\
		       --ramdisk-image<path>"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }

    case $prev in
    --account|-A) offer_list "$(_accounts)" ;;
    --constraint|-C) offer_list "$(_features)" ;;
    --cpu-bind) offer "none rank map_cpu: mask_cpu: sockets \
			    cores threads ldoms" ;;
    --dependency|-d) offer "after: afterany: afternotok: \
			    afterok: expand: singleton" ;;
    --gid) _gids ;;
    --partition|-p) offer_list "$(_partitions)" ;;
    --gres) offer_list "$(_gres)" ;;
    --hint) offer "compute_bound memory_bound multithread \
			nomultithread" ;;
    --jobid) offer_list "$(_jobs)" ;;
    --licenses|-L) offer_list "$(_licenses)" ;;
    --distribution|-m) offer_list "block cyclic plane arbitrary" ;;
    --mail-type) offer_list "BEGIN END FAIL REQUEUE ALL STAGE_OUT TIME_LIMIT\
			     TIME_LIMIT_90 TIME_LIMIT_80 TIME_LIMIT_50" ;;
    --mem-bind) offer "quiet verbose none rank local map_mem: mask_mem:" ;;
    --mpi) offer "openmpi pmi2 pmix none" ;;
    --propagate) offer_list "all as core cpu data fsize memlock \
			      nofile nproc rss stack" ;;
    # TODO --network) _configured_interfaces ;;
    --reservation) offer_list "$(_reservations)" ;;
    --clusters|-M) offer_list "$(_clusters)" ;;
    --nodelist|-w) offer_list "$(_nodes)" ;;
    --exclude|-x) offer_list "$(_nodes)" ;;
    --qos) offer_list "$(_qos)" ;;
    :|afterany|after|afternotok|afterok) offer_list "$(_jobs)" ;;
    --profile) offer_list "all none energy task lustre network" ;;
    --propagate) offer_list "ALL AS CORE CPU DATA FSIZE MEMLOCK NOFILE NPROC\
			     RSS STACK" ;;
    --wait-all-nodes) offer_list "1 0" ;;
    *)  _filedir
    esac
    #TODO options for blue gene systems
}
complete -o filenames -F _sbatch sbatch

_srun()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-A -B -C -c -d -D -e -E -g  -h -H -i -I -J -k -K -l -L\
                        -m -N -n -o -O -p\
			-q -Q -r -s -S -t -T -u -V -v -W -w -x"
    local longoptions=" --account<account> --acctg-freq\
			--extra-node-info<spec>\
			--bb<spec> --begin<time> --checkpoint<time>\
			--checkpoint-dir<directory> --comment<string>\
			--constraint<list> --contiguous\
			--cores-per-socket<cores> --cpu-bind=<type>\
			--cpu-freq<freq> --cpus-per-task<ncpus>\
			--dependency=<dependency_list> --chdir=<path>\
			--error<mode> --preserve-env --epilog<executable>\
			--exclusive=<user> --export<var> --gid<group>\
			--gres<list> --hold --help --hint<type>\
			--immediate=<seconds> --input<mode>\
			--job-name<jobname> --jobid<jobid>\
			--kill-on-bad-exit<0|1> --no-kill --launch-cmd\
			--launcher-opts<options> --label --licenses<license>\
			--distribution<type> --mail-type<type>\
			--mail-user<user> --mem<MB> --mem-per-cpu<MB>\
			--mem-bind<type> --mincpus<n> --msg-timeout<seconds>\
			--mpi<mpi_type> --multi-prog\
			--nodes<minnodes[-maxnodes]> --ntasks<number>\
			--network<type> --nice<adjustment>\
			--ntasks-per-core<ntasks> --ntasks-per-node<ntasks>\
			--ntasks-per-socket<ntasks> --overcommit\
			--output<mode> --open-mode<append|truncate>\
			--partition<partition_names> --power<flags>\
			--priority<value> --profile<type> --prolog<executable>\
			--propagate<limits> --pty --quiet --quit-on-interrupt\
			--qos<qos> --relative<n> --reboot --resv-ports\
			--reservation<name> --restart-dir<directory> --share\
			--core-spec<num> --sicp --signal=<num>\
			--slurmd-debug<level> --sockets-per-node<sockets>\
			--switches<type> --threads<nthreads> --time<time>\
			--task-epilog<executable> --task-prolog<executable>\
			--test-only --threads-per-core<threads>\
			--time-min<time> --tmp<MB> --unbuffered --usage\
			--uid<user> --version --verbose --wait<seconds>\
			--nodelist=<hostlist> --wckey<wckey> --disable-status\
			--exclude=<hostlist> --no-allocate --blrts-image<path>\
			--cnload-image<path> --conn-type<type>\
			--geometry<XxYxZ> --ioload-image<path>\
			--linux-image<path> --mloader-image<path> --no-rotate\
			--ramdisk-image<path>
"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }

    case $prev in
    --account|-A) offer_list "$(_accounts)" ;;
    --begin) offer $(date -dtomorrow +"%Y-%m-%d");;
    --chdir|--restart-dir|--checkpoint-dir) _filedir ;;
    --clusters) offer_list "$(_clusters)" ;;
    --constraint|-C) offer_list "$(_features)" ;;
    --cpu-bind) offer "none rank map_cpu: mask_cpu: sockets \
			   cores threads ldoms" ;;
    --dependency|-d) offer "after: afterany: afternotok: afterok: expand:\
			    singleton" ;;
    :|afterany|after|afternotok|afterok) offer_list "$(_jobs)" ;;
    --exclude|-x) offer_list "$(_nodes)" ;;
    --gid) _gids ;;
    --gres) offer_list "$(_gres)" ;;
    --hint) offer "compute_bound memory_bound multithread \
		       nomultithread" ;;
    --job-name|-J) "$(_jobname)" ;;
    --jobid) offer_list "$(_jobs)" ;;
    --distribution|-m) offer "block cyclic plane arbitrary pack nopack" ;;
    --licenses|-L) offer_list "$(_licenses)" ;;
    --mail-type) offer_list "begin end fail requeue all" ;;
    --mem-bind) offer "none rank local map_mem: mask_mem:" ;;
    --mpi) offer "openmpi pmi2 pmix none" ;;
    --partition|-p) offer_list "$(_partitions)" ;;
    --profile) offer_list "all none energy task filesystem network" ;;
    --propagate) offer_list "all as core cpu data fsize memlock \
			      nofile nproc rss stack" ;;
    --qos) offer_list "$(_qos)" ;;
    --reservation) offer_list "$(_reservations)" ;;
    --slurmd-debug) offer_list "quiet fatal error info verbose" ;;
    --nodefile) _filedir ;;
    # TODO --network) _configured_interfaces ;;
    --prolog|--task-epilog|--task-prolog) _filedir ;;
    --nodelist|-w) offer_list "$(_nodes)" ;;
    --open-mode) offer "append truncate" ;;
    --wait-all-nodes) offer_list "1 0" ;;
    --conn-type) offer_list "MESH TORUS NAV" ;;
    *)  COMPREPLY=( $( compgen -c -- "$cur" ) ) ; return
    esac
    #TODO options for blue gene systems
}
complete -F _srun srun

_sattach()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions=" -h -l -Q -u -V -v"
    local longoptions=" --help --input-filter[=]<task number>\
                        --output-filter[=]<task number>\
                        --error-filter[=]<task number> --label --layout --pty\
                        --quiet --usage --version --verbose"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }
}
complete -F _sattach sattach

_sdiag()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-a -h -i -r -t -T -V"
    local longoptions="--all --help --sort-by-id --reset --sort-by-time\
                       --sort-by-time2 --usage --version"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }
}
complete -F _sdiag sdiag

_sstat()
{
    _get_comp_words_by_ref cur prev words cword
    _split_long_opt

    local shortoptions="-a -e -h -i -j -n -o -p -P -v -V"
    local longoptions="--allstep --helpformat --help --pidformat --jobs\
                       --noheader --format --parsable --parsable2 --usage\
                       --verbose --version"

    [[ $cur == - ]] && { offer "$shortoptions" ; return ; }
    [[ $cur == -- ]] && { offer "$longoptions" ; return ; }
    [[ $cur == --* ]] && { offer "$(sed 's/<[^>]*>//g' <<< $longoptions)"; return ; }
}
complete -F _sstat sstat
# vim: sw=4:ts=4:expandtab
