# OpenIndiana svcadm(1M) & svcs(1) completions              -*- shell-script -*-
# ------------------------------------------------------------------------------
# Copyright 2006 Yann Rouillard <yann@opencsw.org>
# Portions copyright 2013, Nexenta Systems, Inc.
# Copyright (c) 2018, Michal Nowak <mnowak@startmail.com>

# svcadm accepts any complete FMRI or abbreviated FMRI
#   - a complete FMRI is svc:/foo/bar/bar/baz,
#   - abbreviated FMRI are foo/bar/bar/baz, bar/bar/baz, bar/baz or baz.
#
# The goal of this function is to propose all alternatives,
# but to not clutter the interface with all completions, we will only
# cut every completion alternative at the next slash.
#
# For exeaple, if the user types <nothing><tab>, for the svc://foo/bar/bar/baz
# we will propose the following completion: foo/, bar/, and baz.
# If the user types <b><tab>, we will propose: bar/ and baz.
# If the user types <bar/><tab>, we will propose: bar/bar/ and bar/baz.
#
# By default, the function proposes only abbreviated completions except
# if the user already types 'svc:'. In that case we will propose only
# the complete FMRI beginning with the pattern.

# Provide running zone names
_gen_zoneadm_list()
{
    if [[ ${prev} =~ "-z" ]]; then
        local zones="$(zoneadm list -c | grep -v '^global$')"
        COMPREPLY=( $(compgen -W "${zones}" -- ${cur}) )
    fi
}

