#!/bin/sh
#------------------------------------------------------------------------------
# Apple Open Systems Software Installer
#
#	@(#)Revision is $Id: mae_installer,v 2.31 1995/02/16 20:14:58 dumais Exp $
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Copyright 1993-94 Apple Computer, Inc.
# All Rights Reserved.
#
# THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF APPLE COMPUTER, INC.
# The copyright notice above does not evidence any actual or
# intended publication of such source code.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Save argc and arg1 as globals
#------------------------------------------------------------------------------
argc=$#                                 # Number of command line args
argv=${@:-''}                           # Arg list - NULL if not set

#------------------------------------------------------------------------------
# Debugging options set here
#------------------------------------------------------------------------------
# set -u
# set -x

#------------------------------------------------------------------------------
# Make sure we can read and write the message file
#------------------------------------------------------------------------------
msg_file="/tmp/$$.MSG"                        # File used for message passing
/bin/cp /dev/null "$msg_file" 2> /dev/null
if [ ! -w "$msg_file" -o ! -r "$msg_file" ]; then
    AssertErr "Cannot access message file [ $msg_file ]"
fi
/bin/rm -f "$msg_file"

#------------------------------------------------------------------------------
# Make sure we can write to the report file
#------------------------------------------------------------------------------
report_file="/tmp/installer_report$$" 
/bin/cp /dev/null "$report_file" 2> /dev/null
if [ ! -w "$report_file" ]; then
    Alert "$no_report_msg [ $report_file ]"  
    report_file="/dev/null"
fi

#--------------------------------------------------------------------------
# Load terminal specific escape sequences into variables
#--------------------------------------------------------------------------
bld=`tput bold 2> /dev/null`            # Turn BOLD atribute ON 
rev=`tput rev 2> /dev/null`             # Turn REVERSE atribute ON 
off=`tput sgr0 2> /dev/null`            # Turn ALL atributes OFF 
clr_scrn=`tput clear 2>/dev/null`       # Clear screen 

#------------------------------------------------------------------------------
# Set umask and PATH (include cwd), and save the default IFS
#------------------------------------------------------------------------------
umask 0022
PATH=/bin:/usr/bin:/usr/ucb:/etc:/usr/etc:/usr/5bin:.
S_IFS="$IFS"                            # Save a copy of a "good" IFS

#------------------------------------------------------------------------------
# Save Location and Name information
#------------------------------------------------------------------------------
start_dir="`/bin/pwd`"                  # Directory where user was at launch
launch_dir="`dirname \"$0\"`"           # Directory where installer lives
if [ "$launch_dir" = '.' ]; then        # Make sure that it is an absolute path
    launch_dir="$start_dir"     
elif [ "`expr \"$launch_dir\"// : '\/.*'`" -eq 0 ]; then
    if [ "$start_dir" = '/' ]; then
        launch_dir="${start_dir}$launch_dir"  
    else
        launch_dir="${start_dir}/$launch_dir"  
    fi
fi
installer="`basename \"$0\"`"           # Name of install script

#------------------------------------------------------------------------------
# Find the correct strings file 
#------------------------------------------------------------------------------
catalog_dir="${LC_ALL:-${LC_MESSAGES:-${LANG:-C}}}"

# Use explicit list to work correctly in certain locales
eval catalog_dir=\"`echo $catalog_dir | tr "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"`\" 

if [ ! -r "${launch_dir}/LOCALE/${catalog_dir}/INSTALL.STR" ]; then
    echo "
    WARNING:
    Cannot locate message catalog for '$catalog_dir' locale." >&2
    
    catalog_dir="C"                               # Try finding English strings
    if [ ! -r "${launch_dir}/LOCALE/${catalog_dir}/INSTALL.STR" ]; then
        echo "
    WARNING:
    Cannot locate message catalog for '$catalog_dir' locale." >&2

        if [ ! -r "${start_dir}/INSTALL.STR" ]; then
            echo "
    ERROR:
    The Installation cannot continue until a valid message file is
    available in '${launch_dir}/LOCALE'.

    Try setting your locale using the LC_ALL, LC_MESSAGES, or LANG
    environment variables.
    " >&2
            exit 5
        else
            echo "
    WARNING:
    The Installer will use the message catalog in
    '${start_dir}/INSTALL.STR'.

    Press RETURN to continue or CONTROL-C to quit." >&2
            read i_am_waiting                     # Accept any input and continue
            . "${start_dir}/INSTALL.STR"          # Load last resort strings
        fi
    else
        echo "
    WARNING:
    The Installer will use the message catalog for the 'C' locale.

    Press RETURN to continue or CONTROL-C to quit." >&2
        read i_am_waiting                         # Accept any input and continue
        . "${launch_dir}/LOCALE/${catalog_dir}/INSTALL.STR"  # Load 'C' locale strings
    fi
else
    . "${launch_dir}/LOCALE/${catalog_dir}/INSTALL.STR"  # Load localized strings
fi


#------------------------------------------------------------------------------
# Save tty state 
#------------------------------------------------------------------------------
save_stty="`/bin/stty -g`"                        # Save stty settings
/bin/stty intr "^C"                               # Ensure that we know what sends SIGINT

#------------------------------------------------------------------------------
# Traps 
#------------------------------------------------------------------------------
trap 'CleanUpExit 3' 1 2 3 15                     # Clean up on signal

#------------------------------------------------------------------------------
# Clear Functions - we don't use absolute path names for these so...
#------------------------------------------------------------------------------
unset -f cd echo exit kill


###############################################################################
## Function definitions begin here                                           ##
###############################################################################

#------------------------------------------------------------------------------
#   Alert - display message
#------------------------------------------------------------------------------
Alert()
{
    if [ $# -ne 1 ]; then
        AssertErr "No args passed to Alert function."
    fi
    _alert_msg="$1"

    TopScreen "$hit_ret_banner_msg"
    echo 
    echo 
    echo "${bld}    ERROR:${off}$_alert_msg" 
    echo $off                                     # Just in case bold is left on
    Report "ERROR: $_alert_msg"                   # Save a copy in the report file
    WaitToContinue;
}

#------------------------------------------------------------------------------
#   Report - save message in report file
#------------------------------------------------------------------------------
Report()
{
    if [ "$report_file" = "/dev/null" ]; then
        return                                    # Report NOT!
    fi
    if [ $# -ne 1 ]; then
        AssertErr "No args passed to Report function."
    fi
    _report_msg="$1"

    echo '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -' >> "$report_file"
    echo "$_report_msg"                                                          >> "$report_file"
}

#------------------------------------------------------------------------------
# AskDir - Prompt for a directory name.  Validate before returning.
#------------------------------------------------------------------------------
AskDir()
{
    if [ $# -ne 4 ]; then
        AssertErr "Incorrect number of args passed to AskDir()."
    fi

    _ask_dir_prompt="$1"
    _ask_dir_help="$2"
    _ask_dir_pkg_name="$3"
    _ask_dir_write_req="$4"                       # Either 'W' or NULL
    eval _ask_dir_def_loc="\$d_${_ask_dir_pkg_name}_dir"
	
    while :; do
        TopScreen;                                # Clear screen and display banner
        echo "$_ask_dir_prompt" 
        if [ -z "$_ask_dir_def_loc" ]; then
            Prompt "    [$bld$no_default_msg$off] >>> "
        else
            Prompt "    [$default_equals_msg$bld$_ask_dir_def_loc$off] >>> "
        fi
        read _ask_dir_location
    
        ToLower _ask_dir_location                 # Convert to Lowercase
        case "$_ask_dir_location_lwr" in
            '')                  if [ -z "$_ask_dir_def_loc" ]; then
                                     Alert "$alert_no_default_msg"
                                     continue     # No default, ask again
                                 else 
                                     _ask_dir_location="$_ask_dir_def_loc" # Use default
                                 fi
                                 ;;
            "$help_msg"|\
            "$help_short_msg")   DoHelp "$_ask_dir_help"
                                 continue
                                 ;;
            "$quit_msg"|\
            "$quit_short_msg")   AskQuit "$yes_msg"
                                 continue
                                 ;;
            "$q_bang_msg")       CleanUpExit 0
                                 ;;
        esac

        # Run validation script
        "${source_dir}/${_ask_dir_pkg_name}.VAL" "$_ask_dir_location" "$msg_file" destination 2> /dev/null
        _ask_dir_pkg_result=$?
        if [ $_ask_dir_pkg_result -ne 0 ]; then   # Error
            if [ -r "$msg_file" -a -s "$msg_file" ]; then
                ShowErrMsg
            else
                Alert "$an_error_msg    (destination)"  # Generic msg
            fi
            return 1
        fi                            
        if BadDir "$_ask_dir_location" "$_ask_dir_pkg_name" "$_ask_dir_write_req"; then
            if [ "$_ask_dir_location" = "$_ask_dir_def_loc" ]; then
                _ask_dir_def_loc=                 # Default didn't work; reset
                eval ${_ask_dir_pkg_name}_dir=
                eval d_${_ask_dir_pkg_name}_dir=
            fi
            return 1                              # Bad dir, return ERROR
        fi
        return 0                                  # Dir ok, return SUCCESS
    done
}

