# 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

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

# $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
}

_vyatta_op_init ()
{
    # empty and default line compeletion
    complete -E -F _vyatta_op_expand 
    complete -D -F _vyatta_op_default_expand

    for xd in $vyatta_op_templates/* ; do
	if [ -d $xd ] ; then
	    cmd=${xd##*/}
	    complete -F _vyatta_op_expand $cmd
	    eval alias $cmd=\'_vyatta_op_run $cmd\'
	fi
    done

    shopt -s histverify
}


# $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 ()
{
    _vyatta_op_node_path=$vyatta_op_templates
    for (( i=0 ; i<COMP_CWORD ; i++ )) ; do
	if [ -f "${_vyatta_op_node_path}/${COMP_WORDS[i]}/node.def" ] ; then
	    _vyatta_op_node_path+=/${COMP_WORDS[i]}
	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
	    completions+=( ${allowed[i]} )
	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
}

_vyatta_image_is_file()
{
  local cur=$1
  cur=${cur/:/}
  topdir=${cur%%/*}
  cur=${cur#$topdir/}
  if [[ $topdir == "running" ]]; then
    cur="/${cur}"
  elif [[ $topdir == "disk-install" ]]; then
    cur="/live/image/${cur}"
  else
    cur="/live/image/boot/${topdir}/live-rw/${cur}"
  fi  
  if [[ -f ${cur} ]]; then
    return 0;
  else
    return 1;
  fi  
}

_vyatta_image_file_complete()
{
  if _vyatta_image_is_file ${COMP_WORDS[(( ${#COMP_WORDS[@]}-2 ))]}; then
    _vyatta_op_completions=( "" " " )
    return 0;
  fi
  _vyatta_image_path_complete
}

_vyatta_image_path_complete()
{
  compopt -o nospace
  local -a reply
  if _vyatta_image_is_file $cur ; then
    foundfile=1
    _vyatta_op_completions=( "${cur} " )
    return 0;
  fi
  if [[ ${cur} == "" ]]; then
    reply=( $(compgen -d /live/image/boot/ | grep -v grub) )
    for i in `seq 0 $[${#reply[@]}-1]`; do
      file=${reply[$i]}
      reply[$i]=${file/#\/live\/image\/boot\//}
      reply[$i]="${reply[$i]}://config/"
    done
    reply+=( "running://config/" )
    if [[ -d /live/image/opt/vyatta/etc/config || -d /live/image/config ]]; then
      reply+=( "disk-install://" )
    fi
  else 
    if ! [[ $cur =~ .*:\/\/ ]]; then
      if [[ $cur =~ .*:\/ ]]; then
        cur=${cur/:\//}
      fi
      if [[ $cur =~ .*: ]]; then
        cur=${cur/:/}
      fi
      local isrunningimg=$(compgen -W "running" -- ${cur})
      local isdiskinstall=$(compgen -W "disk-install" -- ${cur})
      if [[ $isrunningimg == "running" ]];then
        cur="/"
      elif [[ $isdiskinstall == "disk-install" ]]; then
        cur="/live/image/"
      else
        cur="/live/image/boot/${cur}"
      fi
      reply=( $(compgen -f ${cur}) )
      for i in `seq 0 $[${#reply[@]}-1]`; do
        file=${reply[$i]}
        if [[ $isrunningimg == "running" ]];then
          reply[$i]="running://config/"
        elif [[ $isdiskinstall == "disk-install" ]]; then
          reply[$i]="disk-install://" 
        else
          reply[$i]=${file/#\/live\/image\/boot\//}
          if [[ -d /live/image/boot/${reply[$i]} ]]; then
            reply[$i]="${reply[$i]/#\//}://config/"
          fi
        fi
      done
    else
      cur=${cur/:/}
      topdir=${cur%%/*}
      cur=${cur#$topdir//}
      if [[ $topdir == "running" ]]; then
        cur="/${cur}"
      elif [[ $topdir == "disk-install" ]]; then
        cur="/live/image/${cur}"
      else
        cur="/live/image/boot/${topdir}/live-rw/${cur}"
      fi
      reply=( $(compgen -f ${cur}) )
      # for loop from _filedirs() in /etc/bash_completion
      for ((i=0; i < ${#reply[@]}; i++)); do
        if [[ ${cur:0:1} != "'" ]]; then 
           [[ -d ${reply[i]} ]] && reply[i]="${reply[i]}"/
           if [[ ${cur:0:1} == '"' ]]; then 
             reply[i]=${reply[i]//\\/\\\\}
             reply[i]=${reply[i]//\"/\\\"}
             reply[i]=${reply[i]//\$/\\\$}
           else
             reply[i]=$(printf %q ${reply[i]})
           fi
        fi
      done
      for i in `seq 0 $[${#reply[@]}-1]`; do
        file=${reply[$i]}
        if [[ $topdir == "running" ]]; then
          reply[$i]=${file/#\//"$topdir://"}
        elif [[ $topdir == "disk-install" ]]; then
          reply[$i]=${file/#\/live\/image\//$"$topdir://"} 
        else
          reply[$i]=${file/#\/live\/image\/boot\/$topdir/"$topdir://"}
          reply[$i]=${reply[$i]/\/live-rw\/}
        fi
      done
    fi 
  fi
  _vyatta_op_completions=( "${reply[@]}" )
  return 0
}



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

_vyatta_op_init $@

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