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

PATCH: 3.1.5-pws-10: _tar



Here's an enhancement to the completion for tar; a lot of it is just
comment, which doesn't clog your shell.  Its main feature is that
completing files for extraction is now pretty close to ordinary file
handling.  It also handles GNU long arguments, but this is a little erratic
in combination with other arguments, since it always assumes the basic
commands are in second position and the archive name in third.  This could
do with some more work.

--- Completion/User/_tar.bk	Mon Mar  1 13:50:36 1999
+++ Completion/User/_tar	Tue Mar  2 11:30:16 1999
@@ -1,11 +1,94 @@
 #defcomp tar
 
-local fl="$words[2]" tf="$words[3]"
+# Tar completion.  Features:
+#  - Assumes tar commands are in second position, tar archive is in third
+#    e.g. tar xvzf zsh-3.0.5.tar.gz ...
+#    Could search better.  Send me the patch.
+#  - `tar' can be called anything, will use the correct name
+#  - Preferentially completes *.tar and *.TAR files in third position
+#  - unless z or Z appears in the commands, in which case prefer *.tar.gz
+#    and similar (GNU tar).
+#  - From fourth position on, if command is x or t, completes files inside
+#    archive.  This is supposed to look pretty much as if the files are
+#    in an ordinary directory hierarchy.  Handles extraction from compressed
+#    archives (GNU tar).
+#  - Anywhere -- appears, gets a list of long options to complete from
+#    tar itself (GNU tar); this needs perl.  If you have GNU tar but not
+#    perl:  your system manager is weird.
+#  - Things like --directory=... are also completed correctly.
 
-if [[ ( "$fl" = *t*f* || "$fl" = *x*f* ) && -position 4 100000 ]]; then
-  compgen -k "( $(tar tf $tf) )"
-elif [[ "$fl" = *c*f* && -position 4 100000 ]]; then
+emulate -LR zsh
+setopt extendedglob
+
+local nm=$NMATCHES tcmd="$words[2]" tf="$words[3]"
+
+if [[ $PREFIX = *=* ]]; then
+  # For GNU tar arguments like --directory=
+  IPREFIX=${PREFIX%%\=*}=
+  PREFIX=${PREFIX#*=}
+  if [[ $IPREFIX = --directory* ]]; then
+    _path_files -/
+  else
+    _files
+  fi
+elif [[ $PREFIX = --* ]]; then
+  # gnu tar, generate completions from --help
+  # ones followed by = get that as a suffix
+  local -a ownlist eqlist
+  local comp
+  $words[1] --help |
+  perl -ne 'while (/--[^[\s,='\'']+=?/g) { print "$&\n"; }' |
+  while read comp; do
+    if [[ $comp = *= ]]; then
+      eqlist[$#eqlist+1]=${comp%=}
+    else
+      ownlist[$#ownlist+1]=$comp
+    fi
+  done
+  compgen -S '=' -k eqlist
+  compgen -k ownlist
+elif [[ "$tcmd" = *[tx]*f* && $CURRENT -ge 4 ]] then
+  # Listing or extracting a particular file.  We run `tar t...'
+  # on the file, keeping the list of filenames cached, plus the
+  # name of the tarfile so we know if it changes.
+  local largs=-tf
+  [[ $words[2] = *z* ]] && largs=-tzf
+  [[ $words[2] = *Z* ]] && largs=-tZf
+  if [[ $tf != $tar_cache_name ]]; then
+    tar_cache_list=($($words[1] $largs $tf))
+    tar_cache_name=$tf
+  fi
+ 
+  local pref matched matchdir
+  # Now generate the matches.  First, treat a directory prefix
+  # separately, just like for real files.
+  [[ $PREFIX = */* ]] && pref="${PREFIX%/*}/"
+  if [[ $SUFFIX != */* ]]; then
+    # From inner to outer:
+    # Filter out anything which does not match what's on the line,
+    # remembering no / should come between $PREFIX and $SUFFIX;
+    # remove $pref from the remainder;
+    # filter out anything with extra directories beyond what we need.
+    matched=(
+    ${${${tar_cache_list##^${~PREFIX}[^/]#${~SUFFIX}*}#$pref}##*/*?}
+    )
+    # We need to separate matches with a slash at the end, where
+    # something else could be completed.  Instead of making the slash
+    # a suffix, since that wouldn't appear in the listing, we leave
+    # it but add matches with an empty suffix.
+    matchdir=(${matched##^*/})
+    (( $#matchdir )) && compadd -p "$pref" -S '' $matchdir
+    matched=(${matched##*/})
+    (( $#matched )) && compadd -p "$pref" $matched
+  else
+    # Completing in the middle:  don't trim trailing suffixes.
+    matched=(${tar_cache_list##^${~PREFIX}*${~SUFFIX}#$pref})
+    (( $#matched )) && compadd -p "$pref" $matched
+  fi
+elif [[ "$tcmd" = *c*f* && $CURRENT -ge 4 ]] then
   _files
-elif [[ "$words[CURRENT-1]" = *f* && CURRENT -eq 3 ]]; then
+elif [[ "$tcmd" = *[zZ]*f* && $CURRENT -eq 3 ]] then
+  _files -g '*.((tar|TAR).(gz|Z)|.tgz)'
+elif [[ "$tcmd" = *f* && $CURRENT -eq 3 ]] then
   _files -g '*.(tar|TAR)'
 fi

-- 
Peter Stephenson <pws@xxxxxxxxxxxxxxxxx>       Tel: +39 050 844536
WWW:  http://www.ifh.de/~pws/
Dipartimento di Fisica, Via Buonarroti 2, 56127 Pisa, Italy



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