#------------------------------------------------------------------------------
# AskInstMode - Sets inst_mode
#------------------------------------------------------------------------------
AskInstMode()
{
    if [ -z "$d_inst_mode" ]; then
        AssertErr "No default installation mode in AskInstmode"
    fi
	
    while :; do
        TopScreen;
        case "$d_inst_mode" in
            EASY)   if [ "$e_pkg_cnt" -ge 1 ]; then
                        _inst_mode_prompt="$easy_msg" 
                    else
                        _inst_mode_prompt="$custom_msg"
                        d_inst_mode=CUSTOM
                    fi
                    ;;
            CUSTOM) _inst_mode_prompt="$custom_msg"
                    ;;
            REMOVE) if [ "$r_pkg_cnt" -ge 1 ]; then
                        _inst_mode_prompt="$remove_msg"
                    else
                        _inst_mode_prompt="$custom_msg"
                        d_inst_mode=CUSTOM
                    fi
                    ;;
            *)      AssertErr "Invalid d_inst_mode in AskInstMode." ;;
        esac

        echo "$instmode_prompt_msg" 
        echo
        Prompt "    [default=$bld$_inst_mode_prompt$off] >>> "
        read _inst_mode_selection
        ToLower _inst_mode_selection;
        case "$_inst_mode_selection_lwr" in
            '')                    inst_mode="$d_inst_mode"
                                   return
                                   ;;
            "$easy_short_msg"|\
            "$easy_msg")           if [ "$e_pkg_cnt" -eq 0 ]; then
                                       Alert "$no_easy_packages_msg"
                                       continue
                                   fi
                                   inst_mode="EASY"
                                   d_inst_mode="EASY"
                                   return
                                   ;;
            "$custom_short_msg"|\
            "$custom_msg")         inst_mode="CUSTOM"
                                   d_inst_mode="CUSTOM"
                                   return
                                   ;;
            "$remove_short_msg"|\
            "$remove_msg")         if [ "$r_pkg_cnt" -eq 0 ]; then
                                       Alert "$no_rem_packages_msg"
                                       continue
                                   fi
                                   inst_mode="REMOVE"
                                   d_inst_mode="REMOVE"
                                   return
                                   ;;
            "$quit_msg"|\
            "$quit_short_msg")     AskQuit "$yes_msg"
                                   continue
                                   ;;
            "$q_bang_msg")         CleanUpExit 0
                                   ;;
            "$help_msg"|\
            "$help_short_msg")     DoHelp "$instmode_help_msg"
                                   continue
                                   ;;
            *)                     Alert "$alert_not_valid_msg"
                                   continue 
                                   ;; 
        esac
    done
}

#------------------------------------------------------------------------------
#   AskQuit - either quit or continue with the install based on input
#------------------------------------------------------------------------------
AskQuit()
{
    if [ $# -ne 1 ]; then
        AssertErr "Incorrect arg count in AskQuit()."
    fi
    quit_default="$1"

    if Interactive; then
        in_quit=TRUE;                   # Prevent recursive calls to AskQuit
        AskYesNo "$ask_quit_msg" "$quit_help_msg" "$quit_default"
        in_quit=FALSE                   # Reset flag
        case "$reply" in
            "$yes_msg") CleanUpExit 0 ;; # Clean up and exit
            "$no_msg")  return 0 ;;     # Continue with installation
            *) AssertErr "Invalid return from AskYesNo() in AskQuit()" ;;
        esac
    else
         CleanUpExit 0                  # Just exit if NON-interactive
    fi
}   

#------------------------------------------------------------------------------
# FindSourceDir - Sets source_dir
#------------------------------------------------------------------------------
FindSourceDir()
{
    # Do a Pre-check to try to find the source dir
    if [ ${INSTALL_SRC:-NULL} != NULL ]; then     # Check environment variable 
        if [ -f "${INSTALL_SRC}/${o_s}.TOP" ]; then
            d_source_dir="$INSTALL_SRC"
            return 0                              # Happy, Happy, Joy, Joy
        elif [ -f "$INSTALL_SRC/${o_s}/${o_s}.TOP" ]; then
            d_source_dir="${INSTALL_SRC}/$o_s"
            return 0
        fi
    else                                          # Environment variable not set
        if [ -z "$d_source_dir" ]; then           # Should be set in InitVars
            d_source_dir="/cdrom"
        fi
        for check_point in "$launch_dir" "$d_source_dir" "$start_dir"; do
            if [ -f "${check_point}/${o_s}.TOP" ]; then
                d_source_dir="$check_point"
                return 0
            elif [ -f "${check_point}/${o_s}/${o_s}.TOP" ]; then
                d_source_dir="${check_point}/$o_s"
                return 0
            fi
        done
    fi

    Alert "$is_not_correct_dir_msg" 
    CleanUpExit 2
}

#------------------------------------------------------------------------------
# FindPackages - Locate and verify the packages to be installed
#------------------------------------------------------------------------------
FindPackages()
{
    source_dir="$d_source_dir"      # Use default (reserved for future use)
    pkg_cnt=0                       # Initialize global array subscript
    e_pkg_cnt=0                     # Initialize global array subscript
    r_pkg_cnt=0                     # Initialize global array subscript

    if [ -x "$source_dir" ]; then     # Exit if cd will fail
        cd "$source_dir"
    else
        Alert "$cannot_chdir_to_msg    [ $source_dir ].  $quitting_msg"
        CleanUpExit 2
    fi

    echo "$find_pkgs_msg"               # Let them know what we are doing
    #--------------------------------------------------------------------------
    # For each PKG file found, try to locate all of the other required files
    #--------------------------------------------------------------------------
    for _fnd_pkg_pkg in `/bin/ls *.PKG 2> /dev/null`; do
        if [ -z "$_fnd_pkg_pkg" ]; then
            Alert "$is_not_correct_dir_msg"
            CleanUpExit 2                         # No PKG files
        fi
        _fnd_pkg_pkg_name="`expr \"$_fnd_pkg_pkg\" : '\(.*\)\.PKG'`"    # Get package (file) name

        #----------------------------------------------------------------------
        # Look for the .STR (strings) file
        #----------------------------------------------------------------------
        if [ ! -r "LOCALE/$catalog_dir/${_fnd_pkg_pkg_name}.STR" ]; then
            continue                              # Get next package
        fi

        #----------------------------------------------------------------------
        # Look for the .VAL (validation) file
        #----------------------------------------------------------------------
        if [ ! -x "${_fnd_pkg_pkg_name}.VAL" ]; then
            continue                              # Get next package
        fi

        #----------------------------------------------------------------------
        # Run validation (preflight) script
        #----------------------------------------------------------------------
        "${source_dir}/${_fnd_pkg_pkg_name}.VAL" '' "$msg_file" preflight 2> /dev/null
        _fnd_pkg_result=$?
        if [ $_fnd_pkg_result -ne 0 ]; then       # Error
            if [ "$quiet" = TRUE ]; then
                continue                          # Do not display message
            fi
            if [ -r "$msg_file" -a -s "$msg_file" ]; then
                ShowErrMsg
            else
                Alert "$an_error_msg    (preflight)"  # Generic msg
            fi
            echo "$find_pkgs_msg"                 # Let them know what we are doing
            continue                              # Get next package
        fi                            

        cd "$source_dir"                          # Just in case VAL script changed cwd

        #----------------------------------------------------------------------
        # Look for the .SIZ (size) file
        #----------------------------------------------------------------------
        if [ ! -r "${_fnd_pkg_pkg_name}.SIZ" ]; then
            continue                              # Get next package
        fi

        space_req="`/bin/cat ${_fnd_pkg_pkg_name}.SIZ`"       # Space requirements in KB

        #----------------------------------------------------------------------
        # Look for the .INS (installer) file
        #----------------------------------------------------------------------
        if [ ! -x "${_fnd_pkg_pkg_name}.INS" ]; then
            continue                              # Get next package
        fi

        #----------------------------------------------------------------------
        # Look for the .NAM (name) file
        #----------------------------------------------------------------------
        if [ ! -r "LOCALE/$catalog_dir/${_fnd_pkg_pkg_name}.NAM" ]; then
            continue                              # Get next package
        fi

        #----------------------------------------------------------------------
        # Make a note if package is part of 'Easy Install'
        #----------------------------------------------------------------------
        match_chars=`expr "$_fnd_pkg_pkg_name" : 'E.*'`  # Is an Easy Install package
        if [ "$match_chars" -ne 0 ]; then
            e_pkg_cnt=`expr $e_pkg_cnt + 1`   
            eval e_package_$e_pkg_cnt="$_fnd_pkg_pkg_name" 
        fi

        #----------------------------------------------------------------------
        # Make a note if package can be listed under 'Remove' option
        #----------------------------------------------------------------------
        "${source_dir}/${_fnd_pkg_pkg_name}.VAL" '' /dev/null ask_rem 2> /dev/null

        if [ $? -eq 0 ]; then
            r_pkg_cnt=`expr $r_pkg_cnt + 1`
            eval r_package_$r_pkg_cnt="$_fnd_pkg_pkg_name"
        fi

        #----------------------------------------------------------------------
        # Load all other package information into arrays
        #----------------------------------------------------------------------
        pkg_cnt=`expr $pkg_cnt + 1`               # Increment package count
        eval package_$pkg_cnt="$_fnd_pkg_pkg_name" # Name saved in array of names
        eval ${_fnd_pkg_pkg_name}_size=$space_req # Space requirements
        eval inst_$_fnd_pkg_pkg_name=''           # Initialize Only
    done

    #--------------------------------------------------------------------------
    # If no packages are found then we have to exit
    #--------------------------------------------------------------------------
    if [ $pkg_cnt -eq 0 ]; then        
        Alert "$is_not_correct_dir_msg"           # No packages found!
        CleanUpExit 2
    fi
    if [ "$e_pkg_cnt" -eq 0 -a "$inst_mode" = EASY ]; then
        Alert "$no_easy_packages_msg"
        CleanUpExit 0
    fi
    if [ "$r_pkg_cnt" -eq 0 -a "$inst_mode" = REMOVE ]; then
        Alert "$no_rem_packages_msg"
        CleanUpExit 0
    fi
}

