changeset 19292:a4e993343e93

Overhaul the archive family (bzip2, gzip, zip, tar) of m-files. * NEWS: Announce changes to archive family of functions. * bunzip2.m: Redo docstring. Use common variable names between docstring and function. Remove file from test statistics. Default to placing uncompressed files in same directory as the compressed one. * bzip2.m: Redo docstring. Use common variable names between docstring and function. Add extensive BIST test. * gunzip.m: Redo docstring. Use common variable names between docstring and function. Remove file from test statistics. Default to placing uncompressed files in same directory as the compressed one. * gzip.m: Redo docstring. Remove input validation test that is no longer applicable. * __xzip__.m: Create output directory if it is named, but does not yet exist. Check return code of mkdir and issue an error if it is unsuccesful. Use the caller's name (gzip or bzip2) in error messages. Rename subfunction myfileparts to fname_only for slightly more clarity. Add FIXME note that compression of directories does not work. * tar.m: Redo docstring. Use common variable names between docstring and function. Add extensive BIST test. * untar.m: Redo docstring. Use common variable names between docstring and function. Remove file from test statistics. Default to placing uncompressed files in same directory as the compressed one. * unzip.m: Redo docstring. Use common variable names between docstring and function. Remove file from test statistics. Default to placing uncompressed files in same directory as the zip archive. * zip.m: Redo docstring. Use common variable names between docstring and function. Add extensive BIST test.
author Rik <rik@octave.org>
date Fri, 17 Oct 2014 10:45:25 -0700
parents 1f2a16d41ba2
children f8552bd35180
files NEWS scripts/miscellaneous/bunzip2.m scripts/miscellaneous/bzip2.m scripts/miscellaneous/gunzip.m scripts/miscellaneous/gzip.m scripts/miscellaneous/private/__xzip__.m scripts/miscellaneous/tar.m scripts/miscellaneous/untar.m scripts/miscellaneous/unzip.m scripts/miscellaneous/zip.m
diffstat 10 files changed, 325 insertions(+), 154 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Fri Oct 17 09:29:10 2014 +0200
+++ b/NEWS	Fri Oct 17 10:45:25 2014 -0700
@@ -53,6 +53,12 @@
     There is still one difference:  Matlab switches to '%e' and Octave
     is currently switching to '%g'.
 
+ ** The archive family of functions (bzip2, gzip, zip, tar) and their
+    unpacking routines (bunzip2, gunzip, unzip, untar, unpack) have
+    been recoded.  Excepting unpack, the default is now to place files
+    in the same directory as the archive (on unpack) or as the original
+    files (on archiving).
+
  ** Z-order stacking issues with patches, grid lines, and line object
     plot markers for on screen display and printing have all been resolved.
     For 2-D plots the axis grid lines can be placed on top of the plot
--- a/scripts/miscellaneous/bunzip2.m	Fri Oct 17 09:29:10 2014 +0200
+++ b/scripts/miscellaneous/bunzip2.m	Fri Oct 17 10:45:25 2014 -0700
@@ -17,27 +17,39 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {Function File} {} bunzip2 (@var{bzfile})
-## @deftypefnx {Function File} {} bunzip2 (@var{bzfile}, @var{dir})
-## Unpack the bzip2 archive @var{bzfile} to the directory @var{dir}.  If
-## @var{dir} is not specified, it defaults to the current directory.
+## @deftypefn  {Function File} {@var{filelist} =} bunzip2 (@var{bzfile})
+## @deftypefnx {Function File} {@var{filelist} =} bunzip2 (@var{bzfile}, @var{dir})
+## Unpack the bzip2 archive @var{bzfile}.
+##
+## If @var{dir} is specified the files are unpacked in this directory rather
+## than the one where @var{bzfile} is located.
+##
+## The optional output @var{filelist} is a list of the uncompressed files.
 ## @seealso{bzip2, unpack, gunzip, unzip, untar}
 ## @end deftypefn
 
 ## Author: Bill Denney <denney@seas.upenn.edu>
 
-function varargout = bunzip2 (bzfile, dir = ".")
+function filelist = bunzip2 (bzfile, dir = [])
 
