function addpath_recurse(strStartDir, caStrsIgnoreDirs, strXorIntAddpathMode, blnRemDirs, blnDebug)
%ADDPATH_RECURSE Adds (or removes) the specified directory and its subfolders
% addpath_recurse(strStartDir, caStrsIgnoreDirs, strXorIntAddpathMode, blnRemDirs, blnDebug)
%
% By default, all hidden directories (preceded by '.'), overloaded method directories
% (preceded by '@'), and directories named 'private' or 'CVS' are ignored.
%
% Input Variables
% ===============
% strStartDir::
% Starting directory full path name. All subdirectories (except ignore list) will be added to the path.
% By default, uses current directory.
% caStrsIgnoreDirs::
% Cell array of strings specifying directories to ignore.
% Will also ignore all subdirectories beneath these directories.
% By default, empty list. i.e. {''}.
% strXorIntAddpathMode::
% Addpath mode, either 0/1, or 'begin','end'.
% By default, prepends.
% blnRemDirs::
% Boolean, when true will run function "in reverse", and
% recursively removes directories from starting path.
% By default, false.
% blnDebug::
% Boolean, when true prints debug info.
% By default, false.
%
% Output Variables
% ================
% None. If blnDebug is specified, diagnostic information will print to the screen.
%
% Example(s)
% ==========
% (1) addpath_recurse(); %Take all defaults.
% (2) addpath_recurse('strStartDir'); %Start at 'strStartDir', take other defaults. i.e. Do addpath().
% (3) addpath_recurse('strStartDir', '', 0, true); %Start at 'strStartDir', and undo example (2). i.e. Do rmpath().
% (4) addpath_recurse('strStartDir', '', 'end', false, true); %Do example (2) again, append to path, and display debug info.
% (5) addpath_recurse('strStartDir', '', 1, true, true); %Undo example (4), and display debug info.
%
% See Also
% ========
% addpath()
%
% Developers
% ===========
% Init Name Contact
% ---- --------------- ---------------------------------------------
% AK Anthony Kendall anthony [dot] kendall [at] gmail [dot] com
% JMcD Joe Mc Donnell
%
% Modifications
% =============
% Version Date Who What
% -------- -------- ----- --------------------------------------------------------------------
% 00.00.00 20080808 AK First created.
% 20090410 JMcD Redo input argument processing/checking.
% Only do processing/checking once. Do recursion in separate function.
% 20090411 JMcD Add debugging mode to display run info.
% 20090414 AK Modified variable names, small edits to code, ignoring CSV by default
% 20091104 AK Modified an optimization for Mac compatibility,
% recursive calls to just build the string for
% addpath/rmpath rather than call it each time
%--------------------------------------------------------------------------
%Error messages.
strErrStartDirNoExist = 'Start directory does not exist ???';
strErrIgnoreDirsType = 'Ignore directories must be a string or cell array. See HELP ???';
strErrIllAddpathMode = 'Illegal value for addpath() mode. See HELP ???';
strErrIllRevRecurseRemType = 'Illegal value for reverse recurse remove, must be a logical/boolean. See HELP ??';
strErrWrongNumArg = 'Wrong number of input arguments. See HELP ???';
strAddpathErrMessage = strErrIllAddpathMode;
%Set input args defaults and/or check them.
intNumInArgs = nargin();
assert(intNumInArgs <= 5, strErrWrongNumArg);
if intNumInArgs < 1
strStartDir = pwd();
end
if intNumInArgs < 2
caStrsIgnoreDirs = {''};
end
if intNumInArgs >= 2 && ischar(caStrsIgnoreDirs)
caStrsIgnoreDirs = { caStrsIgnoreDirs };
end
if intNumInArgs < 3 || (intNumInArgs >= 3 && isempty(strXorIntAddpathMode))
strXorIntAddpathMode = 0;
end
if intNumInArgs >= 3 && ischar(strXorIntAddpathMode) %Use 0/1 internally.
strAddpathErrMessage = sprintf('Input arg addpath() mode "%s" ???\n%s', strXorIntAddpathMode, strErrIllAddpathMode);
assert(any(strcmpi(strXorIntAddpathMode, {'begin', 'end'})), strAddpathErrMessage);
strXorIntAddpathMode = strcmpi(strXorIntAddpathMode, 'end'); %When 'end' 0 sets prepend, otherwise 1 sets append.
end
if intNumInArgs < 4
blnRemDirs = false;
end
if intNumInArgs < 5
blnDebug = false;
end
if size(caStrsIgnoreDirs, 1) > 1
caStrsIgnoreDirs = caStrsIgnoreDirs'; %Transpose from column to row vector, in theory.
end
%Check input args OK, before we do the thing.
strErrStartDirNoExist = sprintf('Input arg start directory "%s" ???\n%s', strStartDir, strErrStartDirNoExist);
assert(exist(strStartDir, 'dir') > 0, strErrStartDirNoExist);
assert(iscell(caStrsIgnoreDirs), strErrIgnoreDirsType);
assert(strXorIntAddpathMode == 0 || strXorIntAddpathMode == 1, strAddpathErrMessage);
assert(islogical(blnRemDirs), strErrIllRevRecurseRemType);
assert(islogical(blnDebug), 'Debug must be logical/boolean. See HELP.');
if blnDebug
intPrintWidth = 34;
rvAddpathModes = {'prepend', 'append'};
strAddpathMode = char(rvAddpathModes{ fix(strXorIntAddpathMode) + 1});
strRevRecurseDirModes = { 'false', 'true' };
strRevRecurseDirs = char(strRevRecurseDirModes{ fix(blnRemDirs) + 1 });
strIgnoreDirs = '';
for intD = 1 : length(caStrsIgnoreDirs)
if ~isempty(strIgnoreDirs)
strIgnoreDirs = sprintf('%s, ', strIgnoreDirs);
end
strIgnoreDirs = sprintf('%s%s', strIgnoreDirs, char(caStrsIgnoreDirs{intD}));
end
strTestModeResults = sprintf('... Debug mode, start recurse addpath arguments ...');
strTestModeResults = sprintf('%s\n%*s: "%s"', strTestModeResults, intPrintWidth, 'Start directory', strStartDir);
strTestModeResults = sprintf('%s\n%*s: "%s"', strTestModeResults, intPrintWidth, 'Ignore directories', strIgnoreDirs);
strTestModeResults = sprintf('%s\n%*s: "%s"', strTestModeResults, intPrintWidth, 'addpath() mode', strAddpathMode);
strTestModeResults = sprintf('%s\n%*s: "%s"', strTestModeResults, intPrintWidth, 'Reverse recurse remove directories', strRevRecurseDirs);
disp(strTestModeResults);
end
%Don't print the MATLAB warning if remove path string is not found
if blnRemDirs, warning('off', 'MATLAB:rmpath:DirNotFound'); end
%Build the list of directories
caAddRemDirs = {};
[caAddRemDirs] = addpath_recursively(caAddRemDirs, strStartDir, caStrsIgnoreDirs, strXorIntAddpathMode, blnRemDirs,blnDebug);
%Remove or add the directory from the search path
if blnRemDirs
if blnDebug, fprintf('"%s", removing from search path ...', strStartDir); end
rmpath(caAddRemDirs{:})
else
if blnDebug, fprintf('"%s", adding to search path ...', strStartDir); end
addpath(caAddRemDirs{:}, strXorIntAddpathMode);
end
%Restore the warning state for rmpath
if blnRemDirs, warning('on', 'MATLAB:rmpath:DirNotFound'); end
end % function addpath_recurse
%--------------------------------------------------------------------------
%--------------------------------------------------------------------------
function [caAddRemDirs] = addpath_recursively(caAddRemDirs, strStartDir, caStrsIgnoreDirs, strXorIntAddpathMode, blnRemDirs, blnDebug)
%Note:Don't need to check input arguments, because caller already has.
%Add this directory to the add/remove path list
caAddRemDirs = [caAddRemDirs,strStartDir];
strFileSep = filesep();
%Get list of directories beneath the specified directory, this two-step process is faster.
if ispc
saSubDirs = dir(sprintf('%s%s%s', strStartDir, strFileSep, '*.'));
else
saSubDirs = dir(strStartDir);
end
saSubDirs = saSubDirs([saSubDirs.isdir]); %Handles files without extensions that otherwise pass through previous filter
%Loop through the directory list and recursively call this function.
for intDirIndex = 1 : length(saSubDirs)
strThisDirName = saSubDirs(intDirIndex).name;
blnIgnoreDir = any(strcmpi(strThisDirName, { 'private', 'CVS', '.', '..', caStrsIgnoreDirs{:} }));
blnDirBegins = any(strncmp(strThisDirName, {'@', '.'}, 1));
if ~(blnIgnoreDir || blnDirBegins)
strThisStartDir = sprintf('%s%s%s', strStartDir, strFileSep, strThisDirName);
if blnDebug, fprintf('"%s", recursing ...', strThisStartDir); end
[caAddRemDirs] = addpath_recursively(caAddRemDirs, strThisStartDir, caStrsIgnoreDirs, strXorIntAddpathMode, blnRemDirs, blnDebug);
end
end % for each directory.
end % function addpath_recursively
%--------------------------------------------------------------------------
%__END__