#!/usr/bin/env bash

#
# # Filesystem Module
#

#
# ## source\_files
#
# Safely source files only if they exists and are nonempty.
#
# ### Input Parameters
#
# One or more files.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# Nonempty files given will be sourced into the calling environment.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no files are given to sources.
#
# ### Usage Examples
#
#     user$ source_files "$HOME/.dotfiles/scripts/functions"
#
# ### Code Walkthrough
source_files()
{
  # Declare the file and files variables, set the files variable to an array
  # containing all of the parameters passed in.
  local _file _files=("$@")

  # If the files array is nonempty then for each file in the files array expand
  #   the HOME path if the file contains '~'.
  #
  # If the file is nonempty then source the file.
  #
  # If the file is sourced then go to the next element in the list.
  #
  # If sourcing the file failed then this indicates either a programming or a
  #   system error.
  #   Fail yielding a message with a backtrace which can be used to debug.
  #
  # If no files were passed given this is a programming error.
  # Fail yielding a message with a backtrace which can be used to debug.
  if array_is_nonempty _files
  then
    for _file in "${_files[@]}"
    do
      _file=${_file/\~\//$HOME\/} # Expand ~/ to full value of $HOME

      if file_is_nonempty "${_file}"
      then
        if bash -n "${_file}"
        then
          if source "${_file}"
          then
            continue
          else
            fail "There was an error sourcing the file '${_file}'."
          fi
        else
          fail "Not sourcing the file '${_file}' as it has invalid syntax."
        fi
      else
        continue
      fi
    done
  else
    fail "Cannot source files as no files were given."
  fi
}

#
# ## nonempty\_files
#
# Outputs a subset of the named files that are nonempty.
#
# ### Input Parameters
#
# One or more files.
#
# ### Stream Outputs
#
# Each nonempty filename parameter will be printed to STDOUT of the calling
# environment.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no file names are given as arguments.
#
# ### Usage Examples
#
#     user$ nonempty_files a $HOME/.bdmsrc c
#     /Users/wayneeseguin/.bdsmrc
#
nonempty_files()
{
  # Declare the file and files variables, set the files variable to an array
  # containing all of the parameters passed in.
  local _file _files=("$@")

  # If the files array is nonempty then expand the HOME path if the file
  #   contains '~'
  #
  # For each file in the files array if the file is nonempty print the file to
  #   STDOUT including a carriage return for explicit
  #   list separation (in case any of the files contain a path).
  #
  # If no files were given.  This indicates a programming error.
  # Fail yielding a message with a backtrace which can be used to debug.
  if array_is_nonempty _files
  then
    _file=${_file/\~\//$HOME\/} # Expand ~/ to full value of $HOME

    for _file in "${_files[@]}"
    do
      if file_is_nonempty "${_file}"
      then
        printf "${_file}\n"
      fi
    done
  else
    fail "Cannot return nonempty files as no files were given."
  fi
}

#
# ## ensure\_paths\_exist
#
# Iterates over the list of given paths, creates the directory if it
# does not exist.
#
# ### Input Parameters
#
# A list of paths to create if missing.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# Paths that were given but do not exist will be created.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no paths were given to.
#
# ### Usage Examples
#
#     user$ ls -a
#     . ..
#     user$ ensure_paths_exist a b c
#     user$ ls -a
#     . .. a b c
#
ensure_paths_exist()
{
  # Declare the path and paths variables, set the files variable to an array
  # containing all of the parameters passed in.
  local _path _paths=("$@")

  # If the paths array is nonempty then for each path in the paths array if the
  #   path already exists as a directory, move along to the next path.
  #
  # If the path already exists as a file then issue a warning (Question: Should
  #   this be a fail scenario?) Create the full path in the filesystem,
  #   including intermediate directories if they do not already exist.
  #
  # If the path was created successfully, continue on to processing the next
  # path, otherwise it failed to create path on filesystem, a system error
  # Fail yielding a message with a backtrace which can be used to debug.
  #
  # If no no files were given then this indicates a programming error
  # Fail yielding a message with a backtrace which can be used to debug.
  if array_is_nonempty _paths
  then
    for _path in "${_paths[@]}"
    do
      if path_exists "${_path}"
      then
        continue
      else
        if file_exists "${_path}"
        then
          warn "Could not ensure path '${_path}' exists"\
            " as a directory as it is a file."
        else
          if mkdir -p "${_path}"
          then
            continue
          else
            fail "Could not create the path '${_path}' "\
              "as the 'mkdir' command failed with error code '$?'"
          fi
        fi
      fi
    done
  else
    fail "Cannot ensure paths exist as no paths were given."
  fi
}

#
# ## remove\_files
#
# Removes the given files, if they exist.
#
# ### Input Parameters
#
# One or more file names/paths.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# The named files will no longer exist on the system, if they existed to begin
# with.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no files were named.
# Fails if a named file is a directory.
# Fails if a named file exists but is not a file.
#
# ### Usage Examples
#
#     user$ touch a b c
#     user$ ls -a
#     . .. a b c
#     user$ remove_files a b c
#     user$ ls
#     . ..
#
remove_files()
{
  # Declare the file and files variables, set the files variable to an array
  # containing all of the parameters passed in.
  local _file _files=("$@")

  # If the files array is nonempty then for each file in the files array
  #   if the file exists or is a symlink then forcedly remove the file
  #   if the file exists as a directory then this indicates an
  #   unexpected scenario, fail with a message and a backtrace for debugging.
  #
  # If the file still exists on the system then we do not know what it is,
  #   fail with an error message and a backtrace for debuging.
  #
  # If the files array is empty then this is a programming error,
  # Fail yielding a message with a backtrace which can be used to debug.
  if array_is_nonempty _files
  then
    for _file in "${_files[@]}"
    do
      if [[ -f "${_file}" || -L "${_file}" ]]
      then
        rm -f "${_file}"

      elif [[ -d "${_file}" ]]
      then
        fail "Cannot remove the file ${_file} as it is a directory."

      elif [[ -e "${_file}" ]]
      then
        fail "Failed to remove the file from the filesystem "\
          "as the file exists as an unknown/unhandled filesystem entity type:"\
          "$(file "${_file}")."
      else
        return 0
      fi
    done
  else
    fail "Cannot remove files as no files were given to remove."
  fi
}

#
# ## remove
#
# Removes the given filesystem entries, if they exist.
#
# ### Input Parameters
#
# One or more file, directory or symlink names/paths.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# The named files will no longer exist on the system, if they existed to begin
# with.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no files were named.
#
# ### Usage Examples
#
#     user$ touch a
#     user$ mkdir b
#     user$ ln -s b c
#     user$ ls -a
#     . .. a b c
#     user$ remove a b c
#     user$ ls
#     . ..
#
remove()
{
  # Declare the entry and entries variables, set the entries variable to an
  # array containing all of the parameters passed in.
  local _entity _entities=("$@")

  # If the entities array is nonempty then for each entity in the array
  #
  # If the entity exists on the filesystem then forcedly remove it recursively
  #   if the remove was successful, continue, otherwise fail yielding an error
  #   message and a backtrace for debugging.
  #
  # If the entities array is empty then this is a programming error,
  # Fail yielding a message with a backtrace which can be used to debug.
  if array_is_nonempty _entities
  then
    for _entity in "${_entities[@]}"
    do
      if [[ -e "${_entity}" ]]
      then
        if rm -rf "${_entity}"
        then
          continue
        else
          fail "Could not remove the entity '${_entity}'"\
            "as the 'rm' command failed with error code '$?'"
        fi
      else
        continue
      fi
    done
  else
    fail "Cannot remove entities as no filesystem entities were given "\
      "as parameters."
  fi
}

#
# ## remove\_paths
#
# Function Description
#
# ### Input Parameters
#
# Positional Parameter listing and descriptions.
#
# ### Stream Outputs
#
# What gets printed to STDOUT and STDERROR. 'None.' if nothing is output.
#
# ### Environmental effects
#
# What, if any, environmental side effects this function causes. 'None.' for none.
# If paths were requested to be removed and they already nonexistent then they
# will be listed in the nonexistant_paths array after this function returns.
# Additionally, paths that were successfully removed will be avialable in the
# remove_paths array.
#
# ### Return Codes
#
# 0 if ...
# 1 if ...
#
# ### Failure Scenarios
#
# Fails if ...
#
# ### Usage Examples
#
#     user$ {{ setup the scenario }}
#     user$ function_name {{ parameters }}
#     user$ {{ demonstrate the results}}
remove_paths()
{
  # Declare the path and paths variables, set the files variable to an array
  # containing all of the parameters passed in.
  local _path _paths=("$@")
  # Declare variables that will be available after the function returns.
  removed_paths=()
  nonexistant_paths=()

  # If the paths array is nonempty then for each path in the paths array
  #
  # If the path is a symlink then remove it from the filesystem
  #   If the remove was successful, add it to the removed_paths array and
  #   continue processing the next path. Otherwise there was a system error,
  #   Fail yielding a message with a backtrace which can be used to debug.
  #
  # If the path is directory then recursively remove it
  #   If the remove was successful, add it to the removed_paths array and
  #   continue processing the next path. Otherwise there was a system error,
  #   Fail yielding a message with a backtrace which can be used to debug.
  #
  # If the path is not a symlink or a directory and it still exists on the
  #   filesystem then path is an unhandled filesystem type and we have failed to
  #   remove it. This likely indicates a system error, fail giving sysadmins an
  #   error message with a backtrace which they can use to debug the issue.
  #
  # If the filesystem path already does not exist, add it to the
  #   nonexistant_paths array continue processing the next path in the list
  #
  # If no paths were given then this is a programming error, fail giving
  # developers an error message with a backtrace for debugging
  if array_is_nonempty _paths
  then
    for _path in "${_paths[@]}"
    do
      if [[ -L "${_path}" ]]
      then
        if rm -f "${_path}"
        then
          removed_paths+=("${_path}")
          continue
        else
          fail "Could not remove the entity '${_entity}'"\
            "as the 'rm' command failed with error code '$?'"
        fi
      elif [[ -d "${_path}" ]]
      then
        if rm -rf "${_path}"
        then
          removed_paths+=("${_path}")
          continue
        else
          fail "Could not remove the entity '${_entity}'"\
            "as the 'rm' command failed with error code '$?'"
        fi
      elif [[ -e "${_path}" ]]
      then
        fail "Cannot remove the path '${_path}' as it is a file."

      else
        nonexistant_paths+=("${_path}")
        continue
      fi
    done
  else
    fail "Cannot remove paths as no paths were given."
  fi
}

#
# ## ensure\_files\_exist
#
# Iterates over the list of given files, creates the directory if it
# does not exist.
#
# ### Input Parameters
#
# A list of files to create if missing.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# files that were given but do not exist will be created.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no files were given to.
#
# ### Usage Examples
#
#     user$ ls -a
#     . ..
#     user$ ensure_files_exist a b c
#     user$ ls -a
#     . .. a b c
#
ensure_files_exist()
{
  # Declare the file, files and path variables, set the files variable to an
  # array containing all of the parameters passed in.
  local _file _files=("$@") _path

  # If the files array is nonempty then for each file in the files array
  #
  # If the file exists, continue on to processing the next file
  #
  # Strip the file path off of file, ensure that the path exists then touch the
  # file to create it.
  #
  # If the file already exists, then continue on to processing the next file
  #
  # If touching the file failed then this indicates either a programming or a
  # system error.
  # Fail yielding a message with a backtrace which can be used
  # to debug the issue.
  #
  # If no files were passed in then this is a programming error,
  # Fail yielding a message with a backtrace which can be used to debug.
  if array_is_nonempty _files
  then
    for _file in "${_files[@]}"
    do
      if file_exists "${_file}"
      then
        continue
      else
        _path="${_file%\/*}"

        ensure_paths_exist "${_path}"

        if touch "${_file}"
        then
          continue
        else
          fail "There was an error creating the file '${_file}' on the"\
            "filesystem, the touch command returned error code '$?'"
        fi
      fi
    done
  else
    fail "Cannot ensure files exist as no files were given."
  fi
}

#
# ## ensure\_files\_are\_executable
#
# Sets the executable bits on a file if it is not executable.
#
# ### Input Parameters
#
# One or more file name/paths
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# Files that were given and not executable will have their execute bits set.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if no files were given as parameters.
# Fails if one of the files given was a directory.
# Fails if one of the files given does not exist.
# Fails if one of the files paths does not exist.
#
# ### Usage Examples
#
#     user$ ensure_files_are_executable /etc/rc.d/postgresql
#
# TODO: change this to be a  fail function if one is not
#       make this into make_files_executable or some such
ensure_files_are_executable()
{
  # Declare the file, files and path variables, set the files variable to an
  # array containing all of the parameters passed in.
  local _file _files=("$@") _path

  # If the files array is nonempty then for each file in the files array
  #
  # if the file exists on the filesystem but is a directory
  # This indicates either a programming or a system error.
  # Fail yielding a message with a backtrace for debugging
  #
  # otherwise strip off the filename to get the path.
  # if the path variable is nonempty and the path exists on the filesystem
  # then we attempt to perform the file mode execute setting.
  # If the file is successfully made executable then continue processing the
  # next file in the files array.
  # otherwise if it failed then this indicates a system error, fail giving
  # an error message with a backtrace for debugging
  #
  # If no files were passed in this indicates a programming error,
  # Fail yielding a message with a backtrace which can be used to debug.
  if array_is_nonempty _files
  then
    for _file in "${_files[@]}"
    do
      if path_exists "${_file}"
      then
        fail "Cannot make the file '${_file}' executable"\
          "as it is unexpectedly a directory."
      elif ! file_exists "${_file}"
      then
        fail "Cannot make the file '${_file}' executable"\
          " as the file does not exist."
      else
        _path="${_file%\/*}"

        if variable_is_nonempty _path && path_exists "${_path}"
        then
          if chmod +x "${_file}"
          then
            continue
          else
            fail "Could not make the file '${_file}' executable"\
              "as the 'chmod +x' command failed with error code '$?'"
          fi
        else
          fail "Cannot make the file '${_file} executable"\
            " as '${_path}' does not even exist."
        fi
      fi
    done
  else
    fail "Cannot ensure files are executable as no files were given."
  fi
}

#
# ## link
#
# Create a symlink from source to target.
#
# ### Input Parameters
#
# First parameter is the source
# Second parameter is the target
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# Symlink is created if no failure conditions are triggered.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if target exists and is a file.
# Fails if target exists and is a directory.
#
# ### Usage Examples
#
# The following usages are equivalent
#
#     user$ link from /home/user/.vim/vimrc to /home/user/.vimrc
#     user$ link /home/user/.vim/vimrc to /home/user/.vimrc
#     user$ link /home/user/.vim/vimrc /home/user/.vimrc
#
link()
{
  # Declare the token, source, target and force_flag variables.
  # Default force_flag to 0.
  local token _source _target _force_flag=0 _path

  # While the parameters list is nonempty.
  # Assign the first parameter to the token variable.
  # Remove the first paramter from the parameters list.
  # Switch based on the token string's contents
  while (( $# > 0 ))
  do
    token="$1"
    shift

    case "${token}" in
      # If the token is 'to' then assign the next parameter to the target
      # variable and remove the first paramter from the parameters list.
      to)
        _target="${1}"
        shift
        ;;

      # if the token is 'from' then assign the first parameter to the source
      # variable  and remove the first paramter from the parameters list.
      from)
        _source="${1}"
        shift
        ;;

      # if the token is '--force' then set the force flag to 1
      --force)
        _force_flag=1
        ;;

      # if the token is unknown and if the source variable is empty then assign
      # the token to the source variable otherwise if the source variable is
      # set but the target variable is empty then assign the token to the target
      # variable
      *)
        if variable_is_empty _source
        then
          _source="$token"

        elif variable_is_empty _target
        then
          _target="$token"
        fi
        ;;
    esac
  done

  # if force_flag is 1 then remove the target from the filesystem
  if (( _force_flag == 1 ))
  then
    remove "${_target}"
  fi

  # TODO: What should we do if files already exist?
  # If target exists and is a file then this is a system or programming error
  # Fail yielding a message with a backtrace which can be used to debug.
  if file_exists "${_target}"
  then
    fail "Cannot link ${_source} to ${_target} exists and is a file!"

  # If target exists and is a directory then this is a system/programming error
  # Fail yielding a message with a backtrace which can be used to debug.
  elif path_exists "${_target}"
  then
    fail "Cannot link ${_source} to ${_target} exists and is a directory"

  else
    # If the targets base path is nonempty then ensure that it exists
    _path="${_target%\/*}"
    if variable_is_nonempty _path
    then
      ensure_paths_exist "${_path}"
    fi

    # try to create a symbolic link the source to the target
    # the fileystem symbolic link was created successfully.
    # if it failed to create a symbolic link from the source to the target.
    # Fail yielding a message with a backtrace which can be used to debug.
    if ln -fs "${_source}" "${_target}"
    then
      return 0
    else
      fail "Failed to create a symbolic link from '${_source}' to '${_target}'"\
        "as the 'ln -fs' command failed with error code '$?'"
    fi
  fi
}