-  if (nargin != 1 && nargin != 2)
+  if (nargin < 1 || nargin > 2)
     print_usage ();
   endif
 
+  if (isempty (dir))
+    dir = fileparts (bzfile);
+  endif
+
   if (nargout > 0)
-    varargout = cell (1, nargout);
-    [varargout{:}] = unpack (bzfile, dir, mfilename ());
+    filelist = unpack (bzfile, dir, "bunzip2");
   else
-    unpack (bzfile, dir, mfilename ());
+    unpack (bzfile, dir, "bunzip2");
   endif
 
 endfunction
 
+
+## Tests for this m-file are located in bzip2.m
+## Remove from test statistics
+%!assert (1)
+
--- a/scripts/miscellaneous/bzip2.m	Fri Oct 17 09:29:10 2014 +0200
+++ b/scripts/miscellaneous/bzip2.m	Fri Oct 17 10:45:25 2014 -0700
@@ -18,46 +18,71 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {Function File} {@var{entries} =} bzip2 (@var{files})
-## @deftypefnx {Function File} {@var{entries} =} bzip2 (@var{files}, @var{outdir})
+## @deftypefn  {Function File} {@var{filelist} =} bzip2 (@var{files})
+## @deftypefnx {Function File} {@var{filelist} =} bzip2 (@var{files}, @var{dir})
 ## Compress the list of files specified in @var{files}.
-## Each file is compressed separately and a new file with a @file{".bz2"}
-## extension is created.  The original files are not modified.  Existing
-## compressed files are silently overwritten.  If @var{outdir} is defined the
-## compressed files are placed in this directory.
-## @seealso{bunzip2, gzip, zip, tar}
+##
+## @var{files} is a character array or cell array of strings.  Shell
+## wildcards in the filename such as @samp{*} or @samp{?} are accepted and
+## expanded.  Each file is compressed separately and a new file with a
+## @file{".bz2"} extension is created.  The original files are not modified,
+## but existing compressed files will be silently overwritten. 
+##
+## If @var{dir} is defined the compressed files are placed in this directory,
+## rather than the original directory where the uncompressed file resides.
+## If @var{dir} does not exist it is created.
+##
+## The optional output @var{filelist} is a list of the compressed files.
+## @seealso{bunzip2, unpack, gzip, zip, tar}
 ## @end deftypefn
 
-function entries = bzip2 (varargin)
+function filelist = bzip2 (varargin)
 
-  if (nargin == 1 || nargin == 2)
-    if (nargout == 0)
-      __xzip__ ("bzip2", "bz2", "bzip2 %s", varargin{:});
-    else
-      entries = __xzip__ ("bzip2", "bz2", "bzip2 %s", varargin{:});
-    endif
+  if (nargin < 1 || nargin > 2 || nargout > 1)
+    print_usage ();
+  endif
+
+  if (nargout == 0)
+    __xzip__ ("bzip2", "bz2", "bzip2 %s", varargin{:});
   else
-    print_usage ();
+    filelist = __xzip__ ("bzip2", "bz2", "bzip2 %s", varargin{:});
   endif
 
 endfunction
 
 
 %!xtest
-%! ## test for correct cleanup of temporary files
+%! ## test bzip2 together with bunzip2
 %! unwind_protect
-%!   filename = tmpnam;
-%!   dummy    = 1;
+%!   filename = tempname;
+%!   dummy    = pi;
 %!   save (filename, "dummy");
