function [notfound,perlout]=loadlibrary(library,header,varargin)
%LOADLIBRARY Load a shared library into MATLAB. 
%   LOADLIBRARY(SHRLIB,HFILE) Loads the functions defined in
%   header file HFILE and found in library SHRLIB into MATLAB.
%
%   LOADLIBRARY(SHRLIB,@MFILE) Loads the functions defined in MFILE
%   and found in library SHRLIB into MATLAB.  MFILE is a MATLAB 
%   M-file that was previously generated by LOADLIBRARY, using the
%   MFILENAME option.  @MFILE is a function handle to that M-file.
%
%   LOADLIBRARY(SHRLIB,...,OPTIONS) Loads the library SHRLIB with one
%   or more of the following OPTIONS.  (Only the ALIAS option is
%   available when loading using a prototype file.)
%
%   OPTIONS:
%      'alias','newlibname'     Allows the library to be loaded as a 
%          different library name.
%
%      'addheader','header'     Loads the functions defined in the 
%          additional header file, 'header'. Specify the header 
%          parameter as a filename without a file extension.  
%          MATLAB does not verify the existence of the header files 
%          and ignores any that are not needed.
%
%          You can specify as many additional header files as you need 
%          using the syntax
%             LOADLIBRARY shrlib hfile ...
%                addheader addfile1 ...
%                addheader addfile2 ...          % and so on
%
%      'includepath','path'     Adds additional include path to that used.
%
%      'mfilename','filename'   Creates the prototype M-file filename.m 
%          in the current directory and uses that file to load the library.  
%          Successive LOADLIBRARY commands can specify the function handle 
%          @filename to use this prototype file as the header in loading
%          the library.  You can used this to speed up and simplify the 
%          load process.
%
%   See also UNLOADLIBRARY, LIBISLOADED.

%   Copyright 2002-2004 The MathWorks, Inc.
%   $Revision: 1.1.6.10 $  $Date: 2004/12/22 20:01:33 $

% This function requires a 'c' compiler (lcc on windows) and Perl
% both of these should be present with a standard MATLAB installation.

if (nargin == 0) 
    error('MATLAB:loadlibrary:NotEnoughInputs',...
          'The library name must be specified.');
end

%init local variables
keepcpreprocfile=false;
usetempdir=true;
perlopt={};
perlout=[];
classname=[];
mfile_name=[];
protofunction=[];
ccinclude=[];
nocpp=false;
noperl=false;
delfiles={};  %files added to this list will be deleted when done
%process optional inputs
if nargin > 2
    i=1;
    while i<=length(varargin)
        str=varargin{i};
        if ~ischar(str)
            error ('MATLAB:loadlibrary:OptionsMustBeStrings','All optional inputs must be strings');
        end
        switch (str)
            case 'notempdir'
                usetempdir=false;
                keepcpreprocfile=true;
            case 'alias'
                i=i+1;
                classname=varargin{i};
            case 'nocpp' % do not use c pre processor
                nocpp=true;
            case 'includepath'
                i=i+1;
                ccinclude=[ccinclude ' -I' varargin{i}];   
            case 'mfilename'
                i=i+1;
                mfile_name=varargin{i};
                usetempdir=false;         
            case 'addheader'
                i=i+1;
                perlopt{end+1}=varargin{i};
            case 'debug'
                if i<length(varargin)
                    i=i+1;
                    perlopt{end+1}=['-debug=' varargin{i}];
                else
                    perlopt{end+1}='-debug';
                end
            otherwise 
                error('MATLAB:loadlibrary:InvalidOption', ...
                      'Option %s is not a valid loadlibrary option.',str); 
        end
        i=i+1;
    end
end

ccinclude=[ccinclude ' -I' fullfile(matlabroot, 'extern','include')];
try 
library=lFullPath(library);
catch
    err=lasterror;
    %The system may still be able to find the library
    if ~strcmp(err.identifier,'MATLAB:loadlibrary:FileNotFound')
        rethrow(err);
    end
end
[dummy,libname,libext]=fileparts(library);

if isempty(classname)
    classname=genvarname(libname);
    if strcmp(classname,libname)~=1
        warning('MATLAB:loadlibrary:ClassRenamed',...
        'The library name is not a valid name.\nThe library will be named "%s".',classname);
    end
end

if nargin==1
    header=libname;
end

if (ischar(header))
  if isempty(strfind(header,'.'))
    header = strcat(header,'.h');
  end
  header=lFullPath(header);
  [dummy,headername,headerext]=fileparts(header);
