#!/bin/sh
#+--------------------------------------------------------------------------+
#| Licensed Materials - Property of IBM                                     |
#| (C) Copyright IBM Corp. 2003, 2010 All Rights Reserved                   |
#|                                                                          |
#| US Government Users Restricted Rights - Use, duplication, or disclosure  |
#| restricted by GSA ADP Schedule Contract with IBM Corp.                   |
#+--------------------------------------------------------------------------+

#########################################################################
#########################################################################
# This script can be used to download and patch the following
# IBM Rational UNIX products:
#   Apex_Native  Apex_Embedded  Rose  RoseRT  SoDA TestMate  AXI
#
# Instructions for using this script.
#
#   1. Download this script along with the Apex_Patch_DB1.15 file.
#
#   2. Put the Apex_Patch_DB1.15 file in the /tmp directory.
#      If you do not do this, rppinstall will download Apex_Patch_DB1.15
#      and put it in /tmp for you.
#
#   3. Make the rppinstall script executable:
#      chmod a+w rppinstall
#
#   4. Run the script:
#      ./rppinstall
#
# The rppinstall script will ask for the following information:
#
#   A. The product to be patched.
#
#   B. The product platform to be patched.
#
#   C. The product version to be patched.
#
#   D. The product installation directory (where it is currently installed)
#      If the product installation directory is not accessible from
#      the local machine where rppinstall is running, you can defer
#      the install.  You will be given instructions to copy the patch files
#      to the machine where the install can take place.
#
#   E. If it is okay to commit all current and new patches.
#
#   F. The download directory where the patch files can be stored.
#      The rrpinstall script will check if there is enough disk space.
#
#   G. Confirmation of the download directory.
#
# A this point, the patch files will be downloaded for the selected
# product.  Unless you are deferring the install, they will be
# automatically installed.  The patches will be committed unless you tell
# rppinstall not to commit them.
#
#########################################################################
#########################################################################


# Current version of this script
#WARNING: The value of rev and Revision: must match exactly
rev=1.15
# Revision: 1.15  Date: 04/05/2010 14:48:30


vendor="IBM Corporation"
title="Rational Product Patch Install"

script=$0
cmd=`basename $script`
originalDir=`pwd`
ftpServer=ftp.software.ibm.com
patchDbName=Apex_Patch_DB
ftpScriptLocation=/software/rational/apex_patch_info
patchInfoUrl="ftp://$ftpServer$ftpScriptLocation/index.html"
host=`uname -n`
user=`id | sed -e 's?).*??g' -e 's?.*(??g'`
logName=rppinstall.$host.$user.log
scriptLog=/tmp/$logName
prodHomeLoc=$scriptLog.prod_home
temp=/tmp/$cmd.$$
commitPatches=no
doCommit=false
autoCommitOption='-auto_commit'
deferFile=deferDirectives
deferScript=rppinstallnow
downloadPatchesOnly=false
upgrading=false

ftpUser=anonymous
ftpPass="${user}@"
firstPrompt=true

# This fixes a problem with 'ls -l' format on Linux systems:
unset LANG

oldIFS=$IFS

# Determine which echo command to use.
if echo "look for \c" | grep c >/dev/null
then
    echon="echo -n"
    unset nocr
else
    echon=echo
    nocr='\c'
fi

osName=`uname -s`
case $osName in
HP-UX )
    dfCmd='/usr/bin/bdf'
    ;;
Linux )
    dfCmd='df -k'
    ;;
* )
    dfCmd='/bin/df -k'
    ;;
esac

if [ -d /ned/patches ]
then
    echo "Will try to get patches from NFS if available that way."
    checkLocalNfs=true
else
    checkLocalNfs=false
fi

while [ $# -gt 0 ]
do
    case $1 in
    -download_patches | -dp )
        echo "Will download patches only.  Will not install them."
        downloadPatchesOnly=true
        ;;
    -upgrading )
        # Internal-use-only option when script upgrades itself from FTP.
        upgrading=true
        ;;
    esac
    shift
done

#------------------------------------------------------------------------
rppLoadDefaults() {
#------------------------------------------------------------------------

    # Set the defaults file name for this program.
    if [ -d "$HOME/.Rational" -a -w "$HOME/.Rational" ]
    then
        defaults=$HOME/.Rational/$cmd
    elif [ -n "$HOME" -a -d "$HOME" -a -w "$HOME" ]
    then
        defaults=$HOME/.$cmd
    else
        defaults=/tmp/$cmd.$user
    fi

    if [ -r $defaults ]
    then
        . $defaults
    fi
    if [ -n "$downloadLocation" ]
    then
        if [ ! -d $downloadLocation ]
        then
            downloadLocation=$originalDir
        fi
    else
        downloadLocation=$originalDir
    fi

    # Check for a rinstall or network_install defaults file.
    # Extract the rational_dir setting from each and make a unique list.
    if [ -f $HOME/.Rational/network_install.list ]
    then
        # Use this list instead of the other default files below.
        rationalDirList=`cat $HOME/.Rational/network_install.list`
        return
    fi

    # Search each rinstall and network_install default file.
    for _idf in \
        $HOME/.Rational/rinstall.$user \
        $HOME/.rinstall \
        /tmp/rinstall.$user \
        $HOME/.Rational/network_install \
        $HOME/.network_install \
        /tmp/network_install.$user
    do
        test -f "$_idf" || continue
        eval `grep "rational_dir=" $_idf`
        echo " $rationalDirList " | grep " $rational_dir " >/dev/null ||
             rationalDirList="$rationalDirList $rational_dir"
    done
}

