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

Another tar-completion function



Here is my completion function for extracting individual files from a
tar archive.  I realise there are others around with different
argument handling, which look for a -f argument to find the archive
name, for example, whereas this simply assumes the options are all in
the first argument and the archive is the second.  This just happens
to fit my normal usage.

I'm posting it because I've improved the way files for extracting are
completed, directory by directory as with -f.  There are a couple of
limitations, noted in the comments, which are related to well-known
(and potentially removable) zsh limitations: you can't tell zsh not to
list a common prefix with completions (the directory path is omitted
if and only if zsh knows it is completing files), and you can't tell
zsh not to insert a space after a unique completion (again, a space is
not inserted if and only if zsh knows it has completed a directory).
This means that it has the annoying features that it prints overlong
completion lists and you have to backspace every time it inserts a
directory.

Anyway, this part of the script (from `Now prune the list...') could
easily be added onto any other completion function which uses
`virtual' paths.


tarnames () {
# Completion function for use with tar:
# get the names of files in the tar archive to extract.
#
# Author: Peter Stephenson <pws@xxxxxx>
#
# The main claim to fame of this particular function is that it
# completes directories in the tar-file in a manner very roughly
# consistent with `compctl -f'.  There are two bugs:  first, the whole
# path prefix up to the present is listed with ^D, not just the new
# part to add; second, after a unique completion a space is always
# inserted, even if the completion ends with a slash.  These are
# currently limitations of zsh.
#
# This only works for the (fairly common) tar argument style where
# the arguments are bunched into the first argument, and the second
# argument is the name of the tarfile.  For example,
#  tar xvzf zsh-3.1.2.tar.gz ...
# You can only use compressed/gzipped files if tar is GNU tar,
# although the correct name for the tar programme will be inferred
# (maybe you need to add gtar or gnutar to the end of the compctl).
#
# This is my completion for tar; it will do the following:
#  1) Look for .tar.gz, .taz, .tar.z, .tar.Z files for argument 2 (for gnu tar)
#  2) Offer c, t, x files at the start of argument 1 )  pretty pointless,
#  3) Offer to add v, z, f flags to argument 1       )   but, well.
#  4) When extracting (x first in arg 1), look in the tar file itself
#     (arg 2) for files to complete for subsequent arguments.
# compctl -f -x 'p[2] C[-1,*z*f]' -g '*.(taz|tar.(gz|z|Z)|tgz)' \
#  - 'p[2] C[-1,*Z*f]' -g '*.(tar.Z|taz)' \
#  - 'p[1] N[-1,ctxvzZ]' -k "(v z f)" - 'p[1] s[]' -k "(c t x)" -S '' \
#  - 'p[3,-1] W[1,x*]' -K tarnames -- tar
# With newer zsh, you might want to change the -g to -/g.

local line list=tf
read -cA line
# $line[2] is the first argument:  check for possible compression args (GNU).
# (This is harmless when used with non-GNU tar, but then the file must
# be uncompressed to be able to use it with tar anyway.)
[[ $line[2] = *z* ]] && list=tfz
# $line[1] is the command name:  something like tar or gnutar.
# $line[3] is the name of the tar archive.

# cache contents for multiple completion: note tar_cache_name
# and tar_cache_list are not local.  Assumes all files with the
# same name are the same file, even if in different directories:
# you can trick it with $PWD/file on the command line.
if [[ $line[3] != $tar_cache_name ]]; then
  tar_cache_list=($($line[1] $list $line[3]))
  tar_cache_name=$line[3]
fi

# Now prune the list to include only appropriate directories.
local file new
reply=()
if [[ $1 = */* ]]; then
  local sofar=${1%/*}/
  for file in $tar_cache_list; do
    if [[ $file = $sofar* ]]; then
      new=${file#$sofar}
      if [[ $new = */* ]]; then
	new=$sofar${new%%/*}/
      else
	new=$file
      fi
      if [[ $1 != */ || $new != $1 ]]; then
	reply=($reply $new)
      fi
    fi
  done
else
  for file in $tar_cache_list; do
    if [[ $file = */* ]]; then
      new=${file%%/*}/
    else
      new=$file
    fi
    reply=($reply $new)
  done
fi
}

-- 
Peter Stephenson <pws@xxxxxx>       Tel: +39 50 844536
WWW:  http://www.ifh.de/~pws/
Gruppo Teorico, Dipartimento di Fisica
Piazza Torricelli 2, 56100 Pisa, Italy



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