aboutsummaryrefslogtreecommitdiffstats
path: root/system/doc/top/src
diff options
context:
space:
mode:
authorLars G Thorsen <[email protected]>2010-02-11 07:13:28 +0000
committerErlang/OTP <[email protected]>2010-02-11 07:13:28 +0000
commit75bc5e521dcc9ab798acefb5400f5f15354f08fd (patch)
tree7d30692d4e91dba347f6fabec4e533df1a1cd998 /system/doc/top/src
parente1b759c0863e677527577af7cbf436d0574cbb00 (diff)
downloadotp-75bc5e521dcc9ab798acefb5400f5f15354f08fd.tar.gz
otp-75bc5e521dcc9ab798acefb5400f5f15354f08fd.tar.bz2
otp-75bc5e521dcc9ab798acefb5400f5f15354f08fd.zip
OTP-8343 The documentation is now possible to build in an open source
environment after a number of bugs are fixed and some features are added in the documentation build process. - The arity calculation is updated. - The module prefix used in the function names for bif's are removed in the generated links so the links will look like "http://www.erlang.org/doc/man/erlang.html#append_element-2" instead of "http://www.erlang.org/doc/man/erlang.html#erlang:append_element- 2". - Enhanced the menu positioning in the html documentation when a new page is loaded. - A number of corrections in the generation of man pages (thanks to Sergei Golovan) - The legal notice is taken from the xml book file so OTP's build process can be used for non OTP applications.
Diffstat (limited to 'system/doc/top/src')
-rw-r--r--system/doc/top/src/erl_html_tools.erl293
-rw-r--r--system/doc/top/src/erlresolvelinks.erl158
-rw-r--r--system/doc/top/src/otp_man_index.erl197
3 files changed, 421 insertions, 227 deletions
diff --git a/system/doc/top/src/erl_html_tools.erl b/system/doc/top/src/erl_html_tools.erl
index d93516768e..8a5c744128 100644
--- a/system/doc/top/src/erl_html_tools.erl
+++ b/system/doc/top/src/erl_html_tools.erl
@@ -1,130 +1,66 @@
-%% ``The contents of this file are subject to the Erlang Public License,
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2010. 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 via the world wide web at http://www.erlang.org/.
-%%
+%% 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.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
--module(erl_html_tools).
-
-%% This file contains tools for updating HTML files in an installed system
-%% depending on the installed applications. Currently the only use is
-%% to update the top index file.
-
-
-%% ------ VERY IMPORTANT ------
%%
-%% Original location for this file:
-%% /clearcase/otp/internal_tools/integration/scripts/make_index/
-%% When updating this file, copy the source to
-%% /home/otp/patch/share/program/
-%% and place .beam files (compiled with correct release) in all
-%% /home/otp/patch/share/program/<release>
-%% for releases >= R9C
+%% %CopyrightEnd%
%%
-%% ----------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% This program generate the top index files for the OTP documentation.
-% Part of the HTML code is in templates, like "index.html.src" and
-% part is written out from this module. So the program and the templates
-% has to match.
-%
-% The templates are searched from the current directory, a directory
-% "templates" relative to the current directory or at RootDir.
-%
-% RootDir is given as an argument or assumed to be code:root_dir(),
-% i.e. the root of the system running this program.
-%
-% The output is put into DestDir or RootDir if not given.
-%
-% The functions to call are
-%
-% top_index()
-% top_index([RootDir])
-% top_index([RootDir,DestDir,OtpRel])
-% top_index(RootDir)
-% top_index(RootDir, DestDir, OtpRel)
-%
-% where RootDir can be a string or an atom.
-%
-%
-% USING THIS SCRIPT FROM THE UNIX COMMAND LINE
-% --------------------------------------------
-% If the Erlang started is the same as the Erlang to create index.html
-% for the use
-%
-% % erl -noshell -s erl_html_tools top_index
-%
-% If you want to create an index for another Erlang installation or
-% documentation located separate from the object code, then use
-%
-% % erl -noshell -s erl_html_tools top_index /path/to/erlang/root
-%
-%
-% COLLECTING INFORMATION
-% ----------------------
-% This script assumes that all applications have an "info" file
-% in their top directory. This file should have some keywords with
-% values defined. The keys are 'group' and 'short' (for "short
-% description). See the OTP applications for examples.
-%
-% Some HTML code is generated by this program, others are taken from
-% the file "index.html.src" that may be located in the patch directory
-% or in the "$ERLANG_ROOT/doc/" directory.
-%
-% The code for creating the top index page assumes all applications
-% have a file "info" with some fields filled in
-%
-% short: Text Short text describing the application
-% group: tag [Heading] Group tag optionally followed by a description.
-% Only one app need to describe the group but
-% more than one can.
-%
-% FIXME: Check that there is documentation for the application, not just
-% an info file.
-% FIXME: Use records, it is now unreadable :-(
-% FIXME: Use a separate URL and URLIndexFile
-% FIXME: Pass the OTP release name as an argument instead of in
-% process dictionary (for elegance).
+%%-----------------------------------------------------------------
+%% File: erl_html_tools.erl
+%%
+%% Description:
+%% This file generates the top index of the documentation.
+%%
+%%-----------------------------------------------------------------
+-module(erl_html_tools).
-export([top_index/0,top_index/1,top_index/3,top_index_silent/3]).
-% This is the order groups are inserted into the file. Groups
-% not in this list is inserted in undefined order.
+-include_lib("kernel/include/file.hrl").
group_order() ->
[
- basic,
- dat,
- oam,
- orb,
- comm,
- tools
+ {basic, "Basic"},
+ {dat, "Database"},
+ {oam, "Operation & Maintenance"},
+ {comm, "Interface and Communication"},
+ {tools, "Tools"},
+ {test, "Test"},
+ {doc, "Documentation"},
+ {orb, "Object Request Broker & IDL"},
+ {misc, "Miscellaneous"}
].
top_index() ->
- top_index(code:root_dir()).
+ case os:getenv("ERL_TOP") of
+ false ->
+ io:format("Variable ERL_TOP is required\n",[]);
+ Value ->
+ {_,RelName} = init:script_id(),
+ top_index(Value, filename:join(Value, "doc"), RelName)
+ end.
-top_index([RootDir]) when atom(RootDir) ->
- top_index(atom_to_list(RootDir));
-top_index([RootDir,DestDir,OtpRel])
+top_index([RootDir, DestDir, OtpRel])
when is_atom(RootDir), is_atom(DestDir), is_atom(OtpRel) ->
top_index(atom_to_list(RootDir), atom_to_list(DestDir), atom_to_list(OtpRel));
-top_index(RootDir) ->
+top_index(RootDir) when is_atom(RootDir) ->
{_,RelName} = init:script_id(),
top_index(RootDir, filename:join(RootDir, "doc"), RelName).
+
+
top_index(RootDir, DestDir, OtpRel) ->
report("****\nRootDir: ~p", [RootDir]),
report("****\nDestDir: ~p", [DestDir]),
@@ -145,7 +81,9 @@ top_index_silent(RootDir, DestDir, OtpRel) ->
Result = top_index(RootDir, DestDir, OtpRel),
erase(silent),
Result.
-
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Main loop - process templates
@@ -173,7 +111,7 @@ process_multi_template(BaseName0, Template, DestDir, Info) ->
BaseName1 = filename:basename(BaseName0, Ext),
[_|BaseName2] = lists:reverse(BaseName1),
BaseName = lists:reverse(BaseName2),
- Groups0 = [{[$_|atom_to_list(G)],G} || G <- group_order()],
+ Groups0 = [{[$_|atom_to_list(G)],G} || {G, _} <- group_order()],
Groups = [{"",basic}|Groups0],
process_multi_template_1(Groups, BaseName, Ext, Template, DestDir, Info).
@@ -240,34 +178,47 @@ find_information(Bases) ->
find_application_paths([]) ->
[];
-find_application_paths([{URL,Dir} | Paths]) ->
+find_application_paths([{URL, Dir} | Paths]) ->
Sub1 = "doc/html/index.html",
%% Sub2 = "doc/index.html",
- case file:list_dir(Dir) of
- {ok, Dirs} ->
- AppDirs =
- lists:filter(
- fun(E) ->
- is_match(E, "^[A-Za-z0-9_]+-[0-9\\.]+")
- end, Dirs),
- AppPaths =
- lists:map(
- fun(AppDir) ->
- {ok,[App,Ver]} = regexp:split(AppDir, "-"),
- DirPath = filename:join(Dir,AppDir),
- AppURL = URL ++ AppDir,
- {App,Ver,DirPath,AppURL ++ "/" ++ Sub1}
-%% case file:read_file_info(
-%% filename:join(DirPath, Sub1)) of
-%% {ok, _} ->
-%% {App,Ver,DirPath,AppURL ++ "/" ++ Sub1};
-%% _ ->
-%% {App,Ver,DirPath,AppURL ++ "/" ++ Sub2}
-%% end
- end, AppDirs),
- AppPaths ++ find_application_paths(Paths)
- end.
+ AppDirs = get_app_dirs(Dir),
+
+ AppPaths =
+ lists:map(
+ fun({App, AppPath}) ->
+ VsnFile = filename:join(AppPath, "vsn.mk"),
+ VsnStr =
+ case file:read_file(VsnFile) of
+ {ok, Bin} ->
+ case re:run(Bin, ".*VSN\s*=\s*([0-9\.]+).*",[{capture,[1],list}]) of
+ {match, [V]} ->
+ V;
+ nomatch ->
+ exit(io_lib:format("No VSN variable found in ~s\n",
+ [VsnFile]))
+ end;
+ {error, Reason} ->
+ exit(io_lib:format("~p : ~s\n", [Reason, VsnFile]))
+ end,
+ AppURL = URL ++ App ++ "-" ++ VsnStr,
+ {App, VsnStr, AppPath, AppURL ++ "/" ++ Sub1}
+ end, AppDirs),
+ AppPaths ++ find_application_paths(Paths).
+
+get_app_dirs(Dir) ->
+ {ok, Files} = file:list_dir(Dir),
+ AFiles =
+ lists:map(fun(File) -> {File, filename:join([Dir, File])} end, Files),
+ lists:zf(fun is_app_with_doc/1, AFiles).
+
+is_app_with_doc({ADir, APath}) ->
+ case file:read_file_info(filename:join([APath, "info"])) of
+ {ok, _FileInfo} ->
+ {true, {ADir, APath}};
+ _ ->
+ false
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Find info for one application.
@@ -281,20 +232,20 @@ find_application_paths([{URL,Dir} | Paths]) ->
find_application_infos([]) ->
[];
-find_application_infos([{App,Ver,AppPath,IndexURL} | Paths]) ->
+find_application_infos([{App, Vsn, AppPath, IndexURL} | Paths]) ->
case read_info(filename:join(AppPath,"info")) of
{error,_Reason} ->
warning("No info for app ~p", [AppPath]),
find_application_infos(Paths);
Db ->
- {Group,Heading} =
+ {Group,_Heading} =
case lists:keysearch("group", 1, Db) of
{value, {_, G0}} ->
% This value may be in two parts,
% tag and desciption
- case string:str(G0," ") of
+ case string:str(G0, " ") of
0 ->
- {list_to_atom(G0),""};
+ {list_to_atom(G0), ""};
N ->
{list_to_atom(string:substr(G0,1,N-1)),
string:substr(G0,N+1)}
@@ -309,7 +260,8 @@ find_application_infos([{App,Ver,AppPath,IndexURL} | Paths]) ->
false ->
""
end,
- [{Group,Heading,{App,{Ver,AppPath,IndexURL,Text}}}
+%% [{Group, Heading, {App, {Vsn, AppPath, IndexURL, Text}}}
+ [{Group, "", {App, {Vsn, AppPath, IndexURL, Text}}}
| find_application_infos(Paths)]
end.
@@ -439,17 +391,17 @@ search_appname(App, [{_Group,_,Apps} | Groups]) ->
search_appname(App, Groups)
end;
search_appname(_App, []) ->
- {error,noapp}.
+ {error, noapp}.
subst_applinks(Info, Group) ->
subst_applinks_1(group_order(), Info, Group).
-subst_applinks_1([G|Gs], Info0, Group) ->
+subst_applinks_1([{G, Heading}|Gs], Info0, Group) ->
case lists:keysearch(G, 1, Info0) of
- {value,{G,Heading,Apps}} ->
+ {value,{G,_Heading,Apps}} ->
Info = lists:keydelete(G, 1, Info0),
- ["\n<li>",Heading,"\n<ul>\n",
- html_applinks(Apps),"\n</ul></li>\n"|
+ ["\n<li>",Heading, "\n<ul>\n",
+ html_applinks(Apps), "\n</ul></li>\n"|
subst_applinks_1(Gs, Info, Group)];
false ->
warning("No applications in group ~w\n", [G]),
@@ -457,7 +409,7 @@ subst_applinks_1([G|Gs], Info0, Group) ->
end;
subst_applinks_1([], [], _) -> [];
subst_applinks_1([], Info, _) ->
- error("Info left:\n", [Info]),
+ error("Info left: ~p\n", [Info]),
[].
html_applinks([{Name,[{_,_,URL,_}|_]}|AppNames]) ->
@@ -482,9 +434,9 @@ subst_groups(Info0) ->
subst_known_groups([], Info, Text) ->
{Text,Info};
-subst_known_groups([Group | Groups], Info0, Text0) ->
+subst_known_groups([{Group, Heading} | Groups], Info0, Text0) ->
case lists:keysearch(Group, 1, Info0) of
- {value,{_,Heading,Apps}} ->
+ {value,{_,_Heading,Apps}} ->
Text = group_table(Heading,Apps),
Info = lists:keydelete(Group, 1, Info0),
subst_known_groups(Groups, Info, Text0 ++ Text);
@@ -634,34 +586,36 @@ combine_key_value([]) ->
lines_to_key_value([]) ->
[];
lines_to_key_value([Line | Lines]) ->
- case regexp:first_match(Line, "^[a-zA-Z_\\-]+:") of
+ case re:run(Line, "^[a-zA-Z_\\-]+:") of
nomatch ->
- case regexp:first_match(Line, "[\041-\377]") of
+ case re:run(Line, "[\041-\377]") of
nomatch ->
lines_to_key_value(Lines);
_ ->
warning("skipping line \"~s\"",[Line]),
lines_to_key_value(Lines)
end;
- {match, _, Length} ->
+ {match, [{0, Length} |_]} ->
Value0 = lists:sublist(Line, Length+1, length(Line) - Length),
- {ok, Value1, _} = regexp:sub(Value0, "^[ \t]*", ""),
- {ok, Value, _} = regexp:sub(Value1, "[ \t]*$", ""),
+ Value1 = re:replace(Value0, "^[ \t]*", "",
+ [{return, list}]),
+ Value = re:replace(Value1, "[ \t]*$", "",
+ [{return, list}]),
Key = lists:sublist(Line, Length-1),
- [{Key,Value} | lines_to_key_value(Lines)]
+ [{Key, Value} | lines_to_key_value(Lines)]
end.
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Extensions to the 'regexp' module.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-is_match(Ex, Re) ->
- case regexp:first_match(Ex, Re) of
- {match, _, _} ->
- true;
- nomatch ->
- false
- end.
+%% is_match(Ex, Re) ->
+%% case regexp:first_match(Ex, Re) of
+%% {match, _, _} ->
+%% true;
+%% nomatch ->
+%% false
+%% end.
%% -type gsub(String, RegExp, Fun, Acc) -> subres().
%% Substitute every match of the regular expression RegExp with the
@@ -669,25 +623,27 @@ is_match(Ex, Re) ->
%% regular expressions. Acc is an argument to the Fun. The Fun should return
%% a tuple {Replacement, NewAcc}.
-gsub(String, RegExp, Fun, Acc) when list(RegExp) ->
- case regexp:parse(RegExp) of
- {ok,RE} -> gsub(String, RE, Fun, Acc);
- {error,E} -> {error,E}
+gsub(String, RegExp, Fun, Acc) when is_list(RegExp) ->
+ case re:compile(RegExp) of
+ {ok, RE} ->
+ gsub(String, RE, Fun, Acc);
+ {error, E} ->
+ {error, E}
end;
gsub(String, RE, Fun, Acc) ->
- {match,Ss} = regexp:matches(String, RE),
- {NewString, NewAcc} = sub_repl(Ss, Fun, Acc, String, 1),
- {ok,NewString,NewAcc}.
+ {match, Ss} = re:run(String, RE, [global]),
+ {NewString, NewAcc} = sub_repl(Ss, Fun, Acc, String, 0),
+ {ok, NewString, NewAcc}.
% New code that uses fun for finding the replacement. Also uses accumulator
% to pass argument between the calls to the fun.
-sub_repl([{St,L}|Ss], Fun, Acc0, S, Pos) ->
- Match = string:substr(S, St, L),
+sub_repl([[{St, L}] |Ss], Fun, Acc0, S, Pos) ->
+ Match = string:substr(S, St+1, L),
{Rep, Acc} = Fun(Match, Acc0),
{Rs, NewAcc} = sub_repl(Ss, Fun, Acc, S, St+L),
- {string:substr(S, Pos, St-Pos) ++ Rep ++ Rs, NewAcc};
-sub_repl([], _Fun, Acc, S, Pos) -> {string:substr(S, Pos), Acc}.
+ {string:substr(S, Pos+1, St-Pos) ++ Rep ++ Rs, NewAcc};
+sub_repl([], _Fun, Acc, S, Pos) -> {string:substr(S, Pos+1), Acc}.
@@ -725,3 +681,4 @@ uc([H | T], Acc) when is_integer(H), [97] =< H, H =< $z ->
uc(T, [H - 32 | Acc]);
uc([H | T], Acc) ->
uc(T, [H | Acc]).
+
diff --git a/system/doc/top/src/erlresolvelinks.erl b/system/doc/top/src/erlresolvelinks.erl
index a891b67421..004d0d8626 100644
--- a/system/doc/top/src/erlresolvelinks.erl
+++ b/system/doc/top/src/erlresolvelinks.erl
@@ -1,42 +1,60 @@
--module(erlresolvelinks).
-
-%% ------ VERY IMPORTANT ------
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2010. 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/.
%%
-%% Original location for this file:
-%% /clearcase/otp/internal_tools/integration/scripts/resolve_links/
-%% When updating this file, copy the source to
-%% /usr/local/otp/patch/share/program/
-%% and place .beam files (compiled with correct release) in all
-%% /usr/local/otp/patch/share/program/<release>
-%% for releases >= R10B
+%% 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%
+%%
+%%-----------------------------------------------------------------
+%% File: erlresolvelinks.erl
+%%
+%% Description:
+%% This file generates the javascript that resolves documentation links.
+%%
+%%-----------------------------------------------------------------
+-module(erlresolvelinks).
--export([make/1, do_make/1, do_make/2, do_make/3]).
+-export([make/0, make/1]).
-include_lib("kernel/include/file.hrl").
-define(JAVASCRIPT_NAME, "erlresolvelinks.js").
-make([RootDir]) ->
- do_make(RootDir);
+make() ->
+ case os:getenv("ERL_TOP") of
+ false ->
+ io:format("Variable ERL_TOP is required\n",[]);
+ Value ->
+ make_from_src(Value, ".")
+ end.
+
make([RootDir, DestDir]) ->
do_make(RootDir, DestDir);
-make([RootDir, DestDir, Name]) ->
- do_make(RootDir, DestDir, Name).
-
-do_make(RootDir) ->
+make(RootDir) when is_atom(RootDir) ->
DestDir = filename:join(RootDir, "doc"),
do_make(RootDir, DestDir).
-do_make(RootDir, DestDir) ->
- do_make(RootDir, DestDir, ?JAVASCRIPT_NAME).
+do_make(_RootDir, _DestDir) ->
+ ok.
-do_make(RootDir, DestDir, Name) ->
+make_from_src(RootDir, DestDir) ->
%% doc/Dir
%% erts-Vsn
%% lib/App-Vsn
- DocDirs0 = get_dirs(filename:join([RootDir, "doc"])),
- DocDirs = lists:map(fun(Dir) ->
+ Name = ?JAVASCRIPT_NAME,
+ DocDirs0 = get_dirs(filename:join([RootDir, "system/doc"])),
+ DocDirs = lists:map(fun({Dir, _DirPath}) ->
D = filename:join(["doc", Dir]),
{D, D} end, DocDirs0),
@@ -68,7 +86,10 @@ do_make(RootDir, DestDir, Name) ->
io:fwrite(Fd, "}\n", []),
file:close(Fd),
ok.
-
+
+
+
+
get_dirs(Dir) ->
{ok, Files} = file:list_dir(Dir),
AFiles =
@@ -79,7 +100,7 @@ is_dir({File, AFile}) ->
{ok, FileInfo} = file:read_file_info(AFile),
case FileInfo#file_info.type of
directory ->
- {true, File};
+ {true, {File, AFile}};
_ ->
false
end.
@@ -88,47 +109,66 @@ latest_app_dirs(RootDir, Dir) ->
ADir = filename:join(RootDir, Dir),
RDirs0 = get_dirs(ADir),
RDirs1 = lists:filter(fun is_app_dir/1, RDirs0),
- %% Build a list of {{App, VsnNumList}, AppVsn}
+
SDirs0 =
- lists:map(fun(AppVsn) ->
- [App, VsnStr] = string:tokens(AppVsn, "-"),
- VsnNumList = vsnstr_to_numlist(VsnStr),
- {{App, VsnNumList}, AppVsn} end,
+ lists:map(fun({App, Dir1}) ->
+ File = filename:join(Dir1, "vsn.mk"),
+ case file:read_file(File) of
+ {ok, Bin} ->
+ case re:run(Bin, ".*VSN\s*=\s*([0-9\.]+).*",[{capture,[1],list}]) of
+ {match, [VsnStr]} ->
+ VsnNumList = vsnstr_to_numlist(VsnStr),
+ {{App, VsnNumList}, App++"-"++VsnStr};
+ nomatch ->
+ io:format("No VSN variable found in ~s\n", [File]),
+ error
+ end;
+ {error, Reason} ->
+ io:format("~p : ~s\n", [Reason, File]),
+ error
+ end
+ end,
RDirs1),
- SDirs1 = lists:keysort(1, SDirs0),
- App2Dirs = lists:foldr(fun({{App, _VsnNumList}, AppVsn}, Acc) ->
- case lists:keymember(App, 1, Acc) of
- true ->
- Acc;
- false ->
- [{App, AppVsn}| Acc]
- end
- end, [], SDirs1),
+ SDirs1 = lists:keysort(1, SDirs0),
+ App2Dirs = lists:foldr(fun({{App, _VsnNumList}, AppVsn}, Acc) ->
+ case lists:keymember(App, 1, Acc) of
+ true ->
+ Acc;
+ false ->
+ [{App, AppVsn}| Acc]
+ end
+ end, [], SDirs1),
lists:map(fun({App, AppVsn}) -> {App, filename:join([Dir, AppVsn])} end,
- App2Dirs).
-
-is_app_dir(Dir) ->
- case string:tokens(Dir, "-") of
- [_Name, Rest] ->
- is_vsnstr(Rest);
- _ ->
+ App2Dirs).
+
+is_app_dir({_Dir, DirPath}) ->
+ case file:read_file_info(filename:join(DirPath, "vsn.mk")) of
+ {ok, FileInfo} ->
+ case FileInfo#file_info.type of
+ regular ->
+ true;
+ _ ->
+ false
+ end;
+ {error, _Reason} ->
false
end.
-is_vsnstr(Str) ->
- case string:tokens(Str, ".") of
- [_] ->
- false;
- Toks ->
- lists:all(fun is_numstr/1, Toks)
- end.
-is_numstr(Cs) ->
- lists:all(fun(C) when $0 =< C, C =< $9 ->
- true;
- (_) ->
- false
- end, Cs).
+%% is_vsnstr(Str) ->
+%% case string:tokens(Str, ".") of
+%% [_] ->
+%% false;
+%% Toks ->
+%% lists:all(fun is_numstr/1, Toks)
+%% end.
+
+%% is_numstr(Cs) ->
+%% lists:all(fun(C) when $0 =< C, C =< $9 ->
+%% true;
+%% (_) ->
+%% false
+%% end, Cs).
%% We know:
diff --git a/system/doc/top/src/otp_man_index.erl b/system/doc/top/src/otp_man_index.erl
new file mode 100644
index 0000000000..78ecd8b954
--- /dev/null
+++ b/system/doc/top/src/otp_man_index.erl
@@ -0,0 +1,197 @@
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. 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%
+%%
+%%-----------------------------------------------------------------
+%% File: otp_man_index.erl
+%%
+%% Description:
+%% This file generates the module overview in the documentation.
+%%
+%%-----------------------------------------------------------------
+
+-module(otp_man_index).
+
+-export([gen/1]).
+-include_lib("kernel/include/file.hrl").
+
+
+gen([RootDir, OutFile]) when is_atom(RootDir), is_atom(OutFile)->
+ Bases = [{"../lib/", filename:join(RootDir, "lib")},
+ {"../", RootDir}],
+ Apps = find_application_paths(Bases),
+ RefPages = find_ref_files(Apps),
+ gen_html(RefPages, atom_to_list(OutFile)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Find Reference files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+find_ref_files(Apps) ->
+ find_ref_files(Apps, []).
+
+find_ref_files([], Acc) ->
+ Acc;
+find_ref_files([{App, Vsn, AppPath, RelPath} |Apps], Acc) ->
+ case filelib:wildcard(filename:join(AppPath, "*.html")) of
+ [] ->
+ find_ref_files(Apps, Acc);
+ Result ->
+
+ Refs1 = lists:filter(fun(Ref) ->
+ case file:read_file(Ref) of
+ {ok, Bin} ->
+ case re:run(Bin, ".*<!-- refpage -->.*",[]) of
+ {match, _} ->
+ true;
+ nomatch ->
+ false
+ end;
+ {error, Reason} ->
+ exit(io_lib:format("~p : ~s\n", [Reason, Ref]))
+ end
+ end,
+ Result),
+
+ Refs2 = lists:map(fun(Ref) ->
+ Module = filename:basename(Ref, ".html"),
+ {string:to_lower(Module),
+ Module,
+ App ++ "-" ++ Vsn,
+ RelPath,
+ filename:join(RelPath, filename:basename(Ref))}
+ end,
+ Refs1),
+ find_ref_files(Apps, Refs2 ++ Acc)
+ end.
+
+find_application_paths([]) ->
+ [];
+find_application_paths([{URL, Dir} | Paths]) ->
+ Sub1 = "doc/html",
+
+ AppDirs = get_app_dirs(Dir),
+
+ AppPaths =
+ lists:map(
+ fun({App, AppPath}) ->
+ VsnFile = filename:join(AppPath, "vsn.mk"),
+ VsnStr =
+ case file:read_file(VsnFile) of
+ {ok, Bin} ->
+ case re:run(Bin, ".*VSN\s*=\s*([0-9\.]+).*",[{capture,[1],list}]) of
+ {match, [V]} ->
+ V;
+ nomatch ->
+ exit(io_lib:format("No VSN variable found in ~s\n",
+ [VsnFile]))
+ end;
+ {error, Reason} ->
+ exit(io_lib:format("~p : ~s\n", [Reason, VsnFile]))
+ end,
+ AppURL = URL ++ App ++ "-" ++ VsnStr,
+ {App, VsnStr, AppPath ++ "/" ++ Sub1, AppURL ++ "/" ++ Sub1}
+ end, AppDirs),
+ AppPaths ++ find_application_paths(Paths).
+
+
+get_app_dirs(Dir) ->
+ {ok, Files} = file:list_dir(Dir),
+ AFiles =
+ lists:map(fun(File) -> {File, filename:join([Dir, File])} end, Files),
+ lists:zf(fun is_app_with_doc/1, AFiles).
+
+is_app_with_doc({ADir, APath}) ->
+ case file:read_file_info(filename:join([APath, "info"])) of
+ {ok, _FileInfo} ->
+ {true, {ADir, APath}};
+ _ ->
+ false
+ end.
+
+
+
+
+gen_html(RefPages, OutFile)->
+ case file:open(OutFile, [write]) of
+ {ok, Out} ->
+ io:fwrite(Out, "~s\n", [html_header()]),
+
+ SortedPages = lists:sort(RefPages),
+
+ lists:foreach(fun({_,Module, App, AppDocDir, RefPagePath}) ->
+ io:fwrite(Out, " <TR>\n",[]),
+ io:fwrite(Out, " <TD><A HREF=\"~s\">~s</A></TD>\n",
+ [RefPagePath, Module]),
+ io:fwrite(Out, " <TD><A HREF=\"~s\">~s</A></TD>\n",
+ [filename:join(AppDocDir, "index.html"),
+ App]),
+ io:fwrite(Out, " </TR>\n",[])
+ end,
+ SortedPages),
+
+ {Year, _, _} = date(),
+ io:fwrite(Out, "~s\n", [html_footer(integer_to_list(Year))]);
+ {error, Reason} ->
+ exit("~p: ~s\n",[Reason, OutFile])
+ end.
+
+
+
+html_header() ->
+ "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
+ "<!-- This file was generated by the otp_man_index -->\n"
+ "<HTML>\n"
+ "<HEAD>\n"
+ " <link rel=\"stylesheet\" href=\"otp_doc.css\" type=\"text/css\"/>\n"
+ " <TITLE>Erlang/OTP Manual Page Index</TITLE>\n"
+ "</HEAD>\n"
+ "<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#FF00FF\" ALINK=\"#FF0000\">\n"
+ "<CENTER>\n"
+ "<!-- A HREF=\"http://www.erlang.org/\">\n"
+ "<img alt=\"Erlang logo\" src=\"erlang-logo.png\"/>\n"
+ "</A><BR -->\n"
+ "<SMALL>\n"
+ "[<A HREF=\"index.html\">Up</A> | <A HREF=\"http://www.erlang.org/\">Erlang</A>]\n"
+ "</SMALL><BR>\n"
+ "<P/><FONT SIZE=\"+4\">OTP Reference Page Index</FONT><BR>\n"
+ "</CENTER>\n"
+ "<CENTER>\n"
+ "<P/>\n"
+ "<TABLE BORDER=1>\n"
+ "<TR>\n"
+ " <TH>Manual Page</TH><TH>Application</TH>\n"
+ "</TR>\n".
+
+
+
+html_footer(Year) ->
+ "</TABLE>\n"
+ "</CENTER>\n"
+ "<P/>\n"
+ "<CENTER>\n"
+ "<HR/>\n"
+ "<SMALL>\n"
+ "Copyright &copy; 1991-" ++ Year ++ "\n"
+ "<a href=\"http://www.ericsson.com/technology/opensource/erlang/\">\n"
+ "Ericsson AB</a>\n"
+ "</SMALL>\n"
+ "</CENTER>\n"
+ "</BODY>\n"
+ "</HTML>\n".