View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2006-2020, University of Amsterdam
    7			      VU University Amsterdam
    8			      CWI, Amsterdam
    9    All rights reserved.
   10
   11    Redistribution and use in source and binary forms, with or without
   12    modification, are permitted provided that the following conditions
   13    are met:
   14
   15    1. Redistributions of source code must retain the above copyright
   16       notice, this list of conditions and the following disclaimer.
   17
   18    2. Redistributions in binary form must reproduce the above copyright
   19       notice, this list of conditions and the following disclaimer in
   20       the documentation and/or other materials provided with the
   21       distribution.
   22
   23    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   24    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   25    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   26    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   27    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   28    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   29    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   30    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   31    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34    POSSIBILITY OF SUCH DAMAGE.
   35*/
   36
   37:- module(pldoc_man,
   38	  [ man_page//2,                % +Obj, +Options
   39	    man_overview//1,            % +Options
   40
   41	    man_content_tree/2,         % +Dir, -Tree
   42	    man_packages_tree/1         % -Tree
   43	  ]).   44:- use_module(library(xpath),[xpath/3, op(_,_,_)]).   45:- use_module(library(http/html_write)).   46:- use_module(library(debug),[assertion/1,debug/3]).   47
   48:- autoload(doc_html,
   49	    [ object_tree/5, private/2, object_page_header/4, objects/4,
   50	      object_href/2, object_synopsis/4, object_footer/4,
   51	      object_page_footer/4,
   52	      object_ref/4, object_page/4,
   53	      object_source_button//2
   54	    ]).   55:- autoload(doc_process,[doc_comment/4]).   56:- autoload(doc_search,[search_form/3]).   57:- autoload(doc_util,[atom_to_object/2,atom_pi/2]).   58:- autoload(man_index,[manual_object/5]).   59:- autoload(library(apply),[maplist/2,maplist/3,convlist/3]).   60:- autoload(library(error),[permission_error/3,existence_error/2]).   61:- autoload(library(filesex),
   62	    [directory_file_path/3,relative_file_name/3]).   63:- autoload(library(lists),
   64	    [select/4,append/3,member/2,last/2,selectchk/3]).   65:- autoload(library(option),[merge_options/3,option/2,option/3]).   66:- autoload(library(pairs),[pairs_values/2,pairs_keys/2]).   67:- autoload(library(prolog_xref),[xref_public_list/3]).   68:- autoload(library(sgml),
   69	    [ load_html/3, dtd/2, new_sgml_parser/2, set_sgml_parser/2,
   70	      sgml_parse/2, free_sgml_parser/1
   71	    ]).   72:- autoload(library(uri),[uri_encoded/3]).   73:- autoload(library(www_browser),[expand_url_path/2]).   74:- autoload(library(http/html_head),[html_requires/3]).   75:- if(exists_source(library(http/http_dispatch))).   76:- autoload(library(http/http_dispatch),
   77	    [ http_link_to_id/3, http_location_by_id/2,
   78	      http_handler/3, http_reply_file/3, http_redirect/3
   79	    ]).   80:- endif.   81:- autoload(library(http/http_path),[http_absolute_location/3]).   82:- autoload(library(http/mimetype),[file_mime_type/2]).   83
   84:- include(hooks).

Process SWI-Prolog HTML manuals

*/

   90:- predicate_options(man_page//2, 2,
   91		     [ for(atom),
   92		       links(boolean),
   93		       navtree(boolean),
   94		       synopsis(boolean),
   95		       footer(boolean),
   96		       link_source(boolean),
   97		       no_manual(oneof([fail,error])),
   98		       search_in(oneof([all, app, man])),
   99		       search_match(oneof([name, summary])),
  100		       search_options(boolean)
  101		     ]).  102
  103
  104		 /*******************************
  105		 *           HIERARCHY          *
  106		 *******************************/
 man_nav_tree(+Obj, +Options) is semidet
Create a navigation tree consisting of a nested ul list that reflects the location of Obj in the manual.
  113man_nav_tree(Obj, Options) -->
  114    { ensure_man_tree,
  115      man_nav_tree(Obj, Tree, Options),
  116      TreeOptions = [ secref_style(title)
  117		    | Options
  118		    ]
  119    },
  120    html(ul(class(nav),
  121	    \object_tree(Tree, [Obj], TreeOptions))).
 man_nav_tree(+Obj, -Tree, +Options) is semidet
True when Tree is the navigation tree for Obj. By default, this is the tree going from the leaf to the root, unfolding the neighbors of Obj.
  130man_nav_tree(Obj, Tree, _Options) :-
  131    man_child_of(Obj, Parent),
  132    !,
  133    findall(Neighbour, man_child_of(Neighbour, Parent), Neighbours0),
  134    (   findall(Child, man_child_of(Child, Obj), Children),
  135	Children \== []
  136    ->  select(Obj, Neighbours0, node(Obj, Children), Neighbours)
  137    ;   Neighbours = Neighbours0
  138    ),
  139    path_up(node(Parent, Neighbours), Tree).
  140man_nav_tree(Obj, node(Obj, Children), _Options) :-
  141    findall(Child, man_child_of(Child, Obj), Children).
  142
  143
  144path_up(Node, Tree) :-
  145    node_id(Node, Id),
  146    man_child_of(Id, Parent),
  147    !,
  148    (   Parent == root
  149    ->  findall(Neighbour, man_child_of(Neighbour, Parent), Neighbours0),
  150	select(Id, Neighbours0, Node, Neighbours),
  151	Tree = node(root, Neighbours)
  152    ;   path_up(node(Parent, [Node]), Tree)
  153    ).
  154path_up(Tree, Tree).
 man_child_of(?Child, ?Parent) is nondet
Query the manual hierarchy.
  161man_child_of(Child, Parent) :-
  162    term_hash(Child, ChildHash),
  163    term_hash(Parent, ParentHash),
  164    man_child_of(ChildHash, Child, ParentHash, Parent).
  165
  166:- dynamic
  167    man_child_of/4,
  168    man_tree_done/0.
 ensure_man_tree
