├── .fileheader ├── .gdbinit ├── .gitignore ├── .gitmodules ├── .well-known └── index.html ├── README.md ├── annotateit.pl ├── annotation.pl ├── api.pl ├── autocomplete.pl ├── blog.pl ├── changelog.pl ├── changes.pl ├── contents.pl ├── customise.pl ├── daemon.pl ├── daemon.sh ├── debug.pl ├── did_you_know.pl ├── doc └── Examples.md ├── docker ├── Dockerfile ├── Makefile ├── README.md ├── health.sh └── start-plweb.sh ├── download-custom ├── devel │ ├── bin │ │ ├── linux.txt │ │ ├── macosx-bundle.txt │ │ ├── macosx-fat-bundle.txt │ │ ├── macosx-lion.txt │ │ ├── macosx-snow_leopard.txt │ │ ├── macosx-snow_leopard_and_later.txt │ │ ├── macosx.txt │ │ ├── space.txt │ │ ├── win32.txt │ │ └── win64.txt │ ├── doc │ │ └── doc-pdf.txt │ ├── footer.txt │ ├── header.txt │ └── src │ │ └── src-tgz.txt ├── old │ └── header.txt └── stable │ ├── bin │ ├── linux-rpm.txt │ ├── linux.txt │ ├── macosx-bundle.txt │ ├── macosx-fat-bundle.txt │ ├── macosx-snow_leopard.txt │ ├── macosx-snow_leopard_and_later.txt │ ├── macosx-tiger.txt │ ├── macosx.txt │ ├── win32.txt │ └── win64.txt │ ├── doc │ └── doc-pdf.txt │ ├── footer.txt │ ├── header.txt │ └── src │ └── src-tgz.txt ├── download.pl ├── examples.pl ├── fastly.pl ├── footer.pl ├── forum.pl ├── generics.pl ├── git-web ├── footer.html ├── gitweb.cgi ├── gitweb.conf ├── header.html ├── plweb-apache.env ├── printenv ├── static │ ├── git-favicon.png │ ├── git-logo.png │ ├── gitweb.css │ └── gitweb.js └── test.c ├── git_html.pl ├── gitweb.pl ├── holidays.pl ├── http_cgi.pl ├── http_fork.pl ├── load.pl ├── log.pl ├── logs ├── .gitignore ├── Makefile ├── README ├── dl-stat ├── dl-summary ├── httpd-2009-02-20.dat ├── httpd-2009-03-12.dat ├── httpd-2009-08-12.dat ├── log2clf.pl ├── logstat.pl ├── plstat.pl ├── report │ ├── .gitignore │ ├── webalizer.css │ ├── webalizer.js │ └── webalizer.xsl ├── stat.pl └── webalizer.conf ├── make.pl ├── markdown.pl ├── markitup.pl ├── messages.pl ├── news.pl ├── notify.pl ├── object_support.pl ├── openid.pl ├── pack.pl ├── pack_analyzer.pl ├── pack_info.pl ├── pack_mirror.pl ├── page.pl ├── parms.pl ├── plweb.pl ├── post.pl ├── proxy.pl ├── rating.pl ├── register.pl ├── review.pl ├── run ├── scripts ├── fix-permissions ├── from-line ├── install-custom ├── ln-install └── sync-server ├── stats.pl ├── stress ├── hg ├── http_cookie.pl ├── maps.pl ├── replay.pl └── stress.pl ├── sw ├── README └── evo │ ├── bom.owl │ ├── som.owl │ └── vom.owl ├── systemd └── plweb.service ├── tagit.pl ├── test_plweb.pl ├── test_recaptcha.pl ├── tests.pl ├── update.pl ├── watchdog.pl ├── wiki.pl └── wiki_edit.pl /.fileheader: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: jan@swi-prolog.org 5 | WWW: https://www.swi-prolog.org 6 | Copyright (C): %Y, SWI-Prolog Solutions b.v. 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | -------------------------------------------------------------------------------- /.gdbinit: -------------------------------------------------------------------------------- 1 | # This gdb script contains some useful settings and functions for 2 | # debugging Prolog using gdb. Link or copy this as `.gdbinit` to the 3 | # directory where you want to debug Prolog. 4 | 5 | # Trap some functions. trap_gdb() is a dummy function that you may call 6 | # conditionally at a place where you want to stop in gdb. This is 7 | # similar to GDB breakpoint conditions, but these are quite slow. 8 | set breakpoint pending on 9 | break trap_gdb 10 | break sysError 11 | break __assert_fail 12 | set breakpoint pending off 13 | 14 | # Pass signals that are commonly used and not needed for debugging 15 | handle SIGPIPE noprint nostop pass 16 | handle SIGUSR1 noprint nostop pass 17 | handle SIGUSR2 noprint nostop pass 18 | handle SIGTERM noprint nostop pass 19 | 20 | # Be silent on threads and processes created. 21 | set print thread-events off 22 | set print inferior-events off 23 | 24 | # Fedora debug info daemon. See https://debuginfod.fedoraproject.org/ 25 | set debuginfod enabled on 26 | 27 | # Allow debugging ASAN events. 28 | set environment ASAN_OPTIONS=abort_on_error=1 29 | 30 | # Print a Prolog backtrace to the current terminal. With one argument, 31 | # change the depth. With two, also set the flags. The (only) useful flag 32 | # is `1`. This prints VM locations rather than Prolog arguments for the 33 | # goals on the stack and almost always works, even if the Prolog data is 34 | # corrupted. 35 | 36 | define pl-bt 37 | if $argc == 0 38 | printf "%s\n", PL_backtrace_string(10, 0) 39 | end 40 | if $argc == 1 41 | printf "%s\n", PL_backtrace_string($arg0, 0) 42 | end 43 | if $argc == 2 44 | printf "%s\n", PL_backtrace_string($arg0, $arg1) 45 | end 46 | end 47 | 48 | # Print Prolog thread id for the current thread 49 | 50 | define pl-tid 51 | p ((PL_local_data_t*)pthread_getspecific(PL_ldata))->thread.info->pl_tid 52 | end 53 | 54 | define ninja 55 | if $argc == 0 56 | shell ninja 57 | end 58 | if $argc == 1 59 | shell ninja $arg0 60 | end 61 | end 62 | 63 | # Re-run the current program until it crashes. Not SWI-Prolog specific, 64 | # but too easy to forget. 65 | 66 | define forever 67 | set pagination off 68 | set breakpoint pending on 69 | break _exit 70 | set breakpoint pending off 71 | commands 72 | run 73 | end 74 | run 75 | end 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | data 3 | *.log 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "www"] 2 | path = data/git/www 3 | url = ../plweb-www.git 4 | [submodule "packs/recaptcha"] 5 | path = packs/recaptcha 6 | url = https://github.com/JanWielemaker/recaptcha.git 7 | [submodule "packs/smtp"] 8 | path = packs/smtp 9 | url = https://github.com/JanWielemaker/smtp.git 10 | [submodule "packs/googleclient"] 11 | path = packs/googleclient 12 | url = https://github.com/JanWielemaker/googleclient.git 13 | [submodule "examples"] 14 | path = data/git/examples 15 | url = ../plweb-examples.git 16 | [submodule "blog"] 17 | path = data/git/blog 18 | url = ../plweb-blog.git 19 | [submodule "packs/libssh"] 20 | path = packs/libssh 21 | url = https://github.com/JanWielemaker/libssh.git 22 | -------------------------------------------------------------------------------- /.well-known/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SWI-Prolog .well-known 6 | 7 | 8 |

This is the SWI-Prolog web server .well-known directory

9 | 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The SWI-Prolog web-site 2 | 3 | This repository contains the software of http://www.swi-prolog.org. The 4 | (wiki) content of the website and required add-ons are stored in git 5 | submodules. These must be installed separately using the command below. 6 | To install the site from scratch locally, perform the following 7 | commands: 8 | 9 | 1. Downloading the site 10 | 11 | ``` 12 | % git clone https://github.com/SWI-Prolog/plweb.git 13 | % cd plweb 14 | % git submodule update --init 15 | ``` 16 | 17 | 2. For a full installation, install the dynamic data. The .db 18 | files must be writeable by the server process. 19 | 20 |
21 |
annotations.db
22 |
Comments on web pages
23 |
tags.db
24 |
Tags on web pages
25 |
openid.db
26 |
User administration
27 |
packs.db
28 |
Known packages
29 |
post.db
30 |
News posts
31 |
reviews.db
32 |
Pack reviews
33 |
download
34 |
Points to the download directory
35 |
36 | 37 | Install the download descriptions by running the script `install-custom` 38 | 39 | 3. Create directories for logging and pack mirrors. These 40 | directories must be writeable by the server and new directories 41 | created below must have the same permissions: 42 | 43 | ``` 44 | % mkdir log pack 45 | % chgrp www-data log pack 46 | % chmod g+ws log pack 47 | ``` 48 | 49 | ## Running the site 50 | 51 | After installation, the website may be started locally using the 52 | commands below. After that, you have access to the same content as 53 | available from https://www.swi-prolog.org, except for the download 54 | section of the website. 55 | 56 | ``` 57 | % swipl load.pl -p 8080 -i 58 | ``` 59 | 60 | ### Running as daemon using Ubuntu upstart 61 | 62 | A good way to run the website on a Linux server is by creating a Linux 63 | container using lxc. After installing the server, you can enable it to 64 | start at boot time by copying `upstart/swi-prolog.conf` to `/etc/init` 65 | after editing it to suit your configuration requirements. By default, 66 | the server runs as user `www-data`, group `www-data` as specified in the 67 | above configuration file. 68 | 69 | Make sure the following components are writeable to the server process. 70 | For files, this means mode 664, group www-data. For directories, this 71 | means mode 2775, group www-data. 72 | 73 | * log 74 | 75 | Write httpd.log and pack-warnings.log 76 | 77 | * pack 78 | 79 | Mirrors known packages. Will be populated as the server is started. 80 | 81 | * www: subdirectories and .txt files 82 | 83 | Needs to make the wiki pages editable. It is also wise to do this in 84 | a git branch. From the www directory, do: 85 | 86 | ``` 87 | % git checkout master 88 | % git pull 89 | % git checkout -b wiki 90 | % find . -type d | xargs chmod 2775 91 | % find . -name '*.txt' | xargs chmod 664 92 | % chgrp -W www-data . 93 | ``` 94 | 95 | * *.db 96 | 97 | 98 | ## Issues with the locally running site 99 | 100 | - There is no download section (but that can't be a big issue) 101 | 102 | - If you want to use the _login_ facility to play with the 103 | interactive aspects of the site, you need to 104 | 105 | 1. Get a reCAPTCHA key-pair from Google 106 | 107 | 2. Run (from a started server) 108 | 109 | ``` 110 | ?- set_setting(recaptcha:public_key, 'public key goes here'). 111 | ?- set_setting(recaptcha:private_key, 'private key goes here'). 112 | ?- save_settings. 113 | ``` 114 | 115 | 3. Run the server from a port that is accessible from the public 116 | internet. 117 | 118 | 4. Use an OpenID provider that is not too picky for your site. 119 | In our experience, Google is less picky than Yahoo. 120 | -------------------------------------------------------------------------------- /annotateit.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2013, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | 31 | :- module(annotateit, 32 | [ convert_annotations/0 33 | ]). 34 | :- use_module(library(persistency)). 35 | 36 | /** Convert old annotations 37 | 38 | */ 39 | 40 | 41 | /******************************* 42 | * DATA * 43 | *******************************/ 44 | 45 | :- persistent 46 | annotation(object:any, % Object attached to 47 | annotation:atom, % Text of the annotation 48 | time:integer, % When was it tagged 49 | user:atom). % User that added the tag 50 | 51 | 52 | :- initialization 53 | db_attach('annotations.db', 54 | [ sync(close) 55 | ]). 56 | 57 | %% convert_annotations 58 | % 59 | % Convert the old annotations to the new format. Simply load this 60 | % file and run convert_annotations/0. 61 | 62 | convert_annotations :- 63 | forall(annotation(Object, Text, Created, User), 64 | ( atom_string(Text, Content), 65 | uuid(PostId), 66 | post:assert_post(PostId, 67 | _{kind: annotation, 68 | content: Content, 69 | meta:_{id:PostId, 70 | author:User, 71 | object:Object, 72 | time:_{created:Created} 73 | } 74 | }))). 75 | -------------------------------------------------------------------------------- /annotation.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2014, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(annotation, 31 | [ annotation//1 % +object:compound 32 | ]). 33 | 34 | /** Annotation 35 | 36 | @author Wouter Beek 37 | @tbd Build annotation2post converter. 38 | @version 2014/01 39 | */ 40 | 41 | :- use_module(generics). 42 | :- use_module(library(http/html_head)). 43 | :- use_module(library(http/html_write)). 44 | :- use_module(library(http/http_dispatch)). 45 | :- use_module(library(pldoc/doc_html), [object_ref//2]). 46 | :- use_module(object_support). 47 | :- use_module(post). 48 | 49 | :- html_resource(css('annotation.css'), 50 | [ requires([css('post.css')]) 51 | ]). 52 | 53 | :- multifile 54 | prolog:doc_object_page_footer/2. 55 | 56 | :- http_handler(root(annotation), annotation_process, [prefix]). 57 | 58 | %% annotation_process(+Request) 59 | % 60 | % REST HTTP handler for /annotation/ID 61 | % 62 | % @tbd Where is this used for? This also seems to do a request 63 | % on ourselves. We'd like to avoid that. 64 | 65 | annotation_process(Request):- 66 | memberchk(method(get), Request), 67 | request_to_id(Request, annotation, Post), !, 68 | post(Post, id, Id), 69 | post(Post, about, Object), 70 | object_label(Object, Label), 71 | atomic_list_concat(['Annotation',Label], '--', Title), 72 | reply_html_page( 73 | wiki(Title), 74 | title(Title), 75 | \post(Id, [])). 76 | annotation_process(Request):- 77 | post_process(Request, annotation). 78 | 79 | %% annotation(+Object)// 80 | % 81 | % Show annotations for Object. 82 | 83 | annotation(Object) --> 84 | { ground(Object), !, 85 | ( prolog:doc_canonical_object(Object, Object2) 86 | -> true 87 | ; Object2 = Object 88 | ), 89 | find_posts(annotation, object_post(Object2), Ids) 90 | }, 91 | html([\html_requires(css('annotation.css')), 92 | \posts(annotation, Object2, Ids, []) 93 | ]). 94 | annotation(_) --> []. 95 | 96 | object_post(About, Id) :- 97 | post(Id, object, About). 98 | 99 | -------------------------------------------------------------------------------- /api.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2020, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(plweb_api, 31 | []). 32 | :- use_module(library(http/http_dispatch)). 33 | :- use_module(library(http/http_cors)). 34 | :- use_module(library(http/http_json)). 35 | :- use_module(library(http/http_parameters)). 36 | :- use_module(library(pldoc/man_index)). 37 | :- use_module(library(pldoc/doc_util)). 38 | :- use_module(library(prolog_code)). 39 | :- use_module(library(prolog_source)). 40 | :- use_module(library(apply)). 41 | :- use_module(library(error)). 42 | :- use_module(library(pairs)). 43 | :- use_module(library(option)). 44 | 45 | :- http_handler(root(doc_link), doc_link, []). 46 | 47 | %! doc_link(+Request) 48 | % 49 | % Get a link to the documentation for a predicate reference. This 50 | % hander may be used in two ways: 51 | % 52 | % - GET 53 | % Adding a parameter `for` providing the predicate you want 54 | % to have information about. 55 | % - POST 56 | % Passing a JSON list of strings. The reply is a JSON object 57 | % with for each string the corresponding reply object (`null` 58 | % if the predicate is not known). 59 | 60 | doc_link(Request) :- 61 | reply_options(Request, [get,post]), 62 | !. 63 | doc_link(Request) :- 64 | memberchk(method(post), Request), 65 | !, 66 | http_read_json_dict(Request, Atoms, 67 | [value_string_as(atom)]), 68 | cors_enable(Request, [methods([get,post])]), 69 | must_be(list(atom), Atoms), 70 | for_links(Atoms, Pairs), 71 | dict_create(Dict, json, Pairs), 72 | reply_json_dict(Dict). 73 | doc_link(Request) :- 74 | http_parameters(Request, 75 | [ for(For, []) 76 | ]), 77 | cors_enable(Request, [methods([get,post])]), 78 | ( for_link(For, Link) 79 | -> reply_json_dict(Link) 80 | ; reply_json_dict(null, [status(404)]) 81 | ). 82 | 83 | for_links([], []). 84 | for_links([H|T0], [H-I|T]) :- 85 | ( for_link(H, I) 86 | -> true 87 | ; I = null 88 | ), 89 | for_links(T0, T). 90 | 91 | for_link(For, Info) :- 92 | atom_to_object(For, Obj), 93 | current_man_object(Obj), 94 | findall(Prop, obj_property(Obj, Prop), Props), 95 | format(string(URL), 96 | 'https://www.swi-prolog.org/pldoc/doc_for?object=~w', 97 | [For]), 98 | dict_pairs(Info, json, [url-URL|Props]). 99 | 100 | obj_property(Obj, summary-Summary) :- 101 | once(man_object_property(Obj, summary(Summary))). 102 | obj_property(PI, Prop) :- 103 | pi_head(PI, Head0), 104 | ( Head0 = M:_ 105 | -> Head = Head0 106 | ; Head = M:Head0 107 | ), 108 | ( M=system, 109 | predicate_property(Head, iso) 110 | -> Prop = (class-iso) 111 | ; M=system, 112 | predicate_property(Head, built_in) 113 | -> Prop = (class-builtin) 114 | ; predicate_property(Head, autoload(File)) 115 | -> ( Prop = (class-autoload) 116 | ; library_prop(File, Prop) 117 | ) 118 | ; predicate_property(Head, file(File)), 119 | predicate_property(Head, exported) 120 | -> ( Prop = (class-library) 121 | ; library_prop(File, Prop) 122 | ) 123 | ). 124 | 125 | library_prop(File, library-LibS) :- 126 | file_name_extension(File, pl, LibFile), 127 | file_name_on_path(LibFile, library(Lib)), 128 | format(string(LibS), '~w', [Lib]). 129 | 130 | %! reply_options(+Request, +Methods) is semidet. 131 | % 132 | % Reply the HTTP OPTIONS request 133 | 134 | reply_options(Request, Allowed) :- 135 | option(method(options), Request), 136 | !, 137 | cors_enable(Request, 138 | [ methods(Allowed) 139 | ]), 140 | format('Content-type: text/plain\r\n'), 141 | format('~n'). % empty body 142 | -------------------------------------------------------------------------------- /blog.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: jan@swi-prolog.org 5 | WWW: https://www.swi-prolog.org 6 | Copyright (C): 2020, SWI-Prolog Solutions b.v. 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalexpandidate any other reasons why the executable file might be 27 | covered by the GNU General Public License. 28 | */ 29 | 30 | :- module(blog, 31 | []). 32 | :- use_module(library(http/html_head)). 33 | :- use_module(library(http/html_write)). 34 | :- use_module(library(http/http_dispatch)). 35 | :- use_module(library(debug)). 36 | :- use_module(library(yaml)). 37 | :- use_module(library(dcg/high_order)). 38 | :- use_module(library(apply)). 39 | :- use_module(library(lists)). 40 | :- use_module(library(pairs)). 41 | :- use_module(library(git)). 42 | :- use_module(library(option)). 43 | :- use_module(library(http/http_json)). 44 | :- use_module(library(http/http_host)). 45 | :- use_module(library(http/js_write)). 46 | :- use_module(library(uri)). 47 | 48 | :- use_module(wiki). 49 | :- use_module(messages). 50 | :- use_module(fastly). 51 | :- use_module(parms). 52 | 53 | :- http_handler(root(blog), blog, [prefix, id(blog)]). 54 | 55 | :- html_resource(pldoc_blog, 56 | [ ordered(true), 57 | requires([ jquery, 58 | js('blog.js') 59 | ]), 60 | virtual(true) 61 | ]). 62 | :- html_resource(css('blog.css'), []). 63 | 64 | 65 | blog(Request) :- 66 | memberchk(path_info(PathInfo), Request), 67 | PathInfo \== '/', 68 | !, 69 | debug(blog, 'Path info ~p', [PathInfo]), 70 | atom_concat(/, File, PathInfo), 71 | safe_file_name(File), 72 | absolute_file_name(blog(File), Path, 73 | [ access(read) 74 | ]), 75 | wiki_file_to_dom(Path, DOM0), 76 | extract_title(DOM0, Title, DOM1), 77 | append(DOM1, [\discourse(Request)], DOM), 78 | title_text(Title, TitleString), 79 | http_link_to_id(blog, [], HREF), 80 | reply_html_page( 81 | blog(Path, [a(href(HREF), 'Blog'), ': ' | Title]), 82 | [ title(TitleString) 83 | ], 84 | DOM). 85 | blog(_Request) :- 86 | blog_index(Blogs), 87 | reply_html_page( 88 | blog(index), 89 | [ title("SWI-Prolog blog") 90 | ], 91 | \blog_index_page(Blogs)). 92 | 93 | blog_index_page(Blogs) --> 94 | html_requires(pldoc_blog), 95 | html_requires(css('blog.css')), 96 | blog_index_title, 97 | blog_tags(Blogs), 98 | blog_index(Blogs). 99 | 100 | blog_index_title --> 101 | html({|html|| 102 |