#------------------------------------------------------------------------
rppSaveDefaults() {
#------------------------------------------------------------------------
    echo "
product='$product'
platform='$platform'
version='$version'
commitPatches='$commitPatches'
doCommit='$doCommit'
downloadLocation='$downloadLocation'
" > $defaults
}

#------------------------------------------------------------------------
rppExit() {
#------------------------------------------------------------------------
    exit $?
}

#------------------------------------------------------------------------
rppSetBannerRev() {
#------------------------------------------------------------------------
    _rev=$2
    banner="$vendor          $title          $1 $_rev"
    underl=`echo "$banner" | sed -e 's?.?-?'g`
}

#------------------------------------------------------------------------
rppPrintHeader() {
#------------------------------------------------------------------------
    # Center arg1 on an 80 column display.  Underline it.
    _h=$1
    _l=`echo "$_h" | wc -c`
    _l=`expr \( 80 - $_l \) / 2`
    _i=`echo "                                          " | cut -c1-$_l`
    _u=`echo "$_h" | sed -e 's?.?-?'g`
    echo
    echo "$_i$_h"
    echo "$_i$_u"
}

#------------------------------------------------------------------------
rppShowBanner() {
#------------------------------------------------------------------------
    echo
    echo "$underl"
    echo "$banner"
    echo "$underl"

    case "$1" in
    product )
        echo "
Select a product that you have already installed and wish to patch."
        ;;
    platform )
        echo "
Select the $product platform that you wish to patch."
        ;;
    version )
        echo "
Select the $product $platform version that you wish to patch."
        ;;
    download )
        rppPrintHeader "Download Location Directory"
        ;;
    install )
        rppPrintHeader "$product $version Installation Directory"
        ;;
    commit )
        rppPrintHeader "Commit Patches"
        ;;
    esac
}

#------------------------------------------------------------------------
rppExpandTildeAndEnv() {
#------------------------------------------------------------------------
    # Use csh to expand tildes since sh cannot.
    # This also has the effect of expanding any environment variables
    # entered by the user (such as $HOME).
    /bin/csh -f <<!
    set i=$1
    echo \$i
!
}

#------------------------------------------------------------------------
rppGetInput() {
#------------------------------------------------------------------------
    # Get input from the user, providing a prompt value.
    if $firstPrompt
    then
        echo
        echo "Enter q at any prompt to quit $cmd."
        firstPrompt=false
    fi

    while true
    do
        echo
        if [ -n "$2" ]
        then
            $echon "$1 [$2]  $nocr"
        else
            $echon "$1 $nocr"
        fi
        read input </dev/tty
        if [ -n "$input" ]
        then
            echo "$input" >> $scriptLog
            case "$input" in
            \~*|*\$* )
                input2=`rppExpandTildeAndEnv $input`
                if [ "$input" != "$input2" ]
                then
                    echo "Expanded user input: $input2" >> $scriptLog
                    input="$input2"
                fi
                ;;
            esac
        else
            echo "<enter-key>" >> $scriptLog
            if [ -n "$2" ]
            then
                input=$2
            else
                echo "You must enter a value."
                continue
            fi
        fi

        case "$input" in
        q|quit|Q|Quit|QUIT )
            rppExit 0
            ;;
        esac
        break
    done
}

#------------------------------------------------------------------------
rppGetYn() {
#------------------------------------------------------------------------
    # Get and validate a yes/no answer.
    while true
    do
        rppGetInput "$1" "$2"
        case $input in
        [Yy]* ) yn=yes; return ;;
        [Nn]* ) yn=no;  return ;;
            * ) echo "Please enter yes or no." ;;
        esac
    done
}

#------------------------------------------------------------------------
rppQuoteItems() {
#------------------------------------------------------------------------
    while read _item
    do
        $echon "'$_item' $nocr"
    done
}

#------------------------------------------------------------------------
rppExtractItems() {
#------------------------------------------------------------------------
    case "$itemType" in
    product )
        # Make 'Apex Native' first in the list.
        # Hide the 'All Runtimes' product since it is automatically included
        # with Apex Embedded.
        echo Apex Native
        egrep -v "^Apex Native:| All Runtimes:" $patchFile |
        awk -F: '{print $1}' | sort -u
        ;;
    platform )
        # Hide the 'any UNIX' platform since it is automatically included.
        # Also hide any 'Media' series ID.
        grep "^$product:" $patchFile | egrep -v ":any UNIX:|:SID:Media" |
        awk -F: '{print $2}' | sort -u
        ;;
    version )
        grep -v ":SID:Media" $patchFile | grep "^$product:$platform" | awk -F: '{print $3}' | sort -u
        ;;
    esac
}

#------------------------------------------------------------------------
rppShowItems() {
#------------------------------------------------------------------------
    unset item1  item2  item3  item4  item5  item6  item7  item8  item9  item10
    unset item11 item12 item13 item14 item15 item16 item17 item18 item19 item20
    unset item21 item22 item23 item24 item25 item26 item27 item28 item29 item30
    unset item31 item32 item33 item34 item35 item36 item37 item38 item39 item40
    unset itemDefNum

    n=$#
    if [ $n = 1 ]
    then
        echo "This program can download patches for this $itemType:"
    else
        echo "This program can download patches for these ${itemType}s:"
    fi
    echo
    for i in  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 \
             21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
    do
        if [ "$i" -gt "$n"  ]
        then
            break
        fi

        if [ "$itemDefault" = "$1" ]
        then
            itemDefNum=$i
            _d=">>>"
        else
            _d="   "
        fi

        if [ "$i" -le 9 ]
        then
            echo "$_d $i)  $1"
        else
            echo "$_d$i)  $1"
        fi
        eval item$i=\"$1\"
        shift
    done

    echo
    case "$itemType" in
    product )
        echo "    d)  Download patch list again ($lastUpdated)."
        ;;
    platform|version )
        echo "    b)  Back to the previous menu."
        ;;
    esac
    echo "    q)  Quit $cmd."
}

