Move executable-check to C++

This was already apparently supposed to work, but didn't because we
just overrode errno again.

This now means that, if a correctly named candidate exists, we don't
start the command-not-found handler.

See #8804
This commit is contained in:
Fabian Homborg 2022-03-31 15:10:45 +02:00
parent 90d52ee669
commit f13979bfbb
5 changed files with 16 additions and 17 deletions

View File

@ -13,12 +13,6 @@ or set -g __fish_added_user_paths
# #
function __fish_default_command_not_found_handler function __fish_default_command_not_found_handler
printf (_ "fish: Unknown command: %s\n") (string escape -- $argv[1]) >&2 printf (_ "fish: Unknown command: %s\n") (string escape -- $argv[1]) >&2
for file in $PATH/$argv[1]
if test -e $file -a ! -x $file
printf (_ "fish: %s exists but isn't executable\n") (string escape -- $file) >&2
break
end
end
end end

View File

@ -13,12 +13,6 @@ end
function __fish_default_command_not_found_handler function __fish_default_command_not_found_handler
printf (_ "fish: Unknown command: %s\n") (string escape -- $argv[1]) >&2 printf (_ "fish: Unknown command: %s\n") (string escape -- $argv[1]) >&2
for file in $PATH/$argv[1]
if test -e $file -a ! -x $file
printf (_ "fish: %s exists but isn't executable\n") (string escape -- $file) >&2
break
end
end
end end
# If an old handler already exists, defer to that. # If an old handler already exists, defer to that.

View File

@ -731,8 +731,12 @@ end_execution_reason_t parse_execution_context_t::handle_command_not_found(
const wchar_t *const cmd = cmd_str.c_str(); const wchar_t *const cmd = cmd_str.c_str();
if (err_code != ENOENT) { if (err_code != ENOENT) {
// TODO: We currently handle all errors here the same,
// but this mainly applies to EACCES. We could also feasibly get:
// ELOOP
// ENAMETOOLONG
return this->report_error(STATUS_NOT_EXECUTABLE, statement, return this->report_error(STATUS_NOT_EXECUTABLE, statement,
_(L"The file '%ls' is not executable by this user"), cmd); _(L"Unknown command. '%ls' exists but is not an executable file."), cmd);
} }
// Handle unrecognized commands with standard command not found handler that can make better // Handle unrecognized commands with standard command not found handler that can make better
@ -872,7 +876,7 @@ end_execution_reason_t parse_execution_context_t::populate_plain_process(
if (!has_command && !use_implicit_cd) { if (!has_command && !use_implicit_cd) {
// No command. If we're --no-execute return okay - it might be a function. // No command. If we're --no-execute return okay - it might be a function.
if (no_exec()) return end_execution_reason_t::ok; if (no_exec()) return end_execution_reason_t::ok;
return this->handle_command_not_found(cmd, statement, no_cmd_err_code); return this->handle_command_not_found(path_to_external_command.empty() ? cmd : path_to_external_command, statement, no_cmd_err_code);
} }
} }

View File

@ -87,6 +87,15 @@ static bool path_get_path_core(const wcstring &cmd, wcstring *out_path,
return true; return true;
} }
err = EACCES; err = EACCES;
if (out_path) *out_path = std::move(next_path);
} else if (errno != ENOENT && err == ENOENT) {
// Keep the first *interesting* error and path around.
// ENOENT isn't interesting because not having a file is the normal case.
auto tmperr = errno;
// Ignore if the parent directory is already inaccessible.
if (access(wcs2string(wdirname(next_path)).c_str(), X_OK) != 0) continue;
err = tmperr;
if (out_path) *out_path = std::move(next_path);
} }
} }

View File

@ -35,9 +35,7 @@ echo $status
set -g PATH . set -g PATH .
echo banana > foobar echo banana > foobar
foobar --banana foobar --banana
# CHECKERR: fish: Unknown command: foobar # CHECKERR: checks/command-not-found.fish (line {{\d+}}): Unknown command. './foobar' exists but is not an executable file.
# CHECKERR: fish: ./foobar exists but isn't executable
# CHECKERR: checks/command-not-found.fish (line 37):
# CHECKERR: foobar --banana # CHECKERR: foobar --banana
# CHECKERR: ^ # CHECKERR: ^