#!/bin/bash

#---------------------------------------------------------------------
## @param string to explode
## @param delimiter
## @param name of array to put it in
##
## @Description
## Turns a string into an array. Each element of the array
## is separated in the string by the delimiter.
##
## Note: The array you want the fields put into must be
## declared before you call this function.
## <pre>
## EXAMPLE
##
## my_array=()
## explode "a_string_to_explode" "_" "my_array"
## echo my_array[*]
##
## Produces "a" "string" "to" "explode".
## </pre>
#---------------------------------------------------------------------
function explode ()
{ # $1==string to explode, $2==delimiter, $3==name of array to put it in

  [[ "$3" ]] || return 1
  local ASSIGN=`echo "$1" | awk -F "$2" "{ for(i=1; i<=NF; i++) print \"${3}[\" i-1 \"]='\" \\\$i \"';\"; }"`
  debug "libmisc" "explode() - $ASSIGN"
  eval "$ASSIGN"
}


#---------------------------------------------------------------------
## @Type API
## @param sed command
## @param file
##
## First argument is a sed command.  Second argument is a file.  
## sedit performs the sed command on the file, modifiying the 
## original file.  For example, 
## <br>sedit "s/foo/bar/g" /tmp/somefile <br>
## will replace all occurances of foo with bar in /tmp/somefile.  
## This function is often used in spells to make changes to source
## files before compiling.  See the sed man page for more information.
##
#---------------------------------------------------------------------
function real_sedit()  {
  (
    # Execute in a subshell to avoid globally affecting the umask
    umask 077
    TMP_FILE="/tmp/`basename  $2`.$$.$RANDOM"  &&
    rm    -rf    $TMP_FILE                     &&
    cp    $2     $TMP_FILE                     &&
    sed   "$1"   $TMP_FILE  >  $2              &&
    rm    -rf    $TMP_FILE

  )
}


#---------------------------------------------------------------------
## @Type API
## @param question 
## @param default answer
##
## @return 0 on yes
## @return 1 on no
##
## Asks the user a yes/no question.  First argument is the question to
## ask, second argument is the default answer.  If a timeout occurs
## before the question is answered, the given default answer is 
## applied.  Returns true or false based on the answer given.
##
#---------------------------------------------------------------------
function real_query()  { (

  debug  "libmisc" "Running query() with the following arguments: '$1' and '$2'"
  
  while true ; do
  
    RESPONSE=""

    if  [  -z  "$SILENT"  ];  then

      echo  -e  -n  "${QUERY_COLOR}$1 [$2] ${DEFAULT_COLOR}"
 
      read   -t  $PROMPT_DELAY  -n  1  RESPONSE
      echo
    fi

    RESPONSE=${RESPONSE:=$2}
    case  $RESPONSE  in
      n|N)  return 1  ;;
      y|Y)  return 0  ;;
    esac
  
  done

) }


#---------------------------------------------------------------------
## @Type API
## @param message to echo
## @Stdout message
## echo's the given arguments if SILENT is not set.
##
#---------------------------------------------------------------------
function real_message()    {

  if  [  !  -n  "$SILENT"  ];  then
    if  [  "$1"  ==  "-n"  ];  then
      shift  1
      echo  -n  -e  "$*"
    else
      echo  -e  "$*"
    fi
  fi

}

#---------------------------------------------------------------------
## @param type
## @param message
##
## Enters a debug message if the type of debug message != 'no'
##
## The 'type' is usually the name of the file the debug statement
## comes from. i.e. DEBUG_liblock, DEBUG_libsorcery, DEBUG_cast etc.
##
#---------------------------------------------------------------------
function debug()  {

  [[ $DEBUG ]] || return 0
  
  local debugVar="DEBUG_${1}"
  if [[ ${!debugVar} != "no" ]] ; then
    echo -n "$1($$): " >>$DEBUG
    shift
    echo $* >>$DEBUG
  fi

  true
}