#------------------------------------------------------------------------------
# AskInstPkgs - Ask user if they want to install each package in turn
#------------------------------------------------------------------------------
AskInstPkgs()
{
    _ask_inst_cnt=1
    while [ $_ask_inst_cnt -le $pkg_cnt ]; do
        default_destination=''                       
        eval _ask_inst_pkg_name="\$package_$_ask_inst_cnt"

        #----------------------------------------------------------------------
        # Default is YES for Easy packages and NO for other packages.
        #----------------------------------------------------------------------
        match_chars=`expr "$_ask_inst_pkg_name" : 'E.*'`  # Is an Easy Install package
        if [ "$match_chars" -ne 0 ]; then
            eval _ask_inst_default="\${inst_$_ask_inst_pkg_name:-$yes_msg}" # Easy
        else
            eval _ask_inst_default="\${inst_$_ask_inst_pkg_name:-$no_msg}"  # Custom
        fi

        . "$source_dir/LOCALE/$catalog_dir/${_ask_inst_pkg_name}.STR"

        #----------------------------------------------------------------------
        # AskYesNo returns "yes" or "no" in $reply
        #----------------------------------------------------------------------
        AskYesNo "$install_prompt_msg" "$install_help_msg" "$_ask_inst_default"

        eval inst_$_ask_inst_pkg_name="$reply" 
        
        if [ "$reply" = "$yes_msg" ]; then
            "${source_dir}/${_ask_inst_pkg_name}.VAL" '' /dev/null ask_dest >/dev/null 2>&1
            ask_dest=$?
            if [ "$ask_dest" -ne 0 ]; then # Dont ask for destination
                #--------------------------------------------------------------
                # Validate destination
                #--------------------------------------------------------------
                if [ -z "$default_destination" ]; then
                    AssertErr "default_destination not set in AskInstPkgs"
                fi
                _dest_dir="`eval echo $default_destination`"
                eval d_${_ask_inst_pkg_name}_dir="$_dest_dir"

                "${source_dir}/${_ask_inst_pkg_name}.VAL" "`eval echo \\\$d_${_ask_inst_pkg_name}_dir`" "$msg_file" destination 2>> "$report_file"
                _ask_inst_result=$?
                if [ $_ask_inst_result -ne 0 ]; then    # Error
                    if [ -r "$msg_file" -a -s "$msg_file" ]; then
                        ShowErrMsg
                    else
                        Alert "$an_error_msg    (destination)"  # Generic msg
                    fi
                    continue
                fi
                if BadDir "`eval echo \\\$d_${_ask_inst_pkg_name}_dir`" "$_ask_inst_pkg_name" W; then
                    continue
                fi
            else
                SetTopDir
                eval d_${_ask_inst_pkg_name}_dir=\${d_${_ask_inst_pkg_name}_dir:-"$d_dest_dir"}
                #--------------------------------------------------------------
                # The AskDir function will modify the value of <_ask_inst_pkg_name>_dir. 
                #--------------------------------------------------------------
                AskDir "$destination_prompt_msg" "$destination_help_msg" "$_ask_inst_pkg_name" W 
                if [ $? -ne 0 ]; then                 # Bad Dir - go back
                    continue                          # Ask if they want to install
                fi
                eval d_dest_dir=\$${_ask_inst_pkg_name}_dir   # For SetTopDir
                master_d_dest_dir="${master_d_dest_dir:-$d_dest_dir}" # Set once and save
            fi
        fi
        _ask_inst_cnt=`expr $_ask_inst_cnt + 1`
    done
        
    return
}

#------------------------------------------------------------------------------
# ListRemPkgs - List all of the packages that the installer knows how to remove
#------------------------------------------------------------------------------
ListRemPkgs()
{
    if [ "$r_pkg_cnt" -eq 0 ]; then
        AssertErr "No removable packages in ListRemPkgs"
    fi

    TopScreen "$hit_ret_banner_msg"               # Clear screen and display header
    echo "$can_remove_these_msg"
    _list_rem_cnt=1
    while [ $_list_rem_cnt -le $r_pkg_cnt ]; do
        eval _list_rem_pkg_name="\$r_package_$_list_rem_cnt"
        echo "        `/bin/cat \"$source_dir/LOCALE/$catalog_dir/${_list_rem_pkg_name}.NAM\"`"
        _list_rem_cnt=`expr $_list_rem_cnt + 1`
    done
    echo
    WaitToContinue
}
#------------------------------------------------------------------------------
# AskRemPkgs - Ask user if they want to remove each package in turn
#------------------------------------------------------------------------------
AskRemPkgs()
{
    if [ "$r_pkg_cnt" -eq 0 ]; then
        AssertErr "No removable packages in AskRemPkgs"
    fi

    ListRemPkgs     # List all of the packages that the installer knows how to remove

    _ask_rem_cnt=1
    while [ $_ask_rem_cnt -le $r_pkg_cnt ]; do
        eval _ask_rem_pkg_name="\$r_package_$_ask_rem_cnt"
        . "$source_dir/LOCALE/$catalog_dir/${_ask_rem_pkg_name}.STR"
        eval _ask_rem_default="\${rem_$_ask_rem_pkg_name:-$no_msg}" 
        #------------------------------------------------------------------
        # AskYesNo returns "yes" or "no" in $reply
        #------------------------------------------------------------------
        AskYesNo "$remove_prompt_msg" "$remove_help_msg" "$_ask_rem_default"
        eval rem_$_ask_rem_pkg_name="$reply" 
      
        if [ "$reply" = "$yes_msg" ]; then
            "${source_dir}/${_ask_rem_pkg_name}.VAL" '' /dev/null ask_dest >/dev/null 2>&1
            ask_dest=$?
            if [ "$ask_dest" -eq 0 ]; then
                SetTopDir
                eval d_${_ask_rem_pkg_name}_dir=\${d_${_ask_rem_pkg_name}_dir:-"$d_dest_dir"}
                #----------------------------------------------------------
                # The AskDir function will modify the value of <_ask_rem_pkg_name>_dir. 
                #----------------------------------------------------------
                AskDir "$where_is_prompt_msg" "$where_is_help_msg" "$_ask_rem_pkg_name" ''
                if [ $? -ne 0 ]; then
                    continue                  # Ask (again) if they want to remove pkg
                fi
                eval d_dest_dir=\$${_ask_rem_pkg_name}_dir   # For SetTopDir
                master_d_dest_dir="${master_d_dest_dir:-$d_dest_dir}" # Set once and save
            fi
        fi
        _ask_rem_cnt=`expr $_ask_rem_cnt + 1`
    done
        
    return
}

#------------------------------------------------------------------------------
#   AskYesNo - keep asking a yes/no question till user answers yes/no
#   $3 is default, assumes help will be provided by all calling routines
#------------------------------------------------------------------------------
AskYesNo()
{
    if [ $# -ne 3 ]; then
        AssertErr "Incorrect argument count in AskYesNo()."
    fi
    _ask_y_n_prompt="$1"                         # Local $prompt
    _ask_y_n_help="$2"                           # Local $help
    _ask_y_n_default="$3"                        # Local $default (yes/no)
    if [ "$_ask_y_n_default" != "$yes_msg" -a "$_ask_y_n_default" != "$no_msg" ]; then
        AssertErr "Problem with arguments passed to AskYesNo()."
    fi

    while :; do
        TopScreen;
        echo "$_ask_y_n_prompt"  
        Prompt "    [$default_equals_msg$bld$_ask_y_n_default$off] >>> "
        read _ask_y_n_answer
        ToLower _ask_y_n_answer                  # Convert to Lowercase
   
        case "$_ask_y_n_answer_lwr" in
        '')                  reply=$_ask_y_n_default    # Use default
                             return 0
                             ;;
        "$yes_msg"|\
        "$yes_short_msg")    reply="$yes_msg"
                             return
                             ;;
        "$no_msg"|\
        "$no_short_msg")     reply="$no_msg"
                             return
                             ;;
        "$help_msg"|\
        "$help_short_msg")   DoHelp "$_ask_y_n_help"
                             continue
                             ;;
        "$quit_msg"|\
        "$quit_short_msg")   if [ "$in_quit" = "TRUE" ]; then
                                 CleanUpExit 0    # Go to exit
                             fi
                              save_ask_y_n_prompt="$_ask_y_n_prompt"
                              save_ask_y_n_help="$_ask_y_n_help"
                              save_ask_y_n_default="$_ask_y_n_default"
                             AskQuit "$yes_msg"   # Quit (or return here)
                              _ask_y_n_prompt="$save_ask_y_n_prompt"
                              _ask_y_n_help="$save_ask_y_n_help"
                              _ask_y_n_default="$save_ask_y_n_default"
                             continue             # Ask again
                             ;;
         "$q_bang_msg")      CleanUpExit 0
                             ;;
        *)   Alert "$alert_not_valid_msg" ;;
       esac
    done
}

