. "$BST_LIB" require which which/present # GNU which 2.21 (poky recipes-extended). Resolves a command name against PATH # with bash's algorithm. busybox also ships a `which` applet, so the first job # here is proving update-alternatives handed /usr/bin/which to the GNU one # (priority 100) and not the applet (50) -- every switch below only exists in # the GNU tool, and a busybox `which` would fail them all. check_out which/version-gnu "GNU which v2.21" which --version check which/version-rc which --version w=$(command -v which) check_eq which/self-path /usr/bin/which "$w" check_out which/self "/usr/bin/which" which which tmp=${TMPDIR:-/tmp}/bst-which.$$ mkdir -p "$tmp/a" "$tmp/b" printf '#!/bin/sh\necho hi\n' > "$tmp/a/bsttool"; chmod +x "$tmp/a/bsttool" cp "$tmp/a/bsttool" "$tmp/b/bsttool" : > "$tmp/a/bstnonexec" # present but not executable # --- PATH resolution --------------------------------------------------------- out=$(PATH="$tmp/a:$PATH" which bsttool 2>&1) check_eq which/resolve "$tmp/a/bsttool" "$out" check_rc which/rc-found 0 env PATH="$tmp/a:$PATH" which bsttool check_rc which/rc-missing 1 env PATH="$tmp/a:$PATH" which bstnosuchcmd # the not-found diagnostic goes to stderr and names the command out=$(PATH="$tmp/a:$PATH" which bstnosuchcmd 2>&1) case "$out" in *"no bstnosuchcmd in"*) bst_ok which/missing-msg ;; *) bst_notok which/missing-msg "$(_bst_1line "$out")" ;; esac # a file on PATH that is not executable is not a match -- the x-bit is the test, # not mere presence check_rc which/skip-nonexec 1 env PATH="$tmp/a:$PATH" which bstnonexec # --- -a / --all -------------------------------------------------------------- # every match, in PATH order; the plain form stops at the first n=$(PATH="$tmp/a:$tmp/b:$PATH" which -a bsttool 2>/dev/null | wc -l | tr -d ' ') check_eq which/all-count 2 "$n" first=$(PATH="$tmp/a:$tmp/b:$PATH" which -a bsttool 2>/dev/null | head -1) check_eq which/all-order "$tmp/a/bsttool" "$first" n1=$(PATH="$tmp/a:$tmp/b:$PATH" which bsttool 2>/dev/null | wc -l | tr -d ' ') check_eq which/first-only 1 "$n1" check_rc which/all-missing-rc 1 env PATH="$tmp/a:$PATH" which -a bstnosuchcmd # --- argument handling ------------------------------------------------------- # an absolute path is reported as given out=$(PATH="$tmp/a:$PATH" which "$tmp/a/bsttool" 2>&1) check_eq which/abs-path "$tmp/a/bsttool" "$out" # -- ends option parsing out=$(PATH="$tmp/a:$PATH" which -- bsttool 2>&1) check_eq which/dashdash "$tmp/a/bsttool" "$out" # several commands: a missing one makes the status 1, the found one still prints out=$(PATH="$tmp/a:$PATH" which bsttool bstnosuchcmd 2>&1); rc=$? check_eq which/multi-rc 1 "$rc" case "$out" in *"$tmp/a/bsttool"*) bst_ok which/multi-found ;; *) bst_notok which/multi-found "found command not printed: $(_bst_1line "$out")" ;; esac check_rc which/multi-all-found 0 env PATH="$tmp/a:$PATH" which bsttool bsttool # --- PATH-hygiene switches (the ones busybox has no answer for) -------------- # a relative "." entry: --show-dot reports it as ./, --skip-dot ignores it ( cd "$tmp" && out=$(PATH="./a:$PATH" which --show-dot bsttool 2>&1) case "$out" in ./a/bsttool) bst_ok which/show-dot ;; *) bst_notok which/show-dot "want './a/bsttool' got '$(_bst_1line "$out")'" ;; esac ) ( cd "$tmp" && out=$(PATH="./a:$tmp/b:$PATH" which --skip-dot bsttool 2>&1) check_eq which/skip-dot "$tmp/b/bsttool" "$out" ) # --show-tilde abbreviates $HOME; run with HOME set to the scratch dir so the # assertion does not depend on the image's real home. Upstream disables the # switch for the superuser -- which.1: "This option is ignored when which is # invoked as root", which.c: show_tilde = (!tty_only && geteuid() != superuser) # -- and this image's shell is root, so there the full path IS the pass. out=$(HOME="$tmp" PATH="$tmp/a:$PATH" which --show-tilde bsttool 2>&1) if [ "$(id -u 2>/dev/null || echo 0)" = 0 ]; then check_eq which/show-tilde "$tmp/a/bsttool" "$out" else check_eq which/show-tilde "~/a/bsttool" "$out" fi # --tty-only stops processing the options to its right when stdout is not a tty; # the command itself still resolves, which is the property that matters here out=$(PATH="$tmp/a:$PATH" which --tty-only bsttool 2>&1) check_eq which/tty-only "$tmp/a/bsttool" "$out" # --- shell-table integration ------------------------------------------------- # --read-alias/--read-functions consume a shell's tables on stdin. The login # shell here is busybox sh, which exports neither, so which only ever sees what # a caller pipes in -- upstream's documented limitation. Feed it explicitly. out=$(printf "alias bstll='ls -l'\n" | which --read-alias bstll 2>&1) case "$out" in *"alias bstll="*) bst_ok which/read-alias ;; *) bst_notok which/read-alias "$(_bst_1line "$out")" ;; esac # --skip-alias ignores the piped table and falls back to PATH out=$(printf "alias bsttool='no-such'\n" | PATH="$tmp/a:$PATH" which --skip-alias bsttool 2>&1) check_eq which/skip-alias "$tmp/a/bsttool" "$out" # --- diagnostics ------------------------------------------------------------- # An unknown option and a bare invocation both print usage and exit non-zero. # 2.21 and 2.25 both use 255, but only "non-zero + a diagnostic" is asserted so # a later version bump cannot turn a working tool red. out=$(which --bst-bogus-option 2>&1); rc=$? if [ "$rc" -ne 0 ]; then bst_ok which/badopt-rc else bst_notok which/badopt-rc "accepted an unknown option (rc=0)"; fi case "$out" in *"unrecognized option"*) bst_ok which/badopt-msg ;; *) bst_notok which/badopt-msg "$(_bst_1line "$out")" ;; esac out=$(which 2>&1); rc=$? if [ "$rc" -ne 0 ]; then bst_ok which/noargs-rc else bst_notok which/noargs-rc "accepted no arguments (rc=0)"; fi case "$out" in *Usage:*) bst_ok which/noargs-usage ;; *) bst_notok which/noargs-usage "$(_bst_1line "$out")" ;; esac rm -rf "$tmp"