103 | The SWI-Prolog blog is intended for articles on how to tackle certain problems 104 | using SWI-Prolog, experience using SWI-Prolog for larger projects, etc. Posts 105 | can be submitted as pull-requests on 106 | GitHub. 107 | |}). 108 | 109 | 110 | %! blog_tags(+Blogs)// 111 | 112 | blog_tags(Blogs) --> 113 | { blog_tag_counts(Blogs, Counts) }, 114 | html(div(class('blog-tags'), \sequence(tag, [' '], Counts))). 115 | 116 | tag(Tag-Count) --> 117 | html(span([ class('blog-tag'), 118 | 'data-tag'(Tag) 119 | ], 120 | [ span(class('blog-tag-tag'), Tag), 121 | span(class('blog-tag-cnt'), Count) 122 | ])). 123 | 124 | blog_tag_counts(Blogs, Pairs) :- 125 | convlist(get_dict(tags), Blogs, Tags), 126 | flatten(Tags, TagList0), 127 | msort(TagList0, TagList), 128 | clumped(TagList, Pairs0), 129 | sort(2, >=, Pairs0, Pairs). 130 | 131 | blog_index(Index) :- 132 | absolute_file_name(blog('Index.yaml'), File, 133 | [ access(read) 134 | ]), 135 | yaml_read(File, Index0), 136 | sort(date, @>=, Index0.posts, Index). 137 | 138 | blog_index(Blogs) --> 139 | { map_list_to_pairs(key_blog_year, Blogs, Tagged), 140 | group_pairs_by_key(Tagged, ByYear) 141 | }, 142 | html(div(class('blog-index'), 143 | \sequence(blog_year, ByYear))). 144 | 145 | key_blog_year(Blog, Year) :- 146 | split_string(Blog.get(date), "-", "", [YS|_]), 147 | number_string(Year, YS). 148 | 149 | blog_year(Year-Blogs) --> 150 | html(div(class('blog-year-index'), 151 | [ div(class('blog-year'), Year), 152 | div(class('blog-year-entries'), 153 | \sequence(blog_index_entry, Blogs)) 154 | ])). 155 | 156 | blog_index_entry(Blog) --> 157 | { atomics_to_string(Blog.get(tags,[]),"|",Tags), 158 | http_link_to_id(blog, path_postfix(Blog.file), HREF) 159 | }, 160 | html(a([ class('blog-index-entry'), 161 | 'data-tags'(Tags), 162 | href(HREF) 163 | ], 164 | [ \block_date(Blog), 165 | \block_title(Blog) 166 | ])). 167 | 168 | block_date(Blog) --> 169 | optional(html(span(class('blog-index-date'),Blog.get(date))), []). 170 | block_title(Blog) --> 171 | optional(html(span(class('blog-index-title'),Blog.get(title))), []). 172 | 173 | 174 | /******************************* 175 | * DISCOURSE * 176 | *******************************/ 177 | 178 | discourse(Request) --> 179 | { cdn_url(Request, URL) }, 180 | html(div(id('discourse-comments'), [])), 181 | js_script({|javascript(URL)|| 182 | window.DiscourseEmbed = { discourseUrl: 'https://swi-prolog.discourse.group/', 183 | discourseEmbedUrl: URL }; 184 | 185 | (function() { 186 | var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true; 187 | d.src = window.DiscourseEmbed.discourseUrl + 'javascripts/embed.js'; 188 | (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(d); 189 | })(); 190 | |}). 191 | 192 | 193 | cdn_url(Request, CDNURL) :- 194 | memberchk(request_uri(ReqURL), Request), 195 | server(cdn, CDN, _), 196 | format(atom(CDNURL), 'https://~w~w', [CDN, ReqURL]). 197 | 198 | 199 | /******************************* 200 | * UPDATE * 201 | *******************************/ 202 | 203 | %! pull_blogs 204 | % 205 | % Do a git pull on the blog repo 206 | 207 | pull_blogs :- 208 | ( absolute_file_name(blog(.), BlogDir, 209 | [ file_type(directory), 210 | access(write), 211 | solutions(all) 212 | ]), 213 | is_git_directory(BlogDir), 214 | git([pull], [directory(BlogDir)]), 215 | fail 216 | ; purge_location('/blog') 217 | ). 218 | 219 | 220 | /******************************* 221 | * HTTP * 222 | *******************************/ 223 | 224 | :- http_handler(root(blog/pull), pull_blogs, []). 225 | 226 | pull_blogs(Request) :- 227 | ( option(method(post), Request) 228 | -> http_read_json(Request, JSON), 229 | print_message(informational, got(JSON)) 230 | ; true 231 | ), 232 | call_showing_messages(pull_blogs, []). 233 | -------------------------------------------------------------------------------- /changelog.pl: -------------------------------------------------------------------------------- 1 | :- module(changelog, 2 | [ 3 | ]). 4 | :- use_module(library(settings)). 5 | :- use_module(library(process)). 6 | :- use_module(library(readutil)). 7 | :- use_module(library(http/http_dispatch)). 8 | :- use_module(library(http/http_parameters)). 9 | :- use_module(library(http/html_write)). 10 | :- use_module(wiki). 11 | 12 | :- setting(sources, 13 | atom, 14 | '~/src/swipl-devel', 15 | 'Sourced directory for getting changelog'). 16 | :- setting(branches, 17 | list(any), 18 | [ development = 'origin/master', 19 | stable = 'stable/master' 20 | ], 21 | 'Branches displayed'). 22 | :- setting(default_branch, 23 | atom, 24 | development, 25 | 'Default branch to show'). 26 | 27 | :- http_handler(root('ChangeLog'), changelog, [pool(wiki)]). 28 | 29 | %% changelog(+Request) 30 | % 31 | % HTTP handler that shows the ChangeLog since a given version. 32 | 33 | changelog(Request) :- 34 | http_parameters(Request, 35 | [ from(VFrom, [optional(true)]), 36 | to(VTo, [optional(true)]), 37 | branch(Branch, [optional(true)]) 38 | ]), 39 | defaults(VFrom, VTo, Branch), 40 | changelog_dom(VFrom-VTo, DOM), 41 | ( memberchk(h1(_, TitleParts), DOM) 42 | -> atomic_list_concat(TitleParts, Title) 43 | ; Title = 'SWI-Prolog ChangeLog' 44 | ), 45 | reply_html_page(pldoc(default), 46 | title(Title), 47 | [ \alt_branches(Branch), ', ', 48 | \alt_versions(Branch, VFrom, VTo) 49 | | DOM 50 | ]). 51 | 52 | defaults(VFrom, VTo, _Branch) :- 53 | nonvar(VFrom), nonvar(VTo), !. 54 | defaults(VFrom, VTo, Branch) :- 55 | ( var(Branch) 56 | -> setting(default_branch, Branch) 57 | ; true 58 | ), 59 | branch_versions(Branch, Versions), 60 | append(_, [VTo,VFrom|_], Versions), !. 61 | 62 | :- dynamic 63 | changelog_cache/2, 64 | changelog_seen/2. 65 | 66 | changelog_dom(Range, DOM) :- 67 | changelog_cache(Range, DOM), !, 68 | get_time(Now), 69 | retractall(changelog_seen(Range, _)), 70 | assertz(changelog_seen(Range, Now)). 71 | changelog_dom(Range, DOM) :- 72 | changelog(Range, Codes), 73 | wiki_file_codes_to_dom(Codes, -, DOM), 74 | assertz(changelog_cache(Range, DOM)), 75 | get_time(Now), 76 | assertz(changelog_seen(Range, Now)), 77 | clean_cached_changelogs(5). 78 | 79 | clean_cached_changelogs(Keep) :- 80 | repeat, 81 | predicate_property(changelog_cache(_,_), number_of_clauses(N)), 82 | ( N > Keep 83 | -> retract(changelog_seen(Range, _)), 84 | retractall(changelog_cache(Range, _)), 85 | fail 86 | ; ! 87 | ). 88 | 89 | changelog(Range, Codes) :- 90 | setting(sources, SourceDir0), 91 | expand_file_name(SourceDir0, [SourceDir]), 92 | directory_file_path(SourceDir, 'scripts/mkchangelog', Script), 93 | range_arg(Range, Versions), 94 | setup_call_cleanup( 95 | process_create(Script, ['--wiki', Versions], 96 | [ stdout(pipe(Out)), 97 | cwd(SourceDir) 98 | ]), 99 | read_stream_to_codes(Out, Codes), 100 | close(Out)). 101 | 102 | range_arg(From-To, Versions) :- 103 | atomic_list_concat([From, To], '..', Versions). 104 | range_arg(From, From). 105 | 106 | %% alt_branches(+Current)// is det. 107 | 108 | alt_branches(Branch) --> 109 | { setting(branches, Branches), 110 | maplist(arg(1), Branches, Aliases) 111 | }, 112 | html(b('Branch:')), 113 | ( { select(Branch, Aliases, Switch) } 114 | -> ( { Switch \== [] } 115 | -> html([' ', Branch, ' (switch to ' | \branches(Switch)]), 116 | html(')') 117 | ; [] 118 | ) 119 | ; branches(Aliases) 120 | ). 121 | 122 | branches([]) --> []. 123 | branches([H|T]) --> branch(H), branches(T). 124 | 125 | branch(B) --> 126 | { http_link_to_id(changelog, [branch(B)], HREF) 127 | }, 128 | html([' ', a(href(HREF), B)]). 129 | 130 | alt_versions(Branch, _VFrom, _VTo) --> 131 | { var(Branch) }, !. 132 | alt_versions(Branch, VFrom, VTo) --> 133 | { branch_versions(Branch, Versions), 134 | http_link_to_id(changelog, [], Action) 135 | }, 136 | html([ form([action(Action), style('display:inline')], 137 | [ input([type(hidden), name(branch), value(Branch)]), 138 | b('version'),' ', 139 | \select(from, Versions, VFrom), 140 | b(' to version '), 141 | \select(to, Versions, VTo), ' ', 142 | input([ type(submit), 143 | value('Update') 144 | ]) 145 | ]) 146 | ]). 147 | 148 | select(Name, Values, Selected) --> 149 | html(select(name(Name), \values(Values, Selected))). 150 | 151 | values([], _) --> []. 152 | values([H|T], Selected) --> value(H, Selected), values(T, Selected). 153 | 154 | value(V, V) --> !, 155 | html(option([selected], V)). 156 | value(V, _) --> 157 | html(option(V)). 158 | 159 | %% versions(Branch, Versions) is det. 160 | % 161 | % Retrieve the versions that are available for Branch. 162 | 163 | :- dynamic 164 | version_cache/3. % Branch, Retrieved, Versions 165 | 166 | branch_versions(Alias, Versions) :- 167 | setting(branches, Map), 168 | memberchk(Alias=Branch, Map), 169 | versions(Alias, Branch, Versions). 170 | 171 | versions(_, Branch, Versions) :- 172 | version_cache(Branch, Retrieved, Versions), 173 | get_time(Now), 174 | Now-Retrieved < 600, !. 175 | versions(Alias, Branch, Versions) :- 176 | retractall(version_cache(Branch, _, _)), 177 | versions_no_cache(Branch, AllVersions), 178 | include(branch_version(Alias), AllVersions, Versions), 179 | get_time(Now), 180 | assertz(version_cache(Branch, Now, Versions)). 181 | 182 | versions_no_cache(Branch, Versions) :- 183 | git_repo(Repo), 184 | git_tags_on_branch(Repo, Branch, Tags), 185 | tags_versions(Tags, Versions). 186 | 187 | tags_versions([], []). 188 | tags_versions([H|T], Versions) :- 189 | atomic_list_concat(Tags, ', ', H), 190 | ( Tags = [Tag] 191 | -> ( atom_concat('V', Version, Tag) 192 | -> Versions = [Version|VT], 193 | tags_versions(T, VT) 194 | ; tags_versions(T, Versions) 195 | ) 196 | ; append(Tags, T, AllTags), 197 | tags_versions(AllTags, Versions) 198 | ). 199 | 200 | git_repo(Repo) :- 201 | setting(sources, SourceDir0), 202 | expand_file_name(SourceDir0, [Repo]). 203 | 204 | %% branch_version(+BranchAlias, +Version) 205 | % 206 | % True if Version is a stable version 207 | 208 | branch_version(stable, Version) :- !, 209 | atomic_list_concat([_Major,MinorAtom,_Patch], '.', Version), 210 | atom_number(MinorAtom, Minor), 211 | Minor mod 2 =:= 0. 212 | branch_version(_, _). 213 | -------------------------------------------------------------------------------- /changes.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2019, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(plweb_changes, 31 | [ load_changes/2, % +GitDir, +Revisions 32 | changes/2 % +About, -Changes 33 | ]). 34 | :- use_module(library(git)). 35 | :- use_module(library(dcg/basics)). 36 | :- use_module(library(debug)). 37 | 38 | /** Examine changelog entries relevant for a predicate 39 | 40 | This is some demo code to extract changelog entries that mention a 41 | particular predicate. The aim is to use this in the online manual to 42 | inform people about relevant changes. 43 | */ 44 | 45 | :- dynamic changelog/5. % Commit, Version, About, Level, Subject 46 | 47 | :- debug(changelog). 48 | 49 | load_changes :- 50 | load_changes('..', 'V5.6.0..'). 51 | 52 | load_changes(Dir, Revisions) :- 53 | retractall(changelog(_,_,_,_,_)), 54 | git_shortlog(Dir, Logs, [revisions(Revisions)]), 55 | length(Logs, Len), 56 | debug(changelog, 'Processing ~D commits', [Len]), 57 | process_logs(Logs, 'HEAD'), 58 | predicate_property(changelog(_,_,_,_,_), number_of_clauses(N)), 59 | debug(changelog, 'Extracted ~D records', [N]). 60 | 61 | process_logs([], _). 62 | process_logs([H|T], Version0) :- 63 | update_version(H, Version0, Version), 64 | parse_commit(H, Level, Abouts), 65 | ( Abouts \== [] 66 | -> git_log_data(commit_hash, H, Commit), 67 | git_log_data(subject, H, Subject), 68 | forall(member(About, Abouts), 69 | assertz(changelog(Commit, Version, About, Level, Subject))) 70 | ; true 71 | ), 72 | process_logs(T, Version). 73 | 74 | update_version(Commit, _Version0, Version) :- 75 | git_log_data(ref_names, Commit, RefNames), 76 | member(RefName, RefNames), 77 | string_concat("tag: V", Nr, RefName), 78 | split_string(Nr, ".", "", NumS), 79 | maplist(number_string, [Major, Minor, Patch], NumS), 80 | !, 81 | Version is 10000*Major+100*Minor+Patch. 82 | update_version(_, Version, Version). 83 | 84 | parse_commit(Commit, Level, Abouts) :- 85 | git_log_data(subject, Commit, Subject), 86 | string_codes(Subject, Codes), 87 | phrase(subject(Level, Abouts), Codes). 88 | 89 | subject(Level, Abouts) --> 90 | level(Level), 91 | abouts(Abouts0), 92 | { sort(Abouts0, Abouts) 93 | }. 94 | 95 | level(Level) --> 96 | capitals(List), ":", 97 | { List \== [] }, 98 | !, 99 | { string_codes(S, List), 100 | downcase_atom(S, Level) 101 | }. 102 | level(unclassified) --> 103 | []. 104 | 105 | abouts([H|T]) --> about(H), !, abouts(T). 106 | abouts([]) --> remainder(_). 107 | 108 | about(Name/Arity) --> 109 | string(_), 110 | blank, 111 | prolog_identifier(Name), 112 | arity(Arity), 113 | !. 114 | 115 | prolog_identifier(Name) --> 116 | [C0], { code_type(C0, prolog_atom_start) }, !, 117 | prolog_id_cont(CL), 118 | { atom_codes(Name, [C0|CL]) }. 119 | 120 | prolog_id_cont([H|T]) --> 121 | [H], { code_type(H, prolog_identifier_continue) }, !, 122 | prolog_id_cont(T). 123 | prolog_id_cont([]) --> "". 124 | 125 | arity(Arity) --> "//", nonneg(A0), !, { Arity is A0+2 }. 126 | arity(Arity) --> "/", nonneg(Arity). 127 | 128 | 129 | capitals([H|T]) --> capital(H), !, capitals(T). 130 | capitals([]) --> []. 131 | 132 | capital(H) --> [H], { between(0'A, 0'Z, H) }. 133 | 134 | nonneg(I) --> int_codes(Codes), { number_codes(I, Codes) }. 135 | 136 | int_codes([D0|D]) --> digit(D0), digits(D). 137 | 138 | 139 | /******************************* 140 | * QUERY * 141 | *******************************/ 142 | 143 | changes(About, Commits) :- 144 | findall(commit(Commit, Version, Level, Subject), 145 | changelog(Commit, Version, About, Level, Subject), 146 | Commits). 147 | 148 | -------------------------------------------------------------------------------- /contents.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2013, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(man_contents, 31 | []). 32 | :- use_module(library(pldoc/doc_man)). 33 | :- use_module(library(pldoc/doc_html)). 34 | :- use_module(library(http/html_write)). 35 | :- use_module(library(http/http_dispatch)). 36 | 37 | :- http_handler(root(contents), contents, []). 38 | 39 | /** Documentation contents as a hierarchy 40 | */ 41 | 42 | contents(_Request) :- 43 | reply_html_page( 44 | pldoc(tree), 45 | title('SWI-Prolog manual'), 46 | html(\contents([ secref_style(title) 47 | ]))). 48 | 49 | %% contents(+Options)// 50 | % 51 | % Emit a =ul= hierarchy of the entire manual, including the 52 | % packages. 53 | 54 | contents(Options) --> 55 | html(ul([ \refman_contents(swi('doc/Manual'), Options), 56 | \package_contents(Options) 57 | ])). 58 | 59 | refman_contents(Dir, Options) --> 60 | { man_content_tree(Dir, Tree) 61 | }, 62 | man_tree(Tree, Options). 63 | 64 | package_contents(Options) --> 65 | { man_packages_tree(Tree) 66 | }, 67 | man_tree(Tree, Options). 68 | 69 | man_tree(node(Obj, Children), Options) --> 70 | html(li(\contents_link(Obj, Options))), 71 | ( { Children == [] } 72 | -> [] 73 | ; html(ul(\man_children(Children, Options))) 74 | ). 75 | man_tree(Obj, Options) --> 76 | html(li(\contents_link(Obj, Options))). 77 | 78 | man_children([], _) --> []. 79 | man_children([H|T], Options) --> man_tree(H, Options), man_children(T, Options). 80 | 81 | contents_link(manual, _) --> !, 82 | { http_link_to_id(pldoc_root, [], HREF) }, 83 | html(a(href(HREF), 'Base system')). 84 | contents_link(packages, _) --> !, 85 | { http_link_to_id(pldoc_index, [], HREF) }, % wrong link 86 | html(a(href(HREF), 'Packages')). 87 | contents_link(Obj, Options) --> 88 | object_ref(Obj, Options). 89 | -------------------------------------------------------------------------------- /customise.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2009, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(plweb_customise, 31 | [ 32 | ]). 33 | :- include(library(pldoc/hooks)). 34 | 35 | prolog:doc_places_menu(_) --> 36 | []. 37 | -------------------------------------------------------------------------------- /daemon.pl: -------------------------------------------------------------------------------- 1 | :- use_module(library(http/http_log)). 2 | 3 | % Avoid that XPCE creates a thread because that stops us forking. 4 | :- set_prolog_flag(xpce_threaded, false). 5 | :- [load]. 6 | 7 | :- http_schedule_logrotate(monthly(1, 04:00), 8 | [ keep_logs(6) 9 | ]). 10 | % library(http/http_unix_daemon) is loaded when loading the library 11 | :- initialization(http_daemon, main). 12 | 13 | -------------------------------------------------------------------------------- /daemon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: swipl-website 4 | # Required-Start: $local_fs $remote_fs $network $syslog $named 5 | # Required-Stop: $local_fs $remote_fs $network $syslog $named 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # X-Interactive: true 9 | # Short-Description: Start/stop SWI-Prolog web server 10 | ### END INIT INFO 11 | 12 | # Installation 13 | # 14 | # 1. Copy this file to /etc/init.d/ 15 | # 2. Edit the Configuration section below 16 | # 3. Run 17 | # % update-rc.d defaults 18 | # % /etc/init.d/ start 19 | 20 | # Configuration section 21 | # 22 | SWIPL=/home/janw/bin/swipl 23 | DIR=/home/janw/src/plweb 24 | SCRIPT=daemon.pl 25 | USER=www 26 | PORT=80 27 | DAEMONARGS= 28 | 29 | # Uncomment to only listen for connections from localhost 30 | # DAEMONARGS="$DAEMONARGS --ip=localhost" 31 | 32 | HTTPID=swipl-httpd-$PORT 33 | PIDFILE=/var/run/$HTTPID.pid 34 | SYSLOG=$HTTPID 35 | 36 | # End of normal configuration 37 | 38 | . /lib/lsb/init-functions 39 | test -f /etc/default/rcS && . /etc/default/rcS 40 | 41 | DAEMONARGS="$DAEMONARGS --port=$PORT --user=$USER --fork" 42 | if [ ! -z "$SYSLOG" ]; then DAEMONARGS="$DAEMONARGS --syslog=$SYSLOG"; fi 43 | if [ ! -z "$PIDFILE" ]; then DAEMONARGS="$DAEMONARGS --pidfile=$PIDFILE"; fi 44 | 45 | pidofserver() 46 | { if [ -f "$PIDFILE" ]; then 47 | cat "$PIDFILE" 48 | else 49 | ps aux | grep "[0-9] *$SWIPL.*--port=$PORT" 2>/dev/null | awk '{print $2}' 50 | fi 51 | } 52 | 53 | running() 54 | { if [ -z "$1" ]; then return 1; fi 55 | 56 | if kill -0 $1 2> /dev/null; then 57 | return 0 58 | else 59 | return 1 60 | fi 61 | } 62 | 63 | waitserver() 64 | { i=0; 65 | 66 | while running $1; do 67 | if [ $i = '60' ]; then 68 | return 1; 69 | else 70 | if [ $i = '0' ]; then 71 | echo -n " ... waiting " 72 | else 73 | echo -n "." 74 | fi 75 | i=$(($i+1)) 76 | sleep 1 77 | fi 78 | done 79 | } 80 | 81 | case $1 in 82 | start) 83 | log_daemon_msg "Starting web server" "$HTTPID" 84 | if (cd $DIR && $SWIPL -q -f $SCRIPT -- $DAEMONARGS); then 85 | log_end_msg 0 86 | else 87 | log_end_msg 1 88 | fi 89 | ;; 90 | stop) 91 | log_daemon_msg "Stopping web server" "$HTTPID" 92 | PID=$(pidofserver) 93 | kill $PID 94 | if waitserver $PID; then 95 | log_end_msg 0 96 | else 97 | kill -9 $PID 98 | waitserver $PID 99 | fi 100 | ;; 101 | reload) 102 | PID=$(pidofserver) 103 | kill -HUP $PID 104 | ;; 105 | restart) 106 | $0 stop && $0 start 107 | ;; 108 | status) 109 | PID=$(pidofserver) 110 | if running "$PID"; then 111 | echo "SWI-Prolog HTTP server is running (pid $PID)." 112 | exit 0 113 | else 114 | echo "SWI-Prolog HTTP server is NOT running." 115 | if [ -e $PIDFILE ]; then 116 | exit 1 117 | else 118 | exit 3 119 | fi 120 | fi 121 | ;; 122 | *) 123 | log_success_msg "Usage: /etc/init.d/swipl-httpd {start|stop|restart|status}" 124 | exit 1 125 | esac 126 | -------------------------------------------------------------------------------- /debug.pl: -------------------------------------------------------------------------------- 1 | % Debug file for project `plweb`. 2 | user:debug_project. 3 | 4 | %:- initialization(init_plweb). 5 | %init_plweb:- 6 | % source_file(init_plweb, ThisFile), 7 | % file_directory_name(ThisFile, ThisDir), 8 | % assert(user:file_search_path(plweb, ThisDir)). 9 | 10 | % Print code strings with their code table replacements. 11 | :- use_module(library(portray_text)). 12 | :- portray_text(true). 13 | :- set_portray_text(ellipsis, 100). 14 | 15 | % Enforce more stringent style checking. 16 | :- style_check(+atom). 17 | :- style_check(+charset). 18 | :- style_check(+discontiguous). 19 | :- style_check(+no_effect). 20 | :- style_check(+singleton). 21 | %:- style_check(+var_branches). 22 | 23 | % Notice this gives information to hackers 24 | % when loaded from within a production environment! 25 | :- use_module(library(http/http_error)). 26 | 27 | % This library allows for exploiting the color and attribute facilities 28 | % of most modern terminals using ANSI escape sequences. 29 | % The Windows console (swipl-win) does not (yet) support ANSI (color) codes. 30 | :- use_module(library(ansi_term)). 31 | 32 | % PCE-based debug monitor. 33 | %:- use_module(library(swi_ide)). 34 | %:- prolog_ide(debug_monitor). 35 | 36 | % Make some debug message categories visible. 37 | :- use_module(library(debug)). 38 | :- debug(plweb). 39 | 40 | % Run unit tests, unless compiled with optimisation turned on. 41 | :- use_module(library(plunit)). 42 | :- set_test_options([load(normal),run(manual)]). 43 | %:- set_test_options([load(normal),run(make(all))]). 44 | 45 | :- [load]. 46 | :- server. 47 | 48 | %:- use_module(library(settings)). 49 | %:- set_setting_default(http:public_host, localhost). 50 | %:- set_setting_default(http:public_port, setting(http:port)). 51 | 52 | :- use_module(library(settings)). 53 | :- set_setting_default( 54 | recaptcha:public_key, 55 | '6LeRb-wSAAAAAAAMSfqiceu8u7QHOsfe4WOBJL44' 56 | ). 57 | :- set_setting_default( 58 | recaptcha:private_key, 59 | '6LeRb-wSAAAAAM9IwyUZBtSvbK38kTjUn5xJhpha' 60 | ). 61 | 62 | -------------------------------------------------------------------------------- /doc/Examples.md: -------------------------------------------------------------------------------- 1 | # Adding examples to the SWI-Prolog website 2 | 3 | ## An Example is a markdown file 4 | 5 | ---------------------------------------------------------------- 6 | # Title 7 | 8 | Bla bla 9 | 10 | ``` 11 | Code 12 | ``` 13 | 14 | ``` 15 | ?- query 16 | ``` 17 | 18 | @see p/1, p/2, library(x) 19 | ---------------------------------------------------------------- 20 | 21 | ## Indexing 22 | 23 | - If @see, link to the predicate indicators and libraries listed 24 | - Else, scan the code and find it 25 | 26 | ## Display 27 | 28 | - Code rendered using htmlsrc when possible. 29 | - Extend to link goals. 30 | 31 | ## Include in the website 32 | 33 | - Show shortest if < N lines 34 | - Show max M others as carousel 35 | - _Show all all_ to fill carousel 36 | 37 | Show only fragment containing predicate from a more extensive example 38 | with _view all_? 39 | 40 | Embedded SWISH like LPN? 41 | 42 | 43 | 44 | ## Import/export to SWISH 45 | 46 | - Sequence of markdown and code blocks. 47 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bookworm-slim 2 | 3 | RUN apt-get update && apt-get install -y --no-install-recommends \ 4 | git curl ca-certificates unzip \ 5 | build-essential cmake autoconf ninja-build pkg-config \ 6 | gdb \ 7 | cleancss node-requirejs uglifyjs \ 8 | ncurses-dev libreadline-dev libedit-dev \ 9 | libgoogle-perftools-dev \ 10 | libgmp-dev \ 11 | libssl-dev \ 12 | unixodbc-dev \ 13 | zlib1g-dev libarchive-dev \ 14 | libossp-uuid-dev \ 15 | libxext-dev libice-dev libjpeg-dev libxinerama-dev libxft-dev \ 16 | libxpm-dev libxt-dev \ 17 | libdb-dev \ 18 | libpcre2-dev \ 19 | libyaml-dev \ 20 | libpython3-dev \ 21 | default-jdk junit4 \ 22 | libssh-dev openssh-client \ 23 | locales 24 | 25 | RUN sed -i -e 's/# en_GB.UTF-8 UTF-8/en_GB.UTF-8 UTF-8/' /etc/locale.gen && \ 26 | locale-gen 27 | ENV LC_ALL en_GB.UTF-8 28 | ENV LANG en_GB.UTF-8 29 | ENV LANGUAGE en_GB:en 30 | 31 | # Primary build 32 | 33 | ENV REBUILD_MOST 4 34 | RUN mkdir -p /usr/local/src && cd /usr/local/src && \ 35 | git clone --recursive https://github.com/SWI-Prolog/swipl-devel.git && \ 36 | cd swipl-devel && mkdir build && cd build && \ 37 | cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=PGO -DSWIPL_DOC_SERVER=ON -G Ninja .. && \ 38 | ninja && ninja install 39 | 40 | ENV REBUILD_PLWEB 5 41 | RUN mkdir -p /srv && cd /srv && \ 42 | git clone https://github.com/SWI-Prolog/plweb.git && \ 43 | cd /srv/plweb && \ 44 | git submodule update --init packs/googleclient \ 45 | packs/recaptcha \ 46 | packs/smtp \ 47 | packs/libssh 48 | 49 | RUN cd /srv/plweb && \ 50 | SWIPL_PACK_PATH=packs swipl -g pack_rebuild -t halt 51 | 52 | RUN cd /usr/local/src/swipl-devel && \ 53 | git remote add stable https://github.com/SWI-Prolog/swipl.git && \ 54 | git fetch stable 55 | 56 | # Update. Run `make update-plweb` or `make update-swipl` to update 57 | # the `ENV` command below and redo the relevant part of the build 58 | 59 | ENV SWIPL_VERSION Wed 31 Jan 2024 06:17:31 PM CET 60 | RUN git config --global pull.ff only 61 | RUN cd /usr/local/src/swipl-devel && (git pull || git pull) && \ 62 | git fetch stable && \ 63 | git submodule update --init && \ 64 | find build -name '*.qlf' | xargs rm && \ 65 | cd build && rm -rf home && cmake . && ninja && \ 66 | ninja install 67 | ENV PLWEB_VERSION Sun Feb 11 10:09:26 AM CET 2024 68 | RUN cd /srv/plweb && (git pull || git pull) && \ 69 | git submodule update 70 | 71 | # Running 72 | 73 | copy health.sh health.sh 74 | HEALTHCHECK --interval=30s --timeout=2m --start-period=1m CMD /health.sh 75 | 76 | COPY start-plweb.sh /srv/start-plweb.sh 77 | 78 | ENV PLWEB_DATA /srv/plweb/data 79 | ENV PLWEB_HOME /srv/plweb 80 | VOLUME ${PLWEB_DATA} 81 | WORKDIR ${PLWEB_HOME} 82 | 83 | ENTRYPOINT ["/srv/start-plweb.sh"] 84 | -------------------------------------------------------------------------------- /docker/Makefile: -------------------------------------------------------------------------------- 1 | RESTART=--restart unless-stopped 2 | VOLUME=$(shell cd .. && pwd)/data 3 | PORT=3400 4 | 5 | PUBLISH=--publish=${PORT}:3400 --publish=3420:2022 6 | DOPTS=${PUBLISH} -v ${VOLUME}:/srv/plweb/data 7 | IMG=plweb 8 | 9 | all: 10 | @echo "Targets" 11 | @echo 12 | @echo "image Build the plweb image" 13 | @echo "run Run the image (detached)" 14 | @echo "restart Stop and restart the image" 15 | @echo 16 | @echo "update-mostly Rebuild most except for the OS" 17 | @echo "update-swipl Rebuild swipl and plweb" 18 | @echo "update-plweb Rebuild plweb" 19 | 20 | image:: 21 | docker build -t $(IMG) . 22 | 23 | run: 24 | docker run --name=plweb -d ${RESTART} ${DOPTS} $(IMG) 25 | 26 | restart: 27 | docker stop plweb 28 | docker rm plweb 29 | make run 30 | 31 | bash: 32 | docker run -it ${DOPTS} $(IMG) --bash 33 | 34 | update-plweb: 35 | sed -i "s/PLWEB_VERSION.*/PLWEB_VERSION $$(date)/" Dockerfile 36 | make image 37 | 38 | update-swipl: 39 | sed -i "s/SWIPL_VERSION.*/SWIPL_VERSION $$(date)/" Dockerfile 40 | make image 41 | 42 | update-mostly: 43 | sed -i "s/REBUILD_MOST.*/REBUILD_MOST $$(date)/" Dockerfile 44 | make image 45 | 46 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # A SWISH (SWI-Prolog for SHaring) docker 2 | 3 | This repository provides a [Docker](https://www.docker.com/) image for 4 | the [public swish instance](https://swish.swi-prolog.org). This image 5 | is similar to the normal [SWISH](https://hub.docker.com/r/swipl/swish/) 6 | Docker image. However, it is based on the GIT version of Prolog and 7 | contains several additional packages such as CHAT80 and Wordnet. 8 | 9 | ## Building the image 10 | 11 | The image is built by running 12 | 13 | make image 14 | 15 | ## Running the image 16 | 17 | > Needs to be updated 18 | 19 | The image may be used in many configurations, both controlled by docker 20 | options and options to the entry point. As basic operation typically 21 | already requires publishing ports and setting up a volume for the data, 22 | we added a bash script `swish.sh` that automates the common scenarios by 23 | providing `docker run` options from defaults and provided options. When 24 | called with `-n`, as in `./swish.sh -n option ...`, it merery prints the 25 | docker command it will execute. The following options are processed: 26 | 27 | - `--port=N`
28 | Modify the `-p` option to `-p N:3050`. Default is 3050. 29 | - `--data=dir`
30 | Mount the given directory as data. Default is the working 31 | directory. 32 | - `--with-R[=from]`
33 | Add `--volumes-from from` where _from_ defaults to `rserve` 34 | to connect to an 35 | [R docker image](https://github.com/JanWielemaker/rserve-sandbox) 36 | - `-n`
37 | Just print the docker command that will be executed. 38 | - `-d`
39 | Detach from the terminal 40 | - `-it`
41 | Pass on (interactive) 42 | 43 | All remaining options are passed to the entry point of the image. 44 | 45 | ### Data 46 | 47 | The docker image maintains its data (user programs and configuration) on 48 | the _volume_ `/data`. This may be mapped to a host directory using the 49 | docker `-v` options (see also the `--data=dir` option of `swish.sh`). 50 | 51 | Within the data directory, SWISH manages the following items: 52 | 53 | - **config-enabled** is a directory where the configuration is stored. 54 | If it doesn't exist it is created and filled with a default 55 | configuration that depends on the provided options. The directory 56 | and its files have the same owner and group as the root of the managed 57 | volume. 58 | 59 | - **data** is the directory where all dynamic user data is maintained. 60 | If the directory exists, the SWISH server is started with the user and 61 | group of the directory. If it doesn't exist the directory is created 62 | as owned by `daemon.daemon` and the server is started with these 63 | credentials. 64 | 65 | - **https** If the `--https` option is passed, it creates or reuses 66 | this directory and the files `server.crt` and `server.key`. The 67 | created certificate is _self-signed_. 68 | 69 | - **passwd** If authenticated mode is enabled, this file maintains 70 | the users and password hashes. 71 | 72 | 73 | ### Network access 74 | 75 | The container creates a server at port `3050`. By default this is an 76 | HTTP server. If `run swish --https` is used, an HTTPS 77 | server is started. 78 | 79 | 80 | ### The entry point 81 | 82 | The entry point of the containser is `/entry.sh`, a shell script that 83 | initialises the data volume if needed and starts the server. It accepts 84 | the following options: 85 | 86 | - `--bash`
87 | Instead of starting the server, start a bash shell. Terminate after 88 | bash completes. 89 | 90 | - `--help`
91 | Emit short help. 92 | 93 | 94 | ### Configuration 95 | 96 | The SWISH configuration is controlled by files in the `config-enabled` 97 | directory. Several commands may be used to control the configuration. 98 | These are executed as below: 99 | 100 | ``` 101 | docker run -it swish option ... 102 | ``` 103 | 104 | Multiple configuration options may be passed to update multiple facets 105 | of the configuration. Normally the image stops after updating the 106 | configuration. If `--run` is added the entry point starts the server 107 | after updating the configuration. 108 | 109 | The options provided are: 110 | 111 | - `--list-config`
112 | List installed and available configuration items. If an item is 113 | installed, indicate whether it is modified. 114 | 115 | - `--auth=type`
116 | Change the configured authentication scheme. This is one of 117 | 118 | - `always`
119 | Run in fully _authenticated_ mode, forcing the user to login 120 | and allowing to execute arbitrary commands. When executed for 121 | the first time, docker must be run _interactively_ (`run -it`) 122 | to create the first user. Additional users are created using 123 | 124 | `docker run -it swish --add-user`. 125 | 126 | - `social`
127 | Use _social_ login. By default enables optional http login, 128 | login using google and stackoverflow. Both need to be further 129 | configured by editing `config-enabled/auth_google.pl` and 130 | `config-enabled/auth_stackoverflow.pl` 131 | 132 | - `anon` (or anonymous)
133 | This is the initial default, providing fully anonymous login, 134 | executing only sandboxed Prolog queries. 135 | 136 | - `--add-config file ...`
137 | Add one or more configuration files by copying them from the 138 | available configuration directory. 139 | 140 | - `--add-user`
141 | Add a new user to the HTTP authentication. Prompts for user name, 142 | email, group (unused, use `users`) and password. 143 | 144 | - `--https`
145 | Create an HTTPS server. This uses the certificate from the 146 | `https` directory (see above). If no certificate exists, a 147 | self-signed certificate is created. The details may be refined 148 | using `--CN=host`, `--O=organization` and `--C=country` 149 | 150 | ## Starting R 151 | 152 | docker pull swipl/rserve 153 | docker run -d --net=none --name=rserve --restart unless-stopped swipl/rserve 154 | 155 | -------------------------------------------------------------------------------- /docker/health.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | exec &> health.log 3 | 4 | check() 5 | { auth= 6 | if [ -r health.auth ]; then 7 | auth="$(cat health.auth)" 8 | fi 9 | curl --fail -s --retry 3 --max-time 5 \ 10 | http://localhost:3400/health 11 | return $? 12 | } 13 | 14 | stop() 15 | { pid=1 16 | echo "Health check failed. Killing swish with SIGTERM" 17 | kill -s TERM 1 $pid 18 | timeout 10 tail --pid=$pid -f /dev/null 19 | if [ $? == 124 ]; then 20 | echo "Gracefull termination failed. Trying QUIT" 21 | kill -s QUIT $pid 22 | timeout 10 tail --pid=$pid -f /dev/null 23 | if [ $? == 124 ]; then 24 | echo "QUIT failed. Trying KILL" 25 | kill -s KILL $pid 26 | fi 27 | fi 28 | echo "Done" 29 | } 30 | 31 | starting() 32 | { if [ -f /var/run/epoch ]; then 33 | epoch=$(cat /var/run/epoch) 34 | running=$(($(date "+%s") - $epoch)) 35 | [ $running -gt 60 ] || return 1 36 | fi 37 | echo "Starting, so not killing" 38 | return 0 39 | } 40 | 41 | check || starting || stop 42 | 43 | -------------------------------------------------------------------------------- /docker/start-plweb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Start script for the PlWeb docker 4 | # 5 | # This script is started in /srv/plweb. 6 | 7 | start=--no-fork 8 | ssl= 9 | scheme=http 10 | udaemon=daemon 11 | uconfig=root 12 | config_run=no 13 | 14 | date "+%s" > /var/run/epoch 15 | 16 | usage() 17 | { echo "Usage: docker run [docker options] swish [swish options]" 18 | echo "swish options:" 19 | echo " --help Display this message" 20 | echo " --bash Just run bash in the container" 21 | echo " --auth=type Configure authentication>:" 22 | echo " always Force HTTP authentication" 23 | echo " social Allow HTTP and oauth2 authentication" 24 | echo " anonymous No authentication" 25 | echo " --add-user Add a new user" 26 | echo " --http Create an HTTP server" 27 | echo " --https Create an HTTPS server" 28 | echo " --CN=host Hostname for certificate" 29 | echo " --O=organization Organization for certificate" 30 | echo " --C=country Country for certificate" 31 | echo " --run Start SWISH after config options" 32 | echo " --list-config List configuration files" 33 | echo " --add-config file ... Add a configuration file" 34 | echo "" 35 | echo "--add-config should be the last option if used." 36 | } 37 | 38 | # `mkuser file user` creates user with the uid and gid of file. 39 | 40 | mkuser() 41 | { f="$1" 42 | u="$2" 43 | 44 | groupadd "$(ls -nd "$f" | awk '{printf "-g %s\n",$4 }')" -o $u 45 | useradd "$(ls -nd "$f" | awk '{printf "-u %s\n",$3 }')" -g $u -o $u 46 | } 47 | 48 | # If there is a data directory, reuse it and set our user to be the 49 | # native user of this directory. 50 | 51 | if [ -d /srv/plweb/data ]; then 52 | mkuser /srv/plweb/data plweb 53 | udaemon=plweb 54 | else 55 | mkdir /srv/plweb/data 56 | chown $udaemon.$udaemon /srv/plweb/data 57 | fi 58 | 59 | # Allow the daemon to get the git version 60 | mkdir -p /home/$udaemon 61 | chown $udaemon /home/$udaemon 62 | # su -c "git config --global --add safe.directory /swish" $udaemon 63 | 64 | if [ -t 0 ] ; then 65 | start=--interactive 66 | fi 67 | 68 | did_config=no 69 | 70 | while [ ! -z "$1" ]; do 71 | case "$1" in 72 | --bash) su $udaemon -c /bin/bash 73 | did_config=yes 74 | shift 75 | ;; 76 | --help) usage 77 | exit 0 78 | ;; 79 | *) usage 80 | exit 1 81 | ;; 82 | esac 83 | done 84 | 85 | if [ $did_config = yes -a $config_run = no ]; then 86 | exit 0 87 | fi 88 | 89 | ## Make the server stop on signals sent from the health.sh. Process 90 | ## 1 only accepts signals for which there is an installed signal 91 | ## handler. We cannot install a signal handler for SIGKILL and 92 | ## therefore forcefully killing that even works in the case of 93 | ## deadlocks does not work. We run the server in another pid 94 | ## to work around this issue. 95 | 96 | stop() 97 | { echo "signal = $1; child = $child_pid" 98 | 99 | kill -s $1 $child_pid 100 | timeout 10 tail --pid=$child_pid -f /dev/null 101 | if [ $? == 124 ]; then 102 | echo "Gracefull termination failed. Killing" 103 | kill -s KILL $child_pid 104 | fi 105 | 106 | exit 1 107 | } 108 | 109 | hangup() 110 | { echo "child = $child_pid" 111 | kill -s HUP $child_pid 112 | } 113 | 114 | trap "stop TERM" SIGTERM 115 | trap "stop QUIT" SIGQUIT 116 | trap "hangup" SIGHUP 117 | 118 | export HOME=/home/$udaemon 119 | 120 | git config --global --add safe.directory '*' 121 | git config --global user.email "wiki@swi-prolog.org" 122 | git config --global user.name "Wiki editor" 123 | 124 | swipl -Dsource ${PLWEB_HOME}/daemon.pl --port=3400 --user=$udaemon $start & 125 | child_pid=$! 126 | 127 | stat=129 128 | while [ $stat = 129 ]; do 129 | wait -f $child_pid 130 | stat=$? 131 | done 132 | -------------------------------------------------------------------------------- /download-custom/devel/bin/linux.txt: -------------------------------------------------------------------------------- 1 | Linux RPM package created on SuSE Linux 11.2. You may try on other 2 | Linux releases. If you are a one-time user, check whether your 3 | Linux distro provides SWI-Prolog. If you are a frequent user, 4 | consider to download and [[compile][]] the source. 5 | -------------------------------------------------------------------------------- /download-custom/devel/bin/macosx-bundle.txt: -------------------------------------------------------------------------------- 1 | Mac OS X disk image with [relocatable application 2 | bundle](https://en.wikipedia.org/wiki/Bundle_%28macOS%29#macOS_application_bundles). 3 | Needs [[xquartz][]]. Same as the _fat_ bundle, but only 4 | contains the `x86_64` binaries, compiled using gcc13 from Macports. This 5 | version is __30-40% faster__ than the fat binaries on Intel Macs. 6 | -------------------------------------------------------------------------------- /download-custom/devel/bin/macosx-fat-bundle.txt: -------------------------------------------------------------------------------- 1 | Mac OS X disk image with [relocatable application 2 | bundle](https://en.wikipedia.org/wiki/Bundle_%28macOS%29#macOS_application_bundles). 3 | Needs [[xquartz][]] (X11) installed for running the 4 | [[development tools][]]. The bundle also provides the 5 | commandline tools in the =Contents/MacOS= directory. Users of older 6 | MacOS versions are adviced to use Macports, Homebrew or install from 7 | source. This bundle contains universal (fat) binaries that run natively 8 | on Intel and Apple Silicon (M1, arm64). 9 | 10 | -------------------------------------------------------------------------------- /download-custom/devel/bin/macosx-lion.txt: -------------------------------------------------------------------------------- 1 | Installer with binaries created using [[Macports][]]. 2 | Installs =|/opt/local/bin/swipl|=. Needs [[xquartz][]] (X11) 3 | installed for running the [[development tools][]]. Currently, 4 | version 2.7.4 is required. You can check the version by opening an X11 5 | application and then checking `about' in the X11 menu. This binary is 6 | linked against GMP 5, which implies that it is covered by the LGPL-V3 7 | license. See below. 8 | -------------------------------------------------------------------------------- /download-custom/devel/bin/macosx-snow_leopard.txt: -------------------------------------------------------------------------------- 1 | Installer with binaries created using [[Macports][]]. 2 | Installs =|/opt/local/bin/swipl|=. Needs X11 (bundled with the MacOS X 3 | installer) and Developer Tools (Xcode) installed for running the 4 | [[development tools][]]. 5 | -------------------------------------------------------------------------------- /download-custom/devel/bin/macosx-snow_leopard_and_later.txt: -------------------------------------------------------------------------------- 1 | Mac OS X disk image with [relocatable application 2 | bundle](https://en.wikipedia.org/wiki/Bundle_%28macOS%29#macOS_application_bundles). 3 | Needs [[xquartz][]] (X11) installed for running the 4 | [[development tools][]]. The bundle also provides the 5 | commandline tools in the =Contents/MacOS= directory. 6 | 7 | -------------------------------------------------------------------------------- /download-custom/devel/bin/macosx.txt: -------------------------------------------------------------------------------- 1 | Installer with binaries created using [[Macports][]]. 2 | Installs =|/opt/local/bin/swipl|=. Needs [[xquartz][]] (X11) 3 | installed for running the [[development tools][]]. Currently, 4 | version 2.4.0 is required. You can check the version by opening an X11 5 | application and then checking `about' in the X11 menu. 6 | -------------------------------------------------------------------------------- /download-custom/devel/bin/space.txt: -------------------------------------------------------------------------------- 1 | Binary installer for the spatial indexing package 2 | [[space][]] 3 | 4 | -------------------------------------------------------------------------------- /download-custom/devel/bin/win32.txt: -------------------------------------------------------------------------------- 1 | Self-installing executable for Microsoft Windows 32-bit editions. 2 | Version 9.3 is that last version of SWI-Prolog that is also released for 3 | 32-bit. Note that this version lacks the Janus interface to Python. 4 | -------------------------------------------------------------------------------- /download-custom/devel/bin/win64.txt: -------------------------------------------------------------------------------- 1 | Self-installing executable for Microsoft Windows 64-bit editions. 2 | -------------------------------------------------------------------------------- /download-custom/devel/doc/doc-pdf.txt: -------------------------------------------------------------------------------- 1 | SWI-Prolog reference manual as PDF file. This does _not_ include the 2 | [[package][]] documentation. 3 | -------------------------------------------------------------------------------- /download-custom/devel/footer.txt: -------------------------------------------------------------------------------- 1 | ## About the development releases 2 | 3 | The development branches have their own GIT repository at the address 4 | below. See the general [[GIT][]] instructions for details. 5 | 6 | ``` 7 | % git clone https://github.com/SWI-Prolog/swipl-devel.git 8 | ``` 9 | 10 | ### Should I use the development release or the stable one? 11 | 12 | Many active SWI-Prolog users track the development releases or GIT 13 | versions for developing Prolog applications. Most of the time the 14 | development versions are fairly stable. Infrequent larger rewrites to 15 | the core infrastructure that may cause instability is typically 16 | announced on the [forum](https://swi-prolog.discourse.group/). 17 | 18 | Tracking the latest version offers some advantages for you are user: 19 | 20 | - Bug fixes are quickly available. 21 | - When you are stuck on a missing feature you can use this quickly 22 | after it has been added. 23 | - Although you are a little more often confronted with 24 | incompatibilities and regressions, such issues can be discussed and 25 | resolved. 26 | -------------------------------------------------------------------------------- /download-custom/devel/header.txt: -------------------------------------------------------------------------------- 1 | ---+ Download SWI-Prolog development versions 2 | 3 | [[linux.png;align="left",width="24px"][]] 4 | We collect information about available packages and issues for building 5 | on specific distros [[here][]]. 6 | We provide a [[PPA][]] 7 | for [[Ubuntu][http://www.ubuntu.com/]] and [[snap 8 | images][]] 9 | 10 | [[WIP.png;align="left",width="24px"][]] 11 | Examine the [[ChangeLog][]]. 12 | 13 | -------------------------------------------------------------------------------- /download-custom/devel/src/src-tgz.txt: -------------------------------------------------------------------------------- 1 | Sources in =|.tar.gz|= format, including packages. See [[build 2 | instructions][]]. See also the [GIT 3 | repository](https://github.com/SWI-Prolog). 4 | -------------------------------------------------------------------------------- /download-custom/old/header.txt: -------------------------------------------------------------------------------- 1 | ---+ Download SWI-Prolog old binaries 2 | 3 | These are binaries of some old SWI-Prolog [[versions][]]. 4 | We only provide the Windows binaries. You can access the sources via 5 | [[GIT][]]. 6 | -------------------------------------------------------------------------------- /download-custom/stable/bin/linux-rpm.txt: -------------------------------------------------------------------------------- 1 | Linux RPM package created on SuSE Linux 11.2. You may try on other 2 | Linux releases. If you are a one-time user, check whether your 3 | Linux distro provides SWI-Prolog. If you are a frequent user, 4 | consider to download and [[compile][]] the source. 5 | -------------------------------------------------------------------------------- /download-custom/stable/bin/linux.txt: -------------------------------------------------------------------------------- 1 | Tar image of a relocatable SWI-Prolog installation. This merged image 2 | was created on Ubuntu 10.10 (64-bit) and Debian 6.0 (32-bit). It 3 | contains copies of shared libraries that are less widely available 4 | and/or suffer regularly from version issues. If you are a one-time user, 5 | check whether your Linux distro provides SWI-Prolog. If you are a 6 | frequent user, consider to download and [[compile][]] 7 | the source. 8 | -------------------------------------------------------------------------------- /download-custom/stable/bin/macosx-bundle.txt: -------------------------------------------------------------------------------- 1 | Mac OS X disk image with [relocatable application 2 | bundle](https://en.wikipedia.org/wiki/Bundle_%28macOS%29#macOS_application_bundles). 3 | Needs [[xquartz][]]. Same as the _fat_ bundle, but only 4 | contains the `x86_64` binaries, compiled using gcc13 from Macports. This 5 | version is __30-40% faster__ than the fat binaries on Intel Macs. The intel 6 | version is not regularly updated. 7 | -------------------------------------------------------------------------------- /download-custom/stable/bin/macosx-fat-bundle.txt: -------------------------------------------------------------------------------- 1 | Mac OS X disk image with [relocatable application 2 | bundle](https://en.wikipedia.org/wiki/Bundle_%28macOS%29#macOS_application_bundles). 3 | Needs [[xquartz][]] (X11) installed for running the 4 | [[development tools][]]. The bundle also provides the 5 | commandline tools in the =Contents/MacOS= directory. Users of older 6 | MacOS versions are adviced to use Macports, Homebrew or install from 7 | source. This bundle contains universal (fat) binaries that run natively 8 | on Intel (x86_64) and Apple Silicon (M1-3, arm64). 9 | -------------------------------------------------------------------------------- /download-custom/stable/bin/macosx-snow_leopard.txt: -------------------------------------------------------------------------------- 1 | Installer with binaries created using [[Macports][]]. 2 | Installs =|/opt/local/bin/swipl|=. Needs X11 (bundled with the MacOS X 3 | installer) and Developer Tools (Xcode) installed for running the 4 | [[development tools][]]. 5 | -------------------------------------------------------------------------------- /download-custom/stable/bin/macosx-snow_leopard_and_later.txt: -------------------------------------------------------------------------------- 1 | Mac OS X disk image with *relocatable application bundle*. Needs 2 | [[xquartz][]] (X11) installed for running the [[development 3 | tools][]]. Currently, version 2.7.11 is required. You can 4 | check the version by opening an X11 application and then checking 5 | `about' in the X11 menu. The bundle also provides the commandline tools 6 | in =Contents/MacOS=. The command line tools need at least MacOS *10.6* 7 | (Snow Leopard). The graphical application needs at least MacOS *10.7* 8 | (Lion). 9 | -------------------------------------------------------------------------------- /download-custom/stable/bin/macosx-tiger.txt: -------------------------------------------------------------------------------- 1 | Installer with binaries created using [[Macports][]]. 2 | Installs =|/opt/local/bin/swipl|=. Needs X11 (bundled with the 3 | MacOS X installer installed for running the [[development tools][]] 4 | -------------------------------------------------------------------------------- /download-custom/stable/bin/macosx.txt: -------------------------------------------------------------------------------- 1 | Installer with binaries created using [[Macports][]]. 2 | Installs =|/opt/local/bin/swipl|=. Needs [[xquartz][]] (X11) 3 | and the Developer Tools (Xcode) installed for running the [[development 4 | tools][]] 5 | -------------------------------------------------------------------------------- /download-custom/stable/bin/win32.txt: -------------------------------------------------------------------------------- 1 | Self-installing executable for Microsoft Windows 32-bit editions. 2 | Version 9.3 is that last version of SWI-Prolog that is also released for 3 | 32-bit. Note that this version lacks the Janus interface to Python. 4 | -------------------------------------------------------------------------------- /download-custom/stable/bin/win64.txt: -------------------------------------------------------------------------------- 1 | Self-installing executable for Microsoft Windows 64-bit editions. 2 | -------------------------------------------------------------------------------- /download-custom/stable/doc/doc-pdf.txt: -------------------------------------------------------------------------------- 1 | SWI-Prolog reference manual as PDF file. This does _not_ include the 2 | [[package][]] documentation. 3 | -------------------------------------------------------------------------------- /download-custom/stable/footer.txt: -------------------------------------------------------------------------------- 1 | ## SWI-Prolog version 9.2 2 | 3 | The SWI-Prolog 9.2 is the latest stable release. Highlights: 4 | 5 | - Bundled Python bi-directional interface (Janus) 6 | - C++ interface version 2 (`SWI-ccp2.h`) covers much more 7 | of the SWI-Prolog C API and is more (type-)safe. 8 | - Sources are now strict C11 and may be compiled using 9 | e.g. VS2022 on Windows. 10 | - Many improvements to the tooling. 11 | - The profiler can provide accurate port counts 12 | - The coverage analysis tool can annotate files by line 13 | and collect data from multiple threads and Prolog runs. 14 | - The unit testing tool supports concurrent testing and 15 | hiding of output of succeeded tests. 16 | - The pack manager can deal with version and feature 17 | restrictions, plan multiple packs together, build using 18 | CMake. 19 | - Added an option for commandline tools that can be called 20 | as ``swipl [arg ...]``. Provides cli tools for 21 | pack management and `.qlf` compilation. 22 | -------------------------------------------------------------------------------- /download-custom/stable/header.txt: -------------------------------------------------------------------------------- 1 | # Download SWI-Prolog stable versions 2 | 3 | [[linux.png;align="left",width="24px"][]] 4 | Linux versions are often available as a package for your distribution. 5 | We collect information about available packages and issues for building 6 | on specific distros [[here][]]. 7 | We provide a [[PPA][]] 8 | for [[Ubuntu][http://www.ubuntu.com/]] and [[snap 9 | images][]] 10 | 11 | [[Android.png;align="left",width="24px"][]] Android 12 | binaries are available for [Termux](https://termux.com/) as the package 13 | `swi-prolog`. See also [[Building SWI-Prolog on Android using 14 | LinuxOnAndroid][]] 15 | 16 | [[windows.jpg;align="left",width="24px"][]] Please 17 | check the [[windows release notes][]] (also in the 18 | SWI-Prolog startup menu of your installed version) for details. 19 | 20 | [[WIP.png;align="left",width="24px"][]] 21 | Examine the [[ChangeLog][]]. 22 | 23 | -------------------------------------------------------------------------------- /download-custom/stable/src/src-tgz.txt: -------------------------------------------------------------------------------- 1 | Sources in =|.tar.gz|= format, including packages and generated documentation 2 | files. See [[build instructions][]]. 3 | -------------------------------------------------------------------------------- /fastly.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: jan@swi-prolog.org 5 | WWW: https://www.swi-prolog.org 6 | Copyright (C): 2020, SWI-Prolog Solutions b.v. 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(fastly, 31 | [ purge_location/1 % +Location 32 | ]). 33 | :- use_module(library(http/json)). 34 | :- use_module(library(http/http_open)). 35 | :- use_module(library(http/http_path)). 36 | 37 | :- use_module(parms). 38 | 39 | /** Purge pages on our CDN 40 | */ 41 | 42 | :- multifile 43 | http_open:map_method/2. 44 | 45 | http_open:map_method(purge, 'PURGE'). 46 | 47 | %! purge_location(+Location) is det. 48 | % 49 | % Send a purge request for a Fastly URL 50 | 51 | purge_location(Location) :- 52 | compound(Location), 53 | !, 54 | http_absolute_location(Location, Path, []), 55 | purge_location(Path). 56 | purge_location(Location) :- 57 | server(cdn, Server), 58 | format(string(URL), 'https://~w~w', [Server, Location]), 59 | setup_call_cleanup( 60 | http_open(URL, In, 61 | [ method(purge) 62 | ]), 63 | json_read_dict(In, Reply), 64 | close(In)), 65 | ( Reply.get(status) == "ok" 66 | -> true 67 | ; print_message(warning, fastly(purge(Reply))) 68 | ). 69 | 70 | -------------------------------------------------------------------------------- /footer.pl: -------------------------------------------------------------------------------- 1 | :- module( 2 | footer, 3 | [ 4 | footer//1, % +Options 5 | server_information//0 6 | ] 7 | ). 8 | 9 | /** Footer 10 | 11 | Footer for SWI-Prolog Web pages. 12 | 13 | @author Wouter Beek 14 | @version 2014/01 15 | */ 16 | 17 | :- use_module(library(http/html_head)). 18 | :- use_module(library(http/html_write)). 19 | :- use_module(library(http/js_write)). 20 | :- use_module(openid). 21 | :- use_module(tagit). 22 | :- use_module(annotation). 23 | 24 | :- html_resource(css('footer.css'), []). 25 | 26 | 27 | 'community-content'(Options) --> 28 | { option(object(Object), Options) }, !, 29 | html(div(id='community-content', 30 | [ \tagit_footer(Object, []), 31 | \annotation(Object) 32 | ])). 33 | 'community-content'(_) --> 34 | []. 35 | 36 | %% footer(+Options)// is det. 37 | % 38 | % Emit the footer, which contains the community content, server 39 | % address and user information. Options: 40 | % 41 | % * object(Object) 42 | % Display community content area for Object. 43 | % * show_user(+Boolean) 44 | % If =false=, omit the user 45 | 46 | footer(Options) --> 47 | html_requires(css('footer.css')), 48 | html(div(class=footer, 49 | [ \'community-content'(Options), 50 | \'footer-footer'(Options) 51 | ])), 52 | balance_columns_script. 53 | 54 | 'footer-footer'(Options) --> 55 | html(div(id=footer, 56 | [ \show_user(Options), 57 | \server_information 58 | ])). 59 | 60 | show_user(Options) --> 61 | { option(show_user(false), Options) }, !. 62 | show_user(_) --> 63 | current_user. 64 | 65 | balance_columns_script --> 66 | js_script({|javascript|| 67 | $().ready(function() 68 | { var $navtree = $(".navwindow"); 69 | var $navcontent = $(".navcontent"); 70 | if ( $navtree.length > 0 && $navcontent.length > 0 ) 71 | { var $window = $(window).on("resize", function() 72 | { var ch = $navcontent.height(); 73 | var nh = $navtree.height(); 74 | if ( nh > 400 && nh > ch + 200 ) 75 | { if ( ch < 300 ) ch = 300; 76 | $navtree.height(ch); 77 | $navtree.css('overflow-y', 'scroll'); 78 | 79 | var current = $navtree.find("li.nav.current"); 80 | if ( current.position().top > ch-40 ) 81 | { $navtree.scrollTop(current.position().top - (ch-40)); 82 | } 83 | } 84 | }).trigger("resize") 85 | } 86 | }); 87 | |}). 88 | 89 | 90 | prolog_version(Version) :- 91 | current_prolog_flag(version_git, Version), !. 92 | prolog_version(Version) :- 93 | current_prolog_flag(version_data, swi(Ma,Mi,Pa,_)), 94 | format(atom(Version), '~w.~w.~w', [Ma,Mi,Pa]). 95 | 96 | %! server_information// is det. 97 | % Emit server information. 98 | 99 | server_information --> 100 | {prolog_version(Version)}, 101 | html( 102 | a([id=powered,href='http://www.swi-prolog.org'], [ 103 | 'Powered by SWI-Prolog ', 104 | Version 105 | ]) 106 | ). 107 | 108 | -------------------------------------------------------------------------------- /forum.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2009-2013, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(plweb_forum, []). 31 | :- use_module(library(http/html_write)). 32 | :- use_module(library(http/js_write)). 33 | :- use_module(library(http/http_dispatch)). 34 | 35 | :- http_handler(root(forum), forum, []). 36 | 37 | forum(_Request) :- 38 | reply_html_page( 39 | forum(default), 40 | title('SWI-Prolog user forum'), 41 | \embed_forum). 42 | 43 | embed_forum --> 44 | html({|html|| 45 | |}), 48 | js_script({|javascript|| 49 | document.getElementById('forum_embed').src = 50 | 'https://groups.google.com/forum/embed/?place=forum/swi-prolog' 51 | + '&showsearch=true&showpopout=true&showtabs=false' 52 | + '&parenturl=' + encodeURIComponent(window.location.href); 53 | |}). 54 | 55 | :- multifile plweb:page_title//1. 56 | 57 | plweb:page_title(forum(default)) --> 58 | html('SWI-Prolog user forum'). 59 | -------------------------------------------------------------------------------- /generics.pl: -------------------------------------------------------------------------------- 1 | :- module( 2 | generics, 3 | [ 4 | clean_dom/2, % +DOM:list 5 | % -CleanedDOM:list 6 | ensure_number/2, % +Something:term 7 | % -Number:number 8 | is_empty/1, % +Content:atom 9 | login_link//0, 10 | request_to_id/3, % +Request:list 11 | % +Kind:oneof([annotation,news,post]) 12 | % -Id:atom 13 | true/1, % +Term 14 | uri_query_add/4, % +FromURI:uri 15 | % +Name:atom 16 | % +Value:atom 17 | % -ToURI:atom 18 | wiki_file_codes_to_dom/3 % +Codes:list(code) 19 | % +File:atom 20 | % -DOM:list 21 | ] 22 | ). 23 | 24 | /** Generics 25 | 26 | Generic predicates in plweb. 27 | Candidates for placement in some library. 28 | 29 | @author Wouter Beek 30 | @version 2013/12-2014/01 31 | */ 32 | 33 | :- use_module(library(apply)). 34 | :- use_module(library(http/http_dispatch)). 35 | :- use_module(library(http/http_path)). 36 | :- use_module(library(http/http_wrapper)). 37 | :- use_module(library(option)). 38 | :- use_module(library(pldoc/doc_wiki)). 39 | :- use_module(library(semweb/rdf_db)). 40 | :- use_module(library(uri)). 41 | :- use_module(openid). 42 | 43 | %! add_option( 44 | %! +FromOptions:list(nvpair), 45 | %! +Name:atom, 46 | %! +Value:atom, 47 | %! +ToOptions:list(nvpair) 48 | %! ) is det. 49 | % Adds an option with the given name and value (i.e. `Name(Value)`), 50 | % and ensures that old options are overwritten and 51 | % that the resultant options list is sorted. 52 | 53 | add_option(Os1, N, V, Os2):- 54 | O =.. [N,V], 55 | merge_options([O], Os1, Os2). 56 | 57 | clean_dom([p(X)], X) :- !. 58 | clean_dom(X, X). 59 | 60 | ensure_number(X, X):- 61 | number(X), !. 62 | ensure_number(X, Y):- 63 | atom(X), !, 64 | atom_number(X, Y). 65 | 66 | %! is_empty(+Content:atom) is semidet. 67 | 68 | is_empty(Content):- 69 | var(Content), !. 70 | is_empty(Content):- 71 | normalize_space(atom(''), Content). 72 | 73 | login_link --> 74 | {http_current_request(Request)}, 75 | login_link(Request). 76 | 77 | %% request_to_id(+Request, ?Kind, -Id) is semidet. 78 | % 79 | % True when Request is a request to the post service for the given 80 | % Kind and Id. Id is '' when accessing without an id. 81 | 82 | request_to_id(Request, Kind, Id) :- 83 | memberchk(path(Path), Request), 84 | ( atomic_list_concat(['',Kind,Id], '/', Path) 85 | -> true 86 | ; atom_concat(/, Kind, Path) 87 | -> Id = '' 88 | ). 89 | 90 | true(_). 91 | 92 | %! uri_query_add(+FromURI:uri, +Name:atom, +Value:atom, -ToURI:atom) is det. 93 | % Inserts the given name-value pair as a query component into the given URI. 94 | 95 | uri_query_add(URI1, Name, Value, URI2):- 96 | uri_components( 97 | URI1, 98 | uri_components(Scheme, Authority, Path, Search1_, Fragment) 99 | ), 100 | (var(Search1_) -> Search1 = '' ; Search1 = Search1_), 101 | uri_query_components(Search1, SearchPairs1), 102 | add_option(SearchPairs1, Name, Value, SearchPairs2), 103 | uri_query_components(Search2, SearchPairs2), 104 | uri_components( 105 | URI2, 106 | uri_components(Scheme, Authority, Path, Search2, Fragment) 107 | ). 108 | 109 | %% wiki_file_codes_to_dom(+Codes, +File, -DOM) 110 | % 111 | % DOM is the HTML dom representation for Codes that originate from 112 | % File. 113 | 114 | wiki_file_codes_to_dom(String, File, DOM):- 115 | nb_current(pldoc_file, OrgFile), !, 116 | setup_call_cleanup( 117 | b_setval(pldoc_file, File), 118 | wiki_codes_to_dom(String, [], DOM), 119 | b_setval(pldoc_file, OrgFile) 120 | ). 121 | wiki_file_codes_to_dom(String, File, DOM):- 122 | setup_call_cleanup( 123 | b_setval(pldoc_file, File), 124 | wiki_codes_to_dom(String, [], DOM), 125 | nb_delete(pldoc_file) 126 | ). 127 | -------------------------------------------------------------------------------- /git-web/footer.html: -------------------------------------------------------------------------------- 1 |


2 |

3 | 4 | Further information about the SWI-Prolog GIT repositories 5 |

6 | -------------------------------------------------------------------------------- /git-web/gitweb.conf: -------------------------------------------------------------------------------- 1 | use File::Basename; 2 | 3 | # turn off potentially CPU-intensive features 4 | $feature{'search'}{'default'} = [undef]; 5 | $feature{'blame'}{'default'} = [undef]; 6 | $feature{'pickaxe'}{'default'} = [undef]; 7 | $feature{'grep'}{'default'} = [undef]; 8 | 9 | $GIT = "git"; 10 | $projectroot = $ENV{PROJECT_ROOT} || "/home/pl/git/"; 11 | #$projectroot = "/home/pl/git/"; 12 | 13 | $gitwebdir = dirname($ENV{SCRIPT_FILENAME}); 14 | $site_header = $gitwebdir . "/header.html"; 15 | $site_footer = $gitwebdir . "/footer.html"; 16 | $project_maxdepth = 1; 17 | 18 | @stylesheets = ("/git/static/gitweb.css"); 19 | $javascript = "/git/static/gitweb.js"; 20 | $logo = "/git/static/git-logo.png"; 21 | $favicon = "/git/static/git-favicon.png"; 22 | 23 | # nicer-looking URLs 24 | $feature{'pathinfo'}{'default'} = [1]; 25 | 26 | $site_name = "SWI-Prolog GIT browser"; 27 | 28 | $site_uri = "http://" . $ENV{'SERVER_NAME'}; 29 | $port = $ENV{SERVER_PORT} || "80"; 30 | if ( $port != "80" ) 31 | { $site_uri = $site_uri . ":" . $port; 32 | } 33 | $my_uri = $site_uri . "/git/"; 34 | $home_link = $site_uri . "/git/"; 35 | -------------------------------------------------------------------------------- /git-web/header.html: -------------------------------------------------------------------------------- 1 |

The SWI-Prolog GIT repositories

2 | 3 | -------------------------------------------------------------------------------- /git-web/plweb-apache.env: -------------------------------------------------------------------------------- 1 | SERVER_SIGNATURE= 2 | HTTP_KEEP_ALIVE=300 3 | HTTP_USER_AGENT=Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.9.0.5) Gecko/2008121300 SUSE/3.0.5-1.1 Firefox/3.0.5 4 | SERVER_PORT=80 5 | HTTP_HOST=prolog.cs.vu.nl 6 | DOCUMENT_ROOT=/home/pl/web 7 | HTTP_ACCEPT_CHARSET=ISO-8859-1,utf-8;q=0.7,*;q=0.7 8 | SCRIPT_FILENAME=/home/pl/web/git/test.cgi 9 | REQUEST_URI=/git/test.cgi/pl-57x.git?a=commit;h=0dfb0aa39fd3f51382d16a3c931997b58031a108 10 | SCRIPT_NAME=/git/test.cgi 11 | SCRIPT_URI=http://prolog.cs.vu.nl/git/test.cgi/pl-57x.git 12 | HTTP_CONNECTION=keep-alive 13 | PATH_INFO=/pl-57x.git 14 | REMOTE_PORT=56315 15 | PATH=/usr/local/bin:/usr/bin:/bin 16 | SCRIPT_URL=/git/test.cgi/pl-57x.git 17 | PWD=/home/pl/web/git 18 | SERVER_ADMIN=jrvosse@cs.vu.nl 19 | HTTP_ACCEPT_LANGUAGE=en-gb,en;q=0.5 20 | PATH_TRANSLATED=/home/pl/web/pl-57x.git 21 | HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 22 | REMOTE_ADDR=130.37.30.3 23 | SHLVL=1 24 | SERVER_NAME=prolog.cs.vu.nl 25 | SERVER_SOFTWARE=Apache/2.2.9 (Unix) DAV/2 SVN/1.4.2 26 | QUERY_STRING=a=commit;h=0dfb0aa39fd3f51382d16a3c931997b58031a108 27 | SERVER_ADDR=130.37.193.11 28 | GATEWAY_INTERFACE=CGI/1.1 29 | SERVER_PROTOCOL=HTTP/1.1 30 | HTTP_CACHE_CONTROL=max-age=0 31 | HTTP_ACCEPT_ENCODING=gzip,deflate 32 | REQUEST_METHOD=GET 33 | HTTP_COOKIE=__utma=214905872.3938434877739744000.1221743656.1224244323.1232619166.3; __utmz=214905872.1232619166.3.2.utmcsr=cs.vu.nl|utmccn=(referral)|utmcmd=referral|utmcct=/ 34 | _=/usr/bin/printenv 35 | -------------------------------------------------------------------------------- /git-web/printenv: -------------------------------------------------------------------------------- 1 | #!/bin/bash -f 2 | 3 | echo "Content-type: text/plain" 4 | echo "" 5 | echo "Hello world" 6 | printenv 7 | -------------------------------------------------------------------------------- /git-web/static/git-favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SWI-Prolog/plweb/5788f8e26d2041e200dba8d5587bf3850422009b/git-web/static/git-favicon.png -------------------------------------------------------------------------------- /git-web/static/git-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SWI-Prolog/plweb/5788f8e26d2041e200dba8d5587bf3850422009b/git-web/static/git-logo.png -------------------------------------------------------------------------------- /git-web/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int 4 | main(int argc, char **argv) 5 | { printf("Content-type: text/plain\n\n"); 6 | printf("Hello world\n"); 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /gitweb.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2009-2011, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(gitweb, []). 31 | :- use_module(library(http/http_dispatch)). 32 | :- use_module(library(http/html_write)). 33 | :- use_module(library(apply)). 34 | :- use_module(library(url)). 35 | :- use_module(library(debug)). 36 | :- use_module(http_cgi). 37 | 38 | /** Provide gitweb support 39 | 40 | @tbd Also serve the GIT repository over this gateway 41 | @tbd Better way to locate the GIT project root 42 | */ 43 | 44 | :- if(true). 45 | 46 | :- http_handler(root('git'), github, []). 47 | :- http_handler(root('git/'), github, [ prefix, spawn(cgi) ]). 48 | :- http_handler(root('home/pl/git/'), github, [prefix, spawn(download)]). 49 | 50 | github(_Request) :- 51 | reply_html_page( 52 | git(github), 53 | title('SWI-Prolog git services moved to github'), 54 | \github). 55 | 56 | github --> 57 | html({|html|| 58 |

