function s = mfileparse(mfile, mdirs, names, options)
%MFILEPARSE Parsing of an M-file to obtain synopsis, help and references
%  S = MFILEPARSE(MFILE, MDIRS, NAMES, OPTIONS) parses the M-file MFILE looking
%  for synopsis (function), H1 line, subroutines and todo tags (if any).
%  It also fills in a boolean array indicating whether MFILE calls M-files 
%  defined by MDIRS (M-files directories) AND NAMES (M-file names).
%  The input OPTIONS comes from M2HTML: fields used are 'verbose', 'global'
%  and 'todo'.
%  Output S is a structure whose fields are:
%     o synopsis: char array (empty if MFILE is a script)
%     o h1line: short one-line description into the first help line
%     o subroutine: cell array of char containing subroutines synopsis
%     o hrefs: boolean array with hrefs(i) = 1 if MFILE calls mdirs{i}/names{i}
%     o todo: structure containing information about potential todo tags
%
%  See also M2HTML

%  Copyright (C) 2003 Guillaume Flandin <Guillaume@artefact.tk>
%  Aug. 22, 2017Date: 2003/29/04 17:33:43 $

error(nargchk(3,4,nargin));
if nargin == 3,
	options = struct('verbose',1, 'globalHypertextLinks',0, 'todo',0);
end

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

%- Open for reading the M-file
fid = openfile(mfile,'r');
it = 0; % line number

%- Initialize Output
s = struct('synopsis',   '', ...
		   'h1line',     '', ...
		   'subroutine', {{}}, ...
		   'hrefs',      sparse(1,length(names)), ...
		   'todo',       struct('line',[],'comment',{{}}), ...
		   'ismex',      zeros(size(mexexts)));

%- Initialize flag for synopsis cont ('...')
flagsynopcont = 0;
%- Look for synopsis and H1 line
%  Help is the first set of contiguous comment lines in an m-file
%  The H1 line is a short one-line description into the first help line
while 1
	tline = fgetl(fid);
	if ~ischar(tline), break, end
	it = it + 1;
	tline = deblank(fliplr(deblank(fliplr(tline))));
	%- Synopsis line
	if ~isempty(strmatch('function',tline))
		s.synopsis = tline;
		if ~isempty(strmatch('...',fliplr(tline)))
			flagsynopcont = 1;
			s.synopsis = deblank(s.synopsis(1:end-3));
		end
	%- H1 Line
	elseif ~isempty(strmatch('%',tline))
		% allow for the help lines to be before the synopsis
		if isempty(s.h1line)
			s.h1line = fliplr(deblank(tline(end:-1:2)));
		end
		if ~isempty(s.synopsis), break, end
	%- Go through empty lines
	elseif isempty(tline)
		
	%- Code found. Stop.
	else
		if flagsynopcont
			if isempty(strmatch('...',fliplr(tline)))
				s.synopsis = [s.synopsis tline];
				flagsynopcont = 0;
			else
				s.synopsis = [s.synopsis deblank(tline(1:end-3))];
			end
		else
			break;
		end
	end
end

%- Global Hypertext Links option
%  If false, hypertext links are done only among functions in the same
%  directory.
if options.globalHypertextLinks
	hrefnames = names;
else
	indhref = find(strcmp(fileparts(mfile),mdirs));
	hrefnames = {names{indhref}};
end

%- Compute cross-references and extract subroutines
%  hrefs(i) is 1 if mfile calls mfiles{i} and 0 otherwise
while ischar(tline)
	% Remove blanks at both ends
	tline = deblank(fliplr(deblank(fliplr(tline))));
	
	% Split code into meaningful chunks
	splitc = splitcode(tline);
	for j=1:length(splitc)
		if isempty(splitc{j}) | ...
			splitc{j}(1) == '''' | ...
			~isempty(strmatch('...',splitc{j}))
			% Forget about empty lines, char strings or conts
		elseif splitc{j}(1) == '%'
			% Cross-references are not taken into account in comments
			% Just look for potential TODO or FIXME line
			if options.todo
				if ~isempty(strmatch('% TODO',splitc{j})) | ...
				   ~isempty(strmatch('% FIXME',splitc{j}))
					s.todo.line   = [s.todo.line it];
					s.todo.comment{end+1} = splitc{j}(9:end);
				end
			end
		else
			% detect if this line is a declaration of a subroutine
			if ~isempty(strmatch('function',splitc{j}))
				s.subroutine{end+1} = splitc{j};
			else
				% get list of variables and functions
				symbol = {};
				while 1
					[t,splitc{j}] = strtok(splitc{j},strtok_delim);
					if isempty(t), break, end;
					symbol{end+1} = t;
				end
				if options.globalHypertextLinks
					s.hrefs = s.hrefs + ismember(hrefnames,symbol);
				else
					if ~isempty(indhref)
						s.hrefs(indhref) = s.hrefs(1,indhref) + ...
										   ismember(hrefnames,symbol);
					end
				end
			end
		end
	end
	tline = fgetl(fid);
	it = it + 1;
end	

fclose(fid);

%- Look for Mex files
[pathstr,name] = fileparts(mfile);
samename = dir(fullfile(pathstr,[name	'.*']));
samename = {samename.name};
ext = {};
for i=1:length(samename)
	[dummy, dummy, ext{i}] = fileparts(samename{i});
end
s.ismex = ismember(mexexts,ext);
