WFDB SWIG Toolbox for MATLAB 1.0.0

File: <base>/install.sh (19,775 bytes)
#!/bin/bash
#
# headless wfdb-swig-matlab installer for *nix systems
#


#
#
#
BASE_URL=""
MATLAB=""
TEMP_JAR_DIR=""
WSM_TEMP_DIR=~/.wfdb-swig-matlab-tmp
WSM_FINAL_DIR=~/.wfdb-swig-matlab
WSM_VERSION=0.0.1


#
#
#
CURL=$(which curl 2>/dev/null || echo)
JAR=$(which jar 2>/dev/null || echo)
UUIDGEN=$(which uuidgen 2>/dev/null || echo)
WGET=$(which wget 2>/dev/null || echo)


#
#
#
INSTALL_GLOBALLY=""
JAR_NAME=""
PRINT_HELP=""
SOURCE_PATH=$(pwd)
SU_METHOD=sudo


#
#
#
detailed_usage_msg()
{
    echo "Try \`$0 -h' to see detailed usage." 1>&2
}


#
# $1 is the location of a matlab binary (may be a symlink)
#
# if OK, return 0, and return the result into stdout;
# on error, return 1, and return the error string into stdout
#
find_default_matlab_path()
{
    matlab_bin="$1"
    if [ -z "$matlab_bin" ]
    then
        echo "*** Error: No default MATLAB found." 1>&2
        return 1
    fi

    matlab=$("$matlab_bin" -e | sed -n 's/^MATLAB=\(.*\)$/\1/p')
    if [ -z "$matlab" ]
    then
        echo "*** Error: No MATLAB env. var. for default MATLAB." 1>&2
        return 1
    fi

    echo "$matlab"
    return 0
}


#
# $1 is the url,
# $2 is the location to put the file
#
http_get()
{
    if [ ! -z "$WGET" ]
    then
        wget -O "$2" "$1"
    elif [ ! -z "$CURL" ]
    then
        curl -o "$2" "$1"
    else
        echo "*** Error: neither wget nor curl available; can't get $1." 1>&2
        return 1
    fi
}


#
# $1 is the OS name
# $2 is the arch name
#
is_valid_kernel_and_arch()
{
    if [ "$#" -ne "2" ]
    then
        echo "*** Error: is_valid_kernel_and_arch() used incorrectly;"
        echo "           expected 2 args., but passed $#."
        echo "           Exiting."
        exit 1
    fi

    case "$1" in
        "Linux" )
            case "$2" in
                "i386" )
                    return 0
                    ;;
                "amd64" )
                    return 0
                    ;;
                * )
                    echo "*** Error: Unsupported architecture for operating system $1: $2" 1>&2
                    return 1
                    ;;
            esac
            ;;
        "Darwin" )
            case "$2" in
                "ppc" )
                    return 0
                    ;;
                * )
                    echo "*** Error: Unsupported architecture for operating system $1: $2" 1>&2
                    return 1
                    ;;
            esac
            ;;
        * )
            echo "*** Error: Unsupported OS: $1" 1>&2
            return 1
            ;;
    esac
}


#
# $1 is the kernel name as printed by uname
# the result is what i've been calling it elsewhere
#
kernel_to_os()
{
    case "$1" in
        "Linux" )
            echo "linux"
            return 0
            ;;
        "Darwin" )
            echo "macosx"
            return 0
            ;;
        * )
            echo "*** Error: Unknown kernel name: $1" 1>&2
            return 1
    esac
}


#
#
#
make_uuid()
{
    if [ ! -z "$UUIDGEN" ]
    then
        "$UUIDGEN"
    else
        echo "00000000-0000-0000-0000-000000000000"
    fi
}


#
#
#
on_err_exit()
{
    echo "Exiting."
    exit 1
}