#------------------------------------------------------------------------
rppSelectItem() {
#------------------------------------------------------------------------
    itemType=$1
    itemDefault="$2"
    rppShowBanner $itemType

    eval rppShowItems `rppExtractItems | rppQuoteItems`

    case $n in
    1 ) range="1"  ; a_range="a 1" ;;
    2 ) range="1 or 2"  ; a_range="a 1 or 2" ;;
    * ) range="1 to $n" ; a_range="a number from 1 to $n" ;;
    esac

    while true
    do
        rppGetInput "Enter a $itemType number ($range):" "$itemDefNum"
        case "$input" in
        b|B )
            return 1
            ;;
        d|D )
            rppDownloadPatchList
            rppSetLastUpdated
            ;;
        [1-9]|[1-9][0-9] )
            eval val=\$item$input
            if [ -n "$val" ]
            then
                eval $itemType=\$val
                return 0
            fi
            echo "You must enter $a_range."
            ;;
        * )
            echo "'$input' not recognized."
            echo "You must enter $a_range."
            ;;
        esac
     done
}

#------------------------------------------------------------------------
rppSelectProduct() {
#------------------------------------------------------------------------
    rppSelectItem product "$product"
    return $?
}

#------------------------------------------------------------------------
rppSelectOs() {
#------------------------------------------------------------------------
    rppSelectItem platform "$platform"
    return $?
}

#------------------------------------------------------------------------
rppSelectVer() {
#------------------------------------------------------------------------
    rppSelectItem version "$version"
    return $?
}

#------------------------------------------------------------------------
rppLocatePatch() {
#------------------------------------------------------------------------

    for src in $1/*.*/$pid
    do
        test -d $src && return
        unset src
    done
}

#------------------------------------------------------------------------
rppTarSizeOkay() {
#------------------------------------------------------------------------
    tarActual=`rppSetSize $tarFile`
    if [ "$tarSize" = "$tarActual" ]
    then
        return 0
    else
        return 1
    fi
}

#------------------------------------------------------------------------
rppSetVarsFromPatchData() {
#------------------------------------------------------------------------
    IFS=:
    set -- $patchData
    IFS=$oldIFS
    product=$1
    platform=$2
    version=$3
    directory=$4
    pid=`basename $directory`
    readme=$5
    install=$6
    inventory=$7
    tarFile=$8
    tarSize=$9
    shift 9
    if [ "$1" = SID ]
    then
        sid=$2
    else
        unset sid
    fi
}

#------------------------------------------------------------------------
rppSetCommitName() {
#------------------------------------------------------------------------
    commitName=`echo $inventory | sed -e 's?\.inventory?.committed?'`
}

#------------------------------------------------------------------------
rppPatchInstalled() {
#------------------------------------------------------------------------
    # If the patch is installed, there will be a file in the patch
    # directory with the name of the patch ID.
    for patchDir in $patchDirList
    do
        if [ -f $patchDir/$pid ]
        then
            # Yes, it is installed. See if it is committed.
            # The inventory file gets renamed from mumble.inventory
            # to mumble.committed.
            rppSetCommitName
            if [ -f $patchDir/$commitName ]
            then
                installStatus="Already installed and committed"
                notCommitted=false
            else
                installStatus="Already installed, not committed"
                notCommitted=true
            fi
            return 0
        fi
    done
    return 1
}

#------------------------------------------------------------------------
rppFixInstallScript() {
#------------------------------------------------------------------------

    # Nothing to do unless ftp.rational.com is listed in the install script.
    grep "^DEF_SERVER=.ftp.rational.com" $install >/dev/null || return

    # Fix a few things in the install script for the new ftp server.
    sed -e "s?^DEF_SERVER=.*?DEF_SERVER=\"$ftpServer\"?" \
        -e "s?^FTPDIR=.*?FTPDIR=\"$directory\"?" \
        -e "s?ftp://ftp.*.com.req_series_on_ftp?$patchInfoUrl?" \
        -e "s?Rational's FTP server?IBM's FTP server?" \
        -e "s?Rational FTP server?IBM FTP server?" \
        $install > $install.new
    cp $install.new $install
    rm -f $install.new
}