The SWI-Prolog source repository has been moved to 59 | GitHub. 60 | |}). 61 | 62 | :- multifile plweb:page_title//1. 63 | 64 | plweb:page_title(git(github)) --> 65 | html('SWI-Prolog git services moved to github'). 66 | 67 | :- else. 68 | 69 | :- http_handler(root('git'), gitroot, []). 70 | :- http_handler(root('git/'), gitweb, [ prefix, spawn(cgi) ]). 71 | :- http_handler(root('home/pl/git/'), git_http, [prefix, spawn(download)]). 72 | 73 | 74 | 75 | 76 | %% gitroot(+Request) is det. 77 | % 78 | % Some toplevel requests are send to /git, while working inside 79 | % the repository asks for /git/. This is a hack to work around 80 | % these problems. 81 | 82 | gitroot(Request) :- 83 | http_location_by_id(gitroot, Me), 84 | atom_concat(Me, /, NewPath), 85 | include(local, Request, Parts), 86 | http_location([path(NewPath)|Parts], Moved), 87 | throw(http_reply(moved(Moved))). 88 | 89 | local(search(_)). 90 | local(fragment(_)). 91 | 92 | %% gitweb(+Request) 93 | % 94 | % Call gitweb script 95 | 96 | gitweb(Request) :- 97 | memberchk(path(Path), Request), 98 | file_base_name(Path, Base), 99 | resource_file(Base, File), !, 100 | debug(gitweb, 'Sending resource ~q', [File]), 101 | http_reply_file(File, [], Request). 102 | gitweb(Request) :- 103 | absolute_file_name(gitweb('gitweb.cgi'), ScriptPath, 104 | [ access(execute) 105 | ]), 106 | http_run_cgi(ScriptPath, [], Request). 107 | 108 | 109 | resource_file('gitweb.css', gitweb('static/gitweb.css')). 110 | resource_file('gitweb.js', gitweb('static/gitweb.js')). 111 | resource_file('git-logo.png', gitweb('static/git-logo.png')). 112 | resource_file('git-favicon.png', gitweb('static/git-favicon.png')). 113 | 114 | 115 | :- multifile 116 | http_cgi:environment/2. 117 | 118 | http_cgi:environment('PROJECT_ROOT', Root) :- % gitweb 119 | git_project_root(Root). 120 | http_cgi:environment('GIT_PROJECT_ROOT', Root) :- % git-http 121 | git_project_root(Root). 122 | http_cgi:environment('GITWEB_CONFIG', Config) :- 123 | absolute_file_name(gitweb('gitweb.conf'), Config, 124 | [ access(read) 125 | ]). 126 | http_cgi:environment('PATH', '/bin:/usr/bin:/usr/local/bin'). 127 | 128 | 129 | git_project_root(Root) :- 130 | absolute_file_name(plgit(.), RootDir, 131 | [ access(read), 132 | file_type(directory) 133 | ]), 134 | atom_concat(RootDir, /, Root), 135 | debug(gitweb, 'PROJECT_ROOT = ~q', [Root]). 136 | 137 | 138 | %% git_http(+Request) is det. 139 | % 140 | % Server files from the git tree to make this work: 141 | % 142 | % == 143 | % git clone http://www.swi-prolog.org/nl/home/pl/git/pl.git 144 | % == 145 | % 146 | % The comment "git http-backend" does not provide much meaningful 147 | % info when accessed from a browser. Therefore we run "git 148 | % http-backend" only if w think this the request comes from a git 149 | % backend. Otherwise we redirect to the gitweb page. 150 | 151 | git_http(Request) :- 152 | ( memberchk(method(post), Request) 153 | ; memberchk(search(Search), Request), 154 | memberchk(service=_, Search) 155 | ; memberchk(user_agent(Agent), Request), 156 | sub_atom(Agent, 0, _, _, git) 157 | ), !, 158 | http_run_cgi(path(git), 159 | [ argv(['http-backend']), 160 | transfer_encoding(chunked), 161 | buffer(line) 162 | ], 163 | Request). 164 | git_http(Request) :- 165 | memberchk(request_uri(URI), Request), 166 | atom_concat('/home/pl', GitWebURI, URI), 167 | throw(http_reply(see_other(GitWebURI))). 168 | 169 | :- endif. 170 | -------------------------------------------------------------------------------- /holidays.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Anne Ogborn 4 | WWW: http://www.swi-prolog.org 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License 8 | as published by the Free Software Foundation; either version 2 9 | of the License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | As a special exception, if you link this library with other files, 21 | compiled with a Free Software compiler, to produce an executable, this 22 | library does not by itself cause the resulting executable to be covered 23 | by the GNU General Public License. This exception does not however 24 | invalidate any other reasons why the executable file might be covered by 25 | the GNU General Public License. 26 | */ 27 | :- module(holidays, [ 28 | todays_holiday/1 29 | ]). 30 | 31 | :- dynamic current_holiday/2. 32 | 33 | current_holiday(0, none). 34 | 35 | /** todays_holiday(-Holiday:atom) is det 36 | * 37 | * succeeds if Holiday is 'todays holiday' 38 | * 39 | * This is none on 'ordinary' days, or 40 | * one of 41 | * 42 | * april_fools_day 43 | * christmas 44 | * koningsdag 45 | * santiklaas 46 | * 47 | * succeeds only within 12 hours either 48 | * side of April 1 49 | * 50 | * April fools day is a traditional holiday celebrated 51 | * by playing hoaxes and practical jokes. A common form 52 | * of this is to substitute nonsensical information where 53 | * useful info is normally displayed. 54 | * 55 | * Koningsdag is 'Kings day' in the Netherlands 56 | * Santiklaas is St. Nicholas' feast 57 | * 58 | */ 59 | todays_holiday(Holiday) :- 60 | current_holiday(Time, Holiday), 61 | get_time(Now), 62 | Now - Time < 3600, !. 63 | todays_holiday(Holiday) :- 64 | year(Year), 65 | todays_holiday(Year, Holiday), 66 | get_time(Now), 67 | retractall(current_holiday(_, _)), 68 | asserta(current_holiday(Now, Holiday)). 69 | 70 | todays_holiday(YY, april_fools_day) :- 71 | date_between(YY-03-30, 12:00:00, 72 | YY-04-02, 12:00:00). 73 | todays_holiday(YY, christmas) :- 74 | date_between(YY-12-20, 12:00:00, 75 | YY-12-27, 12:00:00). 76 | todays_holiday(YY, koningsdag) :- 77 | date_between(YY-04-26, 12:00:00, 78 | YY-04-28, 12:00:00). 79 | todays_holiday(YY, santiklaas) :- 80 | date_between(YY-12-05, 12:00:00, 81 | YY-12-06, 12:00:00). 82 | todays_holiday(YY, halloween) :- 83 | date_between(YY-10-30, 12:00:00, 84 | YY-11-01, 12:00:00). 85 | todays_holiday(YY, liberation_day) :- 86 | date_between(YY-05-04, 12:00:00, 87 | YY-05-06, 12:00:00). 88 | todays_holiday(YY, carnival) :- 89 | carnival_date(Start, End), 90 | Start = (YY-_-_), 91 | End = (YY-_-_), 92 | date_between(Start, 12:00:00, 93 | End, 12:00:00). 94 | todays_holiday(YY, chinese_new_year) :- 95 | chinese_new_year_date(Start, End), 96 | Start = (YY-_-_), 97 | End = (YY-_-_), 98 | date_between(Start, 12:00:00, 99 | End, 12:00:00). 100 | todays_holiday(_, none). 101 | 102 | carnival_date(2015-2-15, 2015-2-17). 103 | carnival_date(2016-2-07, 2016-2-09). 104 | carnival_date(2017-2-26, 2017-2-28). 105 | carnival_date(2018-2-11, 2018-2-13). 106 | carnival_date(2019-3-03, 2019-3-05). 107 | carnival_date(2020-2-23, 2020-2-25). 108 | carnival_date(2021-2-14, 2021-2-16). 109 | carnival_date(2022-2-27, 2022-2-29). 110 | carnival_date(2023-2-19, 2023-2-21). 111 | carnival_date(2024-2-11, 2025-2-13). 112 | carnival_date(2025-3-02, 2025-3-04). 113 | 114 | chinese_new_year_date(2015-02-19,2015-02-20). 115 | chinese_new_year_date(2016-02-08,2016-02-09). 116 | chinese_new_year_date(2017-01-28,2017-01-29). 117 | chinese_new_year_date(2018-02-16,2018-02-17). 118 | chinese_new_year_date(2019-02-05,2019-02-06). 119 | chinese_new_year_date(2020-01-25,2020-01-26). 120 | chinese_new_year_date(2021-02-12,2021-02-13). 121 | chinese_new_year_date(2022-02-01,2022-02-02). 122 | chinese_new_year_date(2023-01-22,2023-01-23). 123 | chinese_new_year_date(2024-02-10,2024-02-11). 124 | chinese_new_year_date(2025-01-29,2025-01-30). 125 | chinese_new_year_date(2026-02-17,2026-02-18). 126 | chinese_new_year_date(2027-02-06,2027-02-07). 127 | chinese_new_year_date(2028-01-26,2028-01-27). 128 | chinese_new_year_date(2029-02-13,2029-02-14). 129 | chinese_new_year_date(2030-02-03,2030-02-04). 130 | 131 | /** 132 | * chinese_new_year_animal(?Year, ?Animal). 133 | * 134 | * Succeeds iff Animal matches the Chinese zodiac animal for Year. 135 | */ 136 | chinese_new_year_animal(2015, 'Sheep'). 137 | chinese_new_year_animal(2016, 'Monkey'). 138 | chinese_new_year_animal(2017, 'Rooster'). 139 | chinese_new_year_animal(2018, 'Dog'). 140 | chinese_new_year_animal(2019, 'Pig'). 141 | chinese_new_year_animal(2020, 'Rat'). 142 | chinese_new_year_animal(2021, 'Ox'). 143 | chinese_new_year_animal(2022, 'Tiger'). 144 | chinese_new_year_animal(2023, 'Rabbit'). 145 | chinese_new_year_animal(2024, 'Dragon'). 146 | chinese_new_year_animal(2025, 'Snake'). 147 | chinese_new_year_animal(2026, 'Horse'). 148 | chinese_new_year_animal(2027, 'Sheep'). 149 | chinese_new_year_animal(2028, 'Monkey'). 150 | chinese_new_year_animal(2029, 'Rooster'). 151 | chinese_new_year_animal(2030, 'Dog'). 152 | 153 | 154 | %% year(-Year) 155 | % 156 | % True when Year is the current year 157 | 158 | year(Year) :- 159 | get_time(Now), 160 | stamp_date_time(Now, Term, 'UTC'), 161 | date_time_value(year, Term, Year). 162 | 163 | date_between(SDate, STime, EDate, ETime) :- 164 | get_time(Now), 165 | stamp(SDate, STime, Start), Now >= Start, 166 | stamp(EDate, ETime, End), Now =< End. 167 | 168 | stamp(YY-MM-DD, H:M:S, Time) :- 169 | date_time_stamp(date(YY,MM,DD,H,M,S,0,'UTC',-), Time). 170 | -------------------------------------------------------------------------------- /http_fork.pl: -------------------------------------------------------------------------------- 1 | :- module(http_fork, 2 | [ forked_server/2 % +Port, +Options 3 | ]). 4 | :- use_module(library(unix)). 5 | :- use_module(library(socket)). 6 | :- use_module(library(option)). 7 | :- use_module(library(debug)). 8 | :- use_module(library(http/thread_httpd)). 9 | :- use_module(library(http/http_dispatch)). 10 | 11 | /** Manage HTTP servers using forking 12 | 13 | This module is designed to create robust Prolog-based HTTP servers. The 14 | main Prolog process loads the program and preforks _N_ times, each of 15 | the childs runs a normal multi-threaded HTTP server. If a child dies, 16 | the main server forks again to create a new server. 17 | 18 | @compat This library is limited to systems providing a proper 19 | copy-on-write implementation of the POSIX fork() system call. 20 | In practice, these are Unix versions (including Linux, MacOSX, etc. 21 | but excluding Windows). 22 | 23 | @tbd The current implementation does not support session-management 24 | if multiple servers are preforked because sessions may `hop' 25 | between servers. This could be fixed using TIPC to establish 26 | a distributed communication service between the cooperating 27 | clients. 28 | 29 | @tbd Deal with logging of the clients. Apache does so using a log 30 | process. The main server creates a pipe. The input is used 31 | by each client for writing the log. The output is processed 32 | by the log process. This process merely combines all terms 33 | and writes them to the logfile. Note that we cannot use 34 | threads because fork() doesn't play well with threads, so 35 | the main server must remain single-threaded. 36 | 37 | @tbd At a next step, the threaded HTTP server must be extended 38 | to support scheduled graceful suicide. By gracefully comitting 39 | suicide, the server can avoid suffering too much from memory leaks. 40 | */ 41 | 42 | 43 | %% forked_server(+Port, +Options) 44 | % 45 | % Similar to http_server/2 from library(http/thread_httpd), but 46 | % the main process starts and monitors a pool of processes. 47 | % Additional options processed: 48 | % 49 | % * prefork(+Count) 50 | % The number of servers to fork (default 1) 51 | % * init(:Goal) 52 | % Goal to run in a new server after the fork and before 53 | % starting the HTTP server. 54 | 55 | forked_server(Port, Options) :- 56 | tcp_socket(Socket), 57 | tcp_setopt(Socket, reuseaddr), 58 | tcp_bind(Socket, Port), 59 | tcp_listen(Socket, 5), 60 | thread_httpd:make_addr_atom('httpd@', Port, Queue), 61 | prefork_servers([ queue(Queue), 62 | tcp_socket(Socket), 63 | port(Port) 64 | | Options 65 | ]). 66 | 67 | prefork_servers(Options) :- 68 | option(prefork(Count), Options, 1), 69 | prefork_servers(Count, Options, PIDS), 70 | monitor_servers(PIDS, Options). 71 | 72 | prefork_servers(Count, Options, [PID|PIDS]) :- 73 | Count > 0, !, 74 | prefork_server(Options, PID), 75 | Count2 is Count - 1, 76 | prefork_servers(Count2, Options, PIDS). 77 | prefork_servers(_, _, []). 78 | 79 | prefork_server(Options, PID) :- 80 | fork(PID), 81 | ( PID == child 82 | -> option(goal(Goal), Options, http_dispatch), 83 | option(init(Init), Options, true), 84 | call(Init), 85 | http_server(Goal, Options), 86 | forall(thread_get_message(Goal), 87 | Goal) 88 | ; debug(http(fork), 'Preforked server (PID=~d)', [PID]) 89 | ). 90 | 91 | :- multifile 92 | thread_httpd:make_socket_hook/3. 93 | 94 | thread_httpd:make_socket_hook(_Port, Options, Options) :- 95 | memberchk(tcp_socket(_), Options), !. 96 | 97 | monitor_servers(PIDS, Options) :- 98 | wait(PID, Status), 99 | debug(http(fork), 'Forked server ~d died with status ~p', 100 | [PID, Status]), 101 | delete(PIDS, PID, Rest), 102 | prefork_server(Options, New), 103 | monitor_servers([New|Rest], Options). 104 | -------------------------------------------------------------------------------- /load.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog web site 2 | 3 | Author: Jan Wielemaker 4 | E-mail: jan@swi-prolog.org 5 | WWW: https://www.swi-prolog.org 6 | Copyright (C): 2009-2025, SWI-Prolog Solutions b.v. 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- dynamic 31 | pre_files/1. 32 | :- findall(F, source_file(F), FL), 33 | assertz(pre_files(FL)). 34 | :- doc_collect(true). 35 | :- attach_packs(packs, [duplicate(replace)]). 36 | :- load_files([ library(pldoc/doc_library), 37 | library(thread_pool), 38 | library(http/http_session), 39 | library(http/http_unix_daemon), 40 | library(http/http_dyn_workers), 41 | library(prolog_source), 42 | plweb, 43 | examples, 44 | blog, 45 | api, 46 | wiki_edit, 47 | stats, 48 | pack, 49 | register, 50 | changelog, 51 | tagit, 52 | forum, 53 | make, 54 | test_recaptcha, 55 | watchdog 56 | ]). 57 | 58 | :- if(exists_source(library(ssh_server))). 59 | :- use_module(library(ssh_server)). 60 | :- use_module(library(broadcast)). 61 | :- listen(http(pre_server_start), 62 | start_sshd). 63 | 64 | start_sshd :- 65 | absolute_file_name(private('etc/ssh/authorized_keys'), File, 66 | [ access(read)]), 67 | absolute_file_name(private('etc/ssh/ssh_host_ecdsa_key'), HostKey, 68 | [ access(read)]), 69 | ssh_server([ port(2022), 70 | bind_address(*), 71 | authorized_keys_file(File), 72 | host_key_file(HostKey) 73 | ]). 74 | :- endif. 75 | 76 | %! read_comments(+File) 77 | % 78 | % Reads PlDoc comments for a file that was already loaded before 79 | % the server was started. 80 | 81 | read_comments(File) :- 82 | access_file(File, read), 83 | source_file_property(File, module(M)), 84 | !, 85 | setup_call_cleanup( 86 | ( prolog_open_source(File, In), 87 | set_prolog_flag(xref, true), 88 | '$set_source_module'(Old, M) 89 | ), 90 | ( repeat, 91 | prolog_read_source_term(In, Term, _, 92 | [ process_comment(true) 93 | ]), 94 | Term == end_of_file, 95 | ! 96 | ), 97 | ( '$set_source_module'(_, Old), 98 | set_prolog_flag(xref, false), 99 | prolog_close_source(In) 100 | )). 101 | read_comments(_). % not a module, we do not care 102 | 103 | reload_pre_files :- 104 | pre_files(FL), 105 | forall(member(F, FL), 106 | read_comments(F)). 107 | 108 | :- reload_pre_files. 109 | :- doc_load_library. 110 | :- http_set_session_options([enabled(false)]). 111 | :- send(@(pce), catch_error_signals, @(off)). 112 | 113 | %! show_fd 114 | % 115 | % Show open file descriptors. Sanity-check that works only on 116 | % Linux systems. 117 | 118 | show_fd :- 119 | current_prolog_flag(pid, Pid), 120 | format(string(Cmd), 121 | '/bin/sh -c "(cd /proc/~w/fd && ls -l | grep socket)"', 122 | [Pid]), 123 | shell(Cmd). 124 | 125 | show_pools :- 126 | format('~`-t~52|~n'), 127 | format('~w~t~20|~t~w~8+~t~w~8+~t~w~8+~t~w~8+~n', 128 | [ 'Pool name', 'Running', 'Size', 'Waiting', 'Backlog' ]), 129 | format('~`-t~52|~n'), 130 | forall(current_thread_pool(Pool), show_pool(Pool)), 131 | format('~`-t~52|~n'). 132 | 133 | show_pool(Pool) :- 134 | findall(P, thread_pool_property(Pool, P), List), 135 | memberchk(size(Size), List), 136 | memberchk(running(Running), List), 137 | memberchk(backlog(Waiting), List), 138 | memberchk(options(Options), List), 139 | option(backlog(MaxBackLog), Options, infinite), 140 | format('~w~t~20|~t~D ~8+~t~D ~8+~t~D ~8+~t~w ~8+~n', 141 | [Pool, Running, Size, Waiting, MaxBackLog]). 142 | 143 | stop :- 144 | halt(42). 145 | -------------------------------------------------------------------------------- /log.pl: -------------------------------------------------------------------------------- 1 | log :- 2 | catch(delete_file(log), _, true), 3 | debug_message_context(+time), 4 | debug(http(request)), 5 | % debug(http(_)>log), 6 | tmon. 7 | -------------------------------------------------------------------------------- /logs/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | dns_cache 3 | *.hist 4 | -------------------------------------------------------------------------------- /logs/Makefile: -------------------------------------------------------------------------------- 1 | all: sync convert webalize upload 2 | 3 | sync:: 4 | rsync -av ec:/home/vnc/prolog/src/plweb/httpd.log . 5 | 6 | convert:: 7 | pl -G1g -s log2clf.pl -q -g main -- -o apache.log httpd.log 8 | 9 | webalize:: 10 | webalizer apache.log 11 | 12 | upload:: 13 | rsync -aCv --delete report/ ec:/home/vnc/prolog/src/plweb/www/logs 14 | -------------------------------------------------------------------------------- /logs/README: -------------------------------------------------------------------------------- 1 | rsync -av ec:/home/vnc/prolog/src/plweb/httpd.log . 2 | swipl -s log2clf.pl -q -g main -- -o apache.log httpd.log 3 | webalizer apache.log 4 | rsync -aCv report/ ec:/home/vnc/prolog/src/plweb/www/logs 5 | 6 | 7 | Update graphics: 8 | 9 | ./dl-summary httpd.log 10 | ./dl-stat -o swi-prolog-downloads.jpeg *.dat 11 | rsync -av swi-prolog-downloads.jpeg ec:~vnc/prolog/src/plweb/www/logs 12 | -------------------------------------------------------------------------------- /logs/dl-stat: -------------------------------------------------------------------------------- 1 | #!/home/swipl/bin/swipl 2 | 3 | :- set_prolog_flag(verbose, silent). 4 | :- initialization main, halt. 5 | 6 | :- use_module(stat). 7 | :- use_module(plstat). 8 | 9 | load_data_files(Files, [End|ByMonth]) :- 10 | maplist(read_file_to_terms, Files, NestTerms), 11 | append(NestTerms, AllTerms), 12 | partition(month_term, AllTerms, AllMonths, Ends), 13 | sort(Ends, AllEnds), 14 | last(AllEnds, End), 15 | map_list_to_pairs(month_key, AllMonths, Keyed), 16 | keysort(Keyed, SortedMonths), 17 | group_pairs_by_key(SortedMonths, Grouped), 18 | make_months(Grouped, ByMonth). 19 | 20 | read_file_to_terms(File, Terms) :- 21 | read_file_to_terms(File, Terms, []). 22 | 23 | month_term(downloads(_Y, _M, _C)). 24 | 25 | month_key(downloads(Y, M, _C), Y/M). 26 | 27 | make_months([], []). 28 | make_months([_-List|T0], [M|T]) :- 29 | sum_counts(List, M), 30 | make_months(T0, T). 31 | 32 | sum_counts([One], One) :- !. 33 | sum_counts([H1,H2|T], Sum) :- 34 | sum_count(H1, H2, H), 35 | sum_counts([H|T], Sum). 36 | 37 | sum_count(downloads(Y,M,Cs1), 38 | downloads(Y,M,Cs2), 39 | downloads(Y,M,Cs)) :- 40 | sort(Cs1, Css1), 41 | sort(Cs2, Css2), 42 | sum(Css1, Css2, Cs). 43 | 44 | sum([], [], []) :- !. 45 | sum(L, [], L) :- !. 46 | sum([], L, L) :- !. 47 | sum([N=V1|T1], [N=V2|T2], [N=V|T]) :- !, 48 | V is V1+V2, 49 | sum(T1, T2, T). 50 | sum([N1=V1|T1], [N2=V2|T2], [N1=V1|T]) :- 51 | N1 @< N2, !, 52 | sum(T1, [N2=V2|T2], T). 53 | sum(L1, [H|T2], [H|T]) :- 54 | sum(L1, T2, T). 55 | 56 | sums(Options, Terms) :- 57 | option(sum_year(Y), Options), !, 58 | findall(C, member(downloads(Y, _, C), Terms), L), 59 | flatten(L, List), 60 | aggregate_all(sum(X), member(_=X, List), Count), 61 | format('Total downloads over ~d: ~D~n', [Y, Count]). 62 | sums(_,_). 63 | 64 | 65 | :- prolog_load_context(directory, Dir), 66 | working_directory(_, Dir). 67 | 68 | main :- catch(run, E, print_message(error, E)). 69 | run :- 70 | current_prolog_flag(argv, Argv), 71 | main_options(Argv, Options, Files), 72 | option(out(Out), Options, _), 73 | ( var(Out) 74 | -> Out = '../www/logs/swi-prolog-downloads.jpeg' 75 | ; true 76 | ), 77 | load_data_files(Files, Terms), 78 | sums(Options, Terms), 79 | stat2jpeg(Out, Terms). 80 | 81 | main_options(['-o', Out|T0], [out(Out)|OT], T) :- !, 82 | main_options(T0, OT, T). 83 | main_options(['-s', YA|T0], [sum_year(Y)|OT], T) :- !, 84 | atom_number(YA, Y), 85 | main_options(T0, OT, T). 86 | main_options(Files, [], Files). 87 | 88 | -------------------------------------------------------------------------------- /logs/dl-summary: -------------------------------------------------------------------------------- 1 | #!/home/swipl/bin/swipl 2 | 3 | :- initialization main. 4 | 5 | :- use_module(logstat). 6 | :- use_module(plstat). 7 | 8 | terms([ date(Year, Month, Day) 9 | | Terms 10 | ]) :- 11 | Term = downloads(_Year, _Month, _Counts), 12 | findall(Term, Term, Terms), 13 | get_time(Stamp), 14 | stamp_date_time(Stamp, Date, 'UTC'), 15 | date_time_value(year, Date, Year), 16 | date_time_value(month, Date, Month), 17 | date_time_value(day, Date, Day). 18 | 19 | save_terms(File, Terms) :- 20 | setup_call_cleanup(open(File, write, Out), 21 | ( format(Out, 22 | '%%\tdownloads(Year, Month, Counts)~n~n', 23 | []), 24 | forall(member(T, Terms), 25 | format(Out, '~q.~n', [T])) 26 | ), 27 | close(Out)). 28 | 29 | usage :- 30 | format(user_error, 'Usage: dl-summary logfile [out]~n', []), 31 | fail. 32 | 33 | main :- 34 | catch(run, E, (print_message(error, E), fail)). 35 | 36 | 37 | run :- 38 | current_prolog_flag(argv, Argv), 39 | ( Argv = [LogFile, Out] 40 | -> true 41 | ; Argv = [LogFile] 42 | -> file_name_extension(Base, log, LogFile), 43 | file_name_extension(Base, dat, Out) 44 | ; usage 45 | ), 46 | read_log(LogFile, [skip_bad_requests(true)]), 47 | terms(Terms), 48 | save_terms(Out, Terms). 49 | 50 | -------------------------------------------------------------------------------- /logs/httpd-2009-02-20.dat: -------------------------------------------------------------------------------- 1 | %% downloads(Year, Month, Counts) 2 | 3 | date(2009, 2, 20). 4 | downloads(2009, 2, [doc=35, linux=382, mac=387, source=558, win32=5465, win64=545]). 5 | -------------------------------------------------------------------------------- /logs/httpd-2009-08-12.dat: -------------------------------------------------------------------------------- 1 | %% downloads(Year, Month, Counts) 2 | 3 | date(2009, 8, 12). 4 | downloads(2009, 2, [doc=19, linux=197, mac=175, source=251, win32=2469, win64=294]). 5 | downloads(2009, 3, [doc=38, linux=851, mac=832, source=1177, win32=10550, win64=1375]). 6 | downloads(2009, 4, [doc=10, linux=753, mac=787, source=937, win32=9014, win64=1232]). 7 | downloads(2009, 5, [doc=15, linux=706, mac=533, source=923, win32=8246, win64=1186]). 8 | downloads(2009, 6, [doc=47, linux=459, mac=421, source=772, win32=6664, win64=906]). 9 | downloads(2009, 7, [doc=62, linux=321, mac=358, source=606, win32=4336, win64=651]). 10 | downloads(2009, 8, [doc=19, linux=104, mac=123, source=240, win32=1243, win64=207]). 11 | -------------------------------------------------------------------------------- /logs/log2clf.pl: -------------------------------------------------------------------------------- 1 | %#!/home/janw/bin/pl -q -g main -s 2 | 3 | % You can make this program executable by removing the % from the first 4 | % line and update the path the SWI-Prolog interpreter 5 | 6 | % Convert Prolog logs to apache's Combined Log Format 7 | % http://httpd.apache.org/docs/1.3/logs.html#combined 8 | 9 | :- use_module(logstat). 10 | :- use_module(library(http/http_header)). 11 | :- use_module(library(main)). 12 | 13 | :-dynamic 14 | outfile/1, 15 | logfile/1. 16 | 17 | main([]) :- !, 18 | usage. 19 | main(Argv) :- 20 | retractall(outfile(_)), 21 | retractall(logfile(_)), 22 | ( process_argv(Argv, Options) 23 | -> true 24 | ; usage 25 | ), 26 | ( outfile(Out) 27 | -> write_clf(Out, Options) 28 | ; write_records(current_output, Options) 29 | ). 30 | 31 | usage :- 32 | format(user_error, 33 | 'Usage: log2clf [-o out] [-a date] [-b date] file ...~n', []), 34 | halt(1). 35 | 36 | process_argv([], []). 37 | process_argv(['-o', OutFile|T], Options) :- 38 | assert(outfile(OutFile)), !, 39 | process_argv(T, Options). 40 | process_argv(['-a', After|T0], [after(Date)|T]) :- !, 41 | text_to_data(After, Date), 42 | process_argv(T0, T). 43 | process_argv(['-b', Before|T0], [before(Date)|T]) :- !, 44 | text_to_data(Before, Date), 45 | process_argv(T0, T). 46 | process_argv([F|T], Options) :- 47 | assert(logfile(F)), 48 | read_log(F),!, 49 | process_argv(T, Options). 50 | 51 | text_to_data(Text, Y/M/D) :- 52 | atomic_list_concat([YA, MA, DA], /, Text), 53 | atom_number(YA, Y), 54 | atom_number(MA, M), 55 | atom_number(DA, D). 56 | 57 | 58 | writerecord(Out, LogRecord) :- 59 | memberchk(ip(IP), LogRecord), 60 | memberchk(time(Time), LogRecord), 61 | memberchk(path(Path), LogRecord), 62 | memberchk(referer(Referer), LogRecord), 63 | memberchk(result(Result), LogRecord), 64 | memberchk(extra(Extra), LogRecord), 65 | (memberchk(user_agent(Agent), Extra) -> true; Agent='-'), 66 | memberchk(http_version(Http1-Http2), Extra), 67 | format_time(atom(TimeStamp), "[%d/%b/%Y:%H:%M:%S %z]",Time), 68 | result_code(LogRecord, Extra, Result, ResultCode), 69 | (memberchk(bytes(Bytes), Extra) -> true; Bytes='-'), 70 | format(Out, '~w - - ~w "GET ~w HTTP/~w.~w" ~w ~w "~w" "~w"~n', 71 | [IP, TimeStamp, Path, Http1, Http2, ResultCode, Bytes, Referer, Agent]), 72 | !. 73 | 74 | writerecord(_, LogRecord) :- 75 | format(user_error, "Could not convert log record ~w~n", [LogRecord]). 76 | 77 | write_clf(Out, Options):- 78 | open(Out, write, OutS, [encoding(utf8)]), 79 | call_cleanup(write_records(OutS, Options), close(OutS)). 80 | 81 | %% write_records(+Stream) is det. 82 | % 83 | % Write the records. If we have multiple files, we must sort them. 84 | % Possibly this should be an option. It was designed to deal with 85 | % multiple files from a load-balancer, but it could of course also 86 | % be used with multiple files from a single server. 87 | 88 | write_records(OutS, Options) :- 89 | Queries = [ ip(_), 90 | path(_), 91 | referer(_), 92 | result(_), 93 | code(_), 94 | extra(_) 95 | | Options 96 | ], 97 | findall(F, logfile(F), Fs), 98 | length(Fs, NFiles), 99 | ( NFiles =< 1 100 | -> Params = [time(Time)|Queries], 101 | forall(logrecord(Params), 102 | writerecord(OutS,Params)) 103 | ; findall(Time-logrecord([time(Time)|Queries]), 104 | logrecord([time(Time)|Queries]), 105 | Logrecords), 106 | keysort(Logrecords, Sorted), 107 | forall(member(_-logrecord(Params),Sorted), 108 | writerecord(OutS,Params)) 109 | ). 110 | 111 | 112 | result_code(Record, _, _, Code) :- 113 | memberchk(code(Code), Record), !. 114 | result_code(_, Extra, _, Code) :- 115 | memberchk(code(Code), Extra), !. 116 | result_code(_, _, Status, Code) :- 117 | status_to_code(Status, Code). 118 | 119 | status_to_code(ok,200) :- !. 120 | status_to_code(true,200) :- !. 121 | status_to_code(file(_,_), 200) :- !. 122 | status_to_code(tmp_file(_,_), 200) :- !. 123 | status_to_code(moved(_), 301) :- !. 124 | status_to_code(not_modified, 304) :- !. 125 | status_to_code(moved_temporary(_), 307) :- !. 126 | status_to_code(error(404,_), 404) :- !. 127 | status_to_code(forbidden(_), 403) :- !. 128 | status_to_code(error(_R), 400) :- !. 129 | status_to_code(unavailable(_), 503) :- !. 130 | status_to_code(no_reply, 500) :- !. 131 | status_to_code(busy, 503) :- !. 132 | status_to_code(R,-) :- 133 | format('Error: No map for result ~w~n', [R]),!. 134 | 135 | -------------------------------------------------------------------------------- /logs/plstat.pl: -------------------------------------------------------------------------------- 1 | :- module(plstat, 2 | [ downloads/3 % -Term 3 | ]). 4 | :- use_module(logstat). 5 | :- use_module(library(aggregate)). 6 | 7 | % downloads(Month, Year, Counts:pairs) 8 | 9 | downloads(Year, Month, Pairs) :- 10 | setof(Platform=Count, 11 | aggregate(count, download(Year, Month, Platform), Count), 12 | Pairs). 13 | 14 | 15 | download(Year, Month, Platform) :- 16 | ( logrecord([time(Stamp), path(Path), code(200)]) 17 | *-> true 18 | ; logrecord([time(Stamp), path(Path), result(file(_,_))]) 19 | ), 20 | atom_concat('/download/', More, Path), 21 | atom_codes(More, Codes), 22 | phrase(file_details(_Version, Platform), Codes), 23 | stamp_date_time(Stamp, Date, 'UTC'), 24 | date_time_value(year, Date, Year), 25 | date_time_value(month, Date, Month). 26 | 27 | file_details(Version, Platform) --> 28 | string(_), "/", !, 29 | file_details(Version, Platform). 30 | file_details(Version, win32) --> 31 | "w32pl", 32 | short_version(Version), 33 | ".exe". 34 | file_details(Version, win64) --> 35 | "w64pl", 36 | short_version(Version), 37 | ".exe". 38 | file_details(Version, linux) --> 39 | "pl-", 40 | long_version(Version), 41 | "-", integer(_), ".", identifier(_), ".rpm". 42 | file_details(Version, source) --> 43 | "pl-", 44 | long_version(Version), 45 | ".tar.gz". 46 | file_details(Version, doc) --> 47 | "pl-doc-", 48 | long_version(Version), 49 | ".tar.gz". 50 | file_details(Version, mac) --> 51 | "swi-prolog-", 52 | long_version(Version), 53 | mac. 54 | file_details(Version, mac) --> 55 | "swi-prolog-devel-", 56 | long_version(Version), 57 | mac. 58 | 59 | mac --> "-mac", !, skip_rest(_). 60 | mac --> macos, macarch, ".mpkg", !, skip_rest(_). 61 | 62 | macos --> "-tiger". 63 | macos --> "-leopard". 64 | macos --> "". 65 | macarch --> "-powerpc". 66 | macarch --> "-intel". 67 | macarch --> "". 68 | 69 | integer(I) --> 70 | digit(D0), 71 | digits(DT), 72 | { number_codes(I, [D0|DT]) 73 | }. 74 | 75 | digit(D) --> 76 | [D], 77 | { code_type(D, digit) 78 | }. 79 | 80 | digits([H|T]) --> 81 | digit(H), !, 82 | digits(T). 83 | digits([]) --> 84 | []. 85 | 86 | identifier(Atom) --> 87 | [C0], 88 | { code_type(C0, alpha) 89 | }, 90 | alnums(CT), 91 | { atom_codes(Atom, [C0|CT]) 92 | }. 93 | 94 | alnums([H|T]) --> 95 | [H], 96 | { code_type(H, alnum) 97 | }, !, 98 | alnums(T). 99 | alnums([]) --> 100 | []. 101 | 102 | string([]) --> 103 | []. 104 | string([H|T]) --> 105 | [H], 106 | string(T). 107 | 108 | skip_rest(Rest, Rest, ""). 109 | 110 | long_version(V) --> 111 | integer(Major), ".", integer(Minor), ".", integer(Patch), 112 | { concat_atom([Major, Minor, Patch], '.', V) 113 | }. 114 | 115 | short_version(V) --> 116 | digit(Major), 117 | digit(Minor), 118 | digits(Patch), 119 | { atom_codes(V, [Major, 0'., Minor, 0'. | Patch]) 120 | }. 121 | -------------------------------------------------------------------------------- /logs/report/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.png 3 | *.db 4 | -------------------------------------------------------------------------------- /logs/report/webalizer.css: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- 2 | * Stone Steps Webalizer Style Sheet 3 | * Copyright 2004-2007 Stone Steps Inc. (www.stonesteps.ca) 4 | * ----------------------------------------------------------------------- */ 5 | 6 | /* 7 | * Basic HTML Styles 8 | */ 9 | body {background-color: white; color: black; font: 10pt Arial, sans-serif; margin: 0; padding: 0;} 10 | table {font: 9pt Arial, sans-serif;} 11 | h1 {font-size: 14pt; margin: 0.5em 0;} 12 | a {color: blue;} 13 | a:visited {color: red;} 14 | img {margin: 0 auto; display: block;} 15 | 16 | /* 17 | * Shared Classes 18 | */ 19 | pre.details_pre {font-size: 8pt; margin-left: 5px;} 20 | p.note_p {margin: auto 30px; color: #606060; font: 8pt Arial, sans-serif; text-align: center;} 21 | 22 | /* 23 | * 24 | */ 25 | td.spammer, span.spammer {color: red;} 26 | td.robot, span.robot {color: green;} 27 | td.converted, span.converted {color: inherit;} 28 | td.target, span.target {color: inherit;} 29 | 30 | /* 31 | * Graph Holders 32 | */ 33 | div.graph_holder {margin: 1em auto; padding: 0; border: 1px solid #333333; background-color: #E3E3E3;} 34 | div#monthly_summary_graph {} 35 | div#daily_usage_graph, div#hourly_usage_graph, div#country_usage_graph {width: 512px;} 36 | 37 | /* 38 | * Colorized Header Cells (Hits, Pages, Visits, etc) 39 | */ 40 | th.counter_th, .counter {background-color: #C0C0C0;} 41 | th.hits_th, .hits {background-color: #008040;} 42 | th.files_th, .files {background-color: #0080FF;} 43 | th.pages_th, .pages {background-color: #00E0FF;} 44 | th.visits_th, .visits {background-color: #FFFF00;} 45 | th.duration_th, .duration {background-color: #FFD700;} 46 | th.hosts_th, .hosts {background-color: #FF8000;} 47 | th.kbytes_th, .xfer {background-color: #FF2F2F;} 48 | th.time_th, .time {background-color: #66A1FF;} 49 | th.errors_th, .errors {background-color: #FFD700;} 50 | th.count_th, .count {background-color: #FFD700;} 51 | th.item_th, .item {background-color: #00E0FF;} 52 | th.method_th, .method {background-color: #8FBC8F;} 53 | th.dlname_th, .dlname {background-color: #8FBC8F;} 54 | th.country_th, .country {background-color: #B0E0E6;} 55 | 56 | tr.weekend_tr, .weekend {background-color: #D2EDF1;} 57 | 58 | /* 59 | * Highlight the row under the mouse pointer (IE doesn't support this) 60 | */ 61 | tbody.summary_data_tbody tr:hover th, 62 | tbody.summary_data_tbody tr:hover td, 63 | tbody.totals_data_tbody tr:hover th, 64 | tbody.totals_data_tbody tr:hover td, 65 | tbody.stats_data_tbody tr:hover td, 66 | tbody.stats_data_tbody tr:hover th {background-color: #FFFFD4;} 67 | tbody tr.group_shade_tr:hover td, 68 | tbody tr.group_shade_tr:hover th {background-color: #ADD8E6;} 69 | 70 | /* 71 | * Search Type Span 72 | */ 73 | span.search_type {color: gray;} 74 | 75 | /* 76 | * Page Header & Footer Classes 77 | */ 78 | div.page_header_div {background-color: #E5E5E5; border-bottom: 1px solid #CCCCCC; margin: 0; padding: .5em;} 79 | 80 | div.usage_summary_div {font-size: 8pt; margin: 0;} 81 | div.usage_summary_div em {font-size: 10pt; font-weight: bold; font-style: normal;} 82 | 83 | div.page_footer_div {border-top: 1px solid #CCCCCC; margin-top: 1em; padding: .5em 1em;} 84 | div.page_footer_div div {float: right;} 85 | div.page_footer_div a, 86 | div.page_footer_div a:visited {color: black;} 87 | div.page_footer_div a:hover {color: red;} 88 | 89 | /* 90 | * Page Links 91 | */ 92 | table.page_links_table {font-size: 9pt; margin: 1em auto 0; border-collapse: collapse; text-align: center; border-style: none;} 93 | table.page_links_table td {padding: 0; border: 1px solid #578EBE; background: #EEE url("page-links-bg.png") repeat-x;} 94 | table.page_links_table a {color: blue; text-decoration: none; border-style: none; display: block; padding: 1px 5px; margin: 0;} 95 | table.page_links_table a:visited {color: blue;} 96 | table.page_links_table a:hover {color: red; background: #FFF6BF url("page-links-hover-bg.png") repeat-x;} 97 | 98 | /* 99 | * Report table base classes 100 | */ 101 | table.report_table {border-collapse: collapse; background-color: #FAFAFA; margin: 1em auto; border-spacing: 1px;} 102 | table.report_table thead {background-color: #CCC;} 103 | table.report_table th, 104 | table.report_table td {padding: 2px 4px; border: 1px solid #888;} 105 | table.report_table thead th {border-color: #333;} 106 | table.report_table tr.table_title_tr {text-align: center; font-size: 12pt; background-color: #DDD;} 107 | table.report_table tr.table_footer_tr {background-color: #DDD;} 108 | table.report_table tr.all_items_tr {background-color: #D8D9E6;} 109 | /* anchors are not links in XML reports, so style them to look like links */ 110 | table.report_table tr.all_items_tr a {cursor: pointer;} 111 | table.report_table tr.all_items_tr a:hover {text-decoration: underline;} 112 | /* turn off highlighting for the max-items-displayed row */ 113 | table.report_table tr.max_items_tr {background-color: #D8D9E6;} 114 | table.report_table tr.max_items_tr:hover th {background-color: inherit;} 115 | 116 | table.report_table th.small_font_th {font-size: 7pt;} 117 | table.report_table tr.group_shade_tr {background-color: #D8D9E6;} 118 | 119 | /* 120 | * Monthly Summary Table 121 | */ 122 | table.monthly_summary_table {width: 600px;} 123 | table.monthly_summary_table tbody.summary_data_tbody {text-align: right;} 124 | table.monthly_summary_table tbody.summary_data_tbody th {text-align: left; font-weight: normal; white-space: nowrap;} 125 | table.monthly_summary_table tbody.summary_footer_tbody td {text-align: right; font-weight: bold;} 126 | table.monthly_summary_table tbody.summary_footer_tbody th {text-align: left;} 127 | 128 | /* 129 | * Monthly Totals Table 130 | */ 131 | table.monthly_totals_table {width: 510px;} 132 | table.monthly_totals_table tbody.totals_data_tbody th {text-align: left; font-weight: normal;} 133 | table.monthly_totals_table tbody.totals_data_tbody td {text-align: right; font-weight: bold;} 134 | table.monthly_totals_table tbody.totals_header_tbody {background-color: #DDD;} 135 | table.monthly_totals_table tbody.totals_header_tbody th {text-align: left;} 136 | table.monthly_totals_table tbody.totals_header_tbody td {text-align: right; font-weight: bold;} 137 | table.monthly_totals_table col.totals_data_col {width: 65px;} 138 | 139 | /* 140 | * Daily and Hourly Totals Tables 141 | */ 142 | table.totals_table {width: 510px;} 143 | table.totals_table tbody.totals_data_tbody td {text-align: right; font-weight: bold;} 144 | table.totals_table tbody.totals_data_tbody td.data_percent_td {font-size: 7pt; font-weight: normal;} 145 | 146 | /* 147 | * Stats Table 148 | */ 149 | table.stats_table {width: 510px;} 150 | table.stats_table tbody.stats_data_tbody td {text-align: right; font-weight: bold;} 151 | table.stats_table tbody.stats_data_tbody td.data_percent_td {font-size: 7pt; font-weight: normal;} 152 | table.stats_table tbody.stats_data_tbody td.stats_data_item_td {font-weight: normal; text-align: left; white-space: nowrap;} 153 | table.stats_table tbody.stats_footer_tbody td {text-align: center;} 154 | table.stats_table tbody.all_items_tbody {display: none;} 155 | -------------------------------------------------------------------------------- /logs/stat.pl: -------------------------------------------------------------------------------- 1 | :- module(stat, 2 | [ stat/1, 3 | stat2jpeg/2 % +Out, +Terms 4 | ]). 5 | :- use_module(library(pce)). 6 | :- use_module(library(autowin)). 7 | :- use_module(library('plot/barchart')). 8 | :- use_module(library(gradient)). 9 | :- use_module(library(lists)). 10 | 11 | :- ensure_x_server(125, 24). 12 | 13 | stat(Terms) :- 14 | Win = @stats, 15 | free(Win), 16 | stat(Terms, BarChart), 17 | new(Win, auto_sized_picture('Statistics')), 18 | send(Win, display, BarChart), 19 | send(Win, open). 20 | 21 | stat2jpeg(Jpeg, Terms) :- 22 | stat(Terms, BarChart), 23 | get(@pce, convert, BarChart, pixmap, Img), 24 | send(Img, save, Jpeg, jpeg). 25 | 26 | stat(Terms, BC) :- 27 | length(Terms, N), 28 | new(BC, bar_chart(vertical, 0, 15000, 300, N, 15, 2)), 29 | title(BC, Terms), 30 | legenda(Legenda), 31 | send(BC, display, Legenda, point(50, 50)), 32 | ( member(downloads(Y, Mon, Counts), Terms), 33 | sum_counts(Counts, T), 34 | T > 0, 35 | Y >= 2000, 36 | count(source, Counts, S), 37 | count(linux, Counts, L), 38 | count(mac, Counts, M), 39 | count(win32, Counts, W32), 40 | count(win64, Counts, W64), 41 | count(doc, Counts, DOC), 42 | month_name(Mon, Month), 43 | atomic_list_concat([Month, -, Y], Label), 44 | ( member(date(Y, Mon, Day), Terms) 45 | -> Day > 2, 46 | XS is round(S * 30/Day) - S, 47 | XL is round(L * 30/Day) - L, 48 | XM is round(M * 30/Day) - M, 49 | XW32 is round(W32 * 30/Day) - W32, 50 | XW64 is round(W64 * 30/Day) - W64, 51 | XDOC is round(DOC * 30/Day) - DOC, 52 | send(BC, append, 53 | bar_stack(Label, 54 | download_bar(source, true, XS), 55 | download_bar(source, false, S), 56 | download_bar(linux, true, XL), 57 | download_bar(linux, false, L), 58 | download_bar(mac, true, XM), 59 | download_bar(mac, false, M), 60 | download_bar(win32, true, XW32), 61 | download_bar(win32, false, W32), 62 | download_bar(win64, true, XW64), 63 | download_bar(win64, false, W64), 64 | download_bar(doc, true, XDOC), 65 | download_bar(doc, false, XDOC))) 66 | ; send(BC, append, 67 | bar_stack(Label, 68 | download_bar(source, false, S), 69 | download_bar(linux, false, L), 70 | download_bar(mac, false, M), 71 | download_bar(win32, false, W32), 72 | download_bar(win64, false, W64), 73 | download_bar(doc, false, DOC))) 74 | ), 75 | fail 76 | ; send(BC, nbars) 77 | ). 78 | 79 | sum_counts([], 0). 80 | sum_counts([_=C|T], Sum) :- 81 | sum_counts(T, Sum0), 82 | Sum is C+Sum0. 83 | 84 | count(Which, Counts, Count) :- 85 | memberchk(Which=Count, Counts), !. 86 | count(_, _, 0). 87 | 88 | month_name(1, jan). 89 | month_name(2, feb). 90 | month_name(3, mar). 91 | month_name(4, apr). 92 | month_name(5, may). 93 | month_name(6, jun). 94 | month_name(7, jul). 95 | month_name(8, aug). 96 | month_name(9, sep). 97 | month_name(10, oct). 98 | month_name(11, nov). 99 | month_name(12, dec). 100 | 101 | title(BC, Terms) :- 102 | member(date(Y, Mon, Day), Terms), !, 103 | send(BC, display, 104 | text(string('SWI-Prolog downloads until %d %s %s', 105 | Day, Mon, Y), 106 | left, 107 | boldlarge), 108 | point(50, 15)). 109 | title(BC, _) :- 110 | send(BC, display, 111 | text(string('SWI-Prolog downloads'), left, boldlarge), 112 | point(50, 15)). 113 | 114 | 115 | legenda(Dev) :- 116 | new(Dev, figure), 117 | send(Dev, format, new(F, format(horizontal, 2, @on))), 118 | send(F, row_sep, 0), 119 | legenda_entry(Dev, source, false, 'Source'), 120 | legenda_entry(Dev, doc, false, 'Documentation'), 121 | legenda_entry(Dev, linux, false, 'Linux RPM (exluding CD-distributions)'), 122 | legenda_entry(Dev, mac, false, 'MacOS X'), 123 | legenda_entry(Dev, win32, false, 'MS-Windows'), 124 | legenda_entry(Dev, win64, false, 'MS-Windows 64-bit edition'), 125 | legenda_entry(Dev, win64, true, 'interpolated (current month)'). 126 | 127 | legenda_entry(Dev, Id, Invert, Label) :- 128 | send(Dev, display, new(B, box(15, 10))), 129 | send(B, fill_offset, point(0,0)), 130 | bar_colour(Id, Invert, Gradient), 131 | send(B, fill_pattern, Gradient), 132 | send(Dev, display, text(Label)). 133 | 134 | :- pce_begin_class(download_bar, bar). 135 | 136 | initialise(Bar, Type:name, Estimate, Value:int) :-> 137 | bar_colour(Type, Estimate, Colour), 138 | send_super(Bar, initialise, Type, Value, Colour). 139 | 140 | :- pce_end_class(download_bar). 141 | 142 | 143 | /******************************* 144 | * COLOURS * 145 | *******************************/ 146 | 147 | 148 | % nth_bar_gradient(N, Hue, S, Vtop, VBottom) 149 | 150 | bar_gradient(source, 193, 80, 100, 20). 151 | bar_gradient(doc, 250, 80, 100, 20). 152 | bar_gradient(linux, 300, 80, 100, 20). 153 | bar_gradient(mac, 160, 80, 100, 20). 154 | bar_gradient(win32, 54, 80, 100, 20). 155 | bar_gradient(win64, 340, 80, 100, 20). 156 | bar_gradient( 5, 80, 80, 100, 20). 157 | bar_gradient( 6, 45, 80, 100, 20). 158 | bar_gradient( 7, 61, 80, 100, 20). 159 | bar_gradient( 8, 280, 80, 100, 20). 160 | bar_gradient( 9, 225, 80, 100, 20). 161 | 162 | :- dynamic 163 | colour_cache/2. 164 | 165 | bar_colour(Name, Inverse, Img) :- 166 | bar_gradient(Name, H, S0, VT, VB), !, 167 | ( Inverse == true 168 | -> Va = VT, Vz = VB, S = 30 169 | ; Va = VB, Vz = VT, S = S0 170 | ), 171 | new(Img, gradient(@nil, 172 | colour(@default, H, S, Va, hsv), 173 | colour(@default, H, S, Vz, hsv), 174 | 15, horizontal)), 175 | asserta(colour_cache(Name, Img)). 176 | 177 | -------------------------------------------------------------------------------- /logs/webalizer.conf: -------------------------------------------------------------------------------- 1 | LogType clf 2 | HostName www.swi-prolog.org 3 | OutputDir report 4 | DNSCache dns_cache 5 | DNSChildren 5 6 | GeoIPDBPath /usr/local/webalizer/GeoIP.dat 7 | AllURLs yes 8 | AllSearchStr yes 9 | AllHosts yes 10 | MangleAgents 5 11 | TopCountries 300 12 | -------------------------------------------------------------------------------- /make.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2015, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(web_make, []). 31 | :- use_module(library(option)). 32 | :- use_module(library(http/http_dispatch)). 33 | :- use_module(library(make)). 34 | :- use_module(library(broadcast)). 35 | 36 | :- use_module(messages). 37 | :- use_module(openid). 38 | 39 | :- http_handler(root(make), web_make, []). 40 | 41 | web_make(_Request) :- 42 | site_user_logged_in(User), 43 | site_user_property(User, granted(admin)), !, 44 | call_showing_messages(update, []). 45 | web_make(Request) :- 46 | option(path(Path), Request), 47 | throw(http_reply(forbidden(Path))). 48 | 49 | update :- 50 | make, 51 | broadcast(modified(wiki(reindex))). 52 | -------------------------------------------------------------------------------- /markdown.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2013, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(markdown, 31 | [ markdown_dom/2 % +MarkDown, -DOM 32 | ]). 33 | :- use_module(library(sgml)). 34 | :- use_module(library(process)). 35 | :- use_module(library(error)). 36 | 37 | /** Parse markdown documents into a DOM 38 | */ 39 | 40 | :- create_prolog_flag(markdown_program, markdown, []). 41 | 42 | %% markdown_dom(+Input, -DOM) is det. 43 | % 44 | % Process markdown input into an HTML DOM structure compatible to 45 | % load_structure/3 and html//1 as provided by 46 | % library(http/html_write). 47 | % 48 | % @param Input is either a term stream(+Stream) or the name of a 49 | % file. 50 | 51 | markdown_dom(stream(Stream), DOM) :- !, 52 | must_be(stream, Stream), 53 | current_prolog_flag(markdown_program, Prog), 54 | process_create(path(Prog), [], 55 | [ stdin(pipe(In)), 56 | stdout(pipe(Out)), 57 | process(PID) 58 | ]), 59 | thread_create(( copy_stream_data(Stream, In), 60 | close(In) 61 | ), _, [detached(true)]), 62 | load_structure(Out, DOM, [dialect(xml)]), 63 | process_wait(PID, _). 64 | markdown_dom(File, DOM) :- 65 | current_prolog_flag(markdown_program, Prog), 66 | process_create(path(Prog), [file(File)], 67 | [ stdout(pipe(Out)), 68 | process(PID) 69 | ]), 70 | load_structure(Out, DOM, [dialect(xml)]), 71 | process_wait(PID, _). 72 | 73 | -------------------------------------------------------------------------------- /markitup.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2013, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(markitup, 31 | [ markitup//1 32 | ]). 33 | :- use_module(library(http/http_dispatch)). 34 | :- use_module(library(http/html_head)). 35 | :- use_module(library(http/http_parameters)). 36 | :- use_module(library(http/html_write)). 37 | :- use_module(library(http/js_write)). 38 | :- use_module(library(option)). 39 | :- use_module(library(debug)). 40 | :- use_module(markdown). 41 | :- use_module(wiki). 42 | 43 | /** Wrapper for markItUp ajax markup editor 44 | 45 | @see http://markitup.jaysalvat.com/home/ 46 | */ 47 | 48 | :- http_handler(root('markitup/preview/markdown'), preview_markdown, []). 49 | :- http_handler(root('markitup/preview/pldoc'), preview_pldoc, []). 50 | 51 | :- html_resource(js('markitup/jquery.markitup.js'), 52 | [ requires([ jquery 53 | ]) 54 | ]). 55 | :- html_resource(js('markitup/sets/markdown/set.js'), 56 | [ requires([ js('markitup/jquery.markitup.js'), 57 | js('markitup/skins/markitup/style.css'), 58 | js('markitup/sets/markdown/style.css') 59 | ]) 60 | ]). 61 | :- html_resource(markdown, 62 | [ virtual(true), 63 | requires([ js('markitup/sets/markdown/set.js') 64 | ]) 65 | ]). 66 | :- html_resource(js('markitup/sets/pldoc/set.js'), 67 | [ requires([ js('markitup/jquery.markitup.js'), 68 | js('markitup/skins/markitup/style.css'), 69 | js('markitup/sets/pldoc/style.css') 70 | ]) 71 | ]). 72 | :- html_resource(pldoc, 73 | [ virtual(true), 74 | requires([ js('markitup/sets/pldoc/set.js') 75 | ]) 76 | ]). 77 | 78 | %% markitup(Options)// is det. 79 | % 80 | % Insert a =textarea= with markItUp support. 81 | 82 | markitup(Options) --> 83 | { option(markup(Language), Options, markdown), 84 | option(id(Id), Options, markdown), 85 | option(name(Name), Options, Id), 86 | option(cols(Cols), Options, 80), 87 | option(rows(Rows), Options, 20), 88 | option(value(Content), Options, []), 89 | option(preview(Preview), Options, false) 90 | }, 91 | html_requires(Language), 92 | html(textarea([id(Id), name(Name), cols(Cols), rows(Rows)], Content)), 93 | js_script({|javascript(Id,Language,Preview)|| 94 | $(document).ready(function() { 95 | $("#"+Id).markItUp(eval(Language+"_settings")); 96 | if ( eval(Preview) ) { 97 | $('a[title="Preview"]').trigger("mouseup"); 98 | } 99 | }); 100 | |}). 101 | 102 | 103 | %% preview_markdown(+Request) 104 | % 105 | % Handle preview requests from markItUp. The data is send using 106 | % a POST request, where the =data= field contains the content of 107 | % the textarea. 108 | 109 | preview_markdown(Request) :- 110 | http_parameters(Request, 111 | [ data(Data, [optional(true), default('')]) 112 | ]), 113 | debug(markitup(preview), 'Preview:~n~w~n', [Data]), 114 | open_atom_stream(Data, In), 115 | markdown_dom(stream(In), DOM), 116 | phrase(html(DOM), Tokens), 117 | format('Content-type: text/html; charset=UTF-8\n\n'), 118 | print_html(Tokens). 119 | 120 | %% preview_pldoc(+Request) 121 | % 122 | % Handle preview requests from markItUp. The data is send using 123 | % a POST request, where the =data= field contains the content of 124 | % the textarea. 125 | 126 | preview_pldoc(Request) :- 127 | http_parameters(Request, 128 | [ data(Data, [optional(true), default('')]) 129 | ]), 130 | debug(markitup(preview), 'Preview:~n~w~n', [Data]), 131 | atom_codes(Data, Codes), 132 | wiki_file_codes_to_dom(Codes, '/', DOM), % FIXME: What file to pass? 133 | phrase(page(plain, [], [\html_requires(pldoc)|DOM]), Tokens), 134 | format('Content-type: text/html; charset=UTF-8\n\n'), 135 | print_html(Tokens). 136 | 137 | 138 | /******************************* 139 | * UTIL * 140 | *******************************/ 141 | 142 | open_atom_stream(Atom, Stream) :- 143 | atom_to_memory_file(Atom, MF), 144 | open_memory_file(MF, read, Stream, 145 | [ free_on_close(true) 146 | ]). 147 | -------------------------------------------------------------------------------- /messages.pl: -------------------------------------------------------------------------------- 1 | /* Part of ClioPatria 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2010, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(html_messages, 31 | [ call_showing_messages/2 32 | ]). 33 | :- use_module(library(http/html_write)). 34 | :- use_module(library(http/html_head)). 35 | :- use_module(library(option)). 36 | 37 | /** Run goals that produce messages 38 | 39 | This module allows executing (long running) Prolog goals and see the 40 | messages appear in the browser. 41 | */ 42 | 43 | :- meta_predicate 44 | call_showing_messages(0, +). 45 | 46 | %% call_showing_messages(:Goal, +Options) is det. 47 | % 48 | % Execute Goal, showing the feedback in the browser. This 49 | % predicate builds a default application page with a placeholder 50 | % for the messages. It then sends all HTML upto the placeholder 51 | % and flushes the output to the browser. During execution, all 52 | % output from Goal emitted through print_message/2 is caught in 53 | % the message-box. After completion of Goal the page is completed. 54 | % 55 | % This predicate is intended for action such as loading RDF files, 56 | % while providing feedback on files loaded and possible error 57 | % messages. Note that this call creates a complete page. 58 | % 59 | % @bug This call uses =chunked= transfer encoding to send the 60 | % page in parts. Not all browsers support this and not 61 | % all browsers update the page incrementally. 62 | 63 | :- create_prolog_flag(html_messages, false, [type(boolean)]). 64 | 65 | assert_message_hook :- 66 | Head = message_hook(_Term, Level, Lines), 67 | Body = send_message(Level, Lines), 68 | ( clause(user:Head, Body) 69 | -> true 70 | ; asserta((user:Head :- Body)) 71 | ). 72 | 73 | :- initialization 74 | assert_message_hook. 75 | 76 | call_showing_messages(Goal, Options) :- 77 | option(style(Style), Options, default), 78 | option(head(Head), Options, title('SWI-Prolog -- make')), 79 | option(header(Header), Options, 80 | div(class(msg_header), 81 | h4('Messages ...'))), 82 | ( option(footer(Footer), Options) 83 | -> true 84 | ; ( option(return_to(ReturnURI), Options) 85 | -> FooterRest = [ p(['Go ', a(href(ReturnURI), 'back'), 86 | ' to the previous page']) ] 87 | ; FooterRest = [] 88 | ), 89 | Footer = div(class(msg_footer), [ h4('Done') | FooterRest ]) 90 | ), 91 | format('Content-Type: text/html~n'), 92 | format('Transfer-Encoding: chunked~n~n'), 93 | header(Style, Head, Header, Footer, FooterTokens), 94 | setup_call_cleanup( 95 | set_prolog_flag(html_messages, true), 96 | catch(Goal, E, print_message(error, E)), 97 | set_prolog_flag(html_messages, false)), !, 98 | footer(FooterTokens). 99 | 100 | send_message(Level, Lines) :- 101 | current_prolog_flag(html_messages, true), 102 | level_css_class(Level, Class), 103 | phrase(html(pre(class(Class), \html_message_lines(Lines))), Tokens), 104 | with_mutex(html_messages, print_html(Tokens)), 105 | flush_output, 106 | fail. 107 | 108 | level_css_class(informational, msg_informational). 109 | level_css_class(warning, msg_warning). 110 | level_css_class(error, msg_error). 111 | 112 | html_message_lines([]) --> 113 | []. 114 | html_message_lines([nl|T]) --> !, 115 | html('\n'), % we are in a

 environment