-%!   n_tmpfiles_before = length (find (strncmp ("oct-", cellstr (ls (tempdir)), 4)));
-%!   entry = bzip2 (filename);
-%!   n_tmpfiles_after = length (find (strncmp ("oct-", cellstr (ls (tempdir)), 4)));
-%!   if (n_tmpfiles_before != n_tmpfiles_after)
-%!     error ("bzip2 has not cleaned up temporary files correctly!");
+%!   dirname  = tempname;
+%!   mkdir (dirname);
+%!   filelist = bzip2 (filename, dirname);
+%!   filelist = filelist{1};
+%!   [~, basename, extension] = fileparts (filename);
+%!   if (! strcmp (filelist, [dirname, filesep, basename, extension, ".bz2"]))
+%!     error ("bzipped file does not match expected name!");
+%!   endif
+%!   if (! exist (filelist, "file"))
+%!     error ("bzipped file cannot be found!");
+%!   endif
+%!   bunzip2 (filelist);
+%!   fid = fopen (filename, "rb");
+%!   assert (fid >= 0);
+%!   orig_data = fread (fid);
+%!   fclose (fid);
+%!   fid = fopen ([dirname filesep basename extension], "rb");
+%!   assert (fid >= 0);
+%!   new_data = fread (fid);
+%!   fclose (fid);
+%!   if (orig_data != new_data)
+%!     error ("bunzipped file not equal to original file!");
 %!   endif
 %! unwind_protect_cleanup
 %!   delete (filename);
-%!   [path, basename, extension] = fileparts (filename);
-%!   delete ([basename, extension, ".bz2"]);
+%!   delete ([dirname, filesep, basename, extension]);
+%!   rmdir (dirname);
 %! end_unwind_protect
 
--- a/scripts/miscellaneous/gunzip.m	Fri Oct 17 09:29:10 2014 +0200
+++ b/scripts/miscellaneous/gunzip.m	Fri Oct 17 10:45:25 2014 -0700
@@ -17,8 +17,8 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {Function File} {@var{files} =} gunzip (@var{gzfile})
-## @deftypefnx {Function File} {@var{files} =} gunzip (@var{gzfile}, @var{dir})
+## @deftypefn  {Function File} {@var{filelist} =} gunzip (@var{gzfile})
+## @deftypefnx {Function File} {@var{filelist} =} gunzip (@var{gzfile}, @var{dir})
 ## Unpack the gzip archive @var{gzfile}.
 ##
 ## If @var{gzfile} is a directory, all gzfiles in the directory will be
@@ -27,13 +27,13 @@
 ## If @var{dir} is specified the files are unpacked in this directory rather
 ## than the one where @var{gzfile} is located.
 ##
-## The optional output @var{files} is a list of the uncompressed files.
-## @seealso{gzip, bunzip2, unzip, untar, unpack}
+## The optional output @var{filelist} is a list of the uncompressed files.
+## @seealso{gzip, unpack, bunzip2, unzip, untar}
 ## @end deftypefn
 
 ## Author: Bill Denney <denney@seas.upenn.edu>
 
-function files = gunzip (gzfile, dir)
+function filelist = gunzip (gzfile, dir = [])
 
   if (nargin < 1 || nargin > 2)
     print_usage ();
@@ -44,7 +44,7 @@
   endif
 
   if (nargout > 0)
-    files = unpack (gzfile, dir, "gunzip");
+    filelist = unpack (gzfile, dir, "gunzip");
   else
     unpack (gzfile, dir, "gunzip");
   endif
@@ -54,5 +54,5 @@
 
 ## Tests for this m-file are located in gzip.m
 ## Remove from test statistics
-%!assert (1);
+%!assert (1)
 
--- a/scripts/miscellaneous/gzip.m	Fri Oct 17 09:29:10 2014 +0200
+++ b/scripts/miscellaneous/gzip.m	Fri Oct 17 10:45:25 2014 -0700
@@ -21,16 +21,20 @@
 ## @deftypefnx {Function File} {@var{filelist} =} gzip (@var{files}, @var{dir})
 ## Compress the list of files and directories specified in @var{files}.
 ##
-## @var{files} is a character array or cell array of strings.  Each file is
-## compressed separately and a new file with a @file{".gz"} extension is
-## created.  The original files are not modified, but, existing compressed
-## files will be silently overwritten.  If a directory is specified then
-## @code{gzip} recursively compresses all files in the directory.
+## @var{files} is a character array or cell array of strings.  Shell
+## wildcards in the filename such as @samp{*} or @samp{?} are accepted and
+## expanded.  Each file is compressed separately and a new file with a
+## @file{".gz"} extension is created.  The original files are not modified,
+## but existing compressed files will be silently overwritten. If a directory
+## is specified then @code{gzip} recursively compresses all files in the
+## directory.
 ##