#
# ## move\_directory
#
# Moves a directory from one location to another.
# (This is also used to rename a directory.)
#
# ### Input Parameters
#
# The first parameter is the source directory name
# The second parameter is the target directory name
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# If successful, a directory is renamed (moved).
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if the source directory is not specified.
# Fails if the target directory is not specified.
#
# ### Usage Examples
#
#     user$ move_directory "freetds-0.91rc"* to "freetds-0.91"
#
move_directory()
{
  local _source _target _mode _owner

  # While the parameters list is nonempty.
  while (( $# > 0 ))
  do
    # Assign the first parameter to the token variable and remove the first
    # paramter from the parameters list.
    token="$1"
    shift

    # Switch based on the token string's contents
    case "${token}" in
      # if the token is 'to' then assign the next parameter to the target
      # variable and remove the first paramter from the parameters list.
      to)
        _target="${1}"
        shift
        ;;

      # if the token is 'to' then assign the next parameter to the source
      # variable and remove the first paramter from the parameters list.
      from)
        _source="${1}"
        shift
        ;;

      # if the token is 'mode' then assign the next parameter to the mode
      # variable and remove the first paramter from the parameters list.
      mode)
        _mode="${1}"
        shift
        ;;

      # if the token is 'owner' then assign the next parameter to the owner
      # variable and remove the first paramter from the parameters list.
      owner)
        _owner="${1}"
        shift
        ;;

      # if the token is unknown and if the source variable is empty assign the
      # token to the source variable otherwise if the source variable is set
      # but the target variable is empty assign the token to the target variable
      *)
        if variable_is_empty _source
        then
          _source="$token"

        elif variable_is_empty _target
        then
          _target="$token"
        fi
        ;;
    esac
  done

  # if the source variable is not empty and if the target variable is not empty
  # if we successfully move the source to the target
  # and if the mode variable is not empty
  # then set the filesystem mode of the target to the given mode
  #
  # if the filesystem mode change failedrecursively of the target
  # This indicates either a a system or programming error.
  # Fail yielding a message with a backtrace which can be used to debug.
  #
  # if the owner variable is not empty then set the filesystem owner of
  # the target to the given mode
  #
  # If the filesystem ownership change recursively of the target failed
  # This indicates either a system or programming error.
  # Fail yielding a message with a backtrace which can be used to debug.
  #
  # If the move of the source to the target failed this indicates an error.
  # Fail yielding a message with a backtrace which can be used to debug.
  #
  # If the target was not passed in this is a system / programming error.
  # Fail yielding a message with a backtrace which can be used to debug.
  #
  # If the source was not passed in this is a system / programming error.
  # Fail yielding a message with a backtrace which can be used to debug.
  if variable_is_nonempty _source
  then
    if variable_is_nonempty _target
    then
      if mv "${_source}" "${_target}"
      then
        if variable_is_nonempty _mode
        then
          if ! chmod -R "${_mode}" "${_target}"
          then
            fail "Could not change mode recursively of '${_target}' to"\
              "${_mode} as the 'chmod -R' command failed with error code '$?'"
          fi
        fi

        if variable_is_nonempty _owner
        then
          if ! chown -R "${_owner}" "${_target}"
          then
            fail "Could not change ownership recursively of '${_target}' to"\
              "${_owner} as the 'chown -R' command failed with error code '$?'"
          fi
        fi
      else
        fail "Could not move '${_source}' to '${_target}'"\
          "as the 'chmod -R' command failed with error code '$?'"
      fi
    else
      fail "Cannot move file as the target must be specified. "
    fi
  else
    fail "Cannot move file as the source must be specified. "
  fi
}

