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
   47:- autoload(doc_html,
   48	    [ object_tree/5, private/2, object_page_header/4, objects/4,
   49	      object_href/2, object_synopsis/4, object_footer/4,
   50	      object_page_footer/4,
   51	      object_ref/4, object_page/4,
   52	      object_source_button//2
   53	    ]).   54:- autoload(doc_process,[doc_comment/4]).   55:- autoload(doc_search,[search_form/3]).   56:- autoload(doc_util,[atom_to_object/2,atom_pi/2]).   57:- autoload(man_index,[manual_object/5]).   58:- autoload(library(apply),[maplist/2,maplist/3,convlist/3]).   59:- autoload(library(debug),[assertion/1,debug/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:
Name / Arity
Predicate indicator: display documentation of the predicate
f(Name/Arity)
display documentation of an arithmetic function
c(Function)
display documentation of a C API function
section(Level, Number, Id, File)
Display a section of the manual
sec(#(DocFile,Id))
Display a section of the manual (from short form)

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,
  566	      ( doc_comment(Full, File:_, _, _),
  567		\+ private(Full, Options)
  568	      ),
  569	      Pairs),
  570      Pairs \== [],
  571      pairs_keys(Pairs, Objs)
  572    },
  573    !,
  574    html_requires(pldoc),
  575    (   { Pairs = [_-File] }
  576    ->  object_page_header(File, Options)
  577    ;   object_page_header(-, Options)
  578    ),
  579    { merge_options(Options,
  580		    [ synopsis(true),
  581		      navtree(true)
  582		    ], Options2)
  583    },
  584    objects(Objs, Options2).
  585man_page(Obj, Options) -->                      % failure
  586    { \+ option(no_manual(fail), Options)
  587    },
  588    html_requires(pldoc),
  589    man_links([], Options),
  590    html(p(class(noman),
  591	   [ 'Sorry, No manual entry for ',
  592	     b('~w'-[Obj])
  593	   ])).
  594
  595%special_node(manual).          % redirected to the Introduction section
  596special_node(root).
  597special_node(packages).
  598
  599full_page(Obj, _) :-
  600    var(Obj), !, fail.
  601full_page(Obj, Obj) :-
  602    Obj = section(_,_,_,_),
  603    !.
  604full_page(section(ID), section(_,_,ID,_)) :- !.
  605full_page(manual, section(_,_,'sec:intro',_)) :- !.
  606full_page(Obj0, Obj) :-
  607    ground(Obj0),
  608    alt_obj(Obj0, Obj),
  609    manual_object(Obj, _, _, _, _),
  610    !.
  611full_page(Obj, Obj) :-
  612    ground(Obj).
  613
  614alt_obj(Obj, Obj).
  615alt_obj(Name/Arity, Name//DCGArity) :-
  616    integer(Arity),
  617    Arity >= 2,
  618    DCGArity is Arity - 2.
  619alt_obj(Name//DCGArity, Name/Arity) :-
  620    integer(DCGArity),
  621    Arity is DCGArity + 2.
 full_object(+Object, -Full) is semidet
Translate to canonical PlDoc object
  627full_object(Object, M:Obj) :-
  628    qualify(Object, M:Obj0),
  629    alt_obj(Obj0, Obj),
  630    doc_comment(M:Obj, _, _, _),
  631    !.
  632
  633qualify(M:O, M:O).
  634qualify(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.

  646man_qualified_object(Text, Parent, LibOpt, Object, Section) :-
  647    atom(Text),
  648    atom_pi(Text, PI),
  649    ground(PI),
  650    !,
  651    man_qualified_object_2(PI, Parent, LibOpt, Object, Section).
  652man_qualified_object(Object0, Parent, LibOpt, Object, Section) :-
  653    man_qualified_object_2(Object0, Parent, LibOpt, Object, Section).
  654
  655man_qualified_object_2(Name/Arity, Parent,
  656		       LibOpt, Module:Name/Arity, Section) :-
  657    object_module(Parent, Module, Section, LibOpt),
  658    !.
  659man_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.
  667:- public
  668    man_synopsis//2.                % called from man_match//2
  669
  670man_synopsis(PI, Section) -->
  671    man_synopsis(PI, Section, []).
  672
  673man_synopsis(PI, Section, Options) -->
  674    { object_href(Section, HREF)
  675    },
  676    object_synopsis(PI, [href(HREF)|Options]).
 object_module(+Section0, -Module, -Section, -LibOpt) is semidet
Find the module documented by Section.
  682object_module(Section0, Module, Section, [source(Term)]) :-
  683    parent_section_ndet(Section0, Section),
  684    manual_object(Section, Title, _File, _Class, _Offset),
  685    (   once(sub_atom(Title, B, _, _, :)),
  686	sub_atom(Title, 0, B, _, Atom),
  687	catch(term_to_atom(Term, Atom), _, fail),
  688	ground(Term),
  689	Term = library(_)
  690    ->  !,
  691	absolute_file_name(Term, PlFile,
  692			   [ file_type(prolog),
  693			     access(read),
  694			     file_errors(fail)
  695			   ]),
  696	(   module_property(Module, file(PlFile))
  697	->  true
  698	;   xref_public_list(PlFile, -,         % module is not loaded
  699			     [ module(Module)
  700			     ])
  701	)
  702    ).
  703
  704parent_section_ndet(Section, Section).
  705parent_section_ndet(Section, Parent) :-
  706    parent_section(Section, Parent0),
  707    parent_section_ndet(Parent0, Parent).
  708
  709
  710man_matches(Matches, Object, Options) -->
  711    { option(navtree(false), Options) },
  712    !,
  713    man_matches_nt(Matches, Object, Options).
  714man_matches(Matches, Object, Options) -->
  715    html([ div(class(navtree),
  716	       div(class(navwindow),
  717		   \man_nav_tree(Object, Options))),
  718	   div(class(navcontent),
  719	       \man_matches_nt(Matches, Object, Options))
  720	 ]).
  721
  722
  723man_matches_nt([Match], Object, Options) -->
  724    { option(footer(true), Options, true) },
  725    !,
  726    man_match(Match, Object, Options),
  727    object_page_footer(Object, []).
  728man_matches_nt(Matches, Object, Options) -->
  729    man_matches_list(Matches, Object, Options).
  730
  731man_matches_list([], _, _) --> [].
  732man_matches_list([H|T], Obj, Options) -->
  733    man_match(H, Obj, Options),
  734    man_matches_list(T, Obj, Options).
 man_match(+Term, +Object, +Options)// is det
If possible, insert the synopsis into the title of the description.
  741man_match(packages, packages, _) -->
  742    !,
  743    html({|html||
  744	  <p>
  745	  Packages are relatively independent add-on libraries that
  746	  may not be available in all installations.  Packages are
  747	  part of the source code releases of SWI-Prolog and may be
  748	  enabled or disabled during the build.</p>
  749
  750	  <p>
  751	  See also <a href="/pack/list">Add-ons</a> for extensions
  752	  provided by the community that must be installed separately
  753	  using
  754	  <a href="/pldoc/doc_for?object=pack_install/1">pack_install/1</a>.</p>
  755	 |}).
  756man_match(root, root, _) -->
  757    !,
  758    man_overview([]).
  759man_match((Parent+Path)-(Obj+DOM), Obj, Options) -->
  760    { \+ option(synopsis(false), Options),
  761      DOM = [element(dt,A,C0)|DD],
  762      convlist(dt_obj, DOM, Objs),
  763      option(link_source(Link), Options, true),
  764      man_qualified_object(Obj, Parent, LibOpt, QObj, Section),
  765      !,
  766      C = [ span(style('float:right;margin-left:5px;'),
  767		 \object_source_button(QObj, [source_link(Link)]))
  768	  | C0
  769	  ]
  770    },
  771    dom_list([ element(dt,[],[\man_synopsis(QObj, Section, LibOpt)]),
  772	       element(dt,A,C)
  773	     | DD
  774	     ], Path, Options),
  775    object_footer(Objs, Options).
  776man_match((_Parent+Path)-(Obj+DOM), Obj, Options) -->
  777    dom_list(DOM, Path, Options).
  778
  779dt_obj(element(dt,_,C), Obj) :-
  780    xpath(C, //a(@id=Atom), _),
  781    atom_to_object(Atom, Obj).
  782
  783:- html_meta
  784    dom_list(html, +, +, ?, ?).  785
  786dom_list(_:[], _, _) -->
  787    !,
  788    [].
  789dom_list(M:[H|T], Path, Options) -->
  790    dom(H, Path, Options),
  791    dom_list(M:T, Path, Options).
  792
  793dom(element(E, Atts, Content), Path, Options) -->
  794    !,
  795    dom_element(E, Atts, Content, Path, Options).
  796dom(CDATA, _, _) -->
  797    html(CDATA).
  798
  799dom_element(a, _, [], _, _) -->                % Useless back-references
  800    !,
  801    [].
  802dom_element(a, Att, Content, Path, Options) -->
  803    { memberchk(href=HREF, Att),
  804      (   memberchk(class=Class, Att)
  805      ->  true
  806      ;   Class = unknown
  807      ),
  808      rewrite_ref(Class, HREF, Path, Myref, Options)
  809    },
  810    !,
  811    html(a(href(Myref), \dom_list(Content, Path, Options))).
  812dom_element(span, Att, [CDATA], _, Options) -->
  813    { memberchk(class='pred-ext', Att),
  814      atom_pi(CDATA, PI),
  815      documented(PI),
  816      (   option(server(false), Options)
  817      ->  public_link(predicate(CDATA), HREF)
  818      ;   http_link_to_id(pldoc_man, [predicate=CDATA], HREF)
  819      )
  820    },
  821    !,
  822    html(a(href(HREF), CDATA)).
  823dom_element(img, Att0, [], Path, _Options) -->
  824    { selectchk(src=Src, Att0, Att1),
  825      relative_file_name(ImgFile, Path, Src),
  826      handler_alias(Handler, DirAlias),
  827      absolute_file_name(DirAlias, Dir,
  828			 [ file_errors(fail),
  829			   solutions(all),
  830			   file_type(directory)
  831			 ]),
  832      ensure_slash(Dir, DirS),
  833      atom_concat(DirS, NewSrc, ImgFile),
  834      !,
  835      http_link_to_id(Handler, [], ManRef),
  836      directory_file_path(ManRef, NewSrc, NewPath),
  837      Begin =.. [img, src(NewPath) | Att1]
  838    },
  839    html_begin(Begin),
  840    html_end(img).
  841dom_element(div, Att, _, _, _) -->
  842    { memberchk(class=navigate, Att) },
  843    !.
  844dom_element(html, _, Content, Path, Options) -->
  845    !,                              % do not emit a html for the second time
  846    dom_list(Content, Path, Options).
  847dom_element(head, _, Content, Path, Options) -->
  848    !,                              % do not emit a head for the second time
  849    dom_list(Content, Path, Options).
  850dom_element(title, _, _, _, _) --> !.
  851dom_element(link, _, _, _, _) --> !.
  852dom_element(body, _, Content, Path, Options) -->
  853    !,                              % do not emit a body for the second time
  854    dom_list(Content, Path, Options).
  855dom_element(Name, Attrs, Content, Path, Options) -->
  856    { Begin =.. [Name|Attrs] },
  857    html_begin(Begin),
  858    dom_list(Content, Path, Options),
  859    html_end(Name).
  860
  861handler_alias(manual_file,   swi_man_manual(.)).
  862handler_alias(pldoc_package, swi_man_packages(.)).
  863
  864ensure_slash(Dir, DirS) :-
  865    (   sub_atom(Dir, _, _, 0, /)
  866    ->  DirS = Dir
  867    ;   atom_concat(Dir, /, DirS)
  868    ).
 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.
  877public_link(predicate(CDATA), HREF) :-
  878    uri_encoded(query_value, CDATA, Encoded),
  879    atom_concat('https://www.swi-prolog.org/pldoc/doc_for?object=',
  880		Encoded, HREF).
 documented(+PI) is semidet
True if we have documentation about PI
  887documented(PI) :-
  888    manual_object(PI, _, _, _, _),
  889    !.
  890documented(PI) :-
  891    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
  925rewrite_ref(_Class, Ref, _Path, Ref, Options) :-
  926    option(server(false), Options),
  927    !.
  928rewrite_ref(Class, Ref0, Path, ManRef, _Options) :-
  929    rewrite_ref(Class, Ref0, Path, ManRef).
  930
  931rewrite_ref(pred, Ref0, _, Ref) :-              % Predicate/DCG reference
  932    sub_atom(Ref0, _, _, A, '#'),
  933    !,
  934    sub_atom(Ref0, _, A, 0, Fragment),
  935    atom_to_object(Fragment, PI),
  936    manual_object(PI, _, _, _, _),
  937    uri_encoded(query_value, Fragment, Enc),
  938    http_location_by_id(pldoc_man, ManHandler),
  939    format(string(Ref), '~w?predicate=~w', [ManHandler, Enc]).
  940rewrite_ref(function, Ref0, _, Ref) :-          % Arithmetic function reference
  941    sub_atom(Ref0, _, _, A, '#'),
  942    !,
  943    sub_atom(Ref0, _, A, 0, Fragment),
  944    atom_to_object(Fragment, PI),
  945    manual_object(PI, _, _, _, _),
  946    PI=f(Name/Arity),
  947    format(atom(PIName), '~w/~w', [Name,Arity]),
  948    uri_encoded(query_value, PIName, Enc),
  949    http_location_by_id(pldoc_man, ManHandler),
  950    format(string(Ref), '~w?function=~w', [ManHandler, Enc]).
  951rewrite_ref(func, Ref0, _, Ref) :-              % C-API reference
  952    sub_atom(Ref0, _, _, A, '#'),
  953    !,
  954    sub_atom(Ref0, _, A, 0, Fragment),
  955    atom_to_object(Fragment, Obj),
  956    manual_object(Obj, _, _, _, _),
  957    Obj = c(Function),
  958    uri_encoded(query_value, Function, Enc),
  959    http_location_by_id(pldoc_man, ManHandler),
  960    format(string(Ref), '~w?CAPI=~w', [ManHandler, Enc]).
  961rewrite_ref(sec, Ref0, Path, Ref) :-            % Section inside a file
  962    sub_atom(Ref0, B, _, A, '#'),
  963    !,
  964    sub_atom(Ref0, _, A, 0, Fragment),
  965    sub_atom(Ref0, 0, B, _, File),
  966    referenced_section(Fragment, File, Path, Section),
  967    object_href(Section, Ref).
  968rewrite_ref(sec, File, Path, Ref) :-            % Section is a file
  969    file_directory_name(Path, Dir),
  970    atomic_list_concat([Dir, /, File], SecPath),
  971    Obj = section(_, _, _, SecPath),
  972    manual_object(Obj, _, _, _, _),
  973    !,
  974    object_href(Obj, Ref).
  975rewrite_ref(cite, Ref0, Path, Ref) :-           % Citation (bit hard-wired)
  976    debug(pldoc(cite), 'Cite ref ~q ~q', [Ref0, Path]),
  977    sub_atom(Ref0, _, _, A, '#'),
  978    !,
  979    sub_atom(Ref0, _, A, 0, Fragment),
  980    uri_encoded(query_value, Fragment, Enc),
  981    http_location_by_id(pldoc_man, ManHandler),
  982    format(string(Ref), '~w?section=bibliography#~w', [ManHandler, Enc]).
  983rewrite_ref(flag, Ref0, Path, Ref) :-
  984    sub_atom(Ref0, B, _, A, '#'),
  985    !,
  986    sub_atom(Ref0, 0, B, _, File),
  987    sub_atom(Ref0, _, A, 0, Fragment),
  988    file_directory_name(Path, Dir),
  989    atomic_list_concat([Dir, /, File], SecPath),
  990    Obj = section(_, _, _, SecPath),
  991    manual_object(Obj, _, _, _, _),
  992    !,
  993    object_href(Obj, Ref1),
  994    format(string(Ref), '~w#~w', [Ref1, Fragment]).
  995rewrite_ref(gloss, Ref0, Path, Ref) :-
  996    sub_atom(Ref0, B, _, A, '#'),
  997    !,
  998    sub_atom(Ref0, 0, B, _, File),
  999    sub_atom(Ref0, _, A, 0, Fragment),
 1000    file_directory_name(Path, Dir),
 1001    atomic_list_concat([Dir, /, File], SecPath),
 1002    Obj = section(_, _, _, SecPath),
 1003    manual_object(Obj, _, _, _, _),
 1004    !,
 1005    object_href(Obj, Ref1),
 1006    format(string(Ref), '~w#~w', [Ref1, Fragment]).
 referenced_section(+Fragment, +File, +Path, -Section)
 1010referenced_section(Fragment, File, Path, section(Level, Nr, ID, SecPath)) :-
 1011    atom_concat('sec:', Nr, Fragment),
 1012    (   File == ''
 1013    ->  SecPath = Path
 1014    ;   file_directory_name(Path, Dir),
 1015	atomic_list_concat([Dir, /, File], SecPath)
 1016    ),
 1017    manual_object(section(Level, Nr, ID, SecPath), _, _, _, _).
 man_links(+ParentPaths, +Options)// is det
Create top link structure for manual pages.
 1024man_links(ParentPaths, Options) -->
 1025    prolog:doc_page_header(parents(ParentPaths), Options),
 1026    !.
 1027man_links(ParentPaths, Options) -->
 1028    { option(links(true), Options, true),
 1029      option(header(true), Options, true)
 1030    },
 1031    !,
 1032    html([ div(class(navhdr),
 1033	       [ div(class(jump), \man_parent(ParentPaths)),
 1034		 div(class(search), \search_form(Options)),
 1035		 br(clear(right))
 1036	       ]),
 1037	   p([])
 1038	 ]).
 1039man_links(_, _) -->
 1040    [].
 1041
 1042man_parent(ParentPaths) -->
 1043    { maplist(parent_to_section, ParentPaths, [Section|MoreSections]),
 1044      maplist(=(Section), MoreSections)
 1045    },
 1046    !,
 1047    object_ref(Section, [secref_style(number_title)]).
 1048man_parent(_) --> [].
 1049
 1050parent_to_section(X+_, X) :-
 1051    X = section(_,_,_,_),
 1052    !.
 1053parent_to_section(File+_, Section) :-
 1054    atom(File),
 1055    manual_object(Section, _Title, File, _Class, _Offset),
 1056    !.
 section_link(+Obj, +Options)// is det
Create link to a section. Options recognised:
secref_style(+Style)
One of number, title or number_title.
 1065section_link(Section, Options) -->
 1066    { option(secref_style(Style), Options, number)
 1067    },
 1068    section_link(Style, Section, Options).
 1069
 1070section_link(number, section(_, Number, _, _), _Options) -->
 1071    !,
 1072    (   {Number == '0'}             % Title.  Package?
 1073    ->  []
 1074    ;   html(['Sec. ', Number])
 1075    ).
 1076section_link(title, Obj, _Options) -->
 1077    !,
 1078    { manual_object(Obj, Title, _File, _Class, _Offset)
 1079    },
 1080    html(Title).
 1081section_link(_, Obj, _Options) -->
 1082    !,
 1083    { Obj = section(_, Number, _, _),
 1084      manual_object(Obj, Title, _File, _Class, _Offset)
 1085    },
 1086    (   { Number == '0' }
 1087    ->  html(Title)
 1088    ;   html([Number, ' ', Title])
 1089    ).
 function_link(+Function, +Options) is det
Create a link to a C-function
 1095function_link(Function, _) -->
 1096    html([Function, '()']).
 1097
 1098
 1099		 /*******************************
 1100		 *       INDICES & OVERVIEW     *
 1101		 *******************************/
 man_overview(+Options)// is det
Provide a toplevel overview on the manual: the reference manual and the available packages.
 1108man_overview(Options) -->
 1109    { http_absolute_location(pldoc_man(.), RefMan, [])
 1110    },
 1111    html([ h1('SWI-Prolog documentation'),
 1112	   blockquote(class(refman_link),
 1113		      a(href(RefMan),
 1114			'SWI-Prolog reference manual')),
 1115	   \package_overview(Options),
 1116	   \paperback(Options)
 1117	 ]).
 1118
 1119package_overview(Options) -->
 1120    html([ h2(class(package_doc_title),
 1121	      'SWI-Prolog package documentation'),
 1122	   blockquote(class(package_overview),
 1123		      \packages(Options))
 1124	 ]).
 1125
 1126packages(Options) -->
 1127    { findall(Pkg, current_package(Pkg), Pkgs)
 1128    },
 1129    packages(Pkgs, Options).
 1130
 1131packages([], _) -->
 1132    [].
 1133packages([Pkg|T], Options) -->
 1134    package(Pkg, Options),
 1135    packages(T, Options).
 1136
 1137package(pkg(Title, HREF, HavePackage), Options) -->
 1138    { package_class(HavePackage, Class, Options)
 1139    },
 1140    html(div(class(Class),
 1141	     a([href(HREF)], Title))).
 1142
 1143package_class(true,  pkg_link, _).
 1144package_class(false, no_pkg_link, _).
 1145
 1146current_package(pkg(Title, HREF, HavePackage)) :-
 1147    manual_object(section(0, _, _, _), Title, File, packages, _),
 1148    file_base_name(File, FileNoDir),
 1149    file_name_extension(Base, _, FileNoDir),
 1150    (   exists_source(library(Base))
 1151    ->  HavePackage = true
 1152    ;   HavePackage = false
 1153    ),
 1154    http_absolute_location(pldoc_pkg(FileNoDir), HREF, []).
 1155
 1156
 1157:- if(current_predicate(http_handler/3)). 1158:- http_handler(pldoc(jpl),      pldoc_jpl,              [prefix]). 1159:- http_handler(pldoc_pkg(.),    pldoc_package,          [prefix]). 1160:- http_handler(pldoc_man(.),    pldoc_refman,           [prefix]). 1161:- http_handler(pldoc(packages), pldoc_package_overview, []).
 pldoc_jpl(+Request)
Hack to include JPL documentation in server.
 1167pldoc_jpl(Request) :-
 1168    memberchk(path_info(JPLFile), Request),
 1169    atom_concat('doc/packages/jpl', JPLFile, Path),
 1170    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.
 1179pldoc_package(Request) :-
 1180    (   \+ option(path_info(_), Request)
 1181    ->  true
 1182    ;   option(path_info(/), Request)
 1183    ),
 1184    http_link_to_id(pldoc_object, [object=packages], HREF),
 1185    http_redirect(see_other, HREF, Request).
 1186pldoc_package(Request) :-
 1187    memberchk(path_info(Img), Request),
 1188    file_mime_type(Img, image/_),
 1189    !,
 1190    http_reply_file(swi_man_packages(Img), [], Request).
 1191pldoc_package(Request) :-
 1192    memberchk(path_info('jpl'), Request),
 1193    !,
 1194    memberchk(path(Path0), Request),
 1195    atom_concat(Path0, /, Path),
 1196    http_redirect(moved, Path, Request).
 1197pldoc_package(Request) :-
 1198    memberchk(path_info(JPLFile), Request),
 1199    (   JPLFile == 'jpl/'
 1200    ->  Path = 'doc/packages/jpl/index.html'
 1201    ;   sub_atom(JPLFile, 0, _, _, 'jpl/')
 1202    ->  atom_concat('doc/packages/', JPLFile, Path)
 1203    ),
 1204    http_reply_file(swi(Path), [], Request).
 1205pldoc_package(Request) :-
 1206    memberchk(path_info(PkgDoc), Request),
 1207    ensure_html_ext(PkgDoc, PkgHtml),
 1208    atom_concat('packages/', PkgHtml, Path),
 1209    term_to_atom(section(Path), Object),
 1210    http_link_to_id(pldoc_object, [object=Object], HREF),
 1211    http_redirect(see_other, HREF, Request).
 1212
 1213ensure_html_ext(Pkg, PkgHtml) :-
 1214    file_name_extension(_, html, Pkg),
 1215    !,
 1216    PkgHtml = Pkg.
 1217ensure_html_ext(Pkg, PkgHtml) :-
 1218    file_name_extension(Pkg, html, PkgHtml).
 pldoc_package_overview(+Request)
Provide an overview of the package documentation
 1224pldoc_package_overview(_Request) :-
 1225    reply_html_page(
 1226	pldoc(packages),
 1227	title('SWI-Prolog package documentation'),
 1228	\package_overview([])).
 1229:- endif.
 paperback(+Options)//
Link to the paperback version of the manual.
 1235paperback(_Options) -->
 1236    { expand_url_path(swipl_book(.), HREF)
 1237    },
 1238    html([ h2('The manual as a book'),
 1239	   p([ 'A paperback version of the manual is ',
 1240	       a(href(HREF), 'available'), '.'
 1241	     ])
 1242	 ]).
 pldoc_refman(+Request)
HTTP handler for PlDoc Reference Manual access. Accepts /refman/[<package>.html.]
 1249pldoc_refman(Request) :-
 1250    memberchk(path_info(Section), Request),
 1251    \+ sub_atom(Section, _, _, _, /),
 1252    Obj = section(0,_,_,_),
 1253    manual_object(Obj, Title, File, manual, _),
 1254    file_base_name(File, Section),
 1255    !,
 1256    reply_html_page(pldoc(man),
 1257		    title(Title),
 1258		    \object_page(Obj, [])).
 1259pldoc_refman(Request) :-                % server Contents.html
 1260    \+ memberchk(path_info(_), Request),
 1261    !,
 1262    http_link_to_id(pldoc_object, [object(manual)], HREF),
 1263    http_redirect(see_other, HREF, Request).
 1264pldoc_refman(Request) :-
 1265    memberchk(path(Path), Request),
 1266    existence_error(http_location, Path).
 1267
 1268
 1269		 /*******************************
 1270		 *          HOOK SEARCH         *
 1271		 *******************************/
 1272
 1273prolog:doc_object_summary(section(ID), Class, File, Summary) :-
 1274    nonvar(ID),                     % when generating, only do full ones
 1275    manual_object(section(_Level, _No, ID, _Path), Summary, File, Class, _Offset).
 1276prolog:doc_object_summary(Obj, Class, File, Summary) :-
 1277    manual_object(Obj, Summary, File, Class, _Offset).
 1278
 1279prolog:doc_object_page(Obj, Options) -->
 1280    man_page(Obj, [no_manual(fail),footer(false)|Options]).
 prolog:doc_object_link(+Obj, +Options)//
Provide the HTML to describe Obj for linking purposes.
 1286prolog:doc_object_link(Obj, Options) -->
 1287    { Obj = section(_,_,_,_) },
 1288    !,
 1289    section_link(Obj, Options).
 1290prolog:doc_object_link(Obj0, Options) -->
 1291    { Obj0 = section(ID),
 1292      Obj = section(_Level, _No, ID, _Path),
 1293      manual_object(Obj, _, _, _, _)
 1294    },
 1295    !,
 1296    section_link(Obj, Options).
 1297prolog:doc_object_link(Obj, Options) -->
 1298    { Obj = c(Function) },
 1299    !,
 1300    function_link(Function, Options).
 1301prolog:doc_object_link(root, _) -->
 1302    !,
 1303    html('Documentation').
 1304prolog:doc_object_link(manual, _Options) -->
 1305    !,
 1306    html('Reference manual').
 1307prolog:doc_object_link(packages, _) -->
 1308    html('Packages').
 1309
 1310prolog:doc_category(manual,   30, 'Reference Manual').
 1311prolog:doc_category(packages, 40, 'Packages').
 1312
 1313prolog:doc_file_index_header(File, Options) -->
 1314    { Section = section(_Level, _No, _ID, File),
 1315      manual_object(Section, _Summary, File, _Cat, _Offset)
 1316    },
 1317    !,
 1318    html(tr(th([colspan(3), class(section)],
 1319	       [ \object_ref(Section,
 1320			     [ secref_style(number_title)
 1321			     | Options
 1322			     ])
 1323	       ]))).
 1324
 1325prolog:doc_object_title(Obj, Title) :-
 1326    Obj = section(_,_,_,_),
 1327    manual_object(Obj, Title, _, _, _),
 1328    !.
 1329
 1330prolog:doc_canonical_object(section(_Level, _No, ID, _Path),
 1331			    section(ID)).
 prolog:doc_object_href(+Object, -HREF) is semidet
Produce a HREF for section objects.
 1337prolog:doc_object_href(section(ID), HREF) :-
 1338    nonvar(ID),
 1339    atom_concat('sec:', Sec, ID),
 1340    http_link_to_id(pldoc_man, [section(Sec)], HREF).
 1341prolog:doc_object_href(section(_Level, _No, ID, _Path), HREF) :-
 1342    nonvar(ID),
 1343    atom_concat('sec:', Sec, ID),
 1344    http_link_to_id(pldoc_man, [section(Sec)], HREF).
 1345
 1346		 /*******************************
 1347		 *           NO HTTP		*
 1348		 *******************************/
 1349
 1350:- if(\+current_predicate(http_link_to_id/3)). 1351
 1352http_link_to_id(Id, Parameters, HREF) :-
 1353    must_be(list, Parameters),
 1354    format(atom(Location), '/pldoc/~w', [Id]),
 1355    (   Parameters == []
 1356    ->  HREF = Location
 1357    ;   uri_data(path, Components, Location),
 1358        uri_query_components(String, Parameters),
 1359        uri_data(search, Components, String),
 1360        uri_components(HREF, Components)
 1361    ).
 1362
 1363:- endif. 1364
 1365		 /*******************************
 1366		 *           MESSAGES           *
 1367		 *******************************/
 1368
 1369:- multifile prolog:message//1. 1370
 1371prolog:message(pldoc(no_section_id(File, Title))) -->
 1372    [ 'PlDoc: ~w: no id for section "~w"'-[File, Title] ].
 1373prolog:message(pldoc(duplicate_ids(L))) -->
 1374    [ 'PlDoc: duplicate manual section IDs:'-[], nl
 1375    ],
 1376    duplicate_ids(L).
 1377
 1378duplicate_ids([]) --> [].
 1379duplicate_ids([H|T]) --> duplicate_id(H), duplicate_ids(T).
 1380
 1381duplicate_id(Id) -->
 1382    { findall(File, manual_object(section(_,_,Id,File),_,_,_,_), Files) },
 1383    [ '    ~w: ~p'-[Id, Files], nl ]