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 20:33:15 -0400
User-agent: Thunderbird 1.5.0.7 (Windows/20060909)

John W. Eaton wrote:
On 20-Oct-2006, Bill Denney wrote:

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

The cleanup code in an unwind_protect block is always executed, so the
above code does "cd (origdir") twice.
OK, I've attached a version with those changes. Also, now that I know what a bit of the difference is, I made a patch to help.cc that explains some of the difference between try and unwind_protect. Please fix anything that I got wrong there.

Bill

src/ChangeLog:

2006-10-20  Bill Denney  <address@hidden>
   * help.cc: better describe the try and unwind_protect functions
## 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{bzip2,bunzip2,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, etc.
      if any (strcmpi ({".gz" ".Z" ".bz2" ".bz"}, ext))
        [tmppathstr, tmpname, tmpext] = fileparts (name);
        if strcmpi (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 field names are the file extension without periods.
  ## The first cell is what is executed to unpack an archive.
  ## The second cell is the function to execute on output to get the
  ##   files list.
  ## The third cell 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).
  persistent commandlist;
  if (isempty (commandlist))
    commandlist.gz = {"gunzip -v -r \"%s\"", @__parse_gzip__, true};
    commandlist.z = commandlist.gz;
    commandlist.bz2 = {"bunzip2 -v \"%s\"", @__parse_bzip2__, true};
    commandlist.bz = commandlist.bz2;
    commandlist.tar = {"tar -x -v -f \"%s\"", @__parse_tar__, false};
    commandlist.targz = {"gunzip -c \"%s\" | tar -x -v", @__parse_tar__, false};
    commandlist.tgz = commandlist.targz;
    commandlist.tarbz2 = {"bunzip2 -c \"%s\" | tar -x -v", @__parse_tar__, 
false};
    commandlist.tarbz = commandlist.tarbz2;
    commandlist.tbz2 = commandlist.tarbz2;
    commandlist.tbz = commandlist.tarbz2;
    commandlist.zip = {"unzip \"%s\"", @__parse_zip__, false};
  endif

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

  if isfield (commandlist, nodotext)
    [command, parser, move] = deal (commandlist.(nodotext){:});
    cstartdir = canonicalize_file_name (origdir);
    cenddir = canonicalize_file_name (directory);
    needmove = 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));
  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: help.cc
===================================================================
RCS file: /cvs/octave/src/help.cc,v
retrieving revision 1.165
diff -u -r1.165 help.cc
--- help.cc     16 Aug 2006 06:52:19 -0000      1.165
+++ help.cc     21 Oct 2006 00:30:39 -0000
@@ -452,7 +452,12 @@
     "-*- texinfo -*-\n\
 @deffn Keyword try\n\
 Begin a try-catch block.\n\
address@hidden
+\n\
+If an error occurs within a try block, then the catch code will be run and\n\
+execution will proceed after the catch block (though it is often\n\
+recommended to use the lasterr function to re-throw the error after cleanup\n\
+is completed).\n\
address@hidden,unwind_protect}\n\
 @end deffn", }, 
 
   { "until",
@@ -466,7 +471,14 @@
     "-*- texinfo -*-\n\
 @deffn Keyword unwind_protect\n\
 Begin an unwind_protect block.\n\
address@hidden
+\n\
+If an error occurs within the first part of an unwind_protect block\n\
+the commands within the unwind_protect_cleanup block are executed before\n\
+the error is thrown.  If an error is not thrown, then the\n\
+unwind_protect_cleanup block is still executed (in other words, the\n\
+unwind_protect_cleanup will be run with or without an error in the\n\
+unwind_protect block).\n\
address@hidden,try}\n\
 @end deffn", }, 
 
   { "unwind_protect_cleanup",

reply via email to

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