#------------------------------------------------------------------------------
# AssertErr - Display Alert and exit
#------------------------------------------------------------------------------
AssertErr()
{
    if [ $# -lt 1 ]; then
        _assert_err="No information available"
    else
        _assert_err="$1"
    fi

    Alert "$assert_msg

    [ $_assert_err ]"

    CleanUpExit 1                               
}

#------------------------------------------------------------------------------
# BadDir - Check directory accessability, (and writablity) ( return 0 if bad)
#------------------------------------------------------------------------------
BadDir()
{
    # Usage: BadDir <dir_name> <pkg_name>  W | ''

    if [ $# -ne 3 -o "$3" != W -a "$3" != '' ]; then
        AssertErr "Problem with argument list in BadDir()." 
    fi

    _bad_dir_dir="$1"
    _bad_dir_pkg_name="$2"
    if [ "$3" = W ]; then
        _need_write_access=TRUE
    else
        _need_write_access=FALSE
    fi
    
    echo "$checking_directory_msg"                # This takes a bit of time - warn them

    #--------------------------------------------------------------------------
    # Prevent user from getting the shell to do nasty things here.
    #--------------------------------------------------------------------------
    _bad_dir_dir="`echo "$_bad_dir_dir" | cut -d';' -f1`"           # Remove any junk after a ';'
    _bad_dir_dir="`echo "$_bad_dir_dir" | cut -d'|' -f1`"           # Remove any junk after a '|'

    #--------------------------------------------------------------------------
    # Use shell to expand path.  Allows for $HOME stuff
    #--------------------------------------------------------------------------
    _bad_dir_dir="`eval echo \"$_bad_dir_dir\"`"  # Expand path
    
    if [ $? -ne 0 -o "`expr \"$_bad_dir_dir\"// : '.*~.*'`" -ne 0 ]; then
        Alert "$badly_formed_path_msg" 
        return 0                                  # Path must contain bad chars
    fi

    if [ "`expr \"$_bad_dir_dir\"// : '\/.*'`" -eq 0 ]; then
        _bad_dir_dir="$start_dir/$_bad_dir_dir"   # Prepend path if not absolute
    fi

    if [ ! -d "$_bad_dir_dir" ]; then
        if [ "$_need_write_access" = TRUE ]; then # Write access required
            if MakeDir "$_bad_dir_dir"; then      # Try to create dir
                :                                 # Remember to remove this dir later
            else                                  
                Alert "$cannot_create_dir_msg    [$_bad_dir_dir]." 
                return 0                          # Return TRUE (is bad dir)
            fi
        else                                      # We should not create dir
            Alert "$dir_not_exist_msg    [$_bad_dir_dir]."    
            return 0                              # Return TRUE (is bad dir)
        fi
    fi                                            # Dir must exist

    chmod u+rwx "$_bad_dir_dir" 2> /dev/null      # Ensure full access to dir

    #------------------------------------------------------------------------
    # If destination directory is not accessable then display alert and return
    #------------------------------------------------------------------------
    if [ ! -x "$_bad_dir_dir" ]; then
        Alert "$cannot_chdir_to_msg    [$_bad_dir_dir]"       # Cannot chdir to directory
        return 0                                  # Return TRUE (is bad dir)
    else
        cd "$_bad_dir_dir" 2> /dev/null           
        _bad_dir_dir="`/bin/pwd`"                 # Get REAL path (resolves sym-links, etc.)
    fi
    if [ ! -r "$_bad_dir_dir" ]; then
        Alert "$cannot_read_from_msg    [$_bad_dir_dir]"      # Cannot read from directory
        return 0                                  # Return TRUE (is bad dir)
    fi

    #--------------------------------------------------------------------------
    # Must be fully expanded before calling SpaceCheck
    #--------------------------------------------------------------------------
    eval ${_bad_dir_pkg_name}_dir=\"$_bad_dir_dir\"                 
    eval d_${_bad_dir_pkg_name}_dir=\"$_bad_dir_dir\"  
    
    #------------------------------------------------------------------------
    # If two args then test for writability and free space
    #------------------------------------------------------------------------
    if [ "$_need_write_access" = TRUE ]; then     # Write test request
        if [ -w "$_bad_dir_dir" ]; then           # If we can write to the dir
            $mkdir_cmd "$mkdir_opt" "${_bad_dir_dir}/$$" 2> /dev/null  
            if [ -d "${_bad_dir_dir}/$$" ]; then  # Double Check!! (r/o NFS mounts)
                /bin/rmdir "${_bad_dir_dir}/$$" 2> /dev/null        # Clean up tmp dir
            else                                  # Don't be fooled by CD-ROMs
                Alert "$cannot_write_to_msg    [$_bad_dir_dir]"   # Cannot write to directory
                return 0                          # Return TRUE (is bad dir) 
            fi
        else
            Alert "$cannot_write_to_msg    [$_bad_dir_dir]"   # Cannot write to directory
            return 0                              # Return TRUE (is bad dir)
        fi
        #--------------------------------------------------------------------
        # Check for free space on volume
        #--------------------------------------------------------------------
        if SpaceCheck "$_bad_dir_dir" "$_bad_dir_pkg_name"; then
            if [ "${we_made_dir:-FALSE}" = TRUE ]; then    # Only if we made the dir
                cd "$start_dir"                   # In case we're in the dir to be removed
                /bin/rmdir "$_bad_dir_dir" 2> /dev/null
            fi
            return 1                              # Return FALSE (not bad dir)
        else
            if [ "${we_made_dir:-FALSE}" = TRUE ]; then    # Only if we made the dir
                cd "$start_dir"                   # In case we're in the dir to be removed
                /bin/rmdir "$_bad_dir_dir" 2> /dev/null
            fi
            return 0                              # Return TRUE (is bad dir)
        fi
    fi

    return 1                                      # Return FALSE (not bad dir)
}

#------------------------------------------------------------------------------
#   CleanUpExit -  Clean up and partially installed files then call exit
#   with the status in arg[1].
#------------------------------------------------------------------------------
CleanUpExit()
{
trap 'echo $done_msg; exit 0' 1 2 3 15            # Just exit if we get another signal
        #---------------------------------------------------#
        #   exit status 0 is normal                         #
        #   exit status 1 is ASSERTION ERROR                #
        #   exit status 2 is I/O ERROR, parameter ERROR     #
        #   exit status 3 is SIGNAL                         #
        #---------------------------------------------------#

    if [ $# -ne 1 ]; then
        AssertErr "Incorrect arg count in CleanUpExit()."
    fi
    _exit_status=$1

    echo "$off"                                   # Undo any screen effects
    /bin/stty "$save_stty"                        # Reset to original settings
    /bin/rm -f "$msg_file"                        # Remove temp file for this session

    if [ "$_exit_status" -eq 3 ]; then
        Report "Terminated by signal"
    fi

    #--------------------------------------------------------------------------
    # Tell them about the report file, if one exists
    #--------------------------------------------------------------------------
    if [ "$report_file" != "/dev/null" -a -s "$report_file" -a $_exit_status -ne 99 ]; then
        echo "$read_the_report_msg [ $report_file ]"
        echo; echo
    fi

    #--------------------------------------------------------------------------
    # No cleanup required if status 0 
    #--------------------------------------------------------------------------
    if [ $_exit_status -eq 0 ]; then
        exit 
    fi

    if [ $_exit_status -ne 99 ]; then 
        #----------------------------------------------------------------------
        # Set a signal handler to protect us and then kill everyone in our proc grp
        # (HP-UX 9.01 does not support killing negative PIDs; can you say BUG!))
        #----------------------------------------------------------------------
        trap 'CleanUpExit 99' 15                  # Run this func again on SIGTERM
        kill -15 0  > /dev/null 2>&1              # Send SIGTERM to init (sent to all in proc grp)
        # kill -15 -$$ > /dev/null 2>&1             # Kill the currently running install script
    fi

    #--------------------------------------------------------------------------
    # Otherwise clean up any installed files
    #--------------------------------------------------------------------------
    Prompt "$cleaning_up_msg"                   

    #--------------------------------------------------------------------------
    # Make sure those pesky kids are dead
    #--------------------------------------------------------------------------
    wait $! > /dev/null 2>&1

    if [ "$inst_mode" != REMOVE -a ${_do_inst_status:-0} -eq 99 ]; then
        "${source_dir}/${_do_inst_pkg_name}.INS" "$_do_inst_destination" "$msg_file" cleanup 2> /dev/null
        if [ $? -ne 0 ]; then
            if [ -r "$msg_file" -a -s "$msg_file" ]; then
                ShowErrMsg
            fi
        fi
    fi
    #--------------------------------------------------------------------------
    # Add any new cleanup code here.     
    #--------------------------------------------------------------------------

    echo "$done_msg"               
    exit $_exit_status
}

#------------------------------------------------------------------------------
# Confirm - Return TRUE (0) to continue; FALSE (1) to make changes
#------------------------------------------------------------------------------
Confirm()
{
    Interactive || return 0                      # Don't do this if non-interactive

    if [ "$inst_mode" != EASY -a "$inst_mode" != CUSTOM -a "$inst_mode" != REMOVE ]; then
         AssertErr "Invalid installation mode in Confirm()."
    fi

    #--------------------------------------------------------------------------
    # Check for No Packages Selected
    #--------------------------------------------------------------------------
    _confirm_cnt=1
    while [ $_confirm_cnt -le $pkg_cnt ]; do
        eval _confirm_pkg_name="\$package_$_confirm_cnt"
        if [ "$inst_mode" = REMOVE ]; then
            eval _confirm_install="\$rem_$_confirm_pkg_name"
        else
            eval _confirm_install="\$inst_$_confirm_pkg_name"
        fi
        if [ "$_confirm_install" = "$yes_msg" ]; then
            break                                 # There is at least one package to install
        fi
        _confirm_cnt="`expr $_confirm_cnt + 1`"
    done
    if [ $_confirm_cnt -gt $pkg_cnt ]; then  
        if [ "$inst_mode" = REMOVE ]; then
            Alert "$no_rem_packages_selected_msg"
        else
            Alert "$no_inst_packages_selected_msg"
        fi
        return 1
    fi
        
    if [ "$inst_mode" = REMOVE ]; then 
        _confirm_prompt="$confirm_remove_msg"
    else
        _confirm_prompt="$confirm_install_msg"
    fi

    _confirm_cnt=1
    while [ $_confirm_cnt -le $pkg_cnt ]; do
        eval _confirm_pkg_name="\$package_$_confirm_cnt"
        if [ "$inst_mode" = REMOVE ]; then
            eval _confirm_install="\$rem_$_confirm_pkg_name"
        else
            eval _confirm_install="\$inst_$_confirm_pkg_name"
        fi
        if [ "$_confirm_install" = "$yes_msg" ]; then
            . "$source_dir/LOCALE/$catalog_dir/${_confirm_pkg_name}.STR"   # Load package strings
            _confirm_prompt="$_confirm_prompt
        $package_name_msg"
        fi
    _confirm_cnt="`expr $_confirm_cnt + 1`"
    done

    Report "$_confirm_prompt"                     # Keep a list in the report file
    if [ "$inst_mode" = REMOVE ]; then
        _confirm_prompt="$_confirm_prompt $do_you_want_to_cont_rem_msg"
        _confirm_help="$confirm_rem_help_msg"
    else
        _confirm_prompt="$_confirm_prompt $do_you_want_to_cont_inst_msg"
        _confirm_help="$confirm_inst_help_msg"
    fi

    AskYesNo "$_confirm_prompt" "$_confirm_help" "$yes_msg"
    case "$reply" in
        "$yes_msg") return 0 ;;                   # Proceed
        "$no_msg")  return 1 ;;                   # Start over
    esac
}

#------------------------------------------------------------------------------
# DoClear - Clear screen (if supported)
#------------------------------------------------------------------------------
DoClear()
{
    if [ "$can_clear" = TRUE ]; then
        Prompt "$clr_scrn"                        # Clear screen
    else
        # Incrementing loops in sh are toooo slooooww.
        echo; echo; echo; echo; echo; echo; echo; echo; 
        echo; echo; echo; echo; echo; echo; echo; echo; 
        echo; echo; echo; echo; echo; echo; echo; echo; 
    fi
}

#------------------------------------------------------------------------------
#   DoHelp - display help message and wait for a RETURN to continue
#------------------------------------------------------------------------------
DoHelp()
{
    if [ $# -ne 1 ]; then
        AssertErr "Incorrect arg count in DoHelp()."
    fi
    _do_help_msg="$1"
	
    DoClear;
    echo "$rev$help_side_msg$help_title_msg$help_side_msg$off"
    echo "$_do_help_msg" 
    echo
    echo 
    echo "$help_trailer1_msg"
    echo "$help_trailer2_msg"
    echo

    WaitToContinue;
}

#------------------------------------------------------------------------------
# DoInstall - Installs requested packages 
#------------------------------------------------------------------------------
DoInstall()
{
    if [ "$pkg_cnt" -le 0 ]; then
        AssertErr "Bogus pkg_cnt in DoInstall."
    fi

    if Interactive; then
        TopScreen "$press_to_stop_msg"
    else
        echo; echo "$press_to_stop_msg"
        sleep 3                                   # Give them a moment to quit
    fi

    _error_occurred=FALSE                         # Initialize global
    appledir=''                                   # set to NULL
    _do_inst_cnt=1
    while [ $_do_inst_cnt -le $pkg_cnt ]; do
        eval _do_inst_pkg_name="\$package_$_do_inst_cnt"
        eval _do_inst_install="\$inst_$_do_inst_pkg_name"
        if [ "$_do_inst_install" = "$yes_msg" ]; then
            _do_inst_destination="`eval echo \\$\${_do_inst_pkg_name}_dir`"
            appledir="${appledir:-$_do_inst_destination}"     # Set to first directory used  
            . "$source_dir/LOCALE/$catalog_dir/${_do_inst_pkg_name}.STR" # Load package strings

            #------------------------------------------------------------------
            # Run the package install script
            #------------------------------------------------------------------
            _do_inst_status=99
            Report "Installing $package_name_msg in $_do_inst_destination."
            "${source_dir}/${_do_inst_pkg_name}.INS" "$_do_inst_destination" "$msg_file" install 2>> "$report_file"
            _do_inst_status=$?

            if [ $_do_inst_status -ne 0 ]; then   # Error
                _error_occurred=TRUE              # Global Flag
                if [ -r "$msg_file" -a -s "$msg_file" ]; then
                    ShowErrMsg
                else
                    Alert "$an_error_msg    (install)"  # A generic message
                    echo
                fi
                Report "Cleaning up $package_name_msg in $_do_inst_destination."
                "${source_dir}/${_do_inst_pkg_name}.INS" "$_do_inst_destination" "$msg_file" cleanup 2>> "$report_file"
                if [ $? -ne 0 ]; then
                    if [ -r "$msg_file" -a -s "$msg_file" ]; then
                        ShowErrMsg
                    else
                        Alert "$an_error_msg    (cleanup)"  # A generic message
                        echo
                    fi
                fi
                if [ $_do_inst_cnt -lt $pkg_cnt ]; then    # Still more packages
                    AskQuit "$yes_msg"            # Quit (or return here)
                fi
            else                                  # Package installed without error
                _do_inst_status=99
                Report "Verifying $package_name_msg in $_do_inst_destination."
                "${source_dir}/${_do_inst_pkg_name}.INS" "$_do_inst_destination" "$msg_file" verify 2>> "$report_file"
                _do_inst_status=$?
                if [ $_do_inst_status -ne 0 ]; then   # Verify failed
                    _error_occurred=TRUE              # Global Flag
                    if [ -r "$msg_file" -a -s "$msg_file" ]; then
                        ShowErrMsg
                    else
                        Alert "$an_error_msg    (verify)"  # A generic message
                        echo
                    fi
                    Report "Cleaning up $package_name_msg in $_do_inst_destination."
                    "${source_dir}/${_do_inst_pkg_name}.INS" "$_do_inst_destination" "$msg_file" cleanup 2>> "$report_file"
                    if [ $? -ne 0 ]; then         # Remove failed
                        if [ -r "$msg_file" -a -s "$msg_file" ]; then
                            ShowErrMsg
                        else
                            Alert "$an_error_msg    (cleanup)" # A generic message
                            echo
                        fi
                    fi
                else
                    #----------------------------------------------------------
                    # Put entry in AliasList file  # DEBUG: Ick! MAE Specific!
                    #----------------------------------------------------------
                    if [ -d "${appledir}/lib" -a \
                         -w "${appledir}/lib" -a \
                         -d "${appledir}/Fonts" ]; then
                        "${source_dir}/${_do_inst_pkg_name}.VAL" '' /dev/null mk_alias >/dev/null 2>&1
                        if [ $? -eq 0 ]; then
                            echo "nonpersistent|home|$_do_inst_destination" >> "${appledir}/lib/AliasList" 2> /dev/null
                            $sort_cmd -u -o "${appledir}/lib/AliasList" "${appledir}/lib/AliasList"
                        fi
                    fi
                fi  
            fi
        fi
        _do_inst_cnt=`expr $_do_inst_cnt + 1`
    done
        
    return
}

#------------------------------------------------------------------------------
# DoRemove - Removes requested packages 
#------------------------------------------------------------------------------
DoRemove()
{
    if [ "$r_pkg_cnt" -le 0 ]; then
        AssertErr "No removable packages in DoRemove"
    fi

    if Interactive; then
        TopScreen "$press_to_stop_msg"
    else
        echo; echo "$press_to_stop_msg"
        sleep 3                                   # Give them a moment to quit
    fi

    _error_occurred=FALSE                         # Initialize global
    _do_rem_cnt=1
    while [ $_do_rem_cnt -le $r_pkg_cnt ]; do
        eval _do_rem_pkg_name="\$r_package_$_do_rem_cnt"
        eval _do_rem_remove="\$rem_$_do_rem_pkg_name"
        if [ "$_do_rem_remove" = "$yes_msg" ]; then
            _do_rem_destination="`eval echo \\$\${_do_rem_pkg_name}_dir`"
            . "$source_dir/LOCALE/$catalog_dir/${_do_rem_pkg_name}.STR" # Load package strings

            #------------------------------------------------------------------
            # Run the package remove script 
            #------------------------------------------------------------------
            Report "Removing $package_name_msg from $_do_rem_destination."
            "${source_dir}/${_do_rem_pkg_name}.INS" "$_do_rem_destination" "$msg_file" remove 2>> "$report_file"
            if [ $? -ne 0 ]; then
                if [ -r "$msg_file" -a -s "$msg_file" ]; then
                    ShowErrMsg
                else
                    Alert "$an_error_msg    (remove)"  # A generic message
                    echo
                fi
                _error_occurred=TRUE
            fi
        fi
        _do_rem_cnt=`expr $_do_rem_cnt + 1`
    done
        
    return
}

#------------------------------------------------------------------------------
# Epilogue - Any last requests?
#------------------------------------------------------------------------------
Epilogue()
{
    if [ "${_error_occurred:-FALSE}" = TRUE ]; then
        if [ "$inst_mode" = REMOVE ]; then
            echo "$removal_failed_msg"
            Report "$removal_failed_msg"
        else
            echo "$installation_failed_msg"
            Report "$installation_failed_msg"
        fi
        _exit_code=2
    else
        if [ "$inst_mode" = REMOVE ]; then
            echo "$remove_complete_msg" 
            Report "$remove_complete_msg" 
        else
            echo "$install_complete_msg" 
            Report "$install_complete_msg" 
        fi
        _exit_code=0 
    fi
    CleanUpExit $_exit_code
}

#------------------------------------------------------------------------------
# GetOS - Determine system type and OS
#------------------------------------------------------------------------------
GetOS()
{
    case "`/bin/uname -s`" in
         A/UX*)  o_s='AUX'     ;;                 # Apple
          AIX*)  o_s='unknown' ;;                 # IBM
        SunOS*)  o_s='SUN'     ;;                 # Sun
        HP-UX*)  o_s='HPUX'    ;;                 # HP
             *)  o_s='unknown' ;;                 # unknown
    esac
    
    #--------------------------------------------------------------------------
    # Additional code to determine O_S version and release
    #--------------------------------------------------------------------------
    case $o_s in
        SUN) case "`/bin/uname -r`" in
                 4*) o_s='unknown' ;;             # SunOS 
                 5*) o_s='SOLARIS' ;;             # Solaris 2.x
                  *) o_s='unknown' ;;             # unknown
             esac
             ;;
    esac
    
    if [ "$o_s" = unknown ]; then
        Alert "[`/bin/uname -sr`]  $not_supported_os_msg." 
        CleanUpExit 2
    fi
}

