function varargout = gunzip(files,varargin)
%GUNZIP Uncompress files in the GZIP format.
%   GUNZIP(FILES) uncompresses GZIP-files from the list of files specified
%   in FILES.
%
%   FILES is a string or cell array of strings containing a list of files
%   or directories. Paths specified in FILES must be either relative to the
%   current directory or absolute. Directories recursively gunzip all of
%   their content.  The output gunzipped files have the same name,
%   excluding the extension '.gz', and are written to the same directory as
%   the input files.
%
%   GUNZIP(FILES,OUTPUTDIR) writes the gunzipped file into the directory
%   OUTPUTDIR. OUTPUTDIR is created if it does not exist.
%
%   GUNZIP(URL, ...) extracts the gzip contents from an Internet URL. The
%   URL must include the protocol type (e.g., "http://"). The URL is
%   downloaded to the temp directory and deleted.
%
%   FILENAMES = GUNZIP(...) gunzips the files and returns the relative path
%   names of the gunzipped files into the string cell array, FILENAMES.
%
%   Examples
%   --------
%   % gunzip all *.gz files in the current directory
%   gunzip('*.gz');
%
%   % gunzip Cleve Moler's Numerical Computing with MATLAB examples
%   % to the output directory 'ncm'.
%   url ='http://www.mathworks.com/moler/ncm.tar.gz';
%   gunzip(url,'ncm')
%   untar('ncm/ncm.tar','ncm')
%
%   See also GZIP, TAR, UNTAR, UNZIP, ZIP.

%   Copyright 2004 The MathWorks, Inc.
%   $Revision: 1.1.6.1 $   $Date: 2004/11/23 20:39:47 $

error(nargchk(1,2,nargin,'struct'))
error(nargoutchk(0,1,nargout,'struct'));

% rootDir is always ''
dirs = {'',varargin{:}};

% Check input arguments.
[files, rootDir, outputDir] = checkFilesDirInputs(mfilename, files, dirs{:});

% Check files input for URL .
[files, url, urlFilename] = checkFilesURLInput(files, {'gz'},'FILES',mfilename);

if ~url
   % Get and gunzip the files
   entries = getArchiveEntries('', files, rootDir, mfilename);
   names = gunzipEntries(entries, outputDir);
else
   % Gunzip the URL
   names = gunzipURL(files{1}, outputDir, urlFilename);
end

% Return the names if requested
if nargout == 1
   varargout{1} = names;
end

%----------------------------------------------------------------------
function [files, url, urlFilename] = ...
   checkFilesURLInput(inputFiles, validExtensions, argName, fcnName)


if numel(inputFiles) == 1 && isempty(findstr('*',inputFiles{1}))
   [files, url] = checkfilename(inputFiles{1}, validExtensions, fcnName, ...
                                argName,true, tempdir);
   if url
     % Remove extension
     [path, urlFilename, ext] = fileparts(inputFiles{1});
     if ~any(strcmp(ext,{'.tgz','.gz'}))
        % Add the extension if the URL file is not .gz or .tgz
        % The URL may not be a GZIPPED file, but let pass
        urlFilename = [urlFilename ext];
     end
   else
     urlFilename = '';
   end
   files = {files};
else
   url = false;
   urlFilename = '';
   files = inputFiles;  
end


%----------------------------------------------------------------------
function names = gunzipEntries(entries, outputDir)
streamCopier = ...
   com.mathworks.mlwidgets.io.InterruptibleStreamCopier.getInterruptibleStreamCopier;
names = {};
for i=1:numel(entries)
  [path, baseName] = fileparts(entries(i).file);
  names{end+1} = gunzipwrite(entries(i).file, outputDir, baseName, streamCopier);
end

%----------------------------------------------------------------------
function names = gunzipURL(filename, outputDir, urlFilename)

streamCopier = ...
   com.mathworks.mlwidgets.io.InterruptibleStreamCopier.getInterruptibleStreamCopier;
try
   names{1} = gunzipwrite(filename, outputDir, urlFilename, streamCopier);
   % Filename is temporary for URL
   delete(filename);
catch
   [msg, id] = lasterr;
   if ~isequal('MATLAB:gunzip:notGzipFormat', id)
     delete(filename);
     rethrow(lasterror);
   else
     names{1} = fullfile(outputDir, urlFilename);
     if exist(names{1},'file') == 2
        delete(filename);
        eid = sprintf('MATLAB:%s:urlFileExists',mfilename);
        error(eid,'File "%s" exists and is not overwritten.',names{1});
     else
        copyfile(filename, names{1})
        delete(filename);
     end
   end
end

%----------------------------------------------------------------------
function gunzipFilename = gunzipwrite(gzipFilename, outputDir, baseName, streamCopier)
%GUNZIP GNU-unzip a file.
%
%   GUNZIP GNU-unzips the file GZIPFILENAME. OUTPUTDIR is the name of the
%   directory for the output file. BASENAME is the name of the output file.
%   STREAMCOPIER is a Java copy stream object. The output GUNZIPFILENAME is
%   the full filename of the GNU-unzipped file.

%   Copyright 2004 The MathWorks, Inc.
%   $Revision: 1.1.6.1 $   $Date: 2004/11/23 20:39:47 $

% Create the output filename from [outputDir baseName]
gunzipFilename = fullfile(outputDir,baseName);

% Create streams
fileInStream = [];

try
   fileInStream = java.io.FileInputStream(java.io.File(gzipFilename));
catch
   % Unable to access file
   if ~isempty(fileInStream)
     fileInStream.close;
   end
   eid = sprintf('MATLAB:%s:javaOpenError',mfilename);
   error(eid,'Could not open file "%s" for reading.',gzipFilename);
end

try
   gzipInStream = java.util.zip.GZIPInputStream( fileInStream );
catch
   % Not in gzip format
   if ~isempty(fileInStream)
     fileInStream.close;
   end
   eid = sprintf('MATLAB:%s:notGzipFormat',mfilename);
   error(eid,'File "%s" is not in GZIP format.',gzipFilename);
end

% gunzip the .gz file
outStream = [];
try
   javaFile  = java.io.File(gunzipFilename);
   outStream = java.io.FileOutputStream(javaFile);
   streamCopier.copyStream(gzipInStream,outStream);
catch
   if ~isempty(outStream)
      outStream.close;
   end
   gzipInStream.close;
   fileInStream.close;
   eid = sprintf('MATLAB:%s:javaOutputOpenError',mfilename);
   error(eid,'Could not open file "%s" for writing.',gunzipFilename);
end

% Cleanup and close the streams
outStream.close;
gzipInStream.close;
fileInStream.close;

if ispc
   gunzipFilename = strrep(gunzipFilename,'\','/');
end