#
# ## move\_file
#
# Moves a file from one location to another.
# (This is also used to rename a file.)
#
# ### Input Parameters
#
# The first parameter is the source file name
# The second parameter is the target file name
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# If successful, a file is renamed (moved).
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if the source file is not specified.
# Fails if the target file is not specified.
#
# ### Usage Examples
#
#     user$ move_file "$HOME/.bashrc" to "$HOME/.bashrc.orig"
#
# ### Code Walkthrough
move_file()
{
  trace_filter move_file

  # Declare the source, target, mode, owner and token local variables.
  local _source _target _mode _owner token

  # While the parameter list is not empty
  while (( $# > 0 ))
  do
    # Set the token variable to the first parameter
    token="$1"
    # and remove the token parameter from the list of parameters
    shift
    # Now we examine the token and switch based on what is found.
    case "${token}" in
      # the token is 'to' assign the target variable to the next parameter
      # and remove the target paramter from the parameters.
      to)
        _target="${1}"
        shift
        ;;
      # if the token is 'from' tehn assign the first parameter to the source
      # variable and remove the from paramter from the parameters.
      from)
        _source="${1}"
        shift
        ;;
      # if the token is 'mode' then assign the mode variable to the first
      # parameter and remove the mode paramter from the parameters.
      mode)
        _mode="${1}"
        shift
        ;;
      # if the token is 'owner' then assign the owner variable to the first
      # parameter and remove the owner paramter from the parameters.
      owner)
        _owner="${1}"
        shift
        ;;

      # if the token is unknown and if the source variable is empty then assign
      # the unknown token to the source variable
      #
      # otherwise if the target variable is empty then assign the unknown token
      # to the target variable.
      #
      # Otherwise we have both a source and a target, so we do not know
      # what to do with further unknown parameters. This indicates a
      # programming error, fail giving developers an error message and
      # a backtrace which they can use to debug their code.
      *)
        if variable_is_empty _source
        then
          _source="$token"

        elif variable_is_empty _target
        then
          _target="$token"

        else
          fail "The source '${_source}' and target '${_target}' have been set"\n
            "and the current token token is unknown'${token}'."
        fi
        ;;
    esac
  done

  # If the source variable is not empty
  if variable_is_nonempty _source
  then
    # and if the target variable is not empty
    if variable_is_nonempty _target
    then
      # then move the source to the target
      if mv "${_source}" "${_target}"
      then
        # The move was successful, now we process mode and owner if specified.

        # If the mode variable is set, change the target's filesystem mode
        if variable_is_nonempty _mode
        then
          # If the file mode change is not successful
          if ! chmod "${_mode}" "${_target}"
          then
            # Then print out a warning to the user and continue processing
            # this is typically a non-fatal error which can be rectified
            # afterwards.
            warn "There was an error ($?) changing the "\
              "target's '${_target}' mode to '${_mode}'"
          fi
        fi

        # If the owner variable is set, change the target's owner
        if variable_is_nonempty _owner
        then
          # If the ownership change is not successful
          if ! chown "${_owner}" "${_target}"
          then
            # Then print out a warning to the user and continue processing
            # this is typically a non-fatal error which can be rectified
            # afterwards.
            warn "There was an error ($?) changing the "\
              "target's '${_target}' owner to '${_owner}'"
          fi
        fi
      else
        # There was an error moving the file, inform the user with a failure
        # message and provide a backtrace as this could very well indicate a
        # larger issue overall and debugging will be necessary.
        fail "There was an error ($?) moving the source '${_source}'" \
          "to the target '${_target}'"
      fi
    else
      # The target was not specified, this is a programming error
      # send a fail message which will send a backtrace allowing the developer
      # to quickly debug and pass in the proper parameters.
      fail "Cannot move file as the target must be specified."
    fi
  else
      # The source was not specified, this is a programming error
      # send a fail message which will send a backtrace allowing the developer
      # to quickly debug and pass in the proper parameters.
      fail "Cannot move file as both the source must be specified."
  fi
}

#
# ## copy\_file
#
# Copys a file from one location to another.
# (This is also used to rename a file.)
#
# ### Input Parameters
#
# The first parameter is the source file name
# The second parameter is the target file name
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# If successful, a file is renamed (copyd).
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if the source file is not specified.
# Fails if the target file is not specified.
#
# ### Usage Examples
#
#     user$ copy_file "$HOME/.bashrc" to "$HOME/.bashrc.orig"
#
copy_file()
{
  # Declare the source and target variables.
  local _source _target _flags=() _force_flag

  # While there are parameters left
  while (( $# > 0 ))
  do
    # Assign the first parameter into the token variable
    token="$1"
    # Remove the token parameter from the parameter's list
    shift

    # switch based on the token contents
    case "${token}" in
      # if the token is 'to'
      to)
        # assign the next parameter to the target variable.
        _target="${1}"
        # Remove the first paramter from the parameters list.
        shift
        ;;
      # if the token is 'to' then
      from)
        # assign the next parameter to the source variable.
        _source="${1}"
        # Remove the first paramter from the parameters list.
        shift
        ;;
      # if the token is '--force'
      --force)
        # set the force_flag variable to 1
        _force_flag=1
        ;;
      # if the token is 'mode' then
      mode)
        # set the mode variable to the first parameter and
        _mode="${1}"
        # Remove the first paramter from the parameters list.
        shift
        ;;
      # if the token is 'owner' then
      owner)
        # set the mode variable to the next parameter
        _owner="${1}"
        # Remove the first paramter from the parameters list.
        shift
        ;;
      # if the token is unknown then
      *)
        # and if the source variable is empty
        if variable_is_empty _source
        then
          # assign the token to the source variable
          _source="$token"

        # if the source variable is set but the target variable is empty
        elif variable_is_empty _target
        then
          # assign the token to the target variable
          _target="$token"
        fi
        ;;
    esac
  done

  if (( _force_flag == 1 ))
  then
    _flags+=(-f)
  fi

  # if the source variable is not empty
  if variable_is_nonempty _source
  then
    # if the target variable is not empty
    if variable_is_nonempty _target
    then
      if cp ${_flags[*]} "${_source}" "${_target}"
      then
        if variable_is_nonempty _mode
        then
          # then set the filesystem mode of the target to the given mode
          if ! chmod "${_mode}" "${_target}"
          then
            # Failed to change filesystem mode of the target
            # This indicates either a a system or programming error.
            # Fail giving a message with a backtrace that can be used to debug.
            fail "Could not change mode of '${_target}' to"\
              "${_mode} as the 'chmod' command failed with error code '$?'"
          fi
        fi
        # if the owner variable is not empty
        if variable_is_nonempty _owner
        then
          # then set the filesystem owner of the target to the given mode
          if ! chown "${_owner}" "${_target}"
          then
            # Failed to change filesystem ownership of the target
            # This indicates either a a system or programming error.
            # Fail giving a message with a backtrace that can be used to debug.
            fail "Could not change ownership of '${_target}' to"\
              "${_owner} as the 'chown' command failed with error code '$?'"
          fi
        fi
      else
        # There was an error copying the file, inform the user with a failure
        # message and provide a backtrace as this could very well indicate a
        # larger issue overall and debugging will be necessary.
        fail "Could not copy '${_source}' to '${_target}' "\
          "as the 'cp' command failed with error code '$?'"
      fi
    else
      # The target was not specified, this is a programming error
      # send a fail message which will send a backtrace allowing the developer
      # to quickly debug and pass in the proper parameters.
      fail "Cannot move file as the target must be specified."
    fi
  else
      # The source was not specified, this is a programming error
      # send a fail message which will send a backtrace allowing the developer
      # to quickly debug and pass in the proper parameters.
      fail "Cannot move file as the source must be specified."
  fi
}

