KarrLab/SynNetGen

View on GitHub
lib/m2html/m2html.m

Summary

Maintainability
Test Coverage
function m2html(varargin)
%M2HTML - Documentation Generator for Matlab M-files and Toolboxes in HTML
%  M2HTML by itself generates an HTML documentation of the Matlab M-files found
%  in the direct subdirectories of the current directory. HTML files are 
%  written in a 'doc' directory (created if necessary). All the others options
%  are set to default (in brackets in the following).
%  M2HTML('PropertyName1',PropertyValue1,'PropertyName2',PropertyValue2,...)
%  sets multiple option values. The list of option names and default values is:
%    o mFiles - Cell array of strings or character array containing the
%       list of M-files and/or directories of M-files for which an HTML
%       documentation will be built (use relative paths without backtracking).
%       Launch M2HTML one directory above the directory your wanting to
%       generate documentation for  [ <all direct subdirectories> ]
%    o htmlDir - Top level directory for generated HTML files [ 'doc' ]
%    o recursive - Process subdirectories recursively [ on | {off} ]
%    o source - Include Matlab source code in the generated documentation
%                               [ {on} | off ]
%    o download - Add a link to download each M-file separately [ on | {off} ]
%    o syntaxHighlighting - Source Code Syntax Highlighting [ {on} | off ]
%    o tabs - Replace '\t' (horizontal tab) in source code by n white space
%        characters [ 0 ... {4} ... n ]
%    o globalHypertextLinks - Hypertext links among separate Matlab 
%        directories [ on | {off} ]
%    o todo - Create a TODO list in each directory summarizing all the
%        '% TODO %' lines found in Matlab code [ on | {off}]
%    o graph - Compute a dependency graph using GraphViz [ on | {off}]
%        'dot' required, see <http://www.graphviz.org/>
%    o indexFile - Basename of the HTML index file [ 'index' ]
%    o extension - Extension of generated HTML files [ '.html' ]
%    o template - HTML template name to use [ {'blue'} | 'frame' | ... ]
%    o search - Add a PHP search engine [ on | {off}] - beta version!
%    o ignoredDir - List of directories to be ignored [ {'.svn' 'cvs'} ]
%    o save - Save current state after M-files parsing in 'm2html.mat' 
%        in directory htmlDir [ on | {off}]
%    o load - Load a previously saved '.mat' M2HTML state to generate HTML 
%        files once again with possibly other options [ <none> ]
%    o verbose - Verbose mode [ {on} | off ]
%
%  For more information, please read the M2HTML tutorial and FAQ at:
%    <http://www.artefact.tk/software/matlab/m2html/>
%
%  Examples:
%    >> m2html('mfiles','matlab', 'htmldir','doc');
%    >> m2html('mfiles',{'matlab/signal' 'matlab/image'}, 'htmldir','doc');
%    >> m2html('mfiles','matlab', 'htmldir','doc', 'recursive','on');
%    >> m2html('mfiles','mytoolbox', 'htmldir','doc', 'source','off');
%    >> m2html('mfiles','matlab', 'htmldir','doc', 'global','on');
%    >> m2html( ... , 'template','frame', 'index','menu');
%
%  See also MWIZARD, MDOT, TEMPLATE.

%  Copyright (C) 2005 Guillaume Flandin <Guillaume@artefact.tk>
%  $Revision: 1.5 $Date: 2005/05/01 16:15:30 $

%  This program is free software; you can redistribute it and/or
%  modify it under the terms of the GNU General Public License
%  as published by the Free Software Foundation; either version 2
%  of the License, or any later version.
% 
%  This program is distributed in the hope that it will be useful,
%  but WITHOUT ANY WARRANTY; without even the implied warranty of
%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%  GNU General Public License for more details.
% 
%  You should have received a copy of the GNU General Public License
%  along with this program; if not, write to the Free Software
%  Foundation Inc, 59 Temple Pl. - Suite 330, Boston, MA 02111-1307, USA.

%  Suggestions for improvement and fixes are always welcome, although no
%  guarantee is made whether and when they will be implemented.
%  Send requests to <Guillaume@artefact.tk>

%  For tips on how to write Matlab code, see:
%     * MATLAB Programming Style Guidelines, by R. Johnson:
%       <http://www.datatool.com/prod02.htm>
%     * For tips on creating help for your m-files 'type help.m'.
%     * Matlab documentation on M-file Programming:
%  <http://www.mathworks.com/access/helpdesk/help/techdoc/matlab_prog/ch_funh8.html>

%  This function uses the Template class so that you can fully customize 
%  the output. You can modify .tpl files in templates/blue/ or create new
%  templates in a new directory.
%  See the template class documentation for more details.
%  <http://www.artefact.tk/software/matlab/template/>

%  Latest information on M2HTML is available on the web through:
%  <http://www.artefact.tk/software/matlab/m2html/>

%  Other Matlab to HTML converters available on the web:
%  1/ mat2html.pl, J.C. Kantor, in Perl, 1995: 
%     <http://fresh.t-systems-sfr.com/unix/src/www/mat2html>
%  2/ htmltools, B. Alsberg, in Matlab, 1997:
%     <http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=175>
%  3/ mtree2html2001, H. Pohlheim, in Perl, 1996, 2001:
%     <http://www.pohlheim.com/perl_main.html#matlabdocu>
%  4/ MatlabCodeColorizer, S. Faridani, in C#, 2005:
%     <http://www.pitchup.com/matlabcolorizer/>
%  5/ Highlight, G. Flandin, in Matlab, 2003:
%     <http://www.artefact.tk/software/matlab/highlight/>
%  6/ mdoc, P. Brinkmann, in Matlab, 2003:
%     <http://www.math.uiuc.edu/~brinkman/software/mdoc/>
%  7/ Ocamaweb, Miriad Technologies, in Ocaml, 2002:
%     <http://ocamaweb.sourceforge.net/>
%  8/ Matdoc, M. Kaminsky, in Perl, 2003:
%     <http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=3498>
%  9/ Matlab itself, The Mathworks Inc, with HELPWIN, DOC and PUBLISH (R14)

%-------------------------------------------------------------------------------
%- Set up options and default parameters
%-------------------------------------------------------------------------------
t0 = clock; % for statistics
msgInvalidPair = 'Bad value for argument: ''%s''';

options = struct('verbose', 1,...
                 'mFiles', {{'.'}},...
                 'htmlDir', 'doc',...
                 'recursive', 0,...
                 'source', 1,...
                 'download',0,...
                 'syntaxHighlighting', 1,...
                 'tabs', 4,...
                 'globalHypertextLinks', 0,...
                 'graph', 0,...
                 'todo', 0,...
                 'load', 0,...
                 'save', 0,...
                 'search', 0,...
                 'helptocxml', 0,...
                 'indexFile', 'index',...
                 'extension', '.html',...
                 'template', 'blue',...
                 'rootdir', pwd,...
                 'ignoredDir', {{'.svn' 'cvs'}}, ...
                 'language', 'english');

