#!/bin/sh
# $Id$
# 
# utils.sh -- utility routines for the TeX Live installation scripts.
# Do not call directly.
# 
# Copyright Sebastian Rahtz and Karl Berry 2003, 2004, 2005, 2006.
#
#    This program 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 program 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 program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
#    02110-1301, USA.
#     
#    Send bug reports or suggestions to tex-live@tug.org.

################################################################
# Basic IO:
################################################################

################################################################
# read: called with the name of a variable and (optionally) a
#       different message text. Keeps the old value of the
#       variable, if the empty string in entered.
################################################################
gets()
{
    gets_var=$1; gets_text=${2-$gets_var}
    eval old=\$$gets_var
    eval $echon \""New value for $gets_text [$old]: "\"
    read $gets_var
    eval test -z \"\$$gets_var\" && eval $gets_var=\'$old\'
}

################################################################
# getopt: get a menu choice.
#   $1: string with valid characters
#   $2: message for prompting
################################################################
getopt()
{
    chars=$1; msg=$2
    while true; do
        #$echon "$msg ([$chars]): "
        $echon "$msg: " >&2
        read ans
        ans=`echo $ans | tr '[a-z]' '[A-Z]'`
        case "$ans" in
          [$chars])
            echo "$ans"
            return
        esac
    done
}

getoptallcase()
{
    chars=$1; msg=$2
    while true; do
        #$echon "$msg ([$chars]): "
        $echon "$msg: " >&2
        read ans
        case "$ans" in
          [$chars])
            echo "$ans"
            return
        esac
    done
}

################################################################
# toggle: given the name of a variable, toogle switches between
#         the values ' ' and X.
################################################################
toggle()
{
    if eval test \"\$$1\" = X; then
        eval $1=\' \'
    else
        eval $1=X
    fi
}

################################################################
# This tricky function displays the value of a variable that can
# contain newline characters. Variables will be expanded, too.
# Arguments:
#       1: the name of the variable
################################################################
textvar_show()
{
    OLDIFS=$IFS; IFS='';
    eval echo \"`eval echo \\$${1}`\"
    IFS=$OLDIFS
}

readln()
{
    $echon 'Press return to continue... '
    read foo
}

helpme()
{
    var=$1
    cls
    textvar_show $var 2>&1 | $PAGER
    echo
    readln
}

################################################################
# Use the yesno functions to ask the user a simple yes/no
# question.
# Arguments:
#   $1: test to display for the question (" (Y/N)? " will
#       automatically be appended).
################################################################
yesno()
{
    while true; do
    $echon "$1 (Y/N)? "
    read ans
    case $ans in
        y|Y)    return 0;;
        n|N)    return 1;;
        esac
    done
}

################################################################
# A set of functions the might do an echo without linefeed in
# the end. Function find_echo sets the variable "echon" to a
# suitable function.
################################################################
echo_a() { echo      -n "$@";   }
echo_b() { echo         "$@\c"; }
echo_c() { echo      -e "$@\c"; }
echo_d() { /bin/echo -n "$@";   }
echo_e() { /bin/echo    "$@\c"; }
echo_f() { /bin/echo -e "$@\c"; }

################################################################
# Test which of the above functions does the trick. We set
# the variable "echon" to the first function that works
# correctly.
################################################################
find_echo()
{
    for i in a b c d e f; do
        test "`echo_$i c``echo_$i a`"  = ca && echon=echo_$i && return
    done
    echon=echo
}


################################################################
# a replacement for xargs (for systems that dont have it)
################################################################
mxargs()
{
  (
    COUNT=25
    COUNTP=26
    TEMP=${TMP-/tmp}
    TMPA=$TEMP/.tmp_xargs_a.$$
    TMPB=$TEMP/.tmp_xargs_b.$$
    trap "rm -f $TMPA $TMPB; trap '' 0; exit 0" 0 1 2 15
    cat > $TMPA
    while test -s $TMPA; do
      args=`sed -n "1,${COUNT}p" <$TMPA`
      "$@" $args
      mv $TMPA $TMPB
      sed -n "${COUNTP},\$p" <$TMPB >$TMPA
    done
  )
}

################################################################
# search for a binary in PATH, find all occurences
################################################################
find_in_path()
{
  fip_testbin=$1
  fip_result=""
  case "$fip_testbin" in
    /*) test -x "$fip_testbin" && test -f "$fip_testbin" &&
          fip_result="$fip_result $fip_testbin";;
    *)
       OLDIFS=$IFS; IFS=:; eval set $PATH; IFS=$OLDIFS
       for fip_this_dir
       do
           test -x "$fip_this_dir/$fip_testbin" &&
             test -f "$fip_this_dir/$fip_testbin" &&
             fip_result="$fip_result $fip_this_dir/$fip_testbin"
       done
  esac
  echo $fip_result
}

################################################################
# search for a tar that either understands -I or -T
################################################################
test_tar()
{
  testtar_tmp=${TMP-/tmp}/.testtar.$$
  TARS="`find_in_path gtar` `find_in_path tar`"
  test -z "$TARS" && return
  (
    exec 3>&1 1>/dev/null 2>&1
    trap "cd /; rm -rf $testtar_tmp; trap '' 0; exit 0" 0 1 2 15
    mkdir $testtar_tmp; cd $testtar_tmp

    mkdir a b
    tar cf test.tar a b
    rmdir a b
    echo a > list
    for TAR in $TARS; do
      $TAR -x -f test.tar -T list >/dev/null 2>&1
      if test -d a && test ! -d b; then
        echo "$TAR -T" >&3
        break
      fi
      rmdir a b >/dev/null 2>&1
      $TAR -x -f test.tar -I list >/dev/null 2>&1
      if test -d a && test ! -d b; then
        echo "$TAR -I" >&3
        break
      fi
    done
  )
}

################################################################
# if we find a good tar: use it. Else use xargs (or even mxargs)
################################################################
find_tar()
{
  XARGS=`find_in_path xargs | sed 's/ .*//'`
  test -x "$XARGS" || XARGS=mxargs
  set x `test_tar`; shift
  if test -n "$1"; then
    GOODTAR=true
    TARPROG=$1; TAROPT=$2
  else
    GOODTAR=false
    TARPROG=tar; TAROPT=""
  fi
}