#------------------------------------------------------------------------
rppDownloadPatches() {
#------------------------------------------------------------------------

    cd $downloadDir || rppExit 1
    echo
    echo "Downloading patches in:"
    echo "  `pwd`"

    n=0
    while read patchData
    do
        n=`expr $n + 1`
        rppSetVarsFromPatchData

        if [ "$prodHome" != defer ]
        then
            # Check if this patch is already installed.
            if rppPatchInstalled
            then
                echo
                echo "Skipping patch $n of $numPatches: $pid  ($installStatus)"
                continue
            fi
        fi

        echo
        echo "Getting patch $n of $numPatches: $pid"
        echo "    $product $version for $platform"

        getList=""

        if $downloadPatchesOnly
        then
            test -d $downloadDir/$pid || mkdir $downloadDir/$pid
            cd $downloadDir/$pid
            # Do not need the readme file.
            fileList="$install         $inventory $tarFile"
        else
            fileList="$install $readme $inventory $tarFile"
        fi

        for file in $fileList
        do
            if [ -f $file ]
            then
                if [ "$file" != "$tarFile" ]
                then
                    continue
                fi
                # Check size of tar file.
                if rppTarSizeOkay
                then
                    continue
                fi
                /bin/rm -f $tarFile
            fi
            getList="$getList $file"
        done

        if [ -z "$getList" ]
        then
            rppFixInstallScript
            echo "    This patch has already been downloaded."
            continue
        fi

        getCmd=""
        fList=""
        for file in $getList
        do
            if [ "$file" = "$tarFile" ]
            then
                fList="$fList
      $file (expected size: $tarSize bytes)"
            elif [ "$file" != "$readme" ]
            then
                fList="$fList
      $file"
            fi
            getCmd="$getCmd
get $file"
        done
        set -- $getList
        nFiles=$#

        if $checkLocalNfs
        then
            # Locate patch on local NFS.
            patchAvailViaNfs=true
            $echon "    Locating patch on NFS: $pid ...$nocr"
            rppLocatePatch /ned/patches
            if [ -z "$src" ]
            then
                rppLocatePatch /ned/.Rational_Domains/aloha/patches_aloha
                if [ -z "$src" ]
                then
                    rppLocatePatch /cdrom/cdrom0
                    if [ -z "$src" ]
                    then
                        patchAvailViaNfs=false
                    fi
                fi
            fi
            if $patchAvailViaNfs
            then
                echo " found"
                case $nFiles in
                1 ) echo "    Copying 1 file from $src:" ;;
                * ) echo "    Copying $nFiles files from $src:" ;;
                esac
            else
                echo " not found--will get using ftp."
            fi
        else
            patchAvailViaNfs=false
        fi

        if $patchAvailViaNfs
        then
            for file in $getList
            do
                if [ "$file" = "$readme" ]
                then
                    # Do not copy this file from NFS if it does not exist.
                    # It is not required.
                    test -f $src/$file || continue
                fi
                $echon "      $file...$nocr"
                /bin/cp -p $src/$file .
                echo " done."
            done
            ftpProblem=false
        else
            case $nFiles in
            1 ) echo "    Downloading 1 file from $ftpServer:$fList" ;;
            * ) echo "    Downloading $nFiles files from $ftpServer:$fList" ;;
            esac
            rppDoFtp &
            if rppShowActivity
            then
                ftpProblem=false
            else
                kill $!
                return 1
            fi
        fi

        for file in $getList
        do
            if [ "$file" = "$readme" ]
            then
                continue
            fi
            if [ ! -f $file ]
            then
                echo "Failed to download $file from:"
                echo "  ftp://$ftpServer$directory"
                ftpProblem=true
            elif [ "$file" = "$tarFile" ]
            then
                rppTarSizeOkay && continue
                echo "Expected tar file size ($tarSize) does"
                echo "not  match actual size ($tarActual)."
                ftpProblem=true
            elif [ "$file" = "$install" ]
            then
                rppFixInstallScript
                chmod 755 $install
            fi
        done

        if $ftpProblem
        then
            echo "$cmd stopping due to ftp problems."
            break
        fi
    done
    cd $originalDir
}

#------------------------------------------------------------------------
rppDoFtp() {
#------------------------------------------------------------------------
    echo "open $ftpServer
    user $ftpUser $ftpPass
    bin
    cd $directory
    $getCmd
    quit" | ftp -n 2>&1
}

#------------------------------------------------------------------------
rppDownloadPatchList() {
#------------------------------------------------------------------------
    directory=$ftpScriptLocation
    cd /tmp
    rm -f $patchList
    getCmd="
dir $patchList
get $patchList"

    echo "Downloading $patchList from $ftpServer:"
    rppDoFtp

    if [ -s $patchFile ]
    then
        cd $originalDir
        return
    fi

    if $upgrading
    then
        echo "The latest version of $cmd is not yet available on $ftpServer."
        echo "Try again in an hour or so.  Exiting $cmd."
        rppExit 1
    fi

    # Attempt to download this script.
    # There may be a newer version available.
    echo "You may be running an older version of $cmd."
    echo "Downloading the latest $cmd from $ftpServer:"
    cd $originalDir
    dir=`dirname $script`
    dir=`(cd $dir; pwd)`
    cd $dir
    getCmd="
get $cmd $cmd.new"
    rppDoFtp

    if [ -s $cmd.new ]
    then
        newRev=`rppExtractRevision $cmd.new`
        echo "$cmd version $rev is obsolete.  Upgrading to"
        echo "$cmd version $newRev."
        rm -f $cmd.old
        mv $cmd $cmd.old
        mv $cmd.new $cmd
        chmod 755 $cmd
        echo "Saved version $rev as $script.old."
        echo "Running $cmd version $newRev now..."
        exec $script -upgrading
        exit 0
    fi

    echo "$cmd: Failed to downloaded $cmd script from $ftpServer."
    echo "You do not appear to have ftp connectivity to $ftpServer"
    echo "from $host.  Exiting $cmd."
    rppExit 1
}

#------------------------------------------------------------------------
rppExtractRevision() {
#------------------------------------------------------------------------
     set -- `grep 'Revision: .*Date: ' $1`
     while [ $# -gt 0 ]
     do
         case "$1" in
         *Revision: )
             echo $2
             return
             ;;
         esac
         shift
     done
}

#------------------------------------------------------------------------
rppSetSize() {
#------------------------------------------------------------------------
    # Set the size of file (arg1).
    set -- `ls -ld $1`
    _n=`expr $# - 5`
    shift $_n
    echo $1
}

