summaryrefslogtreecommitdiff
path: root/nextbin
blob: 31241efa5fcf682684eb9d45fbb340aa91e6c966 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/bin/sh

set -e

die() {
    fmt=$1
    shift
    printf "nextbin %s failed: $fmt\n" "$orig_exe" "$@" >&2
    exit 1
}

usage() {
    # shellcheck disable=SC2016
    echo 'usage: nextbin [-v] "$0" "$@"' >&2
}

case "$1" in
    -v) action="echo"; shift;;
    -*|'') usage; exit 1;;
    *) action="exec"
esac
orig_exe=$1
shift
exe_name=${orig_exe##*/}

case ":$PATH:" in
    ::) die 'empty PATH not supported' ;;
    *:[!/]*) die 'relative PATH elements not supported' ;;
    *:*/./*:*|*:*/../*:*) die 'PATH elements containing . or .. not supported' ;;
esac

case $orig_exe in
    # theoretically this could be optimized but it's negligible cost
    */*) orig_dir=$(cd -L "${orig_exe%/*}" && pwd -L) ;;
    *) orig_dir=$PWD ;;
esac

# clean up path. theoretically we could allow e.g. PATH=/dir://dir but which
# would we match for cd /dir; ./exe
tmp_path=$(printf '%s\n' ":$PATH:" | sed -e 's|/*:|:|g' -e 's|//|/|g')

case $tmp_path in
    *:$orig_dir:$orig_dir:*|*:$orig_dir:*:$orig_dir:*)
        die 'duplicate entry in PATH: %s' "$orig_dir"
esac

new_path=${tmp_path#*":$orig_dir:"}
new_path=${new_path%:}
exe=$(PATH="$new_path" command -v "$exe_name") || true
[ -n "$exe" ] || die 'PATH lookup failed using %s' "$new_path"

$action "$exe" "$@"