#
# ## copy\_files
#
# Copys a given list of files into the named path.
#
# ### Input Parameters
#
# The first parameter is the path to copy the files into.
# Remaining parameters are the file name/path list.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# If successful, all named files are copied into the target path.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if no target path is given.
# Fails if no files are given.
#
# ### Usage Examples
#
#     user$ copy_files "$HOME/.bashrc" "$HOME/.bash_profile" \
#             to "$HOME/backup"
#
copy_files()
{
  # Declare the source, target, file, force_flag and files variables.
  local _source _target _file _force_flag=0 _files=()

  # while the parameters list is not empty
  while (( $# > 0 ))
  do
    # Set the token variable to the first parameter
    token="$1"
    # and remove the token parameter from the list of parameters
    shift
    # Now we examine the token and switch based on what is found.
    case "${token}" in
      # if the token is 'to',
      to)
        _target="${1}"
        # and remove the target paramter from the parameters.
        shift
        ;;

      # if the token is 'from',
      from)
        # assign the first parameter to the source variable.
        _source="${1}"
        # and remove the from paramter from the parameters.
        shift
        ;;

      # if the token is '--force'
      --force)
        # set the force_flag variable to 1
        _force_flag=1
        ;;

      # if the token is unknown
      *)
        # then append it to the files array
        _files+=( "${token}" )
        ;;
    esac
  done

  # and if the target variable is not empty
  if variable_is_nonempty _target
  then
    # and if the files array is nonempty
    if array_is_nonempty _files
    then
      # Then ensure that the target path exists
      ensure_paths_exist "${_target}"

      # For each file in the files
      for _file in "${_files[@]}"
      do
        # If the file exists
        if file_exists "${_file}"
        then
          # if force flag has been set
          if (( _force_flag == 1 ))
          then
            # then forcedly copy the source file to the target file.
            if cp -f "${_file}" "${_target}/${_file//*\/}"
            then
              # Successfully forcedly copied the filesytem entity,
              # continue processing the next entity in the list
              continue
            else
              # Failed to remove the entity from the filesystem.
              # This indicates a system error, fail giving sysadmins an error
              # message with a backtrace which they can use to debug the issue.
              fail "Could not remove the entity '${_entity}'"\
                "as the 'rm' command failed with error code '$?'"
            fi
          else
            # copy the source file to the target file.
            if cp "${_file}" "${_target}/${_file//*\/}"
            then
              # Successfully copied the filesytem entity,
              # continue processing the next entity in the list
              continue
            else
              # Failed to copy the entity from the filesystem.
              # This indicates a system error, fail giving sysadmins an error
              # message with a backtrace which they can use to debug the issue.
              fail "Failed to copy '${_file}' to '${_target}/${_file//*\/}'"
                "as the 'cp' command failed with error code '$?'"
            fi
          fi
        else
          error "Cannot copy file ${_file} to ${_target}"\
            "as the file does not exist."
        fi
      done
    else
      # No files were given.
      # This indicates a programming error, fail giving developers an error
      # message with a backtrace which they can use to debug their code.
      fail "Cannot copy files as no files were given."
    fi

  else
    # The target was not specified, this is a programming error.
    # send a fail message which will send a backtrace allowing the developer
    # to quickly debug and pass in the proper parameters.
    fail "Cannot move file as the and target must be specified."
  fi
}

#
# ## copy\_directories to
#
# Copys a given list of directories into the named path.
#
# ### Input Parameters
#
# The first parameter is the path to copy the directorys into.
# Remaining parameters are the directory name/path list.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# If successful, all named directories are copied into the target path.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if no target path is given.
# Fails if no directories are given.
#
# ### Usage Examples
#
#     user$ copy_paths to "$HOME/backup/" "$HOME/bin" "$HOME/projects"
#
copy_paths()
{
  local _target _directory _force_flag=0 _directories=()

  # while the parameters list is not empty
  while (( $# > 0 ))
  do
    # Set the token variable to the first parameter
    token="$1"
    # and remove the token parameter from the list of parameters
    shift
    # Now we examine the token and switch based on what is found.
    case "${token}" in
      # if the token is 'to',
      to)
        _target="${1}"
        # and remove the target paramter from the parameters.
        shift
        ;;

      # if the token is 'from',
      from)
        # Add the first parameter to the source directories.
        _directories+=( "${1}" )
        # and remove the from paramter from the parameters.
        shift
        ;;

      # if the token is '--force'
      --force)
        # set the force_flag variable to 1
        _force_flag=1
        ;;

      # if the token is unknown
      *)
        # then append it to the files array
        _directories+=( "${token}" )
        ;;
    esac
  done

  # and if the target variable is not empty
  if variable_is_nonempty _target
  then
    # and if the directories array is nonempty
    if array_is_nonempty _directories
    then
      # ensure that the target path exists
      ensure_paths_exist "${_target}"

      # for each directory in the directories array
      for _directory in "${_directories[@]}"
      do
        # if the directory exists
        if path_exists "${_directory}"
        then
          # then recurively copy it into the target path.
          if cp -Rf "${_directory}" "${_target}/"
          then
            # Successfully forcedly copied the filesytem entity,
            # continue processing the next entity in the list
            continue
          else
            # Failed to remove the entity from the filesystem.
            # This indicates a system error, fail giving sysadmins an error
            # message with a backtrace which they can use to debug the issue.
            fail "Could not copy the entity '${_entity}'"\
              "as the 'cp -Rf' command failed with error code '$?'"
          fi
        else
          error "Cannot copy directory ${_directory} to ${_target}"\
            "as the directory '${_directory}' does not exist (in '$PWD')."
        fi
      done
    else
      # No directories were passed given.
      # This indicates a programming error, fail giving developers an error
      # message with a backtrace which they can use to debug their code.
      fail "Cannot copy directories as no files were given."
    fi

  else
    # The target was not specified, this is a programming error.
    # send a fail message which will send a backtrace allowing the developer
    # to quickly debug and pass in the proper parameters.
    fail "Cannot move file as the and target must be specified."
  fi
}


#
# ## copy\_directory
#
# Copys a directory from one location to another.
# (This is also used to rename a directory.)
#
# ### Input Parameters
#
# The first parameter is the source directory name
# The second parameter is the target directory name
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# If successful, a directory is renamed (copyd).
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if the source directory is not specified.
# Fails if the target directory is not specified.
#
# ### Usage Examples
#
#     user$ copy_directory "freetds-0.91rc"* to "freetds-0.91"
#
copy_directory()
{
  # Declare the source and target variables.
  local _source _target

  # While there are parameters left
  while (( $# > 0 ))
  do
    # Assign the first parameter into the token variable
    token="$1"
    # Remove the token parameter from the parameter's list
    shift

    # switch based on the token contents
    case "${token}" in
      # if the token is 'to'
      to)
      # assign the next parameter to the target variable.
      _target="${1}"
      # Remove the first paramter from the parameters list.
      shift
      ;;
      # if the token is 'to' then
      from)
      # assign the next parameter to the source variable.
      _source="${1}"
      # Remove the first paramter from the parameters list.
      shift
      ;;
      # if the token is unknown then
      *)
        # and if the source variable is empty
        if variable_is_empty _source
        then
          # assign the token to the source variable
          _source="$token"

        # if the source variable is set but the target variable is empty
        elif variable_is_empty _target
        then
          # assign the token to the target variable
          _target="$token"
        fi
        ;;
    esac
  done

  # if the source variable is not empty
  if variable_is_nonempty _source
  then
    # if the target variable is not empty
    if variable_is_nonempty _target
    then
      if cp -Rf "${_source}" "${_target}"
      then
        # successfully recursively copied source to the target return true, 0
        return 0
      else
        # There was an error copying the file, inform the user with a failure
        # message and provide a backtrace as this could very well indicate a
        # larger issue overall and debugging will be necessary.
        fail "Could not copy '${_source}' to '${_target}' "\
          "as the 'cp' command failed with error code '$?'"
      fi
    else
      # The target was not specified, this is a programming error
      # send a fail message which will send a backtrace allowing the developer
      # to quickly debug and pass in the proper parameters.
      fail "Cannot move file as both the target must be specified."
    fi
  else
    # The source was not specified, this is a programming error
    # send a fail message which will send a backtrace allowing the developer
    # to quickly debug and pass in the proper parameters.
    fail "Cannot move file as both the source must be specified."
  fi
}

