%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%%------------------------------------------------------------------
%%% Purpose:Convert Erlang files to html.
%%%------------------------------------------------------------------
-module(erl2html2).
-export([convert/2, convert/3]).
convert([], _Dest) -> % Fake clause.
ok;
convert(File, Dest) ->
%% The generated code uses the BGCOLOR attribute in the
%% BODY tag, which wasn't valid until HTML 3.2. Also,
%% good HTML should either override all colour attributes
%% or none of them -- *never* just a few.
%%
%% FIXME: The colours should *really* be set with
%% stylesheets...
Header = ["<!DOCTYPE HTML PUBLIC "
"\"-//W3C//DTD HTML 3.2 Final//EN\">\n"
"<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
"<html>\n"
"<head><title>", File, "</title></head>\n\n"
"<body bgcolor=\"white\" text=\"black\""
" link=\"blue\" vlink=\"purple\" alink=\"red\">\n"],
convert(File, Dest, Header).
convert(File, Dest, Header) ->
%% statistics(runtime),
case parse_file(File) of
{ok,Functions} ->
%% {_, Time1} = statistics(runtime),
%% io:format("Parsed file in ~.2f Seconds.~n",[Time1/1000]),
case file:open(File,[raw,{read_ahead,10000}]) of
{ok,SFd} ->
case file:open(Dest,[write,raw]) of
{ok,DFd} ->
file:write(DFd,[Header,"<pre>\n"]),
Lines = build_html(SFd,DFd,Functions),
file:write(DFd,["</pre>\n",footer(),
"</body>\n</html>\n"]),
%% {_, Time2} = statistics(runtime),
%% io:format("Converted ~p lines in ~.2f Seconds.~n",
%% [Lines, Time2/1000]),
file:close(SFd),
file:close(DFd),
ok;
Error ->
Error
end;
Error ->
Error
end;
Error ->
Error
end.
%%%-----------------------------------------------------------------
%%% Parse the input file to get the line numbers for all function
%%% definitions. This will be used when creating link targets for each
%%% function in build_html/5.
%%%
%%% All function clauses are also marked in order to allow
%%% possibly_enhance/2 to write these in bold.
parse_file(File) ->
case epp:open(File, [], []) of
{ok,Epp} ->
Forms = parse_file(Epp,File,false),
epp:close(Epp),
{ok,Forms};
{error,E} ->
{error,E}
end.
parse_file(Epp,File,InCorrectFile) ->
case epp:parse_erl_form(Epp) of
{ok,Form} ->
case Form of
{attribute,_,file,{File,_}} ->
parse_file(Epp,File,true);
{attribute,_,file,{_OtherFile,_}} ->
parse_file(Epp,File,false);
{function,L,F,A,[_|C]} when InCorrectFile ->
Clauses = [{clause,CL} || {clause,CL,_,_,_} <- C],
[{atom_to_list(F),A,L} | Clauses] ++
parse_file(Epp,File,true);
_ ->
parse_file(Epp,File,InCorrectFile)
end;
{error,_E} ->
parse_file(Epp,File,InCorrectFile);
{eof,_Location} ->
[]
end.
%%%-----------------------------------------------------------------
%%% Add a link target for each line and one for each function definition.
build_html(SFd,DFd,Functions) ->
build_html(SFd,DFd,file:read_line(SFd),1,Functions,false).
build_html(SFd,DFd,{ok,Str},L,[{F,A,L}|Functions],_IsFuncDef) ->
FALink = http_uri:encode(F++"-"++integer_to_list(A)),
file:write(DFd,["<a name=\"",FALink,"\"/>"]),
build_html(SFd,DFd,{ok,Str},L,Functions,true);
build_html(SFd,DFd,{ok,Str},L,[{clause,L}|Functions],_IsFuncDef) ->
build_html(SFd,DFd,{ok,Str},L,Functions,true);
build_html(SFd,DFd,{ok,Str},L,Functions,IsFuncDef) ->
LStr = line_number(L),
Str1 = line(Str,IsFuncDef),
file:write(DFd,[LStr,Str1]),
build_html(SFd,DFd,file:read_line(SFd),L+1,Functions,false);
build_html(_SFd,_DFd,eof,L,_Functions,_IsFuncDef) ->
L.
line_number(L) ->
LStr = integer_to_list(L),
Pred =
case length(LStr) of
Length when Length < 5 ->
lists:duplicate(5-Length,$\s);
_ ->
[]
end,
["<a name=\"",LStr,"\"/>",Pred,LStr,": "].
line(Str,IsFuncDef) ->
Str1 = htmlize(Str),
possibly_enhance(Str1,IsFuncDef).
%%%-----------------------------------------------------------------
%%% Substitute special characters that should not appear in HTML
htmlize([$<|Str]) ->
[$&,$l,$t,$;|htmlize(Str)];
htmlize([$>|Str]) ->
[$&,$g,$t,$;|htmlize(Str)];
htmlize([$&|Str]) ->
[$&,$a,$m,$p,$;|htmlize(Str)];
htmlize([$"|Str]) ->
[$&,$q,$u,$o,$t,$;|htmlize(Str)];
htmlize([Ch|Str]) ->
[Ch|htmlize(Str)];
htmlize([]) ->
[].
%%%-----------------------------------------------------------------
%%% Write comments in italic and function definitions in bold.
possibly_enhance(Str,true) ->
case lists:splitwith(fun($() -> false; (_) -> true end, Str) of
{_,[]} -> Str;
{F,A} -> ["<b>",F,"</b>",A]
end;
possibly_enhance([$%|_]=Str,_) ->
["<i>",Str--"\n","</i>","\n"];
possibly_enhance([$-|_]=Str,_) ->
possibly_enhance(Str,true);
possibly_enhance(Str,false) ->
Str.
%%%-----------------------------------------------------------------
%%% End of the file
footer() ->
"".