octave-maintainers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: untar.m on Solaris with non-GNU tar


From: Bill Denney
Subject: Re: untar.m on Solaris with non-GNU tar
Date: Fri, 20 Oct 2006 18:39:32 -0400
User-agent: Thunderbird 1.5.0.7 (Windows/20060909)

John Swensen wrote:
I was trying to install octave-forge packages on a Solaris machine and kept getting a "tar: tape read error" and after digging found out that it is because the Solaris provided tar executable does not include the capability to gunzip *and* untar from a single 'tar' call. Would it be possible to split the gunzip and tar process into 2 calls to accomodate?
OK, here is the code that can do it. This can unpack any of these extensions: .gz, .Z, .bz2, .bz, .tar, .tar.gz, .tgz, .tar.bz2, .tbz2, .tbz, or .zip. For full functionality, you'll need the attached patch that makes movefile.m and copyfile.m work with cellstrs as the first input (it also protects the filenames so that they spaces won't cause problems).

If everyone likes it, I'll add wrapper functions to make gunzip, untar, unzip, and bunzip2 work with this.

Bill

scripts/Changelog:

2006-10-20  Bill Denney  <address@hidden>
* miscellaneous/movefile.m, miscellaneous/copyfile.m: accept cell input and protect the filenames.
   * miscellaneous/unpack.m: new file, unpack most archive types.
## Copyright (C) 2006 Bill Denney
## 
## This file is part of Octave.
##
## Octave is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2, or (at your option)
## any later version.
##
## Octave is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING.  If not, write to the Free
## Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
## 02110-1301, USA.

## -*- texinfo -*-
## @deftypefn {Function File} address@hidden =} unpack (@var{file}, @var{dir})
## @deftypefnx {Function File} address@hidden =} unpack (@var{file}, @var{dir}, 
@var{filetype})
## Unpack the archive @var{file} based on its extension to the directory
## @var{dir}.  If @var{file} is a cellstr, then all files will be
## handled individually.  If @var{dir} is not specified, it defaults to
## the current directory.  It returns a list of @var{files}
## unpacked. If a directory is in the file list, then the
## @var{filetype} to unpack must also be specified.
##
## The @var{files} includes the entire path to the output files.
## @seealso{tar, untar, gzip, gunzip, zip, unzip}
## @end deftypefn

## Author: Bill Denney <address@hidden>

function varargout = unpack (file, directory, filetype)

  if (nargin < 1 || nargin > 3)
    print_usage ();
  endif

  if (nargin < 2)
    directory = ".";
  endif
  if (nargin < 3)
    filetype = "";
  endif

  if ischar (file)
    if isdir (file)
      if isempty (filetype)
        error ("unpack: filetype must be given for a directory");
      elseif ~ any (strcmpi (filetype, "gunzip"))
        error ("unpack: filetype must be gunzip for a directory");
      endif
    else
      [pathstr, name, ext] = fileparts (file);

      ## check to see if it's .tar.gz, .tar.Z, or .tar.bz2
      if strcmp (ext, ".gz") || strcmp (ext, ".Z") || strcmp (ext, ".bz2")
        [tmppathstr, tmpname, tmpext] = fileparts (name);
        if strcmp(tmpext, ".tar")
          name = tmpname;
          ext = [tmpext ext];
        endif
      endif

      ## if the file is a url, download it and then work with that
      ## file
      if ~ isempty (strfind (file, "://"))
        ## FIXME: the above is not a perfect test for a url
        urlfile = file;
        ## FIXME: should we name the file that we download with the
        ## same file name as the url requests?
        tmpfile = [tmpnam() ext];
        [file, success, msg] = urlwrite (urlfile, tmpfile);
        if (~ success)
          error ("unpack: could not get \"%s\": %s", urlfile, msg);
        endif
      endif

    endif

    ## canonicalize_file_name returns empty if the file isn't found, so
    ## use that to check for existence
    cfile = canonicalize_file_name (file);

    if isempty (cfile)
      error ("unpack: file \"%s\" not found.", file)
    else
      file = cfile;
    endif

  elseif iscellstr (file)
    files = {};
    for i = 1:numel (file)
      tmpfiles = unpack (file{i}, directory);
      files = {files{:} tmpfiles{:}};
    endfor

  else
    error ("unpack: invalid input file class, %s", class(file));
  endif

  ## Instructions on what to do for any extension.
  ##
  ## The first field names are the file extension without periods.
  ## The .cmd part is what is executed to unpack an archive.
  ## The .parser is the function to execute on output to get the files list.
  ## The .move part indicates if the files may need to be manually moved
  ##   (i.e. tar and unzip decompress into the current directory while
  ##   bzip2 and gzip decompress the file at its location).
  commandlist.gz.cmd = "gunzip -v -r \"%s\"";
  commandlist.gz.parser = @__parse_gzip__;
  commandlist.gz.move = true;
  commandlist.z = commandlist.gz;
  commandlist.bz2.cmd = "bunzip2 -v \"%s\"";
  commandlist.bz2.parser = @__parse_bzip2__;
  commandlist.bz2.move = true;
  commandlist.bz = commandlist.bz2;
  commandlist.tar.cmd = "tar -x -v -f \"%s\"";
  commandlist.tar.parser = @__parse_tar__;
  commandlist.tar.move = false;
  commandlist.targz.cmd = "gunzip -c \"%s\" | tar -x -v";
  commandlist.targz.parser = @__parse_tar__;
  commandlist.targz.move = false;
  commandlist.tgz = commandlist.targz;
  commandlist.tarbz2.cmd = "bunzip2 -c \"%s\" | tar -x -v";
  commandlist.tarbz2.parser = @__parse_tar__;
  commandlist.tarbz2.move = false;
  commandlist.tbz2 = commandlist.tarbz2;
  commandlist.tbz = commandlist.tarbz2;
  commandlist.zip.cmd = "unzip \"%s\"";
  commandlist.zip.parser = @__parse_zip__;
  commandlist.zip.move = false;

  nodotext = ext(~ ismember (ext, "."));
  
  origdir = pwd ();

  if isfield (commandlist, nodotext)
    command = commandlist.(nodotext).cmd;
    parser = commandlist.(nodotext).parser;
    cstartdir = canonicalize_file_name (origdir);
    cenddir = canonicalize_file_name (directory);
    needmove = commandlist.(nodotext).move && ...
        (~ strcmp(cstartdir, cenddir));
  else
    warning ("unpack:filetype", "unrecognised file type, %s", ext);
    files = file;
    return;
  endif

  ## create the directory if necessary
  s = stat (directory);
  if (isempty (s))
    [status, msg] = mkdir (directory);
    if (! status)
      error ("unpack: mkdir failed to create %s: %s", directory, msg);
    endif
  elseif (! S_ISDIR (s.mode))
    error ("unpack: %s: not a directory", directory);
  endif

  unwind_protect
    cd (directory);
    [status, output] = system (sprintf ([command " 2>&1"], file));
    cd (origdir);
  unwind_protect_cleanup
    cd (origdir);
  end_unwind_protect

  if status
    error ("unpack: unarchiving program exited with status: %d\n%s", ...
           status, output);
  endif

  ## trim the last cr if needed
  ## FIXME: will this need to change to a check for "\r\n" for windows?
  if (output(length (output)) == "\n")
    output(length (output)) = [];
  endif
  files = parser (cellstr (split (output, "\n")))';

  ## move files if necessary
  if needmove
    [st, msg, msgid] = movefile (files, directory);
    if ~ st
      error ("unpack: unable to move files to \"%s\": %s", ...
             directory, msg);
    endif

    ## fix the names for the files since they were moved.
    for i = 1:numel (files)
      files{i} = strrep (files{i}, cstartdir, cenddir);
    endfor
  endif

  ## return output if requested
  if (nargout > 0)
    varargout{1} = files;
  endif
