diff --git a/scripts/install.sh b/scripts/install.sh index e1507fe..12d32e7 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -648,7 +648,46 @@ rc_lists_dir() { { n = 0 if ($0 ~ /(^|[^A-Za-z_])fish_add_path([^A-Za-z_]|$)/) { - for (i = 1; i <= NF; i++) if ($i !~ /^-/ && $i !~ /fish_add_path/) cand[++n] = $i + # Parse quotes instead of field-splitting. We WRITE + # fish_add_path "$PREFIX", so a prefix containing spaces became + # two whitespace fields here and never matched -- the installer + # then appended a second block and claimed it had added a PATH + # entry that was already present. The sibling reader further up + # already keeps the remainder intact; this now agrees with it. + # Tokenise the argument list, honouring quotes. Three things + # this has to get right, each of which broke a previous + # attempt (cli#439): + # * FIRST occurrence of the command, via index() -- a greedy + # /^.*fish_add_path/ strips through an inline comment that + # mentions it and loses the real argument; + # * quoted arguments are ONE path, spaces included, because we + # write fish_add_path "$PREFIX"; + # * fish_add_path takes MULTIPLE directories, so parsing must + # continue past the first closing quote. + at = index($0, "fish_add_path") + rest = substr($0, at + 13) + while (length(rest) > 0) { + sub(/^[[:space:]]+/, "", rest) + if (length(rest) == 0) break + ch = substr(rest, 1, 1) + if (ch == "#") break # trailing comment + if (ch == "\"" || ch == "\047") { + body = substr(rest, 2) + close_at = index(body, ch) + if (close_at > 0) { + cand[++n] = substr(body, 1, close_at - 1) + rest = substr(body, close_at + 1) + } else { # unterminated quote + cand[++n] = body + rest = "" + } + } else { + sp = match(rest, /[[:space:]]/) + tok = (sp > 0) ? substr(rest, 1, sp - 1) : rest + rest = (sp > 0) ? substr(rest, sp) : "" + if (tok !~ /^-/) cand[++n] = tok # skip flags + } + } } else if ($0 ~ /(^|[^A-Za-z_])[Pp][Aa][Tt][Hh][+]?=/) { value = $0 sub(/^[^=]*=/, "", value) diff --git a/scripts/tests/install-verify.sh b/scripts/tests/install-verify.sh index 1b42ca7..58d30a1 100755 --- a/scripts/tests/install-verify.sh +++ b/scripts/tests/install-verify.sh @@ -469,6 +469,66 @@ for spec in "zsh:.zshrc:export PATH=" "fish:.config/fish/config.fish:fish_add_pa drop_sandbox done +# -- 16. a fish prefix containing spaces is recognised as already listed ------ +# We WRITE `fish_add_path "$PREFIX"`, so rc_lists_dir must parse the quotes +# rather than field-split: it used to see two whitespace fields, match neither, +# append a second block, and report adding an entry that was already present. +for quote_style in double single; do + make_sandbox yes + RC="$HOMEDIR/.config/fish/config.fish" + mkdir -p "$(dirname "$RC")" + SPACED="$SBX/my tools/bin" + mkdir -p "$SPACED" + if [ "$quote_style" = double ]; then + printf 'fish_add_path "%s"\n' "$SPACED" > "$RC" + else + printf "fish_add_path '%s'\n" "$SPACED" > "$RC" + fi + before=$(cat "$RC") + FAKE_SHELL=/bin/fish COSIGN_RESULT=0 run_installer_at "$SPACED" >/dev/null 2>&1 + if [ "$(cat "$RC")" = "$before" ]; then + ok "fish: $quote_style-quoted prefix with spaces seen as already listed" + else + bad "fish: $quote_style-quoted prefix with spaces not matched (rc rewritten)"; sed 's/^/ /' "$RC" + fi + drop_sandbox +done + +# -- 17. an inline comment that mentions fish_add_path must not defeat the match +# A greedy strip through the LAST `fish_add_path` on the line would leave the +# comment text as the "path" and miss the real argument (Bugbot, cli#439). +make_sandbox yes +RC="$HOMEDIR/.config/fish/config.fish" +mkdir -p "$(dirname "$RC")" +printf 'fish_add_path "%s" # set by fish_add_path\n' "$SBX/s1" > "$RC" +mkdir -p "$SBX/s1" +before=$(cat "$RC") +FAKE_SHELL=/bin/fish COSIGN_RESULT=0 run_installer_at "$SBX/s1" >/dev/null 2>&1 +if [ "$(cat "$RC")" = "$before" ]; then + ok "fish: inline comment naming fish_add_path does not break the match" +else + bad "fish: inline comment naming fish_add_path broke the match (rc rewritten)"; sed 's/^/ /' "$RC" +fi +drop_sandbox + +# -- 18. the prefix is the SECOND quoted arg on a multi-path fish line -------- +# fish_add_path accepts several directories. Parsing that stopped at the first +# closing quote missed a later one, appended a redundant block, and reported a +# PATH add that was already there (Bugbot, cli#439). +make_sandbox yes +RC="$HOMEDIR/.config/fish/config.fish" +mkdir -p "$(dirname "$RC")" +mkdir -p "$SBX/other" "$SBX/s1" +printf 'fish_add_path "%s" "%s"\n' "$SBX/other" "$SBX/s1" > "$RC" +before=$(cat "$RC") +FAKE_SHELL=/bin/fish COSIGN_RESULT=0 run_installer_at "$SBX/s1" >/dev/null 2>&1 +if [ "$(cat "$RC")" = "$before" ]; then + ok "fish: prefix as the second quoted arg is seen as already listed" +else + bad "fish: second quoted arg was not matched (rc rewritten)"; sed 's/^/ /' "$RC" +fi +drop_sandbox + echo echo "install-verify: $PASS passed, $FAIL failed" [ "$FAIL" -eq 0 ]