#------------------------------------------------------------------------------
# GetOpts - Get command line options
#------------------------------------------------------------------------------
GetOpts()
{
    if [ "$argc" -gt 2 ]; then
        echo; echo "$usage_msg"; CleanUpExit 0
    fi

    inst_mode=''                                  # Initialize 
    quiet=FALSE                                   # Warn about pkgs that cannot be installed
    interactive=TRUE                              # Interactive mode
    for _arg in $argv; do
        case "$_arg" in
            '')  return                           # Start from the beginning
                 ;;
            -q)  quiet=TRUE
                 ;;
            -e)  inst_mode=EASY
                 return                           # Quick move to WherePutAll
                 ;;
            -c)  inst_mode=CUSTOM
                 return                           # Quick move to AskInstPkg
                 ;;
            -r)  inst_mode=REMOVE
                 return                           # Quick move to AskRemPkg
                 ;;
            -*)  echo; echo "$usage_msg"
                 CleanUpExit 0
                 ;;
             *)  inst_mode=EASY
                 interactive=FALSE
                 d_dest_dir="$_arg"
                 return                            # Non-interactive Easy install
                 ;;
        esac
    done
}

#------------------------------------------------------------------------------
# InitVars - O/S specific commands loaded into variables
#------------------------------------------------------------------------------
InitVars()
{
    d_inst_mode="EASY" 
    d_source_dir='/cdrom'
    d_dest_dir='/usr/apple'                    

    df_cmd='/bin/df'                              
    format_cmd='/bin/fmt'                         
    mkdir_cmd='/bin/mkdir'                      
    mkdir_opt='-p'                      
    sort_cmd='/bin/sort'                      
    tput_cmd='/bin/tput'                      
    if `echo -n XXXX | /bin/grep n > /dev/null 2>&1`; then
        bsd_echo=FALSE
    else
        bsd_echo=TRUE
    fi

    case "$o_s" in
          AIX)    df_arg='-I'                       
                  ;;
        SUN_OS)   df_arg=''                       
                  d_dest_dir='/opt/apple'            
                  ;;
        SOLARIS)  df_arg='-k'                       
                  d_dest_dir='/opt/apple'            
                  ;;
         HPUX)    format_cmd='/bin/adjust'         
                  df_cmd='/usr/bin/bdf'             
                  df_arg=''                         
                  tput_cmd='/usr/bin/tput'          
                  ;;
          AUX)    df_arg='-B'                       
                  tput_cmd='/usr/bin/tput'          
                  format_cmd='/usr/ucb/fmt'
                  mkdir_opt=''                      
                  ;;
           *)     AssertErr "Unknown O_S type in InitVars()."
                  ;;
    esac


    #--------------------------------------------------------------------------
    # Test to determine whether we are dumb terminal or smart.
    #--------------------------------------------------------------------------
    $tput_cmd clear > /tmp/tput.test$$ 2> /dev/null
    if [ -s /tmp/tput.test$$ ]; then
        can_clear=TRUE
    else
        can_clear=FALSE
    fi
    /bin/rm -f /tmp/tput.test$$
}