-## If @var{dir} is defined the compressed files are placed in this directory.
+## If @var{dir} is defined the compressed files are placed in this directory,
+## rather than the original directory where the uncompressed file resides.
+## If @var{dir} does not exist it is created.
 ##
 ## The optional output @var{filelist} is a list of the compressed files.
-## @seealso{gunzip, bzip2, zip, tar, unpack}
+## @seealso{gunzip, unpack, bzip2, zip, tar}
 ## @end deftypefn
 
 function filelist = gzip (varargin)
@@ -85,6 +89,5 @@
 
 %!error gzip ()
 %!error gzip ("1", "2", "3")
-%!error <output directory does not exist> gzip ("1", tempname)
 %!error <FILES must be a character array or cellstr> gzip (1)
 
--- a/scripts/miscellaneous/private/__xzip__.m	Fri Oct 17 09:29:10 2014 +0200
+++ b/scripts/miscellaneous/private/__xzip__.m	Fri Oct 17 10:45:25 2014 -0700
@@ -18,7 +18,7 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} {@var{entries} =} __xzip__ (@var{commandname}, @var{extension}, @var{commandtemplate}, @var{files}, @var{outdir})
+## @deftypefn {Function File} {@var{filelist} =} __xzip__ (@var{commandname}, @var{extension}, @var{commandtemplate}, @var{files}, @var{outdir})
 ## Undocumented internal function.
 ## @end deftypefn
 
@@ -30,23 +30,28 @@
 ## are not touched. Existing compressed files are silently overwritten.
 ## This is an internal function. Do not use directly.
 
-function entries = __xzip__ (commandname, extension,
-                             commandtemplate, files, outdir)
+function filelist = __xzip__ (commandname, extension, commandtemplate,
+                              files, outdir)
 
   if (nargin == 5 && ! exist (outdir, "dir"))
-    error ("__xzip__: OUTDIR output directory does not exist");
+    r = mkdir (outdir);
+    if (! r)
+      error ("%s: Failed to create output directory DIR", commandname);
+    endif
   endif
 
   if (ischar (files))
     files = cellstr (files);
-  endif
-  if (! iscellstr (files))
-    error ("__xzip__: FILES must be a character array or cellstr");
+  elseif (! iscellstr (files))
+    error ("%s: FILES must be a character array or cellstr", commandname);
   endif
 
   if (nargin == 4)
-    outdir = tmpnam ();
-    mkdir (outdir);
+    outdir = tempname ();
+    r = mkdir (outdir);
+    if (! r)
+      error ("%s: Failed to create temporary output directory", commandname);
+    endif
   endif
 
   cwd = pwd ();
@@ -61,31 +66,35 @@
 
     copyfile (files, outdir);
 
-    [d, f] = myfileparts (files);
+    fnames = fname_only (files);
 
     cd (outdir);
 
-    cmd = sprintf (commandtemplate, sprintf (" %s", f{:}));
+    cmd = sprintf (commandtemplate, sprintf (" %s", fnames{:}));
 
     [status, output] = system (cmd);
     if (status)
-      error ("__xzip__: %s command failed with exit status = %d",
-             commandname, status);
+      fcn = evalin ("caller", "mfilename ()");
+      error ("%s: %s command failed with exit status = %d",
+             fcn, commandname, status);
     endif
 
     if (nargin == 5)
       if (nargout > 0)
-        entries = cellfun(
+        ## FIXME: This doesn't work if a directory is compressed
+        filelist = cellfun (
             @(x) fullfile (outdir, sprintf ("%s.%s", x, extension)),
-            f, "uniformoutput", false);
+            fnames, "uniformoutput", false);
       endif
     else
-      movefile (cellfun (@(x) sprintf ("%s.%s", x, extension), f,
-                        "uniformoutput", false), cwd);
+      ## FIXME: This does not work when you try to compress directories
+      ##        The resulting name is dir/.gz which is totally wrong.
+      ##        See bug #43431.
+      movefile (cellfun (@(x) sprintf ("%s.%s", x, extension),
+                         fnames, "uniformoutput", false), cwd);
       if (nargout > 0)