#
# FIXME: should use an associative array for the USED_? vars.
#
parse_args()
{
    local USED_J=""
    local USED_M=""
    local USED_P=""
    local USED_S=""

    while [ "$#" -ne "0" ]
    do
        case "$1" in
            # FIXME: re-launch as root, or at least warn about not being root
            "-g" )
                INSTALL_GLOBALLY=1
                ;;
            "-h" )
                PRINT_HELP=true
                ;;
            "-j" )
                shift
                if [ ! -z "$USED_J" ]
                then
                    echo "*** Error: -j used more than once." 1>&2
                    detailed_usage_msg
                    return 1
                elif [ -z "$1" ]
                then
                    echo "*** Error: -j used without a JAR_NAME." 1>&2
                    detailed_usage_msg
                    return 1
                else
                    JAR_NAME="$1"
                    USED_J=1
                fi
                ;;
            "-m" )
                shift
                if [ ! -z "$USED_M" ]
                then
                    echo "*** Error: -m used more than once." 1>&2
                    detailed_usage_msg
                    return 1
                elif [ -z "$1" ]
                then
                    echo "*** Error: -m used without a MATLAB_PATH." 1>&2
                    detailed_usage_msg
                    return 1
                else
                    MATLAB="$1"
                    USED_M=1
                fi
                ;;
            "-p" )
                shift
                if [ ! -z "$USED_P" ]
                then
                    echo "*** Error: -p used more than once." 1>&2
                    detailed_usage_msg
                    return 1
                elif [ -z "$1" ]
                then
                    echo "*** Error: -p used without a PATH." 1>&2
                    detailed_usage_msg
                    return 1
                else
                    SOURCE_PATH="$1"
                    USED_P=1
                fi
                ;;
            "-s" )
                shift
                if [ ! -z "$USED_S" ]
                then
                    echo "*** Error: -s used more than once." 1>&2
                    detailed_usage_msg
                    return 1
                elif [ -z "$1" ]
                then
                    echo "*** Error: -s used without a METHOD." 1>&2
                    detailed_usage_msg
                    return 1
                else
                    if [ "$1" != "sudo" ] && [ "$1" != "su" ]
                    then
                        echo "*** Error: -s used with invalid method." 1>&2
                        detailed_usage_msg
                        return 1
                    else
                        SU_METHOD="$1"
                        USED_S=1
                    fi
                fi
                ;;
            * )
                echo "*** Error: invalid argument: $1" 1>&2
                detailed_usage_msg
                return 1
                ;;
        esac
        shift
    done
}


#
#
#
print_help()
{
    echo "Usage: $0 [OPTION] ..."
    echo "Install WFDB Tools for MATLAB."
    echo
    echo "All arguments are optional:"
    echo "  -g                 install globally, i.e. in one location"
    echo "                     accessible to all users; must be run"
    echo "                     as superuser (default: no)"
    echo "  -h                 print this usage information"
    echo "  -j JAR_NAME        use a jar program called JAR_NAME;"
    echo "                     on some systems, it is called \"gjar\" or"
    echo "                     \"fastjar\" (default: \"jar\")"
    echo "  -m MATLAB_PATH     use the Matlab installation in MATLAB_PATH"
    echo "                     (default: first executable matlab in your \$PATH)"
    echo "  -p PATH            install from the source path PATH,"
    echo "                     which is either a directory in the"
    echo "                     local file system or a URL starting"
    echo "                     with \"http://\" (default: current working dir.)"
    echo "  -s METHOD          if superuser access is necessary, use"
    echo "                     su if METHOD is \"su\" and sudo if \"sudo\""
    echo "                     (default: \"sudo\")"
    return 0
}


#
# $1 should be the prompt to display;
# $2 should be a string of characters that don't need
#   to be escaped in a regular expression
#
# returns result into $RESULT
#
# FIXMEFIXME: have DEFAULTS!!!
# FIXME: change this to use stdout
#
read_char_prompt()
{
    if [ "$#" -ne "2" ]
    then
        echo "*** Error: read_char_prompt() used incorrectly;"
        echo "           expected 2 args., but passed $#."
        echo "           Exiting."
        exit 1
    fi

    prompt="$1"
    chars_allowed="$2"

    while [ true ]
    do
        echo -n "$prompt [$chars_allowed] "
        read v
        v=$(echo "$v" | sed -n "/^[$chars_allowed]\$/p")
        if [ ! -z "$v" ]
        then
            RESULT="$v"
            return 0
        fi
    done
}