#------------------------------------------------------------------------------
# Interactive - Return TRUE (0) if interactive=TRUE
#------------------------------------------------------------------------------
Interactive()
{
    if [ "$interactive" = TRUE ]; then
         return 0                                 # We ARE interactive
    else
        return 1                                  # We are NOT interactive
    fi
}

#--------------------------------------------------------------------------
# ListPackages - Let the user know what packages are available
#--------------------------------------------------------------------------
ListPackages()
{
    TopScreen "$hit_ret_banner_msg"               # Clear screen and display header
    echo "$here_are_the_pkgs_msg"
    #----------------------
    # Easy Only Packages
    #----------------------
    if [ "$e_pkg_cnt" -ge 1 ]; then
        echo "$here_are_the_e_pkgs_msg"
         _list_pkgs_cnt=1
        while [ $_list_pkgs_cnt -le $e_pkg_cnt ]; do
            eval _list_pkgs_pkg_name="\$e_package_$_list_pkgs_cnt"
            eval _list_pkgs_size="\$${_list_pkgs_pkg_name}_size"
            # Convert size to number of megabytes
            _list_pkgs_size="`expr $_list_pkgs_size / 1024`"  
            #------------------------------------------------------------------
            # Add 1 to inflate the number slightly (almost the same as rounding up)
            #------------------------------------------------------------------
            _list_pkgs_size="`expr $_list_pkgs_size + 1`"
    
            echo "        ${_list_pkgs_size}MB	`/bin/cat \"$source_dir/LOCALE/$catalog_dir/${_list_pkgs_pkg_name}.NAM\"`"
    
        _list_pkgs_cnt="`expr $_list_pkgs_cnt + 1`"
        done
    fi
    #--------------------------------------------------------------------------
    # Other Packages
    #--------------------------------------------------------------------------
    if [ "$pkg_cnt" -ge 1 -a "$pkg_cnt" -gt "$e_pkg_cnt" ]; then
        if [ "$e_pkg_cnt" -ge 1 ]; then
            echo "$here_are_the_c_pkgs_msg1"
        else
            echo "$here_are_the_c_pkgs_msg2"
        fi
        _list_pkgs_cnt=1
        while [ $_list_pkgs_cnt -le $pkg_cnt ]; do
            eval _list_pkgs_pkg_name="\$package_$_list_pkgs_cnt"
            match_chars=`expr "$_list_pkgs_pkg_name" : 'E.*'`  # Is an Easy Install package
            if [ "$match_chars" -ne 0 ]; then
                _list_pkgs_cnt="`expr $_list_pkgs_cnt + 1`"
                continue                          # Has already been displayed
            fi
            eval _list_pkgs_size="\$${_list_pkgs_pkg_name}_size"
            #------------------------------------------------------------------
            # Use 1000 (instead of 1024) to inflate the number slightly
            #------------------------------------------------------------------
            _list_pkgs_size="`expr $_list_pkgs_size / 1000`"  
            if [ "$_list_pkgs_size" -eq 0 ]; then
                _list_pkgs_size='<1'
            fi
    
            . "$source_dir/LOCALE/$catalog_dir/${_list_pkgs_pkg_name}.STR"   # Load package strings
            echo "        ${_list_pkgs_size}MB	$package_name_msg"
    
        _list_pkgs_cnt="`expr $_list_pkgs_cnt + 1`"
        done
    else
        echo "$here_are_the_c_pkgs_msg0"
    fi
    echo
    WaitToContinue
}

#--------------------------------------------------------------------------
# LoadMountList - load (global) array with mount points and free space
#--------------------------------------------------------------------------
LoadMountList()
{
    IFS='
'
    _ld_mnt_cnt=1
    _skipped_header=''
    for _mnt_point in `$df_cmd $df_arg 2> /dev/null`; do
        if [ -z "$_skipped_header" ]; then
            _skipped_header="TRUE"
            continue                              # Skip the header
        fi
        set -- `echo X$_mnt_point | awk '{ print($4); print($6) }'`  # Prepend an X for df() bug
        if [ "${1:-NULL}" = NULL -o "${2:-NULL}" = NULL ]; then
            continue                              # Must be a bad mount - ignore
        fi
        eval free_space_$_ld_mnt_cnt="$1"
        if [ -d "$2" -a -x "$2" ]; then           # If dir exists and is searchable
            cd "$2"                               # We're pretty darn sure that the dir exists
            _tmp_dir="`/bin/pwd 2> /dev/null`"    # Get "real" path (resolve sym-links)
            if [ $? -ne 0 ]; then
                _tmp_dir="$2"                     # Use whatever df() gave us
            fi
        else
            _tmp_dir="$2"                         # Use whatever df() gave us
        fi
        eval mnt_point_$_ld_mnt_cnt="$_tmp_dir"

        _ld_mnt_cnt=`expr $_ld_mnt_cnt + 1`
    done

    MAX_MNTS=`expr $_ld_mnt_cnt - 1`              # Global
    IFS="$S_IFS"
}

