#!/usr/bin/env bash

#
# # Bash System Module DSL
#
:
# ## init\_scripts\_path()
#
# Returns the system init scripts path (eg. /etc/init.d /etc/rc.d)
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# The system init scripts path is printed to the calling environments STDOUT.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# Fails the OS is not of a (yet) recognized type.
#
# ### Usage Examples
#
#     user@archlinux$ init_scripts_path
#     /etc/rc.d
#
# ### Code Walkthrough
init_scripts_path()
{
  # If the user id is 0 (eg. the user is root)
  if (( UID == 0 ))
  then
    # then based on operating system type
    case "$MACHTYPE" in
      *darwin*)
        # If the OS is darwin, there is no 'init.d path' so to speak, the
        # service module will create and use one at /etc/init.d.
        printf "/etc/init.d"
        ;;

      *linux*|*)
        # If the OS is Linux, then we test for the location of the init.d
        # scripts, either /etc/init.d (eg. CentOS) or /etc/rc.d (eg. ArchLinux)
        if [[ -d "/etc/init.d" ]]
        then
          printf "/etc/init.d"
        elif [[ -d "/etc/rc.d" ]]
        then
          printf "/etc/rc.d"
        else
          # If neither /etc/init.d nor /etc/rc.d were found, fail throwing a
          # backtrace as this case needs to be accounted for. Currently I am not
          # aware of a linux that does not use either one of these two schemes.
          fail "Unknown init scripts directory (/etc/init.d or /etc/rc.d not found)."
        fi
        ;;
    esac
  else
    # If the user is not root, set the init.d path inside the user's $HOME.
    printf "$HOME/.init.d"
  fi
}

#
# ## confd\_path()
#
# Return the system conf.d path.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# Prints the conf.d path for the current system and user.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     root# confd_path
#     /etc/conf.d
#
# ### Code Walkthrough
confd_path()
{
  # If the user id is 0 (eg. the user is root) then Use the FHS standard /etc
  # direcotry otherwise place the conf.d directory inside the user's $HOME
  if (( UID == 0 ))
  then
    printf "/etc/conf.d"
  else
    printf "$HOME/.conf.d"
  fi
}

#
# ## os\_is\_linux()
#
# Tests if the operating system the process is running on is Linux.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the operating system is linux
# 1 if the operating system is not linux
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     root@archlinux# os_is_linux
#     root@archlinux# echo $?
#     0
#
# ### Code Walkthrough
os_is_linux()
{
  # If the machine type is linux then return the code for true/success, 0
  # Otherwise return a nonzero error code, 1
  if [[ "$MACHTYPE" = *linux* ]]
  then
    return 0
  else
    return 1
  fi
}

#
# ## os\_is\_darwin()
#
# Tests if the operating system the process is running on is darwin.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the operating system is darwin
# 1 if the operating system is not darwin
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user@OSX# os_is_darwin
#     user@OSX# echo $?
#     0
#
# ### Code Walkthrough
os_is_darwin()
{
  # If the machine type is darwin then return the code for true/success, 0
  # otherwise return a nonzero error code, 1
  if [[ "$MACHTYPE" = *darwin* ]]
  then
    return 0
  else
    return 1
  fi
}

#
# ## os\_is\_bsd()
#
# Tests if the operating system the process is running on is bsd.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the operating system is bsd
# 1 if the operating system is not bsd
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     root@bsd# os_is_bsd
#     root@bsd# echo $?
#     0
#
# ### Code Walkthrough
os_is_bsd()
{
  # If the machine type is BSD then return the code for true/success, 0
  # otherwise return a nonzero error code, 1
  if [[ "$MACHTYPE" = *bsd* ]]
  then
    return 0
  else
    return 1
  fi
}

#
# ## os\_is\_solaris()
#
# Tests if the operating system the process is running on is solaris.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the operating system is linux
# 1 if the operating system is not linux
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     root@linux# os_is_linux
#     root@linux# echo $?
#     0
#
# ### Code Walkthrough
os_is_solaris()
{
  # If the machine type is Solaris then return the code for true/success, 0
  # otherwise return a nonzero error code, 1
  if [[ "$MACHTYPE" = *solaris* ]]
  then
    return 0
  else
    return 1
  fi
}

#
# ## os\_is\_aix()
#
# Tests if the operating system the process is running on is aix.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the operating system is aix
# 1 if the operating system is not aix
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     root@aix# os_is_aix
#     root@aix# echo $?
#     0
#
# ### Code Walkthrough
os_is_aix()
{
  # If the machine type is aix then return the code for true/success, 0
  # otherwise return a nonzero error code, 1
  if [[ "$MACHTYPE" = *aix* ]]
  then
    return 0
  else
    return 1
  fi
}