_smf_complete_fmri()
{
    local cur="$1" prefix="$2"
    local cur_prefix fmri fmri_list=""
    local exact_mode pattern

    if [[ "$cur" == $prefix* ]]; then
        [[ "$cur" == $prefix ]] && cur+="/"
        pattern="$cur*"
        exact_mode=1
    else
        pattern="$prefix*/$cur*"
    fi

    cur_prefix="${cur%"${cur##*/}"}"

    for fmri in $(svcs -H -o FMRI "$pattern" 2>/dev/null); do
        local fmri_part_list fmri_part
        if [[ -z "$exact_mode" ]]; then
            fmri=${fmri#$prefix/}

            # We generate all possibles abbreviations for the FMRI
            # no need to have a generic loop as we will have a finite
            # number of components.
            local OIFS="$IFS"; IFS="/"; set -- $fmri; IFS="$OIFS"
            case $# in
                1) fmri_part_list=" $1";;
                2) fmri_part_list=" $2 $1/$2";;
                3) fmri_part_list=" $3 $2/$3 $1/$2/$3";;
                4) fmri_part_list=" $4 $3/$4 $2/$3/$4 $1/$2/$3/$4";;
            esac
        else
            fmri_part_list="$fmri"
        fi

        # Here we make sure the completions begin with the pattern and
        # we cut them at the first slash.
        for fmri_part in $fmri_part_list; do
            [[ "$fmri_part" == $cur* ]] || continue
            local first_part=${fmri_part#$cur_prefix}
            first_part=$cur_prefix${first_part%%/*}
            [[ "$first_part" != "$fmri_part" ]] && first_part+="/"
            fmri_list+=" $first_part"
        done
    done

    COMPREPLY=( $fmri_list )

    # Here we want to detect if there is only one completion proposed and that
    # it ends with a slash. That means the user will still have to complete
    # after, so we save him one tab keystroke by immediately proposing the
    # next completion alternatives.
    local i=${#COMPREPLY[*]}
    if [[ $i -gt 0 ]] && [[ "${COMPREPLY[$((--i))]}" == */ ]]; then
        # We have to iterate throught the list as we may have duplicates.
        while [[ $i -ne 0 ]]; do
            [[ "${COMPREPLY[$i]}" != "${COMPREPLY[$((i - 1))]}" ]] && break
            ((i--))
        done
        if [[ $i -eq 0 ]]; then
            _smf_complete_fmri "${COMPREPLY[0]}" "$prefix"
            return
        fi
    fi

    # Work-around bash_completion issue where bash interprets a colon
    # as a separator, borrowed from maven completion code which borrowed
    # it from darcs completion code :).
    local colonprefixes=${cur%"${cur##*:}"}
    local i=${#COMPREPLY[*]}
    while [ $((--i)) -ge 0 ]; do
        COMPREPLY[$i]=${COMPREPLY[$i]#"$colonprefixes"}
    done
}

_svcadm()
{
    local cur prev words cword line
    line="${COMP_LINE}"
    _init_completion -n : || return

    local states="uninitialized offline online degraded maintenance disabled legacy-run"
    local command_list="enable disable restart refresh clear mark milestone"
    local command i

    for (( i=1; i < $cword; i++ )); do
        if [[ ${words[i]} == @(enable|disable|restart|refresh|clear|mark|milestone) ]]; then
            command=${words[i]}
        fi
    done

    if [[ -z "$command" ]]; then
        # svcadm [-S state]...
        if [[ ${prev} == -S ]]; then
            COMPREPLY=( $(compgen -W "${states}" -- ${cur}) )
        elif [[ ${cur} == -* ]]; then
            if [[ "$(zonename)" == "global" ]]; then
                zones="-Z -z"
            fi
            COMPREPLY=( $(compgen -W "-S -v ${zones}" -- ${cur}) )
        else
            # svcadm [-v] milestone [-d] milestone_FMRI
            if [[ ${line} =~ -S ]]; then
                command_list="$(echo ${command_list} | sed -e 's/milestone//')"
            fi
            COMPREPLY=( $(compgen -W "${command_list}" -- ${cur}) )
        fi
    else
        if [[ ${cur} == -* ]]; then
            case "$command" in
                enable)
                    # enable [-rst]
                    COMPREPLY=( $(compgen -W "-r -s -t" -- ${cur}) );;
                disable)
                    # disable [-st]
                    COMPREPLY=( $(compgen -W "-s -t" -- ${cur}) );;
                mark)
                    # mark [-It]
                    COMPREPLY=( $(compgen -W "-I -t" -- ${cur}) );;
                milestone)
                    # milestone [-d]
                    COMPREPLY=( $(compgen -W "-d" -- ${cur}) );;
            esac
        else
            if [[ "$command" == "mark" ]] && [[ "$prev" != @(degraded|maintenance) ]]; then
                COMPREPLY=( $(compgen -W "degraded maintenance" -- ${cur}) )
            elif [[ "$command" == "milestone" ]]; then
                _smf_complete_fmri "${cur}" "svc:/milestone"
            else
                _smf_complete_fmri "${cur}" "svc:"
            fi
        fi
    fi

    _gen_zoneadm_list
}

_svcs()
{
    local cur prev
    local svcs_cols="ctid desc fmri inst nsta nstate scope svc sta state stime"

    cur=${COMP_WORDS[COMP_CWORD]}
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    case "$prev" in
        -o|-s|-S)
            # -o col[,col]... -s col -S col
            COMPREPLY=($(compgen -W "$svcs_cols" -- "${cur##*,}"))
            local existing_opts=$(expr "$cur" : '\(.*,\)')
            if [[ -n "$existing_opts" ]]; then
                COMPREPLY=( "${COMPREPLY[@]/#/${existing_opts}}" )
            fi
            if [[ ${prev} == -o ]]; then
                compopt -o nospace
            fi
            return
            ;;
    esac

    if [[ $cur == -* ]]; then
        # zone options are usable only in global zone
        if [[ "$(zonename)" == "global" ]]; then
            zones="-Z -z"
        fi
        COMPREPLY=( $(compgen -W "-? -a -H -p -o -s -S -d -D -R -l -L -v -x $zones" -- "${cur}") )
    else
        _smf_complete_fmri "${cur}" "svc:"
    fi

    _gen_zoneadm_list
}

complete -F _svcadm svcadm
complete -F _svcs svcs

# ex: filetype=sh
# vim: tabstop=2 shiftwidth=2 expandtab smartindent