#------------------------------------------------------------------------
rppSetLastUpdated() {
#------------------------------------------------------------------------
    # Set lastUpdated to be the modification date of the patchList file.
    set -- `ls -ld $patchFile`
    n=`expr $# - 4`
    shift $n
    lastUpdated="Last updated: $1 $2, $3"
}

#------------------------------------------------------------------------
rppShowActivity() {
#------------------------------------------------------------------------
    expectedSize=$tarSize
    waitInterval=5     # Time to wait between checks (seconds).
    maxChecks=120      # Maximum number of checks and file does not grow.
    barSize=66
    spaces="                                                                  "
    backup=""

    $echon "   0%|$spaces|100%$backup$nocr"

    while [ ! -f $tarFile ]
    do
        sleep $waitInterval
    done

    actualSize=`rppSetSize $tarFile`

    # Now figure out how many bytes for each - in the activity bar.
    # Every time the tarFile grows by this increment, will print another tick.
    increment=`expr $expectedSize / $barSize + 1`

    # Now figure out how many ticks to start with.  The tar file could already
    # have grown to several increments of its final size already.
    # Set the meter to the starting place in the activity bar.
    start=`expr $actualSize / $increment`
    remain=`expr $actualSize % $increment`
    meter=0
    while [ $meter -lt $start ]
    do
        meter=`expr $meter + 1`
        $echon "-$nocr"
    done

    numChecks=0
    aborted=false
    while true
    do
        actualSize=`rppSetSize $tarFile`
        new=`expr $actualSize / $increment`
        if [ $waitInterval -gt 1 ]
        then
            delta=`expr $new - $meter`
            if [ $delta -gt 1 ]
            then
                # Speed up the check frequency by a second as long as the
                # delta is more than one tick per wait cycle and the wait
                # is more than 1 second.
                waitInterval=`expr $waitInterval - 1`
            fi
        fi
        while [ $meter -lt $new ]
        do
            # File has grown.  Indicate this and check if file is done.
            $echon "-$nocr"
            meter=`expr $meter + 1`
            test $meter -ge $barSize && break 2
            numChecks=0
        done

        # Need to check if the tarFile has reached its maximum size.
        # This is another boundary condition.
        if [ $actualSize -eq $expectedSize ]
        then
            $echon "-$nocr"
            break
        fi

        sleep $waitInterval
        numChecks=`expr $numChecks + $waitInterval`
        if [ $numChecks -gt $maxChecks ]
        then
            # Abort
            aborted=true
            break
        fi
    done

    echo
    if $aborted
    then
        echo "$cmd: Timeout downloading patch tar file:"
        /bin/ls -l $tarFile
        return 1
    fi
    return 0
}

#------------------------------------------------------------------------
rppSetPatchDirList() {
#------------------------------------------------------------------------
    # Set the list of patch directories to check before downloading
    # each patch.
    case $homeVar in
    APEX_EMBEDDED_HOME )
        # For this product we need to check these patch directories.
        for _dir in \
            $prodHome/*/install/patches \
            $prodHome/install/patches
        do
            if [ -d "$_dir" ]
            then
                patchDirList="$patchDirList $_dir"
            fi
        done
        ;;
    * )
        patchDirList="$prodHome/install/patches"
        ;;
    esac
}

#------------------------------------------------------------------------
rppFindProdHome() {
#------------------------------------------------------------------------
    $echon "Looking for $product $version directory$nocr"
    # First check the directories passed in on the function invocation line.
    for _dir in $*
    do
        $echon ".$nocr"
        if [ -d "$_dir" ]
        then
            # Normalize the product home directory.
            _dir=`(cd $_dir; pwd)`
            case $_dir in
            /tmp_mnt/* ) _dir=`echo "$_dir" | sed -e 's?/tmp_mnt??'` ;;
            esac
            prodHome=$_dir
            echo found
            return
        fi
    done

    # None found.  Now check the rational_dir directories found from
    # the install defaults files.
    for _dir in $rationalDirList
    do
        $echon ".$nocr"
        if [ -d "$_dir/releases/$prodDir" ]
        then
            prodHome=$_dir/releases/$prodDir
            echo found
            return
        fi
    done
    echo none found.
}

#------------------------------------------------------------------------
rppGetCommitFlag() {
#------------------------------------------------------------------------
    rppShowBanner commit
    echo "
As each patch is installed, it can also be committed.

You should commit the patches, making them permanent.
This will save disk space, but then the patches cannot be uninstalled.

If you do not commit the patches, more disk space is consumed.
This also leaves you the option of uninstalling the patches."

    rppGetYn "Commit all current and new patches?" $commitPatches
    commitPatches=$yn
    if [ "$commitPatches" = yes ]
    then
        doCommit=true
        autoCommitOption='-auto_commit'
    else
        doCommit=false
        unset autoCommitOption
    fi
}

#------------------------------------------------------------------------
rppGetProductHomeDir() {
#------------------------------------------------------------------------
    # Ask the user for the location of the selected product homd directory.
    case "$product" in
    Apex*Embedded* )
        homeVar=APEX_EMBEDDED_HOME
        prodDir=apex_embedded.$version
        ;;
    Apex*Native )
        homeVar=APEX_HOME
        prodDir=apex.$version
        ;;
    AXI )
        homeVar=APEX_AXI_HOME
        prodDir=axi.$version
        ;;
    RoseRT )
        homeVar=ROSERT
        prodDir=RoseRT.$version
        ;;
    Rose* )
        homeVar=ROSE
        prodDir=rose.$version
        ;;
    SoDA* )
        homeVar=SODA_HOME
        prodDir=soda.$version
        ;;
    TestMate )
        homeVar=APEX_TM_HOME
        prodDir=testmate.$version
        ;;
    esac

    eval prodHome=\$$homeVar
    if [ -z "$prodHome" ]
    then
        # Check up and down the current directory for the product release.
            rppFindProdHome \
            releases/$prodDir \
            ../releases/$prodDir \
            ../../releases/$prodDir \
            ../../../releases/$prodDir \
            ../../../../releases/$prodDir \
            */releases/$prodDir
    fi

    rppShowBanner install
    echo "