#
# ## os\_type()
#
# Prints out the operating system category / type.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# Prints the OS category/type to the calling environments STDOUT.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     root@archlinux# os_type
#     linux
#
#     user@osx# os_type
#     darwin
#
# ### Code Walkthrough
os_type()
{
  # Print out the OS type based on the detected machine type.
  case "$MACHTYPE" in
    (*linux*)
      printf "%s" "linux"
      ;;
    (*darwin*)
      printf "%s" "darwin"
      ;;
    (*bsd*)
      printf "%s" "bsd"
      ;;
    (*solaris*)
      printf "%s" "solaris"
      ;;
    (*aix*)
      printf "%s" "aix"
      ;;
    (*)
      # If the machine type is an unknown category then simply echo it.
      printf "%s" "$MACHTYPE"
      ;;
  esac
}

#
# ## os\_arch\_type()
#
# Prints out the OS Arch type.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# Prints out the architecture type string of the system that is running the
# current process.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user$ os_arch_type
#     x86_64
#
# ### Code Walkthrough
os_arch_type()
{
  # Print out the OS architecture type based on the detected machine type.
  case "${MACHTYPE//-*}" in
    # The machine type is 32 bit
    # Note that this accounts for Solaris 32 bit as well (i86pc).
    (i386|i686|i86pc)
      printf "%s" "i386"
      ;;
    # The machine type is 64 bit
    (*x86_64*)
      printf "%s" "x86_64"
      ;;
    # The machine type is PowerPC
    (*ppc*)
      printf "%s" "ppc"
      ;;
  esac
}

#
# ## os\_arch()
#
# Prints out the full OS machine type string.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# Print the OS machine type string to STDOUT of the calling environment.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success.
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user@OSX# os_arch
#     i386-apple-darwin10.7.0
#
# ### Code Walkthrough
os_arch()
{
  # Print out the full OS machine type string which includes the architecture
  # to the calling environments STDOUT stream.
  printf '%s' "${MACHTYPE//-*}"
}

