Branch: development (switch to
stable),
SWI-Prolog Changelog from version 10.1.11 to 10.1.12
[Jul 19 2026]
- ENHANCED: win_process_modules/1: use disk case Used to downcase the
file names.
- FIXED: Drive letters on Windows are now normalized as upper case.
As we now normalize on the disk case and (thus) paths are typically
mixed case, we better use the Windows uppercase default for drive
letters as well. Reported by Paulo Moura.
- DOC: Describe the initial view of the Prolog Navigator Co-Authored-By:
Claude Opus 4.8 <noreply@anthropic.com> Claude-Session:
https://claude.ai/code/session_013EGUkPkGnzYjZUQtYF3tH4
[Jul 18 2026]
- ADDED: Process -D flags that the boot sequence depends on before
starting Prolog apply_defines/0 applies -D options after the boot
files have been loaded, which is too late for a flag the boot sequence
itself depends on. Prolog flags listed in early_defines[] are now
picked from the raw argv by
setEarlyDefines(), which runs before
initPaths(), and are left out of the list handed to apply_defines/0.
Add a flag to that table to handle it early; -Dhome=Dir is a candidate.
- ENHANCED: Windows: normalise file names to their on-disk case,
not lower case On case-preserving file systems (the Windows default)
SWI-Prolog now normalises a file name to the case in which it is stored
on disk rather than down-casing it. So absolute_file_name/2 and
working_directory/2 return e.g. 'c:/Users/Paulo Moura/...' instead
of 'c:/users/paulo moura/...'. The result is still a single normal
form per physical file, which source tracking relies on, but it is
readable and matches what the user sees in the file manager. Set the
flag file_name_case_handling to case_insensitive to restore the old
down-casing behaviour.
[Jul 17 2026]
- FIXED: file_name_case_handling flag reported a value inconsistent
with its setting
currentFileNameCaseHandling() did not match
setFileNameCaseHandling(): on a case-preserving system (the Windows
default) it reported case_sensitive, and it could never report
case_preserving. Rewrite the getter to mirror the two flag bits the
setter uses.
- FIXED: #1511 Failure to normalize bigint to tagged int on Windows.
This happened when using GMP for integers that do not fit in a long
but do fit in a tagged integer.
- FIXED: crmath build failed on old compilers not knowing
-march=x86-64-v3 Upstream core-math adds -march=x86-64-v3
unconditionally on x86_64. This microarchitecture level is only
known to gcc >= 11 and clang >= 12; older compilers error out.
Drop the flag in our crmath.cmake layer when
check_c_compiler_flag()
reports it is unsupported. It is only a tuning hint, so dropping it
loses AVX2 tuning but not correctness.
[Jul 16 2026]
- FIXED: predicate_options drift check failed where foreign
source locations are unavailable On platforms such as WASM
predicate_location/2 does not resolve foreign (C) predicates to their
source file, so the drift check scanned no C files and reported every
option array as stale. Gate the check on a drift_testable/0 probe
that verifies a foreign predicate can be located, skipping the drift
test otherwise.
- FIXED: predicate_options drift check failed on single-threaded builds
The thread option declarations in library(dialect/swi/syspred_options)
referred to predicates that do not exist without O_PLMT, so the
predicate_options drift test flagged their (unreachable) C option
arrays as stale. Guard the thread-only declarations and the matching
c_option_array/2 mappings with :-
if(current_prolog_flag(threads,
true)).
- FIXED:
setCloseOnExec() silently failed on missing fd / failing fcntl
set_stream/2 close_on_exec now raises a permission_error when the
stream has no OS file descriptor and a system_error (preserving errno)
when fcntl()/SetHandleInformation() fails, instead of failing silently.
- FIXED: set_stream/2 close_on_exec silently succeeded on unsupported
platforms
setCloseOnExec() returned -1 where close-on-exec is
unavailable, which set_stream/2 treated as success. Raise a
not_implemented(feature, close_on_exec) exception instead and return
bool.
- CLEANUP: remove unused
setBoolean() setBoolean() had no callers
anywhere in the tree.
- CLEANUP: int -> bool for boolean by-value function parameters Type
the boolean flag parameters of
setCloseOnExec(), declareModule()
(allow_newfile), PopTty() (do_free), lookupSourceFile()/get_mutex()/
intern_indirect() (create) and trie_intern_indirect() (add) as bool.
Callers already pass true/false; the $declare_module local now reads
with PL_get_stdbool_ex().
- CLEANUP: local int -> bool for booleans read with PL_get_stdbool_ex()
Convert local int helpers that only hold a two-valued boolean to
C11 bool, reading them with the new PL_get_stdbool_ex() instead of
PL_get_bool_ex().
- ADDED: C API support for C11
bool type. This adds
PL_get_stdbool(), PL_get_stdbool_ex() and adds the type OPT_STDBOOL
to PL_scan_options.
[Jul 15 2026]
- ENHANCED: Support INRIAs crmath library for correct rounding floats.
This patch allows configuration using
cmake -DUSE_CRMATH=ON.
This downloads and builds the crmath.a library and replaces the
explicit up and downward rounding in src/pl-arith.c to achieve
a safe interval with correct functions.
- CLEANUP: use $option/2 instead of raw memberchk/2 for option access
in boot Replace memberchk/2 on option lists with the bootstrap
$option/2, which also handles dict options. Behaviour is unchanged
for classical option lists. Affects load option handling in init.pl,
the answer_write_options queries in toplevel.pl and the version tag
in messages.pl.
- ADDED: predicate_options drift check and order-sensitive-access
linter - check_raw_option_access/0,1: an opt-in linter (not part of
check/0) that
reports order-sensitive option handling: raw memberchk/2 on option
lists
(Category A) and [Opt|Tail] overrides passed to rightmost-wins
builtins
(Category B). The clause analysis is strengthened accordingly:
descend
into (If->Then)/(If*->Then), propagate option-list-ness through
append/3
and merge_options/3, and seed the declared option arguments of the
head. - FIXED: check_predicate_option/3 left a choicepoint despite
being det. - A drift check (tests/library/test_predicate_options.pl)
verifying that
every option in a core C PL_option_t[] array is declared, wired
into the
swipl:library ctest.
- ENHANCED: predicate_options declarations for built-in option predicates
Mirror the PL_option_t[] arrays of more built-in option predicates in
library(dialect/swi/syspred_options): thread_wait/2, thread_update/2,
transaction/2, engine_create/4 and the low-level zip predicates.
Also complete options that were already scanned by C but not declared,
found by the new drift check: open/4 unicode_atoms; read_term/3
var_prefix, back_quotes, quasi_quotations, dotlists, unicode_atoms;
read_clause/3 unicode_atoms; write_term/3 dotlists, portrayed,
back_quotes, integer_format, float_format; create_prolog_flag/3 local.
[Jul 14 2026]
- DOC: remove unused code that's the same as the documentation
- ADDED: #667 phrase_from_file/3:
as(Type) argument,
phrase_from_stream/3, stream_to_lazy_codes/2 and stream_to_lazy_chars/2
Allow library(pure_input) to work with chars lists as well as
codes lists.
- CLEANUP: drop
compileClauseGuarded() and its C_STACK_OVERFLOW_GUARDED
wrapper compileBody(), analyseVariables2(), setVars(),
compileArgument() and compileArithArgument() no longer recurse on the C
stack, so the SIGSEGV-catching guard around the compiler is redundant.
Fold compileClauseGuarded() back into compileClause() and remove the
cleanupCompile() helper the guard used.
- ENHANCED:
compileArgument() iterative via segstack Fold the recursive
argument walk into an explicit state machine over a segstack of
ca_frame records. Frame kinds: CA_MIDDLE tracks progress through
args[0..N-2] of a compound (advance on resume, or transition to
last-arg handling when done); CA_LAST_POP emits the closing H_POP/B_POP
after the non-right last-arg sub-compilation. The list right-chain
tail-jump (was `goto right_recursion`) becomes a plain `goto next_arg`.
- ENHANCED:
compileArithArgument() iterative via segstack Fold the
recursive arithmetic-argument walk into a state machine over a
segstack of aa_frame records. Each function application pushes a
frame capturing the operator's fdef/index/arity and how many operands
are still pending; operands are compiled right-to-left just like
the original loop; the closing A_ADD / A_MUL / A_FUNCn is emitted
when the last operand completes. Push failure calls outOfCore(),
matching the setVars() pattern.
[Jul 13 2026]
- ENHANCED:
setVars() iterative via segstack Walk the term with
an explicit segstack of pending arg pointers rather than C-stack
recursion. compileBody() calls setVars() on both branches of every
disjunction, so deeply nested (A;B;C;...) clauses recursed setVars()
to the same depth as the term and overflowed the C stack. Segstack
push failure calls outOfCore(), matching the pattern used elsewhere
for void-returning term walkers.
- ENHANCED:
analyseVariables2() iterative via segstack Fold the recursive
shape into an explicit state machine over a segstack of av_frame
records. Frame kinds match the recursive call-sites: AV_ARG_LOOP
for the default N-arg descent, AV_SUBCLAUSE_LOOP for the islocal +
subclausearg loop, AV_SEMI_AFTER_LEFT / _AFTER_RIGHT for the two-branch
semicolon analysis with branch_vars save/restore, and AV_NOT_AFTER
for the \+ singleton pass. Depth is tracked in a per-frame field so
cycle checks fire on the same paths as before. Segstack allocation
failure returns MEMORY_OVERFLOW, which analyse_variables() already
turns into an ERR_NOMEM resource_error.
- ENHANCED:
compileBody() iterative via segstack Fold the recursive
shape into an explicit state machine over a segstack of resume frames.
Each control functor pushes a frame capturing its phase-specific state
(saved cut, VarTables, C_JMP/C_OR patch offsets, target_module) and the
post-processing to run when the sub-compilation returns. Push failure
returns MEMORY_OVERFLOW, which compileClauseGuarded() converts to a
resource_error after resetVars() has restored setVarInfo() tags on
the stack; the unwind restores ci->colon_context and ci->at_context
so the caller sees consistent state.
[Jul 14 2026]
- FIXED: crash on the debugger "depth" command without argument The
default depth was passed as a plain C integer rather than a tagged
Prolog word, creating a bogus float term that crashes clause indexing.
- FIXED: absolute_file_name/3 to enumerate all solutions while loading
a file If a file is being loaded, '$chk_file'/5 searches relative to
the directory of the file being loaded. This search was guarded by
an if-then-else to implement the deprecated fallback to the working
directory, killing the choice points of the file candidates. As a
result,
solutions(all) only produced the first candidate. Now uses
a soft cut.
[Jul 13 2026]
- TEST: Randomised property test for dif/2 Generate a random ground
term T with library(random_terms), produce a generalisation G by
replacing a random subset of subterms with fresh variables (recorded
as
V=Value), and optionally alias two of those variables so G also
carries a sharing constraint. Post dif(G, T), apply a random plan
of matching and/or clash bindings, then require the observed outcome
to match the prediction:
- ENHANCED: dif/2 canonical propagation via unifiable/3 on accumulated
lists Reimplement the pending-unifier simplification along the lines
flagged by the @tbd in the previous dif_c_c_l/3 comment: instead of
dedup-ing Var=Val pairs list-wise, split the pending set into left-
and right-hand-side lists and call unifiable/3 to compute a most
general unifier for the whole set at once.
- FIXED: #919 dif/2 loops on cyclic terms The pending unifier stored
on an OrNode could contain both
X=Y and Y=X for the same pair of
unbound variables, because unifiable/3 sometimes returns a chained
form (e.g. [E=[], C=E]) alongside a pre-existing reverse entry
(E=C). simplify_ornode_/3 sorts by the first argument only, so the
reverse-duplicate never becomes adjacent and its dedup clause never
fires, driving the algorithm into an infinite oscillation.
[Jul 12 2026]
- FIXED: Too strict checking in
restoreStandardStream() Introduced in
9d40a82ad7fbf8d4f9aa24f72080036db0936705
- FIXED: WASM build was broken by
3d79bc7caee4c08e941496dfb5f2c70f9b8224d3 This commit modified how
the build home is detected, breaking teh WASM build.
[Jul 11 2026]
- FIXED: single-threaded build of push_prolog_flag/2 and
pop_prolog_flag/1
copy_prolog_flag() was defined only under O_PLMT
but is called unconditionally by push_prolog_flag/2. Move the guard
so it is compiled in all configurations. Also move the now-unused
in_local tracking in pop_prolog_flag/1 inside the O_PLMT block.
- ENHANCED: use push_prolog_flag/2 for xref-flag scoping
boot/autoload.pl exports/5, library/prolog_xref.pl with_xref/1 and
library/prolog_source.pl read_directives. Drops the fallback for
"xref flag might not exist" and the state term threading OldXref,
both handled by push/pop.
- ADDED: push_prolog_flag/2 and pop_prolog_flag/1 Thread-local, nestable
save/restore of Prolog flag values. Both go through the existing
set_prolog_flag/2 code path, so type checks, read-only checks,
thread-local COW, and side effects (character escapes, module bits,
boolean fast mask, ...) stay consistent. The push stack lives
inside the prolog_flag struct as a linked list of snapshot frames.
A FF_PUSH_ABSENT sentinel records that the flag did not exist at push
time; pop then removes the local entry.
[Jul 10 2026]
- FIXED: PL_get_term_value: classify numeric terms; raise on double
overflow PL_get_term_value() previously reported PL_INTEGER for
rationals and bignums but left val.i as garbage (get_int64 silently
misread MPQ storage as MPZ). Now it dispatches via
get_number():
- DOC: CODE_SIGNING: add signtool/osslsigncode verification commands
Document the exact commands users can run on Windows, Linux and macOS
to verify the Authenticode signature and trusted timestamp on the
Windows installer.
- ADDED: library(random_terms) — random Prolog term generator
Generates terms suitable for stress-testing predicates that handle
arbitrary structures: variant checks, factorisation, unification,
copy vs share, etc. The generator aggressively mixes:
[Jul 6 2026]
- FIXED: generate INDEX.pl for library files referenced with a
source path prefix
add_index() skipped any file whose source path
contained a '/', which was intended to exclude files destined for
library subdirectories. Those are already dispatched through their
own PL_LIB_SUBDIR iterations, so the guard only did harm: it also
rejected a top-level PL_LIBS file whose source path happens to include
a directory component, e.g. swipy's janus/janus.pl. As a result
home/library/ext/swipy had no INDEX.pl and the janus predicates were
not autoloadable via the index.
- FIXED: macOS (MacPorts) build: include <xlocale.h> for
newlocale()/uselocale() On the MacPorts CommandLineTools MacOSX14.sdk,
<locale.h> only declares locale_t/newlocale()/uselocale()/freelocale()
when USE_EXTENDED_LOCALES is set (DARWIN_C_LEVEL >=
DARWIN_C_FULL). There the feature level ends up below FULL,
so the declarations are hidden even though check_function_exists()
sets HAVE_NEWLOCALE/HAVE_USELOCALE (the symbols do live in libc).
This broke the fill_locale_from_name() code added for #1093.
Package RDF
[Jul 15 2026]
- CLEANUP: use option/2 for embedded option in rdf.pl Replace memberchk/2
on the option list with option/2, which also accepts Name=Value and
dict options. Behaviour is unchanged for classical option lists.
Package archive
[Jul 16 2026]
- CLEANUP: archive: filters/formats list options + PL_scan_options()
Repeating the filter/format option to support multiple filters/formats
forced a hand-rolled option loop that accumulates into a mask.
Introduce
filters(List) and formats(List) options: archive_open/4 now
folds any (possibly repeated) filter/compression/format options into
these lists in Prolog, raising a deprecation warning for the repeated
singular form.
[Jul 15 2026]
- FIXED: archive_open/4 could leak the file stream on
close_parent(false)
When archive_open/4 is given a file (not a stream), it opens
the file itself and must close it together with the archive.
It prepended close_parent(true), but archive_open_stream/4 resolves
options rightmost-wins, so a caller-supplied close_parent(false)
defeated the override and leaked the stream. Use merge_options/3 so
close_parent(true) takes precedence regardless of order.
Package clib
[Jul 16 2026]
- ENHANCED: process_create/3 uses PL_scan_options() Replace the
hand-rolled option loop in
parse_options() with PL_scan_options().
detached and window scan as OPT_BOOL and priority as OPT_INT straight
into the p_options struct; the remaining options are OPT_TERM so the
nested values (pipe(Stream), std, null and the env list) still reach
get_stream()/parse_environment() unchanged. Drops the now-unused
option-name atoms.
- CLEANUP: use PL_scan_options() in alarm/4, ossp_uuid/2 and
process_wait/3 Convert the hand-rolled option loops:
- time.c alarm4_gen: remove/install bool flags (removes the now-unused
local pl_get_bool_ex helper).
- uuid.c pl_uuid: version/format and the dns/url/oid/x500 namespace
options, scanned as terms for their validation.
- process.c process_wait: timeout/release, using OPT_UNKNOWN_ERROR
to keep raising domain_error on unknown options.
[Jul 15 2026]
- CLEANUP: modernise clib error handling to use the public C API -
Replace context-free
pl_error(NULL, 0, NULL, ERR_TYPE|ERR_ARGTYPE|
ERR_DOMAIN|ERR_EXISTENCE|ERR_RESOURCE, ...) calls with the equivalent
PL_type_error/PL_domain_error/PL_existence_error/PL_resource_error,
which report the actual calling predicate as the error context.
pl_error() is kept for errno mapping (ERR_ERRNO), ERR_NOTIMPLEMENTED,
ERR_SYNTAX and calls carrying an explicit predicate/message context.
break_form_argument() now raises errors directly (PL_resource_error /
PL_syntax_error) and returns bool instead of ad-hoc ERROR_* integer
codes that pl_cgi_get_form() had to map back to exceptions. Removes
the ERROR_* defines from form.h. - Type the form-decoding callback
(add_to_form/break_form_argument) as
bool, matching mp_add_to_form/break_multipart. - Simplify
unify_number() with PL_put_term_from_chars(), which takes a
length so the value no longer needs copying into a NUL-terminated
buffer.
- CLEANUP: use PL_scan_options() in open_hash_stream/3 Replace the
hand-rolled option loop with PL_scan_options(). The algorithm option
is scanned as a term and passed straight to get_hash_algorithm/2.
Behaviour is unchanged; unknown options follow the unknown_option flag
(ignored by default).
- CLEANUP: use PL_scan_options() in udp_receive/4 and udp_send/4 Replace
the hand-rolled option loops with PL_scan_options(), matching the
existing
pl_host_address() usage in this file. as, encoding and
max_message_size are scanned as terms and passed straight to get_as/2,
get_representation/2 and the range check. Unknown options follow
the unknown_option flag (ignored by default).
- CLEANUP: use PL_scan_options() in open_memory_file/4 Replace the
hand-rolled option loop with PL_scan_options(). The encoding
option is scanned as a term and passed straight to get_encoding/2.
Behaviour is unchanged; unknown options follow the unknown_option flag
(ignored by default).
- CLEANUP: use PL_scan_options() in sha_hash/3 and friends Replace the
hand-rolled option loop with PL_scan_options(). algorithm and encoding
are scanned as terms; the algorithm term is reused directly for the
HMAC-SHA domain_error. Behaviour is unchanged; unknown options follow
the unknown_option flag (ignored by default).
- CLEANUP: use PL_scan_options() in md5_hash/3 Replace the hand-rolled
option loop with PL_scan_options() using the string-name PL_OPTION()
form. The encoding option is scanned as a term so its value can be
validated and reported in a domain_error without reconstructing it.
Behaviour is unchanged; unknown options follow the unknown_option flag
(ignored by default).
- CLEANUP: use option/2 for option access in socket.pl and
prolog_server.pl Replace raw memberchk/2 and a member/2 *-> soft-cut
idiom on option lists with option/2, which also accepts Name=Value
and dict options. Behaviour is unchanged for classical option lists.
Package cpp
[Jul 14 2026]
- DOC: remove unused code that's the same as the documentation
[Jul 11 2026]
Package cql
[Jul 15 2026]
- CLEANUP: use option/2 for option access in sql_write.pl Replace the
~44 raw memberchk/2 calls on the Options list with option/2, which
also accepts Name=Value and dict options. Behaviour is unchanged
for classical option lists.
Package http
[Jul 16 2026]
- CLEANUP: use PL_scan_options() in stream_range_open/3 and cgi_open/4
Convert the hand-rolled option loops:
- stream_range.c: size (nonneg) and onclose (a recorded
module-qualified callback), scanned as terms.
- cgi_stream.c: the request option, using OPT_UNKNOWN_ERROR to keep
erroring on unknown options.
[Jul 15 2026]
[Jul 12 2026]
- TEST: proxy: fix startup race between server thread and control assert
start_http_proxy/1 and start_socks_server/1 asserted the control record
after thread_create/3. If the newly spawned accept-loop reached
its first
http_proxy_control(_, Self, _) (or \+ socks_control(_,
Self, _)) check before the assert committed, the guard flipped, the
loop exited, and setup_call_cleanup/3 closed the listening socket
— after which the test dialing the proxy port saw ECONNREFUSED.
Manifested as sporadic "All TCP connections via HTTP" failures on CI.
[Jul 11 2026]
Package json
[Jul 15 2026]
- CLEANUP: use option/2 for json_object option in http_json.pl Replace
memberchk/2 on the option list with option/2, which also accepts
Name=Value and dict options. Behaviour is unchanged for classical
option lists.
Package libedit
[Jul 11 2026]
Package mqi
[Jul 15 2026]
- CLEANUP: use option/2 for unix_domain_socket check in mqi.pl Replace
memberchk/2 on the option list with option/2, consistent with the
surrounding option/3 calls. Behaviour is unchanged for classical
option lists.
[Jul 11 2026]
Package odbc
[Jul 16 2026]
- DOC: odbc_query/4 options cannot be given as a dict Document that
the statement option list is handled by a dedicated routine (needed
for the two-argument findall/2 option) and therefore must be a list,
not a dict.
- DOC: note
set_statement_options() does not use PL_scan_options()
Explain that the option loop is hand-written because findall(Template,
Row) is an arity-2 option, and that these options therefore cannot
be given as a dict.
- ENHANCED: odbc_connect/3 uses PL_scan_options() Replace the hand-rolled
option loop in
pl_odbc_connect() with PL_scan_options(). A value-level
get_typed_ex() (and matching get_*_ex() macros) mirror the existing
get_*_arg_ex() helpers so the scanned option values keep their
original type checks. The five after-connect options (auto_commit,
null, access_mode, cursor_type, wide_column_threshold) are rebuilt
with PL_cons_functor() and handed to odbc_set_connection() as before.
Package pcre
[Jul 15 2026]
- CLEANUP: use option/2 for capture_type option in pcre.pl Replace
memberchk/2 on the option list with option/2, which also accepts
Name=Value and dict options. Behaviour is unchanged for classical
option lists.
Package pengines
[Jul 15 2026]
- CLEANUP: use option/2 for answer option in pengines.pl Replace
memberchk/2 on the create-event option list with option/2, which
also accepts Name=Value and dict options. Behaviour is unchanged
for classical option lists.
Package pldoc
[Jul 15 2026]
- CLEANUP: use option/2 for module/public options in doc_html.pl Replace
memberchk/2 on the option list with option/2, which also accepts
Name=Value and dict options. Behaviour is unchanged for classical
option lists.
[Jul 14 2026]
Package plunit
[Jul 15 2026]
- CLEANUP: use option/2 for fixme option in plunit.pl Replace memberchk/2
on the test option list with option/2, which also accepts Name=Value
and dict options. Behaviour is unchanged for classical option lists.
[Jul 11 2026]
Package semweb
[Jul 16 2026]
- CLEANUP: use PL_scan_options() in rdf_transaction/3 and the turtle
parser Convert the hand-rolled option loops:
- rdf_db.c rdf_transaction: the snapshot option.
- turtle.c create_turtle_parser: base_uri, anon_prefix, graph, format
and on_error, scanned as terms and applied to the parser state.
- turtle.c turtle_parse: parse and count.
[Jul 15 2026]
- CLEANUP: use option/2 for option access in rdf_db.pl Replace
raw memberchk/2 on option lists (graph, db, document_language,
convert_typed_literal) with option/2, which also accepts Name=Value
and dict options. Behaviour is unchanged for classical option lists.
[Jul 11 2026]
Package sgml
[Jul 16 2026]
- DOC: sgml_parse/2 options cannot be given as a dict Document that
the option list is handled by a dedicated routine (needed for the
two-argument
call(Event, Pred) option) and therefore must be a list,
not a dict.
- DOC: note sgml_parse/2 does not use PL_scan_options() Explain that
the option loop is hand-written because
call(Type, Goal) is an arity-2
option, and that these options therefore cannot be given as a dict.
- CLEANUP: use PL_scan_options() in new_sgml_parser/2 and open_dtd/3
Convert the hand-rolled option loops for the dtd option of
new_sgml_parser/2 and the dialect option of open_dtd/3. sgml_parse/2
is left hand-rolled: it has a
call(Type, Goal) option of arity 2,
which PL_scan_options() cannot express.
Package ssl
[Jul 16 2026]
- ENHANCED: _ssl_context/4 uses PL_scan_options() Replace the hand-rolled
first-pass option loop in
pl_ssl_context() with PL_scan_options(),
using OPT_UNKNOWN_IGNORE so the malleable options still fall through to
parse_malleable_options(). The two options whose values are processed
in complex ways are refactored into get_crls() and get_cert_key_pairs()
helpers that take the value term; password/require_crl scan as
OPT_STRING/OPT_BOOL and the remaining values as OPT_TERM. Drops the
now-unused option-name atoms and the unused get_file_arg() helper.
- CLEANUP: use PL_scan_options() in the crypto option loops Convert
the hand-rolled option loops in crypto4pl.c:
- hash_options: algorithm, hmac, close_parent and encoding.
- parse_options: encoding and padding (the atom-as-encoding shorthand
is kept).
[Jul 14 2026]
- MODIFIED: ed25519_sign/4 and ed25519_verify/4 default to
encoding(utf8)
This matches Scryer Prolog. Unlike ecdsa_sign/4 and rsa_sign/4,
which default to hex because they are typically applied to a hash
of the data, Ed25519 signs the data itself.
- TEST: Ed25519 and X25519 against the RFC 8032 and RFC 7748 test
vectors Also documents the new predicates in the crypto library manual.
- ADDED: load_private_key/3 and load_public_key/2 support
Ed25519 and X25519 Ed25519 private keys are unified as
private_key(ed25519(KeyPair)), where KeyPair is the hexadecimal
PKCS#8 v2 key pair that ed25519_sign/4 of library(crypto) accepts.
X25519 keys are unified as x25519(Hex), which curve25519_scalar_mult/3
accepts. This allows using keys created with `openssl genpkey
-algorithm ed25519` (or x25519).
- ADDED: #156 Ed25519 signatures and X25519 key exchange library(crypto)
now provides ed25519_new_keypair/1, ed25519_seed_keypair/2,
ed25519_keypair_public_key/2, ed25519_sign/4, ed25519_verify/4,
curve25519_generator/1 and curve25519_scalar_mult/3. The API is
that of Scryer Prolog's library(crypto). Key pairs are PKCS#8 v2
(RFC 8410), the format used by `openssl genpkey -algorithm ed25519`.
[Jul 11 2026]
- FIXED: single-threaded build of crypto4pl
crypto_lib_init() sat
inside an #ifdef _REENTRANT block but is called unconditionally from
install_crypto4pl(). OpenSSL >= 1.1.0 is thread safe, so the guard
is dead — drop it and keep the stub compiled in all configurations.
Package swipy
[Jul 7 2026]
- ENHANCED: `builtins' resolves to the builtins module, not
its dict Previously the special-cased atom `builtins' returned
PyEval_GetBuiltins(), which is the builtins dict. That worked
for arity>=1 calls via
builtin_function() (which does dict lookup) but
made py_call(builtins:list, X, [py_object(true)]) raise AttributeError,
since the dict has no attribute `list'.
- FIXED: py_call/N leaked exception from the initial-target probe
py_eval() probes the first argument as a Python object or module.
If that probe failed with an exception (e.g. PyImport_Import("list")
raising ModuleNotFoundError) but the fallback builtins path then
succeeded, py_call/N returned success with the exception still pending,
triggering `did not clear exception'. Propagate the exception instead.
Package tipc
[Jul 15 2026]
- CLEANUP: use PL_scan_options() in tipc_receive/4 Replace the
hand-rolled option loop with PL_scan_options(). as is scanned as a
term for its domain check; nonblock uses OPT_BOOL, so it is accepted
both as the bare atom and as
nonblock(Bool).
Package utf8proc
[Jul 17 2026]
- BUILD: doc generation racing ahead of foreign plugin builds
[Jul 16 2026]
- CLEANUP: use PL_scan_options() for the unicode mapping flags
get_map_mask() accepted a list of bare atom flags (stable, compose,
...) OR-ed into a mask. These are now scanned with PL_scan_options()
as arity-0 OPT_BOOL options (so a flag may also be written flag(true)),
using OPT_UNKNOWN_ERROR to keep raising domain_error(unicode_mapping,
_) on an unknown flag. An integer mask is still accepted directly.
Package xpce
[Jul 19 2026]
- CLEANUP: Changed timer time to
num (was real) and use xpce int
for SDL reference.
- ENHANCED: timer: keep a scheduled timer alive and destroy it when done
A scheduled timer now holds a code reference to itself, so a timer that
is not referenced from elsewhere can simply be created and started:
- FIXED: toc_directory: crash if the directory disappeared ->update
deletes the node if its directory no longer exists (common under
e.g. /proc), after which ->refresh and ->expand_all continued to use
the freed node.
- ENHANCED: Prolog Navigator: virtual root for multiple Windows drives
If the loaded source files have no common directory because they are
spread over multiple Windows drives, the new class toc_roots provides
a virtual root ("This computer") holding these drives. Pressing "up"
on a drive root now creates this node as well.
- ENHANCED: Prolog Navigator starts from the loaded source files The
Navigator is now rooted at the common directory of all loaded source
files and initially shows only the loaded files and the directories
leading to them, rather than browsing the file system from the
working directory.
[Jul 18 2026]
- ENHANCED: Windows: flat scroll bars with a thin thumb The `win'
look now fills the trough with the light system colour and draws the
thumb in a darker shade at about 1/5th of the width of the scroll bar,
widening to 1/3th while the pointer is inside the bar. This replaces
the dark trough with a raised 3-D bubble and is much closer to the
native Windows look.
- FIXED: SDL: generate area_enter and area_exit events for windows
SDL only reports enter and leave for its native windows, which are
our frames. As xpce windows never received these events, PceWindow
<->has_pointer was never updated and the <->pointed chain of a window
was only maintained as long as the pointer stayed in the same window.
[Jul 13 2026]
- FIXED: Epilog terminal wide glyph right half wiped by placeholder
r_clear The paint-run segmentation in
rlc_paint_text() split segments
on flags.raw, but a wide char's base has flags.width=2 while its
code-0 placeholder has flags.width=0, so base and placeholder ended
up in separate segments. The placeholder's own r_clear/paint step
(chunk width = cw) then wiped the right half of the glyph the base had
just drawn. The same latent split applies to width-0 combining marks
following their base. Followers (code==0 placeholders or width==0
combiners) now stay in the current base's segment regardless of
flags.raw, since they inherit the base's styling and armed status.
- FIXED: Epilog terminal draws BMP symbol glyphs (dingbats, misc symbols)
as 2 cells
uchar_display_width() classifies from static Unicode
tables + host wcwidth, which reports width 1 for BMP symbol blocks
(Dingbats U+2700-U+27BF, Misc Symbols U+2600-U+26FF, Misc Technical,
some arrows) that emoji-presenting fonts actually render at ~2 cells
wide, breaking the terminal grid. rlc_put() now consults the font's
own measured advance via c_width() when the static classification
is width 1 and the char is non-ASCII, promoting to width 2 when
the glyph exceeds 1.5x the base cell width. Reuses the SDL font's
existing lazy per-code-point width cache, so the Pango measurement
happens at most once per (font, char).
[Jul 12 2026]
- CLEANUP: Remove hacks to redirect xpce messages to Epilog. As we
now rebind the main thread
user_output and user_error streams,
we no longer need hacks to redirect messages printed by xpce.
If such messages are generated in a thread, they are printed in
the Epilog console of that thread. If they are generated in the
main (gui) thread, they are redirected to the main Epilog console.
Deleting this console causes subsequent output to go to the process
stdout/stderr streams.
- ENHANCED: Rebind main thread output to Epilog window. Rather than
intercepting print_message/2 to redirect messages from the GUI
(main) thread to the Epilog console, we rebind the
user_output and
user_error of the main thread to the main Epilog console.
- ADDED: date<-time_zone and date<-dst; use numeric offset in
<-rfc_string <-time_zone returns the local zone as a numeric offset
(default) or as the abbreviation (with @on). <-dst reports whether
daylight-saving time is in effect. <-rfc_string now emits the numeric
offset, matching RFC 5322 3.3 which obsoletes zone abbreviations.
[Jul 11 2026]
[Jul 10 2026]
- FIXED: xpce numeric conversion: rationals, bignums, fractional nums
Two related fixes plus a test suite:
- ADDED: xdot_group url slot populated from graphviz URL/href Nodes and
edges now expose their graphviz URL attribute (or its href alias) as a
url slot. Click handlers wired via <-node_clicked and <-edge_clicked
can fetch it with get(Group, url, URL).
- ADDED: Random terms demo Uses library(random_terms) with
library(graphviz_term) to generate random Prolog terms and visualise
them as graphs. A dialog exposes the generator parameters as sliders
(int/float) plus text_items for functors and atoms. The xdot_window
is configured with natural_zoom 1.5 so small terms fill the pane.
- ADDED:
library(graphviz_term), converting Prolog terms into `.dot`
files Emits Graphviz digraphs where each compound is a node whose HTML
label (shape=plaintext, `<TABLE>/<TD PORT="aN">`) carries a soft
pastel palette: a bold steel-blue functor cell plus per-primitive-
type backgrounds for integer, float, rational, atom, string, var,
and shared / cyclic back-references. Colours can be overridden by
redefining type_color/2.
- ENHANCED: xdot_window: <->natural_zoom slot for ->fit Adds a
natural_zoom slot (num, default 1.0). ->fit uses it as the preferred
scale: when the graph fits the visible area at natural_zoom that scale
is used; otherwise ->fit falls back to the shrink-to-fit behaviour.
Setting natural_zoom above 1.0 lets small graphs be enlarged on ->fit.
- REMOVED:
library(pce_edit), providing pce_edit/1. Editing xpce
objects is provided by the hookable edit/1 infrastructure.
[Jul 9 2026]
- ADDED:
library(xdot) + demo — graphviz pan/zoom viewer New Prolog
library and xpce demo that renders graphviz-laid-out graphs as XPCE
graphicals.
- FIXED: edit/1 support for xpce classes.
- ENHANCED: colour: <-
fade(factor) — same RGB with alpha multiplied
New send/get method colour<-fade(factor=[0.0..1.0]) -> colour
returns a colour with the same RGB and alpha := alpha * factor,
clamped to 0..255. Complements <-hilite and <-reduce; uses the same
associateColour caching so repeated calls with the same factor return
the same object.
- ENHANCED: ellipse, circle: shape-precise ->in_event_area + ->inside
Share
ellipseNormDistance() (in ellipse.c, exported via proto.h):
the normalised distance of a point from the centre of the ellipse
inscribed in an axis-aligned bounding box. <= 1 means inside;
semi-axes clamped to 1 to survive degenerate 0-width shapes.
- ENHANCED: path: precise event hit-test, ->inside, ref manual Register
inEventAreaPath via setInEventAreaFunctionClass so events land on
the actual path rather than its bounding rectangle:
- ENHANCED: bezier_curve: precise event hit-test on the curve
Register inEventAreaBezier via setInEventAreaFunctionClass so the
graphical class dispatcher runs a shape-accurate hit test after the
bounding-box pre-filter, mirroring what class line already does with
inEventAreaLine.
- ADDED: xpce: class gradient with cairo linear/radial rendering Native
cairo-backed gradient class usable as fill_pattern:
[Jul 8 2026]
- FIXED: pan_zoom: zoom around cursor when figure isn't at (0,0) The zoom
formula used the cursor position in window coords as the fixed point,
which only matches the figure's origin when the figure is displayed at
(0,0). Subtract the figure's display position so the scale pivots
on the actual cursor location regardless of where the figure lives
in its parent.
Package yaml
[Jul 16 2026]
- CLEANUP: use PL_scan_options() in yaml_emitter_create/3 Convert
the hand-rolled option loop for the canonical and unicode options,
scanned as terms so the "unicode defaults to true when not given"
behaviour is preserved.
Package zlib
[Jul 15 2026]
- CLEANUP: use PL_scan_options() in zopen/3 Replace the hand-rolled
option loop with PL_scan_options(). format and level are scanned
as terms for their domain checks; close_parent and multi_part use
OPT_BOOL.