#------------------------------------------------------------------------------
# MakeDir - Make directory(s) from supplied path - always absolute path
#------------------------------------------------------------------------------
MakeDir()
{
    if [ $# -ne 1 ]; then
        AssertErr "Incorrect arg count in MakeDir()."
    fi
    _mk_dir_target="$1"
    we_made_dir=FALSE                             # Global Flag

    if [ -d "$_mk_dir_target" ]; then             # Already exists
        return 0              
    fi

    if [ -f "$_mk_dir_target" ]; then             # File with same name exists
        Alert "$file_not_dir_msg"
        return 1                                  # Error
    fi

    $mkdir_cmd "$mkdir_opt" "$_mk_dir_target" > /dev/null 2>&1
    if [ ! -d "$_mk_dir_target" ]; then           # Could not make directory
        return 1                                  # Return Error
    fi

    we_made_dir=TRUE                              # Global Flag
    return 0                                      # Return Success
}

#------------------------------------------------------------------------------
# Prompt - Display prompt - suppress CR/LF 
#------------------------------------------------------------------------------
Prompt()
{
    if [ $# -ne 1 ]; then
        AssertErr "Incorrect arg count in Prompt()."
    fi

    if [ "$bsd_echo" = TRUE ]; then
        echo -n "$1"
    else
        echo "$1\c"
    fi
}

#------------------------------------------------------------------------------
# RemoveDir - Remove directory(s) from supplied path, if empty
#------------------------------------------------------------------------------
RemoveDir()
{
    if [ $# -ne 1 ]; then
        AssertErr "Incorrect arg count in RemoveDir()."
    fi

    _rem_dir_dir="$1"
    cd "$start_dir"                               # In case we're in the dir to be removed
    
    until [ -z "$_rem_dir_dir" ]; do
        /bin/rmdir "$_rem_dir_dir" 2> /dev/null   # Remove dir if possible
        if [ $? -ne 0 ]; then                     # Could not remove dir (not an error)
            return 0
        fi
        _rem_dir_dir="`expr \"$_rem_dir_dir\" : '\(.*\)\/.*'`"      # remove rightmost dirname
    done

    return 0                                      # Return TRUE
}

#------------------------------------------------------------------------------
# MountPoint - determine the mountpoint of a directory
#------------------------------------------------------------------------------
MountPoint()
{
    if [ $# -ne 1 ]; then
        AssertErr "Incorrect arg count in MountPoint()."
    fi

    _mnt_pnt_dir_name="$1"
    #--------------------------------------------------------------------------
    # Save the name of the dir; strip all spaces in path
    #--------------------------------------------------------------------------
    _mnt_pnt_save_name="`echo $_mnt_pnt_dir_name | tr -d  ' ./-'`"

    #--------------------------------------------------------------------------
    # Use cached data if it exists
    #--------------------------------------------------------------------------
    _mnt_pnt_mount_point="`eval echo \\$_mp_\${_mnt_pnt_save_name}_dir`" 

    until [ -n "$_mnt_pnt_mount_point" ]; do      # Loop until mountpoint is found
        IFS='
'
        for _mnt_pnt_tmp_line in `$df_cmd $df_arg 2> /dev/null`; do
            IFS="$S_IFS"
            set -- "`echo X$_mnt_pnt_tmp_line | awk '{ print($6) }'`"
            if [ "${1:-NULL}" = "$_mnt_pnt_dir_name" ]; then
                _mnt_pnt_mount_point="$_mnt_pnt_dir_name"
                continue 2                        # Continue in 'until' loop
            fi
        done

        if [ -z "$_mnt_pnt_mount_point" ]; then            # Haven't found it
            _mnt_pnt_dir_name="`expr \"$_mnt_pnt_dir_name\" : '\(.*\)\/.*'`"  # remove rightmost dir
            #------------------------------------------------------------------
            # The command above will turn '/foo' into '' so we put '/' back
            #------------------------------------------------------------------
            if [ -z "$_mnt_pnt_dir_name" ]; then
                _mnt_pnt_dir_name='/'                      # If doesn't match now, we're hosed!
            fi
        fi
    done

    eval _mp_${_mnt_pnt_save_name}_dir=$_mnt_pnt_mount_point 

    _mnt_pnt_cnt=1
    while [ $_mnt_pnt_cnt -le $MAX_MNTS ]; do
        eval _mnt_pnt_mount_point_tmp="\$mnt_point_$_mnt_pnt_cnt"
        if [ "$_mnt_pnt_mount_point" = "$_mnt_pnt_mount_point_tmp" ]; then
            return $_mnt_pnt_cnt                           # Return with the mount index
        else
            _mnt_pnt_cnt=`expr $_mnt_pnt_cnt + 1` 
        fi
    done

    Alert "$could_not_find_mnt_point_msg"         # Should have returned above
    return 99                                     # Error detected
}

#------------------------------------------------------------------------------
# SetTopDir - Sets d_dest_dir
#------------------------------------------------------------------------------
SetTopDir()
{
    if [ "${d_dest_dir:-NULL}" = NULL ]; then
        AssertErr "d_dest_dir not set in SetTopDir"
        exit 1
    fi

    # If not the default default (or first time) remove the last dir name
    if [ "$d_dest_dir" != "${master_d_dest_dir:-$d_dest_dir}" ]; then
        d_dest_dir="`dirname \"$d_dest_dir\"`" # Use parent of current d_dest_dir
    fi

    #--------------------------------------------------------------------------
    # Get the name of prefered dir
    #--------------------------------------------------------------------------
    if [ -n "$default_destination" ]; then        # Set in <package>.STR
        d_dest_dir="`eval echo $default_destination`"
    fi

    #--------------------------------------------------------------------------
    # If target dir does not exist create it (remove it before leaving this function)
    #--------------------------------------------------------------------------
    if [ ! -d "$d_dest_dir" ]; then
        MakeDir "$d_dest_dir"                     # Try to create dir
    fi
 
    #--------------------------------------------------------------------------
    # If d_dest_dir is not accessable then default to HOME directory
    #--------------------------------------------------------------------------
    if [ ! -d "$d_dest_dir" -o ! -x "$d_dest_dir" -o ! -w "$d_dest_dir" ]; then
        dir="`basename \"$d_dest_dir\"`"          # Get the name of prefered dir
        if [ "$dir" != "apple" ]; then
            dir="apple/$dir"                      # Add "apple" if not there
        fi
        set_top_tmp="${HOME:-/tmp}"               # Use HOME, or /tmp if not set
        d_dest_dir="${set_top_tmp}/$dir"          # Make it a sub dir of HOME
    else
        if [ "${we_made_dir:-FALSE}" = TRUE ]; then
            cd "$start_dir"                       # In case we're in the dir to be removed
            /bin/rmdir "$d_dest_dir" 2> /dev/null # Remove dir if empty
        fi
    fi
}

#------------------------------------------------------------------------------
# SpaceCheck - calculate free space - needed space; return 1 if insufficient
#------------------------------------------------------------------------------
SpaceCheck()
{
    if [ $# -ne 2 ]; then
        AssertErr "Incorrect arg count in SpaceCheck()."
    fi
    
    _spc_chk_dir="$1"                             # Target dir

    echo "$checking_space_msg"                    # This takes some time - warn them

    LoadMountList                                 # Get current avail space
    MountPoint "$_spc_chk_dir"                    # Get mount point for arg
    mnt_num=$?                                    # Entry into mount_point[]
    if [ $mnt_num -eq 99 ]; then
        return 1                                  # Error in MountPoint function
    fi

    alloc_space=0
    _spc_chk_cnt=1
    while [ $_spc_chk_cnt -le $pkg_cnt ]; do      
        eval _spc_chk_pkg_name="\$package_$_spc_chk_cnt"
        eval _spc_chk_inst_var="\$inst_$_spc_chk_pkg_name"   
        if [ "$_spc_chk_inst_var" != "$yes_msg" ]; then
            _spc_chk_cnt=`expr $_spc_chk_cnt + 1`
            continue                              # Not to be installed
        fi
        var_dir="`eval echo \\$\${_spc_chk_pkg_name}_dir`"
        MountPoint "$var_dir"                     # Get mount point for arg dest
        var_mnt_num=$?                            # Entry into mount_point[]
        if [ $var_mnt_num -eq 99 ]; then
            return 1                              # Error in MountPoint function
        fi
        if [ $var_mnt_num -ne $mnt_num ]; then
            _spc_chk_cnt=`expr $_spc_chk_cnt + 1`
            continue                              # Not on same mount point
        fi
        _spc_chk_pkg_size="`eval echo \\$\${_spc_chk_pkg_name}_size`"
        alloc_space="`expr $alloc_space + $_spc_chk_pkg_size`"
        _spc_chk_cnt=`expr $_spc_chk_cnt + 1`
    done

    eval free_space="\$free_space_$mnt_num"
    if [ $free_space -lt $alloc_space ]; then
        Alert "$not_enough_free_space_msg    [ $_spc_chk_dir ]" 
        return 1
    else
        return 0
    fi
}

#------------------------------------------------------------------------------
# ToLower - Convert input string to lowercase
#------------------------------------------------------------------------------
ToLower()
{
    if [ $# -ne 1 ]; then AssertErr "ToLower"; fi

    mixed="`eval echo \\$\$1`"

    if [ -z "$mixed" ]; then 
        eval ${1}_lwr=\"\"
    else
        #----------------------------------------------------------------------
        # Convert alphabetic to lowercase and $ to _
        # Use explicit list to work correctly in certain locales
        #----------------------------------------------------------------------
        eval ${1}_lwr=\"`echo $mixed | tr "ABCDEFGHIJKLMNOPQRSTUVWXYZ$" "abcdefghijklmnopqrstuvwxyz_"`\" 
    fi
}

#------------------------------------------------------------------------------
# TopScreen - clear screen - home cursor - display help banner
#------------------------------------------------------------------------------
TopScreen()
{
    if Interactive ; then
        :
    else
        return                                    # Don't do anything in batch mode
    fi

    if [ $# -eq 0 ]; then
        banner="$banner_msg"                      # Set default banner
    else
        banner="$1"                               # Override default with arg1
    fi

    DoClear;                                      # Clear screen - Home cursor
    echo  "$rev$banner$off"                       # Display banner in rev_video
}

#------------------------------------------------------------------------------
# WaitToContinue - Wait for user to hit RETURN key
#------------------------------------------------------------------------------
WaitToContinue()
{
    if Interactive ; then
        :
    else
        return                                    # Don't do anything in batch mode
    fi

    while :; do
        Prompt "${hit_ret_side_msg}${bld}${hit_return_msg}${off}"
        while :; do
            /bin/stty -echo                       # Don't display input
            read  key
            /bin/stty echo                        # Reset to norm
            case "$key" in
            '')                   return 0        # Time to move on
                                  ;;
            "$help_msg"|\
            "$help_short_msg")   DoHelp "$no_help_msg"
                                 return 0
                                 ;;
            "$quit_msg"|\
            "$quit_short_msg")   if [ "$in_quit" = "TRUE" ]; then
                                     CleanUpExit 0
                                 fi
                                 AskQuit "$yes_msg" # Either exit or return here
                                 continue 2        # Display "Hit RETURN" msg again
                                 ;;
            "$q_bang_msg")       CleanUpExit 0
                                 ;;
            esac
        done
    done
}

#------------------------------------------------------------------------------
# Welcome - Display welcome message and wait to continue
#------------------------------------------------------------------------------
Welcome()
{
    DoClear
    echo "$welcome1_msg" 
    echo "$welcome2_msg" 
    echo "$welcome3_msg" 
    WaitToContinue
}

#------------------------------------------------------------------------------
# ShowErrMsg - Display error message, save in report file, and wait to continue
#------------------------------------------------------------------------------
ShowErrMsg()
{
    TopScreen "$hit_ret_banner_msg";
    echo
    /bin/cat "$msg_file"
    echo
    Report "`/bin/cat \"$msg_file\"`"    # Save message in report file
    WaitToContinue
}

#------------------------------------------------------------------------------
# WherePutAll - Ask user where they want to install everything
#------------------------------------------------------------------------------
WherePutAll()
{
    if [ "$e_pkg_cnt" -lt 1 ]; then               # No Easy packages!
        AssertErr "No Easy packages in WherePutAll"
    fi

    _whr_put_cnt=1
    _easy_size=0
    #--------------------------------------------------------------------------
    # Interested in Easy packages only
    #--------------------------------------------------------------------------
    while [ $_whr_put_cnt -le $e_pkg_cnt ]; do
        eval _whr_put_pkg_name="\$e_package_$_whr_put_cnt"
        eval inst_$_whr_put_pkg_name="$no_msg"   # Just pretending for a moment
        _easy_size="`eval expr $_easy_size + \\$\${_whr_put_pkg_name}_size`"
        _whr_put_cnt=`expr $_whr_put_cnt + 1`
    done

    #--------------------------------------------------------------------------
    # Generate default location based on first package (#1)
    #--------------------------------------------------------------------------
    eval _whr_put_pkg_name="\$e_package_1"        # Get name for package number 1
    eval ${_whr_put_pkg_name}_size=$_easy_size    # Total of all packages
    eval inst_${_whr_put_pkg_name}="$yes_msg"     # For BadDir call
    default_destination=''                        # Should be set in *.STR
    . "$source_dir/LOCALE/$catalog_dir/${_whr_put_pkg_name}.STR"     # Load localized strings file
    Interactive && SetTopDir                      # Sets d_dest_dir (if interactive)
    eval d_${_whr_put_pkg_name}_dir=\${d_${_whr_put_pkg_name}_dir:-"$d_dest_dir"} # Used in AskDir

    while :; do
        if Interactive; then
            #------------------------------------------------------------------
            # The AskDir function will modify the value of ${_whr_put_pkg_name}_dir. 
            #------------------------------------------------------------------
            AskDir "$easy_install_prompt_msg" "$easy_install_help_msg" "$_whr_put_pkg_name" W
        else
            #--------------------------------------------------------------
            # Run validation script
            #--------------------------------------------------------------
            "${source_dir}/${_whr_put_pkg_name}.VAL" "`eval echo \\\$d_${_whr_put_pkg_name}_dir`" "$msg_file" destination 2>> "$report_file"
            if [ $? -ne 0 ]; then                 # Error
                if [ -r "$msg_file" -a -s "$msg_file" ]; then
                    ShowErrMsg
                else
                    Alert "$an_error_msg    (destination)"  # Generic msg
                fi
                CleanUpExit 0 
            fi
            if BadDir "`eval echo \\\$d_${_whr_put_pkg_name}_dir`" "$_whr_put_pkg_name" W; then
                CleanUpExit 0 
            fi
        fi
        if [ $? -ne 0 ]; then
            continue                              # Bad dir - Ask again
        else
            break                                 # Good dir - Go on
        fi
    done

    _whr_put_cnt=1
    d_dest_dir="`eval echo \\$\${_whr_put_pkg_name}_dir`"                       
    master_d_dest_dir="${master_d_dest_dir:-$d_dest_dir}" # Set once and save
    while [ $_whr_put_cnt -le $e_pkg_cnt ]; do
        eval _whr_put_pkg_name="\$e_package_$_whr_put_cnt"
        . "$source_dir/LOCALE/$catalog_dir/${_whr_put_pkg_name}.STR"    # Load strings 
        if [ -n "$default_destination" ]; then    # Set in <package>.STR
            eval ${_whr_put_pkg_name}_dir="`echo $default_destination`"
        else
            eval ${_whr_put_pkg_name}_dir="$d_dest_dir" 
        fi
        # DEBUG We really should do the full check for each package/destination
        # but that would take a long time
        eval inst_$_whr_put_pkg_name="$yes_msg"   # We really do want to install this package
        _whr_put_cnt=`expr $_whr_put_cnt + 1`     # Increment counter
    done

    return 0
}

###############################################################################
## Main - Main program begins                                                ##
###############################################################################

#------------------------------------------------------------------------------
# Identify OS   - sets o_s (or exits)
#------------------------------------------------------------------------------
GetOS

#------------------------------------------------------------------------------
# Initialize platform specific vars - sets lots of things
#------------------------------------------------------------------------------
InitVars

#------------------------------------------------------------------------------
# Get command line options - sets interactive, inst_mode, d_dest_dir
#------------------------------------------------------------------------------
GetOpts

#------------------------------------------------------------------------------
# Locate source files (or exit) - sets source_dir
#------------------------------------------------------------------------------
FindSourceDir

#------------------------------------------------------------------------------
# Start the report log file with the date, command args, and O/S version
#------------------------------------------------------------------------------
echo "`date`"               >  "$report_file"     # Create the file
echo "$0 $argv"             >> "$report_file"
echo "`/bin/uname -sr`"     >> "$report_file"
echo '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -' >> "$report_file"

#------------------------------------------------------------------------------
# Based on the current settings, jump into the main loop at the apropriate state
#------------------------------------------------------------------------------
case $inst_mode in
    '')        state=Welcome ;;                   # Normal default
    EASY)      FindPackages; state=WherePutAll ;; # Quick Easy
    CUSTOM)    FindPackages; state=AskInstPkgs ;; # Quick Custom
    REMOVE)    FindPackages; state=AskRemPkgs ;;  # Quick Remove
    *)         AssertErr "Invalid state entering main loop" ;;