#
# $1 should be the prompt to display;
# $2 should be the default choice
#
# returns result into $RESULT
# does not allow empty string to be returned
#
# FIXME: change this to use stdout
#
read_string_prompt()
{
    if [ "$#" -ne "2" ]
    then
        echo "*** Error: read_string_prompt() used incorrectly;"
        echo "           expected 2 args., but passed $#."
        echo "           Exiting."
        exit 1
    fi

    prompt="$1"
    default="$2"

    while [ true ]
    do
        echo -n "$prompt [default: $default] "
        read v
        if [ -z "$v" ]
        then
            v="$default"
        fi
          
        if [ ! -z "$v" ]
        then
            RESULT="$v"
            return 0
        fi
    done
}


#
#
#
run_su()
{
    case "$SU_METHOD" in
        "su" )
            su -c "$*"
            return "$?"
            ;;
        "sudo" )
            sudo $*
            return "$?"
            ;;
    esac
}


#
# if any individual command fails, exit
#
trap on_err_exit ERR


#
#
#
parse_args $*
if [ ! -z "$PRINT_HELP" ]
then
    print_help
    exit 0
fi


#
# prompt for final dir. if installing globally
#
if [ ! -z "$INSTALL_GLOBALLY" ]
then
    if [ $(id -u) -ne "0" ]
    then
        echo "*** You've chosen global installation; please restart $0 as superuser."
        exit 1
    fi

    read_string_prompt "Where would you like to put the wfdb-swig-matlab directory?" \
        "/usr/local/wfdb-swig-matlab"
    WSM_FINAL_DIR=$RESULT
    echo "OK, I'll install to $WSM_FINAL_DIR."
fi


#
#
#
if [ ! -z "$JAR_NAME" ]
then
    JAR=$(which "$JAR_NAME" 2>/dev/null || echo)
fi

if [ -z "$JAR" ]
then
    echo "*** Error: No jar program found. Please put it in your path,"
    echo "           or use -j to specify an alternate name (e.g. gjar or fastjar.)"
    exit 1
fi


#
# find matlab
#
if [ -z "$MATLAB" ]
then
    echo
    echo "Looking for MATLAB ..."
    MATLABBIN=$(which matlab 2>/dev/null || echo)
    MATLAB=$(find_default_matlab_path "$MATLABBIN")
    echo "Found MATLAB in $MATLAB"
else
    MATLABBIN="$MATLAB/bin/matlab"
    if [ ! -e "$MATLABBIN" ]
    then
        echo "*** Error: I expected a Matlab executable at $MATLABBIN, but it wasn't there." 1>&2
        exit 1
    fi
fi

# FIXME: is this strictly necessary?
for pf in classpath.txt librarypath.txt
do
    if [ ! -e "$MATLAB/toolbox/local/$pf" ]
    then
        echo "*** Error: I expected $MATLAB/toolbox/local/$pf to exist, but it doesn't." 1>&2
        exit 1
    fi
done


#
# get matlab version and jvm arch.
# FIXME: this is ever so slightly brittle; i preferred keeping the
#        version and arch. on separate lines, but matlab is not
#        consistent about it what it prints before/after my fprintf()
#
echo "Asking MATLAB for version and JVM arch. information ..."
VERSION_AND_ARCH=$("$MATLABBIN" -nodesktop -nodisplay -nosplash \
      -r "nl=['\\','n'];fprintf(1,['%s vvvvv %s',nl], \
                                version, \
                                char(java.lang.System.getProperty('os.arch')));exit;"\
        | grep ".* vvvvv .*")

OLDIFS="$IFS"
IFS=''
MATLAB_VERSION=$(echo "$VERSION_AND_ARCH" | sed -n -e 's/\(.*\) vvvvv \(.*\)/\1/p')
ARCH=$(echo "$VERSION_AND_ARCH" | sed -n -e 's/\(.*\) vvvvv \(.*\)/\2/p')
IFS="$OLDIFS"

