# Library functions for bash scripts at Stack Exchange.

# NOTE: This file is open sourced. Do not put Stack-proprietary code here.

# Usage:
#
#   set -e
#   . _stack_lib.sh

# ----- Utility Functions -----

function debugmsg() {
  # Log to stderr.
  echo 1>&2 LOG: "$@"
  :
}

function logit() {
  # Log to stderr.
  echo 1>&2 LOG: "$@"
}

function fail_out() {
    echo "FAILED:" "$*"
    echo 'Exiting...'
    exit 1
}

# on_exit and add_on_exit from http://www.linuxjournal.com/content/use-bash-trap-statement-cleanup-temporary-files
# Usage:
#   add_on_exit rm -f /tmp/foo
#   add_on_exit echo "I am exiting"
#   tempfile=$(mktemp)
#   add_on_exit rm -f "$tempfile"
function on_exit()
{
    for i in "${on_exit_items[@]}"
    do
        eval $i
    done
}

function add_on_exit()
{
    local n=${#on_exit_items[*]}
    on_exit_items[$n]="$*"
    if [[ $n -eq 0 ]]; then
        trap on_exit EXIT
    fi
}

function create_self_deleting_tempfile() {
  local filename

  case $(uname -s) in
    Darwin | FreeBSD )
      : "${TMPDIR:=/tmp}" ;
      filename=$(mktemp -t _stacklib_.XXXXXXXX )
      ;;
    Linux | CYGWIN* | MINGW* | NetBSD | SunOS )
      filename=$(mktemp)
      ;;
    * )
      echo 'ERROR: Unknown OS. Exiting. (create_self_deleting_tempfile)'
      exit 1
      ;;
  esac

  add_on_exit rm -f "$filename"
  echo "$filename"
}

function create_self_deleting_tempdir() {
  local filename

  case $(uname -s) in
    Darwin | FreeBSD )
      : "${TMPDIR:=/tmp}" ;
      filename=$(mktemp -d -t _stacklib_.XXXXXXXX )
      ;;
    Linux | CYGWIN* | MINGW* | NetBSD | SunOS )
      filename=$(mktemp -d)
      ;;
    * )
      echo 'ERROR: Unknown OS. Exiting. (create_self_deleting_tempdir)'
      exit 1
      ;;
  esac

  add_on_exit rm -rf "$filename"
  echo "$filename"
}

# Securely and portably create a temporary file that will be deleted
# on EXIT.  $1 is the variable name to store the result.
function make_self_deleting_tempfile() {
  local __resultvar="$1"
  local name

  case $(uname -s) in
    Darwin | FreeBSD )
      : "${TMPDIR:=/tmp}" ;
      name=$(mktemp -t _stacklib_.XXXXXXXX )
      ;;
    Linux | CYGWIN* | MINGW* | NetBSD | SunOS )
      name=$(mktemp)
      ;;
    * )
      echo 'ERROR: Unknown OS. Exiting. (make_self_deleting_tempfile)'
      exit 1
      ;;
  esac

  add_on_exit rm -f "$name"
  eval $__resultvar="$name"
}

function make_tempdir() {
  local __resultvar="$1"
  local name

  case $(uname -s) in
    Darwin | FreeBSD )
      : "${TMPDIR:=/tmp}" ;
      # The full path to the temp directory must be short.
      # This is used by blackbox's testing suite to make a fake GNUPGHOME,
      # which needs to fit within sockaddr_un.sun_path (see unix(7)).
      name=$(mktemp -d -t SO )
      ;;
    Linux | CYGWIN* | MINGW* | NetBSD | SunOS )
      name=$(mktemp -d)
      ;;
    * )
      echo 'ERROR: Unknown OS. Exiting. (make_tempdir)'
      exit 1
      ;;
  esac

  eval $__resultvar="$name"
}

function make_self_deleting_tempdir() {
  local __resultvar="$1"
  local dname

  make_tempdir dname

  add_on_exit rm -rf "$dname"
  eval $__resultvar="$dname"
}

