aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tools/src/cover_web.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tools/src/cover_web.erl')
-rw-r--r--lib/tools/src/cover_web.erl1184
1 files changed, 1184 insertions, 0 deletions
diff --git a/lib/tools/src/cover_web.erl b/lib/tools/src/cover_web.erl
new file mode 100644
index 0000000000..69f2f3b1aa
--- /dev/null
+++ b/lib/tools/src/cover_web.erl
@@ -0,0 +1,1184 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2009. 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%
+%%
+
+-module(cover_web).
+-author('[email protected]').
+-behaviour(gen_server).
+
+%%Export of configuration function
+-export([configData/0]).
+%% External exports
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-export([start_link/0,start/0,stop/0]).
+-export([menu_frame/2,nodes_frame/2,import_frame/2,
+ compile_frame/2,result_frame/2]).
+-export([list_dir/2,compile/2,add_node/2,remove_node/2,result/2,
+ calls/2,coverage/2,import/2]).
+
+-record(state,{dir}).
+
+-include_lib("kernel/include/file.hrl").
+
+%% Timeouts
+-define(DEFAULT_TIME,10000).
+-define(MAX_COMPILE_TIME,60000).
+-define(MAX_ANALYSE_TIME,30000).
+
+%% Colors
+-define(INFO_BG_COLOR,"#C0C0EA").
+
+%%%----------------------------------------------------------------------
+%%% API - called from erlang shell
+%%%----------------------------------------------------------------------
+%% Start webtool and webcover from erlang shell
+start() ->
+ webtool:start(),
+ webtool:start_tools([],"app=webcover"),
+ ok.
+
+%% Stop webtool and webcover from erlang shell
+stop() ->
+ webtool:stop_tools([],"app=webcover"),
+ webtool:stop().
+
+
+
+%%%----------------------------------------------------------------------
+%%% API - called from webtool
+%%%----------------------------------------------------------------------
+start_link() ->
+ gen_server:start_link({local, webcover_server},cover_web, [], []).
+
+
+nodes_frame(Env,Input)->
+ call({nodes_frame,Env,Input}).
+
+add_node(Env,Input)->
+ call({add_node,Env,Input}).
+
+remove_node(Env,Input)->
+ call({remove_node,Env,Input}).
+
+compile_frame(Env,Input)->
+ call({compile_frame,Env,Input}).
+
+list_dir(Env,Input) ->
+ call({list_dir,Env,Input}).
+
+compile(Env,Input)->
+ call({compile,Env,Input},?MAX_COMPILE_TIME).
+
+result_frame(Env,Input)->
+ call({result_frame,Env,Input}).
+
+result(Env,Input) ->
+ call({result,Env,Input},?MAX_ANALYSE_TIME).
+
+calls(Env,Input) ->
+ call({calls,Env,Input}).
+
+coverage(Env,Input) ->
+ call({coverage,Env,Input}).
+
+import_frame(Env,Input)->
+ call({import_frame,Env,Input}).
+
+import(Env,Input)->
+ call({import,Env,Input}).
+
+menu_frame(Env,Input)->
+ call({menu_frame,Env,Input}).
+
+call(Msg) ->
+ call(Msg,?DEFAULT_TIME).
+call(Msg,Time) ->
+ gen_server:call(webcover_server,Msg,Time).
+
+
+
+configData()->
+ {webcover,[{web_data,{"WebCover","/webcover"}},
+ {alias,{"/webcover",code:priv_dir(tools)}},
+ {alias,{erl_alias,"/webcover/erl",[cover_web]}},
+ {start,{child,{{local,webcover_server},
+ {cover_web,start_link,[]},
+ permanent,100,worker,[cover_web]}}}
+ ]}.
+
+
+%%%----------------------------------------------------------------------
+%%% Callback functions from gen_server
+%%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%%----------------------------------------------------------------------
+init([]) ->
+ cover:start(),
+ CS = whereis(cover_server),
+ link(CS),
+ GL = spawn_link(fun group_leader_proc/0),
+ group_leader(GL,CS),
+
+ %% Must trap exists in order to have terminate/2 executed when
+ %% crashing because of a linked process crash.
+ process_flag(trap_exit,true),
+ {ok,Cwd} = file:get_cwd(),
+ {ok, #state{dir=Cwd}}.
+
+group_leader_proc() ->
+ register(cover_group_leader_proc,self()),
+ group_leader_loop([]).
+group_leader_loop(Warnings) ->
+ receive
+ {io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}} ->
+ Msg = (catch io_lib:Func(Format,Args)),
+ From ! {io_reply,ReplyAs,ok},
+ case lists:member(Msg,Warnings) of
+ true -> group_leader_loop(Warnings);
+ false -> group_leader_loop([Msg|Warnings])
+ end;
+ {io_request,From,ReplyAs,{put_chars,_Encoding,io_lib,Func,[Format,Args]}} ->
+ Msg = (catch io_lib:Func(Format,Args)),
+ From ! {io_reply,ReplyAs,ok},
+ case lists:member(Msg,Warnings) of
+ true -> group_leader_loop(Warnings);
+ false -> group_leader_loop([Msg|Warnings])
+ end;
+ IoReq when element(1,IoReq)=:= io_request ->
+ group_leader() ! IoReq,
+ group_leader_loop(Warnings);
+ {From,get_warnings} ->
+ Warnings1 =
+ receive
+ {io_request,From,ReplyAs,
+ {put_chars,io_lib,Func,[Format,Args]}} ->
+ Msg = (catch io_lib:Func(Format,Args)),
+ From ! {io_reply,ReplyAs,ok},
+ case lists:member(Msg,Warnings) of
+ true -> Warnings;
+ false -> [Msg|Warnings]
+ end
+ after 0 ->
+ Warnings
+ end,
+ From ! {warnings,Warnings1},
+ group_leader_loop([])
+ end.
+
+%%----------------------------------------------------------------------
+%% Func: handle_call/3
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_call({nodes_frame,_Env,_Input},_From,State)->
+ {reply,nodes_frame1(),State};
+
+handle_call({add_node,_Env,Input},_From,State)->
+ {reply,do_add_node(Input),State};
+
+handle_call({remove_node,_Env,Input},_From,State)->
+ {reply,do_remove_node(Input),State};
+
+handle_call({compile_frame,_Env,_Input},_From,State)->
+ {reply,compile_frame1(State#state.dir),State};
+
+handle_call({list_dir,_Env,Input},_From,State)->
+ Dir = get_input_data(Input,"path"),
+ case filelib:is_dir(Dir) of
+ true ->
+ {reply,compile_frame1(Dir),State#state{dir=Dir}};
+ false ->
+ Err = Dir ++ " is not a directory",
+ {reply,compile_frame1(State#state.dir,Err),State}
+ end;
+handle_call({compile,_Env,Input},_From,State)->
+ {reply,do_compile(Input,State#state.dir),State};
+
+handle_call({result_frame,_Env,_Input},_From,State)->
+ {reply,result_frame1(),State};
+
+handle_call({result,_Env,Input},_From,State)->
+ {reply,handle_result(Input),State};
+
+handle_call({calls,_Env,Input},_From,State)->
+ {reply,call_page(Input),State};
+
+handle_call({coverage,_Env,Input},_From,State)->
+ {reply,coverage_page(Input),State};
+
+handle_call({import_frame,_Env,_Input},_From,State)->
+ {ok,Cwd} = file:get_cwd(),
+ {reply,import_frame1(Cwd),State};
+
+handle_call({import,_Env,Input},_From,State)->
+ {reply,do_import(Input),State};
+
+handle_call({menu_frame,_Env,_Input},_From,State)->
+ {reply,menu_frame1(),State};
+
+handle_call(_Request, _From, State) ->
+ Reply = bad_request,
+ {reply, Reply, State}.
+
+
+%%----------------------------------------------------------------------
+%% Func: handle_cast/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_info/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_info({'EXIT',_Pid,Reason}, State) ->
+ {stop, Reason, State}.
+
+%%----------------------------------------------------------------------
+%% Func: terminate/2
+%% Purpose: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%----------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ cover:stop(),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Convert process state when code is changed
+%% Returns: {ok, NewState}
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% The functions that creates the whole pages by collecting all the %%
+%% neccessary data for each page. These functions are the public %%
+%% interface. %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%----------------------------------------------------------------------
+%% Returns the page to the left frame
+%%----------------------------------------------------------------------
+menu_frame1()->
+ [header(),html_header(""),menu_body(),html_end()].
+
+%%----------------------------------------------------------------------
+%% Creates the page where the user can add and remove nodes
+%%----------------------------------------------------------------------
+
+nodes_frame1()->
+ nodes_frame1([]).
+nodes_frame1(Err)->
+ [header(),html_header("Add/remove nodes"),nodes_body(Err),html_end()].
+
+%%----------------------------------------------------------------------
+%% Creates the page where the user can cover compile modules
+%%----------------------------------------------------------------------
+
+compile_frame1(Dir)->
+ compile_frame1(Dir,[]).
+compile_frame1(Dir,Err) ->
+ [header(),html_header("Cover compile"),compile_body(Dir,Err),html_end()].
+
+%%----------------------------------------------------------------------
+%% Creates the page where the user can handle results
+%%----------------------------------------------------------------------
+
+result_frame1()->
+ result_frame1([]).
+result_frame1(Err) ->
+ [header(),html_header("Show cover results"),result_body(Err),html_end()].
+
+%%----------------------------------------------------------------------
+%%The beginning of the page that clear the cover information on a cover
+%%compiled module
+%%----------------------------------------------------------------------
+call_page(Input)->
+ [header(),html_header("Code coverage"),call_result(Input),html_end()].
+
+coverage_page(Input)->
+ [header(),html_header("Code coverage"),coverage_result(Input),html_end()].
+
+%%----------------------------------------------------------------------
+%% Creates the page where the user an import files
+%%----------------------------------------------------------------------
+import_frame1(Dir) ->
+ import_frame1(Dir,"").
+import_frame1(Dir,Err) ->
+ [header(),html_header("Import coverdata"),import_body(Dir,Err),html_end()].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% The functions that build the body of the menu frame %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+menu_body() ->
+ Nodes = cover:which_nodes(),
+ Modules = cover:modules(),
+ Imported = cover:imported(),
+ ["<A HREF=\"./nodes_frame\" TARGET=\"main\">Nodes</A><BR>\n",
+ "<A HREF=\"./compile_frame\" TARGET=\"main\">Compile</A><BR>\n",
+ "<A HREF=\"./import_frame\" TARGET=\"main\">Import</A><BR>\n",
+ "<A HREF=\"./result_frame\" TARGET=\"main\">Result</A>\n",
+ "<P><B>Nodes:</B>\n",
+ "<UL>\n",
+ lists:map(fun(N) -> "<LI>"++atom_to_list(N)++"</LI>\n" end,[node()|Nodes]),
+ "</UL>\n",
+ "<P><B>Compiled modules:</B>\n",
+ "<UL>\n",
+ lists:map(fun(M) -> "<LI>"++atom_to_list(M)++"</LI>\n" end,Modules),
+ "</UL>\n",
+ "<P><B>Imported files:</B>\n",
+ "<UL>\n",
+ "<FONT SIZE=-1>\n",
+ lists:map(fun(F) ->
+ Short = filename:basename(F),
+ "<LI TITLE=\""++F++"\">"++Short++"</LI>\n" end,Imported),
+ "</FONT>\n",
+ "</UL>\n"].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% The functions that build the body of the nodes frame %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+nodes_body(Err) ->
+ CN = cover:which_nodes(),
+ Fun = fun(N) ->
+ NStr = atom_to_list(N),
+ ["<OPTION VALUE=",NStr,
+ " onClick=\"node.value=selected_node.value\">",NStr,
+ "</OPTION>\n"]
+ end,
+ AllNodes = lists:append(lists:map(Fun,nodes()--CN)),
+ CoverNodes = lists:append(lists:map(Fun,CN)),
+
+ [reload_menu_script(Err),
+ "<H1 ALIGN=center>Nodes</H1>\n",
+ "<TABLE BORDER=0 WIDTH=600 ALIGN=center>\n",
+ "<TR><TD BGCOLOR=",?INFO_BG_COLOR," COLSPAN=2>\n",
+ "<P>You can run cover over several nodes simultaneously. Coverage data\n",
+ "from all involved nodes will be merged during analysis.\n",
+ "<P>Select or enter node names to add or remove here.\n",
+ "</TD></TR>\n",
+ "<TR><TD COLSPAN=2><BR><BR></TD></TR>\n",
+ "<FORM ACTION=\"./add_node\" NAME=add_node>\n",
+ "<TR><TD VALIGN=top>Add node:</TD>\n",
+ "<TD><INPUT TYPE=text NAME=\"node\" SIZE=40 >",
+ "<INPUT TYPE=submit\n",
+ " onClick=\"if(!node.value){node.value=selected_node.value};\" VALUE=Add>"
+ "<BR><SELECT NAME=selected_node TITLE=\"Select node\">\n",
+ AllNodes ++
+ "</SELECT>\n",
+ "</TD></TR>\n"
+ "</FORM>\n",
+ "<TR><TD COLSPAN=2><BR><BR></TD></TR>\n",
+ "<FORM ACTION=\"./remove_node\" NAME=remove_node>\n",
+ "<TR><TD>Remove node:</TD>\n",
+ "<TD><SELECT NAME=node TITLE=\"Select node\">\n",
+ CoverNodes ++
+ "</SELECT>\n",
+ "<INPUT TYPE=submit VALUE=Remove>"
+ "</TD></TR>\n",
+ "</FORM>",
+ "</TABLE>"].
+
+
+do_add_node(Input) ->
+ NodeStr = get_input_data(Input, "node"),
+ Node = list_to_atom(NodeStr),
+ case net_adm:ping(Node) of
+ pong ->
+ cover:start(Node),
+ nodes_frame1();
+ pang ->
+ nodes_frame1("Node \\\'" ++ NodeStr ++ "\\\' is not alive")
+ end.
+
+do_remove_node(Input) ->
+ Node = list_to_atom(get_input_data(Input, "node")),
+ cover:stop(Node),
+ nodes_frame1().
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% The functions that is used when the user wants to compile something %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+compile_body(Dir,Err) ->
+ Erls = filelib:wildcard(filename:join(Dir,"*.erl")),
+ Beams = filelib:wildcard(filename:join(Dir,"*.beam")),
+
+ [reload_menu_script(Err),
+ "<H1 ALIGN=center>Compile</H1>\n",
+ "<TABLE WIDTH=600 ALIGN=center BORDER=0>\n",
+ "<TR><TD COLSPAN=3 BGCOLOR=",?INFO_BG_COLOR,">\n",
+ "Each module which shall be part of the cover analysis must be prepared\n",
+ "or 'cover compiled'. On this page you can select .erl files and/or\n",
+ ".beam files to include in the analysis. If you select a .erl file it\n",
+ "will first be compiled with the Erlang compiler and then prepared for\n",
+ "coverage analysis. If you select a .beam file it will be prepared for\n",
+ "coverage analysis directly.\n",
+ "</TD></TR>\n",
+ "<FORM ACTION=\"./list_dir\" NAME=list_dir>\n",
+ "<TR><TD WIDTH=30% BGCOLOR=",?INFO_BG_COLOR," ROWSPAN=2>\n",
+ "To list a different directory, enter the directory name here.\n",
+ "</TD>\n",
+ "<TH COLSPAN=2><BR>List directory:<BR></TH>\n",
+ "</TR>\n",
+ "<TR><TD ALIGN=center COLSPAN=2>\n",
+ "<INPUT TYPE=text NAME=\"path\" SIZE=40 VALUE=",Dir,">",
+ "<INPUT TYPE=submit VALUE=Ok>",
+ "<BR><BR></TD></TR>\n",
+ "</FORM>\n",
+ "<FORM ACTION=\"./compile\" NAME=compile_selection>\n",
+ "<TR><TD BGCOLOR=",?INFO_BG_COLOR," ROWSPAN=2>\n",
+ "<P>Select one or more .erl or .beam files to prepare for coverage\n"
+ "analysis, and click the \"Compile\" button.\n",
+ "<P>To reload the original file after coverage analysis is complete,\n"
+ "select one or more files and click the \"Uncompile\" button, or\n",
+ "simply click the \"Uncompile all\" button to reload all originals.\n"
+ "</TD>\n",
+ "<TH>.erl files</TH><TH>.beam files</TH></TR>\n",
+ "<TR><TD ALIGN=center VALIGN=top>\n",
+ "<SELECT NAME=erl TITLE=\"Select .erl files to compile\" MULTIPLE=true",
+ " SIZE=15>\n",
+ list_modules(Erls) ++
+ "</SELECT></TD>\n",
+ "<TD ALIGN=center VALIGN=top>\n",
+ "<SELECT NAME=beam TITLE=\"Select .beam files to compile\"MULTIPLE=true",
+ " SIZE=15>\n",
+ list_modules(Beams) ++
+ "</SELECT></TD></TR>\n"
+ "<TR><TD BGCOLOR=",?INFO_BG_COLOR," ROWSPAN=2>\n",
+ "Compile options are only needed for .erl files. The options must be\n"
+ "given e.g. like this: \n"
+ "<FONT SIZE=-1>[{i,\"/my/path/include\"},{i,\"/other/path/\"}]</FONT>\n"
+ "</TD>\n",
+ "<TH COLSPAN=2><BR>Compile options:<BR></TH>\n",
+ "</TR>\n",
+ "<TR><TD COLSPAN=2 ALIGN=center>\n",
+ "<INPUT TYPE=text NAME=\"options\" SIZE=40>\n",
+ "<INPUT TYPE=hidden NAME=\"action\"></TD></TR>\n",
+ "<TR><TD></TD><TD ALIGN=center COLSPAN=2>\n",
+ "<INPUT TYPE=submit onClick=\"action.value=\'compile\';\"VALUE=Compile>",
+ "<INPUT TYPE=submit onClick=\"action.value=\'uncompile\';\" ",
+ "VALUE=Uncompile>",
+ "<INPUT TYPE=submit onClick=\"action.value=\'uncompile_all\';\" ",
+ "VALUE=\"Uncompile all\">",
+ "<BR><INPUT TYPE=reset VALUE=\"Reset form\"></TD></TR>\n",
+ "</FORM>\n",
+ "</TABLE>\n"].
+
+list_modules([File|Files]) ->
+ Mod = filename:basename(File),
+ ["<OPTION VALUE=",File," onDblClick=\"action.value=\'compile\';submit();\">",
+ Mod,"</OPTION>\n" | list_modules(Files)];
+list_modules([]) ->
+ [].
+
+do_compile(Input,Dir) ->
+ {Erls,Beams,Opts,Action} = get_compile_input(parse(Input),[],[]),
+ Errs =
+ case Action of
+ "compile" ->
+ do_compile(Erls,Beams,Opts,[]);
+ "uncompile" ->
+ do_uncompile(Erls++Beams);
+ "uncompile_all" ->
+ do_uncompile(cover:modules())
+ end,
+ compile_frame1(Dir,Errs).
+
+get_compile_input([{"erl",File}|Input],Erl,Beam) ->
+ get_compile_input(Input,[File|Erl],Beam);
+get_compile_input([{"beam",File}|Input],Erl,Beam) ->
+ get_compile_input(Input,Erl,[File|Beam]);
+get_compile_input([{"options",Opts0},{"action",Action}],Erl,Beam) ->
+ Opts = parse_options(Opts0),
+ {Erl,Beam,Opts,Action}.
+
+do_compile([Erl|Erls],Beams,Opts,Errs) ->
+ case cover:compile_module(Erl,Opts) of
+ {ok,_} ->
+ do_compile(Erls,Beams,Opts,Errs);
+ {error,File} ->
+ do_compile(Erls,Beams,Opts,["\\n"++File|Errs])
+ end;
+do_compile([],[Beam|Beams],Opts,Errs) ->
+ case cover:compile_beam(Beam) of
+ {ok,_} ->
+ do_compile([],Beams,Opts,Errs);
+ {error,{no_abstract_code,File}} ->
+ do_compile([],Beams,Opts,["\\n"++File++" (no_abstract_code)"|Errs])
+ end;
+do_compile([],[],_,[]) ->
+ [];
+do_compile([],[],_,Errs) ->
+ "Compilation failed for the following files:" ++ Errs.
+
+parse_options(Options)->
+ case erl_scan:string(Options ++".") of
+ {ok,Tokens,_Line} ->
+ case erl_parse:parse_exprs(Tokens) of
+ {ok,X}->
+ case lists:map(fun erl_parse:normalise/1, X) of
+ [List] when is_list(List) -> List;
+ List -> List
+ end;
+ _ ->
+ []
+ end;
+ _ ->
+ []
+ end.
+
+
+do_uncompile(Files) ->
+ lists:foreach(
+ fun(File) ->
+ Module =
+ if is_atom(File) ->
+ File;
+ true ->
+ ModStr = filename:basename(filename:rootname(File)),
+ list_to_atom(ModStr)
+ end,
+ case code:which(Module) of
+ cover_compiled ->
+ code:purge(Module),
+ case code:load_file(Module) of
+ {module, Module} ->
+ ok;
+ {error, _Reason2} ->
+ code:delete(Module)
+ end;
+ _ ->
+ ok
+ end
+ end,
+ Files),
+ [].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% The functions that builds the body of the page for coverage analysis%
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+result_body(Err) ->
+ [reload_menu_script(Err),
+ "<H1 ALIGN=center>Result</H1>\n",
+ "<TABLE BORDER=0 WIDTH=600 ALIGN=center>\n",
+ "<TR><TD BGCOLOR=",?INFO_BG_COLOR,">\n",
+ "<P>After executing all your tests you can view the result of the\n",
+ "coverage analysis here. For each module you can\n",
+ "<DL>\n",
+ "<DT><B>Analyse to file</B></DT>\n",
+ "<DD>The source code of the module is shown with the number of calls\n",
+ "to each line stated in the left margin. Lines which are never called\n",
+ "are colored red.</DD>\n",
+ "<DT><B>Analyse coverage</B></DT>\n",
+ "<DD>Show the number of covered and uncovered lines in the module.</DD>\n",
+ "<DT><B>Analyse calls</B></DT>\n",
+ "<DD>Show the number of calls in the module.</DD>\n",
+ "<DT><B>Reset module</B></DT>\n",
+ "<DD>Delete all coverage data for the module.</DD>\n",
+ "<DT><B>Export module</B></DT>\n",
+ "<DD>Write all coverage data for the module to a file. The data can\n",
+ "later be imported from the \"Import\" page.</DD>\n",
+ "</DL>\n",
+ "<P>You can also reset or export data for all modules with the\n",
+ "<B>Reset all</B> and <B>Export all</B> actions respectively. For these\n",
+ "two actions there is no need to select a module.\n",
+ "<P>Select module and action from the drop down menus below, and click\n",
+ "the \"Execute\" button.\n",
+ "</TD></TR>\n",
+ "<TR><TD><BR><BR>\n",
+ result_selections(),
+ "</TD></TR></TABLE>"].
+
+result_selections() ->
+ ModList = filter_modlist(cover:modules()++cover:imported_modules(),[]),
+
+ ["<FORM ACTION=\"./result\" NAME=result_selection>\n",
+ "<TABLE WIDTH=\"300\" BORDER=0 ALIGN=center>\n",
+ "<TR><TD ALIGN=left>\n",
+ "Module:\n",
+ "<BR><SELECT NAME=module TITLE=\"Select module\">\n",
+ ModList ++
+ "</SELECT>\n",
+ "</TD>\n",
+ "<TD ALIGN=left>\n",
+ "Action:\n",
+ "<BR><SELECT NAME=action TITLE=\"Select action\">\n",
+ "<OPTION VALUE=\"analyse_to_file\">Analyse to file</OPTION>\n"
+ "<OPTION VALUE=\"coverage\">Analyse coverage</OPTION>\n"
+ "<OPTION VALUE=\"calls\">Analyse calls</OPTION>\n"
+ "<OPTION VALUE=\"reset\">Reset module</OPTION>\n"
+ "<OPTION VALUE=\"reset_all\">Reset all</OPTION>\n"
+ "<OPTION VALUE=\"export\">Export module</OPTION>\n"
+ "<OPTION VALUE=\"export_all\">Export all</OPTION>\n"
+ "</SELECT>\n",
+ "</TD>\n",
+ "<TD ALIGN=center VALIGN=bottom><INPUT TYPE=submit VALUE=Execute>\n"
+ "</TD></TR>\n"
+ "</TABLE>\n",
+ "</FORM>\n"].
+
+filter_modlist([M|Ms],Already) ->
+ case lists:member(M,Already) of
+ true ->
+ filter_modlist(Ms,Already);
+ false ->
+ MStr = atom_to_list(M),
+ ["<OPTION VALUE=",MStr,">",MStr,"</OPTION>\n" |
+ filter_modlist(Ms,[M|Already])]
+ end;
+filter_modlist([],_Already) ->
+ [].
+
+
+
+handle_result(Input) ->
+ case parse(Input) of
+ [{"module",M},{"action",A}] ->
+ case A of
+ "analyse_to_file" ->
+ case cover:analyse_to_file(list_to_atom(M),[html]) of
+ {ok,File} ->
+ case file:read_file(File) of
+ {ok,HTML}->
+ file:delete(File),
+ [header(),
+ reload_menu_script(""),
+ binary_to_list(HTML)];
+ _ ->
+ result_frame1("Can not read file" ++ File)
+ end;
+ {error,no_source_code_found} ->
+ result_frame1("No source code found for \\\'" ++
+ M ++ "\\\'")
+ end;
+ "calls" ->
+ call_page(Input);
+ "coverage" ->
+ coverage_page(Input);
+ "reset" ->
+ cover:reset(list_to_atom(M)),
+ result_frame1("Coverage data for \\\'" ++ M ++
+ "\\\' is now reset");
+ "reset_all" ->
+ cover:reset(),
+ result_frame1("All coverage data is now reset");
+ "export" ->
+ ExportFile = generate_filename(M),
+ cover:export(ExportFile,list_to_atom(M)),
+ result_frame1("Coverage data for \\\'" ++ M ++
+ "\\\' is now exported to file \\\"" ++
+ ExportFile ++ "\\\"");
+ "export_all" ->
+ ExportFile = generate_filename("COVER"),
+ cover:export(ExportFile),
+ result_frame1(
+ "All coverage data is now exported to file \\\"" ++
+ ExportFile ++ "\\\"")
+ end;
+ [{"action",_A}] ->
+ result_frame1("No module is selected")
+ end.
+
+generate_filename(Prefix) ->
+ {ok,Cwd} = file:get_cwd(),
+ filename:join(Cwd,Prefix ++ "_" ++ ts() ++ ".coverdata").
+
+ts() ->
+ {{Y,M,D},{H,Min,S}} = calendar:now_to_local_time(now()),
+ io_lib:format("~4.4.0w~2.2.0w~2.2.0w-~2.2.0w~2.2.0w~2.2.0w",
+ [Y,M,D,H,Min,S]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% The functions that builds the body of the page that shows the calls %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+call_result(Input)->
+ Mod = list_to_atom(get_input_data(Input, "module")),
+ case cover:analyse(Mod,calls) of
+ {error,_}->
+ error_body();
+ {ok,_} ->
+ call_result2(Mod,Input)
+ end.
+
+call_result2(Mod,Input)->
+ Result =
+ case get_input_data(Input,"what") of
+ "mod" ->
+ call_result(mod,Mod);
+ "func" ->
+ call_result(func,Mod);
+ "clause" ->
+ call_result(clause,Mod);
+ _->
+ call_result(all,Mod)
+ end,
+ result_choice("calls",Mod) ++ Result.
+
+result_choice(Level,Mod)->
+ ModStr=atom_to_list(Mod),
+ [reload_menu_script(""),
+ "<TABLE WIDTH=100%><TR>\n",
+ "<TD><A HREF=./",Level,"?module=",ModStr,"&what=all>All Data</A></TD>\n",
+ "<TD><A HREF=./",Level,"?module=",ModStr,"&what=mod>Module</A></TD>\n",
+ "<TD><A HREF=./",Level,"?module=",ModStr,"&what=func>Function</A></TD>\n",
+ "<TD><A HREF=./",Level,"?module=",ModStr,"&what=clause>Clause</A></TD>\n",
+ "</TR></TABLE><BR>\n"].
+
+call_result(Mode,Module)->
+ Content =
+ case Mode of
+ mod->
+ format_cover_call(cover:analyse(Module,calls,module),mod);
+ func->
+ format_cover_call(cover:analyse(Module,calls,function),func);
+ clause->
+ format_cover_call(cover:analyse(Module,calls,clause),clause);
+ _->
+ format_cover_call(cover:analyse(Module,calls,module),mod) ++
+ format_cover_call(cover:analyse(Module,calls,function),func)++
+ format_cover_call(cover:analyse(Module,calls,clause),clause)
+ end,
+ getModDate(Module,date())++"<BR>"++
+ "<TABLE WIDTH=\"100%\" BORDER=1>"
+ ++ Content ++"</TABLE>".
+
+
+format_cover_call({error,_},_)->
+ ["<TR><TD>\n",
+ "<BR><BR><BR><BR>\n",
+ "<FONT SIZE=5>The selected module is not Cover Compiled</FONT>\n",
+ "<BR>\n",
+ "</TD></TR>\n"];
+
+format_cover_call({ok,{Mod,Calls}},mod)->
+ ["<TR BGCOLOR=\"#8899AA\"><TD COLSPAN=5><B>Module calls</B></TD></TR>\n",
+ "<TR><TD COLSPAN=4><I>Module</I></TD>",
+ "<TD ALIGN=\"right\"><I>Number of calls</I></TD></TR>\n",
+ "<TR><TD COLSPAN=4>" ++ atom_to_list(Mod) ++"</TD>"
+ "<TD ALIGN=\"right\">" ++ integer_to_list(Calls)++"</TD></TR>\n"];
+
+format_cover_call({ok,Calls},func)->
+ ["<TR BGCOLOR=\"#8899AA\"><TD COLSPAN=5><B>Function calls</B></TD></TR>\n",
+ "<TR><TD><I>Module</I></TD><TD><I>Function</I></TD>",
+ "<TD COLSPAN=2 ALIGN=\"right\"><I>Arity</I></TD>",
+ "<TD ALIGN=\"right\"><I>Number of calls </I></TD></TR>\n",
+ lists:append(
+ lists:map(
+ fun({{Mod,Func,Arity},Nr_of_calls})->
+ ["<TR><TD WIDTH=\"20%\">"++ atom_to_list(Mod)++"</TD>\n",
+ "<TD WIDTH=\"20%\" >" ++ atom_to_list(Func) ++" </TD>\n",
+ "<TD COLSPAN=2 WIDTH=\"40%\" ALIGN=\"right\">",
+ integer_to_list(Arity),
+ "</TD>\n",
+ "<TD WIDTH=\"20%\" ALIGN=\"right\">",
+ integer_to_list(Nr_of_calls),
+ "</TD></TR>\n"]
+ end,
+ Calls))];
+
+format_cover_call({ok,Calls},clause)->
+ ["<TR BGCOLOR=\"#8899AA\"><TD COLSPAN=5><B>Clause calls</B></TD></TR>\n",
+ "<TR><TD><I>Module</I></TD><TD><I>Function</I></TD>",
+ "<TD ALIGN=\"right\"><I>Arity</I></TD>",
+ "<TD ALIGN=\"right\"><I>Ordinal</I></TD>",
+ "<TD ALIGN=\"right\"><I>Number of calls</I></TD></TR>\n",
+ lists:append(
+ lists:map(
+ fun({{Mod,Func,Arity,Ord},Nr_of_calls})->
+ ["<TR><TD WIDTH=\"20%\" >", atom_to_list(Mod), "</TD>\n",
+ "<TD WIDTH=\"20%\" >", atom_to_list(Func), "</TD>\n",
+ "<TD WIDTH=\"20%\" ALIGN=\"right\">",
+ integer_to_list(Arity),
+ "</TD>\n",
+ "<TD WIDTH=\"20%\" ALIGN=\"right\">",
+ integer_to_list(Ord),
+ "</TD>\n",
+ "<TD WIDTH=\"20%\" ALIGN=\"right\">",
+ integer_to_list(Nr_of_calls),
+ "</TD></TR>\n"]
+ end,
+ Calls))].
+
+
+error_body()->
+ ["<TABLE WIDTH=\"100%\" BORDER=1>\n",
+ "<TR ALIGN=\"center\">\n",
+ "<TD>\n",
+ "<BR><BR><BR><BR><BR><BR>\n",
+ "<FONT SIZE=5>The selected module is not Cover Compiled</FONT>\n",
+ "<BR>\n",
+ "</TD>\n",
+ "</TR>\n",
+ "</TABLE>\n"].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% The functions that builds the body of the page that shows coverage %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+coverage_result(Input)->
+ Mod = list_to_atom(get_input_data(Input, "module")),
+ case cover:analyse(Mod,coverage) of
+ {error,_}->
+ error_body();
+ {ok,_} ->
+ coverage_result2(Mod,Input)
+ end.
+
+coverage_result2(Mod,Input)->
+ Result =
+ case get_input_data(Input,"what") of
+ "mod" ->
+ coverage_result(mod,Mod);
+ "func" ->
+ coverage_result(func,Mod);
+ "clause" ->
+ coverage_result(clause,Mod);
+ _->
+ coverage_result(all,Mod)
+ end,
+ result_choice("coverage",Mod) ++ Result.
+
+coverage_result(Mode,Module)->
+ Content =
+ case Mode of
+ mod->
+ format_cover_coverage(cover:analyse(Module,coverage,module),
+ mod);
+ func->
+ format_cover_coverage(cover:analyse(Module,coverage,function),
+ func);
+ clause->
+ format_cover_coverage(cover:analyse(Module,coverage,clause),
+ clause);
+ _->
+ format_cover_coverage(cover:analyse(Module,coverage,module),
+ mod) ++
+ format_cover_coverage(cover:analyse(Module,coverage,function),
+ func)++
+ format_cover_coverage(cover:analyse(Module,coverage,clause),
+ clause)
+ end,
+ getModDate(Module,date())++"<BR>"++
+ "<TABLE WIDTH=\"100%\" BORDER=1>"
+ ++ Content ++"</TABLE>".
+
+getModDate(Module,{Year,Mon,Day})->
+ "<TABLE>
+ <TR>
+ <TD>Module:</TD>
+ <TD>" ++ atom_to_list(Module) ++ "</TD>
+ </TR>
+ <TR>
+ <TD>Date:</TD>
+ <TD>" ++ integer_to_list(Day) ++ "/" ++
+ integer_to_list(Mon) ++"&nbsp;-&nbsp;"++
+ integer_to_list(Year) ++
+ "</TD>
+ </TR>
+ </TABLE>".
+
+
+format_cover_coverage({error,_},_)->
+ "<TR><TD>
+ <BR><BR><BR><BR>
+ <FONT SIZE=5>The selected module is not Cover Compiled</FONT>
+ <BR>
+ </TD></TR>";
+
+
+format_cover_coverage({ok,{Mod,{Cov,Not_cov}}},mod)->
+ ["<TR BGCOLOR=\"#8899AA\"><TD COLSPAN=6><B>Module coverage</B></TD></TR>\n",
+ "<TR><TD COLSPAN=4><I>Module</I></TD>\n",
+ "<TD ALIGN=\"right\"><I>Covered</I></TD>\n"
+ "<TD ALIGN=\"RIGHT\" NOWRAP=\"true\"><I>Not Covered</I></TD>\n",
+ "</TR>\n",
+ "<TR><TD COLSPAN=4>", atom_to_list(Mod), "</TD>\n"
+ "<TD ALIGN=\"right\">", integer_to_list(Cov), "</TD>\n"
+ "<TD ALIGN=\"right\" >", integer_to_list(Not_cov), "</TD></TR>\n"];
+
+format_cover_coverage({ok,Cov_res},func)->
+ ["<TR BGCOLOR=\"#8899AA\"><TD COLSPAN=6><B>Function coverage</B></TD>\n",
+ "</TR>\n",
+ "<TR><TD><I>Module</I></TD><TD><I>Function</I></TD>",
+ "<TD ALIGN=\"right\"><I>Arity</I></TD>",
+ "<TD COLSPAN=2 ALIGN=\"right\"><I>Covered</I></TD>",
+ "<TD ALIGN=\"right\" STYLE=\"white-space:nowrap\"><I>Not Covered</I></TD>",
+ "</TR>\n",
+ lists:append(
+ lists:map(
+ fun({{Mod,Func,Arity},{Cov,Not_cov}})->
+ ["<TR><TD WIDTH=\"20%\" >"++ atom_to_list(Mod) ++" </TD>\n",
+ "<TD WIDTH=\"20%\" >" ++ atom_to_list(Func) ++"</TD>\n",
+ "<TD WIDTH=\"40%\" ALIGN=\"right\">",
+ integer_to_list(Arity),
+ "</TD>\n",
+ "<TD WIDTH=\"40%\" ALIGN=\"right\" COLSPAN=2>",
+ integer_to_list(Cov),
+ "</TD>\n"
+ "<TD WIDTH=\"20%\" ALIGN=\"right\">",
+ integer_to_list(Not_cov),
+ "</TD></TR>\n"]
+ end,
+ Cov_res))];
+
+format_cover_coverage({ok,Cov_res},clause)->
+ ["<TR BGCOLOR=\"#8899AA\"><TD COLSPAN=6><B>Clause coverage</B></TD></TR>\n",
+ "<TR><TD><I>Module</I></TD><TD><I>Function</I></TD>\n",
+ "<TD ALIGN=\"right\"><I>Arity</I></TD>\n",
+ "<TD ALIGN=\"right\"><I>Ordinal<I></TD>\n",
+ "<TD ALIGN=\"right\">Covered</TD>\n",
+ "<TD ALIGN=\"right\" STYLE=\"white-space:nowrap\">Not Covered</TD></TR>\n",
+ lists:append(
+ lists:map(
+ fun({{Mod,Func,Arity,Ord},{Cov,Not_cov}})->
+ ["<TR><TD WIDTH=\"20%\" >"++ atom_to_list(Mod) ++"</TD>\n",
+ "<TD WIDTH=\"20%\" >" ++ atom_to_list(Func) ++" </TD>\n",
+ "<TD WIDTH=\"20%\" ALIGN=\"right\">",
+ integer_to_list(Arity),
+ "</TD>\n"
+ "<TD WIDTH=\"20%\" ALIGN=\"right\">",
+ integer_to_list(Ord),
+ "</TD>\n"
+ "<TD WIDTH=\"20%\" ALIGN=\"right\">",
+ integer_to_list(Cov),
+ "</TD>\n"
+ "<TD WIDTH=\"20%\" ALIGN=\"right\">",
+ integer_to_list(Not_cov),
+ "</TD></TR>\n"]
+ end,
+ Cov_res))].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% The functions that builds the body of the import page %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+import_body(Dir,Err) ->
+ [reload_menu_script(Err),
+ "<H1 ALIGN=center>Import</H1>\n",
+ "<TABLE BORDER=0 WIDTH=600 ALIGN=center>\n",
+ "<TR><TD BGCOLOR=",?INFO_BG_COLOR,">\n",
+ "<P>You can import coverage data from a previous analysis. If you do so\n",
+ "the imported data will be merged with the current coverage data.\n",
+ "<P>You can export data from the current analysis from the \"Result\"\n",
+ "page.\n",
+ "<P>Select the file to import here.\n",
+ "</TD></TR>\n",
+ "<TR><TD ALIGN=center><BR><BR>\n",
+ "<FORM NAME=change_import_dir METHOD=post ACTION=\"./import\">\n",
+ "<B>Change directory:</B><BR>\n",
+ "<INPUT TYPE=text NAME=\"file\" SIZE=30 VALUE=",Dir,">",
+ "<INPUT TYPE=hidden NAME=dir VALUE=",Dir,">\n",
+ "<INPUT TYPE=submit VALUE=Ok><BR>\n",
+ "</FORM>\n",
+ browse_import(Dir),
+ "</TABLE>"].
+
+browse_import(Dir) ->
+ {ok,List} = file:list_dir(Dir),
+ Sorted = lists:reverse(lists:sort(List)),
+ {Dirs,Files} = filter_files(Dir,Sorted,[],[]),
+ ["<FORM NAME=browse_import METHOD=post ACTION=\"./import\">\n"
+ "<SELECT NAME=file TITLE=\"Select import file\" SIZE=10>\n",
+ "<OPTION VALUE=\"..\" onDblClick=submit()>../</OPTION>\n",
+ Dirs,
+ Files,
+ "</SELECT>\n",
+ "<INPUT TYPE=hidden NAME=dir VALUE=",Dir,">\n",
+ "<BR><INPUT TYPE=submit VALUE=Ok>\n"
+ "</FORM>\n"].
+
+filter_files(Dir,[File|Files],Ds,Fs) ->
+ case filename:extension(File) of
+ ".coverdata" ->
+ Fs1 = ["<OPTION VALUE=",File," onDblClick=submit()>",
+ File,"</OPTION>\n" | Fs],
+ filter_files(Dir,Files,Ds,Fs1);
+ _ ->
+ FullName = filename:join(Dir,File),
+ case filelib:is_dir(FullName) of
+ true ->
+ Ds1 = ["<OPTION VALUE=",File," onDblClick=submit()>",
+ File,"/</OPTION>\n" | Ds],
+ filter_files(Dir,Files,Ds1,Fs);
+ false ->
+ filter_files(Dir,Files,Ds,Fs)
+ end
+ end;
+filter_files(_Dir,[],Ds,Fs) ->
+ {Ds,Fs}.
+
+
+
+
+do_import(Input) ->
+ case parse(Input) of
+ [{"file",File0},{"dir",Dir}] ->
+ File = filename:join(Dir,File0),
+ case filelib:is_dir(File) of
+ true ->
+ import_frame1(File);
+ false ->
+ case filelib:is_file(File) of
+ true ->
+ case cover:import(File) of
+ ok ->
+ import_frame1(Dir);
+ {error,{cant_open_file,ExportFile,_Reason}} ->
+ import_frame1(Dir,
+ "Error importing file\\n\\\""
+ ++ ExportFile ++ "\\\"")
+ end;
+ false ->
+ import_frame1(Dir,
+ "Error importing file\\n\\\"" ++
+ File ++ "\\\"")
+ end
+ end;
+ [{"dir",Dir}] ->
+ import_frame1(Dir,"No file is selected")
+ end.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% Different private helper functions %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%Create the Header for the page If we now the mimetype use that type %%
+%%otherwise use text %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+header() ->
+ header("text/html").
+header(MimeType) ->
+ "Pragma:no-cache\r\n" ++
+ "Content-type: " ++ MimeType ++ "\r\n\r\n".
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%Create the Htmlheader set the title of the page %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+html_header(Title) ->
+ "<HTML>\n" ++
+ "<HEAD>\n" ++
+ "<TITLE>" ++ Title ++ "</TITLE>\n" ++
+ "</HEAD>\n"
+ "<BODY BGCOLOR=\"#FFFFFF\">\n".
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Close the body- and Html tags %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+html_end()->
+ "</BODY></HTML>".
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% A script which reloads the menu frame and possibly pops up an alert%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+reload_menu_script(Err) ->
+ ["<SCRIPT>\n",
+ "function reloadMenu()\n",
+ " {\n",
+ " parent.menu.document.location.href=\"./menu_frame\";\n",
+ case Err of
+ "" -> "";
+ _ -> " alert(\""++Err++"\");\n"
+ end,
+ case get_warnings() of
+ [] ->
+ "";
+ Warnings ->
+ " alert(\""++fix_newline(lists:flatten(Warnings))++"\");\n"
+ end,
+ " }\n",
+ "</SCRIPT>\n",
+ "<BODY onLoad=reloadMenu() BGCOLOR=\"#FFFFFF\">"].
+
+fix_newline([$\n|Rest]) ->
+ [$\\,$n|fix_newline(Rest)];
+fix_newline([$"|Rest]) ->
+ [$\\,$"|fix_newline(Rest)];
+fix_newline([Char|Rest]) ->
+ [Char|fix_newline(Rest)];
+fix_newline([]) ->
+ [].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Control the input data and return the intresting values or error %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+get_input_data(Input,Key)->
+ case lists:keysearch(Key,1,parse(Input)) of
+ {value,{Key,Value}} ->
+ Value;
+ false ->
+ undefined
+ end.
+
+parse(Input) ->
+ httpd:parse_query(Input).
+
+
+get_warnings() ->
+ cover_group_leader_proc ! {self(), get_warnings},
+ receive {warnings,Warnings} ->
+ Warnings
+ end.