bash-completion icon indicating copy to clipboard operation
bash-completion copied to clipboard

Completing sudo after --user= doesn't suggest commands

Open AdrianVovk opened this issue 3 years ago • 1 comments

Describe the bug

When trying to complete sudo --user=foobar <HIT TAB HERE>, it suggests the files in the current directory instead of commands

To reproduce

  1. Type sudo --user=<A USER ON YOUR SYSTEM>
  2. Try to complete
  3. See problem

Expected behavior

Trying to complete sudo --user=foo should suggest command names, not files in the current directory

Versions (please complete the following information)

  • [ ] Operating system name/distribution and version: Fedora 35 Silverblue
  • [ ] bash version, echo "$BASH_VERSION": 5.1.8(1)-release
  • [ ] bash-completion version, (IFS=.; echo "${BASH_COMPLETION_VERSINFO[*]}"): 2.11

Additional context

I discovered this when trying to write a completion for pkexec. There's another bug in sudo's completion that mishandles sudo --user foobar, so this context doesn't apply to sudo. My completion doesn't have this bug, so I can get this additional behavior: pkexec --user=foobar <TAB> completes the directory, but pkexec --user foobar <TAB> completes commands. I can confirm that, at least with my completion, _command_offset is being executed. So I suspect soething in _command_offset can't deal with --arg=data arguments?

Debug trace

bash-completion.log

AdrianVovk avatar Mar 31 '22 08:03 AdrianVovk

This is caused by the mismatching of the offset in words and that in COMP_WORDS. While _command_offset assumes its first argument being the offset in COMP_WORDS, _sudo passes the offset in words. Similar mismatching is also found in other completions.

  • 7 completions pass the offset in words
    • screen 976ba4482 (2011-04)
    • strace 8048917 (2011-04) Converted from COMP_WORDS. I think this is a mistake in refactoring.
    • _ionice d54fcf100 (2011-10)
    • watch 049b60ec6 (2011-10)
    • gdb b1f780355 (2014-04) Contributed by others
    • sudo ca3b1f664 (2018-06) Converted from COMP_WORDS. This also seems like an oversight in fixing another problem.
    • find 3e849c25e (2020-04)
  • 4 completions pass the offset in COMP_WORDS
    • ccache ed1703497 (2011-11)
    • valgrind 834379ef6 (2011-11)
    • timeout 63b499593 (2013-10)
    • xvfb-run 5c0e98832 (2019-06)

We can determine which offset _command_offset receives and convert the above to one side. In case we decide that _command_offset should receive the offset in words, we can convert the offset in words to that in COMP_WORDS in _command_offset as

diff --git a/bash_completion b/bash_completion
index e5220cac..5172be30 100644
--- a/bash_completion
+++ b/bash_completion
@@ -2220,6 +2220,18 @@ _command_offset()
     # rewrite current completion context before invoking
     # actual command completion

+    # convert the new first-word position $1 to the index in COMP_WORDS.
+    if [[ -v cword && -v words ]]; then
+        local reassembled_offset=$1 i=0 j
+        for ((j = 0; j < reassembled_offset; j++)); do
+            local word=${words[j]}
+            while [[ $word && i -lt ${#COMP_WORDS[@]} ]]; do
+                word=${word#*"${COMP_WORDS[i++]}"}
+            done
+        done
+        set -- "$i"
+    fi
+
     # find new first word position, then
     # rewrite COMP_LINE and adjust COMP_POINT
     local word_offset=$1 i j

I tried to find which one of words vs COMP_WORDS is preferred in the change history, but I couldn't find it there. I find some conversions from COMP_WORDS to words, but I'm not sure if that was the intentional change. Also, the difference doesn't seem to be due to the time of the introduction.

akinomyoga avatar Sep 04 '22 02:09 akinomyoga