diff options
Diffstat (limited to 'lib/docbuilder/src')
32 files changed, 6360 insertions, 0 deletions
diff --git a/lib/docbuilder/src/Makefile b/lib/docbuilder/src/Makefile new file mode 100644 index 0000000000..e8a07a54e8 --- /dev/null +++ b/lib/docbuilder/src/Makefile @@ -0,0 +1,121 @@ +# ``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/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +# Utvecklings AB. All Rights Reserved.'' +# +# $Id$ +# + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../vsn.mk +VSN=$(DOCB_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/docbuilder-$(VSN) + +# ---------------------------------------------------- +# Common Macros +# ---------------------------------------------------- + +MODULES= \ + docb_edoc_xml_cb \ + docb_gen \ + docb_html \ + docb_html_layout \ + docb_html_ref \ + docb_html_util \ + docb_html_util_iso \ + docb_main \ + docb_pretty_format \ + docb_tr_application2html \ + docb_tr_appref2html \ + docb_tr_chapter2html \ + docb_tr_cite2html \ + docb_tr_comref2html \ + docb_tr_cref2html \ + docb_tr_erlref2html \ + docb_tr_fileref2html \ + docb_tr_first2html \ + docb_tr_index2html \ + docb_tr_part2html \ + docb_tr_refs2kwic \ + docb_tr_report2html \ + docb_tr_term2html \ + docb_transform \ + docb_util \ + docb_xmerl_tree_cb \ + docb_xmerl_xml_cb \ + docb_xml_check + +HRL_FILES= \ + docb_util.hrl + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +APP_FILE= docbuilder.app +APPUP_FILE= docbuilder.appup +APP_SRC= $(APP_FILE).src +APPUP_SRC= $(APPUP_FILE).src +APP_TARGET= $(EBIN)/$(APP_FILE) +APPUP_TARGET= $(EBIN)/$(APPUP_FILE) + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_FLAGS += +XMERL = ../../xmerl +ERL_COMPILE_FLAGS += -I$(XMERL)/include + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) + +clean: + rm -f $(TARGET_FILES) $(APP_TARGET) + rm -f errs core *~ + +$(APP_TARGET): $(APP_SRC) ../vsn.mk + sed -e 's;%VSN%;$(VSN);' $< > $@ +$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk + sed -e 's;%VSN%;$(VSN);' $< > $@ + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src + $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin + +release_docs_spec: + + + + + diff --git a/lib/docbuilder/src/docb_edoc_xml_cb.erl b/lib/docbuilder/src/docb_edoc_xml_cb.erl new file mode 100644 index 0000000000..4dba843341 --- /dev/null +++ b/lib/docbuilder/src/docb_edoc_xml_cb.erl @@ -0,0 +1,1163 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See +%% the Licence for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson AB. +%% Portions created by Ericsson are Copyright 1999-2006, Ericsson AB. +%% All Rights Reserved.�� +%% +%% $Id$ +%% +-module(docb_edoc_xml_cb). + +%% This is the EDoc callback module for creating DocBuilder erlref +%% documents (man pages) in XML format, and also a DocBuilder chapter +%% document based on "overview.edoc". +%% +%% Usage examples: +%% docb_gen File +%% docb_gen -chapter overview.edoc +%% or (from an Erlang shell) +%% edoc:file(File, [{layout,docb_edoc_xml_cb},{file_suffix,".xml"}, +%% {preprocess,true}]). +%% +%% The origin of this file is the edoc module otpsgml_layout.erl +%% written by Richard Carlsson. + +-export([module/2, overview/2]). + +-include("xmerl.hrl"). + +-define(NL, "\n"). + +%%-User interface------------------------------------------------------- + +%% ERLREF +module(Element, Opts) -> + SortP = proplists:get_value(sort_functions, Opts, true), + XML = layout_module(Element, SortP), + xmerl:export_simple([XML], docb_xmerl_xml_cb, []). + +%% CHAPTER +overview(Element, _Opts) -> + XML = layout_chapter(Element), + xmerl:export_simple([XML], docb_xmerl_xml_cb, []). + +%%--Internal functions-------------------------------------------------- + +layout_module(#xmlElement{name = module, content = Es}=E, SortP) -> + Name = get_attrval(name, E), + Desc = get_content(description, Es), + ShortDesc = text_only(get_content(briefDescription, Desc)), + FullDesc = otp_xmlify(get_content(fullDescription, Desc)), + Types0 = get_content(typedecls, Es), + Types1 = lists:sort([{type_name(Et), Et} || Et <- Types0]), + Functions = + case SortP of + true -> + lists:sort([{function_name(Ef), Ef} || + Ef <- get_content(functions, Es)]); + false -> + [{function_name(Ef), Ef} || + Ef <- get_content(functions, Es)] + end, + Header = {header, [ + ?NL,{title, [Name]}, + ?NL,{prepared, [""]}, + ?NL,{responsible, [""]}, + ?NL,{docno, ["1"]}, + ?NL,{approved, [""]}, + ?NL,{checked, [""]}, + ?NL,{date, [""]}, + ?NL,{rev, ["A"]}, + ?NL,{file, [Name++".xml"]} + ]}, + Module = {module, [Name]}, + ModuleSummary = {modulesummary, ShortDesc}, + Description = {description, [?NL|FullDesc]}, + Types = case Types1 of + [] -> []; + _ -> + [?NL, {section,[{title,["DATA TYPES"]}, + {marker,[{id,"types"}],[]}, + ?NL|types(Types1)]}] + end, + Funcs = functions(Functions), + See = seealso_module(Es), + Authors = {authors, authors(Es)}, + {erlref, + [?NL,Header, + ?NL,Module, + ?NL,ModuleSummary, + ?NL,Description] + ++ Types ++ + [?NL,Funcs, + ?NL,See, + ?NL,Authors] + }. + +layout_chapter(#xmlElement{name=overview, content=Es}) -> + Title = get_text(title, Es), + Header = {header, [ + ?NL,{title,[Title]}, + ?NL,{prepared,[""]}, + ?NL,{docno,[""]}, + ?NL,{date,[""]}, + ?NL,{rev,[""]}, + ?NL,{file, ["chapter.xml"]} + ]}, + DescEs = get_content(description, Es), + FullDescEs = get_content(fullDescription, DescEs), + Sections = chapter_ify(FullDescEs, first), + {chapter, [?NL, Header, ?NL | Sections]}. + +chapter_ify([], _) -> + []; +chapter_ify(Es, first) -> + %% Everything up to the first section should be made into + %% plain paragraphs -- or if no first section is found, everything + %% should be made into one + case find_next(h3, Es) of + {Es, []} -> + SubSections = subchapter_ify(Es, first), + [{section, [?NL,{title,["Overview"]}, + ?NL | SubSections]}]; + {FirstEs, RestEs} -> + otp_xmlify(FirstEs) ++ chapter_ify(RestEs, next) + end; +chapter_ify([#xmlElement{name=h3} = E | Es], next) -> + {SectionEs, RestEs} = find_next(h3, Es), + SubSections = subchapter_ify(SectionEs, first), + {Marker, Title} = chapter_title(E), + [{section, [?NL,{marker,[{id,Marker}],[]}, + ?NL,{title,[Title]}, + ?NL | SubSections]} | chapter_ify(RestEs, next)]. + +subchapter_ify([], _) -> + []; +subchapter_ify(Es, first) -> + %% Everything up to the (possible) first subsection should be + %% made into plain paragraphs + {FirstEs, RestEs} = find_next(h4, Es), + otp_xmlify(FirstEs) ++ subchapter_ify(RestEs, next); +subchapter_ify([#xmlElement{name=h4} = E | Es], next) -> + {SectionEs, RestEs} = find_next(h4, Es), + Elements = otp_xmlify(SectionEs), + {Marker, Title} = chapter_title(E), + [{section, [?NL,{marker,[{id,Marker}],[]}, + ?NL,{title,[Title]}, + ?NL | Elements]} | subchapter_ify(RestEs, next)]. + +chapter_title(#xmlElement{content=Es}) -> % name = h3 | h4 + case Es of + [#xmlElement{name=a} = E] -> + {get_attrval(name, E), get_text(E)} + end. + +%%--XHTML->XML transformation------------------------------------------- + +%% otp_xmlify(Es1) -> Es2 +%% Es1 = Es2 = [#xmlElement{} | #xmlText{}] +%% Fix things that are allowed in XHTML but not in chapter/erlref DTDs. +%% 1) lists (<ul>, <ol>, <dl>) and code snippets (<pre>) can not occur +%% within a <p>, such a <p> must be splitted into a sequence of <p>, +%% <ul>, <ol>, <dl> and <pre>. +%% 2) <a> must only have either a href attribute (corresponds to a +%% <seealso> or <url> in the XML code) in which case its content +%% must be plain text; or a name attribute (<marker>). +%% 3a) <b> content must be plain text. +%% b) <em> content must be plain text (or actually a <code> element +%% as well, but a simplification is used here). +%% c) <pre> content must be plain text (or could actually contain +%% <input> as well, but a simplification is used here). +%% 4) <code> content must be plain text, or the element must be split +%% into a list of elements. +%% 5a) <h1>, <h2> etc is not allowed - replaced by its content within +%% a <b> tag. +%% b) <center> is not allowed - replaced by its content. +%% c) <font> -"- +%% 6) <table> is not allowed in erlref, translated to text instead. +%% Also a <table> in chapter without a border is translated to text. +%% A <table> in chapter with a border must contain a <tcaption>. +%% 7) <sup> is not allowed - is replaced with its text content +%% within parenthesis. +%% 8) <blockquote> contents may need to be made into paragraphs +%% 9) <th> (table header) is not allowed - is replaced by +%% <td><em>...</em></td>. +otp_xmlify([]) -> + []; +otp_xmlify(Es0) -> + Es = case is_paragraph(hd(Es0)) of + + %% If the first element is a <p> xmlElement, then + %% the entire element list is ready to be otp_xmlified. + true -> + Es0; + + %% If the first element is not a <p> xmlElement, then all + %% elements up to the first <p> (or end of list) must be + %% made into a paragraph (using the {p, Es} construction) + %% before the list is otp_xmlified. + false -> + case find_next(p, Es0, []) of + {[#xmlText{value=Str}] = First, Rest} -> + %% Special case: Making a paragraph out of a + %% blank line isn't a great idea. + case is_empty(Str) of + true -> + Rest; + false -> + [{p,First}|Rest] + end; + {First, Rest} -> + [{p,First}|Rest] + end + end, + + %% Fix paragraph breaks not needed in XHTML but in XML + EsFixed = otp_xmlify_fix(Es), + + otp_xmlify_es(EsFixed). + +%% EDoc does not always translate empty lines (with leading "%%") +%% as paragraph break, this is the fix +otp_xmlify_fix(Es) -> + otp_xmlify_fix(Es, []). +otp_xmlify_fix([#xmlText{value="\n \n"++_} = E1, E2 | Es], Res) -> + %% This is how it looks when generating an erlref from a .erl file + case is_paragraph(E2) of + false -> + {P, After} = find_p_ending(Es, []), + otp_xmlify_fix(After, [{p, [E2|P]}, E1 | Res]); + true -> + otp_xmlify_fix([E2|Es], [E1|Res]) + end; +otp_xmlify_fix([#xmlText{value="\n\n"} = E1, E2 | Es], Res) -> + %% This is how it looks when generating a chapter from overview.edoc + case is_paragraph(E2) of + false -> + {P, After} = find_p_ending(Es, []), + otp_xmlify_fix(After, [{p, [E2|P]}, E1 | Res]); + true -> + otp_xmlify_fix([E2|Es], [E1|Res]) + end; +otp_xmlify_fix([E|Es], Res) -> + otp_xmlify_fix(Es, [E|Res]); +otp_xmlify_fix([], Res) -> + lists:reverse(Res). + +otp_xmlify_es([E | Es]) -> + case is_paragraph(E) of + true -> + case otp_xmlify_psplit(E) of + + %% paragraph contained inline tags and text only + nosplit -> + otp_xmlify_e(E) ++ otp_xmlify_es(Es); + + %% paragraph contained dl, ul and/or pre and has been + %% splitted + SubEs -> + lists:flatmap(fun otp_xmlify_e/1, SubEs) ++ + otp_xmlify_es(Es) + end; + false -> + otp_xmlify_e(E) ++ otp_xmlify_es(Es) + end; +otp_xmlify_es([]) -> + []. + +%% otp_xmlify_psplit(P) -> nosplit | [E] +%% Handles case 1) above. +%% Uses the {p, Es} construct, thus replaces an p xmlElement with one +%% or more {p, Es} tuples if splitting the paraghrap is necessary. +otp_xmlify_psplit(P) -> + otp_xmlify_psplit(p_content(P), [], []). +otp_xmlify_psplit([#xmlElement{name=Name}=E | Es], Content, Res) -> + if + Name==blockquote; Name==ul; Name==ol; Name==dl; Name==pre; + Name==table -> + case Content of + [] -> + otp_xmlify_psplit(Es, [], [E|Res]); + [#xmlText{value=Str}] -> + %% Special case: Making a paragraph out of a blank + %% line isn't a great idea. Instead, this can be + %% viewed as the case above, where there is no + %% content to make a paragraph out of + case is_empty(Str) of + true -> + otp_xmlify_psplit(Es, [], [E|Res]); + false -> + Pnew = {p, lists:reverse(Content)}, + otp_xmlify_psplit(Es, [], [E,Pnew|Res]) + end; + _ -> + Pnew = {p, lists:reverse(Content)}, + otp_xmlify_psplit(Es, [], [E,Pnew|Res]) + end; + + true -> + otp_xmlify_psplit(Es, [E|Content], Res) + end; +otp_xmlify_psplit([E | Es], Content, Res) -> + otp_xmlify_psplit(Es, [E|Content], Res); +otp_xmlify_psplit([], _Content, []) -> + nosplit; +otp_xmlify_psplit([], [], Res) -> + lists:reverse(Res); +otp_xmlify_psplit([], [#xmlText{value="\n\n"}], Res) -> + lists:reverse(Res); +otp_xmlify_psplit([], Content, Res) -> + Pnew = {p, lists:reverse(Content)}, + lists:reverse([Pnew|Res]). + +%% otp_xmlify_e(E) -> [E] +%% This is the function which does the actual transformation of +%% single elements, normally by making sure the content corresponds +%% to what is allowed by the OTP DTDs. +%% Returns a list of elements as the xmlification in some cases +%% returns no element or more than one element (although in most cases +%% exactly one element). +otp_xmlify_e(#xmlElement{name=a} = E) -> % 2) above + otp_xmlify_a(E); +otp_xmlify_e(#xmlElement{name=Tag} = E) % 3a-c) + when Tag==b; Tag==em; Tag==pre -> + Content = text_only(E#xmlElement.content), + [E#xmlElement{content=Content}]; +otp_xmlify_e(#xmlElement{name=code} = E) -> % 4) + case catch text_only(E#xmlElement.content) of + {'EXIT', _Error} -> + otp_xmlify_code(E); + Content -> + [E#xmlElement{content=Content}] + end; +otp_xmlify_e(#xmlElement{name=Tag} = E) % 5a + when Tag==h1; Tag==h2; Tag==h3; Tag==h4; Tag==h5; + Tag==center; + Tag==font -> + Content = text_only(E#xmlElement.content), + [E#xmlElement{name=b, content=Content}]; +otp_xmlify_e(#xmlElement{name=Tag} = E) % 5b-c) + when Tag==center; + Tag==font -> + otp_xmlify_e(E#xmlElement.content); +otp_xmlify_e(#xmlElement{name=table} = E) -> % 6) + case parent(E) of + module -> + otp_xmlify_table(E#xmlElement.content); + overview -> + case get_attrval(border, E) of + "" -> % implies border="0" + [{p, otp_xmlify_table(E#xmlElement.content)}]; + "0" -> + [{p, otp_xmlify_table(E#xmlElement.content)}]; + _Val -> + Content0 = otp_xmlify_e(E#xmlElement.content), + Summary = #xmlText{value=get_attrval(summary, E)}, + TCaption = E#xmlElement{name=tcaption, + attributes=[], + content=[Summary]}, + Content = Content0 ++ [TCaption], + [E#xmlElement{attributes=[], content=Content}] + end + end; +otp_xmlify_e(#xmlElement{name=sup} = E) -> % 7) + Text = get_text(E), + [#xmlText{parents = E#xmlElement.parents, + pos = E#xmlElement.pos, + language = E#xmlElement.language, + value = "(" ++ Text ++ ")"}]; +otp_xmlify_e(#xmlElement{name=blockquote} = E) -> % 8) + Content = otp_xmlify_blockquote(E#xmlElement.content), + [E#xmlElement{content=Content}]; +otp_xmlify_e(#xmlElement{name=th} = E) -> % 9) + Content = otp_xmlify_e(E#xmlElement.content), + EmE = E#xmlElement{name=em, content=Content}, + [E#xmlElement{name=td, content=[EmE]}]; +otp_xmlify_e(#xmlElement{name=p} = E) -> % recurse + Content = otp_xmlify_e(E#xmlElement.content), + [E#xmlElement{content=Content}]; +otp_xmlify_e({p, Content1}) -> + Content2 = otp_xmlify_e(Content1), + [{p, Content2}]; +otp_xmlify_e(#xmlElement{name=ul} = E) -> + Content = otp_xmlify_e(E#xmlElement.content), + [E#xmlElement{content=Content}]; +otp_xmlify_e(#xmlElement{name=li} = E) -> + %% Content may need to be made into <p>s etc. + Content = otp_xmlify(E#xmlElement.content), + [E#xmlElement{content=Content}]; +otp_xmlify_e(#xmlElement{name=dl} = E) -> + Content0 = otp_xmlify_e(E#xmlElement.content), + Content = otp_xmlify_dl(Content0), + [E#xmlElement{content=Content}]; +otp_xmlify_e(#xmlElement{name=dt} = E) -> + %% Special fix: Markers in <taglist> <tag>s are not allowed, + %% save it using 'put' and place the marker first in the <item> + %% instead + Content = case E#xmlElement.content of + [#xmlElement{name=a} = A] -> + put(dt_marker, otp_xmlify_e(A)), + otp_xmlify_e(A#xmlElement.content); + _ -> + otp_xmlify_e(E#xmlElement.content) + end, + [E#xmlElement{content=Content}]; +otp_xmlify_e(#xmlElement{name=dd} = E) -> + %% Content may need to be made into <p>s etc. + Content0 = otp_xmlify(E#xmlElement.content), + Content = case get(dt_marker) of + undefined -> Content0; + [Marker] -> + put(dt_marker, undefined), + [Marker#xmlElement{content=[]}|Content0] + end, + [E#xmlElement{content=Content}]; +otp_xmlify_e(#xmlElement{name=tr} = E) -> + Content = otp_xmlify_e(E#xmlElement.content), + [E#xmlElement{content=Content}]; +otp_xmlify_e(#xmlElement{name=td} = E) -> + Content = otp_xmlify_e(E#xmlElement.content), + [E#xmlElement{content=Content}]; +otp_xmlify_e([E | Es]) -> + otp_xmlify_e(E) ++ otp_xmlify_e(Es); +otp_xmlify_e([]) -> + []; +otp_xmlify_e(E) -> + [E]. + +%%--Tags with special handling------------------------------------------ + +%% otp_xmlify_a(A1) -> [A2] +%% Takes an <a> element and filters the attributes to decide wheather +%% its a seealso/url or a marker. +%% In the case of a seealso/url, the href part is checked, making +%% sure a .xml/.html file extension is removed (as DocBuilder inserts +%% .html extension when resolving cross references). +%% Also, references to other applications //App has a href attribute +%% value "OTPROOT/..." (due to app_default being set to "OTPROOT" in +%% docb_gen.erl), in this case both href attribute and content must be +%% formatted correctly according to DocBuilder requirements. +otp_xmlify_a(A) -> + [Attr0] = filter_a_attrs(A#xmlElement.attributes), + case Attr0 of + #xmlAttribute{name=href, value=Href0} -> % seealso | url + Content0 = text_only(A#xmlElement.content), + {Href, Content} = otp_xmlify_a_href(Href0, Content0), + [A#xmlElement{attributes=[Attr0#xmlAttribute{value=Href}], + content=Content}]; + #xmlAttribute{name=name} -> % marker + Content = otp_xmlify_e(A#xmlElement.content), + [A#xmlElement{attributes=[Attr0], content=Content}] + end. + +%% filter_a_attrs(Attrs) -> [Attr] +%% Removes all attributes from a <a> element except the href or +%% name attribute. +filter_a_attrs([#xmlAttribute{name=href} = Attr | _Attrs]) -> + [Attr]; +filter_a_attrs([#xmlAttribute{name=name} = Attr | _Attrs]) -> + [Attr]; +filter_a_attrs([_Attr|Attrs]) -> + filter_a_attrs(Attrs); +filter_a_attrs([]) -> + []. + +%% otp_xmlify_a_href(Href0, Es0) -> {Href1, Es1} +%% Href = string() +otp_xmlify_a_href("#"++_ = Marker, Es0) -> % <seealso marker="#what"> + {Marker, Es0}; +otp_xmlify_a_href("http:"++_ = URL, Es0) -> % external URL + {URL, Es0}; +otp_xmlify_a_href("OTPROOT"++AppRef, Es0) -> % <.. marker="App:FileRef + case split(AppRef, "/") of + [AppS, "doc", FileRef1] -> + FileRef = AppS++":"++otp_xmlify_a_fileref(FileRef1, AppS), + [#xmlText{value=Str0} = T] = Es0, + Str = case split(Str0, "/") of + %% //Application + [AppS2] -> + %% AppS2 can differ from AppS + %% Example: xmerl/XMerL + AppS2; + [_AppS,ModRef] -> + case split(ModRef, ":") of + %% //Application/Module + [Module] -> + Module++"(3)"; + %% //Application/Module:Type() + [_Module,_Type] -> + ModRef + end; + %% //Application/Module:Function/Arity + [_AppS,ModFunc,Arity] -> + ModFunc++"/"++Arity + end, + {FileRef, [T#xmlText{value=Str}]} + end; +otp_xmlify_a_href("../"++File, Es0) -> + %% Special case: This kind of relative path is used on some + %% places within i.e. EDoc and refers to a file within the same + %% application tree. + %% Correct the path according to the OTP directory structure + {"../../"++File, Es0}; +otp_xmlify_a_href(FileRef1, Es0) -> % File within the same application + FileRef2 = otp_xmlify_a_fileref(FileRef1, this), + {FileRef2, Es0}. + +%% otp_xmlify_a_fileref(FileRef1, AppS|this) -> FileRef2 +%% AppS = FileRef = string() +otp_xmlify_a_fileref(FileRef1, AppS) -> + case split(FileRef1, ".#") of + + %% EDoc default name is "overview-summary.html, + %% name of OTP User's Guide chapter is "chapter.xml" + ["overview-summary", _Ext] -> + "chapter"; + ["overview-summary", _Ext, Marker] -> + "chapter#"++Marker; + + [File, Ext] when Ext=="xml"; + Ext=="html", AppS/=this -> + File; + [File, Ext, Marker0] -> + %% Here is an awkward solution to an awkward problem + %% The marker automatically inserted by DocBuilder at + %% each function does not seem to work for EDoc generated + %% ERLREFs. + %% So if the referenced marker is in an ERLREF generated + %% by EDoc, keep it "as is", ie "function-arity". + %% If the referenced marker is NOT in an ERLREF generated + %% by EDoc, the marker should be on the format + %% "function/arity". + %% The awkward part of the solution is to decide wheather + %% the ERLREF is generated by EDoc or not: Here we make + %% the decision based on which application the module + %% belongs to -- which is ok when the module was written + %% but probably not in the future... + EDocApps = ["edoc","hipe","syntax_tools","xmerl"], + IsEDocGenerated = lists:member(AppS, EDocApps), + Marker = if + %% The marker is in a file in *this* + %% application (which documentation obviously + %% is generated by EDoc), or it is in a file + %% in an application which documentation + %% is assumed to be generated by EDoc + AppS==this; IsEDocGenerated -> + Marker0; + + %% The marker is in a file in an application + %% which documentation is assumed NOT to be + %% generated by EDoc + true -> + case split(Marker0, "-") of + [Func,Arity] -> + Func++"/"++Arity; + _ -> + Marker0 + end + end, + if + %% Ignore file extension in file reference if it either + %% is ".xml" or if it is ".html" but AppS/=this, that + %% is, we're resolving an OTPROOT file reference + Ext=="xml"; + Ext=="html", AppS/=this -> + File++"#"++Marker; + true -> + File++"."++Ext++"#"++Marker + end; + + %% References to other files than XML files are kept as-is + _ -> + FileRef1 + end. + +%% otp_xmlify_blockquote(Es1) -> Es2 +%% Ensures that the content of a <blockquote> is divided into +%% <p>s using the {p, Es} construct. +otp_xmlify_blockquote([#xmlElement{name=p} = E|Es]) -> + [E | otp_xmlify_blockquote(Es)]; +otp_xmlify_blockquote([#xmlText{} = E|Es]) -> + {P, After} = find_p_ending(Es, []), + [{p, [E|P]} | otp_xmlify_blockquote(After)]; +otp_xmlify_blockquote([]) -> + []. + +%% otp_xmlify_code(E) -> Es +%% Takes a <code> xmlElement and split it into a list of <code> and +%% other xmlElements. Necessary when it contains more than a single +%% xmlText element. +%% Example: +%% #xmlElement{name=code, +%% content=[#xmlText{}, #xmlElement{name=br}, #xmlText{}]} +%% => +%% [#xmlElement{name=code, content=[#xmlText{}]}, +%% #xmlElement{name=br}, +%% #xmlElement{name=code, content=[#xmlText{}]}] +otp_xmlify_code(E) -> + otp_xmlify_code(E, E#xmlElement.content, []). +otp_xmlify_code(Code, [#xmlText{} = E|Es], Acc) -> + otp_xmlify_code(Code, Es, [Code#xmlElement{content=[E]}|Acc]); +otp_xmlify_code(Code, [#xmlElement{} = E|Es], Acc) -> + otp_xmlify_code(Code, Es, [E|Acc]); +otp_xmlify_code(_Code, [], Acc) -> + lists:reverse(Acc). + +%% otp_xmlify_dl(Es1) -> Es2 +%% Insert empty <dd> elements if necessary. +%% OTP DTDs does not allow <taglist>s with <tag>s but no <item>s. +otp_xmlify_dl([#xmlElement{name=dt} = E|Es]) -> + [E|otp_xmlify_dl(Es, E)]; +otp_xmlify_dl([E|Es]) -> + [E|otp_xmlify_dl(Es)]; +otp_xmlify_dl([]) -> + []. + +otp_xmlify_dl([#xmlElement{name=dd} = E|Es], _DT) -> + [E|otp_xmlify_dl(Es)]; +otp_xmlify_dl([#xmlElement{name=dt} = E|Es], DT) -> + DD = DT#xmlElement{name=dd, attributes=[], content=[]}, + [DD,E|otp_xmlify_dl(Es, E)]; +otp_xmlify_dl([E|Es], DT) -> + [E|otp_xmlify_dl(Es, DT)]; +otp_xmlify_dl([], DT) -> + DD = DT#xmlElement{name=dd, attributes=[], content=[]}, + [DD]. + +%% otp_xmlify_table(Es1) -> Es2 +%% Transform <table> contents into "text", that is, inline elements. +otp_xmlify_table([#xmlText{} = E|Es]) -> + [E | otp_xmlify_table(Es)]; +otp_xmlify_table([#xmlElement{name=tbody} = E|Es]) -> + otp_xmlify_table(E#xmlElement.content)++otp_xmlify_table(Es); +otp_xmlify_table([#xmlElement{name=tr} = E|Es]) -> + %% Insert newlines between table rows + otp_xmlify_table(E#xmlElement.content)++[{br,[]}]++otp_xmlify_table(Es); +otp_xmlify_table([#xmlElement{name=th} = E|Es]) -> + [{em, E#xmlElement.content} | otp_xmlify_table(Es)]; +otp_xmlify_table([#xmlElement{name=td} = E|Es]) -> + otp_xmlify_e(E#xmlElement.content) ++ otp_xmlify_table(Es); +otp_xmlify_table([]) -> + []. + +%%--Misc help functions used by otp_xmlify/1 et al--------------------- + +%% find_next(Tag, Es) -> {Es1, Es2} +%% Returns {Es1, Es2} where Es1 is the list of all elements up to (but +%% not including) the first element with tag Tag in Es, and Es2 +%% is the remaining elements of Es. +find_next(Tag, Es) -> + find_next(Tag, Es, []). +find_next(Tag, [#xmlElement{name=Tag} = E | Es], AccEs) -> + {lists:reverse(AccEs), [E|Es]}; +find_next(Tag, [E|Es], AccEs) -> + find_next(Tag, Es, [E|AccEs]); +find_next(_Tag, [], AccEs) -> + {lists:reverse(AccEs), []}. + +%% find_p_ending(Es, []) -> {Es1, Es2} +%% Returns {Es1, Es2} where Es1 is the list of all elements up to (but +%% not including) the first paragraph break in Es, and Es2 is +%% the remaining elements of Es2. +%% Paragraph break = <p> tag or empty line +%% the next blank line, <p> or end-of-list as P, and the remaining +%% elements of Es as After. +find_p_ending([#xmlText{value="\n \n"++_} = E|Es], P) -> + {lists:reverse(P), [E|Es]}; +find_p_ending([#xmlElement{name=p} = E|Es], P) -> + {lists:reverse(P), [E|Es]}; +find_p_ending([E|Es], P) -> + find_p_ending(Es, [E|P]); +find_p_ending([], P) -> + {lists:reverse(P), []}. + +%% is_paragraph(E | P) -> bool() +%% P = {p, Es} +is_paragraph(#xmlElement{name=p}) -> true; +is_paragraph({p, _Es}) -> true; +is_paragraph(_E) -> false. + +%% p_content(E | P) -> Es +p_content(#xmlElement{content=Content}) -> Content; +p_content({p, Content}) -> Content. + +%% is_empty(Str) -> bool() +%% Str = string() +%% Returns true if Str is empty in the sense that it contains nothing +%% but spaces, tabs or newlines. +is_empty("\n"++Str) -> + is_empty(Str); +is_empty(" "++Str) -> + is_empty(Str); +is_empty("\t"++Str) -> + is_empty(Str); +is_empty("") -> + true; +is_empty(_) -> + false. + +%% split(Str, Seps) -> [Str] +split(Str, Seps) -> + split(Str, Seps, []). + +split([Ch|Str], Seps, Acc) -> + case lists:member(Ch, Seps) of + true -> split(Str, Seps, Acc); + false -> split(Str, Seps, Acc, [Ch]) + end; +split([], _Seps, Acc) -> + lists:reverse(Acc). + +split([Ch|Str], Seps, Acc, Chs) -> + case lists:member(Ch, Seps) of + true -> split(Str, Seps, [lists:reverse(Chs)|Acc]); + false -> split(Str, Seps, Acc, [Ch|Chs]) + end; +split([], _Seps, Acc, Chs) -> + lists:reverse([lists:reverse(Chs)|Acc]). + +%%--Functions for creating an erlref document--------------------------- + +%% function_name(F) -> string() +%% F = #xmlElement{name=function} +%% Returns the name of a function as "name/arity". +function_name(E) -> + get_attrval(name, E) ++ "/" ++ get_attrval(arity, E). + +%% functions(Fs) -> Es +%% Fs = [{Name, F}] +%% Name = string() "name/arity" +%% F = #xmlElement{name=function} +functions(Fs) -> + Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs), + if + Es==[] -> + []; + true -> + {funcs, Es} + end. + +function(_Name, E=#xmlElement{content = Es}) -> + TypeSpec = get_content(typespec, Es), + [?NL,{func, [ ?NL, + {name, + case funcheader(TypeSpec) of + [] -> + signature(get_content(args, Es), + get_attrval(name, E)); + Spec -> Spec + end + }, + ?NL,{fsummary, fsummary(Es)}, + ?NL,local_types(TypeSpec), + ?NL,{desc, + label_anchor(E)++ + deprecated(Es)++ + fulldesc(Es)++ + seealso_function(Es)} + ]}]. + +fsummary([]) -> ["\s"]; +fsummary(Es) -> + Desc = get_content(description, Es), + case get_content(briefDescription, Desc) of + [] -> + fsummary_equiv(Es); % no description at all if no equiv + ShortDesc -> + text_only(ShortDesc) + end. + +fsummary_equiv(Es) -> + case get_content(equiv, Es) of + [] -> ["\s"]; + Es1 -> + case get_content(expr, Es1) of + [] -> ["\s"]; + [Expr] -> + ["Equivalent to ", Expr, ".",?NL] + end + end. + +label_anchor(E) -> + case get_attrval(label, E) of + "" -> []; + Ref -> [{marker, [{id, Ref}],[]},?NL] + end. + +label_anchor(Content, E) -> + case get_attrval(label, E) of + "" -> Content; + Ref -> {p,[{marker, [{id, Ref}],[]}, + {em, Content}]} + end. + +signature(Es, Name) -> + [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> term()", ?NL]. + +arg(#xmlElement{content = Es}) -> + [get_text(argName, Es)]. + +funcheader([]) -> []; +funcheader(Es) -> + [t_name(get_elem(erlangName, Es))] ++ t_utype(get_elem(type, Es)). + +local_types([]) -> []; +local_types(Es) -> + local_defs2(get_elem(localdef, Es)). + +local_defs2([]) -> []; +local_defs2(Es) -> + {type,[?NL | [{v, localdef2(E)} || E <- Es]]}. + +%% Like localdef/1, but does not use label_anchor/2 -- we don't want any +%% markers or em tags in <v> tag, plain text only! +localdef2(#xmlElement{content = Es}) -> + case get_elem(typevar, Es) of + [] -> + t_utype(get_elem(type, Es)); + [V] -> + t_var(V) ++ [" = "] ++ t_utype(get_elem(type, Es)) + end. + +type_name(#xmlElement{content = Es}) -> + t_name(get_elem(erlangName, get_content(typedef, Es))). + +types(Ts) -> + Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E) end, Ts), + [?NL, {taglist,[?NL|Es]}]. + +typedecl(Name, #xmlElement{content = Es}) -> + TypedefEs = get_content(typedef, Es), + Id = "type-"++Name, + [{tag, typedef(TypedefEs)}, + ?NL, + {item, [{marker,[{id,Id}],[]} | + local_defs(get_elem(localdef, TypedefEs)) ++ fulldesc(Es)]}, + ?NL]. + +typedef(Es) -> + Name = ([t_name(get_elem(erlangName, Es)), "("] + ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])), + case get_elem(type, Es) of + [] -> + [{tt, Name}]; + Type -> + [{tt, Name ++ [" = "] ++ t_utype(Type)}] + end. + +local_defs([]) -> []; +local_defs(Es) -> + [?NL, {ul, [{li, [{tt, localdef(E)}]} || E <- Es]}]. + +localdef(E = #xmlElement{content = Es}) -> + Var = case get_elem(typevar, Es) of + [] -> + [label_anchor(t_abstype(get_content(abstype, Es)), E)]; + [V] -> + t_var(V) + end, + Var ++ [" = "] ++ t_utype(get_elem(type, Es)). + +deprecated(Es) -> + case get_content(deprecated, Es) of + [] -> []; + DeprEs -> + Es2 = get_content(fullDescription, + get_content(description, DeprEs)), + Es3 = otp_xmlify_e(Es2), + [{p, [{em, ["This function is deprecated: "]} |Es3]}, ?NL] + end. + +fulldesc(Es) -> + case get_content(fullDescription, get_content(description, Es)) of + [] -> + index_desc(Es); + Desc -> + [?NL|otp_xmlify(Desc)] ++ [?NL] + end. + +index_desc(Es) -> + Desc = get_content(description, Es), + case get_content(briefDescription, Desc) of + [] -> + equiv(Es); % no description at all if no equiv + ShortDesc -> + ShortDesc + end. + +seealso_module(Es) -> + case get_elem(see, Es) of + [] -> []; + Es1 -> + {section,[{title,["See also"]},{p,seq(fun see/1, Es1, [])}]} + end. +seealso_function(Es) -> + case get_elem(see, Es) of + [] -> []; + Es1 -> + [{p, [{em, ["See also:"]}, " "] ++ seq(fun see/1, Es1, ["."])}, + ?NL] + end. + +%% ELEMENT see PCDATA +%% ATTLIST name PCDATA +%% href PCDATA +see(#xmlElement{content=Es0} = E) -> + Href0 = get_attrval(href, E), + {Href, Es} = otp_xmlify_a_href(Href0, Es0), + [{seealso, [{marker, Href}], Es}]. + +equiv(Es) -> + case get_content(equiv, Es) of + [] -> ["\s"]; + Es1 -> + case get_content(expr, Es1) of + [] -> []; + [Expr] -> + Expr1 = [Expr], + Expr2 = case get_elem(see, Es1) of + [] -> + {c,Expr1}; + [E=#xmlElement{}] -> + case get_attrval(href, E) of + "" -> + {c,Expr1}; + Ref -> + {seealso, [{marker, Ref}], Expr1} + end + end, + [{p, ["Equivalent to ", Expr2, "."]}, ?NL] + end + end. + +authors(Es) -> + case get_elem(author, Es) of + [] -> + [?NL,{aname,["\s"]},?NL,{email,["\s"]}]; + Es1 -> + [?NL|seq(fun author/1, Es1, "", [])] + end. + +author(E=#xmlElement{}) -> + Name = case get_attrval(name, E) of + [] -> "\s"; + N -> N + end, + Mail = case get_attrval(email, E) of + [] -> "\s"; + M -> M + end, + [?NL,{aname,[Name]},?NL,{email,[Mail]}]. + +t_name([E]) -> + N = get_attrval(name, E), + case get_attrval(module, E) of + "" -> N; + M -> + S = M ++ ":" ++ N, + case get_attrval(app, E) of + "" -> S; + A -> "//" ++ A ++ "/" ++ S + end + end. + +t_utype([E]) -> + t_utype_elem(E). + +t_utype_elem(E=#xmlElement{content = Es}) -> + case get_attrval(name, E) of + "" -> t_type(Es); + Name -> + T = t_type(Es), + case T of + [Name] -> T; % avoid generating "Foo::Foo" + T -> [Name] ++ ["::"] ++ T + end + end. + +t_type([E=#xmlElement{name = typevar}]) -> + t_var(E); +t_type([E=#xmlElement{name = atom}]) -> + t_atom(E); +t_type([E=#xmlElement{name = integer}]) -> + t_integer(E); +t_type([E=#xmlElement{name = float}]) -> + t_float(E); +t_type([#xmlElement{name = nil}]) -> + t_nil(); +t_type([#xmlElement{name = list, content = Es}]) -> + t_list(Es); +t_type([#xmlElement{name = tuple, content = Es}]) -> + t_tuple(Es); +t_type([#xmlElement{name = 'fun', content = Es}]) -> + t_fun(Es); +t_type([#xmlElement{name = abstype, content = Es}]) -> + t_abstype(Es); +t_type([#xmlElement{name = union, content = Es}]) -> + t_union(Es); +t_type([#xmlElement{name = record, content = Es}]) -> + t_record(Es). + +t_var(E) -> + [get_attrval(name, E)]. + +t_atom(E) -> + [get_attrval(value, E)]. + +t_integer(E) -> + [get_attrval(value, E)]. + +t_float(E) -> + [get_attrval(value, E)]. + +t_nil() -> + ["[]"]. + +t_list(Es) -> + ["["] ++ t_utype(get_elem(type, Es)) ++ ["]"]. + +t_tuple(Es) -> + ["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]). + +t_fun(Es) -> + ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), + [") -> "] ++ t_utype(get_elem(type, Es))). + +t_record([E|Es]) -> + ["#", get_attrval(value, E), "{"++ seq(fun t_field/1, Es) ++"}"]. +t_field(#xmlElement{name=field, content=[Atom,Type]}) -> + [get_attrval(value, Atom), "="] ++ t_utype_elem(Type). + +t_abstype(Es) -> + case split_at_colon(t_name(get_elem(erlangName, Es)),[]) of + {Mod,Type} -> + [Type, "("] ++ + seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) ++ + [" (see module ", Mod, ")"]; + Type -> + [Type, "("] ++ + seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) + end. + +%% Split at one colon, but not at two (or more) +split_at_colon([$:,$:|_]=Rest,Acc) -> + lists:reverse(Acc)++Rest; +split_at_colon([$:|Type],Acc) -> + {lists:reverse(Acc),Type}; +split_at_colon([Char|Rest],Acc) -> + split_at_colon(Rest,[Char|Acc]); +split_at_colon([],Acc) -> + lists:reverse(Acc). + +t_union(Es) -> + seq(fun t_utype_elem/1, Es, " | ", []). + +%% seq(Fun, Es) +%% seq(Fun, Es, Tail) +%% seq(Fun, Es, Sep, Tail) -> [string()] +%% Fun = function(E) -> [string()] +%% Sep = string() +%% Tail = [string()] +%% Applies Fun to each element E in Es and return the appended list of +%% strings, separated by Sep which defaults to ", " and ended by Tail +%% which defaults to []. +seq(Fun, Es) -> + seq(Fun, Es, []). +seq(Fun, Es, Tail) -> + seq(Fun, Es, ", ", Tail). +seq(Fun, [E], _Sep, Tail) -> + Fun(E) ++ Tail; +seq(Fun, [E | Es], Sep, Tail) -> + Fun(E) ++ [Sep] ++ seq(Fun, Es, Sep, Tail); +seq(_Fun, [], _Sep, Tail) -> + Tail. + +%%--Misc functions for accessing fields etc----------------------------- + +%% Type definitions used below: +%% E = #xmlElement{} | #xmlText{} +%% Es = [E] +%% Tag = atom(), XHTML tag +%% Name = atom(), XHTML attribute name +%% Attrs = [#xmlAttribute{}] +%% Ts = [#xmlText{}] + +%% parent(E) -> module | overview +parent(E) -> + Parents = E#xmlElement.parents, + {Parent,_} = lists:last(Parents), + Parent. + +%% get_elem(Tag, Es1) -> Es2 +%% Returns a list of all elements in Es which have the name Tag. +get_elem(Name, [#xmlElement{name = Name} = E | Es]) -> + [E | get_elem(Name, Es)]; +get_elem(Name, [_ | Es]) -> + get_elem(Name, Es); +get_elem(_, []) -> + []. + +%% get_attr(Name, Attrs1) -> Attrs2 +%% Returns a list of all attributes in Attrs1 which have the name Name. +get_attr(Name, [#xmlAttribute{name = Name} = A | As]) -> + [A | get_attr(Name, As)]; +get_attr(Name, [_ | As]) -> + get_attr(Name, As); +get_attr(_, []) -> + []. + +%% get_attrval(Name, E) -> string() +%% If E has one attribute with name Name, return its value, otherwise "" +get_attrval(Name, #xmlElement{attributes = As}) -> + case get_attr(Name, As) of + [#xmlAttribute{value = V}] -> + V; + [] -> "" + end. + +%% get_content(Tag, Es1) -> Es2 +%% If there is one element in Es1 with name Tag, returns its contents, +%% otherwise [] +get_content(Name, Es) -> + case get_elem(Name, Es) of + [#xmlElement{content = Es1}] -> + Es1; + [] -> [] + end. + +%% get_text(Tag, Es) -> string() +%% If there is one element in Es with name Tag, and its content is +%% a single xmlText, return the value of this xmlText. +%% Otherwise return "". +get_text(Name, Es) -> + case get_content(Name, Es) of + [#xmlText{value = Text}] -> + Text; + [] -> "" + end. + +%% get_text(E) -> string() +%% Return the value of an single xmlText which is the content of E, +%% possibly recursively. +get_text(#xmlElement{content=[#xmlText{value=Text}]}) -> + Text; +get_text(#xmlElement{content=[E]}) -> + get_text(E). + +%% text_only(Es) -> Ts +%% Takes a list of xmlElement and xmlText and return a lists of xmlText. +text_only([#xmlElement{} = E |Es]) -> + text_only(E#xmlElement.content) ++ text_only(Es); +text_only([#xmlText{} = E |Es]) -> + [E | text_only(Es)]; +text_only([]) -> + []. diff --git a/lib/docbuilder/src/docb_gen.erl b/lib/docbuilder/src/docb_gen.erl new file mode 100644 index 0000000000..0d8d640324 --- /dev/null +++ b/lib/docbuilder/src/docb_gen.erl @@ -0,0 +1,138 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_gen). + +-export([module/1, module/2, users_guide/1, users_guide/2]). + +-record(args, {suffix=".xml", + layout=docb_edoc_xml_cb, + def=[], + includes=[], + preprocess=false, + sort_functions=true}). + +%% module(File) -> ok | {error, Reason} +%% module(File, Opts) -> ok | {error, Reason} +%% File = string(), file name with or without ".erl" extension +%% Opts -- see code +%% Reason = badfile | {badopt, Term} +module(File0) -> + module(File0, []). +module(File0, RawOpts) -> + File = case filename:extension(File0) of + ".erl" -> File0; + _ -> File0++".erl" + end, + case filelib:is_regular(File) of + true -> + case parse(RawOpts, #args{}) of + {ok, Args} -> + Opts = [{def, Args#args.def}, + {includes, Args#args.includes}, + {preprocess, Args#args.preprocess}, + {sort_functions, Args#args.sort_functions}, + + {app_default, "OTPROOT"}, + {file_suffix, Args#args.suffix}, + {dir, "."}, + {layout, Args#args.layout}], + edoc:file(File, Opts); + Error -> + Error + end; + false -> + {error, badfile} + end. + +%% users_guide(File) -> ok | {error, Reason} +%% users_guide(File, Opts) -> ok | {error, Reason} +%% File = string() +%% Opts -- see code +%% Reason = badfile | {badopt, Opt} +users_guide(File) -> + users_guide(File, []). +users_guide(File, RawOpts) -> + case filelib:is_regular(File) of + true -> + case parse(RawOpts, #args{}) of + {ok, Args} -> + Opts = [{def, Args#args.def}, + {app_default, "OTPROOT"}, + {file_suffix, Args#args.suffix}, + {layout, Args#args.layout}], + + Env = edoc_lib:get_doc_env(Opts), + + {ok, Tags} = + edoc_extract:file(File, overview, Env, Opts), + Data = + edoc_data:overview("Overview", Tags, Env, Opts), + F = fun(M) -> M:overview(Data, Opts) end, + Text = edoc_lib:run_layout(F, Opts), + + OutFile = "chapter" ++ Args#args.suffix, + edoc_lib:write_file(Text, ".", OutFile); + Error -> + Error + end; + false -> + {error, badfile} + end. + +parse([{output,xml} | RawOpts], Args) -> + parse(RawOpts, Args); % default, no update of record necessary +parse([{output,sgml} | RawOpts], Args) -> + parse(RawOpts, Args#args{suffix=".sgml", layout=docb_edoc_sgml_cb}); +parse([{def,Defs} | RawOpts], Args) -> + case parse_defs(Defs) of + true -> + Args2 = Args#args{def=Args#args.def++Defs}, + parse(RawOpts, Args2); + false -> + {error, {badopt, {def,Defs}}} + end; +parse([{includes,Dirs} | RawOpts], Args) -> + case parse_includes(Dirs) of + true -> + Args2 = Args#args{includes=Args#args.includes++Dirs}, + parse(RawOpts, Args2); + false -> + {error, {badopt, {includes,Dirs}}} + end; +parse([{preprocess,Bool} | RawOpts], Args) when Bool==true; + Bool==false -> + parse(RawOpts, Args#args{preprocess=Bool}); +parse([{sort_functions,Bool} | RawOpts], Args) when Bool==true; + Bool==false -> + parse(RawOpts, Args#args{sort_functions=Bool}); +parse([], Args) -> + {ok, Args}; +parse([Opt | _RawOpts], _Args) -> + {error, {badopt, Opt}}. + +parse_defs(Defs) -> + lists:all(fun({Key,Val}) when is_atom(Key), is_list(Val) -> true; + (_) -> false + end, + Defs). + +parse_includes(Dirs) -> + lists:all(fun(Dir) when is_list(Dir) -> true; + (_) -> false + end, + Dirs). diff --git a/lib/docbuilder/src/docb_html.erl b/lib/docbuilder/src/docb_html.erl new file mode 100644 index 0000000000..9aea4c8a66 --- /dev/null +++ b/lib/docbuilder/src/docb_html.erl @@ -0,0 +1,394 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_html). + +-export([rule/2, rule/3]). + +rule([p, item, list|_], {_, _, _}) -> + {"", "<br />\n"}; +rule([p, item, taglist|_], {_, _, _}) -> + {"", "<br />\n"}; +rule([p|_], _) -> + {"\n<p>", "\n</p>"}; + +rule([pre|_], _) -> + {"\n<div class=\"example\"><pre>\n", "\n</pre></div>\n"}; + +rule([input|_], _) -> + {"<strong>", "</strong>"}; + +rule([quote|_], _) -> + {"\n<blockquote>\n", "\n</blockquote>\n"}; + +rule([i|_], _) -> + {"<em>", "</em>"}; + +rule([b|_], _) -> + {"<strong>", "</strong>"}; + +rule([c|_], _) -> + {"<span class=\"code\">", "</span>"}; + +rule([em|_], _) -> + {"<strong>", "</strong>"}; + +rule([sub|_], _) -> + {"<sub>", "</sub>"}; + +rule([sup|_], _) -> + {"<sup>", "</sup>"}; + +rule([termdef|_], _) -> + {drop, ""}; + +rule([citedef|_], _) -> + {drop, ""}; + +rule([br|_], _) -> + {"<br />\n", ""}; + +rule([digression|_], _) -> + {"<table>\n" + " <tr>\n" + " <td width=\"23\"></td>\n" + " <td>\n" + " <font size=\"-1\">\n", + " </font>\n" + " </td>\n" + " </tr>\n" + "</table>\n"}; + +rule([list, item, list|_], {_, ["ORDERED"], _}) -> + {"\n<ol>\n", "\n</ol>\n"}; +rule([list, item, taglist|_], {_, ["ORDERED"], _}) -> + {"\n<ol>\n", "\n</ol>\n"}; +rule([list|_], {_, ["ORDERED"], _}) -> + {"\n<ol>\n", "\n</ol>\n"}; +rule([list, item, list|_], {_, ["BULLETED"], _}) -> + {"\n<ul>\n", "\n</ul>\n"}; +rule([list, item, taglist|_], {_, ["BULLETED"], _}) -> + {"\n<ul>\n", "\n</ul>\n"}; +rule([list|_], {_, ["BULLETED"], _}) -> + {"\n<ul>\n", "\n</ul>\n"}; + +rule([taglist, item, taglist|_], _) -> + {"\n<dl>\n", "\n</dl>\n"}; +rule([taglist, item, list|_], _) -> + {"\n<dl>\n", "\n</dl>\n"}; +rule([taglist|_], _) -> + {"\n<dl>\n", "\n</dl>\n"}; + +rule([tag|_], _) -> + {"\n<dt>\n", "\n</dt>\n"}; + +rule([item, list|_], _) -> + {"\n<li>\n", "\n</li>\n\n"}; +rule([item, taglist|_], _) -> + {"\n<dd>\n", "\n</dd>\n"}; + +rule([image|_], {_, [File], _}) -> + File2 = + case filename:extension(File) of + [] -> File ++ ".gif"; + _ -> File + end, + {["\n<center>\n", "<img alt=\"", File2, "\" src=\"", File2, + "\"/><br/>\n"], + "\n</center>\n"}; + +rule([icaption|_], _) -> + {"<em>", "</em>\n"}; + +rule([url|_], {_, [HREF], _}) -> + URI = docb_html_util:make_uri(HREF), + {io_lib:format("<a target=\"_top\" href=\"~s\">", [URI]), "</a>"}; + +rule([marker|_], {_, [ID], _}) -> + %% remove all chars before first # including the # + {ok, NewID, _} = regexp:sub(ID, "^[^#]*#", ""), + %% replace "/" with "-" because "/" xhtml does not + %% allow "/" in the name attribute of element <a> + %% so we have to do the same as for marker i.e + %% Function/Arity is translated to an anchor in xhtml + %% like this : <a name="Function-Arity"/> + NewID2 = [case X of $/ -> $-;_->X end||X <- NewID], + {drop, ["<a name=\"", NewID2, "\"><!-- Empty --></a>"]}; + +rule([table|_], {_, ["", ""], Ts}) -> + {newargs, + "\n<center>\n" + "<table cellspacing=\"0\" cellpadding=\"2\" border=\"1\">\n", + reorder_table(Ts), + "\n</table>\n" + "</center>\n"}; +rule([table|_], {_, [Width, ""], Ts}) -> + {newargs, + ["\n<center>\n" + "<table cellspacing=\"0\" cellpadding=\"2\" border=\"1\" ", + "width=\"", Width, "%\">\n"], + reorder_table(Ts), + "\n</table>\n" + "</center>\n"}; + +%% The clauses above are for the report DTD. This one is for the other +%% DTDs. +rule([table|_], {_, ["LEFT"], Ts}) -> + {newargs, + "\n" + "<table cellspacing=\"0\" cellpadding=\"2\" border=\"1\">\n", + reorder_table(Ts), + "\n</table>\n"}; + +rule([table|_], {_, _, Ts}) -> + {newargs, + "\n<center>\n" + "<table cellspacing=\"0\" cellpadding=\"2\" border=\"1\">\n", + reorder_table(Ts), + "\n</table>\n" + "</center>\n"}; + +rule([row|_], _) -> + {" <tr>\n", "\n </tr>\n"}; + +rule([cell|_], {_, ["", ""], _}) -> + {" <td>\n", "\n </td>\n"}; +rule([cell|_], {_, [Align, ""], _}) -> + {[" <td align=\"", string:to_lower(Align), "\">\n"], "\n </td>\n"}; +rule([cell|_], {_, ["", VAlign], _}) -> + {[" <td valign=\"", string:to_lower(VAlign), "\">\n"], "\n </td>\n"}; +rule([cell|_], {_, [Align, VAlign], _}) -> + {[" <td align=\"", string:to_lower(Align), "\" valign=\"", string:to_lower(VAlign), "\">\n"], + "\n </td>\n"}; + +rule([tcaption|_], _) -> + {" <caption align=\"bottom\"><em>", "</em></caption>\n"}; + +rule([codeinclude|_], {_, [File, Tag, _Type], _}) -> +%% Type can be "ERL", "C" or "NONE" + {ok,Data} = docb_html_util:code_include(File, Tag), + {drop, ["\n<div class=\"example\"><pre>\n", Data, + "\n</pre></div>\n"]}; + +rule([erleval|_], {_, [Expr], _}) -> + docb_html_util:erl_eval(Expr); + +rule([pcdata, pre|_], {_, _, Data}) -> + %% Do not remove leading spaces. + {drop, docb_html_util:pcdata_to_html(Data, false)}; + +rule([pcdata|_], {_, _, Data}) -> + {drop, docb_html_util:pcdata_to_html(Data)}. + +rule([seealso|_], {_, [Marker], _}, Opts) -> + Href = + case docb_util:html_snippet(seealso, Marker, Opts) of + "" -> + %% DocBuilder default behavior: + %% Marker is of format "Path#Fragment", both optional. + %% Translated to <A HREF="Path.html#Fragment"> + case string:chr(Marker, $#) of + 0 -> % No Fragment + Marker++".html"; + 1 -> % No Path + %% replace "/" with "-" because "/" xhtml does not + %% allow "/" in the name attribute of element <a> + %% so we have to do the same as for marker i.e + %% Function/Arity is translated to an anchor in xhtml + %% like this : <a name="Function-Arity"/> + [case X of $/ -> $-;_->X end||X <- Marker]; + _ -> + Marker1 = [case X of $/ -> $-;_->X end||X <- Marker], + case string:tokens(Marker1, "#") of + [Path] -> % # at end, remove it + Path++".html"; + [Path | Frag0] -> + Path++".html#"++ + docb_util:join(Frag0, "#") + end + end; + Href0 -> + %% User defined behavior, use result as-is + Href0 + end, + {{["<a href=\"", Href, "\">"], "</a>"}, Opts}; + +rule([warning|_], _, Opts) -> + docb_html_util:copy_pics("warning.gif", "warning.gif", Opts), + {{"\n<div class=\"warning\">\n" + "<div class=\"label\">Warning</div>\n" + "<div class=\"content\">\n", + "\n</div>" + "\n</div>\n"}, Opts}; + +rule([note|_], _, Opts) -> + docb_html_util:copy_pics("note.gif", "note.gif", Opts), + {{"\n<div class=\"note\">\n" + "<div class=\"label\">Note</div>\n" + "<div class=\"content\">", + "\n</div>" + "\n</div>\n"}, Opts}; + +rule([path|_], {_, [UNIX, Windows], [{pcdata, _, Text}]}, Opts) -> + UnixPart = + docb_util:an_option({ptype,"unix"}, Opts) and (UNIX/=""), + WinPart = + docb_util:an_option({ptype,"windows"}, Opts) and (Windows/=""), + if + UnixPart, WinPart -> + {{drop, [docb_html_util:pcdata_to_html(Text), + " <font size=\"-2\">(<code>UNIX: ", + docb_html_util:attribute_cdata_to_html(UNIX), + ", ", + "Windows: ", + docb_html_util:attribute_cdata_to_html(Windows), + "</code>)</font>"]}, + Opts}; + UnixPart -> + {{drop, [docb_html_util:pcdata_to_html(Text), + " <font size=\"-1\">(<code>UNIX: ", + docb_html_util:attribute_cdata_to_html(UNIX), + "</code>)</font>"]}, + Opts}; + WinPart -> + {{drop, [docb_html_util:pcdata_to_html(Text), + " <font size=\"-1\">(<code>Windows: ", + docb_html_util:attribute_cdata_to_html(Windows), + "</code>)</font>"]}, + Opts}; + true -> + {{drop, docb_html_util:pcdata_to_html(Text)}, Opts} + end; + +rule([term|_], {_, [ID], _}, Opts) -> + case docb_util:an_option(dict, Opts) of + false -> + case docb_util:lookup_option({defs, term}, Opts) of + false -> + {{drop, ["<em><strong>", + ID, + "</strong></em> "]}, Opts}; + TermList -> + case lists:keysearch(ID, 1, TermList) of + false -> + {{drop, ["<em><strong>", ID, + "</strong></em> "]}, + Opts}; + {value, {ID, Name, _Description, _Resp}} -> + {{drop, ["<em><strong>", Name, + "</strong></em> "]}, + Opts}; + {value, {ID, Name, _Description}} -> + {{drop, [ "<em><strong>", Name, + "</strong></em> "]}, + Opts} + end + end; + true -> + case docb_util:lookup_option({defs, term}, Opts) of + false -> + {{drop, ["<em><strong>", ID, + "</strong></em> "]}, Opts}; + TermList -> + PartApplication = + docb_util:lookup_option(part_application, Opts), + case lists:keysearch(ID, 1, TermList) of + false -> + {{drop, ["<a href=\"", PartApplication, + "_term.html#", ID, "\">", ID, + "</a> "]}, Opts}; + {value, {ID, Name, _Description, _Resp}} -> + {{drop, ["<a href=\"", PartApplication, + "_term.html#", ID, "\">", Name, + "</a> "]}, Opts}; + {value, {ID, Name, _Description}} -> + {{drop, ["<a href=\"", PartApplication, + "_term.html#", ID, "\">", Name, + "</a> "]}, Opts} + end + end + end; + +rule([cite|_], {_, [ID], _}, Opts) -> + case docb_util:an_option(dict, Opts) of + false -> + case docb_util:lookup_option({defs, cite}, Opts) of + false -> + {{drop, ["<em><strong>", ID, "</strong></em> "]}, + Opts}; + CiteList -> + case lists:keysearch(ID, 1, CiteList) of + false -> + {{drop, + ["<em><strong>", ID, "</strong></em> "]}, + Opts}; + + {value, {ID, Name, _Description, _Resp}} -> + {{drop, ["<em><strong>", Name, + "</strong></em> "]}, + Opts}; + {value, {ID, Name, _Description}} -> + {{drop, ["<em><strong>", Name, + "</strong></em> "]}, + Opts} + end + end; + true -> + case docb_util:lookup_option({defs, cite}, Opts) of + false -> + {{drop, ["<em><strong>", ID, "</strong></em> "]}, + Opts}; + CiteList -> + PartApp = + docb_util:lookup_option(part_application, Opts), + case lists:keysearch(ID, 1, CiteList) of + false -> + {{drop, ["<a href=\"", PartApp, + "_cite.html#", ID, "\">", ID, + "</a> "]}, + Opts}; + {value, {ID, Name, _Description, _Resp}} -> + {{drop, ["<a href=\"", PartApp, + "_cite.html#", ID, "\">", Name, + "</a> "]}, + Opts}; + {value, {ID, Name, _Description}} -> + {{drop, ["<a href=\"", PartApp, + "_cite.html#", ID, "\">", Name, + "</a> "]}, + Opts} + end + end + end; + +rule([code|_], {_, [Type], [{pcdata, _, Code}]}, Opts) -> + case lists:member(Type,["ERL","C","NONE"]) of + true -> + {{drop, ["\n<div class=\"example\"><pre>\n", docb_html_util:element_cdata_to_html(Code), + "\n</pre></div>\n"]}, Opts}; + false -> + exit({error,"unknown type of <code>"}) + end. + +reorder_table(TableContent) -> + reorder_table(TableContent, [], []). +reorder_table([], Caption, NewTableContent) -> + Caption ++ lists:reverse(NewTableContent); +reorder_table([{tcaption,_,_} = Caption | TableContent], _, NewTableContent) -> + reorder_table(TableContent, [Caption], NewTableContent); +reorder_table([Row | TableContent], Caption, NewTableContent) -> + reorder_table(TableContent, Caption, [Row | NewTableContent]). diff --git a/lib/docbuilder/src/docb_html_layout.erl b/lib/docbuilder/src/docb_html_layout.erl new file mode 100644 index 0000000000..dca80ac58e --- /dev/null +++ b/lib/docbuilder/src/docb_html_layout.erl @@ -0,0 +1,380 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_html_layout). + +-export([report_top/2, report_bot/1, + first_top/2, first_bot/1, + ref_top/2, ref_bot/1, + chapter_top/2, chapter_bot/1, + application_toc_top/3, application_toc_top/4, + part_toc_top/3, part_toc_top/4, part_toc_bot/0, + index_top/1, index_bot/0]). + +%% Report + +report_top(Data, Opts) -> + [Title, Prepared, _Responsible, DocNo, _Approved, _Checked, _Date, + Vsn0, _File] = Data, + html_header(Title, Opts) ++ + docb_util:html_snippet(top, Opts) ++ +"<center> +<h1>" ++ Title ++ "</h1> +<big> + " ++ DocNo ++ version(Opts, Vsn0) ++ "<br /> + " ++ Prepared ++ "<br /> +</big> +</center> +". + +report_bot(Opts) -> + docb_util:html_snippet(bottom, Opts) ++ +"</body> +</html> +". + +%% First + +first_top(Data, Opts) -> + [Title, _Prepared, _Responsible, DocNo, _Approved, _Checked, _Date, + Vsn0, _File] = Data, + html_header(Title, Opts) ++ + docb_util:html_snippet(top, Opts) ++ +"<center> +<h1>" ++ Title ++ "</h1> +<big>" ++ DocNo ++ version(Opts, Vsn0) ++ "<br /> +</big> +</center> +". + +first_bot(Opts) -> + report_bot(Opts). + +%% Reference + +ref_top(Data, Opts) -> + [Title, _Prepared, _Responsible, _DocNo, _Approved, _Checked, + _Date, _Rev, _File] = Data, + ref_html_header(Title, Opts) ++ +"<!-- refpage -->\n" ++ + docb_util:html_snippet(top, Opts) ++ +"<center> +<h1>" ++ Title ++ "</h1> +</center>". + +ref_bot(Opts) -> + docb_util:html_snippet(bottom, Opts) ++ +"</body> +</html> +". + +%% Chapter + +chapter_top(Data, Opts) -> + [Title, _Prepared, _Responsible, _DocNo, _Approved, _Checked, + _Date, _Rev, _File] = Data, + html_header(Title, Opts) ++ + docb_util:html_snippet(top, Opts). + +chapter_bot(Opts) -> + report_bot(Opts). + +%% Application ToC + +application_toc_top(Data, DocName, Opts) -> + [Title, _Prepared, _Responsible, DocNo, _Approved, _Checked, + _Date, Vsn0, _File] = Data, + html_header(Title, []) ++ +"<center> +<strong>" ++ Title ++ "</strong> +<p> +<small> + " ++ DocNo ++ version(Opts, Vsn0) ++ " +</small> +</p> +<p> +<small> + <a target=\"document\" href=\"" ++ DocName ++ "_cite.html\">Bibliography</a> | + <a target=\"document\" href=\"" ++ DocName ++ "_term.html\">Glossary</a> | + <a target=\"document\" href=\"" ++ DocName ++ "_index.html\">Index</a> | + <a target=\"document\" href=\"" ++ DocName ++ "_first.html\">Cover</a>" ++ top_index(Opts) ++ +"</small> +</p> +</center> +<p> +<small> +<strong>Table of Contents</strong> +</small> +</p> +". + +application_toc_top(Data, DocName, Opts, HRefTexts) -> + [Title, _Prepared, _Responsible, DocNo, _Approved, _Checked, + _Date, Vsn0, _File] = Data, + html_header(Title, []) ++ +"<center> +<small> +" ++ + docb_util:join( + lists:map( + fun({HRef, Text}) -> + "<a target=\"_top\" href=\"" ++ HRef ++ "\">" ++ + Text ++ "</a>" + end, + HRefTexts), " | ") ++ top_index(Opts) ++ +"</small> +<p> +<strong>" ++ Title ++ "</strong> +</p> +<p> +<small>" ++ DocNo ++ version(Opts, Vsn0) ++ "<br /> +</small> +</p> +<p> +<small> + <a target=\"document\" href=\"" ++ DocName ++ "_cite.html\">Bibliography</a> | + <a target=\"document\" href=\"" ++ DocName ++ "_term.html\">Glossary</a> | + <a target=\"document\" href=\"" ++ DocName ++ "_index.html\">Index</a> | + <a target=\"document\" href=\"" ++ DocName ++ "_first.html\">Cover</a> +</small> +</p> +</center> +<p> +<small> +<strong>Table of Contents</strong> +</small> +</p> +". + +%% Part ToC + +part_toc_top(Data, DocName, Opts) -> + [Title, _Prepared, _Responsible, DocNo, _Approved, _Checked, + _Date, Vsn0, _File] = Data, + html_header(Title, []) ++ +"<center> +<p> +<strong>" ++ Title ++ "</strong> +</p> +<p> +<small>" ++ DocNo ++ version(Opts, Vsn0) ++ "<br /> +</small> +</p> +<p> +<small> + <a target=\"document\" href=\"" ++ DocName ++ "_cite.html\">Bibliography</a> | + <a target=\"document\" href=\"" ++ DocName ++ "_term.html\">Glossary</a> | + <a target=\"document\" href=\"" ++ DocName ++ "_first.html\">Cover</a>" ++ + top_index(Opts) ++ +"</small> +</p> +</center> +<p> +<small> +<strong>Table of Contents</strong> +</small> +</p> +". + +part_toc_top(Data, DocName, Opts, HRefTexts) -> + [Title, _Prepared, _Responsible, DocNo, _Approved, _Checked, + _Date, Vsn0, _File] = Data, + html_header(Title, []) ++ +"<center> +<p> +<small> +" ++ + docb_util:join( + lists:map( + fun({HRef, Text}) -> + "<a target=\"_top\" href=\"" ++ HRef ++ "\">" ++ + Text ++ "</a>" + end, + HRefTexts), " | ") ++ top_index(Opts) ++ +"</small> +</p> +<p> +<strong>" ++ Title ++ "</strong> +</p> +<p> +<small> + " ++ DocNo ++ version(Opts, Vsn0) ++ "<br /> +</small> +</p> +<p> +<small> + <a target=\"document\" href=\"" ++ DocName ++ "_cite.html\">Bibliography</a> | + <a target=\"document\" href=\"" ++ DocName ++ "_term.html\">Glossary</a> | + <a target=\"document\" href=\"" ++ DocName ++ "_first.html\">Cover</a> +</small> +</p> +</center> +<p> +<small> +<strong>Table of Contents</strong> +</small> +</p> +". + +part_toc_bot() -> +"</body > +</html> +". + +%% Index + +index_top(_Data) -> + ref_html_header("INDEX", []) ++ +"<h1>INDEX</h1> +<p><em>Emphasized</em> index entries refer to <em>modules</em> +and <code>Courier</code> ditos to <code>functions</code>.\n</p>\n". + +index_bot() -> + part_toc_bot(). + +%% Internal functions + +html_header(Title, Opts) -> + Vsn = docb_util:version(), +%%"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"> +"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" + \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> +<!-- This document was generated using DocBuilder-" ++ Vsn ++ " --> +<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"> +<head> + <title>" ++ Title ++ "</title> + <meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\"/> + " ++ docb_util:html_snippet(head, Opts) ++ " + <style type=\"text/css\"> +<!-- + body { font-family: Verdana, Arial, Helvetica, sans-serif } + span.bold_code { font-family: courier;font-weight: bold} + span.code { font-family: courier;font-weight: normal} + +.note, .warning { + border: solid black 1px; + margin: 1em 3em; +} + +.note .label { + background: #30d42a; + color: white; + font-weight: bold; + padding: 5px 10px; +} +.note .content { + background: #eafeea; + color: black; + line-height: 120%; + font-size: 90%; + padding: 5px 10px; +} +.warning .label { + background: #C00; + color: white; + font-weight: bold; + padding: 5px 10px; +} +.warning .content { + background: #FFF0F0; + color: black; + line-height: 120%; + font-size: 90%; + padding: 5px 10px; +} + + .example { background-color:#eeeeff } + pre { font-family: courier; font-weight: normal } + .REFBODY { margin-left: 13mm } + .REFTYPES { margin-left: 8mm } +--> + </style> +</head> +<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" vlink=\"#FF00FF\" alink=\"#FF0000\"> +". + +ref_html_header(Title, Opts) -> + Vsn = docb_util:version(), +%%"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"> +"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" + \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> +<!-- This document was generated using DocBuilder-" ++ Vsn ++ " --> +<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"> +<head> + <title>" ++ Title ++ "</title> + <meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\"/> + " ++ docb_util:html_snippet(head, Opts) ++ " + <style type=\"text/css\"> +<!-- + body { font-family: Verdana, Arial, Helvetica, sans-serif } + span.bold_code { font-family: courier;font-weight: bold} + span.code { font-family: courier;font-weight: normal} + +.note, .warning { + border: solid black 1px; + margin: 1em 3em; +} + +.note .label { + background: #30d42a; + color: white; + font-weight: bold; + padding: 5px 10px; +} +.note .content { + background: #eafeea; + color: black; + line-height: 120%; + font-size: 90%; + padding: 5px 10px; +} +.warning .label { + background: #C00; + color: white; + font-weight: bold; + padding: 5px 10px; +} +.warning .content { + background: #FFF0F0; + color: black; + line-height: 120%; + font-size: 90%; + padding: 5px 10px; +} + + .example { background-color:#eeeeff } + pre { font-family: courier; font-weight: normal } + .REFBODY { margin-left: 13mm } + .REFTYPES { margin-left: 8mm } +--> + </style> +</head> +<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" vlink=\"#FF00FF\" alink=\"#FF0000\"> +". + +version(Opts, Vsn0) -> + case docb_util:lookup_option(vsn, Opts, Vsn0) of + "" -> ""; + Vsn -> " Version " ++ Vsn + end. + +top_index(Opts) -> + case docb_util:lookup_option(top, Opts) of + false -> ""; + TIFile -> + " | <a target=\"_top\" href=\"" ++ TIFile ++ "\">Top</a>" + end. diff --git a/lib/docbuilder/src/docb_html_ref.erl b/lib/docbuilder/src/docb_html_ref.erl new file mode 100644 index 0000000000..c5c166f1ae --- /dev/null +++ b/lib/docbuilder/src/docb_html_ref.erl @@ -0,0 +1,79 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_html_ref). + +-export([rule/2, rule/3]). + +rule([description|_],_) -> + {"\n<h3>DESCRIPTION</h3>\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule([funcs|_],_) -> + {"\n<h3>EXPORTS</h3>\n",""}; + +rule([func|_],_) -> + {"\n<p>",""}; + +rule([name, func, funcs, RefType|_], {_,_,[{pcdata,[],Name0}]}) -> + Name1 = docb_html_util:make_anchor_name_short(Name0, RefType), + {"<a name=\"" ++ Name1 ++ "\"><span class=\"bold_code\">", + "</span></a><br/>\n"}; + +rule([fsummary|_],_) -> + {drop, "\n</p>\n"}; + +rule([type|_], _) -> + {"\n<div class=\"REFBODY\"><p>Types:</p>\n <div class=\"REFTYPES\">\n<p>\n", + "\n </p> </div>\n</div>\n"}; + +rule([v|_], _) -> + {"<span class=\"bold_code\">","</span><br/>\n"}; + +rule([d|_], _) -> + {"\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule([desc|_], _) -> + {"\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule([authors|_], _) -> + {"\n<h3>AUTHORS</h3>\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule([aname|_], _) -> + {"", " - "}; + +rule([section|_], {1,_,_}) -> + {"", ""}; +rule([section|_], {_N,_,_}) -> + {"", "\n</div>\n"}; + +rule([title|_], _) -> + {"\n<h3>", "</h3>\n<div class=\"REFBODY\">\n"}; + +rule(TagHistory, TagBody) -> + docb_html:rule(TagHistory, TagBody). + +rule([email|_], _, Opts) -> + case docb_util:html_snippet(email, Opts) of + "" -> + {{"","<br/>\n"}, Opts}; + Email -> + {{drop, Email++"<br/>\n"}, Opts} + end; + +rule(TagHistory, TagBody, Opts) -> + docb_html:rule(TagHistory, TagBody, Opts). + diff --git a/lib/docbuilder/src/docb_html_util.erl b/lib/docbuilder/src/docb_html_util.erl new file mode 100644 index 0000000000..b2951706ea --- /dev/null +++ b/lib/docbuilder/src/docb_html_util.erl @@ -0,0 +1,543 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_html_util). + +-export([attribute_cdata_to_html/1, + element_cdata_to_html/1, + pcdata_to_html/1, pcdata_to_html/2]). +-export([copy_pics/3]). +-export([extract_header_data/2, all_header_data/1]). +-export([make_uri/1, + make_anchor_href/1, make_anchor_href_short/3, + make_anchor_name_short/2, + make_funcdef_short/2]). +-export([erl_include/2, code_include/2, erl_eval/1]). +-export([number/3, count_sections/1]). +-export([format_toc/1]). +-export([html_latin1_sort_order/1]). + +%%--Handle CDATA and PCDATA--------------------------------------------- + +%% NB: Functions for transforming sgmls/XMerL data output to html. +%% Do not use these for included text files (cf code_include and +%% erl_include). + +attribute_cdata_to_html(Data) -> + data2html(Data, false). + +element_cdata_to_html(Data) -> + data2html(Data, false). + +pcdata_to_html(Data) -> + data2html(Data, true). + +pcdata_to_html(Data, RmSp) -> + data2html(Data, RmSp). + +%% PCDATA, CDATA: Replace entities, and optionally delete +%% leading and multiple spaces. CDATA never contains entities to +%% replace. + +%% data2html(Cs, RmSpace) +data2html([246| Cs], RmSp) -> + [$&, $#, $2, $4, $6, $;| data2html(Cs, RmSp)]; +data2html([$>| Cs], RmSp) -> + [$&, $#, $6, $2, $;| data2html(Cs, RmSp)]; +data2html([$<| Cs], RmSp) -> + [$&, $#, $6, $0, $;| data2html(Cs, RmSp)]; +data2html([$&| Cs], RmSp) -> + [$&, $#, $3, $8, $;| data2html(Cs, RmSp)]; +data2html([$\"| Cs], RmSp) -> + [$&, $#, $3, $4, $;| data2html(Cs, RmSp)]; +data2html([$\n| Cs], RmSp) -> + data2html(Cs, RmSp); +data2html([$\\, $n| Cs], false) -> + [$\n| data2html(Cs, false)]; +data2html([$\\, $n| Cs], true) -> + [$\n| data2html(delete_leading_space(Cs), true)]; +data2html([$ , $ | Cs], true) -> % delete multiple space + [$ | data2html(delete_leading_space(Cs), true)]; +data2html([$\\, $|| Cs0], RmSp) -> + {Ent, Cs1} = collect_entity(Cs0), + [entity_to_html(Ent)| data2html(Cs1, RmSp)]; +data2html([$\\, $0, $1, $2| Cs], RmSp) -> + data2html(Cs, RmSp); +data2html([$\\, $\\, $n| Cs], RmSp) -> + [$\\, $n| data2html(Cs, RmSp)]; +data2html([$\\, O1, O2, O3| Cs], RmSp) + when O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 -> + case octal2dec(O1, O2, O3) of + 173 -> % soft hyphen + data2html(Cs, RmSp); + C when C > 31, C < 256 -> + Ent = io_lib:format("&#~w;", [C]), + [Ent| data2html(Cs, RmSp)]; + C -> + [C| data2html(Cs, RmSp)] + end; +data2html([$\\, $\\| Cs], RmSp) -> + [$\\| data2html(Cs, RmSp)]; +data2html([C| Cs], RmSp) -> + [C| data2html(Cs, RmSp)]; +data2html([], _) -> + []. + +delete_leading_space([$ | Cs]) -> + delete_leading_space(Cs); +delete_leading_space(Cs) -> + Cs. + +collect_entity(Data) -> + collect_entity(Data, []). + +collect_entity([$\\, $|| Cs], Rs) -> + {lists:reverse(Rs), Cs}; +collect_entity([C| Cs], Rs) -> + collect_entity(Cs, [C| Rs]); +collect_entity([], Rs) -> + {[], lists:reverse(Rs)}. + +entity_to_html("&") -> "&"; +entity_to_html("\"") -> """; +entity_to_html("<") -> "<"; +entity_to_html(">") -> ">"; +entity_to_html([$\\, O1, O2, O3]) + when O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 -> + case octal2dec(O1, O2, O3) of + 173 -> % soft hyphen + ""; + Value -> + io_lib:format("&#~w;", [Value]) + end; +entity_to_html(Other) -> + docb_html_util_iso:entity_to_html(Other). + +octal2dec(O1, O2, O3) -> + (O1*8+O2)*8+O3-73*$0. + +%%--Copy images--------------------------------------------------------- + +copy_pics(Src, Dest, Opts) -> + Dir = code:lib_dir(docbuilder), + InFile = filename:join([Dir, "etc", Src]), + OutFile = docb_util:outfile(Dest, "", Opts), + + case filelib:last_modified(OutFile) of + 0 -> % File doesn't exist + file:copy(InFile, OutFile); + + OutMod2 -> + InMod1s = calendar:datetime_to_gregorian_seconds( + filelib:last_modified(InFile)), + OutMod2s = calendar:datetime_to_gregorian_seconds(OutMod2), + if + InMod1s > OutMod2s -> % InFile is newer than OutFile + file:copy(InFile, OutFile); + true -> + ok + end + end. + +%%--Resolve header data------------------------------------------------- + +extract_header_data(Key, {header, [], List}) -> + case lists:keysearch(Key, 1, List) of + {value, {Key, [], []}} -> + ""; + {value, {Key, [], [{pcdata, [], Value}]}} -> + pcdata_to_html(Value); + false -> + "" + end. + +all_header_data(Header) -> + all_header_data(Header, + [title, prepared, responsible, docno, approved, + checked, date, rev, file]). + +all_header_data(_Header, []) -> + []; +all_header_data(Header, [Key| Rest]) -> + [extract_header_data(Key, Header) | all_header_data(Header, Rest)]. + +%%--Resolve hypertext references---------------------------------------- + +%% URI regular expression (RFC 2396): +%% "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?" +%% We split it in five parts: +%% scheme: "^(([^:/?#]+):)?" (includes trailing `:') +%% authority: "^(//([^/?#]*))?" (includes leading `//') +%% path: "^([^?#]*)" +%% query: "^(\\?([^#]*))?" (includes leading `?') +%% fragment: "^(#(.*))?" (includes leading `#') + +make_uri(Cs) -> + lists:flatmap( + fun({path, S}) -> + case regexp:match(S, "\.xml?\$") of + {match, _, _} -> + {ok, NS, _} = regexp:sub(S, "\.xml?\$", ".html"), + NS; + _ -> + S + end; + ({_, S}) -> + S + end, + split_uri(Cs)). + +split_uri(URI) -> + split_uri(URI, [{scheme, "^(([^:/?#]+):)?"}, + {authority, "^(//([^/?#]*))?"}, + {path, "^([^?#]*)"}, + {'query', "^(\\?([^#]*))?"}, + {fragment, "^(#(.*))?"}]). + +split_uri("", [{Tag, _R}| T]) -> + [{Tag, ""}| split_uri("", T)]; +split_uri(Cs0, [{Tag, R}| T]) -> + {match, 1, N} = regexp:match(Cs0, R), + Cs1 = string:substr(Cs0, 1, N), + Cs2 = strip_and_escape_uri_component(Tag, Cs1), + [{Tag, Cs2}| split_uri(string:substr(Cs0, N+1), T)]; +split_uri(_, []) -> + []. + +strip_and_escape_uri_component(authority, "//" ++ Cs) -> + "//" ++ escape_uri(string:strip(Cs)); +strip_and_escape_uri_component(path, Cs) -> + escape_uri(string:strip(Cs)); +strip_and_escape_uri_component('query', "?" ++ Cs) -> + "?" ++ escape_uri(string:strip(Cs)); +strip_and_escape_uri_component(fragment, "#" ++ Cs) -> + "#" ++ escape_uri(string:strip(Cs)); +strip_and_escape_uri_component(_, "") -> + ""; +strip_and_escape_uri_component(_, Cs) -> + escape_uri(string:strip(Cs)). + +escape_uri([C|Cs]) when C =< 32; + C == $<; C == $<; C == $#; C == $%; C == $"; + C == $?; + C == ${; C == $}; C ==$|; C == $\\; C == $^; + C == $[; C == $]; C ==$'; + C >= 127 -> + [$%, mk_hex(C div 16), mk_hex(C rem 16)| escape_uri(Cs)]; +escape_uri([C|Cs]) -> + [C|escape_uri(Cs)]; +escape_uri([]) -> + []. + +mk_hex(C) when C<10 -> + C + $0; +mk_hex(C) -> + C - 10 + $a. + +make_anchor_href(HRef) -> + case regexp:split(HRef, "#") of + {ok, [HRef]} -> + %% No `#' in HRef, i.e. only path + make_anchor_href(HRef, ""); + {ok, [Path, Fragment]}-> + make_anchor_href(Path, Fragment) + end. + +make_anchor_href(Path0, Frag0) -> + Frag1 = string:strip(Frag0), + Path1 = case Path0 of + "" -> + ""; + _ -> + case regexp:match(Path0, "\.xml?\$") of + nomatch -> + Path0 ++ ".html"; + _ -> + {ok, NewPath, _} = regexp:sub(Path0, + "\.xml?\$", + ".html"), + NewPath + end + end, + case Frag1 of + "" -> + attribute_cdata_to_html(Path1); + _ -> + attribute_cdata_to_html(Path1) ++ + "#" ++ + attribute_cdata_to_html([case Ch of $/ -> $-; _ -> Ch end|| + Ch <-Frag1]) + end. + +make_anchor_href_short(Path, Frag, RefType) -> + ShortFrag = make_funcdef_short(Frag, RefType,"-"), + make_anchor_href(Path, ShortFrag). + +make_anchor_name_short(FuncName0, RefType) -> + FuncName1 = make_funcdef_short(FuncName0, RefType,"-"), + attribute_cdata_to_html(FuncName1). + +make_funcdef_short(FuncDef0, RefType) -> + make_funcdef_short(FuncDef0, RefType, "/"). + +make_funcdef_short(FuncDef0, RefType,Delimiter) -> + FuncDef1 = docb_util:trim(FuncDef0), + Any0 = case lists:member(RefType, [cref, erlref]) of + true -> + case catch docb_util:fknidx(FuncDef1, Delimiter) of + {'EXIT', _} -> + false; + Any1 -> + Any1 + end; + false -> + false + end, + case Any0 of + false -> + case string:tokens(FuncDef1, " ") of + [Any2| _] -> + Any2; + _ -> + FuncDef1 + end; + _ -> + Any0 + end. + +%%--Include tags-------------------------------------------------------- + +%% Only used in report DTD +erl_include(File, Tag) -> + case docb_main:include_file(File, Tag) of + {ok, Cs} -> + {drop, "\n<pre>\n" ++ text_to_html(Cs) ++ "\n</pre>\n"}; + error -> + {drop, ""} + end. + +code_include(File, Tag) -> + case docb_main:include(File, Tag, Tag) of + {ok, Cs} -> + {ok,text_to_html(Cs)}; + error -> + {error, {codeinclude,File}} + end. + +erl_eval(Expr) -> + Cs = docb_main:eval_str(Expr), + {drop, "\n<pre>\n" ++ text_to_html(Cs) ++ "\n</pre>\n"}. + +%% Only replaces certain characters. Spaces and new lines etc are kept. +%% Used for plain text (e.g. inclusions of code). +text_to_html([$>| Cs]) -> + [$&, $#, $6, $2, $;| text_to_html(Cs)]; +text_to_html([$<| Cs]) -> + [$&, $#, $6, $0, $;| text_to_html(Cs)]; +text_to_html([$&| Cs]) -> + [$&, $#, $3, $8, $;| text_to_html(Cs)]; +text_to_html([$\"| Cs]) -> + [$&, $#, $3, $4, $;| text_to_html(Cs)]; +text_to_html([C| Cs]) -> + [C| text_to_html(Cs)]; +text_to_html([]) -> + []. + +%%--Number sections----------------------------------------------------- + +number({Tag, Attrs, More}, none, File) -> + {Tag, Attrs, do_number(More, [1], File)}; +number({Tag, Attrs, More}, Prefix, File) -> + {Tag, Attrs, do_number(More, [list_to_integer(Prefix)], File)}. + +do_number([], _, _) -> + []; +do_number([{header, Attrs, More}| Rest], NN, File) -> + [{header, Attrs, More}| do_number(Rest, NN, File)]; +do_number([{section, Attrs, More}| Rest], [N| NN], File) -> + [{section, Attrs, do_number(More, [1, N| NN], File)}| + do_number(Rest, [N+1| NN], File)]; +do_number([{title, _, [{pcdata, _, Title}]}| More], [N| NN], File) -> + Format = make_format(length(NN)), + Number = lists:flatten(io_lib:format(Format, lists:reverse(NN))), + [{marker, [{"ID", "CDATA", Number}], []}, + {title, [{"NUMBER", "CDATA", Number}, + {"FILE", "CDATA", File}], + [{pcdata, [], Title}]}| do_number(More, [N| NN], File)]; +do_number([{pcdata, Attrs, More}| Rest], NN, File) -> + [{pcdata, Attrs, More}| do_number(Rest, NN, File)]; +do_number([{Tag, Attrs, More}| Rest], NN, File) -> + [{Tag, Attrs, do_number(More, NN, File)}|do_number(Rest, NN, File)]. + +make_format(1) -> + "~w"; +make_format(N) -> + "~w." ++ make_format(N-1). + +count_sections([section| Rest]) -> + 1 + count_sections(Rest); +count_sections([_| Rest]) -> + count_sections(Rest); +count_sections([]) -> + 0. + +%%--Make a ToC---------------------------------------------------------- + +format_toc(Toc) -> + lists:map(fun({Number, Title}) -> + [Number, " <a href = \"#", Number, + "\">", Title, "</a><br/>\n"] + end, Toc). + +%%--Convert HTML ISO Latin 1 characters to ordinary characters---------- + +%% To be used for sorting. Cs must be flat. +html_latin1_sort_order(Cs) -> + hlso(Cs). + +hlso([]) -> + []; +hlso([$&, $#, C2, C1, C0, $;| Cs]) + when $0 =< C2, C2 =< $9, $0 =< C1, C1 =< $9, $0 =< C0, C0 =< $9 -> + C = ((C2-$0)*10 + (C1-$0))*10 + C0-$0, + hlso0(C, Cs); +hlso([$&, $#, C1, C0, $;| Cs]) + when $0 =< C1, C1 =< $9, $0 =< C0, C0 =< $9 -> + C = (C1-$0)*10 + C0-$0, + hlso0(C, Cs); +hlso([C| Cs]) -> + [C| hlso(Cs)]. + +hlso0(C, Cs) when 0 =< C, C =< 159 -> + [C| hlso(Cs)]; +hlso0(160, Cs) -> %% no-break space + hlso(Cs); % Remove it. +hlso0(161, Cs) -> %% inverted exclamation mark + [$? |hlso(Cs)]; +hlso0(162, Cs) -> %% cent sign + [$$|hlso(Cs)]; +hlso0(163, Cs) -> %% pound sterling sign + [$$|hlso(Cs)]; +hlso0(164, Cs) -> %% general currency sign + [$$|hlso(Cs)]; +hlso0(165, Cs) -> %% yen sign + [$$|hlso(Cs)]; +hlso0(166, Cs) -> %% broken (vertical) bar + [$| |hlso(Cs)]; +hlso0(167, Cs) -> %% section sign + [$$|hlso(Cs)]; +hlso0(168, Cs) -> %% umlaut (dieresis) + [$: |hlso(Cs)]; +hlso0(169, Cs) -> %% copyright sign + [$c |hlso(Cs)]; +hlso0(170, Cs) -> %% ordinal indicator, feminine + [$f |hlso(Cs)]; +hlso0(171, Cs) -> %% angle quotation mark, left + [$" |hlso(Cs)]; +hlso0(172, Cs) -> %% not sign + [$- |hlso(Cs)]; +hlso0(173, Cs) -> %% soft hyphen + [$- |hlso(Cs)]; +hlso0(174, Cs) -> %% registered sign + [$r |hlso(Cs)]; +hlso0(175, Cs) -> %% macron + [$- |hlso(Cs)]; +hlso0(176, Cs) -> %% degree sign + [$d |hlso(Cs)]; +hlso0(177, Cs) -> %% plus-or-minus sign + [$+ |hlso(Cs)]; +hlso0(178, Cs) -> %% superscript two + [$2 |hlso(Cs)]; +hlso0(179, Cs) -> %% superscript three + [$3 |hlso(Cs)]; +hlso0(180, Cs) -> %% acute accent + [$' |hlso(Cs)]; +hlso0(181, Cs) -> %% micro sign + [$' |hlso(Cs)]; +hlso0(182, Cs) -> %% pilcrow (paragraph sign) + [$$|hlso(Cs)]; +hlso0(183, Cs) -> %% middle dot + [$. |hlso(Cs)]; +hlso0(184, Cs) -> %% cedilla + [$c |hlso(Cs)]; +hlso0(185, Cs) -> %% superscript one + [$1 |hlso(Cs)]; +hlso0(186, Cs) -> %% ordinal indicator, masculine + [$m |hlso(Cs)]; +hlso0(187, Cs) -> %% angle quotation mark, right + [$" |hlso(Cs)]; +hlso0(188, Cs) -> %% fraction one-quarter + [$4 |hlso(Cs)]; +hlso0(189, Cs) -> %% fraction one-half + [$2 |hlso(Cs)]; +hlso0(190, Cs) -> %% fraction three-quarters + [$3 |hlso(Cs)]; +hlso0(191, Cs) -> %% inverted question mark + [$? |hlso(Cs)]; + +hlso0(C, Cs) when 192 =< C, C =< 198 -> %% capital A + [$A |hlso(Cs)]; +hlso0(199, Cs) -> %% capital C, cedilla + [$C |hlso(Cs)]; +hlso0(C, Cs) when 200 =< C, C =< 203 -> %% capital E + [$E |hlso(Cs)]; +hlso0(C, Cs) when 204 =< C, C =< 207 -> %% capital I + [$I |hlso(Cs)]; +hlso0(208, Cs) -> %% capital Eth, Icelandic + [$D |hlso(Cs)]; +hlso0(209, Cs) -> %% capital N, tilde + [$N |hlso(Cs)]; +hlso0(C, Cs) when 210 =< C, C =< 214 -> %% capital O + [$O |hlso(Cs)]; +hlso0(215, Cs) -> %% multiply sign + [$x |hlso(Cs)]; +hlso0(216, Cs) -> %% capital O, slash + [$O |hlso(Cs)]; +hlso0(C, Cs) when 217 =< C, C =< 220 -> %% capital U + [$U |hlso(Cs)]; +hlso0(221, Cs) -> %% capital Y, acute accent + [$Y |hlso(Cs)]; +hlso0(222, Cs) -> %% capital THORN, Icelandic + [$T |hlso(Cs)]; +hlso0(223, Cs) -> %% small sharp s, German (sz + [$s |hlso(Cs)]; +hlso0(C, Cs) when 224 =< C, C =< 230-> %% small a + [$a |hlso(Cs)]; +hlso0(231, Cs) -> %% small c, cedilla + [$c |hlso(Cs)]; +hlso0(C, Cs) when 232 =< C, C =< 235 -> %% small e + [$e |hlso(Cs)]; +hlso0(C, Cs) when 236 =< C, C =< 239 -> %% small i + [$i |hlso(Cs)]; +hlso0(240, Cs) -> %% small eth, Icelandic + [$d |hlso(Cs)]; +hlso0(241, Cs) -> %% small n, tilde + [$n |hlso(Cs)]; +hlso0(C, Cs) when 242 =< C, C =< 246 -> %% small o + [$o |hlso(Cs)]; +hlso0(247, Cs) -> %% divide sign + [$/ |hlso(Cs)]; +hlso0(248, Cs) -> %% small o, slash + [$o |hlso(Cs)]; +hlso0(C, Cs) when 249 =< C, C =< 252 -> %% small u + [$u |hlso(Cs)]; +hlso0(253, Cs) -> %% small y, acute accent + [$y |hlso(Cs)]; +hlso0(254, Cs) -> %% small thorn, Icelandic + [$t |hlso(Cs)]; +hlso0(255, Cs) -> %% small y, dieresis or umlaut + [$y |hlso(Cs)]. diff --git a/lib/docbuilder/src/docb_html_util_iso.erl b/lib/docbuilder/src/docb_html_util_iso.erl new file mode 100644 index 0000000000..c836805cd2 --- /dev/null +++ b/lib/docbuilder/src/docb_html_util_iso.erl @@ -0,0 +1,204 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_html_util_iso). +-export([entity_to_html/1]). + +%% Encodes ISOtech, ISOnum and ISOgrk3. +%% +%% All entities are of the form "[abcdef]". +%% +entity_to_html(Entity) when is_list(Entity), hd(Entity) =/= $[ -> + Entity; +entity_to_html(Entity) -> + case (catch enc(Entity)) of + {'EXIT', _} -> + Entity; + Enc -> + "<font face=symbol>" ++ Enc ++ "</font>" + end. + +enc("[Delta ]") -> "D"; +%% enc("[Dot ]") -> "�"; +%% enc("[DotDot]") -> "�"; +enc("[Gamma ]") -> "G"; +enc("[Lambda]") -> "L"; +enc("[Omega ]") -> "W"; +enc("[Phi ]") -> "F"; +enc("[Pi ]") -> "P"; +enc("[Prime ]") -> "²"; +enc("[Psi ]") -> "Y"; +enc("[Sigma ]") -> "S"; +enc("[Theta ]") -> "Q"; +enc("[Upsi ]") -> "¡"; +%% enc("[Verbar]") -> "�"; +enc("[Xi ]") -> "X"; + +enc("[aleph ]") -> "À"; +enc("[alpha ]") -> "a"; +enc("[amp ]") -> "&"; +enc("[and ]") -> "Ù"; +enc("[ang ]") -> "Ð"; +%% enc("[ang90 ]") -> "�"; +%% enc("[angsph]") -> "�"; +%% enc("[angst ]") -> "�"; +enc("[ap ]") -> "»"; + +%% enc("[becaus]") -> "�"; +%% enc("[bernou]") -> "�"; +enc("[bepsi ]") -> "'"; +enc("[beta ]") -> "b"; +enc("[bottom]") -> "^"; +enc("[bull ]") -> "·"; + +enc("[cap ]") -> "Ç"; +enc("[chi ]") -> "c"; +enc("[clubs ]") -> "§"; +%% enc("[compfn]") -> "�"; +enc("[cong ]") -> "@"; +enc("[copy ]") -> "Ó"; +%% enc("[conint]") -> "�"; +enc("[cup ]") -> "È"; + +enc("[dArr ]") -> "ß"; +enc("[darr ]") -> "¯"; +enc("[deg ]") -> "°"; +enc("[delta ]") -> "d"; +enc("[diam ]") -> "à"; +enc("[diams ]") -> "¨"; +enc("[divide]") -> "¸"; + +enc("[empty ]") -> "Æ"; +%% enc("[epsi ]") -> "�"; +%% enc("[epsis ]") -> "�"; +enc("[epsiv ]") -> "e"; +enc("[equiv ]") -> "º"; +enc("[eta ]") -> "h"; +enc("[exist ]") -> "$"; + +enc("[fnof ]") -> "¦"; +enc("[forall]") -> """; + +enc("[gamma ]") -> "g"; +%% enc("[gammad]") -> "�"; +enc("[ge ]") -> "³"; +enc("[gt ]") -> ">"; + +%% enc("[hamilt]") -> "�"; +enc("[hArr ]") -> "Û"; +enc("[harr ]") -> "«"; +enc("[hearts]") -> "©"; +enc("[horbar]") -> "¾"; + +enc("[iff ]") -> "Û"; +enc("[image ]") -> "Á"; +enc("[infin ]") -> "¥"; +enc("[int ]") -> "ò"; +enc("[iota ]") -> "i"; +enc("[isin ]") -> "Î"; + +enc("[kappa ]") -> "k"; +%%enc("[kappav]") -> "�"; + +enc("[lArr ]") -> "Ü"; +%% enc("[lagran]") -> "�"; +enc("[lambda]") -> "l"; +enc("[lang ]") -> "á"; +enc("[larr ]") -> "¬"; +enc("[le ]") -> "£"; +%% enc("[lowast]") -> "�"; +enc("[lowbar]") -> "_"; +enc("[lt ]") -> "<"; + +enc("[middot]") -> "×"; +enc("[minus ]") -> "-"; +%% enc("[mnplus]") -> "�"; +enc("[mu ]") -> "m"; + +enc("[nabla ]") -> "Ñ"; +enc("[ne ]") -> "¹"; +enc("[ni ]") -> "'"; +enc("[nsub ]") -> "Ë"; +enc("[not ]") -> "Ø"; +enc("[notin ]") -> "Ï"; +enc("[nu ]") -> "n"; + +enc("[omega ]") -> "w"; +enc("[or ]") -> "Ú"; +%% enc("[order ]") -> "�"; +enc("[oplus ]") -> "Å"; +enc("[otimes]") -> "Ä"; + +%% enc("[par ]") -> "�"; +enc("[part ]") -> "¶"; +%% enc("[permil]") -> "�"; +enc("[perp ]") -> "^"; % bottom +enc("[phis ]") -> "f"; +enc("[phiv ]") -> "j"; +%% enc("[phmmat]") -> "�"; +enc("[pi ]") -> "p"; +enc("[piv ]") -> "v"; +enc("[plus ]") -> "+"; +enc("[plusmn]") -> "±"; +enc("[prime ]") -> "¢"; +enc("[prod ]") -> "Õ"; +enc("[prop ]") -> "µ"; +enc("[psi ]") -> "y"; + +enc("[radic ]") -> "Ö"; +enc("[rang ]") -> "ñ"; +enc("[rArr ]") -> "Þ"; +enc("[rarr ]") -> "®"; +enc("[real ]") -> "Â"; +enc("[reg ]") -> "Ò"; +enc("[rho ]") -> "r"; +%% enc("[rhov ]") -> "�"; + +enc("[sigma ]") -> "s"; +enc("[sigmav]") -> "V"; +enc("[sim ]") -> "~"; +%% enc("[sime ]") -> "�"; +%% enc("[square]") -> "�"; +enc("[sol ]") -> "¤"; +enc("[spades]") -> "ª"; +enc("[sub ]") -> "Ì"; +enc("[sube ]") -> "Í"; +enc("[sup ]") -> "É"; +enc("[supe ]") -> "Ê"; +enc("[sum ]") -> "å"; + +enc("[tau ]") -> "t"; +enc("[tdot ]") -> "¼"; +enc("[there4]") -> "\"; +enc("[thetas]") -> "q"; +enc("[thetav]") -> "J"; +enc("[times ]") -> "´"; +%% enc("[tprime]") -> "�"; +enc("[trade ]") -> "Ô"; + +enc("[uArr ]") -> "Ý"; +enc("[uarr ]") -> "­"; +enc("[upsi ]") -> "u"; + +enc("[verbar]") -> "½"; + +%% enc("[wedgeq]") -> "�"; +enc("[weierp]") -> "Ã"; + +enc("[xi ]") -> "x"; + +enc("[zeta ]") -> "z". diff --git a/lib/docbuilder/src/docb_main.erl b/lib/docbuilder/src/docb_main.erl new file mode 100644 index 0000000000..ef21f65557 --- /dev/null +++ b/lib/docbuilder/src/docb_main.erl @@ -0,0 +1,651 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_main). + +-export([process/2, + parse/2, parse1/2, + pp/2, + insert_after/3, + transform/5, pp/5, + include_file/2, include/3, + eval_str/1, + validate_html/1 + ]). +-export([do_parse_sgmls/1]). + +%%---------------------------------------------------------------------- + +%% process(File, Opts) -> errors | ok +%% Parses the source file File and transforms the result to html, +%% latex and/or man page format. +process(File, Opts) -> + + File1 = File ++ ".tmpconv", + os:cmd("sed -e 's/xi:include[ \t]*href/include file/g' -e 's/xmlns:xi=\"http:\\/\\/www.w3.org\\/2001\\/XInclude\"//g' < " ++ + File ++ ".xml > " ++ File1 ++ ".xml"), %LATH + + case parse1(File1, Opts) of + errors -> + file:delete(File1 ++ ".xml"), + errors; + {ok, Tree} -> + From = element(1, Tree), + Tos0 = + lists:foldl( + fun(latex, Acc) -> [latex|Acc]; + (html, Acc) -> [html|Acc]; + ({man, _Section}, Acc) -> [man|Acc]; + (_, Acc) -> Acc + end, [], Opts), + + %% If no target format is specified, assume HTML: + Tos = if + Tos0==[] -> [html]; + true -> Tos0 + end, + + Result = [transform(From, To, Opts, File, Tree)||To <- Tos], + case lists:member(transformation_error,Result) of + true -> + file:delete(File1 ++ ".xml"), + errors; + _ -> + file:delete(File1 ++ ".xml"), + ok + end + + end. + +%%---------------------------------------------------------------------- + +%% parse(File, Opts) -> {ok, Tree} | errors +%% Parses the source file File, resulting in a tree structure. +parse(File, Opts) -> + case docb_util:lookup_option(src_type, Opts) of + ".xml" -> + parse_xml(File++".xml", Opts); + ".sgml" -> + parse_sgml(File, Opts) + end. + +%% parse1(File, Opts) -> {ok, Tree} | errors +%% Like parse/2, but in the SGML case also prints the parse errors +%% (in File.html.sgmls_errs) information to stdout. +parse1(File, Opts) -> + parse(File, [{print_parse_errs, true}|Opts]). + + +validate_html(InFile) -> + ScanOpts = [{validation,true}, {fetch_fun, fun fetch_dtd/2}], + case xmerl_scan:file(InFile, ScanOpts) of + {_XMLTuple,[]} -> % ok + {InFile,ok}; + {'EXIT',Reason} -> + {InFile,Reason} + end. + +fetch_dtd({public,_,"http://www.w3.org/TR/xhtml1/DTD/"++ Rest},GlobalState) -> + Filename = filename:join(docb_util:dtd_dir(),Rest), + {ok,{file,Filename},GlobalState}; +fetch_dtd({public,_,Str},GlobalState) -> + {ok,{file,filename:join(docb_util:dtd_dir(),Str)},GlobalState}. + + + +parse_xml(InFile, Opts) -> + DtdDir = docb_util:dtd_dir(), + ScanOpts = [{validation,true}, {fetch_path, [DtdDir]}], + PrintP = docb_util:lookup_option(print_parse_errs, Opts), + case catch xmerl_scan:file(InFile, ScanOpts) of + {'EXIT', Error} when PrintP -> + docb_util:message(error, + "XML validation error:~n~p", [Error]), + errors; + {'EXIT', _Error} -> + errors; + {error, Reason} -> % probably file error + docb_util:message(error, "XML scan error: ~p", [Reason]), + errors; + {Doc, []} -> + case catch xmerl:export([Doc], docb_xmerl_tree_cb) of + [Tree] -> + verify(Tree), + {ok, Tree}; + {'EXIT', Error} -> + docb_util:message(error, + "XML export error:~n~p", [Error]), + errors + end + end. + +parse_sgml(InFile, Opts) -> + + Pfx = tmp_file_prefix(InFile, Opts), + OutFile = Pfx ++ "sgmls_output", + ErrFile = Pfx ++ "sgmls_errs", + + EntVals = lists:usort(docb_util:lookup_options(ent, Opts)), + Ents = lists:flatten([" -ent " ++ Val || Val <- EntVals]), + Cmd = docb_util:old_docb_dir() ++ "/bin/docb_sgmls_run " ++ + Ents ++ " " ++ InFile ++ ".sgml " ++ OutFile ++ " " ++ ErrFile, + + case os:cmd(Cmd) of + [] -> + PrintP = docb_util:lookup_option(print_parse_errs, Opts), + case filelib:file_size(ErrFile) of + 0 -> % implies no errors + parse_sgmls(InFile, OutFile); + _ when PrintP -> + cat(ErrFile), + errors; + _ -> + errors + end; + Msg -> + docb_util:message(error, "~p", [Msg]), + errors + end. + +tmp_file_prefix(File, Opts) -> + lists:concat( + [File, "." | lists:foldl( + fun(latex, Acc) -> ["latex."|Acc]; + (html, Acc) -> ["html."|Acc]; + ({man, Section}, Acc) -> ["man", Section, "."|Acc]; + (_, Acc) -> Acc + end, [], Opts)]). + +parse_sgmls(InFile, SgmlsFile) -> + case file:open(SgmlsFile, [read]) of + {ok, Fd} -> + Res = case (catch do_parse_sgmls(Fd)) of + {ok, Tree} -> + {ok, Tree}; + {'EXIT', Reason} -> + docb_util:message( + error, + "Cannot parse sgmls output file " + "~s, obtained from parsing ~s, " + "reason: ~w", + [SgmlsFile, InFile, Reason]), + errors; + {error, Reason} -> + docb_util:message( + error, + "Cannot parse sgmls output file " + "~s, obtained from parsing ~s, " + "reason: ~w", + [SgmlsFile, InFile, Reason]), + errors + end, + file:close(Fd), + case Res of + {ok, Tree0} -> + verify(Tree0), + {ok, Tree0}; + _Other -> + errors + end; + {error, Reason} -> + docb_util:message(error, + "Cannot open sgmls output file ~s, " + "obtained from parsing ~s, reason: ~w", + [SgmlsFile, InFile, Reason]), + errors + end. + +do_parse_sgmls(Fd) -> + do_parse_sgmls(Fd, []). + +do_parse_sgmls(Fd, Attrs) -> + case get_line(Fd) of + {attrs, A} -> + do_parse_sgmls(Fd, [A|Attrs]); + {startTag, Tag} -> + {ok, {Tag, Attrs, get_args(Fd)}}; + Other -> + {error, Other} + end. + +get_args(Fd) -> + case get_line(Fd) of + {startTag, Tag} -> + H = {Tag, [], get_args(Fd)}, + [H|get_args(Fd)]; + {dataTag, Str} -> + [{pcdata, [], Str}|get_args(Fd)]; + {attrs, A} -> + get_args_attr(Fd, [A]); + close -> + []; + ok -> + [] + end. + +get_args_attr(Fd, Attrs) -> + case get_line(Fd) of + {startTag, Tag} -> + H = {Tag, lists:reverse(Attrs), get_args(Fd)}, + [H|get_args(Fd)]; + {dataTag, Str} -> + [{pcdata, lists:reverse(Attrs), Str}|get_args(Fd)]; + {attrs, A} -> + get_args_attr(Fd, [A|Attrs]); + close -> + []; + ok -> + [] + end. + +get_line(Fd) -> + Str = io:get_line(Fd, ''), + case Str of + [$(|T] -> + {startTag, tag_name(T)}; + [$-|T] -> + {dataTag, T}; + [$)|_T] -> + close; + [$A|T] -> + {attrs, attrs(remove_nl(T))}; + [$?|_T] -> + get_line(Fd); + [$C|_] -> + ok + end. + +remove_nl([$\n|_]) -> []; +remove_nl([H|T]) -> [H|remove_nl(T)]; +remove_nl([]) -> []. + +%% attrs +%% splits a string like +%% AAAAA BBBBB ...... +%% into {"AAA", "BBB", Rest} + +attrs(T) -> + {X, T1} = get_item(T), + {Y, T2} = get_item(T1), + T3 = skip_blanks(T2), + {X, Y, T3}. + +get_item(T) -> get_item(skip_blanks(T), []). + +get_item([$ |T], L) -> {lists:reverse(L), [$ |T]}; +get_item([H|T], L) -> get_item(T, [H|L]); +get_item([], L) -> {lists:reverse(L), []}. + +skip_blanks([$ |T]) -> skip_blanks(T); +skip_blanks(T) -> T. + +tag_name(Str) -> tag_name(Str, []). + +tag_name([H|T], L) when $A =< H, H =< $Z -> + tag_name(T, [H-$A+$a|L]); +tag_name([$\n], L) -> + list_to_atom(lists:reverse(L)); +tag_name([H|T], L) -> + tag_name(T, [H|L]). + +cat(File) -> + case file:open(File, [read]) of + {ok, Fd} -> + cat1(Fd), + file:close(Fd); + Other -> + Other + end. + +cat1(Fd) -> + case io:get_line(Fd, '') of + eof -> + eof; + Str -> + io:format("~s", [Str]), + cat1(Fd) + end. + +%%---------------------------------------------------------------------- + +verify(Tree) -> verify(Tree, [], 1). + +verify({pcdata, Optional, _}, Path, Level) -> + verify_optional(Optional, Path, Level); +verify({Tag, Optional, Args}, Path, Level) when is_list(Args) -> + case verify_optional(Optional, Path, Level) of + true -> + verify_list(Args, [Tag|Path], Level); + false -> + false + end; +verify(Other, Path, Level) -> + verify_error(Other, Path, Level). + +verify_error(X, Path, Level) -> + docb_util:message(error, "Invalid object found at: ~p level:~w~n~s", + [Path, Level, docb_pretty_format:term(X)]), + false. + +verify_list([H|T], Path, Level) -> + case verify(H, Path, Level) of + true -> + verify_list(T, Path, Level +1); + false -> + false + end; +verify_list([], _, _) -> + true. + +verify_optional([{_, _, _}|T], Path, Level) -> + verify_optional(T, Path, Level); +verify_optional([], _Path, _Level) -> + true; +verify_optional(X, Path, Level) -> + verify_error(X, Path, Level). + +%%---------------------------------------------------------------------- + +%% pp(File, Opts) -> {ok, OutFile} | errors +%% Parses the source file and, if successful, prints the resulting tree +%% structure to a file with the extension ".pp". +pp(File, Opts) -> + case parse(File, Opts) of + {ok, Tree} -> + OutFile = File ++ ".pp", + dump(OutFile, Tree), + {ok, OutFile}; + errors -> + errors + end. + +dump(File, Struct) -> + {ok, Stream} = file:open(File, [write]), + io:format("Info: Dump on ~p ...", [File]), + io:format(Stream, "~n~s~n", [docb_pretty_format:term(Struct)]), + io:format(" done.\n"), + file:close(Stream). + +%%---------------------------------------------------------------------- + +%% insert_after(Tag, Tree, Obj) -> Tree | {'EXIT', Reason} +%% Insert an element in a tree structure +insert_after(Tag, Tree, Obj) -> + edit(Tag, Tree, {insert_after, Obj}). + +%% edit Op = delete, insert_before, insert_after +edit(Tag, Tree, Op) -> + case catch edit1(Tag, Tree, Op) of + error -> + docb_util:message(error, "Cannot do ~p to ~w", [Op, Tag]), + Tree; + Other -> + Other + end. + +edit1(Tag, {Tag, _O, _A}, _Op) -> + throw(error); +edit1(Tag, {Tag1, O, A}, Op) -> + {Tag1, O, edit1_list(Tag, A, Op)}; +edit1(_, _, _) -> + throw(error). + +edit1_list(Tag, [{pcdata, Str}|T], Op) -> + [{pcdata, Str}|edit1_list(Tag, T, Op)]; +edit1_list(Tag, [{Tag, O, A}|T], {insert_after, Obj}) -> + [{Tag, O, A}, Obj|T]; +edit1_list(Tag, [H|T], Op) -> + [H|edit1_list(Tag, T, Op)]; +edit1_list(_Tag, [], _Op) -> + []. + +%%______________________________________________________________________ + +%% transform(From, To, Opts, File, Tree) -> void() +%% Actual transformation of tree structure to desired format. +transform(From, To, Opts, File, Tree) -> + Filter = if + To==html; To==kwic -> + list_to_atom("docb_tr_" ++ atom_to_list(From) ++ + [$2|atom_to_list(To)]); + true -> + list_to_atom("sgml_tr_" ++ atom_to_list(From) ++ + [$2|atom_to_list(To)]) + end, + + case catch apply(Filter, transform, [File, Tree, Opts]) of + + %% R5C + {'EXIT', {undef, [{Filter, transform, [File, Tree, Opts]}|_]}}-> + %% No transformation defined + finish_transform(Tree, File, Opts, Filter); + + {'EXIT', {undef, {Filter, transform, [File, Tree, Opts]}}} -> + %% No transformation defined + finish_transform(Tree, File, Opts, Filter); + + {'EXIT', What} -> + docb_util:message(error, + "Transformation trouble in ~P", [What, 9]), + transformation_error; + + {error, Reason} -> + docb_util:message(error, Reason), + transformation_error; + + {Tree1, Opts1} -> + %% transformation returning both new parse and new options + finish_transform(Tree1, File, Opts1, Filter); + + Tree1 -> + %% transformation returning only new parse + finish_transform(Tree1, File, Opts, Filter) + end. + +finish_transform(Tree, File, Opts, Filter) -> + {Str, NewOpts} = pp(Tree, [], 1, Filter, Opts), + Extension = + case catch apply(Filter, extension, [NewOpts]) of + {'EXIT', _} -> + apply(Filter, extension, []); + Others -> + Others + end, + {ok, Out} = + file:open(docb_util:outfile(File, Extension, NewOpts), [write]), + put_chars(Out, Str), + file:close(Out). + +put_chars(Out, Str) -> put_chars(Out, Str, 0). + +put_chars(Out, [$\n|Cs], _Pos) -> + io:put_chars(Out, [$\n]), + put_chars(Out, Cs, 0); + +put_chars(Out, [$\011|Cs], Pos) -> % tab + TabbedPos = 8*((Pos div 8)+1), + Nblanks = TabbedPos - Pos, + io:put_chars(Out, lists:duplicate(Nblanks, $ )), + put_chars(Out, Cs, Pos+Nblanks); + +put_chars(Out, [C|Cs], Pos) when is_integer(C) -> + io:put_chars(Out, [C]), + put_chars(Out, Cs, Pos+1); + +put_chars(Out, [L|Cs], Pos) when is_list(L) -> + put_chars(Out, Cs, put_chars(Out, L, Pos)); + +put_chars(_Out, [], Pos) -> + Pos. + +pp({Tag, Optional, Args}, TagPath, Level, Filter, Opts) -> + TagPath1 = [Tag|TagPath], + Optional1 = reduce_optional(Optional), + + %% First try Filter:rule/3. It returns {Return, NewOpts} + %% where Return is as from rule/2: + Rule_3_result = + case catch Filter:rule(TagPath1, {Level,Optional1,Args},Opts) of + %% R5C + {'EXIT', {undef, [{_, rule, _}|_]}} -> % No rule/3 defined + failed; + + {'EXIT', {undef, {_, rule, _}}} -> % No rule/3 defined + failed; + %% R5C + {'EXIT', {function_clause, [{_, rule, _}|_]}} -> % No MATCHING rule/3 + failed; + + {'EXIT', {function_clause, {_, rule, _}}} -> % No MATCHING rule/3 + failed; + + {'EXIT', What} -> + docb_util:message(error, + "Serious Error: ~P", [What, 9]); + Others -> + Others + end, + handle_rule_call_result({r3, Rule_3_result}, Filter, TagPath1, Tag, + Level, Optional1, Args, Opts). + +handle_rule_call_result({r3, failed}, Filter, TagPath1, Tag, Level, Optional1, + Args, Opts) -> + %% Hmmm, try Filter:rule/2 + Rule_2_result = (catch Filter:rule(TagPath1, {Level, Optional1, Args})), + handle_rule_call_result({r2, Rule_2_result}, Filter, TagPath1, Tag, + Level, Optional1, Args, Opts); +handle_rule_call_result({r3, {Result, NewOpts}}, Filter, TagPath1, Tag, Level, + Optional1, Args, _Opts) -> + handle_rule_call_result({r2, Result}, Filter, TagPath1, Tag, Level, + Optional1, Args, NewOpts); +handle_rule_call_result({_, {func, F}}, _Filter, _TagPath1, _Tag, _Level, + _Optional1, Args, Opts) -> + {F(Args), Opts}; +handle_rule_call_result({_, {'EXIT', Why}}, _Filter, TagPath1, _Tag, Level, + Optional1, Args, Opts) -> + report_error(TagPath1, Why, {Level, Optional1, Args}), + {[], Opts}; +handle_rule_call_result({_, {drop, Str}}, _Filter, _TagPath1, _Tag, _Level, + _Optional1, _Args, Opts) -> + {[Str], Opts}; +handle_rule_call_result({_, {newargs, NewArgs}}, Filter, TagPath1, _Tag, _Level, + _Optional1, _Args, Opts) -> + {List, NewOpts} = pp_list(NewArgs, TagPath1, 1, Filter, Opts), + {[List], NewOpts}; +handle_rule_call_result({_, {newargs, Before, NewArgs, After}}, Filter, TagPath1, _Tag, _Level, + _Optional1, _Args, Opts) -> + {List, NewOpts} = pp_list(NewArgs, TagPath1, 1, Filter, Opts), + {[Before, List, After], NewOpts}; +handle_rule_call_result({_, {Before, After}}, Filter, TagPath1, _Tag, _Level, + _Optional1, Args, Opts) when is_list(Before) -> + {List, NewOpts} = pp_list(Args, TagPath1, 1, Filter, Opts), + {[Before, List, After], NewOpts}. + +pp_list([H|T], TagPath, Level, Rules, Opts) -> + {Hpp, Hopts} = pp(H, TagPath, Level, Rules, Opts), + {Tpp, Tops} = pp_list(T, TagPath, Level + 1, Rules, Hopts), + {[Hpp|Tpp], Tops}; +pp_list([], _, _, _, Opts) -> + {[], Opts}. + +reduce_optional([{_, _, H}|T]) -> [H|reduce_optional(T)]; +reduce_optional([]) -> []. + +report_error(Arg1, Cause, Arg2) -> + [Tag|_] = Arg1, + docb_util:message(error, + "Formatting trouble in ~p: ~p", [Tag, Cause]), + docb_util:message(error, "Failure in rule(~p, ~p)", [Arg1, Arg2]). + +%%---------------------------------------------------------------------- + +%% include_file(File, Tag) -> {ok, String} | error +include_file(File, Tag) -> + include(File, "%S" ++ Tag, "%E" ++ Tag). + +%% include(File, StartTag, StopTag) -> {ok, String} | error +include(File, "", "") -> + case file:open(File, [read]) of + {ok, Fd} -> + String = include_all(Fd), + file:close(Fd), + {ok, String}; + _ -> + docb_util:message(error, + "Include file ~s not found", [File]), + error + end; +include(File, StartTag, StopTag) -> + case file:open(File, [read]) of + {ok, Fd} -> + String = extract(File, Fd, StartTag, StopTag, searching), + file:close(Fd), + {ok, lists:flatten(String)}; + _ -> + docb_util:message(error, + "Include file ~s not found", [File]), + error + end. + +include_all(Fd) -> + case io:get_line(Fd, '') of + eof -> + []; + ListOfChars -> + lists:append(ListOfChars, include_all(Fd)) + end. + +extract(File, Fd, StartTag, StopTag, State) -> + Line=io:get_line(Fd, ''), + extract(File, Fd, StartTag, StopTag, State, Line). + +extract(File, _, _, _, _, eof) -> + docb_util:message(error, + "Premature end of file in include file ~p", + [File]), + []; +extract(File, Fd, StartTag, StopTag, searching, Line) -> + case regexp:match(Line, "^" ++ StartTag) of + {match, _Start, _Length} -> + extract(File, Fd, StartTag, StopTag, copying); + nomatch -> + extract(File, Fd, StartTag, StopTag, searching); + {error, _Error} -> + docb_util:message(error, "Bad syntax in ~s", [File]), + [] + end; +extract(File, Fd, StartTag, StopTag, copying, Line) -> + case regexp:match(Line, "^" ++ StopTag) of + {match, _Start, _Length} -> + []; + nomatch -> + [Line|extract(File, Fd, StartTag, StopTag, copying)]; + {error, _Error} -> + docb_util:message(error, "Bad syntax in ~s", [File]), + [] + end. + +%%---------------------------------------------------------------------- + +eval_str(Str) -> + case lib:eval_str(Str) of + {error, Report} -> + docb_util:message(error, + "ErlEval failed: ~s (~s)", [Str, Report]); + {ok, S} -> + io_lib:format("~p~n", [S]) + end. diff --git a/lib/docbuilder/src/docb_pretty_format.erl b/lib/docbuilder/src/docb_pretty_format.erl new file mode 100644 index 0000000000..0c4fb0507b --- /dev/null +++ b/lib/docbuilder/src/docb_pretty_format.erl @@ -0,0 +1,177 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_pretty_format). + +-export([term/1]). + +%% pretty_format:term(Term) -> PNF list of characters +%% +%% Note: this is usually used in expressions like: +%% io:format('~s\n', [pretty_format:term(Term)]). +%% +%% Uses the following simple heuristics: +%% +%% 1) Simple tuples are printed across the page. +%% (Simple means *all* the elements are "flat") +%% 2) The complex tuple {Arg1, Arg2, Arg3,....} is printed thus: +%% {Arg1, +%% Arg2, +%% Arg3, +%% ...} +%% 3) Lists are treated as for tuples. +%% 4) Lists of printable characters are treated as strings. +%% +%% This method seems to work reasonable well for {Tag, ...} type +%% data structures. +term(Term) -> + element(2, term(Term, 0)). + +%% pretty_format:term(Term, Indent} -> {Indent', Chars} +%% Format <Term> -- use <Indent> to indent the *next* line. +%% Note: Indent' is a new indentaion level (sometimes printing <Term> +%% the next line to need an "extra" indent!). +term([], Indent) -> + {Indent, [$[,$]]}; +term(L, Indent) when list(L) -> + case is_string(L) of + true -> + {Indent, io_lib:write_string(L)}; + false -> + case complex_list(L) of + true -> + write_complex_list(L, Indent); + false -> + write_simple_list(L, Indent) + end + end; +term(T, Indent) when tuple(T) -> + case complex_tuple(T) of + true -> + write_complex_tuple(T, Indent); + false -> + write_simple_tuple(T, Indent) + end; +term(A, Indent) -> + {Indent, io_lib:write(A)}. + +%% write_simple_list([H|T], Indent) -> {Indent', Chars} +write_simple_list([H|T], Indent) -> + {_, S1} = term(H, Indent), + {_, S2} = write_simple_list_tail(T, Indent), + {Indent, [$[,S1|S2]}. + +write_simple_list_tail([H|T], Indent) -> + {_, S1} = term(H, Indent), + {_, S2} = write_simple_list_tail(T, Indent), + {Indent, [$,,S1| S2]}; +write_simple_list_tail([], Indent) -> + {Indent, "]"}; +write_simple_list_tail(Other, Indent) -> + {_, S} = term(Other, Indent), + {Indent, [$|,S,$]]}. + +%% write_complex_list([H|T], Indent) -> {Indent', Chars} +write_complex_list([H|T], Indent) -> + {I1, S1} = term(H, Indent+1), + {_, S2} = write_complex_list_tail(T, I1), + {Indent, [$[,S1|S2]}. + +write_complex_list_tail([H|T], Indent) -> + {I1, S1} = term(H, Indent), + {_, S2} = write_complex_list_tail(T, I1), + {Indent, [$,,nl_indent(Indent),S1,S2]}; +write_complex_list_tail([], Indent) -> + {Indent, "]"}; +write_complex_list_tail(Other, Indent) -> + {_, S} = term(Other, Indent), + {Indent, [$|,S,$]]}. + +%% complex_list(List) -> true | false +%% Returns true if the list is complex otherwise false. +complex_list([]) -> + false; +complex_list([H|T]) when is_list(H) -> + case is_string(H) of + true -> + complex_list(T); + false -> + true + end; +complex_list([H|_]) when is_tuple(H) -> true; +complex_list(_) -> false. + +%% complex_tuple(Tuple) -> true | false +%% Returns true if the tuple is complex otherwise false. +complex_tuple(T) -> + complex_list(tuple_to_list(T)). + +%% write_simple_tuple(Tuple, Indent} -> {Indent', Chars} +write_simple_tuple({}, Indent) -> + {Indent, "{}"}; +write_simple_tuple(Tuple, Indent) -> + {_, S} = write_simple_tuple_args(tuple_to_list(Tuple), Indent), + {Indent, [${, S, $}]}. + +write_simple_tuple_args([X], Indent) -> + term(X, Indent); +write_simple_tuple_args([H|T], Indent) -> + {_, SH} = term(H, Indent), + {_, ST} = write_simple_tuple_args(T, Indent), + {Indent, [SH, $,, ST]}. + +%% write_complex_tuple(Tuple, Indent} -> {Indent', Chars} +write_complex_tuple(Tuple, Indent) -> + [H|T] = tuple_to_list(Tuple), + {I1, SH} = term(H, Indent+2), + {_, ST} = write_complex_tuple_args(T, I1), + {Indent, [${, SH, ST, $}]}. + +write_complex_tuple_args([X], Indent) -> + {_, S} = term(X, Indent), + {Indent, [$,, nl_indent(Indent), S]}; +write_complex_tuple_args([H|T], Indent) -> + {I1, SH} = term(H, Indent), + {_, ST} = write_complex_tuple_args(T, I1), + {Indent, [$,, nl_indent(Indent) , SH, ST]}; +write_complex_tuple_args([], Indent) -> + {Indent, []}. + +%% utilities + +nl_indent(I) when I >= 0 -> + ["\n"|indent(I)]; +nl_indent(_I) -> + [$ ]. + +indent(I) when I >= 8 -> + [$\t|indent(I-8)]; +indent(I) when I > 0 -> + [$ |indent(I-1)]; +indent(_) -> + []. + +is_string([9|T]) -> + is_string(T); +is_string([10|T]) -> + is_string(T); +is_string([H|T]) when H >31, H < 127 -> + is_string(T); +is_string([]) -> + true; +is_string(_) -> + false. diff --git a/lib/docbuilder/src/docb_tr_application2html.erl b/lib/docbuilder/src/docb_tr_application2html.erl new file mode 100644 index 0000000000..4084cfe6ba --- /dev/null +++ b/lib/docbuilder/src/docb_tr_application2html.erl @@ -0,0 +1,288 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_application2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +transform(File, {application, _Attrs, [Header|Rest]}, Opts0) -> + + %% Extract header data + Title = docb_html_util:extract_header_data(title, Header), + + case docb_util:an_option(kwicindex_only, Opts0) of + false -> + + %% Create the framing HTML document + OutFile = docb_util:outfile(File++"_frame", ".html", Opts0), + case file:open(OutFile, [write]) of + {ok, Fd} -> + io:format(Fd, +"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" +\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\"> +<!-- This document was generated using DocBuilder-" ++ docb_util:version() ++ " --> +<html> +<head> + <title>~s</title> + " ++ docb_util:html_snippet(head, Opts0) ++ " +</head> +<frameset cols=\"150, *\"> + <frame src=\"~s\" name=\"toc\"> + <frame src=\"~s\" name=\"document\"> + <noframes> + <body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" + vlink=\"#FF00FF\" alink=\"#FF0000\"> + <p>This documentation requires a browser that can handle frames</p> + </body> + </noframes> +</frameset> +</html> +", + [Title, + File++".html", File++"_first.html"]), + file:close(Fd) + end, + + %% Create the front HTML document + docb_main:transform(first, html, Opts0, File ++ "_first", + {first, [], [Header|Rest]}); + + true -> + ok + end, + + %% Extract files to include + Files = case Rest of + [{description, _, _}|NewRest] -> + lists:map(fun({include, [{_, _, F}], _}) ->filename:rootname(F) end, + NewRest); + [{include, _, _}|_NewRest] -> + lists:map(fun({include, [{_, _, F}], _}) -> filename:rootname(F) end, + Rest) + end, + + %% Concat all reference manuals into a *big* parse tree + ConcatTree = concat_files(Files, Opts0), + + %% Create the kwic index src file to be put in outdir + docb_main:transform(refs, kwic, Opts0, File, {refs,[],ConcatTree}), + + case docb_util:an_option(kwicindex_only, Opts0) of + false -> + + %% Create an index + docb_main:transform(index, html, Opts0, File ++ "_index", + {index, [], [Header|ConcatTree]}), + %% Create a cite dictionary + docb_main:transform(cite, html, Opts0, File ++ "_cite", + {cite, [], [Header|ConcatTree]}), + + %% Create a term dictionary + docb_main:transform(term, html, Opts0, File ++ "_term", + {term, [], [Header|ConcatTree]}), + + %% Transform each reference page + case docb_util:an_option(framework_only, Opts0) of + true -> + ok; + false -> + transform_refs(Files, + [dict,{part_application,File}|Opts0]) + end; + true -> + ok + end, + + %% Find all fascicules to be put in the top menu of the table of + %% contents + Ext = docb_util:lookup_option(src_type, Opts0), + Opts2 = + case filelib:is_regular("fascicules"++Ext) of + true -> + case docb_main:parse1("fascicules", Opts0) of + {ok, Parse} -> + FascData = get_fasc_data(Parse), + case lists:keysearch(File, 1, FascData) of + {value, {_, _, "YES", _}} -> + OrigFile = + docb_util:outfile(File++"_frame", + ".html", Opts0), + EntryFile = + docb_util:outfile("index", + ".html",Opts0), + docb_util:message(info, + "Copying ~s to ~s", + [OrigFile,EntryFile]), + file:copy(OrigFile, EntryFile); + _ -> + ok + end, + [{fascdata, FascData}| Opts0]; + errors -> + %% Do not bother + docb_util:message( + warning, + "fascicules~s could not be parsed," + " no index.html created", + [Ext]), + Opts0 + end; + false -> + %% do not bother + docb_util:message(warning, + "fascicules~s not found, " + "no index.html created", + [Ext]), + Opts0 + end, + + %% Create ToC parse tree + {{toc, [{"FILE", "CDATA", File}], [Header|make_toc(ConcatTree)]}, + Opts2}. + +concat_files(Files, Opts) -> + concat_files(Files, [], Opts). + +concat_files([File|Rest], Body, Opts) -> + case docb_main:parse1(File, Opts) of + {ok, Parse} -> + NewParse=expand([Parse], File), + %% Remove the reference manual header + [{Ref, [], [_Hdr| NewBody]}] = NewParse, + RefParse = [{Ref, [], NewBody}], + lists:append(Body, concat_files(Rest, RefParse, Opts)); + errors -> + errors + end; +concat_files([], Body, _Opts) -> + Body. + +expand([], _) -> + []; +expand([{pcdata, Attrs, More}|Rest], File) -> + [{pcdata, Attrs, More}|expand(Rest, File)]; +expand([{name, Attrs, More}|Rest], File) -> + [{name, [{"FILE", "CDATA", File}|Attrs], More}|expand(Rest, File)]; +expand([{module, Attrs, More}|Rest], File) -> + [{module, [{"FILE", "CDATA", File}|Attrs], More}|expand(Rest,File)]; +expand([{file, Attrs, More}|Rest], File) -> + [{file, [{"FILE", "CDATA", File}|Attrs], More}|expand(Rest, File)]; +expand([{app, Attrs, More}|Rest], File) -> + [{app, [{"FILE", "CDATA", File}|Attrs], More}|expand(Rest, File)]; +expand([{lib, Attrs, More}|Rest], File) -> + [{lib, [{"FILE", "CDATA", File}|Attrs], More}|expand(Rest, File)]; +expand([{com, Attrs, More}|Rest], File) -> + [{com, [{"FILE", "CDATA", File}|Attrs], More}|expand(Rest, File)]; +expand([{Tag, Attrs, More}|Rest], File) -> + [{Tag, Attrs, expand(More, File)}|expand(Rest, File)]. + +transform_refs([], _) -> + ok; +transform_refs([File|Rest], Opts) -> + Ext = docb_util:lookup_option(src_type, Opts), + docb_util:message(info, "Processing \"~s~s\"", [File, Ext]), + docb_main:process(File, Opts), + transform_refs(Rest, Opts). + +make_toc([]) -> + []; +make_toc([{pcdata, _Attrs, _More}|Rest]) -> + make_toc(Rest); +make_toc([{module, Attrs, More}|Rest]) -> + [{module, Attrs, More}|make_toc(Rest)]; +make_toc([{file, Attrs, More}|Rest]) -> + [{file, Attrs, More}|make_toc(Rest)]; +make_toc([{app, Attrs, More}|Rest]) -> + [{app, Attrs, More}|make_toc(Rest)]; +make_toc([{lib, Attrs, More}|Rest]) -> + [{lib, Attrs, More}|make_toc(Rest)]; +make_toc([{com, Attrs, More}|Rest]) -> + [{com, Attrs, More}|make_toc(Rest)]; +make_toc([{_Tag, _Attrs, More}|Rest]) -> + lists:append(make_toc(More), make_toc(Rest)). + +rule([module|_], {_, [File], _}) -> + {"<small><a target=\"document\" href=\"" ++ + docb_html_util:make_anchor_href(File) ++ "\">", + "</a></small><br/>\n"}; + +rule([file|_], {_, [File], _}) -> + {"<small><a target=\"document\" href=\"" ++ + docb_html_util:make_anchor_href(File) ++ "\">", + "</a></small><br/>\n"}; + +rule([app|_], {_, [File], _}) -> + {"<small><a target=\"document\" href=\"" ++ + docb_html_util:make_anchor_href(File) ++ "\">", + "</a></small><br/>\n"}; + +rule([lib|_], {_, [File], _}) -> + {"<small><a target=\"document\" href=\"" ++ + docb_html_util:make_anchor_href(File) ++ "\">", + "</a></small><br/>\n"}; + +rule([com|_], {_, [File], _}) -> + {"<small><a target=\"document\" href=\"" ++ + docb_html_util:make_anchor_href(File) ++ "\">", + "</a></small><br/>\n"}; + +rule([pcdata|_], {_, _, Data}) -> + {drop, docb_html_util:pcdata_to_html(Data)}; + +rule(_, _) -> + {drop, ""}. + +rule([toc|_], {_Depth, [File], [Header|_]}, Opts) -> + case docb_util:lookup_option(fascdata, Opts) of + false -> + {{docb_html_layout:application_toc_top( + docb_html_util:all_header_data(Header), + File, Opts), + docb_html_layout:part_toc_bot()}, Opts}; + FascData -> + HRefTexts = + lists:map( + fun({_File, HRef, _Entry, PCText}) -> + {HRef, docb_html_util:pcdata_to_html(PCText)} + end, + FascData), + {{docb_html_layout:application_toc_top( + docb_html_util:all_header_data(Header), + File, Opts, HRefTexts) ++ "\n", + docb_html_layout:part_toc_bot()}, Opts} + end. + +%% Returns: [{File, HRef, Entry, Text}]. +get_fasc_data({fascicules, _, Fascs}) -> + lists:map( + fun({fascicule, Atts, Trees}) -> + AVals = get_avals(Atts), + PCText = get_pc_text(Trees), + list_to_tuple(lists:append([AVals, [PCText]])) + end, + Fascs). + +get_avals(Atts) -> + lists:map(fun(Tuple) -> + element(3, Tuple) end, + Atts). + +get_pc_text([{pcdata, _, Text}]) -> + Text. diff --git a/lib/docbuilder/src/docb_tr_appref2html.erl b/lib/docbuilder/src/docb_tr_appref2html.erl new file mode 100644 index 0000000000..6b4cc0f815 --- /dev/null +++ b/lib/docbuilder/src/docb_tr_appref2html.erl @@ -0,0 +1,48 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_appref2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +%% Transform the parse tree. Header data is stored in an extra +%% argument to make life easier later on. +transform(_File, {appref,_,[Header|Rest]}, _Opts) -> + Data = [{[], [], docb_html_util:all_header_data(Header)}], + {appref, Data, [{header,[],[]}|Rest]}. + +rule([header|_],_) -> + {drop, ""}; + +rule([app|_],_) -> + {"\n<h3>APPLICATION</h3>\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule([appsummary|_],_) -> + {"\n<h3>APPLICATION SUMMARY</h3>\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule(TagHistory, TagBody) -> + docb_html_ref:rule(TagHistory,TagBody). + +rule([appref|_], {_,[Data],_}, Opts) -> + {{docb_html_layout:ref_top(Data, Opts), + docb_html_layout:ref_bot(Opts)}, Opts}; + +rule(TagHistory, TagBody, Opts) -> + docb_html_ref:rule(TagHistory, TagBody, Opts). diff --git a/lib/docbuilder/src/docb_tr_chapter2html.erl b/lib/docbuilder/src/docb_tr_chapter2html.erl new file mode 100644 index 0000000000..185cdc7cc3 --- /dev/null +++ b/lib/docbuilder/src/docb_tr_chapter2html.erl @@ -0,0 +1,59 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_chapter2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +transform(File, {chapter,_,[Header|Rest]}, Opts) -> + Data = [{[], [], docb_html_util:all_header_data(Header)}], + Tree = {chapter, Data, [{header,[],[]}|Rest]}, + ChapterLevel = + case docb_util:lookup_option(number, Opts) of + false -> none; + Value -> Value + end, + docb_html_util:number(Tree, ChapterLevel, File). + +rule([header|_], _) -> + {drop, ""}; + +rule([toc|_], {_,_,ToC}) -> + {drop, + "\n<h3>Table of Contents</h3>\n" ++ + docb_html_util:format_toc(ToC) ++ "\n"}; + +rule([section|_], _) -> + {"", ""}; + +rule([title|Rest], {_,[Number,_File], [{pcdata,_,Title}]}) -> + N = integer_to_list(docb_html_util:count_sections(Rest)+1), + {drop,"\n<h" ++ N ++ ">" ++ Number ++ " " ++ + docb_html_util:pcdata_to_html(Title) ++ "</h" ++ N ++ ">\n"}; + +rule(TagHistory, TagBody) -> + docb_html:rule(TagHistory, TagBody). + +rule([chapter|_], {_,[Data],_}, Opts) -> + {{docb_html_layout:chapter_top(Data, Opts), + docb_html_layout:chapter_bot(Opts)}, Opts}; + +rule(TagHistory, TagBody, Opts) -> + docb_html:rule(TagHistory, TagBody, Opts). diff --git a/lib/docbuilder/src/docb_tr_cite2html.erl b/lib/docbuilder/src/docb_tr_cite2html.erl new file mode 100644 index 0000000000..4ecbfa4e91 --- /dev/null +++ b/lib/docbuilder/src/docb_tr_cite2html.erl @@ -0,0 +1,136 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_cite2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +transform(_File, Tree, Opts) -> + purge(Tree, Opts). + +purge({Tag, Attrs, [Header|Body]}, Opts) -> + CiteList = case docb_util:lookup_option({defs,cite}, Opts) of + false -> []; + Value -> Value + end, + B1 = purge_body(Body, CiteList), + B2 = lists:ukeysort(2, B1), + {Tag, Attrs, [Header|B2]}. + +purge_body([], _) -> + []; +purge_body([{pcdata,_Attrs,_More}|Rest], CiteList) -> + purge_body(Rest, CiteList); +purge_body([{cite,[{"ID","CDATA",ID}],More}|Rest], CiteList) -> + case lists:keysearch(ID, 1, CiteList) of + false -> + [{cite, [{"NAME","CDATA",ID}, {"ID","CDATA",ID}], More}| + purge_body(Rest, CiteList)]; + {value, {ID, Name, _Description, _Responsible}} -> + [{cite, [{"NAME","CDATA",Name}, {"ID","CDATA",ID}], More}| + purge_body(Rest, CiteList)]; + {value, {ID, Name, _Description}} -> + [{cite, [{"NAME","CDATA",Name}, {"ID","CDATA",ID}], More}| + purge_body(Rest, CiteList)] + end; +purge_body([{_Tag,_Attrs,More}|Rest], CiteList) -> + lists:append(purge_body(More, CiteList), + purge_body(Rest, CiteList)). + +rule([header|_], _) -> + {drop, ""}; + +rule(_, _) -> + {drop, ""}. + +rule([cite|_], {_,[],[Header]}, Opts) -> + HeaderData = docb_html_util:all_header_data(Header), + {{docb_html_layout:chapter_top(HeaderData, Opts) ++ + "\n<center><h1>Bibliography</h1></center>\n", + docb_html_layout:chapter_bot(Opts)}, Opts}; + +rule([cite|_], {_,[],[Header|_]}, Opts) -> + HeaderData = docb_html_util:all_header_data(Header), + {{docb_html_layout:chapter_top(HeaderData, Opts) ++ + "\n<center><h1>Bibliography</h1></center>\n<dl>\n", + "\n</dl>\n" ++ docb_html_layout:chapter_bot(Opts)}, Opts}; + +rule([cite|_], {_,[Data],_}, Opts) -> + {{docb_html_layout:chapter_top(Data, Opts) ++ + "\n<center><h1>Bibliography</h1></center>\n<dl>\n", + "\n</dl>\n" ++ docb_html_layout:chapter_bot(Opts)}, Opts}; + +rule([cite|T], {A, B, [{citedef,C, + [{ctitle, [], [{pcdata,[],CTitle}]}, + {cauthor, [], [{pcdata,[],CAuthor}]}, + {chowpublished, [], + [{pcdata,[],Chowpublished}]}]}]}, Opts) -> + CiteDef = CTitle ++ " " ++ CAuthor ++ " " ++ Chowpublished, + rule([cite|T], {A,B,[{citedef,C,[{pcdata,[],CiteDef}]}]}, Opts); + +rule([cite|_], {_,[Name,ID], [{citedef,[],[{pcdata,[],Def}]}]}, Opts) -> + CiteList = + case docb_util:lookup_option({defs,cite}, Opts) of + false -> []; + Value -> Value + end, + case lists:keysearch(ID, 1, CiteList) of + false -> + {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++ + docb_html_util:pcdata_to_html(Def) ++ "\n</dd>\n"}, Opts}; + {value, {ID, Name, Description, _Responsible}} -> + docb_util:message(warning, + "Global cite ~s overriding local", [ID]), + {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ + docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, + Opts}; + {value, {ID, Name, Description}} -> + docb_util:message(warning, + "Global cite ~s overriding local", [ID]), + {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ + docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts} + end; + +rule([cite|_], {_,[Name,ID],_}, Opts) -> + CiteList = + case docb_util:lookup_option({defs,cite}, Opts) of + false -> []; + Value -> Value + end, + case lists:keysearch(ID, 1, CiteList) of + false -> + docb_util:message(error, + "The cite ~s has no definition", [ID]), + {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++ + "??" ++ "\n</dd>\n"}, Opts}; + {value, {ID, Name, Description, _Responsible}} -> + {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ + docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, + Opts}; + {value, {ID, Name, Description}} -> + {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ + docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts} + end. diff --git a/lib/docbuilder/src/docb_tr_comref2html.erl b/lib/docbuilder/src/docb_tr_comref2html.erl new file mode 100644 index 0000000000..25207dccb4 --- /dev/null +++ b/lib/docbuilder/src/docb_tr_comref2html.erl @@ -0,0 +1,46 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_comref2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +transform(_File, {comref,_,[Header|Rest]}, _Opts) -> + Data = [{[], [], docb_html_util:all_header_data(Header)}], + {comref, Data, [{header,[],[]}|Rest]}. + +rule([header|_],_) -> + {drop,""}; + +rule([com|_],_) -> + {"\n<h3>COMMAND</h3>\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule([comsummary|_],_) -> + {"\n<h3>COMMAND SUMMARY</h3>\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule(TagHistory, TagBody) -> + docb_html_ref:rule(TagHistory, TagBody). + +rule([comref|_], {_,[Data],_}, Opts) -> + {{docb_html_layout:ref_top(Data, Opts), + docb_html_layout:ref_bot(Opts)}, Opts}; + +rule(TagHistory, TagBody, Opts) -> + docb_html_ref:rule(TagHistory, TagBody, Opts). diff --git a/lib/docbuilder/src/docb_tr_cref2html.erl b/lib/docbuilder/src/docb_tr_cref2html.erl new file mode 100644 index 0000000000..06748b8c57 --- /dev/null +++ b/lib/docbuilder/src/docb_tr_cref2html.erl @@ -0,0 +1,61 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_cref2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +transform(_File, {cref,_,[Header|Rest]}, _Opts) -> + Data = [{[], [], docb_html_util:all_header_data(Header)}], + {cref, Data, [{header,[],[]}|Rest]}. + +rule([header|_],_) -> + {drop, ""}; + +rule([ret|_],_) -> + {"",""}; + +rule([nametext|_],_) -> + {" ",""}; + +rule([name|_], {_,_,[_Ret,{nametext,[],[{pcdata,[],Name}]}]}) -> + FName = lists:flatten(docb_html_util:pcdata_to_html(Name)), + TName = docb_util:trim(FName), + CAnchor = docb_util:fknidx(TName, "/"), + {"<A NAME=\"" ++ CAnchor ++ "\"><STRONG><CODE>", + "</CODE></STRONG></A><BR>\n"}; +rule([name|T], {I,As,[Ret,{pcdata,[],Name}]}) -> % For SGML DTD + rule([name|T], {I,As,[Ret,{nametext,[],[{pcdata,[],Name}]}]}); + +rule([lib|_],_) -> + {"\n<H3>C LIBRARY</H3>\n<DIV CLASS=REFBODY>\n","\n</DIV>\n"}; + +rule([libsummary|_],_) -> + {"\n<H3>C LIBRARY SUMMARY</H3>\n<DIV CLASS=REFBODY>\n","\n</DIV>\n"}; + +rule(TagHistory, TagBody) -> + docb_html_ref:rule(TagHistory, TagBody). + +rule([cref|_], {_,[Data],_}, Opts) -> + {{docb_html_layout:ref_top(Data, Opts), + docb_html_layout:ref_bot(Opts)}, Opts}; + +rule(TagHistory, TagBody, Opts) -> + docb_html_ref:rule(TagHistory, TagBody, Opts). diff --git a/lib/docbuilder/src/docb_tr_erlref2html.erl b/lib/docbuilder/src/docb_tr_erlref2html.erl new file mode 100644 index 0000000000..b264c46bce --- /dev/null +++ b/lib/docbuilder/src/docb_tr_erlref2html.erl @@ -0,0 +1,46 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_erlref2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +transform(_File, {erlref,_,[Header|Rest]}, _Opts) -> + Data = [{[], [], docb_html_util:all_header_data(Header)}], + {erlref, Data, [{header,[],[]}|Rest]}. + +rule([header|_],_) -> + {drop, ""}; + +rule([module|_],_) -> + {"\n<h3>MODULE</h3>\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule([modulesummary|_],_) -> + {"\n<h3>MODULE SUMMARY</h3>\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule(TagHistory, TagBody) -> + docb_html_ref:rule(TagHistory, TagBody). + +rule([erlref|_], {_,[Data],_}, Opts) -> + {{docb_html_layout:ref_top(Data, Opts), + docb_html_layout:ref_bot(Opts)}, Opts}; + +rule(TagHistory, TagBody, Opts) -> + docb_html_ref:rule(TagHistory, TagBody, Opts). diff --git a/lib/docbuilder/src/docb_tr_fileref2html.erl b/lib/docbuilder/src/docb_tr_fileref2html.erl new file mode 100644 index 0000000000..60280543a8 --- /dev/null +++ b/lib/docbuilder/src/docb_tr_fileref2html.erl @@ -0,0 +1,46 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_fileref2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +transform(_File, {fileref,_,[Header|Rest]}, _Opts) -> + Data = [{[], [], docb_html_util:all_header_data(Header)}], + {fileref, Data, [{header,[],[]}|Rest]}. + +rule([header|_],_) -> + {drop, ""}; + +rule([file|_],_) -> + {"\n<h3>FILE</h3>\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule([filesummary|_],_) -> + {"\n<h3>FILE SUMMARY</h3>\n<div class=\"REFBODY\">\n","\n</div>\n"}; + +rule(TagHistory, TagBody) -> + docb_html_ref:rule(TagHistory, TagBody). + +rule([fileref|_], {_,[Data],_}, Opts) -> + {{docb_html_layout:ref_top(Data, Opts), + docb_html_layout:ref_bot(Opts)}, Opts}; + +rule(TagHistory, TagBody, Opts) -> + docb_html_ref:rule(TagHistory, TagBody, Opts). diff --git a/lib/docbuilder/src/docb_tr_first2html.erl b/lib/docbuilder/src/docb_tr_first2html.erl new file mode 100644 index 0000000000..e9ecbe73cb --- /dev/null +++ b/lib/docbuilder/src/docb_tr_first2html.erl @@ -0,0 +1,46 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_first2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +transform(_File, Tree, _Opts) -> + Tree. + +rule([header|_], _) -> + {drop, ""}; + +rule([description|_], _) -> + {"", ""}; + +rule([include|_], _) -> + {drop, ""}; + +rule(TagHistory, TagBody) -> + docb_html:rule(TagHistory, TagBody). + +rule([first|_], {_,[],[Header|_]}, Opts) -> + HeaderData = docb_html_util:all_header_data(Header), + {{docb_html_layout:first_top(HeaderData, Opts), + docb_html_layout:first_bot(Opts)}, Opts}; + +rule(TagHistory, TagBody, Opts) -> + docb_html:rule(TagHistory, TagBody, Opts). diff --git a/lib/docbuilder/src/docb_tr_index2html.erl b/lib/docbuilder/src/docb_tr_index2html.erl new file mode 100644 index 0000000000..bbf419f3ef --- /dev/null +++ b/lib/docbuilder/src/docb_tr_index2html.erl @@ -0,0 +1,197 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_index2html). + +-export([extension/0, transform/3, rule/2]). + +extension() -> + ".html". + +transform(_File0, {index, Attrs, [Header| Trees0]}, _Opts) -> + Trees1 = prune_flat(Trees0, false), + %% + %% Now each element of Trees1 is a tree with tag `name' and + %% attribute `File', and with one `pcdata' subtree containing the + %% name `Func' of the function. We extract `File' and `Func', and + %% create new trees. + %% + %% `File' is attribute CDATA (from an <include file=...>), and + %% `Func' is PCDATA. + %% + FileFuncs = + [{File, RefType, Func} || + {name, [{_, _, File}, {_, _, RefType}|_], + [{pcdata, [], Func}]} + <- Trees1], + Trees2 = new_trees(FileFuncs), + {index, Attrs, [Header| Trees2]}. + +%% Remove all elements except those with tag equal to `name'. +%% Within `name' remove all elements except those equal to `pcdata'. +%% Add attribute `filetype' to `name'. +%% +%% Refs: appref, comref, cref, erlref, fileref +prune_flat([{appref, _Attrs, More}| Rest], _) -> + RefType = appref, + lists:append(prune_flat(More, RefType), prune_flat(Rest, RefType)); +prune_flat([{comref, _Attrs, More}| Rest], _) -> + RefType = comref, + lists:append(prune_flat(More, RefType), prune_flat(Rest, RefType)); +prune_flat([{cref, _Attrs, More}| Rest], _) -> + RefType = cref, + lists:append(prune_flat(More, RefType), prune_flat(Rest, RefType)); +prune_flat([{erlref, _Attrs, More}| Rest], _) -> + RefType = erlref, + lists:append(prune_flat(More, RefType), prune_flat(Rest, RefType)); +prune_flat([{fileref, _Attrs, More}| Rest], _) -> + RefType = fileref, + lists:append(prune_flat(More, RefType), prune_flat(Rest, RefType)); +prune_flat([{name, [Attr0|Attrs0], More}| Rest], RefType) -> + Attrs = [Attr0, {"FILETYPE", "CDATA", RefType} | + Attrs0], + [{name, Attrs, keep_pcdata(More)}| prune_flat(Rest, RefType)]; +prune_flat([{pcdata, _, _}| Rest], RefType) -> % special case + prune_flat(Rest, RefType); +prune_flat([{_Tag, _Attrs, More}| Rest], RefType) -> + lists:append(prune_flat(More, RefType), prune_flat(Rest, RefType)); +prune_flat([], _) -> + []. + +keep_pcdata(Trees) -> + lists:filter(fun({pcdata, _, _}) -> true; + (_) -> false + end, Trees). + +new_trees(FileFuncs) -> + Files0 = [{File, RefType} || {File, RefType, _} <- FileFuncs], + Files1 = lists:usort(Files0), + FileEntries = [{reffile, File, RefType, + [Fu || {Fi, _, Fu} <- FileFuncs, Fi == File]} + || {File, RefType} <- Files1], + FuncEntries = [{func, Func, RefType, [File]} + || {File, RefType, Func} <- FileFuncs], + Entries = FileEntries ++ FuncEntries, + SortedEntries = sort_entries(Entries), + %% + %% We create a tree according to the following "dtd": + %% + %% element index (reffile | funcdef)* + %% element reffile (funcdef2)* + %% attribute reffile filename CDATA + %% attribute reffile filetype CDATA + %% element funcdef2 PCDATA + %% attribute funcdef2 filename CDATA + %% attribute funcdef2 filetype CDATA + %% element funcdef PCDATA + %% attribute funcdef filename CDATA + %% attribute funcdef filetype CDATA + %% + %% For example: + %% <index> + %% <reffile filename="mymod" filetype="erlref"> + %% <funcdef2 filename="mymod" filetype="erlref">myfunca(A)</> + %% <funcdef2 filename="mymod" filetype="erlref">myfuncb(A, B)</> + %% </> + %% <funcdef filename="mymod" filetype="erlref">myfunca(A)</> + %% <funcdef filename="mymod" filetype="erlref">myfuncb(A, B)</> + %% </> + lists:flatmap( + fun({reffile, File, RefType, Funcs}) -> + %% A reffile tree + [{reffile, [{"FILENAME", "CDATA", File}, + {"FILETYPE", "CDATA", RefType}], + [{funcdef2, [{"FILENAME", "CDATA", File}, + {"FILETYPE", "CDATA", RefType}], + [{pcdata, [], Func}]} || Func <- Funcs]}]; + ({func, Func, RefType, [File]}) -> + %% A func tree + [{funcdef, [{"FILENAME", "CDATA", File}, + {"FILETYPE", "CDATA", RefType}], + [{pcdata, [], Func}]}] + end, SortedEntries). + +%% Sorting of entries +%% +%% The sorting is based on how names of files and functions are +%% presented (in a browser). +%% Requires conversion to "function/2" etc. +%% +sort_entries(Entries) -> + ExpEntries = + lists:map( + fun({reffile, File, RefType, Funcs}) -> + HFile = filename_sort_order(File), + HFuncs = [{funcdef_sort_order(Fu, RefType), Fu} || Fu <- Funcs], + {reffile, HFile, File, RefType, lists:sort(HFuncs)}; + ({func, Func, RefType, [File]}) -> + HFunc = funcdef_sort_order(Func, RefType), + HFile = filename_sort_order(File), + {func, HFunc, Func, RefType, [{HFile, File}]} + end, Entries), + SortedExpEntries = lists:keysort(2, ExpEntries), + lists:map( + fun({Tag, _HName, Name, RefType, Vals}) -> + NVals = lists:map(fun({_HVal, Val}) -> Val end, Vals), + {Tag, Name, RefType, NVals} + end, SortedExpEntries). + +rule([index| _], _) -> + {docb_html_layout:index_top("") ++ + "<dl>\n", + "</dl>\n" ++ docb_html_layout:index_bot()}; + +rule([header| _], _) -> + {drop, ""}; + +rule([reffile| _], {_, [File, _RefType|_], _}) -> + CFile = docb_html_util:attribute_cdata_to_html(File), + {"<dt><em>" ++ CFile ++ "</em></dt>\n", ""}; + +rule([funcdef2| _], {_, [File, RefType|_], [{pcdata, [], FuncDef}]}) -> + FFuncDef = lists:flatten(docb_html_util:pcdata_to_html(FuncDef)), + TFuncDef = docb_util:trim(FFuncDef), + ShortFuncDef = docb_html_util:make_funcdef_short(TFuncDef, RefType), + HRef = + docb_html_util:make_anchor_href_short(File, TFuncDef, RefType), + {drop, + "<dd><a href=\"" ++ HRef ++ "\"><code>" ++ + ShortFuncDef ++ "</code></a></dd>\n"}; + +rule([funcdef| _], {_, [File, RefType|_], [{pcdata, [], FuncDef}]}) -> + FFuncDef = lists:flatten(docb_html_util:pcdata_to_html(FuncDef)), + TFuncDef = docb_util:trim(FFuncDef), + ShortFuncDef = docb_html_util:make_funcdef_short(TFuncDef, RefType), + HRef = + docb_html_util:make_anchor_href_short(File, TFuncDef, RefType), + CFile = docb_html_util:attribute_cdata_to_html(File), + {drop, + "<dt><code>" ++ ShortFuncDef ++ "</code></dt>\n" + "<dd><a href=\"" ++ HRef ++ "\"><em>" ++ + CFile ++ "</em></a></dd>\n"}; + +rule(_, _) -> + {drop, ""}. + +filename_sort_order(File) -> + docb_html_util:html_latin1_sort_order( + lists:flatten( + docb_html_util:attribute_cdata_to_html(string:strip(File)))). + +funcdef_sort_order(FuncDef, RefType) -> + docb_html_util:html_latin1_sort_order( + docb_html_util:make_anchor_name_short(FuncDef, RefType)). diff --git a/lib/docbuilder/src/docb_tr_part2html.erl b/lib/docbuilder/src/docb_tr_part2html.erl new file mode 100644 index 0000000000..dd44c4a8df --- /dev/null +++ b/lib/docbuilder/src/docb_tr_part2html.erl @@ -0,0 +1,240 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_part2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +transform(File, {part, _Attrs, [Header| Rest]}, Opts0) -> + + %% Extract header data + Title = docb_html_util:extract_header_data(title, Header), + + %% Create the framing HTML document + OutFile = docb_util:outfile(File ++ "_frame", ".html", Opts0), + case file:open(OutFile, [write]) of + {ok, Frame} -> + io:format(Frame, +"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" + \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\"> +<!-- This document was generated using DocBuilder-" ++ docb_util:version() ++ " --> +<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"> +<head> + <title>~s</title> + " ++ docb_util:html_snippet(head, Opts0) ++ " +</head> +<frameset cols=\"200, *\"> + <frame src=\"~s\" name=\"toc\"/> + <frame src=\"~s\" name=\"document\"/> + <noframes> + <body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" + vlink=\"#FF00FF\" alink=\"#FF0000\"> + <p>This documentation requires a browser that can handle frames</p> + </body> + </noframes> +</frameset> +</html> +", + [Title, File ++ ".html", File ++ "_first.html"]), + file:close(Frame) + end, + + %% Create the front HTML document + docb_main:transform(first, html, Opts0, File ++ "_first", + {first, [], [Header| Rest]}), + + %% Extract files to include + Files = + case Rest of + [{description, _, _}| NewRest] -> + lists:map(fun({include, [{_, _, F}], _}) -> filename:rootname(F) end, + NewRest); + [{include, _, _}| _NewRest] -> + lists:map(fun({include, [{_, _, F}], _}) -> filename:rootname(F) end, Rest) + end, + + %% Concat all chapters into a *big* parse tree + %% Also transform them to HTML + TransformP = not docb_util:an_option(framework_only, Opts0), + TOpts = [dict, {part_application,File}], + ConcatTree = concat_files(Files, Opts0, TransformP, TOpts), + + %% Create a cites dictionary + docb_main:transform(cite, html, Opts0, File ++ "_cite", + {cite, [], [Header| ConcatTree]}), + + %% Create a terms dictionary + docb_main:transform(term, html, Opts0, File ++ "_term", + {term, [], [Header| ConcatTree]}), + + %% Find all fascicules to be put in the top menu of the table of + %% contents + Ext = docb_util:lookup_option(src_type, Opts0), + Opts2 = + case filelib:is_regular("fascicules"++Ext) of + true -> + case docb_main:parse1("fascicules", Opts0) of + {ok, Parse} -> + FascData = get_fasc_data(Parse), + case lists:keysearch(File, 1, FascData) of + {value, {_, _, "YES", _}} -> + OrigFile = + docb_util:outfile(File++"_frame", + ".html", Opts0), + EntryFile = + docb_util:outfile("index", + ".html", Opts0), + docb_util:message(info, + "Copying ~s to ~s", + [OrigFile,EntryFile]), + file:copy(OrigFile, EntryFile); + _ -> + ok + end, + [{fascdata, FascData}| Opts0]; + errors -> + %% do not bother + docb_util:message( + warning, + "fascicules~s could not be parsed," + " no index.html created~n", [Ext]), + Opts0 + end; + _ -> + %% do not bother + docb_util:message(warning, + "fascicules~s not found, " + "no index.html created~n", + [Ext]), + Opts0 + end, + + %% Create ToC parse tree + {{toc, [{"FILE", "CDATA", File}], [Header| ConcatTree]}, Opts2}. + +concat_files(Files, Opts, TransformP, TOpts) -> + Ext = docb_util:lookup_option(src_type, Opts), + concat_files(Files, [], 1, Opts, TransformP, TOpts, Ext). + +concat_files([File | Rest], Body, ChLevel, Opts, TP, TOpts, Ext) -> + case docb_main:parse1(File, Opts) of + {ok, Parse} -> + {TopTag, Attrs, [Header = {header, _, HeaderContents} | More]} = Parse, + {value,{title,_,Title}} = lists:keysearch(title,1,HeaderContents), + NewMore = [{section, [], [{title, [], Title}| More]}], + NewParse = {TopTag, Attrs, [Header| NewMore]}, + if + TP -> + docb_util:message(info, + "Processing \"~s~s\"", + [File, Ext]), + Opts2 = + [html, {number,integer_to_list(ChLevel)}] ++ + TOpts ++ Opts, + docb_main:transform(TopTag, html, Opts2, File, + NewParse); + true -> ignore + end, + NumberTree = + docb_html_util:number(NewParse, + integer_to_list(ChLevel), File), + {_, [], [_| NewBody]} = NumberTree, + lists:append(Body, + concat_files(Rest, NewBody, ChLevel+1, Opts, + TP, TOpts, Ext)); + errors -> + throw({error,"Parse error when building chapter "++File}) + end; +concat_files([], Body, _ChLevel, _Opts, _TP, _TOpts, _Ext) -> + Body. + +rule([section| _], _) -> + {"", ""}; + +rule(_, _) -> + {drop, ""}. + +rule([toc| _], {_Depth, [File], [Header| _]}, Opts) -> + case docb_util:lookup_option(fascdata, Opts) of + false -> + {{docb_html_layout:part_toc_top( + docb_html_util:all_header_data(Header), File, Opts), + docb_html_layout:part_toc_bot()}, Opts}; + FascData -> + HRefTexts = + lists:map( + fun({_File, HRef, _Entry, PCText}) -> + {HRef, docb_html_util:pcdata_to_html(PCText)} + end, + FascData), + {{docb_html_layout:part_toc_top( + docb_html_util:all_header_data(Header), + File, Opts, HRefTexts), + docb_html_layout:part_toc_bot()}, Opts} + end; + +rule([title| Rest], {_, [Number, File], [{pcdata, _, Title}]}, Opts) -> + N = docb_html_util:count_sections(Rest), + OutFile = docb_html_util:make_anchor_href(File), + if + N == 1 -> + {{drop, + "<hr/>\n<small>" ++ + Number ++ + " <a target=\"document\" href=\"" ++ OutFile ++ "#" ++ + Number ++ "\">" ++ + docb_html_util:pcdata_to_html(Title) ++ + "</a></small><br/>\n"}, + Opts}; + N < 3 -> + {{drop, + "<small>" ++ + Number ++ + " <a target=\"document\" href=\"" ++ OutFile ++ "#" ++ + Number ++ "\">" ++ + docb_html_util:pcdata_to_html(Title) ++ + "</a></small><br/>\n"}, + Opts}; + true -> + {{drop, ""}, Opts} + end. + +%% Parsed fascicules: +%% {fascicules,[], +%% [{fascicule, [{"FILE","CDATA","refman"}, +%% {"HREF","CDATA","refman_frame.html"}, +%% {"ENTRY","TOKEN","YES"}], +%% [{pcdata, [], "" Reference Manual\\n \n"}]}, +%% Returns: [{File, HRef, Entry, Text}]. +get_fasc_data({fascicules, _, Fascs}) -> + lists:map( + fun({fascicule, Atts, Trees}) -> + AVals = get_avals(Atts), + PCText = get_pc_text(Trees), + list_to_tuple(lists:append([AVals, [PCText]])) end, + Fascs). + +get_avals(Atts) -> + lists:map(fun(Tuple) -> + element(3, Tuple) end, + Atts). + +get_pc_text([{pcdata, _, Text}]) -> + Text. diff --git a/lib/docbuilder/src/docb_tr_refs2kwic.erl b/lib/docbuilder/src/docb_tr_refs2kwic.erl new file mode 100644 index 0000000000..dc60c329fc --- /dev/null +++ b/lib/docbuilder/src/docb_tr_refs2kwic.erl @@ -0,0 +1,156 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_refs2kwic). + +-export([extension/0, transform/3, rule/2]). + +%% Output parts of a parsetree that contains a series of reference +%% manual pages. The tags considered are: module, file, app, com and lib +%% (and their corresponding *summary tags), and name, fsummary, c, em, +%% ret and pcdata. + +extension() -> + ".kwc". + +transform(File, Tree, Opts) -> + {refs, [], Trees} = Tree, + FileTree = {srcfile, [], [{pcdata, [], File}]}, + AppName = docb_util:lookup_option(name, Opts, "unknown"), + AppTree = {appname, [], [{pcdata, [], AppName}]}, + Vsn = docb_util:lookup_option(vsn, Opts, "unknown"), + VsnTree = {appvsn, [], [{pcdata, [], Vsn}]}, + NewTree = {refs, [], [FileTree, AppTree, VsnTree| Trees]}, + {NewTree, Opts}. + +rule([refs|_],_) -> + {"%% Automatically generated. Do not edit.\n", ""}; + +rule([srcfile| _], _) -> + {"{srcfile, \"", "\"}.\n"}; + +rule([appname| _], _) -> + {"{appname, \"", "\"}.\n"}; + +rule([appvsn| _], _) -> + {"{appvsn, \"", "\"}.\n"}; + +rule([erlref|_ ], _) -> + {"", ""}; + +rule([fileref|_ ], _) -> + {"", ""}; + +rule([appref|_ ], _) -> + {"", ""}; + +rule([comref|_ ], _) -> + {"", ""}; + +rule([cref|_ ], _) -> + {"", ""}; + +rule([module| _], {_, [File], _}) -> + {drop, "{module, \"" ++ File ++ "\"}.\n"}; + +rule([file|_], {_, [File], _}) -> + {drop, "{file, \"" ++ File ++ "\"}.\n"}; + +rule([app|_], {_, [File], _}) -> + {drop, "{app, \"" ++ File ++ "\"}.\n"}; + +rule([com|_], {_, [File], _}) -> + {drop, "{com, \"" ++ File ++ "\"}.\n"}; + +rule([lib|_], {_, [File], _}) -> + {drop, "{lib, \"" ++ File ++ "\"}.\n"}; + +rule([modulesummary|_], _) -> + {"{modulesummary, \"", "\"}.\n"}; + +rule([filesummary|_], _) -> + {"{filesummary, \"", "\"}.\n"}; + +rule([appsummary|_], _) -> + {"{appsummary, \"", "\"}.\n"}; + +rule([comsummary|_], _) -> + {"{comsummary, \"", "\"}.\n"}; + +rule([libsummary|_], _) -> + {"{libsummary, \"", "\"}.\n"}; + +rule([funcs|_ ], _) -> + {"", ""}; + +rule([func|_ ], _) -> + {"", ""}; + +rule([name,func,funcs,cref|_], {_,[_File], [_Ret,{pcdata,[],Name}]}) -> + FName = lists:flatten(docb_html_util:pcdata_to_html(Name)), + TName = docb_util:trim(FName), + case catch docb_util:fknidx(TName, "/") of + {'EXIT',_} -> + {drop, ["{name, \"", escq(TName), "\"}.\n"]}; + FuncName -> + {drop, ["{name, \"", escq(FuncName), "\"}.\n"]} + end; + +rule([name,func,funcs,erlref|_], {_,[_File], [{pcdata,[],Name}]}) -> + FName = lists:flatten(docb_html_util:pcdata_to_html(Name)), + TName = docb_util:trim(FName), + case catch docb_util:fknidx(TName, "/") of + {'EXIT',_} -> + {drop, ["{name, \"", escq(TName), "\"}.\n"]}; + FuncName -> + {drop, ["{name, \"", escq(FuncName), "\"}.\n"]} + end; + +rule([name, func| _], {_, [_File], [{pcdata, [], Name}]}) -> + FName = lists:flatten(docb_html_util:pcdata_to_html(Name)), + TName = docb_util:trim(FName), + Cmd = case string:tokens(TName, " ") of + [Cmd0| _] -> + Cmd0; + _ -> + TName + end, + {drop, ["{name, \"", escq(Cmd), "\"}.\n"]}; + +rule([fsummary| _], _) -> + {"{fsummary, \"", "\"}.\n"}; + +rule([c, fsummary|_], _) -> + {"", ""}; + +rule([em, fsummary|_], _) -> + {"", ""}; + +rule([pcdata| _], {_, _, Data}) -> + FData = lists:flatten(docb_html_util:pcdata_to_html(Data)), + Out = lists:map(fun($\n) -> $ ; (C) -> C end, FData), + {drop, escq(Out)}; + +rule(_, _) -> + {drop, ""}. + +escq(Cs) -> + lists:flatmap(fun($") -> + "\\\""; + (C) -> [C] + end, + Cs). diff --git a/lib/docbuilder/src/docb_tr_report2html.erl b/lib/docbuilder/src/docb_tr_report2html.erl new file mode 100644 index 0000000000..3386ed972a --- /dev/null +++ b/lib/docbuilder/src/docb_tr_report2html.erl @@ -0,0 +1,70 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_report2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +%% +%% File extension +%% + +extension() -> + ".html". + +transform(File, {report,_,[Header|Rest]}, Opts) -> + Data = [{[], [], docb_html_util:all_header_data(Header)}], + Tree = {report, Data, [{header,[],[]}|Rest]}, + ChapterLevel = case docb_util:lookup_option(number, Opts) of + false -> none; + Value -> Value + end, + NumberTree = docb_html_util:number(Tree, ChapterLevel, File), + options(NumberTree, Opts). + +options(Tree, []) -> + Tree; +options(Tree, [_|Rest]) -> + options(Tree, Rest). + +rule([header|_], _) -> + {drop, ""}; + +rule([toc|_], {_,_,ToC}) -> + {drop, "\n<h3>Table of Contents</h3>\n" ++ + docb_html_util:format_toc(ToC) ++ "\n"}; + +rule([section|_], _) -> + {"", ""}; + +rule([title|Rest], {_,[Number,_File], [{pcdata,_,Title}]}) -> + N = integer_to_list(docb_html_util:count_sections(Rest)+1), + {drop, "\n<h" ++ N ++ ">" ++ Number ++ " " ++ + docb_html_util:pcdata_to_html(Title) ++ "</h" ++ N ++ ">\n"}; + +rule([erlinclude|_], {_,[File,Tag],_}) -> + docb_html_util:erl_include(File, Tag); + +rule(TagHistory, TagBody) -> + docb_html:rule(TagHistory, TagBody). + +rule([report|_], {_,[Data],_}, Opts) -> + {{docb_html_layout:report_top(Data, Opts), + docb_html_layout:report_bot(Opts)}, Opts}; + +rule(TagHistory, TagBody, Opts) -> + docb_html:rule(TagHistory, TagBody, Opts). diff --git a/lib/docbuilder/src/docb_tr_term2html.erl b/lib/docbuilder/src/docb_tr_term2html.erl new file mode 100644 index 0000000000..0a993cebb1 --- /dev/null +++ b/lib/docbuilder/src/docb_tr_term2html.erl @@ -0,0 +1,126 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_tr_term2html). + +-export([extension/0, transform/3, rule/2, rule/3]). + +extension() -> + ".html". + +transform(_File, Tree, Opts) -> + purge(Tree, Opts). + +purge({Tag, Attrs, [Header|Body]}, Opts) -> + TermList = case docb_util:lookup_option({defs,term}, Opts) of + false -> []; + Value -> Value + end, + B1 = purge_body(Body, TermList), + B2 = lists:ukeysort(2, B1), + {Tag, Attrs, [Header|B2]}. + +purge_body([], _) -> + []; +purge_body([{pcdata,_Attrs,_More}|Rest], TermList) -> + purge_body(Rest, TermList); +purge_body([{term,[{"ID","CDATA",ID}],More}|Rest], TermList) -> + case lists:keysearch(ID, 1, TermList) of + false -> + [{term,[{"NAME","CDATA",ID},{"ID","CDATA",ID}],More}| + purge_body(Rest, TermList)]; + {value, {ID, Name, _Description, _Responsible}} -> + [{term,[{"NAME","CDATA",Name},{"ID","CDATA",ID}],More}| + purge_body(Rest, TermList)]; + {value, {ID, Name, _Description}} -> + [{term,[{"NAME","CDATA",Name},{"ID","CDATA",ID}],More}| + purge_body(Rest, TermList)] + end; +purge_body([{_Tag,_Attrs,More}|Rest], TermList) -> + lists:append(purge_body(More, TermList), + purge_body(Rest, TermList)). + +rule([header|_], _) -> + {drop, ""}; + +rule(_, _) -> + {drop, ""}. + +rule([term|_], {_,[],[Header]}, Opts) -> + {{docb_html_layout:chapter_top( + docb_html_util:all_header_data(Header), Opts) ++ + "\n<center><h1>Glossary</h1></center>\n", + docb_html_layout:chapter_bot(Opts)}, Opts}; + +rule([term|_], {_,[],[Header|_]},Opts) -> + {{docb_html_layout:chapter_top( + docb_html_util:all_header_data(Header), Opts) ++ + "\n<center><h1>Glossary</h1></center>\n<dl>\n", + "\n</dl>\n" ++ docb_html_layout:chapter_bot(Opts)}, Opts}; + +rule([term|_], {_,[Data],_}, Opts) -> + {{docb_html_layout:chapter_top(Data, Opts) ++ + "\n<center><h1>Bibliography</h1></center>\n<dl>\n", + "\n</dl>\n" ++ docb_html_layout:chapter_bot(Opts)}, Opts}; + +rule([term|_], {_,[Name,ID],[{termdef,[],[{pcdata,[],Def}]}]}, Opts) -> + TermList = case docb_util:lookup_option({defs,term}, Opts) of + false -> []; + Value -> Value + end, + case lists:keysearch(ID, 1, TermList) of + false -> + {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ ID ++ "</strong></a>\n</dt>\n<dd>" ++ + docb_html_util:pcdata_to_html(Def) ++ "\n</dd>\n"}, Opts}; + {value, {ID, Name, Description, _Responsible}} -> + docb_util:message(warning, + "Global term ~s overriding local", [ID]), + {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ + docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, + Opts}; + {value, {ID, Name, Description}} -> + docb_util:message(warning, + "Global term ~s overriding local", [ID]), + {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ + docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts} + end; + +rule([term|_], {_,[Name,ID],_}, Opts) -> + TermList = case docb_util:lookup_option({defs,term}, Opts) of + false -> []; + Value -> Value + end, + case lists:keysearch(ID, 1, TermList) of + false -> + docb_util:message(error, + "The term ~s has no definition", [ID]), + {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++ + "??" ++ "\n</dd>\n"}, Opts}; + {value, {ID, Name, Description, _Responsible}} -> + {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ + docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, + Opts}; + {value, {ID, Name, Description}} -> + {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ + "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ + docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts} + end. diff --git a/lib/docbuilder/src/docb_transform.erl b/lib/docbuilder/src/docb_transform.erl new file mode 100644 index 0000000000..a432038adf --- /dev/null +++ b/lib/docbuilder/src/docb_transform.erl @@ -0,0 +1,161 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_transform). + +-export([file/1, file/2]). + +%% file(File) -> ok | {error, Reason} +%% file(File, Opts) -> ok | {error, Reason} +%% File = string(), file name with or without ".xml" extension +%% Opts = [Opt] +%% Reason = badfile | {badopt, Term} +file(File0) -> + file(File0, []). +file(File0, RawOpts) -> + File = filename:rootname(File0), % without extension + Ext = case filename:extension(File0) of + ".xml" -> ".xml"; + ".sgml" -> ".sgml"; + "" -> + %% If the file is given without extension, we try to + %% infer if the source file is XML or SGML. + %% SGML is supported *internally within OTP* for + %% backwards compatibility reasons. + case filelib:is_regular(File++".xml") of + true -> ".xml"; + false -> ".sgml" + end; + _Ext0 -> % this is probably an error... + ".xml" + end, + case filelib:is_regular(File++Ext) of + true -> + case parse(RawOpts) of + {ok, Opts0} -> + {ok, Cwd} = file:get_cwd(), + Opts = [{src_type,Ext}, + {src_dir,Cwd}, + {src_file,File}, + {{local_defs,term},[]}, + {{local_defs,cite},[]} | Opts0], + case docb_main:process(File, Opts) of + errors -> error; + ok -> ok + end; + Error -> % {error, {badopt,Term}} + Error + end; + false -> + {error, badfile} + end. + +parse(RawOpts) -> + parse(RawOpts, []). + +%% Officially supported options + +parse([{html_mod,Module} | RawOpts], Opts) when is_atom(Module) -> + parse(RawOpts, [{html_mod,Module} | Opts]); +parse([{outdir,Dir} | RawOpts], Opts) when is_list(Dir) -> + parse(RawOpts, [{outdir,Dir} | Opts]); +parse([{number,N} | RawOpts], Opts) when is_integer(N) -> + parse(RawOpts, [{number,integer_to_list(N)} | Opts]); +parse([{number,Nstr} | RawOpts], Opts) -> % list when called from script + parse(RawOpts, [{number,Nstr} | Opts]); +parse([{ptype,Type} | RawOpts], Opts) when Type==unix; + Type==windows -> + parse(RawOpts, [{ptype,atom_to_list(Type)} | Opts]); +parse([{ptype,Type} | RawOpts], Opts) -> % list when called from script + parse(RawOpts, [{ptype,Type} | Opts]); +parse([silent | RawOpts], Opts) -> + put(option_silent, true), + parse(RawOpts, [silent | Opts]); +parse([{top,Index} | RawOpts], Opts) when is_list(Index) -> + parse(RawOpts, [{top,Index} | Opts]); +parse([{vsn,Vsn} | RawOpts], Opts) when is_list(Vsn) -> + parse(RawOpts, [{vsn,Vsn} | Opts]); + +parse([{term_defs,File} | RawOpts], Opts) when is_list(File) -> + Opts2 = get_defs(term, File, Opts), + parse(RawOpts, Opts2); +parse([{cite_defs,File} | RawOpts], Opts) when is_list(File) -> + Opts2 = get_defs(cite, File, Opts), + parse(RawOpts, Opts2); + +%% OTP internal options (SGML and PDF support etc.) + +parse([html | RawOpts], Opts) -> + parse(RawOpts, [html | Opts]); +parse([latex | RawOpts], Opts) -> + parse(RawOpts, [latex | Opts]); +parse([{man,Level} | RawOpts], Opts) -> % Level = 1..9 + parse(RawOpts, [{man,Level} | Opts]); + +parse([{booksty,StyFile} | RawOpts], Opts) -> % "otpA4" | "otpBOOK" + parse(RawOpts, [{booksty,StyFile} | Opts]); +parse([{includepath,Dir} | RawOpts], Opts) -> + parse(RawOpts, [{includepath,Dir} | Opts]); +parse([showpaths | RawOpts], Opts) -> + parse(RawOpts, [showpaths | Opts]); +parse([straight | RawOpts], Opts) -> + parse(RawOpts, [straight | Opts]); +parse([{ent,Ent} | RawOpts], Opts) -> + parse(RawOpts, [{ent,Ent} | Opts]); + +%% Undocumented options + +parse([{name, Name} | RawOpts], Opts) -> + parse(RawOpts, [{name, Name} | Opts]); +parse([framework_only | RawOpts], Opts) -> + parse(RawOpts, [framework_only | Opts]); +parse([kwicindex_only | RawOpts], Opts) -> + parse(RawOpts, [kwicindex_only | Opts]); + +parse([], Opts) -> + {ok, Opts}; +parse([Opt | _RawOpts], _Opts) -> + {error, {badopt, Opt}}. + +%% Type = term | cite +get_defs(Type, File, Opts) -> + Key = {defs,Type}, + {PrevDefs, Opts2} = + case lists:keysearch(Key, 1, Opts) of + {value, {_, Defs0}} -> + {Defs0, lists:keydelete(Key, 1, Opts)}; + false -> + {[], Opts} + end, + NewDefs = case file:consult(File) of + {ok, [DefL]} when is_list(DefL) -> + DefL; + {ok, _Terms} -> + docb_util:message(error, + "Skipping defs file ~s, does " + "not contain one list", [File]), + []; + {error, Error} -> + Expl = lists:flatten(file:format_error(Error)), + docb_util:message(error, + "Skipping defs file ~s, ~s", + [File, Expl]), + [] + end, + [{Key,PrevDefs++NewDefs} | Opts2]. + + diff --git a/lib/docbuilder/src/docb_util.erl b/lib/docbuilder/src/docb_util.erl new file mode 100644 index 0000000000..59673ef3a4 --- /dev/null +++ b/lib/docbuilder/src/docb_util.erl @@ -0,0 +1,237 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_util). + +-export([version/0, old_docb_dir/0, dtd_dir/0]). +-export([html_snippet/2, html_snippet/3]). +-export([lookup_option/2, lookup_option/3, lookup_options/2, + an_option/2]). +-export([outfile/3, full_file_name/4]). +-export([message/2, message/3]). +-export([ltrim/1, rtrim/1, trim/1]). +-export([join/2]). +-export([fknidx/2]). + +-include("docb_util.hrl"). + +%%--DocBuilder info----------------------------------------------------- + +%% version() -> string() +%% Returns the DocBuilder application version. +version() -> + DocbDir = code:lib_dir(docbuilder), + case string:tokens(filename:basename(DocbDir), "-") of + [_, Vsn] -> Vsn; + _ -> "unknown" + end. + +%% old_docb_dir() -> string() +%% Returns the root directory of Old_DocBuilder (OTP internal). +old_docb_dir() -> + "/home/otp/sgml/docb". + +%% dtd_dir() -> string() +%% Returns the directory where the XML DTDs are located. +dtd_dir() -> + DocbDir = code:lib_dir(docbuilder), + filename:join(DocbDir, "dtd"). + +%%--User defined HTML snippets------------------------------------------ + +%% html_snippet(What, Opts) -> HTML +%% html_snippet(What, Arg, Opts) -> HTML +%% What = head | seealso +%% HTML = string() +html_snippet(What, Opts) -> + case lookup_option(html_mod, Opts) of + false -> ""; + Module -> + case catch apply(Module, What, []) of + HTML when is_list(HTML) -> + HTML; + {'EXIT', {undef, _}} -> + ""; + {'EXIT', Reason} -> + message(warning, + "Callback function ~p:~p() => ~p", + [Module, What, Reason]), + ""; + Other -> + message(warning, + "Callback function ~p:~p() => ~p", + [Module, What, Other]), + "" + end + end. +html_snippet(What, Arg, Opts) -> + case lookup_option(html_mod, Opts) of + false -> ""; + Module -> + case catch apply(Module, What, [Arg]) of + HTML when is_list(HTML) -> + HTML; + {'EXIT', {undef, _}} -> + ""; + {'EXIT', Reason} -> + message(warning, + "Callback function ~p:~p(~p) => ~p", + [Module, What, Arg, Reason]), + ""; + Other -> + message(warning, + "Callback function ~p:~p(~p) => ~p", + [Module, What, Arg, Other]), + "" + end + end. + +%%--Option utilities---------------------------------------------------- + +%% Opts = [{Opt,Value} | Opt] + +%% lookup_option(Opt, Opts) -> Value | false +lookup_option(Opt, Opts) -> + case lists:keysearch(Opt, 1, Opts) of + {value, {Opt,Value}} -> Value; + false -> false + end. + +%% lookup_option(Opt, Opts, DefaultValue) -> Value | DefaultValue +lookup_option(Opt, Opts, DefaultValue) -> + case lookup_option(Opt,Opts) of + false -> DefaultValue; + Value -> Value + end. + +%% lookup_options(Opt, Opts) -> [Value] +%% Used when the same option can be defined several times and returns +%% the (possibly empty) list of values. +lookup_options(Opt, Opts) -> + [V || {O, V} <- Opts, O == Opt]. + +%% an_option(Opt, Opts) -> bool() +an_option(Opt, Opts) -> + lists:member(Opt, Opts). + +%%--File handling------------------------------------------------------- + +%% outfile(File0, Extension, Opts) -> File +%% Build the full filename for where to place a resulting file. +outfile(File0, Extension, Opts) -> + File = + case regexp:match(File0, "[^/]*\$") of + {match,Start,Length} -> + string:substr(File0, Start, Length); + _ -> + File0 + end, + full_file_name(File, Extension, outdir, Opts). + +%% full_file_name(File, Extension, What, Opts) -> File' +%% File = string() +%% What = outdir | includepath +%% Prepend the full path name. +full_file_name(File, Extension, What, Opts) -> + Path = lookup_option(What, Opts, ""), + full_file_name(File, Extension, Path). + +full_file_name(File0, Extension, Path) -> + File = case filename:extension(File0) of + Extension -> File0; + _ -> File0++Extension + end, + + case File of + [$/|_] -> File; + [$~|_] -> File; + _ when Path=/="" -> filename:join(Path, File); + _ -> File + end. + +%%--Messages to the user------------------------------------------------ + +%% message(Class, Format) +%% message(Class, Format, Values) -> ok +%% Class = info | warning | error +%% Format, Values -- as in io:format/2 +%% Prints a warning or error message. +%% Call as util:message(warning, "~w is undefined", [foo]). +message(Class, Format) -> + message(Class, Format, []). +message(Class, Format, Values) -> + Prefix = case Class of + info -> ""; + warning -> "*** Warning: "; + error -> "*** Error: " + end, + case get(option_silent) of + true when Class==warning -> + ok; + _ -> + io:format(Prefix, []), + io:format(Format, Values), + io:nl() + end. + +%%--String handling----------------------------------------------------- + +%% ltrim(Str) -> Str' +%% rtrim(Str) -> Str' +%% trim(Str) -> Str' +%% Strips whitespace from left, right or both. +ltrim(Str) -> + lists:dropwhile(fun white_space/1, Str). +rtrim(Str) -> + lists:reverse(ltrim(lists:reverse(Str))). +trim(Str) -> + rtrim(ltrim(Str)). + +white_space($ ) -> true; +white_space(C) when C<$ -> true; +white_space($\n) -> true; +white_space($\t) -> true; +white_space(_) -> false. + +%% join(Strings, With) -> string() +join([H1, H2| T], S) -> + H1 ++ S ++ join([H2| T], S); +join([H], _) -> + H; +join([], _) -> + []. + +%%--Other--------------------------------------------------------------- + +%% fknidx(FNdef0, Fn_arity_sep) -> string() +%% Get me the function name and arity. +fknidx(FNdef0, Fn_arity_sep) -> + FNdef = string:strip(FNdef0), + case string:tokens(FNdef,"(") of + [FNdef] -> + %% No parentheses, assume variable: remove nl:s at end, + %% and strip blanks. + string:strip(string:strip(FNdef, right, $\n)); + [Name0|Args0] -> + [Args1|_] = string:tokens(string:strip(hd(Args0)), "-"), + Arity = case Args1 of + [$)|_] -> 0; + _ -> + length(string:tokens(Args1, ",")) + end, + string:strip(Name0)++Fn_arity_sep++integer_to_list(Arity) + end. diff --git a/lib/docbuilder/src/docb_util.hrl b/lib/docbuilder/src/docb_util.hrl new file mode 100644 index 0000000000..01ef3f7fca --- /dev/null +++ b/lib/docbuilder/src/docb_util.hrl @@ -0,0 +1,34 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +%%% For character conversion + +-record(in_opts, {expand_entities=false, + encode_filter = fun(X) -> X end}). +-record(out_opts, {escape_chars=false, + remove_nl=false, + delete_trailing_whitespace=false, + delete_trailing_nl=false, + compress_white_space=false, + escape_filter = fun(X) -> X end}). + + +-define(pcdata_IN, #in_opts{expand_entities=true}). +-define(rcdata_IN, #in_opts{expand_entities=true}). +-define(cdata_IN, #in_opts{}). + diff --git a/lib/docbuilder/src/docb_xmerl_tree_cb.erl b/lib/docbuilder/src/docb_xmerl_tree_cb.erl new file mode 100644 index 0000000000..d57f55bff8 --- /dev/null +++ b/lib/docbuilder/src/docb_xmerl_tree_cb.erl @@ -0,0 +1,343 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See +%% the Licence for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson AB. +%% Portions created by Ericsson are Copyright 1999-2006, Ericsson AB. +%% All Rights Reserved.�� +%% +%% $Id$ +%% +-module(docb_xmerl_tree_cb). + +%% This is the XMerL callback module for exporting XML to the internal +%% tree format used by DocBuilder. +%% {Doc, _Misc} = xmerl_scan:file("file.xml", [{validation,true}]) +%% Tree = xmerl:export([Doc], docb_xmerl_tree_cb) + +-export(['#xml-inheritance#'/0]). + +-export(['#root#'/4, + '#text#'/1, + '#element#'/5]). +-include("xmerl.hrl"). + +%%--Functions used by xmerl--------------------------------------------- + +'#xml-inheritance#'() -> + []. + +'#root#'(Data, _Attrs, [], _E) -> + Data. + +'#text#'(Text) -> + Text2 = strip_leading_blanks(Text), +%% before +%% case Text2 of +%% [$\n|T] -> +%% case is_empty(T) of +%% true -> []; +%% false -> {pcdata, [], nl(Text2)} +%% end; +%% +%% _ -> +%% {pcdata, [], nl(Text2)} +%% end. +%% after + {pcdata, [], nl(Text2)}. + +'#element#'(Tag, Data, Attrs, Parents, _E) when Tag==pre; Tag==code -> + [H|T] = reinsert_nl(Data), + NewData = [strip_nl(H)|T], + NewData2 = case Tag of + code -> + fix_single_pcdata(NewData); + pre -> + NewData + end, + {Tag, attrs(get_dtd(Parents), Tag, Attrs), NewData2}; +'#element#'(Tag, Data, Attrs, Parents, _E) -> + NewData = case tag_content(Tag) of + no_pcdata -> % remove all pcdata + [Dat|| + Dat <- Data, + begin + Fun = fun({pcdata,_,_}) -> false; + (_) -> true end, + Fun(Dat) + end]; + single_pcdata when length(Data)>1 -> + %% merge several pcdata's into one single pcdata + fix_single_pcdata(Data); + _ -> + lists:flatten(Data) + end, + {Tag, attrs(get_dtd(Parents), Tag, Attrs), NewData}. + +%%--Internal functions-------------------------------------------------- + +%% is_empty(Str) -> bool() +%% Returns true if the string Str only contains blanks, tabs and +%% newlines, false otherwise. +%% is_empty("\n" ++ Text) -> +%% is_empty(Text); +%% is_empty("\t" ++ Text) -> +%% is_empty(Text); +%% is_empty(" " ++ Text) -> +%% is_empty(Text); +%% is_empty("") -> +%% true; +%% is_empty(_) -> +%% false. + +%% reinsert_nl(L1) -> L2 +%% Workaround for <pre>: Normally empty lines are ignored. However, +%% Xmerl splits lines whenever it encounters an entity. In the case of +%% <pre>, this may lead to that we ignores what we think is an empty +%% line but is actually a line break that should be kept, for example +%% in this case: +%% <pre> +%% <input>some command</input> <-- this line break is lost! +%% <some result> +%% </pre> +%% This function reinserts line breaks where necessary. +reinsert_nl([[]|T]) -> + [{pcdata,[],"\\n"} | reinsert_nl(T)]; +reinsert_nl([H|T]) -> + [H | reinsert_nl(T)]; +reinsert_nl([]) -> + []. + +%% sgmls treats line breaks in a way that DocBuilder relies on and +%% which must be imitated here. Replace all "\n" with "\\n" and add +%% "\n" to the end of each text element. +nl("") -> + "\n"; +nl("\n"++Text) -> + "\\n"++nl(Text); +nl([Ch|Text]) -> + [Ch|nl(Text)]. + + +%% strip_leading_blanks(Str) -> Str +%% Leading spaces and tabs before a newline are always redundant +%% and are therefore stripped of here +%% If no newline is found the original string is returned unchanged + +strip_leading_blanks(Str) -> + strip_leading_blanks(Str,Str). + +strip_leading_blanks([],Str) -> + Str; +strip_leading_blanks([$\s|T],Str) -> + strip_leading_blanks(T,Str); +strip_leading_blanks([$\t|T],Str) -> + strip_leading_blanks(T,Str); +strip_leading_blanks(Rest=[$\n|_],_) -> + Rest; +strip_leading_blanks(_,Str) -> + Str. + +%% strip_nl(Str) -> Str +%% The XMerL scan will often result in the contents of <pre> or <code> +%% starting with a newline, as the format is normally: +%% <pre> +%% ..contents.. +%% </pre> +%% However, this newline must be removed, or the resulting HTML will be +%% <pre> +%% +%% ..content.. +%% </pre> +strip_nl({pcdata,[],"\\n"++Str}) -> {pcdata,[],Str}; +strip_nl(E) -> E. + +get_dtd([]) -> + none; +get_dtd(Parents) -> + {DTD, _} = lists:last(Parents), + DTD. + +%% attrs(DTD, Tag, GivenAttrs) -> AllAttrs +%% DTD = Tag = atom() DTD and tag name +%% GivenAttrs = [#xmlAttribute{}] +%% AllAttrs = [{Name, Type, Val}] +%% Name = string() (uppercase) Example: "VALIGN" +%% Type = "CDATA" | "TOKEN" +%% Val = string() (uppercase if type is "TOKEN", as-is otherwise) +%% The XMerL scanning of <file>.xml renders only the given attributes. +%% However, DocBuilder needs also the optional attributes (which not +%% necessarily have been given), so we add them here, using the default +%% values according to the DTDs. +%% NOTE: Uses the information from the DTDs. That is, if some change is +%% done to the DTDs, also this file must be updated. Ideally, the DTDs +%% should be parsed automatically in some way. +%% It can also be noted that this check is superfluous in the case where +%% all attributes are required (except that the attributes are sorted +%% in the same order as in the DTD) and where an optional attribute has +%% type "CDATA" as no sensible default value can be specified in this +%% case. +attrs(DTD, Tag, GivenAttrs) -> + merge_attrs(Tag, default_attrs(DTD, Tag), GivenAttrs). + +merge_attrs(Tag, [{NameA, Type, DefVal}|Default], GivenAttrs) -> + Val = case lists:keysearch(NameA, #xmlAttribute.name, GivenAttrs) of + {value, #xmlAttribute{value=Val0}} -> Val0; + false -> DefVal + end, + Attr = {attr_name(NameA), Type, attr_val(Type, Val)}, + [Attr | merge_attrs(Tag, Default, GivenAttrs)]; +merge_attrs(_Tag, [], _GivenAttrs) -> + []. + +attr_name(Atom) -> + string:to_upper(atom_to_list(Atom)). + +attr_val("CDATA", Val) -> Val; +attr_val("TOKEN", Val) -> string:to_upper(Val). + +%% Given the DTD and element tag, return a list [{Name, Value}] where +%% Name (atom) is the name of each possible attribute and +%% Value (lowercase string) its default value. +default_attrs(_, cell) -> + [{align, "TOKEN", "left"}, + {valign, "TOKEN", "middle"}]; +default_attrs(_, cite) -> + [{id, "CDATA", ""}]; % required +default_attrs(_, code) -> + [{type, "TOKEN", "none"}]; +default_attrs(_, codeinclude) -> + [{file, "CDATA", ""}, % required + {tag, "CDATA", ""}, + {type, "TOKEN", "none"}]; +default_attrs(book, contents) -> + [{level, "TOKEN", "2"}]; +default_attrs(_, erleval) -> + [{expr, "CDATA", ""}]; % required +default_attrs(report, erlinclude) -> + [{file, "CDATA", ""}, % required + {tag, "CDATA", ""}]; % required +default_attrs(_, fascicule) -> + [{file, "CDATA", ""}, % required + {href, "CDATA", ""}, % required + {entry, "TOKEN", "no"}]; +default_attrs(book, header) -> + [{titlestyle, "TOKEN", "normal"}]; +default_attrs(_, image) -> + [{file, "CDATA", ""}]; % required +default_attrs(_, include) -> + [{file, "CDATA", ""}]; % required +default_attrs(report, index) -> + [{txt, "CDATA", ""}]; % required +default_attrs(_, list) -> + [{type, "TOKEN", "bulleted"}]; +default_attrs(_, marker) -> + [{id, "CDATA", ""}]; % required +default_attrs(book, onepart) -> + [{lift, "TOKEN", "no"}]; +default_attrs(book, parts) -> + [{lift, "TOKEN", "no"}]; +default_attrs(_, path) -> + [{unix, "CDATA", ""}, + {windows, "CDATA", ""}]; +default_attrs(_, seealso) -> + [{marker, "CDATA", ""}]; % required +default_attrs(report, table) -> + [{width, "CDATA", "0"}, + {colspec, "CDATA", ""}]; +default_attrs(_, table) -> + [{align, "TOKEN", "center"}]; +default_attrs(_, term) -> + [{id, "CDATA", ""}]; % required +default_attrs(book, theheader) -> + [{tag, "TOKEN", "none"}]; +default_attrs(bookinsidecover, theheader) -> + [{tag, "TOKEN", "none"}]; +default_attrs(_, url) -> + [{href, "CDATA", ""}]; % required +default_attrs(_, _) -> []. + +%%--Single PCDATA broken into several fix------------------------------- + +%% When text contains an entity, then XMERL splits it into two +%% PCDATA elements, the second starting with the entity. +%% +%% Example: +%% Magnus Fr�berg => [{pcdata,[],"Magnus Fr\n"},{pcdata,[],"�berg\n"}] +%% +%% This is not handled by DocBuilder which expects many tags, for +%% example title and aname, to contain a single PCDATA element. (That +%% is also what nsgmls returned.) + +fix_single_pcdata([{pcdata,[],Str1}, {pcdata,[],Str2}|T]) -> + fix_single_pcdata([{pcdata,[],Str1++Str2}|T]); +fix_single_pcdata(FixedData) -> + FixedData. + +tag_content(aname) -> single_pcdata; +tag_content(app) -> single_pcdata; +tag_content(approved) -> single_pcdata; +tag_content(appsummary) -> single_pcdata; +tag_content(b) -> single_pcdata; +tag_content(c) -> single_pcdata; +tag_content(cauthor) -> single_pcdata; +tag_content(cell) -> mixed_content; +tag_content(checked) -> single_pcdata; +tag_content(chowpublished) -> single_pcdata; +tag_content(code) -> single_pcdata; % mixed? +tag_content(com) -> single_pcdata; +tag_content(comsummary) -> single_pcdata; +tag_content(copyright) -> mixed_content; +tag_content(ctitle) -> single_pcdata; +tag_content(d) -> mixed_content; +tag_content(date) -> single_pcdata; +tag_content(docno) -> single_pcdata; +tag_content(em) -> mixed_content; +tag_content(email) -> single_pcdata; +tag_content(fascicule) -> single_pcdata; +tag_content(file) -> single_pcdata; +tag_content(filesummary) -> single_pcdata; +tag_content(fsummary) -> mixed_content; +tag_content(headline) -> single_pcdata; +tag_content(holder) -> single_pcdata; +tag_content(i) -> single_pcdata; +tag_content(icaption) -> single_pcdata; +tag_content(id) -> single_pcdata; +tag_content(input) -> mixed_content; +tag_content(item) -> mixed_content; +tag_content(legalnotice) -> single_pcdata; +tag_content(lib) -> single_pcdata; +tag_content(libsummary) -> single_pcdata; +tag_content(module) -> single_pcdata; +tag_content(modulesummary) -> single_pcdata; +tag_content(name) -> single_pcdata; +tag_content(nametext) -> single_pcdata; +tag_content(p) -> mixed_content; +tag_content(pagetext) -> single_pcdata; +tag_content(path) -> single_pcdata; % mixed? +tag_content(pre) -> mixed_content; +tag_content(prepared) -> single_pcdata; +tag_content(resp) -> single_pcdata; +tag_content(responsible) -> single_pcdata; +tag_content(ret) -> single_pcdata; +tag_content(rev) -> single_pcdata; +tag_content(seealso) -> single_pcdata; % mixed? +tag_content(shortdef) -> single_pcdata; +tag_content(shorttitle) -> single_pcdata; +tag_content(tag) -> mixed_content; +tag_content(tcaption) -> single_pcdata; +tag_content(termdef) -> single_pcdata; +tag_content(title) -> single_pcdata; +tag_content(url) -> single_pcdata; % mixed +tag_content(v) -> single_pcdata; +tag_content(year) -> single_pcdata; +tag_content(_) -> no_pcdata. + + diff --git a/lib/docbuilder/src/docb_xmerl_xml_cb.erl b/lib/docbuilder/src/docb_xmerl_xml_cb.erl new file mode 100644 index 0000000000..089b8f0c7d --- /dev/null +++ b/lib/docbuilder/src/docb_xmerl_xml_cb.erl @@ -0,0 +1,88 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See +%% the Licence for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson AB. +%% Portions created by Ericsson are Copyright 1999-2006, Ericsson AB. +%% All Rights Reserved.�� +%% +%% $Id$ +%% +-module(docb_xmerl_xml_cb). + +%% This is the callback module for exporting XHTML to a DocBuilder +%% erlref or chapter document in XML format. +%% See docb_edoc_xml_cb.erl for further information. +%% +%% The origin of this file is the xmerl module xmerl_otpsgml.erl +%% written by Ulf Wiger and Richard Carlsson. + +-export(['#xml-inheritance#'/0]). + +-export(['#root#'/4, + '#element#'/5, + '#text#'/1]). + +-include("xmerl.hrl"). + +'#xml-inheritance#'() -> + [xmerl_xml]. + +'#root#'(Data, _Attrs, [], _E) -> + ["<",DTD,">"] = hd(hd(Data)), + ["<?xml version=\"1.0\" encoding=\"latin1\" ?>\n", + "<!DOCTYPE "++DTD++" SYSTEM \""++DTD++".dtd\">\n", + Data]. + +'#element#'(Tag, Data, Attrs, _Parents, _E) -> + {NewTag, NewAttrs} = convert_tag(Tag, Attrs), + xmerl_lib:markup(NewTag, NewAttrs, Data). + +'#text#'(Text) -> + xmerl_lib:export_text(Text). + +%% Utility functions + +convert_tag(a, [Attr]) -> + case Attr#xmlAttribute.name of + href -> + Val = Attr#xmlAttribute.value, + case is_url(Val) of + true -> + {url, [Attr]}; + false -> + {seealso, [Attr#xmlAttribute{name=marker}]} + end; + name -> + {marker, [Attr#xmlAttribute{name=id}]} + end; +convert_tag(b, Attrs) -> {em, Attrs}; +convert_tag(blockquote, Attrs) -> {quote, Attrs}; +convert_tag(code, Attrs) -> {c, Attrs}; +convert_tag(dd, Attrs) -> {item, Attrs}; +convert_tag(dl, Attrs) -> {taglist, Attrs}; +convert_tag(dt, Attrs) -> {tag, Attrs}; +convert_tag(li, Attrs) -> {item, Attrs}; +convert_tag(ol, Attrs) -> {list, Attrs}; +convert_tag(strong, Attrs) -> {em, Attrs}; +convert_tag(td, Attrs) -> {cell, Attrs}; +convert_tag(tr, Attrs) -> {row, Attrs}; +convert_tag(tt, Attrs) -> {c, Attrs}; +convert_tag(ul, Attrs) -> {list, Attrs}; +convert_tag(underline, Attrs) -> {em, Attrs}; +convert_tag(Tag, Attrs) -> {Tag, Attrs}. + +is_url("http:"++_) -> true; +is_url("../"++_) -> true; +is_url(FileRef) -> + case filename:extension(FileRef) of + "" -> false; % no extension = xml file, DocBuilder resolves + _Ext -> true % extension, DocBuilder must not resolve + end. diff --git a/lib/docbuilder/src/docb_xml_check.erl b/lib/docbuilder/src/docb_xml_check.erl new file mode 100644 index 0000000000..8ae5cd2eac --- /dev/null +++ b/lib/docbuilder/src/docb_xml_check.erl @@ -0,0 +1,44 @@ +%% ``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/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed 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-2000, Ericsson +%% Utvecklings AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(docb_xml_check). + +-export([validate/1]). + +%% validate(File) -> ok | error | {error, badfile} +%% File = string(), file name with or without ".xml" extension +%% If XML validation fails for a file, the error information from +%% Xmerl is printed to terminal and the function returns error. +validate(File0) -> + File = case filename:extension(File0) of + ".xml" -> File0; + _ -> File0++".xml" + end, + case filelib:is_regular(File) of + true -> + DtdDir = docb_util:dtd_dir(), + case catch xmerl_scan:file(File, [{validation,true}, + {fetch_path,[DtdDir]}]) of + {'EXIT', Error} -> + io:format("~p~n", [Error]), + error; + {_Doc, _Misc} -> + ok + end; + false -> + {error, badfile} + end. diff --git a/lib/docbuilder/src/docbuilder.app.src b/lib/docbuilder/src/docbuilder.app.src new file mode 100644 index 0000000000..64c4770964 --- /dev/null +++ b/lib/docbuilder/src/docbuilder.app.src @@ -0,0 +1,37 @@ +{application, docbuilder, + [{description, "Tool for building HTML documentation"}, + {vsn, "%VSN%"}, + {modules, [docb_edoc_xml_cb, + docb_gen, + docb_html, + docb_html_layout, + docb_html_ref, + docb_html_util, + docb_html_util_iso, + docb_main, + docb_pretty_format, + docb_tr_application2html, + docb_tr_appref2html, + docb_tr_chapter2html, + docb_tr_cite2html, + docb_tr_comref2html, + docb_tr_cref2html, + docb_tr_erlref2html, + docb_tr_fileref2html, + docb_tr_first2html, + docb_tr_index2html, + docb_tr_part2html, + docb_tr_refs2kwic, + docb_tr_report2html, + docb_tr_term2html, + docb_transform, + docb_util, + docb_xmerl_tree_cb, + docb_xmerl_xml_cb, + docb_xml_check + ]}, + {registered, []}, + {applications, [kernel, stdlib]}, + {env, []}]}. + + diff --git a/lib/docbuilder/src/docbuilder.appup.src b/lib/docbuilder/src/docbuilder.appup.src new file mode 100644 index 0000000000..54a63833e6 --- /dev/null +++ b/lib/docbuilder/src/docbuilder.appup.src @@ -0,0 +1 @@ +{"%VSN%",[],[]}. |