#---------------------------------------------------------------------
## @Stdout progress spinner
## Displays progress spinner like the one in fsck.
##
#---------------------------------------------------------------------
function progress_spinner()  {

  ((  PROGRESS_SPINNER=$PROGRESS_SPINNER+1  ))
  if ((  PROGRESS_SPINNER>((  ${#PROGRESS_SPINNER_CHARS}-1  ))  )); then
    PROGRESS_SPINNER=0;
  fi
  echo -en "\b${PROGRESS_SPINNER_CHARS:$PROGRESS_SPINNER:1}"
}


#---------------------------------------------------------------------
## @param opt "dot"
## @param count
## @param total
## @param opt length
## <pre>
## Displays progress bar in the form of [---->   ] 100%
## Or just a dot in the dot format
##</pre>
#---------------------------------------------------------------------
function progress_bar()  {
debug "libmisc" "progress_bar - $*"
  local percent i len num_dash

  if [[ $1 == -dot ]] ; then
    len=0
    shift 1
  else
    len="$3"
    len="${len:-$COLUMNS}"
  fi
  
  # Can't make a bar because there's no total, or the length isn't
  #  long enough, or if there is no length 
  if [ $# -lt 2 ] || [ $len -lt 8 ] ; then
    message -n "."
    return 0
  fi
  
  if [ $1 -lt 1 ] || [ $2 -lt 1 ] ; then return 1 ; fi
  
  percent=$((100*$1/$2))
  percent=`printf "%.0f" $percent`
  
  if [[ $LAST_PERCENT == $percent ]] ; then
    return 0
  fi

  LAST_PERCENT=$percent


  dash_len=$(($len-8))
  num_dash=$(( $dash_len*$1/$2 ))

  #Format: [--->  ] 100%

  #opening of the bar
  BAR_STRING="["

  #The '=' signs
  for (( i=0 ; i<$num_dash ; i++ )) ; do
    BAR_STRING="${BAR_STRING}="
  done

  #Now a pretty indicator
  [ $num_dash -lt $((dash_len-1)) ] && BAR_STRING="${BAR_STRING}>"

  #Fill the rest of the bar up with blanks
  for (( i++ ; i<$dash_len-1 ; i++ )) ; do
    BAR_STRING="${BAR_STRING} "
  done

  #Put on the closing for the bar and the percent
  BAR_STRING="${BAR_STRING}] ${percent}%"

  #Clear the rest of the space
  for (( i+=3+${#percent} ; i<$len ; i++ )) ; do
    BAR_STRING="${BAR_STRING} "
  done

  clear_line
  message -n "$BAR_STRING"
  progress_spinner

 return 0


}
#---------------------------------------------------------------------
## @Stdout Clear line
## Clears the current line and puts the cursor at the begining of it.
##
#---------------------------------------------------------------------
function clear_line()  {

  echo -en '\r\033[2K'
  return 0

}


#---------------------------------------------------------------------
##
## @Globals SOUND
## Plays a given soundfile if sounds are on and the file exists in 
## the chosen theme.
##
#---------------------------------------------------------------------
function sound()  {

  case  $SOUND  in
    on)  SOUND_FILE=$SOUND_DIRECTORY/$SOUND_THEME/$1
         if  [  -e  $SOUND_FILE  ];  then
           (  cd  /  ;  play  $SOUND_FILE  2>/dev/null  & )
	   debug "libmisc" "Playing $SOUND_FILE"
	 else
	   debug "libmisc" "Error playing $SOUND_FILE: no such file"
         fi
    ;;
  esac

}


#---------------------------------------------------------------------
## @param filename
##
## Runs the editor given by EDITOR on the file given in the first
## argument.  If EDITOR is not set, "nano -w" is used.
##
#---------------------------------------------------------------------
function edit_file()  {

  ${EDITOR:-nano -w} $1

}


#---------------------------------------------------------------------
## @param string
## @Stdout escaped string
##
## Adds escape sequences to strings for special characters.
## Does NOT escape the string ".*".
## Used for putting escape sequences in regexp strings that should
## be treated literaly.
##
#---------------------------------------------------------------------
function esc_str()
{

  local OUT
  if [[ $* == .* ]] ; then
    OUT="$1"
  else
    OUT=`echo "$@" | sed -e 's:\.:\\\.:' \
                         -e 's:\*:\\\*:'`
  fi
  echo "$OUT"

}

#---------------------------------------------------------------------
## @param Yes, there is a parameter.. 
## @Stdout wheee, even output.
## Dunno what that one does..
#---------------------------------------------------------------------
function parent_PIDs()
{
  
  local pid ppid
  
  pid="$1"
  if [[ $1 ]] ; then
    pid=$1
  else
    pid="$$"
    echo $$
  fi
  
  if [[ $pid == 0 ]] || ! [ -e /proc/$pid/status ] ; then return 0; fi
  
  ppid=`grep "^PPid:" /proc/$pid/status | cut -f 2`
  echo $pid
  parent_PIDs $ppid

}


#---------------------------------------------------------------------
## @Stdin directories.
## @Stdout directories' basenames
## Takes newline separated list of directories
## and outputs their base names.
##
#---------------------------------------------------------------------
function get_basenames() {
debug "DB" "basename - $*"
  local DIRECTORY=''
  while read DIRECTORY; do
    basename "$DIRECTORY"
  done
}


#---------------------------------------------------------------------
## @Stdin directories.
## @Stdout directories' basenames
## Takes newline separated list of pathnames
## and outputs their directory names.
##
#---------------------------------------------------------------------
function get_dirnames() {
  local PATH_NAME=''
  while read PATH_NAME; do
    dirname "$PATH_NAME"
  done
}

#---------------------------------------------------------------------
##  @param The function to call on each itteration and it's arguments
##  @param The separator string. If there is no $3, only the first char counts
##  @param The string to itterate over. (optional)
##  @Stdin is used when $3 isn't given.
##
##  $3 is optional, when it isn't used, 
##  stdin is used and only the first letter in $2 is used. Note, using stdin
##  can have odd side effects when your function uses read and stdin itself.
#3
## Special vars: 
##  BREAK: Use this to break out of the loop prematurely, also causes a return of 1.
##
## If the function returns the value of the last return
## Notes:
##  In stdin mode, if the string does not terminate with the delimiter, the last 
##  token will be ignored.
#----------------------------------------------------------------------
function iterate()
{ # $1=callback+args, $2=separator, $3=(opt)string
#  debug "libmisc" "iterate - $@"
  
  [ $# -lt 3 ] && return 2
  
  local oldIFS="$IFS"
  local token
  local func="$1"
  local returnValue=0
  if [[ $# -gt 2 ]] ; then
    IFS="$2"
    shift 2
    for token in $* ; do
      IFS="$oldIFS"
      
      eval "$func \"$token\""
      [[ $BREAK ]] && break
      
    done
    IFS="$oldIFS"
  else
    while read -r -d "$2" token ; do
      eval "$func \"$token\""
      [[ $BREAK ]] && break
    done
    echo "leftover token: $token" >> $DEBUG
  fi
  
  returnValue=$?
  [[ $BREAK ]] && debug "libmisc" "iterate - I was BREAKed."
  unset BREAK
  return $returnValue
}

#---------------------------------------------------------------------
## not yet implemented
#---------------------------------------------------------------------
function cache_env() 
{

	debug "libmisc" "Storing environment for $1 in $2"
	echo "function ${1}_env()" >> $2
	echo "{" >> $2

	local exp_vars=`export | sed 's/declare -x //' | cut -d= -f1`
	local set_vars=`set | \
		awk 'BEGIN {funcs=0;} /^[^=]*[[:blank:]]/{funcs=1;} {if(funcs==0) print $0;}' | cut -d= -f1`
	local special_vars=`echo -e "BASH\nBASH_VERSINFO\nBASH_VERSION\nCOLUMNS\nDIRSTACK\nEUID
FUNCNAME\nGROUPS\nHISTFILE\nHISTFILESIZE\nHISTSIZE\nHOSTNAME\nHOSTTYPE\nIFS\nLINES\nMACHTYPE
MAILCHECK\nOPTERR\nOPTIND\nOSTYPE\nPATH1\nPATH2\nPATH3\nPATH4\nPIPESTATUS\nPPID\nPS2
PS4\nSHELLOPTS\nUID\n_"`
	local vars_to_cache=`{ echo "$exp_vars" ; echo "$special_vars" ; echo "$set_vars" ; echo "$set_vars" ; echo "$set_vars"; } | \
		sort | uniq -c | awk '$1=3{print $2;}' | egrep -v '^([[:punct:]]*|.*COLOR.*)$'`
	
	for i in $vars_to_cache ; do
		echo ${i}"='${!i}'" >> $2
	done
	
	echo "}" >> $2
	less $2
	echo "Check it."
	read
	
}
#---------------------------------------------------------------------
## not yet implemented
#---------------------------------------------------------------------
function restore_env()
{
	debug "libmisc" "Restoring env for $1 from $2"
	. $2
	${1}_env 2>/dev/null
	
}

#---------------------------------------------------------------------
## @param return_var 
## @param elements, ..
## 
## gives the user some nice select list and puts the selected
## item in return_var
##
#---------------------------------------------------------------------
function select_list()
{
    local i
    local number foo temp
    local returnvar=$1
    shift
    let i=0
    for foo in "$@"; do
        message "\t$DEFAULT_COLOR($i)  $SPELL_COLOR$foo$DEFAULT_COLOR"
        temp[$i]=$foo
        let i++
    done

    message -n "\n${QUERY_COLOR}Which one do you want? [0]$DEFAULT_COLOR "
    read   -t  $PROMPT_DELAY  -n  1  number
    while ! [[ ${temp[$number]} ]]; do
        message -n "\n${QUERY_COLOR}Which one do you want? [0]$DEFAULT_COLOR "
        read number
    done

    echo

    eval ''$returnvar'='${temp[$number]}''
}

#---------------------------------------------------------------------
## @param title
## 
## sets the terminal title if TERM=xterm|rxvt or the window title if
## TERM=screen
##
#---------------------------------------------------------------------
function set_term_title()
{
    if [ "$SET_TERM_TITLE" != "on" ]; then return; fi
    case $TERM in
        xterm*|rxvt)  echo -ne "\e]0;$@\007"
                      ;;
             screen)  echo -ne "\ek$@\e\\"
                      ;;
                  *) ;;
    esac
}

#---------------------------------------------------------------------
## @License
##
## This software is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This software is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this software; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
##
#---------------------------------------------------------------------
