# greenos configuration mode command interpreter functions

# **** 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.
#
# A copy of the GNU General Public License is available as
# `/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution
# or on the World Wide Web at `http://www.gnu.org/copyleft/gpl.html'.
# You can also obtain it by writing to the Free Software Foundation,
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.
# 
# Author: GreenOS
# Description: Command interpreter functions for config mode
# 
# **** End License ****

### Top level commands and help ###
_greenos_cfg_cmds=( "confirm" \
                   "comment" \
                   "commit" \
                   "commit-confirm" \
                   "compare" \
                   "copy" \
                   "delete" \
                   "discard" \
                   "edit" \
                   "exit" \
                   "load" \
                   "merge" \
                   "rename" \
                   "rollback" \
                   "rollback-soft" \
                   "run" \
                   "save" \
                   "set" \
                   "show" )
_greenos_cfg_helps=( \
      "Confirm prior commit-confirm" \
      "Add comment to this configuration element" \
      "Commit the current set of changes" \
      "Commit the current set of changes with 'confirm' required" \
      "Compare configuration revisions" \
      "Copy a configuration element" \
      "Delete a configuration element" \
      "Discard uncommitted changes" \
      "Edit a sub-element" \
      "Exit from this configuration level" \
      "Load configuration from a file and replace running configuration" \
      "Load configuration from a file and merge running configuration" \
      "Rename a configuration element" \
      "Rollback to a prior config revision (requires reboot)" \
      "Rollback to a prior config revision (without reboot)" \
      "Run an operational-mode command" \
      "Save configuration to a file" \
      "Set the value of a parameter or create a new element" \
      "Show the configuration (default values may be suppressed)" \
    )
###  End Top level commands and help ###

greenos_cfg_expand_top_level () {
  local cmd=$1
  local -a filtered_cmds=()
  get_prefix_filtered_list ${cmd} _greenos_cfg_cmds filtered_cmds
  local found
  is_elem_of "${cmd}" _greenos_cfg_cmds
  found=$?
  local fcmd
  if [[ "${#filtered_cmds[@]}" == "1" || "$found" == "0" ]]; then
    if [[ "${#filtered_cmds[@]}" == "1" ]]; then
      fcmd=${filtered_cmds[0]}
    else
      fcmd=$cmd
    fi
  else
    fcmd=$cmd
  fi 
  echo $fcmd
}

### Top level command wrappers ###
greenos_config_show ()
{
  local -a opts=()
  local -a args=()
  for arg in "$@"; do
    if [ "$arg" == "-all" ]; then
      opts+=('--show-show-defaults')
    else
      args+=("$arg")
    fi
  done
  if test -f "/var/run/gosconf_backend"; then
    ${gosconf_bin_dir}/gosconf_cli_compat showCfg "${args[@]}" \
      | eval "${GREENOS_PAGER:-cat}"
  else
    cli-shell-api "${opts[@]}" -- showCfg "${args[@]}" \
      | eval "${GREENOS_PAGER:-cat}"
  fi
}