Materialize the manual tree as a binary relation.
  174ensure_man_tree :-
  175    man_tree_done,
  176    !.
  177ensure_man_tree :-
  178    with_mutex(man_tree,
  179	       make_man_tree).
  180
  181make_man_tree :-
  182    man_tree_done,
  183    !.
  184make_man_tree :-
  185    man_content_tree(swi_man_manual('.'), ManTree),
  186    man_packages_tree(PkgTree),
  187    assert_tree(node(root, [ManTree, PkgTree])),
  188    assertz(man_tree_done).
  189
  190assert_tree(node(Id, Children)) :-
  191    !,
  192    maplist(assert_parent(Id), Children),
  193    maplist(assert_tree, Children).
  194assert_tree(_).
  195
  196assert_parent(Id, Child) :-
  197    node_id(Child, ChildId),
  198    term_hash(Id, ParentHash),
  199    term_hash(ChildId, ChildHash),
  200    assertz(man_child_of(ChildHash, ChildId, ParentHash, Id)).
  201
  202node_id(node(Id, _), Id) :- !.
  203node_id(Id, Id).
 man_content_tree(+Dir, -Tree) is det
Compute the content tree for a multi-file HTML document. We do this by processing Contents.html for making the toplevel tree that links to the individual files. Then we use html_content_tree/2 to materialize the trees for the files.
  213man_content_tree(Spec, node(manual, Chapters)) :-
  214    absolute_file_name(Spec, Dir,
  215		       [ file_type(directory),
  216			 access(read)
  217		       ]),
  218    directory_file_path(Dir, 'Contents.html', ContentsFile),
  219    load_html(ContentsFile, DOM, [cdata(string)]),
  220    findall(Level-Path,
  221	    ( xpath(DOM, //div(@class=Class), DIV),
  222	      class_level(Class, Level),
  223	      xpath(DIV, a(@class=sec,@href=File), _),
  224	      \+ sub_atom(File, _, _, _, #),
  225	      directory_file_path(Dir, File, Path)
  226	    ),
  227	    Pairs),
  228    index_chapters(Pairs, Chapters).
  229
  230class_level('toc-h1', 1).
  231class_level('toc-h2', 2).
  232class_level('toc-h3', 3).
  233class_level('toc-h4', 4).
  234
  235index_chapters([], []).
  236index_chapters([Level-File|T0], [node(Chapter, Children)|T]) :-
  237    html_content_tree(File, Node),
  238    Node = node(Chapter, Children0),
  239    append(Children0, Sections, Children),
  240    index_sections(T0, Level, Sections, T1),
  241    index_chapters(T1, T).
  242
  243index_sections([], _, [], []) :- !.
  244index_sections([SLevel-File|T0], Level, [Node|T], Rest) :-
  245    SLevel > Level,
  246    !,
  247    html_content_tree(File, Node),
  248    index_sections(T0, Level, T, Rest).
  249index_sections(Rest, _, [], Rest).
 man_packages_tree(-Tree) is det
Tree is the content tree of all packages
  256man_packages_tree(node(packages, Packages)) :-
  257    Section = section(0, _, _, _),
  258    findall(File,
  259	    manual_object(Section, _Title, File, packages, _),
  260	    Files),
  261    maplist(package_node, Files, Packages).
  262
  263package_node(File, Tree) :-
  264    html_content_tree(File, Tree).
 html_content_tree(+ManualFile, -Tree) is det
True when Tree represents the hierarchical structure of objects documented in the HTML file ManualFile. Tree is a term where of the form below. Object is a documentation object (typically a section or predicate indicator) that may be handed to object_link//1 and similar predicates to make a table of contents.
node(Object, ListOfTree).
  277html_content_tree(FileIn, Tree) :-
  278    absolute_file_name(FileIn, File),
  279    findall(Offset-Obj,
  280	    manual_object(Obj, _Summary, File, _Class, Offset),
  281	    Pairs),
  282    keysort(Pairs, Sorted),
  283    pairs_values(Sorted, Objects),
  284    make_tree(Objects, Trees),
  285    assertion(Trees = [_]),
  286    Trees = [Tree].
  287
  288make_tree([], []).
  289make_tree([Obj|T0], [node(Obj, Children)|T]) :-
  290    children(T0, Obj, Children, T1),
  291    make_tree(T1, T).
  292
  293children([], _, [], []) :- !.
  294children([Obj|T0], Root, [Node|T], Rest) :-
  295    section_level(Obj, ObjLevel),
  296    section_level(Root, Level),
  297    ObjLevel > Level,
  298    !,
  299    Node = node(Obj, Children),
  300    children(T0, Obj, Children, T1),
  301    children(T1, Root, T, Rest).
  302children([Obj|T0], Root, [Obj|T], Rest) :-
  303    \+ section_level(Obj, _),
  304    !,
  305    children(T0, Root, T, Rest).
  306children(Rest, _, [], Rest).
  307
  308section_level(section(Level, _Nr, _Id, _File), Level).
  309
  310
  311		 /*******************************
  312		 *            RETRIEVE          *
  313		 *******************************/
 load_man_object(+Obj, -Parent, -Path, -DOM) is nondet
load the desription of the object matching Obj from the HTML sources and return the DT/DD pair in DOM.
To be done
- Nondet?
  322load_man_object(Obj, ParentSection, Path, DOM) :-
  323    resolve_section(Obj, For),
  324    For = section(_,SN,_ID,Path),
  325    parent_section(For, ParentSection),
  326    findall(Nr-Pos, section_start(Path, Nr, Pos), Pairs),
  327    (   (   Pairs = [SN-_|_]
  328	;   Pairs == []
  329	)
  330    ->  !,
  331	load_html(Path, DOM, [cdata(string)])           % Load whole file
  332    ;   append(_, [SN-Start|Rest], Pairs)
  333    ->  !,
  334	(   member(N-End, Rest),
  335	    \+ sub_atom(N, 0, _, _, SN),
  336	    Len is End - Start,
  337	    Options = [content_length(Len)]
  338	->  true
  339	;   Options = []
  340	),
  341	open(Path, read, In, [type(binary)]),
  342	seek(In, Start, bof, _),
  343	dtd(html, DTD),
  344	new_sgml_parser(Parser,
  345			[ dtd(DTD)
  346			]),
  347	set_sgml_parser(Parser, file(Path)),
  348	set_sgml_parser(Parser, dialect(sgml)),
  349	set_sgml_parser(Parser, shorttag(false)),
  350	set_sgml_parser(Parser, defaults(false)),
  351	call_cleanup(sgml_parse(Parser,
  352				[ document(DOM),
  353				  source(In),
  354				  syntax_errors(quiet),
  355				  cdata(string)
  356				| Options
  357				]),
  358		     ( free_sgml_parser(Parser),
  359		       close(In)
  360		     ))
  361    ).
  362load_man_object(For, Parent, Path, DOM) :-
  363    object_spec(For, Obj),
  364    manual_object(Obj, _, Path, _, Position),
  365    (   object_section(Path, Position, Parent)
  366    ->  true
  367    ;   Parent = Path
  368    ),
  369    open(Path, read, In, [type(binary)]),
  370    seek(In, Position, bof, _),
  371    dtd(html, DTD),
  372    new_sgml_parser(Parser,
  373		    [ dtd(DTD)
  374		    ]),
  375    set_sgml_parser(Parser, file(Path)),
  376    set_sgml_parser(Parser, dialect(sgml)),
  377    set_sgml_parser(Parser, shorttag(false)),
  378    set_sgml_parser(Parser, defaults(false)),
  379    call_cleanup(parse_dts_upto_dd(Parser, In, DOM),
  380		 ( free_sgml_parser(Parser),
  381		   close(In)
  382		 )).
  383
  384parse_dts_upto_dd(Parser, In, Description) :-
  385    sgml_parse(Parser,
  386	       [ document(DOM0),
  387		 cdata(string),
  388		 source(In),
  389		 parse(element),
  390		 syntax_errors(quiet)
  391	       ]),
  392    (   DOM0 = [Element],
  393	Element = element(dt, _, _)
  394    ->  Description = [Element|More],
  395	parse_dts_upto_dd(Parser, In, More)
  396    ;   Description = DOM0
  397    ).
  398
  399section_start(Path, Nr, Pos) :-
  400    manual_object(section(_,Nr,_,_), _, Path, _, Pos).
 resolve_section(+SecIn, -SecOut) is det
Resolve symbolic path reference and fill in level and section number if this information is missing. The latter allows us to refer to files of the manual.
  408resolve_section(section(Level, No, Spec), Section) :-
  409    !,
  410    resolve_section(section(Level, No, _, Spec), Section).
  411resolve_section(section(Level, No, ID, Path),
  412		section(Level, No, ID, Path)) :-
  413    nonvar(ID),
  414    manual_object(section(Level,No,ID,Path), _, _, _, _),
  415    !.
  416resolve_section(section(Level, No, ID, Spec),
  417		section(Level, No, ID, Path)) :-
  418    ground(Spec),
  419    absolute_file_name(Spec, Path,
  420		       [ access(read)
  421		       ]),
  422    (   manual_object(section(Level, No, ID, Path), _, _, _, _)
  423    ->  true
  424    ;   path_allowed(Path)
  425    ->  true
  426    ;   permission_error(read, manual_file, Spec)
  427    ).
  428
  429
  430path_allowed(Path) :-                   % allow all files from swi/doc
  431    absolute_file_name(swi(doc), Parent,
  432		       [ access(read),
  433			 file_type(directory)
  434		       ]),
  435    sub_atom(Path, 0, _, _, Parent).
 parent_section(+Section, -Parent) is det
Parent is the parent-section of Section. First computes the section number and than finds the required number in the same file or same directory. If this doesn't exist, get the file as a whole.
  445parent_section(section(Level, Nr, _ID, File), Parent) :-
  446    integer(Level),
  447    Parent = section(PL, PNr, _PID, _PFile),
  448    PL is Level - 1,
  449    findall(B, sub_atom(Nr, B, _, _, '.'), BL),
  450    last(BL, Before),
  451    sub_atom(Nr, 0, Before, _, PNr),
  452    (   manual_object(Parent, _, File, _, _)
  453    ->  true
  454    ;   manual_object(Parent, _, ParentFile, _, _),
  455	same_dir(File, ParentFile)
  456    ->  true
  457    ;   manual_object(Parent, _, _, _, _)
  458    ),
  459    !.
  460parent_section(section(Level, _, _, File), Parent) :-
  461    Parent = section(ParentLevel, _, _, File),
  462    manual_object(Parent, _, _, _, _),
  463    ParentLevel < Level,
  464    !.
  465parent_section(section(_, _, _, File), File).
 object_section(+Path, +Position, -Section) is semidet
Section is the section in which object appears. This is the last section object before position.
  473object_section(Path, Pos, Section) :-
  474    Section = section(_,_,_,_),
  475    findall(Section,
  476	   (manual_object(Section, _, Path, _, SecPos), SecPos =< Pos),
  477	    List),
  478    last(List, Section).
  479
  480same_dir(File1, File2) :-
  481    file_directory_name(File1, Dir),
  482    file_directory_name(File2, Dir).
 object_spec(+Atom, -SpecTerm)
Tranform the Name/Arity, etc strings as received from the HTTP into a term. Must return unique results.
  489object_spec(Spec, Spec) :-
  490    compound(Spec),
  491    !.
  492object_spec(Atom, Spec) :-
  493    catch(atom_to_term(Atom, Spec, _), _, fail),
  494    !,
  495    Atom \== Spec.
  496object_spec(Atom, PI) :-
  497    atom_to_object(Atom, PI).
  498
  499
  500		 /*******************************
  501		 *            EMIT              *
  502		 *******************************/
 man_page(+Obj, +Options)// is semidet
Produce a Prolog manual page for Obj. The page consists of a link to the section-file and a search field, followed by the predicate description. Obj is one of:

Options:

no_manual(Action)
If Action = fail, fail instead of displaying a not-found message.
synopsis(Bool)
If false, omit the synopsis line
links(Bool)
If true (default), include links to the parent object; if false, just emit the manual material.
navtree(Bool)
If true (default), display the navigation tree, otherwise suppress it.
  542man_page(Obj, Options) -->
  543    { ground(Obj),
  544      special_node(Obj)
  545    },
  546    !,
  547    html_requires(pldoc),
  548    man_links([], Options),
  549    man_matches([Obj], Obj, Options).
  550man_page(Obj0, Options) -->                     % Manual stuff
  551    { full_page(Obj0, Obj),
  552      findall((Parent+Path)-(Obj+DOM),
  553	      load_man_object(Obj, Parent, Path, DOM),
  554	      Matches),
  555      Matches = [_|_],
  556      !,
  557      pairs_keys(Matches, ParentPaths),
  558      Matches = [Parent+Path-_|_]
  559    },
  560    html_requires(pldoc),
  561    man_links(ParentPaths, Options),
  562    man_matches(Matches, Obj, Options).
  563man_page(Obj, Options) -->                      % PlDoc predicates, etc.
  564    { full_object(Obj, Full),
  565      findall(Full-File, visible_doc_comment(Full, File, Options), Pairs),
  566      Pairs \== [],
  567      pairs_keys(Pairs, Objs)
  568    },
  569    !,
  570    html_requires(pldoc),
  571    (   { Pairs = [_-File] }
  572    ->  object_page_header(File, Options)
  573    ;   object_page_header(-, Options)
  574    ),
  575    { merge_options(Options,
  576		    [ synopsis(true),
  577		      navtree(true)
  578		    ], Options2)
  579    },
  580    objects(Objs, Options2).
  581man_page(Obj, Options) -->                      % failure
  582    { \+ option(no_manual(fail), Options)
  583    },
  584    html_requires(pldoc),
  585    man_links([], Options),
  586    html(p(class(noman),
  587	   [ 'Sorry, No manual entry for ',
  588	     b('~w'-[Obj])
  589	   ])).
  590
  591% Show an object if it is not private   or  it is fully qualified and we
  592% want to show all fully qualified objects. Used by help/1.
  593
  594visible_doc_comment(Obj, File, Options) :-
  595    doc_comment(Obj, File:_, _, _),
  596    \+ ( private(Obj, Options),
  597         \+ ( Obj = _:_,
  598              option(qualified(always), Options)
  599            )
  600       ).
  601
  602%special_node(manual).          % redirected to the Introduction section
  603special_node(root).
  604special_node(packages).
  605
  606full_page(Obj, _) :-
  607    var(Obj), !, fail.
  608full_page(Obj, Obj) :-
  609    Obj = section(_,_,_,_),
  610    !.
  611full_page(section(ID), section(_,_,ID,_)) :- !.
  612full_page(manual, section(_,_,'sec:intro',_)) :- !.
  613full_page(Obj0, Obj) :-
  614    ground(Obj0),
  615    alt_obj(Obj0, Obj),
  616    manual_object(Obj, _, _, _, _),
  617    !.
  618full_page(Obj, Obj) :-
  619    ground(Obj).
  620
  621alt_obj(Obj, Obj).
  622alt_obj(Name/Arity, Name//DCGArity) :-
  623    integer(Arity),
  624    Arity >= 2,
  625    DCGArity is Arity - 2.
  626alt_obj(Name//DCGArity, Name/Arity) :-
  627    integer(DCGArity),
  628    Arity is DCGArity + 2.
 full_object(+Object, -Full) is semidet
Translate to canonical PlDoc object
  634full_object(Object, M:Obj) :-
  635    qualify(Object, M:Obj0),
  636    alt_obj(Obj0, Obj),
  637    doc_comment(M:Obj, _, _, _),
  638    !.
  639
  640qualify(M:O, M:O).
  641qualify(O, _:O).
 man_qualified_object(+Object, +Parent, -LibraryOpt, -QObject, -Section) is semidet
Get a qualified predicate description from Text that appears in the section Parent.

The tricky part is that there are cases where multiple modules export the same predicate. We must find from the title of the manual section which library is documented.

  653man_qualified_object(Text, Parent, LibOpt, Object, Section) :-
  654    atom(Text),
  655    atom_pi(Text, PI),
  656    ground(PI),
  657    !,
  658    man_qualified_object_2(PI, Parent, LibOpt, Object, Section).
  659man_qualified_object(Object0, Parent, LibOpt, Object, Section) :-
  660    man_qualified_object_2(Object0, Parent, LibOpt, Object, Section).
  661
  662man_qualified_object_2(Name/Arity, Parent,
  663		       LibOpt, Module:Name/Arity, Section) :-
  664    object_module(Parent, Module, Section, LibOpt),
  665    !.
  666man_qualified_object_2(Object, Parent, [], Object, Parent).
 man_synopsis(+Object, +Section)//
Give synopsis details for a fully specified predicate indicator and link this to the section.
  674:- public
  675    man_synopsis//2.                % called from man_match//2
  676
  677man_synopsis(PI, Section) -->
  678    man_synopsis(PI, Section, []).
  679
  680man_synopsis(PI, Section, Options) -->
  681    { object_href(Section, HREF)
  682    },
  683    object_synopsis(PI, [href(HREF)|Options]).
 object_module(+Section0, -Module, -Section, -LibOpt) is semidet
Find the module documented by Section.
  689object_module(Section0, Module, Section, [source(Term)]) :-
  690    parent_section_ndet(Section0, Section),
  691    manual_object(Section, Title, _File, _Class, _Offset),
  692    (   once(sub_atom(Title, B, _, _, :)),
  693	sub_atom(Title, 0, B, _, Atom),
  694	catch(term_to_atom(Term, Atom), _, fail),
  695	ground(Term),
  696	Term = library(_)
  697    ->  !,
  698	absolute_file_name(Term, PlFile,
  699			   [ file_type(prolog),
  700			     access(read),
  701			     file_errors(fail)
  702			   ]),
  703	(   module_property(Module, file(PlFile))
  704	->  true
  705	;   xref_public_list(PlFile, -,         % module is not loaded
  706			     [ module(Module)
  707			     ])
  708	)
  709    ).
  710
  711parent_section_ndet(Section, Section).
  712parent_section_ndet(Section, Parent) :-
  713    parent_section(Section, Parent0),
  714    parent_section_ndet(Parent0, Parent).
  715
  716
  717man_matches(Matches, Object, Options) -->
  718    { option(navtree(false), Options) },
  719    !,
  720    man_matches_nt(Matches, Object, Options).
  721man_matches(Matches, Object, Options) -->
  722    html([ div(class(navtree),
  723	       div(class(navwindow),
  724		   \man_nav_tree(Object, Options))),
  725	   div(class(navcontent),
  726	       \man_matches_nt(Matches, Object, Options))
  727	 ]).
  728
  729
  730man_matches_nt([Match], Object, Options) -->
  731    { option(footer(true), Options, true) },
  732    !,
  733    man_match(Match, Object, Options),
  734    object_page_footer(Object, []).
  735man_matches_nt(Matches, Object, Options) -->
  736    man_matches_list(Matches, Object, Options).
  737
  738man_matches_list([], _, _) --> [].
  739man_matches_list([H|T], Obj, Options) -->
  740    man_match(H, Obj, Options),
  741    man_matches_list(T, Obj, Options).
 man_match(+Term, +Object, +Options)// is det
If possible, insert the synopsis into the title of the description.
  748man_match(packages, packages, _) -->
  749    !,
  750    html({|html||
  751	  <p>
  752	  Packages are relatively independent add-on libraries that
  753	  may not be available in all installations.  Packages are
  754	  part of the source code releases of SWI-Prolog and may be
  755	  enabled or disabled during the build.</p>
  756
  757	  <p>
  758	  See also <a href="/pack/list">Add-ons</a> for extensions
  759	  provided by the community that must be installed separately
  760	  using
  761	  <a href="/pldoc/doc_for?object=pack_install/1">pack_install/1</a>.</p>
  762	 |}).
  763man_match(root, root, _) -->
  764    !,
  765    man_overview([]).
  766man_match((Parent+Path)-(Obj+DOM), Obj, Options) -->
  767    { \+ option(synopsis(false), Options),
  768      DOM = [element(dt,A,C0)|DD],
  769      convlist(dt_obj, DOM, Objs),
  770      option(link_source(Link), Options, true),
  771      man_qualified_object(Obj, Parent, LibOpt, QObj, Section),
  772      !,
  773      C = [ span(style('float:right;margin-left:5px;'),
  774		 \object_source_button(QObj, [source_link(Link)]))
  775	  | C0
  776	  ]
  777    },
  778    dom_list([ element(dt,[],[\man_synopsis(QObj, Section, LibOpt)]),
  779	       element(dt,A,C)
  780	     | DD
  781	     ], Path, Options),
  782    object_footer(Objs, Options).
  783man_match((_Parent+Path)-(Obj+DOM), Obj, Options) -->
  784    dom_list(DOM, Path, Options).
  785
  786dt_obj(element(dt,_,C), Obj) :-
  787    xpath(C, //a(@id=Atom), _),
  788    atom_to_object(Atom, Obj).
  789
  790:- html_meta
  791    dom_list(html, +, +, ?, ?).  792
  793dom_list(_:[], _, _) -->
  794    !,
  795    [].
  796dom_list(M:[H|T], Path, Options) -->
  797    dom(H, Path, Options),
  798    dom_list(M:T, Path, Options).
  799
  800dom(element(E, Atts, Content), Path, Options) -->
  801    !,
  802    dom_element(E, Atts, Content, Path, Options).
  803dom(CDATA, _, _) -->
  804    html(CDATA).
  805
  806dom_element(a, _, [], _, _) -->                % Useless back-references
  807    !,
  808    [].
  809dom_element(a, Att, Content, Path, Options) -->
  810    { memberchk(href=HREF, Att),
  811      (   memberchk(class=Class, Att)
  812      ->  true
  813      ;   Class = unknown
  814      ),
  815      rewrite_ref(Class, HREF, Path, Myref, Options)
  816    },
  817    !,
  818    html(a(href(Myref), \dom_list(Content, Path, Options))).
  819dom_element(span, Att, [CDATA], _, Options) -->
  820    { memberchk(class='pred-ext', Att),
  821      atom_pi(CDATA, PI),
  822      documented(PI),
  823      (   option(server(false), Options)
  824      ->  public_link(predicate(CDATA), HREF)
  825      ;   http_link_to_id(pldoc_man, [predicate=CDATA], HREF)
  826      )
  827    },
  828    !,
  829    html(a(href(HREF), CDATA)).
  830dom_element(img, Att0, [], Path, _Options) -->
  831    { selectchk(src=Src, Att0, Att1),
  832      relative_file_name(ImgFile, Path, Src),
  833      handler_alias(Handler, DirAlias),
  834      absolute_file_name(DirAlias, Dir,
  835			 [ file_errors(fail),
  836			   solutions(all),
  837			   file_type(directory)
  838			 ]),
  839      ensure_slash(Dir, DirS),
  840      atom_concat(DirS, NewSrc, ImgFile),
  841      !,
  842      http_link_to_id(Handler, [], ManRef),
  843      directory_file_path(ManRef, NewSrc, NewPath),
  844      Begin =.. [img, src(NewPath) | Att1]
  845    },
  846    html_begin(Begin),
  847    html_end(img).
  848dom_element(div, Att, _, _, _) -->
  849    { memberchk(class=navigate, Att) },
  850    !.
  851dom_element(html, _, Content, Path, Options) -->
  852    !,                              % do not emit a html for the second time
  853    dom_list(Content, Path, Options).
  854dom_element(head, _, Content, Path, Options) -->
  855    !,                              % do not emit a head for the second time
  856    dom_list(Content, Path, Options).
  857dom_element(title, _, _, _, _) --> !.
  858dom_element(link, _, _, _, _) --> !.
  859dom_element(body, _, Content, Path, Options) -->
  860    !,                              % do not emit a body for the second time
  861    dom_list(Content, Path, Options).
  862dom_element(Name, Attrs, Content, Path, Options) -->
  863    { Begin =.. [Name|Attrs] },
  864    html_begin(Begin),
  865    dom_list(Content, Path, Options),
  866    html_end(Name).
  867
  868handler_alias(manual_file,   swi_man_manual(.)).
  869handler_alias(pldoc_package, swi_man_packages(.)).
  870
  871ensure_slash(Dir, DirS) :-
  872    (   sub_atom(Dir, _, _, 0, /)
  873    ->  DirS = Dir
  874    ;   atom_concat(Dir, /, DirS)
  875    ).
 public_link(+Spec, -HREF)
We do not have a web server. Create a link to the public server instead.
bug
- The predicate may not be there.
  884public_link(predicate(CDATA), HREF) :-
  885    uri_encoded(query_value, CDATA, Encoded),
  886    atom_concat('https://www.swi-prolog.org/pldoc/doc_for?object=',
  887		Encoded, HREF).
 documented(+PI) is semidet
True if we have documentation about PI
  894documented(PI) :-
  895    manual_object(PI, _, _, _, _),
  896    !.
  897documented(PI) :-
  898    full_object(PI, _Obj).
 rewrite_ref(+Class, +Ref0, +Path, -ManRef, +Options) is semidet
Rewrite Ref0 from the HTML reference manual format to the server format. Reformatted:
File#Name/Arity
Local reference using the manual presentation /man?predicate=PI.
File#sec:NR
Rewrite to section(Level, NT, ID, FilePath)
File#flag:Name
Rewrite to section(Level, NT, ID, FilePath)#flag:Name
File#gloss:Name
Rewrite to section(Level, NT, ID, FilePath)#gloss:Name

$ File#Name() Rewrite to /man/CAPI=Name

Arguments:
Class- Class of the <A>. Supported classes are
secLink to a section
predLink to a predicate
flagLink to a Prolog flag
glossLink to a glossary
Ref0- Initial reference from the a element
Path- Currently loaded file
ManRef- PlDoc server reference
  932rewrite_ref(_Class, Ref, _Path, Ref, Options) :-
  933    option(server(false), Options),
  934    !.
  935rewrite_ref(Class, Ref0, Path, ManRef, _Options) :-
  936    rewrite_ref(Class, Ref0, Path, ManRef).
  937
  938rewrite_ref(pred, Ref0, _, Ref) :-              % Predicate/DCG reference
  939    sub_atom(Ref0, _, _, A, '#'),
  940    !,
  941    sub_atom(Ref0, _, A, 0, Fragment),
  942    atom_to_object(Fragment, PI),
  943    manual_object(PI, _, _, _, _),
  944    uri_encoded(query_value, Fragment, Enc),
  945    http_location_by_id(pldoc_man, ManHandler),
  946    format(string(Ref), '~w?predicate=~w', [ManHandler, Enc]).
  947rewrite_ref(function, Ref0, _, Ref) :-          % Arithmetic function reference
  948    sub_atom(Ref0, _, _, A, '#'),
  949    !,
  950    sub_atom(Ref0, _, A, 0, Fragment),
  951    atom_to_object(Fragment, PI),
  952    manual_object(PI, _, _, _, _),
  953    PI=f(Name/Arity),
  954    format(atom(PIName), '~w/~w', [Name,Arity]),
  955    uri_encoded(query_value, PIName, Enc),
  956    http_location_by_id(pldoc_man, ManHandler),
  957    format(string(Ref), '~w?function=~w', [ManHandler, Enc]).
  958rewrite_ref(func, Ref0, _, Ref) :-              % C-API reference
  959    sub_atom(Ref0, _, _, A, '#'),
  960    !,
  961    sub_atom(Ref0, _, A, 0, Fragment),
  962    atom_to_object(Fragment, Obj),
  963    manual_object(Obj, _, _, _, _),
  964    Obj = c(Function),
  965    uri_encoded(query_value, Function, Enc),
  966    http_location_by_id(pldoc_man, ManHandler),
  967    format(string(Ref), '~w?CAPI=~w', [ManHandler, Enc]).
  968rewrite_ref(sec, Ref0, Path, Ref) :-            % Section inside a file
  969    sub_atom(Ref0, B, _, A, '#'),
  970    !,
  971    sub_atom(Ref0, _, A, 0, Fragment),
  972    sub_atom(Ref0, 0, B, _, File),
  973    referenced_section(Fragment, File, Path, Section),
  974    object_href(Section, Ref).
  975rewrite_ref(sec, File, Path, Ref) :-            % Section is a file
  976    file_directory_name(Path, Dir),
  977    atomic_list_concat([Dir, /, File], SecPath),
  978    Obj = section(_, _, _, SecPath),
  979    manual_object(Obj, _, _, _, _),
  980    !,
  981    object_href(Obj, Ref).
  982rewrite_ref(cite, Ref0, Path, Ref) :-           % Citation (bit hard-wired)
  983    debug(pldoc(cite), 'Cite ref ~q ~q', [Ref0, Path]),
  984    sub_atom(Ref0, _, _, A, '#'),
  985    !,
  986    sub_atom(Ref0, _, A, 0, Fragment),
  987    uri_encoded(query_value, Fragment, Enc),
  988    http_location_by_id(pldoc_man, ManHandler),
  989    format(string(Ref), '~w?section=bibliography#~w', [ManHandler, Enc]).
  990rewrite_ref(flag, Ref0, Path, Ref) :-
  991    sub_atom(Ref0, B, _, A, '#'),
  992    !,
  993    sub_atom(Ref0, 0, B, _, File),
  994    sub_atom(Ref0, _, A, 0, Fragment),
  995    file_directory_name(Path, Dir),
  996    atomic_list_concat([Dir, /, File], SecPath),
  997    Obj = section(_, _, _, SecPath),
  998    manual_object(Obj, _, _, _, _),
  999    !,
 1000    object_href(Obj, Ref1),
 1001    format(string(Ref), '~w#~w', [Ref1, Fragment]).
 1002rewrite_ref(gloss, Ref0, Path, Ref) :-
 1003    sub_atom(Ref0, B, _, A, '#'),
 1004    !,
 1005    sub_atom(Ref0, 0, B, _, File),
 1006    sub_atom(Ref0, _, A, 0, Fragment),
 1007    file_directory_name(Path, Dir),
 1008    atomic_list_concat([Dir, /, File], SecPath),
 1009    Obj = section(_, _, _, SecPath),
 1010    manual_object(Obj, _, _, _, _),
 1011    !,
 1012    object_href(Obj, Ref1),
 1013    format(string(Ref), '~w#~w', [Ref1, Fragment]).
 referenced_section(+Fragment, +File, +Path, -Section)
 1017referenced_section(Fragment, File, Path, section(Level, Nr, ID, SecPath)) :-
 1018    atom_concat('sec:', Nr, Fragment),
 1019    (   File == ''
 1020    ->  SecPath = Path
 1021    ;   file_directory_name(Path, Dir),
 1022	atomic_list_concat([Dir, /, File], SecPath)
 1023    ),
 1024    manual_object(section(Level, Nr, ID, SecPath), _, _, _, _).
 man_links(+ParentPaths, +Options)// is det
Create top link structure for manual pages.
 1031man_links(ParentPaths, Options) -->
 1032    prolog:doc_page_header(parents(ParentPaths), Options),
 1033    !.
 1034man_links(ParentPaths, Options) -->
 1035    { option(links(true), Options, true),
 1036      option(header(true), Options, true)
 1037    },
 1038    !,
 1039    html([ div(class(navhdr),
 1040	       [ div(class(jump), \man_parent(ParentPaths)),
 1041		 div(class(search), \search_form(Options)),
 1042		 br(clear(right))
 1043	       ]),
 1044	   p([])
 1045	 ]).
 1046man_links(_, _) -->
 1047    [].
 1048
 1049man_parent(ParentPaths) -->
 1050    { maplist(parent_to_section, ParentPaths, [Section|MoreSections]),
 1051      maplist(=(Section), MoreSections)
 1052    },
 1053    !,
 1054    object_ref(Section, [secref_style(number_title)]).
 1055man_parent(_) --> [].
 1056
 1057parent_to_section(X+_, X) :-
 1058    X = section(_,_,_,_),
 1059    !.
 1060parent_to_section(File+_, Section) :-
 1061    atom(File),
 1062    manual_object(Section, _Title, File, _Class, _Offset),
 1063    !.
 section_link(+Obj, +Options)// is det
Create link to a section. Options recognised:
secref_style(+Style)
One of number, title or number_title.
 1072section_link(Section, Options) -->
 1073    { option(secref_style(Style), Options, number)
 1074    },
 1075    section_link(Style, Section, Options).
 1076
 1077section_link(number, section(_, Number, _, _), _Options) -->
 1078    !,
 1079    (   {Number == '0'}             % Title.  Package?
 1080    ->  []
 1081    ;   html(['Sec. ', Number])
 1082    ).
 1083section_link(title, Obj, _Options) -->
 1084    !,
 1085    { manual_object(Obj, Title, _File, _Class, _Offset)
 1086    },
 1087    html(Title).
 1088section_link(_, Obj, _Options) -->
 1089    !,
 1090    { Obj = section(_, Number, _, _),
 1091      manual_object(Obj, Title, _File, _Class, _Offset)
 1092    },
 1093    (   { Number == '0' }
 1094    ->  html(Title)
 1095    ;   html([Number, ' ', Title])
 1096    ).
 function_link(+Function, +Options) is det
Create a link to a C-function
 1102function_link(Function, _) -->
 1103    html([Function, '()']).
 1104
 1105
 1106		 /*******************************
 1107		 *       INDICES & OVERVIEW     *
 1108		 *******************************/
 man_overview(+Options)// is det
Provide a toplevel overview on the manual: the reference manual and the available packages.
 1115man_overview(Options) -->
 1116    { http_absolute_location(pldoc_man(.), RefMan, [])
 1117    },
 1118    html([ h1('SWI-Prolog documentation'),
 1119	   blockquote(class(refman_link),
 1120		      a(href(RefMan),
 1121			'SWI-Prolog reference manual')),
 1122	   \package_overview(Options),
 1123	   \paperback(Options)
 1124	 ]).
 1125
 1126package_overview(Options) -->
 1127    html([ h2(class(package_doc_title),
 1128	      'SWI-Prolog package documentation'),
 1129	   blockquote(class(package_overview),
 1130		      \packages(Options))
 1131	 ]).
 1132
 1133packages(Options) -->
 1134    { findall(Pkg, current_package(Pkg), Pkgs)
 1135    },
 1136    packages(Pkgs, Options).
 1137
 1138packages([], _) -->
 1139    [].
 1140packages([Pkg|T], Options) -->
 1141    package(Pkg, Options),
 1142    packages(T, Options).
 1143
 1144package(pkg(Title, HREF, HavePackage), Options) -->
 1145    { package_class(HavePackage, Class, Options)
 1146    },
 1147    html(div(class(Class),
 1148	     a([href(HREF)], Title))).
 1149
 1150package_class(true,  pkg_link, _).
 1151package_class(false, no_pkg_link, _).
 1152
 1153current_package(pkg(Title, HREF, HavePackage)) :-
 1154    manual_object(section(0, _, _, _), Title, File, packages, _),
 1155    file_base_name(File, FileNoDir),
 1156    file_name_extension(Base, _, FileNoDir),
 1157    (   exists_source(library(Base))
 1158    ->  HavePackage = true
 1159    ;   HavePackage = false
 1160    ),
 1161    http_absolute_location(pldoc_pkg(FileNoDir), HREF, []).
 1162
 1163
 1164:- if(current_predicate(http_handler/3)). 1165:- http_handler(pldoc(jpl),      pldoc_jpl,              [prefix]). 1166:- http_handler(pldoc_pkg(.),    pldoc_package,          [prefix]). 1167:- http_handler(pldoc_man(.),    pldoc_refman,           [prefix]). 1168:- http_handler(pldoc(packages), pldoc_package_overview, []).
 pldoc_jpl(+Request)
Hack to include JPL documentation in server.
 1174pldoc_jpl(Request) :-
 1175    memberchk(path_info(JPLFile), Request),
 1176    atom_concat('doc/packages/jpl', JPLFile, Path),
 1177    http_reply_file(swi(Path), [], Request).
 pldoc_package(+Request)
HTTP handler for PlDoc package documentation. Accepts /pldoc/package/<package>.{html,gif}. The path =/pldoc/package/<package>= is redirected to the canonical object version.
 1186pldoc_package(Request) :-
 1187    (   \+ option(path_info(_), Request)
 1188    ->  true
 1189    ;   option(path_info(/), Request)
 1190    ),
 1191    http_link_to_id(pldoc_object, [object=packages], HREF),
 1192    http_redirect(see_other, HREF, Request).
 1193pldoc_package(Request) :-
 1194    memberchk(path_info(Img), Request),
 1195    file_mime_type(Img, image/_),
 1196    !,
 1197    http_reply_file(swi_man_packages(Img), [], Request).
 1198pldoc_package(Request) :-
 1199    memberchk(path_info('jpl'), Request),
 1200    !,
 1201    memberchk(path(Path0), Request),
 1202    atom_concat(Path0, /, Path),
 1203    http_redirect(moved, Path, Request).
 1204pldoc_package(Request) :-
 1205    memberchk(path_info(JPLFile), Request),
 1206    (   JPLFile == 'jpl/'
 1207    ->  Path = 'doc/packages/jpl/index.html'
 1208    ;   sub_atom(JPLFile, 0, _, _, 'jpl/')
 1209    ->  atom_concat('doc/packages/', JPLFile, Path)
 1210    ),
 1211    http_reply_file(swi(Path), [], Request).
 1212pldoc_package(Request) :-
 1213    memberchk(path_info(PkgDoc), Request),
 1214    ensure_html_ext(PkgDoc, PkgHtml),
 1215    atom_concat('packages/', PkgHtml, Path),
 1216    term_to_atom(section(Path), Object),
 1217    http_link_to_id(pldoc_object, [object=Object], HREF),
 1218    http_redirect(see_other, HREF, Request).
 1219
 1220ensure_html_ext(Pkg, PkgHtml) :-
 1221    file_name_extension(_, html, Pkg),
 1222    !,
 1223    PkgHtml = Pkg.
 1224ensure_html_ext(Pkg, PkgHtml) :-
 1225    file_name_extension(Pkg, html, PkgHtml).
 pldoc_package_overview(+Request)
Provide an overview of the package documentation
 1231pldoc_package_overview(_Request) :-
 1232    reply_html_page(
 1233	pldoc(packages),
 1234	title('SWI-Prolog package documentation'),
 1235	\package_overview([])).
 1236:- endif.
 paperback(+Options)//
Link to the paperback version of the manual.
 1242paperback(_Options) -->
 1243    { expand_url_path(swipl_book(.), HREF)
 1244    },
 1245    html([ h2('The manual as a book'),
 1246	   p([ 'A paperback version of the manual is ',
 1247	       a(href(HREF), 'available'), '.'
 1248	     ])
 1249	 ]).
 pldoc_refman(+Request)
HTTP handler for PlDoc Reference Manual access. Accepts /refman/[<package>.html.]
 1256pldoc_refman(Request) :-
 1257    memberchk(path_info(Section), Request),
 1258    \+ sub_atom(Section, _, _, _, /),
 1259    Obj = section(0,_,_,_),
 1260    manual_object(Obj, Title, File, manual, _),
 1261    file_base_name(File, Section),
 1262    !,
 1263    reply_html_page(pldoc(man),
 1264		    title(Title),
 1265		    \object_page(Obj, [])).
 1266pldoc_refman(Request) :-                % server Contents.html
 1267    \+ memberchk(path_info(_), Request),
 1268    !,
 1269    http_link_to_id(pldoc_object, [object(manual)], HREF),
 1270    http_redirect(see_other, HREF, Request).
 1271pldoc_refman(Request) :-
 1272    memberchk(path(Path), Request),
 1273    existence_error(http_location, Path).
 1274
 1275
 1276		 /*******************************
 1277		 *          HOOK SEARCH         *
 1278		 *******************************/
 1279
 1280prolog:doc_object_summary(section(ID), Class, File, Summary) :-
 1281    nonvar(ID),                     % when generating, only do full ones
 1282    manual_object(section(_Level, _No, ID, _Path), Summary, File, Class, _Offset).
 1283prolog:doc_object_summary(Obj, Class, File, Summary) :-
 1284    manual_object(Obj, Summary, File, Class, _Offset).
 1285
 1286prolog:doc_object_page(Obj, Options) -->
 1287    man_page(Obj, [no_manual(fail),footer(false)|Options]).
 prolog:doc_object_link(+Obj, +Options)//
Provide the HTML to describe Obj for linking purposes.
 1293prolog:doc_object_link(Obj, Options) -->
 1294    { Obj = section(_,_,_,_) },
 1295    !,
 1296    section_link(Obj, Options).
 1297prolog:doc_object_link(Obj0, Options) -->
 1298    { Obj0 = section(ID),
 1299      Obj = section(_Level, _No, ID, _Path),
 1300      manual_object(Obj, _, _, _, _)
 1301    },
 1302    !,
 1303    section_link(Obj, Options).
 1304prolog:doc_object_link(Obj, Options) -->
 1305    { Obj = c(Function) },
 1306    !,
 1307    function_link(Function, Options).
 1308prolog:doc_object_link(root, _) -->
 1309    !,
 1310    html('Documentation').
 1311prolog:doc_object_link(manual, _Options) -->
 1312    !,
 1313    html('Reference manual').
 1314prolog:doc_object_link(packages, _) -->
 1315    html('Packages').
 1316
 1317prolog:doc_category(manual,   30, 'Reference Manual').
 1318prolog:doc_category(packages, 40, 'Packages').
 1319
 1320prolog:doc_file_index_header(File, Options) -->
 1321    { Section = section(_Level, _No, _ID, File),
 1322      manual_object(Section, _Summary, File, _Cat, _Offset)
 1323    },
 1324    !,
 1325    html(tr(th([colspan(3), class(section)],
 1326	       [ \object_ref(Section,
 1327			     [ secref_style(number_title)
 1328			     | Options
 1329			     ])
 1330	       ]))).
 1331
 1332prolog:doc_object_title(Obj, Title) :-
 1333    Obj = section(_,_,_,_),
 1334    manual_object(Obj, Title, _, _, _),
 1335    !.
 1336
 1337prolog:doc_canonical_object(section(_Level, _No, ID, _Path),
 1338			    section(ID)).
 prolog:doc_object_href(+Object, -HREF) is semidet
Produce a HREF for section objects.
 1344prolog:doc_object_href(section(ID), HREF) :-
 1345    nonvar(ID),
 1346    atom_concat('sec:', Sec, ID),
 1347    http_link_to_id(pldoc_man, [section(Sec)], HREF).
 1348prolog:doc_object_href(section(_Level, _No, ID, _Path), HREF) :-
 1349    nonvar(ID),
 1350    atom_concat('sec:', Sec, ID),
 1351    http_link_to_id(pldoc_man, [section(Sec)], HREF).
 1352
 1353		 /*******************************
 1354		 *           NO HTTP		*
 1355		 *******************************/
 1356
 1357:- if(\+current_predicate(http_link_to_id/3)). 1358
 1359http_link_to_id(Id, Parameters, HREF) :-
 1360    must_be(list, Parameters),
 1361    format(atom(Location), '/pldoc/~w', [Id]),
 1362    (   Parameters == []
 1363    ->  HREF = Location
 1364    ;   uri_data(path, Components, Location),
 1365        uri_query_components(String, Parameters),
 1366        uri_data(search, Components, String),
 1367        uri_components(HREF, Components)
 1368    ).
 1369
 1370:- endif. 1371
 1372		 /*******************************
 1373		 *           MESSAGES           *
 1374		 *******************************/
 1375
 1376:- multifile prolog:message//1. 1377
 1378prolog:message(pldoc(no_section_id(File, Title))) -->
 1379    [ 'PlDoc: ~w: no id for section "~w"'-[File, Title] ].
 1380prolog:message(pldoc(duplicate_ids(L))) -->
 1381    [ 'PlDoc: duplicate manual section IDs:'-[], nl
 1382    ],
 1383    duplicate_ids(L).
 1384
 1385duplicate_ids([]) --> [].
 1386duplicate_ids([H|T]) --> duplicate_id(H), duplicate_ids(T).
 1387
 1388duplicate_id(Id) -->
 1389    { findall(File, manual_object(section(_,_,Id,File),_,_,_,_), Files) },
 1390    [ '    ~w: ~p'-[Id, Files], nl ]