-        ## FIXME: This does not work when you try to compress directories
-        entries  = cellfun (@(x) sprintf ("%s.%s", x, extension),
-                            files, "uniformoutput", false);
+        filelist  = cellfun (@(x) sprintf ("%s.%s", x, extension),
+                             files, "uniformoutput", false);
       endif
     endif
 
@@ -99,34 +108,10 @@
 
 endfunction
 
-function [d, f] = myfileparts (files)
-  [d, f, ext] = cellfun ("fileparts", files, "uniformoutput", false);
-  f = cellfun (@(x, y) sprintf ("%s%s", x, y), f, ext,
-               "uniformoutput", false);
+function f = fname_only (files)
+  [~, f, ext] = cellfun ("fileparts", files, "uniformoutput", false);
+  f = cellfun (@(x, y) sprintf ("%s%s", x, y), f, ext, "uniformoutput", false);
   idx = cellfun ("isdir", files);
-  d(idx) = "";
   f(idx) = files(idx);
 endfunction
 
-
-## FIXME: Reinstate tests if we invent a way to test private functions directly.
-##
-## %!error <extension has to be a string with finite length>
-## %!  __xzip__ ("gzip", "", "gzip -r %s", "bla");
-## %!error <no files to move>
-## %!  __xzip__ ("gzip", ".gz", "gzip -r %s", tmpnam);
-## %!error <command failed with exit status>
-## %!  # test __xzip__ with invalid compression command
-## %!  unwind_protect
-## %!    filename = tmpnam;
-## %!    dummy    = 1;
-## %!    save (filename, "dummy");
-## %!    dirname  = tmpnam;
-## %!    mkdir (dirname);
-## %!    entry = __xzip__ ("gzip", ".gz", "xxxzipxxx -r %s 2>/dev/null",
-## %!                     filename, dirname);
-## %!  unwind_protect_cleanup
-## %!    delete (filename);
-## %!    rmdir (dirname);
-## %!  end_unwind_protect
-
--- a/scripts/miscellaneous/tar.m	Fri Oct 17 09:29:10 2014 +0200
+++ b/scripts/miscellaneous/tar.m	Fri Oct 17 10:45:25 2014 -0700
@@ -17,37 +17,46 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {Function File} {@var{entries} =} tar (@var{tarfile}, @var{files})
-## @deftypefnx {Function File} {@var{entries} =} tar (@var{tarfile}, @var{files}, @var{root})
-## Pack @var{files} @var{files} into the TAR archive @var{tarfile}.  The
-## list of files must be a string or a cell array of strings.
+## @deftypefn  {Function File} {@var{filelist} =} tar (@var{tarfile}, @var{files})
+## @deftypefnx {Function File} {@var{filelist} =} tar (@var{tarfile}, @var{files}, @var{rootdir})
+## Pack the list of files and directories specified in @var{files} into the
+## TAR archive @var{tarfile}.
 ##
-## The optional argument @var{root} changes the relative path of @var{files}
-## from the current directory.
+## @var{files} is a character array or cell array of strings.  Shell
+## wildcards in the filename such as @samp{*} or @samp{?} are accepted and
+## expanded.  Directories are recursively traversed and all files are added to
+## the archive.
 ##
-## If an output argument is requested the entries in the archive are
-## returned in a cell array.
-## @seealso{untar, bzip2, gzip, zip}
+## If @var{rootdir} is defined then any files without absolute pathnames are
+## located relative to @var{rootdir} rather than the current directory.
+##
+## The optional output @var{filelist} is a list of the files that were included
+## in the archive.
+## @seealso{untar, unpack, bzip2, gzip, zip}
 ## @end deftypefn
 
 ## Author: Søren Hauberg <hauberg@gmail.com>
 
-function entries = tar (tarfile, files, root = ".")
+function filelist = tar (tarfile, files, rootdir = ".")
 
   if (nargin < 2 || nargin > 3)
     print_usage ();
   endif
 
-  if (ischar (files))
+  if (! ischar (tarfile))
+    error ("tar: TARFILE must be a string");
+  elseif (ischar (files))
     files = cellstr (files);