echo "This is MATLAB version $MATLAB_VERSION, with JVM arch. $ARCH"


#
#
#
KERNEL=$(uname -s)
is_valid_kernel_and_arch "$KERNEL" "$ARCH"
OS=$(kernel_to_os "$KERNEL")


#
# decide on a temp. dir.
#
while [ -e "$WSM_TEMP_DIR" ]
do
    echo
    # FIXME: break this up into separate lines
    read_char_prompt \
      "I want to use $WSM_TEMP_DIR but it already exists; can I remove it?" \
      "yn"

    if [ "$RESULT" = "y" ]
    then
        { rm -rf "$WSM_TEMP_DIR"; } || \
        { echo "Unable to remove $WSM_TEMP_DIR -- exiting."; exit 1; }
    else
        # "$RESULT" = "n"
        read_string_prompt \
          "OK, what should I use as a temp. directory?" \
          ""
        WSM_TEMP_DIR="$RESULT"
    fi
done
mkdir "$WSM_TEMP_DIR"


#
# verify that the given path contains the right files
#
if [[ "$SOURCE_PATH" == "http://"* ]]
then
    BASE_URL="$SOURCE_PATH"
    TEMP_JAR_DIR="$WSM_TEMP_DIR/tmp"
    SOURCE_PATH="$TEMP_JAR_DIR"
    mkdir "$SOURCE_PATH"
    http_get "$BASE_URL/wfdb-swig.jar" "$SOURCE_PATH/wfdb-swig.jar"
    http_get "$BASE_URL/wsm-matlab-code.jar" "$SOURCE_PATH/wsm-matlab-code.jar"
    http_get "$BASE_URL/wfdb-swig-matlab-$OS.jar" \
             "$SOURCE_PATH/wfdb-swig-matlab-$OS.jar"
    http_get "$BASE_URL/wfdb-nativelibs-$OS-$ARCH.jar" \
             "$SOURCE_PATH/wfdb-nativelibs-$OS-$ARCH.jar"
fi


#
# check that all the files are there
#
FILES_MISSING=""
for f in wfdb-swig.jar wsm-matlab-code.jar \
         "wfdb-swig-matlab-$OS.jar" "wfdb-nativelibs-$OS-$ARCH.jar"
do
    if [ ! -e "$SOURCE_PATH/$f" ]
    then
        echo "*** Error: $SOURCE_PATH/$f should exist, but it doesn't." 1>&2
#         if [[ "$SOURCE_PATH" != "http://"* ]]
#         then
#             echo "           (Did you not run make first?)" 1>&2
#         fi
        FILES_MISSING=1
    fi
done

if [ ! -z "$FILES_MISSING" ]
then
    if [[ "$SOURCE_PATH" == "http://"* ]]
    then
        exit 1
    else
        echo
        read_char_prompt "Should I try running make in ${SOURCE_PATH}?" "yn"
        if [ "$RESULT" = "n" ]
        then
            echo "OK, giving up."
            exit 1
        fi

        (pushd $(pwd) >/dev/null && \
         cd "$SOURCE_PATH" && \
         make && \
         popd >/dev/null)

        # FIXME: a bit of redundancy here, with the above check
        for f in wfdb-swig.jar wsm-matlab-code.jar \
                 "wfdb-swig-matlab-$OS.jar" "wfdb-nativelibs-$OS-$ARCH.jar"
        do
            if [ ! -e "$SOURCE_PATH/$f" ]
            then
                echo "*** Error: $SOURCE_PATH/$f still doesn't exist;" 1>&2
                echo "           something has gone very wrong." 1>&2
                exit 1
            fi
        done
    fi
fi



#
# copy the files into ~/.wfdb-swig-matlab
#
echo
echo "OK, copying files into temp. dir. ${WSM_TEMP_DIR} ..."
mkdir "$WSM_TEMP_DIR/$WSM_VERSION"
mkdir "$WSM_TEMP_DIR/$WSM_VERSION/util"
mkdir "$WSM_TEMP_DIR/$WSM_VERSION/lib-$ARCH"