116 | 	html_message_lines(T).
117 | html_message_lines([flush]) -->
118 | 	[].
119 | html_message_lines([H|T]) --> !,
120 | 	html(H),
121 | 	html_message_lines(T).
122 | 
123 | 
124 | %%	header(+Style, +Head, +Header, +Footer, -FooterTokens)
125 | %
126 | %	Emit all tokens upto the placeholder for the actual messages and
127 | %	return the remaining page-tokens in FooterTokens. Style and Head
128 | %	are passed
129 | 
130 | header(Style, Head, Header, Footer, FooterTokens) :-
131 | 	Magic = '$$$MAGIC$$$',
132 | 	Body = [ Header,
133 | 		 \(html_messages:html_requires(css('messages.css'))),
134 | 		 div(class(messages), Magic),
135 | 		 Footer
136 | 	       ],
137 | 	phrase(html_write:page(Style, Head, Body), Tokens),
138 | 	html_write:mailman(Tokens),
139 | 	append(HeaderTokens, [Magic|FooterTokens], Tokens), !,
140 | 	current_output(Out),
141 | 	html_write:write_html(HeaderTokens, Out),
142 | 	flush_output(Out).
143 | 
144 | footer(Footer) :-
145 | 	current_output(Out),
146 | 	html_write:write_html(Footer, Out).
147 | 