if nargin == 1 & isstruct(varargin{1})
    paramlist = [ fieldnames(varargin{1}) ...
                  struct2cell(varargin{1}) ]';
    paramlist = { paramlist{:} };
else
    if mod(nargin,2)
        error('Invalid parameter/value pair arguments.');
    end
    paramlist = varargin;
end

optionsnames = lower(fieldnames(options));
for i=1:2:length(paramlist)
    pname = paramlist{i};
    pvalue = paramlist{i+1};
    ind = strmatch(lower(pname),optionsnames);
    if isempty(ind)
        error(['Invalid parameter: ''' pname '''.']);
    elseif length(ind) > 1
        error(['Ambiguous parameter: ''' pname '''.']);
    end
    switch(optionsnames{ind})
        case 'verbose'
            if strcmpi(pvalue,'on')
                options.verbose = 1;
            elseif strcmpi(pvalue,'off')
                options.verbose = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'mfiles'
            if iscellstr(pvalue)
                options.mFiles = pvalue;
            elseif ischar(pvalue)
                options.mFiles = cellstr(pvalue);
            else
                error(sprintf(msgInvalidPair,pname));
            end
            options.load = 0;
        case 'htmldir'
            if ischar(pvalue)
                if isempty(pvalue),
                    options.htmlDir = '.';
                else
                    options.htmlDir = pvalue;
                end
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'recursive'
            if strcmpi(pvalue,'on')
                options.recursive = 1;
            elseif strcmpi(pvalue,'off')
                options.recursive = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
            options.load = 0;
        case 'source'
            if strcmpi(pvalue,'on')
                options.source = 1;
            elseif strcmpi(pvalue,'off')
                options.source = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'download'
            if strcmpi(pvalue,'on')
                options.download = 1;
            elseif strcmpi(pvalue,'off')
                options.download = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'syntaxhighlighting'
            if strcmpi(pvalue,'on')
                options.syntaxHighlighting = 1;
            elseif strcmpi(pvalue,'off')
                options.syntaxHighlighting = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'tabs'
            if pvalue >= 0
                options.tabs = pvalue;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'globalhypertextlinks'
            if strcmpi(pvalue,'on')
                options.globalHypertextLinks = 1;
            elseif strcmpi(pvalue,'off')
                options.globalHypertextLinks = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
            options.load = 0;
        case 'graph'
            if strcmpi(pvalue,'on')
                options.graph = 1;
            elseif strcmpi(pvalue,'off')
                options.graph = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'todo'
            if strcmpi(pvalue,'on')
                options.todo = 1;
            elseif strcmpi(pvalue,'off')
                options.todo = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'load'
            if ischar(pvalue)
                if exist(pvalue) == 7 % directory provided
                    pvalue = fullfile(pvalue,'m2html.mat');
                end         
                try
                    load(pvalue);
                catch
                    error(sprintf('Unable to load %s.', pvalue));
                end
                options.load = 1;
                [dummy options.template] = fileparts(options.template);
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'save'
            if strcmpi(pvalue,'on')
                options.save = 1;
            elseif strcmpi(pvalue,'off')
                options.save = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'search'
            if strcmpi(pvalue,'on')
                options.search = 1;
            elseif strcmpi(pvalue,'off')
                options.search = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'helptocxml'
            if strcmpi(pvalue,'on')
                options.helptocxml = 1;
            elseif strcmpi(pvalue,'off')
                options.helptocxml = 0;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'indexfile'
            if ischar(pvalue)
                options.indexFile = pvalue;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'extension'
            if ischar(pvalue) & pvalue(1) == '.'
                options.extension = pvalue;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'template'
            if ischar(pvalue)
                options.template = pvalue;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'ignoreddir'
            if iscellstr(pvalue)
                options.ignoredDir = pvalue;
            elseif ischar(pvalue)
                options.ignoredDir = cellstr(pvalue);
            else
                error(sprintf(msgInvalidPair,pname));
            end
        case 'language'
            if ischar(pvalue)
                options.language = pvalue;
            else
                error(sprintf(msgInvalidPair,pname));
            end
        otherwise
            error(['Invalid parameter: ''' pname '''.']);
    end
end

%-------------------------------------------------------------------------------
%- Get template files location
%-------------------------------------------------------------------------------
s = fileparts(which(mfilename));
options.template = fullfile(s,'templates',options.template);
if exist(options.template) ~= 7
    error('[Template] Unknown template.');
end

%-------------------------------------------------------------------------------
%- Get list of M-files
%-------------------------------------------------------------------------------
if ~options.load
    if strcmp(options.mFiles,'.')
        d = dir(pwd); d = {d([d.isdir]).name};
        options.mFiles = {d{~ismember(d,{'.' '..' options.ignoredDir{:}})}};
    end
    mfiles = getmfiles(options.mFiles,{},options.recursive,options.ignoredDir);
    if ~length(mfiles), fprintf('Nothing to be done.\n'); return; end
    if options.verbose,
        fprintf('Found %d M-files.\n',length(mfiles));
    end
    mfiles = sort(mfiles); % sort list of M-files in dictionary order
end

%-------------------------------------------------------------------------------
%- Get list of (unique) directories and (unique) names
%-------------------------------------------------------------------------------
if ~options.load
    mdirs = {};
    names = {};
    for i=1:length(mfiles)
        [mdirs{i}, names{i}] = fileparts(mfiles{i});
        if isempty(mdirs{i}), mdirs{i} = '.'; end
    end

    mdir = unique(mdirs);
    if options.verbose,
        fprintf('Found %d unique Matlab directories.\n',length(mdir));
    end

    name = names;
    %name = unique(names); % output is sorted
    %if options.verbose,
    %    fprintf('Found %d unique Matlab files.\n',length(name));
    %end
end

%-------------------------------------------------------------------------------
%- Create output directory, if necessary
%-------------------------------------------------------------------------------
if isempty(dir(options.htmlDir))                                               
    %- Create the top level output directory                                   
    if options.verbose                                                         
        fprintf('Creating directory %s...\n',options.htmlDir);                 
    end                                                                        
    if options.htmlDir(end) == filesep,                                        
        options.htmlDir(end) = [];                                             
    end                                                                        
    [pathdir, namedir] = fileparts(options.htmlDir);                           
    if isempty(pathdir)                                                        
        [status, msg] = mkdir(escapeblank(namedir));                                        
    else                                                                       
        [status, msg] = mkdir(escapeblank(pathdir), escapeblank(namedir));                               
    end                                                                        
    if ~status, error(msg); end                                                                
end                                                                            

%-------------------------------------------------------------------------------
%- Get synopsis, H1 line, script/function, subroutines, cross-references, todo
%-------------------------------------------------------------------------------
if ~options.load
    synopsis   = cell(size(mfiles));
    h1line     = cell(size(mfiles));
    subroutine = cell(size(mfiles));
    hrefs      = sparse(length(mfiles), length(mfiles));
    todo       = struct('mfile',[], 'line',[], 'comment',{{}});
    ismex      = zeros(length(mfiles), length(mexexts));
    statlist   = {};
    statinfo   = sparse(1,length(mfiles));
    kw         = cell(size(mfiles));
    freq       = cell(size(mfiles));

    for i=1:length(mfiles)
        if options.verbose
            fprintf('Processing file %s...',mfiles{i});
        end
        s = mfileparse(mfiles{i}, mdirs, names, options);
        synopsis{i}   = s.synopsis;
        h1line{i}     = s.h1line;
        subroutine{i} = s.subroutine;
        hrefs(i,:)    = s.hrefs;
        todo.mfile    = [todo.mfile repmat(i,1,length(s.todo.line))];
        todo.line     = [todo.line s.todo.line];
        todo.comment  = {todo.comment{:} s.todo.comment{:}};
        ismex(i,:)    = s.ismex;
        if options.search
            if options.verbose, fprintf('search...'); end
            [kw{i}, freq{i}] = searchindex(mfiles{i});
            statlist = union(statlist, kw{i});
        end
        if options.verbose, fprintf('\n'); end
    end
    hrefs = hrefs > 0;
    if options.search
        if options.verbose
            fprintf('Creating the search index...');
        end
        statinfo = sparse(length(statlist),length(mfiles));
        for i=1:length(mfiles)
            i1 = find(ismember(statlist, kw{i}));
            i2 = repmat(i,1,length(i1));
            if ~isempty(i1)
                statinfo(sub2ind(size(statinfo),i1,i2)) = freq{i};
            end
            if options.verbose, fprintf('.'); end
        end
        clear kw freq;
        if options.verbose, fprintf('\n'); end
    end
end

%-------------------------------------------------------------------------------
%- Save M-filenames and cross-references for further analysis
%-------------------------------------------------------------------------------
matfilesave = 'm2html.mat';

if options.save
    if options.verbose
        fprintf('Saving MAT file %s...\n',matfilesave);
    end
    save(fullfile(options.htmlDir,matfilesave), ...
        'mfiles', 'names', 'mdirs', 'name', 'mdir', 'options', ...
        'hrefs', 'synopsis', 'h1line', 'subroutine', 'todo', 'ismex', ...
        'statlist', 'statinfo');
end

%-------------------------------------------------------------------------------
%- Setup the output directories
%-------------------------------------------------------------------------------
for i=1:length(mdir)
    if exist(fullfile(options.htmlDir,mdir{i})) ~= 7
        ldir = splitpath(mdir{i});
        for j=1:length(ldir)
            if exist(fullfile(options.htmlDir,ldir{1:j})) ~= 7
                %- Create the output directory
                if options.verbose
                    fprintf('Creating directory %s...\n',...
                            fullfile(options.htmlDir,ldir{1:j}));
                end
                if j == 1
                    [status, msg] = mkdir(escapeblank(options.htmlDir), ...
                        escapeblank(ldir{1}));
                else
                    [status, msg] = mkdir(escapeblank(options.htmlDir), ...
                        escapeblank(fullfile(ldir{1:j})));
                end
                error(msg);
            end
        end
    end
end

%-------------------------------------------------------------------------------
%- Write the master index file
%-------------------------------------------------------------------------------
tpl_master = 'master.tpl';
tpl_master_identifier_nbyline = 4;
php_search = 'search.php';
dotbase = 'graph';

%- Create the HTML template
tpl = template(options.template,'remove');
tpl = set(tpl,'file','TPL_MASTER',tpl_master);
tpl = set(tpl,'block','TPL_MASTER','rowdir','rowdirs');
tpl = set(tpl,'block','TPL_MASTER','idrow','idrows');
tpl = set(tpl,'block','idrow','idcolumn','idcolumns');
tpl = set(tpl,'block','TPL_MASTER','search','searchs');
tpl = set(tpl,'block','TPL_MASTER','graph','graphs');

%- Open for writing the HTML master index file
curfile = fullfile(options.htmlDir,[options.indexFile options.extension]);
if options.verbose
    fprintf('Creating HTML file %s...\n',curfile);
end
fid = openfile(curfile,'w');

%- Set some template variables
tpl = set(tpl,'var','DATE',[datestr(now,8) ' ' datestr(now,1) ' ' ...
                            datestr(now,13)]);
tpl = set(tpl,'var','MASTERPATH', './');
tpl = set(tpl,'var','DIRS', sprintf('%s ',mdir{:}));

%- Print list of unique directories
for i=1:length(mdir)
    tpl = set(tpl,'var','L_DIR',...
              fullurl(mdir{i},[options.indexFile options.extension]));
    tpl = set(tpl,'var','DIR',mdir{i});
    tpl = parse(tpl,'rowdirs','rowdir',1);
end

%- Print full list of M-files (sorted by column)
[sortnames, ind] = sort(names);
m_mod = mod(length(sortnames), tpl_master_identifier_nbyline);
ind = [ind zeros(1,tpl_master_identifier_nbyline-m_mod)];
m_floor = floor(length(ind) / tpl_master_identifier_nbyline);
ind = reshape(ind,m_floor,tpl_master_identifier_nbyline)';

for i=1:prod(size(ind))
    if ind(i)
        tpl = set(tpl,'var','L_IDNAME',...
            fullurl(mdirs{ind(i)},[names{ind(i)} options.extension]));
        tpl = set(tpl,'var','T_IDNAME',mdirs{ind(i)});
        tpl = set(tpl,'var','IDNAME',names{ind(i)});
        tpl = parse(tpl,'idcolumns','idcolumn',1);
    else
        tpl = set(tpl,'var','L_IDNAME','');
        tpl = set(tpl,'var','T_IDNAME','');
        tpl = set(tpl,'var','IDNAME','');
        tpl = parse(tpl,'idcolumns','idcolumn',1);
    end
    if mod(i,tpl_master_identifier_nbyline) == 0
        tpl = parse(tpl,'idrows','idrow',1);
        tpl = set(tpl,'var','idcolumns','');
    end
end

%- Add a search form if necessary
tpl = set(tpl,'var','searchs','');
if options.search
    tpl = set(tpl,'var','PHPFILE',php_search);
    tpl = parse(tpl,'searchs','search',1);
end

%- Link to a full dependency graph, if necessary
tpl = set(tpl,'var','graphs','');
if options.graph & options.globalHypertextLinks & length(mdir) > 1
    tpl = set(tpl,'var','LGRAPH',[dotbase options.extension]);
    tpl = parse(tpl,'graphs','graph',1);
end

%- Print the template in the HTML file
tpl = parse(tpl,'OUT','TPL_MASTER');
fprintf(fid,'%s',get(tpl,'OUT'));
fclose(fid);

%-------------------------------------------------------------------------------
%- Copy template files (CSS, images, ...)
%-------------------------------------------------------------------------------
% Get list of files
d = dir(options.template);
d = {d(~[d.isdir]).name};
% Copy files
for i=1:length(d)
    [p, n, ext] = fileparts(d{i});
    if ~strcmp(ext,'.tpl') ... % do not copy .tpl files
       & ~strcmp([n ext],'Thumbs.db') % do not copy this Windows generated file
        if isempty(dir(fullfile(options.htmlDir,d{i})))
            if options.verbose
                fprintf('Copying template file %s...\n',d{i});
            end
            %- there is a bug with <copyfile> in Matlab 6.5 :
            %   http://www.mathworks.com/support/solutions/data/1-1B5JY.html
            %- and <copyfile> does not overwrite files even if newer...
            [status, errmsg] = copyfile(fullfile(options.template,d{i}),...
                                        options.htmlDir);
            %- If you encounter this bug, please uncomment one of the following lines
            % eval(['!cp -rf ' fullfile(options.template,d{i}) ' ' options.htmlDir]);
            % eval(['!copy ' fullfile(options.template,d{i}) ' ' options.htmlDir]);
            % status = 1;
            if ~status
                if ~isempty(errmsg)
                    error(errmsg)
                else
                    warning(sprintf(['<copyfile> failed to do its job...\n' ...
                'This is a known bug in Matlab 6.5 (R13).\n' ...
                'See http://www.mathworks.com/support/solutions/data/1-1B5JY.html']));
                end
            end
        end
    end
end

%-------------------------------------------------------------------------------
%- Search engine (index file and PHP script)
%-------------------------------------------------------------------------------
tpl_search = 'search.tpl';
idx_search = 'search.idx';

% TODO % improving the fill in of 'statlist' and 'statinfo'
% TODO % improving the search template file and update the CSS file

if options.search
    %- Write the search index file in output directory
    if options.verbose
        fprintf('Creating Search Index file %s...\n', idx_search);
    end
    docinfo = cell(length(mfiles),2);
    for i=1:length(mfiles)
        docinfo{i,1} = h1line{i};
        docinfo{i,2} = fullurl(mdirs{i}, [names{i} options.extension]);
    end
    doxywrite(fullfile(options.htmlDir,idx_search),statlist,statinfo,docinfo);
    
    %- Create the PHP template
    tpl = template(options.template,'remove');
    tpl = set(tpl,'file','TPL_SEARCH',tpl_search);
    
    %- Open for writing the PHP search script
    curfile = fullfile(options.htmlDir, php_search); 
    if options.verbose
        fprintf('Creating PHP script %s...\n',curfile);
    end
    fid = openfile(curfile,'w');
    
    %- Set template fields
    tpl = set(tpl,'var','INDEX',[options.indexFile options.extension]);
    tpl = set(tpl,'var','MASTERPATH','./');
    tpl = set(tpl,'var','DATE',[datestr(now,8) ' ' datestr(now,1) ' ' ...
                                datestr(now,13)]);
    tpl = set(tpl,'var','IDXFILE',idx_search);
    tpl = set(tpl,'var','PHPFILE',php_search);
    
    %- Print the template in the HTML file
    tpl = parse(tpl,'OUT','TPL_SEARCH');
    fprintf(fid,'%s',get(tpl,'OUT'));
    fclose(fid);
end

%-------------------------------------------------------------------------------
%- Create <helptoc.xml> needed to display hierarchical entries in Contents panel
%-------------------------------------------------------------------------------
% See http://www.mathworks.com/access/helpdesk/help/techdoc/matlab_env/guiref16.html
% and http://www.mathworks.com/support/solutions/data/1-18U6Q.html?solution=1-18U6Q

% TODO % display directories in TOC hierarchically instead of linearly
if options.helptocxml
    curfile = fullfile(options.htmlDir, 'helptoc.xml');
    if options.verbose
        fprintf('Creating XML Table-Of-Content %s...\n',curfile);
    end
    fid = openfile(curfile,'w');
    fprintf(fid,'<?xml version=''1.0'' encoding=''ISO-8859-1'' ?>\n');
    fprintf(fid,'<!-- $Date: %s $ -->\n\n', datestr(now,31));
    fprintf(fid,'<toc version="1.0">\n\n');
    fprintf(fid,['<tocitem target="%s" ',...
        'image="$toolbox/matlab/icons/book_mat.gif">%s\n'], ...
        [options.indexFile options.extension],'Toolbox');
    for i=1:length(mdir)
        fprintf(fid,['<tocitem target="%s" ',...
            'image="$toolbox/matlab/icons/reficon.gif">%s\n'], ...
            fullfile(mdir{i}, ...
                [options.indexFile options.extension]),mdir{i});
        if options.graph
            fprintf(fid,['\t<tocitem target="%s" ',...
            'image="$toolbox/matlab/icons/simulinkicon.gif">%s</tocitem>\n'], ...
                fullfile(mdir{i},...
                [dotbase options.extension]),'Dependency Graph');
        end
        if options.todo
            if ~isempty(intersect(find(strcmp(mdir{i},mdirs)),todo.mfile))
                fprintf(fid,['\t<tocitem target="%s" ',...
                'image="$toolbox/matlab/icons/demoicon.gif">%s</tocitem>\n'], ...
                    fullfile(mdir{i},...
                    ['todo' options.extension]),'Todo list');
            end
        end
        for j=1:length(mdirs)
            if strcmp(mdirs{j},mdir{i})
                curfile = fullfile(mdir{i},...
                    [names{j} options.extension]);
                fprintf(fid,'\t<tocitem target="%s">%s</tocitem>\n', ...
                    curfile,names{j});
            end
        end
        fprintf(fid,'</tocitem>\n');
    end
    fprintf(fid,'</tocitem>\n');
    fprintf(fid,'\n</toc>\n');
    fclose(fid);
end

%-------------------------------------------------------------------------------
%- Write an index for each output directory
%-------------------------------------------------------------------------------
tpl_mdir = 'mdir.tpl';
tpl_mdir_link = '<a href="%s">%s</a>';
%dotbase defined earlier

%- Create the HTML template
tpl = template(options.template,'remove');
tpl = set(tpl,'file','TPL_MDIR',tpl_mdir);
tpl = set(tpl,'block','TPL_MDIR','row-m','rows-m');
tpl = set(tpl,'block','row-m','mexfile','mex');
tpl = set(tpl,'block','TPL_MDIR','othermatlab','other');
tpl = set(tpl,'block','othermatlab','row-other','rows-other');
tpl = set(tpl,'block','TPL_MDIR','subfolder','subfold');
tpl = set(tpl,'block','subfolder','subdir','subdirs');
tpl = set(tpl,'block','TPL_MDIR','todolist','todolists');
tpl = set(tpl,'block','TPL_MDIR','graph','graphs');
tpl = set(tpl,'var','DATE',[datestr(now,8) ' ' datestr(now,1) ' ' ...
                            datestr(now,13)]);

for i=1:length(mdir)
    %- Open for writing each output directory index file
    curfile = fullfile(options.htmlDir,mdir{i},...
                       [options.indexFile options.extension]);
    if options.verbose
        fprintf('Creating HTML file %s...\n',curfile);
    end
    fid = openfile(curfile,'w');

    %- Set template fields
    tpl = set(tpl,'var','INDEX',     [options.indexFile options.extension]);
    tpl = set(tpl,'var','MASTERPATH',backtomaster(mdir{i}));
    tpl = set(tpl,'var','MDIR',      mdir{i});
    
    %- Display Matlab m-files, their H1 line and their Mex status
    tpl = set(tpl,'var','rows-m','');
    for j=1:length(mdirs)
        if strcmp(mdirs{j},mdir{i})
            tpl = set(tpl,'var','L_NAME', [names{j} options.extension]);
            tpl = set(tpl,'var','NAME',   names{j});
            tpl = set(tpl,'var','H1LINE', h1line{j});
            if any(ismex(j,:))
                tpl = parse(tpl,'mex','mexfile');
            else
                tpl = set(tpl,'var','mex','');
            end
            tpl = parse(tpl,'rows-m','row-m',1);
        end
    end
    
    %- Display other Matlab-specific files (.mat,.mdl,.p)
    tpl = set(tpl,'var','other','');
    tpl = set(tpl,'var','rows-other','');
    w = what(mdir{i}); w = w(1);
    w = {w.mat{:} w.mdl{:} w.p{:}};
    for j=1:length(w)
        tpl = set(tpl,'var','OTHERFILE',w{j});
        tpl = parse(tpl,'rows-other','row-other',1);
    end
    if ~isempty(w)
        tpl = parse(tpl,'other','othermatlab');
    end
    
    %- Display subsequent directories and classes
    tpl = set(tpl,'var','subdirs','');
    tpl = set(tpl,'var','subfold','');
    d = dir(mdir{i});
    d = {d([d.isdir]).name};
    d = {d{~ismember(d,{'.' '..' options.ignoredDir{:}})}};
    for j=1:length(d)
        if ismember(fullfile(mdir{i},d{j}),mdir)
            tpl = set(tpl,'var','SUBDIRECTORY',...
                sprintf(tpl_mdir_link,...
                fullurl(d{j},[options.indexFile options.extension]),d{j}));
        else
            tpl = set(tpl,'var','SUBDIRECTORY',d{j});
        end
        tpl = parse(tpl,'subdirs','subdir',1);
    end
    if ~isempty(d)
        tpl = parse(tpl,'subfold','subfolder');
    end
    
    %- Link to the TODO list if necessary
    tpl = set(tpl,'var','todolists','');
    if options.todo
        if ~isempty(intersect(find(strcmp(mdir{i},mdirs)),todo.mfile))
            tpl = set(tpl,'var','LTODOLIST',['todo' options.extension]);
            tpl = parse(tpl,'todolists','todolist',1);
        end
    end
    
    %- Link to the dependency graph if necessary
    tpl = set(tpl,'var','graphs','');
    if options.graph
        tpl = set(tpl,'var','LGRAPH',[dotbase options.extension]);
        tpl = parse(tpl,'graphs','graph',1);
    end
    
    %- Print the template in the HTML file
    tpl = parse(tpl,'OUT','TPL_MDIR');
    fprintf(fid,'%s',get(tpl,'OUT'));
    fclose(fid);
end

%-------------------------------------------------------------------------------
%- Write a TODO list file for each output directory, if necessary
%-------------------------------------------------------------------------------
tpl_todo = 'todo.tpl';

if options.todo
    %- Create the HTML template
    tpl = template(options.template,'remove');
    tpl = set(tpl,'file','TPL_TODO',tpl_todo);
    tpl = set(tpl,'block','TPL_TODO','filelist','filelists');
    tpl = set(tpl,'block','filelist','row','rows');
    tpl = set(tpl,'var','DATE',[datestr(now,8) ' ' datestr(now,1) ' ' ...
                                datestr(now,13)]);

    for i=1:length(mdir)
        mfilestodo = intersect(find(strcmp(mdir{i},mdirs)),todo.mfile);
        if ~isempty(mfilestodo)
            %- Open for writing each TODO list file
            curfile = fullfile(options.htmlDir,mdir{i},...
                               ['todo' options.extension]);
            if options.verbose
                fprintf('Creating HTML file %s...\n',curfile);
            end
            fid = openfile(curfile,'w');
            
            %- Set template fields
            tpl = set(tpl,'var','INDEX',[options.indexFile options.extension]);
            tpl = set(tpl,'var','MASTERPATH', backtomaster(mdir{i}));
            tpl = set(tpl,'var','MDIR',       mdir{i});
            tpl = set(tpl,'var','filelists',  '');
    
            for k=1:length(mfilestodo)
                tpl = set(tpl,'var','MFILE',names{mfilestodo(k)});
                tpl = set(tpl,'var','rows','');
                nbtodo = find(todo.mfile == mfilestodo(k));
                for l=1:length(nbtodo)
                    tpl = set(tpl,'var','L_NBLINE',...
                        [names{mfilestodo(k)} ...
                            options.extension ...
                            '#l' num2str(todo.line(nbtodo(l)))]);
                    tpl = set(tpl,'var','NBLINE',num2str(todo.line(nbtodo(l))));
                    tpl = set(tpl,'var','COMMENT',todo.comment{nbtodo(l)});
                    tpl = parse(tpl,'rows','row',1);
                end
                tpl = parse(tpl,'filelists','filelist',1);
            end
    
            %- Print the template in the HTML file
            tpl = parse(tpl,'OUT','TPL_TODO');
            fprintf(fid,'%s',get(tpl,'OUT'));
            fclose(fid);
        end
    end
end

%-------------------------------------------------------------------------------
%- Create dependency graphs using GraphViz, if requested
%-------------------------------------------------------------------------------
tpl_graph = 'graph.tpl';
% You may have to modify the following line with Matlab7 (R14) to specify
% the full path to where GraphViz is installed
dot_exec  = 'dot';
%dotbase defined earlier

if options.graph
    %- Create the HTML template
    tpl = template(options.template,'remove');
    tpl = set(tpl,'file','TPL_GRAPH',tpl_graph);
    tpl = set(tpl,'var','DATE',[datestr(now,8) ' ' datestr(now,1) ' ' ...
                                datestr(now,13)]);
    
    %- Create a full dependency graph for all directories if possible
    if options.globalHypertextLinks & length(mdir) > 1
        mdotfile = fullfile(options.htmlDir,[dotbase '.dot']);
        if options.verbose
            fprintf('Creating full dependency graph %s...',mdotfile);
        end
        mdot({hrefs, names, options, mdirs}, mdotfile); %mfiles
        calldot(dot_exec, mdotfile, ...
                fullfile(options.htmlDir,[dotbase '.map']), ...
                fullfile(options.htmlDir,[dotbase '.png']));
        if options.verbose, fprintf('\n'); end
        fid = openfile(fullfile(options.htmlDir, [dotbase options.extension]),'w');
        tpl = set(tpl,'var','INDEX',[options.indexFile options.extension]);
        tpl = set(tpl,'var','MASTERPATH', './');
        tpl = set(tpl,'var','MDIR',       'the whole toolbox');
        tpl = set(tpl,'var','GRAPH_IMG',  [dotbase '.png']);
        try % if <dot> failed...
            fmap = openfile(fullfile(options.htmlDir,[dotbase '.map']),'r');
            tpl = set(tpl,'var','GRAPH_MAP',  fscanf(fmap,'%c'));
            fclose(fmap);
        end
        tpl = parse(tpl,'OUT','TPL_GRAPH');
        fprintf(fid,'%s', get(tpl,'OUT'));
        fclose(fid);
    end
    
    %- Create a dependency graph for each output directory
    for i=1:length(mdir)
        mdotfile = fullfile(options.htmlDir,mdir{i},[dotbase '.dot']);
        if options.verbose
            fprintf('Creating dependency graph %s...',mdotfile);
        end
        ind = find(strcmp(mdirs,mdir{i}));
        href1 = zeros(length(ind),length(hrefs));
        for j=1:length(hrefs), href1(:,j) = hrefs(ind,j); end
        href2 = zeros(length(ind));
        for j=1:length(ind), href2(j,:) = href1(j,ind); end
        mdot({href2, {names{ind}}, options}, mdotfile); %{mfiles{ind}}
        calldot(dot_exec, mdotfile, ...
                fullfile(options.htmlDir,mdir{i},[dotbase '.map']), ...
                fullfile(options.htmlDir,mdir{i},[dotbase '.png']));
        if options.verbose, fprintf('\n'); end
        fid = openfile(fullfile(options.htmlDir,mdir{i},...
            [dotbase options.extension]),'w');
        tpl = set(tpl,'var','INDEX',[options.indexFile options.extension]);
        tpl = set(tpl,'var','MASTERPATH', backtomaster(mdir{i}));
        tpl = set(tpl,'var','MDIR',       mdir{i});
        tpl = set(tpl,'var','GRAPH_IMG',  [dotbase '.png']);
        try % if <dot> failed, no '.map' file has been created
            fmap = openfile(fullfile(options.htmlDir,mdir{i},[dotbase '.map']),'r');
            tpl = set(tpl,'var','GRAPH_MAP',  fscanf(fmap,'%c'));
            fclose(fmap);
        end
        tpl = parse(tpl,'OUT','TPL_GRAPH');
        fprintf(fid,'%s', get(tpl,'OUT'));
        fclose(fid);
    end
end

%-------------------------------------------------------------------------------
%- Write an HTML file for each M-file
%-------------------------------------------------------------------------------
%- List of Matlab keywords (output from iskeyword)
matlabKeywords = {'break', 'case', 'catch', 'continue', 'elseif', 'else', ...
                  'end', 'for', 'function', 'global', 'if', 'otherwise', ...
                  'persistent', 'return', 'switch', 'try', 'while'};
                  %'keyboard', 'pause', 'eps', 'NaN', 'Inf'

tpl_mfile = 'mfile.tpl';

tpl_mfile_code     = '<a href="%s" class="code" title="%s">%s</a>';
tpl_mfile_keyword  = '<span class="keyword">%s</span>';
tpl_mfile_comment  = '<span class="comment">%s</span>';
tpl_mfile_string   = '<span class="string">%s</span>';
tpl_mfile_aname    = '<a name="%s" href="#_subfunctions" class="code">%s</a>';
tpl_mfile_line     = '%04d %s\n';

%- Delimiters used in strtok: some of them may be useless (% " .), removed '.'
strtok_delim = sprintf(' \t\n\r(){}[]<>+-*~!|\\@&/,:;="''%%');

%- Create the HTML template
tpl = template(options.template,'remove');
tpl = set(tpl,'file','TPL_MFILE',tpl_mfile);
tpl = set(tpl,'block','TPL_MFILE','pathline','pl');
tpl = set(tpl,'block','TPL_MFILE','mexfile','mex');
tpl = set(tpl,'block','TPL_MFILE','script','scriptfile');
tpl = set(tpl,'block','TPL_MFILE','crossrefcall','crossrefcalls');
tpl = set(tpl,'block','TPL_MFILE','crossrefcalled','crossrefcalleds');
tpl = set(tpl,'block','TPL_MFILE','subfunction','subf');
tpl = set(tpl,'block','subfunction','onesubfunction','onesubf');
tpl = set(tpl,'block','TPL_MFILE','source','thesource');
tpl = set(tpl,'block','TPL_MFILE','download','downloads');
tpl = set(tpl,'var','DATE',[datestr(now,8) ' ' datestr(now,1) ' ' ...
                            datestr(now,13)]);

nblinetot = 0;
for i=1:length(mdir)
    for j=1:length(mdirs)
        if strcmp(mdirs{j},mdir{i})
        
            curfile = fullfile(options.htmlDir,mdir{i},...
                               [names{j} options.extension]);
                               
            %- Copy M-file for download, if necessary
            if options.download
                if options.verbose
                    fprintf('Copying M-file %s.m to %s...\n',names{j},...
                        fullfile(options.htmlDir,mdir{i}));
                end
                [status, errmsg] = copyfile(mfiles{j},...
                    fullfile(options.htmlDir,mdir{i}));
                error(errmsg);
            end
            
            %- Open for writing the HTML file
            if options.verbose
                fprintf('Creating HTML file %s...\n',curfile);
            end
            fid = openfile(curfile,'w');
            if strcmp(names{j},options.indexFile)
                fprintf(['Warning: HTML index file %s will be ' ...
                        'overwritten by Matlab function %s.\n'], ...
                        [options.indexFile options.extension], mfiles{j});
            end
            
            %- Open for reading the M-file
            fid2 = openfile(mfiles{j},'r');
            
            %- Set some template fields
            tpl = set(tpl,'var','INDEX', [options.indexFile options.extension]);
            tpl = set(tpl,'var','MASTERPATH',       backtomaster(mdir{i}));
            tpl = set(tpl,'var','MDIR',             mdirs{j});
            tpl = set(tpl,'var','NAME',             names{j});
            tpl = set(tpl,'var','H1LINE',           entity(h1line{j}));
            tpl = set(tpl,'var','scriptfile',       '');
            if isempty(synopsis{j})
                tpl = set(tpl,'var','SYNOPSIS',get(tpl,'var','script'));
            else
                tpl = set(tpl,'var','SYNOPSIS', synopsis{j});
            end
            s = splitpath(mdir{i});
            tpl = set(tpl,'var','pl','');
            for k=1:length(s)
                c = cell(1,k); for l=1:k, c{l} = filesep; end
                cpath = {s{1:k};c{:}}; cpath = [cpath{:}];
                if ~isempty(cpath), cpath = cpath(1:end-1); end
                if ismember(cpath,mdir)
                    tpl = set(tpl,'var','LPATHDIR',[repmat('../',...
                        1,length(s)-k) options.indexFile options.extension]);
                else
                    tpl = set(tpl,'var','LPATHDIR','#');
                end
                tpl = set(tpl,'var','PATHDIR',s{k});
                tpl = parse(tpl,'pl','pathline',1);
            end
            
            %- Handle mex files
            tpl = set(tpl,'var','mex', '');
            samename = dir(fullfile(mdir{i},[names{j}    '.*']));
            samename = {samename.name};
            tpl = set(tpl,'var','MEXTYPE', 'mex');
            for k=1:length(samename)
                [dummy, dummy, ext] = fileparts(samename{k});
                switch ext
                    case '.c'
                        tpl = set(tpl,'var','MEXTYPE', 'c');
                    case {'.cpp' '.c++' '.cxx' '.C'}
                        tpl = set(tpl,'var','MEXTYPE', 'c++');
                    case {'.for' '.f' '.FOR' '.F'}
                        tpl = set(tpl,'var','MEXTYPE', 'fortran');
                    otherwise
                        %- Unknown mex file source
                end
            end
            [exts, platform] = mexexts;
            mexplatforms = sprintf('%s, ',platform{find(ismex(j,:))});
            if ~isempty(mexplatforms)
                tpl = set(tpl,'var','PLATFORMS', mexplatforms(1:end-2));
                tpl = parse(tpl,'mex','mexfile');
            end
            
            %- Set description template field
            descr = '';
            flagsynopcont = 0;
            flag_seealso  = 0;
            while 1
                tline = fgets(fid2);
                if ~ischar(tline), break, end
                tline = entity(fliplr(deblank(fliplr(tline))));
                %- Synopsis line
                if ~isempty(strmatch('function',tline))
                    if ~isempty(strmatch('...',fliplr(deblank(tline))))
                        flagsynopcont = 1;
                    end
                %- H1 line and description
                elseif ~isempty(strmatch('%',tline))
                    %- Hypertext links on the "See also" line
                    ind = findstr(lower(tline),'see also');
                    if ~isempty(ind) | flag_seealso
                        %- "See also" only in files in the same directory
                        indsamedir = find(strcmp(mdirs{j},mdirs));
                        hrefnames = {names{indsamedir}};
                        r = deblank(tline);
                        flag_seealso = 1; %(r(end) == ',');
                        tline = '';
                        while 1
                            [t,r,q] = strtok(r,sprintf(' \t\n\r.,;%%'));
                            tline = [tline q];
                            if isempty(t), break, end;
                            ii = strcmpi(hrefnames,t);
                            if any(ii)
                                jj = find(ii);
                                tline = [tline sprintf(tpl_mfile_code,...
                                    [hrefnames{jj(1)} options.extension],...
                                    synopsis{indsamedir(jj(1))},t)];
                            else
                                tline = [tline t];
                            end
                        end
                        tline = sprintf('%s\n',tline);
                    end
                    descr = [descr tline(2:end)];
                elseif isempty(tline)
                    if ~isempty(descr), break, end;
                else
                    if flagsynopcont
                        if isempty(strmatch('...',fliplr(deblank(tline))))
                            flagsynopcont = 0;
                        end
                    else
                        break;
                    end
                end
            end
            tpl = set(tpl,'var','DESCRIPTION',...
                horztab(descr,options.tabs));
            
            %- Set cross-references template fields:
            %  Function called
            ind = find(hrefs(j,:) == 1);
            tpl = set(tpl,'var','crossrefcalls','');
            for k=1:length(ind)
                if strcmp(mdirs{j},mdirs{ind(k)})
                    tpl = set(tpl,'var','L_NAME_CALL', ...
                        [names{ind(k)} options.extension]);
                else
                    tpl = set(tpl,'var','L_NAME_CALL', ...
                              fullurl(backtomaster(mdirs{j}), ...
                                         mdirs{ind(k)}, ...
                                       [names{ind(k)} options.extension]));
                end
                tpl = set(tpl,'var','SYNOP_CALL',   synopsis{ind(k)});
                tpl = set(tpl,'var','NAME_CALL',   names{ind(k)});
                tpl = set(tpl,'var','H1LINE_CALL', h1line{ind(k)});
                tpl = parse(tpl,'crossrefcalls','crossrefcall',1);
            end
            %  Callers
            ind = find(hrefs(:,j) == 1);
            tpl = set(tpl,'var','crossrefcalleds','');
            for k=1:length(ind)
                if strcmp(mdirs{j},mdirs{ind(k)})
                    tpl = set(tpl,'var','L_NAME_CALLED', ...
                        [names{ind(k)} options.extension]);
                else
                    tpl = set(tpl,'var','L_NAME_CALLED', ...
                        fullurl(backtomaster(mdirs{j}),...
                            mdirs{ind(k)}, ...
                            [names{ind(k)} options.extension]));
                end
                tpl = set(tpl,'var','SYNOP_CALLED',   synopsis{ind(k)});
                tpl = set(tpl,'var','NAME_CALLED',   names{ind(k)});
                tpl = set(tpl,'var','H1LINE_CALLED', h1line{ind(k)});
                tpl = parse(tpl,'crossrefcalleds','crossrefcalled',1);
            end
            
            %- Set subfunction template field
            tpl = set(tpl,'var',{'subf' 'onesubf'},{'' ''});
            if ~isempty(subroutine{j}) & options.source
                for k=1:length(subroutine{j})
                    tpl = set(tpl, 'var', 'L_SUB', ['#_sub' num2str(k)]);
                    tpl = set(tpl, 'var', 'SUB',   subroutine{j}{k});
                    tpl = parse(tpl, 'onesubf', 'onesubfunction',1);
                end
                tpl = parse(tpl,'subf','subfunction');
            end
            subname = extractname(subroutine{j});
            
            %- Link to M-file (for download)
            tpl = set(tpl,'var','downloads','');
            if options.download
                tpl = parse(tpl,'downloads','download',1);
            end
            
            %- Display source code with cross-references
            if options.source & ~strcmpi(names{j},'contents')
                fseek(fid2,0,-1);
                it = 1;
                matlabsource = '';
                nbsubroutine = 1;
                %- Get href function names of this file
                indhrefnames = find(hrefs(j,:) == 1);
                hrefnames = {names{indhrefnames}};
                %- Loop over lines
                while 1
                    tline = fgetl(fid2);
                    if ~ischar(tline), break, end
                    myline = '';
                    splitc = splitcode(entity(tline));
                    for k=1:length(splitc)
                        if isempty(splitc{k})
                        elseif ~isempty(strmatch('function', ...
                                        fliplr(deblank(fliplr(splitc{k})))))
                            if isspace(splitc{k}(1))
                                nbs = find(diff(isspace(splitc{k}))==-1);
                                myline = [myline splitc{k}(1:nbs(1))];
                                splitc{k} = splitc{k}(nbs(1)+1:end);
                            end
                            %- Subfunctions definition
                            myline = [myline ...
                                sprintf(tpl_mfile_aname,...
                                    ['_sub' num2str(nbsubroutine-1)],splitc{k})];
                            nbsubroutine = nbsubroutine + 1;
                        elseif splitc{k}(1) == ''''
                            myline = [myline ...
                                sprintf(tpl_mfile_string,splitc{k})];
                        elseif splitc{k}(1) == '%'
                            myline = [myline ...
                                sprintf(tpl_mfile_comment,deblank(splitc{k}))];
                        elseif ~isempty(strmatch('...',splitc{k}))
                            myline = [myline sprintf(tpl_mfile_keyword,'...')];
                            if ~isempty(splitc{k}(4:end))
                                myline = [myline ...
                                    sprintf(tpl_mfile_comment,splitc{k}(4:end))];
                            end
                        else
                            %- Look for keywords
                            r = splitc{k};
                            while 1
                                [t,r,q] = strtok(r,strtok_delim);
                                myline = [myline q];
                                if isempty(t), break, end;
                                %- Highlight Matlab keywords &
                                %  cross-references on known functions
                                if options.syntaxHighlighting & ...
                                        any(strcmp(matlabKeywords,t))
                                    if strcmp('end',t)
                                        rr = fliplr(deblank(fliplr(r)));
                                        icomma = strmatch(',',rr);
                                        isemicolon = strmatch(';',rr);
                                        if ~(isempty(rr) | ~isempty([icomma isemicolon]))
                                            myline = [myline t];
                                        else
                                            myline = [myline sprintf(tpl_mfile_keyword,t)];
                                        end
                                    else
                                        myline = [myline sprintf(tpl_mfile_keyword,t)];
                                    end
                                elseif any(strcmp(hrefnames,t))
                                    indt = indhrefnames(logical(strcmp(hrefnames,t)));
                                    flink = [t options.extension];
                                    ii = ismember({mdirs{indt}},mdirs{j});
                                    if ~any(ii)
                                        % take the first one...
                                        flink = fullurl(backtomaster(mdirs{j}),...
                                                          mdirs{indt(1)}, flink);
                                    else
                                        indt = indt(logical(ii));
                                    end
                                    myline = [myline sprintf(tpl_mfile_code,...
                                              flink, synopsis{indt(1)}, t)];
                                elseif any(strcmp(subname,t))
                                    ii = find(strcmp(subname,t));
                                    myline = [myline sprintf(tpl_mfile_code,...
                                        ['#_sub' num2str(ii)],...
                                        ['sub' subroutine{j}{ii}],t)];
                                else
                                    myline = [myline t];
                                end
                            end
                        end
                    end
                    matlabsource = [matlabsource sprintf(tpl_mfile_line,it,myline)];
                    it = it + 1;
                end
                nblinetot = nblinetot + it - 1;
                tpl = set(tpl,'var','SOURCECODE',...
                          horztab(matlabsource,options.tabs));
                tpl = parse(tpl,'thesource','source');
            else
                tpl = set(tpl,'var','thesource','');
            end
            tpl = parse(tpl,'OUT','TPL_MFILE');
            fprintf(fid,'%s',get(tpl,'OUT'));
            fclose(fid2);
            fclose(fid);
        end
    end
end

%-------------------------------------------------------------------------------
%- Display Statistics
%-------------------------------------------------------------------------------
if options.verbose
    prnbline = '';
    if options.source
        prnbline = sprintf('(%d lines) ', nblinetot);
    end
    fprintf('Stats: %d M-files %sin %d directories documented in %d s.\n', ...
            length(mfiles), prnbline, length(mdir), round(etime(clock,t0)));
end

%===============================================================================
function mfiles = getmfiles(mdirs, mfiles, recursive, ignoredDir)
    %- Extract M-files from a list of directories and/or M-files

    if nargin < 4, ignoredDir = {}; end
    for i=1:length(mdirs)
        currentdir = fullfile(pwd, mdirs{i});
        if exist(currentdir) == 2 % M-file
            mfiles{end+1} = mdirs{i};
        elseif exist(currentdir) == 7 % Directory
            d = dir(fullfile(currentdir, '*.m'));
            d = {d(~[d.isdir]).name};
            for j=1:length(d)
                %- don't take care of files containing ','
                %  probably a sccs file...
                if isempty(findstr(',',d{j}))
                    mfiles{end+1} = fullfile(mdirs{i}, d{j});
                end
            end
            if recursive
                d = dir(currentdir);
                d = {d([d.isdir]).name};
                d = {d{~ismember(d,{'.' '..' ignoredDir{:}})}};
                for j=1:length(d)
                    mfiles = getmfiles(cellstr(fullfile(mdirs{i},d{j})), ...
                                       mfiles, recursive, ignoredDir);
                end
            end
        else
            fprintf('Warning: Unprocessed file %s.\n',mdirs{i});
            if ~isempty(strmatch('/',mdirs{i})) | findstr(':',mdirs{i})
                fprintf('         Use relative paths in ''mfiles'' option\n');
            end 
        end
    end

%===============================================================================
function calldot(dotexec, mdotfile, mapfile, pngfile, opt)
    %- Draw a dependency graph in a PNG image using <dot> from GraphViz

    if nargin == 4, opt = ''; end
    try
        %- See <http://www.graphviz.org/>
        %  <dot> must be in your system path, see M2HTML FAQ:
        %  <http://www.artefact.tk/software/matlab/m2html/faq.php>

        eval(['!"' dotexec '" ' opt ' -Tcmap -Tpng "' mdotfile ...
              '" -o "' mapfile ... 
              '" -o "' pngfile '"']);
        % use '!' rather than 'system' for backward compability with Matlab 5.3
    catch % use of '!' prevents errors to be catched...
        fprintf('<dot> failed.');
    end
    
%===============================================================================
function s = backtomaster(mdir)
    %- Provide filesystem path to go back to the root folder

    ldir = splitpath(mdir);
    s = repmat('../',1,length(ldir));
    
%===============================================================================
function ldir = splitpath(p)
    %- Split a filesystem path into parts using filesep as separator

    ldir = {};
    p = deblank(p);
    while 1
        [t,p] = strtok(p,filesep);
        if isempty(t), break; end
        if ~strcmp(t,'.')
            ldir{end+1} = t;
        end
    end
    if isempty(ldir)
        ldir{1} = '.'; % should be removed
    end

%===============================================================================
function name = extractname(synopsis)
    %- Extract function name in a synopsis

    if ischar(synopsis), synopsis = {synopsis}; end
    name = cell(size(synopsis));
    for i=1:length(synopsis)
        ind = findstr(synopsis{i},'=');
        if isempty(ind)
            ind = findstr(synopsis{i},'function');
            s = synopsis{i}(ind(1)+8:end);
        else
            s = synopsis{i}(ind(1)+1:end);
        end
        name{i} = strtok(s,[9:13 32 40]); % white space characters and '('
    end
    if length(name) == 1, name = name{1}; end

%===============================================================================
function f = fullurl(varargin)
    %- Build full url from parts (using '/' and not filesep)
    
    f = strrep(fullfile(varargin{:}),'\','/');

%===============================================================================
function str = escapeblank(str)
    %- Escape white spaces using '\'
    
    str = deblank(fliplr(deblank(fliplr(str))));
    str = strrep(str,' ','\ ');

%===============================================================================
function str = entity(str)
    %- Escape HTML special characters
    %- See http://www.w3.org/TR/html4/charset.html#h-5.3.2
    
    str = strrep(str,'&','&amp;');
    str = strrep(str,'<','&lt;');
    str = strrep(str,'>','&gt;');
    str = strrep(str,'"','&quot;');
    
%===============================================================================
function str = horztab(str,n)
    %- For browsers, the horizontal tab character is the smallest non-zero 
    %- number of spaces necessary to line characters up along tab stops that are
    %- every 8 characters: behaviour obtained when n = 0.
    
    if n > 0
        str = strrep(str,sprintf('\t'),blanks(n));
    end