end
     
    if (isa(header,'function_handle') ... 
            || (exist(header,'file')==2 && strcmp(headerext,'')))
        nocpp=true;
        noperl=true;
        usetempdir=false;
        if (nargin>=2)
            if isa(header,'function_handle')
                protofunction=header;
            else
                protofunction=headername;
            end
        end             
    else
        prototypes=which('prototypes.pl');
        if (nocpp)
            preprocfile=header;
        else    
            preprocfile=[headername '.i'];
            if (~keepcpreprocfile)
                delfiles={delfiles{:} preprocfile};
            end
        end
        if ispc
            lcc=fullfile(matlabroot, 'sys','lcc','bin','lcc.exe');
            if exist(lcc,'file')~=2
                error('MATLAB:loadlibrary:LccNotFound','LCC was not found at %s.',lcc);
            end
            ccinclude=[ccinclude ' -I' fullfile(matlabroot, 'sys', ...
                       'lcc','include')];
            cc=[lcc ' -noregistrylookup' ccinclude ' -E "' header '"'];
        else
            cc='cc';
            %ccinclude=''; % no extra includes
            cc=[cc ccinclude ' -E ' header ' > ' headername '.i'];
        end
    end
   

if isempty(protofunction)
    if isempty(mfile_name)    
        protofunction=genvarname([classname '_proto']);
        mfile_name=[protofunction '.m'];
        if (usetempdir) 
            delfiles={delfiles{:} mfile_name};
        end
    else
        [dummy,fn]=fileparts(mfile_name);
        mfile_name=[fn '.m'];
        protofunction=fn;
    end
end

if ~isa(protofunction,'function_handle') && exist(protofunction,'file')==3 && strcmpi(libext,mexext)
    error('MATLAB:loadlibrary:invalid_mfilename', ...
          'The mfilename is the same as the library name and will confict.');
end

savedir=pwd;

try
    if usetempdir
        cd(tempdir);
    end
    if (~nocpp)
        [res,ccout]=system(cc);
        if (res==1)
            error('MATLAB:loadlibrary:cppfailure',...
                  'Failed to preprocess the input file.\n Output from preprocessor is:%s',ccout);
        end
        if (~isempty(ccout))
            warning('MATLAB:loadlibrary:cppoutput','Message from C preprocessor:\n%s',ccout);
        end
    end

    if ~noperl
        clear(mfile_name);
        try
            perlout=perl(prototypes , preprocfile,['-outfile=' mfile_name],...
                     perlopt{:});
        catch
            error('MATLAB:loadlibrary:cannotgeneratemfile', ...
                  'Call to Perl failed.  %s\n%s\n%s', ...
                  'Possible error processing header file.', ...
                  'Output of Perl command:' ,lasterr);
        end
        i=1;
        while (exist(mfile_name,'file')~=2)
            if (i>100)
                error('MATLAB:loadlibrary:PrototypeFileNotFound',...
                      ['The mfile describing your library could not be found' ...
                       ' possible Perl or file system error.']);
            end
            pause(i*0.01);
            i=i*2;
        end;
        clear i;
    end
    
    rehash; % make shure the new m file is seen by matlab
    try
        [fcns,structs,enums]=feval(protofunction);
        loaddefinedlibrary(library,fcns,classname,structs,enums);
    catch
        err=lasterror;
        if isempty(strfind(err.identifier,'LoadFailed'))
            if length(perlout)>0
                disp 'Error loading library perl output folows';
                disp '*********';
                disp(perlout);
                disp '*********';
            end
            location=regexp(err.message,'Line:\s+(?<line>\d+)\s+Column:\s+(?<column>\d+)','names');
            msg{1}='There was an error running the loader mfile.  Use the mfilename option';
            msg{2}='to produce a file that you can debug and fix if needed.  Please report';
            msg{3}='this error to the MathWorks so we can improve this function.';
            if numel(location) >= 1
                location.line=str2double(location.line);
                msg{4}=evalc(sprintf('dbtype %s %d:%d', mfile_name, location.line-5, location.line));
            end
            err.message=sprintf('%s\n',err.message,msg{:});
            rethrow(err);
        else
            err.message=sprintf('There was an error loading the library "%s"\n%s',library,err.message);
            error(err);
        end

    end
    deltempfiles(delfiles);

    cd(savedir);

    if (nargout>=1)
        loaded=methods(['lib.' classname]);
        if (isempty(loaded) )
            warning('MATLAB:loadlibrary:nofunctions', ...
                    'No functions found in library.');
            notfound=fcns.name;
        else
            notfound=setdiff(fcns.name,loaded );
        end
    end
catch
    deltempfiles(delfiles);
    cd(savedir);
    rethrow(lasterror);
end

end % function loadlibrary

function filepath=lFullPath(srcfile)
% find the full path to a file on the path that is not an m file
% but is on the matlab path
filepath=which(srcfile);  %
if isempty(filepath) || (exist(filepath,'file')==2 &&  strcmpi(filepath(end-1:end),'.m'))
    % the next code is for relitive paths
    if ~isempty(dir(fullfile(pwd,srcfile)))
        srcfile=fullfile(pwd,srcfile);
    end
    if isempty(dir(srcfile))
        error('MATLAB:loadlibrary:FileNotFound',...
              'Could not find file %s.',srcfile);
    end
    filepath=srcfile;
end
end % function lFullPath

function deltempfiles(tempfiles)
for i=tempfiles
    if exist(i{1},'file')
        delete(i{1});
    end
end
end % function deltempfiles
