# vyatta bash operational mode completion
# **** License ****
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
# 
# This program 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.
# 
# This code was originally developed by Vyatta, Inc.
# Portions created by Vyatta are Copyright (C) 2006, 2007 Vyatta, Inc.
# All Rights Reserved.
# 
# Author: Tom Grennan
# Date: 2007
# Description: setup bash completion for Vyatta operational commands
# 
# **** End License ****

test -z "$_vyatta_less_options" && \
    declare -r _vyatta_less_options="\
	--QUIT-AT-EOF\
	--quit-if-one-screen\
	--RAW-CONTROL-CHARS\
	--squeeze-blank-lines\
	--no-init"
test -z "$_vyatta_default_pager" && \
    declare -r _vyatta_default_pager="less \
	--buffers=64\
	--auto-buffers\
	--no-lessopen\
	$_vyatta_less_options"
declare -x VYATTA_PAGER=$_vyatta_default_pager

_vyatta_op_do_key_bindings ()
{
  if [ "$SHELL" != "/bin/vbash" ]; then
    # only do bindings if vbash
    return
  fi

  bind '"?": possible-completions' 2> /dev/null
  bind 'set show-all-if-ambiguous on' 2> /dev/null

  nullglob_save=$(shopt -p nullglob)
  shopt -u nullglob
  bind_cmds=$(grep '^bind .* # vyatta key binding$' $HOME/.bashrc)
  eval $bind_cmds
  eval $nullglob_save
}

_vyatta_op_do_key_bindings

test -f /etc/default/vyatta && \
    source /etc/default/vyatta

test ! -d "$vyatta_op_templates" && \
    return 0

declare -r _vyatta_op_last_comp_init='>>>>>>LASTCOMP<<<<<<'
declare _vyatta_op_last_comp=${_vyatta_op_last_comp_init}
declare _vyatta_op_node_path
declare -a _vyatta_op_noncompletions _vyatta_op_completions
declare -x -a _vyatta_pipe_noncompletions _vyatta_pipe_completions
declare _vyatta_comptype
declare -x -a reply
declare -a _vyatta_operator_allowed

if [[ "$VYATTA_USER_LEVEL_DIR" != "/opt/vyatta/etc/shell/level/admin" ]]; then
   _vyatta_operator_allowed=( $(cat $VYATTA_USER_LEVEL_DIR/allowed-op) )
fi

#source /etc/bash_completion.d/vyatta-op-run

