function files_out = gipper (directory, include, exclude, exclude_hidden)
%GIPPER zip selected files and subdirectories (gipper = grep + zip)
%
% files = gipper (directory, include, exclude, exclude_hidden) ;
%
% Creates a zip file of all files and subdirectories in a directory. A file in
% the directory or any of its subdirectories whose name matches any expression
% in 'include' via regexp is added to the zip file. A file that matches any
% expression in 'exclude' is not added. A subdirectory whose name or full
% pathname matches any expression in 'exclude' is not searched. The name of
% the zip file is the name of the directory, with '.zip' appended.
%
% 'include' and 'exclude' are either cells of strings, or just single strings.
%
% With no outputs, a list of files is printed and the user is prompted before
% proceeding. Otherwise, the gipper proceeds without prompting and returns a
% list of files that were added to the zip file.
%
% By default, all files and subdirectories of the directory are included, except
% that hidden files and directories (those whose names start with a dot, '.')
% are excluded.
%
% If any parameter is empty or not present, the defaults are used:
% directory: defaults to the current directory
% include: defaults to include all files and directories
% exclude: defaults to exclude nothing, as modified by 'exclude_hidden'
% exclude_hidden: 1 (exclude hidden files and directories)
%
% Empty directories or subdirectories are never included.
%
% Example:
% % suppose 'X' is the name of the current directory.
%
% % include all files in X (except hidden files) in the zip file ../X.zip
% gipper
%
% % create mytoolbox.zip archive of the 'X/mytoolbox' directory
% gipper mytoolbox
%
% % only include *.m files in ../X.zip
% gipper '' '\.m$'
%
% % create ../X.zip, but exclude compiled object and MEX files
% gipper ('', '', { '\.o$' '\.obj$', ['\.' mexext '$'] })
%
% % include everything, including hidden files, in ../X.zip
% gipper ('', '', '', 0)
%
% % zip mytoolbox, except hidden files and the mytoolbox/old directory
% gipper mytoolbox '' old
%
% % these are the same, except gipper also traverses subdirectories
% gipper ('', { '\.m$', '\.*mat$' })
% zip ('../X', { '*.m', '*.mat' })
%
% See also zip, regexp, unzip.
% NOTE: if the directory name is empty or not present, and you hit control-C
% while the gipper is running, your current directory will now be the parent.
% You must install the gipper first, by placing it in your MATLAB path.
% Copyright 2007, Timothy A. Davis, Univ. of Florida. Win one for the gipper.
% Created May 2007, using MATLAB 7.4 (R2007a). Requires MATLAB 6.5 or later.
% exclude hidden files and directories by default
if (nargin < 4)
exclude_hidden = 1 ;
end
% exclude nothing by default (as modified by exclude_hidden)
if (nargin < 3)
exclude = { } ;
end
exclude = cleanup (exclude) ;
% append the hidden file and directory rule, if requested
if (exclude_hidden)
exclude = union (exclude, { '^\.', [ '\' filesep '\.' ] }) ;
end
% always exclude '.' and '..' files
exclude = union (exclude, { '^\.$', '^\.\.$' }) ;
% include all files by default
if (nargin < 2 || isempty (include))
include = { '.' } ;
end
include = cleanup (include) ;
% operate on the current directory, if not specified
if (nargin < 1 || isempty (directory))
here = pwd ;
directory = here ((find (here == filesep, 1, 'last') + 1) : end) ;
% use try-catch so that if a failure occurs, we go back to current
% directory. Unfortunately, this mechanism does not catch a control-C.
gipper_found = 0 ;
try
% run the gipper in the parent
cd ('..') ;
% if gipper.m is not in the path, it will no longer exist
gipper_found = ~isempty (which ('gipper')) ;
if (gipper_found)
if (nargout == 0)
fprintf ('Note that if you terminate gipper with control-C, ') ;
fprintf ('your\ndirectory be changed to the parent') ;
fprintf (' (as in "cd ..").\n') ;
gipper (directory, include, exclude, exclude_hidden) ;
else
files_out = gipper (directory, include, exclude,exclude_hidden);
end
end
catch
cd (here) ;
rethrow (lasterror) ;
end
% go back to where we started
cd (here) ;
if (~gipper_found)
fprintf ('To install the gipper, type "pathtool" and add\n') ;
fprintf ('the directory in which it resides:\n') ;
fprintf ('%s\n', which (mfilename)) ;
error ('You must install the gipper first.') ;
end
return
else
if (nargout == 0)
fprintf ('\ngipper: creating %s%s%s.zip\n', pwd, filesep, directory) ;
end
end
% get the list of files to zip
n = 0 ;
files = { } ;
for file = dir (directory)'
[files, n] = finder (files, n, directory, file.name, include, exclude) ;
end
files = files (1:n)' ;
% cannot create an empty zip file
if (isempty (files))
warning ('gipper:nothing', 'nothing to zip; no zip file created') ;
if (nargout > 0)
files_out = files ;
end
return
end
% return the list of files, or confirm
if (nargout == 0)
% print the list of files and ask for confirmation first
fprintf ('Creating a zip archive containing these files:\n\n') ;
for k = 1:length(files)
fprintf (' %s\n', files {k}) ;
end
fprintf ('\nCreating the zip archive: %s', directory) ;
if (isempty (regexp (directory, '\.zip$', 'once')))
fprintf ('.zip') ;
end
fprintf ('\n') ;
reply = input ('Proceed? (yes or no, default is yes): ', 's') ;
if (~isempty (reply) && lower (reply (1)) == 'n')
fprintf ('zip file not created\n') ;
return
end
else
% zip the files without asking
files_out = files ;
end
% zip the files
zip (directory, files) ;
%-------------------------------------------------------------------------------
function [files, n] = finder (files, n, prefix, name, include, exclude)
% finder: return a list of files to zip
% fullname includes the entire path to the file or directory
fullname = [prefix filesep name] ;
if (isdir (fullname))
% always traverse a subdirectory to look for files to include, unless the
% directory name or fullname itself is explicitly excluded.
if (~(grep (name, exclude) || grep (fullname, exclude)))
% the directory is selected, recursively traverse it
for file = dir (fullname)'
[files, n] = finder (files, n, fullname, file.name, ...
include, exclude) ;
end
end
else
% this is a file, apply the include/exclude rules to just the file name
% itself not the fullname.
if (grep (name, include) && ~grep (name, exclude))
% the file is selected for the archive. Use a dynamic-table approach
% to speed up the dynamic growth of the table.
n = n + 1 ;
files {n} = fullname ;
if (n == length (files))
files {2*n} = [ ] ;
end
end
end
%-------------------------------------------------------------------------------
function match = grep (string, list)
% grep: determine if a string matches an expression in a list
match = 0 ;
for expression = list
if (~isempty (regexp (string, expression {1}, 'once')))
match = 1 ;
return ;
end
end
%-------------------------------------------------------------------------------
function s = cleanup (s)
% cleanup: ensure the input list is in the proper format
s = s (:)' ; % make sure it is a row vector
if (ischar (s))
s = { s } ; % if it is a string, convert it into a cell with one string
end