#
# ## os\_arch\_capable()
#
# Tests if the operating system the process is running on is Linux.
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# None.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 if the operating system is linux
# 1 if the operating system is not linux
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user@OSX$ os_arch_capable
#     x86_64
#
# ### Code Walkthrough
os_arch_capable()
{
  # If the operating system is 'darwin' (OSX)
  # Otherwise return the default for the OS, defer to os_arch_type()
  if os_is_darwin
  then
    # Now determine whether the CPU has 64 bit capabilities, on OSX we use
    # sysctl key hw.cpu64bit_capable key to find this out.  If the value is 1
    # then the CPU is 64 bit capable, emit x86_64 to the calling environments
    # STDOUT otherwise output either i386 or ppc, by derring to os_arch_type()
    local _string
    read -r _string < <(sysctl hw.cpu64bit_capable)
    if (( ${_string//* } == 1 ))
    then
      printf "%s" "x86_64"
    else
      os_arch_type
    fi
  else
    os_arch_type
  fi
}

#
# ## os\_cpu\_count()
#
# Print the number of cpu cores on the current system to STDOUT
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# An integer representing the number of CPU cores or 1 if unknown.
#
# ### Environmental effects
#
# None.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user$ os_cpu_count
#     24
#
# ### Code Walkthrough
os_cpu_count()
{
  # Default the CPU count to 1
  local _cpu_count=1

  # If the operating system is Darwin (OSX)
  # Then read the CPU count using the sysctl key hw.ncpu, and trim off
  # everything except the last space separated field.
  if os_is_darwin
  then
    read -r _cpu_count < <(sysctl hw.ncpu)
    _cpu_count="${_cpu_count//* }"
  # If the operating system is Linux then gather the information directly from
  # the proc filesystem
  elif os_is_linux
  then
    _cpu_count=$(awk '/^processor/{count++} END{print count}' /proc/cpuinfo 2>&1)

  # Warn the user that this function has not yet been implemented for solaris.
  elif os_is_solaris
  then
    warn "CPU counting for solaris has not yet been implemented, returning 1."

  # Warn the user that this function has not yet been implemented for bsd.
  elif os_is_bsd
  then
    warn "CPU counting for bsd has not yet been implemented, returning 1."

  # Warn the user that this function has not yet been implemented for aix.
  elif os_is_aix
  then
    warn "CPU counting for aix has not yet been implemented, returning 1."
  fi

  printf '%d' ${_cpu_count}
}

#
# ## os\_version()
#
# Prints the OS version to STDOUT and sets the 'os_version' variable
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# The OS (Kernel) Version is printed to STDOUT.
#
# ### Environmental effects
#
# After running this function the os version is stored in the 'os_version'
# variable.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user@archlinux$ os_version
#     2.6.38
#
#     user@osx$ os_version
#     10.7.0
#
# ### Code Walkthrough
os_version()
{
  # Read the revisin string using the uname utility and strip off the rightmost
  # piece, which typically contains additional info. Then print the remaining
  # string to the STDOUT of the calling environment.
  read -r os_version < <(uname -r)
  os_version=${os_version//-*}
  printf '%s' "${os_version}"
}

#
# ## os\_version\_major()
#
# Prints the OS major version to STDOUT and sets the 'os_version_major' variable
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# The OS Major Version is printed to STDOUT.
#
# ### Environmental effects
#
# After running this function the os major version is stored in the
# 'os_version_major' variable.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user@archlinux$ os_version
#     2.6.38
#
#     user@osx$ os_version_major
#     2
#
# ### Code Walkthrough
os_version_major()
{
  # Grab the full version string from os_version(). Then strip off everything to
  # the right of the first set of digits and print it to STDOUT.
  os_version_major=$(os_version)
  os_version_major="${os_version_major//\.*}"
  printf "${os_version_major}"
}

#
# ## os\_version\_minor()
#
# Prints the OS minor version to STDOUT and sets the 'os_version_minor' variable
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# The OS minor Version is printed to STDOUT.
#
# ### Environmental effects
#
# After running this function the os minor version is stored in the
# 'os_version_minor' variable.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user@archlinux$ os_version
#     2.6.38
#
#     user@osx$ os_version_minor
#     6
#
# ### Code Walkthrough
os_version_minor()
{
  # Grab the full version string from os_version(). Strip off everything to the
  # left of the middle set of digits, then strip off everything to the right of
  # the middle set of digits. Print the remaining string to STDOUT.
  os_version_minor=$(os_version)
  os_version_minor="${os_version_minor#*\.}"
  os_version_minor="${os_version_minor%\.*}"
  printf "${os_version_minor}"
}

#
# ## os\_version\_patch()
#
# Prints the OS patch version to STDOUT and sets the 'os_version_patch' variable
#
# ### Input Parameters
#
# None.
#
# ### Stream Outputs
#
# The OS patch Version is printed to STDOUT.
#
# ### Environmental effects
#
# After running this function the os patch version is stored in the
# 'os_version_patch' variable.
#
# ### Return Codes
#
# 0 for success
#
# ### Failure Scenarios
#
# No failure scenarios currently.
#
# ### Usage Examples
#
#     user@archlinux$ os_version
#     2.6.38
#
#     user@osx$ os_version_patch
#     38
#
# ### Code Walkthrough
os_version_patch()
{
  # Grab the full version string from os_version(). Strip off everything to the
  # left of the last set of digits. Print the version string to STDOUT.
  os_version_patch=$(os_version)
  os_version_patch="${os_version_patch//*\.}"
  printf "${os_version_patch}"
}

#
# ## os\_open()
#
# Opens given URI via whatever OS mechanism can be found.
#
# ### Input Parameters
#
# The first parameter is a URI.
#
# ### Code Walkthrough
os_open()
{
  # Store the URI in a local variable, set to the empty string if it was not
  # passed in.
  local _uri="${1:-}"

  # If the URI variable is not empty
  if variable_is_nonempty _uri
  then
    # and if the 'open' command exists on the system
    if command_exists open
    then
      # then call open passing in the URI
      open "${_uri}"

    # otherwise if the 'xdg-open' command exists on the system
    elif command_exists xdg-open
    then
      # then call xdg-open passing in the URI
      xdg-open "${_uri}"

    # otherwise
    else
      # If the shell is interactive
      # and if the uri is a file uri then open it up in the shell set editor
      # or less if the editor variable has not been set.
      # Otherwise the shell is non-interactive so for now print the uri.
      modules bdsm/interactive
      if shell_is_interactive
      then
        if [[ "${_uri}" == file* ]]
        then
          ${EDITOR:-less} "${_uri}"
        else
          curl -s "${_uri}"
        fi
      else
        echo "$_uri"
      fi
    fi
  else
    # otherwise the uri was not passed in, this is a programming error
    # so send a fail message which will send a backtrace allowing the developer
    # to quickly debug where they are missing the URI.
    fail "Cannot open URI as no uri was given."
  fi
}