# unjar
(pushd $(pwd) >/dev/null && \
 cd "$WSM_TEMP_DIR/$WSM_VERSION/" && \
 "$JAR" xvf "$SOURCE_PATH/wfdb-swig.jar" wfdb.jar && \
 "$JAR" xvf "$SOURCE_PATH/wfdb-swig-matlab-$OS.jar" wsm-classes.jar && \
 "$JAR" xvf "$SOURCE_PATH/wsm-matlab-code.jar" && \
 cd "lib-$ARCH" && \
 "$JAR" xvf "$SOURCE_PATH/wfdb-nativelibs-$OS-$ARCH.jar" && \
 popd >/dev/null)

# remove the extraneous stuff that got unjarred
rm "$WSM_TEMP_DIR/$WSM_VERSION/matlab-files.txt"
rm -r "$WSM_TEMP_DIR/$WSM_VERSION/META-INF"
for f in "$WSM_TEMP_DIR/$WSM_VERSION/lib-$ARCH/"*
do
    if [[ "$f" != *"-$ARCH" ]]
    then
        rm -r "$f"
    fi
done

# rename the shared libs.
for lf in "$WSM_TEMP_DIR/$WSM_VERSION/lib-$ARCH/"*"-$ARCH"
do
    mv "$lf" "$WSM_TEMP_DIR/$WSM_VERSION/lib-$ARCH/$(basename $lf -$ARCH)"
done

# check that these jars exist
if [ ! -e "$WSM_TEMP_DIR/$WSM_VERSION/wfdb.jar" ]
then
    echo "*** Error: $SOURCE_PATH/wfdb-swig.jar does not appear to contain wfdb.jar" 1>&2
    exit 1
fi
if [ ! -e "$WSM_TEMP_DIR/$WSM_VERSION/wsm-classes.jar" ]
then
    echo "*** Error: $SOURCE_PATH/wfdb-swig-matlab-$OS.jar does not appear to contain wsm-classes.jar" 1>&2
    exit 1
fi
# FIXME: should probably check that the other files are there, too


#
# if we downloaded from the web site, remove the temp. dir.
#
if [ ! -z "$TEMP_JAR_DIR" ]
then
    rm -r "$TEMP_JAR_DIR"
fi


#
# update classpath.txt and librarypath.txt, into temp. files
# FIXME: why doesn't [[:graph:]]+ work???
#
SED_PROG='1{h;$p;d;};x;G;/^# DO NOT EDIT: WSM v\. [[:graph:]][[:graph:]]* [[:graph:]][[:graph:]]* \(.*\)\n\1$/!{P;$!d;g;p;};$!{n;h;${g;p;};d;}'
sed -n -e "$SED_PROG" "$MATLAB/toolbox/local/classpath.txt" \
    > "$WSM_TEMP_DIR/classpath.txt"
sed -n -e "$SED_PROG" "$MATLAB/toolbox/local/librarypath.txt" \
    > "$WSM_TEMP_DIR/librarypath.txt"

WSM_CODE_DIR="$WSM_FINAL_DIR/$WSM_VERSION"
WSM_LIB_DIR="$WSM_CODE_DIR/lib-$ARCH"
NEW_UUID=$(make_uuid)
WSM_COMMENT_PREFIX="# DO NOT EDIT: WSM v. $WSM_VERSION $NEW_UUID"

echo "$WSM_COMMENT_PREFIX $WSM_LIB_DIR" >> "$WSM_TEMP_DIR/librarypath.txt"
echo "$WSM_LIB_DIR" >> "$WSM_TEMP_DIR/librarypath.txt"

echo "$WSM_COMMENT_PREFIX $WSM_CODE_DIR/wfdb.jar" >> "$WSM_TEMP_DIR/classpath.txt"
echo "$WSM_CODE_DIR/wfdb.jar" >> "$WSM_TEMP_DIR/classpath.txt"
echo "$WSM_COMMENT_PREFIX $WSM_CODE_DIR/wsm-classes.jar" \
    >> "$WSM_TEMP_DIR/classpath.txt"