+  elseif (! iscellstr (files))
+    error ("tar: FILES must be a character array or cellstr");
   endif
 
-  if (! (ischar (tarfile) && iscellstr (files) && ischar (root)))
-    error ("tar: all arguments must be character strings");
-  endif
+  rootdir = tilde_expand (rootdir);
 
-  cmd = sprintf ("tar cvf %s -C %s %s", tarfile, root,
-                 sprintf (" %s", files{:}));
+  tarfile = make_absolute_filename (tarfile);
+       
+  cmd = sprintf ("tar cvf %s -C %s %s",
+                          tarfile, rootdir, sprintf (" %s", files{:}));
 
   [status, output] = system (cmd);
 
@@ -59,9 +68,61 @@
     if (output(end) == "\n")
       output(end) = [];
     endif
-    entries = ostrsplit (output, "\n");
-    entries = entries';
+    filelist = ostrsplit (output, "\n");
+    filelist = filelist';
   endif
 
 endfunction
 
+
+%!xtest
+%! ## test gzip together with gunzip
+%! unwind_protect
+%!   dirname = tempname;
+%!   assert (mkdir (dirname));
+%!   dirname2 = fullfile (dirname, "dir2");
+%!   assert (mkdir (dirname2));
+%!   fname1 = fullfile (dirname, "f1");
+%!   fname2 = fullfile (dirname2, "f2");
+%!   fid = fopen (fname1, "wt");
+%!   assert (fid >= 0);
+%!   fdisp (fid, "Hello World");
+%!   fclose (fid); 
+%!   fid = fopen (fname2, "wt");
+%!   assert (fid >= 0);
+%!   fdisp (fid, "Goodbye World");
+%!   fclose (fid); 
+%!   tarname = [tempname ".tar"];
+%!   filelist = tar (tarname, {dirname2, fname1});
+%!   if (! strcmp (filelist{3}, fname1))
+%!     error ("tar file contents does not match expected file");
+%!   endif
+%!   if (! exist (tarname, "file"))
+%!     error ("tar archive file cannot be found!");
+%!   endif
+%!   outdir = tempname;
+%!   untar (tarname, outdir);
+%!   fid = fopen (fullfile (outdir, fname1), "rt");
+%!   assert (fid >= 0);
+%!   str = fgetl (fid);
+%!   fclose (fid);
+%!   assert (str, "Hello World");
+%!   fid = fopen (fullfile (outdir, fname2), "rt");
+%!   assert (fid >= 0);
+%!   str = fgetl (fid);
+%!   fclose (fid);
+%!   assert (str, "Goodbye World");
+%! unwind_protect_cleanup
+%!   delete (tarname);
+%!   confirm_recursive_rmdir (false, "local");
+%!   rmdir (dirname, "s");
+%!   rmdir (outdir, "s");
+%! end_unwind_protect
+
+## Test input validation
+%!error tar ()
+%!error tar (1)
+%!error tar (1,2,3,4)
+%!error <TARFILE must be a string> tar (1, "foobar")
+%!error <FILES must be a character array or cellstr> tar ("foobar", 1)
+
--- a/scripts/miscellaneous/untar.m	Fri Oct 17 09:29:10 2014 +0200
+++ b/scripts/miscellaneous/untar.m	Fri Oct 17 10:45:25 2014 -0700
@@ -19,26 +19,38 @@
 ## -*- texinfo -*-
 ## @deftypefn  {Function File} {} untar (@var{tarfile})
 ## @deftypefnx {Function File} {} untar (@var{tarfile}, @var{dir})
-## Unpack the TAR archive @var{tarfile} to the directory @var{dir}.
-## If @var{dir} is not specified, it defaults to the current directory.
+## Unpack the TAR archive @var{tarfile}.
+##
+## If @var{dir} is specified the files are unpacked in this directory rather
+## than the one where @var{tarfile} is located.
+##
+## The optional output @var{filelist} is a list of the uncompressed files.
 ## @seealso{tar, unpack, bunzip2, gunzip, unzip}
 ## @end deftypefn
 
 ## Author: Søren Hauberg <hauberg@gmail.com>
 ## Adapted-By: jwe, Bill Denney
 
