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-09 Thread Moshe Looks
Thank you this is very illuminating and makes it clear that my naive
one-line fix would be inappropriate. Given the current state of
affairs there is really no good reason for PATH to ever be unset or
set to empty vs. explicitly set to '.', right? So might be worth
spelling out in the docs that this is inadvisable :).

On Wed, Mar 8, 2023 at 7:31 PM Grisha Levit  wrote:
>
> 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])