You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
679 lines
15 KiB
Bash
679 lines
15 KiB
Bash
#!/bin/bash
|
|
|
|
# This program contains parts of narwhal's "sea" program,
|
|
# as well as bits borrowed from Tim Caswell's "nvm"
|
|
|
|
# nave install <version>
|
|
# Fetch the version of node and install it in nave's folder.
|
|
|
|
# nave use <version>
|
|
# Install the <version> if it isn't already, and then start
|
|
# a subshell with that version's folder at the start of the
|
|
# $PATH
|
|
|
|
# nave use <version> program.js
|
|
# Like "nave use", but have the subshell start the program.js
|
|
# immediately.
|
|
|
|
# When told to use a version:
|
|
# Ensure that the version exists, install it, and
|
|
# then add its prefix to the PATH, and start a subshell.
|
|
|
|
if [ "$NAVE_DEBUG" != "" ]; then
|
|
set -x
|
|
fi
|
|
|
|
if [ -z "$BASH" ]; then
|
|
cat >&2 <<MSG
|
|
Nave is a bash program, and must be run with bash.
|
|
MSG
|
|
exit 1
|
|
fi
|
|
|
|
shell=`basename "$SHELL"`
|
|
case "$shell" in
|
|
bash) ;;
|
|
zsh) ;;
|
|
*)
|
|
echo "Nave only supports zsh and bash shells." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Use fancy pants globs
|
|
shopt -s extglob
|
|
|
|
# Try to figure out the os and arch for binary fetching
|
|
uname="$(uname -a)"
|
|
os=
|
|
arch=x86
|
|
case "$uname" in
|
|
Linux\ *) os=linux ;;
|
|
Darwin\ *) os=darwin ;;
|
|
SunOS\ *) os=sunos ;;
|
|
esac
|
|
case "$uname" in
|
|
*x86_64*) arch=x64 ;;
|
|
esac
|
|
|
|
tar=${TAR-tar}
|
|
|
|
main () {
|
|
local SELF_PATH DIR SYM
|
|
# get the absolute path of the executable
|
|
SELF_PATH="$0"
|
|
if [ "${SELF_PATH:0:1}" != "." ] && [ "${SELF_PATH:0:1}" != "/" ]; then
|
|
SELF_PATH=./"$SELF_PATH"
|
|
fi
|
|
SELF_PATH=$( cd -P -- "$(dirname -- "$SELF_PATH")" \
|
|
&& pwd -P \
|
|
) && SELF_PATH=$SELF_PATH/$(basename -- "$0")
|
|
|
|
# resolve symlinks
|
|
while [ -h "$SELF_PATH" ]; do
|
|
DIR=$(dirname -- "$SELF_PATH")
|
|
SYM=$(readlink -- "$SELF_PATH")
|
|
SELF_PATH=$( cd -- "$DIR" \
|
|
&& cd -- $(dirname -- "$SYM") \
|
|
&& pwd \
|
|
)/$(basename -- "$SYM")
|
|
done
|
|
|
|
if ! [ -d "$NAVE_DIR" ]; then
|
|
if [ -d "$HOME" ]; then
|
|
NAVE_DIR="$HOME"/.nave
|
|
else
|
|
NAVE_DIR=/usr/local/lib/nave
|
|
fi
|
|
fi
|
|
if ! [ -d "$NAVE_DIR" ] && ! mkdir -p -- "$NAVE_DIR"; then
|
|
NAVE_DIR="$(dirname -- "$SELF_PATH")"
|
|
fi
|
|
|
|
# set up the naverc init file.
|
|
# For zsh compatibility, we name this file ".zshenv" instead of
|
|
# the more reasonable "naverc" name.
|
|
# Important! Update this number any time the init content is changed.
|
|
local rcversion="#3"
|
|
local rcfile="$NAVE_DIR/.zshenv"
|
|
if ! [ -f "$rcfile" ] \
|
|
|| [ "$(head -n1 "$rcfile")" != "$rcversion" ]; then
|
|
|
|
cat > "$rcfile" <<RC
|
|
$rcversion
|
|
[ "\$NAVE_DEBUG" != "" ] && set -x || true
|
|
if [ "\$BASH" != "" ]; then
|
|
if [ "\$NAVE_LOGIN" != "" ]; then
|
|
[ -f ~/.bash_profile ] && . ~/.bash_profile || true
|
|
[ -f ~/.bash_login ] && . ~/.bash_login || true
|
|
[ -f ~/.profile ] && . ~/.profile || true
|
|
else
|
|
[ -f ~/.bashrc ] && . ~/.bashrc || true
|
|
fi
|
|
else
|
|
[ -f ~/.zshenv ] && . ~/.zshenv || true
|
|
export DISABLE_AUTO_UPDATE=true
|
|
if [ "\$NAVE_LOGIN" != "" ]; then
|
|
[ -f ~/.zprofile ] && . ~/.zprofile || true
|
|
[ -f ~/.zshrc ] && . ~/.zshrc || true
|
|
[ -f ~/.zlogin ] && . ~/.zlogin || true
|
|
else
|
|
[ -f ~/.zshrc ] && . ~/.zshrc || true
|
|
fi
|
|
fi
|
|
unset ZDOTDIR
|
|
export PATH=\$NAVEPATH:\$PATH
|
|
[ -f ~/.naverc ] && . ~/.naverc || true
|
|
RC
|
|
|
|
cat > "$NAVE_DIR/.zlogout" <<RC
|
|
[ -f ~/.zlogout ] && . ~/.zlogout || true
|
|
RC
|
|
|
|
fi
|
|
|
|
# couldn't write file
|
|
if ! [ -f "$rcfile" ] || [ "$(head -n1 "$rcfile")" != "$rcversion" ]; then
|
|
fail "Nave dir $NAVE_DIR is not writable."
|
|
fi
|
|
|
|
export NAVE_DIR
|
|
export NAVE_SRC="$NAVE_DIR/src"
|
|
export NAVE_ROOT="$NAVE_DIR/installed"
|
|
ensure_dir "$NAVE_SRC"
|
|
ensure_dir "$NAVE_ROOT"
|
|
|
|
local cmd="$1"
|
|
shift
|
|
case $cmd in
|
|
ls-remote | ls-all)
|
|
cmd="nave_${cmd/-/_}"
|
|
;;
|
|
# use)
|
|
# cmd="nave_named"
|
|
# ;;
|
|
install | fetch | use | clean | test | named | \
|
|
ls | uninstall | usemain | latest | stable | has | installed )
|
|
cmd="nave_$cmd"
|
|
;;
|
|
* )
|
|
cmd="nave_help"
|
|
;;
|
|
esac
|
|
$cmd "$@"
|
|
local ret=$?
|
|
if [ $ret -eq 0 ]; then
|
|
exit 0
|
|
else
|
|
echo "failed with code=$ret" >&2
|
|
exit $ret
|
|
fi
|
|
}
|
|
|
|
function enquote_all () {
|
|
local ARG ARGS
|
|
ARGS=""
|
|
for ARG in "$@"; do
|
|
[ -n "$ARGS" ] && ARGS="$ARGS "
|
|
ARGS="$ARGS'""$( echo " $ARG" \
|
|
| cut -c 2- \
|
|
| sed 's/'"'"'/'"'"'"'"'"'"'"'"'/g' \
|
|
)""'"
|
|
done
|
|
echo "$ARGS"
|
|
}
|
|
|
|
ensure_dir () {
|
|
if ! [ -d "$1" ]; then
|
|
mkdir -p -- "$1" || fail "couldn't create $1"
|
|
fi
|
|
}
|
|
|
|
remove_dir () {
|
|
if [ -d "$1" ]; then
|
|
rm -rf -- "$1" || fail "Could not remove $1"
|
|
fi
|
|
}
|
|
|
|
fail () {
|
|
echo "$@" >&2
|
|
exit 1
|
|
}
|
|
|
|
nave_fetch () {
|
|
local version=$(ver "$1")
|
|
if nave_has "$version"; then
|
|
echo "already fetched $version" >&2
|
|
return 0
|
|
fi
|
|
|
|
local src="$NAVE_SRC/$version"
|
|
remove_dir "$src"
|
|
ensure_dir "$src"
|
|
|
|
local url
|
|
local urls=(
|
|
"http://nodejs.org/dist/v$version/node-v$version.tar.gz"
|
|
"http://nodejs.org/dist/node-v$version.tar.gz"
|
|
"http://nodejs.org/dist/node-$version.tar.gz"
|
|
)
|
|
for url in "${urls[@]}"; do
|
|
curl -#Lf "$url" > "$src".tgz
|
|
if [ $? -eq 0 ]; then
|
|
$tar xzf "$src".tgz -C "$src" --strip-components=1
|
|
if [ $? -eq 0 ]; then
|
|
echo "fetched from $url" >&2
|
|
return 0
|
|
fi
|
|
fi
|
|
done
|
|
|
|
rm "$src".tgz
|
|
remove_dir "$src"
|
|
echo "Couldn't fetch $version" >&2
|
|
return 1
|
|
}
|
|
|
|
build () {
|
|
local version="$1"
|
|
|
|
# shortcut - try the binary if possible.
|
|
if [ -n "$os" ]; then
|
|
local binavail
|
|
# binaries started with node 0.8.6
|
|
case "$version" in
|
|
0.8.[012345]) binavail=0 ;;
|
|
0.[1234567]) binavail=0 ;;
|
|
*) binavail=1 ;;
|
|
esac
|
|
if [ $binavail -eq 1 ]; then
|
|
local t="$version-$os-$arch"
|
|
local url="http://nodejs.org/dist/v$version/node-v${t}.tar.gz"
|
|
local tgz="$NAVE_SRC/$t.tgz"
|
|
curl -#Lf "$url" > "$tgz"
|
|
if [ $? -ne 0 ]; then
|
|
# binary download failed. oh well. cleanup, and proceed.
|
|
rm "$tgz"
|
|
echo "Binary download failed, trying source." >&2
|
|
else
|
|
# unpack straight into the build target.
|
|
$tar xzf "$tgz" -C "$2" --strip-components 1
|
|
if [ $? -ne 0 ]; then
|
|
rm "$tgz"
|
|
nave_uninstall "$version"
|
|
echo "Binary unpack failed, trying source." >&2
|
|
fi
|
|
# it worked!
|
|
echo "installed from binary" >&2
|
|
return 0
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
nave_fetch "$version"
|
|
if [ $? != 0 ]; then
|
|
# fetch failed, don't continue and try to build it.
|
|
return 1
|
|
fi
|
|
|
|
local src="$NAVE_SRC/$version"
|
|
local jobs=$NAVE_JOBS
|
|
jobs=${jobs:-$JOBS}
|
|
jobs=${jobs:-$(sysctl -n hw.ncpu)}
|
|
jobs=${jobs:-2}
|
|
|
|
( cd -- "$src"
|
|
[ -f ~/.naverc ] && . ~/.naverc || true
|
|
if [ "$NAVE_CONFIG" == "" ]; then
|
|
NAVE_CONFIG=()
|
|
fi
|
|
JOBS=$jobs ./configure "${NAVE_CONFIG[@]}" --prefix="$2" \
|
|
|| fail "Failed to configure $version"
|
|
JOBS=$jobs make -j$jobs \
|
|
|| fail "Failed to make $version"
|
|
make install || fail "Failed to install $version"
|
|
) || fail "fail"
|
|
return $?
|
|
}
|
|
|
|
nave_usemain () {
|
|
if [ ${NAVELVL-0} -gt 0 ]; then
|
|
fail "Can't usemain inside a nave subshell. Exit to main shell."
|
|
fi
|
|
local version=$(ver "$1")
|
|
local current=$(node -v || true)
|
|
local wn=$(which node || true)
|
|
local prefix="/usr/local"
|
|
if [ "x$wn" != "x" ]; then
|
|
prefix="${wn/\/bin\/node/}"
|
|
if [ "x$prefix" == "x" ]; then
|
|
prefix="/usr/local"
|
|
fi
|
|
fi
|
|
current="${current/v/}"
|
|
if [ "$current" == "$version" ]; then
|
|
echo "$version already installed" >&2
|
|
return 0
|
|
fi
|
|
|
|
build "$version" "$prefix"
|
|
}
|
|
|
|
nave_install () {
|
|
local version=$(ver "$1")
|
|
if nave_installed "$version"; then
|
|
echo "Already installed: $version" >&2
|
|
return 0
|
|
fi
|
|
local install="$NAVE_ROOT/$version"
|
|
ensure_dir "$install"
|
|
|
|
build "$version" "$install"
|
|
local ret=$?
|
|
if [ $ret -ne 0 ]; then
|
|
remove_dir "$install"
|
|
return $ret
|
|
fi
|
|
}
|
|
|
|
nave_test () {
|
|
local version=$(ver "$1")
|
|
nave_fetch "$version"
|
|
local src="$NAVE_SRC/$version"
|
|
( cd -- "$src"
|
|
[ -f ~/.naverc ] && . ~/.naverc || true
|
|
if [ "$NAVE_CONFIG" == "" ]; then
|
|
NAVE_CONFIG=()
|
|
fi
|
|
./configure "${NAVE_CONFIG[@]}" || fail "failed to ./configure"
|
|
make test-all || fail "Failed tests"
|
|
) || fail "failed"
|
|
}
|
|
|
|
nave_ls () {
|
|
ls -- $NAVE_SRC | version_list "src" \
|
|
&& ls -- $NAVE_ROOT | version_list "installed" \
|
|
&& nave_ls_named \
|
|
|| return 1
|
|
}
|
|
|
|
nave_ls_remote () {
|
|
curl -s http://nodejs.org/dist/ \
|
|
| version_list "remote" \
|
|
|| return 1
|
|
}
|
|
|
|
nave_ls_named () {
|
|
echo "named:"
|
|
ls -- "$NAVE_ROOT" \
|
|
| egrep -v '[0-9]+\.[0-9]+\.[0-9]+' \
|
|
| sort \
|
|
| while read name; do
|
|
echo "$name: $(ver $($NAVE_ROOT/$name/bin/node -v 2>/dev/null))"
|
|
done
|
|
}
|
|
|
|
nave_ls_all () {
|
|
nave_ls \
|
|
&& (echo ""; nave_ls_remote) \
|
|
|| return 1
|
|
}
|
|
|
|
ver () {
|
|
local version="$1"
|
|
local nonames="$2"
|
|
version="${version/v/}"
|
|
case $version in
|
|
latest | stable) nave_$version ;;
|
|
+([0-9])\.+([0-9])) nave_version_family "$version" ;;
|
|
+([0-9])\.+([0-9])\.+([0-9])) echo $version ;;
|
|
*) [ "$nonames" = "" ] && echo $version ;;
|
|
esac
|
|
}
|
|
|
|
nave_version_family () {
|
|
local family="$1"
|
|
family="${family/v/}"
|
|
curl -s http://nodejs.org/dist/ \
|
|
| egrep -o $family'\.[0-9]+' \
|
|
| sort -u -k 1,1n -k 2,2n -k 3,3n -t . \
|
|
| tail -n1
|
|
}
|
|
|
|
nave_latest () {
|
|
curl -s http://nodejs.org/dist/ \
|
|
| egrep -o '[0-9]+\.[0-9]+\.[0-9]+' \
|
|
| sort -u -k 1,1n -k 2,2n -k 3,3n -t . \
|
|
| tail -n1
|
|
}
|
|
|
|
nave_stable () {
|
|
curl -s http://nodejs.org/dist/ \
|
|
| egrep -o '[0-9]+\.[0-9]*[02468]\.[0-9]+' \
|
|
| sort -u -k 1,1n -k 2,2n -k 3,3n -t . \
|
|
| tail -n1
|
|
}
|
|
|
|
version_list_named () {
|
|
egrep -v '[0-9]+\.[0-9]+\.[0-9]+' \
|
|
| sort -u -k 1,1n -k 2,2n -k 3,3n -t . \
|
|
| organize_version_list \
|
|
|| return 1
|
|
}
|
|
|
|
version_list () {
|
|
echo "$1:"
|
|
egrep -o '[0-9]+\.[0-9]+\.[0-9]+' \
|
|
| sort -u -k 1,1n -k 2,2n -k 3,3n -t . \
|
|
| organize_version_list \
|
|
|| return 1
|
|
}
|
|
|
|
organize_version_list () {
|
|
local i=0
|
|
local v
|
|
while read v; do
|
|
if [ $i -eq 8 ]; then
|
|
i=0
|
|
echo "$v"
|
|
else
|
|
let 'i = i + 1'
|
|
echo -ne "$v\t"
|
|
fi
|
|
done
|
|
echo ""
|
|
[ $i -ne 0 ] && echo ""
|
|
return 0
|
|
}
|
|
|
|
nave_has () {
|
|
local version=$(ver "$1")
|
|
[ -x "$NAVE_SRC/$version/configure" ] || return 1
|
|
}
|
|
|
|
nave_installed () {
|
|
local version=$(ver "$1")
|
|
[ -x "$NAVE_ROOT/$version/bin/node" ] || return 1
|
|
}
|
|
|
|
nave_use () {
|
|
local version=$(ver "$1")
|
|
|
|
# if it's not a version number, then treat as a name.
|
|
case "$version" in
|
|
+([0-9])\.+([0-9])\.+([0-9])) ;;
|
|
*)
|
|
nave_named "$@"
|
|
return $?
|
|
;;
|
|
esac
|
|
|
|
if [ -z "$version" ]; then
|
|
fail "Must supply a version"
|
|
fi
|
|
|
|
if [ "$version" == "$NAVENAME" ]; then
|
|
echo "already using $version" >&2
|
|
if [ $# -gt 1 ]; then
|
|
shift
|
|
"$@"
|
|
fi
|
|
return $?
|
|
fi
|
|
|
|
nave_install "$version" || fail "failed to install $version"
|
|
local prefix="$NAVE_ROOT/$version"
|
|
local lvl=$[ ${NAVELVL-0} + 1 ]
|
|
echo "using $version" >&2
|
|
if [ $# -gt 1 ]; then
|
|
shift
|
|
nave_exec "$lvl" "$version" "$version" "$prefix" "$@"
|
|
return $?
|
|
else
|
|
nave_login "$lvl" "$version" "$version" "$prefix"
|
|
return $?
|
|
fi
|
|
}
|
|
|
|
# internal
|
|
nave_exec () {
|
|
nave_run "exec" "$@"
|
|
return $?
|
|
}
|
|
|
|
nave_login () {
|
|
nave_run "login" "$@"
|
|
return $?
|
|
}
|
|
|
|
nave_run () {
|
|
local exec="$1"
|
|
shift
|
|
local lvl="$1"
|
|
shift
|
|
local name="$1"
|
|
shift
|
|
local version="$1"
|
|
shift
|
|
local prefix="$1"
|
|
shift
|
|
|
|
local bin="$prefix/bin"
|
|
local lib="$prefix/lib/node"
|
|
local man="$prefix/share/man"
|
|
ensure_dir "$bin"
|
|
ensure_dir "$lib"
|
|
ensure_dir "$man"
|
|
|
|
# now $@ is the command to run, or empty if it's not an exec.
|
|
local exit_code
|
|
local args=()
|
|
local isLogin
|
|
|
|
if [ "$exec" == "exec" ]; then
|
|
isLogin=""
|
|
# source the nave env file, then run the command.
|
|
args=("-c" ". $(enquote_all $NAVE_DIR/.zshenv); $(enquote_all "$@")")
|
|
elif [ "$shell" == "zsh" ]; then
|
|
isLogin="1"
|
|
# no need to set rcfile, since ZDOTDIR is set.
|
|
args=()
|
|
else
|
|
isLogin="1"
|
|
# bash, use --rcfile argument
|
|
args=("--rcfile" "$NAVE_DIR/.zshenv")
|
|
fi
|
|
|
|
local nave="$version"
|
|
if [ "$version" != "$name" ]; then
|
|
nave="$name"-"$version"
|
|
fi
|
|
|
|
NAVELVL=$lvl \
|
|
NAVEPATH="$bin" \
|
|
NAVEVERSION="$version" \
|
|
NAVENAME="$name" \
|
|
NAVE="$nave" \
|
|
npm_config_binroot="$bin"\
|
|
npm_config_root="$lib" \
|
|
npm_config_manroot="$man" \
|
|
npm_config_prefix="$prefix" \
|
|
NODE_PATH="$lib" \
|
|
NAVE_LOGIN="$isLogin" \
|
|
NAVE_DIR="$NAVE_DIR" \
|
|
ZDOTDIR="$NAVE_DIR" \
|
|
"$SHELL" "${args[@]}"
|
|
|
|
exit_code=$?
|
|
hash -r
|
|
return $exit_code
|
|
}
|
|
|
|
nave_named () {
|
|
local name="$1"
|
|
shift
|
|
|
|
local version=$(ver "$1" NONAMES)
|
|
if [ "$version" != "" ]; then
|
|
shift
|
|
fi
|
|
|
|
add_named_env "$name" "$version" || fail "failed to create $name env"
|
|
|
|
if [ "$name" == "$NAVENAME" ] && [ "$version" == "$NAVEVERSION" ]; then
|
|
echo "already using $name" >&2
|
|
if [ $# -gt 0 ]; then
|
|
"$@"
|
|
fi
|
|
return $?
|
|
fi
|
|
|
|
if [ "$version" = "" ]; then
|
|
version="$(ver "$("$NAVE_ROOT/$name/bin/node" -v 2>/dev/null)")"
|
|
fi
|
|
|
|
local prefix="$NAVE_ROOT/$name"
|
|
|
|
local lvl=$[ ${NAVELVL-0} + 1 ]
|
|
# get the version
|
|
if [ $# -gt 0 ]; then
|
|
nave_exec "$lvl" "$name" "$version" "$prefix" "$@"
|
|
return $?
|
|
else
|
|
nave_login "$lvl" "$name" "$version" "$prefix"
|
|
return $?
|
|
fi
|
|
}
|
|
|
|
add_named_env () {
|
|
local name="$1"
|
|
local version="$2"
|
|
local cur="$(ver "$($NAVE_ROOT/$name/bin/node -v 2>/dev/null)" "NONAMES")"
|
|
|
|
if [ "$version" != "" ]; then
|
|
version="$(ver "$version" "NONAMES")"
|
|
else
|
|
version="$cur"
|
|
fi
|
|
|
|
if [ "$version" = "" ]; then
|
|
echo "What version of node?"
|
|
read -p "stable, latest, x.y, or x.y.z > " version
|
|
version=$(ver "$version")
|
|
fi
|
|
|
|
# if that version is already there, then nothing to do.
|
|
if [ "$cur" = "$version" ]; then
|
|
return 0
|
|
fi
|
|
|
|
echo "Creating new env named '$name' using node $version" >&2
|
|
|
|
nave_install "$version" || fail "failed to install $version"
|
|
ensure_dir "$NAVE_ROOT/$name/bin"
|
|
ensure_dir "$NAVE_ROOT/$name/lib/node"
|
|
ensure_dir "$NAVE_ROOT/$name/lib/node_modules"
|
|
ensure_dir "$NAVE_ROOT/$name/share/man"
|
|
|
|
ln -sf -- "$NAVE_ROOT/$version/bin/node" "$NAVE_ROOT/$name/bin/node"
|
|
ln -sf -- "$NAVE_ROOT/$version/bin/node-waf" "$NAVE_ROOT/$name/bin/node-waf"
|
|
}
|
|
|
|
nave_clean () {
|
|
rm -rf "$NAVE_SRC/$(ver "$1")" "$NAVE_SRC/$(ver "$1")".tgz "$NAVE_SRC/$(ver "$1")"-*.tgz
|
|
}
|
|
|
|
nave_uninstall () {
|
|
remove_dir "$NAVE_ROOT/$(ver "$1")"
|
|
}
|
|
|
|
nave_help () {
|
|
cat <<EOF
|
|
|
|
Usage: nave <cmd>
|
|
|
|
Commands:
|
|
|
|
install <version> Install the version passed (ex: 0.1.103)
|
|
use <version> Enter a subshell where <version> is being used
|
|
use <ver> <program> Enter a subshell, and run "<program>", then exit
|
|
use <name> <ver> Create a named env, using the specified version.
|
|
If the name already exists, but the version differs,
|
|
then it will update the link.
|
|
usemain <version> Install in /usr/local/bin (ie, use as your main nodejs)
|
|
clean <version> Delete the source code for <version>
|
|
uninstall <version> Delete the install for <version>
|
|
ls List versions currently installed
|
|
ls-remote List remote node versions
|
|
ls-all List remote and local node versions
|
|
latest Show the most recent dist version
|
|
help Output help information
|
|
|
|
<version> can be the string "latest" to get the latest distribution.
|
|
<version> can be the string "stable" to get the latest stable version.
|
|
|
|
EOF
|
|
}
|
|
|
|
main "$@"
|