greenos_config_commit ()
{
  if test -f "/var/run/gosconf_backend"; then
    if ! greenos_cli_shell_api sessionChanged; then
      echo "No configuration changes to commit"
      return 1;
    fi
  else
    if ! gos_legacy_cli_shell_api sessionChanged; then
      echo "No configuration changes to commit"
      return 1;
    fi
  fi

  local comment="commit"
  if [ $# -gt 0 ] ; then
    if [ $# = 1 ] || [ $# -gt 2 ] || [ "$1" != "comment" ]; then
      if [ "$1" == "confirm" ]; then
        echo "Use commit-confirm command"
        return 1
      fi
      echo "Error: commit accepts either no arguments, or optional 'comment'" \
           "with comment text as second argument."
      echo -e "\tUsage: 'commit [comment COMMENTTEXT]'"
      return 1;
    fi
    comment="$2"
  fi

  export COMMIT_COMMENT="$comment"
  export COMMIT_VIA=cli
  if test -f "/var/run/gosconf_backend"; then
    /usr/libexec/greenos/gosconf/bin/gos_commit 2>&1
  else
    /opt/greenos/sbin/my_commit 2>&1
  fi
  unset COMMIT_VIA
  unset COMMIT_COMMENT
}

greenos_config_commit-confirm ()
{
  if test -f "/var/run/gosconf_backend"; then
    if ! greenos_cli_shell_api sessionChanged; then
      echo "No configuration changes to commit"
      return 1;
    fi
  else
    if ! gos_legacy_cli_shell_api sessionChanged; then
      echo "No configuration changes to commit"
      return 1;
    fi
  fi

  local -a args=()
  local first=1
  local minutes=10
  for arg in "$@"; do
    if [ "$first" = "1" ]; then
      if [[ $arg = *[[:digit:]]* ]]; then
        minutes=$arg
      else
        args[${#args[@]}]="$arg"    
      fi
      first=0
    else
      args[${#args[@]}]="$arg"    
    fi
  done
  cmd="${greenos_bin_dir}/config-mgmt commit_confirm -t=$minutes"
  eval "sudo sg greenoscfg \"$cmd\" "
  if [ $? = 0 ]; then
    export IN_COMMIT_CONFIRM=t
    greenos_config_commit "${args[@]}"
    unset IN_COMMIT_CONFIRM
  fi
}

greenos_config_confirm ()
{
  ${greenos_bin_dir}/config-mgmt confirm
}

greenos_config_compare ()
{
  local -a comp=( "saved" )
  local -a filtered=()
  get_prefix_filtered_list $1 comp filtered
  if [[ "${filtered[0]}" == "saved" ]]; then
     ${greenos_bin_dir}/config-mgmt compare --saved
  else  
     ${greenos_bin_dir}/config-mgmt wrap_compare --options "$@" | eval "${GREENOS_PAGER:-cat}"
  fi 
}

greenos_config_save ()
{
  if test -f "/var/run/gosconf_backend"; then
    if greenos_cli_shell_api sessionChanged; then
      echo -e "Warning: you have uncommitted changes that will not be saved.\n"
    fi
  else
    if gos_legacy_cli_shell_api sessionChanged; then
      echo -e "Warning: you have uncommitted changes that will not be saved.\n"
    fi
  fi

  # return to top level.
  reset_edit_level
  # transform individual args into quoted strings
  local arg=''
  local save_cmd="${greenos_libexec_dir}/greenos-save-config.py"
  for arg in "$@"; do
    save_cmd+=" '$arg'"
  done
  eval "sudo sg greenoscfg \"umask 0002 ; $save_cmd\""
  gos_legacy_cli_shell_api unmarkSessionUnsaved
}

reboot ()
{
  echo "Exit from configure mode before rebooting."
}

poweroff ()
{
  echo "Exit from configure mode before shutting down."
}

greenos_config_rollback ()
{
  if [ $# != 1 ]; then
    echo "Error: must include a revision # to rollback to"
    return 1;
  fi
  sudo ${greenos_bin_dir}/config-mgmt rollback --rev "$@"
}

greenos_config_rollback-soft ()
{
  if [ $# != 1 ]; then
    echo "Error: must include a revision # to rollback to"
    return 1;
  fi
  ${greenos_bin_dir}/config-mgmt rollback_soft --rev "$@"
}

shutdown ()
{
  echo "Exit from configure mode before shutting down system."
}

reset_edit_level ()
{
  if test -f "/var/run/gosconf_backend"; then
    greenos_cli_shell_api getEditResetEnv
    return $?
  else
    gos_legacy_cli_shell_api getEditResetEnv
    return $?
  fi
}

greenos_config_load ()
{
  # don't load if there are uncommitted changes.
  if test -f "/var/run/gosconf_backend"; then
    if greenos_cli_shell_api sessionChanged; then
      echo "Cannot load: configuration modified."
      echo "Commit or discard the changes before loading a config file."
      return 1
    fi
  else
    if gos_legacy_cli_shell_api sessionChanged; then
      echo "Cannot load: configuration modified."
      echo "Commit or discard the changes before loading a config file."
      return 1
    fi
  fi
  # return to top level.
  reset_edit_level
  ${greenos_libexec_dir}/greenos-load-config.py "$@"
}

greenos_config_merge ()
{
  # don't load if there are uncommitted changes.
  if test -f "/var/run/gosconf_backend"; then
    if greenos_cli_shell_api sessionChanged; then
      echo "Cannot merge: configuration modified."
      echo "Commit or discard the changes before merging a config file."
      return 1
    fi
  else
    if gos_legacy_cli_shell_api sessionChanged; then
      echo "Cannot merge: configuration modified."
      echo "Commit or discard the changes before merging a config file."
      return 1
    fi
  fi
  # return to top level.
  reset_edit_level
  ${greenos_libexec_dir}/greenos-merge-config.py "$@"
}

top ()
{

  if test -f "/var/run/gosconf_backend"; then
    if greenos_cli_shell_api editLevelAtRoot; then
      echo "Already at the top level"
      return 0
    fi
  else
    if gos_legacy_cli_shell_api editLevelAtRoot; then
      echo "Already at the top level"
      return 0
    fi
  fi

  # go to the top level.
  reset_edit_level
}

greenos_config_edit ()
{
  if test -f "/var/run/gosconf_backend"; then
    greenos_cli_shell_api getEditEnv "$@"
    return $?
  else
    gos_legacy_cli_shell_api getEditEnv "$@"
    return $?
  fi
}

up ()
{
  if test -f "/var/run/gosconf_backend"; then
    greenos_cli_shell_api getEditUpEnv "$@"
    return $?
  else
    gos_legacy_cli_shell_api getEditUpEnv "$@"
    return $?
  fi
}

really_exit()
{
  if test -f "/var/run/gosconf_backend"; then
    if greenos_cli_shell_api sessionUnsaved; then
      echo "Warning: configuration changes have not been saved."
    fi
    local exit_cmd="${greenos_libexec_dir}/teardown-config-session.py $$"
    eval "sudo sg greenoscfg \"$exit_cmd\""
  else
    if gos_legacy_cli_shell_api sessionUnsaved; then
      echo "Warning: configuration changes have not been saved."
    fi
    gos_legacy_cli_shell_api teardownSession
  fi
  unset _OFR_CONFIGURE
  builtin exit 0
}

greenos_config_exit ()
{
  local discard
  local -a comp=( "discard" )
  local -a filtered=()
  get_prefix_filtered_list $1 comp filtered
  if [ $# == 0 ]; then
    discard=0
  elif [ $# == 1 ] && [ "${filtered[0]}" == "discard" ]; then
    discard=1
  else
    echo "Invalid argument \"$*\" for 'exit'"
    return 1
  fi

  if test -f "/var/run/gosconf_backend"; then
    if greenos_cli_shell_api editLevelAtRoot; then
      # we are at the root level. check if we can really exit.
      if greenos_cli_shell_api sessionChanged; then
        if (( ! discard )); then
          echo "Cannot exit: configuration modified."
          echo "Use 'exit discard' to discard the changes and exit."
          return 1
        fi
      fi
      really_exit
    fi
  else
    if gos_legacy_cli_shell_api editLevelAtRoot; then
      # we are at the root level. check if we can really exit.
      if gos_legacy_cli_shell_api sessionChanged; then
        if (( ! discard )); then
          echo "Cannot exit: configuration modified."
          echo "Use 'exit discard' to discard the changes and exit."
          return 1
        fi
      fi
      really_exit
    fi
  fi

  # "exit" to the root level.
  reset_edit_level
}

# run op mode commands
greenos_config_run ()
{
  if [ $# == 0 ]; then
    echo -e "\n  Incomplete command: run\n"
    return 1
  fi
  if [[ "set" =~ "$1" ]]; then
    _greenos_op_run "$@"
  elif [[ "$1" =~ "/" ]]; then
    local args=("$@")
    ${args[0]} "${args[@]:1}"
  else
    /opt/greenos/bin/greenos-op-cmd-wrapper "$@"
  fi
}

### End Top level command wrappers ###

### Top level wrappers ###
greenos_cfg_cmd_run ()
{
    local cmd=$1
    local output=''
    if [[ "$cmd" == "edit" ]]; then 
      greenos_config_edit "${@:2}"
    elif [[ "$cmd" == "show" ]]; then 
      greenos_config_show "${@:2}"
    else 
      if test -f "/var/run/gosconf_backend"; then
        cmd="${gosconf_bin_dir}/gos_$cmd"
      else
        cmd="/opt/greenos/sbin/my_$cmd"
      fi
      output=$($cmd "${@:2}")
    fi   
    greenos_cfg_print_output "$output"
}

greenos_cfg_print_output ()
{
  local output=$1
  if [[ ! -z "${output}" ]];then
    output=$(echo "$output" | sed -e 's/^/  /')
    echo -ne "\n${output}\n\n" | eval "${GREENOS_PAGER:-cat}"
  fi 
}

greenos_cfg_validate_cmd ()
{
    local cmd=$1 
    local -a expanded_api_args=( "$@" )
    if test -f "/var/run/gosconf_backend"; then
      local editlvl=$(${gosconf_bin_dir}/gosconf_cli_compat getEditLevelStr)
    else
      local editlvl=$(cli-shell-api getEditLevelStr)
    fi
    local path=''
    local opath=''
    for arg in "${expanded_api_args[@]:1}"; do
      if [[ "$path" == '' ]]; then
        path="$arg"
      else 
        path="$path $arg"
      fi
      if test -f "/var/run/gosconf_backend"; then
        if ! ${gosconf_bin_dir}/gosconf_cli_compat validateTmplPath ${editlvl} ${path}; then
          _cli_shell_api_comp_values=()
          greenos_cli_shell_api getCompletionEnv $cmd ${path}
          if [[ "${#_cli_shell_api_comp_values[@]}" != "1"
             && "${#_cli_shell_api_comp_values[@]}" != "0" ]]; then
            local -a _get_help_text_items=( "${_cli_shell_api_hitems[@]}" )
            local -a _get_help_text_helps=( "${_cli_shell_api_hstrs[@]}" )
            local greenos_help_text=''
            if [[ $opath == '' ]]; then
              echo -ne "\n  Configuration path: [$arg] is ambiguous\n" >&2
            else
              echo -ne "\n  Configuration path: $opath [$arg] is ambiguous\n" >&2
            fi
            get_help_text
            echo -e "$greenos_help_text\n" | sed 's/^P/  P/'
            echo -e "  ${cmd^} failed\n"
            break
          else
            if [[ $opath == '' ]]; then
              echo -ne "\n  Configuration path: [$arg] is not valid\n  ${cmd^} failed\n\n" >&2
            else
              echo -ne "\n  Configuration path: $opath [$arg] is not valid\n  ${cmd^} failed\n\n" >&2
            fi
            break
          fi
        else
          opath=$path
        fi
      else
        if ! cli-shell-api validateTmplPath -- ${editlvl} ${path}; then
          _cli_shell_api_comp_values=()
          gos_legacy_cli_shell_api getCompletionEnv $cmd ${path}
          if [[ "${#_cli_shell_api_comp_values[@]}" != "1"
             && "${#_cli_shell_api_comp_values[@]}" != "0" ]]; then
            local -a _get_help_text_items=( "${_cli_shell_api_hitems[@]}" )
            local -a _get_help_text_helps=( "${_cli_shell_api_hstrs[@]}" )
            local greenos_help_text=''
            if [[ $opath == '' ]]; then
              echo -ne "\n  Configuration path: [$arg] is ambiguous\n" >&2
            else
              echo -ne "\n  Configuration path: $opath [$arg] is ambiguous\n" >&2
            fi
            get_help_text
            echo -e "$greenos_help_text\n" | sed 's/^P/  P/'
            echo -e "  ${cmd^} failed\n"
            break
          else
            if [[ $opath == '' ]]; then
              echo -ne "\n  Configuration path: [$arg] is not valid\n  ${cmd^} failed\n\n" >&2
            else
              echo -ne "\n  Configuration path: $opath [$arg] is not valid\n  ${cmd^} failed\n\n" >&2
            fi
            break
          fi
        else
          opath=$path
        fi
      fi
    done
}

greenos_config_copy ()
{
    local cmd=$1 
    if [[ "${#@}" == "1" ]]; then
      greenos_cfg_cmd_run $cmd
      return
    fi
    local -a args=( "$@" )
    local -a param1=( "$cmd" ${args[@]:1:2} )
    local -a param2=( "$cmd" ${args[@]:4:5} )
    local editlvl=$(cli-shell-api getEditLevelStr)
    expanded_api_args=( )
    greenos_config_expand_compwords "${param1[@]}"
    param1=( "${expanded_api_args[@]}" )
    expanded_api_args=( )
    greenos_config_expand_compwords "${param2[@]}"
    param2=( "${expanded_api_args[@]}" )
    if [[ "${args[3]}" != "to" ]]; then
        echo -ne "\n  Invalid command: $cmd ${param1[@]:1} ${args[3]} ${param2[@]:1}\n\n" >&2
    elif cli-shell-api validateTmplPath -- ${editlvl[*]} "${param1[@]:1}" &&
       cli-shell-api validateTmplPath -- ${editlvl[*]} "${param2[@]:1}" ; then
      cmd="/opt/greenos/sbin/my_$cmd"
      output=$(eval "$cmd ${param1[@]:1} to ${param2[@]:1} | sed -e 's/^/  /'")
      if [[ ! -z "${output}" ]];then
        echo -ne "\n${output}\n\n"
      fi
    else
      if ! cli-shell-api validateTmplPath -- ${editlvl[*]} "${param1[@]:1}"; then
        _cli_shell_api_comp_values=()
        gos_legacy_cli_shell_api getCompletionEnv $cmd ${param1[1]}
        if [[ "${#_cli_shell_api_comp_values[@]}" != "1"
           && "${#_cli_shell_api_comp_values[@]}" != "0" ]]; then
          echo -ne "\n  Ambiguous command: $cmd [${param1[1]}]\n" >&2
          echo -ne "\n  Possible completions: ${_cli_shell_api_comp_values[@]}\n\n" >&2
        else
          echo -ne "\n  Invalid command: $cmd [${param1[1]}]\n\n" >&2
        fi
      elif ! cli-shell-api validateTmplPath -- ${editlvl[*]} "${param1[@]:2}"; then
        _cli_shell_api_comp_values=()
        gos_legacy_cli_shell_api getCompletionEnv $cmd "${param2[1]}"
        if [[ "${#_cli_shell_api_comp_values[@]}" != "1" 
           && "${#_cli_shell_api_comp_values[@]}" != "0" ]]; then
          echo -ne "\n  Ambiguous command: $cmd ${param2[@]:1} to [${param2[1]}]\n" >&2
          echo -ne "\n  Possible completions: ${_cli_shell_api_comp_values[@]}\n\n" >&2
        else
          echo -ne "\n  Invalid command: $cmd ${param1[@]:1} to [${param2[1]}]\n\n" >&2
        fi
      else
        echo -ne "\n  Invalid command: $cmd ${param1[@]:1} to ${param2[@]:1}\n\n" >&2
      fi
    fi
}

greenos_config_comment ()
{
  local cmd=$1
  if [[ "${#@}" == "1" ]]; then
    greenos_cfg_cmd_run $cmd
    return
  fi
  # change the ifs so we can extract the entire comment
  local -a args=( "$@" )
  # extract the comment
  local comment="'${args[$[${#args[@]}-1]]}'"
  args=( "${args[@]:0:$[${#args[@]}-1]}" )
  local -a expanded_api_args=()
  # expand the comment command
  local editlvl=$(cli-shell-api getEditLevelStr)
  greenos_config_expand_compwords "${args[@]}"
  if [[ "$#" != "${#expanded_api_args[@]}" ]]; then
     expanded_api_args+=( $comment )
  fi
  # use the standard run function with the comment expansion
  output=$(eval "/opt/greenos/sbin/my_${expanded_api_args[0]} ${expanded_api_args[@]:1}")
  greenos_cfg_print_output "$output"
}

greenos_cfg_cmd () 
{ 
  # commands that need expanded paths get called through here
  local cmd=$1
  if [[ "$#" == "1" ]]; then
    greenos_cfg_cmd_run $cmd
    return
  fi
  local -a args=( "$@" )
  local -a expanded_api_args=()
  if test -f "/var/run/gosconf_backend"; then
    local editlvl=$(${gosconf_bin_dir}/gosconf_cli_compat getEditLevelStr)
  else
    local editlvl=$(cli-shell-api getEditLevelStr)
  fi
  greenos_config_expand_compwords "${args[@]}"
  if test -f "/var/run/gosconf_backend"; then
    if [[ "${expanded_api_args[@]:$[${#expanded_api_args[@]}-1]}" == "-all" ]] &&
       [[ "${expanded_api_args[0]}" == "show" ]]; then
      if [[ $[${#expanded_api_args[@]}-2] -eq 0 ]]; then
        greenos_cfg_cmd_run "${expanded_api_args[@]}"
      elif ${gosconf_bin_dir}/gosconf_cli_compat validateTmplPath ${editlvl[*]} \
           "${expanded_api_args[@]:1:$[${#expanded_api_args[@]}-2]}"; then
        greenos_cfg_cmd_run "${expanded_api_args[@]}"
      else
        greenos_cfg_validate_cmd "${expanded_api_args[@]}"
      fi
    elif ${gosconf_bin_dir}/gosconf_cli_compat validateTmplPath ${editlvl[*]} "${expanded_api_args[@]:1}"; then
      greenos_cfg_cmd_run "${expanded_api_args[@]}"
    else
      # find broken portion of command
      greenos_cfg_validate_cmd "${expanded_api_args[@]}"
    fi
  else
    if [[ "${expanded_api_args[@]:$[${#expanded_api_args[@]}-1]}" == "-all" ]] &&
       [[ "${expanded_api_args[0]}" == "show" ]]; then
      if [[ $[${#expanded_api_args[@]}-2] -eq 0 ]]; then
        greenos_cfg_cmd_run "${expanded_api_args[@]}"
      elif cli-shell-api validateTmplPath -- ${editlvl[*]} \
           "${expanded_api_args[@]:1:$[${#expanded_api_args[@]}-2]}"; then
        greenos_cfg_cmd_run "${expanded_api_args[@]}"
      else
        greenos_cfg_validate_cmd "${expanded_api_args[@]}"
      fi
    elif cli-shell-api validateTmplPath -- ${editlvl[*]} "${expanded_api_args[@]:1}"; then
      greenos_cfg_cmd_run "${expanded_api_args[@]}"
    else
      # find broken portion of command
      greenos_cfg_validate_cmd "${expanded_api_args[@]}"
    fi
  fi
}
### Top level wrappers ###

### Main run command ###
greenos_cfg_run ()
{
  # if run with bash builtin "set -/+*" run set and return
  # this happens when a different completion script runs eval "set ..."
  # (GreenOS T1604)
  if [[ "$1" == "set" && "$2" =~ ^(-|\+).* ]]; then
    set "${@:2}"
    return
  fi

  # validate top level command and execute proper function
  local cmd=$1
  local -a args=( "$@" )
  local -a filtered_cmds
  get_prefix_filtered_list $cmd _greenos_cfg_cmds filtered_cmds
  local found
  is_elem_of "${cmd}" _greenos_cfg_cmds
  found=$?
  stty echo 2> /dev/null # turn echo on, this is a workaround for bug 7570
                         # not a fix we need to look at why the readline library 
                         # is getting confused on paged help text.
  if [[ "${#filtered_cmds[@]}" == "0" ]]; then
    echo -ne "\n  Invalid command: [$cmd]\n\n" >&2
    return 1
  elif [[ "${#filtered_cmds[@]}" != "1" && "$found" == "1" ]];  then
    echo -ne "\n  Ambiguous command: [$cmd]\n" >&2
    local -a fitems=()
    local -a fstrs=()
    local -a _get_help_text_items=( "${_greenos_cfg_cmds[@]}" )
    local -a _get_help_text_helps=( "${_greenos_cfg_helps[@]}" )
    get_prefix_filtered_list2 "$cmd" \
      _get_help_text_items fitems _get_help_text_helps fstrs
    _get_help_text_items=( "${fitems[@]}" )
    _get_help_text_helps=( "${fstrs[@]}" )
    get_help_text
    echo -e "$greenos_help_text\n" | sed 's/^P/  P/'
    return 1
  fi
  local fcmd;
  if is_elem_of "${cmd}" _greenos_cfg_cmds; then
    fcmd=$cmd
  else
    fcmd=${filtered_cmds[0]}
  fi
  case $fcmd in
    compare) greenos_config_compare "${@:2}" ;;
    comment) greenos_config_comment "${args[@]}" ;; # comment is a special case
    copy|rename) greenos_config_copy $fcmd ${@:2} ;; # copy is a special case
    exit) greenos_config_exit "${@:2}" ;; 
    run) greenos_config_run "${@:2}" ;; 
    load) greenos_config_load "${@:2}" ;; 
    commit) greenos_config_commit "${@:2}";;
    confirm) greenos_config_confirm "${@:2}";;
    rollback) greenos_config_rollback "${@:2}";;
    rollback-soft) greenos_config_rollback-soft "${@:2}";;
    commit-confirm) greenos_config_commit-confirm "${@:2}";;
    compare) greenos_config_compare "${@:2}";;
    save) greenos_config_save "${@:2}" ;;
    merge) greenos_config_merge "${@:2}" ;;
    *) greenos_cfg_cmd $fcmd "${@:2}" ;; # commands requiring path expansion must go through here
  esac
}

### Initalize top level command alias and completion functions
_greenos_cfg_init ()
{
    # empty and default line compeletion
    complete -E -F greenos_config_complete 
    complete -D -F greenos_config_default_complete

    # create the top level aliases for the unambiguous portions of the commands
    # this is the only place we need an entire enumerated list of the subcommands
    for cmd in "${_greenos_cfg_cmds[@]}"; do
      for pos in $(seq 1 ${#cmd}); do
        case ${cmd:0:$pos} in
          for|do|done|if|fi|case|while|tr )
            continue ;;
          *) ;;
        esac
        local -a filtered_cmds=()
        get_prefix_filtered_list ${cmd:0:$pos} _greenos_cfg_cmds filtered_cmds
        local found
        is_elem_of "${cmd:0:$pos}" _greenos_cfg_cmds
        found=$?
        if [[ "${#filtered_cmds[@]}" == "1" || "${cmd:0:$pos}" == "$cmd"  || "$found" == "0" ]]; then
          local fcmd
          if [[ "${#filtered_cmds[@]}" == "1" ]]; then
            fcmd=${filtered_cmds[0]}
          elif is_elem_of "${cmd:0:$pos}" _greenos_cfg_cmds; then
            fcmd=${cmd:0:$pos}
          else
            fcmd=$cmd
          fi
          case $fcmd in
            save|load|merge)
              complete -F greenos_loadsave_complete ${cmd:0:$pos} ;;
            discard|confirm)
               complete -F greenos_single_word_complete ${cmd:0:$pos} ;; 
            run)
              complete -F greenos_run_complete ${cmd:0:$pos} ;;
            compare)
              complete -F greenos_compare_complete ${cmd:0:$pos} ;;
            rollback)
              complete -F greenos_rollback_complete ${cmd:0:$pos} ;;
            rollback-soft)
              complete -F greenos_rollback-soft_complete ${cmd:0:$pos} ;;
            commit|commit-confirm)
               complete -F greenos_commit_complete ${cmd:0:$pos} ;;
            *)
               complete -F greenos_config_complete ${cmd:0:$pos} ;;
          esac
        else
          complete -F greenos_config_complete ${cmd:0:$pos} 
        fi
        eval alias ${cmd:0:$pos}=\'greenos_cfg_run ${cmd:0:$pos}\'
      done
    done
    shopt -s histverify
}
