From f1a54433a2fb62ec34e9a5756ac653af4fe6aec2 Mon Sep 17 00:00:00 2001 From: aarondill Date: Wed, 5 Jul 2023 12:48:58 -0500 Subject: [PATCH] Add support for options in install script This adds support for serveral new command line options (taken from the usage): "-b, --bin-dir" "Override the bin installation directory [default: ${_bin_dir}]" \ "-m, --man-dir" "Override the man installation directory [default: ${_man_dir}]" \ "-a, --arch" "Override the architecture identified by the installer [default: ${_arch}]" \ "-h, --help" "Display this help message" This also (coincidentally) allows for $BIN_DIR, $MAN_DIR, and $ARCH to be set to create default settings for these. These variables are *always* overwritten by the command line arguments. This diff is unusually large, as my formatter ([shfmt](https://github.com/mvdan/sh)) ran, reformatting the majority of the script. This also adds a few utilities to the script (`log`/`abort`) and modifies `err` to simply output to stderr. This allows for more fine grain control over script flow and output, without relying on `echo >&2`. --- install.sh | 755 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 443 insertions(+), 312 deletions(-) diff --git a/install.sh b/install.sh index 796c4d9..201c1fe 100755 --- a/install.sh +++ b/install.sh @@ -1,120 +1,203 @@ #!/bin/sh # shellcheck shell=dash +# shellcheck disable=SC3043 # The official zoxide installer. # # It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local` # extension. Note: Most shells limit `local` to 1 var per line, contra bash. -main() { - if [ "${KSH_VERSION-}" = 'Version JM 93t+ 2010-03-05' ]; then - # The version of ksh93 that ships with many illumos systems does not - # support the "local" extension. Print a message rather than fail in - # subtle ways later on: - err 'the installer does not work with this ksh93 version; please try bash' +usage() { + printf "%s\n" \ + "install.sh [option]" \ + "" \ + "Fetch and install the latest version of zoxide, if zoxide is already" \ + "installed it will be updated to the latest version." + + printf "\n%s\n" "Options" + printf "\t%s\n\t\t%s\n\n" \ + "-b, --bin-dir" "Override the bin installation directory [default: ${_bin_dir}]" \ + "-m, --man-dir" "Override the man installation directory [default: ${_man_dir}]" \ + "-a, --arch" "Override the architecture identified by the installer [default: ${_arch}]" \ + "-h, --help" "Display this help message" +} + +# outputs sudo command to use to stdout, exits script if no command can be found +elevate_priv() { + if has sudo; then + if sudo true; then + printf 'sudo' + return 0 + else + err "Sudo failed. Checking doas support." fi + fi - set -u + if has doas; then + if doas true; then + printf "doas" + return 0 + else + err "doas failed." + fi + fi - # Detect and print host target triple. + # returns before this if a command is found. + err 'Could not find the command "sudo" or "doas", needed to get permissions for install.' + err "If you are on Windows, please run your shell as an administrator, then" + err "rerun this script. Otherwise, please run this script as root, or install" + err "sudo or doas." + exit 1 +} + +main() { + if [ "${KSH_VERSION-}" = 'Version JM 93t+ 2010-03-05' ]; then + # The version of ksh93 that ships with many illumos systems does not + # support the "local" extension. Print a message rather than fail in + # subtle ways later on: + abort 'the installer does not work with this ksh93 version; please try bash' + fi + + set -u + + # Detect and print host target triple. + if [ -n "${ARCH:-}" ]; then + # if the user specifed, trust them - don't error on unrecognized hardware. + local _arch="${ARCH}" + else ensure get_architecture local _arch="${RETVAL}" - assert_nz "${_arch}" "arch" - echo "Detected architecture: ${_arch}" + fi + assert_nz "${_arch}" "arch" + log "Detected architecture: ${_arch}" - # Create and enter a temporary directory. - local _tmp_dir - _tmp_dir="$(mktemp -d)" || err "mktemp: could not create temporary directory" - cd "${_tmp_dir}" || err "cd: failed to enter directory: ${_tmp_dir}" + local _bin_dir="${HOME}/.local/bin" + local _bin_name + local _man_dir="${HOME}/.local/share/man" - # Download and extract zoxide. - ensure download_zoxide "${_arch}" - local _package="${RETVAL}" - assert_nz "${_package}" "package" - echo "Downloaded package: ${_package}" - case "${_package}" in - *.tar.gz) - need_cmd tar - ensure tar -xf "${_package}" - ;; - *.zip) - need_cmd unzip - ensure unzip -oq "${_package}" - ;; - *) - err "unsupported package format: ${_package}" - ;; - esac + parse_args "$@" # sets global variables (BIN_DIR, MAN_DIR, ARCH) - # Install binary. - local _bin_dir="${HOME}/.local/bin" - local _bin_name - case "${_arch}" in - *windows*) _bin_name="zoxide.exe" ;; - *) _bin_name="zoxide" ;; - esac - ensure mkdir -p "${_bin_dir}" - ensure cp "${_bin_name}" "${_bin_dir}" - ensure chmod +x "${_bin_dir}/${_bin_name}" - echo "Installed zoxide to ${_bin_dir}" + _bin_dir=${BIN_DIR:-$_bin_dir} + _man_dir=${MAN_DIR:-$_man_dir} + _arch=${ARCH:-$_arch} - # Install manpages. - local _man_dir="${HOME}/.local/share/man" - ensure mkdir -p "${_man_dir}/man1" - ensure cp "man/man1/"* "${_man_dir}/man1/" - echo "Installed manpages to ${_man_dir}" + case "${_arch}" in + *windows*) _bin_name="zoxide.exe" ;; + *) _bin_name="zoxide" ;; + esac - # Print success message and check $PATH. - echo "" - echo "zoxide is installed!" - if ! echo ":${PATH}:" | grep -Fq ":${_bin_dir}:"; then - echo "NOTE: ${_bin_dir} is not on your \$PATH. zoxide will not work unless it is added to \$PATH." - fi + if ! [ -d "${_bin_dir}" ]; then + abort "Please ensure the destination exists! (${_bin_dir})" + fi + if ! [ -d "${_man_dir}" ]; then + abort "Please ensure the manual destination exists! (${_man_dir})" + fi + + local sudo="" + if test_writeable "${_bin_dir}"; then + log "Installing zoxide, please wait…" + else + log "Escalated permissions are required to install to ${_bin_dir}" + sudo=$(elevate_priv) + log "Installing zoxide as root, please wait…" + fi + + # Create and enter a temporary directory. + local _tmp_dir + _tmp_dir="$(mktemp -d)" || abort "mktemp: could not create temporary directory" + cd -- "${_tmp_dir}" || abort "cd: failed to enter directory: ${_tmp_dir}" + + # Download and extract zoxide. + ensure download_zoxide "${_arch}" + local _package="${RETVAL}" + assert_nz "${_package}" "package" + log "Downloaded package: ${_package}" + case "${_package}" in + *.tar.gz) + need_cmd tar + ensure tar -xf "${_package}" + ;; + *.zip) + need_cmd unzip + ensure unzip -oq "${_package}" + ;; + *) + abort "unsupported package format: ${_package}" + ;; + esac + + # Install binary. + # shellcheck disable=SC2086 # The lack of quoting is intentional. This may not be the best way to do it, but it's hard to properly do in POSIX + { + ensure ${sudo} cp "${_bin_name}" "${_bin_dir}" + ensure ${sudo} chmod +x "${_bin_dir}/${_bin_name}" + } + log "Installed zoxide to ${_bin_dir}" + + # Install manpages. + # shellcheck disable=SC2086 # The lack of quoting is intentional. + ensure ${sudo} cp "man/man1/"* "${_man_dir}/man1/" + log "Installed manpages to ${_man_dir}" + + # Print success message and check $PATH. + log "" + log "zoxide is installed!" + case ":${PATH}:" in + *":${_bin_dir}:"*) true ;; # noop + *) log "NOTE: ${_bin_dir} is not on your \$PATH. zoxide will not work unless it is added to \$PATH." ;; + esac } download_zoxide() { - local _arch="$1" + local _arch="$1" - if check_cmd curl; then - _dld=curl - elif check_cmd wget; then - _dld=wget - else - need_cmd 'curl or wget' - fi - need_cmd grep + if has_cmd curl; then + _dld=curl + elif has_cmd wget; then + _dld=wget + elif has_cmd fetch; then + _dld="fetch" + # cmd="fetch --quiet --output=$file $url" + else + abort 'curl or wget are required to download zoxide' + fi - local _releases_url="https://api.github.com/repos/ajeetdsouza/zoxide/releases/latest" - local _releases - case "${_dld}" in - curl) _releases="$(curl -sL "${_releases_url}")" || - err "curl: failed to download ${_releases_url}" ;; - wget) _releases="$(wget -qO- "${_releases_url}")" || - err "wget: failed to download ${_releases_url}" ;; - *) err "unsupported downloader: ${_dld}" ;; - esac - (echo "${_releases}" | grep -q 'API rate limit exceeded') && - err "you have exceeded GitHub's API rate limit. Please try again later, or use a different installation method: https://github.com/ajeetdsouza/zoxide/#installation" + need_cmd grep - local _package_url - _package_url="$(echo "${_releases}" | grep "browser_download_url" | cut -d '"' -f 4 | grep "${_arch}")" || - err "zoxide has not yet been packaged for your architecture (${_arch}), please file an issue: https://github.com/ajeetdsouza/zoxide/issues" + local _releases_url="https://api.github.com/repos/ajeetdsouza/zoxide/releases/latest" + local _releases + case "${_dld}" in + curl) _releases="$(curl -sL "${_releases_url}")" || + abort "curl: failed to download ${_releases_url}" ;; + wget) _releases="$(wget -qO- "${_releases_url}")" || + abort "wget: failed to download ${_releases_url}" ;; + fetch) _releases="$(fetch --quiet "${_releases_url}")" || + abort "fetch: failed to download ${_releases_url}" ;; + *) abort "unsupported downloader: ${_dld}" ;; + esac + (printf "%s" "${_releases}" | grep -q 'API rate limit exceeded') && + abort "you have exceeded GitHub's API rate limit. Please try again later, or use a different installation method: https://github.com/ajeetdsouza/zoxide/#installation" - local _ext - case "${_package_url}" in - *.tar.gz) _ext="tar.gz" ;; - *.zip) _ext="zip" ;; - *) err "unsupported package format: ${_package_url}" ;; - esac + local _package_url + _package_url="$(printf "%s" "${_releases}" | grep "browser_download_url" | cut -d '"' -f 4 | grep "${_arch}")" || + abort "zoxide has not yet been packaged for your architecture (${_arch}), please file an issue: https://github.com/ajeetdsouza/zoxide/issues" - local _package="zoxide.${_ext}" - case "${_dld}" in - curl) _releases="$(curl -sLo "${_package}" "${_package_url}")" || err "curl: failed to download ${_package_url}" ;; - wget) _releases="$(wget -qO "${_package}" "${_package_url}")" || err "wget: failed to download ${_package_url}" ;; - *) err "unsupported downloader: ${_dld}" ;; - esac + local _ext + case "${_package_url}" in + *.tar.gz) _ext="tar.gz" ;; + *.zip) _ext="zip" ;; + *) abort "unsupported package format: ${_package_url}" ;; + esac - RETVAL="${_package}" + local _package="zoxide.${_ext}" + case "${_dld}" in + curl) _releases="$(curl -sLo "${_package}" "${_package_url}")" || abort "curl: failed to download ${_package_url}" ;; + wget) _releases="$(wget -qO "${_package}" "${_package_url}")" || abort "wget: failed to download ${_package_url}" ;; + fetch) _releases="$(fetch --quiet --output="${_package}" "${_package_url}")" || abort "fetch: failed to download ${_package_url}" ;; + *) abort "unsupported downloader: ${_dld}" ;; + esac + + RETVAL="${_package}" } # The below functions have been extracted with minor modifications from the @@ -123,269 +206,317 @@ download_zoxide() { # https://github.com/rust-lang/rustup/blob/4c1289b2c3f3702783900934a38d7c5f912af787/rustup-init.sh get_architecture() { - local _ostype _cputype _bitness _arch _clibtype - _ostype="$(uname -s)" - _cputype="$(uname -m)" - _clibtype="musl" + local _ostype _cputype _bitness _arch _clibtype + _ostype="$(uname -s)" + _cputype="$(uname -m)" + _clibtype="musl" - if [ "${_ostype}" = Linux ]; then - if [ "$(uname -o || true)" = Android ]; then - _ostype=Android - fi + if [ "${_ostype}" = Linux ]; then + if [ "$(uname -o || true)" = Android ]; then + _ostype=Android + fi + fi + + if [ "${_ostype}" = Darwin ] && [ "${_cputype}" = i386 ]; then + # Darwin `uname -m` lies + if sysctl hw.optional.x86_64 | grep -q ': 1'; then + _cputype=x86_64 + fi + fi + + if [ "${_ostype}" = SunOS ]; then + # Both Solaris and illumos presently announce as "SunOS" in "uname -s" + # so use "uname -o" to disambiguate. We use the full path to the + # system uname in case the user has coreutils uname first in PATH, + # which has historically sometimes printed the wrong value here. + if [ "$(/usr/bin/uname -o || true)" = illumos ]; then + _ostype=illumos fi - if [ "${_ostype}" = Darwin ] && [ "${_cputype}" = i386 ]; then - # Darwin `uname -m` lies - if sysctl hw.optional.x86_64 | grep -q ': 1'; then - _cputype=x86_64 - fi + # illumos systems have multi-arch userlands, and "uname -m" reports the + # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86 + # systems. Check for the native (widest) instruction set on the + # running kernel: + if [ "${_cputype}" = i86pc ]; then + _cputype="$(isainfo -n)" fi + fi - if [ "${_ostype}" = SunOS ]; then - # Both Solaris and illumos presently announce as "SunOS" in "uname -s" - # so use "uname -o" to disambiguate. We use the full path to the - # system uname in case the user has coreutils uname first in PATH, - # which has historically sometimes printed the wrong value here. - if [ "$(/usr/bin/uname -o || true)" = illumos ]; then - _ostype=illumos - fi + case "${_ostype}" in + Android) + _ostype=linux-android + ;; + Linux) + check_proc + _ostype=unknown-linux-${_clibtype} + _bitness=$(get_bitness) + ;; + FreeBSD) + _ostype=unknown-freebsd + ;; + NetBSD) + _ostype=unknown-netbsd + ;; + DragonFly) + _ostype=unknown-dragonfly + ;; + Darwin) + _ostype=apple-darwin + ;; + illumos) + _ostype=unknown-illumos + ;; + MINGW* | MSYS* | CYGWIN* | Windows_NT) + _ostype=pc-windows-msvc + ;; + *) + abort "unrecognized OS type: ${_ostype}" + ;; + esac - # illumos systems have multi-arch userlands, and "uname -m" reports the - # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86 - # systems. Check for the native (widest) instruction set on the - # running kernel: - if [ "${_cputype}" = i86pc ]; then - _cputype="$(isainfo -n)" - fi + case "${_cputype}" in + i386 | i486 | i686 | i786 | x86) + _cputype=i686 + ;; + xscale | arm) + _cputype=arm + if [ "${_ostype}" = "linux-android" ]; then + _ostype=linux-androideabi fi + ;; + armv6l) + _cputype=arm + if [ "${_ostype}" = "linux-android" ]; then + _ostype=linux-androideabi + else + _ostype="${_ostype}eabihf" + fi + ;; + armv7l | armv8l) + _cputype=armv7 + if [ "${_ostype}" = "linux-android" ]; then + _ostype=linux-androideabi + else + _ostype="${_ostype}eabihf" + fi + ;; + aarch64 | arm64) + _cputype=aarch64 + ;; + x86_64 | x86-64 | x64 | amd64) + _cputype=x86_64 + ;; + mips) + _cputype=$(get_endianness mips '' el) + ;; + mips64) + if [ "${_bitness}" -eq 64 ]; then + # only n64 ABI is supported for now + _ostype="${_ostype}abi64" + _cputype=$(get_endianness mips64 '' el) + fi + ;; + ppc) + _cputype=powerpc + ;; + ppc64) + _cputype=powerpc64 + ;; + ppc64le) + _cputype=powerpc64le + ;; + s390x) + _cputype=s390x + ;; + riscv64) + _cputype=riscv64gc + ;; + *) + abort "unknown CPU type: ${_cputype}" + ;; + esac - case "${_ostype}" in - Android) - _ostype=linux-android - ;; - Linux) - check_proc - _ostype=unknown-linux-${_clibtype} - _bitness=$(get_bitness) - ;; - FreeBSD) - _ostype=unknown-freebsd - ;; - NetBSD) - _ostype=unknown-netbsd - ;; - DragonFly) - _ostype=unknown-dragonfly - ;; - Darwin) - _ostype=apple-darwin - ;; - illumos) - _ostype=unknown-illumos - ;; - MINGW* | MSYS* | CYGWIN* | Windows_NT) - _ostype=pc-windows-msvc - ;; - *) - err "unrecognized OS type: ${_ostype}" - ;; - esac - - case "${_cputype}" in - i386 | i486 | i686 | i786 | x86) + # Detect 64-bit linux with 32-bit userland + if [ "${_ostype}" = unknown-linux-musl ] && [ "${_bitness}" -eq 32 ]; then + case ${_cputype} in + x86_64) + # 32-bit executable for amd64 = x32 + if is_host_amd64_elf; then + abort "x32 userland is unsupported" + else _cputype=i686 - ;; - xscale | arm) - _cputype=arm - if [ "${_ostype}" = "linux-android" ]; then - _ostype=linux-androideabi - fi - ;; - armv6l) - _cputype=arm - if [ "${_ostype}" = "linux-android" ]; then - _ostype=linux-androideabi - else - _ostype="${_ostype}eabihf" - fi - ;; - armv7l | armv8l) - _cputype=armv7 - if [ "${_ostype}" = "linux-android" ]; then - _ostype=linux-androideabi - else - _ostype="${_ostype}eabihf" - fi - ;; - aarch64 | arm64) - _cputype=aarch64 - ;; - x86_64 | x86-64 | x64 | amd64) - _cputype=x86_64 - ;; - mips) - _cputype=$(get_endianness mips '' el) - ;; + fi + ;; mips64) - if [ "${_bitness}" -eq 64 ]; then - # only n64 ABI is supported for now - _ostype="${_ostype}abi64" - _cputype=$(get_endianness mips64 '' el) - fi - ;; - ppc) - _cputype=powerpc - ;; - ppc64) - _cputype=powerpc64 - ;; - ppc64le) - _cputype=powerpc64le - ;; - s390x) - _cputype=s390x - ;; - riscv64) - _cputype=riscv64gc - ;; - *) - err "unknown CPU type: ${_cputype}" - ;; + _cputype=$(get_endianness mips '' el) + ;; + powerpc64) + _cputype=powerpc + ;; + aarch64) + _cputype=armv7 + if [ "${_ostype}" = "linux-android" ]; then + _ostype=linux-androideabi + else + _ostype="${_ostype}eabihf" + fi + ;; + riscv64gc) + abort "riscv64 with 32-bit userland unsupported" + ;; + *) ;; esac + fi - # Detect 64-bit linux with 32-bit userland - if [ "${_ostype}" = unknown-linux-musl ] && [ "${_bitness}" -eq 32 ]; then - case ${_cputype} in - x86_64) - # 32-bit executable for amd64 = x32 - if is_host_amd64_elf; then { - echo "x32 userland is unsupported" 1>&2 - exit 1 - }; else - _cputype=i686 - fi - ;; - mips64) - _cputype=$(get_endianness mips '' el) - ;; - powerpc64) - _cputype=powerpc - ;; - aarch64) - _cputype=armv7 - if [ "${_ostype}" = "linux-android" ]; then - _ostype=linux-androideabi - else - _ostype="${_ostype}eabihf" - fi - ;; - riscv64gc) - err "riscv64 with 32-bit userland unsupported" - ;; - *) ;; - esac + # Detect armv7 but without the CPU features Rust needs in that build, + # and fall back to arm. + # See https://github.com/rust-lang/rustup.rs/issues/587. + if [ "${_ostype}" = "unknown-linux-musleabihf" ] && [ "${_cputype}" = armv7 ]; then + if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then + # At least one processor does not have NEON. + _cputype=arm fi + fi - # Detect armv7 but without the CPU features Rust needs in that build, - # and fall back to arm. - # See https://github.com/rust-lang/rustup.rs/issues/587. - if [ "${_ostype}" = "unknown-linux-musleabihf" ] && [ "${_cputype}" = armv7 ]; then - if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then - # At least one processor does not have NEON. - _cputype=arm - fi - fi - - _arch="${_cputype}-${_ostype}" - RETVAL="${_arch}" + _arch="${_cputype}-${_ostype}" + RETVAL="${_arch}" } get_bitness() { - need_cmd head - # Architecture detection without dependencies beyond coreutils. - # ELF files start out "\x7fELF", and the following byte is - # 0x01 for 32-bit and - # 0x02 for 64-bit. - # The printf builtin on some shells like dash only supports octal - # escape sequences, so we use those. - local _current_exe_head - _current_exe_head=$(head -c 5 /proc/self/exe) - if [ "${_current_exe_head}" = "$(printf '\177ELF\001')" ]; then - echo 32 - elif [ "${_current_exe_head}" = "$(printf '\177ELF\002')" ]; then - echo 64 - else - err "unknown platform bitness" - fi + need_cmd head + # Architecture detection without dependencies beyond coreutils. + # ELF files start out "\x7fELF", and the following byte is + # 0x01 for 32-bit and + # 0x02 for 64-bit. + # The printf builtin on some shells like dash only supports octal + # escape sequences, so we use those. + local _current_exe_head + _current_exe_head=$(head -c 5 /proc/self/exe) + if [ "${_current_exe_head}" = "$(printf '\177ELF\001')" ]; then + printf 32 + elif [ "${_current_exe_head}" = "$(printf '\177ELF\002')" ]; then + printf 64 + else + abort "unknown platform bitness" + fi } get_endianness() { - local cputype=$1 - local suffix_eb=$2 - local suffix_el=$3 + local cputype="$1" + local suffix_eb="$2" + local suffix_el="$3" - # detect endianness without od/hexdump, like get_bitness() does. - need_cmd head - need_cmd tail + # detect endianness without od/hexdump, like get_bitness() does. + need_cmd head + need_cmd tail - local _current_exe_endianness - _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)" - if [ "${_current_exe_endianness}" = "$(printf '\001')" ]; then - echo "${cputype}${suffix_el}" - elif [ "${_current_exe_endianness}" = "$(printf '\002')" ]; then - echo "${cputype}${suffix_eb}" - else - err "unknown platform endianness" - fi + local _current_exe_endianness + _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)" + if [ "${_current_exe_endianness}" = "$(printf '\001')" ]; then + printf "%s" "${cputype}${suffix_el}" + elif [ "${_current_exe_endianness}" = "$(printf '\002')" ]; then + printf "%s" "${cputype}${suffix_eb}" + else + abort "unknown platform endianness" + fi } is_host_amd64_elf() { - need_cmd head - need_cmd tail - # ELF e_machine detection without dependencies beyond coreutils. - # Two-byte field at offset 0x12 indicates the CPU, - # but we're interested in it being 0x3E to indicate amd64, or not that. - local _current_exe_machine - _current_exe_machine=$(head -c 19 /proc/self/exe | tail -c 1) - [ "${_current_exe_machine}" = "$(printf '\076')" ] + need_cmd head + need_cmd tail + # ELF e_machine detection without dependencies beyond coreutils. + # Two-byte field at offset 0x12 indicates the CPU, + # but we're interested in it being 0x3E to indicate amd64, or not that. + local _current_exe_machine + _current_exe_machine=$(head -c 19 /proc/self/exe | tail -c 1) + [ "${_current_exe_machine}" = "$(printf '\076')" ] +} + +# Test if a location is writeable by trying to write to it. Windows does not let +# you test writeability other than by writing: https://stackoverflow.com/q/1999988 +test_writeable() { + path="${1:-}/test.txt" + if touch "${path}" 2>/dev/null; then + rm "${path}" + return 0 + else + return 1 + fi } check_proc() { - # Check for /proc by looking for the /proc/self/exe link. - # This is only run on Linux. - if ! test -L /proc/self/exe; then - err "unable to find /proc/self/exe. Is /proc mounted? Installation cannot proceed without /proc." - fi + # Check for /proc by looking for the /proc/self/exe link. + # This is only run on Linux. + if ! test -L /proc/self/exe; then + abort "unable to find /proc/self/exe. Is /proc mounted? Installation cannot proceed without /proc." + fi } need_cmd() { - if ! check_cmd "$1"; then - err "need '$1' (command not found)" - fi + if ! has_cmd "$1"; then + abort "need '$1' (command not found)" + fi } -check_cmd() { - command -v "$1" >/dev/null 2>&1 +has_cmd() { command -v "$1" >/dev/null 2>&1; } + +# parse the arguments passed and set the environment variables accordingly +parse_args() { + # parse argv variables + while [ "$#" -gt 0 ]; do + case "$1" in + -b | --bin-dir) + BIN_DIR="$2" + shift 2 + ;; + -m | --man-dir) + MAN_DIR="$2" + shift 2 + ;; + -a | --arch) + ARCH="$2" + shift 2 + ;; + -h | --help) + usage + exit 0 + ;; + + -b=* | --bin-dir=*) + BIN_DIR="${1#*=}" + shift 1 + ;; + -m=* | --man-dir=*) + MAN_DIR="${1#*=}" + shift 1 + ;; + -a=* | --arch=*) + ARCH="${1#*=}" + shift 1 + ;; + *) + err "Unknown option: $1" + usage + exit 1 + ;; + esac + done } # Run a command that should never fail. If the command fails execution # will immediately terminate with an error showing the failing # command. -ensure() { - if ! "$@"; then err "command failed: $*"; fi -} - -assert_nz() { - if [ -z "$1" ]; then err "found empty string: $2"; fi -} - -err() { - echo "Error: $1" >&2 - exit 1 +ensure() { if ! "$@"; then abort "command failed: $*"; fi; } +assert_nz() { if [ -z "$1" ]; then abort "found empty string: $2"; fi; } +log() { printf '%s\n' "$1"; } +err() { printf 'Error: %s\n' "$1" >&2; } +abort() { + err "$1" + exit "${2:-1}" } # This is put in braces to ensure that the script does not run until it is # downloaded completely. { - main "$@" || exit 1 + main "$@" || exit 1 }