command_not_found_handle not run when PATH is empty

2023-03-08 Thread Moshe Looks
Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -g -O2 -flto=auto -ffat-lto-objects -flto=auto
-ffat-lto-objects -fstack-protector-strong -Wformat
-Werror=format-security -Wall
uname output: Linux xi 5.15.0-67-generic #74-Ubuntu SMP Wed Feb 22
14:14:39 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
Machine Type: x86_64-pc-linux-gnu

Bash Version: 5.1
Patch Level: 16
Release Status: release

Description:
When PATH is empty, the command_not_found_handle function is not
called when a command is not found.

Repeat-By:
> bash -c 'command_not_found_handle() { echo ok; }; no-such-command'
ok

> bash -c 'PATH=; command_not_found_handle() { echo ok; }; no-such-command'
bash: line 1: no-such-command: No such file or directory

I verified this behavior on master as well.

Fix:
--- a/findcmd.c
+++ b/findcmd.c
@@ -632,3 +632,3 @@ find_user_command_in_path (name, path_list, flags, rflagsp)
   if (path_list == 0 || *path_list == '\0')
-return (savestring (name));/* XXX */
+return ((char *)NULL);

This is very old code and I have no idea what the original intention
was in returning 'name' here or if doing so is still performing useful
work, but the tests pass at least.

Thank you for your attention!

Best, Moshe



Re: command_not_found_handle not run when PATH is empty

2023-03-08 Thread Grisha Levit
On Wed, Mar 8, 2023 at 4:28 PM Moshe Looks  wrote:
> This is very old code and I have no idea what the original intention
> was in returning 'name' here or if doing so is still performing useful
> work, but the tests pass at least.

A null $PATH component is treated as the current directory, and bash
treats an empty $PATH as having only a single null component, so we
can't just fail to return a command when $PATH is empty.

The current code's behavior of returning the path as provided when
$PATH is empty accomplishes the goal of "finding" the path in $PATH
but ignores any flags that would normally limit the results to files
that exist/are executable/etc.

This leads to weirdness not just with command_not_found_handle but
with command name completion and `type -a' and `command output as
well.

$ cd /bin
$ PATH= compgen -c -X '!bash'
$ PATH= type -a bash
-bash: type: bash: not found

I think it might make sense to change code that looks at the value of
PATH to explicitly treat an empty value as `.' so that all such
behavior is consistent.

diff --git a/bashline.c b/bashline.c
index 3238b13a..e0d6107c 100644
--- a/bashline.c
+++ b/bashline.c
@@ -2063,6 +2063,8 @@ command_word_completion_function (const char
*hint_text, int state)
  dequoted_hint = bash_dequote_filename (hint, 0);

   path = get_string_value ("PATH");
+  if (path == 0 || *path == '\0')
+ path = ".";
   path_index = dot_in_path = 0;

   /* Initialize the variables for each type of command word. */
diff --git a/findcmd.c b/findcmd.c
index 186871ac..b413c870 100644
--- a/findcmd.c
+++ b/findcmd.c
@@ -256,13 +256,13 @@ _find_user_command_internal (const char *name, int flags)

   /* Search for the value of PATH in both the temporary environments and
  in the regular list of variables. */
-  if (var = find_variable_tempenv ("PATH")) /* XXX could be array? */
-path_list = value_cell (var);
+  if (var = find_variable_tempenv ("PATH"))
+path_list = get_variable_value (var);
   else
-path_list = (char *)NULL;
+path_list = 0;

   if (path_list == 0 || *path_list == '\0')
-return (savestring (name));
+path_list = ".";

   cmd = find_user_command_in_path (name, path_list, flags, (int *)0);

@@ -363,11 +363,14 @@ search_for_command (const char *pathname, int flags)
 {
   if (flags & CMDSRCH_STDPATH)
  path_list = conf_standard_path ();
-  else if (temp_path || path)
- path_list = value_cell (path);
+  else if (path)
+ path_list = get_variable_value (path);
   else
  path_list = 0;

+  if (path_list == 0 || *path_list == '\0')
+ path_list = ".";
+
   command = find_user_command_in_path (pathname, path_list,
FS_EXEC_PREFERRED|FS_NODIRS, &st);

   if (command && hashing_enabled && temp_path == 0 && (flags &
CMDSRCH_HASH))
@@ -440,7 +443,9 @@ user_command_matches (const char *name, int flags,
int state)
if (stat (".", &dotinfo) < 0)
  dotinfo.st_dev = dotinfo.st_ino = 0; /* so same_file won't match */
path_list = get_string_value ("PATH");
-path_index = 0;
+   if (path_list == 0 || *path_list == '\0')
+ path_list = ".";
+   path_index = 0;
  }

   while (path_list && path_list[path_index])