#
# ## move\_files
#
# Moves a given list of files into the named path.
#
# ### Input Parameters
#
# Parameters are file name/path for one or more files
# Target directory is specified as 'to {{path}}'
# Optional source directory is specified as 'from {{path}}'
# Optional force flag '--force' may be specified
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# If successful, all named files are copied into the target path.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails if no target path is given.
# Fails if no files are given.
#
# ### Usage Examples
#
#     user$
#
# Alternatively,
#
#     user$ move_files ".bashrc" ".bash_profile" from "$HOME" to "$HOME/backup"
#
# ### Code Walkthrough
move_files()
{
  # Declare the source target file force_flag and files variables
  # Initialize force_flag to zero and the files array to empty
  local _source _target _file _force_flag=0 _files=() _if_exists_flag=0

  # While the parameters list is nonempty.
  while (( $# > 0 ))
  do
    # Assign the first parameter to the token variable and remove the first
    # paramter from the parameters list.
    token="$1"
    shift

    # Switch based on the token string's contents
    case "${token}" in
      # if the token is 'to' then assign the next parameter to the target
      # variable and remove the first paramter from the parameters list.
      to)
        _target="${1}"
        shift
        ;;

      # if the token is 'to' then assign the first parameter to the source
      # variable and remove the first paramter from the parameters list.
      from)
        _source="${1}"
        shift
        ;;
      --if-exists)
        _if_exists_flag=1
        ;;

      # if the token is '--force' then set the force flag to 1
      --force)
        _force_flag=1
        ;;

      # if the token is unknown then add the token to the files array
      *)
        _files+=( "${token}" )
        ;;
    esac
  done

  # and if the target variable is not empty
  if variable_is_nonempty _target
  then
    # and if the files array is nonempty
    if array_is_nonempty _files
    then
      # for each file in the files array
      for _file in "${_files[@]}"
      do
        # if the source variable is nonempty then prepend source to the file.
        if variable_is_nonempty _source
        then
          _file="${_source}/${_file}"
        fi

        # if the file exists
        if  file_exists "${_file}"
        then
          # and force_flag is set
          if (( _force_flag == 1 ))
          then
            # then forcedly move the file and continue processing the next
            # entity in the list or fail with an error message and a backtrace
            # As there was a system error, fail with a message and backtrace
            # which can be used to debug the issue
            if mv -f "${_file}" "${_target}/${_file//*\/}"
            then
              continue
            else
              fail "Could not forcedly move the entity '${_entity}'"\
                "as the 'mv -f' command failed with error code '$?'"
            fi
          else
            # otherwise move the file
            if mv "${_file}" "${_target}/${_file//*\/}"
            then
              # Successfully copied the filesytem entity,
              # continue processing the next entity in the list
              continue
            else
              # Failed to remove the entity from the filesystem.
              # This indicates a system error, fail giving sysadmins an error
              # message with a backtrace which they can use to debug the issue.
              fail "Could not move the file '${_file}' to "\
                "'${_target}/${_file//*\/}"\
                "as the 'mv' command failed with error code '$?'"
            fi
          fi
        else
          if (( _if_exists_flag == 0 ))
          then
            # Failed to move the the file as the file is missing
            # This indicates a system error, fail giving sysadmins an error
            # message with a backtrace which they can use to debug the issue.
            fail "Could not move the source file '${_file}' to the target"\
              "'${_target}/${_file//*\/}"\
              "as the source file does not exist"
          else
            continue
          fi
        fi
      done
    else
      # No files were given.
      # This indicates a programming error, fail giving developers an error
      # message with a backtrace which they can use to debug their code.
      fail "Cannot move files as no files were given."
    fi
  else
    # The target was not specified, this is a programming error
    # send a fail message which will send a backtrace allowing the developer
    # to quickly debug and pass in the proper parameters.
    fail "Cannot move file as the target must be specified."
  fi
}

#
# ## chown\_files
#
# Change ownership of a list of files.
#
# ### Input Parameters
#
# First parameter is the new owner[:group] of the files.
# Remaining parameters are the file names/paths.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no new owner[:group] was specified.
# Fails if no list of files was given.
#
# ### Usage Examples
#
#     user$ chown_files $USER "$HOME/.bashrc" "$HOME/.bash_profile"
#
chown_files()
{
  # Declare the identity and file and set the identiy to the first parameter
  local _identity _file _flags=()

  # While the parameters list is nonempty.
  while (( $# > 0 ))
  do
    # Assign the first parameter to the token variable and remove the first
    # paramter from the parameters list.
    token="$1"
    shift

    # Switch based on the token string's contents
    case "${token}" in
      --recursive)
        _flags+=("-R")
        ;;
      *)
        # if the identity variable is nonempty
        if variable_is_empty _identity
        then
          _identity="${token}"
        else
          _files+=("${token}")
        fi
        ;;

    esac
  done

  if variable_is_nonempty _identity
  then
    # if the file sarray is nonempty
    if array_is_nonempty _files
    then
      # for each file in the files array
      for _file in "${_files[@]}"
      do
        # if the file exists on the filesystem
        if file_exists "${_file}"
        then
          if chown ${_flags[*]} ${_identity} "${_file}"
          then
            # Successfully copied the filesytem entity,
            # continue processing the next entity in the list
            continue
          else
            # Failed to recursively chown the entity on the filesystem.
            # This indicates a system error, fail giving sysadmins an error
            # message with a backtrace which they can use to debug the issue.
            fail "Could not recursively chown the file '${_file}' to "\
              "'${_target}/${_file//*\/}"\
              "as the 'chown -R' command failed with error code '$?'"
          fi
        else
          # The file does not exist.
          # This indicates a system error, fail giving sysadmins an error
          # message with a backtrace which they can use to debug the issue.
          fail "Cannot chown file ${_file} to ${_identity}"\
            "as the file does not exist."
        fi
      done
    else
      # No files were given.
      # This indicates a programming error, fail giving developers an error
      # message with a backtrace which they can use to debug their code.
      fail "Cannot chown files as no files were given."
    fi
  else
    fail "user[:group] not given as the first parameter."
  fi
}

#
# ## chown\_paths
#
# Change ownership of a list of paths.
#
# ### Input Parameters
#
# First parameter is the new owner[:group] of the paths.
# Remaining parameters are the path names/paths.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no new owner[:group] was specified.
# Fails if no list of paths was given.
#
# ### Usage Examples
#
#     user$ chown_paths $USER "$HOME" "/usr/local"
#
chown_paths()
{
  # Declare the identity and file and set the identiy to the first parameter
  local _identity="${1:-}" _path _recursive_flag=0 _command _flags=()

  # If the first parameter is --recursive
  if [[ "${1:-}" == --recursive* ]]
  then
    # Set the recursive flag to 1
    _recursive_flag=1
    # Remove the recursive flag parameter
    shift
    # Set identity to the next parameter
    _identity="${1}"
  fi

  if (( _recursive_flag == 1 ))
  then
    # the chown command will be with the recursive flag
    _flags+=(-R)
  fi

  # if the identity variable is nonempty
  if variable_is_nonempty _identity
  then
    # Remove the identity from the path
    shift
    # Set the paths array to the remaining parameters
    local _paths=("$@")

    # if the paths sarray is nonempty
    if array_is_nonempty _paths
    then
      # for each path in the paths array
      for _path in "${_paths[@]}"
      do
        # if the path exists on the filesystem
        if path_exists "${_path}"
        then

          # Run the command
          if chown ${_flags[*]} ${_identity} "${_path}"
          then
            # Successfully changed ownership of the filesytem entity,
            # continue processing the next entity in the list
            continue
          else
            # Failed to chown the entity on the filesystem.
            # This indicates a system error, fail giving sysadmins an error
            # message with a backtrace which they can use to debug the issue.
            fail "Could not chown the path '${_path}' to '${_identity}"\
              "as the 'chown' command failed with error code '$?'"
          fi
        else
          # The file does not exist.
          # This indicates a system error, fail giving sysadmins an error
          # message with a backtrace which they can use to debug the issue.
          fail "Cannot chown path ${_path} to ${_identity}"\
            "as the path (directory) does not exist."
        fi
      done
    else
      # No paths were given.
      # This indicates a programming error, fail giving developers an error
      # message with a backtrace which they can use to debug their code.
      fail "Cannot chown paths as no files were given."
    fi
  else
    # The user[:group] was not given.
    # This indicates a programming error, fail giving developers an error
    # message with a backtrace which they can use to debug their code.
    fail "Cannot chown paths as no user[:group] was given as the first parameter"
  fi
}

