% LR = mklib(LN,MF1,...,MFn)
% to create a library loader file LR.m
% for all subfunctions found in file(s) MF|1:n|
%
% MF: valid MATLAB file(s) with function(s)
% valid formats for each MF|1:n|:
% strings 'file'
% cell array of strings {'file1','file2',...'fileN'}
% determination of the location of MFs
% froot by <which(MF)>
% MLpath/froot by <which(MF)>
% froot.m in current folder
% fpath/froot.m in fpath folder
% fpath/froot file-not-found entry in LR
% LN: name of the library loader LR.m
% LR: root name of the library loader
% library loaders are always created in the current folder
% fh = LR (literally!) | fh = feval(LR)
% will assign handles from MF subfunctions
% to structure <fh> with fieldnames set to
% the name of the resp.ctive subfunction
% LR | help LR | help(LR)
% shows the auto-created help section
% duplicates and nested functions are listed
%
% NOTE
% - MKLIB removes an existing library loader at startup!
% - MKLIB aborts if any
% MF is a library loader
% MF.subfunction_name == LN
% - MKLIB discards ALL subfunctions in a MF by block commenting if any
% MF.subfunction_name == previous MF.subfunction_name
% - nested functions are listed but not assigned
% - users are responsible for proper code termination
% of all MFs according to conventions (END|RETURN)
% - MKLIB uses an undocumented feature of <mlint>
%
% EXAMPLE
%% assume function <flib.m> is in the current folder
% function r=mysin(v)
% r=sind(v);
% function r=mysum(v)
% r=sum(v);
%% create the library loader <goo.m>
% lib = mklib('goo','flib');
%% assign the function handles
% fh = goo % or: fh = feval(lib);
%% fh =
%% mysin: @mysin
%% mysum: @mysum
%% use a subfunction
% fh.mysin(0:30:90)
%% create an alias
% fa = fh.mysum;
% fa([30 60])
% created:
% us 05-May-2005
% modified:
% us 05-Jan-2006 17:45:18
%--------------------------------------------------------------------------------
function [lib,p]=mklib(lnam,varargin)
lrot=[];
p.magic='MKLIB';
p.ver='05-Jan-2006 17:45:18';
p.rel=version;
p.par=[];
p.lrot=lrot;
p.nf=0;
p.nc=0;
p.ca=[0 0];
p.bl=0;
p.nl=[{'M'},{'S'},{'U'},{'N'}];
p.nm=zeros(size(p.nl));
p.f=[];
p.c=[];
p.b=[];
p.s=[];
if nargout
lib=lrot;
end
if nargin < 2
help(mfilename);
return;
end
% create appropriate loader file name in CURRENT folder!
[lpat,lrot,lext]=fileparts(lnam);
lnam=fullfile(cd,[lrot,'.m']);
if exist(lnam,'file') == 2
disp(sprintf('MKLIB> loader deleted! %s',lnam));
delete(lnam);
end
p.lrot=lrot;
% common parameters
par.cr= sprintf('\n');
par.tab= sprintf('\t');
% library templates header/footer
par.eofun= '___@ENDOFFUNCTION@___';
par.nrfile= '___@MKLIBALLFILES@___';
par.nrsub= '___@MKLIBALLFUNCTIONCALLS@___';
par.nrall= '___@MKLIBALLFUNCTIONS@___';
par.nrass= '___@MKLIBALLFUNCTIONSASSIGNED@___';
par.nrfun= '___@MKLIBNRFUNCTIONS@___';
par.nrcall= '___@MKLIBNRCALLS@___';
par.valfun= '___@MKLIBNRVALIDFUNCTIONSFOUND@___';
par.buflen= '___@MKLIBFILEBUFFERLENGTH@___';
par.fmtf= ['%% file %7d :' par.tab '%s %s'];
par.fmts= ['%% %5d:%6d =' par.tab '%s %s'];
par.fmtd= ['%% %5d:%6d !' par.tab '%s %s'];
par.magic= ['% MAKELIB :' par.tab 'a function library loader generator'];
par.hdr={
par.magic
['% version :' par.tab p.ver]
['% ML release :' par.tab p.rel]
['% -------------']
['% OUTPUT >']
['% library :' par.tab lnam]
['% created :' par.tab datestr(clock)]
['% -------------']
['% INPUT <']
['% files :' par.tab par.nrfile]
['% routines :' par.tab par.nrsub]
['% subroutines :' par.tab par.nrall]
['% assigned :' par.tab par.nrass]
['% other :' par.tab par.nrcall]
['% lines :' par.tab par.buflen]
['% -------------']
};
par.fcn={
par.cr
['function' par.tab 'fh=' lrot '(varargin)']
[par.tab 'if' par.tab '~nargout']
[par.tab par.tab 'help(mfilename);']
[par.tab 'else']
[par.tab par.tab 'fh=[];']
};
par.ftr={
[par.tab par.tab par.eofun];
par.cr
};
p.par=par;
% prepare file list
argv={};
for i=1:nargin-1
if iscell(varargin{i})
targ=varargin{i};
argv=[argv;targ(:)];
else
argv=[argv;varargin(i)];
end
end
argv=argv(:);
argc=numel(argv);
flst={};
com={};
cc=0;
nc=0;
mc=0;
% read file(s) and create list of functions
for j=1:argc
fin=argv{j};
p=get_functions(j,fin,p);
end
p=prune_functions(p);
for j=1:p.nf
cmc=0;
cna=0;
cnc=0;
cnd=0;
cns=0;
f=p.f(j,:);
fnam=f{2};
sx=find([p.c{:,1}]==j);
s=p.c(sx,:);
ns=length(sx);
par.hdr{end+1,1}=sprintf(par.fmtf,j,fnam);
if ~isempty(s)
if f{6} == -1
par.hdr{end+1,1}=['% subroutines :' par.tab '0' par.tab par.tab 'script?'];
disp(sprintf('MKLIB> no subfunctions %s',fnam));
else
par.hdr{end+1,1}=['% subroutines :' par.tab par.nrfun];
for i=1:ns
snam=s{i,3};
if strcmp(snam,lrot)
disp( 'MKLIB> conflict subfunction == library name');
disp(sprintf(' %s > %s',fnam,snam));
lrot=[];
return;
end
ss=s{i,5};
switch ss
case -10
mc=mc+1;
cmc=cmc+1;
cns=cns+1;
disp(sprintf('MKLIB> -nested %s > %s',fnam,snam));
par.hdr{end+1,1}=sprintf(par.fmts,-mc,-cmc,'-nested',[par.tab snam]);
case {-1 -2 1 10 2 20}
nc=nc+1;
cnc=cnc+1;
cns=cns+1;
if ss < 0
cnd=cnd+1;
p.ca(2)=p.ca(2)+1;
disp(sprintf('MKLIB> -duplicate %s > %s',fnam,snam));
par.hdr{end+1,1}=sprintf(par.fmtd,-nc,-cnc,'-duplicate',[par.tab snam]);
else
p.ca(1)=p.ca(1)+1;
cc=cc+1;
cna=cna+1;
disp(sprintf('MKLIB> +handle %s > %s',fnam,snam));
par.hdr{end+1,1}=sprintf(par.fmts,nc,cnc,'+handle',[par.tab snam]);
com{cc,1}=sprintf('%s%sfh.%s=@%s;',par.tab,par.tab,snam,snam);
end
end
end % list of subroutines
par.hdr=strrep(par.hdr,par.nrfun,sprintf('\t\t%-1d [SUB %-1d: NST -%-1d/DUP -%-1d]',cna,cns,cmc,cnd));
end % list of functions
end
end % list of files
ftr=strrep(par.ftr,par.eofun,'end');
par.hdr=strrep(par.hdr,par.nrcall,sprintf('%-1d',p.nm(3)));
par.hdr=strrep(par.hdr,par.nrfile,sprintf('%-1d',p.nf));
par.hdr=strrep(par.hdr,par.nrsub,sprintf('%-1d',p.nc));
par.hdr=strrep(par.hdr,par.nrall,sprintf('%-1d\t[SUB: %-1d / NST: %-1d]',nc+mc,nc,mc));
par.hdr=strrep(par.hdr,par.nrass,sprintf('%-1d\t[SUB: %-1d / DUP: %-1d]',p.ca(1),nc,p.ca(2)));
par.hdr=strrep(par.hdr,par.buflen,sprintf('%-1d',p.bl));
tcom=com;
tcom{end+1,1}=[par.tab 'end'];
com=char([par.hdr;par.fcn;tcom;ftr;p.b]);
trm=repmat(par.cr,size(com,1),1);
com=[com,trm];
lnam=write_loader(lnam,com,p);
lint=mlint(lnam,'-id');
if ~isempty(lint)
id=struct2cell(lint);
id=strmatch('SyntaxErr:EndCount',id(end,:).');
if ~isempty(id)
ftr=strrep(par.ftr,par.eofun,'return');
com=char([par.hdr;par.fcn;tcom;ftr;p.b]);
com=[com,trm];
lnam=write_loader(lnam,com,p);
end
end
disp(sprintf('MKLIB> loader created %s',lnam));
if nargout
lib=lrot;
end
return;
%--------------------------------------------------------------------------------
% SUBROUTINES
%--------------------------------------------------------------------------------
function [fnam,buf]=read_file(fin,p)
buf=[];
[fpat,frot,fext]=fileparts(fin);
if isempty(fpat) && isempty(fext)
[fpat,frot,fext]=fileparts(which(fin));
end
fnam=fullfile(fpat,[frot,fext]);
if exist(fnam) ~= 2
disp(sprintf('MKLIB> file not found %s',fin));
fnam=fin;
return;
elseif isempty(fext)
fnam=fullfile(fpat,[frot,'.m']);
end
[fp,msg]=fopen(fnam,'rt');
if fp > 0
buf=fread(fp,inf,'*char').';
fclose(fp);
while ~isempty(buf) & isspace(buf(end))
buf=buf(1:end-1);
end
else
disp(sprintf('MKLIB> could not open %s',fin));
fnam=fin;
return;
end
if strfind(buf,p.par.magic) == 1
error('MKLIB> input is already a library loader\n %s',fnam);
end
return;
%--------------------------------------------------------------------------------
function lnam=write_loader(lnam,com,p)
[fp,msg]=fopen(lnam,'wb');
if fp > 0
fwrite(fp,com.','char');
fclose(fp);
else
error([msg,': ',p.par.tab,lnam]);
end
while exist(lnam,'file') ~= 2
pause(0); % allow for break!
end
return;
%--------------------------------------------------------------------------------
function p=get_functions(ix,fin,p)
ns=0;
s={};
[fnam,buf]=read_file(fin,p);
hasbuf=true;
if isempty(buf)
hasbuf=false;
buf='empty';
fnam=[fnam ' (not found or empty)'];
else
% retrieve functions from <mlint> via an undocumented option
txt=sprintf('mlint(''-calls'',''%s'');',fnam);
s=evalc(txt);
s=strread(s,'%s','delimiter','');
p.s=[p.s;s];
end
if hasbuf
tbuf= strread(buf.','%s',...
'whitespace','',...
'delimiter','');
buf=[ {sprintf('%% %s',fnam)}
'LINE'
'COMMENT BEGIN'
tbuf
'COMMENT END'
];
p.bl=p.bl+numel(tbuf);
else
buf=[ {sprintf('%% %s',fnam)}
'LINE'
];
end
buf{2}=['% ' repmat('-',1,size(fnam,2))];
% ... decode function specs
pat=sprintf('^%s|',p.nl{:});
pat=pat(1:end-1);
sx=regexp(s,pat);
sx=find(~cellfun('isempty',sx));
s=s(sx);
m=regexp(s,'^\w','match'); % function type
s=regexp(s,'\w+$','match'); % function name
m=[m{:}].';
s=[s{:}].';
ns=size(s,1);
nb=size(p.b,1);
sb=size(buf,1);
ixb=nb+1;
ixe=nb+sb;
% ... accumulate func specs
ixm=[];
for i=char(p.nl(:)).'
ixm=[ixm,numel(find(strcmp(m,i)))];
end
if ~ixm(1) && any(ixm)
ixm(1)=-1;
elseif ~any(ixm)
ixm(1)=-2;
end
p.nm=p.nm+ixm;
p.nf=p.nf+1;
p.f=[p.f;{ix,fnam,ixb,ixe,any(ixm)},num2cell(ixm)];
ixf=repmat({ix},ns,1);
ixs=num2cell(1:ns).';
ixb=repmat(num2cell(ixb),ns,1);
ixe=repmat(num2cell(ixe),ns,1);
ixm=repmat({ixm},ns,1);
p.nc=p.nc+numel(s);
p.c=[p.c;[ixf,ixs,s,m]];
% ... accumulate func contents
p.b=[p.b;buf];
return;
%--------------------------------------------------------------------------------
function p=prune_functions(p)
% nr spec
% -- -----------------
% 1 M unique
% 10 M with duplicates
% -1 M duplicate
% 2 S unique
% 20 S with duplicates
% -2 S duplicate
% -10 N
% -20 U
par=p.par;
if ~p.nc
return;
end
p.c=[p.c,repmat({0},size(p.c,1),1)];
ix=find(strncmp('N',p.c(:,4),1));
p.c(ix,5)={-10};
ix=find(strncmp('U',p.c(:,4),1));
p.c(ix,5)={-20};
% mark duplicates
ix=find(strncmp('M',p.c(:,4),1));
p.c(ix,5)={1};
for i=ix(:).'
s=p.c{i,3};
id=strmatch(s,p.c(ix,3));
if length(id) > 1
p.c(ix(id(1)),5)={10};
end
p.c(ix(id(2:end)),5)={-1};
end
ix=find(strncmp('S',p.c(:,4),1));
p.c(ix,5)={2};
for i=ix(:).'
s=p.c{i,3};
id=strmatch(s,p.c(ix,3));
if length(id) > 1
p.c(ix(id(1)),5)={20};
end
p.c(ix(id(2:end)),5)={-2};
end
% mark buffer
mrk=[p.c{:,end}];
% remove any function with duplicates using block-commenting
ix=find(abs(mrk)==1 |abs(mrk)==2);
for i=1:2
ix=find(mrk==-i);
fn=[p.c{ix,1}];
for j=fn(:).'
ixb=p.f{j,3}+2;
ixe=p.f{j,4};
p.b([ixb ixe])={'%{','%}'};
end
end
% tag scripts
ix=find([p.f{:,6}]==-1);
for i=ix(:).'
ixb=p.f{i,3}+2;
ixe=p.f{i,4};
p.b([ixb ixe])={'%{','%}'};
end
% tag valid function entries
mrk=[p.c{:,end}];
ix=find(mrk>0);
fn=unique([p.c{ix,1}]);
for i=fn(:).'
ixb=p.f{i,3}+2;
ixe=p.f{i,4};
p.b([ixb ixe])={par.valfun};
end
ix=strmatch(par.valfun,p.b,'exact');
p.b(ix)=[];
return;
%--------------------------------------------------------------------------------