--------------------------------------------------------------------------------
/news.pl:
--------------------------------------------------------------------------------
  1 | /*  Part of SWI-Prolog
  2 | 
  3 |     Author:        Wouter Beek & Jan Wielemaker
  4 |     E-mail:        J.Wielemaker@cs.vu.nl
  5 |     WWW:           http://www.swi-prolog.org
  6 |     Copyright (C): 2013-2014, VU University Amsterdam
  7 | 
  8 |     This program is free software; you can redistribute it and/or
  9 |     modify it under the terms of the GNU General Public License
 10 |     as published by the Free Software Foundation; either version 2
 11 |     of the License, or (at your option) any later version.
 12 | 
 13 |     This program is distributed in the hope that it will be useful,
 14 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 |     GNU General Public License for more details.
 17 | 
 18 |     You should have received a copy of the GNU General Public
 19 |     License along with this library; if not, write to the Free Software
 20 |     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 21 | 
 22 |     As a special exception, if you link this library with other files,
 23 |     compiled with a Free Software compiler, to produce an executable, this
 24 |     library does not by itself cause the resulting executable to be covered
 25 |     by the GNU General Public License. This exception does not however
 26 |     invalidate any other reasons why the executable file might be covered by
 27 |     the GNU General Public License.
 28 | */
 29 | 
 30 | :- module(news,
 31 | 	  [ random_news//0
 32 | 	  ]).
 33 | :- use_module(generics).
 34 | :- use_module(library(aggregate)).
 35 | :- use_module(library(random)).
 36 | :- use_module(library(http/html_head)).
 37 | :- use_module(library(http/html_write)).
 38 | :- use_module(library(http/http_dispatch)).
 39 | :- use_module(library(pldoc/doc_html)).
 40 | :- use_module(post).
 41 | 
 42 | :- html_resource(css('news.css'), [requires([css('post.css')])]).
 43 | 
 44 | :- http_handler(root(news), news_process, [prefix]).
 45 | :- http_handler(root(news/archive), news_archive, []).
 46 | 
 47 | /**  News on the SWI-Prolog Web site
 48 | 
 49 | @author Wouter Beek
 50 | @tbd Calculate relevance based on freshness lifetime and importance.
 51 | @tbd User-specific influencing of relevance. Based on login/based on cookies.
 52 | @version 2013/12
 53 | */
 54 | 
 55 | 
 56 | 
 57 | 
 58 | %%	news_process(+Request)
 59 | %
 60 | %	HTTP handler for /news/.  Distinguishes three cases:
 61 | %
 62 | %	  1. GET to /news// to render a single news item.
 63 | %	  2. GET to /news/ to render the _fresh_ news.
 64 | %	  3. POST provides a REST API for managing news.
 65 | 
 66 | news_process(Request) :-			% list specific article
 67 | 	memberchk(method(get), Request),
 68 | 	request_to_id(Request, news, Post),
 69 | 	Post \== '', !,
 70 | 	post(Post, title, Title1),
 71 | 	post(Post, kind, Kind),
 72 | 	(   post(Post, object, Object)
 73 | 	->  true
 74 | 	;   Object = null
 75 | 	),
 76 | 	atomic_list_concat(['News',Title1], ' -- ', Title2),
 77 | 	reply_html_page(
 78 | 	    news(Post),
 79 | 	    title(Title2),
 80 | 	    [ \post(Post, []),
 81 | 	      \news_backlink(Kind, Object)
 82 | 	    ]).
 83 | news_process(Request) :-			% list fresh news
 84 | 	memberchk(method(get), Request), !,
 85 | 	find_posts(news, fresh, Ids),
 86 | 	Title = 'News',
 87 | 	reply_html_page(
 88 | 	    news(fresh),
 89 | 	    title(Title),
 90 | 	    [ \html_requires(css('news.css')),
 91 | 	      \posts(news, null, Ids,
 92 | 		     [ order_by(created),
 93 | 		       add_add_link(false)
 94 | 		     ]),
 95 | 	      \news_archive_link(news, Ids),
 96 | 	      \add_post_link(news, null)
 97 | 	    ]).
 98 | news_process(Request) :-			% handle editing news
 99 | 	post_process(Request, news).