If the installation directory is not accessible from $host,
you can defer the installation of the patches.  If you defer, you would
need to download all of the patches and then move the patch files to the
installation machine and run the $deferScript command.

Since $cmd can check what patches are already installed, deferring
the install is not recommended.  The performance of $cmd can be
much better if it can check if patches are already installed.
This way, $cmd can avoid downloading unnecessary files."

    while true
    do
        rppGetInput "Enter the installation directory or 'defer' install.
:" ${prodHome:-defer}
        case "$input" in
        defer|\'defer\' )
            echo "Okay, $cmd will defer the patch installation."
            prodHome=defer
            return
            ;;
        */install )
            input=`dirname $input`
            ;;
        esac

        if [ -d "$input" ]
        then
            if [ -d $input/install ]
            then
                echo
                prodHome=$input
                eval echo $homeVar=$input
                eval $homeVar=\$input
                echo export $homeVar
                export $homeVar
                rppSetPatchDirList
                return
            else
                echo "$cmd: $input/install directory not found."
                echo "This does not appear to be the installation directory"
                echo "for $product."
            fi
        else
            echo "$cmd: $input not found."
        fi
        echo "
Enter the word 'defer' if you want to download patches now and install
them later on another machine."
    done
}

#------------------------------------------------------------------------
rppSetFreeDiskSpace() {
#------------------------------------------------------------------------
    # Determine the disk space available from the DF command.
    _dir=$1
    if [ -z "$dfBlockSize" ]
    then
        if $dfCmd $_dir | grep "512-blocks" >/dev/null
        then
            dfBlockSize=512
        else
            dfBlockSize=1K
        fi
    fi

    set -- `(cd $_dir; $dfCmd .) | tail -1`
    case "$osName" in
    AIX ) freeDiskSpace=$3 ;;
      * ) shift `expr $# - 3` ; freeDiskSpace=$1 ;;
    esac

    # If the df command returns 512-byte blocks divide freeDiskSpace by 2.
    if [ "$dfBlockSize" = 512 ]
    then
        freeDiskSpace=`expr $freeDiskSpace / 2`
    fi
}

#------------------------------------------------------------------------
rppSetTotalKb() {
#------------------------------------------------------------------------
    # Compute the total disk space needed for all patches.
    # The byte size of the tar file is in the 9th position.
    # Convert the size to KB by dividing by 1024.
    # Add the extra 111 KB for install script, inventory, etc.
    _tot=0
    echo "$patchSet
    1:2:3:4:5:6:7:8:END" | awk -F: '{print $9}' | while read _tfs
    do
        case $_tfs in
        [0-9]* )
            _tot=`expr $_tot + \( $_tfs / 1024 \) + 111`
            ;;
        END )
            echo $_tot
            return
            ;;
        esac
    done
}

#------------------------------------------------------------------------
rppSetNeedKb() {
#------------------------------------------------------------------------
    # Calculate how much is disk space is needed.  Round up to the
    # nearest 1000 KB.  Add 1000 and change the last 3 digits to zeros.
    needKb=`expr $totalKb - $freeDiskSpace + 1000 | sed -e 's?...$?000?'`
}

#------------------------------------------------------------------------
rppNotInList() {
#------------------------------------------------------------------------
# Check if arg1 in arg2..argN (the list).
# If it is, return false.
# If not in the list, return true.

    _i_=$1
    shift
    for _j_ in $*
    do
        if [ "$_i_" = "$_j_" ]
        then
            unset _i_ _j_
            return 1
        fi
    done
    unset _i_ _j_
    return 0
}