#
# ## chmod\_files
#
# Change ownership of a list of files.
#
# ### Input Parameters
#
# First parameter is the new owner[:group] of the files.
# Remaining parameters are the file names/files.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no new owner[:group] was specified.
# Fails if no list of files was given.
#
# ### Usage Examples
#
#     user$ chmod_files $USER "$HOME/.bashrc" "$HOME/.bsah_profile"
#
chmod_files()
{
  # Declare the identity and file and set the identiy to the first parameter
  local _permissions="${1:-}" _file _command

  # if the permissions variable is nonempty
  if variable_is_nonempty _permissions
  then
    # Remove the permissions from the file
    shift
    # Set the files array to the remaining parameters
    local _files=("$@")

    # if the files sarray is nonempty
    if array_is_nonempty _files
    then
      # for each file in the files array
      for _file in "${_files[@]}"
      do
        # if the file exists on the filesystem
        if file_exists "${_file}"
        then
          # Run the chmod command
          if chmod ${_permissions} "${_file}"
          then
            # Successfully changed ownership of the filesytem entity,
            # continue processing the next entity in the list
            continue
          else
            # Failed to chmod the entity on the filesystem.
            # This indicates a system error, fail giving sysadmins an error
            # message with a backtrace which they can use to debug the issue.
            fail "Could not chmod the file '${_file}' to "\
              "'${_target}/${_file//*\/}"\
              "as the 'chmod' command failed with error code '$?'"
          fi
        else
          # The file does not exist.
          # This indicates a system error, fail giving sysadmins an error
          # message with a backtrace which they can use to debug the issue.
          fail "Cannot chmod file ${_file} to ${_permissions}"\
            "as the file (directory) does not exist."
        fi
      done
    else
      # No files were given.
      # This indicates a programming error, fail giving developers an error
      # message with a backtrace which they can use to debug their code.
      fail "Cannot chmod files as no files were given."
    fi
  else
    # The user[:group] was not given.
    # This indicates a programming error, fail giving developers an error
    # message with a backtrace which they can use to debug their code.
    fail "Cannot chmod files as no user[:group] was given as the first parameter"
  fi
}

#
# ## chmod\_paths
#
# Change ownership of a list of paths.
#
# ### Input Parameters
#
# First parameter is the new owner[:group] of the paths.
# Remaining parameters are the path names/paths.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no new owner[:group] was specified.
# Fails if no list of paths was given.
#
# ### Usage Examples
#
#     user$ chmod_paths $USER "$HOME" "/usr/local"
#
chmod_paths()
{
  # Declare the permissions, flags and file and set the identiy to the
  # first parameter
  local _permissions="${1:-}" _path _recursive_flag=0 _command _flags=()

  # If the first parameter is --recursive
  if [[ "${1:-}" == "--recursive" ]]
  then
    # Set the recursive flag to 1
    _recursive_flag=1
    # Remove the recursive flag parameter
    shift
    # Set permissions to the next parameter
    _permissions="${1}"
  fi

  if (( _recursive_flag == 1 ))
  then
    # the chmod command will be with the recursive flag
    _flags+=(-R)
  fi

  # if the permissions variable is nonempty
  if variable_is_nonempty _permissions
  then
    # Remove the permissions from the path
    shift
    # Set the paths array to the remaining parameters
    local _paths=("$@")

    # if the paths sarray is nonempty
    if array_is_nonempty _paths
    then
      # for each path in the paths array
      for _path in "${_paths[@]}"
      do
        # if the path exists on the filesystem
        if path_exists "${_path}"
        then

          # Run the command
          if chmod ${_flags[*]} ${_permissions} "${_path}"
          then
            # Successfully changed ownership of the filesytem entity,
            # continue processing the next entity in the list
            continue
          else
            # Failed to chmod the entity on the filesystem.
            # This indicates a system error, fail giving sysadmins an error
            # message with a backtrace which they can use to debug the issue.
            fail "Could not chmod the file '${_file}' to "\
              "'${_target}/${_file//*\/}"\
              "as the 'chmod' command failed with error code '$?'"
          fi
        else
          # The file does not exist.
          # This indicates a system error, fail giving sysadmins an error
          # message with a backtrace which they can use to debug the issue.
          fail "Cannot chmod path ${_path} to ${_permissions}"\
            "as the path (directory) does not exist."
        fi
      done
    else
      # No paths were given.
      # This indicates a programming error, fail giving developers an error
      # message with a backtrace which they can use to debug their code.
      fail "Cannot chmod paths as no files were given."
    fi
  else
    # The user[:group] was not given.
    # This indicates a programming error, fail giving developers an error
    # message with a backtrace which they can use to debug their code.
    fail "Cannot chmod paths as no user[:group] was given as the first parameter"
  fi
}

#
# ## enter
#
# Changes the current working directory ($PWD) to the given directory with
# sanity checks.
#
# ### Input Parameters
#
# First parameter is the directory to change into.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# The current working directory (PWD) will become the given directory if
# successful.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no directory was given.
# Fails if the given directory does not exist.
#
# ### Usage Examples
#
#     user$ enter "/usr/local/src"
#
enter()
{
  # set the path variable to the first parameter.
  local _path="$1"

  # If the path variable was passed in
  if variable_is_nonempty _path
  then
    # and if the path exists on the filesystem
    if path_exists "${_path}"
    then
      # then change the calling environment's working directory into the
      # given path
      if builtin cd "${_path}"
      then
        # return true, 0 for success
        return 0
      else
        # Failed to chmod the entity on the filesystem.
        # This indicates a system error, fail giving sysadmins an error
        # message with a backtrace which they can use to debug the issue.
        fail "Could not cd into the given path '${_path}' "\
          "as the 'cd' command failed with error code '$?'"
      fi
    else
    # The path does not exist on the filesystem.
    # This indicates a programming error, fail giving developers an error
    # message with a backtrace which they can use to debug their code.
      fail "Cannot enter the directory '${_path}' as it does not exist."
    fi
  else
    # No path was passed into the function.
    # This indicates a programming error, fail giving developers an error
    # message with a backtrace which they can use to debug their code.
    fail "Cannot enter a directory as no directory was given."
  fi
}

#
# ## hash\_file
#
# Set and get key/value pairs from a given has file.
#
# ### Input Parameters
#
# The first parameter is the file name/path for the hash file.
# The second parameter is the key name.
# The third (optional) parameter is the value to assign to the given key.
#
# ### Stream Outputs
#
# The value of the key retrieved is printed if no value parameter was given.
#
# ### Environmental effects
#
# If a value parameter was given the key=value pair is written to the named
# hash file.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no file name/path was provided.
# Fails if no key name was given.
# Fails if the file path does not exist.
#
# ### Usage Examples
#
#     user$ hash_file /usr/local/bdsm/extensions/core/config/defaults website_url
#     https://bdsm.beginrescueend.com/
#
hash_file()
{
  # Declare the message, key, value and file variables
  local _message _key _value _file="${1:-}"

  # If the file variable is nonempty
  if variable_is_nonempty _file
  then
    # remove the file from the parameters list
    shift
    # assign the next parameter into the key varibale
    local _key="${1:-}"
    # if the key variable is nonempty
    if variable_is_nonempty _key
    then
      # then remove the key variable from the parameters list
      shift
      # Determine the path of the file
      # Note that this will be '.' if only a file name was given.
      local _path=$(dirname "${_file}")

      # if the directory exists on the filesystem
      if path_exists "${_path}"
      then
        # ensure that the file exists in the directory, creating an empty file
        # if it does not exist.
        ensure_files_exist "$_file"
      else
        # The directory does not exist.
        # This indicates a system error, fail giving sysadmins an error
        # message with a backtrace which they can use to debug the issue.
        fail "{$_message[*]}"\
          "Cannot access the database file '${_file}' "\
          "as the directory '${_path}' does not exist on the filesystem."
      fi

      # Assign the remaining parameters as a single string into the value
      # variable
      value="$*"

      # pattern match against the parameters value
      case "$value" in
        # if unset or delete was specified then
        unset|delete)
          sed -i.tmp "s#^${_key}=.*\$##" "${_file}"
          ;;
        # if the parameters value was not unset or delete
        *)
          # if the value variable is nonempty then
          if variable_is_empty value
          then
            # use awk in order to retrieve the value for the given key from
            # the file on the filesystem
            awk -F= '/^'"${_key}"'=/{print $2}' "${_file}"
          else # set
            # use awk in order to set the value for the given key to the value
            # given onto the file on the filesystem
            if ! awk -F= "/^'"${_key}"'=/{print $2}" "${_file}" >/dev/null 2>&1
            then
              echo "${_key}=$value" >> "${_file}"
            else # overwrite
              sed -i.tmp "s#^${_key}=.*\$#${_key}=$value#" "${_file}"
            fi
          fi
          ;;
      esac
    else
      fail "Key not given.\n => Usage: $0 <filename> <key> [value]"
    fi
  else
    fail "File path/name not given.\n => Usage: $0 <filename> <key> [value]"
  fi
}

#
# ## file\_is\_executable
#
# Check if a file name/path is executable.
#
# ### Input Parameters
#
# First parameter is a file name/path.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the named file is executable.
# 1 if the named file is not executable or does not exist.
#
# ### Failure Scenarios
#
# Fails if no file name/path was given.
#
# ### Usage Examples
#
#     user$ file_is_executable /bin/bash
#     user$ echo $?
#     0
#
#     user$ file_is_executable /bin/asdfasdf
#     user$ echo $?
#     1
#
file_is_executable()
{
  local _file="${1:-}"

  if [[ -n "${_file}" ]]
  then
    [[ -r "${_file}" && -x "${_file}" ]]
  else
    fail "Cannot check if a file is executable as no file path/name was given"
  fi
}