100 | 
101 | news_archive_link(Kind, Ids) -->
102 | 	{ find_posts(Kind, all, All),
103 | 	  length(All, Total)
104 | 	},
105 | 	(   { length(Ids, Total) }
106 | 	->  []
107 | 	;   { http_link_to_id(news_archive, [], HREF)
108 | 	    },
109 | 	    html(div(class('news-archive-link'),
110 | 		     a(href(HREF), 'View all ~D news articles'-[Total])))
111 | 	).
112 | 
113 | 
114 | %%	news_archive(+Request) is det.
115 | %
116 | %	Show all available news.
117 | 
118 | news_archive(_Request):-
119 | 	find_posts(news, all, Ids),
120 | 
121 | 	reply_html_page(
122 | 	    news(all),
123 | 	    title('News archive'),
124 | 	    [ \posts(news, null, Ids,
125 | 		     [ order_by(created),
126 | 		       add_add_link(false)
127 | 		     ]),
128 | 	      \news_backlink(news, null),
129 | 	      \add_post_link(news, null)
130 | 	    ]).
131 | 
132 | news_backlink(news, _Object) --> !,
133 | 	{ http_link_to_id(news_process, [], Link) },
134 | 	html(a(href=Link, 'Back to fresh news items')).
135 | news_backlink(_Kind, Object) -->
136 | 	html('View annotation in context of '),
137 | 	object_ref(Object, [style(title)]).
138 | 
139 | %!	random_news// is semidet.
140 | %
141 | %	Emit a random news item for the Did You Know place of the page.
142 | %	Fails if there is no news.
143 | 
144 | random_news -->
145 | 	{ random_new_item(Id, Title),
146 | 	  http_link_to_id(news_process, path_postfix(Id), Link)
147 | 	},
148 | 	html([ span(class(lbl), 'News: '),
149 | 	       span(id(dyknow), a(href=Link, Title))
150 | 	     ]).
151 | 
152 | %% random_new_item(-Id:atom, -Title:atom) is det.
153 | 
154 | random_new_item(Id, Title):-
155 | 	aggregate_all(
156 | 	    sum(Relevance),
157 | 	    ( post(Id, kind, news),
158 | 	      relevance(Id, Relevance)
159 | 	    ),
160 | 	    SummedRelevance),
161 | 	random(0.0, SummedRelevance, R),
162 | 	find_posts(news, fresh, Ids),
163 | 	random_new_item(0.0, R, Ids, Id, Title).
164 | 
165 | random_new_item(_V, _R, [Id], Id, Title):- !,
166 | 	post(Id, title, Title).
167 | random_new_item(V1, R, [Id|_], Id, Title):-
168 | 	relevance(Id, Relevance),
169 | 	V2 is V1 + Relevance,
170 | 	R =< V2, !,
171 | 	post(Id, title, Title).
172 | random_new_item(V1, R, [Id0|Ids], Id, Title):-
173 | 	relevance(Id0, Relevance),
174 | 	V2 is V1 + Relevance,
175 | 	random_new_item(V2, R, Ids, Id, Title).
176 | 
177 | 


