#!/usr/bin/env bash

# detect_action $extension action extension_args "${extension_args[@]}"
detect_action()
{
  extensions_path=$1
  shift
  extension="${1:-core}"
  shift

  local token _word _action_path="$extensions_path/$extension/actions"

  local _params=("$@")

  #loop when params or .actions exists
  while (( ${#_params[@]} > 0 )) || file_exists "${_action_path}/.actions"
  do
    array_shift _params token

    # Detect function
    if  variable_is_nonempty token &&
      match "$token" "*\(\)"
    then
      _params=( "${token}" "${_params[@]}" )
      break

    # Detect a namespace
    elif variable_is_nonempty token &&
      path_exists "${_action_path}/${token}"
    then
      _action_path+="/${token}"

    # Detect action
    elif variable_is_nonempty token &&
      file_is_executable "${_action_path}/${token}"
    then
      _params=( "${token}" "${_params[@]}" )
      break

    # Detect mapping in .actions
    elif file_exists "${_action_path}/.actions" &&
         file_contains "${_action_path}/.actions" "^${token}="
    then
      _array=( $(hash_file "${_action_path}/.actions" ${token}) )
      _params=( "${_array[@]}" "${_params[@]}" )
      [[ "${_array[@]}" == "$token" ]] && break #break if detected 2nd time the same

    # Detect default mapping in .actions
    elif file_exists "${_action_path}/.actions" &&
         file_contains "${_action_path}/.actions" "^default="
    then
      _array=( $(hash_file "${_action_path}/.actions" default) )
      _params=( "${_array[@]}" "${token}" "${_params[@]}" )
      [[ "${_array[@]}" == "$token" ]] && break #break if detected 2nd time the same

    # Detect help mapping in .actions
    elif file_exists "${_action_path}/.actions" \
    && file_contains "${_action_path}/.actions" "^help=" \
    && (( ${#_params[@]} == 0 ))
    then
      _array=( $(hash_file "${_action_path}/.actions" help) )
      _params=( "${_array[@]}" "${token}" "${_params[@]}" )
      [[ "${_array[@]}" == "$token" ]] && break #break if detected 2nd time the same

    # finish searching path
    else
      _params=( "${token}" "${_params[@]}" )
      break
    fi
  done

  if array_is_empty _params
  then
    token=""
  else
    array_shift _params token
  fi

  # Detect function
  if variable_is_nonempty token &&
    match "${token}" "*\(\)"
  then
    action_path="${_action_path}"
    action="$token"
    extension_args=( "${_params[@]}" )

  # Detect a file
  elif variable_is_nonempty token &&
    file_is_executable "${_action_path}/${token}"
  then
    action_path="${_action_path}"
    action="$token"
    extension_args=( "${_params[@]}" )

  # Detect default if nothing else matches
  elif file_exists "${_action_path}/default"
  then
    action_path="${_action_path}"
    action="default"
    extension_args=( "$token" "${_params[@]}" )

  # Detect help if no default
  elif file_exists "${_action_path}/help" \
  && (( ${#_params[@]} == 0 )) \
  && [[ "$token" == "" ]]
  then
    action_path="${_action_path}"
    action="help"
    [[ "$token" != "" ]] && extension_args=("$token") || extension_args=()
    extension_args+=( "${_params[@]}" )

  else
    # Not found, error.
    return 1
  fi

  # Found, OK
  return 0
}

# find modules on path
detect_modules_paths()
{
  local root="$1" path="" IFS='/'
  shift
  while (( $# > 0 ))
  do
    if is_module "${root}${path}"
    then
      printf "${path##\/}=$*\n"
    else
      break
    fi
    path+="/$1"
    shift
  done
}

# search for extension that modules dependency providing matching [namespace...] action
detect_modules_action()
{
  trace_filter modules_action

  extensions_path=$1
  shift
  extension="${1:-core}"
  shift

  local token _word _modules_path="$extensions_path/$extension/modules/shell"

  local _params=("$@") _module_path _module_params _module_name

  # iterate modules from longest path
  for _module_definition in $(detect_modules_paths ${_modules_path} "$@" | sort -r)
  do
    _module_path="${_module_definition//=*}"
    _module_params="${_module_definition//*=}"
    if [[ -s "${_modules_path}/${_module_path}/modules" ]]
    then
      for _module_name in $( cat "${_modules_path}/${_module_path}/modules" )
      do
        if in_search_paths detect_module "${_module_name}"
        then
          if [[ -s "${module_path}/actions" ]]
          then
            local _params="" _action="${_module_params}" _read_action
            while [[ -n "_action" ]]
            do
              if file_contains "${module_path}/actions" "${_action}="
              then
                _read_action=$( hash_file "${module_path}/actions" "${_action//\//\/}" )
                if [[ -n "$_read_action" ]]
                then
                  action="$_read_action"
                  action_path="$( path_join $extensions_path $extension actions ${_module_path})"
                  extension_args=( $_params )
                  return 0
                fi
              fi
              _params="$(basename $_action) $_params"
              _action="$(dirname $_action)"
              if [[ "$_action" =~ "." || "$_action" =~ "/" ]]
              then
                break
              fi
            done
          fi
        fi
      done
    fi
  done
  return 1
}

detect_action_type()
{
  local _path="${action_path:-}"

  case "${action}" in
    (*\(\))
      action_type="function"
      return 0
      ;;
    *)
      _path_type="$(file "${_path}/${action}")"
      ;;
  esac

  case "${_path_type}" in

    *sh[[:space:]]script*|*POSIX[[:space:]]shell*|*Bourne-Again*)
      action_type="shell"
      ;;

    *ASCII*)
      # Launch with helper dsl, if possible.
      extension="${_path//.}"

      case "${extension}" in
        shell|zsh|bash|sh)
          action_type="shell"
          #rb) action_type="ruby"   ;;
          ;;
        *)
          read -r shebang < "${_path}"

          case "${shebang}" in
            *ruby|*rbx|*jruby|*macruby)
              binary="${shebang##*(#|!)}"
              binary="${binary##* }"
              action_type="ruby"
              ;;
            *)
              if [[ -x "${_path}" ]] ; then
                action_type="binary"
              else
                action_type="not executable"
              fi
              ;;
          esac
          ;;
      esac
      result=$?
      ;;

    cannot[[:space:]]open)
      action_type="dne"
      ;;

    *)
      if file_is_executable "${_path}"
      then
        action_type="binary"
      else
        action_type="noexec"
      fi
      ;;
  esac

  return $?
}

extension_modules_load()
{
  local _token _namespace=()

  module_name="${extension}"
  module_path="${extension_modules_path}/shell"
  module "$module_name" skip_path_detection

  while (( $# > 0 ))
  do
    _namespace+=( "$1" )
    shift

    module_name="$(path_join "${extension}" ${_namespace[*]})"
    module_path="$(path_join "${extension_modules_path}" "shell" ${_namespace[*]})"
    module "$module_name" skip_path_detection || true
  done
}

extension_run()
{
  local _type="${action_type:-}" _path="${action_path:-}" _namespaces

  builtin cd "${initial_pwd}"

  # Now based on the determined _type we launch the extension.
  case "${_type}" in
    function)
      _namespaces=${_path##${extension_actions_path}}
      extension_modules_load ${_namespaces//\// }

      if command_exists "${action##_*}_cli"
      then
        "${action##_*}_cli"
      fi

      action ${action%%(*} "${extension_args[@]}"
      ;;

    shell)
      if [[ "${action##*/}" == help ]]
      then # Load the help module also.
        modules bdsm/help
      fi
      _namespaces=${_path##${extension_actions_path}}
      extension_modules_load ${_namespaces//\// }
      action . "${_path}/${action}"
      ;;

    ruby)
      requires=()
      for script in dsl initialize
      do
        requires+=( "-r${modules_path}/ruby/core/${script}.rb" )
      done

      "${binary:-ruby}" -I"${modules_path}/ruby" -I"${extension_modules_path}/ruby" \
        ${requires[@]} "${_path}/${action}"
      ;;
    # python|lua|javascript)
      #   ADD "${modules_path}/${_type}/" to the lib path so the script can require "bdsm"
      #  "${_path}"
      #  ;;
    dne|noexec)
      fail "Processing ${action} failed, file type is unknown, file does not exist or file is not executable."
      ;;
    binary|*)
      "${_path}/${action}" ${extension_action} ${extension_args[*]}
      ;;
  esac
}

#
# ## extension\_action()
#
# Load the environment for a given extension action and then call it.
# This is the main function for BDSM.
#
# ### Input Parameters
#
# One or more extension names.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# The current extension's initialize file will be resourced into the current
# environment.
#
# ### Return Codes
#
# 0 for success
# 1 for failure
#
# ### Failure Scenarios
#
# Fails if no extension was given.
# Fails if no action was given.
#
# ### Usage Examples
#
#     $ cat $HOME/test
#     #!/usr/bin/env bash
#     source "/usr/local/bdsm/extensions/builtin/core/modules/shell/core/initialize" # Load BDSM framework core.
#     modules extensions
#     extension_action ext list
#
#     $  $HOME/test
#     bash fossil git libevent nginx p7zip postgresql rails redis srv tig tmux zeromq zlib
#
#
extension_action()
{
  if (( extension_action_calls > 0 ))
  then
    fail "can not call 2nd time\n  extension_action ${extension_args[@]}"
  fi

  extension_action_calls+=1

  unset extension action

  #TODO: Should be moved to core/cli?
  case "${extension_args[0]}" in
    *=*) # exact version specifier
      package_name="${extension_args[0]}"
      export package_version="${package_name##*=}"
      export package_name="${package_name%%=*}"
      export extension="${package_name}"
      extension_args[0]="${extension}"
      ;;
    *:*)
      package_name="${extension_args[0]}"
      export package_version="${package_name##*:}"
      export package_name="${package_name%%:*}"
      export extension="${package_name}"
      extension_args[0]="${extension}"
      ;;
  esac

  # Search in extensions actions
  if in_search_paths detect_action "${extension_args[@]}"
  then
    log_search action "$action" "${action_path##${bdsm_path}\/extensions\/} params:${extension_args[@]}"

  # Search in loaded modules actions
  elif in_search_paths detect_modules_action "${extension_args[@]}"
  then
    log_search action "$action" "${action_path##${bdsm_path}\/extensions\/} params:${extension_args[@]}"

  else
    error "Unknown action path: "${extension_args[@]}"\n\nUsage: \n  bdsm extension [namespace] [action]\n"
  fi

  extension_path="$extensions_path/$extension"
  extension_config_path="$extension_path/config"
  extension_templates_path="$extension_path/templates"
  extension_modules_path="$extension_path/modules"
  extension_actions_path="$extension_path/actions"
  extension_log_path="$log_path/$extension"

  paths=$(env | awk -F= -v ORS=' ' '/_path/{print $1}')
  flags=$(env | awk -F= -v ORS=' ' '/_flag/{print $1}')

  enter "${extension_path}"
  bdsm_exports
}

extension_path()
{
  local path extension=$1
  for path in $(extensions_search_paths)
  do
    if [[ -d "${path}/${extension}/actions" || -d "${path}/${extension}/modules" ]]
    then
      extension_path="${path}/${extension}"
      return 0
    fi
  done
  return 1
}

#
# ## extension\_reload()
#
# Reloads (re-sources) the current extension's DSL and initialization files.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# The current extension's DSL and initialization files are re-sourced into the
# calling environment.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# No current failure scenarios.
#
# ### Usage Examples
#
#     user$ extension_reload
#
extension_reload()
{
  local _path="${extension_modules_path}/shell"

  source_files "${_path}/dsl" "${_path}/initialize"
}

#
# ## extension\_reinitialize()
#
# Reinitializes the current extension.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# The current extension's initialize file will be resourced into the current
# environment.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user$ extension_reinitialize
#
extension_reinitialize()
{
  local _path="${extension_modules_path}/shell"

  source_files "${_path}/initialize"
}

#
# ## extension\_version()
#
# Outputs the named extension's version
#
# ### Input Parameters
#
# First parameter must be the name of an extension.
#
# ### Stream Outputs
#
# The extension-version string for the named extension.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if the extension name is not given.
#
# ### Usage Examples
#
#     user$ extension_reinitialize
#
extension_version()
{
  local _extension="${1:-}"

  if variable_is_nonempty _extension
  then
    shift
    true ${extension_path:="${extensions_path}/${_extension}"}

    if file_exists "${extension_path}/VERSION"
    then
      read -r extension_version < "${extension_path}/VERSION"
    else
      extension_version="head"
    fi

    log "${extension}-${extension_version}"
  else
    fail "Can not retrieve extension version as no extension was given."
  fi
}

#
# ## extension\_licence()
#
# Emits the extension's license file, if it exists.
#
# ### Input Parameters
#
# First parameter may optionally be an extension name.
#
# ### Stream Outputs
#
# If the extension has a LICENSE file then it wil be printed to STDOUT.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# No failure scenarios currently exist.
#
# ### Usage Examples
#
#     $ cat $HOME/test
#     !/usr/bin/env bash
#     source "/usr/local/bdsm/extensions/builtin/core/modules/shell/core/initialize" # Load BDSM framework core.
#     modules extensions
#     extension_license postgresql
#
#     $ $HOME/test
#     Copyright (c) 2009-2011 Wayne E. Seguin
#
#     Licensed under the Apache License, Version 2.0 (the \"License\");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an \"AS IS\" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
extension_license()
{
  local _extension="${1:-${extension}}"

  if variable_is_nonempty _extension
  then
    cat -v "${extensions_path}/${_extension}/LICENSE"
  else
    fail "Can not display extension license as an extension was not given."
  fi
}

#
# ## extensions\_installed()
#
# Lists the extensions installed on the currently running process's system.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# The names of the currently installed non-core extensions are printed to the
# calling environment's STDOUT.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     $ cat $HOME/test
#     #!/usr/bin/env bash
#     source "/usr/local/bdsm/extensions/builtin/core/modules/shell/core/initialize" # Load BDSM framework core.
#     modules extensions
#     extensions_installed
#
#     $ $HOME/test
#     bash fossil git libevent nginx p7zip postgresql rails redis tig tmux zeromq zlib
#
extensions_installed()
{
  extensions_in "$1"
}

#
# ## extensions\_available()
#
# Lists the extensions available to install on the currently running process's
# system.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# The names of the available to install non-core extensions are printed to the
# calling environment's STDOUT.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#      $ cat $HOME/test
#      #!/usr/bin/env bash
#      source "/usr/local/bdsm/extensions/builtin/core/modules/shell/core/initialize" # Load BDSM framework core.
#      modules extensions
#      extensions_available

#      $  $HOME/test
#      ack androidsdk apache bash bcrypt cacti cmake ctags curl deploy erlang fossil freetype ghc ghostscript git god iconv imagemagick jpeg keepalived lcms libevent libpng libwmf libxml2 libxslt logrotate lua memcached mercurial mongodb monit mysql nginx node npm openpkg openssl p7zip passenger pcre perl postgresql r rails rainbows readline redis riak rsync rvm screen sphinx sqlite3 subversion system thin tiff tig tmux unicorn unrar zeromq zlib zsh
#
extensions_available()
{
  NIY
}

#
# ## extensions\_in\_development()
#
# Lists the extensions available in the development repository, if set.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# The names of the available to install non-core development repository
# extensions are printed to the calling environment's STDOUT, if defined.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#      $ cat $HOME/test
#      #!/usr/bin/env bash
#      source "/usr/local/bdsm/extensions/builtin/core/modules/shell/core/initialize" # Load BDSM framework core.
#      modules extensions
#      extensions_in_development

#      $  $HOME/test
#      ack androidsdk apache bash bcrypt cacti cmake ctags curl deploy erlang fossil freetype ghc ghostscript git god iconv imagemagick jpeg keepalived lcms libevent libpng libwmf libxml2 libxslt logrotate lua memcached mercurial mongodb monit mysql nginx node npm openpkg openssl p7zip passenger pcre perl postgresql r rails rainbows readline redis riak rsync rvm screen sphinx sqlite3 subversion system thin tiff tig tmux unicorn unrar zeromq zlib zsh
#
extensions_in_development()
{
  true "${extensions_development_path:="${extensions_src_path}"}"

  list_extensions_in "${extensions_development_path}"
}

#
# ## extension\_actions()
#
# Lists actions exposed by the named extension.
#
# ### Input Parameters
#
# First parameter is the name of the extension to list actions for.
#
# ### Stream Outputs
#
# The names of the extensions in the given directory are printed to the
# calling environment's STDOUT.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if no extension name is given.
#
# ### Usage Examples
#
#      $ cat $HOME/test
#      #!/usr/bin/env bash
#      source "/usr/local/bdsm/extensions/builtin/core/modules/shell/core/initialize" # Load BDSM framework core.
#      modules extensions
#      extension_actions "postgresql"
#
#      $ $HOME/test
#      backup configure help initialize install restart start status stop uninstall upgrade
#
#
#
extension_actions()
{
  local _extension="${1:-}" _files _file _actions=()

  if variable_is_nonempty _extension
  then
    if path_exists "${extension_actions_path}"
    then
      _files=($(
      find "${extension_actions_path}" -mindepth 1 -type f -perm -o+rx
      ))

      for _file in "${_files[@]}"
      do
        _actions+=("${_file#${extension_actions_path}}")
      done
      _actions="${_actions[@]}" # Convert from array to string.
      printf "${_actions}"
    else
      return 0
    fi
  else
    fail "Cannot list actions for extension as no extension was given."
  fi
}

#
# ## extension\_is\_installed()
#
# Tests if a given extension is installed.
#
# ### Input Parameters
#
# First parameter is an extension name.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the extension is installed.
# 1 if the extension is not installed.
#
# ### Failure Scenarios
#
# Fails if no extension name is given.
#
# ### Usage Examples
#
#     $ cat $HOME/test
#     #!/usr/bin/env bash
#     source "/usr/local/bdsm/extensions/builtin/core/modules/shell/core/initialize" # Load BDSM framework core.
#     modules extensions
#     extensions_installed
#     echo
#     for extension in postgresql asdf
#     do
#       if extension_is_installed "${extension}"
#       then
#         echo "${extension} is installed! "
#       else
#         echo "${extension} is NOT installed! "
#       fi
#     done
#
#     $  $HOME/test
#     bash fossil git libevent mysql nginx node npm p7zip postgresql rails redis tig tmux zeromq zlib
#     postgresql is installed!
#     asdf is NOT installed!
#
extension_is_installed()
{
  local _name="${1:-}"

  if [[ -n "${_name}" ]]
  then
    local _extensions=" $(extensions_in "${extensions_path}") "

    case "${_extensions}" in
      (*[[:space:]]${_name}[[:space:]]*)
        return 0
        ;;
      (*)
        return 1
        ;;
    esac
  else
    fail "Can not check for an installed extension as no extension name was given."
  fi
}

#
# ## extension\_install()
#
# Installs the given external extension.
#
# ### Input Parameters
#
# First parameter is the name of the extension to install
# Second parameter is the src_path of the extension to install
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# The named extension will be installed to the filesystem in the BDSM
# external extension directory.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no extension are passed in to install.
#
# ### Usage Examples
#
#     user$ extension_install backup mpapis_bdsm_backup
#
extension_install()
{
  local _extension="${1:-}" _src
  _src="${2:-${_extension}}"

  if variable_is_nonempty _extension
  then
    if extension_is_valid "${extensions_src_path}/${_src}"
    then
      log "Installing ${_extension}"
      if path_exists "${bdsm_path}/extensions/external/${_extension}"
      then
        remove_paths "${bdsm_path}/extensions/external/${_extension}"
      fi

      copy_directory "${extensions_src_path}/${_src}" \
        to "${bdsm_path}/extensions/external/${_extension}"

      write "${extension_uri}" to "${bdsm_path}/extensions/external/${_extension}/.uri"

      #
      # TODO: Process extension dependencies...
      #
    else
      error "${extensions_src_path}/${_src} is not a valid extension"\
        "as it is missing VERSION and/or actions/help files."
    fi
  else
    fail "Cannot install an extension as no extension name was given."
  fi
}

extension_is_valid()
{
  local _path="$1"

  if [[ -n "${_path}" ]]
  then
    # TODO: Add all extension requirement checks here.
    [[ -d "${_path}/actions" ]] || [[ -d "${_path}/modules" ]]
  else
    fail "Cannot determine if an extension is valid as the path to the extension was not given."
  fi
}

extension_package()
{
  local _name="${1:-}" _path _version file

  if [[ -n "${_name}" ]]
  then
    _path="${extensions_development_path:-"$extensions_src_path"}"

    enter "${_path}"

    if extension_is_valid "${_path}/${_name}"
    then
      _version="$(cat "${_name}/VERSION")"
      log "Packaging extension ${_name}"
      log "${_name}-${_version}:"

      for archiver in "gzip" "bzip2 -z" "xz -z"
      do
        if command_exists ${archiver// *}
        then
          if tar cf "${_name}-${_version}.tar" "${_name}/"
          then
            # TODO: parallelize this.
            ${archiver} -f -9 "${_name}-${_version}.tar"
          else
            error "There was an error ($?) while trying to create a tar of the '${_name}' directory while packaging."
          fi
        fi

        ensure_paths_exist "${_path}/pkg"

        for file in "${_name}-${_version}".tar.*
        do
          log "  - ${file}"
          write "$( file_md5 "${file}" )" to "${file}.md5"
          log "  - ${file}.md5"
          move_files --force "${file}" "${file}.md5" \
            to "${_path}/pkg" from "${_path}"
        done
      done
      log "Packaging complete (packages are located in '${_path}/pkg' )"
    else
      error "Cannot package extension '${_name}' as the extension is not valid."
    fi
  else
    fail "Cannot package an extension as no extension name was given."
  fi
}

extension_publish()
{
  local _name="${1:-}" _path _version

  NIY "Extension publishing to extensions.beginrescueend.com has not yet been implemented."

  if [[ -n "${_name}" ]]
  then
    _path="${extensions_development_path:-"$extensions_src_path"}"

    enter "${_path}"

    if extension_is_valid "${_path}/${_name}"
    then
      _version="$(cat "${_name}/VERSION")"
      log "Packaging extension ${_name}"
      log "${_name}-${_version}:"

      for formats in ".tar.gz" ".tar.bz2" ".tar.xz"
      do
        if path_exists "${_path}/pkg/"
        then
          log "publishing ${_name}-${_version}"

          for file in "${_pth}/${_name}-${_version}".tar.{gz,xz,bz2}
          do
            log "  - ${file}"
            log "  - ${file}.md5"
            NIY "publishing"
          done
        else
          error "The local package path '${_path}' does not exist, did you run"\
            "'extension package ${_name}' before 'extension publish ${_name}'?"
        fi
      done
      log "Packaging complete (packages are located in '${_path}/pkg' )"
    else
      error "Cannot package extension '${_name}' as the extension is not valid."
    fi
  else
    fail "Cannot publish an extension as no extension name was given."
  fi
}

extension_module_add()
{
  local _extension="${1}" _module="${2}" _path
  _path="${3:-"${extensions_development_path}/${_extension}"}"

  if ! file_contains "${_path}/modules/shell/modules" "${_module}"
  then
    write "\n${_module}" append to "${_path}/modules/shell/modules"
  fi
}