#
# ## file\_exists
#
# Check if a file name/path exists.
#
# ### Input Parameters
#
# First parameter is a file name/path.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the named file exists.
# 1 if the named file does not exist or does not exist.
#
# ### Failure Scenarios
#
# Fails if no file name/path was given.
#
# ### Usage Examples
#
#     user$ file_exists /bin/bash
#     user$ echo $?
#     0
#
#     user$ file_exists /bin/asdfasdf
#     user$ echo $?
#     1
#
file_exists()
{
  local _file="${1:-}"
  if [[ -n "${_file}" ]]
  then
    [[ -f "${_file}" ]]
  else
    fail "Cannot check to see if a file exists as no file name/path was given"
  fi
}

#
# ## file\_does\_not\_exist
#
# Check if a file name/path exists.
#
# ### Input Parameters
#
# First parameter is a file name/path.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the named file does not exist.
# 1 if the named file exists.
#
# ### Failure Scenarios
#
# Fails if no file name/path was given.
#
# ### Usage Examples
#
#     user$ file_does_not_exist /bin/bash
#     user$ echo $?
#     1
#
#     user$ file_does_not_exist /bin/asdfasdf
#     user$ echo $?
#     0
#
file_does_not_exist()
{
  local _file="${1:-}"
  if [[ -n "${_file}" ]]
  then
    [[ ! -f "${_file}" ]]
  else
    fail "Cannot check to see if a file exists as no file name/path was given"
  fi
}

#
# ## file\_is\_nonempty
#
# Check if a file name/path is nonempty.
#
# ### Input Parameters
#
# First parameter is a file name/path.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the named file is nonempty.
# 1 if the named file is empty or does not exist.
#
# ### Failure Scenarios
#
# Fails if no file name/path was given.
#
# ### Usage Examples
#
#     user$ file_is_nonempty /bin/bash
#     user$ echo $?
#     0
#
#     user$ file_is_nonempty /bin/asdfasdf
#     user$ echo $?
#     1
#
file_is_nonempty()
{
  local _file="${1:-}"

  if [[ -n "${_file}" ]]
  then
    if [[ -d "${_file}" ]]
    then
      error "When checking whether the file '${_file}' is nonempty, a directory was found in its place."
    else
      [[ -s "${_file}" ]]
    fi
  else
    fail "Cannot check if file is nonempty as no file path/name was given"
  fi
}

#
# ## file\_is\_empty
#
# Check if a file name/path is empty.
#
# ### Input Parameters
#
# First parameter is a file name/path.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the named file is empty.
# 1 if the named file is not empty or does not exist.
#
# ### Failure Scenarios
#
# Fails if no file name/path was given.
#
# ### Usage Examples
#
#     user$ file_is_empty /bin/bash
#     user$ echo $?
#     1
#
#     user$ file_is_empty /bin/asdfasdf
#     user$ echo $?
#     0
#
file_is_empty()
{
  local _file="${1:-}"

  if [[ -n "${_file}" ]]
  then
    [[ -f "${_file}" && ! -s "${_file}" ]]
  else
    fail "Cannot check if a file is empty as no file path/name was given"
  fi
}

#
# ## file\_is\_missing
#
# Check if a file name/path is missing.
#
# ### Input Parameters
#
# First parameter is a file name/path.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the named file is missing.
# 1 if the named file is not missing or does not exist.
#
# ### Failure Scenarios
#
# Fails if no file name/path was given.
#
# ### Usage Examples
#
#     user$ file_is_missing /bin/bash
#     user$ echo $?
#     1
#
#     user$ file_is_missing /bin/asdfasdf
#     user$ echo $?
#     0
#
file_is_missing()
{
  local _file="${1:-}"

  if [[ -n "${_file}" ]]
  then
    [[ ! -f "${_file}" ]]
  else
    fail "Cannot check if file is missing as no file path/name was given"
  fi
}

#
# ## file\_is\_empty\_or\_missing()
#
# Check if a file name/path is empty or missing.
#
# ### Input Parameters
#
# First parameter is a file name/path.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the named file is empty or missing.
# 1 if the named file exists or is nonempty.
#
# ### Failure Scenarios
#
# Fails if no file name/path was given.
#
# ### Usage Examples
#
#     user$ file_is_missing /bin/bash
#     user$ echo $?
#     1
#
#     user$ file_is_missing /bin/asdfasdf
#     user$ echo $?
#     0
#
file_is_empty_or_missing()
{
  local _file="${1:-}"

  if [[ -n "${_file}" ]]
  then
    [[ ! -f "${_file}" || ! -s "${_file}" ]]
  else
    fail "Cannot check if file is empty or missing as no file path/name was given"
  fi
}

#
# ## file\_contains()
#
# Checks if a named file contains a given string/unix regexp pattern.
#
# ### Input Parameters
#
# First parameter is the file name/path.
# Second parameter is the pattern to match.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the given file contains the given pattern.
# 1 if the given file does not contain the given pattern.
#
# ### Failure Scenarios
#
# Fails if the file path/name was not given
# Fails if the pattern is not given.
# Fails if the file path/name given either does not exist or is not a file.
#
# ### Usage Examples
#
#     user$ cat "$HOME/test"
#     #!/usr/bin/env bash
#     source "/usr/local/bdsm/extensions/builtin/core/modules/shell/core/initialize" # Load BDSM framework core.
#
#     modules filesystem
#
#     if file_contains "$HOME/.bdsmrc" ".*w00t.*"
#     then
#       echo "w00t! "
#     else
#       echo "no w00t :("
#     fi
#
#
file_contains()
{
  local _file="${1:-}" _message

  if [[ -n "${_file}" ]]
  then
    shift
    local _pattern="${1:-}"
    if [[ -n "${_pattern}" ]]
    then
      if [[ -f "${_file}" ]]
      then
        grep "${_pattern}" "${_file}" >/dev/null 2>&1
      else
        _message=(
        "First parameter must be a file, '${_file}'"
        " does not appear to be a file."
        )
        fail "${_message[*]}"
      fi
    else
      fail "String pattern for search was not given!"
    fi
  else
    fail "File path/name was not given!"
  fi
}

#
# ## symlink\_exists()
#
# Check if a symlink name/path exists.
#
# ### Input Parameters
#
# First parameter is a symlink name/path.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the named symlink exists.
# 1 if the named symlink does not exist or does not exist.
#
# ### Failure Scenarios
#
# Fails if no symlink name/path was given.
#
# ### Usage Examples
#
#     user$ symlink_exists /usr/local/bin/bdsm
#     user$ echo $?
#     0
#
#     user$ symlink_exists /bin/asdfasdf
#     user$ echo $?
#     1
#
symlink_exists()
{
  local _file="${1:-}"

  if [[ -n "${_file}" ]]
  then
    [[ -L "${_file}" ]]
  else
    fail "Cannot check if symlink exists as no path/name was given"
  fi
}

#
# ## path\_exists()
#
# Check if a directory name/path exists.
#
# ### Input Parameters
#
# First parameter is a directory name/path.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the named directory exists.
# 1 if the named directory does not exist or does not exist.
#
# ### Failure Scenarios
#
# Fails if no directory name/path was given.
#
# ### Usage Examples
#
#     user$ path_exists /usr/local/bin/bdsm
#     user$ echo $?
#     0
#
#     user$ path_exists /bin/asdfasdf
#     user$ echo $?
#     1
#
path_exists()
{
  local _directory="${1:-}"

  if [[ -n "${_directory}" ]]
  then
    [[ -d "$_directory" ]]
  else
    fail "Cannot check if directory exists as no name/path was given"
  fi
}

#
# ## path\_join()
#
# Joins path elements together passed as arguments into one path string.
#
path_join()
{
  local IFS='/'
  printf "$*"
}

#
# ## directories\_in()
#
# Function Description
#
# ### Input Parameters
#
# Positional Parameter listing and descriptions.
#
# ### Stream Outputs
#
# What gets printed to STDOUT and STDERROR. 'None.' if nothing is output.
#
# ### Environmental effects
#
# What, if any, environmental side effects this function causes. 'None.' for none.
#
# ### Return Codes
#
# 0 if ...
# 1 if ...
#
# ### Failure Scenarios
#
# Fails if ...
#
# ### Usage Examples
#
#     user$ {{ setup the scenario }}
#     user$ function_name {{ parameters }}
#     user$ {{ demonstrate the results}}
directories_in()
{
  local _path="${1:-}"

  if [[ -n "${path}" ]]
  then
    if [[ -d "${_path}" ]]
    then
      find "${_path}" -mindepth 1 -maxdepth 1 -type d
    else
      fail "'${_path}' is not a directory!"
    fi
  else
    fail "Directory name/path was not given!"
  fi
}

#
# ## directories\_in\_matching()
#
#
directories_in_matching()
{
  local _path="${1:-}" _pattern="${2:-}"

  if [[ -n "${path}" ]]
  then
    if [[ -d "${_path}" ]]
    then
      if [[ -n "${_pattern}" ]]
      then
        find "${_path}" -mindepth 1 -maxdepth 1 -type d
      else
        fail "'${_path}' is not a directory!"
      fi
    else
      fail "'${_path}' is not a directory!"
    fi
  else
    fail "Directory name/path was not given!"
  fi
}