-function varargout = untar (tarfile, dir = ".")
+function filelist = untar (tarfile, dir = [])
 
-  if (nargin != 1 && nargin != 2)
+  if (nargin < 1 || nargin > 2)
     print_usage ();
   endif
 
+  if (isempty (dir))
+    dir = fileparts (tarfile);
+  endif
+
   if (nargout > 0)
-    varargout = cell (1, nargout);
-    [varargout{:}] = unpack (tarfile, dir, mfilename ());
+    filelist = unpack (tarfile, dir, "untar");
   else
-    unpack (tarfile, dir, mfilename ());
+    unpack (tarfile, dir, "untar");
   endif
 
 endfunction
 
+
+## Tests for this m-file are located in tar.m
+## Remove from test statistics
+%!assert (1)
+
--- a/scripts/miscellaneous/unzip.m	Fri Oct 17 09:29:10 2014 +0200
+++ b/scripts/miscellaneous/unzip.m	Fri Oct 17 10:45:25 2014 -0700
@@ -17,28 +17,40 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {Function File} {} unzip (@var{zipfile})
-## @deftypefnx {Function File} {} unzip (@var{zipfile}, @var{dir})
-## Unpack the ZIP archive @var{zipfile} to the directory @var{dir}.
-## If @var{dir} is not specified, it defaults to the current directory.
+## @deftypefn  {Function File} {@var{filelist} =} unzip (@var{zipfile})
+## @deftypefnx {Function File} {@var{filelist} =} unzip (@var{zipfile}, @var{dir})
+## Unpack the ZIP archive @var{zipfile}.
+##
+## If @var{dir} is specified the files are unpacked in this directory rather
+## than the one where @var{zipfile} is located.
+##
+## The optional output @var{filelist} is a list of the uncompressed files.
 ## @seealso{zip, unpack, bunzip2, gunzip, untar}
 ## @end deftypefn
 
 ## Author: Søren Hauberg <hauberg@gmail.com>
 ## Adapted-By: jwe, Bill Denney
 
-function varargout = unzip (zipfile, dir = ".")
+function filelist = unzip (zipfile, dir = [])
 
-  if (nargin != 1 && nargin != 2)
+  if (nargin < 1 || nargin > 2)
     print_usage ();
   endif
 
+  if (isempty (dir))
+    dir = fileparts (zipfile);
+  endif
+
   if (nargout > 0)
-    varargout = cell (1, nargout);
-    [varargout{:}] = unpack (zipfile, dir, mfilename ());
+    filelist = unpack (zipfile, dir, "unzip");
   else
-    unpack (zipfile, dir, mfilename ());
+    unpack (zipfile, dir, "unzip");
   endif
 
 endfunction
 
+
+## Tests for this m-file are located in zip.m
+## Remove from test statistics
+%!assert (1)
+
--- a/scripts/miscellaneous/zip.m	Fri Oct 17 09:29:10 2014 +0200
+++ b/scripts/miscellaneous/zip.m	Fri Oct 17 10:45:25 2014 -0700
@@ -17,35 +17,46 @@
 ## <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {Function File} {@var{entries} =} zip (@var{zipfile}, @var{files})
-## @deftypefnx {Function File} {@var{entries} =} zip (@var{zipfile}, @var{files}, @var{rootdir})
-## Compress the list of files and/or directories specified in @var{files}
-## into the archive @var{zipfile} in the same directory.  If @var{rootdir}
-## is defined the @var{files} are located relative to @var{rootdir} rather
-## than the current directory.
-## @seealso{unzip, bzip2, gzip, tar}
+## @deftypefn  {Function File} {@var{filelist} =} zip (@var{zipfile}, @var{files})
+## @deftypefnx {Function File} {@var{filelist} =} zip (@var{zipfile}, @var{files}, @var{rootdir})
+## Compress the list of files and directories specified in @var{files} into the
+## ZIP archive @var{zipfile}.
+##
+## @var{files} is a character array or cell array of strings.  Shell
+## wildcards in the filename such as @samp{*} or @samp{?} are accepted and
+## expanded.  Directories are recursively traversed and all files are
+## compressed and added to the archive.
+##
+## If @var{rootdir} is defined then any files without absolute pathnames are
+## located relative to @var{rootdir} rather than the current directory.
+##
+## The optional output @var{filelist} is a list of the files that were included
+## in the archive.
+## @seealso{unzip, unpack, bzip2, gzip, tar}
 ## @end deftypefn
 
 ## Author: Sylvain Pelissier <sylvain.pelissier@gmail.com>
 
