summaryrefslogtreecommitdiff
path: root/nextbin
blob: fd8faa5d27b73eb0c26200724ff89ebe6c45257b (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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/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##*/}

[ -n "$PATH" ] || die 'PATH is empty'

case "$orig_exe" in
    */)
        die 'ends with /'
        ;;

    */*)
        orig_dir=${orig_exe%/*}
        ;;

    *)
        # either shell has not resolved script-file into a path, or script is
        # in cwd and PATH has . or empty entries
        if ! [ -d "$orig_exe" ] && [ -x "$orig_exe" ]; then
            case ":$PATH:" in
                *::*:.:*|*:.:*::*)
                    die 'exe in cwd and PATH has both . and empty entries'
                    ;;

                *::*)
                    orig_dir=
                    ;;

                *:.:*)
                    orig_dir=.
                    ;;

                *)
                    die 'missing /'
            esac
        fi
esac

tmp_path=:$PATH:
new_path=${tmp_path#*:$orig_dir:}
[ -n "$new_path" ] \
    || die '%s is last PATH entry' "$orig_dir"
if [ "$new_path" != "$tmp_path" ]; then
    new_path_2=${new_path#*:$orig_dir:}
    [ "$new_path" = "$new_path_2" ] \
        || die 'duplicate entry in PATH: %s' \
            "$orig_exe" "$orig_dir"
else
    # try to find equivalent path
    found_alt_dir=
    new_orig_dir=
    equiv_orig_dir=$(cd "${orig_dir:-.}" && pwd -L)
    tmp_path_2=$PATH
    while :; do
        p=${tmp_path_2%%:*}
        if [ "$(cd "$p" >/dev/null 2>&1 && pwd -L)" = "$equiv_orig_dir" ]; then
            [ -z "$found_alt_dir" ] \
                || die "duplicate equivalent path in PATH: '%s' = '%s'" \
                    "$new_orig_dir" "$p"
            found_alt_dir=1
            new_orig_dir=$p
        fi
        case "$tmp_path_2" in
            *:*) tmp_path_2=${tmp_path_2#*:};;
            *) break
        esac
    done
    if [ -n "$found_alt_dir" ]; then
        new_path=${tmp_path#*:$new_orig_dir:}
        [ -n "$new_path" ] \
            || die '%s is last PATH entry' "$new_orig_dir"
    else
        new_path=$PATH:
    fi
fi

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

$action "$exe" "$@"