--------------------------------------------------------------------------------
/object_support.pl:
--------------------------------------------------------------------------------
 1 | /*  Part of SWI-Prolog
 2 | 
 3 |     Author:        Jan Wielemaker
 4 |     E-mail:        J.Wielemaker@cs.vu.nl
 5 |     WWW:           http://www.swi-prolog.org
 6 |     Copyright (C): 2014, VU University Amsterdam
 7 | 
 8 |     This program is free software; you can redistribute it and/or
 9 |     modify it under the terms of the GNU General Public License
10 |     as published by the Free Software Foundation; either version 2
11 |     of the License, or (at your option) any later version.
12 | 
13 |     This program is distributed in the hope that it will be useful,
14 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
15 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 |     GNU General Public License for more details.
17 | 
18 |     You should have received a copy of the GNU General Public
19 |     License along with this library; if not, write to the Free Software
20 |     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 | 
22 |     As a special exception, if you link this library with other files,
23 |     compiled with a Free Software compiler, to produce an executable, this
24 |     library does not by itself cause the resulting executable to be covered
25 |     by the GNU General Public License. This exception does not however
26 |     invalidate any other reasons why the executable file might be covered by
27 |     the GNU General Public License.
28 | */
29 | 
30 | :- module(object_support,
31 | 	  [ object_label/2,		% +Object:compound
32 | 					% -Label:atom
33 | 	    object_id/2			% ?Object:compound
34 | 					% ?Id:atom
35 | 	  ]).
36 | 
37 | /**  Object support
38 | 
39 | */
40 | 
41 | :- use_module(wiki).
42 | 
43 | :- dynamic
44 | 	object_id_cache/2.
45 | 
46 | %!	object_id(+Object:compound, -Id:atom) is det.
47 | %!	object_id(-Object:compound, +Id:atom) is semidet.
48 | %
49 | %	True when Id is a (hash) id for Object.
50 | 
51 | object_id(Object, Id) :-
52 | 	object_id_cache(Object, Id), !.
53 | object_id(Object, Id) :-
54 | 	ground(Object),
55 | 	variant_sha1(Object, Id),
56 | 	assertz(object_id_cache(Object, Id)).
57 | 
58 | 
59 | %!	object_label(+Object:compound, -Label:atom) is det.
60 | %
61 | %	True when Label is a label for Object.
62 | 
63 | object_label(Name/Arity, Label) :- !,
64 | 	format(atom(Label), 'predicate ~w/~w', [Name, Arity]).
65 | object_label(Name//Arity, Label) :- !,
66 | 	format(atom(Label), 'non-terminal ~w/~w', [Name, Arity]).
67 | object_label(M:Name/Arity, Label) :- !,
68 | 	format(atom(Label), 'predicate ~w:~w/~w', [M, Name, Arity]).
69 | object_label(M:Name//Arity, Label) :- !,
70 | 	format(atom(Label), 'non-terminal ~w:~w//~w', [M, Name, Arity]).
71 | object_label(f(Name/Arity), Label) :- !,
72 | 	format(atom(Label), 'function ~w/~w', [Name, Arity]).
73 | object_label(c(Function), Label) :- !,
74 | 	format(atom(Label), 'C API function ~w()', [Function]).
75 | object_label(Module:module(_Title), Label) :-
76 | 	module_property(Module, file(File)), !,
77 | 	file_base_name(File, Base),
78 | 	format(atom(Label), 'module ~w', [Base]).
79 | object_label(section(ID), Label) :-
80 | 	prolog:doc_object_summary(section(_Level, _No, ID, _File),_,_,Title), !,
81 | 	format(atom(Label), 'Section "~w"', [Title]).
82 | object_label(wiki(Location), Label) :-
83 | 	wiki_page_title(Location, Title),
84 | 	format(atom(Label), 'Wiki page "~w"', [Title]).
85 | object_label(Obj, Label) :-
86 | 	term_to_atom(Obj, Label).
87 | 
88 | 


--------------------------------------------------------------------------------
/parms.pl:
--------------------------------------------------------------------------------
  1 | /*  Part of SWI-Prolog
  2 | 
  3 |     Author:        Jan Wielemaker
  4 |     E-mail:        J.Wielemaker@cs.vu.nl
  5 |     WWW:           http://www.swi-prolog.org
  6 |     Copyright (C): 2009-2024, VU University Amsterdam
  7 | 			      SWI-Prolog Solutions b.v.
  8 | 
  9 |     This program is free software; you can redistribute it and/or
 10 |     modify it under the terms of the GNU General Public License
 11 |     as published by the Free Software Foundation; either version 2
 12 |     of the License, or (at your option) any later version.
 13 | 
 14 |     This program is distributed in the hope that it will be useful,
 15 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 17 |     GNU General Public License for more details.
 18 | 
 19 |     You should have received a copy of the GNU General Public
 20 |     License along with this library; if not, write to the Free Software
 21 |     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 22 | 
 23 |     As a special exception, if you link this library with other files,
 24 |     compiled with a Free Software compiler, to produce an executable, this
 25 |     library does not by itself cause the resulting executable to be covered
 26 |     by the GNU General Public License. This exception does not however
 27 |     invalidate any other reasons why the executable file might be covered by
 28 |     the GNU General Public License.
 29 | */
 30 | 
 31 | :- module(plweb_parms,
 32 | 	  [ server/2,			% ?Role, ?Host
 33 | 	    server/3			% ?Role, ?Host, -HostName
 34 | 	  ]).
 35 | :- use_module(library(http/http_log)).
 36 | :- use_module(library(http/http_path)).
 37 | :- use_module(library(http/http_dispatch)).
 38 | :- use_module(library(http/http_cors)).
 39 | :- use_module(library(http/html_head)).
 40 | :- use_module(library(www_browser)).
 41 | :- use_module(library(settings)).
 42 | :- use_module(library(pengines)).
 43 | 
 44 | 
 45 | :- setting(http:served_file_extensions,
 46 | 	   list(atom),
 47 | 	   [ html, gif, png, jpeg, jpg, css, js, tgz, exe, c, zip ],
 48 | 	   'List of extensions that are served as plain files').
 49 | :- setting(http:index_files,
 50 | 	   list(atom),
 51 | 	   [ 'index.html', 'index.md' ],
 52 | 	   'List of files that provide a directory index').
 53 | :- setting(http:port,
 54 | 	   integer,
 55 | 	   3040,
 56 | 	   'Default port').
 57 | :- setting(http:workers,
 58 | 	   integer,
 59 | 	   10,
 60 | 	   'Number of worker threads').
 61 | 
 62 | :- set_setting_default(pengines:allow_from, []).
 63 | :- set_setting_default(http:logfile, log('httpd.log')).
 64 | :- set_setting_default(http:cors, [*]).
 65 | 
 66 | 
 67 | 		 /*******************************
 68 | 		 *	     LOCATIONS		*
 69 | 		 *******************************/
 70 | 
 71 | http:location(pldoc,	root(pldoc),	   [priority(10)]).
 72 | http:location(download,	root(download),	   []).
 73 | http:location(icons,	root(icons),	   []).
 74 | http:location(css,	root(css),	   []).
 75 | http:location(jq,	root('js/jquery'), []).
 76 | 
 77 | 
 78 | 		 /*******************************
 79 | 		 *	   EXTERNAL URLS	*
 80 | 		 *******************************/
 81 | 
 82 | :- multifile
 83 | 	user:url_path/2.
 84 | 
 85 | user:url_path(swi,	'/').
 86 | user:url_path(pkg,	swi('pldoc/package/')).
 87 | user:url_path(pack,	swi('pack/list/')).
 88 | user:url_path(swipub,	 swi('download/publications/')).
 89 | user:url_path(fsf,	'https://www.fsf.org').
 90 | user:url_path(gnu,	'https://www.gnu.org').
 91 | user:url_path(gpl,	gnu('licences/gpl.html')).
 92 | user:url_path(lgpl,	gnu('licences/lgpl.html')).
 93 | user:url_path(wordnet,	'https://wordnet.princeton.edu/').
 94 | user:url_path(gmp,	'https://gmplib.org/').
 95 | user:url_path(gitweb,	 'https://github.com/SWI-Prolog').
 96 | user:url_path(swieditor, 'https://arbeitsplattform.bildung.hessen.de/fach/informatik/swiprolog/indexe.html').
 97 | user:url_path(git,	 'https://git-scm.com/').
 98 | user:url_path(macports,	 'https://www.macports.org/').
 99 | user:url_path(xquartz,	 'https://www.xquartz.org/').
100 | user:url_path(json,	 'https://json.org/').
101 | user:url_path(thea,	 'http://vangelisv.github.io/thea/').
102 | user:url_path(dig,	 'https://dl.kr.org/dig/').
103 | user:url_path(sparql,	 'https://www.w3.org/TR/sparql11-query/').
104 | 
105 | 
106 | 		 /*******************************
107 | 		 *	      RESOURCES		*
108 | 		 *******************************/
109 | 
110 | :- html_resource(swipl_css,
111 | 		 [ virtual(true),
112 | 		   requires([ css('swipl.css') ])
113 | 		 ]).
114 | :- html_resource(plweb,
115 | 		 [ virtual(true),
116 | 		   requires([ pldoc_css,
117 | 			      css('plweb.css')
118 | 			    ])
119 | 		 ]).
120 | :- if(\+html_current_resource(jquery)).
121 | :- html_resource(jquery,
122 | 		 [ virtual(true),
123 | 		   requires([ jq('jquery.js')
124 | 			    ])
125 | 		 ]).
126 | :- endif.
127 | :- html_resource(js('jquery/ui/jquery-ui.min.js'),
128 | 		 [ requires([ jquery
129 | 			    ])
130 | 		 ]).
131 | :- html_resource(jquery_ui,
132 | 		 [ virtual(true),
133 | 		   requires([ js('jquery/ui/jquery-ui.min.js'),
134 | 			      js('jquery/ui/jquery-ui.min.css')
135 | 			    ])
136 | 		 ]).
137 | :- html_resource(jq('menu.js'),
138 | 		 [ requires([ jquery
139 | 			    ])
140 | 		 ]).
141 | 
142 | 
143 | 		 /*******************************
144 | 		 *	       FILES		*
145 | 		 *******************************/
146 | 
147 | :- multifile user:file_search_path/2.
148 | :- dynamic   user:file_search_path/2.
149 | 
150 | :- prolog_load_context(directory, Dir),
151 |    (   user:file_search_path(plweb, Dir)
152 |    ->  true
153 |    ;   asserta(user:file_search_path(plweb, Dir))
154 |    ).
155 | 
156 | user:file_search_path(data,          plweb(data)).
157 | user:file_search_path(git_data,      data(git)).
158 | user:file_search_path(git_data,      plweb(.)).
159 | user:file_search_path(document_root, git_data(www)).
160 | user:file_search_path(examples,      git_data(examples)).
161 | user:file_search_path(blog,	     git_data(blog)).
162 | user:file_search_path(private,       data(private)).
163 | user:file_search_path(log,           data(log)).
164 | user:file_search_path(download,	     data(download)).
165 | user:file_search_path(icons,	     document_root(icons)).
166 | user:file_search_path(css,	     document_root(css)).
167 | user:file_search_path(js,	     document_root(js)).
168 | 
169 | 
170 | 		 /*******************************
171 | 		 *	   MASTER/SLAVE		*
172 | 		 *******************************/
173 | 
174 | %%	server(?Type, ?Host) is nondet.
175 | %%	server(?Type, ?Host, ?HostName) is nondet.
176 | %
177 | %	Describe known servers  and  their   role.  Currently,  the only
178 | %	important role is `master`. Logged in sessions are redirected to
179 | %	the master to simplify keeping one view   of the data. In future
180 | %	versions we might go for a more distributed database.
181 | 
182 | server(Type, Host) :-
183 | 	server(Type, Host, _HostName).
184 | 
185 | server(cdn,    'www.swi-prolog.org', -).
186 | server(slave,  'us.swi-prolog.org', 'swi-prolog.osuosl.org').
187 | server(master, 'eu.swi-prolog.org', -).
188 | %server(master, 'localhost', -).
189 | 


--------------------------------------------------------------------------------
/proxy.pl:
--------------------------------------------------------------------------------
  1 | /*  Part of SWI-Prolog
  2 | 
  3 |     Author:        Jan Wielemaker
  4 |     E-mail:        J.Wielemaker@cs.vu.nl
  5 |     WWW:           http://www.swi-prolog.org
  6 |     Copyright (C): 2015, VU University Amsterdam
  7 | 
  8 |     This program is free software; you can redistribute it and/or
  9 |     modify it under the terms of the GNU General Public License
 10 |     as published by the Free Software Foundation; either version 2
 11 |     of the License, or (at your option) any later version.
 12 | 
 13 |     This program is distributed in the hope that it will be useful,
 14 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 |     GNU General Public License for more details.
 17 | 
 18 |     You should have received a copy of the GNU General Public
 19 |     License along with this library; if not, write to the Free Software
 20 |     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 21 | 
 22 |     As a special exception, if you link this library with other files,
 23 |     compiled with a Free Software compiler, to produce an executable, this
 24 |     library does not by itself cause the resulting executable to be covered
 25 |     by the GNU General Public License. This exception does not however
 26 |     invalidate any other reasons why the executable file might be covered by
 27 |     the GNU General Public License.
 28 | */
 29 | 
 30 | :- module(plweb_proxy,
 31 | 	  [ proxy/2,			% +Target, +Request
 32 | 	    proxy/3			% +Target, +Request, +HdrExtra
 33 | 	  ]).
 34 | :- use_module(library(http/http_open)).
 35 | :- use_module(library(option)).
 36 | :- use_module(library(apply)).
 37 | 
 38 | %%	proxy(+To, +Request) is det.
 39 | %%	proxy(+To, +Request, +Options) is det.
 40 | %
 41 | %	Proxy a request to a remote   server.  This proxies all methods,
 42 | %	including those carrying data such as POST and PUT.  Options:
 43 | %
 44 | %	  - request_headers(+List)
 45 | %	  Additional headers for the request.  List is of the form
 46 | %	  Name = Value.
 47 | %	  - reply_headers+List)
 48 | %	  Additional headers for the reply
 49 | 
 50 | proxy(To, Request) :-
 51 | 	proxy(To, Request, []).
 52 | proxy(To, Request, Options) :-
 53 | 	memberchk(method(Method), Request),
 54 | 	proxy(Method, To, Request, Options).
 55 | 
 56 | proxy(Method, To, Request, Options) :-
 57 | 	data_method(Method), !,
 58 | 	read_data(Request, Data),
 59 | 	memberchk(request_uri(URI), Request),
 60 |         atomic_list_concat([To,URI], Target),
 61 | 	option(request_headers(ReqHrd0), Options, []),
 62 | 	maplist(request_header, ReqHrd0, ReqHrd),
 63 | 	http_open(Target, In,
 64 | 		  [ method(Method),
 65 | 		    post(Data),
 66 | 		    header(content_type, ContentType)
 67 | 		  | ReqHrd
 68 | 		  ]),
 69 |         call_cleanup(
 70 | 	    read_string(In, _, Bytes),
 71 | 	    close(In)),
 72 | 	option(reply_headers(HdrExtra0), Options, []),
 73 | 	maplist(reply_header, HdrExtra0, HdrExtra),
 74 | 	throw(http_reply(bytes(ContentType, Bytes), HdrExtra)).
 75 | proxy(Method, To, Request, Options) :-
 76 | 	memberchk(request_uri(URI), Request),
 77 |         atomic_list_concat([To,URI], Target),
 78 | 	option(request_headers(ReqHrd0), Options, []),
 79 | 	maplist(request_header, ReqHrd0, ReqHrd),
 80 | 	http_open(Target, In,
 81 | 		  [ method(Method),
 82 | 		    header(content_type, ContentType)
 83 | 		  | ReqHrd
 84 | 		  ]),
 85 |         call_cleanup(
 86 | 	    read_string(In, _, Bytes),
 87 | 	    close(In)),
 88 | 	option(reply_headers(HdrExtra0), Options, []),
 89 | 	maplist(reply_header, HdrExtra0, HdrExtra),
 90 | 	throw(http_reply(bytes(ContentType, Bytes), HdrExtra)).
 91 | 
 92 | read_data(Request, bytes(ContentType, Bytes)) :-
 93 | 	memberchk(input(In), Request),
 94 | 	memberchk(content_type(ContentType), Request),
 95 | 	(   memberchk(content_length(Len), Request)
 96 | 	->  read_string(In, Len, Bytes)
 97 | 	;   read_string(In, _, Bytes)
 98 | 	).
 99 | 
100 | data_method(post).
101 | data_method(put).
102 | 
103 | request_header(Name = Value, request_header(Name = Value)).
104 | 
105 | reply_header(Name = Value, Term) :-
106 | 	Term =.. [Name,Value].
107 | 


--------------------------------------------------------------------------------
/rating.pl:
--------------------------------------------------------------------------------
  1 | /*  Part of SWI-Prolog
  2 | 
  3 |     Author:        Jan Wielemaker
  4 |     E-mail:        J.Wielemaker@cs.vu.nl
  5 |     WWW:           http://www.swi-prolog.org
  6 |     Copyright (C): 2013, VU University Amsterdam
  7 | 
  8 |     This program is free software; you can redistribute it and/or
  9 |     modify it under the terms of the GNU General Public License
 10 |     as published by the Free Software Foundation; either version 2
 11 |     of the License, or (at your option) any later version.
 12 | 
 13 |     This program is distributed in the hope that it will be useful,
 14 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 |     GNU General Public License for more details.
 17 | 
 18 |     You should have received a copy of the GNU General Public
 19 |     License along with this library; if not, write to the Free Software
 20 |     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 21 | 
 22 |     As a special exception, if you link this library with other files,
 23 |     compiled with a Free Software compiler, to produce an executable, this
 24 |     library does not by itself cause the resulting executable to be covered
 25 |     by the GNU General Public License. This exception does not however
 26 |     invalidate any other reasons why the executable file might be covered by
 27 |     the GNU General Public License.
 28 | */
 29 | 
 30 | :- module(rating,
 31 | 	  [ rate//1
 32 | 	  ]).
 33 | :- use_module(library(http/http_path)).
 34 | :- use_module(library(http/http_dispatch), []).
 35 | :- use_module(library(http/html_head)).
 36 | :- use_module(library(http/html_write)).
 37 | :- use_module(library(option)).
 38 | 
 39 | :- html_resource(jq('jRating.jquery.js'),
 40 | 		 [ requires([ jquery,
 41 | 			      jq('jRating.jquery.css')
 42 | 			    ])
 43 | 		 ]).
 44 | 
 45 | 
 46 | /**  Provide a star-rating widget
 47 | 
 48 | @see http://www.myjqueryplugins.com/jquery-plugin/jrating
 49 | */
 50 | 
 51 | rate(Options) -->
 52 | 	{ option(class(Class), Options, jrating),
 53 | 	  select_option(data_id(Id), Options, Options1, rating),
 54 | 	  (   select_option(data_average(Avg), Options1, Options2)
 55 | 	  ->  Extra = ['data-average'(Avg)]
 56 | 	  ;   Extra = [],
 57 | 	      Options2 = Options
 58 | 	  )
 59 | 	},
 60 | 	html_requires(jq('jRating.jquery.js')),
 61 | 	html(div([ class(Class), 'data-id'(Id)| Extra], [])),
 62 | 	(   { option(post(Post), Options2) }
 63 | 	->  html_post(Post, \script(Options2))
 64 | 	;   script(Options2)
 65 | 	).
 66 | 
 67 | script(Options) -->
 68 | 	{ option(length(Length), Options, 5),
 69 | 	  option(rate_max(RateMax), Options, 20),
 70 | 	  option(step(Step), Options, false),
 71 | 	  option(type(Type), Options, big),
 72 | 	  option(class(Class), Options, jrating),
 73 | 	  option(can_rate_again(CanRateAgain), Options, false),
 74 | 	  http_absolute_location(jq('icons/stars.png'), BSP, []),
 75 | 	  http_absolute_location(jq('icons/small.png'), SSP, [])
 76 | 	},
 77 | 	html(script(type('text/javascript'),
 78 | 		    [ \[ '$(document).ready(function(){\n',
 79 | 			 '$(".',Class,'").jRating(\n',
 80 | 			 '   { bigStarsPath:"',BSP,'",\n',
 81 | 			 '     smallStarsPath:"',SSP,'",\n',
 82 | 			 '     step:',Step,',\n',
 83 | 			 '     type:"',Type,'",\n',
 84 | 			 '     length:',Length,',\n',
 85 | 			 '     rateMax:',RateMax,',\n',
 86 | 			 '     canRateAgain:',CanRateAgain,',\n'
 87 | 		       ],
 88 | 		      \set_disabled(Options),
 89 | 		      \set_action(Options),
 90 | 		      \set_field(Options),
 91 | 		      \[ '   });\n',
 92 | 			 '});\n'
 93 | 		       ]
 94 | 		    ])).
 95 | 
 96 | set_disabled(Options) -->
 97 | 	{ option(disabled(true), Options) }, !,
 98 | 	html(\[ '     isDisabled:true,\n'
 99 | 	      ]).
100 | set_disabled(_) --> [].
101 | 
102 | set_action(Options) -->
103 | 	{ option(on_rating(OnRating), Options) }, !,
104 | 	html(\[ '     phpPath:"',OnRating,'",\n'
105 | 	      ]).
106 | set_action(_) --> [].
107 | 
108 | set_field(Options) -->
109 | 	{ option(set_field(Field), Options) }, !,
110 | 	html(\[ '     onSuccess: function(e,r)\n',
111 | 		'     { $(\'input[name=~w]\').val(r);\n'-[Field],
112 | 		'     }\n'
113 | 	      ]).
114 | set_field(_) --> [].
115 | 


--------------------------------------------------------------------------------
/register.pl:
--------------------------------------------------------------------------------
  1 | /*  Part of SWI-Prolog
  2 | 
  3 |     Author:        Jan Wielemaker
  4 |     E-mail:        J.Wielemaker@cs.vu.nl
  5 |     WWW:           http://www.swi-prolog.org
  6 |     Copyright (C): 2013, VU University Amsterdam
  7 | 
  8 |     This program is free software; you can redistribute it and/or
  9 |     modify it under the terms of the GNU General Public License
 10 |     as published by the Free Software Foundation; either version 2
 11 |     of the License, or (at your option) any later version.
 12 | 
 13 |     This program is distributed in the hope that it will be useful,
 14 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 |     GNU General Public License for more details.
 17 | 
 18 |     You should have received a copy of the GNU General Public
 19 |     License along with this library; if not, write to the Free Software
 20 |     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 21 | 
 22 |     As a special exception, if you link this library with other files,
 23 |     compiled with a Free Software compiler, to produce an executable, this
 24 |     library does not by itself cause the resulting executable to be covered
 25 |     by the GNU General Public License. This exception does not however
 26 |     invalidate any other reasons why the executable file might be covered by
 27 |     the GNU General Public License.
 28 | */
 29 | 
 30 | :- module(register, []).
 31 | :- use_module(library(http/html_write)).
 32 | :- use_module(library(http/http_dispatch)).
 33 | :- use_module(library(http/http_parameters)).
 34 | :- use_module(openid).
 35 | :- use_module(library(smtp)).
 36 | 
 37 | :- http_handler(root(register),            register,            []).
 38 | :- http_handler(root(submit_registration), submit_registration, []).
 39 | 
 40 | register(Request) :-
 41 | 	http_parameters(Request,
 42 | 			[ for(Kind, [ oneof([wiki,news]),
 43 | 				      default(wiki)
 44 | 				    ])
 45 | 			]),
 46 | 	site_user(Request, User),
 47 | 	reg_title(Kind, Title),
 48 | 	reply_html_page(
 49 | 	    user(form(register(User, Kind))),
 50 | 	    title(Title),
 51 | 	    \reg_body(User, Kind)).
 52 | 
 53 | reg_title(wiki, 'Register to edit SWI-Prolog wiki pages').
 54 | reg_title(news, 'Register to manage news postings').
 55 | 
 56 | reg_body(User, Kind) -->
 57 | 	reg_explain(Kind),
 58 | 	form(User, Kind).
 59 | 
 60 | reg_explain(wiki) -->
 61 | 	html({|html||
 62 | 	      

This form allows you to request permission to edit 63 | the SWI-Prolog wiki pages. That is, the pages that have 64 | an Edit this page in the WIKI menu. 65 |

66 | 67 |

Registration form

68 | |}). 69 | reg_explain(news) --> 70 | html({|html|| 71 |

This form allows you to request permission to post 72 | news articles using the menu COMMUNITY/News 73 |

74 | 75 |

Registration form

76 | |}). 77 | 78 | 79 | form(UUID, Kind) --> 80 | { http_location_by_id(submit_registration, Action), 81 | PlaceHolder = 'Please tell us your plans, so that we can \c 82 | tell you are a genuine human Prolog user', 83 | site_user_property(UUID, name(Name), 'anonymous'), 84 | site_user_property(UUID, email(Email), 'unknown') 85 | }, 86 | html(form(action(Action), 87 | [ input([type(hidden), name(kind), value(Kind)]), 88 | table([ tr([ th([align(right)], 'Name'), 89 | td(input([name(name), 90 | placeholder( 91 | 'Name associated to commits'), 92 | disabled(disabled), 93 | value(Name) 94 | ])) 95 | ]), 96 | tr([ th([align(right)], 'Email'), 97 | td(input([name(email), 98 | placeholder( 99 | 'Displayed with GIT commit'), 100 | disabled(disabled), 101 | value(Email) 102 | ])) 103 | ]), 104 | tr([ th([align(right), valign(top)], 'Comments:'), 105 | td([ class(wiki_text), colspan(2) ], 106 | textarea([ cols(50),rows(10),name(comment), 107 | placeholder(PlaceHolder) 108 | ], 109 | '')) 110 | ]), 111 | tr([ td([ colspan(2), align(right) ], 112 | input([ type(submit), 113 | value('Sent request') 114 | ])) 115 | ]) 116 | ]) 117 | ])). 118 | 119 | 120 | %% submit_registration(+Request) is det. 121 | % 122 | % Sent E-mail to submit a registration 123 | 124 | submit_registration(Request) :- 125 | site_user(Request, UUID), 126 | http_parameters(Request, 127 | [ comment(Comment, [optional(true)]), 128 | kind(Kind, []) 129 | ]), 130 | mail(UUID, Kind, Comment), 131 | reply_html_page( 132 | user(mailed(admin, permission(Kind))), 133 | title('Mail sent'), 134 | [ p([ 'A mail has been sent to the site adminstrator. ', 135 | 'You will be informed when the account has been ', 136 | 'created.' 137 | ]) 138 | ]). 139 | 140 | mail(UUID, Kind, Comment) :- 141 | smtp_send_mail('jan@swi-prolog.org', 142 | message(UUID, Kind, Comment), 143 | [ subject('SWI-Prolog permission request'), 144 | from('jan@swi-prolog.org') 145 | ]). 146 | 147 | message(UUID, Kind, Comment, Out) :- 148 | site_user_property(UUID, name(Name), 'anonymous'), 149 | site_user_property(UUID, email(EMail), 'unknown'), 150 | format(Out, 'New site permission request\n\n', []), 151 | format(Out, '\t Kind: ~w~n', [Kind]), 152 | format(Out, '\t UUID: ~w~n', [UUID]), 153 | format(Out, '\t Name: ~w~n', [Name]), 154 | format(Out, '\tE-Mail: ~w~n', [EMail]), 155 | format(Out, '~n~w~n', [Comment]). 156 | 157 | 158 | site_user_property(UUID, P, Default) :- 159 | ( site_user_property(UUID, P) 160 | -> true 161 | ; arg(1, P, Default) 162 | ). 163 | 164 | :- multifile 165 | plweb:page_title//1. 166 | 167 | 168 | plweb:page_title(user(form(register(_User, Kind)))) --> 169 | { reg_title(Kind, Title) }, 170 | html(Title). 171 | plweb:page_title(user(mailed(To, permission(_Kind)))) --> 172 | html('Mail sent to ~w'-[To]). 173 | -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #avoid interactive behaviour 4 | export GIT_ASKPASS=/bin/false 5 | 6 | while true; do 7 | swipl -s load.pl -g server 8 | if [ "$?" = 42 ]; then 9 | break; 10 | fi 11 | reset 12 | done 13 | -------------------------------------------------------------------------------- /scripts/fix-permissions: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # fix permissions to run the SWI-Prolog website as www-data, group www-data 3 | 4 | groupshared() 5 | { chgrp -R www-data . 6 | find . -type d | xargs chmod 2775 7 | find refs -type f | xargs chmod 664 8 | git config core.sharedRepository group 9 | } 10 | 11 | fixwiki() 12 | { git config user.email "wiki@swi-prolog.org" 13 | git config user.name "Wiki manager" 14 | chgrp -R www-data . 15 | find . -name '*.txt' | xargs chmod 664 16 | find . -type d | xargs chmod 2775 17 | (cd `git rev-parse --git-dir` && groupshared) 18 | } 19 | 20 | fixerepo() 21 | { ( chgrp -R www-data . 22 | find . -type f | xargs chmod 664 23 | find . -type d | xargs chmod 2775 24 | (cd `git rev-parse --git-dir` && groupshared) 25 | ) 26 | } 27 | 28 | for repo in www examples blog; do 29 | [ -d data/git/$repo ] || git clone https://github.com/SWI-Prolog/plweb-$repo.git data/git/$repo 30 | done 31 | 32 | (cd data/git/www && fixwiki) 33 | (cd data/git/blog && fixrepo) 34 | (cd data/git/examples && fixrepo) 35 | 36 | # Create and fix the logfile permissions 37 | datadirs="data/log data/pack" 38 | 39 | for d in $datadirs; do 40 | mkdir -p $d 41 | chgrp -R www-data $d 42 | find $d -type d | xargs chmod 2775 43 | done 44 | 45 | # Make the database writeable 46 | chgrp www-data data/*.db 47 | chmod 664 data/*.db 48 | 49 | [ -d data/private/etc/ssh ] || mkdir -p data/private/etc/ssh 50 | [ -f data/private/etc/ssh/authorized_keys ] || touch data/private/etc/ssh/authorized_keys 51 | [ -f data/private/etc/ssh/ssh_host_ecdsa_key ] || ssh-keygen -A -f data/private 52 | -------------------------------------------------------------------------------- /scripts/from-line: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" -o -z "$2" ]; then 4 | echo "Usage: $0 line file" 5 | exit 1 6 | fi 7 | 8 | line=$1 9 | shift 10 | 11 | case "$1" in 12 | *.gz) zcat $1 | awk "{if ( FNR >= $line ) "'print $0}' 13 | ;; 14 | *) awk "{if ( FNR >= $line ) "'print $0}' $* 15 | ;; 16 | esac 17 | -------------------------------------------------------------------------------- /scripts/install-custom: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for f in `find download-custom -name '*.txt'`; do 4 | target="`echo $f | sed 's@download-custom@data/download@'`" 5 | rm -f $target 6 | cp $f `dirname $target` 7 | done 8 | 9 | -------------------------------------------------------------------------------- /scripts/ln-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash -f 2 | 3 | mode=755 4 | files= 5 | target= 6 | here=`pwd` 7 | 8 | while [ "$#" != 0 ]; do 9 | case "$1" in 10 | -m) mode="$2" 11 | shift 12 | ;; 13 | *) if [ "$#" = 1 ]; then 14 | target="$1" 15 | done=yes 16 | else 17 | case $1 in 18 | /*) files="$files $1" 19 | ;; 20 | *) files="$files $here/$1" 21 | ;; 22 | esac 23 | fi 24 | shift 25 | ;; 26 | esac 27 | done 28 | 29 | #echo files = $files 30 | #echo target = $target 31 | 32 | if [ -d "$target" ]; then 33 | for f in $files; do 34 | rm -f "$target"/`basename $f` 35 | (cd $target >/dev/null && ln -v -s $f .) 36 | done 37 | else 38 | rm -f $target 39 | ln -v -s $files $target 40 | fi 41 | -------------------------------------------------------------------------------- /scripts/sync-server: -------------------------------------------------------------------------------- 1 | source=ops:/home/swipl/src/plweb 2 | 3 | mkdir -p data/private 4 | mkdir -p data/log 5 | 6 | rsync -auv $source/{annotations,openid,packs,post,reviews,tags}.db data 7 | rsync -auv $source/{passwd,plweb.conf} data/private 8 | rsync -avk $source/download data 9 | -------------------------------------------------------------------------------- /stress/hg: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | valgrind --tool=helgrind --gen-suppressions=all $* 4 | -------------------------------------------------------------------------------- /stress/http_cookie.pl: -------------------------------------------------------------------------------- 1 | /* This file is part of ClioPatria. 2 | 3 | Author: 4 | HTTP: http://e-culture.multimedian.nl/ 5 | GITWEB: http://gollem.science.uva.nl/git/ClioPatria.git 6 | GIT: git://gollem.science.uva.nl/home/git/ClioPatria.git 7 | GIT: http://gollem.science.uva.nl/home/git/ClioPatria.git 8 | Copyright: 2007, E-Culture/MultimediaN 9 | 10 | ClioPatria is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation, either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | ClioPatria is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with ClioPatria. If not, see . 22 | */ 23 | 24 | :- module(http_cookie, 25 | [ http_get/4, % +ClientId, +Request, -Reply, +Options 26 | http_remove_client/1, % +ClientId 27 | http_remove_all_clients/0, 28 | http_current_cookie/4 % ?ClientId, ?Name, ?Value, ?Options 29 | ]). 30 | :- use_module(library('http/http_client')). 31 | :- use_module(library(url)). 32 | :- use_module(library(debug)). 33 | 34 | /** HTTP client cookie handling 35 | 36 | This module defines http_get/4, a wrapper around http_get/3 where we can 37 | define multiple `virtual' clients, each managing a cookie database. 38 | 39 | It was designed to deal with cookie-based session management in the 40 | client, where the same Prolog process is client in multiple concurrent 41 | sessions. 42 | */ 43 | 44 | :- dynamic 45 | client_cookie/4. % Id, Name, Value, Options 46 | 47 | 48 | %% http_get(+ClientId, +Request, -Reply, +Options) is det. 49 | % 50 | % Add cookie handling to http_get/3. ClientId is a ground term 51 | % representing the client. The library takes care of cookies send 52 | % by the server and updates its cookie information. 53 | 54 | http_get(ClientId, Request0, Reply, Options) :- 55 | break_url(Request0, Request), 56 | ( memberchk(reply_header(Header), Options) 57 | -> GetOptions = Options 58 | ; GetOptions = [reply_header(Header)|Options] 59 | ), 60 | add_cookies(ClientId, Request, GetOptions, AllOptions), 61 | http_get(Request, Reply, AllOptions), 62 | update_cookies(ClientId, Request, Header). 63 | 64 | %% break_url(+UrlOrRequest, -Parts) is det. 65 | % 66 | % Break a URL into parts. Returns input if it already contains 67 | % parts. See parse_url/2 for the format. 68 | 69 | break_url(URL, Request) :- 70 | atomic(URL), !, 71 | parse_url(URL, Request). 72 | break_url(Request, Request). 73 | 74 | %% add_cookies(+ClientId, +Request, +Options0, -Options) is det. 75 | % 76 | % Add cookies to an HTTP request. 77 | 78 | add_cookies(ClientId, Request, Options, 79 | [request_header('Cookie'=Cookie)|Options]) :- 80 | request_host(Request, Host), 81 | request_path(Request, Path), 82 | findall(N=V, current_cookie(ClientId, Host, Path, N, V), Cookies), 83 | Cookies \== [], !, 84 | debug(cookie, 'Cookies for ~w at ~w~w: ~p', 85 | [ClientId, Host, Path, Cookies]), 86 | cookie_value(Cookies, Cookie). 87 | add_cookies(_, _, Options, Options). 88 | 89 | request_host(Request, Host) :- 90 | ( memberchk(host(Host), Request) 91 | -> true 92 | ; throw(error(existence_error(parameter, host), _)) 93 | ). 94 | 95 | request_path(Request, Path) :- 96 | ( memberchk(path(Path), Request) 97 | -> true 98 | ; Path = (/) 99 | ). 100 | 101 | %% cookie_value(+NameValueList, -CookieString) is det. 102 | % 103 | % Create a cookie value string with name=value, seperated by ";". 104 | 105 | cookie_value(List, Cookie) :- 106 | with_output_to(string(Cookie), 107 | write_cookies(List)). 108 | 109 | write_cookies([]). 110 | write_cookies([Name=Value|T]) :- 111 | format('~w=~w', [Name, Value]), 112 | ( T == [] 113 | -> true 114 | ; format('; ', []), 115 | write_cookies(T) 116 | ). 117 | 118 | %% update_cookies(+ClientId, +Request, +Header) is det. 119 | % 120 | % Update the client cookie database. Request is the original 121 | % request. Header is the HTTP reply-header. 122 | 123 | update_cookies(ClientId, Request, Header) :- 124 | memberchk(set_cookie(set_cookie(Name, Value, Options)), Header), !, 125 | request_host(Request, Host), 126 | request_path(Request, Path), 127 | with_mutex(http_cookie, 128 | update_cookie(ClientId, Host, Path, Name, Value, Options)). 129 | update_cookies(_, _, _). 130 | 131 | update_cookie(ClientId, Host, Path, Name, Value, Options) :- 132 | remove_cookies(ClientId, Host, Path, Name, Options), 133 | debug(cookie, 'New for ~w: ~w=~p', [ClientId, Name, Value]), 134 | assert(client_cookie(ClientId, Name, Value, [host=Host|Options])). 135 | 136 | %% remove_cookies(+ClientId, +Host, +Path, +Name, +SetOptions) is det. 137 | % 138 | % Remove all cookies that conflict with the new set-cookie 139 | % command. 140 | 141 | remove_cookies(ClientId, Host, Path, Name, SetOptions) :- 142 | ( client_cookie(ClientId, Name, Value, OldOptions), 143 | cookie_match_host(Host, SetOptions, OldOptions), 144 | cookie_match_path(Path, SetOptions, OldOptions), 145 | debug(cookie, 'Del for ~w: ~w=~p', [ClientId, Name, Value]), 146 | retract(client_cookie(ClientId, Name, Value, OldOptions)), 147 | fail 148 | ; true 149 | ). 150 | 151 | cookie_match_host(Host, SetOptions, OldOptions) :- 152 | ( memberchk(domain=Domain, SetOptions) 153 | -> cookie_match_host(Domain, OldOptions) 154 | ; cookie_match_host(Host, OldOptions) 155 | ). 156 | 157 | cookie_match_path(Path, SetOptions, OldOptions) :- 158 | ( memberchk(path=PathO, SetOptions) 159 | -> cookie_match_path(PathO, OldOptions) 160 | ; cookie_match_path(Path, OldOptions) 161 | ). 162 | 163 | %% current_cookie(+ClientId, +Host, +Path, -Name, -Value) is nondet. 164 | % 165 | % Find cookies that match the given request. 166 | 167 | current_cookie(ClientId, Host, Path, Name, Value) :- 168 | client_cookie(ClientId, Name, Value, Options), 169 | cookie_match_host(Host, Options), 170 | cookie_match_path(Path, Options), 171 | cookie_match_expire(Options). 172 | 173 | cookie_match_host(Host, Options) :- 174 | ( memberchk(domain=Domain, Options) 175 | -> downcase_atom(Host, LHost), 176 | downcase_atom(Domain, LDomain), 177 | sub_atom(LHost, _, _, 0, LDomain) % TBD: check '.'? 178 | ; memberchk(host=CHost, Options), 179 | downcase_atom(Host, LHost), 180 | downcase_atom(CHost, LHost) 181 | ). 182 | 183 | cookie_match_path(Path, Options) :- 184 | ( memberchk(path=Root, Options) 185 | -> sub_atom(Path, 0, _, _, Root) % TBD: check '/'? 186 | ; true 187 | ). 188 | 189 | cookie_match_expire(Options) :- 190 | ( memberchk(expire=Expire, Options) 191 | -> get_time(Now), 192 | Now =< Expire 193 | ; true 194 | ). 195 | 196 | %% http_remove_client(+ClientId) is det. 197 | % 198 | % Fake user quitting a browser. Removes all cookies that do 199 | % not have an expire date. 200 | 201 | http_remove_client(ClientId) :- 202 | var(ClientId), !, 203 | throw(error(instantiation_error, _)). 204 | http_remove_client(ClientId) :- 205 | ( client_cookie(ClientId, Name, Value, Options), 206 | \+ memberchk(expire=_, Options), 207 | retract(client_cookie(ClientId, Name, Value, Options)), 208 | fail 209 | ; true 210 | ). 211 | 212 | %% http_remove_all_clients is det. 213 | % 214 | % Simply logout all clients. See http_remove_client/1. 215 | 216 | http_remove_all_clients :- 217 | forall(current_client(ClientId), 218 | http_remove_client(ClientId)). 219 | 220 | %% current_client(?ClientId) is nondet. 221 | % 222 | % True if ClientId is the identifier of a client. 223 | 224 | current_client(ClientId) :- 225 | client_cookie(ClientId, _Name, _Value, _Options). 226 | 227 | %% http_current_cookie(?ClientId, ?Name, ?Value, ?Options) 228 | % 229 | % Query current cookie database 230 | 231 | http_current_cookie(ClientId, Name, Value, Options) :- 232 | client_cookie(ClientId, Name, Value, Options). 233 | -------------------------------------------------------------------------------- /stress/maps.pl: -------------------------------------------------------------------------------- 1 | :- module(procps_maps, 2 | [ maps/2, % +File, -Records 3 | map_size/2, 4 | prot/1, 5 | anon/1 6 | ]). 7 | :- use_module(library(pure_input)). 8 | :- use_module(library(dcg/basics)). 9 | 10 | maps(PID, Records) :- 11 | integer(PID), 12 | !, 13 | format(string(File), "/proc/~w/maps", [PID]), 14 | maps(File, Records). 15 | maps(File, Records) :- 16 | phrase_from_file(maps(Records), File). 17 | 18 | maps([H|T]) --> map(H), !, maps(T). 19 | maps([]) --> []. 20 | 21 | map(map{start:From, 22 | end:To, 23 | access:Access, 24 | flags: f{f1:F1,f2:F2}, 25 | inode: INode, 26 | file: File 27 | }) --> 28 | xinteger(From), "-", xinteger(To), " ", 29 | access(Access), " ", 30 | xinteger(_Unknown), " ", 31 | xinteger(F1), ":", xinteger(F2), " ", 32 | integer(INode), 33 | ( {INode == 0} 34 | -> ( blanks_to_nl 35 | -> {File = (-)} 36 | ; blanks, 37 | "[", string(Res), "]", blanks_to_nl 38 | -> { atom_codes(File, Res) } 39 | ) 40 | ; blanks, 41 | string(S), 42 | blanks_to_nl 43 | -> { atom_codes(File, S) } 44 | ). 45 | 46 | access([R,W,X,P]) --> r(R), w(W), x(X), p(P). 47 | 48 | r(r) --> "r". 49 | r(-) --> "-". 50 | w(w) --> "w". 51 | w(-) --> "-". 52 | x(x) --> "x". 53 | x(-) --> "-". 54 | p(p) --> "p". 55 | p(s) --> "s". 56 | 57 | 58 | map_size(Map, Size) :- 59 | Size is Map.end - Map.start. 60 | 61 | prot(Map) :- 62 | Map.access = [-,-,-,p]. 63 | 64 | anon(Map) :- 65 | Map.access = [r,w,-,p], 66 | Map.inode == 0. 67 | -------------------------------------------------------------------------------- /stress/stress.pl: -------------------------------------------------------------------------------- 1 | :- use_module(replay). 2 | 3 | r(C) :- 4 | debug(replay), 5 | http_replay('httpd-ec.log', 6 | [host(localhost), port(3040), concurrent(C)]). 7 | -------------------------------------------------------------------------------- /sw/README: -------------------------------------------------------------------------------- 1 | Semantic modelling of the SWI-Prolog project 2 | 3 | * EvoOnt --- http://www.ifi.uzh.ch/ddis/evo/ 4 | * Baetle --- http://code.google.com/p/baetle/ 5 | * Mandriva Nepomuk --- http://nepomuk.linbox.org/ 6 | -------------------------------------------------------------------------------- /systemd/plweb.service: -------------------------------------------------------------------------------- 1 | # swi-prolog - SWI-Prolog website server 2 | # 3 | # The SWI-Prolog website server 4 | 5 | [Unit] 6 | Description=SWI-Prolog website 7 | 8 | [Service] 9 | UMask=022 10 | Environment=LANG=en_US.utf8 11 | Environment=LD_LIBRARY_PATH=/usr/lib/jvm/default-java/jre/lib/amd64/server:/usr/lib/jvm/default-java/jre/lib/amd64:/usr/lib/jvm/default-java/lib 12 | Environment=HOME=/var/www 13 | Restart=on-failure 14 | ExecReload=/bin/kill -HUP $MAINPID 15 | StartLimitInterval=60 16 | StartLimitBurst=5 17 | WorkingDirectory=/home/swipl/src/plweb 18 | ExecStart=/home/swipl/bin/swipl daemon.pl --no-fork --port=80 --user=www-data 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /test_plweb.pl: -------------------------------------------------------------------------------- 1 | :- module(test_plweb, 2 | [ test_links/0 3 | ]). 4 | 5 | :- use_module(library(ansi_term)). 6 | :- use_module(library(apply)). 7 | :- use_module(library(error)). 8 | :- use_module(library(http/http_client)). 9 | :- use_module(library(http/http_ssl_plugin)). % HTTPS support. 10 | :- use_module(library(uri)). 11 | 12 | :- use_module(page). 13 | 14 | /** Test various aspects of plweb 15 | 16 | @author Wouter Beek 17 | @version 2016/05/02 18 | */ 19 | 20 | :- meta_predicate 21 | verbose(0, +, +). 22 | 23 | 24 | 25 | 26 | 27 | test_links :- 28 | clause(plweb_page:menu(_,L), _), 29 | maplist(test_links, L). 30 | 31 | 32 | test_links(_=L) :- 33 | is_list(L), !, 34 | maplist(test_links, L). 35 | test_links(_=Uri) :- 36 | is_link(Uri), !, 37 | verbose(test_link(Uri), "Checking URL ‘~a’", [Uri]). 38 | test_links(_). 39 | 40 | 41 | test_link(Uri) :- 42 | http_get(Uri, _, [status_code(Code)]), 43 | (between(200, 299, Code) -> true ; throw(error(Code))). 44 | 45 | 46 | 47 | 48 | 49 | % HELPERS % 50 | 51 | %! is_link(@Term) is semidet. 52 | 53 | is_link(Uri) :- 54 | atom(Uri), 55 | uri_components(Uri, uri_components(Scheme,Auth,_,_,_)), 56 | maplist(atom, [Scheme,Auth]). 57 | 58 | 59 | 60 | %! verbose(:Goal_0, +Format, +Args) is det. 61 | 62 | verbose(Goal_0, Format, Args) :- 63 | get_time(Start), 64 | format(Format, Args), 65 | ( catch(Goal_0, E, true) 66 | -> ( var(E) 67 | -> get_time(End), 68 | Delta is End - Start, 69 | ansi_format([fg(green)], "~`.t success (~2f sec.)~72|", [Delta]) 70 | ; message_to_string(E, S), 71 | ansi_format([fg(red)], "~`.t ERROR: ~w~72|", [S]) 72 | ) 73 | ; ansi_format([fg(red)], "~`.t ERROR: (failed)~72|", []) 74 | ), 75 | nl. 76 | -------------------------------------------------------------------------------- /test_recaptcha.pl: -------------------------------------------------------------------------------- 1 | :- module(test_recaptcha, 2 | [ 3 | ]). 4 | :- use_module(library(http/http_dispatch)). 5 | :- use_module(library(http/http_parameters)). 6 | :- use_module(library(http/html_write)). 7 | :- use_module(library(http/recaptcha)). 8 | 9 | :- http_handler(root(test/recaptcha), captcha_form, []). 10 | :- http_handler(root(test/callback), captcha_callback, []). 11 | 12 | captcha_form(_Request) :- 13 | reply_html_page( 14 | plain, 15 | title('Captcha test'), 16 | \form). 17 | 18 | form --> 19 | { http_link_to_id(captcha_callback, [], HREF) 20 | }, 21 | html(h1('Test page for recaptcha configuration')), 22 | html(form([method('POST'), action(HREF)], 23 | [ \recaptcha([]), 24 | input([name(name)]), 25 | input(type(submit)) 26 | ])). 27 | 28 | captcha_callback(Request) :- 29 | recaptcha_parameters(RecapthaParams), 30 | http_parameters(Request, 31 | RecapthaParams, 32 | [form_data(Form)]), 33 | format('Content-type: text/plain\n\n'), 34 | print_term(Form, [output(current_output)]), 35 | ( recaptcha_verify(Request, RecapthaParams) 36 | -> format('Welcome human!~n') 37 | ; format('Go away, alien!~n') 38 | ). 39 | -------------------------------------------------------------------------------- /tests.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2010, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(http_test_server, []). 31 | :- use_module(library(http/http_dispatch)). 32 | 33 | :- http_handler(root('Tests/chunked/data'), send_chunked, []). 34 | 35 | %% send_chunked(+Request) is det. 36 | % 37 | % HTTP handler that sends a long string in chunked mode. Used to 38 | % test the client. 39 | 40 | send_chunked(_Request) :- 41 | chunked_data(String), 42 | format('Content-type: text/plain; charset=UTF-8~n'), 43 | format('Transfer-encoding: chunked~n~n'), 44 | format('~s', [String]), 45 | flush_output, 46 | format('~s', [String]). 47 | 48 | chunked_data(S) :- 49 | findall(C, 50 | ( between(1, 1000, X), 51 | C is "a" + X mod 26 52 | ), S). 53 | 54 | -------------------------------------------------------------------------------- /update.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2009, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(web_update, 31 | [ db_sync_thread/0, 32 | db_sync_thread/1 % +Time 33 | ]). 34 | :- use_module(library(http/http_dispatch)). 35 | :- use_module(library(http/http_authenticate)). 36 | :- use_module(library(http/html_write)). 37 | :- use_module(library(readutil)). 38 | :- use_module(library(process)). 39 | :- use_module(library(persistency)). 40 | :- use_module(library(socket)). 41 | 42 | :- use_module(parms). 43 | 44 | :- http_handler(root(update), update, []). 45 | 46 | :- meta_predicate 47 | collect_messages(0, -). 48 | 49 | %% update(+Request) 50 | % 51 | % HTTP Handler for /update. Performs a GIT pull and a Prolog 52 | % make/0. 53 | 54 | update(Request) :- 55 | ( http_authenticate(basic(private(passwd)), Request, _User) 56 | -> true 57 | ; throw(http_reply(authorise(basic, 'Admin user'))) 58 | ), 59 | reply_html_page(title('Server update'), 60 | [ h1('Server update'), 61 | hr([]), 62 | h2('GIT'), 63 | \git_update, 64 | h2('make'), 65 | \make, 66 | h2('Persistent file sync'), 67 | \db_sync 68 | ]). 69 | 70 | 71 | %% git_update// 72 | % 73 | % Run =|git update|=, collecting the output 74 | 75 | git_update --> 76 | { process_create(path(git), [pull], 77 | [ stdout(pipe(Out)), 78 | stderr(pipe(Error)) 79 | ]), 80 | read_stream_to_codes(Out, OutCodes), 81 | read_stream_to_codes(Error, ErrorCodes), 82 | close(Out), 83 | close(Error) 84 | }, 85 | output('', informational, OutCodes), 86 | output('', error, ErrorCodes). 87 | 88 | output(_Prefix, _Class, Codes) --> 89 | { Codes == [] }, !. 90 | output(Prefix, Class, Codes) --> 91 | html(pre(class(Class), 92 | [ Prefix, '~s'-[Codes] ])). 93 | 94 | %% make// 95 | % 96 | % Run make, collecting output 97 | 98 | make --> 99 | { collect_messages(make, Messages) 100 | }, 101 | messages(Messages). 102 | 103 | 104 | :- thread_local 105 | message/2. 106 | 107 | collect_messages(Goal, Messages) :- 108 | asserta((user:thread_message_hook(_Term, Level, Lines) :- 109 | assert(message(Level, Lines))), Ref), 110 | call_cleanup(Goal, erase(Ref)), 111 | findall(Level-Lines, retract(message(Level, Lines)), Messages). 112 | 113 | messages([]) --> 114 | []. 115 | messages([H|T]) --> 116 | message(H), 117 | messages(T). 118 | 119 | message(Level-Lines) --> 120 | html(div(class(Level), \html_message_lines(Lines))). 121 | 122 | html_message_lines([]) --> 123 | []. 124 | html_message_lines([nl|T]) --> !, 125 | html([br([])]), 126 | html_message_lines(T). 127 | html_message_lines([flush]) --> 128 | []. 129 | html_message_lines([Fmt-Args|T]) --> !, 130 | { format(string(S), Fmt, Args) 131 | }, 132 | html([S]), 133 | html_message_lines(T). 134 | html_message_lines([Fmt|T]) --> !, 135 | { format(string(S), Fmt, []) 136 | }, 137 | html([S]), 138 | html_message_lines(T). 139 | 140 | db_sync --> 141 | { db_sync_all(reload) }. 142 | 143 | db_sync_thread :- 144 | gethostname(HostName), 145 | server(slave, _, HostName), !, 146 | db_sync_thread(3600). 147 | db_sync_thread. 148 | 149 | %% db_sync_thread(+Time) 150 | % 151 | % Sync the persistency database every Time seconds. 152 | 153 | db_sync_thread(Time) :- 154 | catch(thread_create(sync_loop(Time), _, 155 | [ alias('__sync_db') ]), 156 | E, print_message(warning, E)). 157 | 158 | sync_loop(Time) :- 159 | repeat, 160 | sleep(Time), 161 | catch(db_sync_all(reload), 162 | E, print_message(warning, E)), 163 | fail. 164 | 165 | -------------------------------------------------------------------------------- /watchdog.pl: -------------------------------------------------------------------------------- 1 | /* Part of SWI-Prolog 2 | 3 | Author: Jan Wielemaker 4 | E-mail: J.Wielemaker@cs.vu.nl 5 | WWW: http://www.swi-prolog.org 6 | Copyright (C): 2020, VU University Amsterdam 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 | 22 | As a special exception, if you link this library with other files, 23 | compiled with a Free Software compiler, to produce an executable, this 24 | library does not by itself cause the resulting executable to be covered 25 | by the GNU General Public License. This exception does not however 26 | invalidate any other reasons why the executable file might be covered by 27 | the GNU General Public License. 28 | */ 29 | 30 | :- module(http_watchdog, []). 31 | :- use_module(library(http/http_exception), []). 32 | :- autoload(library(broadcast), [broadcast/1]). 33 | :- autoload(library(aggregate), [aggregate_all/3]). 34 | 35 | :- multifile http:map_exception_to_http_status_hook/4. 36 | 37 | http:map_exception_to_http_status_hook(error(resource_error(Which),_), _, _, _) :- 38 | outof(Which), 39 | fail. 40 | 41 | :- dynamic 42 | outof/2. 43 | 44 | outof(Which) :- 45 | get_time(Now), 46 | Del is Now - 3600, 47 | forall(( outof(Which, Then), 48 | Then < Del 49 | ), 50 | retractall(outof(Which, Then))), 51 | aggregate_all(count, outof(Which, _), Count), 52 | Count > 3, 53 | !, 54 | broadcast(http(watchdog(Which))), 55 | halt(1). 56 | outof(Which) :- 57 | get_time(Now), 58 | asserta(outof(Which, Now)). 59 | 60 | --------------------------------------------------------------------------------