function fail_if_not_running_as_root() {
  if [[ $EUID -ne 0 ]]; then
    echo 'ERROR: This command should only be run as root.'
    echo 'Exiting...'
    exit 1
  fi
}

function fail_if_in_root_directory() {
  # Verify nobody has tricked us into being in "/".
  case $(uname -s) in
    Darwin | FreeBSD | NetBSD )
      if [[ $(stat -f'%i' / ) == $(stat -f'%i' . ) ]] ; then
        echo 'SECURITY ALERT: The current directory is the root directory.'
        echo 'Exiting...'
        exit 1
      fi
      ;;
    Linux | CYGWIN* | MINGW* | SunOS )
      if [[ $(stat -c'%i' / ) == $(stat -c'%i' . ) ]] ; then
        echo 'SECURITY ALERT: The current directory is the root directory.'
        echo 'Exiting...'
        exit 1
      fi
      ;;
    * )
      echo 'ERROR: Unknown OS. Exiting. (fail_if_in_root_directory)'
      exit 1
      ;;
  esac
}

function semverParseInto() {
    local RE='[^0-9]*\([0-9]*\)[.]\([0-9]*\)[.]\([0-9]*\)\([0-9A-Za-z-]*\)'
    #MAJOR
    eval $2=`echo $1 | sed -e "s#$RE#\1#"`
    #MINOR
    eval $3=`echo $1 | sed -e "s#$RE#\2#"`
    #MINOR
    eval $4=`echo $1 | sed -e "s#$RE#\3#"`
    #SPECIAL
    eval $5=`echo $1 | sed -e "s#$RE#\4#"`
}

function semverEQ() {
    local MAJOR_A=0
    local MINOR_A=0
    local PATCH_A=0
    local SPECIAL_A=0

    local MAJOR_B=0
    local MINOR_B=0
    local PATCH_B=0
    local SPECIAL_B=0

    semverParseInto $1 MAJOR_A MINOR_A PATCH_A SPECIAL_A
    semverParseInto $2 MAJOR_B MINOR_B PATCH_B SPECIAL_B

    if [ $MAJOR_A -ne $MAJOR_B ]; then
        return 1
    fi

    if [ $MINOR_A -ne $MINOR_B ]; then
        return 1
    fi

    if [ $PATCH_A -ne $PATCH_B ]; then
        return 1
    fi

    if [[ "_$SPECIAL_A" != "_$SPECIAL_B" ]]; then
        return 1
    fi


    return 0

}

function semverLT() {
    local MAJOR_A=0
    local MINOR_A=0
    local PATCH_A=0
    local SPECIAL_A=0

    local MAJOR_B=0
    local MINOR_B=0
    local PATCH_B=0
    local SPECIAL_B=0

    semverParseInto $1 MAJOR_A MINOR_A PATCH_A SPECIAL_A
    semverParseInto $2 MAJOR_B MINOR_B PATCH_B SPECIAL_B

    if [ $MAJOR_A -lt $MAJOR_B ]; then
        return 0
    fi

    if [[ $MAJOR_A -le $MAJOR_B  && $MINOR_A -lt $MINOR_B ]]; then
        return 0
    fi

    if [[ $MAJOR_A -le $MAJOR_B  && $MINOR_A -le $MINOR_B && $PATCH_A -lt $PATCH_B ]]; then
        return 0
    fi

    if [[ "_$SPECIAL_A"  == "_" ]] && [[ "_$SPECIAL_B"  == "_" ]] ; then
        return 1
    fi
    if [[ "_$SPECIAL_A"  == "_" ]] && [[ "_$SPECIAL_B"  != "_" ]] ; then
        return 1
    fi
    if [[ "_$SPECIAL_A"  != "_" ]] && [[ "_$SPECIAL_B"  == "_" ]] ; then
        return 0
    fi

    if [[ "_$SPECIAL_A" < "_$SPECIAL_B" ]]; then
        return 0
    fi

    return 1

}

function semverGT() {
    semverEQ $1 $2
    local EQ=$?

    semverLT $1 $2
    local LT=$?

    if [ $EQ -ne 0 ] && [ $LT -ne 0 ]; then
        return 0
    else
        return 1
    fi
}