-function entries = zip (zipfile, files, rootdir = ".")
+function filelist = zip (zipfile, files, rootdir = ".")
 
-  if (nargin != 2 && nargin != 3)
+  if (nargin < 2 || nargin > 3)
     print_usage ();
   endif
 
+  if (! ischar (zipfile))
+    error ("zip: ZIPFILE must be a string");
+  elseif (ischar (files))
+    files = cellstr (files);
+  elseif (! iscellstr (files))
+    error ("zip: FILES must be a character array or cellstr");
+  endif
+  
   rootdir = tilde_expand (rootdir);
 
-  if (ischar (files))
-    files = cellstr (files);
-  endif
-
-  if (! ischar (zipfile) && ! iscellstr (files))
-    error ("zip: expecting all arguments to be character strings");
-  endif
-
-  cmd = sprintf ("cd %s; zip -r %s/%s %s", rootdir, pwd (), zipfile,
-                 sprintf (" %s", files{:}));
+  zipfile = make_absolute_filename (zipfile);
+       
+  cmd = sprintf ("cd %s; zip -r %s %s",
+                     rootdir,   zipfile, sprintf (" %s", files{:}));
 
   [status, output] = system (cmd);
 
@@ -54,16 +65,60 @@
   endif
 
   if (nargout > 0)
-    cmd = sprintf ("unzip -Z -1 %s", zipfile);
-    [status, entries] = system (cmd);
+    cmd = ["unzip -Z -1 " zipfile];
+    [status, filelist] = system (cmd);
     if (status)
       error ("zip: zipinfo failed with exit status = %d", status);
     endif
-    if (entries(end) == "\n")
-      entries(end) = [];
+    if (filelist(end) == "\n")
+      filelist(end) = [];
     endif
-    entries = ostrsplit (entries, "\n");
+    filelist = ostrsplit (filelist, "\n");
   endif
 
 endfunction
 
+
+%!xtest
+%! ## test zip together with unzip
+%! unwind_protect
+%!   filename = tempname;
+%!   tmp_var  = pi;
+%!   save (filename, "tmp_var");
+%!   dirname = tempname;
+%!   mkdir (dirname);
+%!   zipfile = tempname;
+%!   [~, basename, ext] = fileparts (filename);
+%!   filelist = zip (zipfile, [basename ext], tempdir);
+%!   filelist = filelist{1};
+%!   if (! strcmp (filelist, [basename ext]))
+%!     error ("zip archive does not contain expected name!");
+%!   endif
+%!   if (! exist ([zipfile ".zip"], "file"))
+%!     error ("zip file cannot be found!");
+%!   endif
+%!   unzip ([zipfile ".zip"], dirname);
+%!   fid = fopen (filename, "rb");
+%!   assert (fid >= 0);
+%!   orig_data = fread (fid);
+%!   fclose (fid);
+%!   fid = fopen ([dirname filesep basename ext], "rb");
+%!   assert (fid >= 0);
+%!   new_data = fread (fid);
+%!   fclose (fid);
+%!   if (orig_data != new_data)
+%!     error ("unzipped file not equal to original file!");
+%!   endif
+%! unwind_protect_cleanup
+%!   delete (filename);
+%!   delete ([dirname, filesep, basename, extension]);
+%!   rmdir (dirname);
+%! end_unwind_protect
+
+## Test input validation
+%!error zip ()
+%!error zip (1)
+%!error zip (1,2,3,4)
+%!error <ZIPFILE must be a string> zip (1, "foobar")
+%!error <FILES must be a character array or cellstr> zip ("foobar", 1)
+