################################################################
# utility functions:
################################################################

bad_sh()
{
    /bin/sh -c 'exit 1'
    retval=$?
    if test "$retval" != 1; then
        echo
        echo 'Your /bin/sh is completely broken. A simple program like'
        echo
        echo "                /bin/sh -c 'exit 1'; echo \$?"
        echo
        echo "gives a wrong result."
        echo
        echo 'Your shell is likely to break some scripts of TeX Live. Please'
        echo 'update your /bin/sh first and try to install TeX Live later.'
        echo
        exit 1
   fi
}

warning()
{
    echo "$@"
}

cls()
{
    test "$debug" = true || clear
}

check_for_binary()
{
    testbin=$1
    case "$testbin" in
      /*) test -x "$testbin" && test -f "$testbin"; return;;
      *)
         OLDIFS=$IFS; IFS=:; eval set $PATH; IFS=$OLDIFS
         for this_dir
         do
             test -x "$this_dir/$testbin" &&
             test -f "$this_dir/$testbin" && return 0
         done
         return 1;;
    esac
}

require_binaries()
{
    for this_bin
    do
        check_for_binary $this_bin ||
          fatal "program '$this_bin' not found in PATH"
    done
}


unset_vars()
{
    for var in $envvars; do
        unset $var
    done
}

unset_vars()
{
    for var in $envvars; do
        unset $var
    done
}

mkdirhier()
{
  case $1 in
    /*) cd /;;
  esac
  OLDIFS=$IFS; IFS=/; eval set $1; IFS=$OLDIFS
  for i
  do
    test -d $i || mkdir $i || break
    cd $i || break
  done
}

# fill a string with blanks:
setlength()
{
    var=$1
    length=$2
    eval value=\"\$$var\"
    l=`expr "$value" : '.*'`
    test $l -lt $length || return
    d=`expr $length - $l`
    OLDIFS=$IFS; IFS=''
    substr=`awk 'END {print substr("                                         ", 1, ANZ)}' ANZ=$d </dev/null`
    eval $var='$substr$value'
    IFS=$OLDIFS
}

# [t]ry again, [r]econfigure, [a] abort
tra()
{
    msg=$1
    cls
    echo "Oops, I am in trouble here! The error was:"
    echo ">> $msg <<"
    echo
    case `getopt TRQ 'What shall we do now: <t>ry again, <r>econfigure, <q>uit'` in
        T) return;;
        R) menu_main;;
        Q) exit 1;;
    esac
}

warn()
{
    msg=$1
    cls
    echo "Oops, I am in trouble here! The problem was:" >&2
    echo ">> $msg <<" >&2
    echo >&2
    case `getopt CQ 'What shall we do now: <c>continue, <q>uit'` in
        C) return;;
        Q) exit 1;;
    esac
}

fatal()
{
    echo; echo
    echo "ERROR: $@."
    echo "This was a fatal error. Installation aborted."
    $debug && read foo
    exit 1
}

show_error()
{
    test $? = 0 && return
    cls
    echo 'WARNING: the last command returned an error.'
    if yesno 'Do you want to see the errorlog'; then
        cat $ERRLOG | $PAGER
        readln
    fi
    cls
}

locate_binaries()
{
    require_binaries \
      pwd touch sed awk gzip ln rm ls tar tr mkdir cat \
      `echo $PAGER | sed 's@ .*@@'` 
}

# convert standard names to our binary names.
platform_guess()
{
  sys=`(cd ${TMP-/tmp}; sh ${OVERRIDE_GUESS-$CDDIR}/config.guess)`
  system=`echo $sys | sed 's/-[^-]*-/-/'`
  case $system in
  *86-cygwin*)     Sys=i386_cgywin;;
  *86-darwin*)     Sys=i386_darwin;;
  *86-freebsd*)    Sys=i386_freebsd;;
  *86-openbsd*)    Sys=i386_openbsd;;
  *86-linux*)      Sys=i386_linux;;
  *86-solaris*)    Sys=i386_solaris;;
  *86_64*-linux*)  Sys=x86_64_linux;;
  alpha*-linux*)   Sys=alpha_linux;;
  hppa*-hpux*)     Sys=hppa_hpux;;
  mips-irix*)      Sys=mips_irix;;
  powerpc-aix*)    Sys=powerpc_aix;;
  powerpc-*darwin*) Sys=powerpc_darwin;;
  powerpc-*linux*) Sys=powerpc_linux;;
  sparc-solaris*)  Sys=sparc_solaris;;
  sparc*-linux*)   Sys=sparc_linux;;
#  *86-openbsd*)   Sys=i386_openbsd;;
#  alphaev*-osf*)  Sys=alphaev5_osf;;

  esac
  this_system=$Sys
}