function out = CatOrSplitFiles(varargin)
% CATORSPLITFILES concatenates or splits files with an optional token
% header.
%
% CATORSPLITFILES('CAT',FILENAME,TARGETFILENAME,param,value, ...)
% FILENAMES = CATORSPLITFILES('SPLIT',TARGETFILENAME,param,value, ...)
%
% CAT (concatenate) PARAMETERS, VALUES AND DEFAULTS
% Parameter Values Defaults
% --------- ---------------- --------
% HEADER 'OFF','%','NO%' '%'
% SILENT 'OFF','ON' 'OFF'
% PROP 'OFF','ON' 'OFF'
%
% If HEADER is % (default) each file that is combined will be titled with a
% comment that begins with % and is easily recognisible. If you should
% desire to split the file up again, CATORSPLITFILES can use this comment
% to do it properly.
%
% If HEADER is NO%, the action is very similar to the default action except
% the comment does not begin with %.
%
% If HEADER is OFF, then all the files are combined into one large one. If
% this is done it may not be reversible since the only way to split them
% again is with the FUN feature.
%
% If SILENT is ON, the CATORSPLITFILE will not prompt the user before
% overwriting a file.
%
% The PROP feature is proprietary and not available to the public.
%
% which may not split the file where it was originally since it will split
% on the function definitions. If one of the original files had more than
% one function definition or if one of the original files
%
% SPLIT PARAMETERS, VALUES AND DEFAULTS
% Parameter Values Defaults
% --------- ---------------- --------
% HEADER '%','NO%','FUN' '%'
% SILENT 'OFF','ON' 'OFF'
% BACKUP 'OFF','ON' 'ON'
% SPLITTO string where it came from
%
% If HEADER is % (default) the file will be split on a comment produced
% with CATORSPLITFILES that begins with % and is easily recognisible.
%
% If HEADER is NO%, the action is very similar to the default action except
% the comment does not begin with %.
%
% If HEADER is FUN, then CATORSPLITFILE will split on function definitions
% putting each function in a different m-file.
%
% If SILENT is ON, the CATORSPLITFILE will not prompt the user before
% overwriting a file.
%
% If BACKUP is ON (default), CATORSPLITFILE will not backup files before
% overwriting them. The backed up file will have the same name, but will
% have the .bak extension.
%
% If SPLITTO is not specified, then the file will split to the location
% from where it was combined. For example, if you combined c:\ml\file1 and
% c:\ml\file2 to bigfile, bigfile will be split back into c:\ml\file1 and
% c:\ml\file2. But if you specify SPLITTO to be c:\temp, then bigfile will
% be split into c:\temp\file1 and c:\temp\file2.
%
% CATORSPLITFILE will return the names of the files that are produced when
% splitting
%
% Example
% % Combine all the files in the working directory into one
% CatOrSplitFiles('cat','ConcatenateFilesTest','tst.m','header','no%');
% % Display the combined file
% type cattest
% % Split it back up to reverse the process
% filenames = CatOrSplitFiles('split','cattest.m', ...
% 'backup','on','header','no%','splitto',pwd);
% % Look at the directory to see that the backup files have been made
% dir
%
% Uses
% ParseVarargin
%
% Used by
% SearchReplaceManyGUI
%
% keywords
% varargin cat concatenate append add split divide break up combine
% file header
%
% See also: ParseVarargin SearchReplaceManyGUI
%
%
% It's not fancy, but it works
% Michael Robbins
% michaelNOrobbinsSPAMusenet@yahoo.com
% robbins@bloomberg.net
% TO DO
% FUN parameter is not yet complete
if nargin<1 || length(varargin)<2
errordlg('This function requires at least 2 inputs.',mfilename);
end;
switch lower(varargin{1})
case 'cat',
% IDIOTPROOF INPUTS
if length(varargin)<3
errordlg('This function requires at least 3 inputs.',mfilename);
end;
filenames = varargin{2};
target = varargin{3};
if ~(iscell(filenames) || isstr(filenames)) || ~isstr(target)
errordlg('This function requires string inputs.',mfilename);
end;
% PARAMETER INPUTS
Prop = {'header','silent','prop'};
PClass = {'char','char','char'};
ParseVarargin(varargin,Prop,PClass);
% TEST
if iseq(filenames,'ConcatenateFilesTest')
d=dir;
filenames={d(3:end).name};
end;
% INPUT MUST BE A CELL ARRAY, EVEN IF IT IS ONLY ONE ITEM
if ~iscell(filenames) filenames = {filenames}; end;
% DOES TARGET EXIST?
go=1;
if exist(target,'file')==2 && ~iseq(silent,'on')
a=questdlg(['The file, "' target '" exists'],mfilename, ...
'overwrite','abort','abort');
go = iseq(a,'overwrite');
end;
if go
% DO WE NEED HEADERS?
if iseq(header,'off')
% THIS IS VERY FAST, BUT DOESN'T INCLUDE HEADERS
filenames=filenames(:);
s=sprintf('%s+',filenames{:});
dos(['copy ' s(1:end-1) ' ' target ' > c:\silent.txt.']);
else
% SLOWER BUT HAS HEADERS
FID=FopenWithErrDlg(target,'w');
for i=1:length(filenames)
if exist(filenames{i},'file')==2
% PUT IN HEADER TOKEN
switch lower(header),
case '%', s = repmat('%',1,40);
case 'no%', s='';
end;
[pathstr,name,ext]=fileparts(filenames{i});
if isempty(pathstr) pathstr=pwd; end;
fprintf(FID,['\n%s %s <%s> %s\n'], ...
s,mfilename,fullfile(pathstr,[name ext]),s);
% INCLUDE THE FILE
FID2 = FopenWithErrDlg(filenames{i},'r');
slurp = fscanf(FID2,'%c');
fclose(FID2);
fprintf(FID,'%c',slurp);
else % FILE IS NOT A FILE
if ~iseq(silent,'on')
warndlg([filenames{i} ' is not a file.'],mfilename);
end;
end;
end;
fclose(FID);
end;
end;
case 'split',
% IDIOTPROOF INPUTS
target = varargin{2};
if ~isstr(target)
errordlg('This function requires string inputs.',mfilename);
end;
% PARAMETER INPUTS
Prop = {'header','silent','backup','splitto'};
PClass = {'char','char','char','char'};
ParseVarargin(varargin,Prop,PClass);
% GET THE BIG FILE
FID = FopenWithErrDlg(target,'r');
slurp = fscanf(FID,'%c');
fclose(FID);
% EXTRACT TOKENS
% ON FUNCTION
if iseq(header,'fun')
slurp = ['\n' slurp];
[b,e]=regexp(slurp,'\n(?!\s*function)');
[dummy1,dummy2,t]=regexp(slurp,'function ...([^(\s\n]+)'); %%%%%%%%% EXPRESSIONNOT YET FINISHED
else
% ON %
regexstr = ['\n%+\s*' mfilename '\s+<([^>]+)>\s+%+\n'];
% ON NO%
if iseq(header,'no%')
regexstr = strrep(regexstr,'%+','');
end;
slurp = sprintf('\n%s\n',slurp);
[b,e,t]=regexp(slurp,regexstr);
end;
% IF I'VE GOT `EM
outi = 0;
if length(b)>1
% PAD
b(end+1)=length(slurp)+1;
% LOOP THROUGH EACH
for i=1:length(e)
% GET STUFF
filename = slurp(t{i}(1):t{i}(2));
if ~isnan(splitto)
if exist(splitto,'file')==7
[pathstr,name,ext]=fileparts(filename);
filename = fullfile(splitto,[name ext]);
else
errordlg([splitto ' is not a valid directory.'], ...
mfilename);
end;
end;
filecontent = slurp(e(i)+1:b(i+1)-1);
% OVERWRITE?
go = 1;
fileexists = exist(filename,'file');
if fileexists && iseq(silent,'on')
a=questdlg(['The file, "' filename '" exists'], ...
mfilename, 'overwrite','abort','abort');
go = iseq(a,'overwrite');
end;
% GOOD TO GO
if go
% BACKUP
if fileexists && ~iseq(backup,'off')
dos(['copy "' filename '" "' filename ...
'.bak" > c:\silent.txt.']);
end;
% OUTPUT, FINALLY!
outi = outi+1;
out{outi} = filename;
FID = FopenWithErrDlg(filename,'w');
fprintf(FID,'%c',filecontent);
fclose(FID);
end;
end;
else
errordlg(['Nothing to do. ' target ...
' contains only one file.'],mfilename);
end;
otherwise,
errordlg('Bad action.',mfilename);
end;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function out = iseq(str,val)
% ISEQ Tests if two strings are equal
%if ~iscell(str) str = {str}; end;
%if ~iscell(val) val = {val}; end;
%out = ismember(lower(str),lower(val));
% IF ONE IS A STRING
strisstr = isstr(str) || iscell(str);
valisstr = isstr(val) || iscel(val);
if strisstr || valisstr
% IF THEY'RE BOTH STRINGS
if strisstr && valisstr
out = ~isempty(strmatch(lower(str),lower(val),'exact'));
else
out = logical(0);
end;
else
out = all(str==val);
end;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function FID = FopenWithErrDlg(filename,a)
% FOPENWITHERRDLG opens a file and produces an error dialog if there's a
% problem with the process
FID=fopen(filename,a);
if FID == -1
switch a(1)
case 'w', action = 'writing';
case 'a', action = 'writing';
case 'r', action = 'reading';
end;
errordlg(['Error opening ' filename ' for ' action],mfilename);
end;