#
# ## directories\_under()
#
#
directories_under()
{
  local _path="${1:-}"

  if [[ -n "${path}" ]]
  then
    if [[ -d "${_path}" ]]
    then
      find "${_path}" -mindepth 1 -type d
    else
      fail "'${_path}' is not a directory!"
    fi
  else
    fail "Directory name/path was not given!"
  fi
}

#
# ## directories\_under\_matching()
#
#
directories_under_matching()
{
  local _path="${1:-}"

  if [[ -n "${path}" ]]
  then
    shift
    local _pattern="${1:-}"
    if [[ -d "${_path}" ]]
    then
      if [[ -n "${_pattern}" ]]
      then
        find "${_path}" -mindepth 1 -iname "${_pattern}" -type d
      else
        fail "'${_path}' is not a directory!"
      fi
    else
      fail "'${_path}' is not a directory!"
    fi
  else
    fail "Directory name/path was not given!"
  fi
}

#
# ## files\_in()
#
# Function Description
#
# ### Input Parameters
#
# Positional Parameter listing and descriptions.
#
# ### Stream Outputs
#
# What gets printed to STDOUT and STDERROR. 'None.' if nothing is output.
#
# ### Environmental effects
#
# What, if any, environmental side effects this function causes. 'None.' for none.
#
# ### Return Codes
#
# 0 if ...
# 1 if ...
#
# ### Failure Scenarios
#
# Fails if ...
#
# ### Usage Examples
#
#     user$ {{ setup the scenario }}
#     user$ function_name {{ parameters }}
#     user$ {{ demonstrate the results}}
files_in()
{
  local _path="${1:-}"

  if [[ -n "${path}" ]]
  then
    if [[ -d "${_path}" ]]
    then
      find "${_path}" -mindepth 1 -maxdepth 1 -type f
    else
      fail "'${_path}' is not a directory!"
    fi
  else
    fail "Directory name/path was not given!"
  fi
}

#
# ## files\_under()
#
# Function Description
#
# ### Input Parameters
#
# Positional Parameter listing and descriptions.
#
# ### Stream Outputs
#
# What gets printed to STDOUT and STDERROR. 'None.' if nothing is output.
#
# ### Environmental effects
#
# What, if any, environmental side effects this function causes. 'None.' for none.
#
# ### Return Codes
#
# 0 if ...
# 1 if ...
#
# ### Failure Scenarios
#
# Fails if ...
#
# ### Usage Examples
#
#     user$ {{ setup the scenario }}
#     user$ function_name {{ parameters }}
#     user$ {{ demonstrate the results}}
files_under()
{
  local _path="${1:-}"

  if [[ -n "${path}" ]]
  then
    if [[ -d "${_path}" ]]
    then
      find "${_path}" -mindepth 1 -type f
    else
      fail "'${_path}' is not a directory!"
    fi
  else
    fail "Directory name/path was not given!"
  fi
}

#
# ## files\_in\_path\_matching()
#
# Find all files in given path matching a given name glob pattern.
#
# ### Input Parameters
#
# First parameter is the path to search in
# Second parameter is the pattern to match in
#
# ### Stream Outputs
#
# What gets printed to STDOUT and STDERROR. 'None.' if nothing is output.
#
# ### Environmental effects
#
# What, if any, environmental side effects this function causes. 'None.' for none.
#
# ### Return Codes
#
# 0 if ...
# 1 if ...
#
# ### Failure Scenarios
#
# Fails if ...
#
# ### Usage Examples
#
#     user$ files_in_path_matching ...
#
files_in_path_matching()
{
  local _path="${1:-}"

  if [[ -n "${path}" ]]
  then
    shift
    _pattern="${1:-}"
    if [[ -d "${_path}" ]]
    then
      shift
      if [[ -n "${_pattern}" ]]
      then
        find "${_path}" -mindepth 1 -iname "${_pattern}" -type f
      else
        fail "Cannot find files as a filename or pattern was not given."
      fi
    else
      fail "'${_path}' is not a directory!"
    fi
  else
    fail "Directory name/path was not given!"
  fi
}

#
# ## replace\_content()
#
# Replaces a given (sed compatable) pattern with given replacement text in a
# file.
#
# ### Input Parameters
#
# First parameter is the pattern
# [optional 'with']
# Second parameter is the replacement content
# [optional 'in']
# Third parameter is the file name/path
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# The pattern will be matched against the file name/path content and the
# replacement text will be put in place.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no pattern was given
# Fails if replacement content was not given
# Fails if filename was not given
# Fails if the file does not exist
#
# ### Usage Examples
#
# # Replace contents in a file with a given (sed) regex pattern.
#
#     replace_content "^PREFIX =.*" with "${install_path}" in Makefile
#
replace_content()
{
  local _token _pattern _content _file _message

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

    case "${_token}" in
      (with)
        _content="${1:-}"
        shift
        ;;
      (in)
        _file="${1}"
        shift
        ;;
      (*)
        if [[ -z "${_pattern}" ]]
        then
          _pattern="${_token}"
        else
          fail "Unknown token '${_token}' passed as a parameter.'"
        fi
        ;;
    esac
  done

  if [[ -n "${_pattern:-}" ]]
  then
    if [[ -n "${_content:-}" ]]
    then
      if [[ -n "${_file:-}" ]]
      then
        if [[ -f "${_file:-}" ]]
        then
          sed -i -e "s#${_pattern//\#/\\#}#${_content//\#/\\#}#g" "${_file}"
        else
          fail "Can not replace file content as the file does not exist."
        fi
      else
        _message=(
        "Can not file content as the filename was not given,"
        " specify by 'in \"/path/to/filename\"'"
        )
        fail "${_message[*]}"
      fi
    else
      _message=(
      "Cannot replace content as the replacement content was not given, "
      "specify by 'with \"replacement text\"'."
      )
      fail "${_message[*]}"
    fi
  else
    fail "Cannot replace content as not even a pattern was given."
  fi
}

#
# ## file\_matches\_md5()
#
# Checks to see if the given file matches the given md5 sum.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# none.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the file matches the md5sum
# 1 if the file does not match the md5sum
# 1 if the file is empty or missing
#
# ### Failure Scenarios
#
# Fails if no file name/path was given.
# Fails if no md5 string was given.
#
# ### Usage Examples
#
# Example Usage:
#
# user$ file_matches_md5
#
# ### Notes
#
file_matches_md5()
{
  local _file="$1" _md5="$2" _file_md5

  if [[ -n "${_file}" ]]
  then
    if [[ -n "${_md5}" ]]
    then
      if [[ -s "${_file}" ]]
      then
        _file_md5="$(file_md5 "${_file}")"

        [[ "${_file_md5// *}" = "$_md5" ]]
      else
        return 1
      fi
    else
      fail "Cannot determine if file matches md5 as no md5 was given."
    fi
  else
    fail "Cannot determine if file matches md5 as no file was given."
  fi
}

#
# ## file\_md5()
#
#
#
file_md5()
{
  local _file="${1:-}" _command _md5

  if [[ -n "${_file}" ]]
  then
    if os_is_darwin
    then
      _command="/sbin/md5 -q"

    else
      if command_exists "md5sum"
      then
        _command="md5sum"

      elif command_exists md5
      then
        _command="md5"

      else
        error "Could not find a suitable md5 command in the path."
      fi
    fi

    if _md5="$(${_command} "$_file" 2>/dev/null)"
    then
      printf "${_md5// *}"

    else # Should this be error ?
      warn "There was an unknown error computing the md5"
      return 1
    fi
  else
    fail "Cannot calculate md5 of a file as no file name/path was given."
  fi
}

#
# ## write()
#
# Write arguments to the given file.
#
# ### Input Parameters
#
# Strings to be written.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no arguments are given.
#
# ### Usage Examples
#
# user$ write "Hello there! " to /some/file
# user$ cat /some/file
# Hello there!
#
write()
{
  local _token _file _message _append_flag=0

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

    case "${_token}" in
      (append|--append)
        _append_flag=1
        ;;
      (to)
        _file="${1}"
        shift
        ;;
      (*)
        _message="${_token}"
        ;;
    esac
  done

  if variable_is_nonempty _message
  then
    if variable_is_nonempty _file
    then
      if (( _append_flag == 1 ))
      then
        printf "${_message}\n" >> "${_file}"
      else
        printf "${_message}\n" > "${_file}"
      fi
    else
      printf "${_message}\n"
    fi
  else
    fail "Cannot log a message as no message was given."
  fi
}

#
# ## full_path()
#
# return full path to given directory.
#
# ### Input Parameters
#
# path.
#
# ### Stream Outputs
#
# full path
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# Fails if no arguments are given.
# Fails if given path does not exists
#
# ### Usage Examples
#
# user$ cd ; bdsm call full_path test
# /home/user/test
#
full_path()
{
  local _path="$1"

  if variable_is_nonempty _path
  then
    if path_exists "${_path}"
    then
      (
        builtin cd "${_path}"
        pwd
      )
    else
      fail "Path '${_path}' does not seam to be a valid path"
    fi
  else
    fail "Can not go to path as no path was given"
  fi
}