endfunction

function files = __parse_zip__ (output)
  ## parse the output from zip and unzip

  for i = 1:length (output)
    files{i} = output{i}(14:length(output{i}));
  endfor
endfunction

## this is a noop, but it makes things simpler for other cases
function output = __parse_tar__ (output)
endfunction

function files = __parse_gzip__ (output)
  ## parse the output from gzip and gunzip returning the files
  ## commpressed (or decompressed)

  files = {};
  ## the middle ": " should indicate a good place to start looking for
  ## the filename
  for i = 1:length (output)
    colons = strfind(output{i}, ":");
    if isempty (colons)
      warning ("unpack:parsing", "Unable to parse line (gzip missing 
colon):\n%s", output{i})
    else
      midcolon = colons(ceil (length (colons)/2));
      thisstr = output{i}(midcolon+2:length(output{i}));
      idx = index (thisstr, "with") + 5;
      if isempty (idx)
        warning ("unpack:parsing", "Unable to parse line (gzip missing 
with):\n%s", output{i});
      else
        files{i} = thisstr(idx:length (thisstr));
      endif
    endif
  endfor
endfunction

function files = __parse_bzip2__ (output)
  ## parse the output from bzip2 and bunzip2 returning the files
  ## commpressed (or decompressed)

  files = {};
  for i = 1:length (output)
    ## the -5 is to remove the ".bz2:"
    endoffilename = rindex (output{i}, ": ") - 5;
    if isempty(endoffilename)
      warning("unpack:parsing", "Unable to parse line:\n%s", output{i});
    else
      files{i} = output{i}(3:endoffilename);
    endif
  endfor
endfunction
Index: copyfile.m
===================================================================
RCS file: /cvs/octave/scripts/miscellaneous/copyfile.m,v
retrieving revision 1.1
diff -u -r1.1 copyfile.m
--- copyfile.m  10 Oct 2006 19:13:49 -0000      1.1
+++ copyfile.m  20 Oct 2006 22:34:48 -0000
@@ -42,7 +42,15 @@
     else
       cmd = "/bin/cp -r";
     endif
-    [err, msg] = system (sprintf ("%s %s %s", cmd, f1, f2));
+
+    ## allow cell input and protect the file name(s)
+    if iscellstr(f1)
+      f1 = sprintf("\"%s\" ", f1{:});
+    else
+      f1 = sprintf("\"%s\" ", f1);
+    endif
+
+    [err, msg] = system (sprintf ("%s %s \"%s\"", cmd, f1, f2));
     if (err < 0)
       status = false;
       msgid = "copyfile";
Index: movefile.m
===================================================================
RCS file: /cvs/octave/scripts/miscellaneous/movefile.m,v
retrieving revision 1.1
diff -u -r1.1 movefile.m
--- movefile.m  10 Oct 2006 19:13:49 -0000      1.1
+++ movefile.m  20 Oct 2006 22:34:48 -0000
@@ -42,7 +42,15 @@
     else
       cmd = "/bin/mv";
     endif
-    [err, msg] = system (sprintf ("%s %s %s", cmd, f1, f2));
+
+    ## allow cell input and protect the file name(s)
+    if iscellstr(f1)
+      f1 = sprintf("\"%s\" ", f1{:});
+    else
+      f1 = sprintf("\"%s\" ", f1);
+    endif
+
+    [err, msg] = system (sprintf ("%s %s \"%s\"", cmd, f1, f2));
     if (err < 0)
       status = false;
       msgid = "movefile";

reply via email to

[Prev in Thread] Current Thread [Next in Thread]