echo "$WSM_CODE_DIR/wsm-classes.jar" >> "$WSM_TEMP_DIR/classpath.txt"


#
# write config file
#
WSM_CONF_FILE="$WSM_TEMP_DIR/$WSM_VERSION.conf"
touch "$WSM_CONF_FILE"
echo "$NEW_UUID" >> "$WSM_CONF_FILE"
echo "$MATLAB_VERSION" >> "$WSM_CONF_FILE"
echo "$MATLAB" >> "$WSM_CONF_FILE"


#
# create backups of classpath.txt and librarypath.txt
#
echo
echo "Just so you know, I'm going to put backups of classpath.txt and"
echo "librarypath.txt into $WSM_FINAL_DIR, before I change them."
cp "$MATLAB/toolbox/local/classpath.txt" "$WSM_TEMP_DIR/classpath.txt.backup"
cp "$MATLAB/toolbox/local/librarypath.txt" "$WSM_TEMP_DIR/librarypath.txt.backup"


#
# move temp. dir. to final dir.
#
if [ -e "$WSM_FINAL_DIR" ]
then
    echo
    read_char_prompt "$WSM_FINAL_DIR already exists; can I replace it? \
(If not, installation will abort.)" \
        "yn"
    if [ "$RESULT" = "n" ]
    then
        echo "OK, I'm aborting. If you want, you can finish manually:"
        echo "copy $WSM_TEMP_DIR/classpath.txt into $MATLAB/toolbox/local/ and"
        echo "copy $WSM_TEMP_DIR/librarypath.txt into $MATLAB/toolbox/local/ and then"
        echo "move $WSM_TEMP_DIR to $WSM_FINAL_DIR."
        echo "Everything should work after that!"
        exit 1
    fi
    
    rm -rf "$WSM_FINAL_DIR"
fi

echo
echo "OK, moving $WSM_TEMP_DIR to $WSM_FINAL_DIR ..."
if [ ! -e "$(dirname "$WSM_FINAL_DIR")" ]
then
    mkdir -p "$(dirname "$WSM_FINAL_DIR")"
fi
mv "$WSM_TEMP_DIR" "$WSM_FINAL_DIR"


#
# move temp. classpath.txt and librarypath.txt into $MATLAB/toolbox/local
# FIXME: why can't i figure out how to run run_su on multiple commands??
#
echo
echo "OK, now I have to copy classpath.txt and librarypath.txt"
echo "  into $MATLAB/toolbox/local/."
if [ -z "$INSTALL_GLOBALLY" ]
then
    echo "  You may be prompted for a password (maybe more than once.)"
fi
for pf in classpath.txt librarypath.txt
do
    { cp "$WSM_FINAL_DIR/$pf" \
         "$MATLAB/toolbox/local/$pf" 2>/dev/null; } || \
        { [ -z "$INSTALL_GLOBALLY" ] && \
            { run_su cp "$WSM_FINAL_DIR/$pf" \
                        "$MATLAB/toolbox/local/$pf"; }; }
    rm -f "$WSM_FINAL_DIR/$pf"
done


#
# FIXME: need to escape the paths!
# FIXMEFIXME: use the userpath!
#
echo
echo "OK, updating MATLAB path ..."
if [ -z "$INSTALL_GLOBALLY" ]
then
    "$MATLABBIN" -nodesktop -nodisplay -nosplash \
        -r "addpath('$WSM_FINAL_DIR/$WSM_VERSION'); \
            addpath('$WSM_FINAL_DIR/$WSM_VERSION/util'); \
            mkdir('$HOME','matlab'); \
            path2rc('$HOME/matlab/pathdef.m'); \
            exit;" >/dev/null
else
    "$MATLABBIN" -nodesktop -nodisplay -nosplash \
        -r "addpath('$WSM_FINAL_DIR/$WSM_VERSION'); \
            addpath('$WSM_FINAL_DIR/$WSM_VERSION/util'); \
            path2rc; \
            exit;" >/dev/null
fi


#
#
#
echo
echo "All done! Enjoy!"
echo