esac

#-------------------------------------------------------------------------------
# Main loop controls flow of execution
#-------------------------------------------------------------------------------
while :; do
    case "$state" in 
    Welcome)       Welcome                        # Display welcome message
                   state=FindPackages
                   ;;
    FindPackages)  FindPackages                   # Sets pkg_cnt/e_pkg_cnt/r_pkg_cnt (or exit)
                   state=ListPackages
                   ;;
    ListPackages)  ListPackages                   # Display package info 
                   state=AskInstMode
                   ;;
    AskInstMode)   AskInstMode                    # Modifies inst_mode
                   case $inst_mode in
                       EASY)    state=WherePutAll ;;
                       CUSTOM)  state=AskInstPkgs ;;
                       REMOVE)  state=AskRemPkgs ;;
                       *)       AssertErr "Invalid inst_mode in main loop." ;;
                   esac
                   ;;
    AskInstPkgs)   AskInstPkgs                    # Modifies inst_*
                   state=Confirm
                   ;;
    AskRemPkgs)    AskRemPkgs                     # Modifies rem_*
                   state=Confirm
                   ;;
    WherePutAll)   WherePutAll                    # Modifies inst_*
                   state=Confirm
                   ;;
    Confirm)       if Confirm; then               
                       if [ $inst_mode = REMOVE ]; then
                           state=DoRemove         # Go Forward to Remove
                       else
                           state=DoInstall        # Go forward to Install
                       fi
                   else
                       state=AskInstMode          # Go Back 
                   fi
                   ;;
    DoInstall)     DoInstall                      # Does not return
                   state=Epilogue
                   ;;
    DoRemove)      DoRemove                       # Does not return
                   state=Epilogue
                   ;;
    Epilogue)      Epilogue
                   ;;
    *)             AssertErr "Invalid State! State = '$state'."
                   ;;
    esac
done