#------------------------------------------------------------------------
rppSetPatchOrder() {
#------------------------------------------------------------------------
    # Sort the patch set so that any required patches install before
    # the patch that requires it.  To do this, go through the patch set
    # and extract any REQ_PID settings from the install script.  Some of
    # them will require a series id or a specific patch number.  Will need
    # to determine the patch id for each series id.

    n=0
    unset pidSeqList newSeqList
    echo "$patchSet" | while read patchData
    do
        n=`expr $n + 1`
        echo "patchData$n='$patchData'"
        pidSeqList="$pidSeqList $n"
        echo "pidSeqList='$pidSeqList'"
    done > $temp
    . $temp
    rm -f $temp

    for n in $pidSeqList
    do
        eval patchData=\$patchData$n
        rppSetVarsFromPatchData
        unset PID REQ_PID1 SERIES_ID
        eval `egrep "^PID=|^REQ_PID1=|^SERIES_ID=" $install`
        eval pid$n=$PID
        if [ -n "$SERIES_ID" ]
        then
            set -- $SERIES_ID
            eval sid$n=\$1
        fi
        if [ -n "$REQ_PID1" ]
        then
            set -- $REQ_PID1
            eval req$n=\$1
        fi
    done

    # Convert the required information to the current install order number.
    # Later check if the required patches are getting installed late.
    for n in $pidSeqList
    do
        eval req=\$req$n
        if [ -n "$req" ]
        then
            for k in $pidSeqList
            do
                eval pidk=\$pid$k
                eval sidk=\$sid$k
                if [ "$req" = "$sidk" -o "$req" = "$pidk" ]
                then
                    eval rqn$n=$k
                    rqn=$k
                    break
                fi
            done
        fi
    done

    maxRetries=5
    retry=0
    while true
    do
        # Check the list and change the install order so that
        # required patches get installed before the patch that
        # requires it.
        unset newSeqList
        for n in $pidSeqList
        do
            # If the patch has no requirement, add it to the new list.
            # If it does, add the required patch first.
            eval rqn=\$rqn$n
            eval sk1=\$skp$n
            if [ -n "$rqn" ]
            then
                eval sk2=\$skp$rqn
                if [ -z "$sk2" ]
                then
                    # Have not put rqn on the list yet.
                    newSeqList="$newSeqList $rqn"
                    eval skp$rqn=yes
                fi
            fi
            if [ -z "$sk1" ]
            then
                # Have not put n on the list yet.
                newSeqList="$newSeqList $n"
                eval skp$n=yes
            fi
        done
        if [ "$pidSeqList" = "$newSeqList" ]
        then
            break
        else
            retry=`expr $retry + 1`
            if [ $retry -le $maxRetries ]
            then
                pidSeqList="$newSeqList"
                for n in $pidSeqList
                do
                    eval unset skp$n
                done
            else
                break
            fi
        fi
    done

    # Rebuild the patchSet list using the new sequence list.
    unset patchSet
    first=yes
    for n in $newSeqList
    do
        eval patchData=\$patchData$n
        if [ "$first" = yes ]
        then
            patchSet="$patchData"
            first=no
        else
            patchSet="$patchSet
$patchData"
        fi
    done
}

#------------------------------------------------------------------------
rppGetDownloadDir() {
#------------------------------------------------------------------------
    if $downloadPatchesOnly
    then
        case $product in
        Apex*Embedded* )
            downloadDirName=apex_embedded.$version
            ;;
        Apex*Native )
            downloadDirName=apex.$version
            ;;
        AXI* )
            downloadDirName=axi.$version
            ;;
        RoseRT )
            downloadDirName=RoseRT.$version
            ;;
        Rose* )
            downloadDirName=rose.$version
            ;;
        SoDA* )
            downloadDirName=soda.$version
            ;;
        TestMate )
            downloadDirName=testmate.$version
            ;;
        esac
    else
        downloadDirName=`echo $product $version $platform Patches |
                         sed -e 's?[_\. ]??g'`
    fi

    totalKb=`rppSetTotalKb`

    rppShowBanner download
    echo "
The  $cmd command will download files here.
This directory will be created if it does not exist:
  $downloadLocation/$downloadDirName

The disk status of the filesystem that includes
'$downloadLocation' looks like this:"

    $dfCmd .
    rppSetFreeDiskSpace $downloadLocation

    if [ "$totalKb" -lt "$freeDiskSpace" ]
    then
        echo "
You appear to have enough space.  There are $freeDiskSpace KB available
and you need $totalKb KB to download.

If you want to download patches to another location, enter a different
directory where $cmd can store the downloaded patch files."

    else
        rppSetNeedKb
        echo "
You do not appear to have enough space!  There are only $freeDiskSpace KB
available and you need at least $totalKb KB to download.  This may be okay
if some of the patches have already been installed.

You can download patches to another location.  Just enter another
directory where $cmd can store the downloaded patch files.

Or you may want to free some disk space to make room.  To do this, free at
least $needKb KB of additional disk space and then re-enter this directory."

    fi

    while true
    do
        rppGetInput \