declare -a functions
functions=( /opt/vyatta/share/vyatta-op/functions/interpreter/* )

for file in "${functions[@]}";do
  source $file;
done

# $1: label
# #2...: strings
_vyatta_op_debug ()
{
    echo -ne \\n$1:
    shift
    for s ; do
	echo -ne " \"$s\""
    done
}

# this is needed to provide original "default completion" behavior.
# see "vyatta-cfg" completion script for details.
_vyatta_op_default_expand ()
{
  local wc=${#COMP_WORDS[@]}
  if (( wc < 2 )); then
    _vyatta_op_expand
  else
    # after the first word => cannot be vyatta command so use original default
    compopt -o filenames
    _filedir_xspec
  fi
}

# $1: label
# $2...: help
_vyatta_op_print_help ()
{
    local label=$1 help=$2

    if [ ${#help} -eq 0 ] ; then
	echo -ne "\n  $label"
    elif [ ${#label} -lt 6 ] ; then
	echo -ne "\n  $label\t\t$help"
    elif [ ${#label} -lt 14 ] ; then
	echo -ne "\n  $label\t$help"
    else
	echo -ne "\n  $label\n\t\t$help"
    fi
}

# $1: $cur
# $2...: possible completions
_vyatta_op_help ()
{
    local cur=$1; shift
    local ndef node_tag_help node_run help last_help

    ndef=${_vyatta_op_node_path}/node.tag/node.def
    [ -f $ndef ] && \
	node_tag_help=$( _vyatta_op_get_node_def_field $ndef help )

    ndef=${_vyatta_op_node_path}/node.def
    [ -f $ndef ] && \
	node_run=$( _vyatta_op_get_node_def_field $ndef run )

    echo -en "\nPossible completions:"
    if [ -z "$cur" -a -n "$node_run" ]; then
        _vyatta_op_print_help '<Enter>' "Execute the current command"
    fi
    for comp ; do
	if [ -z "$comp" ] ; then
	    if [ "X$node_tag_help" == "X$last_help" ] ; then
		help=""
	    else
		last_help=$node_tag_help
		help=$node_tag_help
	    fi
	    _vyatta_op_print_help '*' "$help"
	elif [[ -z "$cur" || $comp == ${cur}* ]] ; then
	    ndef=${_vyatta_op_node_path}/$comp/node.def
	    if [ -f $ndef ] ; then
		help=$( _vyatta_op_get_node_def_field $ndef help )
	    else
		help=$node_tag_help
	    fi
	    if [ "X$help" == "X$last_help" ] ; then
		help=""
	    else
		last_help=$help
	    fi
	    _vyatta_op_print_help "$comp" "$help"
	fi
    done
}

_vyatta_op_set_node_path ()
{
    local node
    _vyatta_op_node_path=$vyatta_op_templates
    for (( i=0 ; i<COMP_CWORD ; i++ )) ; do
        # expand the command so completion continues to work with short versions
        node=$(_vyatta_op_conv_node_path $_vyatta_op_node_path ${COMP_WORDS[i]})
	if [ -f "${_vyatta_op_node_path}/$node/node.def" ] ; then
	    _vyatta_op_node_path+=/$node
	elif [ -f ${_vyatta_op_node_path}/node.tag/node.def ] ; then
	    _vyatta_op_node_path+=/node.tag
	else
	    return 1
	fi
    done
}

_vyatta_op_set_completions ()
{
    local -a allowed completions

    for ndef in ${_vyatta_op_node_path}/*/node.def ; do
	if [[ $ndef == */node.tag/node.def ]] ; then
	    local acmd=$( _vyatta_op_get_node_def_field $ndef allowed )
	    local -a a=($( eval "$acmd" ))
	    if [ ${#a[@]} -ne 0 ] ; then
		allowed+=( "${a[@]}" )
	    else
		allowed+=( "" )
	    fi
	else
	    local sdir=${ndef%/*}
	    allowed+=( ${sdir##*/} )
	fi
    done

    # donot complete entries like <HOSTNAME> or <A.B.C.D>
    _vyatta_op_noncompletions=( )
    completions=( )
    for (( i=0 ; i<${#allowed[@]} ; i++ )) ; do
	if [[ "${allowed[i]}" == \<*\> ]] ; then
	    _vyatta_op_noncompletions+=( ${allowed[i]} )
	else
      if [[ "$VYATTA_USER_LEVEL_DIR" == "/opt/vyatta/etc/shell/level/admin" ]]; then
	      completions+=( ${allowed[i]} )
      elif is_elem_of ${allowed[i]} _vyatta_operator_allowed; then
        completions+=( ${allowed[i]} )
      elif [[ $_vyatta_op_node_path == $vyatta_op_templates ]];then
        continue
      else 
        completions+=( ${allowed[i]} )
      fi
	fi
    done

    _vyatta_op_completions=($( printf "%s\n" ${completions[@]} | sort -u ))
}

_vyatta_op_comprely_needs_ambiguity ()
{
    local -a uniq

    [ ${#COMPREPLY[@]} -eq 1 ] && return

    uniq=( `printf "%s\n" ${COMPREPLY[@]} | cut -c1 | sort -u` )

    [ ${#uniq[@]} -eq 1 ] && return
    false
}

_vyatta_op_expand ()
{
    local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; )
    shopt -s extglob nullglob
    local cur=""
    local _has_comptype=0
    _vyatta_comptype=""

    if (( ${#COMP_WORDS[@]} > 0 )); then
        cur=${COMP_WORDS[COMP_CWORD]}
    else
        (( COMP_CWORD = ${#COMP_WORDS[@]} ))
    fi

    if _vyatta_pipe_completion "${COMP_WORDS[@]}"; then
      if [ "${COMP_WORDS[*]}" == "$_vyatta_op_last_comp" ] ; then
          _vyatta_do_pipe_help
          COMPREPLY=( "" " " )
          _vyatta_op_last_comp=${_vyatta_op_last_comp_init}
      else
          COMPREPLY=( "${_vyatta_pipe_completions[@]}" )
          _vyatta_op_last_comp="${COMP_WORDS[*]}"
      fi
      eval "$restore_shopts"
      return
    fi
    
    if [ "${COMP_WORDS[*]}" != "$_vyatta_op_last_comp" ] ; then
	if ! _vyatta_op_set_node_path ; then
	    echo -e \\a
	    COMPREPLY=( "" " " )
	    eval "$restore_shopts"
	    return 1
	fi
        _vyatta_set_comptype
        case $_vyatta_comptype in
          'imagefiles') 
              _has_comptype=1
              _vyatta_image_file_complete
          ;;
          *)
              _has_comptype=0
              _vyatta_op_set_completions
          ;;
        esac
    fi
    if [[ $_has_comptype == 1 ]]; then
        COMPREPLY=( "${_vyatta_op_completions[@]}" )
    else
        COMPREPLY=($( compgen -W "${_vyatta_op_completions[*]}" -- $cur ))
    fi

    # if the last command line arg is empty and we have
    # an empty completion option (meaning wild card),
    # append a blank(s) to the completion array to force ambiguity
    if [ -z "$cur" ] ; then
	for comp ; do
	    if [ -z "$comp" ] ; then
		if [ ${#COMPREPLY[@]} -eq 0 ] ; then
		    COMPREPLY+=( " " "" )
		elif _vyatta_op_comprely_needs_ambiguity ; then
		    COMPREPLY+=( " " )
		fi
	    fi
	done
    fi

    if [ "${COMP_WORDS[*]}" == "$_vyatta_op_last_comp" ] ; then
	  _vyatta_op_help "$cur" \
	      ${_vyatta_op_noncompletions[@]} \
	      ${_vyatta_op_completions[@]} \
	      | ${VYATTA_PAGER:-cat}
	  COMPREPLY=( "" " " )
	_vyatta_op_last_comp=${_vyatta_op_last_comp_init}
    else
	_vyatta_op_last_comp="${COMP_WORDS[*]}"
    fi

    eval "$restore_shopts"
}

# "pipe" functions
count ()
{
  wc -l
}

match ()
{
  grep -E -e "$1"
}

no-match ()
{
  grep -E -v -e "$1"
}

no-more ()
{
  cat
}

# pipe command help
# $1: command
_vyatta_pipe_help ()
{
  local help="No help text available"
  case "$1" in
    count) help="Count the number of lines in the output";;
    match) help="Only output lines that match specified pattern";;
    no-match) help="Only output lines that do not match specified pattern";;
    more) help="Paginate the output";;
    no-more) help="Do not paginate the output";;
    '<pattern>') help="Pattern for matching";;
  esac
  echo -n "$help"
}

_vyatta_do_pipe_help ()
{
  local help=''
  if (( ${#_vyatta_pipe_completions[@]} + ${#_vyatta_pipe_noncompletions[@]}
       == 0 )); then
    return
  fi
  echo -en "\nPossible completions:"
  for comp in "${_vyatta_pipe_completions[@]}" \
              "${_vyatta_pipe_noncompletions[@]}"; do
    _vyatta_op_print_help "$comp" "$(_vyatta_pipe_help "$comp")"
  done
}

# pipe completion
# $@: words
_vyatta_pipe_completion ()
{
  local -a pipe_cmd=()
  local -a all_cmds=( 'count' 'match' 'no-match' 'more' 'no-more' )
  local found=0
  _vyatta_pipe_completions=()
  _vyatta_pipe_noncompletions=()

  for word in "$@"; do
    if [ "$found" == "1" -o "$word" == "|" ]; then
      pipe_cmd+=( "$word" )
      found=1
    fi
  done
  if (( found == 0 )); then
    return 1
  fi
  if (( ${#pipe_cmd[@]} == 1 )); then
    # "|" only
    _vyatta_pipe_completions=( "${all_cmds[@]}" )
    return 0
  fi
  if (( ${#pipe_cmd[@]} == 2 )); then
    # "|<space, chars, or space+chars>"
    _vyatta_pipe_completions=($(compgen -W "${all_cmds[*]}" -- ${pipe_cmd[1]}))
    return 0
  fi
  if (( ${#pipe_cmd[@]} == 3 )); then
    # "|<chars or space+chars><space or space+chars>"
    case "${pipe_cmd[1]}" in
      match|no-match) _vyatta_pipe_noncompletions=( '<pattern>' );;
    esac
    return 0
  fi
  return 0
}

# comptype
_vyatta_set_comptype () 
{
  local comptype
  unset _vyatta_comptype
    for ndef in ${_vyatta_op_node_path}/*/node.def ; do
        if [[ $ndef == */node.tag/node.def ]] ; then
            local comptype=$( _vyatta_op_get_node_def_field $ndef comptype )
            if [[ $comptype == "imagefiles" ]] ; then
                _vyatta_comptype=$comptype
                return 0
            else
                _vyatta_comptype=""
                return 1
            fi
        else
            _vyatta_comptype=""
            return 1
        fi
    done
}

nullglob_save=$( shopt -p nullglob )
shopt -s nullglob
for f in ${vyatta_datadir}/vyatta-op/functions/allowed/* ; do
    source $f
done
eval $nullglob_save
unset nullglob_save

# don't initialize if we are in configure mode
if [ "$_OFR_CONFIGURE" == "ok" ]; then
  return 0
fi

if [[ "$VYATTA_USER_LEVEL_DIR" != "/opt/vyatta/etc/shell/level/admin" ]]; then
  vyatta_unpriv_init $@
else
  _vyatta_op_init $@
fi

###  Local Variables:
###  mode: shell-script
###  End: