View source with formatted 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-2017, University of Amsterdam
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(pldoc_http,
   37          [ doc_enable/1,               % +Boolean
   38            doc_server/1,               % ?Port
   39            doc_server/2,               % ?Port, +Options
   40            doc_browser/0,
   41            doc_browser/1               % +What
   42          ]).   43:- use_module(library(pldoc)).   44:- if(exists_source(library(http/thread_httpd))).   45:- use_module(library(http/thread_httpd)).   46:- endif.   47:- use_module(library(http/http_parameters)).   48:- use_module(library(http/html_write)).   49:- use_module(library(http/mimetype)).   50:- use_module(library(dcg/basics)).   51:- use_module(library(http/http_dispatch)).   52:- use_module(library(http/http_hook)).   53:- use_module(library(http/http_path)).   54:- use_module(library(http/http_wrapper)).   55:- use_module(library(uri)).   56:- use_module(library(debug)).   57:- use_module(library(lists)).   58:- use_module(library(url)).   59:- use_module(library(socket)).   60:- use_module(library(option)).   61:- use_module(library(error)).   62:- use_module(library(www_browser)).   63:- use_module(pldoc(doc_process)).   64:- use_module(pldoc(doc_htmlsrc)).   65:- use_module(pldoc(doc_html)).   66:- use_module(pldoc(doc_index)).   67:- use_module(pldoc(doc_search)).   68:- use_module(pldoc(doc_man)).   69:- use_module(pldoc(doc_wiki)).   70:- use_module(pldoc(doc_util)).   71:- use_module(pldoc(doc_access)).   72:- use_module(pldoc(doc_pack)).   73:- use_module(pldoc(man_index)).   74:- if(exists_source(library(pce_emacs))).   75:- autoload(library(pce_emacs), [start_emacs/0]).   76:- endif.   77
   78/** <module> Documentation server
   79
   80The module library(pldoc/http) provides an   embedded HTTP documentation
   81server that allows for browsing the   documentation  of all files loaded
   82_after_ library(pldoc) has been loaded.
   83*/
   84
   85:- dynamic
   86    doc_server_port/1,
   87    doc_enabled/0.   88
   89http:location(pldoc, root(pldoc), []).
   90http:location(pldoc_man, pldoc(refman), []).
   91http:location(pldoc_pkg, pldoc(package), []).
   92http:location(pldoc_resource, Path, []) :-
   93    http_location_by_id(pldoc_resource, Path).
   94
   95%!  doc_enable(+Boolean)
   96%
   97%   Actually activate the PlDoc server. Merely   loading the server does
   98%   not do so to avoid incidental loading   in a user HTTP server making
   99%   the documentation available.
  100
  101doc_enable(true) :-
  102    (   doc_enabled
  103    ->  true
  104    ;   assertz(doc_enabled)
  105    ).
  106doc_enable(false) :-
  107    retractall(doc_enabled).
  108
  109%!  doc_server(?Port) is det.
  110%!  doc_server(?Port, +Options) is det.
  111%
  112%   Start a documentation server in the  current Prolog process. The
  113%   server is started in a separate   thread.  Options are handed to
  114%   http_server/2.  In  addition,   the    following   options   are
  115%   recognised:
  116%
  117%           * allow(HostOrIP)
  118%           Allow connections from HostOrIP.  If HostOrIP is an atom
  119%           it is matched to the hostname.  It if starts with a .,
  120%           suffix match is done, matching the domain.  Finally it
  121%           can be a term ip(A,B,C,D). See tcp_host_to_address/2 for
  122%           details.
  123%
  124%           * deny(HostOrIP)
  125%           See allow(HostOrIP).
  126%
  127%           * edit(Bool)
  128%           Allow editing from localhost connections? Default:
  129%           =true=.
  130%
  131%   The predicate doc_server/1 is defined as below, which provides a
  132%   good default for development.
  133%
  134%   ==
  135%   doc_server(Port) :-
  136%           doc_server(Port,
  137%                      [ allow(localhost)
  138%                      ]).
  139%   ==
  140%
  141%   @see    doc_browser/1
  142
  143doc_server(Port) :-
  144    doc_server(Port,
  145               [ allow(localhost),
  146                 allow(ip(127,0,0,1)) % Windows ip-->host often fails
  147               ]).
  148
  149doc_server(Port, _) :-
  150    doc_enable(true),
  151    catch(doc_current_server(Port), _, fail),
  152    !.
  153:- if(current_predicate(http_server/2)).  154doc_server(Port, Options) :-
  155    doc_enable(true),
  156    prepare_editor,
  157    host_access_options(Options, ServerOptions),
  158    http_absolute_location(pldoc('.'), Entry, []),
  159    merge_options(ServerOptions,
  160                  [ port(Port),
  161                    entry_page(Entry)
  162                  ], HTTPOptions),
  163    http_server(http_dispatch, HTTPOptions),
  164    assertz(doc_server_port(Port)).
  165:- endif.  166
  167%!  doc_current_server(-Port) is det.
  168%
  169%   TCP/IP port of the documentation server.   Fails if no server is
  170%   running. Note that in the current   infrastructure we can easily
  171%   be embedded into another  Prolog  HTTP   server.  If  we are not
  172%   started from doc_server/2, we  return  the   port  of  a running
  173%   HTTP server.
  174%
  175%   @tbd    Trap destruction of the server.
  176%   @error  existence_error(http_server, pldoc)
  177
  178doc_current_server(Port) :-
  179    (   doc_server_port(P)
  180    ->  Port = P
  181    ;   http_current_server(_:_, P)
  182    ->  Port = P
  183    ;   existence_error(http_server, pldoc)
  184    ).
  185
  186:- if(\+current_predicate(http_current_server/2)).  187http_current_server(_,_) :- fail.
  188:- endif.  189
  190%!  doc_browser is det.
  191%!  doc_browser(+What) is semidet.
  192%
  193%   Open user's default browser on the documentation server.
  194
  195doc_browser :-
  196    doc_browser([]).
  197doc_browser(Spec) :-
  198    catch(doc_current_server(Port),
  199          error(existence_error(http_server, pldoc), _),
  200          doc_server(Port)),
  201    browser_url(Spec, Request),
  202    format(string(URL), 'http://localhost:~w~w', [Port, Request]),
  203    www_open_url(URL).
  204
  205browser_url([], Root) :-
  206    !,
  207    http_location_by_id(pldoc_root, Root).
  208browser_url(Name, URL) :-
  209    atom(Name),
  210    !,
  211    browser_url(Name/_, URL).
  212browser_url(Name//Arity, URL) :-
  213    must_be(atom, Name),
  214    integer(Arity),
  215    !,
  216    PredArity is Arity+2,
  217    browser_url(Name/PredArity, URL).
  218browser_url(Name/Arity, URL) :-
  219    !,
  220    must_be(atom, Name),
  221    (   man_object_property(Name/Arity, summary(_))
  222    ->  format(string(S), '~q/~w', [Name, Arity]),
  223        http_link_to_id(pldoc_man, [predicate=S], URL)
  224    ;   browser_url(_:Name/Arity, URL)
  225    ).
  226browser_url(Spec, URL) :-
  227    !,
  228    Spec = M:Name/Arity,
  229    doc_comment(Spec, _Pos, _Summary, _Comment),
  230    !,
  231    (   var(M)
  232    ->  format(string(S), '~q/~w', [Name, Arity])
  233    ;   format(string(S), '~q:~q/~w', [M, Name, Arity])
  234    ),
  235    http_link_to_id(pldoc_object, [object=S], URL).
  236
  237%!  prepare_editor
  238%
  239%   Start XPCE as edit requests coming from the document server can only
  240%   be handled if XPCE is running.
  241
  242:- if(current_predicate(start_emacs/0)).  243prepare_editor :-
  244    current_prolog_flag(editor, pce_emacs),
  245    !,
  246    start_emacs.
  247:- endif.  248prepare_editor.
  249
  250
  251                 /*******************************
  252                 *          USER REPLIES        *
  253                 *******************************/
  254
  255:- http_handler(pldoc(.),          pldoc_root,
  256                [ prefix,
  257                  authentication(pldoc(read)),
  258                  condition(doc_enabled)
  259                ]).  260:- http_handler(pldoc('index.html'), pldoc_index,   []).  261:- http_handler(pldoc(file),       pldoc_file,     []).  262:- http_handler(pldoc(place),      go_place,       []).  263:- http_handler(pldoc(edit),       pldoc_edit,
  264                [authentication(pldoc(edit))]).  265:- http_handler(pldoc(doc),        pldoc_doc,      [prefix]).  266:- http_handler(pldoc(man),        pldoc_man,      []).  267:- http_handler(pldoc(doc_for),    pldoc_object,   [id(pldoc_doc_for)]).  268:- http_handler(pldoc(search),     pldoc_search,   []).  269:- http_handler(pldoc('res/'),     pldoc_resource, [prefix]).  270
  271
  272%!  pldoc_root(+Request)
  273%
  274%   Reply using the index-page  of   the  Prolog  working directory.
  275%   There are various options for the   start directory. For example
  276%   we could also use the file or   directory of the file that would
  277%   be edited using edit/0.
  278
  279pldoc_root(Request) :-
  280    http_parameters(Request,
  281                    [ empty(Empty, [ oneof([true,false]),
  282                                     default(false)
  283                                   ])
  284                    ]),
  285    pldoc_root(Request, Empty).
  286
  287pldoc_root(Request, false) :-
  288    http_location_by_id(pldoc_root, Root),
  289    memberchk(path(Path), Request),
  290    Root \== Path,
  291    !,
  292    existence_error(http_location, Path).
  293pldoc_root(_Request, false) :-
  294    working_directory(Dir0, Dir0),
  295    allowed_directory(Dir0),
  296    !,
  297    ensure_slash_end(Dir0, Dir1),
  298    doc_file_href(Dir1, Ref0),
  299    atom_concat(Ref0, 'index.html', Index),
  300    throw(http_reply(see_other(Index))).
  301pldoc_root(Request, _) :-
  302    pldoc_index(Request).
  303
  304
  305%!  pldoc_index(+Request)
  306%
  307%   HTTP handle for /index.html, providing an overall overview
  308%   of the available documentation.
  309
  310pldoc_index(_Request) :-
  311    reply_html_page(pldoc(index),
  312                    title('SWI-Prolog documentation'),
  313                    [ \doc_links('', []),
  314                       h1('SWI-Prolog documentation'),
  315                      \man_overview([])
  316                    ]).
  317
  318
  319%!  pldoc_file(+Request)
  320%
  321%   Hander for /file?file=File, providing documentation for File.
  322
  323pldoc_file(Request) :-
  324    http_parameters(Request,
  325                    [ file(File, [])
  326                    ]),
  327    (   source_file(File)
  328    ->  true
  329    ;   throw(http_reply(forbidden(File)))
  330    ),
  331    doc_for_file(File, []).
  332
  333%!  pldoc_edit(+Request)
  334%
  335%   HTTP handler that starts the user's   default editor on the host
  336%   running the server. This  handler  can   only  accessed  if  the
  337%   browser connection originates from  =localhost=.   The  call can
  338%   edit files using the =file=  attribute   or  a predicate if both
  339%   =name= and =arity= is given and optionally =module=.
  340
  341pldoc_edit(Request) :-
  342    http:authenticate(pldoc(edit), Request, _),
  343    http_parameters(Request,
  344                    [ file(File,
  345                           [ optional(true),
  346                             description('Name of the file to edit')
  347                           ]),
  348                      line(Line,
  349                           [ optional(true),
  350                             integer,
  351                             description('Line in the file')
  352                           ]),
  353                      name(Name,
  354                           [ optional(true),
  355                             description('Name of a Prolog predicate to edit')
  356                           ]),
  357                      arity(Arity,
  358                            [ integer,
  359                              optional(true),
  360                              description('Arity of a Prolog predicate to edit')
  361                            ]),
  362                      module(Module,
  363                             [ optional(true),
  364                               description('Name of a Prolog module to search for predicate')
  365                             ])
  366                    ]),
  367    (   atom(File)
  368    ->  allowed_file(File)
  369    ;   true
  370    ),
  371    (   atom(File), integer(Line)
  372    ->  Edit = file(File, line(Line))
  373    ;   atom(File)
  374    ->  Edit = file(File)
  375    ;   atom(Name), integer(Arity)
  376    ->  (   atom(Module)
  377        ->  Edit = (Module:Name/Arity)
  378        ;   Edit = (Name/Arity)
  379        )
  380    ),
  381    edit(Edit),
  382    format('Content-type: text/plain~n~n'),
  383    format('Started ~q~n', [edit(Edit)]).
  384pldoc_edit(_Request) :-
  385    http_location_by_id(pldoc_edit, Location),
  386    throw(http_reply(forbidden(Location))).
  387
  388
  389%!  go_place(+Request)
  390%
  391%   HTTP handler to handle the places menu.
  392
  393go_place(Request) :-
  394    http_parameters(Request,
  395                    [ place(Place, [])
  396                    ]),
  397    places(Place).
  398
  399places(':packs:') :-
  400    !,
  401    http_link_to_id(pldoc_pack, [], HREF),
  402    throw(http_reply(moved(HREF))).
  403places(Dir0) :-
  404    expand_alias(Dir0, Dir),
  405    (   allowed_directory(Dir)
  406    ->  format(string(IndexFile), '~w/index.html', [Dir]),
  407        doc_file_href(IndexFile, HREF),
  408        throw(http_reply(moved(HREF)))
  409    ;   throw(http_reply(forbidden(Dir)))
  410    ).
  411
  412
  413%!  allowed_directory(+Dir) is semidet.
  414%
  415%   True if we are allowed to produce and index for Dir.
  416
  417allowed_directory(Dir) :-
  418    source_directory(Dir),
  419    !.
  420allowed_directory(Dir) :-
  421    working_directory(CWD, CWD),
  422    same_file(CWD, Dir).
  423allowed_directory(Dir) :-
  424    prolog:doc_directory(Dir).
  425
  426
  427%!  allowed_file(+File) is semidet.
  428%
  429%   True if we are allowed to serve   File.  Currently means we have
  430%   predicates loaded from File or the directory must be allowed.
  431
  432allowed_file(File) :-
  433    source_file(_, File),
  434    !.
  435allowed_file(File) :-
  436    absolute_file_name(File, Canonical),
  437    file_directory_name(Canonical, Dir),
  438    allowed_directory(Dir).
  439
  440
  441%!  pldoc_resource(+Request)
  442%
  443%   Handler for /res/File, serving CSS, JS and image files.
  444
  445pldoc_resource(Request) :-
  446    http_location_by_id(pldoc_resource, ResRoot),
  447    memberchk(path(Path), Request),
  448    atom_concat(ResRoot, File, Path),
  449    file(File, Local),
  450    http_reply_file(pldoc(Local), [], Request).
  451
  452file('pldoc.css',     'pldoc.css').
  453file('pllisting.css', 'pllisting.css').
  454file('pldoc.js',      'pldoc.js').
  455file('edit.png',      'edit.png').
  456file('editpred.png',  'editpred.png').
  457file('up.gif',        'up.gif').
  458file('source.png',    'source.png').
  459file('public.png',    'public.png').
  460file('private.png',   'private.png').
  461file('reload.png',    'reload.png').
  462file('favicon.ico',   'favicon.ico').
  463file('h1-bg.png',     'h1-bg.png').
  464file('h2-bg.png',     'h2-bg.png').
  465file('pub-bg.png',    'pub-bg.png').
  466file('priv-bg.png',   'priv-bg.png').
  467file('multi-bg.png',  'multi-bg.png').
  468
  469
  470%!  pldoc_doc(+Request)
  471%
  472%   Handler for /doc/Path
  473%
  474%   Reply documentation of a file. Path is  the absolute path of the
  475%   file for which to return the  documentation. Extension is either
  476%   none, the Prolog extension or the HTML extension.
  477%
  478%   Note that we reply  with  pldoc.css   if  the  file  basename is
  479%   pldoc.css to allow for a relative link from any directory.
  480
  481pldoc_doc(Request) :-
  482    memberchk(path(ReqPath), Request),
  483    http_location_by_id(pldoc_doc, Me),
  484    atom_concat(Me, AbsFile0, ReqPath),
  485    (   sub_atom(ReqPath, _, _, 0, /)
  486    ->  atom_concat(ReqPath, 'index.html', File),
  487        throw(http_reply(moved(File)))
  488    ;   clean_path(AbsFile0, AbsFile1),
  489        expand_alias(AbsFile1, AbsFile),
  490        is_absolute_file_name(AbsFile)
  491    ->  documentation(AbsFile, Request)
  492    ).
  493
  494documentation(Path, Request) :-
  495    file_base_name(Path, Base),
  496    file(_, Base),                         % serve pldoc.css, etc.
  497    !,
  498    http_reply_file(pldoc(Base), [], Request).
  499documentation(Path, Request) :-
  500    file_name_extension(_, Ext, Path),
  501    autolink_extension(Ext, image),
  502    http_reply_file(Path, [unsafe(true)], Request).
  503documentation(Path, Request) :-
  504    Index = '/index.html',
  505    sub_atom(Path, _, _, 0, Index),
  506    atom_concat(Dir, Index, Path),
  507    exists_directory(Dir),                 % Directory index
  508    !,
  509    (   allowed_directory(Dir)
  510    ->  edit_options(Request, EditOptions),
  511        doc_for_dir(Dir, EditOptions)
  512    ;   throw(http_reply(forbidden(Dir)))
  513    ).
  514documentation(File, Request) :-
  515    wiki_file(File, WikiFile),
  516    !,
  517    (   allowed_file(WikiFile)
  518    ->  true
  519    ;   throw(http_reply(forbidden(File)))
  520    ),
  521    edit_options(Request, Options),
  522    doc_for_wiki_file(WikiFile, Options).
  523documentation(Path, Request) :-
  524    pl_file(Path, File),
  525    !,
  526    (   allowed_file(File)
  527    ->  true
  528    ;   throw(http_reply(forbidden(File)))
  529    ),
  530    doc_reply_file(File, Request).
  531documentation(Path, _) :-
  532    throw(http_reply(not_found(Path))).
  533
  534:- public
  535    doc_reply_file/2.  536
  537doc_reply_file(File, Request) :-
  538    http_parameters(Request,
  539                    [ public_only(Public),
  540                      reload(Reload),
  541                      show(Show),
  542                      format_comments(FormatComments)
  543                    ],
  544                    [ attribute_declarations(param)
  545                    ]),
  546    (   exists_file(File)
  547    ->  true
  548    ;   throw(http_reply(not_found(File)))
  549    ),
  550    (   Reload == true,
  551        source_file(File)
  552    ->  load_files(File, [if(changed), imports([])])
  553    ;   true
  554    ),
  555    edit_options(Request, EditOptions),
  556    (   Show == src
  557    ->  format('Content-type: text/html~n~n', []),
  558        source_to_html(File, stream(current_output),
  559                       [ skin(src_skin(Request, Show, FormatComments)),
  560                         format_comments(FormatComments)
  561                       ])
  562    ;   Show == raw
  563    ->  http_reply_file(File,
  564                        [ unsafe(true), % is already validated
  565                          mime_type(text/plain)
  566                        ], Request)
  567    ;   doc_for_file(File,
  568                     [ public_only(Public),
  569                       source_link(true)
  570                     | EditOptions
  571                     ])
  572    ).
  573
  574
  575:- public src_skin/5.                   % called through source_to_html/3.
  576
  577src_skin(Request, _Show, FormatComments, header, Out) :-
  578    memberchk(request_uri(ReqURI), Request),
  579    negate(FormatComments, AltFormatComments),
  580    replace_parameters(ReqURI, [show(raw)], RawLink),
  581    replace_parameters(ReqURI, [format_comments(AltFormatComments)], CmtLink),
  582    phrase(html(div(class(src_formats),
  583                    [ 'View source with ',
  584                      a(href(CmtLink), \alt_view(AltFormatComments)),
  585                      ' or as ',
  586                      a(href(RawLink), raw)
  587                    ])), Tokens),
  588    print_html(Out, Tokens).
  589
  590alt_view(true) -->
  591    html('formatted comments').
  592alt_view(false) -->
  593    html('raw comments').
  594
  595negate(true, false).
  596negate(false, true).
  597
  598replace_parameters(ReqURI, Extra, URI) :-
  599    uri_components(ReqURI, C0),
  600    uri_data(search, C0, Search0),
  601    (   var(Search0)
  602    ->  uri_query_components(Search, Extra)
  603    ;   uri_query_components(Search0, Form0),
  604        merge_options(Extra, Form0, Form),
  605        uri_query_components(Search, Form)
  606    ),
  607    uri_data(search, C0, Search, C),
  608    uri_components(URI, C).
  609
  610
  611%!  edit_options(+Request, -Options) is det.
  612%
  613%   Return edit(true) in Options  if  the   connection  is  from the
  614%   localhost.
  615
  616edit_options(Request, [edit(true)]) :-
  617    catch(http:authenticate(pldoc(edit), Request, _), _, fail),
  618    !.
  619edit_options(_, []).
  620
  621
  622%!  pl_file(+File, -PlFile) is semidet.
  623
  624pl_file(File, PlFile) :-
  625    file_name_extension(Base, html, File),
  626    !,
  627    absolute_file_name(Base,
  628                       PlFile,
  629                       [ file_errors(fail),
  630                         file_type(prolog),
  631                         access(read)
  632                       ]).
  633pl_file(File, File).
  634
  635%!  wiki_file(+File, -TxtFile) is semidet.
  636%
  637%   True if TxtFile is an existing file  that must be served as wiki
  638%   file.
  639
  640wiki_file(File, TxtFile) :-
  641    file_name_extension(_, Ext, File),
  642    wiki_file_extension(Ext),
  643    !,
  644    TxtFile = File.
  645wiki_file(File, TxtFile) :-
  646    file_base_name(File, Base),
  647    autolink_file(Base, wiki),
  648    !,
  649    TxtFile = File.
  650wiki_file(File, TxtFile) :-
  651    file_name_extension(Base, html, File),
  652    wiki_file_extension(Ext),
  653    file_name_extension(Base, Ext, TxtFile),
  654    access_file(TxtFile, read).
  655
  656wiki_file_extension(md).
  657wiki_file_extension(txt).
  658
  659
  660%!  clean_path(+AfterDoc, -AbsPath)
  661%
  662%   Restore the path, Notably deals Windows issues
  663
  664clean_path(Path0, Path) :-
  665    current_prolog_flag(windows, true),
  666    sub_atom(Path0, 2, _, _, :),
  667    !,
  668    sub_atom(Path0, 1, _, 0, Path).
  669clean_path(Path, Path).
  670
  671
  672%!  pldoc_man(+Request)
  673%
  674%   Handler for /man, offering one of the parameters:
  675%
  676%       * predicate=PI
  677%       providing documentation from the manual on the predicate PI.
  678%       * function=PI
  679%       providing documentation from the manual on the function PI.
  680%       * 'CAPI'=F
  681%       providing documentation from the manual on the C-function F.
  682
  683pldoc_man(Request) :-
  684    http_parameters(Request,
  685                    [ predicate(PI, [optional(true)]),
  686                      function(Fun, [optional(true)]),
  687                      'CAPI'(F,     [optional(true)]),
  688                      section(Sec,  [optional(true)])
  689                    ]),
  690    (   ground(PI)
  691    ->  atom_pi(PI, Obj)
  692    ;   ground(Fun)
  693    ->  atomic_list_concat([Name,ArityAtom], /, Fun),
  694        atom_number(ArityAtom, Arity),
  695        Obj = f(Name/Arity)
  696    ;   ground(F)
  697    ->  Obj = c(F)
  698    ;   ground(Sec)
  699    ->  atom_concat('sec:', Sec, SecID),
  700        Obj = section(SecID)
  701    ),
  702    man_title(Obj, Title),
  703    reply_html_page(
  704        pldoc(object(Obj)),
  705        title(Title),
  706        \man_page(Obj, [])).
  707
  708man_title(f(Obj), Title) :-
  709    !,
  710    format(atom(Title), 'SWI-Prolog -- function ~w', [Obj]).
  711man_title(c(Obj), Title) :-
  712    !,
  713    format(atom(Title), 'SWI-Prolog -- API-function ~w', [Obj]).
  714man_title(section(Id), Title) :-
  715    !,
  716    (   manual_object(section(_L, _N, Id, _F),
  717                      STitle, _File, _Class, _Offset)
  718    ->  true
  719    ;   STitle = 'Manual'
  720    ),
  721    format(atom(Title), 'SWI-Prolog -- ~w', [STitle]).
  722man_title(Obj, Title) :-
  723    format(atom(Title), 'SWI-Prolog -- ~w', [Obj]).
  724
  725%!  pldoc_object(+Request)
  726%
  727%   Handler for /doc_for?object=Term, Provide  documentation for the
  728%   given term.
  729
  730pldoc_object(Request) :-
  731    http_parameters(Request,
  732                    [ object(Atom, []),
  733                      header(Header, [default(true)])
  734                    ]),
  735    (   catch(atom_to_term(Atom, Obj, _), error(_,_), fail)
  736    ->  true
  737    ;   atom_to_object(Atom, Obj)
  738    ),
  739    (   prolog:doc_object_title(Obj, Title)
  740    ->  true
  741    ;   Title = Atom
  742    ),
  743    edit_options(Request, EditOptions),
  744    reply_html_page(
  745        pldoc(object(Obj)),
  746        title(Title),
  747        \object_page(Obj, [header(Header)|EditOptions])).
  748
  749
  750%!  pldoc_search(+Request)
  751%
  752%   Search the collected PlDoc comments and Prolog manual.
  753
  754pldoc_search(Request) :-
  755    http_parameters(Request,
  756                    [ for(For,
  757                          [ optional(true),
  758                            description('String to search for')
  759                          ]),
  760                      page(Page,
  761                           [ integer,
  762                             default(1),
  763                             description('Page of search results to view')
  764                           ]),
  765                      in(In,
  766                         [ oneof([all,app,noapp,man,lib,pack,wiki]),
  767                           default(all),
  768                           description('Search everying, application only or manual only')
  769                         ]),
  770                      match(Match,
  771                            [ oneof([name,summary]),
  772                              default(summary),
  773                              description('Match only the name or also the summary')
  774                            ]),
  775                      resultFormat(Format,
  776                                   [ oneof(long,summary),
  777                                     default(summary),
  778                                     description('Return full documentation or summary-lines')
  779                                   ])
  780                    ]),
  781    edit_options(Request, EditOptions),
  782    format(string(Title), 'Prolog search -- ~w', [For]),
  783    reply_html_page(pldoc(search(For)),
  784                    title(Title),
  785                    \search_reply(For,
  786                                  [ resultFormat(Format),
  787                                    search_in(In),
  788                                    search_match(Match),
  789                                    page(Page)
  790                                  | EditOptions
  791                                  ])).
  792
  793
  794                 /*******************************
  795                 *     HTTP PARAMETER TYPES     *
  796                 *******************************/
  797
  798:- public
  799    param/2.                        % used in pack documentation server
  800
  801param(public_only,
  802      [ boolean,
  803        default(true),
  804        description('If true, hide private predicates')
  805      ]).
  806param(reload,
  807      [ boolean,
  808        default(false),
  809        description('Reload the file and its documentation')
  810      ]).
  811param(show,
  812      [ oneof([doc,src,raw]),
  813        default(doc),
  814        description('How to show the file')
  815      ]).
  816param(format_comments,
  817      [ boolean,
  818        default(true),
  819        description('If true, use PlDoc for rendering structured comments')
  820      ])