"Please enter a full pathname to an existing download location directory.
:" $downloadLocation
        newDownloadLocation=$input
        case "$newDownloadLocation" in
        /* )
            if [ -d "$newDownloadLocation" ]
            then
                echo
                echo "You have selected: $newDownloadLocation"
                $dfCmd $newDownloadLocation
                rppSetFreeDiskSpace $newDownloadLocation
                echo
                if [ "$totalKb" -lt "$freeDiskSpace" ]
                then
                    echo "This directory appears to have enough space."
                    rppGetYn "Use this directory?" yes
                else
                    rppSetNeedKb
                    echo "This directory does not appear to have enough space."
                    echo "You need at least $needKb KB more disk space."
                    rppGetYn "Use this directory anyway?" no
                fi
                if [ "$yn" = yes ]
                then
                    downloadDir=$newDownloadLocation/$downloadDirName
                    if [ -d $downloadDir ]
                    then
                        if touch $downloadDir/$cmd.test-write-dir.$$
                        then
                            rm -f $downloadDir/$cmd.test-write-dir.$$
                            break
                        else
                            echo "$cmd: $user cannot write:"
                            /bin/ls -ld $downloadDir
                        fi
                    else
                        echo mkdir $downloadDir
                        if mkdir $downloadDir
                        then
                            break
                        else
                            echo "$cmd: $user cannot create: $downloadDir"
                            /bin/ls -ld `dirname $downloadDir`
                        fi
                    fi
                fi
            else
                echo "$newDownloadLocation not found."
            fi
            ;;
        * )
            echo "$newDownloadLocation is not a full path name."
            ;;
        esac
    done
    downloadLocation=$newDownloadLocation
}

#------------------------------------------------------------------------
rppCreateDeferFile() {
#------------------------------------------------------------------------
    # Make the README file and defer script.
    echo "
Run this script to install $product $version patches:
  ./$deferScript
" > $downloadDir/README_install.txt

    echo "
product='$product'
platform='$platform'
version='$version'
patchSet='$patchSet'
numPatches='$numPatches'
commitPatches='$commitPatches'
doCommit='$doCommit'
autoCommitOption='$autoCommitOption'
" > $downloadDir/$deferFile

    # Copy this script to the download directory and rename it to be the
    # defer script.  Pretty clever, huh?
    cp $script $downloadDir/$deferScript

    echo "
To install the patches, move all the files in this directory:
  $downloadDir
to the machine where $product $version is installed.

Then run the $deferScript script.
"

}

#------------------------------------------------------------------------
rppInstallPatches() {
#------------------------------------------------------------------------
    n=0
    cd $downloadDir
    rppSetPatchOrder
    n=0
    for directory in `echo "$patchSet" | awk -F: '{print $4}'`
    do
        n=`expr $n + 1`
        patchData=`echo "$patchSet" | grep ":$directory:"`
        rppSetVarsFromPatchData
        echo "$underl"
        echo "Patch $n of $numPatches: $pid"
        if rppPatchInstalled
        then
            echo "Status: $installStatus"
            if $notCommitted
            then
                if $doCommit
                then
                    echo "Committing this patch..."
                    echo "$patchDir/$install -commit"
                    $patchDir/$install -commit
                fi
            fi
            continue
        fi

        # Install this patch now.
        test -x ./$install || chmod 755 ./$install
        echo "./$install $autoCommitOption"
        ./$install $autoCommitOption
    done
    cd $originalDir
}

#------------------------------------------------------------------------
rppMain() {
#------------------------------------------------------------------------
    if [ "$cmd" = "$deferScript" ]
    then
        if [ -f $deferFile ]
        then
            . $deferFile
            downloadDir=$originalDir
            rppGetProductHomeDir
            echo prodHome=$prodHome > $prodHomeLoc
            echo downloadDir=$downloadDir >> $prodHomeLoc
            rppInstallPatches
            return
        else
            echo "$cmd: $deferFile not found."
            return
        fi
    fi

    rppLoadDefaults

    patchList=$patchDbName$rev
    patchFile=/tmp/$patchList
    # Remove the list if it is more than a day old.  Then download it again.
    find /tmp -type f -name $patchList -mtime +1 -exec rm -f {} \; 2>/dev/null
    if [ ! -s $patchFile ]
    then
        rppDownloadPatchList
    fi
    rppSetLastUpdated

    while rppSelectProduct
    do
        while rppSelectOs
        do
            while rppSelectVer
            do
                echo "You have selected $product $version for $platform."
                break 3
            done
        done
    done

    standardFilter="^$product:$platform:$version:"
    any_unixFilter="^$product:any UNIX:$version:"
    runtimesFilter="^Apex Embedded All Runtimes:any UNIX:$version:"

    case "$product" in
    Apex*Embedded* )
        filter="$standardFilter|$runtimesFilter|$any_unixFilter"
        ;;
    * )
        filter="$standardFilter|$any_unixFilter"
        ;;
    esac

    # Get the list of patches for this product.
    # Exclude superseded patches (marked in file as :SP:).
    # or any Media patches (marked in file as :SID:Media).
    patchSet=`egrep "$filter" $patchFile | egrep -v ':SP:|:SID:Media'`

    set -- `echo "$patchSet" | wc -l`
    numPatches=$1
    case $numPatches in
    1 ) echo "There is one patch for this product." ;;
    * ) echo "There are $numPatches patches for this product." ;;
    esac

    if $downloadPatchesOnly
    then
        rppGetDownloadDir
        rppSaveDefaults
        echo "$patchSet" | rppDownloadPatches || rppExit 1
        if [ -f /ned/patches/patch_install ]
        then
            pwd
            /bin/rm -f patch_install
            cp /ned/patches/patch_install .
        fi
        return
    fi

    rppGetProductHomeDir
    echo prodHome=$prodHome > $prodHomeLoc
    rppGetDownloadDir
    echo downloadDir=$downloadDir >> $prodHomeLoc
    rppGetCommitFlag
    rppSaveDefaults

    # Download patches:
    echo "$patchSet" | rppDownloadPatches || rppExit 1

    # Install patches:
    if [ "$prodHome" = defer ]
    then
        rppCreateDeferFile
    else
        rppInstallPatches
    fi
    echo "The $cmd program defaults have been saved in:"
    echo "  $defaults"
}


#------------------------------------------------------------------------
# The main script is in the rppMain function to make it easy
# to log usage of this script for debugging.
#------------------------------------------------------------------------

rppSetBannerRev Version: $rev
rppMain 2>&1 | tee $scriptLog
echo "$underl"
if [ -f $prodHomeLoc ]
then
    # Put the log file in the product home directory, if we can.
    . $prodHomeLoc
    rm -f $prodHomeLoc
    if [ "$prodHome" = defer ]
    then
        newLogLoc=$downloadDir/rppinstall.log
    elif [ -n "$prodHome" -a -d "$prodHome" ]
    then
        if [ -d "$prodHome/install" ]
        then
            newLogLoc=$prodHome/install/$logName
        else
            newLogLoc=$prodHome/$logName
        fi
    fi
    if [ -n "$newLogLoc" ]
    then
        cat $scriptLog >> $newLogLoc
        rm -f $scriptLog
        scriptLog=$newLogLoc
    fi
fi
echo "Output from $cmd has been appended to this log file:"
logSize=`rppSetSize $scriptLog`
echo "  $scriptLog  # log size: $logSize bytes"
rppExit 0

#----------------------- End of rppinstall -----------------------
