Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

[PATCH] _sudo: fix handling of envvar assignments



sudo completion has been broken since w/52260 (try just sudo <TAB>)

i think the approach there was informed by the sudo documentation's
implication that variable assignments have to appear before -i/-s. but
that's not the case. options and assignments can be interspersed in
any order or combination

in light of that, something like this?

dana

ps: i feel like `_command_names -e` should also complete aliases when
the preceding command name is itself an alias with a trailing space.
maybe it could have another option where you pass your $words[1] and it
checks it for you?


diff --git a/Completion/Unix/Command/_sudo b/Completion/Unix/Command/_sudo
index 33a9d6c0c..e3b3565ef 100644
--- a/Completion/Unix/Command/_sudo
+++ b/Completion/Unix/Command/_sudo
@@ -1,7 +1,7 @@
 #compdef sudo sudoedit
 
 local curcontext="$curcontext" environ e cmd cpp ret=1
-local -a context state line args _comp_priv_prefix
+local -a context state state_descr line args _comp_priv_prefix
 local -A opt_args
 
 zstyle -a ":completion:${curcontext}:" environ environ
@@ -41,7 +41,8 @@ args=(
 # 
 # TODO: use _arguments' $opt_args to detect the cases '-u jrandom -e' and '-Ae'
 if [[ $service = sudoedit ]] || (( $words[(i)-e] < $words[(i)^(*sudo|-[^-]*)] ))  ; then
-  args=( -A "-*" $args '!(-V --version -h --help)-e' '*:file:_files' )
+  args+=( '!(-V --version -h --help)-e' '*: :_files' )
+  _arguments -s -S -A '-?*' : $args && ret=0
 else
   cmd="$words[1]"
   args+=(
@@ -53,38 +54,41 @@ else
     '(-E -i --login -s --shell -e --edit)--preserve-env=-[preserve user environment when running command]::environment variable:_sequence _parameters -g "*export*"' \
     '(-H --set-home -i --login -s --shell -e --edit)'{-H,--set-home}"[set HOME variable to target user's home dir]" \
     '(-P --preserve-groups -i --login -s --shell -e --edit)'{-P,--preserve-groups}"[preserve group vector instead of setting to target's]" \
-    '*:: :->normal'
+    '*: :->normal'
   )
+  # sudo allows environment-variable assignments to be interspersed with
+  # options. this pattern represents the lax method it uses to determine whether
+  # an arg is an assignment
+  _arguments -s -S -A '(-?*|[^/=]*=*)' : $args && ret=0
 fi
 
-_arguments -s -S $args && ret=0
-
 if [[ $state = normal ]]; then
   _comp_priv_prefix=(
     $cmd -n
     ${(kv)opt_args[(I)(-[ugHEP]|--(user|group|set-home|preserve-env|preserve-groups))]}
   )
+
+  # there's no special handling for assignments with -l. they're parsed as
+  # normal, just ignored. we'll ignore them too
   if (( $+opt_args[-l] || $+opt_args[--list] )); then
+    CURRENT=$#line words=( "${(@)line}" )
     _normal -p $service
     return
   fi
-  while [[ $words[1] = *=* ]]; do
-    if (( CURRENT == 1 )); then
-      compstate[parameter]="${PREFIX%%\=*}"
-      compset -P 1 '*='
-      _value && ret=0
-      return ret
-    fi
-    shift words
-    (( CURRENT-- ))
-  done
-  if (( CURRENT == 1 )); then
+
+  # $line excludes words that match the -A pattern, except when it's the current
+  # word. so if $#line == 1 we haven't seen a command yet
+  if (( $#line == 1 )) && [[ $PREFIX == *=* ]]; then
+    compstate[parameter]="${PREFIX%%\=*}"
+    compset -P 1 '*='
+    _value && ret=0
+  elif (( $#line == 1 )); then
     curcontext="${curcontext%:*:*}:-command-:"
     _alternative \
       'commands:: _command_names -e' \
-      'options:option:(-s --shell -l --login)' \
       'parameters: :_parameters -g "*export*~*readonly*" -qS=' && ret=0
   else
+    CURRENT=$#line words=( "${(@)line}" )
     _normal && ret=0
   fi
 fi




Messages sorted by: Reverse Date, Date, Thread, Author